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
6 changes: 6 additions & 0 deletions docs/app/components/demos/recovery-demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ export default function RecoveryDemo(_props: DemoProps) {
setPlaying(true);
}, [reduced, DUR]);

// Auto-play on open; startPlay() shows the finished frame under reduced motion.
// biome-ignore lint/correctness/useExhaustiveDependencies: run once on mount.
useEffect(() => {
startPlay();
}, []);

const setFromX = useCallback(
(clientX: number) => {
const track = trackRef.current;
Expand Down
25 changes: 18 additions & 7 deletions docs/app/components/landing/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { Link } from "react-router";
import { useActiveSdk } from "@/hooks";

const COLS = [
type FootLink = {
label: string;
/** When `sdk` is set, this is an SDK-relative path (prefixed with /python|/node). */
href: string;
external?: boolean;
sdk?: boolean;
};

const COLS: { title: string; links: FootLink[] }[] = [
{
title: "Docs",
links: [
{
label: "Getting Started",
href: "/python/getting-started/installation",
href: "getting-started/installation",
sdk: true,
},
{ label: "Guides", href: "/python/guides" },
{ label: "Guides", href: "guides", sdk: true },
{ label: "Architecture", href: "/architecture" },
{ label: "API Reference", href: "/python/api-reference" },
{ label: "API Reference", href: "api-reference", sdk: true },
],
},
{
title: "More",
links: [
{ label: "Examples", href: "/python/more/examples" },
{ label: "Examples", href: "more/examples", sdk: true },
{ label: "Celery comparison", href: "/resources/comparison" },
{ label: "FAQ", href: "/resources/faq" },
{ label: "Changelog", href: "/resources/changelog" },
Expand Down Expand Up @@ -50,6 +60,7 @@ const COLS = [
];

export function Footer() {
const sdk = useActiveSdk();
return (
<footer className="foot">
<div className="foot-grid">
Expand All @@ -69,12 +80,12 @@ export function Footer() {
<div key={col.title} className="foot-col">
<h4>{col.title}</h4>
{col.links.map((l) =>
"external" in l && l.external ? (
l.external ? (
<a key={l.label} href={l.href} target="_blank" rel="noreferrer">
{l.label}
</a>
) : (
<Link key={l.label} to={l.href}>
<Link key={l.label} to={l.sdk ? `/${sdk}/${l.href}` : l.href}>
{l.label}
</Link>
),
Expand Down
85 changes: 23 additions & 62 deletions docs/app/components/landing/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Link } from "react-router";
import { RawHtml } from "@/components/ui";
import { useSdk } from "@/hooks";
import { highlightPython, highlightTs } from "@/lib/highlight-lite";
import { HERO_PANES, SOON_PANES, type SoonLang } from "@/lib/landing-content";
import { HERO_PANES } from "@/lib/landing-content";

type Lang = "py" | "ts" | "go" | "java";
type Lang = "py" | "ts";

function CopyButton({ text }: { text: string }) {
const [copied, setCopied] = useState(false);
Expand All @@ -24,31 +24,14 @@ function CopyButton({ text }: { text: string }) {
);
}

function SoonBox({ pane }: { pane: SoonLang }) {
return (
<div className="soonbox">
<div className="ring" />
<h4>{pane.heading}</h4>
<p>{pane.body}</p>
<span className="gh">github.com/ByteVeda/taskito</span>
</div>
);
}

export function Hero() {
const { sdk, setSdk } = useSdk();
// Roadmap langs (go/java) have no SDK — selected locally; py/ts mirror the
// global SDK so the hero tab and the docs sidebar switch stay in sync.
const [soon, setSoon] = useState<SoonLang["id"] | null>(null);
const lang: Lang = soon ?? (sdk === "node" ? "ts" : "py");
// py/ts mirror the global SDK so the hero tab and the docs sidebar switch stay in sync.
const isNode = sdk === "node";
const pane = HERO_PANES.find((p) => p.id === lang);
const codeHtml = pane
? lang === "ts"
? highlightTs(pane.code)
: highlightPython(pane.code)
: "";
const active = pane ?? HERO_PANES[0];
const lang: Lang = isNode ? "ts" : "py";
const active = HERO_PANES.find((p) => p.id === lang) ?? HERO_PANES[0];
const codeHtml =
lang === "ts" ? highlightTs(active.code) : highlightPython(active.code);

return (
<section className="hero">
Expand Down Expand Up @@ -106,53 +89,31 @@ export function Hero() {
key={p.id}
type="button"
className={`langtab ${p.id === lang ? "active" : ""}`.trim()}
onClick={() => {
setSoon(null);
setSdk(p.id === "ts" ? "node" : "python");
}}
onClick={() => setSdk(p.id === "ts" ? "node" : "python")}
>
{p.label}
{p.id === "ts" ? <span className="tag beta">Beta</span> : null}
</button>
))}
{SOON_PANES.map((p) => (
<button
key={p.id}
type="button"
className={`langtab dis ${p.id === lang ? "active" : ""}`.trim()}
onClick={() => setSoon(p.id)}
>
{p.label} <span className="tag soon">soon</span>
</button>
))}
{pane ? <CopyButton text={pane.code} /> : null}
<CopyButton text={active.code} />
</div>
<div id="hero-panes">
{pane ? (
<RawHtml as="pre" className="code" html={codeHtml} />
) : (
<SoonBox
pane={SOON_PANES.find((p) => p.id === lang) ?? SOON_PANES[0]}
/>
)}
<RawHtml as="pre" className="code" html={codeHtml} />
</div>
</div>

{pane ? (
<div className="out">
<div className="outset">
{pane.output.map((line) => (
<div className="oline show" key={line.text}>
<span className={line.glyphKind}>{line.glyph}</span>
<span className="var">{line.text}</span>
{line.value ? <span className="v">{line.value}</span> : null}
{line.timing ? (
<span className="t">{line.timing}</span>
) : null}
</div>
))}
</div>
<div className="out">
<div className="outset">
{active.output.map((line) => (
<div className="oline show" key={line.text}>
<span className={line.glyphKind}>{line.glyph}</span>
<span className="var">{line.text}</span>
{line.value ? <span className="v">{line.value}</span> : null}
{line.timing ? <span className="t">{line.timing}</span> : null}
</div>
))}
</div>
) : null}
</div>

<div className="hero-doclinks">
<Link
Expand All @@ -162,7 +123,7 @@ export function Hero() {
Read the Python quickstart →
</Link>
<Link className="hero-doclink" to="/node/getting-started/quickstart">
Read the TypeScript quickstart →
Read the Node.js quickstart →
</Link>
</div>
</div>
Expand Down
17 changes: 16 additions & 1 deletion docs/app/components/landing/scenario-finder.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { type KeyboardEvent, useEffect, useRef, useState } from "react";
import { Link } from "react-router";
import { RawHtml } from "@/components/ui";
import { type Sdk, useActiveSdk } from "@/hooks";
import { DemoModal, type DemoTarget } from "./demo-modal";

// Scenario guides are authored as Python paths; most map to Node by swapping the
// SDK segment, but a few pages live under a different slug in the Node tree.
const NODE_GUIDE_OVERRIDES: Record<string, string> = {
"/python/guides/advanced-execution/streaming": "/node/guides/core/streaming",
"/python/guides/workflows/sagas": "/node/guides/workflows/saga",
};

/** Resolve a scenario's Python guide path to the active SDK's docs. */
function guideHref(pyPath: string, sdk: Sdk): string {
if (sdk !== "node") return pyPath;
return NODE_GUIDE_OVERRIDES[pyPath] ?? pyPath.replace("/python/", "/node/");
}

interface Bullet {
/** Short mono label in the left rail. */
k: string;
Expand Down Expand Up @@ -407,6 +421,7 @@ function motionDisabled(): boolean {
* in {@link DemoModal}, the secondary links the guide that goes deeper.
*/
export function ScenarioFinder() {
const sdk = useActiveSdk();
const [selected, setSelected] = useState(0);
// Drives the card's enter animation; re-triggered on every selection change.
const [entered, setEntered] = useState(true);
Expand Down Expand Up @@ -570,7 +585,7 @@ export function ScenarioFinder() {
{active.demo.label}
{CTA_ARROW}
</button>
<Link className="btn sec" to={active.guide.to}>
<Link className="btn sec" to={guideHref(active.guide.to, sdk)}>
{active.guide.label}
</Link>
</div>
Expand Down
41 changes: 29 additions & 12 deletions docs/app/components/landing/sections.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState } from "react";
import { Link } from "react-router";
import { RawHtml } from "@/components/ui";
import { useActiveSdk } from "@/hooks";
import { highlightPython } from "@/lib/highlight-lite";
import {
CODE_CELERY,
Expand Down Expand Up @@ -233,6 +234,7 @@ export function Features() {
}

export function UseCases() {
const sdk = useActiveSdk();
return (
<section
className="section"
Expand All @@ -248,19 +250,34 @@ export function UseCases() {
lead="Pick the workload — taskito ships the primitives."
/>
<div className="uc-grid">
{USE_CASES.map((c) => (
<div key={c.title} className="uc reveal">
<div className="ic">
<Icon d={c.icon} rect={c.rect} />
</div>
<div>
<h3>
{c.title} <span className="arr">→</span>
</h3>
<RawHtml as="p" html={c.body} />
{USE_CASES.map((c) => {
const inner = (
<>
<div className="ic">
<Icon d={c.icon} rect={c.rect} />
</div>
<div>
<h3>
{c.title} <span className="arr">→</span>
</h3>
<RawHtml as="p" html={c.body} />
</div>
</>
);
return c.href ? (
<Link
key={c.title}
to={`/${sdk}/${c.href}`}
className="uc reveal"
>
{inner}
</Link>
) : (
<div key={c.title} className="uc reveal">
{inner}
</div>
</div>
))}
);
})}
</div>
</div>
</section>
Expand Down
1 change: 1 addition & 0 deletions docs/app/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { usePrefetchDocs } from "./use-prefetch-docs";
export { type Sdk, useActiveSdk, useSdk } from "./use-sdk";
15 changes: 15 additions & 0 deletions docs/app/hooks/use-prefetch-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useEffect } from "react";
import { prefetchSdkDocs } from "@/lib/prefetch";
import { useActiveSdk } from "./use-sdk";

/**
* Warm the active SDK's docs in the background. Runs on mount and whenever the
* selected language changes (hero tab, sidebar switcher, or a `/node|/python`
* URL), so the first navigation into that SDK's docs is instant.
*/
export function usePrefetchDocs(): void {
const sdk = useActiveSdk();
useEffect(() => {
prefetchSdkDocs(sdk);
}, [sdk]);
}
33 changes: 8 additions & 25 deletions docs/app/lib/landing-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ print(job.result()) # → 5`,
},
{
id: "ts",
label: "TypeScript",
label: "Node.js",
filename: "tasks.ts",
install: "pnpm add taskito",
code: `import { Queue } from "taskito";
Expand All @@ -83,30 +83,7 @@ console.log(await queue.result(id)); // → 5`,
},
],
docHref: "/node/getting-started/quickstart",
docLabel: "Read the TypeScript quickstart",
},
];

/** Roadmap languages: disabled tab + "coming soon" panel (no fabricated SDK). */
export interface SoonLang {
id: "go" | "java";
label: string;
heading: string;
body: string;
}

export const SOON_PANES: SoonLang[] = [
{
id: "go",
label: "Go",
heading: "Go client — coming soon",
body: "A native Go client for enqueuing and inspecting taskito jobs is on the roadmap.",
},
{
id: "java",
label: "Java",
heading: "Java client — coming soon",
body: "A JVM client for enqueuing taskito jobs from Java & Kotlin is planned.",
docLabel: "Read the Node.js quickstart",
},
];

Expand All @@ -115,6 +92,8 @@ export interface IconCard {
rect?: boolean;
title: string;
body: string;
/** SDK-relative doc path (no leading slash, no `/python|/node` prefix). */
href?: string;
}

export const FEATURES: IconCard[] = [
Expand Down Expand Up @@ -156,22 +135,26 @@ export const USE_CASES: IconCard[] = [
icon: "M12 2C6.48 2 2 4.02 2 6.5v11C2 19.98 6.48 22 12 22s10-2.02 10-4.5v-11C22 4.02 17.52 2 12 2zM2 6.5C2 8.43 6.48 10 12 10s10-1.57 10-3.5M2 12c0 1.93 4.48 3.5 10 3.5s10-1.57 10-3.5",
title: "ETL pipelines",
body: "Chain extract → transform → load as a DAG. Fan out across workers, fan in to aggregate, restart from any node on failure.",
href: "more/examples/data-pipeline",
},
{
icon: "M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2zM22 6l-10 7L2 6",
title: "Email & notifications",
body: "Bursty SMTP, push, or webhook delivery. Per-task rate limits keep providers happy; retries with backoff handle transient failures.",
href: "more/examples/notifications",
},
{
icon: "M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3",
rect: true,
title: "ML inference & batch",
body: "Long-running model jobs with progress tracking, soft timeouts, and prefork pools for true CPU parallelism without GIL contention.",
href: "more/examples/benchmark",
},
{
icon: "M12 6v6l4 2M12 22a10 10 0 1 1 0-20 10 10 0 0 1 0 20z",
title: "Scheduled jobs",
body: "Six-field cron syntax down to the second. Periodic tasks live in the scheduler — no separate beat daemon to babysit.",
href: "guides/core/scheduling",
},
];

Expand Down
Loading
Loading