@takazudo/zudo-doc 2.5.1 → 3.0.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.
@@ -6,7 +6,10 @@ import type { Settings } from "../settings.js";
6
6
  import { type ColorScheme } from "../color-scheme-utils.js";
7
7
  import { createBodyEndIslands } from "../doc-body-end-islands/index.js";
8
8
  /** Package-default color scheme used when `colorSchemes` is `null` or a key is
9
- * missing. Exported so `createChrome` keeps the identical fallback. */
9
+ * missing. Exported so `createChrome` keeps the identical fallback. Base roles
10
+ * follow the epic's Default Dark defaults on the minimized 5-base ramp
11
+ * (bg={base:4}, fg={base:0}, selBg={base:2}, selFg={base:0}); semantics use
12
+ * `SEMANTIC_RAMP_DEFAULTS`. */
10
13
  export declare const DEFAULT_SCHEME: ColorScheme;
11
14
  /** Package no-op DocHistory island stub — renders an empty fragment (the
12
15
  * `DocHistoryComponent` contract requires a VNode, not null). */
@@ -2,7 +2,8 @@ import { Fragment, jsx } from "preact/jsx-runtime";
2
2
  import { createComposeMetaTitle } from "../compose-meta-title/index.js";
3
3
  import {
4
4
  generateCssCustomProperties,
5
- generateLightDarkCssProperties
5
+ generateLightDarkCssProperties,
6
+ SEMANTIC_RAMP_DEFAULTS
6
7
  } from "../color-scheme-utils.js";
7
8
  import { createBodyEndIslands } from "../doc-body-end-islands/index.js";
8
9
  import { SearchWidget } from "../search-widget/index.js";
@@ -22,31 +23,35 @@ import {
22
23
  getThemeDefaultMode as getThemeDefaultModeBase
23
24
  } from "../nav-data-prep/index.js";
24
25
  import { buildSidebarForSection } from "../sidebar-utils/index.js";
25
- const GREY_RAMP = [
26
- "oklch(0.000 0.000 0.00)",
27
- "oklch(0.218 0.000 0.00)",
28
- "oklch(0.321 0.000 0.00)",
29
- "oklch(0.420 0.000 0.00)",
30
- "oklch(0.510 0.000 0.00)",
31
- "oklch(0.600 0.000 0.00)",
32
- "oklch(0.683 0.000 0.00)",
33
- "oklch(0.767 0.000 0.00)",
34
- "oklch(0.845 0.000 0.00)",
35
- "oklch(0.885 0.000 0.00)",
36
- "oklch(0.925 0.000 0.00)",
37
- "oklch(0.961 0.000 0.00)",
38
- "oklch(0.682 0.206 24.43)",
39
- "oklch(0.871 0.2195 148.02)",
40
- "oklch(0.955 0.134 112.76)",
41
- "oklch(0.883 0.0934 212.85)"
42
- ];
26
+ const GREY_RAMPS = {
27
+ base: [
28
+ "oklch(0.965 0.000 0)",
29
+ "oklch(0.705 0.000 0)",
30
+ "oklch(0.480 0.000 0)",
31
+ "oklch(0.300 0.000 0)",
32
+ "oklch(0.185 0.000 0)"
33
+ ],
34
+ accent: [
35
+ "oklch(0.755 0.000 0)",
36
+ "oklch(0.700 0.000 0)",
37
+ "oklch(0.470 0.000 0)"
38
+ ],
39
+ state: {
40
+ danger: "oklch(0.640 0.170 25)",
41
+ success: "oklch(0.680 0.145 145)",
42
+ warning: "oklch(0.760 0.135 82)",
43
+ info: "oklch(0.680 0.130 245)"
44
+ }
45
+ };
43
46
  const DEFAULT_SCHEME = {
44
- background: "oklch(0.000 0.000 0.00)",
45
- foreground: "oklch(1.000 0.000 0.00)",
46
- cursor: "oklch(1.000 0.000 0.00)",
47
- selectionBg: "oklch(0.387 0.000 0.00)",
48
- selectionFg: "oklch(1.000 0.000 0.00)",
49
- palette: GREY_RAMP
47
+ ramps: GREY_RAMPS,
48
+ map: {
49
+ bg: { base: 4 },
50
+ fg: { base: 0 },
51
+ selectionBg: { base: 2 },
52
+ selectionFg: { base: 0 },
53
+ semantic: { ...SEMANTIC_RAMP_DEFAULTS }
54
+ }
50
55
  };
