the front door and the docs chrome state their tracks, on @hanzo/ui

This commit is contained in:
2026-08-23 11:31:52 -07:00
parent 6a4bc1de49
commit 6e5ffde46f
13 changed files with 403 additions and 248 deletions
+252 -146
View File
@@ -15,17 +15,7 @@ import {
Terminal,
Workflow,
} from 'lucide-react';
import { buttonVariants } from '@hanzo/docs-base-ui/components/ui/button';
import { Badge } from '@hanzo/docs-base-ui/components/ui/badge';
import { cn } from '@/lib/cn';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@hanzo/docs-base-ui/components/ui/card';
import { Card, CardContent, CardTitle, XStack } from '@hanzo/ui';
import { Grid } from '@hanzo/ui/grid';
import { AgentActions } from '@/components/agent-actions';
import { Tabs, Tab } from '@hanzo/docs-base-ui/components/tabs';
@@ -146,7 +136,14 @@ export default function Page() {
// because the sidebar takes 268px from `md` up and 768→1024→1280 step by 256.
<main className="[grid-area:main] min-w-0 pb-6 md:pb-12">
{/* -- Hero ---------------------------------------------------------- */}
<section className="relative flex flex-col items-center text-center mx-auto w-full max-w-5xl px-6 pt-24 pb-16 md:pt-36 md:pb-24">
{/* One column, centred. It was `flex flex-col items-center`, which is the
same picture drawn with the wrong primitive: a column of blocks IS a
one-track grid, and saying so means `justifyItems` centres the children
without each of them having to be told. */}
<section
className="relative mx-auto w-full max-w-5xl px-6 pt-24 pb-16 md:pt-36 md:pb-24"
style={{ display: 'grid', justifyItems: 'center', textAlign: 'center' }}
>
{/* The atmosphere behind the headline, as dots rather than a gradient —
the halftone IS the mark, and a gradient carries none of it. `fade`
dissolves the canvas rectangle on both axes so the field ends in the
@@ -160,9 +157,10 @@ export default function Page() {
href="https://hanzo.ai/enso"
target="_blank"
rel="noreferrer"
className="relative mb-8 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-sm text-neutral-400 backdrop-blur transition-colors hover:border-white/20 hover:text-white"
className="relative mb-8 rounded-full border border-white/10 bg-white/[0.03] px-4 py-2 text-sm text-neutral-400 backdrop-blur transition-colors hover:border-white/20 hover:text-white"
style={{ display: 'inline-grid', gridAutoFlow: 'column', alignItems: 'center', columnGap: 8 }}
>
<span className="h-1.5 w-1.5 rounded-full bg-white" />
<span className="rounded-full bg-white" style={{ width: 6, height: 6 }} />
Meet Enso — our frontier model, default on every surface
<ArrowRight className="size-3.5" />
</Link>
@@ -183,10 +181,30 @@ export default function Page() {
{/* -- Install command -- the main CTA ------------------------------ */}
<div className="relative mt-10 w-full max-w-lg">
<Card className="border-white/[0.08] bg-white/[0.03] p-1 shadow-none gap-0 py-0 rounded-2xl">
<CardContent className="flex items-center gap-3 rounded-xl bg-[#0a0a0a] px-5 py-4 font-mono text-sm">
<span className="text-neutral-400 select-none">$</span>
<span className="text-white flex-1 text-left">curl hanzo.sh | sh</span>
<Card
backgroundColor="rgba(255,255,255,0.03)"
borderColor="rgba(255,255,255,0.08)"
borderRadius={16}
padding={4}
paddingVertical={4}
gap={0}
>
{/* `$` then the command: a fixed leader and a track that takes the
rest. The `flex-1` that used to be on the span was the child
claiming the width; the track owns it now. */}
<CardContent
style={{
display: 'grid',
gridTemplateColumns: 'auto minmax(0, 1fr)',
alignItems: 'center',
columnGap: 12,
borderRadius: 12,
background: '#0a0a0a',
padding: '16px 20px',
}}
>
<span className="font-mono text-sm text-neutral-400 select-none">$</span>
<span className="font-mono text-sm text-white" style={{ textAlign: 'left' }}>curl hanzo.sh | sh</span>
</CardContent>
</Card>
<p className="text-xs text-neutral-400 mt-3">
@@ -206,7 +224,7 @@ export default function Page() {
It also has to be here rather than in the bar: this page has no table
of contents, which is where the doc pages host it, so without this the
front door offered no way to hand anything to an agent at all. */}
<div className="relative mt-6 flex flex-col items-center gap-2">
<div className="relative mt-6" style={{ display: 'grid', justifyItems: 'center', rowGap: 8 }}>
<AgentActions />
<p className="text-xs text-neutral-400">
Or hand this to your agent — it installs the CLI, the MCP server and
@@ -262,16 +280,29 @@ export default function Page() {
href: '/docs/openapi',
},
].map((d) => (
// Three rows — eyebrow, title, body — so the gaps are declared once
// on the card instead of as a margin on each child that has to know
// what follows it.
<Link
key={d.title}
href={d.href}
{...(d.external ? { target: '_blank', rel: 'noreferrer' } : {})}
className="group flex flex-col rounded-2xl border border-white/[0.08] bg-white/[0.02] p-6 text-left transition-all duration-200 hover:-translate-y-0.5 hover:border-white/20 hover:bg-white/[0.05]"
className="group rounded-2xl border border-white/[0.08] bg-white/[0.02] p-6 transition-all duration-200 hover:-translate-y-0.5 hover:border-white/20 hover:bg-white/[0.05]"
style={{
display: 'grid',
gridTemplateRows: 'auto auto auto',
alignContent: 'start',
rowGap: 6,
textAlign: 'left',
}}
>
<span className="mb-2 text-xs font-medium text-neutral-300">
<span className="text-xs font-medium text-neutral-300">
{d.eyebrow}
</span>
<span className="mb-1.5 flex items-center gap-1.5 text-lg font-semibold text-white">
<span
className="text-lg font-semibold text-white"
style={{ display: 'grid', gridAutoFlow: 'column', justifyContent: 'start', alignItems: 'center', columnGap: 6 }}
>
{d.title}
<ArrowRight className="size-4 opacity-40 transition-all group-hover:translate-x-0.5 group-hover:opacity-100" />
</span>
@@ -369,9 +400,27 @@ export default function Page() {
? { target: '_blank' as const, rel: 'noreferrer noopener' }
: {};
return (
// Four rows: the icon/tag line, the name, the description, and
// the footer pushed to the bottom by `1fr` on the description
// row. Before, the footer was a separate flex child relying on
// the frame's own gap; now the card states its own shape and the
// minimum height has something to distribute.
<Card
key={item.name}
className="group relative border-white/[0.08] bg-white/[0.02] hover:border-white/20 hover:bg-white/[0.05] transition-all duration-300 shadow-none py-0 gap-0 rounded-2xl min-h-[140px] sm:min-h-[160px]"
className="group"
minHeight={140}
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.08)"
hoverStyle={{ borderColor: 'rgba(255,255,255,0.2)', backgroundColor: 'rgba(255,255,255,0.05)' }}
borderRadius={16}
paddingVertical={0}
gap={0}
style={{
position: 'relative',
display: 'grid',
gridTemplateRows: 'auto auto 1fr auto',
transition: 'all 300ms',
}}
>
{/* The whole card is the click target, so the link has no text
of its own — it takes its accessible name from the card
@@ -387,27 +436,27 @@ export default function Page() {
) : (
<Link href={item.href} aria-label={item.name} className="absolute inset-0 z-10" />
)}
<CardHeader className="p-5 sm:p-6 pb-0 gap-0 grid-rows-none">
<div className="flex items-center justify-between mb-3">
<div className="rounded-xl bg-white/[0.06] p-2">
<item.icon className="size-5 text-neutral-400 group-hover:text-white transition-colors" />
</div>
<Badge variant="ghost" className="text-[11px] text-neutral-400 font-medium border-none px-0">
{item.tag}
</Badge>
<div
className="p-5 sm:p-6 pb-0"
style={{ display: 'grid', gridTemplateColumns: '1fr auto', alignItems: 'center', marginBottom: 12 }}
>
<div className="rounded-xl bg-white/[0.06] p-2" style={{ justifySelf: 'start' }}>
<item.icon className="size-5 text-neutral-400 group-hover:text-white transition-colors" />
</div>
<CardTitle className="text-sm font-semibold text-white mb-1.5">
{item.name}
</CardTitle>
<CardDescription className="text-xs text-neutral-400 leading-relaxed group-hover:text-neutral-200 transition-colors">
{item.desc}
</CardDescription>
</CardHeader>
<CardFooter className="p-5 sm:p-6 pt-3">
<span className="flex items-center gap-1 text-[10px] font-medium text-neutral-400 group-hover:text-white transition-colors">
{isExternal ? 'Visit' : 'View docs'} <ArrowRight className="size-3" />
</span>
</CardFooter>
<span className="text-[11px] font-medium text-neutral-400">{item.tag}</span>
</div>
<CardTitle paddingHorizontal={24} fontSize={14} fontWeight="600" color="#ffffff" marginBottom={6}>
{item.name}
</CardTitle>
<p className="px-5 sm:px-6 text-xs text-neutral-400 leading-relaxed group-hover:text-neutral-200 transition-colors">
{item.desc}
</p>
<span
className="p-5 sm:p-6 pt-3 text-[10px] font-medium text-neutral-400 group-hover:text-white transition-colors"
style={{ display: 'grid', gridAutoFlow: 'column', justifyContent: 'start', alignItems: 'center', columnGap: 4 }}
>
{isExternal ? 'Visit' : 'View docs'} <ArrowRight className="size-3" />
</span>
</Card>
);
})}
@@ -422,21 +471,34 @@ export default function Page() {
<p className="text-neutral-300 text-sm mb-8">
SDKs, APIs, and protocols for every stack.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4">
{/* 230 is the 1 / 2 / 4 story the three breakpoints told, measured at
the column rather than the window: 1-up on a phone, 2-up once the
column clears ~476, and never more than four however wide it gets. */}
<Grid columns={{ min: 230, max: 4 }} gap={16}>
{devLinks.map((t) => (
<Card
key={t.name}
className="group relative border-white/[0.08] bg-white/[0.02] hover:border-white/20 hover:bg-white/[0.04] transition-all duration-300 shadow-none py-0 gap-0 rounded-2xl"
className="group"
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.08)"
hoverStyle={{ borderColor: 'rgba(255,255,255,0.2)', backgroundColor: 'rgba(255,255,255,0.04)' }}
borderRadius={16}
paddingVertical={0}
gap={0}
style={{ position: 'relative', transition: 'all 300ms' }}
>
<Link href={t.href} aria-label={t.name} className="absolute inset-0 z-10" />
<CardContent className="p-6">
<t.icon className="size-5 text-neutral-400 mb-4 group-hover:text-white transition-colors" />
<CardTitle className="font-semibold text-sm text-white mb-1">{t.name}</CardTitle>
<CardDescription className="text-xs text-neutral-400 group-hover:text-neutral-200 transition-colors">{t.desc}</CardDescription>
<CardContent
padding={24}
style={{ display: 'grid', gridTemplateRows: 'auto auto auto', justifyItems: 'start', rowGap: 4 }}
>
<t.icon className="size-5 text-neutral-400 mb-3 group-hover:text-white transition-colors" />
<CardTitle fontSize={14} fontWeight="600" color="#ffffff">{t.name}</CardTitle>
<p className="text-xs text-neutral-400 group-hover:text-neutral-200 transition-colors">{t.desc}</p>
</CardContent>
</Card>
))}
</div>
</Grid>
</section>
{/* -- Models & providers ------------------------------------------ */}
@@ -447,7 +509,7 @@ export default function Page() {
<p className="text-neutral-300 text-sm mb-8">
Over 400 models across every major provider — call any of them with one credential, one request shape.
</p>
<div className="grid grid-cols-2 sm:grid-cols-4 xl:grid-cols-8 gap-3">
<Grid columns={{ min: 120, max: 8 }} gap={12}>
{[
{ name: 'Zen', spec: 'Open weights' },
{ name: 'OpenAI', spec: 'GPT' },
@@ -458,78 +520,111 @@ export default function Page() {
{ name: 'Mistral', spec: 'Open' },
{ name: 'Gemma', spec: 'Open' },
].map((p) => (
<Card key={p.name} className="border-white/[0.06] bg-white/[0.02] shadow-none py-0 gap-0 rounded-lg">
<CardContent className="px-4 py-3 text-center">
<div className="text-xs font-semibold text-white mb-1">{p.name}</div>
<div className="text-[10px] text-neutral-400">{p.spec}</div>
<Card
key={p.name}
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.06)"
borderRadius={8}
paddingVertical={0}
gap={0}
>
<CardContent style={{ display: 'grid', rowGap: 4, padding: '12px 16px', textAlign: 'center' }}>
<span className="text-xs font-semibold text-white">{p.name}</span>
<span className="text-[10px] text-neutral-400">{p.spec}</span>
</CardContent>
</Card>
))}
</div>
</Grid>
</section>
{/* -- SDKs + connectors ------------------------------------------- */}
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
<Card className="border-white/[0.08] bg-white/[0.02] shadow-none py-0 gap-0 rounded-2xl overflow-hidden">
<CardContent className="p-8">
<div className="flex items-center gap-3 mb-2">
<Code2 className="size-5 text-neutral-400" />
<CardTitle className="text-xl font-bold tracking-tight">SDKs in every language</CardTitle>
</div>
<CardDescription className="text-xs text-neutral-400 mb-6">
Generated from one contract — the same <code className="font-mono">/v1</code> surface, typed for your stack.
</CardDescription>
<div className="flex flex-wrap gap-2">
{['Python', 'TypeScript', 'Go', 'Rust', 'C++', 'Dart'].map((l) => (
<span key={l} className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-xs text-neutral-400">
{l}
</span>
))}
</div>
<Link href="/docs/sdks" className="mt-6 inline-flex items-center gap-1 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
SDK reference <ArrowRight className="size-3" />
</Link>
</CardContent>
</Card>
<Card className="border-white/[0.08] bg-white/[0.02] shadow-none py-0 gap-0 rounded-2xl overflow-hidden">
<CardContent className="p-8">
<div className="flex items-center gap-3 mb-2">
<Workflow className="size-5 text-neutral-400" />
<CardTitle className="text-xl font-bold tracking-tight">Every tool, one MCP surface</CardTitle>
</div>
<CardDescription className="text-xs text-neutral-400 mb-6">
Native connectors + the open MCP registry — Slack, GitHub, Notion, Stripe, and more — exposed as MCP tools any agent can call.
</CardDescription>
<div className="flex flex-wrap gap-2">
{['Slack', 'GitHub', 'Notion', 'Stripe', 'Google', 'Linear', '+700 more'].map((c) => (
<span key={c} className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-xs text-neutral-400">
{c}
</span>
))}
</div>
<Link href="/docs/mcp" className="mt-6 inline-flex items-center gap-1 text-xs font-medium text-neutral-400 hover:text-white transition-colors">
MCP tools <ArrowRight className="size-3" />
</Link>
</CardContent>
</Card>
</div>
<Grid columns={{ min: 470, max: 2 }} gap={16}>
{[
{
icon: Code2,
title: 'SDKs in every language',
body: (
<>
Generated from one contract — the same <code className="font-mono">/v1</code> surface, typed for your stack.
</>
),
chips: ['Python', 'TypeScript', 'Go', 'Rust', 'C++', 'Dart'],
href: '/docs/sdks',
cta: 'SDK reference',
},
{
icon: Workflow,
title: 'Every tool, one MCP surface',
body: 'Native connectors + the open MCP registry — Slack, GitHub, Notion, Stripe, and more — exposed as MCP tools any agent can call.',
chips: ['Slack', 'GitHub', 'Notion', 'Stripe', 'Google', 'Linear', '+700 more'],
href: '/docs/mcp',
cta: 'MCP tools',
},
].map((s) => (
<Card
key={s.title}
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.08)"
borderRadius={16}
paddingVertical={0}
gap={0}
overflow="hidden"
>
<CardContent
padding={32}
style={{ display: 'grid', gridTemplateRows: 'auto auto auto auto', justifyItems: 'start', rowGap: 8 }}
>
<span style={{ display: 'grid', gridAutoFlow: 'column', alignItems: 'center', columnGap: 12 }}>
<s.icon className="size-5 text-neutral-400" />
<CardTitle fontSize={20} fontWeight="700" letterSpacing={-0.4}>{s.title}</CardTitle>
</span>
<p className="text-xs text-neutral-400 mb-4">{s.body}</p>
{/* A wrapping run of chips is the one place flex is still the
right answer — grid cannot wrap items of unequal intrinsic
width without giving them a track — so it is said with gui's
XStack, whose style props travel to native, rather than with
tailwind's. */}
<XStack flexWrap="wrap" gap={8}>
{s.chips.map((c) => (
<span key={c} className="rounded-full border border-white/10 bg-white/[0.03] px-3 py-1.5 text-xs text-neutral-400">
{c}
</span>
))}
</XStack>
<Link
href={s.href}
className="mt-4 text-xs font-medium text-neutral-400 hover:text-white transition-colors"
style={{ display: 'inline-grid', gridAutoFlow: 'column', alignItems: 'center', columnGap: 4 }}
>
{s.cta} <ArrowRight className="size-3" />
</Link>
</CardContent>
</Card>
))}
</Grid>
{/* -- What the CLI can do ----------------------------------------- */}
<Card className="border-white/[0.08] bg-white/[0.02] shadow-none py-0 gap-0 rounded-2xl overflow-hidden">
<CardContent className="p-8 md:p-10">
<div className="flex items-center gap-3 mb-6">
<Card
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.08)"
borderRadius={16}
paddingVertical={0}
gap={0}
overflow="hidden"
>
<div className="p-8 md:p-10" style={{ display: 'grid', rowGap: 24 }}>
<div style={{ display: 'grid', gridTemplateColumns: 'auto minmax(0, 1fr)', alignItems: 'center', columnGap: 12 }}>
<div className="rounded-xl bg-white/[0.06] p-2.5">
<Terminal className="size-5 text-neutral-400" />
</div>
<div>
<CardTitle className="text-2xl font-bold tracking-tight">
<div style={{ display: 'grid', rowGap: 2 }}>
<CardTitle fontSize={24} fontWeight="700" letterSpacing={-0.5}>
The <code className="font-mono">hanzo</code> CLI
</CardTitle>
<CardDescription className="text-xs text-neutral-400 mt-0.5">A ~15 MB Rust client for any live cloud — prod, laptop, or self-host</CardDescription>
<span className="text-xs text-neutral-400">A ~15 MB Rust client for any live cloud — prod, laptop, or self-host</span>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
<Grid columns={{ min: 200, max: 3 }} gap={12}>
{[
{ cmd: 'hanzo chat', desc: 'Chat with any model interactively' },
{ cmd: 'hanzo models list', desc: 'Browse every available model' },
@@ -541,34 +636,46 @@ export default function Page() {
{ cmd: 'hanzo bot', desc: 'Deploy and manage AI bots' },
{ cmd: 'hanzo flow', desc: 'Run workflow automations' },
].map((item) => (
<Card key={item.cmd} className="border-white/[0.06] bg-white/[0.02] shadow-none py-0 gap-0 rounded-lg">
<CardContent className="px-4 py-3">
<div className="text-xs font-mono font-medium text-white mb-1">{item.cmd}</div>
<div className="text-[11px] text-neutral-400">{item.desc}</div>
<Card
key={item.cmd}
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.06)"
borderRadius={8}
paddingVertical={0}
gap={0}
>
<CardContent style={{ display: 'grid', rowGap: 4, padding: '12px 16px' }}>
<span className="text-xs font-mono font-medium text-white">{item.cmd}</span>
<span className="text-[11px] text-neutral-400">{item.desc}</span>
</CardContent>
</Card>
))}
</div>
</CardContent>
</Grid>
</div>
</Card>
{/* -- Zen Models Banner ------------------------------------------- */}
<Card className="border-white/[0.08] bg-white/[0.02] shadow-none py-0 gap-0 rounded-2xl overflow-hidden">
<CardContent className="p-8 md:p-10">
<div className="flex items-center gap-3 mb-4">
<Card
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.08)"
borderRadius={16}
paddingVertical={0}
gap={0}
overflow="hidden"
>
<div className="p-8 md:p-10" style={{ display: 'grid', rowGap: 24 }}>
<div style={{ display: 'grid', gridAutoFlow: 'column', justifyContent: 'start', alignItems: 'center', columnGap: 12 }}>
<Sparkle className="size-5 text-neutral-400" />
<CardTitle className="text-2xl font-bold tracking-tight">
<CardTitle fontSize={24} fontWeight="700" letterSpacing={-0.5}>
Zen
</CardTitle>
<Badge variant="secondary" className="text-xs text-neutral-400 font-mono bg-white/5 px-2 py-0.5 rounded-full border-transparent">
44 models
</Badge>
<span className="rounded-full bg-white/5 px-2 py-0.5 font-mono text-xs text-neutral-400">44 models</span>
</div>
<p className="text-neutral-300 text-sm leading-relaxed max-w-2xl mb-6">
<p className="text-neutral-300 text-sm leading-relaxed max-w-2xl">
Frontier AI models from 4B edge to 1T+ reasoning. MoDE (Mixture of Diverse Experts) architecture.
Text, code, vision, audio, video, 3D, and safety. Open weights on HuggingFace.
</p>
<div className="grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-6 gap-3 mb-6">
<Grid columns={{ min: 130, max: 6 }} gap={12}>
{[
{ name: 'zen4', spec: '~400B MoDE' },
{ name: 'zen4-coder', spec: '~200B MoDE' },
@@ -577,23 +684,27 @@ export default function Page() {
{ name: 'zen3-nano', spec: '4B Edge' },
{ name: 'zen3-guard', spec: '8B Safety' },
].map((m) => (
<Card key={m.name} className="border-white/[0.06] bg-white/[0.02] shadow-none py-0 gap-0 rounded-lg">
<CardContent className="p-3">
<div className="text-xs font-mono font-medium text-white mb-1">{m.name}</div>
<div className="text-[10px] text-neutral-400">{m.spec}</div>
<Card
key={m.name}
backgroundColor="rgba(255,255,255,0.02)"
borderColor="rgba(255,255,255,0.06)"
borderRadius={8}
paddingVertical={0}
gap={0}
>
<CardContent style={{ display: 'grid', rowGap: 4, padding: 12 }}>
<span className="text-xs font-mono font-medium text-white">{m.name}</span>
<span className="text-[10px] text-neutral-400">{m.spec}</span>
</CardContent>
</Card>
))}
</div>
<div className="flex items-center gap-3 flex-wrap">
</Grid>
<XStack flexWrap="wrap" gap={12} alignItems="center">
<a
href="https://zenlm.org"
target="_blank"
rel="noreferrer noopener"
className={cn(
buttonVariants({ variant: 'outline', size: 'sm' }),
'rounded-full border-white/20 text-white hover:bg-white/5 bg-transparent text-xs px-4 py-2 h-auto',
)}
className="rounded-full border border-white/20 px-4 py-2 text-xs text-white transition-colors hover:bg-white/5"
>
zenlm.org &rarr;
</a>
@@ -605,8 +716,8 @@ export default function Page() {
>
HuggingFace &rarr;
</a>
</div>
</CardContent>
</XStack>
</div>
</Card>
{/* -- CTA --------------------------------------------------------- */}
@@ -621,27 +732,22 @@ export default function Page() {
<div className="relative font-mono text-sm text-neutral-400 mb-8">
curl hanzo.sh | sh
</div>
<div className="relative flex items-center justify-center gap-3 flex-wrap">
<XStack flexWrap="wrap" gap={12} alignItems="center" justifyContent="center" position="relative">
<a
href="https://hanzo.id/signup?redirect_uri=https://console.hanzo.ai"
className={cn(
buttonVariants({ variant: 'primary', size: 'lg' }),
'rounded-full bg-white px-8 text-black hover:bg-neutral-200',
)}
className="rounded-full bg-white px-8 py-3 text-sm font-medium text-black transition-colors hover:bg-neutral-200"
style={{ display: 'inline-grid', gridAutoFlow: 'column', alignItems: 'center', columnGap: 4 }}
>
Sign Up Free
<ArrowRight className="size-4 ml-1" />
<ArrowRight className="size-4" />
</a>
<Link
href="/docs"
className={cn(
buttonVariants({ variant: 'outline', size: 'lg' }),
'rounded-full border-white/15 px-8 text-white hover:bg-white/5 bg-transparent',
)}
className="rounded-full border border-white/15 px-8 py-3 text-sm font-medium text-white transition-colors hover:bg-white/5"
>
Browse Documentation
</Link>
</div>
</XStack>
</section>
</div>
</main>
+13 -1
View File
@@ -1,6 +1,7 @@
'use client';
import { RootProvider } from '@hanzo/docs-base-ui/provider/base';
import { Hanzo } from '@hanzo/ui';
import dynamic from 'next/dynamic';
import type { ReactNode } from 'react';
@@ -8,6 +9,17 @@ const SearchDialog = dynamic(() => import('@/components/layouts/search'), {
ssr: false,
});
// `<Hanzo>` is what makes a @hanzo/ui component render as anything at all: it
// carries gui's process-global config, its stylesheet, and the root theme gui
// throws without. Before it was here, this app declared @hanzo/ui and @hanzo/gui
// as dependencies and rendered zero gui classes — the same shape @zenlm/ui is in,
// where the markup names classes no rule ever defines.
//
// INSIDE RootProvider, not above it. Both manage a theme, and the one that wins
// has to be the one the reader can switch: fumadocs owns the toggle in the rail
// and writes `.dark` on <html>, so gui takes the fixed dark identity and the
// document class stays fumadocs' to set. Nesting the other way puts gui's theme
// context above a subtree that never reads it.
export function Provider({ children }: { children: ReactNode }) {
return (
<RootProvider
@@ -19,7 +31,7 @@ export function Provider({ children }: { children: ReactNode }) {
enableSystem: false,
}}
>
{children}
<Hanzo theme="dark">{children}</Hanzo>
</RootProvider>
);
}
+14 -1
View File
@@ -17,8 +17,21 @@ import { linkItems } from '@/components/layouts/shared';
export function DocsNavLinks() {
const pathname = usePathname();
// A run of equal-height links, so the tracks come from the content and the
// spacing is stated once on the container rather than as a margin each link
// carries. `justify-content: start` keeps the row left-aligned inside the
// stretched middle track of the bar above.
return (
<nav className="flex items-center gap-1 max-md:hidden">
<nav
className="max-md:hidden"
style={{
display: 'grid',
gridAutoFlow: 'column',
justifyContent: 'start',
alignItems: 'center',
columnGap: 4,
}}
>
{linkItems
.filter((item) => item.type !== 'icon' && 'url' in item)
.map((item) => {
+29 -10
View File
@@ -3,6 +3,7 @@
import type { ComponentProps } from 'react';
import { useDocsLayout } from '@hanzo/docs-base-ui/layouts/docs';
import { useSidebar } from '@hanzo/docs-base-ui/components/sidebar/base';
import { Grid } from '@hanzo/ui/grid';
import { cn } from '@/lib/cn';
import { DocsNavLinks } from '@/components/docs-nav-links';
import { OrgBadge } from '@/components/org-badge';
@@ -40,8 +41,21 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
id="nd-subnav"
data-transparent={isNavTransparent}
{...props}
// THREE TRACKS: the wordmark, the section links, the account controls.
// It was `flex items-center gap-3` with a `flex-1` wrapper in the middle
// whose only job was to push its neighbours apart — which is a row asking
// to be a grid. The stretch is a property of the TRACK, so saying it here
// deletes the wrapper and the class that made it stretch, and neither
// neighbour has to know the other exists.
style={{
display: 'grid',
gridTemplateColumns: 'auto minmax(0, 1fr) auto',
alignItems: 'center',
columnGap: 12,
...props.style,
}}
className={cn(
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 flex items-center gap-3 pe-2.5',
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 pe-2.5',
// A COLLAPSED RAIL TAKES NO COLUMN, SO THIS BAR STARTS AT THE VIEWPORT EDGE.
// container.tsx sets `--fd-sidebar-col: 0px` when collapsed, and the rail's
// floating control is `fixed inset-s-4` at 66px wide — x∈[16,82] — at z-40,
@@ -65,10 +79,13 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
once here and once six inches to the left. Below md the sidebar collapses
into a drawer and this is the only place a wordmark can live, so it stays
there rather than being dropped outright. */}
{slots.navTitle && (
{/* The three tracks are always present, so the wordmark's cell is emitted
even when the slot is not — a missing child would shift the links into
the wordmark's track and the controls into theirs. */}
{slots.navTitle ? (
<slots.navTitle
className={cn(
'inline-flex items-center gap-2.5 font-semibold',
'inline-grid grid-flow-col items-center gap-2.5 font-semibold',
// Hidden on desktop only while the SIDEBAR is showing it — which it is
// not when collapsed, and collapsed is exactly the landing page. Left
// unconditional the front door rendered no wordmark at all: the rail
@@ -77,9 +94,15 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
!collapsed && 'md:hidden',
)}
/>
) : (
<span />
)}
<div className="flex flex-1 items-center">
{/* The links are `max-md:hidden`, and a hidden element is not a grid item
at all — so below md it would vacate the stretch track and the controls
would slide into it, left-aligned. The cell stays; only its contents
disappear. */}
<div>
<DocsNavLinks />
</div>
@@ -89,7 +112,7 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
the site lost its Sign in and Console buttons: they were configured
correctly and rendered by nobody. Named explicitly so the omission
cannot happen silently again. */}
<div className="flex items-center gap-2">
<Grid columns={['auto', 'auto']} gap={8} style={{ alignItems: 'center' }}>
{/* NO SEARCH HERE. The sidebar carries the search field itself, and the
collapsed rail carries a search button beside its own toggle — so a
third one on the far right was the same action offered twice on one
@@ -102,10 +125,6 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
out of the bar between 768 and 805px. A column can afford a long
label; this row carries the short one instead, and only when the rail
is not on screen to carry either. */}
{/* One control for "hand this page to an agent", derived from the
pathname so it works on every page. It replaced a Copy Markdown
button and an Open menu buried under the title, plus a Copy prompt
that existed only on the front page. */}
<OrgBadge />
{/* Shown exactly where the rail is not showing it: below md the rail is
a drawer, and on desktop only while it is collapsed. Everywhere else
@@ -113,7 +132,7 @@ export function DocsNavbar(props: ComponentProps<'header'>) {
<div className={cn(collapsed ? '' : 'md:hidden')}>
<NavAccount />
</div>
</div>
</Grid>
</header>
);
}
+4
View File
@@ -69,11 +69,15 @@ export function Docs({
return {
...option,
icon: (
// One cell holding one glyph, centred. `place-items` is the
// grid word for it and needs no wrapper to align against.
<div
className="[&_svg]:size-full rounded-lg size-full text-(--tab-color) max-md:bg-(--tab-color)/10 max-md:border max-md:p-1.5"
style={
{
'--tab-color': color,
display: 'grid',
placeItems: 'center',
} as object
}
>
+17 -6
View File
@@ -3,6 +3,7 @@
import { useEffect, useRef, useState } from 'react'
import Link from 'next/link'
import { ChevronsUpDown } from 'lucide-react'
import { Grid } from '@hanzo/ui/grid'
import { currentUser, iam, type DocsUser } from '@/lib/iam'
@@ -48,7 +49,7 @@ export function SidebarAccount() {
function SignedOut() {
return (
<div className="flex flex-col gap-2">
<Grid columns={1} gap={8}>
{/* Sign in first: it is the step that costs nothing and the one most
readers already have an account for. Getting a key is the commitment,
so it sits at the foot as the thing you end on. */}
@@ -64,7 +65,7 @@ function SignedOut() {
>
Get an API key
</a>
</div>
</Grid>
)
}
@@ -136,16 +137,26 @@ function Account({ user, onSignOut }: { user: DocsUser; onSignOut: () => void })
onClick={() => setOpen((v) => !v)}
aria-haspopup="menu"
aria-expanded={open}
className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-start transition-colors hover:bg-fd-accent"
// Initial, name, chevron. The name track is `minmax(0, 1fr)`, which is
// what lets it truncate — a flex child needed `min-w-0` spelled out to
// do the same thing, and a track that forgets it is how a long email
// pushes the chevron out of the rail.
className="w-full rounded-lg px-2 py-1.5 text-start transition-colors hover:bg-fd-accent"
style={{
display: 'grid',
gridTemplateColumns: 'auto minmax(0, 1fr) auto',
alignItems: 'center',
columnGap: 8,
}}
>
<span
aria-hidden
className="grid size-6 shrink-0 place-items-center rounded-full bg-fd-primary text-[0.6875rem] font-medium text-fd-primary-foreground"
className="grid size-6 place-items-center rounded-full bg-fd-primary text-[0.6875rem] font-medium text-fd-primary-foreground"
>
{label.slice(0, 1).toUpperCase()}
</span>
<span className="min-w-0 flex-1 truncate text-sm text-fd-foreground">{label}</span>
<ChevronsUpDown className="size-3.5 shrink-0 text-fd-muted-foreground" />
<span className="truncate text-sm text-fd-foreground">{label}</span>
<ChevronsUpDown className="size-3.5 text-fd-muted-foreground" />
</button>
</div>
)
+12 -3
View File
@@ -28,10 +28,19 @@ export function SidebarSearch() {
<button
type="button"
onClick={() => setOpenSearch(true)}
className="flex w-full items-center gap-2 rounded-lg border bg-fd-secondary/50 px-2.5 py-1.5 text-start transition-colors hover:bg-fd-accent"
// Icon, label, shortcut. The label used to claim the middle with `flex-1`;
// the middle track claims it now, so the label is just text and the two
// fixed ends cannot be squeezed by it.
className="w-full rounded-lg border bg-fd-secondary/50 px-2.5 py-1.5 text-start transition-colors hover:bg-fd-accent"
style={{
display: 'grid',
gridTemplateColumns: 'auto minmax(0, 1fr) auto',
alignItems: 'center',
columnGap: 8,
}}
>
<Search className="size-4 shrink-0 text-fd-muted-foreground" />
<span className="flex-1 text-sm text-fd-muted-foreground">Search or ask AI</span>
<Search className="size-4 text-fd-muted-foreground" />
<span className="text-sm text-fd-muted-foreground">Search or ask AI</span>
<kbd className="rounded border px-1.5 py-0.5 text-[10px] text-fd-muted-foreground">⌘K</kbd>
</button>
);
+1 -1
View File
@@ -35,7 +35,7 @@
"@hanzo/gui": "8.1.1",
"@hanzo/iam": "^0.21.6",
"@hanzo/logo": "^1.0.14",
"@hanzo/ui": "8.2.2",
"@hanzo/ui": "8.3.0",
"@hanzogui/shell": "^7.6.3",
"@llmgateway/ai-sdk-provider": "^3.8.0",
"@openrouter/ai-sdk-provider": "^2.9.0",
@@ -30,7 +30,7 @@ export function Breadcrumb({
return (
<div
{...props}
className={cn('flex items-center gap-1.5 text-sm text-fd-muted-foreground', props.className)}
className={cn('grid grid-flow-col justify-start items-center gap-1.5 text-sm text-fd-muted-foreground', props.className)}
>
{items.map((item, i) => {
const className = cn('truncate', i === items.length - 1 && 'text-fd-primary font-medium');
@@ -19,17 +19,31 @@ export function Header(props: ComponentProps<'header'>) {
id="nd-subnav"
data-transparent={isNavTransparent}
{...props}
// Four tracks: wordmark, nav, search, sidebar toggle. The nav track is the
// one that stretches, which `flex` reached by putting `flex-1` on a
// wrapper; a track says it once and the wrapper stops being load-bearing.
className={cn(
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 flex items-center ps-4 pe-2.5 border-b transition-colors backdrop-blur-sm h-(--fd-header-height) md:hidden max-md:layout:[--fd-header-height:--spacing(14)] data-[transparent=false]:bg-fd-background/80',
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 grid grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center ps-4 pe-2.5 border-b transition-colors backdrop-blur-sm h-(--fd-header-height) md:hidden max-md:layout:[--fd-header-height:--spacing(14)] data-[transparent=false]:bg-fd-background/80',
props.className,
)}
>
{slots.navTitle && (
<slots.navTitle className="inline-flex items-center gap-2.5 font-semibold" />
{/* Every cell is emitted even when its slot is empty. A track list counts
items, so an absent LEADING slot slides each later one a track to the
left — the search trigger lands in the stretch track and the toggle
stops being at the right edge. An empty cell in an `auto` track is
zero wide and costs nothing. */}
{slots.navTitle ? (
<slots.navTitle className="inline-grid grid-flow-col items-center gap-2.5 font-semibold" />
) : (
<span />
)}
<div className="flex-1">{nav?.children}</div>
{slots.searchTrigger && <slots.searchTrigger.sm hideIfDisabled className="p-2" />}
{slots.sidebar && (
<div>{nav?.children}</div>
{slots.searchTrigger ? (
<slots.searchTrigger.sm hideIfDisabled className="p-2" />
) : (
<span />
)}
{slots.sidebar ? (
<slots.sidebar.trigger
className={cn(
buttonVariants({
@@ -41,6 +55,8 @@ export function Header(props: ComponentProps<'header'>) {
>
<SidebarIcon />
</slots.sidebar.trigger>
) : (
<span />
)}
</header>
);
@@ -30,7 +30,7 @@ export function Breadcrumb({
return (
<div
{...props}
className={cn('flex items-center gap-1.5 text-sm text-fd-muted-foreground', props.className)}
className={cn('grid grid-flow-col justify-start items-center gap-1.5 text-sm text-fd-muted-foreground', props.className)}
>
{items.map((item, i) => {
const className = cn('truncate', i === items.length - 1 && 'text-fd-primary font-medium');
@@ -19,17 +19,31 @@ export function Header(props: ComponentProps<'header'>) {
id="nd-subnav"
data-transparent={isNavTransparent}
{...props}
// Four tracks: wordmark, nav, search, sidebar toggle. The nav track is the
// one that stretches, which `flex` reached by putting `flex-1` on a
// wrapper; a track says it once and the wrapper stops being load-bearing.
className={cn(
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 flex items-center ps-4 pe-2.5 border-b transition-colors backdrop-blur-sm h-(--fd-header-height) md:hidden max-md:layout:[--fd-header-height:--spacing(14)] data-[transparent=false]:bg-fd-background/80',
'[grid-area:header] sticky top-(--fd-docs-row-1) z-30 grid grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center ps-4 pe-2.5 border-b transition-colors backdrop-blur-sm h-(--fd-header-height) md:hidden max-md:layout:[--fd-header-height:--spacing(14)] data-[transparent=false]:bg-fd-background/80',
props.className,
)}
>
{slots.navTitle && (
<slots.navTitle className="inline-flex items-center gap-2.5 font-semibold" />
{/* Every cell is emitted even when its slot is empty. A track list counts
items, so an absent LEADING slot slides each later one a track to the
left — the search trigger lands in the stretch track and the toggle
stops being at the right edge. An empty cell in an `auto` track is
zero wide and costs nothing. */}
{slots.navTitle ? (
<slots.navTitle className="inline-grid grid-flow-col items-center gap-2.5 font-semibold" />
) : (
<span />
)}
<div className="flex-1">{nav?.children}</div>
{slots.searchTrigger && <slots.searchTrigger.sm hideIfDisabled className="p-2" />}
{slots.sidebar && (
<div>{nav?.children}</div>
{slots.searchTrigger ? (
<slots.searchTrigger.sm hideIfDisabled className="p-2" />
) : (
<span />
)}
{slots.sidebar ? (
<slots.sidebar.trigger
className={cn(
buttonVariants({
@@ -41,6 +55,8 @@ export function Header(props: ComponentProps<'header'>) {
>
<SidebarIcon />
</slots.sidebar.trigger>
) : (
<span />
)}
</header>
);
+15 -66
View File
@@ -636,8 +636,8 @@ importers:
specifier: ^1.0.14
version: 1.0.14([email protected])
'@hanzo/ui':
specifier: 8.2.2
version: 8.2.2(ef411970f9042f32c22763b08c25278d)
specifier: 8.3.0
version: 8.3.0(ef411970f9042f32c22763b08c25278d)
'@hanzogui/shell':
specifier: ^7.6.3
version: 7.6.3(@hanzo/[email protected]([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
@@ -4294,7 +4294,7 @@ importers:
version: 7.15.1([email protected]([email protected]))([email protected])
waku:
specifier: ^0.26.0 || ^0.27.0
version: 0.27.5(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])
version: 0.27.5(@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected])
zod:
specifier: '>=3.0.0'
version: 4.4.3
@@ -7531,8 +7531,8 @@ packages:
react-native-web:
optional: true
'@hanzo/ui@8.2.2':
resolution: {integrity: sha512-C6hGGs01c96ao5VgIqUgoHyfl00xhL4WBrUl74dxDNs4D6w7NOiiPZ0AoHMvetoUAFjXE3RjQQIkVPGoXsGIUw==}
'@hanzo/ui@8.3.0':
resolution: {integrity: sha512-y7LGVb+GBuWltfXgmObPd7E5Ysdxbysj5yNJp39X9Vj6D7M5v2Yg/x7HZio0ZI5hcNijJ8v09OWKzmPjpxf3Qw==}
peerDependencies:
'@hanzo/canvas': '>=0.1.0'
'@hanzo/cd': '>=0.1.0'
@@ -22118,7 +22118,7 @@ snapshots:
transitivePeerDependencies:
- react-native-svg
'@hanzo/ui@8.2.2(ef411970f9042f32c22763b08c25278d)':
'@hanzo/ui@8.3.0(ef411970f9042f32c22763b08c25278d)':
dependencies:
'@hanzo/appearance': 0.2.2([email protected])
'@hanzo/brand': 1.4.6([email protected])
@@ -27299,7 +27299,9 @@ snapshots:
metro-runtime: 0.84.4
transitivePeerDependencies:
- '@babel/core'
- bufferutil
- supports-color
- utf-8-validate
'@react-native/[email protected]': {}
@@ -29973,7 +29975,7 @@ snapshots:
optionalDependencies:
babel-plugin-react-compiler: 1.0.0
'@vitejs/[email protected]([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))':
'@vitejs/[email protected]([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.18
es-module-lexer: 2.1.0
@@ -29987,7 +29989,7 @@ snapshots:
vite: 7.3.3(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
vitefu: 1.1.3([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
optionalDependencies:
react-server-dom-webpack: 19.2.6([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected]))
react-server-dom-webpack: 19.2.6([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected]))
'@vitejs/[email protected]([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))':
dependencies:
@@ -36115,13 +36117,13 @@ snapshots:
rxjs: 7.8.2
use-effect-event: 2.0.3([email protected])
[email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected])):
[email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected])):
dependencies:
acorn-loose: 8.5.2
neo-async: 2.6.2
react: 19.2.6
react-dom: 19.2.6([email protected])
webpack: 5.107.2([email protected]7.7)([email protected])([email protected])
webpack: 5.107.2([email protected]8.0)([email protected])([email protected])
webpack-sources: 3.5.0
[email protected]([email protected]([email protected]))([email protected])([email protected]([email protected])([email protected])):
@@ -37526,18 +37528,6 @@ snapshots:
[email protected]: {}
[email protected]([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
jest-worker: 27.5.1
schema-utils: 4.3.3
terser: 5.48.0
webpack: 5.107.2([email protected])([email protected])([email protected])
optionalDependencies:
esbuild: 0.27.7
lightningcss: 1.32.0
postcss: 8.5.15
[email protected]([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected])):
dependencies:
'@jridgewell/trace-mapping': 0.3.31
@@ -37549,7 +37539,6 @@ snapshots:
esbuild: 0.28.0
lightningcss: 1.32.0
postcss: 8.5.15
optional: true
[email protected]([email protected])([email protected])([email protected]([email protected])([email protected])):
dependencies:
@@ -38395,18 +38384,18 @@ snapshots:
dependencies:
xml-name-validator: 5.0.0
[email protected](@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected]):
[email protected](@types/[email protected])([email protected])([email protected])([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected])))([email protected])([email protected])([email protected])([email protected]):
dependencies:
'@hono/node-server': 1.19.14([email protected])
'@vitejs/plugin-react': 5.2.0([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
'@vitejs/plugin-rsc': 0.5.26([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
'@vitejs/plugin-rsc': 0.5.26([email protected]([email protected]))([email protected]([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected])))([email protected])([email protected](@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected]))
dotenv: 17.4.2
hono: 4.12.23
magic-string: 0.30.21
picocolors: 1.1.1
react: 19.2.6
react-dom: 19.2.6([email protected])
react-server-dom-webpack: 19.2.6([email protected]([email protected]))([email protected])([email protected]([email protected]7.7)([email protected])([email protected]))
react-server-dom-webpack: 19.2.6([email protected]([email protected]))([email protected])([email protected]([email protected]8.0)([email protected])([email protected]))
rsc-html-stream: 0.0.7
vite: 7.3.3(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
transitivePeerDependencies:
@@ -38589,45 +38578,6 @@ snapshots:
[email protected]: {}
[email protected]([email protected])([email protected])([email protected]):
dependencies:
'@types/estree': 1.0.9
'@types/json-schema': 7.0.15
'@webassemblyjs/ast': 1.14.1
'@webassemblyjs/wasm-edit': 1.14.1
'@webassemblyjs/wasm-parser': 1.14.1
acorn: 8.16.0
acorn-import-phases: 1.0.4([email protected])
browserslist: 4.28.2
chrome-trace-event: 1.0.4
enhanced-resolve: 5.22.0
es-module-lexer: 2.1.0
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
loader-runner: 4.3.2
mime-db: 1.54.0
neo-async: 2.6.2
schema-utils: 4.3.3
tapable: 2.3.3
terser-webpack-plugin: 5.6.1([email protected])([email protected])([email protected])([email protected]([email protected])([email protected])([email protected]))
watchpack: 2.5.1
webpack-sources: 3.5.0
transitivePeerDependencies:
- '@minify-html/node'
- '@swc/core'
- '@swc/css'
- '@swc/html'
- clean-css
- cssnano
- csso
- esbuild
- html-minifier-terser
- lightningcss
- postcss
- uglify-js
[email protected]([email protected]):
dependencies:
'@types/estree': 1.0.9
@@ -38744,7 +38694,6 @@ snapshots:
- lightningcss
- postcss
- uglify-js
optional: true
[email protected]([email protected])([email protected]):
dependencies: