@odla-ai/brand 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,16 @@ The package is a **schema + rules + an agent skill + a color engine + a token co
54
54
  - **Tokens compile to the @odla-ai/ui contract.** `compileBrandTokens` maps swatch roles onto `--ui-*` names — always emitting every required token plus the accent-composing derived set, so a scoped preview island never keeps stale root composites — and `renderTokensCss` emits theme-structured CSS (light, dark, invert). Dark is derived algorithmically and contrast re-tuned per token.
55
55
  - **Zero runtime dependencies.** The db client is injected structurally (a real `@odla-ai/db` `AdminDb` satisfies `BrandDb`); `@odla-ai/ai` is a types-only optional peer.
56
56
 
57
+ - **Claude Designs are a first-class input.** A design exported from
58
+ [Claude](https://claude.ai/design) as standalone HTML is a self-contained
59
+ bundle: a loader plus a base64 asset manifest plus the real document. Upload
60
+ it as a `design` asset and the worker parses it in the same write — the
61
+ `--ui-*` tokens it declares (resolved through `var()` chains, so a vendored
62
+ theme re-keyed by a brand block yields the brand's real colors), its
63
+ typefaces, its configurable props, and its heading outline — and stores that
64
+ digest on the row. `design` is the only kind whose content type may be
65
+ `text/html`, and `text/html` is refused for every other kind.
66
+
57
67
  ## Quick start
58
68
 
59
69
  ```ts
@@ -177,6 +187,41 @@ const { light, dark, warnings } = compileBrandTokens({ swatches });
177
187
  const css = renderTokensCss(light, { dark }); // :root / [data-theme="dark"] / media guard
178
188
  ```
179
189
 
190
+ ## Designs
191
+
192
+ Three resolutions of the same design, because none of them is right for every
193
+ reader:
194
+
195
+ | surface | what it gives you |
196
+ |---|---|
197
+ | `GET /books/:id/assets/:aid/preview` | the design itself, proxied under `Content-Security-Policy: sandbox allow-scripts`. No `allow-same-origin`, so it renders in an **opaque origin** and cannot touch the host app's session, storage, or DOM. Proxied rather than redirected to storage, so the sandbox header is the app's to set. |
198
+ | `GET /books/:id/assets/:aid/design` | the stored digest as JSON — tokens, fonts, colors, props, outline, inventory. Kilobytes, not megabytes. |
199
+ | `read_design`, `read_design_source`, `propose_palette_from_design` | the agent's view: the digest as prose, a *window* of the real template for porting exact markup, and the design's own `--ui-*` declarations read back into a palette proposal. |
200
+
201
+ `compileBrandTokens` runs brand book → `--ui-*`. A design authored on the
202
+ [@odla-ai/ui](https://odla.ai/docs/packages/ui) contract arrives with that
203
+ mapping's output already filled in, so `swatchesFromDesignTokens` inverts it —
204
+ built from the forward map's own role table, so the two cannot drift. Only
205
+ literal opaque colors convert; a token left as `color-mix(…)`, a translucent
206
+ `rgba(…)`, or an unresolved `var(…)` is **reported, not guessed at**, because a
207
+ fabricated hex would silently poison the contrast math. The result is an open
208
+ `brand_proposal` a human resolves through the same guarded route as any other.
209
+
210
+ For work outside the conversation, the CLI unpacks a bundle to disk with no
211
+ account and no network:
212
+
213
+ ```sh
214
+ npx odla-ai brand design unpack "Built Not Found Capital.html" --out site/design
215
+ # index.html the design, asset references rewritten to real files — runs offline
216
+ # assets/ every embedded font, image, and script, decoded (gzip inflated)
217
+ # pages/ nested page bundles, for multi-page designs
218
+ # digest.json tokens, props, outline, inventory
219
+ # tokens.css the design's tokens as an @odla-ai/ui theme sheet (light + dark)
220
+ ```
221
+
222
+ That directory is what you point a coding agent at: real files it can read and
223
+ diff, instead of one megabyte of base64.
224
+
180
225
  ## Authority and asset contract
181
226
 
182
227
  - Install `BRAND_AGENT_PROFILE`: agents receive only `brand.read` and
@@ -994,17 +994,18 @@ export {
994
994
  CHART_DELTA_MIN,
995
995
  deriveChartColors,
996
996
  derivePalette,
997
+ DEFAULT_ACCENT_SEED,
998
+ ROLE_TOKEN,
999
+ mapPaletteToTokens,
997
1000
  BRAND_REQUIRED_TOKENS,
998
1001
  BRAND_DERIVED_TOKENS,
999
1002
  BRAND_CHART_TOKENS,
1000
1003
  BRAND_CHAT_TOKENS,
1001
1004
  BRAND_EMITTED_TOKENS,
1002
- DEFAULT_ACCENT_SEED,
1003
- mapPaletteToTokens,
1004
1005
  DARK_FLIP_L_MIN,
1005
1006
  DARK_FLIP_L_MAX,
1006
1007
  deriveDarkTokens,
1007
1008
  renderTokensCss,
1008
1009
  compileBrandTokens
1009
1010
  };
1010
- //# sourceMappingURL=chunk-APLECQBR.js.map
1011
+ //# sourceMappingURL=chunk-52DO72LS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tokens/roles.ts","../src/color/hex.ts","../src/color/convert.ts","../src/color/contrast.ts","../src/color/harmony.ts","../src/color/ramp.ts","../src/color/delta.ts","../src/color/names.ts","../src/color/palette.ts","../src/tokens/map.ts","../src/tokens/dark.ts","../src/tokens/css.ts","../src/tokens/compile.ts"],"sourcesContent":["// The --ui-* custom-property names @odla-ai/brand emits, as data.\n//\n// PROVENANCE: mirrors @odla-ai/ui — the REQUIRED_TOKENS / ACCENT_TOKENS /\n// CHART_TOKENS / CHAT_TOKENS tiers in js/tokens.js, and the\n// :where([data-ui-accent]) re-declaration block in css/tokens.css.\n//\n// Why more than the required tier: a compiled token object doubles as a\n// runtime override-island payload, and CSS custom properties substitute\n// var() at computed-value time ON THE DECLARING ELEMENT. An island that\n// overrides --ui-accent alone would keep the :root-computed --ui-chart-1,\n// --ui-accent-glow, … stale. So besides the required tier the compiler\n// re-declares the whole accent-composing derived family — everything the\n// ui root's :where([data-ui-accent]) block re-declares, plus the remaining\n// chart series and chat roles so charts and chat surfaces recompute against\n// the brand palette too. test/tokens/map.test.ts cross-checks these lists\n// against the real @odla-ai/ui export, so they cannot drift silently.\n\n/**\n * The @odla-ai/ui required tier (js/tokens.js REQUIRED_TOKENS), in the ui\n * contract's order. The compiler always emits a concrete value for every\n * one of these.\n */\nexport const BRAND_REQUIRED_TOKENS = [\n \"--ui-bg\",\n \"--ui-surface\",\n \"--ui-surface-2\",\n \"--ui-text\",\n \"--ui-text-muted\",\n \"--ui-text-faint\",\n \"--ui-border\",\n \"--ui-border-strong\",\n \"--ui-accent\",\n \"--ui-accent-strong\",\n \"--ui-accent-soft\",\n \"--ui-on-accent\",\n \"--ui-good\",\n \"--ui-good-soft\",\n \"--ui-warn\",\n \"--ui-warn-soft\",\n \"--ui-danger\",\n \"--ui-danger-soft\",\n \"--ui-code-bg\",\n \"--ui-code-text\",\n \"--ui-shadow\",\n \"--ui-font-sans\",\n \"--ui-font-serif\",\n \"--ui-font-mono\",\n \"--ui-font-display\",\n] as const;\n\n/**\n * Accent-composing derived roles from the ui defaulted tier. All five\n * accent-family members of the :where([data-ui-accent]) re-declaration set\n * (--ui-accent-glow, --ui-accent-2, --ui-accent-2-soft, --ui-highlight,\n * --ui-focus) plus --ui-shadow-strong, which composes var(--ui-shadow) —\n * a token this compiler re-declares, so the island rule applies to it too.\n */\nexport const BRAND_DERIVED_TOKENS = [\n \"--ui-accent-glow\",\n \"--ui-accent-2\",\n \"--ui-accent-2-soft\",\n \"--ui-highlight\",\n \"--ui-focus\",\n \"--ui-shadow-strong\",\n] as const;\n\n/**\n * Chart roles the compiler emits: the six series slots (real palette\n * swatches when the palette carries them, ui var() compositions otherwise)\n * and the four accent-composing chart roles the :where([data-ui-accent])\n * block re-declares (band, band-strong, flow, glow).\n */\nexport const BRAND_CHART_TOKENS = [\n \"--ui-chart-1\",\n \"--ui-chart-2\",\n \"--ui-chart-3\",\n \"--ui-chart-4\",\n \"--ui-chart-5\",\n \"--ui-chart-6\",\n \"--ui-chart-band\",\n \"--ui-chart-band-strong\",\n \"--ui-chart-flow\",\n \"--ui-chart-glow\",\n] as const;\n\n/**\n * Chat surface roles (js/tokens.js CHAT_TOKENS, complete). Two are in the\n * accent-swap set (user-bg, tool-accent); the other four compose surface /\n * text tokens the compiler also re-declares, so an island stays coherent.\n */\nexport const BRAND_CHAT_TOKENS = [\n \"--ui-chat-user-bg\",\n \"--ui-chat-user-text\",\n \"--ui-chat-assistant-bg\",\n \"--ui-chat-thinking-bg\",\n \"--ui-chat-thinking-text\",\n \"--ui-chat-tool-accent\",\n] as const;\n\n/**\n * Every token the compiler emits, in emission order — also the deterministic\n * declaration order renderTokensCss uses, so golden CSS fixtures are stable.\n */\nexport const BRAND_EMITTED_TOKENS: readonly string[] = [\n ...BRAND_REQUIRED_TOKENS,\n ...BRAND_DERIVED_TOKENS,\n ...BRAND_CHART_TOKENS,\n ...BRAND_CHAT_TOKENS,\n];\n","// Hex color parsing and formatting.\n//\n// Accepts the CSS `#rgb` and `#rrggbb` forms (case-insensitive, surrounding\n// whitespace tolerated) and normalizes to lowercase `#rrggbb`. Alpha forms\n// (`#rgba` / `#rrggbbaa`) are rejected on purpose: the brand color engine\n// works in opaque colors only — translucency is expressed downstream by the\n// token compiler via `color-mix()` strings, never baked into swatches.\n//\n// Errors are plain RangeError so the color engine stays dependency-free;\n// the package's validate layer wraps them in BrandInputError where a typed\n// HTTP-facing error is needed.\n\n/** An sRGB color with channels as fractions in [0, 1]. */\nexport interface Rgb {\n /** Red channel, 0..1. */\n r: number;\n /** Green channel, 0..1. */\n g: number;\n /** Blue channel, 0..1. */\n b: number;\n}\n\n/** Clamps a number into [0, 1]; NaN clamps to 0. */\nexport function clamp01(x: number): number {\n return Number.isNaN(x) ? 0 : x < 0 ? 0 : x > 1 ? 1 : x;\n}\n\nconst HEX3 = /^#[0-9a-f]{3}$/;\nconst HEX6 = /^#[0-9a-f]{6}$/;\nconst HEX_ALPHA = /^#([0-9a-f]{4}|[0-9a-f]{8})$/;\n\n/**\n * Parses `#rgb` or `#rrggbb` (case-insensitive) into channel fractions.\n *\n * @throws RangeError for anything else — named colors, missing `#`, wrong\n * digit counts; alpha forms (`#rgba`/`#rrggbbaa`) get a dedicated message.\n */\nexport function parseHex(hex: string): Rgb {\n const s = hex.trim().toLowerCase();\n if (HEX_ALPHA.test(s)) {\n throw new RangeError(\n `parseHex: alpha hex \"${hex}\" is not supported — use an opaque \"#rrggbb\" value`,\n );\n }\n if (HEX3.test(s)) {\n return {\n r: parseInt(s[1]! + s[1]!, 16) / 255,\n g: parseInt(s[2]! + s[2]!, 16) / 255,\n b: parseInt(s[3]! + s[3]!, 16) / 255,\n };\n }\n if (HEX6.test(s)) {\n return {\n r: parseInt(s.slice(1, 3), 16) / 255,\n g: parseInt(s.slice(3, 5), 16) / 255,\n b: parseInt(s.slice(5, 7), 16) / 255,\n };\n }\n throw new RangeError(`parseHex: expected \"#rgb\" or \"#rrggbb\", got \"${hex}\"`);\n}\n\n/** Formats channel fractions as lowercase `#rrggbb`, clamping each into [0, 1]. */\nexport function toHex(rgb: Rgb): string {\n const ch = (c: number): string =>\n Math.round(clamp01(c) * 255)\n .toString(16)\n .padStart(2, \"0\");\n return `#${ch(rgb.r)}${ch(rgb.g)}${ch(rgb.b)}`;\n}\n\n/** Normalizes any accepted hex form to lowercase `#rrggbb` (throws like parseHex). */\nexport function normalizeHex(hex: string): string {\n return toHex(parseHex(hex));\n}\n","// Color space conversions: sRGB ↔ HSL, sRGB ↔ linear-light, sRGB ↔ OKLab,\n// OKLab ↔ OKLCH — plus an sRGB gamut clamp that reduces OKLCH chroma while\n// preserving lightness and hue.\n//\n// Algorithm sources:\n// - sRGB transfer function: IEC 61966-2-1 (electro-optical form: c ≤ 0.04045\n// → c/12.92, else ((c+0.055)/1.055)^2.4), as restated in CSS Color 4.\n// - HSL: CSS Color 4 § 7 (the hue-sextant algorithm inherited from CSS3\n// Color § 4.2.4).\n// - OKLab: Björn Ottosson, \"A perceptual color space for image processing\"\n// (bottosson.github.io/posts/oklab, 2020; sRGB matrices as updated\n// 2021-01-25): linear sRGB → LMS via M1, per-component cube root, → OKLab\n// via M2, and his published inverse matrices for the way back.\n// - OKLCH: CSS Color 4 § 9.2 — the polar form of OKLab (C = chroma radius,\n// h = hue angle in degrees).\n\nimport { clamp01, parseHex, toHex, type Rgb } from \"./hex\";\n\n/** A color in HSL: hue in degrees [0, 360), saturation and lightness 0..1. */\nexport interface Hsl {\n /** Hue angle in degrees, [0, 360); 0 when achromatic. */\n h: number;\n /** Saturation, 0..1. */\n s: number;\n /** Lightness, 0..1. */\n l: number;\n}\n\n/** A color in OKLab: L 0..1, a/b roughly within ±0.4 for sRGB colors. */\nexport interface Oklab {\n /** Perceived lightness, 0..1. */\n L: number;\n /** Green–red axis. */\n a: number;\n /** Blue–yellow axis. */\n b: number;\n}\n\n/** A color in OKLCH — OKLab in polar form. */\nexport interface Oklch {\n /** Perceived lightness, 0..1. */\n L: number;\n /** Chroma (radius in the a/b plane), ≥ 0. */\n C: number;\n /** Hue angle in degrees, [0, 360); 0 when achromatic. */\n h: number;\n}\n\n/** IEC 61966-2-1 sRGB decoding: gamma-encoded channel → linear-light, both 0..1. */\nexport function srgbToLinear(c: number): number {\n return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\n/** IEC 61966-2-1 sRGB encoding: linear-light channel → gamma-encoded, both 0..1. */\nexport function linearToSrgb(c: number): number {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055;\n}\n\n/** Converts sRGB channel fractions to HSL (CSS Color 4 § 7). */\nexport function rgbToHsl({ r, g, b }: Rgb): Hsl {\n const max = Math.max(r, g, b);\n const min = Math.min(r, g, b);\n const l = (max + min) / 2;\n const d = max - min;\n if (d === 0) return { h: 0, s: 0, l };\n const s = d / (1 - Math.abs(2 * l - 1));\n let h: number;\n if (max === r) h = 60 * (((g - b) / d) % 6);\n else if (max === g) h = 60 * ((b - r) / d + 2);\n else h = 60 * ((r - g) / d + 4);\n return { h: (h + 360) % 360, s, l };\n}\n\n/** Converts HSL to sRGB channel fractions (CSS Color 4 § 7; any hue angle accepted). */\nexport function hslToRgb({ h, s, l }: Hsl): Rgb {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const sextants: ReadonlyArray<readonly [number, number, number]> = [\n [c, x, 0],\n [x, c, 0],\n [0, c, x],\n [0, x, c],\n [x, 0, c],\n [c, 0, x],\n ];\n const [r, g, b] = sextants[Math.floor(hue / 60) % 6]!;\n return { r: r + m, g: g + m, b: b + m };\n}\n\n/** Converts gamma-encoded sRGB to OKLab (Ottosson M1 → cbrt → M2). */\nexport function rgbToOklab({ r, g, b }: Rgb): Oklab {\n const R = srgbToLinear(r);\n const G = srgbToLinear(g);\n const B = srgbToLinear(b);\n const l = Math.cbrt(0.4122214708 * R + 0.5363325363 * G + 0.0514459929 * B);\n const m = Math.cbrt(0.2119034982 * R + 0.6806995451 * G + 0.1073969566 * B);\n const s = Math.cbrt(0.0883024619 * R + 0.2817188376 * G + 0.6299787005 * B);\n return {\n L: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,\n a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,\n b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,\n };\n}\n\n/**\n * Converts OKLab to gamma-encoded sRGB (Ottosson's inverse matrices).\n * Out-of-gamut inputs yield channels outside [0, 1] — feed the OKLCH form\n * through {@link clampToGamut} first when hue fidelity matters (toHex would\n * clip per channel, which distorts hue).\n */\nexport function oklabToRgb({ L, a, b }: Oklab): Rgb {\n const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;\n const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;\n const s = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;\n return {\n r: linearToSrgb(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s),\n g: linearToSrgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s),\n b: linearToSrgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s),\n };\n}\n\n/** OKLab → OKLCH (polar form). Hue is 0 for achromatic colors (C ≈ 0). */\nexport function oklabToOklch({ L, a, b }: Oklab): Oklch {\n const C = Math.hypot(a, b);\n const h = C < 1e-9 ? 0 : ((Math.atan2(b, a) * 180) / Math.PI + 360) % 360;\n return { L, C, h };\n}\n\n/** OKLCH → OKLab (rectangular form). */\nexport function oklchToOklab({ L, C, h }: Oklch): Oklab {\n const rad = (h * Math.PI) / 180;\n return { L, a: C * Math.cos(rad), b: C * Math.sin(rad) };\n}\n\n/** Convenience: hex → OKLCH (throws like parseHex on bad input). */\nexport function hexToOklch(hex: string): Oklch {\n return oklabToOklch(rgbToOklab(parseHex(hex)));\n}\n\n/**\n * Convenience: OKLCH → lowercase `#rrggbb`. Out-of-gamut input is clipped\n * per channel by toHex; call {@link clampToGamut} first to reduce chroma\n * instead (preserving hue) when the input may be out of gamut.\n */\nexport function oklchToHex(lch: Oklch): string {\n return toHex(oklabToRgb(oklchToOklab(lch)));\n}\n\nconst GAMUT_EPS = 1e-6;\n\n/** True when every sRGB channel is within [0, 1] (tiny epsilon for float noise). */\nexport function inSrgbGamut({ r, g, b }: Rgb): boolean {\n const ok = (c: number): boolean => c >= -GAMUT_EPS && c <= 1 + GAMUT_EPS;\n return ok(r) && ok(g) && ok(b);\n}\n\n/**\n * Brings an OKLCH color into the sRGB gamut by reducing chroma only —\n * lightness and hue are preserved exactly (binary search, 24 iterations,\n * chroma precision well under one 8-bit quantum). L is clamped into [0, 1]\n * first; C = 0 (a pure gray) is always representable, so the search always\n * lands in gamut.\n */\nexport function clampToGamut(lch: Oklch): Oklch {\n const L = clamp01(lch.L);\n const h = lch.h;\n if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: lch.C, h })))) return { L, C: lch.C, h };\n let lo = 0;\n let hi = lch.C;\n for (let i = 0; i < 24; i++) {\n const mid = (lo + hi) / 2;\n if (inSrgbGamut(oklabToRgb(oklchToOklab({ L, C: mid, h })))) lo = mid;\n else hi = mid;\n }\n return { L, C: lo, h };\n}\n","// WCAG 2.1 contrast — relative luminance, contrast ratio, and AA/AAA checks\n// per technique G18 and success criteria 1.4.3 / 1.4.6 / 1.4.11.\n//\n// relativeLuminance: Y = 0.2126·R + 0.7152·G + 0.0722·B over linear-light\n// sRGB channels. Channels are linearized with the IEC 61966-2-1 constant\n// (0.04045) rather than WCAG's 0.03928; the two never disagree for 8-bit\n// channel values (no n/255 falls between them) and CSS Color 4 standardizes\n// on 0.04045.\n// contrastRatio: (L1 + 0.05) / (L2 + 0.05) with the lighter luminance on\n// top — white on black is exactly 21:1, a color against itself 1:1.\n\nimport { parseHex } from \"./hex\";\nimport { srgbToLinear } from \"./convert\";\n\n/** Contrast contexts with distinct WCAG thresholds. */\nexport type ContrastKind = \"text\" | \"large-text\" | \"ui\";\n\n/** WCAG 2.1 relative luminance of a hex color: 0 = black, 1 = white. */\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = parseHex(hex);\n return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);\n}\n\n/** WCAG 2.1 contrast ratio between two hex colors, in [1, 21]; order-independent. */\nexport function contrastRatio(hexA: string, hexB: string): number {\n const la = relativeLuminance(hexA);\n const lb = relativeLuminance(hexB);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\nconst AA: Record<ContrastKind, number> = { text: 4.5, \"large-text\": 3, ui: 3 };\nconst AAA: Record<Exclude<ContrastKind, \"ui\">, number> = { text: 7, \"large-text\": 4.5 };\n\n/**\n * WCAG 2.1 level-AA check for a contrast ratio: 4.5:1 for body text\n * (SC 1.4.3), 3:1 for large text, 3:1 for UI components and graphical\n * objects (SC 1.4.11).\n */\nexport function meetsAA(ratio: number, kind: ContrastKind = \"text\"): boolean {\n return ratio >= AA[kind];\n}\n\n/**\n * WCAG 2.1 level-AAA check for a contrast ratio (SC 1.4.6): 7:1 for body\n * text, 4.5:1 for large text. WCAG defines no AAA bar for non-text UI, so\n * \"ui\" is not an accepted kind here.\n */\nexport function meetsAAA(ratio: number, kind: Exclude<ContrastKind, \"ui\"> = \"text\"): boolean {\n return ratio >= AAA[kind];\n}\n\n/**\n * Default {@link pickTextOn} candidates: white and a near-black chosen so\n * that the better of the two clears 4.5:1 against ANY background. The worst\n * case is a mid-gray backdrop (luminance ≈ 0.18), where this pair still\n * yields ≈ 4.51:1; a softer near-black (e.g. #111111) would drop that floor\n * below 4.5.\n */\nexport const PICK_TEXT_DEFAULT_CANDIDATES: readonly string[] = [\"#ffffff\", \"#050505\"];\n\n/**\n * Picks the candidate with the highest contrast ratio against `bgHex`\n * (earliest candidate wins ties). With the default candidates the winner is\n * always ≥ 4.5:1 — see {@link PICK_TEXT_DEFAULT_CANDIDATES}.\n *\n * @throws RangeError when `candidates` is empty.\n */\nexport function pickTextOn(\n bgHex: string,\n candidates: readonly string[] = PICK_TEXT_DEFAULT_CANDIDATES,\n): string {\n if (candidates.length === 0) throw new RangeError(\"pickTextOn: candidates must be non-empty\");\n let best = candidates[0]!;\n let bestRatio = -1;\n for (const candidate of candidates) {\n const ratio = contrastRatio(bgHex, candidate);\n if (ratio > bestRatio) {\n best = candidate;\n bestRatio = ratio;\n }\n }\n return best;\n}\n","// Classic color-harmony companions, computed as OKLCH hue rotations at\n// constant lightness and chroma so companions keep the seed's perceived\n// lightness and vividness (the same rotations in HSL shift both wildly —\n// compare HSL yellow vs blue at \"50%\"). Results are chroma-clamped into the\n// sRGB gamut, which preserves hue and lightness exactly.\n//\n// Angles follow the traditional color-wheel definitions: complementary 180°,\n// analogous ±30°, triadic ±120°, split-complementary ±150°, tetradic\n// (square) +90°/180°/270°. Hue math per CSS Color 4 § 9.2 OKLCH.\n\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\n\n/**\n * Rotates a color's OKLCH hue by `degrees` (any sign/magnitude), keeping\n * lightness and chroma constant, then chroma-clamps into sRGB. Rotating an\n * achromatic color is a no-op up to 8-bit rounding (its chroma is ~0).\n */\nexport function rotateHue(hex: string, degrees: number): string {\n const { L, C, h } = hexToOklch(hex);\n return oklchToHex(clampToGamut({ L, C, h: (((h + degrees) % 360) + 360) % 360 }));\n}\n\n/** The 180° opposite on the OKLCH hue wheel. */\nexport function complementary(hex: string): string {\n return rotateHue(hex, 180);\n}\n\n/** The two neighbors at ±`angle` (default 30°), as [minus, plus]. */\nexport function analogous(hex: string, angle = 30): [string, string] {\n return [rotateHue(hex, -angle), rotateHue(hex, angle)];\n}\n\n/** The two colors completing an equilateral triad (±120°), as [minus, plus]. */\nexport function triadic(hex: string): [string, string] {\n return [rotateHue(hex, -120), rotateHue(hex, 120)];\n}\n\n/** The two colors flanking the complement (±150°), as [minus, plus]. */\nexport function splitComplementary(hex: string): [string, string] {\n return [rotateHue(hex, -150), rotateHue(hex, 150)];\n}\n\n/** The three colors completing a square (+90°, +180°, +270°). */\nexport function tetradic(hex: string): [string, string, string] {\n return [rotateHue(hex, 90), rotateHue(hex, 180), rotateHue(hex, 270)];\n}\n\n/**\n * A monochrome chroma scale: `steps` colors at the seed's lightness and hue,\n * fading chroma linearly from the seed's own chroma down to 0 (a pure gray).\n * The first entry is the seed itself (normalized).\n *\n * @throws RangeError when `steps` is not an integer ≥ 2.\n */\nexport function monochrome(hex: string, steps = 5): string[] {\n if (!Number.isInteger(steps) || steps < 2) {\n throw new RangeError(`monochrome: steps must be an integer >= 2, got ${steps}`);\n }\n const { L, C, h } = hexToOklch(hex);\n const out: string[] = [];\n for (let i = 0; i < steps; i++) {\n out.push(oklchToHex(clampToGamut({ L, C: C * (1 - i / (steps - 1)), h })));\n }\n return out;\n}\n","// Tint/shade ramps and predicate-driven lightness adjustment, both in OKLCH\n// so steps are perceptually even (equal OKLab ΔL per step; OKLab per Björn\n// Ottosson — see convert.ts for the matrices and citations).\n//\n// tintShadeRamp walks lightness from RAMP_L_MAX down to RAMP_L_MIN with a\n// chroma taper: full chroma mid-ramp, fading toward both ends. Very light\n// tints and very dark shades can't hold much chroma in sRGB anyway, and a\n// deliberate taper reads better than the hard cut the gamut clamp would\n// otherwise apply.\n//\n// adjustLightnessUntil binary-searches lightness (constant chroma and hue,\n// gamut-clamped) for the value nearest the input that satisfies a caller\n// predicate — the workhorse behind \"darken this accent until it clears\n// 4.5:1 on the page background\".\n\nimport { normalizeHex } from \"./hex\";\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\n\n/** Lightest OKLab L emitted by {@link tintShadeRamp} (first step). */\nexport const RAMP_L_MAX = 0.96;\n\n/** Darkest OKLab L emitted by {@link tintShadeRamp} (last step). */\nexport const RAMP_L_MIN = 0.27;\n\n/** Fraction of the seed's chroma kept at the ramp's endpoints. */\nconst CHROMA_FLOOR = 0.25;\n\n/**\n * A light-to-dark ramp of `steps` colors (default 9) built from the seed's\n * hue and chroma: OKLab lightness runs linearly from {@link RAMP_L_MAX} down\n * to {@link RAMP_L_MIN}, and chroma tapers from 100% of the seed's chroma at\n * mid-ramp to 25% at both ends (then gamut-clamps, preserving hue).\n *\n * @throws RangeError when `steps` is not an integer ≥ 2.\n */\nexport function tintShadeRamp(hex: string, steps = 9): string[] {\n if (!Number.isInteger(steps) || steps < 2) {\n throw new RangeError(`tintShadeRamp: steps must be an integer >= 2, got ${steps}`);\n }\n const { C, h } = hexToOklch(hex);\n const out: string[] = [];\n for (let i = 0; i < steps; i++) {\n const t = i / (steps - 1);\n const L = RAMP_L_MAX + (RAMP_L_MIN - RAMP_L_MAX) * t;\n const taper = CHROMA_FLOOR + (1 - CHROMA_FLOOR) * (1 - Math.abs(2 * t - 1));\n out.push(oklchToHex(clampToGamut({ L, C: C * taper, h })));\n }\n return out;\n}\n\n/** Direction {@link adjustLightnessUntil} may move lightness. */\nexport type LightnessDirection = \"lighten\" | \"darken\";\n\nconst SCAN_STEPS = 16;\nconst BISECT_STEPS = 22;\n\n/**\n * Moves a color's OKLCH lightness toward white (\"lighten\") or black\n * (\"darken\") until `predicate(hex)` holds, returning the passing color\n * nearest the input (chroma and hue constant, gamut-clamped per step).\n *\n * Returns the input itself (normalized) when it already passes, and null\n * when no lightness in that direction satisfies the predicate. Work is\n * bounded: a 16-step coarse scan plus 22 bisection rounds. The predicate is\n * always evaluated on exact 8-bit hex strings, including the returned one.\n */\nexport function adjustLightnessUntil(\n hex: string,\n predicate: (hex: string) => boolean,\n direction: LightnessDirection,\n): string | null {\n const start = normalizeHex(hex);\n if (predicate(start)) return start;\n const { L, C, h } = hexToOklch(start);\n const bound = direction === \"lighten\" ? 1 : 0;\n const at = (l: number): string => oklchToHex(clampToGamut({ L: l, C, h }));\n let lastFail = L;\n let firstPass = Number.NaN;\n for (let i = 1; i <= SCAN_STEPS; i++) {\n const l = L + ((bound - L) * i) / SCAN_STEPS;\n if (predicate(at(l))) {\n firstPass = l;\n break;\n }\n lastFail = l;\n }\n if (Number.isNaN(firstPass)) return null;\n for (let i = 0; i < BISECT_STEPS; i++) {\n const mid = (lastFail + firstPass) / 2;\n if (predicate(at(mid))) firstPass = mid;\n else lastFail = mid;\n }\n return at(firstPass);\n}\n","// ΔEOK — the OKLab color-difference metric adopted by CSS Color 4 (§ \"the\n// deltaEOK function\"): plain Euclidean distance in OKLab coordinates.\n// Scale intuition: ~0.02 is roughly one just-noticeable difference, and\n// white ↔ black is exactly 1.\n\nimport { parseHex } from \"./hex\";\nimport { rgbToOklab, type Oklab } from \"./convert\";\n\n/** Euclidean distance between two OKLab coordinates (CSS Color 4 ΔEOK). */\nexport function deltaEOKLab(x: Oklab, y: Oklab): number {\n return Math.hypot(x.L - y.L, x.a - y.a, x.b - y.b);\n}\n\n/**\n * ΔEOK between two hex colors. 0 = identical; ~0.02 ≈ one just-noticeable\n * difference; white ↔ black = 1. Symmetric in its arguments.\n *\n * @throws RangeError on malformed hex (see parseHex).\n */\nexport function deltaEOK(hexA: string, hexB: string): number {\n return deltaEOKLab(rgbToOklab(parseHex(hexA)), rgbToOklab(parseHex(hexB)));\n}\n","// The 148 CSS named colors — CSS Color 4 § 6.1 \"Named colors\" (the CSS3\n// keyword list plus rebeccapurple), with the spec's exact hex values —\n// and nearest-name lookup by ΔEOK (CSS Color 4's OKLab distance metric,\n// see delta.ts). Alphabetical order; gray/grey-style aliases are separate\n// entries sharing one hex.\n\nimport { parseHex } from \"./hex\";\nimport { rgbToOklab, type Oklab } from \"./convert\";\nimport { deltaEOKLab } from \"./delta\";\n\n/** One CSS named color: the keyword and its spec hex value. */\nexport interface NamedColor {\n /** The CSS keyword, e.g. \"rebeccapurple\". */\n name: string;\n /** The spec's `#rrggbb` value, lowercase. */\n hex: string;\n}\n\nconst TABLE: ReadonlyArray<readonly [string, string]> = [\n [\"aliceblue\", \"#f0f8ff\"],\n [\"antiquewhite\", \"#faebd7\"],\n [\"aqua\", \"#00ffff\"],\n [\"aquamarine\", \"#7fffd4\"],\n [\"azure\", \"#f0ffff\"],\n [\"beige\", \"#f5f5dc\"],\n [\"bisque\", \"#ffe4c4\"],\n [\"black\", \"#000000\"],\n [\"blanchedalmond\", \"#ffebcd\"],\n [\"blue\", \"#0000ff\"],\n [\"blueviolet\", \"#8a2be2\"],\n [\"brown\", \"#a52a2a\"],\n [\"burlywood\", \"#deb887\"],\n [\"cadetblue\", \"#5f9ea0\"],\n [\"chartreuse\", \"#7fff00\"],\n [\"chocolate\", \"#d2691e\"],\n [\"coral\", \"#ff7f50\"],\n [\"cornflowerblue\", \"#6495ed\"],\n [\"cornsilk\", \"#fff8dc\"],\n [\"crimson\", \"#dc143c\"],\n [\"cyan\", \"#00ffff\"],\n [\"darkblue\", \"#00008b\"],\n [\"darkcyan\", \"#008b8b\"],\n [\"darkgoldenrod\", \"#b8860b\"],\n [\"darkgray\", \"#a9a9a9\"],\n [\"darkgreen\", \"#006400\"],\n [\"darkgrey\", \"#a9a9a9\"],\n [\"darkkhaki\", \"#bdb76b\"],\n [\"darkmagenta\", \"#8b008b\"],\n [\"darkolivegreen\", \"#556b2f\"],\n [\"darkorange\", \"#ff8c00\"],\n [\"darkorchid\", \"#9932cc\"],\n [\"darkred\", \"#8b0000\"],\n [\"darksalmon\", \"#e9967a\"],\n [\"darkseagreen\", \"#8fbc8f\"],\n [\"darkslateblue\", \"#483d8b\"],\n [\"darkslategray\", \"#2f4f4f\"],\n [\"darkslategrey\", \"#2f4f4f\"],\n [\"darkturquoise\", \"#00ced1\"],\n [\"darkviolet\", \"#9400d3\"],\n [\"deeppink\", \"#ff1493\"],\n [\"deepskyblue\", \"#00bfff\"],\n [\"dimgray\", \"#696969\"],\n [\"dimgrey\", \"#696969\"],\n [\"dodgerblue\", \"#1e90ff\"],\n [\"firebrick\", \"#b22222\"],\n [\"floralwhite\", \"#fffaf0\"],\n [\"forestgreen\", \"#228b22\"],\n [\"fuchsia\", \"#ff00ff\"],\n [\"gainsboro\", \"#dcdcdc\"],\n [\"ghostwhite\", \"#f8f8ff\"],\n [\"gold\", \"#ffd700\"],\n [\"goldenrod\", \"#daa520\"],\n [\"gray\", \"#808080\"],\n [\"green\", \"#008000\"],\n [\"greenyellow\", \"#adff2f\"],\n [\"grey\", \"#808080\"],\n [\"honeydew\", \"#f0fff0\"],\n [\"hotpink\", \"#ff69b4\"],\n [\"indianred\", \"#cd5c5c\"],\n [\"indigo\", \"#4b0082\"],\n [\"ivory\", \"#fffff0\"],\n [\"khaki\", \"#f0e68c\"],\n [\"lavender\", \"#e6e6fa\"],\n [\"lavenderblush\", \"#fff0f5\"],\n [\"lawngreen\", \"#7cfc00\"],\n [\"lemonchiffon\", \"#fffacd\"],\n [\"lightblue\", \"#add8e6\"],\n [\"lightcoral\", \"#f08080\"],\n [\"lightcyan\", \"#e0ffff\"],\n [\"lightgoldenrodyellow\", \"#fafad2\"],\n [\"lightgray\", \"#d3d3d3\"],\n [\"lightgreen\", \"#90ee90\"],\n [\"lightgrey\", \"#d3d3d3\"],\n [\"lightpink\", \"#ffb6c1\"],\n [\"lightsalmon\", \"#ffa07a\"],\n [\"lightseagreen\", \"#20b2aa\"],\n [\"lightskyblue\", \"#87cefa\"],\n [\"lightslategray\", \"#778899\"],\n [\"lightslategrey\", \"#778899\"],\n [\"lightsteelblue\", \"#b0c4de\"],\n [\"lightyellow\", \"#ffffe0\"],\n [\"lime\", \"#00ff00\"],\n [\"limegreen\", \"#32cd32\"],\n [\"linen\", \"#faf0e6\"],\n [\"magenta\", \"#ff00ff\"],\n [\"maroon\", \"#800000\"],\n [\"mediumaquamarine\", \"#66cdaa\"],\n [\"mediumblue\", \"#0000cd\"],\n [\"mediumorchid\", \"#ba55d3\"],\n [\"mediumpurple\", \"#9370db\"],\n [\"mediumseagreen\", \"#3cb371\"],\n [\"mediumslateblue\", \"#7b68ee\"],\n [\"mediumspringgreen\", \"#00fa9a\"],\n [\"mediumturquoise\", \"#48d1cc\"],\n [\"mediumvioletred\", \"#c71585\"],\n [\"midnightblue\", \"#191970\"],\n [\"mintcream\", \"#f5fffa\"],\n [\"mistyrose\", \"#ffe4e1\"],\n [\"moccasin\", \"#ffe4b5\"],\n [\"navajowhite\", \"#ffdead\"],\n [\"navy\", \"#000080\"],\n [\"oldlace\", \"#fdf5e6\"],\n [\"olive\", \"#808000\"],\n [\"olivedrab\", \"#6b8e23\"],\n [\"orange\", \"#ffa500\"],\n [\"orangered\", \"#ff4500\"],\n [\"orchid\", \"#da70d6\"],\n [\"palegoldenrod\", \"#eee8aa\"],\n [\"palegreen\", \"#98fb98\"],\n [\"paleturquoise\", \"#afeeee\"],\n [\"palevioletred\", \"#db7093\"],\n [\"papayawhip\", \"#ffefd5\"],\n [\"peachpuff\", \"#ffdab9\"],\n [\"peru\", \"#cd853f\"],\n [\"pink\", \"#ffc0cb\"],\n [\"plum\", \"#dda0dd\"],\n [\"powderblue\", \"#b0e0e6\"],\n [\"purple\", \"#800080\"],\n [\"rebeccapurple\", \"#663399\"],\n [\"red\", \"#ff0000\"],\n [\"rosybrown\", \"#bc8f8f\"],\n [\"royalblue\", \"#4169e1\"],\n [\"saddlebrown\", \"#8b4513\"],\n [\"salmon\", \"#fa8072\"],\n [\"sandybrown\", \"#f4a460\"],\n [\"seagreen\", \"#2e8b57\"],\n [\"seashell\", \"#fff5ee\"],\n [\"sienna\", \"#a0522d\"],\n [\"silver\", \"#c0c0c0\"],\n [\"skyblue\", \"#87ceeb\"],\n [\"slateblue\", \"#6a5acd\"],\n [\"slategray\", \"#708090\"],\n [\"slategrey\", \"#708090\"],\n [\"snow\", \"#fffafa\"],\n [\"springgreen\", \"#00ff7f\"],\n [\"steelblue\", \"#4682b4\"],\n [\"tan\", \"#d2b48c\"],\n [\"teal\", \"#008080\"],\n [\"thistle\", \"#d8bfd8\"],\n [\"tomato\", \"#ff6347\"],\n [\"turquoise\", \"#40e0d0\"],\n [\"violet\", \"#ee82ee\"],\n [\"wheat\", \"#f5deb3\"],\n [\"white\", \"#ffffff\"],\n [\"whitesmoke\", \"#f5f5f5\"],\n [\"yellow\", \"#ffff00\"],\n [\"yellowgreen\", \"#9acd32\"],\n];\n\n/**\n * All 148 CSS named colors in alphabetical order. Aliases (aqua/cyan,\n * fuchsia/magenta, the gray/grey pairs) are separate entries sharing a hex.\n */\nexport const CSS_NAMED_COLORS: readonly NamedColor[] = TABLE.map(([name, hex]) => ({\n name,\n hex,\n}));\n\n/** A nearest-name match: the keyword, its spec hex, and the ΔEOK distance. */\nexport interface NearestNamedColor extends NamedColor {\n /** ΔEOK from the query color to this named color (0 = exact hit). */\n deltaEOK: number;\n}\n\nlet labCache: Oklab[] | null = null;\n\n/**\n * The CSS named color perceptually closest to `hex`, by ΔEOK in OKLab.\n * Exact hits return distance 0. Ties — including shared-hex aliases like\n * aqua/cyan — go to the alphabetically first keyword. As a rough guide,\n * distances ≲ 0.02 are visually indistinguishable and ≳ 0.1 is only a loose\n * \"same family\" match.\n *\n * @throws RangeError on malformed hex (see parseHex).\n */\nexport function nearestNamedColor(hex: string): NearestNamedColor {\n const target = rgbToOklab(parseHex(hex));\n labCache ??= TABLE.map(([, value]) => rgbToOklab(parseHex(value)));\n let bestIdx = 0;\n let bestD = Infinity;\n for (let i = 0; i < labCache.length; i++) {\n const d = deltaEOKLab(target, labCache[i]!);\n if (d < bestD) {\n bestD = d;\n bestIdx = i;\n }\n }\n const [name, value] = TABLE[bestIdx]!;\n return { name, hex: value, deltaEOK: bestD };\n}\n","// Full-palette derivation from a single seed color. All deterministic —\n// no randomness, no clock — so the same seed always yields the same\n// palette (proposals are reviewable and reproducible).\n//\n// Strategy:\n// - primary: the seed itself (normalized).\n// - secondary / highlight: classic harmony rotations of the seed in OKLCH\n// (complementary 180°, triadic ±120°, split-complementary ±150°).\n// secondary maximizes ΔEOK from the primary; highlight maximizes the\n// minimum ΔEOK to both, so the three accents spread apart. Near-achromatic\n// seeds (C < 0.02) have no usable hue, so rotations run from a synthetic\n// chromatic anchor at ANCHOR_HUE instead (a gray seed still deserves\n// real accents).\n// - good / warn / danger: hue-anchored at 145° / 85° / 25° OKLCH (green /\n// amber / red), darkened via adjustLightnessUntil until they clear WCAG's\n// 3:1 non-text bar (SC 1.4.11) against the derived background.\n// - chart set: hue rotations at a constant, gamut-safe (CHART_L, CHART_C)\n// chosen so every hue fits sRGB without chroma clamping. Adjacent 60°\n// hues then sit 2·C·sin(30°) = C apart in OKLab, so with C = 0.115 the\n// pairwise ΔEOK floor CHART_DELTA_MIN = 0.1 holds by construction.\n// - neutrals bg / surface / neutral / text: a near-achromatic ladder tinted\n// with the seed hue (C ≤ 0.012), L 0.985 → 0.24; text vs bg lands well\n// past 7:1 (AAA).\n\nimport type { Swatch, SwatchRole } from \"../types\";\nimport { normalizeHex } from \"./hex\";\nimport { clampToGamut, hexToOklch, oklchToHex } from \"./convert\";\nimport { contrastRatio } from \"./contrast\";\nimport { rotateHue } from \"./harmony\";\nimport { adjustLightnessUntil } from \"./ramp\";\nimport { deltaEOK } from \"./delta\";\nimport { nearestNamedColor } from \"./names\";\n\n/**\n * Documented floor for pairwise ΔEOK between derived chart colors. The\n * default chart constants guarantee it by construction (see file header);\n * deriveChartColors also enforces it as a filter.\n */\nexport const CHART_DELTA_MIN = 0.1;\n\n/** Chart lightness: chosen with CHART_C so all hues are in-gamut (min max-chroma over hues ≈ 0.119 at L = 0.7). */\nconst CHART_L = 0.7;\n/** Chart chroma: gamut-safe at CHART_L for every hue, so no clamping ever shifts a chart color. */\nconst CHART_C = 0.115;\n/** Hue used when a seed is too gray to have one (a mid blue). */\nconst ANCHOR_HUE = 250;\n/** Below this OKLCH chroma a seed is treated as achromatic. */\nconst ACHROMATIC_C = 0.02;\n/** Status swatch anchors (OKLCH hue degrees): green / amber / red. */\nconst STATUS_HUES = { good: 145, warn: 85, danger: 25 } as const;\nconst STATUS_L = 0.7;\nconst STATUS_C = 0.14;\n/** Harmony rotations tried for secondary/highlight, in tie-break order. */\nconst ROTATIONS = [180, 120, -120, 150, -150] as const;\n\n/** Options for {@link deriveChartColors}. */\nexport interface DeriveChartOptions {\n /** How many colors to return, 1..12 (default 6). */\n count?: number;\n /** Pairwise ΔEOK floor (default {@link CHART_DELTA_MIN}). Best-effort above the default. */\n deltaMin?: number;\n}\n\n/**\n * Derives categorical chart colors from a seed: hue rotations (60° grid,\n * then 30° offsets) around the seed's hue at a constant, gamut-safe\n * lightness/chroma. Candidates are kept only if they stay at least\n * `deltaMin` ΔEOK from every accepted color; if the requested floor is\n * unattainable for `count` colors, remaining distinct candidates fill the\n * set in hue order so callers always get `count` colors back.\n *\n * @throws RangeError when `count` is not an integer in 1..12, or on\n * malformed hex.\n */\nexport function deriveChartColors(seedHex: string, opts: DeriveChartOptions = {}): string[] {\n const count = opts.count ?? 6;\n const deltaMin = opts.deltaMin ?? CHART_DELTA_MIN;\n if (!Number.isInteger(count) || count < 1 || count > 12) {\n throw new RangeError(`deriveChartColors: count must be an integer in 1..12, got ${count}`);\n }\n const lch = hexToOklch(normalizeHex(seedHex));\n const base = lch.C < ACHROMATIC_C ? ANCHOR_HUE : lch.h;\n const offsets = [0, 60, 120, 180, 240, 300, 30, 90, 150, 210, 270, 330];\n const candidates = offsets.map((deg) =>\n oklchToHex(clampToGamut({ L: CHART_L, C: CHART_C, h: (base + deg) % 360 })),\n );\n const picked: string[] = [];\n for (const hex of candidates) {\n if (picked.length >= count) break;\n if (picked.includes(hex)) continue;\n if (picked.every((p) => deltaEOK(p, hex) >= deltaMin)) picked.push(hex);\n }\n for (const hex of candidates) {\n if (picked.length >= count) break;\n if (!picked.includes(hex)) picked.push(hex);\n }\n return picked;\n}\n\n/** Options for {@link derivePalette}. */\nexport interface DerivePaletteOptions {\n /** Attach nearest-CSS-keyword `name`s to every swatch (default true). */\n names?: boolean;\n}\n\n/**\n * Derives a complete brand palette from one seed color: one swatch each for\n * primary, secondary, highlight, good, warn, danger, bg, surface, text and\n * neutral, plus six chart swatches (16 total) — see the file header for the\n * derivation strategy and its WCAG guarantees.\n *\n * @throws RangeError on malformed seed hex (see parseHex).\n */\nexport function derivePalette(seedHex: string, opts: DerivePaletteOptions = {}): Swatch[] {\n const withNames = opts.names !== false;\n const seed = normalizeHex(seedHex);\n const seedLch = hexToOklch(seed);\n const achromatic = seedLch.C < ACHROMATIC_C;\n const hue = achromatic ? ANCHOR_HUE : seedLch.h;\n\n const neutralAt = (L: number, C: number): string => oklchToHex(clampToGamut({ L, C, h: hue }));\n const bg = neutralAt(0.985, 0.005);\n const surface = neutralAt(0.955, 0.008);\n const text = neutralAt(0.24, 0.012);\n const neutral = neutralAt(0.62, 0.012);\n\n const accentBase = achromatic\n ? oklchToHex(\n clampToGamut({ L: Math.min(Math.max(seedLch.L, 0.45), 0.75), C: CHART_C, h: ANCHOR_HUE }),\n )\n : seed;\n const cands = ROTATIONS.map((deg) => {\n const hex = rotateHue(accentBase, deg);\n return { deg, hex, d: deltaEOK(seed, hex) };\n });\n let secondary = cands[0]!;\n for (const c of cands) if (c.d > secondary.d) secondary = c;\n let highlight = cands[0] === secondary ? cands[1]! : cands[0]!;\n let hiScore = Math.min(highlight.d, deltaEOK(highlight.hex, secondary.hex));\n for (const c of cands) {\n if (c === secondary || c === highlight) continue;\n const score = Math.min(c.d, deltaEOK(c.hex, secondary.hex));\n if (score > hiScore) {\n highlight = c;\n hiScore = score;\n }\n }\n\n const status = (anchor: number): string => {\n const start = oklchToHex(clampToGamut({ L: STATUS_L, C: STATUS_C, h: anchor }));\n return adjustLightnessUntil(start, (c) => contrastRatio(c, bg) >= 3, \"darken\") ?? text;\n };\n\n const sw = (role: SwatchRole, hex: string, rationale: string): Swatch => ({\n role,\n hex,\n rationale,\n });\n const swatches: Swatch[] = [\n sw(\"primary\", seed, \"seed color\"),\n sw(\"secondary\", secondary.hex, `harmony rotation ${secondary.deg}°, ΔEOK ${secondary.d.toFixed(3)} from primary`),\n sw(\"highlight\", highlight.hex, `harmony rotation ${highlight.deg}°, spread from primary and secondary`),\n sw(\"good\", status(STATUS_HUES.good), \"green anchor 145°, ≥3:1 on bg\"),\n sw(\"warn\", status(STATUS_HUES.warn), \"amber anchor 85°, ≥3:1 on bg\"),\n sw(\"danger\", status(STATUS_HUES.danger), \"red anchor 25°, ≥3:1 on bg\"),\n sw(\"bg\", bg, \"near-white tinted with the seed hue\"),\n sw(\"surface\", surface, \"raised surface, one step below bg\"),\n sw(\"text\", text, \"near-black tinted with the seed hue\"),\n sw(\"neutral\", neutral, \"mid neutral for borders and muted marks\"),\n ...deriveChartColors(seed).map((hex, i) => sw(\"chart\", hex, `chart series ${i + 1}`)),\n ];\n return withNames\n ? swatches.map((s) => ({ ...s, name: nearestNamedColor(s.hex).name }))\n : swatches;\n}\n","// Palette → light-mode @odla-ai/ui tokens.\n//\n// Every name in BRAND_EMITTED_TOKENS is always emitted. Real swatches map\n// directly; missing roles fall back to derivePalette() output seeded from\n// the accent (with a warning); derived/composed roles use the EXACT\n// var()/color-mix() composition strings @odla-ai/ui css/tokens.css uses as\n// defaults, so the object works as a scoped override-island payload (the\n// compositions recompute at the declaring element — see roles.ts).\n//\n// Contrast guarantees (WCAG 2.1, enforced with adjustLightnessUntil and\n// recorded as warnings with `adjustedFrom` whenever a color had to move):\n// --ui-text ≥ 4.5:1 and --ui-text-muted ≥ 4.5:1 on --ui-bg (SC 1.4.3),\n// --ui-accent-strong ≥ 4.5:1 on --ui-bg (darkened on a light bg),\n// --ui-good/warn/danger ≥ 3:1 on --ui-bg (SC 1.4.11), and --ui-on-accent\n// via pickTextOn (≥ 4.5:1 with the default white/near-black candidates).\n\nimport type { Swatch, SwatchRole } from \"../types\";\nimport {\n adjustLightnessUntil,\n clamp01,\n clampToGamut,\n contrastRatio,\n deriveChartColors,\n derivePalette,\n hexToOklch,\n normalizeHex,\n oklchToHex,\n parseHex,\n pickTextOn,\n relativeLuminance,\n type LightnessDirection,\n} from \"../color\";\nimport type { BrandTokens, CompileInput, TokenWarning } from \"./types\";\n\n/**\n * Seed used when the palette has no usable chromatic swatch at all — the\n * @odla-ai/ui neutral default accent (css/tokens.css restrained blue).\n */\nexport const DEFAULT_ACCENT_SEED = \"#3b5e8c\";\n\n/** Below this OKLCH chroma a swatch is too gray for accent/chart duty. */\nconst CHROMATIC_C = 0.02;\n\n// System font stacks, verbatim from @odla-ai/ui css/tokens.css.\nconst SANS_STACK = `ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif`;\nconst SERIF_STACK = `\"Iowan Old Style\", \"Palatino Linotype\", Palatino, Georgia, serif`;\nconst MONO_STACK = `ui-monospace, \"SF Mono\", Menlo, Consolas, monospace`;\nconst DISPLAY_STACK = `ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif`;\n\n// ui css/tokens.css chart defaults — emitted when the palette lacks ≥3\n// chromatic chart swatches, so an override island never keeps root values.\nconst UI_CHART_DEFAULTS = [\n \"var(--ui-accent)\",\n \"var(--ui-good)\",\n \"var(--ui-warn)\",\n \"var(--ui-danger)\",\n \"color-mix(in srgb, var(--ui-accent) 45%, var(--ui-text))\",\n \"var(--ui-text-faint)\",\n] as const;\n\n/**\n * The token a missing swatch role is reported against — and the canonical\n * role → `--ui-*` correspondence. Exported so the design decompiler can\n * invert exactly this map rather than restating it (see\n * `design/decompile.ts`); a round-trip test pins the two together.\n */\nexport const ROLE_TOKEN: Partial<Record<SwatchRole, string>> = {\n primary: \"--ui-accent\",\n secondary: \"--ui-accent-2\",\n highlight: \"--ui-highlight\",\n bg: \"--ui-bg\",\n surface: \"--ui-surface\",\n text: \"--ui-text\",\n neutral: \"--ui-border-strong\",\n good: \"--ui-good\",\n warn: \"--ui-warn\",\n danger: \"--ui-danger\",\n chart: \"--ui-chart-1\",\n};\n\nconst mixVar = (name: string, pct: number): string =>\n `color-mix(in srgb, var(${name}) ${pct}%, transparent)`;\n\n/** Shift OKLab lightness by dL, constant chroma/hue, gamut-clamped. */\nfunction shiftL(hex: string, dL: number): string {\n const { L, C, h } = hexToOklch(hex);\n return oklchToHex(clampToGamut({ L: clamp01(L + dL), C, h }));\n}\n\n/** Move `fromHex` a fraction `t` of the way to `toHex` in OKLab lightness only. */\nfunction lerpL(fromHex: string, toHex: string, t: number): string {\n const a = hexToOklch(fromHex);\n const b = hexToOklch(toHex);\n return oklchToHex(clampToGamut({ L: a.L + (b.L - a.L) * t, C: a.C, h: a.h }));\n}\n\n/**\n * Push `hex` away from `bg` in lightness until it clears `min`:1. Tries the\n * away-from-bg direction (darken on a light bg), then the other direction,\n * then falls back to pickTextOn(bg) — which always clears 4.5:1.\n */\nfunction ensureOnBg(hex: string, bg: string, min: number): string {\n const pred = (c: string): boolean => contrastRatio(c, bg) >= min;\n const away: LightnessDirection =\n relativeLuminance(bg) >= relativeLuminance(hex) ? \"darken\" : \"lighten\";\n return (\n adjustLightnessUntil(hex, pred, away) ??\n adjustLightnessUntil(hex, pred, away === \"darken\" ? \"lighten\" : \"darken\") ??\n pickTextOn(bg)\n );\n}\n\nconst rgbaOf = (hex: string, alpha: number): string => {\n const { r, g, b } = parseHex(hex);\n const c = (v: number): number => Math.round(v * 255);\n return `rgba(${c(r)}, ${c(g)}, ${c(b)}, ${alpha})`;\n};\n\n/** Brand family + system stack; quotes a single family name when needed. */\nfunction fontValue(family: string | undefined, stack: string): string {\n const f = family?.trim() ?? \"\";\n if (f === \"\") return stack;\n if (f.includes(\",\") || /^[\"']/.test(f) || /^[a-zA-Z][a-zA-Z0-9-]*$/.test(f))\n return `${f}, ${stack}`;\n return `\"${f.replaceAll('\"', \"\")}\", ${stack}`;\n}\n\ninterface Collected {\n byRole: Partial<Record<SwatchRole, string>>;\n charts: string[];\n}\n\n/** First-wins role → hex map + ordered chart swatches; bad hexes dropped. */\nfunction collect(swatches: unknown, warnings: TokenWarning[]): Collected {\n const byRole: Partial<Record<SwatchRole, string>> = {};\n const charts: string[] = [];\n if (!Array.isArray(swatches)) {\n warnings.push({ token: \"--ui-accent\", message: \"swatches is not an array; compiling from defaults\" });\n return { byRole, charts };\n }\n (swatches as Array<Swatch | null>).forEach((s, i) => {\n let hex: string;\n try {\n hex = normalizeHex(String(s?.hex));\n } catch {\n warnings.push({\n token: (s?.role !== undefined && ROLE_TOKEN[s.role]) || \"--ui-accent\",\n message: `swatches[${i}] dropped: invalid hex ${String(s?.hex)}`,\n });\n return;\n }\n const role = s?.role;\n if (role === \"chart\") charts.push(hex);\n else if (role !== undefined && role !== \"custom\" && byRole[role] === undefined) byRole[role] = hex;\n });\n return { byRole, charts };\n}\n\n/** What {@link mapPaletteToTokens} returns. */\nexport interface MapResult {\n /** The light-mode token map — every name in BRAND_EMITTED_TOKENS. */\n tokens: BrandTokens;\n /** Fallbacks taken and contrast adjustments made. */\n warnings: TokenWarning[];\n}\n\n/**\n * Maps a palette (+ optional typography) onto the full light-mode\n * @odla-ai/ui token set. Never throws on bad palettes: invalid swatches are\n * dropped, missing roles derive from the accent via derivePalette, and every\n * degradation is recorded as a {@link TokenWarning}.\n */\nexport function mapPaletteToTokens(input: CompileInput): MapResult {\n const warnings: TokenWarning[] = [];\n const { byRole, charts } = collect(input?.swatches, warnings);\n\n let accent = byRole.primary;\n if (accent === undefined) {\n const chromatic = [...Object.values(byRole), ...charts].find(\n (h) => h !== undefined && hexToOklch(h).C >= CHROMATIC_C,\n );\n accent = chromatic ?? DEFAULT_ACCENT_SEED;\n warnings.push({\n token: \"--ui-accent\",\n message: `no primary swatch; using ${chromatic !== undefined ? \"the first chromatic swatch\" : \"the neutral default seed\"} ${accent}`,\n });\n }\n\n const fb = new Map<SwatchRole, string>();\n for (const s of derivePalette(accent, { names: false })) if (!fb.has(s.role)) fb.set(s.role, s.hex);\n const pick = (role: SwatchRole): string => {\n const own = byRole[role];\n if (own !== undefined) return own;\n const derived = fb.get(role)!;\n warnings.push({ token: ROLE_TOKEN[role]!, message: `no ${role} swatch; derived ${derived} from ${accent}` });\n return derived;\n };\n\n const bg = pick(\"bg\");\n const surface = pick(\"surface\");\n const neutral = pick(\"neutral\");\n const surface2 = shiftL(surface, -0.035);\n\n const rawText = pick(\"text\");\n const text = ensureOnBg(rawText, bg, 4.5);\n if (text !== rawText)\n warnings.push({ token: \"--ui-text\", message: \"adjusted to reach 4.5:1 on --ui-bg\", adjustedFrom: rawText });\n\n const accentStrong = ensureOnBg(accent, bg, 4.5);\n if (accentStrong !== accent)\n warnings.push({\n token: \"--ui-accent-strong\",\n message: \"moved --ui-accent in lightness to reach 4.5:1 on --ui-bg\",\n adjustedFrom: accent,\n });\n\n const status = (role: \"good\" | \"warn\" | \"danger\"): string => {\n const own = pick(role);\n const fixed = ensureOnBg(own, bg, 3);\n if (fixed !== own)\n warnings.push({ token: ROLE_TOKEN[role]!, message: \"adjusted to reach 3:1 on --ui-bg\", adjustedFrom: own });\n return fixed;\n };\n\n const chromaticCharts = charts.filter((h) => hexToOklch(h).C >= CHROMATIC_C);\n let chartValues: readonly string[];\n if (chromaticCharts.length >= 3) {\n const picked = chromaticCharts.slice(0, 6);\n for (const c of deriveChartColors(accent, { count: 12 })) {\n if (picked.length >= 6) break;\n if (!picked.includes(c)) picked.push(c);\n }\n chartValues = picked;\n } else {\n warnings.push({\n token: \"--ui-chart-1\",\n message: `fewer than 3 chromatic chart swatches (${chromaticCharts.length}); using the ui derived chart defaults`,\n });\n chartValues = UI_CHART_DEFAULTS;\n }\n\n const secondary = byRole.secondary;\n if (secondary === undefined)\n warnings.push({ token: \"--ui-accent-2\", message: \"no secondary swatch; --ui-accent-2 collapses onto var(--ui-accent) (ui default)\" });\n const highlight = byRole.highlight;\n if (highlight === undefined)\n warnings.push({ token: \"--ui-highlight\", message: \"no highlight swatch; --ui-highlight aliases var(--ui-accent-strong) (ui default)\" });\n\n const t = input?.typography ?? {};\n const tokens: BrandTokens = {\n \"--ui-bg\": bg,\n \"--ui-surface\": surface,\n \"--ui-surface-2\": surface2,\n \"--ui-text\": text,\n \"--ui-text-muted\": ensureOnBg(lerpL(text, bg, 0.35), bg, 4.5),\n \"--ui-text-faint\": lerpL(text, bg, 0.58),\n \"--ui-border\": lerpL(neutral, bg, 0.55),\n \"--ui-border-strong\": neutral,\n \"--ui-accent\": accent,\n \"--ui-accent-strong\": accentStrong,\n \"--ui-accent-soft\": mixVar(\"--ui-accent\", 10),\n \"--ui-on-accent\": pickTextOn(accent),\n \"--ui-good\": status(\"good\"),\n \"--ui-good-soft\": mixVar(\"--ui-good\", 12),\n \"--ui-warn\": status(\"warn\"),\n \"--ui-warn-soft\": mixVar(\"--ui-warn\", 12),\n \"--ui-danger\": status(\"danger\"),\n \"--ui-danger-soft\": mixVar(\"--ui-danger\", 10),\n \"--ui-code-bg\": surface2,\n \"--ui-code-text\": text,\n \"--ui-shadow\": `0 1px 2px ${rgbaOf(text, 0.04)}, 0 8px 24px ${rgbaOf(text, 0.06)}`,\n \"--ui-font-sans\": fontValue(t.fontBody, SANS_STACK),\n \"--ui-font-serif\": SERIF_STACK,\n \"--ui-font-mono\": fontValue(t.fontMono, MONO_STACK),\n \"--ui-font-display\": fontValue(t.fontDisplay ?? t.fontBody, DISPLAY_STACK),\n \"--ui-accent-glow\": mixVar(\"--ui-accent\", 16),\n \"--ui-accent-2\": secondary ?? \"var(--ui-accent)\",\n \"--ui-accent-2-soft\": mixVar(\"--ui-accent-2\", 10),\n \"--ui-highlight\": highlight ?? \"var(--ui-accent-strong)\",\n \"--ui-focus\": \"0 0 0 3px var(--ui-accent-soft)\",\n \"--ui-shadow-strong\": \"var(--ui-shadow)\",\n \"--ui-chart-1\": chartValues[0]!,\n \"--ui-chart-2\": chartValues[1]!,\n \"--ui-chart-3\": chartValues[2]!,\n \"--ui-chart-4\": chartValues[3]!,\n \"--ui-chart-5\": chartValues[4]!,\n \"--ui-chart-6\": chartValues[5]!,\n \"--ui-chart-band\": mixVar(\"--ui-accent\", 10),\n \"--ui-chart-band-strong\": mixVar(\"--ui-accent\", 22),\n \"--ui-chart-flow\": \"var(--ui-accent)\",\n \"--ui-chart-glow\": \"var(--ui-accent-strong)\",\n \"--ui-chat-user-bg\": \"var(--ui-accent-soft)\",\n \"--ui-chat-user-text\": \"var(--ui-text)\",\n \"--ui-chat-assistant-bg\": \"var(--ui-surface)\",\n \"--ui-chat-thinking-bg\": \"var(--ui-surface-2)\",\n \"--ui-chat-thinking-text\": \"var(--ui-text-muted)\",\n \"--ui-chat-tool-accent\": \"var(--ui-accent)\",\n };\n return { tokens, warnings };\n}\n","// Light → dark token derivation.\n//\n// Neutrals (bg / text ladder / borders / code text) flip around OKLab L 0.5:\n// L' = 1 − L reflected, then squeezed into [DARK_FLIP_L_MIN,\n// DARK_FLIP_L_MAX] so the background lands dark but never black and text\n// lands light but never pure white. Chroma and hue are untouched, so tinted\n// neutrals keep their brand tint.\n//\n// Surfaces are the one place a mirror is wrong: light surfaces sit ABOVE\n// the light bg (lighter), so mirroring would drop them BELOW the dark bg —\n// inverted elevation. Instead the surface ladder is rebuilt upward from the\n// derived dark bg (the @odla-ai/ui dark-neutral pattern: bg < surface <\n// surface-2), reusing the light ladder's own lightness gaps as rung heights\n// (with a small minimum so equal-ish inputs still separate).\n//\n// Accents (accent family, statuses) and chart series keep their hue/chroma\n// and are re-lightened with adjustLightnessUntil until they clear WCAG bars\n// against the derived dark bg: 4.5:1 for the accent family and statuses\n// (SC 1.4.3), 3:1 for chart series (SC 1.4.11 graphics). --ui-on-accent is\n// re-picked against the dark accent. Everything else — var()/color-mix()\n// compositions, font stacks, focus ring — passes through unchanged and\n// recomputes in CSS against the dark values (--ui-shadow is the one literal\n// that gets the ui dark shadow instead).\n\nimport {\n adjustLightnessUntil,\n clampToGamut,\n contrastRatio,\n hexToOklch,\n oklchToHex,\n pickTextOn,\n} from \"../color\";\nimport type { BrandTokens } from \"./types\";\n\n/** Darkest OKLab L a flipped neutral may take — the dark bg floor (never black). */\nexport const DARK_FLIP_L_MIN = 0.2;\n\n/** Lightest OKLab L a flipped neutral may take — keeps flipped text off pure white. */\nexport const DARK_FLIP_L_MAX = 0.95;\n\n/** The ui neutral dark shadow (css/tokens.css `[data-theme=\"dark\"]` value). */\nconst DARK_SHADOW = \"0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35)\";\n\nconst HEX6 = /^#[0-9a-f]{6}$/;\n\n/** Neutral ladder: flipped around L 0.5 with the floor/ceiling squeeze.\n * Surfaces/code-bg are here only as a fallback — with a hex bg present they\n * are rebuilt upward from the dark bg instead (see file header). */\nconst NEUTRAL_FLIP = new Set([\n \"--ui-bg\",\n \"--ui-surface\",\n \"--ui-surface-2\",\n \"--ui-text\",\n \"--ui-text-muted\",\n \"--ui-text-faint\",\n \"--ui-border\",\n \"--ui-border-strong\",\n \"--ui-code-bg\",\n \"--ui-code-text\",\n]);\n\n/** Minimum OKLab ΔL between dark surface rungs, so elevation always reads. */\nconst MIN_RUNG = 0.02;\n\n/** Re-lightened until ≥ 4.5:1 on the dark bg (readable as text). */\nconst ACCENT_AA = [\n \"--ui-accent\",\n \"--ui-accent-strong\",\n \"--ui-accent-2\",\n \"--ui-highlight\",\n \"--ui-good\",\n \"--ui-warn\",\n \"--ui-danger\",\n] as const;\n\n/** Re-lightened until ≥ 3:1 on the dark bg (graphics bar). */\nconst CHART_UI = [\n \"--ui-chart-1\",\n \"--ui-chart-2\",\n \"--ui-chart-3\",\n \"--ui-chart-4\",\n \"--ui-chart-5\",\n \"--ui-chart-6\",\n] as const;\n\nfunction flipNeutral(hex: string): string {\n const { L, C, h } = hexToOklch(hex);\n const flipped = DARK_FLIP_L_MIN + (1 - L) * (DARK_FLIP_L_MAX - DARK_FLIP_L_MIN);\n return oklchToHex(clampToGamut({ L: flipped, C, h }));\n}\n\n/**\n * Rebuild bg < surface < surface-2 upward from the dark bg, using the light\n * ladder's own |ΔL| gaps as rung heights (floored at MIN_RUNG). code-bg\n * follows surface-2 when the light map aliased them, else takes its own\n * bg-relative rung. Mutates `out`; skips any token that is absent/non-hex.\n */\nfunction rebuildSurfaces(out: BrandTokens, light: BrandTokens, lightBgL: number, darkBgL: number): void {\n const lightSurface = light[\"--ui-surface\"];\n const lightSurface2 = light[\"--ui-surface-2\"];\n let surfaceDarkL: number | undefined;\n let surfaceLightL = lightBgL;\n if (lightSurface !== undefined && HEX6.test(lightSurface)) {\n const { L, C, h } = hexToOklch(lightSurface);\n surfaceLightL = L;\n surfaceDarkL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);\n out[\"--ui-surface\"] = oklchToHex(clampToGamut({ L: surfaceDarkL, C, h }));\n }\n if (lightSurface2 !== undefined && HEX6.test(lightSurface2)) {\n const { L, C, h } = hexToOklch(lightSurface2);\n const base = surfaceDarkL ?? darkBgL + MIN_RUNG;\n const L2 = base + Math.max(Math.abs(L - surfaceLightL), MIN_RUNG);\n out[\"--ui-surface-2\"] = oklchToHex(clampToGamut({ L: L2, C, h }));\n }\n const lightCodeBg = light[\"--ui-code-bg\"];\n if (lightCodeBg !== undefined && HEX6.test(lightCodeBg)) {\n if (lightCodeBg === lightSurface2 && out[\"--ui-surface-2\"] !== undefined) {\n out[\"--ui-code-bg\"] = out[\"--ui-surface-2\"];\n } else {\n const { L, C, h } = hexToOklch(lightCodeBg);\n const codeL = darkBgL + Math.max(Math.abs(L - lightBgL), MIN_RUNG);\n out[\"--ui-code-bg\"] = oklchToHex(clampToGamut({ L: codeL, C, h }));\n }\n }\n}\n\n/** Lighten (else darken; else pickTextOn) until ≥ `min`:1 on `bg`. */\nfunction relight(hex: string, bg: string, min: number): string {\n const pred = (c: string): boolean => contrastRatio(c, bg) >= min;\n return (\n adjustLightnessUntil(hex, pred, \"lighten\") ??\n adjustLightnessUntil(hex, pred, \"darken\") ??\n pickTextOn(bg)\n );\n}\n\n/**\n * Derives the dark-mode token map from a light one (see the file header for\n * the flip / re-lighten / passthrough rules). Pure and total: tokens the\n * rules don't recognize — including non-hex values in recognized slots —\n * pass through unchanged, and the result always has exactly the input's\n * key set. Deterministic, never throws.\n */\nexport function deriveDarkTokens(light: BrandTokens): BrandTokens {\n const out: BrandTokens = {};\n for (const [name, value] of Object.entries(light)) {\n if (name === \"--ui-shadow\") out[name] = DARK_SHADOW;\n else if (NEUTRAL_FLIP.has(name) && HEX6.test(value)) out[name] = flipNeutral(value);\n else out[name] = value;\n }\n\n const bg = out[\"--ui-bg\"];\n const lightBg = light[\"--ui-bg\"];\n if (bg === undefined || !HEX6.test(bg) || lightBg === undefined || !HEX6.test(lightBg)) return out;\n\n rebuildSurfaces(out, light, hexToOklch(lightBg).L, hexToOklch(bg).L);\n\n for (const name of [\"--ui-text\", \"--ui-text-muted\"]) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 4.5);\n }\n const codeBg = out[\"--ui-code-bg\"];\n const codeText = out[\"--ui-code-text\"];\n if (codeText !== undefined && HEX6.test(codeText))\n out[\"--ui-code-text\"] = relight(codeText, codeBg !== undefined && HEX6.test(codeBg) ? codeBg : bg, 4.5);\n\n for (const name of ACCENT_AA) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 4.5);\n }\n for (const name of CHART_UI) {\n const v = out[name];\n if (v !== undefined && HEX6.test(v)) out[name] = relight(v, bg, 3);\n }\n\n const accent = out[\"--ui-accent\"];\n const onAccent = out[\"--ui-on-accent\"];\n if (accent !== undefined && HEX6.test(accent) && onAccent !== undefined && HEX6.test(onAccent))\n out[\"--ui-on-accent\"] = pickTextOn(accent);\n\n return out;\n}\n","// Compiled tokens → theme CSS text.\n//\n// The output's shape mirrors a real @odla-ai/ui theme tokens file\n// (themes/juniper/tokens.css): a light block, a `[data-theme=\"dark\"]`\n// attribute block, the same dark values repeated under\n// `@media (prefers-color-scheme: dark)` guarded by\n// `:root:not([data-theme=\"light\"])` (so an explicit light preference beats\n// the system's dark scheme), and optionally a `.ui-invert` island block.\n// The attribute/media blocks carry only the tokens that CHANGE in dark —\n// the ui convention, since var()/color-mix() compositions declared in the\n// light block recompute on the same element. The `.ui-invert` island gets\n// the FULL dark payload: on an island, root-computed composites would be\n// stale (see roles.ts).\n//\n// Declarations render in BRAND_EMITTED_TOKENS order (unknown tokens last,\n// alphabetically), so output is deterministic and golden-testable.\n\nimport { BRAND_EMITTED_TOKENS } from \"./roles\";\nimport type { BrandTokens } from \"./types\";\n\n/** Options for {@link renderTokensCss}. */\nexport interface RenderTokensCssOptions {\n /** Dark-mode tokens (deriveDarkTokens output). Omit for a light-only sheet. */\n dark?: BrandTokens;\n /**\n * Selector the light block declares on (default \":root\"). A scoped\n * selector (e.g. `[data-brand=\"acme\"]`) gets its dark blocks nested under\n * the document-level theme guards instead of replacing them.\n */\n selector?: string;\n /**\n * Also emit a `.ui-invert` block carrying the full dark payload, so the\n * subtree renders dark regardless of the global mode. Requires `dark`.\n */\n includeInvert?: boolean;\n}\n\nconst HEADER =\n \"/* Generated by @odla-ai/brand — @odla-ai/ui token overrides; pair with @odla-ai/ui css/tokens.css. */\";\n\nconst ORDER = new Map<string, number>(\n BRAND_EMITTED_TOKENS.map((name, i): [string, number] => [name, i]),\n);\n\n/** Emission order: BRAND_EMITTED_TOKENS index, then unknown names A→Z. */\nfunction orderedNames(tokens: BrandTokens): string[] {\n return Object.keys(tokens).sort((a, b) => {\n const ia = ORDER.get(a);\n const ib = ORDER.get(b);\n if (ia !== undefined && ib !== undefined) return ia - ib;\n if (ia !== undefined) return -1;\n if (ib !== undefined) return 1;\n return a < b ? -1 : a > b ? 1 : 0;\n });\n}\n\nfunction renderBlock(\n selector: string,\n tokens: BrandTokens,\n names: readonly string[],\n indent: string,\n lead?: string,\n): string {\n const lines: string[] = [];\n if (lead !== undefined) lines.push(`${indent} ${lead}`);\n for (const name of names) lines.push(`${indent} ${name}: ${tokens[name]};`);\n if (lines.length === 0) return `${indent}${selector} {\\n${indent}}`;\n return `${indent}${selector} {\\n${lines.join(\"\\n\")}\\n${indent}}`;\n}\n\n/**\n * Renders a compiled token map (plus optional dark map) as CSS text shaped\n * like an @odla-ai/ui theme tokens file — see the file header for the block\n * structure and ordering guarantees. Deterministic for a given input.\n */\nexport function renderTokensCss(light: BrandTokens, opts: RenderTokensCssOptions = {}): string {\n const selector = opts.selector ?? \":root\";\n const scoped = selector !== \":root\";\n const parts: string[] = [HEADER, renderBlock(selector, light, orderedNames(light), \"\")];\n\n const dark = opts.dark;\n if (dark !== undefined) {\n const diffNames = orderedNames(dark).filter((name) => light[name] !== dark[name]);\n if (diffNames.length > 0) {\n const attrSelector = scoped\n ? `[data-theme=\"dark\"] ${selector}, ${selector}[data-theme=\"dark\"]`\n : `[data-theme=\"dark\"]`;\n const mediaSelector = scoped\n ? `:root:not([data-theme=\"light\"]) ${selector}`\n : `:root:not([data-theme=\"light\"])`;\n parts.push(renderBlock(attrSelector, dark, diffNames, \"\"));\n parts.push(\n `@media (prefers-color-scheme: dark) {\\n${renderBlock(mediaSelector, dark, diffNames, \" \")}\\n}`,\n );\n }\n if (opts.includeInvert === true) {\n parts.push(renderBlock(\".ui-invert\", dark, orderedNames(dark), \"\", \"color-scheme: dark;\"));\n }\n }\n return `${parts.join(\"\\n\\n\")}\\n`;\n}\n","// The compiler facade: palette + typography (+ author overrides) →\n// { light, dark, warnings }. Light comes from mapPaletteToTokens, overrides\n// are layered on last (so authors can pin any token, including ones this\n// compiler doesn't own, like --ui-radius-md), and dark derives from the\n// FINAL light map — an overridden accent flows into the dark derivation.\n//\n// Never throws on bad palettes: map.ts drops invalid swatches and derives\n// missing roles (each degradation becomes a TokenWarning), and malformed\n// overrides are skipped with a warning rather than rejected.\n\nimport { deriveDarkTokens } from \"./dark\";\nimport { mapPaletteToTokens } from \"./map\";\nimport type { CompiledBrandTokens, CompileInput, TokenWarning } from \"./types\";\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Layer `overrides` onto `light` in place; malformed entries warn + skip. */\nfunction applyOverrides(\n light: Record<string, string>,\n overrides: unknown,\n warnings: TokenWarning[],\n): void {\n if (overrides === undefined) return;\n if (!isRecord(overrides)) {\n warnings.push({ token: \"--ui-accent\", message: \"overrides ignored: not an object\" });\n return;\n }\n for (const [name, value] of Object.entries(overrides)) {\n if (!name.startsWith(\"--\")) {\n warnings.push({ token: name, message: \"override ignored: token names must start with --\" });\n continue;\n }\n if (typeof value !== \"string\" || value.trim() === \"\") {\n warnings.push({ token: name, message: \"override ignored: value must be a non-empty string\" });\n continue;\n }\n light[name] = value.trim();\n }\n}\n\n/**\n * Compiles a brand palette into @odla-ai/ui design tokens: a light map\n * covering every name in BRAND_EMITTED_TOKENS (plus overrides), a dark map\n * derived from it (same key set), and the warnings accumulated along the\n * way — missing-role fallbacks and contrast adjustments (`adjustedFrom`).\n *\n * Both maps double as runtime override-island payloads: every derived\n * default that composes the accent family is re-declared, so applying the\n * map on any element recomputes charts, softs, focus and chat roles against\n * the brand palette (see roles.ts). Deterministic; never throws on bad\n * palettes.\n */\nexport function compileBrandTokens(input: CompileInput): CompiledBrandTokens {\n const { tokens: light, warnings } = mapPaletteToTokens(input);\n applyOverrides(light, input?.overrides, warnings);\n const dark = deriveDarkTokens(light);\n return { light, dark, warnings };\n}\n"],"mappings":"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","names":["value","toHex","HEX6"]}
@@ -37,10 +37,16 @@ type ProposalKind = (typeof PROPOSAL_KINDS)[number];
37
37
  declare const PROPOSAL_STATUSES: readonly ["open", "accepted", "rejected", "superseded"];
38
38
  /** Where a proposal stands in the human-gated review flow. */
39
39
  type ProposalStatus = (typeof PROPOSAL_STATUSES)[number];
40
- /** What kind of upload an asset row records. */
41
- declare const ASSET_KINDS: readonly ["logo", "wordmark", "inspiration", "document", "other"];
40
+ /** What kind of upload an asset row records. `design` is the one kind whose
41
+ * bytes are HTML a Claude Design standalone export — and the only kind the
42
+ * preview route will ever serve as a document; see `design/` and
43
+ * `routes/design-preview.ts`. */
44
+ declare const ASSET_KINDS: readonly ["logo", "wordmark", "inspiration", "document", "design", "other"];
42
45
  /** The declared purpose of an uploaded asset. */
43
46
  type AssetKind = (typeof ASSET_KINDS)[number];
47
+ /** The asset kind carrying a Claude Design bundle. Its content type, digest,
48
+ * preview route, and agent tools are all keyed off this one value. */
49
+ declare const DESIGN_ASSET_KIND = "design";
44
50
  /** Every swatch role a palette may assign. `chart` may repeat (a series);
45
51
  * `custom` is the escape hatch for roles the token compiler doesn't map. */
46
52
  declare const SWATCH_ROLES: readonly ["primary", "secondary", "highlight", "bg", "surface", "text", "neutral", "good", "warn", "danger", "chart", "custom"];
@@ -185,6 +191,13 @@ interface CompiledBrandTokens {
185
191
  * @odla-ai/ui neutral default accent (css/tokens.css restrained blue).
186
192
  */
187
193
  declare const DEFAULT_ACCENT_SEED = "#3b5e8c";
194
+ /**
195
+ * The token a missing swatch role is reported against — and the canonical
196
+ * role → `--ui-*` correspondence. Exported so the design decompiler can
197
+ * invert exactly this map rather than restating it (see
198
+ * `design/decompile.ts`); a round-trip test pins the two together.
199
+ */
200
+ declare const ROLE_TOKEN: Partial<Record<SwatchRole, string>>;
188
201
  /** What {@link mapPaletteToTokens} returns. */
189
202
  interface MapResult {
190
203
  /** The light-mode token map — every name in BRAND_EMITTED_TOKENS. */
@@ -250,4 +263,4 @@ declare function renderTokensCss(light: BrandTokens, opts?: RenderTokensCssOptio
250
263
  */
251
264
  declare function compileBrandTokens(input: CompileInput): CompiledBrandTokens;
252
265
 
253
- export { type AssetKind as A, type BookStatus as B, type CompileInput as C, DARK_FLIP_L_MAX as D, SWATCH_ROLES as E, type SwatchRole as F, type TypographySection as G, compileBrandTokens as H, type ImagerySection as I, deriveDarkTokens as J, mapPaletteToTokens as K, type LogoSection as L, type MapResult as M, renderTokensCss as N, type ProposalKind as P, type RenderTokensCssOptions as R, type Swatch as S, type TokenWarning as T, type VoiceSection as V, type ProposalStatus as a, type AssetAnalysis as b, type BrandTokensSnapshot as c, type PaletteStatus as d, type PaletteSource as e, type SectionKind as f, type SectionStatus as g, ASSET_KINDS as h, BOOK_STATUSES as i, BRAND_CHART_TOKENS as j, BRAND_CHAT_TOKENS as k, BRAND_DERIVED_TOKENS as l, BRAND_EMITTED_TOKENS as m, BRAND_NS as n, BRAND_REQUIRED_TOKENS as o, type BrandTokens as p, type CompiledBrandTokens as q, DARK_FLIP_L_MIN as r, DEFAULT_ACCENT_SEED as s, PALETTE_SOURCES as t, PALETTE_STATUSES as u, PROPOSAL_KINDS as v, PROPOSAL_STATUSES as w, type PaletteSection as x, SECTION_KINDS as y, SECTION_STATUSES as z };
266
+ export { type AssetKind as A, type BookStatus as B, type CompileInput as C, DARK_FLIP_L_MAX as D, type RenderTokensCssOptions as E, SECTION_KINDS as F, SECTION_STATUSES as G, SWATCH_ROLES as H, type ImagerySection as I, type TypographySection as J, compileBrandTokens as K, type LogoSection as L, type MapResult as M, deriveDarkTokens as N, mapPaletteToTokens as O, type ProposalKind as P, renderTokensCss as Q, ROLE_TOKEN as R, type Swatch as S, type TokenWarning as T, type VoiceSection as V, type ProposalStatus as a, type AssetAnalysis as b, type BrandTokensSnapshot as c, type PaletteStatus as d, type PaletteSource as e, type SectionKind as f, type SectionStatus as g, type SwatchRole as h, ASSET_KINDS as i, BOOK_STATUSES as j, BRAND_CHART_TOKENS as k, BRAND_CHAT_TOKENS as l, BRAND_DERIVED_TOKENS as m, BRAND_EMITTED_TOKENS as n, BRAND_NS as o, BRAND_REQUIRED_TOKENS as p, type BrandTokens as q, type CompiledBrandTokens as r, DARK_FLIP_L_MIN as s, DEFAULT_ACCENT_SEED as t, DESIGN_ASSET_KIND as u, PALETTE_SOURCES as v, PALETTE_STATUSES as w, PROPOSAL_KINDS as x, PROPOSAL_STATUSES as y, type PaletteSection as z };
@@ -37,10 +37,16 @@ type ProposalKind = (typeof PROPOSAL_KINDS)[number];
37
37
  declare const PROPOSAL_STATUSES: readonly ["open", "accepted", "rejected", "superseded"];
38
38
  /** Where a proposal stands in the human-gated review flow. */
39
39
  type ProposalStatus = (typeof PROPOSAL_STATUSES)[number];
40
- /** What kind of upload an asset row records. */
41
- declare const ASSET_KINDS: readonly ["logo", "wordmark", "inspiration", "document", "other"];
40
+ /** What kind of upload an asset row records. `design` is the one kind whose
41
+ * bytes are HTML a Claude Design standalone export — and the only kind the
42
+ * preview route will ever serve as a document; see `design/` and
43
+ * `routes/design-preview.ts`. */
44
+ declare const ASSET_KINDS: readonly ["logo", "wordmark", "inspiration", "document", "design", "other"];
42
45
  /** The declared purpose of an uploaded asset. */
43
46
  type AssetKind = (typeof ASSET_KINDS)[number];
47
+ /** The asset kind carrying a Claude Design bundle. Its content type, digest,
48
+ * preview route, and agent tools are all keyed off this one value. */
49
+ declare const DESIGN_ASSET_KIND = "design";
44
50
  /** Every swatch role a palette may assign. `chart` may repeat (a series);
45
51
  * `custom` is the escape hatch for roles the token compiler doesn't map. */
46
52
  declare const SWATCH_ROLES: readonly ["primary", "secondary", "highlight", "bg", "surface", "text", "neutral", "good", "warn", "danger", "chart", "custom"];
@@ -185,6 +191,13 @@ interface CompiledBrandTokens {
185
191
  * @odla-ai/ui neutral default accent (css/tokens.css restrained blue).
186
192
  */
187
193
  declare const DEFAULT_ACCENT_SEED = "#3b5e8c";
194
+ /**
195
+ * The token a missing swatch role is reported against — and the canonical
196
+ * role → `--ui-*` correspondence. Exported so the design decompiler can
197
+ * invert exactly this map rather than restating it (see
198
+ * `design/decompile.ts`); a round-trip test pins the two together.
199
+ */
200
+ declare const ROLE_TOKEN: Partial<Record<SwatchRole, string>>;
188
201
  /** What {@link mapPaletteToTokens} returns. */
189
202
  interface MapResult {
190
203
  /** The light-mode token map — every name in BRAND_EMITTED_TOKENS. */
@@ -250,4 +263,4 @@ declare function renderTokensCss(light: BrandTokens, opts?: RenderTokensCssOptio
250
263
  */
251
264
  declare function compileBrandTokens(input: CompileInput): CompiledBrandTokens;
252
265
 
253
- export { type AssetKind as A, type BookStatus as B, type CompileInput as C, DARK_FLIP_L_MAX as D, SWATCH_ROLES as E, type SwatchRole as F, type TypographySection as G, compileBrandTokens as H, type ImagerySection as I, deriveDarkTokens as J, mapPaletteToTokens as K, type LogoSection as L, type MapResult as M, renderTokensCss as N, type ProposalKind as P, type RenderTokensCssOptions as R, type Swatch as S, type TokenWarning as T, type VoiceSection as V, type ProposalStatus as a, type AssetAnalysis as b, type BrandTokensSnapshot as c, type PaletteStatus as d, type PaletteSource as e, type SectionKind as f, type SectionStatus as g, ASSET_KINDS as h, BOOK_STATUSES as i, BRAND_CHART_TOKENS as j, BRAND_CHAT_TOKENS as k, BRAND_DERIVED_TOKENS as l, BRAND_EMITTED_TOKENS as m, BRAND_NS as n, BRAND_REQUIRED_TOKENS as o, type BrandTokens as p, type CompiledBrandTokens as q, DARK_FLIP_L_MIN as r, DEFAULT_ACCENT_SEED as s, PALETTE_SOURCES as t, PALETTE_STATUSES as u, PROPOSAL_KINDS as v, PROPOSAL_STATUSES as w, type PaletteSection as x, SECTION_KINDS as y, SECTION_STATUSES as z };
266
+ export { type AssetKind as A, type BookStatus as B, type CompileInput as C, DARK_FLIP_L_MAX as D, type RenderTokensCssOptions as E, SECTION_KINDS as F, SECTION_STATUSES as G, SWATCH_ROLES as H, type ImagerySection as I, type TypographySection as J, compileBrandTokens as K, type LogoSection as L, type MapResult as M, deriveDarkTokens as N, mapPaletteToTokens as O, type ProposalKind as P, renderTokensCss as Q, ROLE_TOKEN as R, type Swatch as S, type TokenWarning as T, type VoiceSection as V, type ProposalStatus as a, type AssetAnalysis as b, type BrandTokensSnapshot as c, type PaletteStatus as d, type PaletteSource as e, type SectionKind as f, type SectionStatus as g, type SwatchRole as h, ASSET_KINDS as i, BOOK_STATUSES as j, BRAND_CHART_TOKENS as k, BRAND_CHAT_TOKENS as l, BRAND_DERIVED_TOKENS as m, BRAND_EMITTED_TOKENS as n, BRAND_NS as o, BRAND_REQUIRED_TOKENS as p, type BrandTokens as q, type CompiledBrandTokens as r, DARK_FLIP_L_MIN as s, DEFAULT_ACCENT_SEED as t, DESIGN_ASSET_KIND as u, PALETTE_SOURCES as v, PALETTE_STATUSES as w, PROPOSAL_KINDS as x, PROPOSAL_STATUSES as y, type PaletteSection as z };