51
56
  function DocHistoryStub(_props) {
52
57
  return /* @__PURE__ */ jsx(Fragment, {});
@@ -1,113 +1,161 @@
1
1
  /**
2
- * Color-scheme MECHANISM — palette → `--zd-*` CSS custom-property injection.
2
+ * Color-scheme MECHANISM — ramp → `--palette-*` / `--zd-*` CSS custom-property
3
+ * injection.
3
4
  *
4
5
  * This module ships the pure computation layer used by every project that
5
6
  * builds on @takazudo/zudo-doc. It is DATA-free: callers pass in their own
6
7
  * color-scheme manifest and settings.
7
8
  *
9
+ * Ramp-native model (Color Ramp Restructure — zudolab/zudo-doc#2584; minimized
10
+ * to 5/3 in #2602). A `ColorScheme` is `{ ramps, map }`:
11
+ * - `ramps` — the shared Tier-1 source of truth: a warm-neutral `base` ramp
12
+ * (5 stops, index 0 = lightest), an `accent` ramp (3 stops), and 4 `state`
13
+ * colors. Emitted verbatim as `--palette-{group}-{n}`.
14
+ * - `map` — the per-mode Tier-2 wiring: which ramp stop (or literal OKLCH)
15
+ * each UI role (`--zd-*`) points at, via a `RampRef`.
16
+ *
17
+ * The legacy ghostty 16-slot palette (`--zd-0..15`), `cursor`, `shikiTheme`,
18
+ * and the numeric `ColorRef` were dropped in that epic.
19
+ *
8
20
  * Moved from the host's `src/config/color-scheme-utils.ts` as part of the
9
- * package-first migration (S9a zudolab/zudo-doc#2333). The DATA (palette
10
- * values, scheme names, `colorMode` settings) stays project-side.
21
+ * package-first migration (S9a zudolab/zudo-doc#2333). The DATA (ramp values,
22
+ * scheme names, `colorMode` settings) stays project-side.
23
+ */
24
+ /** A color literal — an `oklch(…)` (or any valid CSS color) string. */
25
+ export type OKLCH = string;
26
+ /** The four semantic state colors that live on their own ramp. */
27
+ export type StateRole = "danger" | "success" | "warning" | "info";
28
+ /** Canonical order of the state ramp — used for `--palette-state-*` emit. */
29
+ export declare const STATE_ROLES: readonly StateRole[];
30
+ /**
31
+ * A reference into the shared ramps, resolved by `resolveRampRef`:
32
+ * - `{ base: n }` → `ramps.base[n]`
33
+ * - `{ accent: n }` → `ramps.accent[n]`
34
+ * - `{ state: r }` → `ramps.state[r]`
35
+ * - a bare string → a literal OKLCH value, returned as-is
11
36
  */
12
- /** A color reference: palette index (number) or direct color value (string) */
13
- export type ColorRef = number | string;
37
+ export type RampRef = {
38
+ base: number;
39
+ } | {
40
+ accent: number;
41
+ } | {
42
+ state: StateRole;
43
+ } | OKLCH;
44
+ /** The shared Tier-1 ramps. Values are identical across light/dark modes; the
45
+ * per-mode differences live in `ModeMap`, not here. */
46
+ export interface Ramps {
47
+ /** Warm-neutral base ramp — 5 entries, index 0 = lightest. */
48
+ base: OKLCH[];
49
+ /** Accent ramp — 3 entries. */
50
+ accent: OKLCH[];
51
+ /** The four state colors. */
52
+ state: Record<StateRole, OKLCH>;
53
+ }
54
+ /** The 23 semantic roles that map onto `--zd-*` custom properties. */
55
+ export type SemanticKey = "surface" | "muted" | "accent" | "accentHover" | "codeBg" | "codeFg" | "success" | "danger" | "warning" | "info" | "mermaidNodeBg" | "mermaidText" | "mermaidLine" | "mermaidLabelBg" | "mermaidNoteBg" | "chatUserBg" | "chatUserText" | "chatAssistantBg" | "chatAssistantText" | "imageOverlayBg" | "imageOverlayFg" | "matchedKeywordBg" | "matchedKeywordFg";
56
+ /** Canonical order of the 23 semantic roles — used for `--zd-*` emit. */
57
+ export declare const SEMANTIC_KEYS: readonly SemanticKey[];
58
+ /** The per-mode wiring: each UI role points at a ramp stop (or literal OKLCH)
59
+ * via a `RampRef`. Two `ColorScheme`s (light + dark) share `ramps` but carry
60
+ * their own `ModeMap`. */
61
+ export interface ModeMap {
62
+ bg: RampRef;
63
+ fg: RampRef;
64
+ selectionBg: RampRef;
65
+ selectionFg: RampRef;
66
+ semantic: Record<SemanticKey, RampRef>;
67
+ }
68
+ /** A complete color scheme — shared Tier-1 ramps + per-mode Tier-2 wiring. */
14
69
  export interface ColorScheme {
15
- background: ColorRef;
16
- foreground: ColorRef;
17
- cursor: ColorRef;
18
- selectionBg: ColorRef;
19
- selectionFg: ColorRef;
20
- palette: [
21
- string,
22
- string,
23
- string,
24
- string,
25
- string,
26
- string,
27
- string,
28
- string,
29
- string,
30
- string,
31
- string,
32
- string,
33
- string,
34
- string,
35
- string,
36
- string
37
- ];
38
- /** Optional, vestigial — no visible effect on rendering; carried only for
39
- * the zdtp design-token panel config envelope. See project CLAUDE.md. */
40
- shikiTheme?: string;
41
- /** Optional semantic overrides. When omitted, defaults from
42
- * `SEMANTIC_DEFAULTS` are used (surface=p0, muted=p8, accent=p5, …). */
43
- semantic?: {
44
- surface?: ColorRef;
45
- muted?: ColorRef;
46
- accent?: ColorRef;
47
- accentHover?: ColorRef;
48
- codeBg?: ColorRef;
49
- codeFg?: ColorRef;
50
- success?: ColorRef;
51
- danger?: ColorRef;
52
- warning?: ColorRef;
53
- info?: ColorRef;
54
- mermaidNodeBg?: ColorRef;
55
- mermaidText?: ColorRef;
56
- mermaidLine?: ColorRef;
57
- mermaidLabelBg?: ColorRef;
58
- mermaidNoteBg?: ColorRef;
59
- chatUserBg?: ColorRef;
60
- chatUserText?: ColorRef;
61
- chatAssistantBg?: ColorRef;
62
- chatAssistantText?: ColorRef;
63
- /** UI chrome over user images — enlarge/close overlay buttons */
64
- imageOverlayBg?: ColorRef;
65
- imageOverlayFg?: ColorRef;
66
- /** <mark> highlight for matched keywords in search results */
67
- matchedKeywordBg?: ColorRef;
68
- matchedKeywordFg?: ColorRef;
70
+ ramps: Ramps;
71
+ map: ModeMap;
72
+ }
73
+ /** Default per-mode semantic wiring (Default Dark reference — see epic #2584).
74
+ * Scheme authors spread this into `map.semantic` and override individual roles
75
+ * where a mode needs a different stop (or a per-mode AA-tuned literal). */
76
+ export declare const SEMANTIC_RAMP_DEFAULTS: Record<SemanticKey, RampRef>;
77
+ export declare const SEMANTIC_CSS_NAMES: Record<SemanticKey, string>;
78
+ /**
79
+ * Resolve a `RampRef` to a concrete color string against the given `ramps`.
80
+ * - `{ base: n }` → `ramps.base[n]`
81
+ * - `{ accent: n }` → `ramps.accent[n]`
82
+ * - `{ state: r }` → `ramps.state[r]`
83
+ * - a bare string → the literal OKLCH value, returned as-is
84
+ *
85
+ * Throws on an out-of-range ramp index — a ramp reference to a stop that does
86
+ * not exist is a programming/authoring error and must fail loud rather than
87
+ * silently emit an invalid CSS value.
88
+ */
89
+ export declare function resolveRampRef(ref: RampRef, ramps: Ramps): string;
90
+ /** Resolve every semantic role to a concrete color string via the scheme's
91
+ * per-mode `map.semantic` wiring. Returns all 23 keys. */
92
+ export declare function resolveSemanticColors(scheme: ColorScheme): Record<SemanticKey, string>;
93
+ export interface TierItem {
94
+ /** Stable id used as the Record key in persisted state (e.g. `surface`). */
95
+ id: string;
96
+ /** CSS custom property name written to `:root` (e.g. `--zd-surface`). */
97
+ cssVar: string;
98
+ /** Display label in the panel UI. */
99
+ label: string;
100
+ /** Default value — a panel-encoded ramp reference or literal OKLCH string. */
101
+ default: string;
102
+ /** Always the OKLCH color picker for semantic-tier rows. */
103
+ type: {
104
+ kind: "color";
105
+ format: "oklch";
69
106
  };
70
107
  }
71
- /** Default mapping: semantic token name → palette index */
72
- export declare const SEMANTIC_DEFAULTS: Record<string, number>;
73
- export declare const SEMANTIC_CSS_NAMES: Record<string, string>;
74
- /** Resolve a ColorRef to a concrete color string.
75
- * - number → palette[value]
76
- * - string → used as-is
77
- * - undefined → fallback */
78
- export declare function resolveColor(value: ColorRef | undefined, palette: string[], fallback: string): string;
79
- /** Resolve semantic colors with fallbacks to default palette slots */
80
- export declare function resolveSemanticColors(scheme: ColorScheme): {
81
- surface: string;
82
- muted: string;
83
- accent: string;
84
- accentHover: string;
85
- codeBg: string;
86
- codeFg: string;
87
- success: string;
88
- danger: string;
89
- warning: string;
90
- info: string;
91
- mermaidNodeBg: string;
92
- mermaidText: string;
93
- mermaidLine: string;
94
- mermaidLabelBg: string;
95
- mermaidNoteBg: string;
96
- chatUserBg: string;
97
- chatUserText: string;
98
- chatAssistantBg: string;
99
- chatAssistantText: string;
100
- imageOverlayBg: string;
101
- imageOverlayFg: string;
102
- matchedKeywordBg: string;
103
- matchedKeywordFg: string;
104
- };
105
- /** Convert a ColorScheme to a flat list of [cssProperty, value] pairs.
106
- * The result is the exact set of `--zd-*` custom properties that the
107
- * ColorSchemeProvider emits onto `:root`. */
108
- export declare function schemeToCssPairs(scheme: ColorScheme): [string, string][];
109
108
  /**
110
- * Generate the `:root { --zd-* }` CSS block for a single active color scheme.
109
+ * Encode a `RampRef` to the explicit `tierId:itemId` form the design-token
110
+ * panel's grouped ramp picker (`referencesRamps`) expects as an item default:
111
+ * - `{ base: n }` → `'base:base-n'`
112
+ * - `{ accent: n }` → `'accent:accent-n'`
113
+ * - `{ state: r }` → `'state:state-r'`
114
+ * - a bare string → returned as-is (a literal OKLCH/hex passthrough)
115
+ *
116
+ * Always the explicit form (independent of `referencesRamps` declaration
117
+ * order) — see item-id contract in `buildRampTiers`
118
+ * (`src/config/design-token-panel-config.ts`): tier ids `base`/`accent`/`state`,
119
+ * item ids `base-0..4`/`accent-0..2`/`state-{danger,success,warning,info}`.
120
+ */
121
+ export declare function rampRefToPanelDefault(ref: RampRef): string;
122
+ /**
123
+ * Build the 27-row mode-scoped semantic tier's `items` for the design-token
124
+ * panel, from a single (already mode-resolved) `ColorScheme`: 4 base roles
125
+ * (`bg`, `fg`, `selection-bg`, `selection-fg`) followed by the 23
126
+ * `SEMANTIC_KEYS` rows, in that order. Every row's `default` is
127
+ * `rampRefToPanelDefault` of the scheme's `map` entry for that role — never a
128
+ * resolved literal color — so the panel's grouped ramp dropdown can decode it
129
+ * back to a ramp reference (the no-snapping guarantee: a scheme with no
130
+ * persisted override always round-trips through the panel to the exact same
131
+ * ramp stop, not a resolved color that happens to match).
132
+ */
133
+ export declare function buildSemanticTierItems(scheme: ColorScheme): TierItem[];
134
+ /** Which tiers a `schemeToCssPairs` call emits:
135
+ * - `"all"` (default) — base roles + `--palette-*` + `--zd-*` semantic
136
+ * - `"palette"` — only the bare Tier-1 `--palette-*` pairs
137
+ * - `"roles"` — base roles + the 23 `--zd-*` semantic pairs
138
+ *
139
+ * The split lets `generateLightDarkCssProperties` emit the shared `--palette-*`
140
+ * once (bare) while wrapping the per-mode `--zd-*` roles in `light-dark(…)`.
141
+ * (A literal `"light"|"dark"` param would be inert here — each `ColorScheme`'s
142
+ * own `map` already encodes its mode.) */
143
+ export type CssEmitScope = "all" | "palette" | "roles";
144
+ /**
145
+ * Convert a `ColorScheme` to a flat list of `[cssProperty, value]` pairs.
146
+ *
147
+ * The full (`"all"`) set is the exact set of custom properties the
148
+ * ColorSchemeProvider emits onto `:root`:
149
+ * - base roles: `--zd-bg`, `--zd-fg`, `--zd-selection-bg`, `--zd-selection-fg`
150
+ * - Tier-1 ramps: `--palette-base-0..4`, `--palette-accent-0..2`,
151
+ * `--palette-state-{danger,success,warning,info}`
152
+ * - the 23 Tier-2 `--zd-{role}` semantic tokens
153
+ *
154
+ * It intentionally does NOT emit the retired `--zd-0..15` slots or `--zd-cursor`.
155
+ */
156
+ export declare function schemeToCssPairs(scheme: ColorScheme, scope?: CssEmitScope): [string, string][];
157
+ /**
158
+ * Generate the `:root { … }` CSS block for a single active color scheme.
111
159
  *
112
160
  * The caller passes in the active `ColorScheme` object — resolved from the
113
161
  * project's `colorSchemes` map and `settings.colorScheme`. This function is
@@ -115,11 +163,14 @@ export declare function schemeToCssPairs(scheme: ColorScheme): [string, string][
115
163
  */
116
164
  export declare function generateCssCustomProperties(scheme: ColorScheme): string;
117
165
  /**
118
- * Generate the `:root { --zd-*: light-dark(…, ); }` CSS block for a
119
- * light/dark pair.
166
+ * Generate the `:root { … }` CSS block for a light/dark pair.
167
+ *
168
+ * Tier-1 `--palette-*` properties are shared across modes (the ramps carry the
169
+ * same values in both schemes), so they are emitted ONCE, bare. The per-mode
170
+ * `--zd-*` roles (base roles + semantic) are wrapped in `light-dark(L, D)`.
120
171
  *
121
172
  * The caller passes in the `light` and `dark` `ColorScheme` objects — resolved
122
- * from the project's `colorSchemes` map and `settings.colorMode`. This
123
- * function is DATA-free.
173
+ * from the project's `colorSchemes` map and `settings.colorMode`. This function
174
+ * is DATA-free.
124
175
  */
125
176
  export declare function generateLightDarkCssProperties(light: ColorScheme, dark: ColorScheme): string;
@@ -1,27 +1,58 @@
1
- const SEMANTIC_DEFAULTS = {
2
- surface: 0,
3
- muted: 8,
4
- accent: 5,
5
- accentHover: 14,
6
- codeBg: 10,
7
- codeFg: 11,
8
- success: 2,
9
- danger: 1,
10
- warning: 3,
11
- info: 4,
12
- mermaidNodeBg: 9,
13
- mermaidText: 11,
14
- mermaidLine: 8,
15
- mermaidLabelBg: 10,
16
- mermaidNoteBg: 0,
17
- chatUserBg: 5,
18
- chatUserText: 9,
19
- chatAssistantBg: 9,
20
- chatAssistantText: 11,
21
- imageOverlayBg: 0,
22
- imageOverlayFg: 11,
23
- matchedKeywordBg: 3,
24
- matchedKeywordFg: 15
1
+ const STATE_ROLES = ["danger", "success", "warning", "info"];
2
+ const SEMANTIC_KEYS = [
3
+ "surface",
4
+ "muted",
5
+ "accent",
6
+ "accentHover",
7
+ "codeBg",
8
+ "codeFg",
9
+ "success",
10
+ "danger",
11
+ "warning",
12
+ "info",
13
+ "mermaidNodeBg",
14
+ "mermaidText",
15
+ "mermaidLine",
16
+ "mermaidLabelBg",
17
+ "mermaidNoteBg",
18
+ "chatUserBg",
19
+ "chatUserText",
20
+ "chatAssistantBg",
21
+ "chatAssistantText",
22
+ "imageOverlayBg",
23
+ "imageOverlayFg",
24
+ "matchedKeywordBg",
25
+ "matchedKeywordFg"
26
+ ];
27
+ const SEMANTIC_RAMP_DEFAULTS = {
28
+ // Re-pointed for the 5-base / 3-accent minimized ramp (#2602); mirrors the
29
+ // Default Dark reference wiring (surface/chatAssistantBg/imageOverlayBg merged
30
+ // onto bg=b4). Every index is < 5 (base) / < 3 (accent).
31
+ surface: { base: 4 },
32
+ muted: { base: 1 },
33
+ accent: { accent: 1 },
34
+ accentHover: { accent: 0 },
35
+ codeBg: { base: 3 },
36
+ codeFg: { base: 0 },
37
+ success: { state: "success" },
38
+ danger: { state: "danger" },
39
+ warning: { state: "warning" },
40
+ info: { state: "info" },
41
+ mermaidNodeBg: { base: 3 },
42
+ mermaidText: { base: 0 },
43
+ mermaidLine: { base: 1 },
44
+ mermaidLabelBg: { base: 3 },
45
+ mermaidNoteBg: { base: 2 },
46
+ chatUserBg: { accent: 1 },
47
+ chatUserText: { base: 4 },
48
+ chatAssistantBg: { base: 4 },
49
+ chatAssistantText: { base: 0 },
50
+ imageOverlayBg: { base: 4 },
51
+ imageOverlayFg: { base: 0 },
52
+ // Search-result <mark> highlight — amber fill with dark text (the classic
53
+ // highlighter look, matching the shipped schemes). Kept as raw OKLCH literals.
54
+ matchedKeywordBg: "oklch(0.700 0.158 62)",
55
+ matchedKeywordFg: "oklch(0.300 0.003 65)"
25
56
  };
26
57
  const SEMANTIC_CSS_NAMES = {
27
58
  surface: "--zd-surface",
@@ -48,74 +79,106 @@ const SEMANTIC_CSS_NAMES = {
48
79
  matchedKeywordBg: "--zd-matched-keyword-bg",
49
80
  matchedKeywordFg: "--zd-matched-keyword-fg"
50
81
  };
51
- function resolveColor(value, palette, fallback) {
52
- if (value === void 0) return fallback;
53
- if (typeof value === "number") return palette[value] ?? fallback;
54
- return value;
82
+ function resolveRampRef(ref, ramps) {
83
+ if (typeof ref === "string") return ref;
84
+ if ("base" in ref) {
85
+ const color = ramps.base[ref.base];
86
+ if (color === void 0) {
87
+ throw new RangeError(`RampRef {base:${ref.base}} out of range (base ramp has ${ramps.base.length} stops)`);
88
+ }
89
+ return color;
90
+ }
91
+ if ("accent" in ref) {
92
+ const color = ramps.accent[ref.accent];
93
+ if (color === void 0) {
94
+ throw new RangeError(`RampRef {accent:${ref.accent}} out of range (accent ramp has ${ramps.accent.length} stops)`);
95
+ }
96
+ return color;
97
+ }
98
+ return ramps.state[ref.state];
55
99
  }
56
100
  function resolveSemanticColors(scheme) {
57
- const p = scheme.palette;
58
- return {
59
- surface: resolveColor(scheme.semantic?.surface, p, p[0]),
60
- muted: resolveColor(scheme.semantic?.muted, p, p[8]),
61
- accent: resolveColor(scheme.semantic?.accent, p, p[5]),
62
- accentHover: resolveColor(scheme.semantic?.accentHover, p, p[14]),
63
- codeBg: resolveColor(scheme.semantic?.codeBg, p, p[10]),
64
- codeFg: resolveColor(scheme.semantic?.codeFg, p, p[11]),
65
- success: resolveColor(scheme.semantic?.success, p, p[2]),
66
- danger: resolveColor(scheme.semantic?.danger, p, p[1]),
67
- warning: resolveColor(scheme.semantic?.warning, p, p[3]),
68
- info: resolveColor(scheme.semantic?.info, p, p[4]),
69
- mermaidNodeBg: resolveColor(scheme.semantic?.mermaidNodeBg, p, p[9]),
70
- mermaidText: resolveColor(scheme.semantic?.mermaidText, p, p[11]),
71
- mermaidLine: resolveColor(scheme.semantic?.mermaidLine, p, p[8]),
72
- mermaidLabelBg: resolveColor(scheme.semantic?.mermaidLabelBg, p, p[10]),
73
- mermaidNoteBg: resolveColor(scheme.semantic?.mermaidNoteBg, p, p[0]),
74
- chatUserBg: resolveColor(scheme.semantic?.chatUserBg, p, p[5]),
75
- chatUserText: resolveColor(scheme.semantic?.chatUserText, p, p[9]),
76
- chatAssistantBg: resolveColor(scheme.semantic?.chatAssistantBg, p, p[9]),
77
- chatAssistantText: resolveColor(scheme.semantic?.chatAssistantText, p, p[11]),
78
- imageOverlayBg: resolveColor(scheme.semantic?.imageOverlayBg, p, p[0]),
79
- imageOverlayFg: resolveColor(scheme.semantic?.imageOverlayFg, p, p[11]),
80
- matchedKeywordBg: resolveColor(scheme.semantic?.matchedKeywordBg, p, p[3]),
81
- matchedKeywordFg: resolveColor(scheme.semantic?.matchedKeywordFg, p, p[15])
82
- };
101
+ const { ramps, map } = scheme;
102
+ const out = {};
103
+ for (const key of SEMANTIC_KEYS) {
104
+ out[key] = resolveRampRef(map.semantic[key], ramps);
105
+ }
106
+ return out;
83
107
  }
84
- function schemeToCssPairs(scheme) {
85
- const p = scheme.palette;
86
- const sem = resolveSemanticColors(scheme);
87
- return [
88
- ["--zd-bg", resolveColor(scheme.background, p, p[0])],
89
- ["--zd-fg", resolveColor(scheme.foreground, p, p[15])],
90
- ["--zd-cursor", resolveColor(scheme.cursor, p, p[6])],
91
- // intentionally inert/vestigial: exposed in the design-token panel but no CSS rule consumes it (no caret-color wiring); mirrors shikiTheme's status
92
- ["--zd-sel-bg", resolveColor(scheme.selectionBg, p, resolveColor(scheme.background, p, p[0]))],
93
- ["--zd-sel-fg", resolveColor(scheme.selectionFg, p, resolveColor(scheme.foreground, p, p[15]))],
94
- ...p.map((color, i) => [`--zd-${i}`, color]),
95
- ["--zd-surface", sem.surface],
96
- ["--zd-muted", sem.muted],
97
- ["--zd-accent", sem.accent],
98
- ["--zd-accent-hover", sem.accentHover],
99
- ["--zd-code-bg", sem.codeBg],
100
- ["--zd-code-fg", sem.codeFg],
101
- ["--zd-success", sem.success],
102
- ["--zd-danger", sem.danger],
103
- ["--zd-warning", sem.warning],
104
- ["--zd-info", sem.info],
105
- ["--zd-mermaid-node-bg", sem.mermaidNodeBg],
106
- ["--zd-mermaid-text", sem.mermaidText],
107
- ["--zd-mermaid-line", sem.mermaidLine],
108
- ["--zd-mermaid-label-bg", sem.mermaidLabelBg],
109
- ["--zd-mermaid-note-bg", sem.mermaidNoteBg],
110
- ["--zd-chat-user-bg", sem.chatUserBg],
111
- ["--zd-chat-user-text", sem.chatUserText],
112
- ["--zd-chat-assistant-bg", sem.chatAssistantBg],
113
- ["--zd-chat-assistant-text", sem.chatAssistantText],
114
- ["--zd-image-overlay-bg", sem.imageOverlayBg],
115
- ["--zd-image-overlay-fg", sem.imageOverlayFg],
116
- ["--zd-matched-keyword-bg", sem.matchedKeywordBg],
117
- ["--zd-matched-keyword-fg", sem.matchedKeywordFg]
108
+ function rampRefToPanelDefault(ref) {
109
+ if (typeof ref === "string") return ref;
110
+ if ("base" in ref) return `base:base-${ref.base}`;
111
+ if ("accent" in ref) return `accent:accent-${ref.accent}`;
112
+ return `state:state-${ref.state}`;
113
+ }
114
+ function buildSemanticTierItems(scheme) {
115
+ const { map } = scheme;
116
+ const items = [
117
+ {
118
+ id: "bg",
119
+ cssVar: "--zd-bg",
120
+ label: "bg",
121
+ default: rampRefToPanelDefault(map.bg),
122
+ type: { kind: "color", format: "oklch" }
123
+ },
124
+ {
125
+ id: "fg",
126
+ cssVar: "--zd-fg",
127
+ label: "fg",
128
+ default: rampRefToPanelDefault(map.fg),
129
+ type: { kind: "color", format: "oklch" }
130
+ },
131
+ {
132
+ id: "selection-bg",
133
+ cssVar: "--zd-selection-bg",
134
+ label: "selection-bg",
135
+ default: rampRefToPanelDefault(map.selectionBg),
136
+ type: { kind: "color", format: "oklch" }
137
+ },
138
+ {
139
+ id: "selection-fg",
140
+ cssVar: "--zd-selection-fg",
141
+ label: "selection-fg",
142
+ default: rampRefToPanelDefault(map.selectionFg),
143
+ type: { kind: "color", format: "oklch" }
144
+ }
118
145
  ];
146
+ for (const key of SEMANTIC_KEYS) {
147
+ items.push({
148
+ id: key,
149
+ cssVar: SEMANTIC_CSS_NAMES[key],
150
+ label: key,
151
+ default: rampRefToPanelDefault(map.semantic[key]),
152
+ type: { kind: "color", format: "oklch" }
153
+ });
154
+ }
155
+ return items;
156
+ }
157
+ function schemeToCssPairs(scheme, scope = "all") {
158
+ const { ramps, map } = scheme;
159
+ const pairs = [];
160
+ if (scope === "all" || scope === "roles") {
161
+ pairs.push(
162
+ ["--zd-bg", resolveRampRef(map.bg, ramps)],
163
+ ["--zd-fg", resolveRampRef(map.fg, ramps)],
164
+ ["--zd-selection-bg", resolveRampRef(map.selectionBg, ramps)],
165
+ ["--zd-selection-fg", resolveRampRef(map.selectionFg, ramps)]
166
+ );
167
+ }
168
+ if (scope === "all" || scope === "palette") {
169
+ ramps.base.forEach((color, i) => pairs.push([`--palette-base-${i}`, color]));
170
+ ramps.accent.forEach((color, i) => pairs.push([`--palette-accent-${i}`, color]));
171
+ for (const role of STATE_ROLES) {
172
+ pairs.push([`--palette-state-${role}`, ramps.state[role]]);
173
+ }
174
+ }
175
+ if (scope === "all" || scope === "roles") {
176
+ const sem = resolveSemanticColors(scheme);
177
+ for (const key of SEMANTIC_KEYS) {
178
+ pairs.push([SEMANTIC_CSS_NAMES[key], sem[key]]);
179
+ }
180
+ }
181
+ return pairs;
119
182
  }
120
183
  function generateCssCustomProperties(scheme) {
121
184
  const pairs = schemeToCssPairs(scheme);
@@ -123,15 +186,19 @@ function generateCssCustomProperties(scheme) {
123
186
  return lines.join("\n");
124
187
  }
125
188
  function generateLightDarkCssProperties(light, dark) {
126
- const lightPairs = schemeToCssPairs(light);
127
- const darkPairs = schemeToCssPairs(dark);
128
- if (lightPairs.length !== darkPairs.length) {
129
- throw new Error(`Light scheme has ${lightPairs.length} properties but dark scheme has ${darkPairs.length}`);
189
+ const palettePairs = schemeToCssPairs(light, "palette");
190
+ const lightRoles = schemeToCssPairs(light, "roles");
191
+ const darkRoles = schemeToCssPairs(dark, "roles");
192
+ if (lightRoles.length !== darkRoles.length) {
193
+ throw new Error(`Light scheme has ${lightRoles.length} role properties but dark scheme has ${darkRoles.length}`);
130
194
  }
131
195
  const lines = [":root {", " color-scheme: light dark;"];
132
- for (let i = 0; i < lightPairs.length; i++) {
133
- const lightPair = lightPairs[i];
134
- const darkPair = darkPairs[i];
196
+ for (const [prop, value] of palettePairs) {
197
+ lines.push(` ${prop}: ${value};`);
198
+ }
199
+ for (let i = 0; i < lightRoles.length; i++) {
200
+ const lightPair = lightRoles[i];
201
+ const darkPair = darkRoles[i];
135
202
  if (!lightPair || !darkPair) continue;
136
203
  const [prop, lightVal] = lightPair;
137
204
  const darkVal = darkPair[1];
@@ -142,10 +209,14 @@ function generateLightDarkCssProperties(light, dark) {
142
209
  }
143
210
  export {
144
211
  SEMANTIC_CSS_NAMES,
145
- SEMANTIC_DEFAULTS,
212
+ SEMANTIC_KEYS,
213
+ SEMANTIC_RAMP_DEFAULTS,
214
+ STATE_ROLES,
215
+ buildSemanticTierItems,
146
216
  generateCssCustomProperties,
147
217
  generateLightDarkCssProperties,
148
- resolveColor,
218
+ rampRefToPanelDefault,
219
+ resolveRampRef,
149
220
  resolveSemanticColors,
150
221
  schemeToCssPairs
151
222
  };