Files
zeekay 4ddb1c9ed7 landing: every figure is in the HTML, and the page reads a few KB to keep it
The landing drew Smarter, Faster, Cheaper and the plans only once their
reads answered: the whole research record (1.2 MB), the price list, the
model list and the plans, about 160 KB compressed, asked for after
hydration. On a slow phone the figures appeared some 45 s after the page.

The build now reads the slices those figures need and derives them with
the same functions the browser runs (lib/figures, scripts/figures.ts),
one file a figure under lib/data/figures. The hooks draw that from the
first render, so the export holds every figure and no "Reading" line,
then read the slices again once the page is idle and move a figure only
where the answer differs (lib/api/use useLive). A figure the record no
longer holds comes down; a read that fails keeps the build's.

The slices: one research read a board benchmark with its all and
vs-claude-code measures, the timing runs' p50s, the price-list rows the
boards join (by id, and by each system's name), the model list's rows for
Kai, the frontier LLMs and the LLM its call is timed beside, and the
plans. Lists are walked by their next cursor. Kai's speed and spend
(lib/home call, spend) read those two slices, so the landing never reads
the whole record or the whole model list.

e2e/unit/seed.spec.ts holds each slice to the whole read it replaced, and
the gates serve and narrow every read as the API does and wait for the
page's live reads before they look for a served figure.
2026-10-29 21:31:00 -07:00

203 lines
9.3 KiB
TypeScript

'use client'
import Link from 'next/link'
import { FlaskConical } from 'lucide-react'
import { Text, View, XStack, YStack } from '@hanzo/ui'
import { More } from '@hanzo/ui/marketing'
import { Loading } from '@/components/api/state'
import { DESKS, MEASURED } from '@/components/zen/desks'
import { useCoding } from '@/hooks/useCoding'
import { useEnso } from '@/hooks/useEnso'
import { useClaim, useSuite } from '@/hooks/useSuite'
import { runs } from '@/lib/api/research'
import { useReading } from '@/lib/api/use'
import { LEVEL, range, times } from '@/lib/coding'
import { clock, gpqa, livecode, ms, race, tasks } from '@/lib/home'
import { DECISION } from '@/lib/kai'
import { day } from '@/lib/tally'
import { Board } from './Board'
import { Hold, Section, Win, useGrow, useSeen, useStill } from './chrome'
/**
* The latest results, both read from the research API when the page opens.
*
* THE DECISION SUITE: the tasks Kai wins as a grid, one cell a task, lit in
* turn, and their count as "tasks won by Kai", the suite's own claim ("16 of
* 16" while Kai wins every task, else the bare count) — drawn only once every
* task's figures are in. The same tasks, each with its runs, are on /benchmarks,
* linked below; no task Kai does not win is drawn, counted or named.
*
* LIVECODEBENCH: the board's rows as bars (lib/home `bars`), while the router
* tops every single model on it.
*
* Under them, one tile a result, each a figure and what it measured, opening
* the page that shows it in full: Enso on GPQA Diamond (lib/home `gpqa`), a
* Kai decision's clock (`clock`), Hanzo Dev's coding race (`race`, read when
* the page opens, never from the build's snapshot), and Zen 6's prefill on
* Hanzo's own machine (components/zen/desks). Each tile stands only while its
* read holds what its page would draw.
*
* The section is drawn while any read result holds; Zen's measured figure
* alone does not hold it up.
*/
/** One result: whose it is, the figure, what was measured, the sentence that says it, and the page that shows it in full. */
function Result({ who, figure, what, note, href, slot }: { who: string; figure: string; what: string; note: string; href: string; slot: string }) {
return (
<YStack
render={<Link href={href} />}
data-figure={slot}
gap={8}
p={20}
rounded={16}
borderWidth={1}
borderColor="var(--border)"
bg="$panel"
hoverStyle={{ borderColor: 'var(--border-strong)' }}
$platform-web={{ borderRadius: '16px', textDecoration: 'none' }}
>
<Text fontFamily="$mono" fontSize="$1" color="var(--muted-foreground)">
{who}
</Text>
<Text fontSize="2.5rem" lineHeight="2.5rem" fontWeight="500" color="var(--foreground)" $platform-web={{ letterSpacing: '-0.03em', fontVariantNumeric: 'tabular-nums' }}>
{figure}
</Text>
<Text fontSize="$3" lineHeight="$4" fontWeight="600" color="var(--foreground)">
{what}
</Text>
<Text fontSize="$2" lineHeight="$3" color="var(--muted-foreground)">
{note}
</Text>
</YStack>
)
}
function Cell({ label, i, play, still }: { label: string; i: number; play: boolean; still: boolean }) {
const ref = useGrow(play, still, { delay: i * 85, duration: 360, from: 'scale(0)', to: 'scale(1)' })
return (
<View
role="img"
aria-label={label}
borderWidth={1}
borderColor="var(--foreground)"
p={3}
rounded={9999}
overflow="hidden"
$platform-web={{ aspectRatio: '1', borderRadius: '9999px' }}
>
<View ref={ref} width="100%" height="100%" rounded={9999} bg="var(--foreground)" $platform-web={{ borderRadius: '9999px' }} />
</View>
)
}
export default function Research() {
const [ref, seen] = useSeen(0.2)
const still = useStill()
const said = useClaim()
const suite = useSuite()
const enso = useEnso()
// The tasks Kai wins, and no other: the same rows /benchmarks draws.
const list = suite.state === 'ready' ? tasks(suite.data, DECISION.fixed) : null
const days = list ? [...new Set(list.map((t) => t.set))].sort().map(day) : []
const claim = said?.claim ?? null
const lcb = enso.state === 'ready' ? livecode(enso.data) : null
const all = useReading(runs)
const coding = race(useCoding())
const g = enso.state === 'ready' ? gpqa(enso.data) : null
const time = suite.state === 'ready' && all.state === 'ready' ? clock(suite.data, all.data) : null
const zen = DESKS[0]
const hasSuite = Boolean(claim && list?.length)
const suiteDone = suite.state === 'unreachable' || (said !== null && !hasSuite)
const lcbDone = enso.state === 'unreachable' || (enso.state === 'ready' && !lcb)
const read = Boolean(g || time || coding)
if (suiteDone && lcbDone && !read) return null
return (
<Section id="research" label="Research.Log" title="The latest results, with every task." lede="Each result is read from research runs on public questions, the same for every model.">
<View ref={ref}>
<Hold height={360}>
<View display="grid" gridTemplateColumns="minmax(0, 1fr)" gap={16} $lg={{ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 20 }}>
{suiteDone ? null : (
<Win title="Research.DecisionSuite" icon={<FlaskConical size={14} color="var(--background)" strokeWidth={2.2} />} aside="Kai" slot="suite">
<YStack p={20} gap={16}>
{claim && list?.length ? (
<>
<XStack gap={20} items="center" flexWrap="wrap">
<View display="grid" gridTemplateColumns={`repeat(${Math.ceil(Math.sqrt(list.length))}, 28px)`} gap={4} role="list" aria-label="Decision Suite tasks">
{list.map((t, i) => (
<View key={t.label} role="listitem">
<Cell label={`${t.label}: Kai leads`} i={i} play={seen} still={still} />
</View>
))}
</View>
<YStack gap={4} minW={180} flex={1}>
<Text fontSize="3.5rem" lineHeight="3.5rem" $md={{ fontSize: '4rem', lineHeight: '4rem' }} fontWeight="500" color="var(--foreground)" data-figure="suite" $platform-web={{ letterSpacing: '-0.03em' }}>
{claim.figure}
</Text>
<Text fontSize="$4" lineHeight="$4" fontWeight="600" color="var(--foreground)">
Decision Suite tasks won by Kai
</Text>
</YStack>
</XStack>
<Text fontSize="$3" lineHeight="$4" color="var(--muted-foreground)" data-figure="suite-note">
Each set on {days.join(' or ')}, with its public data and the runs of both models.
</Text>
<More href="/benchmarks#decision-suite">Each task and its runs →</More>
</>
) : (
<Loading what="the Decision Suite's runs" />
)}
</YStack>
</Win>
)}
{lcbDone ? null : lcb ? (
<YStack gap={12} data-figure="livecode">
<Board b={lcb} title="Research.LiveCodeBench" play={seen} still={still} slot="livecode" limit={6} />
<Text fontSize="$3" lineHeight="$4" color="var(--muted-foreground)">
LiveCodeBench&apos;s recent problems: Enso {lcb.lead.figure}, {lcb.lead.note}
</Text>
</YStack>
) : (
<Loading what="Enso's research runs" />
)}
</View>
</Hold>
{read ? (
<View display="grid" gridTemplateColumns="minmax(0, 1fr)" gap={16} mt={16} $sm={{ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))' }} $lg={{ gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 20, mt: 20 }}>
{g ? <Result slot="gpqa" who={g.lead.who} figure={g.lead.figure} what="GPQA Diamond" note={g.lead.note} href="/enso" /> : null}
{time ? (
<Result
slot="decision"
who="Kai"
figure={`${ms(time.fresh ?? time.same ?? time.cached)} ms`}
what="One decision, served"
note={`Kai on GPU, each a p50: ${ms(time.cached)} ms cached${time.same != null ? `, ${ms(time.same)} ms through the runtime` : ''}${time.fresh != null ? `, ${ms(time.fresh)} ms served over HTTP` : ''}.`}
href="/kai"
/>
) : null}
{coding ? (
<Result
slot="race"
who="Hanzo Dev"
figure={times(coding.speed)}
what="Faster on the same coding tasks"
note={`Over the ${coding.speed.n} tasks both agents solved, ${LEVEL} interval ${range(coding.speed)}.`}
href="/dev"
/>
) : null}
{zen ? (
<Result
slot="prefill"
who="Zen 6"
figure={zen.prefill}
what="Prompt tokens a second"
note={`On ${zen.machine}, ${zen.build}. Measured by Hanzo, ${day(MEASURED)}.`}
href="/zen#desks"
/>
) : null}
</View>
) : null}
</View>
</Section>
)
}