mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
Every word the landing, the header menus, the phone sheet, the footer,
the help corner and the way in (sign-in, sign-up, the reset, the
invitation, the callback, onboarding, the 404) write goes through
@hanzo/i18n's t(): one sentence is one message, its figures, names and
links arguments; copy written as data is phrase('…') where written and
t(label) where drawn. Plans, the chart, the API states and the lines
lib/kai and lib/enso build for the landing do the same.
Right to left: <html lang dir> is set before the first paint (the
package's bootScript first in the head); a figure, clock, range or
price outside a sentence is isolated; sides are logical gui props, the
layout's [data-join] corners logical, a bar grows from the start;
arrows along the line mirror (Onward for "words ->" links). The repo
dates read in the reader's language.
Gates select by slot, never by an English label (rtl, help). New gate
translated.spec.ts: in Arabic the landing at 1440 and 390, every menu
open, the phone sheet, sign-in, sign-up and the app signed in hold no
authored English, nothing scrolls sideways, every range is isolated;
French the same for English's commonest words. What another package
draws with no words prop is listed by name (@hanzo/ui's Footer label,
@hanzo/build's composer row and suggestions).
@hanzo/i18n 0.5.3 (the catalogs: 1,443 keys, every offered locale
complete), @hanzo/app 0.2.1 (the app's words). The link gate imports
the terms versions from @hanzo/app, the one SDK main now resolves.
96 lines
5.2 KiB
TypeScript
96 lines
5.2 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { Bot, Brain, Coins, Cpu, Database, Network, Plug, ScanEye } from 'lucide-react'
|
|
import type { ReactNode } from 'react'
|
|
import { phrase } from '@hanzo/i18n'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
import { Text, View, XStack, YStack } from '@hanzo/ui'
|
|
import { More } from '@hanzo/ui/marketing'
|
|
import { Section } from './chrome'
|
|
|
|
/**
|
|
* The unified AI stack every app and model runs on: the eight parts an agent
|
|
* is built from, in the order a builder meets them (the gateway, the agents,
|
|
* their tools, their memory, then compute, data, billing and observability),
|
|
* each with what it gives in three plain lines and a link to its page. The
|
|
* apps and the models have their own sections above, so it names neither.
|
|
*/
|
|
|
|
type Part = { icon: ReactNode; name: string; line: string; gives: readonly [string, string, string]; href: string }
|
|
|
|
const ICON = { size: 20, color: 'var(--foreground)', 'aria-hidden': true } as const
|
|
|
|
const PARTS: readonly Part[] = [
|
|
{ icon: <Network {...ICON} />, name: phrase('AI Gateway'), line: phrase('Every model behind one key.'), gives: [phrase('One OpenAI-compatible API'), phrase('Routing across every frontier lab'), phrase('Spend limits and usage per key')], href: '/gateway' },
|
|
{ icon: <Bot {...ICON} />, name: phrase('Agents'), line: phrase('Agents that do the work, not just the chat.'), gives: [phrase('Background and scheduled runs'), phrase('Sandboxed runtimes'), phrase('Your tools and data, with your permissions')], href: '/agents' },
|
|
{ icon: <Plug {...ICON} />, name: 'MCP', line: phrase('The tools your agents act with.'), gives: [phrase('Search, files, shell and browser'), phrase('Works with any MCP client'), phrase('Bring your own MCP servers')], href: '/mcp' },
|
|
{ icon: <Brain {...ICON} />, name: phrase('Memory and Search'), line: phrase('What your agents know, and how they find it.'), gives: [phrase('Keyword and meaning in one query'), phrase('A knowledge graph for every org'), phrase('Retrieval over your documents')], href: '/search' },
|
|
{ icon: <Cpu {...ICON} />, name: phrase('Compute'), line: phrase('GPUs and machines, in our cloud or yours.'), gives: [phrase('GPUs for inference and training'), phrase('Containers and functions'), phrase('The same stack in your own cloud')], href: '/machines' },
|
|
{ icon: <Database {...ICON} />, name: phrase('Data'), line: phrase('A database for every org, ready to build on.'), gives: [phrase('SQL with live updates'), phrase('Files on S3-compatible storage'), phrase('Analytics over every event')], href: '/base' },
|
|
{ icon: <Coins {...ICON} />, name: phrase('Commerce'), line: phrase('Charge for what your agents do.'), gives: [phrase('Plans, subscriptions and top-ups'), phrase('Usage metered as it happens'), phrase('Invoices and payouts')], href: '/commerce' },
|
|
{ icon: <ScanEye {...ICON} />, name: phrase('Observability'), line: phrase('See every model call, end to end.'), gives: [phrase('Traces of every call'), phrase('Cost and latency per request'), phrase('Logs and metrics in one place')], href: '/o11y' },
|
|
]
|
|
|
|
function Card({ p }: { p: Part }) {
|
|
const { t } = useI18n()
|
|
return (
|
|
<YStack
|
|
render={<Link href={p.href} />}
|
|
gap={14}
|
|
p={20}
|
|
rounded={20}
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="$panel"
|
|
hoverStyle={{ borderColor: 'var(--border-strong)' }}
|
|
$md={{ p: 24 }}
|
|
$platform-web={{ borderRadius: '20px', textDecoration: 'none' }}
|
|
>
|
|
<View width={40} height={40} rounded={12} items="center" justify="center" borderWidth={1} borderColor="var(--border)" $platform-web={{ borderRadius: '12px' }}>
|
|
{p.icon}
|
|
</View>
|
|
<YStack gap={6}>
|
|
<Text fontSize="$5" lineHeight="$5" fontWeight="600" color="var(--foreground)">
|
|
{t(p.name)}
|
|
</Text>
|
|
<Text fontSize="$3" lineHeight="$4" color="var(--muted-foreground)">
|
|
{t(p.line)}
|
|
</Text>
|
|
</YStack>
|
|
<YStack gap={8} pt={12} borderTopWidth={1} borderColor="var(--white-06)">
|
|
{p.gives.map((g) => (
|
|
<XStack key={g} gap={10} items="flex-start">
|
|
<View width={5} height={5} mt={8} shrink={0} rounded={9999} bg="var(--foreground)" $platform-web={{ borderRadius: '9999px' }} />
|
|
<Text fontSize="$2" lineHeight="$3" color="var(--foreground)">
|
|
{t(g)}
|
|
</Text>
|
|
</XStack>
|
|
))}
|
|
</YStack>
|
|
</YStack>
|
|
)
|
|
}
|
|
|
|
export default function Platform() {
|
|
const { t } = useI18n()
|
|
return (
|
|
<Section
|
|
id="platform"
|
|
label="Hanzo.Stack"
|
|
title={t('The unified AI stack.')}
|
|
lede={t('Everything an agent needs, from the first model call to the invoice: one API, one sign-in, one bill. Every part is yours to build on directly.')}
|
|
>
|
|
<View display="grid" width="100%" gap={12} gridTemplateColumns="minmax(0, 1fr)" $sm={{ gridTemplateColumns: 'repeat(2, minmax(0, 1fr))' }} $lg={{ gridTemplateColumns: 'repeat(4, minmax(0, 1fr))', gap: 16 }}>
|
|
{PARTS.map((p) => (
|
|
<Card key={p.name} p={p} />
|
|
))}
|
|
</View>
|
|
<XStack gap={24} flexWrap="wrap">
|
|
<More href="/platform">{t('How the platform fits together')}</More>
|
|
<More href="/cloud">{t('Run it in your cloud')}</More>
|
|
</XStack>
|
|
</Section>
|
|
)
|
|
}
|