// Shared primitives: buttons, badges, dividers, kbd, etc. All formal / hairline. const Btn = ({ children, onClick, variant = 'ghost', size = 'md', icon, iconRight, style, active, title, ...rest }) => { const sizes = { sm: { h: 22, px: 8, fs: 11.5 }, md: { h: 28, px: 10, fs: 12.5 }, lg: { h: 34, px: 14, fs: 13 }, }[size]; const variants = { ghost: { background: active ? 'var(--bg-panel-2)' : 'transparent', color: 'var(--ink)', border: '1px solid transparent', }, line: { background: 'var(--bg-elevated)', color: 'var(--ink)', border: '1px solid var(--line-2)', }, solid: { background: 'var(--ink)', color: 'var(--bg-panel)', border: '1px solid var(--ink)', }, gold: { background: 'var(--gold)', color: '#FBF8F1', border: '1px solid var(--gold)', }, danger: { background: 'transparent', color: 'var(--status-err)', border: '1px solid var(--line-2)', }, }[variant]; return ( ); }; const IconBtn = ({ icon, onClick, active, title, size = 28, style }) => ( ); const Kbd = ({ children }) => ( {children} ); const Badge = ({ children, tone = 'neutral', style }) => { const tones = { neutral: { bg: 'transparent', fg: 'var(--ink-muted)', bd: 'var(--line-2)' }, ok: { bg: 'transparent', fg: 'var(--status-ok)', bd: 'var(--status-ok)' }, warn: { bg: 'transparent', fg: 'var(--status-warn)', bd: 'var(--status-warn)' }, err: { bg: 'transparent', fg: 'var(--status-err)', bd: 'var(--status-err)' }, info: { bg: 'transparent', fg: 'var(--status-info)', bd: 'var(--status-info)' }, gold: { bg: 'var(--gold-soft)', fg: 'var(--gold)', bd: 'var(--gold)' }, solid: { bg: 'var(--ink)', fg: 'var(--bg-panel)', bd: 'var(--ink)' }, }[tone]; return ( {children} ); }; const Divider = ({ vertical, style }) => (
); const Row = ({ children, gap = 8, align = 'center', justify = 'flex-start', style, ...rest }) => (
{children}
); const Col = ({ children, gap = 8, align = 'stretch', style, ...rest }) => (
{children}
); const Label = ({ children, style }) => (
{children}
); // Formal user avatar — square with initials const Avatar = ({ name = '?', tone = 'ink', size = 22, style }) => { const initials = name.split(' ').map(n => n[0]).slice(0, 2).join('').toUpperCase(); const bg = tone === 'gold' ? 'var(--gold)' : tone === 'ink' ? 'var(--ink)' : 'var(--ink-3)'; return (
{initials}
); }; // Number stepper — for panel inputs const NumInput = ({ value, onChange, unit, width = 64, step = 1, min, max, style }) => { const [local, setLocal] = React.useState(value); React.useEffect(() => setLocal(value), [value]); return (
setLocal(e.target.value)} onBlur={() => { const n = parseFloat(local); if (!isNaN(n)) { onChange && onChange(n); } else setLocal(value); }} style={{ width: '100%', height: '100%', padding: '0 6px', background: 'transparent', border: 'none', outline: 'none', fontFamily: 'inherit', fontSize: 11.5, color: 'var(--ink)', }} /> {unit && ( {unit} )}
); }; // Formal group with sidebar label const Field = ({ label, children, style }) => (
{children}
); // Colored swatch tile const Swatch = ({ color, label, size = 20, active, onClick, style }) => (