mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
The owner's spec for a landing page is the models bento's: monochrome, the words left and the art right, no extra prose. The folds carried two to four sentences under each heading, and some a claim, a lede and a list besides. Each now says one line: ProductLanding the tagline where a page states one (the lede still describes it to search), a claim alone where the claim is the line (/payments, /treasury, /zap, the extension), and elsewhere the sentence that says what the thing is. /philosophy's two points, /dns' claim that repeated its title, /calculator's capabilities line and the Spare clauses on /research and /learn go.
365 lines
17 KiB
TypeScript
365 lines
17 KiB
TypeScript
'use client'
|
|
|
|
/**
|
|
* /sdks — the two SDK lines, and where each language's real code lives.
|
|
*
|
|
* NO TAILWIND AND NO STYLE OBJECTS. The two lower section frames are
|
|
* `components/marketing/band`'s `Band`; @hanzo/ui's marketing components carry
|
|
* the type and the controls (`Display`, `Lede`, `Title`, `Eyebrow`, `Chip`,
|
|
* `Action`, `Leaf`); everything left is gui props over @hanzo/design's tokens.
|
|
*
|
|
* `Head` is deliberately NOT used for those two: it centres, and this page's
|
|
* section heads are left-aligned under a label. The frame is shared; the
|
|
* alignment is this page's. The agent prompt and the closing block are the
|
|
* shared blocks whole, head and all.
|
|
*
|
|
* Three things the swap moves, each the shared shape winning over this page's
|
|
* own copy of it:
|
|
*
|
|
* - the section headings take `Title quiet` and the label above each one
|
|
* takes `Eyebrow`, so the ladder is the site's rather than this page's.
|
|
* - `px-6 py-3 rounded-md`, filled and outlined, becomes `Action` at its two
|
|
* weights — the same control every other page draws.
|
|
* - a language card is `Leaf`, which is what its `group` + `hover:border-…`
|
|
* pair was spelling out by hand. The `group` had no `group-hover:` under it
|
|
* at all, so it was doing nothing; it is gone rather than carried.
|
|
*/
|
|
|
|
import { motion } from "@/components/motion"
|
|
import type { ReactNode } from "react"
|
|
import { ArrowRight, Cloud, Package, Sparkles, Terminal } from "lucide-react"
|
|
import { Github } from "@/components/brand"
|
|
import { SDKSection } from "@/components/products/SDKSection"
|
|
import { CodeExamplesSection, type Example } from "@/components/products/CodeExamplesSection"
|
|
import { hanzoSDKs } from "@/lib/data/upstream-projects"
|
|
import { Box, Text, XStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Band } from '@/components/marketing/band'
|
|
import { AgentPrompt } from '@/components/product/AgentPrompt'
|
|
import { Related } from '@/components/product/Related'
|
|
import { product } from '@/lib/product'
|
|
import { OWNED } from '@/lib/go-modules'
|
|
import { Action, Chip, Eyebrow, Title, Leaf, Line } from '@hanzo/ui/marketing'
|
|
import { mix } from '@hanzo/app/lib/mix'
|
|
import { Fold } from '@/components/marketing/fold'
|
|
import { Samples } from '@/components/models/Samples'
|
|
import { Win } from '@/components/home/chrome'
|
|
import { phrase } from '@hanzo/i18n'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// /sdks — the SDK ecosystem landing. One canonical story, two lines:
|
|
// 1. Full Cloud SDK — generated from the Hanzo OpenAPI, one per language.
|
|
// Real code lives in the per-language org (hanzo-<lang>/sdk); the thin
|
|
// wrapper/docs face is hanzoai/<lang>-sdk; the umbrella meta is hanzoai/sdk.
|
|
// 2. AI + Agents — the hand-crafted flagship (Python `hanzo`, Node `@hanzo/ai`).
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/** What the agent prompt and the closing block read, off the one table that
|
|
* holds a product's name, route, audited command and docs page. */
|
|
const PRODUCT = product('sdks')
|
|
|
|
|
|
|
|
/** The inks this page draws in. */
|
|
const INK = 'var(--foreground)'
|
|
const DIM = 'var(--muted-foreground)'
|
|
const SOFT = '$color/80'
|
|
|
|
/** The hairline tile this page sets everything in. */
|
|
const CARD = { p: 24, rounded: 'var(--radius-xl)', borderWidth: 1, borderColor: 'var(--border)', bg: mix('var(--secondary)', 40) } as const
|
|
|
|
/** The two small rungs, each on the leading its utility paired it with. */
|
|
const XS = { fontSize: '$1', lineHeight: '1.3333' } as const
|
|
const SM = { fontSize: '$2', lineHeight: '1.4286' } as const
|
|
|
|
/** A language card: `Leaf` with its edge drawn at rest and 20px inside. */
|
|
const ORG = { p: 20, borderColor: "var(--border)", bg: mix('var(--secondary)', 40), hoverStyle: { borderColor: mix('var(--foreground)', 30) } } as const
|
|
|
|
/** The command a card hands you, set in mono on the page ground. */
|
|
const Command = ({ children }: { children: ReactNode }) => (
|
|
<Line render="div" display="block" mb={16} p={12} rounded="var(--radius-lg)" bg="var(--background)" fontFamily="$mono" {...SM} overflowX="auto" color={SOFT}>
|
|
{children}
|
|
</Line>
|
|
)
|
|
|
|
/** A repository chip: its key in a quieter ink, then the repository. */
|
|
const Repo = ({ name, children }: { name: string; children: ReactNode }) => (
|
|
<Line display="inline-flex" items="center" gap={6} px={10} py={4} rounded="var(--radius-md)" borderWidth={1} borderColor="var(--border)" bg="var(--background)" fontFamily="$mono" {...XS} color={SOFT}>
|
|
<Text color={mix('var(--foreground)', 40)}>{name}</Text>
|
|
{children}
|
|
</Line>
|
|
)
|
|
|
|
const STATE = { px: 8, py: 2, borderColor: "var(--border)", bg: "var(--background)", fontSize: 10, lineHeight: "1.6", fontWeight: "500", textTransform: "uppercase", letterSpacing: "0.05em", color: mix('var(--foreground)', 70) } as const
|
|
|
|
/** Where a line's code lives, in small mono. */
|
|
const Home = ({ children }: { children: ReactNode }) => (
|
|
<Line fontFamily="$mono" {...XS} color={DIM}>
|
|
{children}
|
|
</Line>
|
|
)
|
|
|
|
const CardTitle = ({ render, children }: { render: 'h2' | 'h3'; children: ReactNode }) => (
|
|
<Text render={render} display="block" fontWeight="600" color={INK}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
|
|
const CardBody = ({ children }: { children: ReactNode }) => (
|
|
<Line render="p" display="block" {...SM} color={DIM}>
|
|
{children}
|
|
</Line>
|
|
)
|
|
|
|
/** Mono inside a sentence: `unset` hands the line back to the paragraph, where
|
|
* the face's own leading would open the line it sits on. */
|
|
const Mono = ({ children }: { children: ReactNode }) => (
|
|
<Text fontFamily="$mono" lineHeight="unset" color={SOFT}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
|
|
/** A paragraph under a section head, at the head's measure. */
|
|
const Note = ({ mb, children }: { mb: number; children: ReactNode }) => (
|
|
<Text render="p" display="block" mb={mb} width="100%" maxW={672} color={DIM}>
|
|
{children}
|
|
</Text>
|
|
)
|
|
|
|
/** The disc a card's mark sits on. */
|
|
const Disc = ({ children }: { children: ReactNode }) => (
|
|
<XStack width={40} height={40} items="center" justify="center" rounded="var(--radius-lg)" bg="var(--neutral-800)">
|
|
{children}
|
|
</XStack>
|
|
)
|
|
|
|
type LangOrg = {
|
|
lang: string
|
|
org: string // per-language GitHub org (real code)
|
|
realRepo: string // hanzo-<lang>/sdk — generated cloud SDK
|
|
wrapper: string // hanzoai/<lang>-sdk — docs/landing
|
|
install: string // a command that resolves today
|
|
ai?: string // AI + agents package for this language (only shown when it resolves today)
|
|
flagship?: boolean
|
|
}
|
|
|
|
// Per-language orgs — "we genuinely adopt + love each language." The card links
|
|
// the org (always live); the generated SDK + wrapper repos are its canonical homes.
|
|
// install strings verified against PyPI / npm / Go modules / crates / GitHub.
|
|
const perLanguageOrgs: LangOrg[] = [
|
|
{ lang: "Python", org: "hanzo-py", realRepo: "hanzo-py/sdk", wrapper: "hanzoai/python-sdk", install: "pip install hanzoai", ai: "hanzo", flagship: true },
|
|
{ lang: "Go", org: "hanzo-go", realRepo: "hanzo-go/sdk", wrapper: "hanzoai/go-sdk", install: "go get github.com/hanzoai/go-sdk" },
|
|
{ lang: "TypeScript", org: "hanzo-js", realRepo: "hanzo-js/sdk", wrapper: "hanzoai/js-sdk", install: "npm install @hanzo/sdk", ai: "@hanzo/ai" },
|
|
{ lang: "Rust", org: "hanzo-rs", realRepo: "hanzo-rs/sdk", wrapper: "hanzoai/rust-sdk", install: "cargo add hanzo", ai: "hanzo" },
|
|
{ lang: "Swift", org: "hanzo-swift", realRepo: "hanzo-swift/sdk", wrapper: "hanzoai/swift-sdk", install: ".package(url: \"https://github.com/hanzo-swift/sdk\")" },
|
|
{ lang: "Kotlin", org: "hanzo-kt", realRepo: "hanzo-kt/sdk", wrapper: "hanzoai/kotlin-sdk", install: 'implementation("com.github.hanzo-kt:sdk:v8.0.0")' },
|
|
]
|
|
|
|
// The AI + agents flagship line — distinct from the generated cloud SDK.
|
|
const aiFlagship: { lang: string; package: string; install: string; home: string; note: string }[] = [
|
|
{ lang: "Python", package: "hanzo", install: "pip install hanzo", home: "hanzoai/python-sdk", note: phrase("Flagship — the most complete AI + agents library.") },
|
|
{ lang: "Node", package: "@hanzo/ai", install: "npm install @hanzo/ai", home: "hanzo-js/ai", note: phrase("Models, agents, tools, memory, and MCP for TypeScript.") },
|
|
{ lang: "Rust", package: "hanzo", install: "cargo add hanzo", home: "hanzo-rs/sdk", note: phrase("The AI + agents crate for Rust — crypto, DID, MCP, agents, embeddings, feature-gated.") },
|
|
]
|
|
|
|
export default function SdksPage({ openai, anthropic }: { openai: Example[]; anthropic: Example[] }) {
|
|
const { t } = useI18n()
|
|
return (
|
|
<Box minH="100vh" $platform-web={{ color: 'var(--foreground)' }}>
|
|
{/* The first screen: the two lines, and one call in each language beside them. */}
|
|
<Fold
|
|
label={phrase('SDKs')}
|
|
mark={<Package width={12} height={12} color="currentColor" />}
|
|
title={phrase('SDKs for every language')}
|
|
lede={t('A {cloud} generated from the OpenAPI document the API itself serves, and a hand-written {agents} library.', {
|
|
cloud: <Text color={INK}>{t('full Cloud SDK')}</Text>,
|
|
agents: <Text color={INK}>{t('AI + Agents')}</Text>,
|
|
})}
|
|
actions={
|
|
<>
|
|
<Action href="https://github.com/hanzoai/sdk" target="_blank" rel="noopener noreferrer" fill>
|
|
<Github width={16} height={16} /> hanzoai/sdk
|
|
</Action>
|
|
<Action href="https://docs.hanzo.ai/docs/openapi/">
|
|
{t('API reference')} <ArrowRight width={16} height={16} data-i18n-mirror />
|
|
</Action>
|
|
</>
|
|
}
|
|
preview={
|
|
<Win title="POST https://api.hanzo.ai/v1/chat/completions" icon={<Terminal width={12} height={12} color="currentColor" />}>
|
|
<Box p={12} $md={{ p: 16 }}>
|
|
<Samples samples={openai.map((e) => ({ lang: e.label, code: e.code, sample: e.sample }))} />
|
|
</Box>
|
|
</Win>
|
|
}
|
|
/>
|
|
|
|
{PRODUCT && <AgentPrompt product={PRODUCT} />}
|
|
|
|
{/* Two SDK lines */}
|
|
<Box render="section" pb={16} px={16} $md={{ px: 32 }}>
|
|
<Box width="100%" maxW={896} mx="auto">
|
|
<Grid columns={{ min: 320, max: 2 }} gap={16}>
|
|
<Box render={<motion.div initial={{ opacity: 0, y: 12 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.4 }} />} {...CARD}>
|
|
<XStack items="center" gap={12} mb={12}>
|
|
<Disc>
|
|
<Cloud width={20} height={20} color={mix('var(--foreground)', 80)} />
|
|
</Disc>
|
|
<CardTitle render="h2">{t('Full Cloud SDK')}</CardTitle>
|
|
</XStack>
|
|
<CardBody>
|
|
{t("Generated from one document and never edited by hand, so every route is reachable the day it ships — models, agents, data, compute, network, security, platform and chain. Method and class names are the document's operation ids, which is what makes a client checkable against a release instead of against somebody's memory. Real code lives in {repo}.", {
|
|
repo: <Mono>hanzo-<lang>/sdk</Mono>,
|
|
})}
|
|
</CardBody>
|
|
</Box>
|
|
<Box render={<motion.div initial={{ opacity: 0, y: 12 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.4, delay: 0.05 }} />} {...CARD}>
|
|
<XStack items="center" gap={12} mb={12}>
|
|
<Disc>
|
|
<Sparkles width={20} height={20} color={mix('var(--foreground)', 80)} />
|
|
</Disc>
|
|
<CardTitle render="h2">{t('AI + Agents')}</CardTitle>
|
|
</XStack>
|
|
<CardBody>
|
|
{t('The hand-crafted flagship: models, agents, tools, memory, and MCP. Python {python} is the most complete; Node ships {node}. Distinct from the generated cloud SDK — this is the "we love this language" library.', {
|
|
python: <Mono>hanzo</Mono>,
|
|
node: <Mono>@hanzo/ai</Mono>,
|
|
})}
|
|
</CardBody>
|
|
</Box>
|
|
</Grid>
|
|
</Box>
|
|
</Box>
|
|
|
|
{/* Cloud SDK — install grid, every language */}
|
|
<CodeExamplesSection
|
|
title={phrase('Use the OpenAI SDK')}
|
|
subtitle={phrase('POST /v1/chat/completions — identical request/response to OpenAI Chat Completions.')}
|
|
examples={openai}
|
|
/>
|
|
|
|
<CodeExamplesSection
|
|
title={phrase('Use the Anthropic SDK')}
|
|
subtitle={phrase('POST /v1/messages — the native Anthropic Messages API, x-api-key auth and content-block responses.')}
|
|
examples={anthropic}
|
|
/>
|
|
|
|
<SDKSection productName="Cloud" sdks={hanzoSDKs} />
|
|
|
|
{/* Per-language orgs */}
|
|
<Band pad="$10" measure={1024}>
|
|
<XStack items="center" gap={8} mb={8}>
|
|
<Terminal width={20} height={20} color={INK} />
|
|
<Eyebrow render="span">
|
|
{t('Per-language orgs')}
|
|
</Eyebrow>
|
|
</XStack>
|
|
<Title quiet mb={8}>
|
|
{t('A home for every ecosystem')}
|
|
</Title>
|
|
<Note mb={16}>
|
|
{t('Each language has its own org holding the generated cloud SDK, an AI + agents library and the split-out packages. Every one of them ships the same six runnable examples — prove the key works, one completion, balance and usage, a store round-trip, create and run an agent, list the tools — so knowing one language\'s set is enough to navigate another\'s.')}
|
|
</Note>
|
|
<Grid columns={{ min: 320, max: 2 }} gap={16} mt={32}>
|
|
{perLanguageOrgs.map((l, i) => (
|
|
<Leaf
|
|
render={<motion.a initial={{ opacity: 0, y: 10 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.35, delay: i * 0.04 }} />}
|
|
key={l.lang}
|
|
href={`https://github.com/${l.org}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
{...ORG}
|
|
>
|
|
<XStack items="flex-start" justify="space-between" mb={12}>
|
|
<XStack items="center" gap={12}>
|
|
<CardTitle render="h3">{l.lang}</CardTitle>
|
|
{l.flagship && (
|
|
<Line display="inline-flex" items="center" px={8} py={2} {...XS} rounded="var(--radius)" borderWidth={1} borderColor="var(--border)" bg="var(--white-10)" color={SOFT}>
|
|
{t('Flagship')}
|
|
</Line>
|
|
)}
|
|
<Chip {...STATE}>
|
|
GA
|
|
</Chip>
|
|
</XStack>
|
|
<Home>{l.org}</Home>
|
|
</XStack>
|
|
<Command>
|
|
{l.install}
|
|
</Command>
|
|
<XStack gap={8} flexWrap="wrap">
|
|
<Repo name="sdk">{l.realRepo}</Repo>
|
|
<Repo name="docs">{l.wrapper}</Repo>
|
|
{l.ai && <Repo name="ai">{l.ai}</Repo>}
|
|
</XStack>
|
|
</Leaf>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
|
|
{/* AI + Agents flagship */}
|
|
<Band pad="$10" measure={1024}>
|
|
<XStack items="center" gap={8} mb={8}>
|
|
<Sparkles width={20} height={20} color={INK} />
|
|
<Eyebrow render="span">
|
|
{t('AI + Agents SDK')}
|
|
</Eyebrow>
|
|
</XStack>
|
|
<Title quiet mb={8}>
|
|
{t('The flagship AI library')}
|
|
</Title>
|
|
<Note mb={32}>
|
|
{t('Models, agents, tools, memory, and MCP — hand-crafted, not generated. Python leads; Rust and Node follow.')}
|
|
</Note>
|
|
|
|
<Grid columns={{ min: 280, max: 3 }} gap={16}>
|
|
{aiFlagship.map((a, i) => (
|
|
<Box
|
|
key={a.lang}
|
|
render={<motion.div initial={{ opacity: 0, y: 10 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ duration: 0.35, delay: i * 0.04 }} />}
|
|
{...CARD}
|
|
p={20}
|
|
>
|
|
<XStack items="center" justify="space-between" mb={12}>
|
|
<XStack items="center" gap={8}>
|
|
<CardTitle render="h3">{a.lang}</CardTitle>
|
|
<Chip {...STATE}>
|
|
GA
|
|
</Chip>
|
|
</XStack>
|
|
<Home>{a.home}</Home>
|
|
</XStack>
|
|
<Command>
|
|
{a.install}
|
|
</Command>
|
|
<CardBody>{t(a.note)}</CardBody>
|
|
</Box>
|
|
))}
|
|
</Grid>
|
|
</Band>
|
|
|
|
{/* The Go modules served at hanzo.ai/<name>: each page carries the go-import
|
|
tag the go tool reads, and this list is how a reader finds them. */}
|
|
<Band pad="$10" measure={1024}>
|
|
<Eyebrow render="span">{t('Go modules')}</Eyebrow>
|
|
<Title quiet my={8}>
|
|
{t('Served at hanzo.ai')}
|
|
</Title>
|
|
<Note mb={24}>
|
|
{t('Each module has a page at its import path, with its source and its reference.')}
|
|
</Note>
|
|
<XStack flexWrap="wrap" gap={8}>
|
|
{OWNED.map((mod) => (
|
|
<Chip href={`/${mod}`} key={mod} px={12} py={4} fontFamily="var(--font-mono)">
|
|
hanzo.ai/{mod}
|
|
</Chip>
|
|
))}
|
|
</XStack>
|
|
</Band>
|
|
|
|
{PRODUCT && <Related product={PRODUCT} />}
|
|
</Box>
|
|
)
|
|
}
|