@takazudo/zdtp 0.4.11 → 0.4.13

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/CHANGELOG.md CHANGED
@@ -1,6 +1,83 @@
1
1
  # Changelog
2
2
 
3
- ## 0.4.11
3
+ All notable changes to `@takazudo/zdtp` are documented in this file.
4
+
5
+ The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
6
+
7
+ ## [0.4.13] - 2026-08-29
8
+
9
+ ### Features
10
+
11
+ - feat(zdtp): normalize static CSS colors (20567e7)
12
+
13
+ ### Fixed
14
+
15
+ - fix(zdtp): expose readonly swatches as selectable (c3c8c28)
16
+ - fix(zdtp): restrict referencesRamps to Color tabs (8894399)
17
+ - fix(zdtp): enforce readonly Palette editing (5c2445c)
18
+ - fix(zdtp): guard palette validity transitions (73a59fb)
19
+ - fix(zdtp): preserve invalid palette slots as n-a (0e456e5)
20
+ - fix(doc): align preview with latest zfb (9cd81bb)
21
+
22
+ ### Other Changes
23
+
24
+ - Merge pull request [#636](https://github.com/Takazudo/zudo-design-token-panel/pull/636) from Takazudo/base/sweep-260829: fix(zdtp): correct Palette behavior and public contracts (db3ee47)
25
+ - docs(zdtp): document referencesRamps tab restriction (555b562)
26
+ - Merge pull request [#645](https://github.com/Takazudo/zudo-design-token-panel/pull/645) from Takazudo/base/sweep-260829-portable-contract-docs: fix(zdtp): align portable contracts and ramp validation (452fc32)
27
+ - Merge topic/630-reference-color-mode-docs (7abfa37)
28
+ - docs: clarify reference emission and color modes (f447a99)
29
+ - Merge topic/632-references-ramps-color-tabs (7df5b55)
30
+ - Merge topic/631-required-write-root-docs (ae76e2b)
31
+ - Merge topic/629-shipped-semantic-contract (0ba0294)
32
+ - docs(zdtp): sync shipped semantic tier contract (4bbc5d5)
33
+ - docs: require write-root in apply guidance (10c76b1)
34
+ - Merge pull request [#640](https://github.com/Takazudo/zudo-design-token-panel/pull/640) from Takazudo/base/sweep-260829-palette-correctness: fix(zdtp): correct Palette color and readonly behavior (36c0972)
35
+ - Merge branch 'topic/626-palette-readonly' into base/sweep-260829-palette-correctness (53575d9)
36
+ - Merge branch 'topic/625-palette-invalid-na' into base/sweep-260829-palette-correctness (73c2c6c)
37
+ - Merge branch 'topic/624-static-css-color-normalization' into base/sweep-260829-palette-correctness (858c131)
38
+ - Merge pull request [#635](https://github.com/Takazudo/zudo-design-token-panel/pull/635) from Takazudo/base/sweep-260829-zudo-dependency-refresh: chore(doc): refresh stable zudo dependencies (db555eb)
39
+ - Merge branch 'topic/621-zudo-dependency-family' into base/sweep-260829-zudo-dependency-refresh (16ed6cd)
40
+ - chore(doc): bump stable zudo dependency family (579671a)
41
+ - Merge pull request [#615](https://github.com/Takazudo/zudo-design-token-panel/pull/615) from Takazudo/fix/populate-doc-changelog-20260823: docs: populate changelog release history (9ce99a1)
42
+ - docs: clarify changelog source and locale history (7a0c194)
43
+ - docs: populate changelog release history (559a9c5)
44
+ - Merge pull request [#614](https://github.com/Takazudo/zudo-design-token-panel/pull/614) from Takazudo/agent-fix/zdtp-lint-warnings-20260823: chore: resolve zdtp lint warnings (9f16688)
45
+ - chore: resolve zdtp lint warnings (ed175ca)
46
+ - Merge pull request [#612](https://github.com/Takazudo/zudo-design-token-panel/pull/612) from Takazudo/agent-fix/actions-cache-node24-20260823: ci: update actions/cache to Node 24 (ab0ad0d)
47
+ - ci: update actions/cache to Node 24 (234459b)
48
+ - Merge pull request [#610](https://github.com/Takazudo/zudo-design-token-panel/pull/610) from Takazudo/chore/bump-zudo-deps-20260823: chore(doc): update zudo stack and preview wiring (d6d0d54)
49
+ - chore(doc): bump zudo dependencies (6d50555)
50
+
51
+ ## [0.4.12] - 2026-08-20
52
+
53
+ ### Fixed
54
+
55
+ - fix(doc): stabilize generated skill links (142420c)
56
+
57
+ ### Other Changes
58
+
59
+ - Merge pull request [#599](https://github.com/Takazudo/zudo-design-token-panel/pull/599) from Takazudo/base/sweep-260820: Complete portable storage and documentation refresh (1d78e89)
60
+ - Merge pull request [#609](https://github.com/Takazudo/zudo-design-token-panel/pull/609) from Takazudo/base/sweep-260820-zudo-doc-refresh: Refresh documentation site on zudo-doc 5.7 (157d736)
61
+ - Merge topic/zudo-doc-integration into zudo-doc refresh epic (5c199a0)
62
+ - Merge topic/zudo-doc-content into zudo-doc refresh epic (0b14ccf)
63
+ - docs: fix reviewed cross-locale links (0bc0081)
64
+ - docs: migrate bilingual zudo content (c572f05)
65
+ - Merge topic/zudo-doc-branding into zudo-doc refresh epic (e2e2cca)
66
+ - Brand zudo-doc hero and social metadata (b5c625f)
67
+ - Merge topic/zudo-doc-foundation-gate into zudo-doc refresh epic (68784c3)
68
+ - Harden deploy audit against missing workspaces (30c12c0)
69
+ - Fix deploy audit panel workspace filter (b494335)
70
+ - Merge topic/zudo-doc-scaffold into zudo-doc refresh epic (a0addac)
71
+ - Avoid missing branded favicon asset (f73995e)
72
+ - Rescaffold docs with zudo-doc 5.7 (6079124)
73
+ - = start zudo-doc refresh epic = [skip ci] (e3479d5)
74
+ - Merge pull request [#608](https://github.com/Takazudo/zudo-design-token-panel/pull/608) from Takazudo/base/sweep-260820-portable-storage-contract: Sync portable storage contract to state-v4 (b7997de)
75
+ - Merge branch 'topic/portable-contract-v4' into base/sweep-260820-portable-storage-contract (e6a27b8)
76
+ - docs(zdtp): document v4 portable storage contract (511f6fe)
77
+ - = start portable storage contract epic = [skip ci] (1605c3e)
78
+ - = start sweep-260820 super-epic = [skip ci] (c29cc50)
79
+
80
+ ## [0.4.11] - 2026-08-19
4
81
 
5
82
  ### Features
6
83
 
@@ -27,7 +104,7 @@
27
104
  - test: complete the lazy-load gate decision-matrix coverage ([#591](https://github.com/Takazudo/zudo-design-token-panel/pull/591)) (76cb4a3)
28
105
  - test: confirm multi-instance spawn geometry in a real browser ([#595](https://github.com/Takazudo/zudo-design-token-panel/pull/595)) (11988d6)
29
106
 
30
- ## 0.4.10
107
+ ## [0.4.10] - 2026-08-10
31
108
 
32
109
  ### Fixed
33
110
 
@@ -40,7 +117,7 @@
40
117
  - docs: migrate the doc site to zudo-doc 4.x — S1–S6 series ([#556](https://github.com/Takazudo/zudo-design-token-panel/pull/556)–[#562](https://github.com/Takazudo/zudo-design-token-panel/pull/562)) (e2f36b5, 51c674c, ff38109, e36e718, e7838fb, c20cc83, f89a6bf)
41
118
  - docs(l-make-release): auto-proceed Step 3 gate for routine bumps (45a5aca)
42
119
 
43
- ## 0.4.9
120
+ ## [0.4.9] - 2026-07-13
44
121
 
45
122
  ### Features
46
123
 
@@ -59,7 +136,7 @@
59
136
  - test: record color picker close control VRT (aa66eba)
60
137
  - chore: start panel UI tweaks 540 (57fb4c7)
61
138
 
62
- ## 0.4.8
139
+ ## [0.4.8] - 2026-07-13
63
140
 
64
141
  ### Features
65
142
 
@@ -71,7 +148,7 @@
71
148
  - chore(dom-tweaker): remove consumed prototype resource ([#539](https://github.com/Takazudo/zudo-design-token-panel/pull/539)) (9a0ac51)
72
149
  - docs(l-make-release): add autonomy-after-the-gate principle (7f64206)
73
150
 
74
- ## 0.4.7
151
+ ## [0.4.7] - 2026-07-12
75
152
 
76
153
  ### Fixed
77
154
 
@@ -81,7 +158,7 @@
81
158
 
82
159
  - chore(deps): resolve 19 pnpm audit security advisories ([#525](https://github.com/Takazudo/zudo-design-token-panel/pull/525)) (80c9a0e)
83
160
 
84
- ## 0.4.6
161
+ ## [0.4.6] - 2026-07-12
85
162
 
86
163
  ### Features
87
164
 
@@ -105,7 +182,7 @@
105
182
  - test: VRT baselines re-recorded for zdtp title + square corners; broad coverage added for notes tab, help icons, unit cycling
106
183
  - chore: removed consumed `_temp-resource/` prototype scaffold (ccbd18a)
107
184
 
108
- ## 0.4.5
185
+ ## [0.4.5] - 2026-07-07
109
186
 
110
187
  ### Features
111
188
 
@@ -127,7 +204,7 @@
127
204
  - docs: truth-up schemaId docs, tighten Import modal copy, export `SCHEMA_V1`/`SCHEMA_V2`/`SCHEMA_V3` ([#505](https://github.com/Takazudo/zudo-design-token-panel/issues/505)) (8088281)
128
205
  - docs: replace zmodular references with Takazudo Modular markdown links (afcfea2)
129
206
 
130
- ## 0.4.4
207
+ ## [0.4.4] - 2026-07-06
131
208
 
132
209
  ### Features
133
210
 
@@ -173,7 +250,7 @@
173
250
  - chore(deps): bump `@takazudo/zudo-doc` and `zudo-doc-history-server` to `^2.5.1` (d6c05dd)
174
251
  - chore(doc): sync generated `claude/index.mdx` with build output (d763238)
175
252
 
176
- ## 0.4.3
253
+ ## [0.4.3] - 2026-07-04
177
254
 
178
255
  ### Features
179
256
 
@@ -205,7 +282,7 @@
205
282
  - Browser test files now run serially — parallel files share one origin and raced each other's localStorage (fd37774)
206
283
  - Deps: bump @takazudo/* toolchain (zfb next.76, zudo-doc 2.5.0) (62b62f6)
207
284
 
208
- ## 0.4.2
285
+ ## [0.4.2] - 2026-06-30
209
286
 
210
287
  ### Features
211
288
 
@@ -236,13 +313,13 @@
236
313
  - Bump the `@takazudo/*` toolchain (zfb next.72, zudo-doc 2.1.2). (d711839)
237
314
  - Doc-site migration to zudo-doc v2 — doc-site only, no package impact. ([#432](https://github.com/Takazudo/zudo-design-token-panel/pull/432))
238
315
 
239
- ## 0.4.1
316
+ ## [0.4.1] - 2026-06-30
240
317
 
241
318
  ### Fixed
242
319
 
243
320
  - **Palette tab no longer renders unstyled for self-injection consumers.** The 0.4.0 palette-tab CSS (`palette-chart` / `palette-edit` / `palette-readout`) reached `dist/zdtp.css` but was missing from the stylesheet the panel self-injects at runtime, so consumers using `import('@takazudo/zdtp')` + `configurePanel(...)` without importing `@takazudo/zdtp/styles` saw collapsed swatch bars and a missing curve-editor canvas. The two palette stylesheets are now `@import`ed into the single `panel.css` aggregate (matching `color-picker.css`) so both CSS delivery paths stay in sync, guarded by a new static `Invariant G` test. ([#413](https://github.com/Takazudo/zudo-design-token-panel/issues/413))
244
321
 
245
- ## 0.4.0
322
+ ## [0.4.0] - 2026-06-29
246
323
 
247
324
  ### Features
248
325
 
@@ -260,7 +337,7 @@
260
337
  - Integration + serde + browser-confirm test coverage for the palette tab. ([#396](https://github.com/Takazudo/zudo-design-token-panel/issues/396))
261
338
  - Add a grouped palette tab recipe + reference docs (EN + JA). ([#397](https://github.com/Takazudo/zudo-design-token-panel/issues/397))
262
339
 
263
- ## 0.3.3
340
+ ## [0.3.3] - 2026-06-23
264
341
 
265
342
  ### Features
266
343
 
@@ -279,7 +356,7 @@
279
356
 
280
357
  - Add an end-to-end test asserting the oklch round-trip (panel `onChange` + apply path both emit `oklch(...)`) and wide-gamut P3 preservation. ([#379](https://github.com/Takazudo/zudo-design-token-panel/issues/379)) (0595f28)
281
358
 
282
- ## 0.3.2
359
+ ## [0.3.2] - 2026-06-23
283
360
 
284
361
  ### Fixed
285
362
 
@@ -287,7 +364,7 @@
287
364
 
288
365
  - **Dedupe the reserved toggle event across listeners.** When a custom-prefix host opts into the reserved `toggle-design-token-panel` name via `config.toggleEvent`, both the eager default listener and the instance's own listener fired on one dispatch and resolved to the same instance — toggling it twice (open, then immediately close). The handler now dedupes per dispatch by resolved instance id carried on the shared Event object, so the panel opens exactly once. ([#371](https://github.com/Takazudo/zudo-design-token-panel/pull/371))
289
366
 
290
- ## 0.3.1
367
+ ## [0.3.1] - 2026-06-23
291
368
 
292
369
  ### Fixed
293
370
 
@@ -295,7 +372,7 @@
295
372
 
296
373
  - **Skip the bin integration test suite when `dist/bin/server.js` is absent.** `server.integration.test.ts` spawns the built bin and previously threw in `beforeAll` when it was missing, failing the `node` vitest project on a fresh checkout (before a build). It now gates the suite with `describe.skipIf` on build presence (with a console warning) instead of hard-failing; CI builds before testing, so the suite still runs there. ([#360](https://github.com/Takazudo/zudo-design-token-panel/issues/360))
297
374
 
298
- ## 0.3.0
375
+ ## [0.3.0] - 2026-06-22
299
376
 
300
377
  ### Features
301
378
 
@@ -307,7 +384,7 @@
307
384
 
308
385
  - **`PanelConfig.applySink`.** An optional `{ apply(pairs), clear(names) }` sink routes this instance's CSS-var writes and clears through a caller-supplied object instead of `document.documentElement`. Useful for shadow DOM, iframe, or test-spy contexts. `apply` = upsert; `clear` = remove. Reset sends the full token-name set for the instance to `sink.clear` so the sink target is completely cleaned. Sink errors are non-fatal (`console.warn`). The host owns the sink target's lifecycle. `applySink` carries function references and must not be passed through the Astro inline JSON config. ([#355](https://github.com/Takazudo/zudo-design-token-panel/issues/355))
309
386
 
310
- ## 0.2.3
387
+ ## [0.2.3] - 2026-06-15
311
388
 
312
389
  ### Bug Fixes
313
390
 
@@ -322,13 +399,13 @@
322
399
  - docs: document the global (not scheme-scoped) tweak model in README §9 — on a host `color-scheme-changed` event the panel drops its inline overrides and re-seeds the live state from the new scheme, leaving `localStorage` untouched until the next edit. (238b4db, [#343](https://github.com/Takazudo/zudo-design-token-panel/issues/343))
323
400
  - ci: drop the stale npm `next` dist-tag on stable releases when it lags `latest`, so `@takazudo/zdtp@next` can no longer silently resolve to an older prerelease. (215ec59, [#345](https://github.com/Takazudo/zudo-design-token-panel/issues/345))
324
401
 
325
- ## 0.2.2
402
+ ## [0.2.2] - 2026-06-15
326
403
 
327
404
  ### Other Changes
328
405
 
329
406
  - Post-review internal cleanups for the Element Path Copy feature (no behavior change): single source of truth for the highlight / element-path portal-mount ids (`HIGHLIGHT_PORTAL_MOUNT_ID` / `ELPATH_PORTAL_MOUNT_ID`, folded into `PANEL_EXCLUSION_SELECTOR`); `ElementPathToast` now owns its own fixed top-center positioning and z-index; `buildSummary` escapes the id consistently with the selector line; the always-on `mousemove` listener is passive + non-capture; and the hover-label summary is memoized. (0fdcbd6)
330
407
 
331
- ## 0.2.1
408
+ ## [0.2.1] - 2026-06-14
332
409
 
333
410
  ### Features
334
411
 
@@ -340,7 +417,7 @@
340
417
  - Bump GitHub Actions off the deprecated Node 20 runtime: `checkout` / `setup-node` / pnpm-setup and the artifact actions to their Node-24-matched versions. (08d5b48, b46fd66, [#339](https://github.com/Takazudo/zudo-design-token-panel/pull/339), [#341](https://github.com/Takazudo/zudo-design-token-panel/pull/341))
341
418
  - Add a web-env bootstrap for Claude Code on the web. (23f00c3)
342
419
 
343
- ## 0.2.0
420
+ ## [0.2.0] - 2026-06-09
344
421
 
345
422
  First clean stable release on the `latest` dist-tag, promoting the
346
423
  `0.2.0-next.1` / `0.2.0-next.2` prerelease line. A tagless
@@ -360,13 +437,13 @@ First clean stable release on the `latest` dist-tag, promoting the
360
437
  - Drop slider-describing prose and min/max examples from `README.md`, `PORTABLE-CONTRACT.md`, and the doc-site reference pages (`token-manifest.mdx`, `architecture.mdx`, `configure-panel.mdx`). (e210a2d, 9262585)
361
438
  - Resolve all `pnpm audit` advisories (1 critical, 3 high, 7 moderate, 1 low) in dev/doc-only tooling — none ship in the published package. (f3574e0)
362
439
 
363
- ## 0.2.0-next.2
440
+ ## [0.2.0-next.2] - 2026-05-27
364
441
 
365
442
  ### Features
366
443
 
367
444
  - **Token-name tooltip parity across all tabs (Size / Font / Spacing / Easing / GenericTab).** The Color tab already showed a rich custom tooltip with the full token name on hover; every other tab fell back to the native HTML `title` attribute. A new shared `TokenLabel` component renders the same `.tokenpanel-tooltip` primitive at every token-name display site. `TooltipProvider` was lifted from `color-tab.tsx` to `panel.tsx` so all tabs share a single provider. (559c59f, 426e9c6, 84f5981, [#330](https://github.com/Takazudo/zudo-design-token-panel/issues/330), [#337](https://github.com/Takazudo/zudo-design-token-panel/pull/337))
368
445
 
369
- ## 0.2.0-next.1
446
+ ## [0.2.0-next.1] - 2026-05-27
370
447
 
371
448
  ### Breaking Changes
372
449
 
@@ -379,7 +456,7 @@ First clean stable release on the `latest` dist-tag, promoting the
379
456
  - Update `packages/zdtp/README.md` and `packages/zdtp/PORTABLE-CONTRACT.md` to drop slider-describing prose and the min/max examples. (e210a2d)
380
457
  - Update doc-site reference pages (`token-manifest.mdx`, `architecture.mdx`, `configure-panel.mdx`) to drop the min/max examples and slider-UI prose. (9262585)
381
458
 
382
- ## 0.1.0-next.3
459
+ ## [0.1.0-next.3] - 2026-05-27
383
460
 
384
461
  ### Features
385
462
 
@@ -389,7 +466,7 @@ First clean stable release on the `latest` dist-tag, promoting the
389
466
 
390
467
  - **Number inputs no longer overwrite the user's draft mid-keystroke.** `SliderRow` and the generic-tab item editor used to parse-and-clamp on every keystroke, immediately commit the clamped value, and round-trip it back into the input — so typing `2` then `2` (expecting `22`) snapped the input to `6` when the token's `max` was `6`. Out-of-range or empty drafts are now flagged with a `--invalid` red border and held without committing; on blur, an invalid draft reverts to the last known-good value. The slider thumb continues to scrub live. (21d3afa) ([#313](https://github.com/Takazudo/zudo-design-token-panel/issues/313))
391
468
 
392
- ## 0.1.0-next.2
469
+ ## [0.1.0-next.2] - 2026-05-25
393
470
 
394
471
  ### Fixed
395
472
 
@@ -410,7 +487,7 @@ First clean stable release on the `latest` dist-tag, promoting the
410
487
  base tokens get the same visibility toggle as palette and semantic tokens. The
411
488
  eye is omitted only when the cluster declares no `cssVar` for that role.
412
489
 
413
- ## 0.1.0-next.1
490
+ ## [0.1.0-next.1] - 2026-05-25
414
491
 
415
492
  ### Fixed — Astro consumers: `@takazudo/zdtp/astro` value import broke real npm installs ([#308](https://github.com/Takazudo/zudo-design-token-panel/issues/308))
416
493
 
@@ -436,7 +513,7 @@ First clean stable release on the `latest` dist-tag, promoting the
436
513
  side-effect import is unchanged. Docs (README, PORTABLE-CONTRACT, doc-site
437
514
  recipes) updated to the direct-subexport form.
438
515
 
439
- ## 0.1.0-next.0
516
+ ## [0.1.0-next.0] - 2026-05-24
440
517
 
441
518
  ### Renamed — npm package is now `@takazudo/zdtp`
442
519
 
@@ -264,9 +264,10 @@ derives the keys at runtime from this single base.
264
264
 
265
265
  | Logical key | Derivation | Owner | Purpose |
266
266
  | ----------- | --------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
267
- | `state-v3` | `${storagePrefix}-state-v3` | tweak-state | Current unified envelope: tabs map + color + spacing + typography + size + panelPosition. Added `tabs` map for generic host-coined tabs. |
268
- | `state-v2` | `${storagePrefix}-state-v2` | tweak-state (legacy) | Pre-v3 unified envelope (color + spacing + typography + size + panelPosition). Migrated into `state-v3` on first load, then deleted. |
269
- | `state-v1` | `${storagePrefix}-state` | tweak-state (legacy) | Pre-v2 flat-state format (Color-only). Migrated into `state-v3` on first load, then deleted. |
267
+ | `state-v4` | `${storagePrefix}-state-v4` | tweak-state | Current unified envelope. `color` (and optional `secondary`) is keyed by active scheme/mode identity; global `tabs`, `spacing`, `typography`, and `size` slices remain unkeyed. |
268
+ | `state-v3` | `${storagePrefix}-state-v3` | tweak-state (legacy) | Retained downgrade-compatible envelope with a flat, single-slot `color` (plus global `tabs`, `spacing`, `typography`, and `size`). The selected v3 state is copied into v4; this key is not deleted by the v4 migration. |
269
+ | `state-v2` | `${storagePrefix}-state-v2` | tweak-state (legacy) | Pre-v3 unified envelope (color + spacing + typography + size). When selected, it is written to `state-v3` and the v2 key is deleted, then the result is copied into v4. |
270
+ | `state-v1` | `${storagePrefix}-state` | tweak-state (legacy) | Pre-v2 flat-state format (Color-only). When selected, it is written to `state-v3` and the v1 key is deleted, then the result is copied into v4. |
270
271
  | `open` | `${storagePrefix}-open` | panel | Mirror of the panel's `open` boolean state (so the next mount opens directly into the user's last state without a post-render toggle dispatch). |
271
272
  | `position` | `${storagePrefix}-position` | panel | Drag position (`{ top, left }`) so the panel reappears where the user left it. |
272
273
  | `visible` | `${storagePrefix}:visible` | adapter | Adapter-level visibility-intent flag, owned by the lazy-load gate (§6). |
@@ -283,6 +284,7 @@ The derivation MUST emit the colon literally; do not "fix" it during refactors.
283
284
  the derivation produces:
284
285
 
285
286
  ```
287
+ myapp-design-token-panel-state-v4
286
288
  myapp-design-token-panel-state-v3
287
289
  myapp-design-token-panel-state-v2
288
290
  myapp-design-token-panel-state
@@ -294,8 +296,39 @@ myapp-design-token-panel-domtweaker-enabled
294
296
  ```
295
297
 
296
298
  Unit tests in the package verify these derivations with literal-equality
297
- checks, and the v1 → v3 / v2 → v3 migration paths at first-load are part of
298
- the test matrix.
299
+ checks. The v4 precedence and legacy v1/v2/v3 migration paths at first load
300
+ are part of the test matrix; the version-agnostic `${storagePrefix}-state`
301
+ family probe in §6.2 continues to cover this key and future versions.
302
+
303
+ ### Current `state-v4` envelope
304
+
305
+ The current persisted envelope is stored under one `${storagePrefix}-state-v4`
306
+ key. Its color slices are keyed by the active scheme/mode identity, while the
307
+ non-color slices are global and unkeyed:
308
+
309
+ ```jsonc
310
+ {
311
+ "color": {
312
+ "Default Light": { "palette": [], "semanticMappings": {} /* ... */ },
313
+ "Default Dark": { "palette": [], "semanticMappings": {} /* ... */ }
314
+ },
315
+ "secondary": {
316
+ "Default Light": { "palette": [], "semanticMappings": {} /* ... */ }
317
+ },
318
+ "tabs": { "my-custom-tab": { "tier-id": { "item-id": "value" } } },
319
+ "spacing": { "item-id": "value" },
320
+ "typography": { "item-id": "value" },
321
+ "size": { "item-id": "value" }
322
+ }
323
+ ```
324
+
325
+ `secondary` is optional and, when present, uses the same identity keys as the
326
+ primary `color` map. On load, the active identity's color and secondary slots
327
+ are selected. If that identity has no color slot yet, color is seeded from the
328
+ active scheme's defaults; the global `tabs`, `spacing`, `typography`, and `size`
329
+ slices still load. A save replaces only the active identity's color/secondary
330
+ slots and preserves every other identity slot by merge, so editing one scheme
331
+ cannot overwrite another scheme's tweaks.
299
332
 
300
333
  ### 2.1 Default first-open geometry
301
334
 
@@ -368,6 +401,15 @@ export interface PillSpec {
368
401
  customDefault: string;
369
402
  }
370
403
 
404
+ /** Mapping value for a semantic color token (legacy and ramp-native forms). */
405
+ export type SemanticValue =
406
+ | number
407
+ | 'bg'
408
+ | 'fg'
409
+ | { literal: string }
410
+ | { literal: { light: string; dark: string } }
411
+ | { ref: { tab?: string; tier: string; item: string } };
412
+
371
413
  /** A single editable or reference token within a tier. */
372
414
  export interface TierItem {
373
415
  /** Stable id used as the key in persisted state (e.g. `hsp-2xs`). */
@@ -402,8 +444,26 @@ export interface TierConfig {
402
444
  * pipeline emits `var(--target-cssvar)` for ref-tier items at apply time.
403
445
  */
404
446
  referencesTier?: string;
447
+ /**
448
+ * Marks a Color-tab tier as semantic data whose items hold `SemanticValue`
449
+ * mappings rather than raw palette entries. It is never treated as the
450
+ * palette tier, so a Color tab may contain a lone semantic tier.
451
+ */
452
+ semantic?: true;
453
+ /**
454
+ * Valid only when this semantic tier belongs to a tab whose id is exactly
455
+ * `color` or `color-secondary`; other owning tab ids are rejected at
456
+ * configure time. Declares one or more permitted same- or cross-tab ramp
457
+ * sources for per-row semantic `{ ref }` mappings. Each entry names a tier
458
+ * and optional tab (`tab` omitted means this tab); unlike `referencesTier`,
459
+ * this is a multi-source allow-list for individual semantic mappings.
460
+ */
461
+ referencesRamps?: readonly { tab?: string; tier: string }[];
405
462
  }
406
463
 
464
+ For the full `SemanticValue` mapping and emission behavior, see the maintained
465
+ [Color-cluster reference](https://zudo-design-token-panel.takazudomodular.com/docs/reference/color-cluster/).
466
+
407
467
  /**
408
468
  * Color-cluster extras — the non-tier fields required for the color tab.
409
469
  * Palette and semantic data move into the tier model as TierItems; ColorClusterExtras
@@ -596,7 +656,9 @@ export interface ColorClusterExtras {
596
656
  /**
597
657
  * Optional light/dark pairing. When set to an object, the panel honours
598
658
  * `document.documentElement[data-theme]` and switches schemes accordingly
599
- * on init. Set to `false` to disable the light/dark UI.
659
+ * on init. Set to `false` to disable scheme-to-`data-theme` binding; this
660
+ * does not disable per-mode literal editing or emitted `light-dark(...)`
661
+ * values.
600
662
  */
601
663
  colorMode: false | { defaultMode: 'light' | 'dark'; lightScheme: string; darkScheme: string };
602
664
  };
@@ -899,7 +961,7 @@ panel bundle is NOT fetched and the page is completely free of panel JS.
899
961
  | Signal | Key derivation | Owner |
900
962
  |--------|---------------|-------|
901
963
  | `wasVisible` | `${storagePrefix}:visible`, OR its `${storagePrefix}-open` mirror | adapter |
902
- | `hasPersistedOverrides` | Content check across the `${storagePrefix}-state` family (`-state`, `-state-v2`, `-state-v3`, ... — every version, not a fixed list) | tweak-state |
964
+ | `hasPersistedOverrides` | Content check across the `${storagePrefix}-state` family (`-state`, `-state-v2`, `-state-v3`, `-state-v4`, ... — every version, not a fixed list) | tweak-state |
903
965
  | `shouldAutoload` | `${storagePrefix}:autoload`, matching `'1'` or `'auto'` | autoload-state |
904
966
  | `loadElementPathEnabled` | `${storagePrefix}-elpath-enabled` | element-path-state |
905
967
  | `loadDomTweakerEnabled` | `${storagePrefix}-domtweaker-enabled` | dom-tweaker-state |
@@ -1174,29 +1236,35 @@ For any host's chosen `storagePrefix`, the derivation produces deterministic,
1174
1236
  literal-equal storage keys (see §2). Unit tests pin the derived keys to
1175
1237
  literal strings.
1176
1238
 
1177
- ### 8.3 v1 / v2 → v3 in-place migration
1239
+ ### 8.3 v4 precedence and v1 / v2 / v3 migration
1178
1240
 
1179
- On first load, `loadPersistedState` migrates forward through the chain:
1241
+ `loadPersistedState` first looks for a valid `state-v4` envelope. If that key
1242
+ is absent or invalid, it falls through to the retained legacy chain. The
1243
+ precedence and deletion rules are explicit:
1180
1244
 
1181
- | Source key | Target key | Action after migration |
1182
- | ----------------------- | ------------------------ | ---------------------- |
1183
- | `${storagePrefix}-state` (v1) | `${storagePrefix}-state-v3` | v1 key deleted |
1184
- | `${storagePrefix}-state-v2` (v2) | `${storagePrefix}-state-v3` | v2 key deleted |
1245
+ | Storage condition | Selection / migration action | Key-retention result |
1246
+ | ----------------- | ---------------------------- | --------------------- |
1247
+ | Valid `${storagePrefix}-state-v4` (v4) | Select the active identity's `color` and optional `secondary` slots; load global `tabs`, `spacing`, `typography`, and `size`. | v4 wins; no legacy key is touched. |
1248
+ | v4 key absent or invalid | Fall through and inspect the legacy keys in order. | No deletion is caused by the v4 probe. |
1249
+ | Valid `${storagePrefix}-state-v3` (v3) | Use v3; do not inspect, rewrite, or delete lower legacy keys. | Copy the resulting state into v4 under the active identity; retain v3 for downgrade compatibility. |
1250
+ | Valid `${storagePrefix}-state-v2` (v2), with no valid v3 | Parse v2 and write the resulting flat state to v3. | Delete v2, then copy the resulting v3 state into v4; retain v3 for downgrade compatibility. |
1251
+ | Valid `${storagePrefix}-state` (v1), with no valid v3 or v2 | Lift the flat Color-only state into the unified state and write it to v3. | Delete v1, then copy the resulting v3 state into v4; retain v3 for downgrade compatibility. |
1185
1252
 
1186
- A user who last opened the panel before v3 landed gets their old color/spacing
1187
- tweaks lifted into the new envelope on first load.
1253
+ Thus a v3 key wins over v2 and v1, and a v4 migration never removes v3. The
1254
+ selected or newly written v3 state is always filed into the v4 envelope under
1255
+ the identity active at that moment. Subsequent loads read v4 first; a
1256
+ downgrade can still read the retained v3 envelope. Malformed legacy values are
1257
+ skipped in the same order so the next lower legacy key can be considered.
1188
1258
 
1189
- The v3 envelope adds a `tabs` map alongside the existing per-category slices:
1259
+ The retained v3 envelope has a flat, single-slot `color` and global slices:
1190
1260
 
1191
1261
  ```ts
1192
1262
  // Simplified v3 localStorage envelope shape
1193
1263
  {
1194
- // legacy category slices — preserved for round-trip compatibility
1195
1264
  color: { ... },
1196
1265
  spacing: { ... },
1197
1266
  typography: { ... },
1198
1267
  size: { ... },
1199
- // v3 extension — generic tab overrides keyed by tab id
1200
1268
  tabs: {
1201
1269
  "my-custom-tab": { "item-id-1": "some-value", ... },
1202
1270
  ...
@@ -1204,6 +1272,9 @@ The v3 envelope adds a `tabs` map alongside the existing per-category slices:
1204
1272
  }
1205
1273
  ```
1206
1274
 
1275
+ The current v4 envelope and its active-identity seeding and merge-write rules
1276
+ are specified in §2 above.
1277
+
1207
1278
  ### 8.4 Typography-id rename map
1208
1279
 
1209
1280
  The optional `PanelConfig.legacyIdRenameMap` (`Record<string, string | null>`)
@@ -1304,6 +1375,6 @@ Cross-reference table — what each section pins down.
1304
1375
  | Modal class prefix and `data-design-token-panel-modal` selector contract | §7.3 |
1305
1376
  | Self-contained panel chrome palette (no host theme reads) | §7.4 |
1306
1377
  | Host-adapter side-effect import (paired-unit obligation) | §7.5 |
1307
- | v1/v2 → v3 storage migration and typography-id rename map | §8.3, §8.4 |
1378
+ | v4 envelope precedence, v1/v2/v3 storage migration, and typography-id rename map | §2, §8.3, §8.4 |
1308
1379
  | JSON export/import schema v2 (serde v2) | §9 |
1309
1380
  | Out-of-scope / deferred concerns | §10 |
package/README.md CHANGED
@@ -112,8 +112,8 @@ pnpm exec zdtp-server --help
112
112
  | Flag | Required | Purpose | Default |
113
113
  |---|---|---|---|
114
114
  | `--routing <path>` | yes | Path to the routing JSON file (cluster id → repo-relative CSS file path). Absolute, or relative to `--root`. See §3.2. | — |
115
- | `--write-root <dir>` | no | Sandbox directory: the bin refuses to write outside this tree. Absolute, or relative to `--root`. | `--root` |
116
- | `--root <dir>` | no | Repo-root reference used to resolve `--routing` and `--write-root`. | `process.cwd()` |
115
+ | `--write-root <dir>` | yes | Required write boundary: the only directory tree the bin is allowed to write into. Absolute, or relative to `--root`. | — |
116
+ | `--root <dir>` | no | CWD/resolution base used to resolve `--routing` and `--write-root`. | `process.cwd()` |
117
117
  | `--port <number>` | no | TCP port to bind. `0` asks the OS for an ephemeral port (the bin logs the assigned port on startup). | `24681` |
118
118
  | `--host <addr>` | no | Bind address. Use `0.0.0.0` to expose on the LAN (off by default). | `127.0.0.1` |
119
119
  | `--allow-origin <origin>` | repeatable | Origin allowed to POST to `/apply` (scheme + host + port, no trailing slash). At least one is required for any browser to apply. | none (all origins denied) |
@@ -170,7 +170,7 @@ For the full token-overrides payload schema and the `PanelConfig` shape, see §5
170
170
  The bin is **dev-only** and is built with three independent guards:
171
171
 
172
172
  - **Loopback default.** Binds to `127.0.0.1` so only requests from the same machine are accepted. Override with `--host` if you actually want LAN access.
173
- - **Write sandbox (`--write-root`).** Every routing entry is resolved against `--root` and verified to sit strictly inside `--write-root` before any file I/O happens. An entry whose resolved path escapes the sandbox — via `..` segments or an absolute path that points elsewhere — fails the apply with a 400 and a descriptive error message. `--write-root` defaults to `--root`, so routing entries are sandboxed to your repo root unless you narrow it further.
173
+ - **Write sandbox (`--write-root`).** `--write-root` is required and defines the narrow directory tree the bin is allowed to write into. Every routing entry is resolved against `--root` and verified to sit strictly inside `--write-root` before any file I/O happens. An entry whose resolved path escapes the sandbox — via `..` segments or an absolute path that points elsewhere — fails the apply with a 400 and a descriptive error message. `--root` is the CWD/resolution base for relative `--routing` and `--write-root` paths and defaults to `process.cwd()`.
174
174
  - **CORS allow-list.** By default, **all origins are denied**. To let a browser POST to `/apply` you must list its origin explicitly with `--allow-origin <url>` (repeatable). Without a matching `--allow-origin`, the OPTIONS preflight returns 403 and POST returns 403 — no `Access-Control-Allow-Origin` header is emitted. Origin matching is **verbatim** on the full scheme + host + port string: `http://localhost:5173` and `http://127.0.0.1:5173` are different origins.
175
175
 
176
176
  **Atomic writes.** The bin serialises per-file writes through a small mutex and uses a write-temp-file-then-rename strategy, so a failure mid-write never leaves a half-rewritten CSS file on disk. If any file in a multi-file apply fails to write, every file that was already persisted is restored from the in-memory snapshot taken before the apply started.
@@ -1,6 +1,6 @@
1
- import { c as y, g as m, a as w, b, i as P } from "../panel-config-7Xg59uKj.js";
2
- import { g, Z as k } from "../tweak-state-DRKV1uYj.js";
3
- import { s as A, l as D, a as E, i as c, r as f, c as _, b as I, d } from "../autoload-state-CTDDBxI8.js";
1
+ import { c as y, g as m, a as w, b, i as P } from "../panel-config-COHesW2B.js";
2
+ import { g, Z as k } from "../tweak-state-BKNxtHzb.js";
3
+ import { s as A, l as D, a as E, i as c, r as f, c as _, b as I, d } from "../autoload-state-CfL2VUe1.js";
4
4
  const u = "tokenpanel-config";
5
5
  function S() {
6
6
  if (typeof document > "u")
@@ -1,4 +1,4 @@
1
- import { s as t } from "../panel-config-7Xg59uKj.js";
1
+ import { s as t } from "../panel-config-COHesW2B.js";
2
2
  import { c as d } from "../color-schemes-CgzOBqGO.js";
3
3
  import { F as r, G as m, a as g, S as z } from "../manifest-DCReQE0k.js";
4
4
  const e = {
@@ -1,4 +1,4 @@
1
- import { g as n, j as o } from "./panel-config-7Xg59uKj.js";
1
+ import { g as n, j as o } from "./panel-config-COHesW2B.js";
2
2
  function c() {
3
3
  return typeof document < "u" && document !== null && typeof document.getElementById == "function";
4
4
  }
@@ -35,8 +35,19 @@ import { type Oklcha } from '../../utils/color-oklch';
35
35
  export type { Oklcha } from '../../utils/color-oklch';
36
36
  export type { Channel } from '../../utils/palette-curve';
37
37
  export interface PaletteChartProps {
38
- /** Palette colors in OKLCH space. Band `i` = oklchaToHex(colors[i]). */
39
- colors: Oklcha[];
38
+ /**
39
+ * Dense palette slots in OKLCH space. A null slot preserves its index/band,
40
+ * but has no numeric curve node and is not editable.
41
+ */
42
+ colors: Array<Oklcha | null>;
43
+ /**
44
+ * Dense per-index write permission, separate from color validity. A valid
45
+ * readonly color remains painted in the bands/curves, but receives no node
46
+ * hit target and is excluded from whole-curve editing.
47
+ */
48
+ editable: boolean[];
49
+ /** Dense stable item identities used to invalidate stale index-based gestures. */
50
+ identities: string[];
40
51
  /** Index of the currently selected color/step (highlighted band + node column). */
41
52
  selectedIndex: number;
42
53
  /** Which curves are drawn. Owned by the parent; chart only consumes. */
@@ -64,7 +75,7 @@ export interface PaletteChartProps {
64
75
  */
65
76
  onChangeEnd?: () => void;
66
77
  }
67
- declare function PaletteChartImpl({ colors, selectedIndex, visibleChannels, onChange, onSelectIndex, onChangeStart, onChangeEnd, }: PaletteChartProps): JSX.Element;
78
+ declare function PaletteChartImpl({ colors, editable, identities, selectedIndex, visibleChannels, onChange, onSelectIndex, onChangeStart, onChangeEnd, }: PaletteChartProps): JSX.Element;
68
79
  /**
69
80
  * Memoized so parent re-renders that don't change props don't re-walk every
70
81
  * band/curve/node. The drag state lives in refs, so a controlled-value update
@@ -1,7 +1,7 @@
1
1
  import { jsx as y, jsxs as K, Fragment as kt } from "preact/jsx-runtime";
2
2
  import { useEffect as P, useState as Y, useRef as O, useMemo as J, useCallback as X } from "preact/hooks";
3
- import { b as ft, p as wt, u as ht, a as Ne, A as bt, Z as gt } from "./index-BeYH-tJD.js";
4
- import { g as vt, m as j } from "./panel-config-7Xg59uKj.js";
3
+ import { b as ft, p as wt, u as ht, a as Ne, A as bt, Z as gt } from "./index-U7Ol7tgA.js";
4
+ import { g as vt, m as j } from "./panel-config-COHesW2B.js";
5
5
  const xt = (e, o) => {
6
6
  const t = new Array(e.length + o.length);
7
7
  for (let r = 0; r < e.length; r++)