import type { IntentId } from '@/lib/interactive-demo/types' /** * Desaturated palette — one set for light, one for dark (P11). * No free hex in demo JSON; app maps intents here. */ export const INTENT_PALETTE_LIGHT: Record = { neutral: '#5c5c5c', highlight: '#5a7a9a', // prune / slate-blue flow: '#4a7d68', cache: '#7a6a8a', compute: '#8a6b4a', output: '#4a7a8a', warning: '#9a6548', } export const INTENT_PALETTE_DARK: Record = { neutral: '#a8a8a8', highlight: '#8aabca', flow: '#7ab89a', cache: '#a898b8', compute: '#c0a080', output: '#7ab0c0', warning: '#d09070', } /** Light: ~35% readable on white; dark: ~20% as spec. */ export const DIM_OPACITY_LIGHT = 0.35 export const DIM_OPACITY_DARK = 0.2 export function intentColor( intent: IntentId | null | undefined, dark: boolean ): string { const palette = dark ? INTENT_PALETTE_DARK : INTENT_PALETTE_LIGHT if (!intent) return palette.neutral return palette[intent] ?? palette.neutral } export function spotlightColor(dark: boolean): string { return intentColor('highlight', dark) } export function dimOpacity(dark: boolean): number { return dark ? DIM_OPACITY_DARK : DIM_OPACITY_LIGHT } export const CIRCLED_NUMBERS = ['①', '②', '③', '④', '⑤', '⑥', '⑦', '⑧', '⑨', '⑩'] as const export function badgeGlyph(index: number): string { if (index >= 1 && index <= CIRCLED_NUMBERS.length) { return CIRCLED_NUMBERS[index - 1]! } return String(index) } /** Writing rule for generation prompts (not enforced in renderer). */ export const SPEAK_WRITING_GUIDE = { maxWordsApprox: 25, sentences: '1–2', note: 'Demo tempo is decided at writing time, not at render.', } as const