@chances-ai/ui-core 24.0.0 → 24.2.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.
Files changed (45) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +5 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/theme/builtins.d.ts +25 -0
  6. package/dist/theme/builtins.d.ts.map +1 -0
  7. package/dist/theme/builtins.js +209 -0
  8. package/dist/theme/builtins.js.map +1 -0
  9. package/dist/theme/color.d.ts +78 -0
  10. package/dist/theme/color.d.ts.map +1 -0
  11. package/dist/theme/color.js +200 -0
  12. package/dist/theme/color.js.map +1 -0
  13. package/dist/theme/contrast.d.ts +18 -0
  14. package/dist/theme/contrast.d.ts.map +1 -0
  15. package/dist/theme/contrast.js +25 -0
  16. package/dist/theme/contrast.js.map +1 -0
  17. package/dist/theme/index.d.ts +29 -0
  18. package/dist/theme/index.d.ts.map +1 -0
  19. package/dist/theme/index.js +42 -0
  20. package/dist/theme/index.js.map +1 -0
  21. package/dist/theme/project.d.ts +14 -0
  22. package/dist/theme/project.d.ts.map +1 -0
  23. package/dist/theme/project.js +37 -0
  24. package/dist/theme/project.js.map +1 -0
  25. package/dist/theme/resolve.d.ts +11 -0
  26. package/dist/theme/resolve.d.ts.map +1 -0
  27. package/dist/theme/resolve.js +152 -0
  28. package/dist/theme/resolve.js.map +1 -0
  29. package/dist/theme/schema.d.ts +60 -0
  30. package/dist/theme/schema.d.ts.map +1 -0
  31. package/dist/theme/schema.js +81 -0
  32. package/dist/theme/schema.js.map +1 -0
  33. package/dist/theme/tokens.d.ts +40 -0
  34. package/dist/theme/tokens.d.ts.map +1 -0
  35. package/dist/theme/tokens.js +116 -0
  36. package/dist/theme/tokens.js.map +1 -0
  37. package/dist/theme.d.ts +10 -29
  38. package/dist/theme.d.ts.map +1 -1
  39. package/dist/theme.js +19 -73
  40. package/dist/theme.js.map +1 -1
  41. package/dist/web-theme.d.ts +6 -6
  42. package/dist/web-theme.d.ts.map +1 -1
  43. package/dist/web-theme.js +18 -37
  44. package/dist/web-theme.js.map +1 -1
  45. package/package.json +3 -3
