dsh-live-voice-debugger 0.4.1 → 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/ARCHITECTURE.md CHANGED
@@ -4,6 +4,6 @@ This public, optional client plugin builds separately from Live Voice. It contri
4
4
 
5
5
  The panel samples current state at a selectable 1, 2, 5, 10 or 20 Hz (default 10). State and event panes scroll independently. Event recording can be paused independently from live state sampling and the entire event history can be cleared. Resuming recording starts from the current baseline without replaying changes that occurred while paused. Changing the sampling rate preserves the history. While connected, coordinator notifications produce a bounded 200-entry state-difference history filterable by module. This is an initial inspector, not a complete semantic event logger: changes hidden inside an adapter can only be sampled, and native recognition internals remain unavailable. No audio is retained. Free-form text is hidden by default and can be inspected explicitly in memory, never persisted. Closing the panel releases subscriptions and timers.
6
6
 
7
- Build from the repository root with `pnpm build`, or build this package with `pnpm --filter dsh-live-voice-debugger build`. The generated client/server entries land in lib, not in the main client bundle. For local installation, add the packages/live-voice-debugger directory as a separate DSH plugin using the supported plugin manager; both the main plugin's diagnostic bridge and this plugin must be loaded. Then open Settings → Live Voice → Developer and open the separate inspector window (allow site popups if blocked). Publication is explicit through the npm workflow. Configure npm Trusted Publishing for this package with workflow publish.yml and environment npm. Select the debugger-only target when the aligned main version already exists. Publishing both packages is sequential, not atomic.
7
+ For normal use, install the prebuilt npm package with `npx dsh plugin add --profile web dsh-live-voice-debugger`. Generated `lib/` directories are ignored and never committed. For local development, build before linking: build from the repository root with `pnpm build`, or build this package with `pnpm --filter dsh-live-voice-debugger build`. The generated client/server entries land in lib, not in the main client bundle. For local installation, add the packages/live-voice-debugger directory as a separate DSH plugin using the supported plugin manager; both the main plugin's diagnostic bridge and this plugin must be loaded. Then open Settings → Live Voice → Developer and open the separate inspector window (allow site popups if blocked). Publication is explicit through the npm workflow. Configure npm Trusted Publishing for this package with workflow publish.yml and environment npm. Select the debugger-only target when the aligned main version already exists. Publishing both packages is sequential, not atomic.
8
8
 
9
9
  Tests deliver simulated activity, processing and final results through the real coordinator callback boundary; PCM chunks are supplied through the HTTP adapter's fake Web Audio input. They assert the same diagnostic projection that the panel reads. The panel itself never injects test events into live capture or playback.
package/lib/client.js CHANGED
@@ -34,17 +34,249 @@ __export(apply_exports, {
34
34
  inject: () => inject
35
35
  });
36
36
  module.exports = __toCommonJS(apply_exports);
37
+ var import_react22 = __toESM(require("react"), 1);
38
+
39
+ // ../live-voice/src/shared/design-system/buttons/IconButton.tsx
40
+ var import_react2 = __toESM(require("react"), 1);
41
+
42
+ // ../live-voice/src/shared/design-system/icons/Icon.tsx
43
+ var import_react = __toESM(require("react"), 1);
44
+
45
+ // ../live-voice/src/shared/design-system/icons/icons.ts
46
+ var iconPaths = {
47
+ conversation: "M4 3h16v13H8l-4 4V3M8 7h8M8 11h5",
48
+ keyboard: "M3 6h18v12H3V6M7 10h.01M11 10h.01M15 10h.01M18 10h.01M7 14h10",
49
+ headphones: "M3 14v-3a9 9 0 0 1 18 0v3M3 12h4v8H3v-8M17 12h4v8h-4v-8",
50
+ commands: "M3 6l2 2 3-4M11 6h10M3 14l2 2 3-4M11 14h10M11 20h10",
51
+ bug: "M8 8h8v9a4 4 0 0 1-8 0V8M9 8V6a3 3 0 0 1 6 0v2M3 9l5 2M16 11l5-2M3 16h5M16 16h5M5 22l3-3M16 19l3 3",
52
+ meeting: "M8 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6M16 4a3 3 0 1 0 0 6 3 3 0 0 0 0-6M2 20v-3a6 6 0 0 1 12 0v3M16 13a5 5 0 0 1 6 5v2",
53
+ meetingOff: "M8 3a3 3 0 1 0 0 6 3 3 0 0 0 0-6M16 4a3 3 0 1 0 0 6 3 3 0 0 0 0-6M2 20v-3a6 6 0 0 1 12 0v3M17 16l5 5M22 16l-5 5",
54
+ mic: "M9 5a3 3 0 0 1 6 0v7a3 3 0 0 1-6 0V5M6 10v2a6 6 0 0 0 12 0v-2M12 18v4M8 22h8",
55
+ micOff: "M9 9v3a3 3 0 0 0 5.12 2.12M15 9V5a3 3 0 0 0-5.64-1.42M6 10v2a6 6 0 0 0 9.5 4.88M18 10v2a6 6 0 0 1-.5 2.4M12 18v4M8 22h8M3 3l18 18",
56
+ speaker: "M3 9h4l6-5v16l-6-5H3V9M17 8a6 6 0 0 1 0 8M20 5a10 10 0 0 1 0 14",
57
+ speakerOff: "M3 9h4l6-5v16l-6-5H3V9M17 9l5 6M22 9l-5 6",
58
+ pause: "M8 5v14M16 5v14",
59
+ play: "M7 4l13 8-13 8z",
60
+ stop: "M6 6h12v12H6z",
61
+ skipNext: "M5 5l10 7-10 7V5M19 5v14",
62
+ skipPrevious: "M19 5l-10 7 10 7V5M5 5v14",
63
+ send: "M3 11.5L21 3l-8.5 18-2-7.5L3 11.5zm7.5 2L21 3",
64
+ queue: "M5 6h14M5 12h10M5 18h6M18 15v6M15 18h6",
65
+ settings: "M4 7h16M4 17h16M8 4v6M16 14v6",
66
+ filter: "M3 5h18l-7 8v6l-4 2v-8L3 5z",
67
+ link: "M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.7 1.7M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.7-1.7",
68
+ chevron: "M6 9l6 6 6-6",
69
+ clock: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18M12 7v5l3 2",
70
+ close: "M6 6l12 12M18 6 6 18"
71
+ };
72
+
73
+ // ../live-voice/src/shared/design-system/icons/Icon.tsx
74
+ function Icon({ name, className }) {
75
+ return /* @__PURE__ */ import_react.default.createElement(
76
+ "svg",
77
+ {
78
+ className,
79
+ viewBox: "0 0 24 24",
80
+ fill: "none",
81
+ stroke: "currentColor",
82
+ strokeWidth: 1.8,
83
+ strokeLinecap: "round",
84
+ strokeLinejoin: "round",
85
+ "aria-hidden": true
86
+ },
87
+ /* @__PURE__ */ import_react.default.createElement("path", { d: iconPaths[name] })
88
+ );
89
+ }
90
+
91
+ // ../live-voice/src/shared/design-system/buttons/PillButton.tsx
37
92
  var import_react3 = __toESM(require("react"), 1);
