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

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,32 @@
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
+ subscribeSectionEvents: (args: {
44
+ sectionId: string;
45
+ attemptId?: string;
46
+ listener: (event: ControllerEvent) => void;
47
+ }) => () => void;
48
+ getSectionController?: (args: {
49
+ sectionId: string;
50
+ attemptId?: string;
51
+ }) => unknown;
52
+ onSectionControllerLifecycle?: (
53
+ listener: (event: {
54
+ key?: { sectionId?: string; attemptId?: string };
55
+ }) => void,
56
+ ) => () => void;
57
+ };
35
58
 
36
59
  type EventType =
37
60
  | "item-session-data-changed"
@@ -54,7 +77,6 @@
54
77
  itemId: string | null;
55
78
  canonicalItemId: string | null;
56
79
  intent: string | null;
57
- replayed: boolean;
58
80
  duplicateCount: number;
59
81
  payload: unknown;
60
82
  fingerprint: string;
@@ -70,7 +92,7 @@
70
92
  attemptId = undefined,
71
93
  }: {
72
94
  maxEvents?: number;
73
- toolkitCoordinator?: any;
95
+ toolkitCoordinator?: ToolkitCoordinatorLike | null;
74
96
  sectionId?: string;
75
97
  attemptId?: string;
76
98
  } = $props();
@@ -86,11 +108,18 @@
86
108
  let controllerAvailable = $state(false);
87
109
 
88
110
  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;
111
+ let resubscribeQueued = false;
112
+ const subscriptions: {
113
+ controller: (() => void) | null;
114
+ lifecycle: (() => void) | null;
115
+ activeSectionId: string;
116
+ activeAttemptId?: string;
117
+ } = {
118
+ controller: null,
119
+ lifecycle: null,
120
+ activeSectionId: "",
121
+ activeAttemptId: undefined,
122
+ };
94
123
 
95
124
  function safeClone<T>(value: T): T {
96
125
  try {
@@ -179,7 +208,6 @@
179
208
  itemId: getValueAsString(detail?.itemId),
180
209
  canonicalItemId: getValueAsString(detail?.canonicalItemId),
181
210
  intent: getValueAsString(detail?.intent),
182
- replayed: detail?.replayed === true,
183
211
  duplicateCount: 1,
184
212
  payload,
185
213
  fingerprint,
@@ -211,6 +239,10 @@
211
239
  }
212
240
  }
213
241
 
242
+ function handleControllerEvent(event: ControllerEvent): void {
243
+ pushRecord(event || {});
244
+ }
245
+
214
246
  function getController(): any | null {
215
247
  return getSectionControllerFromCoordinator(
216
248
  toolkitCoordinator,
@@ -219,15 +251,50 @@
219
251
  );
220
252
  }
221
253
 
254
+ function seedFromRuntimeState(controller: {
255
+ getRuntimeState?: () => ControllerRuntimeState;
256
+ }): void {
257
+ const runtimeState = controller?.getRuntimeState?.();
258
+ if (!runtimeState || typeof runtimeState !== "object") return;
259
+ const totalItems =
260
+ typeof runtimeState.totalItems === "number" ? runtimeState.totalItems : 0;
261
+ const now = Date.now();
262
+ pushRecord({
263
+ type: "section-items-complete-changed",
264
+ complete: runtimeState.itemsComplete === true,
265
+ completedCount:
266
+ typeof runtimeState.completedCount === "number"
267
+ ? runtimeState.completedCount
268
+ : 0,
269
+ totalItems,
270
+ timestamp: now,
271
+ });
272
+ if (runtimeState.loadingComplete === true) {
273
+ pushRecord({
274
+ type: "section-loading-complete",
275
+ totalRegistered:
276
+ typeof runtimeState.totalRegistered === "number"
277
+ ? runtimeState.totalRegistered
278
+ : 0,
279
+ totalLoaded:
280
+ typeof runtimeState.totalLoaded === "number"
281
+ ? runtimeState.totalLoaded
282
+ : 0,
283
+ timestamp: now,
284
+ });
285
+ }
286
+ }
287
+
222
288
  function detachControllerSubscription() {
223
- unsubscribeController?.();
224
- unsubscribeController = null;
225
- activeController = null;
289
+ subscriptions.controller?.();
290
+ subscriptions.controller = null;
291
+ subscriptions.activeSectionId = "";
292
+ subscriptions.activeAttemptId = undefined;
226
293
  }
227
294
 
228
295
  function detachLifecycleSubscription() {
229
- unsubscribeLifecycle?.();
230
- unsubscribeLifecycle = null;
296
+ subscriptions.lifecycle?.();
297
+ subscriptions.lifecycle = null;
231
298
  }
232
299
 
233
300
  function ensureControllerSubscription() {
@@ -237,13 +304,34 @@
237
304
  detachControllerSubscription();
238
305
  return;
239
306
  }
