// Static figures: hand-drawn SVG, no data and no session behind them.
//
// A lab shows what a method does once the reader presses run; these show what
// it *is*, on first sight and in print. They are deliberately plain: the same
// palette as the plots, unicode rather than KaTeX for the few symbols that
// appear, and one idea per picture.
import type { ReactNode } from 'react'
import { ink } from '../plot/palette.ts'
export const SANS = 'ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif'
export const MONO = 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'
/** Wrapper: a captioned, width-filling SVG with a fixed aspect ratio. */
export function Figure({
width,
height,
caption,
maxWidth,
children,
}: {
width: number
height: number
caption?: string
/** in px; the figure never grows past this even in a wide column */
maxWidth?: number
children: ReactNode
}) {
return (
{caption ? {caption} : null}
)
}
/** Arrowhead markers, one set per figure so the ids never collide. */
export function Arrowheads({ id, colors }: { id: string; colors: string[] }) {
return (
{colors.map((c, i) => (
))}
)
}
export const arrow = (id: string, i: number) => `url(#${id}-ah-${i})`
/** A text label. `anchor` and `baseline` are the usual SVG values. */
export function Label({
x,
y,
children,
fill = ink.secondary,
size = 12,
anchor = 'middle',
baseline = 'middle',
italic = false,
mono = false,
weight,
}: {
x: number
y: number
children: ReactNode
fill?: string
size?: number
anchor?: 'start' | 'middle' | 'end'
baseline?: 'middle' | 'hanging' | 'auto' | 'central'
italic?: boolean
mono?: boolean
weight?: number
}) {
return (
{children}
)
}
/** A grid node. */
export function Node({ x, y, r = 3.2, fill = ink.node, stroke }: { x: number; y: number; r?: number; fill?: string; stroke?: string }) {
return
}