Files
hanzo.ai/components/home/Models.tsx
Cursor Agentandz c3af929c56 site: one API, one account, better cheaper faster
The bar is Models, Dev, Platform, Pricing, Docs, Enterprise, Log in, and Get API key. The homepage is seven sections. Launch pages stay in the tree and drop out of the index through lib/launch.ts. /platform forwards to /cloud. Model and repository floors come from the snapshots the build already keeps.

Co-authored-by: z <[email protected]>
2026-10-26 21:53:40 +00:00

179 lines
6.2 KiB
TypeScript

'use client'
import { useRef } from 'react'
import Link from 'next/link'
import { ArrowRight } from 'lucide-react'
import { phrase } from '@hanzo/i18n'
import { useI18n } from '@hanzo/i18n/react'
import { Text, View, XStack, YStack } from '@hanzo/ui'
import { CREATIVE_MODELS } from '@/lib/creative-models'
import { NOW } from '@/lib/zen'
import { Section } from './chrome'
/**
* Every model, side by side and named as released (Zen 6, Kai 1, Koma 1): the
* language models (Enso, Kai, Zen) and the
* creative ones (Koma, Iro, Mori, Oto, Koe, Uta), each a panel that opens wide
* under the pointer onto its painting (public/images/models, painted on Hanzo's
* own engine), what it does in a line, and a link to its page, where its
* benchmarks are. The panel a reader points at opens; with
* none pointed at, Enso's stands open. Koma's panel plays its reel while open
* under the pointer. Below 1024px the panels are a row to swipe, every one open.
* The opening is CSS (`[data-models]` in app/layout.tsx), still under reduced
* motion. A model's name is said as released; what it is and what it does are
* authored (`phrase`) and drawn in the reader's language.
*/
interface Model {
slug: string
name: string
kind: string
line: string
href: string
still: string
}
const still = (slug: string) => `/images/models/${slug}.webp`
const LANGUAGE: readonly Model[] = [
{ slug: 'enso', name: 'Enso 1', kind: phrase('Router'), line: phrase('Each request goes to the model that answers it right for the least, across every frontier lab.'), href: '/enso', still: still('enso') },
{ slug: 'kai', name: 'Kai 1', kind: phrase('Decisions'), line: phrase('Deterministic probability distributions with sub-millisecond GPU latency and zero output tokens to pay for.'), href: '/kai', still: still('kai') },
{ slug: 'zen', name: NOW.name, kind: phrase('Open models'), line: phrase('Open-weight models for agentic coding. Download the weights or run them on Hanzo Cloud.'), href: '/zen', still: still('zen') },
]
const CREATIVE: readonly Model[] = CREATIVE_MODELS.map((m) => ({
slug: m.slug,
name: m.release,
kind: m.slug === 'oto' ? phrase('Sound') : m.modalityLabel,
line: m.lede,
href: `/${m.slug}`,
still: still(m.slug),
}))
const MODELS = [...LANGUAGE, ...CREATIVE]
const LEAD = 'enso'
/** Koma's reel, played while its panel is open under the pointer. */
function Reel({ still }: { still: string }) {
const ref = useRef<HTMLVideoElement>(null)
return (
<video
ref={ref}
data-media=""
muted
loop
playsInline
preload="none"
poster={still}
aria-hidden
onPointerEnter={() => void ref.current?.play().catch(() => {})}
onPointerLeave={() => ref.current?.pause()}
>
<source src="/videos/koma-demo.webm" type="video/webm" />
<source src="/videos/koma-demo.mp4" type="video/mp4" />
</video>
)
}
function Panel({ m }: { m: Model }) {
const { t } = useI18n()
const kind = t(m.kind)
return (
<View
data-model={m.slug}
data-lead={m.slug === LEAD ? '' : undefined}
render={<Link href={m.href} aria-label={t('{name}, {kind}', { name: m.name, kind })} />}
position="relative"
overflow="hidden"
rounded={20}
borderWidth={1}
borderColor="var(--border)"
bg="var(--pure-black)"
$platform-web={{ borderRadius: '20px', textDecoration: 'none' }}
>
{m.slug === 'koma' ? (
<Reel still={m.still} />
) : (
<View
data-media=""
render={<img src={m.still} alt="" loading="lazy" />}
position="absolute"
t={0}
l={0}
width="100%"
height="100%"
$platform-web={{ objectFit: 'cover' }}
/>
)}
<View
data-scrim=""
aria-hidden
position="absolute"
t={0}
l={0}
r={0}
b={0}
pointerEvents="none"
$platform-web={{ background: 'linear-gradient(to top, color-mix(in srgb, var(--pure-black) 82%, transparent) 0%, transparent 58%)' }}
/>
<Text
data-spine=""
aria-hidden
fontFamily="$mono"
fontSize="$2"
fontWeight="600"
color="var(--pure-white)"
position="absolute"
b={22}
l="50%"
$platform-web={{ transform: 'translateX(-50%) rotate(180deg)', writingMode: 'vertical-rl', whiteSpace: 'nowrap' }}
>
{m.name}
</Text>
<YStack data-open="" position="absolute" l={0} r={0} b={0} p={24} gap={8}>
<Text fontFamily="$mono" fontSize="$1" color="var(--white-80)">
{kind}
</Text>
<Text fontSize="$11" lineHeight="$11" fontWeight="600" color="var(--pure-white)" $platform-web={{ letterSpacing: '-0.035em' }}>
{m.name}
</Text>
<Text fontSize="$3" lineHeight="1.5rem" color="var(--white-80)" maxW={420}>
{t(m.line)}
</Text>
<XStack items="center" gap={6} mt={6}>
<Text fontSize="$2" fontWeight="600" color="var(--pure-white)">
{t('Explore {name}', { name: m.name })}
</Text>
<ArrowRight size={14} color="var(--pure-white)" aria-hidden data-i18n-mirror />
</XStack>
</YStack>
</View>
)
}
/** Every model, the language ones alone (`language`), or the creative ones (`creative`, as /studio shows them). */
export default function Models({ creative, language }: { creative?: boolean; language?: boolean }) {
const { t } = useI18n()
const shown = creative ? CREATIVE : language ? LANGUAGE : MODELS
return (
<Section
id={creative ? 'creative' : 'models'}
label={creative ? 'Hanzo.Creative' : 'Hanzo.Models'}
title={t(creative ? 'Every creative model, in one studio.' : 'Every kind of model, one API.')}
lede={t(
creative
? 'Video, image, 3D, sound, voice and music, each a node on the canvas. Open one to see what it makes.'
: language
? 'Every model behind one key.'
: 'Language, routing, decisions and creative models. Open one to see what it does, and where it is measured.',
)}
>
<View data-models="" width="100%">
{shown.map((m) => (
<Panel key={m.slug} m={m} />
))}
</View>
</Section>
)
}