mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
244 lines
8.8 KiB
TypeScript
244 lines
8.8 KiB
TypeScript
'use client'
|
|
|
|
import type { ReactNode } from 'react'
|
|
import { ArrowRight, Code2, ExternalLink, Terminal, Layers, BookOpen, Key, Cpu, Zap } from 'lucide-react'
|
|
import { Box, Text, XStack, YStack } from '@hanzo/ui'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import { Action, Chip, Eyebrow, Lede, Line, More, Title } from '@hanzo/ui/marketing'
|
|
import { Band, Card, Head } from '@/components/marketing/band'
|
|
import { Sample } from '@/components/models/kit'
|
|
|
|
export interface ModelSpecItem {
|
|
label: string
|
|
value: string
|
|
mono?: boolean
|
|
note?: string
|
|
}
|
|
|
|
export interface SdkLinkItem {
|
|
language: string
|
|
href: string
|
|
tag?: string
|
|
}
|
|
|
|
export interface ModelCardSectionProps {
|
|
id?: string
|
|
badge: string
|
|
title: string
|
|
description: ReactNode
|
|
modelId: string
|
|
endpoint: string
|
|
specs: ModelSpecItem[]
|
|
sdkLinks: SdkLinkItem[]
|
|
recipesLink?: { label: string; href: string }
|
|
githubRepo: string
|
|
huggingFaceUrl?: string
|
|
hfBadgeText?: string
|
|
docsUrl: string
|
|
sampleCode?: {
|
|
lang: string
|
|
code: string
|
|
}
|
|
}
|
|
|
|
export function ModelCardSection({
|
|
id = 'model-card',
|
|
badge,
|
|
title,
|
|
description,
|
|
modelId,
|
|
endpoint,
|
|
specs,
|
|
sdkLinks,
|
|
recipesLink,
|
|
githubRepo,
|
|
huggingFaceUrl,
|
|
hfBadgeText,
|
|
docsUrl,
|
|
sampleCode,
|
|
}: ModelCardSectionProps) {
|
|
return (
|
|
<Band id={id} measure={1180}>
|
|
<Head
|
|
eyebrow={badge}
|
|
title={title}
|
|
lede={description}
|
|
loud
|
|
/>
|
|
|
|
<Grid columns={{ min: 320, max: 2 }} gap={20} mt="$6">
|
|
{/* Left Card: Core Model Specifications */}
|
|
<Card display="flex" flexDirection="column" gap="$4" p="$6" borderColor="var(--border)" bg="$panel">
|
|
<XStack justify="space-between" items="center" borderBottomWidth={1} borderColor="var(--border)" pb="$3">
|
|
<XStack items="center" gap="$2">
|
|
<Cpu size={18} color="var(--primary)" />
|
|
<Text fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
|
|
Model Card Specifications
|
|
</Text>
|
|
</XStack>
|
|
<Chip px={10} py={3} fontSize="$1" lineHeight="$1">
|
|
{modelId}
|
|
</Chip>
|
|
</XStack>
|
|
|
|
<YStack gap="$2" py="$1">
|
|
<XStack justify="space-between" items="baseline" gap="$3" py="$2" borderBottomWidth={1} borderColor="var(--border)">
|
|
<Text shrink={0} fontSize="$2" lineHeight="$2" color="var(--muted-foreground)">
|
|
Wire Endpoint
|
|
</Text>
|
|
{/* A URL is one word: it breaks anywhere rather than run off a phone. */}
|
|
<Text minW={0} fontFamily="$mono" fontSize="$2" lineHeight="$2" color="$ink" text="right" $platform-web={{ overflowWrap: 'anywhere' }}>
|
|
{endpoint}
|
|
</Text>
|
|
</XStack>
|
|
|
|
{specs.map((item) => (
|
|
<XStack key={item.label} justify="space-between" items="baseline" py="$2" borderBottomWidth={1} borderColor="var(--border)">
|
|
<YStack gap="$1">
|
|
<Text fontSize="$2" lineHeight="$2" color="var(--muted-foreground)">
|
|
{item.label}
|
|
</Text>
|
|
{item.note && (
|
|
<Text fontSize="$1" lineHeight="$1" color="var(--text-tertiary)">
|
|
{item.note}
|
|
</Text>
|
|
)}
|
|
</YStack>
|
|
<Text
|
|
fontFamily={item.mono ? '$mono' : undefined}
|
|
fontSize="$2"
|
|
lineHeight="$2"
|
|
fontWeight="600"
|
|
color="$ink"
|
|
$platform-web={{ fontVariantNumeric: 'tabular-nums', textAlign: 'right' }}
|
|
>
|
|
{item.value}
|
|
</Text>
|
|
</XStack>
|
|
))}
|
|
</YStack>
|
|
|
|
<XStack mt="auto" pt="$3" flexWrap="wrap" gap="$3">
|
|
<Action href="https://platform.hanzo.ai/keys" fill>
|
|
<Key size={14} /> Get API key <ArrowRight size={14} />
|
|
</Action>
|
|
{huggingFaceUrl && (
|
|
<Action href={huggingFaceUrl} target="_blank" rel="noreferrer">
|
|
🤗 {hfBadgeText ?? 'Hugging Face'}
|
|
</Action>
|
|
)}
|
|
<Action href={docsUrl}>
|
|
<BookOpen size={14} /> Documentation
|
|
</Action>
|
|
</XStack>
|
|
</Card>
|
|
|
|
{/* Right Card: Multi-Language SDKs & Runnable Recipes */}
|
|
<Card display="flex" flexDirection="column" gap="$4" p="$6" borderColor="var(--border)" bg="$panel">
|
|
<XStack justify="space-between" items="center" borderBottomWidth={1} borderColor="var(--border)" pb="$3" flexWrap="wrap" gap="$2">
|
|
<XStack items="center" gap="$2">
|
|
<Code2 size={18} color="var(--primary)" />
|
|
<Text fontSize="$4" lineHeight="$4" fontWeight="600" color="$ink">
|
|
Multi-Language SDKs & OSS Examples
|
|
</Text>
|
|
</XStack>
|
|
<XStack items="center" gap="$3">
|
|
{huggingFaceUrl && (
|
|
<More href={huggingFaceUrl} target="_blank" rel="noreferrer" fontSize="$2">
|
|
🤗 Hugging Face
|
|
</More>
|
|
)}
|
|
<More href={githubRepo} fontSize="$2">
|
|
GitHub Repo <ExternalLink size={12} />
|
|
</More>
|
|
</XStack>
|
|
</XStack>
|
|
|
|
<Text fontSize="$2" lineHeight="$3" color="var(--text-secondary)">
|
|
Runnable examples across all tier-one runtimes. Zero-install authentication with <Text fontFamily="$mono">hanzo auth login</Text> or environment variable <Text fontFamily="$mono">HANZO_API_KEY</Text>.
|
|
</Text>
|
|
|
|
{/* SDK Language Links Grid */}
|
|
<Grid columns={{ min: 140, max: 3 }} gap={10} my="$2">
|
|
{sdkLinks.map((sdk) => (
|
|
<YStack
|
|
key={sdk.language}
|
|
render={<a href={sdk.href} target="_blank" rel="noreferrer" />}
|
|
gap={4}
|
|
p={12}
|
|
rounded={10}
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="var(--background)"
|
|
$platform-web={{ textDecoration: 'none' }}
|
|
>
|
|
<XStack justify="space-between" items="center">
|
|
<Text fontSize="$2" lineHeight="$2" fontWeight="600" color="$ink">
|
|
{sdk.language}
|
|
</Text>
|
|
<ExternalLink size={12} color="var(--muted-foreground)" />
|
|
</XStack>
|
|
{sdk.tag && (
|
|
<Text fontFamily="$mono" fontSize="$1" lineHeight="$1" color="var(--muted-foreground)">
|
|
{sdk.tag}
|
|
</Text>
|
|
)}
|
|
</YStack>
|
|
))}
|
|
</Grid>
|
|
|
|
{recipesLink && (
|
|
<Box
|
|
render={<a href={recipesLink.href} target="_blank" rel="noreferrer" />}
|
|
p={12}
|
|
rounded={10}
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="var(--background)"
|
|
$platform-web={{ textDecoration: 'none' }}
|
|
>
|
|
<XStack justify="space-between" items="center">
|
|
<XStack items="center" gap="$2">
|
|
<Zap size={14} color="var(--primary)" />
|
|
<Text fontSize="$2" lineHeight="$2" fontWeight="600" color="$ink">
|
|
{recipesLink.label}
|
|
</Text>
|
|
</XStack>
|
|
<ExternalLink size={12} color="var(--muted-foreground)" />
|
|
</XStack>
|
|
<Text display="block" fontSize="$1" lineHeight="$2" color="var(--text-secondary)" mt="$1">
|
|
Modular production blueprints, enterprise crates, and automated verification suites.
|
|
</Text>
|
|
</Box>
|
|
)}
|
|
|
|
{sampleCode && (
|
|
<YStack gap="$1" mt="auto">
|
|
<Sample lang={sampleCode.lang}>
|
|
{/* Plain text in the code frame's face. components/code's `Code` is an
|
|
async server component; rendered inside this client component it is
|
|
an async client component, which React refuses (#482) and which put
|
|
/kai behind its error boundary on about half of its loads. */}
|
|
<Line
|
|
render="pre"
|
|
display="block"
|
|
m={0}
|
|
p={16}
|
|
overflowX="auto"
|
|
whiteSpace="pre"
|
|
fontFamily="$mono"
|
|
fontSize="$2"
|
|
lineHeight="var(--leading-relaxed)"
|
|
$platform-web={{ backgroundColor: 'light-dark(var(--pure-white), var(--card))' }}
|
|
>
|
|
<code>{sampleCode.code}</code>
|
|
</Line>
|
|
</Sample>
|
|
</YStack>
|
|
)}
|
|
</Card>
|
|
</Grid>
|
|
</Band>
|
|
)
|
|
}
|