Files
zeekay b3eba940f5 Server components reach @hanzo/ui's barrels through the client boundary
components/code.tsx, a server component, imported Line from the
@hanzo/ui/marketing barrel, which registers every marketing component
(Plans, Faq, Hero, Media, ...) as a client entry of its route; the
chunk of app/(marketing)/[...mod], which uses it, loads on every page,
so every page carried them. components/models/Links.tsx, also a server
component, imported the root barrel, about 150 client components onto
every model page (605 KB against a 400 KB budget, and weight.spec red).
Both now import through components/ui, which re-exports Line too.

With the brand marks, a product page's first-party JavaScript goes from
404 KB to 383, /settings from 403 to 389, /bot from 416 to 397 and
/board from 555 to 479. /kai (405) and /gen (403) stay over: their own
page code and @hanzo/rooms' ProviderMark table.
2026-10-17 08:54:33 -07:00

115 lines
4.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { bundledLanguages, getSingletonHighlighter, type BundledLanguage } from 'shiki'
import { codeTheme } from '@hanzo/ui/core'
import type { LineProps } from '@hanzo/ui/marketing'
import { Line } from '@/components/ui'
/**
* A code sample, highlighted where the page is built.
*
* A server component: shiki runs once, during the export, and the page ships
* the coloured spans — no highlighter reaches the browser. A client component
* that shows code takes this as a prop from the server page that owns the
* sample, the way ProductLanding's `code.source` does.
*
* The theme is @hanzo/ui's `codeTheme` (Dracula dark, GitHub Light light),
* written as `light-dark(<light>, <dark>)`, so the page's `color-scheme` —
* which @hanzo/appearance sets with the theme class — picks the palette. The
* tokens are the theme's. The ground is not Dracula's blue-violet #282a36: the
* site is monochrome, so the dark ground is the design system's own neutral
* `--card` (#0f0f0f) and the light one GitHub Light's white; the frame around
* the block stays the page's.
*
* A language shiki does not ship (or none) is set as plain text in the theme's
* ink rather than failing the build.
*
* Every token reads at AA on the light ground: GitHub Light's orange (#E36209)
* is 3.49:1 on its white, so a light ink under 4.5:1 is darkened, hue kept,
* until it clears it (`legible`). The dark half is Dracula's as shipped.
*/
export type CodeProps = { code: string; lang?: string } & Omit<LineProps, 'children'>
const grammar = (lang?: string): BundledLanguage | 'text' =>
lang && lang in bundledLanguages ? (lang as BundledLanguage) : 'text'
/** `#rgb` or `#rrggbb` as its three channels, 0–255; null for anything else. */
function channels(hex: string): [number, number, number] | null {
const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim())
if (!m) return null
const h = m[1].length === 3 ? [...m[1]].map((c) => c + c).join('') : m[1]
return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)) as [number, number, number]
}
/** WCAG 2 relative luminance. */
const luminance = (c: [number, number, number]) => {
const [r, g, b] = c.map((v) => (v / 255 <= 0.03928 ? v / 255 / 12.92 : ((v / 255 + 0.055) / 1.055) ** 2.4))
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
const ratio = (a: [number, number, number], b: [number, number, number]) => {
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x)
return (hi + 0.05) / (lo + 0.05)
}
/** `ink` darkened in 5% steps, hue kept, until it reads at 4.5:1 on `ground`. */
function legible(ink: string, ground: string): string {
const [c, g] = [channels(ink), channels(ground)]
if (!c || !g) return ink
for (let k = 0; k <= 20; k++) {
const shade = c.map((v) => Math.round(v * (1 - k * 0.05))) as [number, number, number]
if (ratio(shade, g) >= 4.5) return k === 0 ? ink : `#${shade.map((v) => v.toString(16).padStart(2, '0')).join('')}`
}
return ink
}
/** A token's `light-dark(<light>, <dark>)` with its light ink made legible on `ground`. */
const readable = (color: string | undefined, ground: string) => {
const m = color && /^light-dark\(([^,]+),\s*(.+)\)$/.exec(color)
return m ? `light-dark(${legible(m[1], ground)}, ${m[2]})` : color
}
export async function Code({ code, lang, ...props }: CodeProps) {
const language = grammar(lang)
const shiki = await getSingletonHighlighter({ themes: [codeTheme.dark, codeTheme.light], langs: [] })
if (language !== 'text') await shiki.loadLanguage(language)
const dark = shiki.getTheme(codeTheme.dark)
const light = shiki.getTheme(codeTheme.light)
const { tokens } = shiki.codeToTokens(code.replace(/\n$/, ''), {
lang: language,
themes: codeTheme,
defaultColor: 'light-dark()',
})
return (
<Line
render="pre"
display="block"
m={0}
p={16}
overflowX="auto"
whiteSpace="pre"
fontFamily="$mono"
fontSize="$2"
lineHeight="var(--leading-relaxed)"
// inline-style: the light theme's ground and both themes' ink, read from shiki at build; the dark ground is the design system's neutral card.
style={{
backgroundColor: `light-dark(${light.bg}, var(--card))`,
color: `light-dark(${light.fg}, ${dark.fg})`,
}}
{...props}
>
<code>
{tokens.map((line, i) => (
<span key={i}>
{line.map((t, j) => (
// inline-style: each token's colour is the code theme's, computed by shiki per token.
<span key={j} style={{ color: readable(t.htmlStyle?.color, light.bg) }}>
{t.content}
</span>
))}
{i < tokens.length - 1 ? '\n' : null}
</span>
))}
</code>
</Line>
)
}