From 1f19ab2571fee99e87dea1ede529da874676a2aa Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:35:36 +0530 Subject: [PATCH 1/6] chore(docs): add cn() utility (clsx + tailwind-merge) --- docs/package.json | 1 + docs/pnpm-lock.yaml | 35 +++++++++++++++++++++++++++++++++++ docs/src/lib/cn.ts | 7 ++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index de0bae5..2ab6dd3 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "@orama/orama": "^3.1.18", + "clsx": "^2.1.1", "fumadocs-core": "16.8.5", "fumadocs-mdx": "14.3.2", "fumadocs-ui": "16.8.5", diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 1d783a4..7142474 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@orama/orama': specifier: ^3.1.18 version: 3.1.18 + clsx: + specifier: ^2.1.1 + version: 2.1.1 fumadocs-core: specifier: 16.8.5 version: 16.8.5(@mdx-js/mdx@3.1.1)(@types/estree-jsx@1.0.5)(@types/hast@3.0.4)(@types/mdast@4.0.4)(@types/react@19.2.14)(lucide-react@1.14.0(react@19.2.5))(next@16.2.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.4.2) @@ -104,24 +107,28 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [musl] '@biomejs/cli-linux-arm64@2.4.14': resolution: {integrity: sha512-2TELhZnW5RSLL063l9rc5xLpA0ZIw0Ccwy/0q384rvNAgFw3yI76bd59547yxowdQr5MNPET/xDLrLuvgSeeWQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] + libc: [glibc] '@biomejs/cli-linux-x64-musl@2.4.14': resolution: {integrity: sha512-R6BWgJdQOwW9ulJatuTVrQkjnODjqHZkKNOqb1sz++3Noe5LYd0i3PchnOBUCYAPHoPWHhjJqbdZlHEu0hpjdA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [musl] '@biomejs/cli-linux-x64@2.4.14': resolution: {integrity: sha512-zHrlQZDBDUz4OLAraYpWKcnLS6HOewBFWYOzY91d1ZjdqZwibOyb6BEu6WuWLugyo0P3riCmsbV9UqV1cSXwQg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] + libc: [glibc] '@biomejs/cli-win32-arm64@2.4.14': resolution: {integrity: sha512-M3EH5hqOI/F/FUA2u4xcLoUgmxd218mvuj/6JL7Hv2toQvr2/AdOvKSpGkoRuWFCtQPVa+ZqkEV3Q5xBA9+XSA==} @@ -374,89 +381,105 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] + libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] + libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] + libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] + libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -523,24 +546,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@next/swc-linux-arm64-musl@16.2.4': resolution: {integrity: sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@next/swc-linux-x64-gnu@16.2.4': resolution: {integrity: sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@next/swc-linux-x64-musl@16.2.4': resolution: {integrity: sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@next/swc-win32-arm64-msvc@16.2.4': resolution: {integrity: sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==} @@ -998,24 +1025,28 @@ packages: engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.2.4': resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.2.4': resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.2.4': resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==} engines: {node: '>= 20'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.2.4': resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==} @@ -1966,24 +1997,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} diff --git a/docs/src/lib/cn.ts b/docs/src/lib/cn.ts index 8e473da..b500fb1 100644 --- a/docs/src/lib/cn.ts +++ b/docs/src/lib/cn.ts @@ -1 +1,6 @@ -export { twMerge as cn } from "tailwind-merge"; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)); +} From 789dcd4724c4f061ca9339f7839a5934bc66d3df Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:36:04 +0530 Subject: [PATCH 2/6] feat(docs): add highlight() helper around fumadocs-core/highlight --- docs/src/lib/highlight.tsx | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/src/lib/highlight.tsx diff --git a/docs/src/lib/highlight.tsx b/docs/src/lib/highlight.tsx new file mode 100644 index 0000000..3e92930 --- /dev/null +++ b/docs/src/lib/highlight.tsx @@ -0,0 +1,32 @@ +import { highlight as fumaHighlight } from "fumadocs-core/highlight"; +import type { ReactNode } from "react"; +import { cn } from "@/lib/cn"; + +export type HighlightLang = "python" | "bash" | "tsx" | "ts" | "json" | "yaml"; + +const DEFAULT_PRE_CLASSES = + "p-5 text-sm leading-relaxed overflow-x-auto bg-fd-card"; + +export async function highlight( + code: string, + lang: HighlightLang, + preClassName?: string, +): Promise { + return fumaHighlight(code, { + lang, + themes: { + light: "github-light", + dark: "github-dark", + }, + components: { + pre: ({ children, className, ...props }) => ( +
+          {children}
+        
+ ), + }, + }); +} From a76ca94f18149a461bfefb88f7e21280fa5adf4f Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:36:55 +0530 Subject: [PATCH 3/6] feat(docs): add Button, SectionHeader, CodePanel UI primitives --- docs/src/components/ui/button.tsx | 63 +++++++++++++++++ docs/src/components/ui/code-panel.tsx | 83 +++++++++++++++++++++++ docs/src/components/ui/section-header.tsx | 32 +++++++++ 3 files changed, 178 insertions(+) create mode 100644 docs/src/components/ui/button.tsx create mode 100644 docs/src/components/ui/code-panel.tsx create mode 100644 docs/src/components/ui/section-header.tsx diff --git a/docs/src/components/ui/button.tsx b/docs/src/components/ui/button.tsx new file mode 100644 index 0000000..bac0894 --- /dev/null +++ b/docs/src/components/ui/button.tsx @@ -0,0 +1,63 @@ +import Link from "next/link"; +import type { ComponentProps, ReactNode } from "react"; +import { cn } from "@/lib/cn"; + +export type ButtonVariant = "primary" | "secondary" | "ghost"; + +const VARIANT_CLASSES: Record = { + primary: + "bg-fd-primary text-fd-primary-foreground hover:opacity-90 transition-opacity", + secondary: + "border border-fd-border bg-fd-card hover:bg-fd-accent transition-colors", + ghost: + "text-fd-muted-foreground hover:text-fd-foreground transition-colors", +}; + +const BASE_CLASSES = + "inline-flex items-center gap-2 rounded-md px-5 py-2.5 text-sm font-medium"; + +type CommonProps = { + variant?: ButtonVariant; + icon?: ReactNode; + children: ReactNode; + className?: string; +}; + +type AsLink = CommonProps & { + href: string; +} & Omit, "href" | "className" | "children">; + +type AsButton = CommonProps & { + href?: undefined; +} & Omit< + ComponentProps<"button">, + "className" | "children" | keyof CommonProps + >; + +export function Button(props: AsLink | AsButton) { + const { + variant = "primary", + icon, + children, + className, + ...rest + } = props; + const classes = cn(BASE_CLASSES, VARIANT_CLASSES[variant], className); + + if ("href" in rest && rest.href !== undefined) { + const { href, ...linkRest } = rest; + return ( + + {children} + {icon} + + ); + } + + return ( + + ); +} diff --git a/docs/src/components/ui/code-panel.tsx b/docs/src/components/ui/code-panel.tsx new file mode 100644 index 0000000..1072eef --- /dev/null +++ b/docs/src/components/ui/code-panel.tsx @@ -0,0 +1,83 @@ +import type { ReactNode } from "react"; +import { cn } from "@/lib/cn"; + +export type CodePanelTone = "primary" | "muted" | "default"; + +const TONE_CLASSES: Record = { + primary: "border-t-2 border-t-fd-primary", + muted: "border-t-2 border-t-fd-border", + default: "", +}; + +const LABEL_TONE_CLASSES: Record = { + primary: "text-fd-primary", + muted: "text-fd-foreground", + default: "text-fd-foreground", +}; + +type LabelHeader = { + label: string; + caption?: string; + header?: undefined; +}; + +type CustomHeader = { + label?: undefined; + caption?: undefined; + header: ReactNode; +}; + +type NoHeader = { + label?: undefined; + caption?: undefined; + header?: undefined; +}; + +export type CodePanelProps = { + tone?: CodePanelTone; + className?: string; + children: ReactNode; +} & (LabelHeader | CustomHeader | NoHeader); + +export function CodePanel(props: CodePanelProps) { + const { tone = "default", className, children } = props; + + return ( +
+ {renderHeader(props)} + {children} +
+ ); +} + +function renderHeader(props: CodePanelProps) { + if (props.header !== undefined) { + return ( +
+ {props.header} +
+ ); + } + if (props.label !== undefined) { + const tone = props.tone ?? "default"; + return ( +
+ + {props.label} + + {props.caption ? ( + + {props.caption} + + ) : null} +
+ ); + } + return null; +} diff --git a/docs/src/components/ui/section-header.tsx b/docs/src/components/ui/section-header.tsx new file mode 100644 index 0000000..d8196d9 --- /dev/null +++ b/docs/src/components/ui/section-header.tsx @@ -0,0 +1,32 @@ +import { cn } from "@/lib/cn"; + +export type SectionHeaderAlign = "center" | "left"; + +export function SectionHeader({ + title, + description, + align = "center", + className, +}: { + title: string; + description?: string; + align?: SectionHeaderAlign; + className?: string; +}) { + return ( +
+

+ {title} +

+ {description ? ( +

{description}

+ ) : null} +
+ ); +} From a38404a433a89c672c4398d87affb0cfae33f8f3 Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:38:05 +0530 Subject: [PATCH 4/6] feat(docs): extract landing copy into lib/landing-content.tsx --- docs/src/lib/landing-content.tsx | 214 +++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 docs/src/lib/landing-content.tsx diff --git a/docs/src/lib/landing-content.tsx b/docs/src/lib/landing-content.tsx new file mode 100644 index 0000000..295aeb6 --- /dev/null +++ b/docs/src/lib/landing-content.tsx @@ -0,0 +1,214 @@ +import { + Activity, + Cpu, + Layers, + type LucideIcon, + Shield, + Workflow, + Zap, +} from "lucide-react"; + +export type CtaTarget = { + href: string; + label: string; +}; + +export type LandingHero = { + badge: string; + headline: string[]; + description: string; + primaryCta: CtaTarget; + secondaryCta: CtaTarget; + ghostCta: CtaTarget; + preview: { + filename: string; + code: string; + }; +}; + +export type LandingFeature = { + icon: LucideIcon; + title: string; + body: string; +}; + +export type ComparisonRow = { + label: string; + taskito: string; + celery: string; +}; + +export type ComparisonStack = { + label: string; + caption: string; + code: string; +}; + +export type LandingComparison = { + title: string; + description: string; + taskito: ComparisonStack; + celery: ComparisonStack; + rows: ComparisonRow[]; +}; + +export type LandingCta = { + title: string; + description: string; + primary: CtaTarget; + secondary: CtaTarget; +}; + +export const HERO: LandingHero = { + badge: "v0.12 — Rust core, native async, DAG workflows", + headline: ["Task queue", "without the broker."], + description: + "Rust-powered task queue for Python. Replace Celery without Redis or RabbitMQ. Start with SQLite, scale to Postgres.", + primaryCta: { + href: "/docs/getting-started/quickstart", + label: "Quickstart", + }, + secondaryCta: { + href: "/docs/getting-started/installation", + label: "Install", + }, + ghostCta: { + href: "https://github.com/ByteVeda/taskito", + label: "GitHub →", + }, + preview: { + filename: "tasks.py", + code: `# pip install taskito +from taskito import Queue + +queue = Queue(db_path="tasks.db") + +@queue.task() +def add(a, b): + return a + b + +job = add.delay(2, 3) +print(job.result()) # 5`, + }, +}; + +export const FEATURES_TITLE = "What you get"; +export const FEATURES_DESCRIPTION = + "The convenience of Celery, the performance of Rust, the simplicity of SQLite."; + +export const FEATURES: LandingFeature[] = [ + { + icon: Zap, + title: "Brokerless", + body: "No Redis, no RabbitMQ. Everything in a single SQLite file — queue, results, rate limits, schedules. Just `pip install` and go.", + }, + { + icon: Cpu, + title: "Rust-powered", + body: "The scheduler, dispatcher, and storage engine are all Rust. Tokio runtime, OS-thread worker pool, thin PyO3 boundary keeps the Python overhead negligible.", + }, + { + icon: Activity, + title: "Async-first", + body: "`async def` tasks dispatch onto a dedicated event loop — no `asyncio.run()` wrapping, no thread-pool bridging. Sync and async tasks coexist transparently.", + }, + { + icon: Workflow, + title: "DAG workflows", + body: "Multi-step pipelines as directed acyclic graphs. Fan-out, fan-in, conditions, approval gates, sub-workflows, incremental re-runs, Mermaid visualization.", + }, + { + icon: Layers, + title: "Resource system", + body: "Inject database connections, HTTP clients, and cloud SDKs by name. Three-layer pipeline: argument interception, worker DI, transparent proxy reconstruction.", + }, + { + icon: Shield, + title: "Production-ready", + body: "Retries with exponential backoff, dead letter queue, rate limits, circuit breakers, distributed locks, structured logs, OTel/Sentry/Prometheus middleware.", + }, +]; + +export const COMPARISON: LandingComparison = { + title: "Less to operate", + description: + "The same task, two stacks. Side by side, with the operational delta.", + taskito: { + label: "taskito", + caption: "Brokerless · single process", + code: `from taskito import Queue + +queue = Queue(db_path="tasks.db") + +@queue.task(max_retries=3, rate_limit="100/m") +def send_email(to, subject, body): + smtp.send(to, subject, body) + +# Enqueue +send_email.delay("alice@example.com", "Hi", "Body") + +# Run the worker +# $ taskito worker --app tasks:queue`, + }, + celery: { + label: "Celery + Redis", + caption: "Requires Redis · 3 processes", + code: `from celery import Celery + +app = Celery( + "myapp", + broker="redis://localhost:6379/0", + backend="redis://localhost:6379/1", +) +app.conf.task_default_rate_limit = "100/m" + +@app.task(bind=True, max_retries=3) +def send_email(self, to, subject, body): + try: + smtp.send(to, subject, body) + except SMTPError as exc: + raise self.retry(exc=exc, countdown=60) + +# Enqueue +send_email.delay("alice@example.com", "Hi", "Body") + +# Run the worker (in a separate terminal, plus Redis) +# $ celery -A myapp worker --loglevel=info`, + }, + rows: [ + { + label: "Install", + taskito: "pip install taskito", + celery: "pip install celery[redis] + run Redis daemon", + }, + { + label: "Background services", + taskito: "1 (worker)", + celery: "3 (worker, beat, Redis)", + }, + { + label: "Default storage", + taskito: "SQLite file (built-in)", + celery: "Redis (separate daemon)", + }, + { + label: "Retry config in the example above", + taskito: "max_retries=3 decorator arg", + celery: "try/except + self.retry(exc=…)", + }, + ], +}; + +export const CTA: LandingCta = { + title: "Five minutes from `pip install` to your first job.", + description: + "The quickstart walks you through defining a task, enqueuing it, and watching the worker run it — no Redis, no broker, no config.", + primary: { + href: "/docs/getting-started/quickstart", + label: "Start the quickstart", + }, + secondary: { + href: "/docs/more/comparison", + label: "See the full comparison", + }, +}; From ef9970f8a0c64ab24239e7d2b3f5d0158c4c03cb Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:45:52 +0530 Subject: [PATCH 5/6] refactor(docs): split landing into _sections/ and use shared primitives --- .../src/app/(home)/_sections/_window-dots.tsx | 14 ++ .../(home)/_sections/comparison-section.tsx | 94 ++++++++ docs/src/app/(home)/_sections/cta.tsx | 45 ++++ docs/src/app/(home)/_sections/features.tsx | 36 +++ docs/src/app/(home)/_sections/hero.tsx | 65 +++++ docs/src/app/(home)/_sections/index.ts | 4 + docs/src/app/(home)/comparison.tsx | 197 --------------- docs/src/app/(home)/page.tsx | 228 +----------------- docs/src/components/ui/button.tsx | 11 +- docs/src/components/ui/index.ts | 10 + docs/src/lib/cn.ts | 2 +- 11 files changed, 272 insertions(+), 434 deletions(-) create mode 100644 docs/src/app/(home)/_sections/_window-dots.tsx create mode 100644 docs/src/app/(home)/_sections/comparison-section.tsx create mode 100644 docs/src/app/(home)/_sections/cta.tsx create mode 100644 docs/src/app/(home)/_sections/features.tsx create mode 100644 docs/src/app/(home)/_sections/hero.tsx create mode 100644 docs/src/app/(home)/_sections/index.ts delete mode 100644 docs/src/app/(home)/comparison.tsx create mode 100644 docs/src/components/ui/index.ts diff --git a/docs/src/app/(home)/_sections/_window-dots.tsx b/docs/src/app/(home)/_sections/_window-dots.tsx new file mode 100644 index 0000000..46047ab --- /dev/null +++ b/docs/src/app/(home)/_sections/_window-dots.tsx @@ -0,0 +1,14 @@ +export function WindowDots({ filename }: { filename: string }) { + return ( + <> +
+ + + +
+ + {filename} + + + ); +} diff --git a/docs/src/app/(home)/_sections/comparison-section.tsx b/docs/src/app/(home)/_sections/comparison-section.tsx new file mode 100644 index 0000000..2aa895a --- /dev/null +++ b/docs/src/app/(home)/_sections/comparison-section.tsx @@ -0,0 +1,94 @@ +import { CodePanel, SectionHeader } from "@/components/ui"; +import { highlight } from "@/lib/highlight"; +import { COMPARISON } from "@/lib/landing-content"; + +export async function ComparisonSection() { + const [taskitoCode, celeryCode] = await Promise.all([ + highlight(COMPARISON.taskito.code, "python"), + highlight(COMPARISON.celery.code, "python"), + ]); + + return ( +
+ +
+
+ + {taskitoCode} + + + {celeryCode} + +
+ +
+
+ ); +} + +function DifferentiatorTable() { + return ( +
+ + + + + + + + + + {COMPARISON.rows.map((row, i) => ( + + + + + + ))} + +
+ Property + + {COMPARISON.taskito.label} + + {COMPARISON.celery.label} +
+ {row.label} + + {row.taskito} + + {row.celery} +
+
+ ); +} diff --git a/docs/src/app/(home)/_sections/cta.tsx b/docs/src/app/(home)/_sections/cta.tsx new file mode 100644 index 0000000..eabd384 --- /dev/null +++ b/docs/src/app/(home)/_sections/cta.tsx @@ -0,0 +1,45 @@ +import { ArrowRight } from "lucide-react"; +import { Button } from "@/components/ui"; +import { CTA as CTA_CONTENT } from "@/lib/landing-content"; + +export function CTA() { + return ( +
+
+

+ {renderTitleWithCode(CTA_CONTENT.title)} +

+

+ {CTA_CONTENT.description} +

+
+ + +
+
+
+ ); +} + +function renderTitleWithCode(title: string) { + const parts = title.split(/(`[^`]+`)/g); + return parts.map((part, i) => { + if (part.startsWith("`") && part.endsWith("`")) { + return ( + // biome-ignore lint/suspicious/noArrayIndexKey: stable split + + {part.slice(1, -1)} + + ); + } + return part; + }); +} diff --git a/docs/src/app/(home)/_sections/features.tsx b/docs/src/app/(home)/_sections/features.tsx new file mode 100644 index 0000000..98a70c7 --- /dev/null +++ b/docs/src/app/(home)/_sections/features.tsx @@ -0,0 +1,36 @@ +import { SectionHeader } from "@/components/ui"; +import { + FEATURES, + FEATURES_DESCRIPTION, + FEATURES_TITLE, + type LandingFeature, +} from "@/lib/landing-content"; + +export function Features() { + return ( +
+ +
+ {FEATURES.map((feature) => ( + + ))} +
+
+ ); +} + +function FeatureCard({ feature }: { feature: LandingFeature }) { + const { icon: Icon, title, body } = feature; + return ( +
+
+ +
+

{title}

+

{body}

+
+ ); +} diff --git a/docs/src/app/(home)/_sections/hero.tsx b/docs/src/app/(home)/_sections/hero.tsx new file mode 100644 index 0000000..626d432 --- /dev/null +++ b/docs/src/app/(home)/_sections/hero.tsx @@ -0,0 +1,65 @@ +import { ArrowRight } from "lucide-react"; +import { Fragment } from "react"; +import { Button, CodePanel } from "@/components/ui"; +import { highlight } from "@/lib/highlight"; +import { HERO } from "@/lib/landing-content"; +import { WindowDots } from "./_window-dots"; + +export async function Hero() { + const highlightedPreview = await highlight(HERO.preview.code, "python"); + + return ( +
+
+
+
+ {HERO.badge} +

+ {HERO.headline.map((line, i) => ( + + {line} + {i < HERO.headline.length - 1 ?
: null} +
+ ))} +

+

+ {HERO.description} +

+
+ + + +
+
+ } + className="shadow-xl shadow-fd-primary/5" + > + {highlightedPreview} + +
+
+ ); +} + +function Badge({ children }: { children: React.ReactNode }) { + return ( +
+ + {children} +
+ ); +} diff --git a/docs/src/app/(home)/_sections/index.ts b/docs/src/app/(home)/_sections/index.ts new file mode 100644 index 0000000..272f7e4 --- /dev/null +++ b/docs/src/app/(home)/_sections/index.ts @@ -0,0 +1,4 @@ +export { ComparisonSection } from "./comparison-section"; +export { CTA } from "./cta"; +export { Features } from "./features"; +export { Hero } from "./hero"; diff --git a/docs/src/app/(home)/comparison.tsx b/docs/src/app/(home)/comparison.tsx deleted file mode 100644 index 37a82c2..0000000 --- a/docs/src/app/(home)/comparison.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import { highlight } from "fumadocs-core/highlight"; - -const TASKITO_CODE = `from taskito import Queue - -queue = Queue(db_path="tasks.db") - -@queue.task(max_retries=3, rate_limit="100/m") -def send_email(to, subject, body): - smtp.send(to, subject, body) - -# Enqueue -send_email.delay("alice@example.com", "Hi", "Body") - -# Run the worker -# $ taskito worker --app tasks:queue`; - -const CELERY_CODE = `from celery import Celery - -app = Celery( - "myapp", - broker="redis://localhost:6379/0", - backend="redis://localhost:6379/1", -) -app.conf.task_default_rate_limit = "100/m" - -@app.task(bind=True, max_retries=3) -def send_email(self, to, subject, body): - try: - smtp.send(to, subject, body) - except SMTPError as exc: - raise self.retry(exc=exc, countdown=60) - -# Enqueue -send_email.delay("alice@example.com", "Hi", "Body") - -# Run the worker (in a separate terminal, plus Redis) -# $ celery -A myapp worker --loglevel=info`; - -type Tone = "primary" | "muted"; - -const ROWS: { label: string; taskito: string; celery: string }[] = [ - { - label: "Install", - taskito: "pip install taskito", - celery: "pip install celery[redis] + run Redis daemon", - }, - { - label: "Background services", - taskito: "1 (worker)", - celery: "3 (worker, beat, Redis)", - }, - { - label: "Default storage", - taskito: "SQLite file (built-in)", - celery: "Redis (separate daemon)", - }, - { - label: "Retry config in the example above", - taskito: "max_retries=3 decorator arg", - celery: "try/except + self.retry(exc=…)", - }, -]; - -export async function Comparison() { - const [taskitoHighlighted, celeryHighlighted] = await Promise.all([ - renderHighlighted(TASKITO_CODE), - renderHighlighted(CELERY_CODE), - ]); - - return ( -
-
- - {taskitoHighlighted} - - - {celeryHighlighted} - -
- -
- ); -} - -async function renderHighlighted(code: string) { - return highlight(code, { - lang: "python", - themes: { - light: "github-light", - dark: "github-dark", - }, - components: { - pre: ({ children, ...props }) => ( -
-          {children}
-        
- ), - }, - }); -} - -function CodePanel({ - label, - caption, - tone, - children, -}: { - label: string; - caption: string; - tone: Tone; - children: React.ReactNode; -}) { - const accent = - tone === "primary" ? "border-t-fd-primary" : "border-t-fd-border"; - return ( -
-
- - {label} - - {caption} -
- {children} -
- ); -} - -function DifferentiatorTable() { - return ( -
- - - - - - - - - - {ROWS.map((row, i) => ( - - - - - - ))} - -
- Property - - taskito - - Celery + Redis -
- {row.label} - - {row.taskito} - - {row.celery} -
-
- ); -} diff --git a/docs/src/app/(home)/page.tsx b/docs/src/app/(home)/page.tsx index 0246559..8ad91ad 100644 --- a/docs/src/app/(home)/page.tsx +++ b/docs/src/app/(home)/page.tsx @@ -1,14 +1,4 @@ -import { - Activity, - ArrowRight, - Cpu, - Layers, - Shield, - Workflow, - Zap, -} from "lucide-react"; -import Link from "next/link"; -import { Comparison } from "./comparison"; +import { ComparisonSection, CTA, Features, Hero } from "./_sections"; export default function HomePage() { return ( @@ -20,219 +10,3 @@ export default function HomePage() { ); } - -function Hero() { - return ( -
-
-
-
-
- - v0.12 — Rust core, native async, DAG workflows -
-

- Task queue -
- without the broker. -

-

- Rust-powered task queue for Python. Replace Celery without Redis or - RabbitMQ. Start with SQLite, scale to Postgres. -

-
- - Quickstart - - - - Install - - - GitHub → - -
-
- -
-
- ); -} - -function CodePreview() { - return ( -
-
-
-
-
-
-
- - tasks.py - -
-
-        
-          
-            # pip install taskito
-          
-          {"\n"}
-          from{" "}
-          taskito{" "}
-          import{" "}
-          Queue{"\n\n"}
-          queue ={" "}
-          Queue
-          (db_path=
-          
-            "tasks.db"
-          
-          ){"\n\n"}
-          @queue.task()
-          {"\n"}
-          def{" "}
-          add(a, b):
-          {"\n"}
-          {"    "}
-          return a
-          + b{"\n\n"}
-          job = add.
-          delay(
-          2,{" "}
-          3){"\n"}
-          print(job.
-          result())
-          {"  "}
-          # 5
-        
-      
-
- ); -} - -const FEATURES = [ - { - icon: Zap, - title: "Brokerless", - body: "No Redis, no RabbitMQ. Everything in a single SQLite file — queue, results, rate limits, schedules. Just `pip install` and go.", - }, - { - icon: Cpu, - title: "Rust-powered", - body: "The scheduler, dispatcher, and storage engine are all Rust. Tokio runtime, OS-thread worker pool, thin PyO3 boundary keeps the Python overhead negligible.", - }, - { - icon: Activity, - title: "Async-first", - body: "`async def` tasks dispatch onto a dedicated event loop — no `asyncio.run()` wrapping, no thread-pool bridging. Sync and async tasks coexist transparently.", - }, - { - icon: Workflow, - title: "DAG workflows", - body: "Multi-step pipelines as directed acyclic graphs. Fan-out, fan-in, conditions, approval gates, sub-workflows, incremental re-runs, Mermaid visualization.", - }, - { - icon: Layers, - title: "Resource system", - body: "Inject database connections, HTTP clients, and cloud SDKs by name. Three-layer pipeline: argument interception, worker DI, transparent proxy reconstruction.", - }, - { - icon: Shield, - title: "Production-ready", - body: "Retries with exponential backoff, dead letter queue, rate limits, circuit breakers, distributed locks, structured logs, OTel/Sentry/Prometheus middleware.", - }, -]; - -function Features() { - return ( -
-
-

- What you get -

-

- The convenience of Celery, the performance of Rust, the simplicity of - SQLite. -

-
-
- {FEATURES.map(({ icon: Icon, title, body }) => ( -
-
- -
-

{title}

-

- {body} -

-
- ))} -
-
- ); -} - -function ComparisonSection() { - return ( -
-
-

- Less to operate -

-

- The same task, two stacks. Side by side, with the operational delta. -

-
- -
- ); -} - -function CTA() { - return ( -
-
-

- Five minutes from{" "} - pip install to your - first job. -

-

- The quickstart walks you through defining a task, enqueuing it, and - watching the worker run it — no Redis, no broker, no config. -

-
- - Start the quickstart - - - - See the full comparison - -
-
-
- ); -} diff --git a/docs/src/components/ui/button.tsx b/docs/src/components/ui/button.tsx index bac0894..343e817 100644 --- a/docs/src/components/ui/button.tsx +++ b/docs/src/components/ui/button.tsx @@ -9,8 +9,7 @@ const VARIANT_CLASSES: Record = { "bg-fd-primary text-fd-primary-foreground hover:opacity-90 transition-opacity", secondary: "border border-fd-border bg-fd-card hover:bg-fd-accent transition-colors", - ghost: - "text-fd-muted-foreground hover:text-fd-foreground transition-colors", + ghost: "text-fd-muted-foreground hover:text-fd-foreground transition-colors", }; const BASE_CLASSES = @@ -35,13 +34,7 @@ type AsButton = CommonProps & { >; export function Button(props: AsLink | AsButton) { - const { - variant = "primary", - icon, - children, - className, - ...rest - } = props; + const { variant = "primary", icon, children, className, ...rest } = props; const classes = cn(BASE_CLASSES, VARIANT_CLASSES[variant], className); if ("href" in rest && rest.href !== undefined) { diff --git a/docs/src/components/ui/index.ts b/docs/src/components/ui/index.ts new file mode 100644 index 0000000..3a4bccd --- /dev/null +++ b/docs/src/components/ui/index.ts @@ -0,0 +1,10 @@ +export { Button, type ButtonVariant } from "./button"; +export { + CodePanel, + type CodePanelProps, + type CodePanelTone, +} from "./code-panel"; +export { + SectionHeader, + type SectionHeaderAlign, +} from "./section-header"; diff --git a/docs/src/lib/cn.ts b/docs/src/lib/cn.ts index b500fb1..a70ebb6 100644 --- a/docs/src/lib/cn.ts +++ b/docs/src/lib/cn.ts @@ -1,4 +1,4 @@ -import { clsx, type ClassValue } from "clsx"; +import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]): string { From f107dd1a67c9fa02d5d349ca5d1991291281101f Mon Sep 17 00:00:00 2001 From: Pratyush Sharma <56130065+pratyush618@users.noreply.github.com> Date: Sun, 3 May 2026 10:45:53 +0530 Subject: [PATCH 6/6] fix(docs): make Shiki dual-theme bg dark-mode aware --- docs/src/lib/highlight.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/lib/highlight.tsx b/docs/src/lib/highlight.tsx index 3e92930..b5b862a 100644 --- a/docs/src/lib/highlight.tsx +++ b/docs/src/lib/highlight.tsx @@ -5,7 +5,7 @@ import { cn } from "@/lib/cn"; export type HighlightLang = "python" | "bash" | "tsx" | "ts" | "json" | "yaml"; const DEFAULT_PRE_CLASSES = - "p-5 text-sm leading-relaxed overflow-x-auto bg-fd-card"; + "p-5 text-sm leading-relaxed overflow-x-auto bg-(--shiki-light-bg) dark:bg-(--shiki-dark-bg)"; export async function highlight( code: string, @@ -14,6 +14,7 @@ export async function highlight( ): Promise { return fumaHighlight(code, { lang, + defaultColor: false, themes: { light: "github-light", dark: "github-dark",