@pie-players/pie-section-player-tools-event-debugger 0.3.4 → 0.3.7

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/EventPanel.svelte CHANGED
@@ -1,7 +1,7 @@
1
1
  <svelte:options
2
2
  customElement={{
3
3
  tag: "pie-section-player-tools-event-debugger",
4
- shadow: "none",
4
+ shadow: "open",
5
5
  props: {
6
6
  maxEvents: { type: "Number", attribute: "max-events" },
7
7
  toolkitCoordinator: { type: "Object", attribute: "toolkit-coordinator" },
@@ -21,7 +21,7 @@
21
21
  getSectionControllerFromCoordinator,
22
22
  isMatchingSectionControllerLifecycleEvent,
23
23
  } from "@pie-players/pie-section-player-tools-shared";
24
- import { createEventDispatcher, onDestroy, onMount } from "svelte";
24
+ import { createEventDispatcher, onDestroy, onMount, untrack } from "svelte";
25
25
 
26
26
  type ControllerEvent = {
27
27
  type?: string;
@@ -29,9 +29,37 @@
29
29
  itemId?: string;
30
30
  canonicalItemId?: string;
31
31
  intent?: string;
32
- replayed?: boolean;
33
32
  [key: string]: unknown;
34
33
  };
34
+ type ControllerRuntimeState = {
35
+ loadingComplete?: boolean;
36
+ totalRegistered?: number;
37
+ totalLoaded?: number;
38
+ itemsComplete?: boolean;
39
+ completedCount?: number;
40
+ totalItems?: number;
41
+ } | null;
42
+ type ToolkitCoordinatorLike = {
43
+ subscribeItemEvents?: (args: {
44
+ sectionId: string;
45
+ attemptId?: string;
46
+ listener: (event: ControllerEvent) => void;
47
+ }) => () => void;
48
+ subscribeSectionLifecycleEvents?: (args: {
49
+ sectionId: string;
50
+ attemptId?: string;
51
+ listener: (event: ControllerEvent) => void;
52
+ }) => () => void;
53
+ getSectionController?: (args: {
54
+ sectionId: string;
55
+ attemptId?: string;
56
+ }) => unknown;
57
+ onSectionControllerLifecycle?: (
58
+ listener: (event: {
59
+ key?: { sectionId?: string; attemptId?: string };
60
+ }) => void,
61
+ ) => () => void;
62
+ };
35
63
 
36
64
  type EventType =
37
65
  | "item-session-data-changed"
@@ -54,7 +82,6 @@
54
82
  itemId: string | null;
55
83
  canonicalItemId: string | null;
56
84
  intent: string | null;
57
- replayed: boolean;
58
85
  duplicateCount: number;
59
86
  payload: unknown;
60
87
  fingerprint: string;
@@ -70,7 +97,7 @@
70
97
  attemptId = undefined,
71
98
  }: {
72
99
  maxEvents?: number;
73
- toolkitCoordinator?: any;
100
+ toolkitCoordinator?: ToolkitCoordinatorLike | null;
74
101
  sectionId?: string;
75
102
  attemptId?: string;
76
103
  } = $props();
@@ -86,11 +113,18 @@
86
113
  let controllerAvailable = $state(false);
87
114
 
88
115
  let nextRecordId = 1;
89
- let activeController: {
90
- subscribe?: (listener: (event: ControllerEvent) => void) => () => void;
91
- } | null = null;
92
- let unsubscribeController: (() => void) | null = null;
93
- let unsubscribeLifecycle: (() => void) | null = null;
116
+ let resubscribeQueued = false;
117
+ const subscriptions: {
118
+ controller: (() => void) | null;
119
+ lifecycle: (() => void) | null;
120
+ activeSectionId: string;
121
+ activeAttemptId?: string;
122
+ } = {
123
+ controller: null,
124
+ lifecycle: null,
125
+ activeSectionId: "",
126
+ activeAttemptId: undefined,
127
+ };
94
128
 
95
129
  function safeClone<T>(value: T): T {
96
130
  try {
@@ -179,7 +213,6 @@
179
213
  itemId: getValueAsString(detail?.itemId),
180
214
  canonicalItemId: getValueAsString(detail?.canonicalItemId),
181
215
  intent: getValueAsString(detail?.intent),
182
- replayed: detail?.replayed === true,
183
216
  duplicateCount: 1,
184
217
  payload,
185
218
  fingerprint,
@@ -211,6 +244,10 @@
211
244
  }
212
245
  }
213
246
 
247
+ function handleControllerEvent(event: ControllerEvent): void {
248
+ pushRecord(event || {});
249
+ }
250
+
214
251
  function getController(): any | null {
215
252
  return getSectionControllerFromCoordinator(
216
253
  toolkitCoordinator,
@@ -219,15 +256,50 @@
219
256
  );
220
257
  }
221
258
 
259
+ function seedFromRuntimeState(controller: {
260
+ getRuntimeState?: () => ControllerRuntimeState;
261
+ }): void {
262
+ const runtimeState = controller?.getRuntimeState?.();
263
+ if (!runtimeState || typeof runtimeState !== "object") return;
264
+ const totalItems =
265
+ typeof runtimeState.totalItems === "number" ? runtimeState.totalItems : 0;
266
+ const now = Date.now();
267
+ pushRecord({
268
+ type: "section-items-complete-changed",
269
+ complete: runtimeState.itemsComplete === true,
270
+ completedCount:
271
+ typeof runtimeState.completedCount === "number"
272
+ ? runtimeState.completedCount
273
+ : 0,
274
+ totalItems,
275
+ timestamp: now,
276
+ });
277
+ if (runtimeState.loadingComplete === true) {
278
+ pushRecord({
279
+ type: "section-loading-complete",
280
+ totalRegistered:
281
+ typeof runtimeState.totalRegistered === "number"
282
+ ? runtimeState.totalRegistered
283
+ : 0,
284
+ totalLoaded:
285
+ typeof runtimeState.totalLoaded === "number"
286
+ ? runtimeState.totalLoaded
287
+ : 0,
288
+ timestamp: now,
289
+ });
290
+ }
291
+ }
292
+
222
293
  function detachControllerSubscription() {
223
- unsubscribeController?.();
224
- unsubscribeController = null;
225
- activeController = null;
294
+ subscriptions.controller?.();
295
+ subscriptions.controller = null;
296
+ subscriptions.activeSectionId = "";
297
+ subscriptions.activeAttemptId = undefined;
226
298
  }
227
299
 
228
300
  function detachLifecycleSubscription() {
229
- unsubscribeLifecycle?.();
230
- unsubscribeLifecycle = null;
301
+ subscriptions.lifecycle?.();
302
+ subscriptions.lifecycle = null;
231
303
  }
232
304
 
233
305
  function ensureControllerSubscription() {
@@ -237,13 +309,44 @@
237
309
  detachControllerSubscription();
238
310
  return;
239
311
  }
240
- if (controller === activeController) return;
312
+
313
+ const nextAttemptId = attemptId || undefined;
314
+ const isSameTarget =
315
+ subscriptions.activeSectionId === sectionId &&
316
+ subscriptions.activeAttemptId === nextAttemptId;
317
+ if (isSameTarget && subscriptions.controller) {
318
+ return;
319
+ }
320
+
241
321
  detachControllerSubscription();
242
- activeController = controller;
243
- unsubscribeController =
244
- controller.subscribe?.((event: ControllerEvent) => {
245
- pushRecord(event || {});
322
+ const unsubscribeItem =
323
+ toolkitCoordinator?.subscribeItemEvents?.({
324
+ sectionId,
325
+ attemptId,
326
+ listener: handleControllerEvent,
327
+ }) || null;
328
+ const unsubscribeSection =
329
+ toolkitCoordinator?.subscribeSectionLifecycleEvents?.({
330
+ sectionId,
331
+ attemptId,
332
+ listener: handleControllerEvent,
246
333
  }) || null;
334
+ subscriptions.controller = () => {
335
+ unsubscribeItem?.();
336
+ unsubscribeSection?.();
337
+ };
338
+ subscriptions.activeSectionId = sectionId;
339
+ subscriptions.activeAttemptId = nextAttemptId;
340
+ seedFromRuntimeState(controller);
341
+ }
342
+
343
+ function queueEnsureControllerSubscription(): void {
344
+ if (resubscribeQueued) return;
345
+ resubscribeQueued = true;
346
+ queueMicrotask(() => {
347
+ resubscribeQueued = false;
348
+ ensureControllerSubscription();
349
+ });
247
350
  }
248
351
 
249
352
  const pointerController = createFloatingPanelPointerController({
@@ -316,39 +419,43 @@
316
419
  panelY = initial.y;
317
420
  panelWidth = initial.width;
318
421
  panelHeight = initial.height;
319
- ensureControllerSubscription();
320
- unsubscribeLifecycle = toolkitCoordinator?.onSectionControllerLifecycle?.(
321
- (event: { key?: { sectionId?: string; attemptId?: string } }) => {
322
- if (
323
- !isMatchingSectionControllerLifecycleEvent(event, sectionId, attemptId)
324
- )
325
- return;
326
- ensureControllerSubscription();
327
- },
328
- );
329
-
330
- return () => {
331
- detachControllerSubscription();
332
- detachLifecycleSubscription();
333
- };
334
422
  });
335
423
 
336
424
  $effect(() => {
337
425
  void toolkitCoordinator;
338
426
  void sectionId;
339
427
  void attemptId;
340
- ensureControllerSubscription();
341
- detachLifecycleSubscription();
342
- unsubscribeLifecycle = toolkitCoordinator?.onSectionControllerLifecycle?.(
343
- (event: { key?: { sectionId?: string; attemptId?: string } }) => {
344
- if (
345
- !isMatchingSectionControllerLifecycleEvent(event, sectionId, attemptId)
346
- )
347
- return;
348
- ensureControllerSubscription();
349
- },
350
- );
428
+ untrack(() => {
429
+ ensureControllerSubscription();
430
+ detachLifecycleSubscription();
431
+ subscriptions.lifecycle = toolkitCoordinator?.onSectionControllerLifecycle?.(
432
+ (event: {
433
+ type?: "ready" | "disposed";
434
+ key?: { sectionId?: string; attemptId?: string };
435
+ }) => {
436
+ if (
437
+ !isMatchingSectionControllerLifecycleEvent(event, sectionId, attemptId)
438
+ )
439
+ return;
440
+ if (event?.type === "disposed") {
441
+ detachControllerSubscription();
442
+ queueEnsureControllerSubscription();
443
+ return;
444
+ }
445
+ const nextAttemptId = attemptId || undefined;
446
+ if (
447
+ subscriptions.controller &&
448
+ subscriptions.activeSectionId === sectionId &&
449
+ subscriptions.activeAttemptId === nextAttemptId
450
+ ) {
451
+ return;
452
+ }
453
+ queueEnsureControllerSubscription();
454
+ },
455
+ ) || null;
456
+ });
351
457
  return () => {
458
+ detachControllerSubscription();
352
459
  detachLifecycleSubscription();
353
460
  };
354
461
  });
@@ -381,7 +488,7 @@
381
488
  >
382
489
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h8M8 14h5m-7 7h12a2 2 0 002-2V5a2 2 0 00-2-2H6a2 2 0 00-2 2v14a2 2 0 002 2z" />
383
490
  </svg>
384
- <h3 class="pie-section-player-tools-event-debugger__title">Session Broadcasts</h3>
491
+ <h3 class="pie-section-player-tools-event-debugger__title">Controller Events</h3>
385
492
  </div>
386
493
  <div class="pie-section-player-tools-event-debugger__header-actions">
387
494
  <PanelWindowControls
@@ -436,7 +543,7 @@
436
543
  <div class="pie-section-player-tools-event-debugger__list">
437
544
  {#if visibleRecords.length === 0}
438
545
  <div class="pie-section-player-tools-event-debugger__empty">
439
- No matching events yet. Interact with an item to capture broadcasts.
546
+ No matching events yet. Interact with an item to capture controller events.
440
547
  </div>
441
548
  {:else}
442
549
  {#each visibleRecords as record (record.id)}
@@ -456,9 +563,6 @@
456
563
  {#if record.itemId}
457
564
  <span>item: {record.itemId}</span>
458
565
  {/if}
459
- {#if record.replayed}
460
- <span>replayed</span>
461
- {/if}
462
566
  {#if record.intent}
463
567
  <span>intent: {record.intent}</span>
464
568
  {/if}
@@ -482,7 +586,6 @@
482
586
  <div><strong>Target:</strong> {selectedRecord.targetTag || "unknown"}</div>
483
587
  <div><strong>Item:</strong> {selectedRecord.itemId || "n/a"}</div>
484
588
  <div><strong>Canonical:</strong> {selectedRecord.canonicalItemId || "n/a"}</div>
485
- <div><strong>Replayed:</strong> {selectedRecord.replayed ? "yes" : "no"}</div>
486
589
  <div><strong>Intent:</strong> {selectedRecord.intent || "n/a"}</div>
487
590
  <div><strong>Duplicates:</strong> {selectedRecord.duplicateCount}</div>
488
591
  <div>