@takazudo/zudo-doc 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ /** Icon plate color — light plate, pinned by #3286. */
2
+ export declare const ICON_BG = "#ffffff";
3
+ /** Icon ink — the default light scheme fg `oklch(.185 .005 65)` as sRGB. */
4
+ export declare const ICON_INK = "#141210";
5
+ /**
6
+ * Render the deterministic square icon variant of the AutoLogo design as a
7
+ * standalone, fully opaque, directly painted SVG string. Same seed always
8
+ * produces byte-identical output; glyph selection reuses
9
+ * {@link pickGlyphName} so the icon shows the same glyph as `logo: "auto"`
10
+ * mode and the ejected logo.
11
+ */
12
+ export declare function renderAutoLogoIconSvg(seed: string): string;
@@ -0,0 +1,23 @@
1
+ import { GLYPH_SHAPES, pickGlyphName } from "./shapes.js";
2
+ import { SQUARE_SIZE, SQUARE_PLATE_SHAPES } from "./shapes-square.js";
3
+ import { escapeAttr, renderShape as renderShapeWith } from "./render-shape.js";
4
+ const ICON_BG = "#ffffff";
5
+ const ICON_INK = "#141210";
6
+ const ICON_COLORS = {
7
+ currentColor: ICON_INK,
8
+ "var(--color-bg)": ICON_BG
9
+ };
10
+ function renderShape(shape) {
11
+ return renderShapeWith(shape, ICON_COLORS);
12
+ }
13
+ function renderAutoLogoIconSvg(seed) {
14
+ const glyphName = pickGlyphName(seed);
15
+ const glyphShapes = GLYPH_SHAPES[glyphName];
16
+ const { plate, innerFrame, rays, disc, glyphTransform } = SQUARE_PLATE_SHAPES;
17
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${SQUARE_SIZE} ${SQUARE_SIZE}" data-auto-logo="${glyphName}">` + renderShape(plate) + renderShape(innerFrame) + rays.map(renderShape).join("") + renderShape(disc) + `<g transform="${escapeAttr(glyphTransform)}">${glyphShapes.map(renderShape).join("")}</g></svg>`;
18
+ }
19
+ export {
20
+ ICON_BG,
21
+ ICON_INK,
22
+ renderAutoLogoIconSvg
23
+ };
@@ -4,6 +4,7 @@ import type { JSX } from "preact";
4
4
  import { pickGlyphName } from "./shapes.js";
5
5
  export { pickGlyphName };
6
6
  export { renderAutoLogoStandaloneSvg } from "./standalone.js";
7
+ export { renderAutoLogoIconSvg } from "./icon.js";
7
8
  /** Props for {@link AutoLogo}. */
