Files
zeekay 0f5536b707 hanzo.ai reads in the reader's language, and Arabic right to left
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.
2026-10-22 19:54:31 -07:00

81 lines
3.0 KiB
TypeScript

'use client'
/**
* What a section draws in place of api.hanzo.ai's answer: while it is on its
* way (`Loading`), and when it did not come (`Unreachable`). The second says the
* backend did not answer and shows nothing in its place, because the only
* other thing it could show is a number somebody else saw earlier.
*
* `data-api` names the state on the element, so a gate finds it without
* reading its words.
*
* `what` is a phrase naming what is read ("the plans"): it is translated where
* drawn, so a caller writes it inside `phrase()` (@hanzo/i18n) for the catalog
* to hold it, and one it does not hold reads in English.
*/
import type { ReactNode } from 'react'
import { useI18n } from '@hanzo/i18n/react'
import { Spinner, XStack, YStack } from '@hanzo/ui'
import { Line } from '@/components/type'
import type { Reading } from '@/lib/api/use'
/** The document is on its way: one spinner, one line saying what is being read. */
export function Loading({ what }: { what: string }) {
const { t } = useI18n()
return (
<XStack role="status" aria-live="polite" data-api="loading" items="center" justify="center" gap="$3" py="$8">
<Spinner size={16} />
<Line size="sm" tone="muted">
{t('Reading {what} from api.hanzo.ai', { what: t(what) })}
</Line>
</XStack>
)
}
/** The backend did not answer: said plainly, with nothing standing in for the numbers. */
export function Unreachable({ what }: { what: string }) {
const { t } = useI18n()
return (
<YStack
role="alert"
data-api="unreachable"
items="center"
gap="$2"
py="$6"
px="$5"
mx="auto"
maxW={560}
rounded="var(--radius-xl)"
borderWidth={1}
borderColor="$edge"
bg="$panel"
>
<Line weight="600" align="center">
{t('api.hanzo.ai is unreachable')}
</Line>
<Line size="sm" tone="muted" align="center">
{t('This reads {what} live from the backend, and it did not answer. Nothing older is shown instead. Reload to try again.', { what: t(what) })}
</Line>
</YStack>
)
}
/** A read drawn: `children` with its data once here, and the two states above until then. */
export function Ready<T>({ reading, what, children }: { reading: Reading<T>; what: string; children: (data: T) => ReactNode }) {
if (reading.state === 'loading') return <Loading what={what} />
if (reading.state === 'unreachable') return <Unreachable what={what} />
return <>{children(reading.data)}</>
}
/** A figure inside a sentence: an ellipsis while it is read, a dash where it could not be. */
export function Inline<T>({ reading, children }: { reading: Reading<T>; children: (data: T) => ReactNode }) {
const { t } = useI18n()
if (reading.state === 'ready') return <>{children(reading.data)}</>
return (
<span data-api={reading.state} title={reading.state === 'loading' ? t('Reading api.hanzo.ai') : t('api.hanzo.ai is unreachable')}>
{reading.state === 'loading' ? '…' : '—'}
</span>
)
}