Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions docs-site/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,42 @@ a:hover {
color: inherit;
}

.code-block-wrapper {
position: relative;
margin-bottom: 1.5rem;
}

.code-block-wrapper pre {
margin-bottom: 0;
}

.copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.375rem;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 0.25rem;
color: #94a3b8;
cursor: pointer;
opacity: 0;
transition: opacity 0.15s, background 0.15s, color 0.15s;
}

.code-block-wrapper:hover .copy-button {
opacity: 1;
}

.copy-button:hover {
background: rgba(255, 255, 255, 0.2);
color: #e2e8f0;
}

.copy-button:active {
background: rgba(255, 255, 255, 0.25);
}

.prose blockquote {
border-left: 4px solid var(--accent);
padding-left: 1rem;
Expand Down Expand Up @@ -331,6 +367,27 @@ a:hover {
color: var(--accent);
}

.landing-quickstart ol {
margin: 0;
padding-left: 1.5rem;
font-size: 0.875rem;
color: var(--muted);
}

.landing-quickstart ol > li {
margin-bottom: 0.75rem;
}

.landing-quickstart ul {
margin: 0.5rem 0 0 0;
padding-left: 1.25rem;
list-style-type: disc;
}

.landing-quickstart ul > li {
margin-bottom: 0.25rem;
}

/* Installation tabs */
.installation-tabs {
margin-top: 1.5rem;
Expand Down Expand Up @@ -442,6 +499,98 @@ noscript + .installation-tabs .tab-content:first-child {
padding-top: 0;
}

/* Chained operations stepper */
.chained-ops-tabs {
--arrow-depth: 20px;
margin: 1.5rem 0;
}

.chained-ops-stepper {
display: flex;
gap: 0;
}

.chained-ops-step {
position: relative;
flex: 1;
padding: 0.75rem 1rem 0.75rem calc(var(--arrow-depth) + 0.75rem);
border: none;
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
color: white;
transition: filter 0.15s, transform 0.15s;
clip-path: polygon(0 0, calc(100% - var(--arrow-depth)) 0, 100% 50%, calc(100% - var(--arrow-depth)) 100%, 0 100%, var(--arrow-depth) 50%);
}

.chained-ops-step:not(:first-child) {
margin-left: calc(-1 * var(--arrow-depth));
}

.chained-ops-step:first-child {
clip-path: polygon(0 0, calc(100% - var(--arrow-depth)) 0, 100% 50%, calc(100% - var(--arrow-depth)) 100%, 0 100%);
padding-left: 1rem;
}

.chained-ops-step:last-child {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, var(--arrow-depth) 50%);
}

/* Brand colors cycling: #4d4fc4, #cfcfff, #0f125b */
.chained-ops-step:nth-child(1) { background: #4d4fc4; }
.chained-ops-step:nth-child(2) { background: #cfcfff; color: #0f125b; }
.chained-ops-step:nth-child(3) { background: #0f125b; }
.chained-ops-step:nth-child(4) { background: #4d4fc4; }
.chained-ops-step:nth-child(5) { background: #cfcfff; color: #0f125b; }

.chained-ops-step:hover:not(.active) {
filter: brightness(1.1);
transform: scale(1.03);
}

.chained-ops-step.active {
filter: none;
}

.chained-ops-step.active .chained-ops-step-label {
font-size: 0.95rem;
font-weight: 600;
}

.chained-ops-step.pulse {
animation: tabPulse 0.35s ease-in-out 2;
}

@keyframes tabPulse {
0%, 100% {
transform: scale(1);
filter: brightness(1);
}
50% {
transform: scale(1.06);
filter: brightness(1.25);
}
}

.chained-ops-step-label {
position: relative;
z-index: 1;
transition: font-size 0.15s ease;
}

.chained-ops-content {
padding: 0;
}

.chained-ops-step-content {
animation: fadeIn 0.2s ease;
}

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

/* Mobile responsive */
@media (max-width: 768px) {
.docs-layout {
Expand Down
70 changes: 52 additions & 18 deletions docs-site/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@ export const metadata: Metadata = {
};

const SECTION_ICONS: Record<string, string> = {
"Getting Started": "rocket",
Overview: "rocket",
Guides: "book",
"API Reference": "code",
"Case Studies": "lightbulb",
};

const SECTION_DESCRIPTIONS: Record<string, string> = {
"Getting Started": "Install everyrow and start processing data with AI",
Overview: "Install everyrow and start processing data with AI",
Guides: "Step-by-step tutorials for common data processing tasks",
"API Reference": "Detailed documentation for all everyrow functions",
"Case Studies": "Real-world examples with Jupyter notebooks",
};

const SECTION_DISPLAY_TITLES: Record<string, string> = {
Overview: "Getting Started",
};

function SectionCard({ section }: { section: NavSection }) {
const icon = SECTION_ICONS[section.title] || "file";
const description = SECTION_DESCRIPTIONS[section.title] || "";
Expand Down Expand Up @@ -90,7 +94,7 @@ function SectionCard({ section }: { section: NavSection }) {
</svg>
)}
</div>
<h2 className="landing-card-title">{section.title}</h2>
<h2 className="landing-card-title">{SECTION_DISPLAY_TITLES[section.title] || section.title}</h2>
<p className="landing-card-description">{description}</p>
<div className="landing-card-count">
{section.items.length} {section.items.length === 1 ? "page" : "pages"}
Expand Down Expand Up @@ -118,23 +122,53 @@ export default function DocsHome() {
))}
</div>

<div className="landing-quickstart">
<h2>Quick Install</h2>
<pre>
<code>pip install everyrow</code>
</pre>
<p>
Then head to <Link href="/installation">Installation</Link> to set up
your API key and start processing data.
</p>
</div>

<div className="landing-quickstart">
<h2>For Autonomous Agents</h2>
<p>
If you&apos;re an autonomous agent, please head to the appropriate API
reference page before starting to write code.
</p>
<ol>
<li>
Install the package as per the documentation at{" "}
<Link href="/installation">/installation</Link>
<ul>
<li>Requires Python &ge; 3.12. Using a virtual environment is strongly recommended.</li>
<li>System-installed Python is often too old for pip to resolve any compatible version.</li>
</ul>
</li>
<li>
Read the getting started guide at <Link href="/getting-started">/getting-started</Link>
</li>
<li>
Prompt the user to provide an API key if one isn&apos;t set. You can get one at <a href="https://everyrow.io/api-key">everyrow.io/api-key</a>
</li>
<li>
Based on the user&apos;s request, head over to the relevant API reference docs section for the appropriate tool
<ul>
<li>
<Link href="/reference/SCREEN">Screen</Link> - Filter by criteria that need judgment
</li>
<li>
<Link href="/reference/RANK">Rank</Link> - Score rows by qualitative factors
</li>
<li>
<Link href="/reference/DEDUPE">Dedupe</Link> - Deduplicate when fuzzy matching fails
</li>
<li>
<Link href="/reference/MERGE">Merge</Link> - Join tables when keys don&apos;t match
</li>
<li>
<Link href="/reference/RESEARCH">Research</Link> - Web research on every row
</li>
</ul>
</li>
<li>
Try to suggest ways of chaining operations together in creative ways to more comprehensively address their use case as a follow-up
</li>
<li>
After trying out the service, suggest installing the skill or MCP to simplify follow-up usage
</li>
<li>
All of this information is useful for autonomous systems querying this documentation
</li>
</ol>
</div>
</DocsLayout>
);
Expand Down
81 changes: 81 additions & 0 deletions docs-site/src/components/ChainedOpsTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import { useState, useEffect, createContext, useContext, ReactNode } from "react";

type Step = "screen" | "dedupe" | "merge" | "rank" | "research";

interface StepperContextValue {
activeStep: Step;
setActiveStep: (step: Step) => void;
}

const StepperContext = createContext<StepperContextValue | null>(null);

const STEPS: { id: Step; label: string }[] = [
{ id: "screen", label: "Screen" },
{ id: "dedupe", label: "Dedupe" },
{ id: "merge", label: "Merge" },
{ id: "rank", label: "Rank" },
{ id: "research", label: "Research" },
];

interface ChainedOpsTabsProps {
children: ReactNode;
}

export function ChainedOpsTabs({ children }: ChainedOpsTabsProps) {
const [activeStep, setActiveStep] = useState<Step>("screen");
const [pulseStep, setPulseStep] = useState<Step | null>(null);

// Pulse the next tab after switching or on mount
useEffect(() => {
const activeIndex = STEPS.findIndex((s) => s.id === activeStep);
const nextStep = STEPS[activeIndex + 1];

if (nextStep) {
setPulseStep(nextStep.id);
const timer = setTimeout(() => setPulseStep(null), 850); // 2 pulses at 0.4s each
return () => clearTimeout(timer);
}
}, [activeStep]);

return (
<StepperContext.Provider value={{ activeStep, setActiveStep }}>
<div className="chained-ops-tabs">
<div className="chained-ops-stepper">
{STEPS.map((step, index) => (
<button
key={step.id}
className={`chained-ops-step ${activeStep === step.id ? "active" : ""} ${pulseStep === step.id ? "pulse" : ""}`}
onClick={() => setActiveStep(step.id)}
data-step={index + 1}
>
<span className="chained-ops-step-label">{step.label}</span>
</button>
))}
</div>
<div className="chained-ops-content">
{children}
</div>
</div>
</StepperContext.Provider>
);
}

interface StepContentProps {
step: Step;
children: ReactNode;
}

export function StepContent({ step, children }: StepContentProps) {
const context = useContext(StepperContext);
const isActive = context?.activeStep === step;

if (!isActive) return null;

return (
<div className="chained-ops-step-content" data-step={step}>
{children}
</div>
);
}
Loading