@takazudo/zdtp 0.4.15 → 0.5.1
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 +73 -0
- package/PORTABLE-CONTRACT.md +56 -14
- package/README.md +58 -5
- package/dist/astro/host-adapter.js +46 -50
- package/dist/astro/index.js +1 -1
- package/dist/{autoload-state-7BQYXI7Y.js → autoload-state-BI4-9A58.js} +1 -1
- package/dist/config/panel-config.d.ts +8 -7
- package/dist/constants.d.ts +86 -0
- package/dist/constants.js +39 -0
- package/dist/controls/actions-menu-popover.d.ts +1 -1
- package/dist/dom-tweaker/dom-tweaker-toggle-button.d.ts +3 -1
- package/dist/element-inspect/element-inspect-toggle-button.d.ts +3 -1
- package/dist/element-path/element-path-toggle-button.d.ts +3 -1
- package/dist/{index-BOOmFtJh.js → index-D4NR_eYT.js} +2677 -2596
- package/dist/{index-C2DIYD0d.js → index-RLAXfjv2.js} +2 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -34
- package/dist/{panel-config-CRf-ONSV.js → panel-config-CqbuB0nh.js} +143 -140
- package/dist/shell/header.d.ts +1 -2
- package/dist/tabs/_generic-item-editor.d.ts +2 -2
- package/dist/tabs/flat/token-row.d.ts +3 -1
- package/dist/testing.js +2 -2
- package/dist/{tweak-state-BGp_7Tfh.js → tweak-state-c53KtLHq.js} +1 -1
- package/dist/zdtp.css +1 -1
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,79 @@ All notable changes to `@takazudo/zdtp` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on Keep a Changelog, and release notes are generated from the changelog MDX pages.
|
|
6
6
|
|
|
7
|
+
## [0.5.1] - 2026-09-06
|
|
8
|
+
|
|
9
|
+
The panel now fits its token grids, card controls, and header controls at the minimum panel width instead of overflowing. Multiple panels keep stable spawn positions: spawn ordinals are persisted and survive Astro client-side navigation. Eager loading is bounded to state keys the panel can actually read, and the specimen stylesheet gained the boundaries the docs-adoption check required.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- fix: fit token grids and card controls at minimum panel width (8db4235)
|
|
14
|
+
- fix(panel): yield header controls before floating panel overflow (06cbb0e)
|
|
15
|
+
- fix: validate doc adoption boundaries (a11fd6a)
|
|
16
|
+
|
|
17
|
+
### Other Changes
|
|
18
|
+
|
|
19
|
+
- Merge pull request [#801](https://github.com/Takazudo/zudo-design-token-panel/pull/801) from Takazudo/base/sweep-260906-2 (2631545)
|
|
20
|
+
- Merge pull request [#813](https://github.com/Takazudo/zudo-design-token-panel/pull/813) from Takazudo/base/sweep-260906-2-doc-zdtp-050 (406fb79)
|
|
21
|
+
- Merge branch 'topic/795-doc-adoption-confirm' into base/sweep-260906-2-doc-zdtp-050 (1f5abd9)
|
|
22
|
+
- Merge branch 'topic/795-doc-pin-manifest' into base/sweep-260906-2-doc-zdtp-050 (2fe626b)
|
|
23
|
+
- Adopt zdtp 0.5.0 in docs (8026c66)
|
|
24
|
+
- Merge pull request [#810](https://github.com/Takazudo/zudo-design-token-panel/pull/810) from Takazudo/base/sweep-260906-2-storage-bootstrap (e8edd31)
|
|
25
|
+
- Merge branch 'topic/790-storage-bootstrap-confirm' into base/sweep-260906-2-storage-bootstrap (aca17d0)
|
|
26
|
+
- Confirm storage bootstrap regressions (1fdb75e)
|
|
27
|
+
- Merge branch 'topic/790-readable-gate-probe' into base/sweep-260906-2-storage-bootstrap (be49753)
|
|
28
|
+
- Bound eager loading to readable state keys (bcc55fd)
|
|
29
|
+
- Merge branch 'topic/790-astro-ordinal-lifecycle' into base/sweep-260906-2-storage-bootstrap (48a76ab)
|
|
30
|
+
- Keep spawn positions stable across Astro navigation (2a6672b)
|
|
31
|
+
- Merge branch 'topic/790-persisted-spawn-ordinal' into base/sweep-260906-2-storage-bootstrap (03deeaa)
|
|
32
|
+
- Persist panel spawn ordinals (3f1caa3)
|
|
33
|
+
- Merge pull request [#805](https://github.com/Takazudo/zudo-design-token-panel/pull/805) from Takazudo/base/sweep-260906-2-panel-width-layout (b62b94d)
|
|
34
|
+
- test: follow compact panel actions in walking skeleton (de19350)
|
|
35
|
+
- Merge branch 'topic/783-containment-confirm' into base/sweep-260906-2-panel-width-layout (1cdbb29)
|
|
36
|
+
- test: update width-layout VRT baselines (f54695a)
|
|
37
|
+
- test: reconcile compact label containment assertion (777ac84)
|
|
38
|
+
- Merge branch 'topic/783-grid-implementation' into base/sweep-260906-2-panel-width-layout (5283cfc)
|
|
39
|
+
- test: isolate and verify persisted highlight toggle state (373239c)
|
|
40
|
+
- test: reconcile card fit fixtures with manifest and query contracts (6345370)
|
|
41
|
+
- Merge branch 'topic/783-header-implementation' into base/sweep-260906-2-panel-width-layout (eacf1ef)
|
|
42
|
+
- test(panel): correct header portal and edge-hit assertions (8fafedd)
|
|
43
|
+
- Merge pull request [#800](https://github.com/Takazudo/zudo-design-token-panel/pull/800) from Takazudo/base/sweep-260906-2-docs-consistency (82073a5)
|
|
44
|
+
- Merge branch 'topic/780-docs-version-skew-note' into base/sweep-260906-2-docs-consistency (d838cc2)
|
|
45
|
+
- Merge branch 'topic/780-readme-section-renumber' into base/sweep-260906-2-docs-consistency (22dd16d)
|
|
46
|
+
- docs: explain release skew in getting-started notes (b62ffb9)
|
|
47
|
+
- docs: renumber README color presets section (fb19bed)
|
|
48
|
+
|
|
49
|
+
## [0.5.0] - 2026-09-06
|
|
50
|
+
|
|
51
|
+
`@takazudo/zdtp/constants` now exposes `DEFAULT_STORAGE_PREFIX`, `DEFAULT_TOGGLE_EVENT`, `resolveToggleEventName`, `EAGER_LOAD_GATE_KEY_SUFFIXES`, and `EAGER_LOAD_GATE_STATE_FAMILY` without importing the panel. Lazy hosts can share toggle-event rules and eager-load metadata; state-family matching still requires the documented content checks. The storage-key documentation is also corrected: position, size, density, and all three highlight keys already existed in published 0.4.14.
|
|
52
|
+
|
|
53
|
+
### Features
|
|
54
|
+
|
|
55
|
+
- feat(zdtp): publish isolated constants entry (11ff48b)
|
|
56
|
+
- feat: describe eager-load gate signals with behavioral drift guard (0a2b610)
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- fix: keep dock controls visible and reconcile menu with rendered CSS (dae7dcc)
|
|
61
|
+
- fix(zdtp): preserve readable labels in changed token rows (97ede7f)
|
|
62
|
+
|
|
63
|
+
### Other Changes
|
|
64
|
+
|
|
65
|
+
- Merge pull request [#769](https://github.com/Takazudo/zudo-design-token-panel/pull/769) from Takazudo/base/sweep-260906 (c4c6230)
|
|
66
|
+
- Merge pull request [#778](https://github.com/Takazudo/zudo-design-token-panel/pull/778) from Takazudo/base/sweep-260906-gate-constants (cc29811)
|
|
67
|
+
- Merge branch 'topic/765-constants-docs' into base/sweep-260906-gate-constants (a82dc82)
|
|
68
|
+
- docs: document constants subpath and storage key continuity (17426e0)
|
|
69
|
+
- Merge branch 'topic/764-constants-package' into base/sweep-260906-gate-constants (572d11c)
|
|
70
|
+
- Merge branch 'topic/763-gate-signals' into base/sweep-260906-gate-constants (c09f456)
|
|
71
|
+
- Merge branch 'topic/762-constants-leaf' into base/sweep-260906-gate-constants (f038f71)
|
|
72
|
+
- refactor(zdtp): extract panel constants leaf (369e495)
|
|
73
|
+
- Merge pull request [#773](https://github.com/Takazudo/zudo-design-token-panel/pull/773) from Takazudo/base/sweep-260906-dock-escape (08678c2)
|
|
74
|
+
- Merge branch 'topic/759-dock-fix' into base/sweep-260906-dock-escape (a44f2a4)
|
|
75
|
+
- Merge branch 'topic/758-dock-decision' into base/sweep-260906-dock-escape (3d1ee55)
|
|
76
|
+
- docs: define visible dock controls and CSS-driven menu contract (0da81f3)
|
|
77
|
+
- Merge pull request [#768](https://github.com/Takazudo/zudo-design-token-panel/pull/768) from Takazudo/base/sweep-260906-changed-row-label (323b9b0)
|
|
78
|
+
- Merge branch 'topic/754-changed-row-label' into base/sweep-260906-changed-row-label (69846f2)
|
|
79
|
+
|
|
7
80
|
## [0.4.15] - 2026-09-05
|
|
8
81
|
|
|
9
82
|
### Features
|
package/PORTABLE-CONTRACT.md
CHANGED
|
@@ -276,6 +276,7 @@ derives the keys at runtime from this single base.
|
|
|
276
276
|
| `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. |
|
|
277
277
|
| `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). |
|
|
278
278
|
| `position` | `${storagePrefix}-position` | panel | Drag position (`{ top, left }`) so the panel reappears where the user left it. |
|
|
279
|
+
| `spawn-ordinal` | `${storagePrefix}-spawn-ordinal` | mount registry | Instance cascade identity: a JSON integer from 0 through 31, retained by Reset and restored across Astro body swaps. Invalid values fall back to lowest-free allocation. |
|
|
279
280
|
| `size` | `${storagePrefix}-size` | panel | Floating shell dimensions (`{ width, height }`) in pixels. |
|
|
280
281
|
| `dock` | `${storagePrefix}-dock` | panel | Presentation mode: `'float'`, `'right'`, `'bottom'`, or `'mini'`. |
|
|
281
282
|
| `dock-size` | `${storagePrefix}-dock-size` | panel | Right/bottom dock dimensions (`{ right, bottom }`), defaulting to `{ right: 440, bottom: 340 }`. |
|
|
@@ -293,6 +294,13 @@ derives the keys at runtime from this single base.
|
|
|
293
294
|
| `highlight-outline-width` | `${storagePrefix}-highlight-outline-width` | highlight-state | Global highlight outline width in local storage, clamped to 1–20. |
|
|
294
295
|
| `highlight-active` | `${storagePrefix}-highlight-active` | highlight-state | Active CSS-variable-to-slot map in session storage. |
|
|
295
296
|
|
|
297
|
+
This is the current storage-key inventory, not a release-history table. The
|
|
298
|
+
published 0.4.14 bundle already wrote `${storagePrefix}-position`,
|
|
299
|
+
`${storagePrefix}-size`, `${storagePrefix}-density`, and all three
|
|
300
|
+
`${storagePrefix}-highlight-*` keys. Their appearance in the 0.4.15 contract
|
|
301
|
+
documentation records existing storage continuity; it does not make those keys
|
|
302
|
+
new in 0.4.15.
|
|
303
|
+
|
|
296
304
|
**Constraint — colon, not dash, for `visible` and `autoload`.** Both adapter-
|
|
297
305
|
level flags use a `:` separator; every other derived key uses `-`. The colon
|
|
298
306
|
form is a historical artifact for `visible`, preserved for storage-key
|
|
@@ -309,6 +317,7 @@ myapp-design-token-panel-state-v2
|
|
|
309
317
|
myapp-design-token-panel-state
|
|
310
318
|
myapp-design-token-panel-open
|
|
311
319
|
myapp-design-token-panel-position
|
|
320
|
+
myapp-design-token-panel-spawn-ordinal
|
|
312
321
|
myapp-design-token-panel-size
|
|
313
322
|
myapp-design-token-panel-dock
|
|
314
323
|
myapp-design-token-panel-dock-size
|
|
@@ -329,8 +338,9 @@ myapp-design-token-panel-highlight-active # sessionStorage
|
|
|
329
338
|
|
|
330
339
|
Unit tests in the package verify these derivations with literal-equality
|
|
331
340
|
checks. The v4 precedence and legacy v1/v2/v3 migration paths at first load
|
|
332
|
-
are part of the test matrix
|
|
333
|
-
|
|
341
|
+
are part of the test matrix. The bounded probe in §6.2 covers exactly the
|
|
342
|
+
state versions the loader can read; every future format bump must update the
|
|
343
|
+
shared readable-suffix registry in the same change.
|
|
334
344
|
|
|
335
345
|
### Current `state-v4` envelope
|
|
336
346
|
|
|
@@ -382,11 +392,20 @@ computed at open time as one coherent rectangle:
|
|
|
382
392
|
against the persisted size, not against the default one.
|
|
383
393
|
- **The fallback is instance-aware.** Each additional panel instance
|
|
384
394
|
concurrently mounted on the page offsets its own fallback position by 24px
|
|
385
|
-
on both axes
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
395
|
+
on both axes. Its `storagePrefix` persists the allocated ordinal, so an Astro
|
|
396
|
+
body swap restores the same cascade position even when remount order differs
|
|
397
|
+
from original open order. An absent, malformed, negative, fractional, or
|
|
398
|
+
out-of-range stored value uses mount order with lowest-free-slot reuse and
|
|
399
|
+
records the result. The stored format is a JSON integer from 0 through 31;
|
|
400
|
+
31 is the cap because 32 identities span at most 744px at the 24px step,
|
|
401
|
+
already the useful cascade range on ordinary desktop viewports. A released
|
|
402
|
+
live slot — e.g. from `destroy()` or a shell that does not re-materialise
|
|
403
|
+
after navigation — can be reused immediately; a retained stored identity
|
|
404
|
+
reserves nothing by itself and Reset does not delete it. If two prefixes
|
|
405
|
+
contain the same ordinal, the already-live holder keeps it and the later
|
|
406
|
+
claimant takes and persists the lowest free slot. This exists only to keep
|
|
407
|
+
simultaneously-opened instances from landing exactly on top of one another;
|
|
408
|
+
it has no effect once a `position` value is persisted.
|
|
390
409
|
- **A persisted `position` value always wins over the cascade.** The 24px
|
|
391
410
|
offset applies only to the computed fallback, never to a stored value —
|
|
392
411
|
once `position` is written, that instance reopens at the exact stored
|
|
@@ -1094,8 +1113,17 @@ This is the reason the JSON-serializable constraint in §4.2 is non-negotiable.
|
|
|
1094
1113
|
|
|
1095
1114
|
### 6.2 Lazy-load gate
|
|
1096
1115
|
|
|
1097
|
-
The
|
|
1098
|
-
|
|
1116
|
+
The machine-readable metadata for these signals is exported from the
|
|
1117
|
+
side-effect-free `@takazudo/zdtp/constants` subpath. Use
|
|
1118
|
+
`EAGER_LOAD_GATE_KEY_SUFFIXES` for the five fixed suffix descriptors
|
|
1119
|
+
(`:visible`, `-open`, `:autoload`, `-elpath-enabled`, and
|
|
1120
|
+
`-domtweaker-enabled`), and `EAGER_LOAD_GATE_STATE_FAMILY` for the versioned
|
|
1121
|
+
`-state` family. These exports are the source of truth for consumers that
|
|
1122
|
+
mirror the gate. Each fixed descriptor supplies `acceptedValues` and
|
|
1123
|
+
`requiredConfig`; presence alone is insufficient, and a named required config
|
|
1124
|
+
property must be defined. The prose below explains the runtime meaning. The host adapter fires one eager
|
|
1125
|
+
`loadPanelModule()` call when any of these signals activates in `localStorage`
|
|
1126
|
+
at page load:
|
|
1099
1127
|
|
|
1100
1128
|
```ts
|
|
1101
1129
|
if (
|
|
@@ -1114,9 +1142,10 @@ if (
|
|
|
1114
1142
|
is applied a second time to the `${storagePrefix}-open` mirror (dash-form,
|
|
1115
1143
|
§2) that `panel.tsx` writes alongside it. Either key holding `'1'` means the
|
|
1116
1144
|
panel was open before the last navigation.
|
|
1117
|
-
- `hasPersistedOverrides()` —
|
|
1118
|
-
`${storagePrefix}-state`
|
|
1119
|
-
`-state-
|
|
1145
|
+
- `hasPersistedOverrides()` — performs bounded `getItem` probes for the
|
|
1146
|
+
`${storagePrefix}-state` keys the loader can read (dash-form, §2: `-state`
|
|
1147
|
+
(v1), `-state-v2`, `-state-v3`, and `-state-v4`) and returns `true` when at
|
|
1148
|
+
least one holds a non-empty envelope
|
|
1120
1149
|
(malformed JSON also counts as `true` — fail open, so the panel loads and
|
|
1121
1150
|
can migrate or reject the payload rather than stranding the user with data
|
|
1122
1151
|
it can never see). This is a **content check**, not a presence check on a
|
|
@@ -1126,6 +1155,19 @@ if (
|
|
|
1126
1155
|
hand or by another tool.) Overrides MUST be re-applied to the configured
|
|
1127
1156
|
sink (or default `:root`) even when the panel stays hidden, otherwise
|
|
1128
1157
|
hard-nav produces a FOUT.
|
|
1158
|
+
|
|
1159
|
+
`READABLE_STATE_KEY_SUFFIXES` is the dependency-free registry of those four
|
|
1160
|
+
readable suffixes. Every storage-format bump MUST update it in the same
|
|
1161
|
+
change as the loader. `EAGER_LOAD_GATE_STATE_FAMILY.matchesKey(storagePrefix,
|
|
1162
|
+
key)` recognizes only exact keys constructed from that registry; unreadable
|
|
1163
|
+
future versions and `${storagePrefix}-spawn-ordinal` do not activate. It does
|
|
1164
|
+
not read storage or perform the content check. The consumer must apply the
|
|
1165
|
+
accompanying `valueRules`: raw empty strings, JSON `null`, and empty
|
|
1166
|
+
objects/arrays do not activate; non-empty collections, every other parsed
|
|
1167
|
+
primitive (including `false`, `0`, and JSON `""`), and malformed JSON do
|
|
1168
|
+
activate. The top-level emptiness check is defensive-only: zdtp-written v4
|
|
1169
|
+
envelopes always contain their top-level buckets, and Reset removes state
|
|
1170
|
+
keys rather than writing empty envelopes.
|
|
1129
1171
|
- `shouldAutoload()` — reads `${storagePrefix}:autoload` (colon-form, §2).
|
|
1130
1172
|
Returns `true` when the flag is `'1'` (explicit, written by `enableAutoload()`)
|
|
1131
1173
|
OR `'auto'` (auto-remembered, written by opening the panel — see "Auto-remember
|
|
@@ -1155,7 +1197,7 @@ that decision.
|
|
|
1155
1197
|
| Signal | Key derivation | Owner |
|
|
1156
1198
|
|--------|---------------|-------|
|
|
1157
1199
|
| `wasVisible` | `${storagePrefix}:visible`, OR its `${storagePrefix}-open` mirror | adapter |
|
|
1158
|
-
| `hasPersistedOverrides` |
|
|
1200
|
+
| `hasPersistedOverrides` | Bounded content checks for the readable suffix registry (`-state`, `-state-v2`, `-state-v3`, `-state-v4`) | tweak-state |
|
|
1159
1201
|
| `shouldAutoload` | `${storagePrefix}:autoload`, matching `'1'` or `'auto'` | autoload-state |
|
|
1160
1202
|
| `loadElementPathEnabled` | `${storagePrefix}-elpath-enabled` | element-path-state |
|
|
1161
1203
|
| `loadDomTweakerEnabled` | `${storagePrefix}-domtweaker-enabled` | dom-tweaker-state |
|
|
@@ -1626,7 +1668,7 @@ Cross-reference table — what each section pins down.
|
|
|
1626
1668
|
| ------------------------------------------------------------------------------------------- | ------------- |
|
|
1627
1669
|
| `configurePanel({...})` signature, multi-instance, `PanelInstanceHandle`, per-instance toggle events | §1 |
|
|
1628
1670
|
| Storage-key derivation | §2, §8 |
|
|
1629
|
-
| Default first-open geometry (coherent size+position, viewport containment, cascade, persisted-position precedence) | §2.1 |
|
|
1671
|
+
| Default first-open geometry (coherent size+position, viewport containment, persisted cascade identity, persisted-position precedence) | §2.1 |
|
|
1630
1672
|
| `PanelDockConfig`, dock modes, body-margin reflow, edge claims, and dock storage | §1, §2, §7 |
|
|
1631
1673
|
| `TabConfig` / `TierConfig` / `TierItem` / `TierValueKind` interfaces and apply behaviour | §3 |
|
|
1632
1674
|
| `TierConfig.preview` / `previewBase` matrix and host-page specimen lifecycle | §3.2 |
|
package/README.md
CHANGED
|
@@ -414,6 +414,57 @@ Copy an example's structure when porting the panel into a new host.
|
|
|
414
414
|
|
|
415
415
|
If you are building a **Rust SSG** or other non-Node host, the bin still runs as a sidecar Node.js subprocess (started by your host's build orchestration). The same routing JSON and host-adapter setup applies — the only difference is your host ships its own config format (not TypeScript) and you invoke the bin via your build system's subprocess spawner rather than npm scripts.
|
|
416
416
|
|
|
417
|
+
#### Pre-import activation from a custom host
|
|
418
|
+
|
|
419
|
+
The side-effect-free `@takazudo/zdtp/constants` subpath exposes
|
|
420
|
+
`DEFAULT_STORAGE_PREFIX`, `DEFAULT_TOGGLE_EVENT`, and
|
|
421
|
+
`resolveToggleEventName` without importing the panel. It also exports the
|
|
422
|
+
machine-readable eager-load metadata (`EAGER_LOAD_GATE_KEY_SUFFIXES` and
|
|
423
|
+
`EAGER_LOAD_GATE_STATE_FAMILY`); the exact gate contract lives in
|
|
424
|
+
[`PORTABLE-CONTRACT.md`](./PORTABLE-CONTRACT.md) §6.2.
|
|
425
|
+
|
|
426
|
+
A custom host can use those constants to register its toggle listener in the
|
|
427
|
+
initial bundle and fetch the panel only when activated. Keep the stylesheet
|
|
428
|
+
setup from §4.2 (or import `@takazudo/zdtp/styles` in your host stylesheet
|
|
429
|
+
pipeline):
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
import type { PanelConfig } from '@takazudo/zdtp';
|
|
433
|
+
import { resolveToggleEventName } from '@takazudo/zdtp/constants';
|
|
434
|
+
|
|
435
|
+
// Call once with your complete config (for example myPanelConfig in §4.1.1).
|
|
436
|
+
export function installLazyPanel(config: PanelConfig): void {
|
|
437
|
+
const toggleEvent = resolveToggleEventName(config);
|
|
438
|
+
let loading: Promise<void> | undefined;
|
|
439
|
+
|
|
440
|
+
function loadOnToggle(): void {
|
|
441
|
+
if (loading) return;
|
|
442
|
+
loading = import('@takazudo/zdtp')
|
|
443
|
+
.then(({ configurePanel }) => {
|
|
444
|
+
configurePanel(config);
|
|
445
|
+
window.removeEventListener(toggleEvent, loadOnToggle);
|
|
446
|
+
window.dispatchEvent(new CustomEvent(toggleEvent));
|
|
447
|
+
})
|
|
448
|
+
.catch((error: unknown) => {
|
|
449
|
+
loading = undefined;
|
|
450
|
+
console.error('Failed to load @takazudo/zdtp', error);
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
window.addEventListener(toggleEvent, loadOnToggle);
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The default `DEFAULT_STORAGE_PREFIX` is
|
|
459
|
+
`'zudo-design-token-panel'`, and its `DEFAULT_TOGGLE_EVENT` is
|
|
460
|
+
`'toggle-design-token-panel'`. `resolveToggleEventName` preserves that
|
|
461
|
+
historical event even when a default-prefix config supplies `toggleEvent`; a
|
|
462
|
+
non-default prefix uses its supplied `toggleEvent`, or derives
|
|
463
|
+
`toggle-${storagePrefix}` when the override is omitted. The same config
|
|
464
|
+
resolves the listener event and configures the loaded panel. This activation-only
|
|
465
|
+
example does not implement the saved-state eager-load gate; see §6.2 of the
|
|
466
|
+
contract when building a complete host adapter.
|
|
467
|
+
|
|
417
468
|
### 4.3 Recipe — Rust SSG (zfb)
|
|
418
469
|
|
|
419
470
|
Worked example for the case where the host is a Rust dev server (e.g. [zfb / zudo-front-builder](https://github.com/Takazudo/zudo-front-builder)) rather than a Node-based runner. The bin itself is unchanged — it remains a Node.js subprocess invoked as `node path/to/dist/bin/server.js ...`. The Rust host's only job is to spawn that Node process, forward shutdown signals to it, and configure `--allow-origin` so the browser POST from the panel UI is accepted.
|
|
@@ -537,7 +588,7 @@ default instance only.
|
|
|
537
588
|
| `exportFilenameBase` | `string` | Default download filename base — exports save as `${exportFilenameBase}.json`. |
|
|
538
589
|
| `toggleEvent` | `string` (optional) | Window-event name that toggles THIS instance. Defaults to `toggle-${storagePrefix}` for non-default instances; the default instance keeps `toggle-design-token-panel`. |
|
|
539
590
|
| `tabs` | `readonly TabConfig[]` | **Required.** Tab strip data — each entry is a tab with one or more `TierConfig` objects. The color tab (id `'color'`) additionally requires `colorExtras`. See §6. |
|
|
540
|
-
| `colorPresets` | `Record<string, ColorScheme>` (optional) | Optional named scheme presets surfaced in the Color tab "Scheme..." dropdown. Defaults to `{}`. See §7.
|
|
591
|
+
| `colorPresets` | `Record<string, ColorScheme>` (optional) | Optional named scheme presets surfaced in the Color tab "Scheme..." dropdown. Defaults to `{}`. See §7.4. |
|
|
541
592
|
| `applyEndpoint` | `string` (optional) | Browser POST target for apply previews and confirmed writes. See §3 and [Apply pipeline reference](/docs/reference/apply-pipeline). |
|
|
542
593
|
| `applyRouting` | `Record<string, string>` (optional) | CSS-var prefix-family to repo-relative CSS-file routing map used by the apply endpoint. |
|
|
543
594
|
| `applySink` | `ApplySink` (optional) | Optional sink that routes this instance's CSS-var writes off `:root`. See §5.4. Not JSON-serializable — do not include in Astro inline config. |
|
|
@@ -595,7 +646,7 @@ const handle = configurePanel({
|
|
|
595
646
|
The Astro entry point (`<DesignTokenPanelHost>`) handles mounting for you. Internally:
|
|
596
647
|
|
|
597
648
|
- The console API (`showDesignPanel` etc.) is **always installed eagerly**, even when the panel module has not loaded — calling them is what triggers the lazy import for cold-start users.
|
|
598
|
-
- The panel module is **dynamically imported on first need**: when the user calls a console helper, OR when first-paint detects any of these gate signals in `localStorage` — `${storagePrefix}:visible` set to `1` or its `${storagePrefix}-open` mirror set to `1`, persisted overrides (
|
|
649
|
+
- The panel module is **dynamically imported on first need**: when the user calls a console helper, OR when first-paint detects any of these gate signals in `localStorage` — `${storagePrefix}:visible` set to `1` or its `${storagePrefix}-open` mirror set to `1`, persisted overrides (bounded content checks for the state formats the loader can read: `-state` (v1), `-state-v2`, `-state-v3`, and `-state-v4`; see §9), the owner-autoload flag (`${storagePrefix}:autoload` set to `'1'` or `'auto'`), or the element-path inspector enabled. `${storagePrefix}-domtweaker-enabled` is also a gate signal when `domTweaker` is configured.
|
|
599
650
|
- This gating keeps the panel bundle out of the initial JS payload for first-time visitors while still re-applying overrides on hard reload for users who have tweaked things. **General visitors** (none of these signals set) pay no panel-bundle cost; the small host adapter/config bootstrap still runs.
|
|
600
651
|
|
|
601
652
|
For a Vite-only / non-Astro host, mount it yourself by importing the adapter module after `configurePanel(...)`. See §8.5.
|
|
@@ -606,7 +657,7 @@ The first time an instance opens with no persisted position, the panel picks its
|
|
|
606
657
|
|
|
607
658
|
- **Size first, position from that size.** The default size follows the historical `min(1200, 0.8·vw) × min(800, 0.8·vh)` rule, clamped to a minimum floor and the viewport. The default position then centers _that same clamped rectangle_. Position and size can no longer disagree about the panel's width — they used to be computed separately, which could center a wider phantom panel than the one actually rendered and spawn it partly off-screen on a narrow viewport.
|
|
608
659
|
- **Always contained in the viewport.** The centered position is run through a containment clamp before use, so the whole panel — not just a grabbable strip of it — sits inside `[0, innerWidth]` × `[0, innerHeight]`. This holds at every viewport width, including phone widths, and it holds for a *persisted* size too: an instance that has a saved size but no saved position (the user resized the panel without ever dragging it) is centered and contained against that saved size, not against the default one.
|
|
609
|
-
- **Instance-aware: concurrent panels cascade apart.** Each additional concurrently-mounted instance offsets its own fresh-open position by 24px on both axes.
|
|
660
|
+
- **Instance-aware: concurrent panels cascade apart.** Each additional concurrently-mounted instance offsets its own fresh-open position by 24px on both axes. Each `storagePrefix` persists its allocated ordinal, so an Astro body swap restores the same cascade position even when remount order differs from original open order. With no valid stored ordinal, allocation uses mount order with lowest-free-slot reuse and then records the result. Unmounting always releases the live claim: a departed instance's retained identity does not reserve a slot, and a later panel may reuse it. If two prefixes contain the same ordinal, the already-live holder keeps it and the later claimant takes and persists the lowest free slot. Two instances opened at once therefore don't spawn stacked exactly on top of each other.
|
|
610
661
|
- **A persisted position always wins.** The cascade offset applies only to the fallback used when an instance has no saved position yet. A panel the user has dragged (and which got saved) reopens exactly where it was left, regardless of how many sibling instances happen to be mounted.
|
|
611
662
|
- **Containment beats distinctness.** These two guarantees are not both promised unconditionally — on a viewport with no spare room, the 24px cascade shrinks toward whatever slack is left, down to a 0px offset, rather than pushing a panel outside the viewport. Two instances' first-open positions may then coincide. Each axis degrades independently: at a 320px viewport width the horizontal offset collapses to 0 once the minimum-width floor eats all the spare width, while the vertical offset may still cascade its full 24px if vertical slack remains.
|
|
612
663
|
- **A different, stricter rule than the drag-recovery clamp.** Once a panel has been dragged, its position is governed by a separate, deliberately permissive clamp: it only guarantees a small grip of the panel's header stays on-screen so the user can drag it back, and otherwise allows the panel to hang off any edge. That drag clamp is unchanged and keeps applying after a drag; the full-containment guarantee above is specific to the first-open fallback position and does not carry over once the user has moved the panel.
|
|
@@ -853,7 +904,7 @@ literal mapping, or `light-dark(light, dark)` for a per-mode literal.
|
|
|
853
904
|
Roles absent from `colorExtras.baseRoles` are not written, so a minimalist
|
|
854
905
|
cluster (just `background` + `foreground`) is fine.
|
|
855
906
|
|
|
856
|
-
### 7.
|
|
907
|
+
### 7.4 Host-supplied scheme presets — `colorPresets`
|
|
857
908
|
|
|
858
909
|
The Color tab's "Scheme..." dropdown surfaces named `ColorScheme` entries. Two sources feed it:
|
|
859
910
|
|
|
@@ -1012,6 +1063,7 @@ Behaviour notes:
|
|
|
1012
1063
|
| `state-v1` | `${storagePrefix}-state` | Legacy pre-v2 flat-state format (Color-only). Migrated into `state-v3` (and from there into `state-v4`) on first load, then deleted. |
|
|
1013
1064
|
| `open` | `${storagePrefix}-open` | Mirror of the panel's `open` boolean, read synchronously at mount so user intent survives reloads. |
|
|
1014
1065
|
| `position` | `${storagePrefix}-position` | Drag position `{ top, left }` so the panel reappears where the user left it. |
|
|
1066
|
+
| `spawn-ordinal` | `${storagePrefix}-spawn-ordinal` | Instance cascade identity: a JSON integer from 0 through 31, retained by Reset and restored across Astro body swaps. Invalid values fall back to lowest-free allocation. |
|
|
1015
1067
|
| `size` | `${storagePrefix}-size` | Floating shell dimensions `{ width, height }` in pixels. |
|
|
1016
1068
|
| `dock` | `${storagePrefix}-dock` | Presentation mode: `'float'`, `'right'`, `'bottom'`, or `'mini'`. |
|
|
1017
1069
|
| `dock-size` | `${storagePrefix}-dock-size` | Right/bottom dock dimensions `{ right, bottom }`, defaulting to `{ right: 440, bottom: 340 }`. |
|
|
@@ -1038,6 +1090,7 @@ myapp-design-token-panel-state-v2
|
|
|
1038
1090
|
myapp-design-token-panel-state
|
|
1039
1091
|
myapp-design-token-panel-open
|
|
1040
1092
|
myapp-design-token-panel-position
|
|
1093
|
+
myapp-design-token-panel-spawn-ordinal
|
|
1041
1094
|
myapp-design-token-panel-size
|
|
1042
1095
|
myapp-design-token-panel-dock
|
|
1043
1096
|
myapp-design-token-panel-dock-size
|
|
@@ -1359,7 +1412,7 @@ For the regression-guard tests that pin this contract, see `package-exports.test
|
|
|
1359
1412
|
|
|
1360
1413
|
**Symptom:** on first paint after a hard reload, the page renders with the consumer's default token values for a beat before snapping to the user's saved overrides.
|
|
1361
1414
|
|
|
1362
|
-
**Resolution:** the host adapter eagerly re-applies persisted overrides during the lazy-load gate (it content
|
|
1415
|
+
**Resolution:** the host adapter eagerly re-applies persisted overrides during the lazy-load gate (it performs bounded content checks for the readable `${storagePrefix}-state`, `-state-v2`, `-state-v3`, and `-state-v4` keys, plus the visibility, autoload, and enabled-feature signals synchronously from `localStorage`). If you still see a flash, your `<DesignTokenPanelHost>` is being rendered too late in the document (e.g. inside a deferred island) — move it to the layout's `<body>` and verify the inline `<script type="application/json" id="tokenpanel-config">` is in the initial HTML.
|
|
1363
1416
|
|
|
1364
1417
|
### 13.2 Auto-mount race on first reload
|
|
1365
1418
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { c as y, g as m, a as w, b, i as P } from "../panel-config-
|
|
2
|
-
import { g, Z as
|
|
3
|
-
import { s as
|
|
1
|
+
import { c as y, g as m, a as w, b, i as P } from "../panel-config-CqbuB0nh.js";
|
|
2
|
+
import { g, Z as A } from "../tweak-state-c53KtLHq.js";
|
|
3
|
+
import { s as D, l as E, a as _, i as c, r as f, c as k, b as S, d } from "../autoload-state-BI4-9A58.js";
|
|
4
|
+
import { READABLE_STATE_KEY_SUFFIXES as T } from "../constants.js";
|
|
4
5
|
const u = "tokenpanel-config";
|
|
5
|
-
function
|
|
6
|
+
function I() {
|
|
6
7
|
if (typeof document > "u")
|
|
7
8
|
throw new Error(
|
|
8
9
|
"[design-token-panel] host-adapter loaded without a document; expected to run in a browser context."
|
|
@@ -12,24 +13,24 @@ function S() {
|
|
|
12
13
|
throw new Error(
|
|
13
14
|
`[design-token-panel] Inline config script #${u} not found. Ensure <DesignTokenPanelHost config={...} /> is rendered on this page before the host script runs.`
|
|
14
15
|
);
|
|
15
|
-
const
|
|
16
|
-
let
|
|
16
|
+
const n = e.textContent ?? "";
|
|
17
|
+
let o;
|
|
17
18
|
try {
|
|
18
|
-
|
|
19
|
+
o = JSON.parse(n);
|
|
19
20
|
} catch (a) {
|
|
20
21
|
throw new Error(
|
|
21
22
|
`[design-token-panel] Failed to parse inline config from #${u}: ${a.message}`
|
|
22
23
|
);
|
|
23
24
|
}
|
|
24
|
-
return b(
|
|
25
|
+
return b(o), o;
|
|
25
26
|
}
|
|
26
|
-
function
|
|
27
|
+
function v(e) {
|
|
27
28
|
return e.__zudoDesignTokenPanelAdapter || (e.__zudoDesignTokenPanelAdapter = {}), e.__zudoDesignTokenPanelAdapter;
|
|
28
29
|
}
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
let a = n
|
|
32
|
-
return a || (a = { bound: !1, modulePromise: null }, n
|
|
30
|
+
function C(e, n) {
|
|
31
|
+
const o = v(e);
|
|
32
|
+
let a = o[n];
|
|
33
|
+
return a || (a = { bound: !1, modulePromise: null }, o[n] = a), a;
|
|
33
34
|
}
|
|
34
35
|
function p(e) {
|
|
35
36
|
try {
|
|
@@ -38,58 +39,53 @@ function p(e) {
|
|
|
38
39
|
return !1;
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
function C(e) {
|
|
42
|
-
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43
|
-
}
|
|
44
42
|
function O(e) {
|
|
45
43
|
if (e === null || e === "") return !1;
|
|
46
|
-
let
|
|
44
|
+
let n;
|
|
47
45
|
try {
|
|
48
|
-
|
|
46
|
+
n = JSON.parse(e);
|
|
49
47
|
} catch {
|
|
50
48
|
return !0;
|
|
51
49
|
}
|
|
52
|
-
return
|
|
50
|
+
return n === null ? !1 : Array.isArray(n) ? n.length > 0 : typeof n == "object" ? Object.keys(n).length > 0 : !0;
|
|
53
51
|
}
|
|
54
52
|
function x(e) {
|
|
55
53
|
try {
|
|
56
|
-
const
|
|
57
|
-
for (
|
|
58
|
-
|
|
59
|
-
if (t !== null && o.test(t) && O(n.getItem(t)))
|
|
54
|
+
const n = window.localStorage;
|
|
55
|
+
for (const o of Object.values(T))
|
|
56
|
+
if (O(n.getItem(e.storagePrefix + o)))
|
|
60
57
|
return !0;
|
|
61
|
-
}
|
|
62
58
|
return !1;
|
|
63
59
|
} catch {
|
|
64
60
|
return !1;
|
|
65
61
|
}
|
|
66
62
|
}
|
|
67
63
|
async function s(e) {
|
|
68
|
-
return e.modulePromise === null && (e.modulePromise = import("@takazudo/zdtp").then((
|
|
64
|
+
return e.modulePromise === null && (e.modulePromise = import("@takazudo/zdtp").then((n) => {
|
|
69
65
|
try {
|
|
70
|
-
const
|
|
71
|
-
|
|
66
|
+
const o = m(), a = n.__panelConfigForTest();
|
|
67
|
+
o !== a && console.warn(
|
|
72
68
|
"[design-token-panel] Singleton-sharing check failed: the host adapter and the panel module observed different PanelConfig singletons. This indicates the package's `config/panel-config` module is no longer code-split into a single shared chunk. The panel may behave correctly today, but storage keys / namespaces / branding could diverge between the two surfaces in future bundles."
|
|
73
69
|
);
|
|
74
|
-
} catch (
|
|
70
|
+
} catch (o) {
|
|
75
71
|
console.warn(
|
|
76
|
-
"[design-token-panel] Singleton-sharing check could not run (likely an older dist without the __panelConfigForTest accessor): " +
|
|
72
|
+
"[design-token-panel] Singleton-sharing check could not run (likely an older dist without the __panelConfigForTest accessor): " + o.message
|
|
77
73
|
);
|
|
78
74
|
}
|
|
79
|
-
return
|
|
75
|
+
return n;
|
|
80
76
|
})), e.modulePromise;
|
|
81
77
|
}
|
|
82
|
-
function
|
|
83
|
-
const i = e[
|
|
78
|
+
function N(e, n, o, a, t) {
|
|
79
|
+
const i = e[n] ?? {};
|
|
84
80
|
i.showDesignPanel = async () => {
|
|
85
81
|
const r = d();
|
|
86
|
-
await s(
|
|
82
|
+
await s(o), c(r) && (f(t), a.open());
|
|
87
83
|
}, i.hideDesignPanel = async () => {
|
|
88
84
|
const r = d();
|
|
89
|
-
await s(
|
|
85
|
+
await s(o), c(r) && a.close();
|
|
90
86
|
}, i.toggleDesignPanel = async () => {
|
|
91
87
|
const r = d();
|
|
92
|
-
if (await s(
|
|
88
|
+
if (await s(o), !c(r)) return;
|
|
93
89
|
a.toggle();
|
|
94
90
|
let l = !1;
|
|
95
91
|
try {
|
|
@@ -98,17 +94,17 @@ function R(e, o, n, a, t) {
|
|
|
98
94
|
}
|
|
99
95
|
l && f(t);
|
|
100
96
|
}, i.enableAutoload = async () => {
|
|
101
|
-
const r = d(), l = await s(
|
|
97
|
+
const r = d(), l = await s(o);
|
|
102
98
|
c(r) && l.enableAutoload(t);
|
|
103
99
|
}, i.disableAutoload = async () => {
|
|
104
100
|
const r = d();
|
|
105
|
-
if (
|
|
106
|
-
const l = await s(
|
|
101
|
+
if (o.modulePromise !== null) {
|
|
102
|
+
const l = await s(o);
|
|
107
103
|
if (!c(r)) return;
|
|
108
104
|
l.disableAutoload(t);
|
|
109
105
|
return;
|
|
110
106
|
}
|
|
111
|
-
|
|
107
|
+
k(t), S(!1, t);
|
|
112
108
|
try {
|
|
113
109
|
window.localStorage.setItem(w(t), "0");
|
|
114
110
|
} catch {
|
|
@@ -117,30 +113,30 @@ function R(e, o, n, a, t) {
|
|
|
117
113
|
window.localStorage.removeItem(g(t));
|
|
118
114
|
} catch {
|
|
119
115
|
}
|
|
120
|
-
}, e[
|
|
116
|
+
}, e[n] = i;
|
|
121
117
|
}
|
|
122
|
-
function
|
|
118
|
+
function F(e, n) {
|
|
123
119
|
P({
|
|
124
120
|
show: async () => {
|
|
125
|
-
const
|
|
126
|
-
await s(e), c(
|
|
121
|
+
const o = d();
|
|
122
|
+
await s(e), c(o) && n.open();
|
|
127
123
|
},
|
|
128
124
|
hide: async () => {
|
|
129
|
-
const
|
|
130
|
-
await s(e), c(
|
|
125
|
+
const o = d();
|
|
126
|
+
await s(e), c(o) && n.close();
|
|
131
127
|
},
|
|
132
128
|
toggle: async () => {
|
|
133
|
-
const
|
|
134
|
-
await s(e), c(
|
|
129
|
+
const o = d();
|
|
130
|
+
await s(e), c(o) && n.toggle();
|
|
135
131
|
}
|
|
136
132
|
});
|
|
137
133
|
}
|
|
138
134
|
(function() {
|
|
139
|
-
const
|
|
140
|
-
if (
|
|
135
|
+
const n = I(), o = n.legacyIdRenameMap ? n : { ...n, legacyIdRenameMap: { ...A } }, a = y(o), t = m(), i = window, r = C(i, t.storagePrefix);
|
|
136
|
+
if (N(i, t.consoleNamespace, r, a, t), F(r, a), r.bound) return;
|
|
141
137
|
r.bound = !0;
|
|
142
138
|
const l = w(t);
|
|
143
|
-
(p(l) || p(g(t)) || x(t) ||
|
|
139
|
+
(p(l) || p(g(t)) || x(t) || D(t) || E(t) || t.domTweaker !== void 0 && _(t)) && s(r).catch((h) => {
|
|
144
140
|
console.error("[design-token-panel] Eager panel-module load failed.", h);
|
|
145
141
|
});
|
|
146
142
|
})();
|
package/dist/astro/index.js
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
*/
|
|
48
48
|
import type { ColorScheme } from './color-schemes';
|
|
49
49
|
import type { TabConfig } from '../tokens/tier-model';
|
|
50
|
+
export { DEFAULT_STORAGE_PREFIX, DEFAULT_TOGGLE_EVENT } from '../constants';
|
|
50
51
|
/**
|
|
51
52
|
* Apply-routing map.
|
|
52
53
|
*
|
|
@@ -470,6 +471,13 @@ export declare function storageKey_stateV3(cfg: PanelConfig): string;
|
|
|
470
471
|
export declare function storageKey_stateV4(cfg: PanelConfig): string;
|
|
471
472
|
/** Legacy v1 key (Color-only flat state). Migrated into v2 on first load, then deleted. */
|
|
472
473
|
export declare function storageKey_stateV1(cfg: PanelConfig): string;
|
|
474
|
+
/**
|
|
475
|
+
* Persisted identity used to keep a fresh panel's cascade position stable
|
|
476
|
+
* across an Astro body swap. This is deliberately separate from the unified
|
|
477
|
+
* tweak-state envelopes: it identifies an instance; it is not user-authored
|
|
478
|
+
* token state and does not participate in state-schema migration.
|
|
479
|
+
*/
|
|
480
|
+
export declare function storageKey_spawnOrdinal(cfg: PanelConfig): string;
|
|
473
481
|
/** Mirror of the panel's `open` boolean (synchronous mount-time read). */
|
|
474
482
|
export declare function storageKey_open(cfg: PanelConfig): string;
|
|
475
483
|
/** Drag position `{ top, left }` so the panel reappears where the user left it. */
|
|
@@ -510,13 +518,6 @@ export declare function storageKey_visible(cfg: PanelConfig): string;
|
|
|
510
518
|
export declare function storageKey_autoload(cfg: PanelConfig): string;
|
|
511
519
|
/** DOM id of the root element the Preact panel tree mounts into. */
|
|
512
520
|
export declare function panelRootId(cfg: PanelConfig): string;
|
|
513
|
-
/**
|
|
514
|
-
* Historical public toggle-event name. Hosts have shipped `window.dispatchEvent(
|
|
515
|
-
* new CustomEvent('toggle-design-token-panel'))` since the single-panel era, so
|
|
516
|
-
* the default instance MUST keep emitting/listening on this name regardless of
|
|
517
|
-
* its derived `toggle-${storagePrefix}` form.
|
|
518
|
-
*/
|
|
519
|
-
export declare const DEFAULT_TOGGLE_EVENT = "toggle-design-token-panel";
|
|
520
521
|
/**
|
|
521
522
|
* Window-event name that toggles this instance's panel.
|
|
522
523
|
*
|