import { TextStyle } from 'pixi.js'; /** Design tokens for the canvas UI. Widgets never hard-code colors or sizes. */ export const Theme = { font: 'Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', monoFont: '"JetBrains Mono", Consolas, "Courier New", monospace', fontSize: 13, smallFontSize: 11, rowHeight: 26, controlHeight: 24, padding: 8, gap: 6, radius: 4, colors: { background: 0x1b1d22, panel: 0x24272e, panelHeader: 0x2c3038, border: 0x383c46, control: 0x16181d, controlHover: 0x1e2127, controlActive: 0x2a2e37, accent: 0x4c8dff, accentText: 0xffffff, text: 0xe6e8ee, textMuted: 0x8a90a0, textDisabled: 0x5a5f6d, selection: 0x2b4a80, override: 0xf0b13d, danger: 0xe0553f, viewport: 0x121316, grid: 0x1f2126, gizmo: 0x4c8dff, }, } as const; const styleCache = new Map(); export type TextStyleToken = 'body' | 'bodyBold' | 'muted' | 'small' | 'title' | 'mono' | 'accent'; /** Shared TextStyle instances so Pixi can cache glyph measurements per token. */ export function textStyle(token: TextStyleToken = 'body'): TextStyle { let style = styleCache.get(token); if (style) return style; const base = { fontFamily: Theme.font, fontSize: Theme.fontSize, fill: Theme.colors.text }; switch (token) { case 'body': style = new TextStyle(base); break; case 'bodyBold': style = new TextStyle({ ...base, fontWeight: '600' }); break; case 'muted': style = new TextStyle({ ...base, fill: Theme.colors.textMuted }); break; case 'small': style = new TextStyle({ ...base, fontSize: Theme.smallFontSize, fill: Theme.colors.textMuted }); break; case 'title': style = new TextStyle({ ...base, fontSize: Theme.smallFontSize, fontWeight: '700', letterSpacing: 0.5, fill: Theme.colors.textMuted }); break; case 'mono': style = new TextStyle({ ...base, fontFamily: Theme.monoFont }); break; case 'accent': style = new TextStyle({ ...base, fill: Theme.colors.accentText, fontWeight: '600' }); break; } styleCache.set(token, style); return style; }