mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-29 07:20:43 +00:00
179 lines
6.2 KiB
TypeScript
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('Meta-LLM'), line: phrase("Enso is a meta-LLM: Hanzo's own small model that coordinates 500+ models for higher intelligence, faster and at lower cost."), 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>
|
|
)
|
|
}
|