Files
zeekayandGitHub e20dc71ad8 i18n: /s* to /z*, the app's rooms and settings read their copy through t (#69)
The product pages from /s3 to /zap, /university and its course data,
/trust, /security, /sdks, /support, /tasks, /startups, /sms-opt-in, the
buying specialist, Zen, Kai's guide, Studio, the settings pages, and the
shared sales, solutions, zap and ui pieces. Client code draws with t(),
server components with <T>, copy held as data is marked with phrase()
and drawn through t() where it lands. A link or a figure inside a
sentence is a value, so a translator reorders the whole sentence.

Rebased on main: every page main moved onto Fold or Steps keeps main's
layout, with its words wrapped (label, title, lede, points). Steps now
draws a step's body through t, as it already drew the title. Settings
keeps main's usage panel, refresh, rewards wallet and sign-in wallet
rows, each label wrapped.

Left English: the skills page's install line (a command and two URLs),
and the solution eyebrow and sibling heading built from a kind label.

Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
2026-10-29 23:51:44 -07:00

128 lines
5.0 KiB
TypeScript

'use client'
import { ArrowUpRight, Link2, Rows3, Smartphone, SquareTerminal, TerminalSquare, Unplug } from 'lucide-react'
import { Github } from '@/components/brand'
import { YStack } from '@hanzo/ui'
import {
CardGrid,
Cta,
Page,
PageHero,
Prose,
Section,
type CardItem,
} from '@/components/marketing/page-kit'
import { phrase } from '@hanzo/i18n'
import { T } from '@hanzo/i18n/react'
/**
* Hanzo Tabs, on hanzo.ai.
*
* EVERY CLAIM ON THIS PAGE IS THE PRODUCT'S OWN. The headline, the lede and the
* six facts below are `hanzo-apps/tabs` — its README and the marketing page it
* serves at tabs.hanzo.ai — rather than copy written fresh for this site. Two
* pages describing one product is how a product comes to be described two ways;
* keeping the words identical is what makes the second page a route rather than
* a second opinion.
*
* The mechanism section is the part worth having here at all. Tabs is unusual in
* a way that reads as a gap if it is not explained — it has NO BACKEND, and a
* reader who does not know that `hanzo link` publishes the terminal from the
* machine will read "no server" as something missing rather than as the design.
*
* NO NUMBER HERE IS THIS SITE'S. The scrollback figure is the product page's
* own. The README's "39 tests" is deliberately absent: the repo actually has 70,
* so the README undercounts its own work, and a count that is wrong at the
* source is not one to republish. Sub-90ms belongs to `hanzoai/runtime` and is
* not a fact about Tabs.
*
* Fork-of-a-sandbox, which `hanzoai/runtime` marks "Coming soon", appears
* nowhere on this page.
*/
const MECHANISM: CardItem[] = [
{
icon: Unplug,
title: phrase('There is no backend'),
description:
phrase('Not a small one — none. Running hanzo link on a machine publishes a terminal over a zero-trust tunnel, already authenticated and already gated, and the browser frames it directly. A server here would be a third party to a conversation that has two.'),
},
{
icon: Link2,
title: phrase('A shell is a URL'),
description:
phrase('One link serves as many independent tmux sessions as you ask for, so a build runs in one pane while you work in another. There is no spawn endpoint because none is needed — asking for a name attaches to that session or creates it.'),
},
{
icon: Rows3,
title: phrase('The layout is a tree'),
description:
phrase('A list of rectangles is the shape that looks easier and then cannot answer the only question that matters: close a pane, and who gets the space? With a tree the space belongs to the sibling, so closing replaces the split with that sibling and nothing else moves.'),
},
]
const FACTS: CardItem[] = [
{
icon: SquareTerminal,
title: phrase('Your terminal, not a viewer'),
description:
phrase('A real xterm.js terminal with 10k lines of scrollback, true colour and a cursor that blinks. Type in it, page through it, resize it.'),
},
{
icon: TerminalSquare,
title: phrase('Closing a pane loses nothing'),
description:
phrase('Every shell is a tmux session that keeps running when the browser goes away. Reopen it by name and the scrollback is exactly where you left it.'),
},
{
icon: Smartphone,
title: phrase('Works on a phone'),
description:
phrase('A phone pages; it does not tile. Splits become swipeable pages instead of unreadable slivers, and a key row gives you Esc, Ctrl, Tab and arrows — the keys a soft keyboard does not have.'),
},
{
icon: Github,
title: phrase('Open source, MIT'),
description:
phrase('The layout engine, the terminal client and the site. Fork it, host it, or run it against your own machines.'),
},
]
export default function TabsPage() {
return (
<Page>
<PageHero
eyebrow="Hanzo Tabs"
icon={TerminalSquare}
title={phrase('Keep tabs on your agents.')}
lede={<T>Your coding agents work in shells on real machines. Tabs puts every one of those shells in front of you — split, tiled, and reachable from anywhere you can open a browser.</T>}
>
<Cta href="https://tabs.hanzo.ai" icon={ArrowUpRight}>
<T>Open Tabs</T>
</Cta>
</PageHero>
<Section title={phrase('Link a machine, and it appears here')}>
<Prose>
<p>
<T values={{ command: <strong>hanzo link</strong> }}>{'Run {command} on a laptop, a workstation, a GPU box — anything with a shell.'}</T>{' '}
<T>The machine keeps the connection, and nothing is exposed to the network it sits on. Open Tabs and the shells are there.</T>
</p>
</Prose>
</Section>
<Section title={phrase('Why it is shaped this way')}>
<CardGrid items={MECHANISM} columns={1} />
</Section>
<Section title={phrase('What you get')}>
<CardGrid items={FACTS} columns={2} />
<YStack mt="$5">
<Cta href="https://github.com/hanzo-apps/tabs" icon={Github}>
<T>Read the source</T>
</Cta>
</YStack>
</Section>
</Page>
)
}