93
+
94
+ // ../live-voice/src/shared/design-system/buttons/ToggleButton.tsx
95
+ var import_react4 = __toESM(require("react"), 1);
96
+
97
+ // ../live-voice/src/shared/design-system/feedback/ErrorMessage.tsx
98
+ var import_react5 = __toESM(require("react"), 1);
99
+
100
+ // ../live-voice/src/shared/design-system/feedback/StatusBadge.tsx
101
+ var import_react6 = __toESM(require("react"), 1);
102
+
103
+ // ../live-voice/src/shared/design-system/feedback/StatusMessage.tsx
104
+ var import_react7 = __toESM(require("react"), 1);
105
+
106
+ // ../live-voice/src/shared/design-system/feedback/Hint.tsx
107
+ var import_react8 = __toESM(require("react"), 1);
108
+ function Hint({
109
+ label,
110
+ children,
111
+ id: suppliedId
112
+ }) {
113
+ const generatedId = import_react8.default.useId();
114
+ const id = suppliedId || generatedId;
115
+ const [open, setOpen] = import_react8.default.useState(false);
116
+ const trigger = import_react8.default.useRef(null);
117
+ const bubble = import_react8.default.useRef(null);
118
+ const hovered = import_react8.default.useRef(false);
119
+ const focused = import_react8.default.useRef(false);
120
+ const timer = import_react8.default.useRef(void 0);
121
+ const cancel = () => clearTimeout(timer.current);
122
+ const closeLater = () => {
123
+ cancel();
124
+ timer.current = setTimeout(() => {
125
+ if (!hovered.current && !focused.current) setOpen(false);
126
+ }, 120);
127
+ };
128
+ import_react8.default.useEffect(() => () => clearTimeout(timer.current), []);
129
+ import_react8.default.useLayoutEffect(() => {
130
+ const node = bubble.current;
131
+ if (!open || !node || !trigger.current) return;
132
+ node.showPopover?.();
133
+ const position = () => {
134
+ const anchor = trigger.current.getBoundingClientRect();
135
+ const rect = node.getBoundingClientRect();
136
+ node.style.left = Math.max(12, Math.min(anchor.left, window.innerWidth - rect.width - 12)) + "px";
137
+ const below = anchor.bottom + 8;
138
+ node.style.top = Math.max(
139
+ 12,
140
+ below + rect.height <= window.innerHeight - 12 ? below : anchor.top - rect.height - 8
141
+ ) + "px";
142
+ };
143
+ position();
144
+ const dismiss = (e) => {
145
+ if (e.key === "Escape") {
146
+ e.stopPropagation();
147
+ setOpen(false);
148
+ }
149
+ };
150
+ const outside = (e) => {
151
+ if (!node.contains(e.target) && !trigger.current?.contains(e.target))
152
+ setOpen(false);
153
+ };
154
+ document.addEventListener("keydown", dismiss, true);
155
+ document.addEventListener("pointerdown", outside, true);
156
+ window.addEventListener("resize", position);
157
+ window.addEventListener("scroll", position, true);
158
+ return () => {
159
+ node.hidePopover?.();
160
+ document.removeEventListener("keydown", dismiss, true);
161
+ document.removeEventListener("pointerdown", outside, true);
162
+ window.removeEventListener("resize", position);
163
+ window.removeEventListener("scroll", position, true);
164
+ };
165
+ }, [open]);
166
+ return /* @__PURE__ */ import_react8.default.createElement(
167
+ "span",
168
+ {
169
+ className: "dlv-hint",
170
+ onMouseEnter: () => {
171
+ hovered.current = true;
172
+ cancel();
173
+ setOpen(true);
174
+ },
175
+ onMouseLeave: () => {
176
+ hovered.current = false;
177
+ closeLater();
178
+ }
179
+ },
180
+ /* @__PURE__ */ import_react8.default.createElement(
181
+ "button",
182
+ {
183
+ ref: trigger,
184
+ type: "button",
185
+ className: "dlv-hint-trigger",
186
+ "aria-label": label,
187
+ "aria-describedby": open ? id : void 0,
188
+ "aria-expanded": open,
189
+ onFocus: () => {
190
+ focused.current = true;
191
+ cancel();
192
+ setOpen(true);
193
+ },
194
+ onBlur: () => {
195
+ focused.current = false;
196
+ closeLater();
197
+ },
198
+ onClick: () => setOpen(true)
199
+ },
200
+ "?"
201
+ ),
202
+ /* @__PURE__ */ import_react8.default.createElement(
203
+ "span",
204
+ {
205
+ ref: bubble,
206
+ id,
207
+ role: "tooltip",
208
+ popover: "manual",
209
+ hidden: !open,
210
+ className: "dlv-hint-bubble"
211
+ },
212
+ children
213
+ )
214
+ );
215
+ }
216
+
217
+ // ../live-voice/src/shared/design-system/forms/CheckboxField.tsx
218
+ var import_react9 = __toESM(require("react"), 1);
219
+
220
+ // ../live-voice/src/shared/design-system/forms/NumberField.tsx
221
+ var import_react11 = __toESM(require("react"), 1);
222
+
223
+ // ../live-voice/src/shared/design-system/forms/DraftField.tsx
224
+ var import_react10 = __toESM(require("react"), 1);
225
+
226
+ // ../live-voice/src/shared/design-system/forms/SelectField.tsx
227
+ var import_react12 = __toESM(require("react"), 1);
228
+
229
+ // ../live-voice/src/shared/design-system/forms/TextAreaField.tsx
230
+ var import_react13 = __toESM(require("react"), 1);
231
+
232
+ // ../live-voice/src/shared/design-system/forms/TextField.tsx
233
+ var import_react14 = __toESM(require("react"), 1);
234
+
235
+ // ../live-voice/src/shared/design-system/layout/SettingsCard.tsx
236
+ var import_react15 = __toESM(require("react"), 1);
237
+
238
+ // ../live-voice/src/shared/design-system/layout/SettingsSection.tsx
239
+ var import_react17 = __toESM(require("react"), 1);
240
+
241
+ // ../live-voice/src/shared/design-system/layout/SettingsSectionBody.tsx
242
+ var import_react16 = __toESM(require("react"), 1);
243
+
244
+ // ../live-voice/src/shared/design-system/layout/SettingsSubcard.tsx
245
+ var import_react18 = __toESM(require("react"), 1);
246
+
247
+ // ../live-voice/src/shared/design-system/layout/SettingsTabs.tsx
248
+ var import_react19 = __toESM(require("react"), 1);
249
+
250
+ // src/apply.tsx
38
251
  var import_client = require("react-dom/client");