8
9
  export interface AutoLogoProps {
9
10
  /** Deterministic seed — pass `settings.siteName`. */
@@ -12,6 +12,7 @@ import {
12
12
  pickGlyphName
13
13
  } from "./shapes.js";
14
14
  import { renderAutoLogoStandaloneSvg } from "./standalone.js";
15
+ import { renderAutoLogoIconSvg } from "./icon.js";
15
16
  function renderShape({ el, attrs }, key) {
16
17
  return h(el, key !== void 0 ? { ...attrs, key } : attrs);
17
18
  }
@@ -29,5 +30,6 @@ function AutoLogo({ seed, class: className }) {
29
30
  export {
30
31
  AutoLogo,
31
32
  pickGlyphName,
33
+ renderAutoLogoIconSvg,
32
34
  renderAutoLogoStandaloneSvg
33
35
  };
@@ -0,0 +1,9 @@
1
+ import type { ShapePrimitive } from "./shapes.js";
2
+ export declare function escapeAttr(value: string): string;
3
+ /**
4
+ * Serialize one primitive, substituting any attr value found in `colors` —
5
+ * keyed on the exactly-two color tokens of the shapes.ts color contract
6
+ * (`currentColor` for ink, `var(--color-bg)` for knockouts), so a full
7
+ * substitution map guarantees neither token reaches the output.
8
+ */
9
+ export declare function renderShape(shape: ShapePrimitive, colors: Record<string, string>): string;
@@ -0,0 +1,14 @@
1
+ function escapeAttr(value) {
2
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
3
+ }
4
+ function renderShape(shape, colors) {
5
+ const attrs = Object.entries(shape.attrs).map(([key, value]) => {
6
+ const resolved = typeof value === "string" && value in colors ? colors[value] : value;
7
+ return `${key}="${escapeAttr(String(resolved))}"`;
8
+ }).join(" ");
9
+ return `<${shape.el} ${attrs} />`;
10
+ }
11
+ export {
12
+ escapeAttr,
13
+ renderShape
14
+ };
@@ -0,0 +1,7 @@
1
+ import { type PlateShapes } from "./shapes.js";
2
+ /** Square plate side length (viewBox units). */
3
+ export declare const SQUARE_SIZE = 200;
4
+ /** Disc radius — preserves the rect logo's disc/short-side ratio ≈ 0.65. */
5
+ export declare const SQUARE_DISC_R = 65;
6
+ /** The square plate composition, built from the shared builder. */
7
+ export declare const SQUARE_PLATE_SHAPES: PlateShapes;
@@ -0,0 +1,14 @@
1
+ import { INSET, buildPlateShapes } from "./shapes.js";
2
+ const SQUARE_SIZE = 200;
3
+ const SQUARE_DISC_R = 65;
4
+ const SQUARE_PLATE_SHAPES = buildPlateShapes({
5
+ w: SQUARE_SIZE,
6
+ h: SQUARE_SIZE,
7
+ inset: INSET,
8
+ discR: SQUARE_DISC_R
9
+ });
10
+ export {
11
+ SQUARE_DISC_R,
12
+ SQUARE_PLATE_SHAPES,
13
+ SQUARE_SIZE
14
+ };
@@ -9,20 +9,46 @@ export declare const INSET = 6;
9
9
  export declare const CX: number;
10
10
  export declare const CY: number;
11
11
  export declare const DISC_R = 34;
12
- /** Full-bleed plate rect, filled with the page background so the tile
13
- * interior reads as the page itself (opaque, not `fill: "none"`, so it also
14
- * covers a textured page background). */
12
+ /** Inputs for {@link buildPlateShapes}. */
13
+ export interface PlateParams {
14
+ w: number;
15
+ h: number;
16
+ inset: number;
17
+ discR: number;
18
+ }
19
+ /** One plate composition (everything except the glyph shape data) as built
20
+ * by {@link buildPlateShapes}. */
21
+ export interface PlateShapes {
22
+ /** Full-bleed plate rect, filled with the page background so the tile
23
+ * interior reads as the page itself (opaque, not `fill: "none"`, so it
24
+ * also covers a textured page background). */
25
+ plate: ShapePrimitive;
26
+ /** Thin inner frame stroke, drawn in ink over the plate. */
27
+ innerFrame: ShapePrimitive;
28
+ /** 4 diagonal corner rays in ink, each stopping just short of the disc. */
29
+ rays: ShapePrimitive[];
30
+ /** Centered ink disc — the glyph is knocked back out of it. */
31
+ disc: ShapePrimitive;
32
+ /** Scale that fits a glyph's 100×100 box onto the disc
33
+ * (slight overscan reads better). */
34
+ glyphScale: number;
35
+ /** Transform that centers the scaled glyph box on the disc. */
36
+ glyphTransform: string;
37
+ }
38
+ /**
39
+ * Build the "decorated plate" composition — plate, inset frame, 4 corner
40
+ * rays, center disc, glyph transform — for any plate size. The 200×105 rect
41
+ * logo constants below and the square icon recomposition
42
+ * (`shapes-square.ts`, epic #3285) are both built from this one function so
43
+ * their geometry cannot drift.
44
+ */
45
+ export declare function buildPlateShapes({ w, h, inset, discR }: PlateParams): PlateShapes;
15
46
  export declare const PLATE_SHAPE: ShapePrimitive;
16
- /** Thin inner frame stroke, drawn in ink over the plate. */
17
47
  export declare const INNER_FRAME_SHAPE: ShapePrimitive;
18
- /** 4 diagonal corner rays in ink, each stopping just short of the disc edge. */
19
48
  export declare const RAY_SHAPES: ShapePrimitive[];
20
- /** Centered ink disc — the glyph below is knocked back out of it. */
21
49
  export declare const DISC_SHAPE: ShapePrimitive;
22
- /** Scale + translate that centers a glyph's 100×100 box onto the disc
23
- * (slight overscan reads better). */
24
50
  export declare const GLYPH_SCALE: number;
25
- export declare const GLYPH_TRANSFORM = "translate(64.3, 16.799999999999997) scale(0.7140000000000001)";
51
+ export declare const GLYPH_TRANSFORM: string;
26
52
  /** Line-art glyphs drawn in a 100×100 box centered at (50,50), as plain
27
53
  * shape data. Every painted `fill`/`stroke` here is the knockout token, so
28
54
  * the glyph reads as a hole punched through the ink disc it sits on.
@@ -6,43 +6,55 @@ const CY = H / 2;
6
6
  const DISC_R = 34;
7
7
  const INK = "currentColor";
8
8
  const KO = "var(--color-bg)";
9
- const PLATE_SHAPE = {
10
- el: "rect",
11
- attrs: { x: 0, y: 0, width: W, height: H, fill: KO }
12
- };
13
- const INNER_FRAME_SHAPE = {
14
- el: "rect",
15
- attrs: {
16
- x: INSET,
17
- y: INSET,
18
- width: W - INSET * 2,
19
- height: H - INSET * 2,
20
- fill: "none",
21
- stroke: INK,
22
- "stroke-width": 1.6
23
- }
24
- };
25
- const RAY_SHAPES = [
26
- [INSET, INSET],
27
- [W - INSET, INSET],
28
- [INSET, H - INSET],
29
- [W - INSET, H - INSET]
30
- ].map(([x, y]) => {
31
- const dx = CX - x;
32
- const dy = CY - y;
33
- const len = Math.hypot(dx, dy);
34
- const stop = (len - DISC_R - 7) / len;
35
- return {
36
- el: "line",
37
- attrs: { x1: x, y1: y, x2: x + dx * stop, y2: y + dy * stop, stroke: INK, "stroke-width": 1.4 }
9
+ function buildPlateShapes({ w, h, inset, discR }) {
10
+ const cx = w / 2;
11
+ const cy = h / 2;
12
+ const plate = {
13
+ el: "rect",
14
+ attrs: { x: 0, y: 0, width: w, height: h, fill: KO }
38
15
  };
39
- });
40
- const DISC_SHAPE = {
41
- el: "circle",
42
- attrs: { cx: CX, cy: CY, r: DISC_R, fill: INK }
43
- };
44
- const GLYPH_SCALE = DISC_R * 2 / 100 * 1.05;
45
- const GLYPH_TRANSFORM = `translate(${CX - 50 * GLYPH_SCALE}, ${CY - 50 * GLYPH_SCALE}) scale(${GLYPH_SCALE})`;
16
+ const innerFrame = {
17
+ el: "rect",
18
+ attrs: {
19
+ x: inset,
20
+ y: inset,
21
+ width: w - inset * 2,
22
+ height: h - inset * 2,
23
+ fill: "none",
24
+ stroke: INK,
25
+ "stroke-width": 1.6
26
+ }
27
+ };
28
+ const rays = [
29
+ [inset, inset],
30
+ [w - inset, inset],
31
+ [inset, h - inset],
32
+ [w - inset, h - inset]
33
+ ].map(([x, y]) => {
34
+ const dx = cx - x;
35
+ const dy = cy - y;
36
+ const len = Math.hypot(dx, dy);
37
+ const stop = (len - discR - 7) / len;
38
+ return {
39
+ el: "line",
40
+ attrs: { x1: x, y1: y, x2: x + dx * stop, y2: y + dy * stop, stroke: INK, "stroke-width": 1.4 }
41
+ };
42
+ });
43
+ const disc = {
44
+ el: "circle",
45
+ attrs: { cx, cy, r: discR, fill: INK }
46
+ };
47
+ const glyphScale = discR * 2 / 100 * 1.05;
48
+ const glyphTransform = `translate(${cx - 50 * glyphScale}, ${cy - 50 * glyphScale}) scale(${glyphScale})`;
49
+ return { plate, innerFrame, rays, disc, glyphScale, glyphTransform };
50
+ }
51
+ const RECT_PLATE_SHAPES = buildPlateShapes({ w: W, h: H, inset: INSET, discR: DISC_R });
52
+ const PLATE_SHAPE = RECT_PLATE_SHAPES.plate;
53
+ const INNER_FRAME_SHAPE = RECT_PLATE_SHAPES.innerFrame;
54
+ const RAY_SHAPES = RECT_PLATE_SHAPES.rays;
55
+ const DISC_SHAPE = RECT_PLATE_SHAPES.disc;
56
+ const GLYPH_SCALE = RECT_PLATE_SHAPES.glyphScale;
57
+ const GLYPH_TRANSFORM = RECT_PLATE_SHAPES.glyphTransform;
46
58
  const GLYPH_SHAPES = {
47
59
  book: [
48
60
  {
@@ -180,5 +192,6 @@ export {
180
192
  PLATE_SHAPE,
181
193
  RAY_SHAPES,
182
194
  W,
195
+ buildPlateShapes,
183
196
  pickGlyphName
184
197
  };
@@ -9,20 +9,14 @@ import {
9
9
  GLYPH_TRANSFORM,
10
10
  pickGlyphName
11
11
  } from "./shapes.js";
12
+ import { escapeAttr, renderShape as renderShapeWith } from "./render-shape.js";
12
13
  const MASK_ID = "auto-logo-mask";
13
14
  const MASK_COLORS = {
14
15
  currentColor: "#fff",
15
16
  "var(--color-bg)": "#000"
16
17
  };
17
- function escapeAttr(value) {
18
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
19
- }
20
18
  function renderShape(shape) {
21
- const attrs = Object.entries(shape.attrs).map(([key, value]) => {
22
- const resolved = typeof value === "string" && value in MASK_COLORS ? MASK_COLORS[value] : value;
23
- return `${key}="${escapeAttr(String(resolved))}"`;
24
- }).join(" ");
25
- return `<${shape.el} ${attrs} />`;
19
+ return renderShapeWith(shape, MASK_COLORS);
26
20
  }
27
21
  function renderAutoLogoStandaloneSvg(seed) {
28
22
  const glyphName = pickGlyphName(seed);
@@ -2,13 +2,22 @@
2
2
  /** @jsxImportSource preact */
3
3
  /**
4
4
  * Design-token panel (zdtp) WIRING MECHANISM + PACKAGE-DEFAULT ISLAND (#2658,
5
- * epic Minimal Scaffold #2651).
5
+ * epic Minimal Scaffold #2651). zdtp itself is LAZY-LOADED (#3282, epic
6
+ * #3261): this module carries NO top-level value import of `@takazudo/zdtp` —
7
+ * the package is `import()`ed on the first dispatch on either resolved toggle
8
+ * channel (the shared `toggle-design-token-panel`, or this instance's own —
9
+ * see {@link resolveInstanceToggleEvent}), or eagerly when the persisted-state
10
+ * probe finds saved tweaks / a previously-open panel, so zdtp's bundle stays
11
+ * out of the initial page load.
6
12
  *
7
13
  * `bootstrapDesignTokenPanel` is a callable that configures zdtp's panel and
8
14
  * wires its lifecycle hooks to zfb's navigation events via
9
15
  * `setLifecycleAdapter()`. It receives a mode-scoped PanelConfig builder —
10
16
  * either a project override or the package default from
11
- * `@takazudo/zudo-doc/design-token-panel-config`.
17
+ * `@takazudo/zudo-doc/design-token-panel-config`. The builder module is
18
+ * zdtp-runtime-free (type-only zdtp imports), which is what lets it stay a
19
+ * STATIC import here without defeating the lazy load — and lets the probe
20
+ * compute the exact active storage prefix before zdtp exists.
12
21
  *
13
22
  * The current call shape is a mode-scoped builder. It rebuilds the panel per
14
23
  * light/dark mode on every `color-scheme-changed` toggle:
@@ -51,7 +60,7 @@
51
60
  * required pull point. See @takazudo/zdtp PORTABLE-CONTRACT.md §7.
52
61
  */
53
62
  import type { JSX } from "preact";
54
- import { type PanelConfig } from "@takazudo/zdtp";
63
+ import type { PanelConfig } from "@takazudo/zdtp";
55
64
  /** Active color-scheme mode, read from `<html data-theme>`. */
56
65
  type ColorSchemeMode = "light" | "dark";
57
66
  /**
@@ -79,14 +88,16 @@ export type PanelConfigBuilder = (mode: ColorSchemeMode) => PanelConfig;
79
88
  */
80
89
  export declare function withPackScopedStoragePrefix(config: PanelConfig, activePack: string): PanelConfig;
81
90
  /**
82
- * Bootstrap zdtp for a project. Configures the panel, drains any pre-hydration
83
- * click queue, and wires the zdtp lifecycle adapter to zfb's navigation events
84
- * so persisted token overrides re-apply on every soft navigation.
85
- *
86
- * Wires a `color-scheme-changed` listener that rebuilds the panel per
87
- * light/dark mode (see the toggle sequence below), and a parallel
88
- * `theme-pack-changed` listener that rebuilds it per theme pack with a
89
- * pack-scoped storage prefix (ADR theme-packs.md Decision 4, #2822).
91
+ * Bootstrap zdtp for a project — LAZILY (#3282). At mount this registers the
92
+ * interim toggle listener on both resolved channels (#3315), drains the
93
+ * pre-hydration click queue, and probes localStorage for persisted panel
94
+ * state; the actual
95
+ * `import("@takazudo/zdtp")` + configure happen on the first toggle (or
96
+ * immediately on a probe hit). The configure body then wires everything the
97
+ * pre-lazy version wired eagerly: `configurePanel` with the pack-scoped
98
+ * mode-config, the `color-scheme-changed` / `theme-pack-changed` rebuild
99
+ * listeners, `reapplyPersistedOverrides`, and the zfb lifecycle adapter via
100
+ * `setLifecycleAdapter`.
90
101
  *
91
102
  * @param buildConfig - The project's `(mode) => PanelConfig` builder for
92
103
  * mode-scoped rebuilds.
@@ -1,10 +1,4 @@
1
1
  "use client";
2
- import {
3
- configurePanel,
4
- reapplyPersistedOverrides,
5
- setLifecycleAdapter,
6
- showDesignTokenPanel
7
- } from "@takazudo/zdtp";
8
2
  import {
9
3
  BEFORE_NAVIGATE_EVENT,
10
4
  AFTER_NAVIGATE_EVENT
@@ -16,6 +10,14 @@ import {
16
10
  } from "./theme-pack-switcher/theme-pack-sync.js";
17
11
  import { buildDesignTokenPanelConfig } from "virtual:zudo-doc-design-token-panel-config";
18
12
  const COLOR_SCHEME_CHANGED_EVENT = "color-scheme-changed";
13
+ const TOGGLE_PANEL_EVENT = "toggle-design-token-panel";
14
+ const ZDTP_DEFAULT_STORAGE_PREFIX = "zudo-design-token-panel";
15
+ function resolveInstanceToggleEvent(config) {
16
+ if (config.storagePrefix === ZDTP_DEFAULT_STORAGE_PREFIX) {
17
+ return TOGGLE_PANEL_EVENT;
18
+ }
19
+ return config.toggleEvent ?? `toggle-${config.storagePrefix}`;
20
+ }
19
21
  function openStateKey(instancePrefix) {
20
22
  return `${instancePrefix}-open`;
21
23
  }
@@ -26,6 +28,41 @@ function readOpenState(instancePrefix) {
26
28
  return false;
27
29
  }
28
30
  }
31
+ const ACTIVATION_FLAG_KEY_SUFFIXES = [
32
+ ":autoload",
33
+ ":visible",
34
+ "-elpath-enabled",
35
+ "-domtweaker-enabled"
36
+ ];
37
+ function isEmptyEnvelope(raw) {
38
+ if (raw === null) return true;
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(raw);
42
+ } catch {
43
+ return false;
44
+ }
45
+ if (parsed === null) return true;
46
+ if (Array.isArray(parsed)) return parsed.length === 0;
47
+ if (typeof parsed === "object") return Object.keys(parsed).length === 0;
48
+ return false;
49
+ }
50
+ function hasPersistedPanelState(instancePrefix) {
51
+ try {
52
+ if (localStorage.getItem(openStateKey(instancePrefix)) === "1") return true;
53
+ for (const suffix of ACTIVATION_FLAG_KEY_SUFFIXES) {
54
+ if (localStorage.getItem(`${instancePrefix}${suffix}`) === "1") return true;
55
+ }
56
+ const stateKeyStem = `${instancePrefix}-state`;
57
+ for (let i = 0; i < localStorage.length; i++) {
58
+ const key = localStorage.key(i);
59
+ if (key === null || !key.startsWith(stateKeyStem)) continue;
60
+ if (!isEmptyEnvelope(localStorage.getItem(key))) return true;
61
+ }
62
+ } catch {
63
+ }
64
+ return false;
65
+ }
29
66
  function readMode() {
30
67
  return document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "light";
31
68
  }
@@ -74,61 +111,156 @@ function bootstrapDesignTokenPanel(buildConfig) {
74
111
  if (typeof window === "undefined" || typeof document === "undefined") {
75
112
  return;
76
113
  }
77
- let currentConfig = withPackScopedStoragePrefix(
78
- buildConfig(readMode()),
79
- readThemePackFromDom()
80
- );
81
- let handle = configurePanel(currentConfig);
82
- let pendingMode = readMode();
83
- let timer = null;
84
- window.addEventListener(COLOR_SCHEME_CHANGED_EVENT, () => {
85
- pendingMode = readMode();
86
- if (timer !== null) return;
87
- timer = setTimeout(() => {
88
- timer = null;
89
- const mode = pendingMode;
90
- const wasOpen = readOpenState(handle.instanceId);
91
- handle.destroy();
92
- currentConfig = withPackScopedStoragePrefix(
93
- buildConfig(mode),
94
- readThemePackFromDom()
114
+ let zdtpImport = null;
115
+ let activationInFlight = false;
116
+ let configurePhase = "pending";
117
+ let pendingToggles = 0;
118
+ function loadZdtp() {
119
+ if (zdtpImport === null) {
120
+ zdtpImport = import("@takazudo/zdtp").catch((err) => {
121
+ zdtpImport = null;
122
+ throw err;
123
+ });
124
+ }
125
+ return zdtpImport;
126
+ }
127
+ function configureAndWire(zdtp) {
128
+ const {
129
+ configurePanel,
130
+ reapplyPersistedOverrides,
131
+ setLifecycleAdapter,
132
+ showDesignTokenPanel
133
+ } = zdtp;
134
+ let currentConfig = withPackScopedStoragePrefix(
135
+ buildConfig(readMode()),
136
+ readThemePackFromDom()
137
+ );
138
+ let handle = configurePanel(currentConfig);
139
+ let pendingMode = readMode();
140
+ let timer = null;
141
+ window.addEventListener(COLOR_SCHEME_CHANGED_EVENT, () => {
142
+ pendingMode = readMode();
143
+ if (timer !== null) return;
144
+ timer = setTimeout(() => {
145
+ timer = null;
146
+ const mode = pendingMode;
147
+ const wasOpen = readOpenState(handle.instanceId);
148
+ handle.destroy();
149
+ currentConfig = withPackScopedStoragePrefix(
150
+ buildConfig(mode),
151
+ readThemePackFromDom()
152
+ );
153
+ handle = configurePanel(currentConfig);
154
+ if (wasOpen) showDesignTokenPanel();
155
+ }, 0);
156
+ });
157
+ let pendingPack = readThemePackFromDom();
158
+ let packTimer = null;
159
+ window.addEventListener(THEME_PACK_CHANGED_EVENT, () => {
160
+ pendingPack = readThemePackFromDom();
161
+ if (packTimer !== null) return;
162
+ packTimer = setTimeout(() => {
163
+ packTimer = null;
164
+ const pack = pendingPack;
165
+ const wasOpen = readOpenState(handle.instanceId);
166
+ const outgoingConfig = currentConfig;
167
+ handle.destroy();
168
+ clearAppliedTokenOverrides(outgoingConfig);
169
+ currentConfig = withPackScopedStoragePrefix(buildConfig(readMode()), pack);
170
+ handle = configurePanel(currentConfig);
171
+ reapplyPersistedOverrides();
172
+ if (wasOpen) showDesignTokenPanel();
173
+ }, 0);
174
+ });
175
+ reapplyPersistedOverrides();
176
+ const adapter = {
177
+ onBeforeSwap(cb) {
178
+ const handler = () => cb();
179
+ document.addEventListener(BEFORE_NAVIGATE_EVENT, handler);
180
+ return () => document.removeEventListener(BEFORE_NAVIGATE_EVENT, handler);
181
+ },
182
+ onPageLoad(cb) {
183
+ const handler = () => cb();
184
+ document.addEventListener(AFTER_NAVIGATE_EVENT, handler);
185
+ return () => document.removeEventListener(AFTER_NAVIGATE_EVENT, handler);
186
+ }
187
+ };
188
+ setLifecycleAdapter(adapter);
189
+ }
190
+ async function activate() {
191
+ if (activationInFlight || configurePhase !== "pending") return;
192
+ activationInFlight = true;
193
+ let zdtp;
194
+ try {
195
+ zdtp = await loadZdtp();
196
+ } catch (err) {
197
+ activationInFlight = false;
198
+ pendingToggles = 0;
199
+ console.error(
200
+ "[zudo-doc] design-token-panel: loading @takazudo/zdtp failed; the next toggle retries.",
201
+ err
95
202
  );
96
- handle = configurePanel(currentConfig);
97
- if (wasOpen) showDesignTokenPanel();
98
- }, 0);
99
- });
100
- let pendingPack = readThemePackFromDom();
101
- let packTimer = null;
102
- window.addEventListener(THEME_PACK_CHANGED_EVENT, () => {
103
- pendingPack = readThemePackFromDom();
104
- if (packTimer !== null) return;
105
- packTimer = setTimeout(() => {
106
- packTimer = null;
107
- const pack = pendingPack;
108
- const wasOpen = readOpenState(handle.instanceId);
109
- const outgoingConfig = currentConfig;
110
- handle.destroy();
111
- clearAppliedTokenOverrides(outgoingConfig);
112
- currentConfig = withPackScopedStoragePrefix(buildConfig(readMode()), pack);
113
- handle = configurePanel(currentConfig);
114
- reapplyPersistedOverrides();
115
- if (wasOpen) showDesignTokenPanel();
116
- }, 0);
117
- });
118
- window.__zdtpReadyClicks?.();
119
- const adapter = {
120
- onBeforeSwap(cb) {
121
- const handler = () => cb();
122
- document.addEventListener(BEFORE_NAVIGATE_EVENT, handler);
123
- return () => document.removeEventListener(BEFORE_NAVIGATE_EVENT, handler);
124
- },
125
- onPageLoad(cb) {
126
- const handler = () => cb();
127
- document.addEventListener(AFTER_NAVIGATE_EVENT, handler);
128
- return () => document.removeEventListener(AFTER_NAVIGATE_EVENT, handler);
203
+ return;
204
+ }
205
+ try {
206
+ configureAndWire(zdtp);
207
+ configurePhase = "configured";
208
+ } catch (err) {
209
+ configurePhase = "failed";
210
+ console.error(
211
+ "[zudo-doc] design-token-panel: configure failed; the panel is disabled until the next full page load.",
212
+ err
213
+ );
214
+ return;
215
+ } finally {
216
+ activationInFlight = false;
129
217
  }
218
+ if (pendingToggles % 2 === 1) zdtp.showDesignTokenPanel();
219
+ pendingToggles = 0;
220
+ }
221
+ function readActiveConfig() {
222
+ return withPackScopedStoragePrefix(
223
+ buildConfig(readMode()),
224
+ readThemePackFromDom()
225
+ );
226
+ }
227
+ const onInterimToggle = () => {
228
+ if (configurePhase !== "pending") return;
229
+ pendingToggles += 1;
230
+ void activate();
130
231
  };
131
- setLifecycleAdapter(adapter);
232
+ const boundToggleChannels = /* @__PURE__ */ new Set();
233
+ let instanceToggleChannel = null;
234
+ function bindToggleChannel(name) {
235
+ if (boundToggleChannels.has(name)) return;
236
+ boundToggleChannels.add(name);
237
+ window.addEventListener(name, onInterimToggle);
238
+ }
239
+ function refreshInstanceToggleChannel(config) {
240
+ const resolved = resolveInstanceToggleEvent(config);
241
+ if (resolved === instanceToggleChannel) return;
242
+ if (instanceToggleChannel !== null && instanceToggleChannel !== TOGGLE_PANEL_EVENT) {
243
+ window.removeEventListener(instanceToggleChannel, onInterimToggle);
244
+ boundToggleChannels.delete(instanceToggleChannel);
245
+ }
246
+ instanceToggleChannel = resolved;
247
+ bindToggleChannel(resolved);
248
+ }
249
+ const bootConfig = readActiveConfig();
250
+ bindToggleChannel(TOGGLE_PANEL_EVENT);
251
+ refreshInstanceToggleChannel(bootConfig);
252
+ window.__zdtpReadyClicks?.();
253
+ if (hasPersistedPanelState(bootConfig.storagePrefix)) void activate();
254
+ window.addEventListener(THEME_PACK_CHANGED_EVENT, () => {
255
+ if (configurePhase !== "pending") return;
256
+ const activeConfig = readActiveConfig();
257
+ refreshInstanceToggleChannel(activeConfig);
258
+ if (hasPersistedPanelState(activeConfig.storagePrefix)) void activate();
259
+ });
260
+ window.addEventListener(COLOR_SCHEME_CHANGED_EVENT, () => {
261
+ if (configurePhase !== "pending") return;
262
+ refreshInstanceToggleChannel(readActiveConfig());
263
+ });
132
264
  }
133
265
  let bootstrapped = false;
134
266
  function DesignTokenPanelBootstrap() {