240
- if (controller === activeController) return;
307
+
308
+ const nextAttemptId = attemptId || undefined;
309
+ const isSameTarget =
310
+ subscriptions.activeSectionId === sectionId &&
311
+ subscriptions.activeAttemptId === nextAttemptId;
312
+ if (isSameTarget && subscriptions.controller) {
313
+ return;
314
+ }
315
+
241
316
  detachControllerSubscription();
242
- activeController = controller;
243
- unsubscribeController =
244
- controller.subscribe?.((event: ControllerEvent) => {
245
- pushRecord(event || {});
317
+ subscriptions.controller =
318
+ toolkitCoordinator?.subscribeSectionEvents({
319
+ sectionId,
320
+ attemptId,
321
+ listener: handleControllerEvent,
246
322
  }) || null;
323
+ subscriptions.activeSectionId = sectionId;
324
+ subscriptions.activeAttemptId = nextAttemptId;
325
+ seedFromRuntimeState(controller);
326
+ }
327
+
328
+ function queueEnsureControllerSubscription(): void {
329
+ if (resubscribeQueued) return;
330
+ resubscribeQueued = true;
331
+ queueMicrotask(() => {
332
+ resubscribeQueued = false;
333
+ ensureControllerSubscription();
334
+ });
247
335
  }
248
336
 
249
337
  const pointerController = createFloatingPanelPointerController({
@@ -316,39 +404,43 @@
316
404
  panelY = initial.y;
317
405
  panelWidth = initial.width;
318
406
  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
407
  });
335
408
 
336
409
  $effect(() => {
337
410
  void toolkitCoordinator;
338
411
  void sectionId;
339
412
  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
- );
413
+ untrack(() => {
414
+ ensureControllerSubscription();
415
+ detachLifecycleSubscription();
416
+ subscriptions.lifecycle = toolkitCoordinator?.onSectionControllerLifecycle?.(
417
+ (event: {
418
+ type?: "ready" | "disposed";
419
+ key?: { sectionId?: string; attemptId?: string };
420
+ }) => {
421
+ if (
422
+ !isMatchingSectionControllerLifecycleEvent(event, sectionId, attemptId)
423
+ )
424
+ return;
425
+ if (event?.type === "disposed") {
426
+ detachControllerSubscription();
427
+ queueEnsureControllerSubscription();
428
+ return;
429
+ }
430
+ const nextAttemptId = attemptId || undefined;
431
+ if (
432
+ subscriptions.controller &&
433
+ subscriptions.activeSectionId === sectionId &&
434
+ subscriptions.activeAttemptId === nextAttemptId
435
+ ) {
436
+ return;
437
+ }
438
+ queueEnsureControllerSubscription();
439
+ },
440
+ ) || null;
441
+ });
351
442
  return () => {
443
+ detachControllerSubscription();
352
444
  detachLifecycleSubscription();
353
445
  };
354
446
  });
@@ -381,7 +473,7 @@
381
473
  >
382
474
  <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
475
  </svg>
384
- <h3 class="pie-section-player-tools-event-debugger__title">Session Broadcasts</h3>
476
+ <h3 class="pie-section-player-tools-event-debugger__title">Controller Events</h3>
385
477
  </div>
386
478
  <div class="pie-section-player-tools-event-debugger__header-actions">
387
479
  <PanelWindowControls
@@ -436,7 +528,7 @@
436
528
  <div class="pie-section-player-tools-event-debugger__list">
437
529
  {#if visibleRecords.length === 0}
438
530
  <div class="pie-section-player-tools-event-debugger__empty">
439
- No matching events yet. Interact with an item to capture broadcasts.
531
+ No matching events yet. Interact with an item to capture controller events.
440
532
  </div>
441
533
  {:else}
442
534
  {#each visibleRecords as record (record.id)}
@@ -456,9 +548,6 @@
456
548
  {#if record.itemId}
457
549
  <span>item: {record.itemId}</span>
458
550
  {/if}
459
- {#if record.replayed}
460
- <span>replayed</span>
461
- {/if}
462
551
  {#if record.intent}
463
552
  <span>intent: {record.intent}</span>
464
553
  {/if}
@@ -482,7 +571,6 @@
482
571
  <div><strong>Target:</strong> {selectedRecord.targetTag || "unknown"}</div>
483
572
  <div><strong>Item:</strong> {selectedRecord.itemId || "n/a"}</div>
484
573
  <div><strong>Canonical:</strong> {selectedRecord.canonicalItemId || "n/a"}</div>
485
- <div><strong>Replayed:</strong> {selectedRecord.replayed ? "yes" : "no"}</div>
486
574
  <div><strong>Intent:</strong> {selectedRecord.intent || "n/a"}</div>
487
575
  <div><strong>Duplicates:</strong> {selectedRecord.duplicateCount}</div>
488
576
  <div>