39
252
 
40
253
  // src/DebuggerPanel.tsx
41
- var import_react2 = __toESM(require("react"), 1);
254
+ var import_react21 = __toESM(require("react"), 1);
42
255
 
43
256
  // ../live-voice/src/modules/core/diagnostics.ts
44
257
  var DIAGNOSTIC_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.diagnostics.v1");
45
258
  var DIAGNOSTIC_CHANGED = "dsh-live-voice:diagnostics-changed";
46
259
 
47
260
  // src/inspector.ts
261
+ function semantic(module2, value) {
262
+ if (value == null) return null;
263
+ if (module2 === "vadRuntime")
264
+ return {
265
+ status: value.status ?? null,
266
+ workerActive: value.workerActive ?? null,
267
+ activeSources: value.activeSources ?? null,
268
+ assets: value.assets ?? null
269
+ };
270
+ const omitted = {
271
+ capture: ["level"],
272
+ vad: ["probability", "silenceMs"],
273
+ chunker: ["bufferedSamples", "bufferedChunks", "silenceSamples", "durationMs"]
274
+ };
275
+ if (!omitted[module2]) return value;
276
+ const result = { ...value };
277
+ for (const key of omitted[module2]) delete result[key];
278
+ return result;
279
+ }
48
280
  function createInspector(source, now = Date.now, limit = 200) {
49
281
  let previous = null;
50
282
  let sequence = 0;
@@ -56,22 +288,56 @@ function createInspector(source, now = Date.now, limit = 200) {
56
288
  previous = next;
57
289
  return;
58
290
  }
291
+ const append = (sessionId, module2, before, after) => {
292
+ if (!previous || JSON.stringify(before) === JSON.stringify(after)) return;
293
+ entries.push({
294
+ sequence: ++sequence,
295
+ timeMs: now(),
296
+ sessionId,
297
+ module: module2,
298
+ level: module2.endsWith("error") && after?.present ? "error" : "debug",
299
+ before,
300
+ after
301
+ });
302
+ };
303
+ append(
304
+ null,
305
+ "vadRuntime",
306
+ semantic("vadRuntime", previous?.vadRuntime),
307
+ semantic("vadRuntime", next?.vadRuntime)
308
+ );
309
+ for (const sourceId of /* @__PURE__ */ new Set([
310
+ ...Object.keys(previous?.audioSources ?? {}),
311
+ ...Object.keys(next?.audioSources ?? {})
312
+ ])) {
313
+ const old = previous?.audioSources?.[sourceId];
314
+ const current = next?.audioSources?.[sourceId];
315
+ if (sourceId === "vad") {
316
+ append(
317
+ null,
318
+ "audioSources.vad",
319
+ semantic("vadRuntime", old),
320
+ semantic("vadRuntime", current)
321
+ );
322
+ continue;
323
+ }
324
+ for (const module2 of /* @__PURE__ */ new Set([...Object.keys(old ?? {}), ...Object.keys(current ?? {})])) {
325
+ append(
326
+ null,
327
+ "audioSources." + sourceId + "." + module2,
328
+ semantic(module2, old?.[module2]),
329
+ semantic(module2, current?.[module2])
330
+ );
331
+ }
332
+ }
59
333
  for (const [sessionId, session] of Object.entries(next?.sessions ?? {})) {
60
334
  for (const [module2, value] of Object.entries(session)) {
61
- const normalized = module2 === "capture" ? { ...value, level: null } : value;
62
- const old = previous?.sessions?.[sessionId]?.[module2];
63
- const before = module2 === "capture" && old ? { ...old, level: null } : old;
64
- if (previous && JSON.stringify(before) !== JSON.stringify(normalized)) {
65
- entries.push({
66
- sequence: ++sequence,
67
- timeMs: now(),
68
- sessionId,
69
- module: module2,
70
- level: module2 === "error" && value?.present ? "error" : "debug",
71
- before: before ?? null,
72
- after: normalized
73
- });
74
- }
335
+ append(
336
+ sessionId,
337
+ module2,
338
+ semantic(module2, previous?.sessions?.[sessionId]?.[module2]),
339
+ semantic(module2, value)
340
+ );
75
341
  }
76
342
  }
77
343
  entries = entries.slice(-Math.max(1, limit));
@@ -106,7 +372,7 @@ function createInspector(source, now = Date.now, limit = 200) {
106
372
  }
107
373
 
108
374
  // src/StateTree.tsx
109
- var import_react = __toESM(require("react"), 1);
375
+ var import_react20 = __toESM(require("react"), 1);
110
376
  function StateTree({
111
377
  name,
112
378
  value,
@@ -114,9 +380,9 @@ function StateTree({
114
380
  depth = 0
115
381
  }) {
116
382
  if (value === null || typeof value !== "object")
117
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "dlvd-leaf" }, /* @__PURE__ */ import_react.default.createElement("code", null, name), /* @__PURE__ */ import_react.default.createElement("span", { "data-kind": typeof value }, String(value)));
383
+ return /* @__PURE__ */ import_react20.default.createElement("div", { className: "dlvd-leaf" }, /* @__PURE__ */ import_react20.default.createElement("code", null, name), /* @__PURE__ */ import_react20.default.createElement("span", { "data-kind": typeof value }, String(value)));
118
384
  const entries = Object.entries(value);
119
- return /* @__PURE__ */ import_react.default.createElement("details", { className: "dlvd-branch", open: depth < 3 }, /* @__PURE__ */ import_react.default.createElement("summary", null, /* @__PURE__ */ import_react.default.createElement("code", null, name), /* @__PURE__ */ import_react.default.createElement("small", null, Array.isArray(value) ? "[" + entries.length + "]" : "{" + entries.length + "}")), entries.slice(0, 100).map(([key, child]) => /* @__PURE__ */ import_react.default.createElement(StateTree, { key, name: key, value: child, omitted, depth: depth + 1 })), entries.length > 100 && /* @__PURE__ */ import_react.default.createElement("p", null, omitted));
385
+ return /* @__PURE__ */ import_react20.default.createElement("details", { className: "dlvd-branch", open: depth < 3 }, /* @__PURE__ */ import_react20.default.createElement("summary", null, /* @__PURE__ */ import_react20.default.createElement("code", null, name), /* @__PURE__ */ import_react20.default.createElement("small", null, Array.isArray(value) ? "[" + entries.length + "]" : "{" + entries.length + "}")), entries.slice(0, 100).map(([key, child]) => /* @__PURE__ */ import_react20.default.createElement(StateTree, { key, name: key, value: child, omitted, depth: depth + 1 })), entries.length > 100 && /* @__PURE__ */ import_react20.default.createElement("p", null, omitted));
120
386
  }
121
387
 
122
388
  // src/DebuggerPanel.tsx
@@ -125,17 +391,17 @@ function DebuggerPanel({
125
391
  onClose,
126
392
  target = window
127
393
  }) {
128
- const [frozen, setFrozen] = import_react2.default.useState(false);
129
- const [refreshHz, setRefreshHz] = import_react2.default.useState(10);
130
- const updateRef = import_react2.default.useRef(null);
131
- const [content, setContent] = import_react2.default.useState(false);
132
- const [module2, setModule] = import_react2.default.useState("");
133
- const [view, setView] = import_react2.default.useState(null);
134
- const [failed, setFailed] = import_react2.default.useState(false);
135
- const [eventsPaused, setEventsPaused] = import_react2.default.useState(false);
136
- const pausedRef = import_react2.default.useRef(false);
137
- const inspectorRef = import_react2.default.useRef(null);
138
- import_react2.default.useEffect(() => {
394
+ const [frozen, setFrozen] = import_react21.default.useState(false);
395
+ const [refreshHz, setRefreshHz] = import_react21.default.useState(10);
396
+ const updateRef = import_react21.default.useRef(null);
397
+ const [content, setContent] = import_react21.default.useState(false);
398
+ const [module2, setModule] = import_react21.default.useState("");
399
+ const [view, setView] = import_react21.default.useState(null);
400
+ const [failed, setFailed] = import_react21.default.useState(false);
401
+ const [eventsPaused, setEventsPaused] = import_react21.default.useState(false);
402
+ const pausedRef = import_react21.default.useRef(false);
403
+ const inspectorRef = import_react21.default.useRef(null);
404
+ import_react21.default.useEffect(() => {
139
405
  if (frozen) return;
140
406
  let inspector = null;
141
407
  let source = null;
@@ -168,13 +434,13 @@ function DebuggerPanel({
168
434
  inspectorRef.current = null;
169
435
  };
170
436
  }, [frozen, content, target]);
171
- import_react2.default.useEffect(() => {
437
+ import_react21.default.useEffect(() => {
172
438
  if (frozen) return;
173
439
  const timer = setInterval(() => updateRef.current?.(), 1e3 / refreshHz);
174
440
  return () => clearInterval(timer);
175
441
  }, [frozen, refreshHz]);
176
442
  const modules = [...new Set(view?.transitions.map((entry) => entry.module) ?? [])].sort();
177
- return /* @__PURE__ */ import_react2.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.title }, /* @__PURE__ */ import_react2.default.createElement("header", null, /* @__PURE__ */ import_react2.default.createElement("strong", null, copy.title), /* @__PURE__ */ import_react2.default.createElement("span", null, refreshHz, " Hz"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.close }, "\xD7")), /* @__PURE__ */ import_react2.default.createElement("p", null, copy.help), /* @__PURE__ */ import_react2.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react2.default.createElement("label", null, copy.refreshRate, /* @__PURE__ */ import_react2.default.createElement("select", { value: refreshHz, onChange: (event) => setRefreshHz(Number(event.target.value)) }, [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react2.default.createElement("option", { key: hz, value: hz }, hz, " Hz")))), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.resume : copy.freeze), /* @__PURE__ */ import_react2.default.createElement("label", null, /* @__PURE__ */ import_react2.default.createElement(
443
+ return /* @__PURE__ */ import_react21.default.createElement("aside", { className: "dlvd-panel", "aria-label": copy.title }, /* @__PURE__ */ import_react21.default.createElement("header", null, /* @__PURE__ */ import_react21.default.createElement("strong", null, copy.title), /* @__PURE__ */ import_react21.default.createElement("span", null, refreshHz, " Hz"), /* @__PURE__ */ import_react21.default.createElement("button", { type: "button", onClick: onClose, "aria-label": copy.close }, "\xD7")), /* @__PURE__ */ import_react21.default.createElement("p", null, copy.help), /* @__PURE__ */ import_react21.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react21.default.createElement("label", null, copy.refreshRate, /* @__PURE__ */ import_react21.default.createElement("select", { value: refreshHz, onChange: (event) => setRefreshHz(Number(event.target.value)) }, [1, 2, 5, 10, 20].map((hz) => /* @__PURE__ */ import_react21.default.createElement("option", { key: hz, value: hz }, hz, " Hz")))), /* @__PURE__ */ import_react21.default.createElement("button", { type: "button", onClick: () => setFrozen(!frozen) }, frozen ? copy.resume : copy.freeze), /* @__PURE__ */ import_react21.default.createElement("label", null, /* @__PURE__ */ import_react21.default.createElement(
178
444
  "input",
179
445
  {
180
446
  type: "checkbox",
@@ -185,7 +451,7 @@ function DebuggerPanel({
185
451
  setContent(event.target.checked);
186
452
  }
187
453
  }
188
- ), copy.content)), failed ? /* @__PURE__ */ import_react2.default.createElement("p", { role: "alert" }, copy.error) : !view ? /* @__PURE__ */ import_react2.default.createElement("p", { role: "status" }, copy.waiting) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.states }, /* @__PURE__ */ import_react2.default.createElement("h3", null, copy.states), /* @__PURE__ */ import_react2.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react2.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.omitted }))), /* @__PURE__ */ import_react2.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.transitions }, /* @__PURE__ */ import_react2.default.createElement("h3", null, copy.transitions), /* @__PURE__ */ import_react2.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react2.default.createElement(
454
+ ), copy.content)), failed ? /* @__PURE__ */ import_react21.default.createElement("p", { role: "alert" }, copy.error) : !view ? /* @__PURE__ */ import_react21.default.createElement("p", { role: "status" }, copy.waiting) : /* @__PURE__ */ import_react21.default.createElement("div", { className: "dlvd-columns" }, /* @__PURE__ */ import_react21.default.createElement("section", { className: "dlvd-state-pane", "aria-label": copy.states }, /* @__PURE__ */ import_react21.default.createElement("h3", null, copy.states), /* @__PURE__ */ import_react21.default.createElement("div", { className: "dlvd-scroll", tabIndex: 0 }, /* @__PURE__ */ import_react21.default.createElement(StateTree, { name: "context", value: view.state, omitted: copy.omitted }))), /* @__PURE__ */ import_react21.default.createElement("section", { className: "dlvd-event-pane", "aria-label": copy.transitions }, /* @__PURE__ */ import_react21.default.createElement("h3", null, copy.transitions), /* @__PURE__ */ import_react21.default.createElement("div", { className: "dlvd-toolbar" }, /* @__PURE__ */ import_react21.default.createElement(
189
455
  "button",
190
456
  {
191
457
  type: "button",
@@ -198,7 +464,7 @@ function DebuggerPanel({
198
464
  }
199
465
  },
200
466
  eventsPaused ? copy.eventsResume : copy.eventsPause
201
- ), /* @__PURE__ */ import_react2.default.createElement(
467
+ ), /* @__PURE__ */ import_react21.default.createElement(
202
468
  "button",
203
469
  {
204
470
  type: "button",
@@ -208,7 +474,7 @@ function DebuggerPanel({
208
474
  }
209
475
  },
210
476
  copy.eventsClear
211
- )), /* @__PURE__ */ import_react2.default.createElement("label", null, copy.filter, /* @__PURE__ */ import_react2.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, copy.all), modules.map((name) => /* @__PURE__ */ import_react2.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react2.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react2.default.createElement("summary", null, /* @__PURE__ */ import_react2.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react2.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react2.default.createElement(StateTree, { name: "before", value: entry.before, omitted: copy.omitted }), /* @__PURE__ */ import_react2.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react2.default.createElement("p", null, copy.empty)))));
477
+ )), /* @__PURE__ */ import_react21.default.createElement("label", null, copy.filter, /* @__PURE__ */ import_react21.default.createElement("select", { value: module2, onChange: (event) => setModule(event.target.value) }, /* @__PURE__ */ import_react21.default.createElement("option", { value: "" }, copy.all), modules.map((name) => /* @__PURE__ */ import_react21.default.createElement("option", { key: name }, name)))), /* @__PURE__ */ import_react21.default.createElement("div", { className: "dlvd-history dlvd-scroll", tabIndex: 0 }, view.transitions.filter((entry) => !module2 || entry.module === module2).slice().reverse().map((entry) => /* @__PURE__ */ import_react21.default.createElement("details", { key: entry.sequence, "data-level": entry.level }, /* @__PURE__ */ import_react21.default.createElement("summary", null, /* @__PURE__ */ import_react21.default.createElement("small", null, new Date(entry.timeMs).toLocaleTimeString()), " ", /* @__PURE__ */ import_react21.default.createElement("code", null, entry.sessionId, " / ", entry.module)), /* @__PURE__ */ import_react21.default.createElement(StateTree, { name: "before", value: entry.before, omitted: copy.omitted }), /* @__PURE__ */ import_react21.default.createElement(StateTree, { name: "after", value: entry.after, omitted: copy.omitted }))), !view.transitions.length && /* @__PURE__ */ import_react21.default.createElement("p", null, copy.empty)))));
212
478
  }