@@ -0,0 +1,29 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) The seed token-engine barrel. The public `../theme.js`
3
+ * (TUI palette) + `../web-theme.js` (CSS vars) are thin back-compat shims over
4
+ * this. Pure / browser-safe.
5
+ */
6
+ export * from "./color.js";
7
+ export * from "./contrast.js";
8
+ export type { HexColor, OklchColor, ColorValue, ThemeSeeds, ChancesThemeVariant, ChancesTheme } from "./schema.js";
9
+ export { parseCustomTheme } from "./schema.js";
10
+ export type { ChancesTokenName, ChancesTokens, TuiRole, WebChromeToken } from "./tokens.js";
11
+ export { TUI_ROLES, WEB_CHROME_TOKENS, ALL_TOKENS, TOKEN_GROUPS } from "./tokens.js";
12
+ export { resolveThemeVariant } from "./resolve.js";
13
+ export { toTuiTheme, toCssVars, kebab } from "./project.js";
14
+ export { BUILTIN_THEMES, DEFAULT_THEME, DALTONIZED_THEME, ANSI_THEME } from "./builtins.js";
15
+ import type { ChancesTheme } from "./schema.js";
16
+ import type { ChancesTokens } from "./tokens.js";
17
+ export type ThemeMode = "dark" | "light";
18
+ /** Resolve a built-in (by id) or custom theme + mode → the full token set. */
19
+ export declare function resolveThemeTokens(theme: ChancesTheme | string, mode: ThemeMode): ChancesTokens;
20
+ /** The expanded theme-selection setting (config / `/theme`). `auto`/`dark`/`light`
21
+ * target the `default` theme; the daltonized/ansi variants target their theme. */
22
+ export type ThemeSettingId = "auto" | "dark" | "light" | "daltonized" | "dark-daltonized" | "light-daltonized" | "ansi" | "dark-ansi" | "light-ansi";
23
+ /** Parse a setting into `{ themeId, mode }`. `auto` → default/dark (terminal-bg
24
+ * detection is a later lever); unknown → default/dark. */
25
+ export declare function parseThemeSetting(setting: string | undefined): {
26
+ themeId: string;
27
+ mode: ThemeMode;
28
+ };
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzC,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,aAAa,CAG/F;AAED;mFACmF;AACnF,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,MAAM,GACN,OAAO,GACP,YAAY,GACZ,iBAAiB,GACjB,kBAAkB,GAClB,MAAM,GACN,WAAW,GACX,YAAY,CAAC;AAEjB;2DAC2D;AAC3D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAmBnG"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) The seed token-engine barrel. The public `../theme.js`
3
+ * (TUI palette) + `../web-theme.js` (CSS vars) are thin back-compat shims over
4
+ * this. Pure / browser-safe.
5
+ */
6
+ export * from "./color.js";
7
+ export * from "./contrast.js";
8
+ export { parseCustomTheme } from "./schema.js";
9
+ export { TUI_ROLES, WEB_CHROME_TOKENS, ALL_TOKENS, TOKEN_GROUPS } from "./tokens.js";
10
+ export { resolveThemeVariant } from "./resolve.js";
11
+ export { toTuiTheme, toCssVars, kebab } from "./project.js";
12
+ export { BUILTIN_THEMES, DEFAULT_THEME, DALTONIZED_THEME, ANSI_THEME } from "./builtins.js";
13
+ import { BUILTIN_THEMES } from "./builtins.js";
14
+ import { resolveThemeVariant } from "./resolve.js";
15
+ /** Resolve a built-in (by id) or custom theme + mode → the full token set. */
16
+ export function resolveThemeTokens(theme, mode) {
17
+ const t = typeof theme === "string" ? (BUILTIN_THEMES[theme] ?? BUILTIN_THEMES.default) : theme;
18
+ return resolveThemeVariant(mode === "dark" ? t.dark : t.light, mode === "dark");
19
+ }
20
+ /** Parse a setting into `{ themeId, mode }`. `auto` → default/dark (terminal-bg
21
+ * detection is a later lever); unknown → default/dark. */
22
+ export function parseThemeSetting(setting) {
23
+ switch (setting) {
24
+ case "light":
25
+ return { themeId: "default", mode: "light" };
26
+ case "daltonized":
27
+ case "dark-daltonized":
28
+ return { themeId: "daltonized", mode: "dark" };
29
+ case "light-daltonized":
30
+ return { themeId: "daltonized", mode: "light" };
31
+ case "ansi":
32
+ case "dark-ansi":
33
+ return { themeId: "ansi", mode: "dark" };
34
+ case "light-ansi":
35
+ return { themeId: "ansi", mode: "light" };
36
+ case "dark":
37
+ case "auto":
38
+ default:
39
+ return { themeId: "default", mode: "dark" };
40
+ }
41
+ }
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/theme/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAE9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI5F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAInD,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB,CAAC,KAA4B,EAAE,IAAe;IAC9E,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,OAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACjG,OAAO,mBAAmB,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC;AAClF,CAAC;AAeD;2DAC2D;AAC3D,MAAM,UAAU,iBAAiB,CAAC,OAA2B;IAC3D,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC/C,KAAK,YAAY,CAAC;QAClB,KAAK,iBAAiB;YACpB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QACjD,KAAK,kBAAkB;YACrB,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAClD,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW;YACd,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC3C,KAAK,YAAY;YACf,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC5C,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ;YACE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ChancesTokens, TuiRole } from "./tokens.js";
2
+ /** Project resolved tokens → the 23 TUI roles (the legacy `Theme` shape). Keys
3
+ * are `TuiRole` which === the `Theme` field names, so the result is assignable
4
+ * to `Theme` (all fields optional). */
5
+ export declare function toTuiTheme(tokens: ChancesTokens): Record<TuiRole, string>;
6
+ /** camelCase token name → kebab CSS-var suffix (`diffAddedBg` → `diff-added-bg`).
7
+ * Matches the legacy `web-theme.ts` deriver so `--ch-*` names are unchanged. */
8
+ export declare function kebab(s: string): string;
9
+ /** Project resolved tokens → `--ch-*` CSS custom properties (the whole set).
10
+ * Hex values are emitted as `rgb(r,g,b)` so each `--ch-*` is VALUE-IDENTICAL to
11
+ * the TUI projection (true single source — the web-theme test asserts this);
12
+ * `var(--…)` passes through. Spread onto an element's inline `style`. */
13
+ export declare function toCssVars(tokens: ChancesTokens): Record<`--${string}`, string>;
14
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/theme/project.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAa1D;;wCAEwC;AACxC,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAIzE;AAED;iFACiF;AACjF,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED;;;0EAG0E;AAC1E,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,KAAK,MAAM,EAAE,EAAE,MAAM,CAAC,CAM9E"}
@@ -0,0 +1,37 @@
1
+ import { TUI_ROLES } from "./tokens.js";
2
+ import { hexToRgbString } from "./color.js";
3
+ /** A token value as the TUI consumes it: `#hex` → `rgb(r,g,b)`; `ansi:<name>`
4
+ * passes through (the TUI shim maps it to chalk's named ANSI); `var(--…)` is
5
+ * not meaningful on the TUI and passes through unchanged (web-only themes never
6
+ * reach here). */
7
+ function tuiValue(v) {
8
+ if (v.startsWith("#"))
9
+ return hexToRgbString(v);
10
+ return v; // ansi:… or var(…)
11
+ }
12
+ /** Project resolved tokens → the 23 TUI roles (the legacy `Theme` shape). Keys
13
+ * are `TuiRole` which === the `Theme` field names, so the result is assignable
14
+ * to `Theme` (all fields optional). */
15
+ export function toTuiTheme(tokens) {
16
+ const out = {};
17
+ for (const role of TUI_ROLES)
18
+ out[role] = tuiValue(tokens[role]);
19
+ return out;
20
+ }
21
+ /** camelCase token name → kebab CSS-var suffix (`diffAddedBg` → `diff-added-bg`).
22
+ * Matches the legacy `web-theme.ts` deriver so `--ch-*` names are unchanged. */
23
+ export function kebab(s) {
24
+ return s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
25
+ }
26
+ /** Project resolved tokens → `--ch-*` CSS custom properties (the whole set).
27
+ * Hex values are emitted as `rgb(r,g,b)` so each `--ch-*` is VALUE-IDENTICAL to
28
+ * the TUI projection (true single source — the web-theme test asserts this);
29
+ * `var(--…)` passes through. Spread onto an element's inline `style`. */
30
+ export function toCssVars(tokens) {
31
+ const vars = {};
32
+ for (const [name, value] of Object.entries(tokens)) {
33
+ vars[`--ch-${kebab(name)}`] = value.startsWith("#") ? hexToRgbString(value) : value;
34
+ }
35
+ return vars;
36
+ }
37
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/theme/project.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C;;;mBAGmB;AACnB,SAAS,QAAQ,CAAC,CAAa;IAC7B,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,cAAc,CAAC,CAAiB,CAAC,CAAC;IAChE,OAAO,CAAC,CAAC,CAAC,mBAAmB;AAC/B,CAAC;AAED;;wCAEwC;AACxC,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,MAAM,GAAG,GAAG,EAA6B,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,SAAS;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,OAAO,GAAG,CAAC;AACb,CAAC;AAED;iFACiF;AACjF,MAAM,UAAU,KAAK,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED;;;0EAG0E;AAC1E,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,MAAM,IAAI,GAAkC,EAAE,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,KAAqB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtG,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) The role-map: a theme variant (9 seeds + 3 optional
3
+ * expressive seeds + overrides) → ~68 resolved tokens `[11.4]`. Adapts opencode's
4
+ * `resolveThemeVariant` step choices, curated to chances' token set. Every token
5
+ * is derived from a seed scale; `overrides` win LAST (the `default` theme pins
6
+ * the 23 TUI roles + surface anchors there ⇒ zero TUI regression). Pure.
7
+ */
8
+ import type { ChancesThemeVariant } from "./schema.js";
9
+ import type { ChancesTokens } from "./tokens.js";
10
+ export declare function resolveThemeVariant(variant: ChancesThemeVariant, isDark: boolean): ChancesTokens;
11
+ //# sourceMappingURL=resolve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/theme/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAoC,MAAM,aAAa,CAAC;AACzF,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,aAAa,CAAC;AAgBnE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,OAAO,GAAG,aAAa,CA6IhG"}
@@ -0,0 +1,152 @@
1
+ import { generateNeutralScale, generateScale, hexToOklch, shift } from "./color.js";
2
+ import { on } from "./contrast.js";
3
+ /** Target OKLCH hues (deg) for the discrete brand colors when a theme does NOT
4
+ * supply explicit expressive seeds. Chosen to land teal / violet / pink and to
5
+ * stay Δh-distinct from each other (gate in engine.test.ts). */
6
+ const DISCRETE_HUE = { planMode: 175, autoAccept: 300, bashPink: 350 };
7
+ function deriveDiscrete(seed, fallbackFrom, targetHue, isDark) {
8
+ if (seed)
9
+ return seed;
10
+ // Rotate the fallback seed to the target hue, keep a vivid, mid chroma/lightness.
11
+ const base = hexToOklch(fallbackFrom);
12
+ return shift(fallbackFrom, { h: targetHue - base.h, c: 1.0, l: isDark ? 0.06 : -0.04 });
13
+ }
14
+ export function resolveThemeVariant(variant, isDark) {
15
+ const s = variant.seeds;
16
+ const neutral = generateNeutralScale(s.neutral, isDark);
17
+ const primary = generateScale(s.primary, isDark);
18
+ const success = generateScale(s.success, isDark);
19
+ const warning = generateScale(s.warning, isDark);
20
+ const error = generateScale(s.error, isDark);
21
+ const info = generateScale(s.info, isDark);
22
+ const interactive = generateScale(s.interactive, isDark);
23
+ const diffAdd = generateScale(s.diffAdd, isDark);
24
+ const diffDelete = generateScale(s.diffDelete, isDark);
25
+ // A vivid text-level value off a scale (opencode `content`): brightest end + a
26
+ // small chroma/lightness nudge so it reads on the base background.
27
+ const content = (scale) => shift(scale[10], { l: isDark ? 0.034 : -0.024, c: isDark ? 1.3 : 1.18 });
28
+ const planMode = deriveDiscrete(s.planMode, s.info, DISCRETE_HUE.planMode, isDark);
29
+ const autoAccept = deriveDiscrete(s.autoAccept, s.interactive, DISCRETE_HUE.autoAccept, isDark);
30
+ const bashPink = deriveDiscrete(s.bashPink, s.error, DISCRETE_HUE.bashPink, isDark);
31
+ const accent = primary[isDark ? 7 : 8];
32
+ const successC = success[6];
33
+ const errorC = error[isDark ? 6 : 9];
34
+ const warningC = warning[isDark ? 6 : 6];
35
+ // The general action color (HeroUI `--accent` family): the same mid step (l≈0.58
36
+ // in both modes, the HeroUI `--accent` sweet spot) used by borderFocus/Selected,
37
+ // so it stays visible on the bg AND `on()` keeps button text legible. Verified
38
+ // ≥ AA-large vs bg across every built-in theme × mode (engine.test).
39
+ const interactiveC = interactive[8];
40
+ const t = {
41
+ // -- brand (23 TUI roles) --
42
+ accent,
43
+ success: successC,
44
+ error: errorC,
45
+ warning: warningC,
46
+ permission: info[isDark ? 7 : 5],
47
+ planMode,
48
+ autoAccept,
49
+ bashPink,
50
+ stall: shift(s.error, { l: isDark ? -0.12 : -0.1, c: 1.0 }),
51
+ userMessageBg: neutral[isDark ? 1 : 2],
52
+ // -- diff line/word backgrounds --
53
+ diffAddedBg: diffAdd[isDark ? 1 : 9],
54
+ diffRemovedBg: diffDelete[isDark ? 1 : 9],
55
+ diffAddedWordBg: diffAdd[isDark ? 3 : 7],
56
+ diffRemovedWordBg: diffDelete[isDark ? 3 : 7],
57
+ // -- syntax (9) --
58
+ synKeyword: content(info),
59
+ synString: content(success),
60
+ synComment: neutral[isDark ? 6 : 7],
61
+ synNumber: content(warning),
62
+ synFunction: content(interactive),
63
+ synType: content(warning),
64
+ synLiteral: content(diffAdd),
65
+ synTitle: content(interactive),
66
+ synAttr: content(warning),
67
+ // -- backgrounds / surfaces --
68
+ bgBase: neutral[0],
69
+ bgWeak: neutral[1],
70
+ bgStrong: isDark ? neutral[1] : neutral[0],
71
+ surfaceBase: neutral[1],
72
+ surfaceRaised: neutral[2],
73
+ surfaceInset: neutral[0],
74
+ surfaceHover: neutral[2],
75
+ surfaceActive: neutral[3],
76
+ // -- text --
77
+ textBase: neutral[10],
78
+ textWeak: neutral[8],
79
+ textWeaker: neutral[7],
80
+ textStrong: neutral[11],
81
+ // -- borders --
82
+ borderBase: neutral[isDark ? 4 : 6],
83
+ borderWeak: neutral[isDark ? 3 : 5],
84
+ borderStrong: neutral[isDark ? 6 : 7],
85
+ borderFocus: interactive[8],
86
+ borderSelected: interactive[8],
87
+ // -- inputs --
88
+ inputBase: isDark ? neutral[1] : neutral[0],
89
+ inputHover: neutral[2],
90
+ inputFocus: interactive[6],
91
+ inputDisabled: neutral[3],
92
+ // -- icons --
93
+ iconBase: neutral[9],
94
+ iconWeak: neutral[6],
95
+ iconStrong: neutral[11],
96
+ iconDisabled: neutral[isDark ? 5 : 6],
97
+ // -- buttons --
98
+ buttonPrimary: neutral[11],
99
+ buttonSecondary: isDark ? neutral[2] : neutral[0],
100
+ buttonGhostHover: neutral[2],
101
+ // -- action / interactive (HeroUI `--accent` family) --
102
+ interactive: interactiveC,
103
+ // -- diff surfaces / change-bar --
104
+ diffAddSurface: diffAdd[2],
105
+ diffDeleteSurface: diffDelete[2],
106
+ diffChangeBarAdd: diffAdd[isDark ? 7 : 6],
107
+ diffChangeBarDelete: diffDelete[isDark ? 7 : 6],
108
+ diffChangeBarModified: warning[isDark ? 7 : 6],
109
+ // -- markdown --
110
+ mdHeading: content(primary),
111
+ mdLink: content(interactive),
112
+ mdCode: content(success),
113
+ mdQuote: content(warning),
114
+ mdStrong: content(primary),
115
+ mdEmph: content(warning),
116
+ // -- state-on (computed from the resolved fills) --
117
+ textOnAccent: on(accent),
118
+ textOnSuccess: on(successC),
119
+ textOnError: on(errorC),
120
+ textOnWarning: on(warningC),
121
+ textOnInteractive: on(interactiveC),
122
+ };
123
+ // Overrides win last (pinning).
124
+ if (variant.overrides) {
125
+ for (const [k, v] of Object.entries(variant.overrides)) {
126
+ t[k] = v;
127
+ }
128
+ }
129
+ // (codex R2 MUST) The state-on foregrounds must legibly contrast the FINAL
130
+ // fills, so recompute them AFTER overrides. The `default` theme pins
131
+ // accent/success/error/warning to legacy RGB, and `on()` run on the
132
+ // pre-override (generated) fill could pick the wrong black/white (e.g. white
133
+ // on the pinned light success → ~2.5:1, sub-WCAG). Skip when the fill isn't a
134
+ // hex (the ansi theme's `ansi:<name>` fills are TUI-only, never read on the
135
+ // web) or when the theme gave an explicit `textOn*` override.
136
+ const STATE_ON = [
137
+ ["textOnAccent", "accent"],
138
+ ["textOnSuccess", "success"],
139
+ ["textOnError", "error"],
140
+ ["textOnWarning", "warning"],
141
+ ["textOnInteractive", "interactive"],
142
+ ];
143
+ for (const [onToken, fillToken] of STATE_ON) {
144
+ if (variant.overrides && onToken in variant.overrides)
145
+ continue;
146
+ const fill = t[fillToken];
147
+ if (typeof fill === "string" && fill.startsWith("#"))
148
+ t[onToken] = on(fill);
149
+ }
150
+ return t;
151
+ }
152
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/theme/resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) Seed-theme types + a dependency-free validator for
3
+ * user/custom themes. 9 semantic seeds + 3 optional "expressive" seeds (plan/
4
+ * accept/bash discrete hues) `[11.4]`. Mirrors opencode's `ThemeSeedColors` +
5
+ * `desktop-theme.schema.json` shape, trimmed to chances' needs. Pure; no `node:*`,
6
+ * no `zod` (ui-core keeps a minimal dep set — hand-rolled validation instead).
7
+ */
8
+ import { type ChancesTokenName } from "./tokens.js";
9
+ /** `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`. */
10
+ export type HexColor = `#${string}`;
11
+ /** A token value: a hex color, an `ansi:<name>` (TUI 16-color), or a `var(--…)` ref. */
12
+ export type ColorValue = HexColor | `ansi:${string}` | `var(--${string})`;
13
+ export interface OklchColor {
14
+ /** Lightness 0–1. */
15
+ l: number;
16
+ /** Chroma 0–~0.4. */
17
+ c: number;
18
+ /** Hue 0–360. */
19
+ h: number;
20
+ }
21
+ /** The 9 semantic seeds (+3 optional discrete-hue seeds). A theme variant
22
+ * (light or dark) is fully described by these + an optional override map. */
23
+ export interface ThemeSeeds {
24
+ neutral: HexColor;
25
+ primary: HexColor;
26
+ success: HexColor;
27
+ warning: HexColor;
28
+ error: HexColor;
29
+ info: HexColor;
30
+ interactive: HexColor;
31
+ diffAdd: HexColor;
32
+ diffDelete: HexColor;
33
+ /** Discrete visual facts; omitted ⇒ derived via the documented shift formula
34
+ * (with a Δh gate, see resolve.ts / engine.test.ts). */
35
+ planMode?: HexColor;
36
+ autoAccept?: HexColor;
37
+ bashPink?: HexColor;
38
+ }
39
+ export interface ChancesThemeVariant {
40
+ seeds: ThemeSeeds;
41
+ /** Pin specific resolved tokens to exact values (opencode `overrides`). The
42
+ * built-in `default` theme uses this to pin every claude-code visual-language
43
+ * role to today's RGB ⇒ zero TUI regression (7.3 §2.5). */
44
+ overrides?: Partial<Record<ChancesTokenName, ColorValue>>;
45
+ }
46
+ export interface ChancesTheme {
47
+ /** Slug `^[a-z0-9-]+$`. */
48
+ id: string;
49
+ /** Human-readable name. */
50
+ name: string;
51
+ light: ChancesThemeVariant;
52
+ dark: ChancesThemeVariant;
53
+ /** Which surfaces this theme is offered on (e.g. `ansi` is TUI-only). Default
54
+ * = both. */
55
+ surfaces?: ("tui" | "web")[];
56
+ }
57
+ /** Parse + validate a user theme JSON (from `~/.chances/themes/<id>.json`).
58
+ * Throws a descriptive Error on any malformed field. */
59
+ export declare function parseCustomTheme(json: unknown): ChancesTheme;
60
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/theme/schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEhE,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;AACpC,wFAAwF;AACxF,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,MAAM,EAAE,GAAG,SAAS,MAAM,GAAG,CAAC;AAE1E,MAAM,WAAW,UAAU;IACzB,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,iBAAiB;IACjB,CAAC,EAAE,MAAM,CAAC;CACX;AAED;8EAC8E;AAC9E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,QAAQ,CAAC;IAClB,OAAO,EAAE,QAAQ,CAAC;IAClB,OAAO,EAAE,QAAQ,CAAC;IAClB,OAAO,EAAE,QAAQ,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,QAAQ,CAAC;IACtB,OAAO,EAAE,QAAQ,CAAC;IAClB,UAAU,EAAE,QAAQ,CAAC;IACrB;6DACyD;IACzD,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,UAAU,CAAC;IAClB;;gEAE4D;IAC5D,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,mBAAmB,CAAC;IAC3B,IAAI,EAAE,mBAAmB,CAAC;IAC1B;kBACc;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC;CAC9B;AAqDD;yDACyD;AACzD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAc5D"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) Seed-theme types + a dependency-free validator for
3
+ * user/custom themes. 9 semantic seeds + 3 optional "expressive" seeds (plan/
4
+ * accept/bash discrete hues) `[11.4]`. Mirrors opencode's `ThemeSeedColors` +
5
+ * `desktop-theme.schema.json` shape, trimmed to chances' needs. Pure; no `node:*`,
6
+ * no `zod` (ui-core keeps a minimal dep set — hand-rolled validation instead).
7
+ */
8
+ import { ALL_TOKENS } from "./tokens.js";
9
+ const HEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
10
+ // (codex R2 MUST) hex branch must match HEX exactly — `{3,8}` wrongly accepted
11
+ // invalid 5/7-digit hex (`#12345`), which then leaked into `--ch-*` vars.
12
+ const COLOR_VALUE = /^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|ansi:[a-zA-Z]+|var\(--[a-z0-9-]+\))$/;
13
+ /** Every valid override key — an unknown key would be copied verbatim by
14
+ * `resolveThemeVariant` and emitted as an arbitrary `--ch-*` var (codex R2 MUST). */
15
+ const TOKEN_NAMES = new Set(ALL_TOKENS);
16
+ const REQUIRED_SEEDS = [
17
+ "neutral",
18
+ "primary",
19
+ "success",
20
+ "warning",
21
+ "error",
22
+ "info",
23
+ "interactive",
24
+ "diffAdd",
25
+ "diffDelete",
26
+ ];
27
+ function isHex(v) {
28
+ return typeof v === "string" && HEX.test(v);
29
+ }
30
+ function validateVariant(v, where) {
31
+ if (typeof v !== "object" || v === null)
32
+ throw new Error(`${where}: expected an object`);
33
+ const seeds = v.seeds;
34
+ if (typeof seeds !== "object" || seeds === null)
35
+ throw new Error(`${where}.seeds: expected an object`);
36
+ for (const k of REQUIRED_SEEDS) {
37
+ if (!isHex(seeds[k])) {
38
+ throw new Error(`${where}.seeds.${k}: expected a hex color (got ${JSON.stringify(seeds[k])})`);
39
+ }
40
+ }
41
+ for (const k of ["planMode", "autoAccept", "bashPink"]) {
42
+ const val = seeds[k];
43
+ if (val !== undefined && !isHex(val))
44
+ throw new Error(`${where}.seeds.${k}: expected a hex color or omitted`);
45
+ }
46
+ const overrides = v.overrides;
47
+ if (overrides !== undefined) {
48
+ if (typeof overrides !== "object" || overrides === null)
49
+ throw new Error(`${where}.overrides: expected an object`);
50
+ for (const [k, val] of Object.entries(overrides)) {
51
+ if (!TOKEN_NAMES.has(k)) {
52
+ throw new Error(`${where}.overrides.${k}: unknown token (must be a ChancesTokenName)`);
53
+ }
54
+ if (typeof val !== "string" || !COLOR_VALUE.test(val)) {
55
+ throw new Error(`${where}.overrides.${k}: expected a hex / ansi: / var(--…) color (got ${JSON.stringify(val)})`);
56
+ }
57
+ }
58
+ }
59
+ return v;
60
+ }
61
+ /** Parse + validate a user theme JSON (from `~/.chances/themes/<id>.json`).
62
+ * Throws a descriptive Error on any malformed field. */
63
+ export function parseCustomTheme(json) {
64
+ if (typeof json !== "object" || json === null)
65
+ throw new Error("theme: expected an object");
66
+ const t = json;
67
+ if (typeof t.id !== "string" || !/^[a-z0-9-]+$/.test(t.id))
68
+ throw new Error("theme.id: expected a slug ^[a-z0-9-]+$");
69
+ if (typeof t.name !== "string" || t.name.length === 0)
70
+ throw new Error("theme.name: expected a non-empty string");
71
+ const light = validateVariant(t.light, "theme.light");
72
+ const dark = validateVariant(t.dark, "theme.dark");
73
+ const surfaces = t.surfaces;
74
+ if (surfaces !== undefined) {
75
+ if (!Array.isArray(surfaces) || surfaces.some((s) => s !== "tui" && s !== "web")) {
76
+ throw new Error('theme.surfaces: expected an array of "tui"|"web"');
77
+ }
78
+ }
79
+ return { id: t.id, name: t.name, light, dark, surfaces: surfaces };
80
+ }
81
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/theme/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * (7.3 / task 03 — W0) The curated ~70-token semantic set the seed engine
3
+ * resolves to (`[11.4]` targets ~100; we stay lean). Two consumer projections
4
+ * (project.ts): the TUI reads the 23 `TUI_ROLES` (→ chalk-rgb `Theme`), the web
5
+ * reads the whole set (→ `--ch-*` CSS vars). Token names are camelCase so the 23
6
+ * brand roles align 1:1 with the existing `Theme` field names (trivial
7
+ * projection) and the web `kebab()` deriver keeps emitting the SAME `--ch-*`
8
+ * names as today (zero web-theme regression).
9
+ */
10
+ /** The 23 brand/diff/syntax roles the TUI consumes — names === `Theme` fields.
11
+ * The `default` theme PINS all of these to today's exact RGB (zero regression). */
12
+ export declare const TUI_ROLES: readonly ["accent", "success", "error", "warning", "permission", "planMode", "autoAccept", "bashPink", "diffAddedBg", "diffRemovedBg", "diffAddedWordBg", "diffRemovedWordBg", "userMessageBg", "stall", "synKeyword", "synString", "synComment", "synNumber", "synFunction", "synType", "synLiteral", "synTitle", "synAttr"];
13
+ /** Web-only chrome tokens generated from the neutral/interactive/diff scales
14
+ * (NEW — no existing snapshot, so generation is unconstrained beyond WCAG). */
15
+ export declare const WEB_CHROME_TOKENS: readonly ["bgBase", "bgWeak", "bgStrong", "surfaceBase", "surfaceRaised", "surfaceInset", "surfaceHover", "surfaceActive", "textBase", "textWeak", "textWeaker", "textStrong", "borderBase", "borderWeak", "borderStrong", "borderFocus", "borderSelected", "inputBase", "inputHover", "inputFocus", "inputDisabled", "iconBase", "iconWeak", "iconStrong", "iconDisabled", "buttonPrimary", "buttonSecondary", "buttonGhostHover", "interactive", "diffAddSurface", "diffDeleteSurface", "diffChangeBarAdd", "diffChangeBarDelete", "diffChangeBarModified", "mdHeading", "mdLink", "mdCode", "mdQuote", "mdStrong", "mdEmph", "textOnAccent", "textOnSuccess", "textOnError", "textOnWarning", "textOnInteractive"];
16
+ export type TuiRole = (typeof TUI_ROLES)[number];
17
+ export type WebChromeToken = (typeof WEB_CHROME_TOKENS)[number];
18
+ export type ChancesTokenName = TuiRole | WebChromeToken;
19
+ /** All token names, for exhaustiveness checks (e.g. tokens.css `@theme` parity). */
20
+ export declare const ALL_TOKENS: readonly ChancesTokenName[];
21
+ /** Grouped for docs / @theme generation. */
22
+ export declare const TOKEN_GROUPS: {
23
+ brand: ("error" | "accent" | "success" | "warning" | "permission" | "planMode" | "autoAccept" | "bashPink" | "userMessageBg" | "stall")[];
24
+ diff: ("diffAddedBg" | "diffRemovedBg" | "diffAddedWordBg" | "diffRemovedWordBg")[];
25
+ syntax: ("synKeyword" | "synString" | "synComment" | "synNumber" | "synFunction" | "synType" | "synLiteral" | "synTitle" | "synAttr")[];
26
+ background: ("bgBase" | "bgWeak" | "bgStrong")[];
27
+ surface: ("surfaceBase" | "surfaceRaised" | "surfaceInset" | "surfaceHover" | "surfaceActive")[];
28
+ text: ("textBase" | "textWeak" | "textWeaker" | "textStrong")[];
29
+ border: ("borderBase" | "borderWeak" | "borderStrong" | "borderFocus" | "borderSelected")[];
30
+ input: ("inputBase" | "inputHover" | "inputFocus" | "inputDisabled")[];
31
+ icon: ("iconBase" | "iconWeak" | "iconStrong" | "iconDisabled")[];
32
+ button: ("buttonPrimary" | "buttonSecondary" | "buttonGhostHover")[];
33
+ action: "interactive"[];
34
+ diffSurface: ("diffAddSurface" | "diffDeleteSurface" | "diffChangeBarAdd" | "diffChangeBarDelete" | "diffChangeBarModified")[];
35
+ markdown: ("mdHeading" | "mdLink" | "mdCode" | "mdQuote" | "mdStrong" | "mdEmph")[];
36
+ stateOn: ("textOnAccent" | "textOnSuccess" | "textOnError" | "textOnWarning" | "textOnInteractive")[];
37
+ };
38
+ /** A fully resolved theme variant: every token → a concrete color value. */
39
+ export type ChancesTokens = Record<ChancesTokenName, import("./schema.js").ColorValue>;
40
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;oFACoF;AACpF,eAAO,MAAM,SAAS,+TAwBZ,CAAC;AAEX;gFACgF;AAChF,eAAO,MAAM,iBAAiB,urBAyDpB,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AACjD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,cAAc,CAAC;AAExD,oFAAoF;AACpF,eAAO,MAAM,UAAU,EAAE,SAAS,gBAAgB,EAAyC,CAAC;AAE5F,4CAA4C;AAC5C,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;CAeqB,CAAC;AAE/C,4EAA4E;AAC5E,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,gBAAgB,EAAE,OAAO,aAAa,EAAE,UAAU,CAAC,CAAC"}