213
479
 
214
480
  // src/locales.ts
@@ -220,6 +486,7 @@ var dictionaries = {
220
486
  close: "Close debugger",
221
487
  content: "Inspect text content (sensitive)",
222
488
  developer: "Developer",
489
+ diagnostics: "Diagnostics",
223
490
  empty: "No transitions recorded yet.",
224
491
  error: "Diagnostic source failed.",
225
492
  eventsClear: "Clear events",
@@ -243,6 +510,7 @@ var dictionaries = {
243
510
  close: "Fechar debugger",
244
511
  content: "Inspecionar conte\xFAdo de texto (sens\xEDvel)",
245
512
  developer: "Desenvolvedor",
513
+ diagnostics: "Diagn\xF3stico",
246
514
  empty: "Nenhuma transi\xE7\xE3o registrada ainda.",
247
515
  error: "A fonte de diagn\xF3stico falhou.",
248
516
  eventsClear: "Limpar eventos",
@@ -266,6 +534,7 @@ var dictionaries = {
266
534
  close: "Cerrar depurador",
267
535
  content: "Inspeccionar contenido de texto (sensible)",
268
536
  developer: "Desarrollador",
537
+ diagnostics: "Diagn\xF3stico",
269
538
  empty: "Todav\xEDa no hay transiciones registradas.",
270
539
  error: "La fuente de diagn\xF3stico fall\xF3.",
271
540
  eventsClear: "Borrar eventos",
@@ -289,6 +558,7 @@ var dictionaries = {
289
558
  close: "Fermer le d\xE9bogueur",
290
559
  content: "Inspecter le texte (sensible)",
291
560
  developer: "D\xE9veloppeur",
561
+ diagnostics: "Diagnostic",
292
562
  empty: "Aucune transition enregistr\xE9e.",
293
563
  error: "La source de diagnostic a \xE9chou\xE9.",
294
564
  eventsClear: "Effacer les \xE9v\xE9nements",
@@ -312,6 +582,7 @@ var dictionaries = {
312
582
  close: "\u0921\u0940\u092C\u0917\u0930 \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
313
583
  content: "\u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0938\u093E\u092E\u0917\u094D\u0930\u0940 \u0926\u0947\u0916\u0947\u0902 (\u0938\u0902\u0935\u0947\u0926\u0928\u0936\u0940\u0932)",
314
584
  developer: "\u0921\u0947\u0935\u0932\u092A\u0930",
585
+ diagnostics: "\u0928\u093F\u0926\u093E\u0928",
315
586
  empty: "\u0905\u092D\u0940 \u0915\u094B\u0908 \u091F\u094D\u0930\u093E\u0902\u091C\u093C\u093F\u0936\u0928 \u0926\u0930\u094D\u091C \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
316
587
  error: "\u0921\u093E\u092F\u0917\u094D\u0928\u094B\u0938\u094D\u091F\u093F\u0915 \u0938\u094D\u0930\u094B\u0924 \u0935\u093F\u092B\u0932 \u0939\u0941\u0906\u0964",
317
588
  eventsClear: "\u0907\u0935\u0947\u0902\u091F \u0938\u093E\u092B\u093C \u0915\u0930\u0947\u0902",
@@ -335,6 +606,7 @@ var dictionaries = {
335
606
  close: "\u5173\u95ED\u8C03\u8BD5\u5668",
336
607
  content: "\u67E5\u770B\u6587\u672C\u5185\u5BB9\uFF08\u654F\u611F\uFF09",
337
608
  developer: "\u5F00\u53D1\u8005",
609
+ diagnostics: "\u8BCA\u65AD",
338
610
  empty: "\u5C1A\u672A\u8BB0\u5F55\u72B6\u6001\u8F6C\u6362\u3002",
339
611
  error: "\u8BCA\u65AD\u6570\u636E\u6E90\u5931\u8D25\u3002",
340
612
  eventsClear: "\u6E05\u9664\u4E8B\u4EF6",
@@ -356,13 +628,26 @@ var dictionaries = {
356
628
 
357
629
  // src/styles.ts
358
630
  var styles = `
359
- .dlvd-columns{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px;flex:1;min-height:0;overflow:hidden}.dlvd-state-pane,.dlvd-event-pane{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.dlvd-event-pane{border-left:1px solid #344154;padding-left:16px}.dlvd-columns h3{margin:0 0 10px}.dlvd-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;padding-right:8px}.dlvd-scroll:focus-visible{outline:2px solid #8db9ff;outline-offset:-2px}.dlvd-history summary{overflow-wrap:anywhere}.dlvd-panel>header,.dlvd-panel>p,.dlvd-panel>.dlvd-toolbar{flex-shrink:0}
631
+ .dlvd-columns{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px;flex:1;min-height:0;overflow:hidden}.dlvd-state-pane,.dlvd-event-pane{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}.dlvd-event-pane{border-left:1px solid var(--dsw-alias-border-l1, #ffffff0f);padding-left:16px}.dlvd-columns h3{margin:0 0 10px}.dlvd-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;scrollbar-gutter:stable;padding-right:8px}.dlvd-scroll:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary, #7aaaff);outline-offset:-2px}.dlvd-history summary{overflow-wrap:anywhere}.dlvd-panel>header,.dlvd-panel>p,.dlvd-panel>.dlvd-toolbar{flex-shrink:0}
360
632
 
361
- .dlvd-window{margin:0;background:#101722;color:#e6edf5;color-scheme:dark}.dlvd-window .dlvd-panel{position:static;width:100%;height:100dvh;max-height:none;min-width:0;resize:none;border:0;border-radius:0;box-shadow:none}
362
- .dlvd-panel{position:fixed;right:16px;top:16px;width:min(520px,calc(100vw - 32px));height:80vh;max-height:80vh;overflow:hidden;display:flex;flex-direction:column;z-index:1000;padding:16px;border:1px solid #526177;border-radius:14px;background:#101722;color:#e6edf5;box-shadow:0 12px 40px #0008;font:13px/1.6 system-ui;resize:both;min-width:280px;box-sizing:border-box;color-scheme:dark}
363
- .dlvd-panel header{display:flex;align-items:center;gap:12px;position:sticky;top:-16px;background:#101722;padding:8px 0;z-index:1}.dlvd-panel header strong{flex:1}.dlvd-panel button,.dlvd-panel select{background:#243247;color:#e6edf5;border:1px solid #526177;border-radius:6px;padding:5px 9px;cursor:pointer}.dlvd-panel button:focus-visible,.dlvd-panel summary:focus-visible{outline:2px solid #8db9ff}.dlvd-toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:12px}.dlvd-panel p{color:#aebed1}.dlvd-branch{margin-left:10px;border-left:1px solid #344154;padding-left:9px}.dlvd-branch summary{cursor:pointer;padding:3px 0}.dlvd-panel small{color:#aebed1;margin-left:8px}.dlvd-leaf{display:flex;gap:12px;justify-content:space-between;padding:2px 0;overflow-wrap:anywhere}.dlvd-leaf code{color:#b4c9e4}.dlvd-leaf span{white-space:pre-wrap;text-align:right;max-width:65%}.dlvd-leaf [data-kind=boolean]{color:#c8a5ff}.dlvd-leaf [data-kind=number]{color:#7cdbb6}.dlvd-history{margin-top:12px}.dlvd-history>details{border-top:1px solid #344154;padding:6px 0}.dlvd-history [data-level=error]{border-left:3px solid #ff8a8a;padding-left:8px}
633
+ .dlvd-window{margin:0;background:var(--dsw-alias-bg-base, #151517);color:var(--dsw-alias-label-primary, #f9fafb);color-scheme:var(--dlvd-color-scheme, dark)}.dlvd-window .dlvd-panel{position:static;width:100%;height:100dvh;max-height:none;min-width:0;resize:none;border:0;border-radius:0;box-shadow:none}
634
+ .dlvd-panel{position:fixed;right:16px;top:16px;width:min(520px,calc(100vw - 32px));height:80vh;max-height:80vh;overflow:hidden;display:flex;flex-direction:column;z-index:1000;padding:16px;border:1px solid var(--dsw-alias-border-l2, #ffffff1f);border-radius:14px;background:var(--dsw-alias-bg-base, #151517);color:var(--dsw-alias-label-primary, #f9fafb);box-shadow:0 12px 40px #0008;font:13px/1.6 system-ui;resize:both;min-width:280px;box-sizing:border-box;color-scheme:var(--dlvd-color-scheme, dark)}
635
+ .dlvd-panel header{display:flex;align-items:center;gap:12px;position:sticky;top:-16px;background:var(--dsw-alias-bg-base, #151517);padding:8px 0;z-index:1}.dlvd-panel header strong{flex:1}.dlvd-panel button,.dlvd-panel select{background:var(--dsw-alias-bg-layer-1, #232324);color:var(--dsw-alias-label-primary, #f9fafb);border:1px solid var(--dsw-alias-border-l2, #ffffff1f);border-radius:6px;padding:5px 9px;cursor:pointer}.dlvd-panel button:focus-visible,.dlvd-panel summary:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary, #7aaaff)}.dlvd-toolbar{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-bottom:12px}.dlvd-panel p{color:var(--dsw-alias-label-tertiary, #adb2b8)}.dlvd-branch{margin-left:10px;border-left:1px solid var(--dsw-alias-border-l1, #ffffff0f);padding-left:9px}.dlvd-branch summary{cursor:pointer;padding:3px 0}.dlvd-panel small{color:var(--dsw-alias-label-tertiary, #adb2b8);margin-left:8px}.dlvd-leaf{display:flex;gap:12px;justify-content:space-between;padding:2px 0;overflow-wrap:anywhere}.dlvd-leaf code{color:var(--dsw-alias-label-secondary, #cfd3d6)}.dlvd-leaf span{white-space:pre-wrap;text-align:right;max-width:65%}.dlvd-leaf [data-kind=boolean]{color:var(--dsw-alias-label-primary, #f9fafb)}.dlvd-leaf [data-kind=number]{color:var(--dsw-alias-label-primary, #f9fafb)}.dlvd-history{margin-top:12px}.dlvd-history>details{border-top:1px solid var(--dsw-alias-border-l1, #ffffff0f);padding:6px 0}.dlvd-history [data-level=error]{border-left:3px solid var(--dsw-alias-state-error-primary, #f25a5a);padding-left:8px}
636
+ .dlvd-panel button,.dlvd-panel select{font:inherit}.dlvd-panel button:hover{background:var(--dsw-alias-interactive-bg-hover, #ffffff14)}.dlvd-panel select:focus-visible,.dlvd-panel input:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary, #7aaaff);outline-offset:2px}.dlvd-panel input{accent-color:var(--dsw-alias-state-business-primary, #7aaaff)}.dlvd-scroll{scrollbar-color:var(--dsw-alias-scrollbar-bg-l1, #3c3c3d) transparent}.dlvd-panel [data-kind=boolean],.dlvd-panel [data-kind=number]{font-variant-numeric:tabular-nums}
364
637
  `;
365
638
 
639
+ // src/theme.ts
640
+ function syncDebuggerTheme(source, destination) {
641
+ const theme = source.getComputedStyle(source.document.body);
642
+ const target = destination.documentElement.style;
643
+ for (const name of Array.from(theme)) {
644
+ if (name.startsWith("--dsw-alias-")) target.setProperty(name, theme.getPropertyValue(name));
645
+ }
646
+ const scheme = theme.colorScheme;
647
+ target.setProperty("--dlvd-color-scheme", scheme && scheme !== "normal" ? scheme : "dark");
648
+ destination.body.style.fontFamily = theme.fontFamily || "system-ui";
649
+ }
650
+
366
651
  // ../live-voice/src/modules/core/developerExtension.ts
367
652
  var DEVELOPER_KEY = /* @__PURE__ */ Symbol.for("dsh-live-voice.developer.v1");
368
653
  var DEVELOPER_CHANGED = "dsh-live-voice:developer-changed";
@@ -415,12 +700,12 @@ function apply(ctx) {
415
700
  notify();
416
701
  };
417
702
  function Host() {
418
- import_react3.default.useSyncExternalStore(localeSubscribe, localeSnapshot);
703
+ import_react22.default.useSyncExternalStore(localeSubscribe, localeSnapshot);
419
704
  const labels = copy();
420
- import_react3.default.useEffect(() => {
705
+ import_react22.default.useEffect(() => {
421
706
  if (popup && !popup.closed) popup.document.title = labels.title;
422
707
  }, [labels.title]);
423
- return /* @__PURE__ */ import_react3.default.createElement(DebuggerPanel, { copy: labels, target: window, onClose: () => queueMicrotask(close) });
708
+ return /* @__PURE__ */ import_react22.default.createElement(DebuggerPanel, { copy: labels, target: window, onClose: () => queueMicrotask(close) });
424
709
  }
425
710
  const open = () => {
426
711
  if (popup && !popup.closed) {
@@ -446,6 +731,7 @@ function apply(ctx) {
446
731
  doc.title = copy().title;
447
732
  doc.body.replaceChildren();
448
733
  doc.body.className = "dlvd-window";
734
+ syncDebuggerTheme(window, doc);
449
735
  const style = doc.createElement("style");
450
736
  style.textContent = styles;
451
737
  doc.head.appendChild(style);
@@ -453,10 +739,11 @@ function apply(ctx) {
453
739
  container.dataset.plugin = namespace;
454
740
  doc.body.appendChild(container);
455
741
  root = (0, import_client.createRoot)(container);
456
- root.render(/* @__PURE__ */ import_react3.default.createElement(Host, null));
742
+ root.render(/* @__PURE__ */ import_react22.default.createElement(Host, null));
457
743
  enabled = true;
458
744
  closedTimer = setInterval(() => {
459
745
  if (popup?.closed) close();
746
+ else if (popup) syncDebuggerTheme(window, popup.document);
460
747
  }, 250);
461
748
  notify();
462
749
  } catch {
@@ -467,18 +754,10 @@ function apply(ctx) {
467
754
  }
468
755
  };
469
756
  function Settings() {
470
- const active = import_react3.default.useSyncExternalStore(subscribe, () => enabled);
471
- const failed = import_react3.default.useSyncExternalStore(subscribe, () => blocked);
472
- import_react3.default.useSyncExternalStore(localeSubscribe, localeSnapshot);
757
+ const failed = import_react22.default.useSyncExternalStore(subscribe, () => blocked);
758
+ import_react22.default.useSyncExternalStore(localeSubscribe, localeSnapshot);
473
759
  const labels = copy();
474
- return /* @__PURE__ */ import_react3.default.createElement("section", null, /* @__PURE__ */ import_react3.default.createElement("h2", null, labels.title), /* @__PURE__ */ import_react3.default.createElement("p", null, labels.help), /* @__PURE__ */ import_react3.default.createElement("label", null, /* @__PURE__ */ import_react3.default.createElement(
475
- "input",
476
- {
477
- type: "checkbox",
478
- checked: active,
479
- onChange: (event) => event.target.checked ? open() : close()
480
- }
481
- ), labels.activate), failed && /* @__PURE__ */ import_react3.default.createElement("p", { role: "alert" }, labels.popupBlocked));
760
+ return /* @__PURE__ */ import_react22.default.createElement("div", { className: "dlv-diagnostics-launcher" }, /* @__PURE__ */ import_react22.default.createElement("button", { type: "button", className: "dlv-diagnostics-button", onClick: open }, /* @__PURE__ */ import_react22.default.createElement(Icon, { name: "bug" }), labels.diagnostics), /* @__PURE__ */ import_react22.default.createElement(Hint, { label: labels.diagnostics }, labels.activate), failed && /* @__PURE__ */ import_react22.default.createElement("p", { role: "alert" }, labels.popupBlocked));
482
761
  }
483
762
  ctx.effect(
484
763
  () => publishDeveloperExtension(window, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-live-voice-debugger",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "Optional read-only state inspector for DSH Live Voice.",
6
6
  "license": "Apache-2.0",
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "repository": {
41
41
  "type": "git",
42
- "url": "git+https://github.com/victorwads/dsh-live-voice.git",
42
+ "url": "git+https://github.com/wads-dev/dsh-live-voice.git",
43
43
  "directory": "packages/live-voice-debugger"
44
44
  }
45
45
  }