@pie-players/pie-section-player-tools-event-debugger 0.3.42 → 0.3.44

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 DELETED
@@ -1,766 +0,0 @@
1
- <svelte:options
2
- customElement={{
3
- tag: "pie-section-player-tools-event-debugger",
4
- shadow: "open",
5
- props: {
6
- maxEvents: { type: "Number", attribute: "max-events" },
7
- maxEventsByLevel: {
8
- type: "Object",
9
- attribute: "max-events-by-level",
10
- },
11
- toolkitCoordinator: { type: "Object", attribute: "toolkit-coordinator" },
12
- sectionId: { type: "String", attribute: "section-id" },
13
- attemptId: { type: "String", attribute: "attempt-id" },
14
- persistenceScope: { type: "String", attribute: "persistence-scope" },
15
- persistencePanelId: { type: "String", attribute: "persistence-panel-id" },
16
- },
17
- }}
18
- />
19
-
20
- <script lang="ts">
21
- import "@pie-players/pie-theme/components.css";
22
- import SharedFloatingPanel from "@pie-players/pie-section-player-tools-shared/SharedFloatingPanel.svelte";
23
- import {
24
- getSectionControllerFromCoordinator,
25
- isMatchingSectionControllerLifecycleEvent,
26
- } from "@pie-players/pie-section-player-tools-shared";
27
- import { createEventDispatcher, onDestroy, untrack } from "svelte";
28
-
29
- type ControllerEvent = {
30
- type?: string;
31
- timestamp?: number;
32
- itemId?: string;
33
- canonicalItemId?: string;
34
- intent?: string;
35
- [key: string]: unknown;
36
- };
37
- type ControllerRuntimeState = {
38
- loadingComplete?: boolean;
39
- totalRegistered?: number;
40
- totalLoaded?: number;
41
- itemsComplete?: boolean;
42
- completedCount?: number;
43
- totalItems?: number;
44
- } | null;
45
- type ToolkitCoordinatorLike = {
46
- // Phase D (>=0.3.35): subscribe* helpers follow the toolkit's
47
- // active section cohort automatically and do not accept
48
- // sectionId / attemptId arguments. The debugger keeps `sectionId`
49
- // + `attemptId` in `subscriptions` purely to gate same-target
50
- // re-subscribe — it does not pass them to the coordinator.
51
- subscribeItemEvents?: (args: {
52
- listener: (event: ControllerEvent) => void;
53
- }) => () => void;
54
- subscribeSectionLifecycleEvents?: (args: {
55
- listener: (event: ControllerEvent) => void;
56
- }) => () => void;
57
- getSectionController?: (args: {
58
- sectionId: string;
59
- attemptId?: string;
60
- }) => unknown;
61
- onSectionControllerLifecycle?: (
62
- listener: (event: {
63
- key?: { sectionId?: string; attemptId?: string };
64
- }) => void,
65
- ) => () => void;
66
- };
67
-
68
- type EventType =
69
- | "item-session-data-changed"
70
- | "item-session-meta-changed"
71
- | "item-selected"
72
- | "section-navigation-change"
73
- | "content-loaded"
74
- | "item-player-error"
75
- | "item-complete-changed"
76
- | "section-loading-complete"
77
- | "section-items-complete-changed"
78
- | "section-error";
79
- type EventLevel = "item" | "section";
80
- type EventLimitOverrides = Partial<Record<EventLevel, number>>;
81
-
82
- type EventRecord = {
83
- id: number;
84
- type: EventType;
85
- timestamp: number;
86
- targetTag: string | null;
87
- itemId: string | null;
88
- canonicalItemId: string | null;
89
- intent: string | null;
90
- duplicateCount: number;
91
- payload: unknown;
92
- fingerprint: string;
93
- semanticFingerprint: string;
94
- };
95
-
96
- const dispatch = createEventDispatcher<{ close: undefined }>();
97
-
98
- let {
99
- maxEvents = 200,
100
- maxEventsByLevel = {},
101
- toolkitCoordinator = null,
102
- sectionId = "",
103
- attemptId = undefined,
104
- persistenceScope = "",
105
- persistencePanelId = "controller-events",
106
- }: {
107
- maxEvents?: number;
108
- maxEventsByLevel?: EventLimitOverrides;
109
- toolkitCoordinator?: ToolkitCoordinatorLike | null;
110
- sectionId?: string;
111
- attemptId?: string;
112
- persistenceScope?: string;
113
- persistencePanelId?: string;
114
- } = $props();
115
- let isPaused = $state(false);
116
- let selectedLevel = $state<EventLevel>("item");
117
- let selectedRecordId = $state<number | null>(null);
118
- let records = $state<EventRecord[]>([]);
119
- let controllerAvailable = $state(false);
120
-
121
- let nextRecordId = 1;
122
- let resubscribeQueued = false;
123
- const subscriptions: {
124
- controller: (() => void) | null;
125
- lifecycle: (() => void) | null;
126
- activeSectionId: string;
127
- activeAttemptId?: string;
128
- } = {
129
- controller: null,
130
- lifecycle: null,
131
- activeSectionId: "",
132
- activeAttemptId: undefined,
133
- };
134
-
135
- function safeClone<T>(value: T): T {
136
- try {
137
- return structuredClone(value);
138
- } catch {
139
- try {
140
- return JSON.parse(JSON.stringify(value)) as T;
141
- } catch {
142
- return value;
143
- }
144
- }
145
- }
146
-
147
- function createFingerprint(type: EventType, payload: unknown): string {
148
- let payloadString = "";
149
- try {
150
- payloadString = JSON.stringify(payload);
151
- } catch {
152
- payloadString = String(payload);
153
- }
154
- return `${type}:${payloadString}`;
155
- }
156
-
157
- function createSemanticFingerprint(type: EventType, payload: unknown): string {
158
- const semantic =
159
- payload && typeof payload === "object"
160
- ? { ...(payload as Record<string, unknown>) }
161
- : payload;
162
- if (semantic && typeof semantic === "object") {
163
- delete (semantic as Record<string, unknown>).timestamp;
164
- delete (semantic as Record<string, unknown>).sourceRuntimeId;
165
- }
166
- let payloadString = "";
167
- try {
168
- payloadString = JSON.stringify(semantic);
169
- } catch {
170
- payloadString = String(semantic);
171
- }
172
- return `${type}:${payloadString}`;
173
- }
174
-
175
- function normalizeEventType(input: unknown): EventType | null {
176
- const value = String(input || "");
177
- if (
178
- value === "item-session-data-changed" ||
179
- value === "item-session-meta-changed" ||
180
- value === "item-selected" ||
181
- value === "section-navigation-change" ||
182
- value === "content-loaded" ||
183
- value === "item-player-error" ||
184
- value === "item-complete-changed" ||
185
- value === "section-loading-complete" ||
186
- value === "section-items-complete-changed" ||
187
- value === "section-error"
188
- ) {
189
- return value;
190
- }
191
- return null;
192
- }
193
-
194
- function getEventLevel(type: EventType): EventLevel {
195
- if (
196
- type === "section-navigation-change" ||
197
- type === "section-loading-complete" ||
198
- type === "section-items-complete-changed" ||
199
- type === "section-error"
200
- ) {
201
- return "section";
202
- }
203
- return "item";
204
- }
205
-
206
- function getValueAsString(value: unknown): string | null {
207
- return typeof value === "string" && value.trim() ? value : null;
208
- }
209
-
210
- function normalizeRecord(detail: ControllerEvent, type: EventType): EventRecord {
211
- const payload = safeClone((detail || {}) as unknown);
212
- const fingerprint = createFingerprint(type, payload);
213
- const semanticFingerprint = createSemanticFingerprint(type, payload);
214
- return {
215
- id: nextRecordId++,
216
- type,
217
- timestamp: typeof detail.timestamp === "number" ? detail.timestamp : Date.now(),
218
- targetTag: "section-controller",
219
- itemId: getValueAsString(detail?.itemId),
220
- canonicalItemId: getValueAsString(detail?.canonicalItemId),
221
- intent: getValueAsString(detail?.intent),
222
- duplicateCount: 1,
223
- payload,
224
- fingerprint,
225
- semanticFingerprint,
226
- };
227
- }
228
-
229
- function pushRecord(detail: ControllerEvent) {
230
- if (isPaused) return;
231
- const type = normalizeEventType(detail?.type);
232
- if (!type) return;
233
- const next = normalizeRecord(detail, type);
234
- const latest = records[0];
235
- if (latest && latest.fingerprint === next.fingerprint) {
236
- records = [
237
- {
238
- ...latest,
239
- timestamp: next.timestamp,
240
- duplicateCount: latest.duplicateCount + 1,
241
- },
242
- ...records.slice(1),
243
- ];
244
- return;
245
- }
246
- records = pruneAndSortRecords([next, ...records]);
247
- if (selectedRecordId == null) {
248
- selectedRecordId = next.id;
249
- }
250
- }
251
-
252
- function resolveCap(rawCap: unknown, fallback: number): number {
253
- const parsed = Number(rawCap);
254
- if (!Number.isFinite(parsed)) return Math.max(10, Math.min(2000, fallback));
255
- return Math.max(10, Math.min(2000, parsed));
256
- }
257
-
258
- function getCapForLevel(level: EventLevel): number {
259
- const globalCap = resolveCap(maxEvents || 200, 200);
260
- const override = maxEventsByLevel?.[level];
261
- return resolveCap(override, globalCap);
262
- }
263
-
264
- function pruneAndSortRecords(nextRecords: EventRecord[]): EventRecord[] {
265
- const sorted = [...nextRecords].sort((left, right) => {
266
- if (left.timestamp === right.timestamp) {
267
- return right.id - left.id;
268
- }
269
- return right.timestamp - left.timestamp;
270
- });
271
- const nextByLevel: Record<EventLevel, number> = { item: 0, section: 0 };
272
- const pruned: EventRecord[] = [];
273
- for (const record of sorted) {
274
- const level = getEventLevel(record.type);
275
- const levelCap = getCapForLevel(level);
276
- if (nextByLevel[level] >= levelCap) continue;
277
- pruned.push(record);
278
- nextByLevel[level] += 1;
279
- }
280
- return pruned;
281
- }
282
-
283
- function reconcileRecordsWithLimits(): void {
284
- const nextRecords = pruneAndSortRecords(records);
285
- if (nextRecords.length !== records.length) {
286
- records = nextRecords;
287
- return;
288
- }
289
- for (let index = 0; index < nextRecords.length; index += 1) {
290
- if (nextRecords[index]?.id !== records[index]?.id) {
291
- records = nextRecords;
292
- return;
293
- }
294
- }
295
- }
296
-
297
- function handleControllerEvent(event: ControllerEvent): void {
298
- pushRecord(event || {});
299
- }
300
-
301
- function handleItemControllerEvent(event: ControllerEvent): void {
302
- handleControllerEvent(event);
303
- }
304
-
305
- function handleSectionControllerEvent(event: ControllerEvent): void {
306
- handleControllerEvent(event);
307
- }
308
-
309
- function getController(): any | null {
310
- return getSectionControllerFromCoordinator(
311
- toolkitCoordinator,
312
- sectionId,
313
- attemptId,
314
- );
315
- }
316
-
317
- function seedFromRuntimeState(controller: {
318
- getRuntimeState?: () => ControllerRuntimeState;
319
- }): void {
320
- const runtimeState = controller?.getRuntimeState?.();
321
- if (!runtimeState || typeof runtimeState !== "object") return;
322
- const totalItems =
323
- typeof runtimeState.totalItems === "number" ? runtimeState.totalItems : 0;
324
- const now = Date.now();
325
- pushRecord({
326
- type: "section-items-complete-changed",
327
- complete: runtimeState.itemsComplete === true,
328
- completedCount:
329
- typeof runtimeState.completedCount === "number"
330
- ? runtimeState.completedCount
331
- : 0,
332
- totalItems,
333
- timestamp: now,
334
- });
335
- if (runtimeState.loadingComplete === true) {
336
- pushRecord({
337
- type: "section-loading-complete",
338
- totalRegistered:
339
- typeof runtimeState.totalRegistered === "number"
340
- ? runtimeState.totalRegistered
341
- : 0,
342
- totalLoaded:
343
- typeof runtimeState.totalLoaded === "number"
344
- ? runtimeState.totalLoaded
345
- : 0,
346
- timestamp: now,
347
- });
348
- }
349
- }
350
-
351
- function detachControllerSubscription() {
352
- subscriptions.controller?.();
353
- subscriptions.controller = null;
354
- subscriptions.activeSectionId = "";
355
- subscriptions.activeAttemptId = undefined;
356
- }
357
-
358
- function detachLifecycleSubscription() {
359
- subscriptions.lifecycle?.();
360
- subscriptions.lifecycle = null;
361
- }
362
-
363
- function ensureControllerSubscription() {
364
- const controller = getController();
365
- controllerAvailable = Boolean(controller);
366
- if (!controller) {
367
- detachControllerSubscription();
368
- return;
369
- }
370
-
371
- const nextAttemptId = attemptId || undefined;
372
- const isSameTarget =
373
- subscriptions.activeSectionId === sectionId &&
374
- subscriptions.activeAttemptId === nextAttemptId;
375
- if (isSameTarget && subscriptions.controller) {
376
- return;
377
- }
378
-
379
- detachControllerSubscription();
380
- const unsubscribeItem =
381
- toolkitCoordinator?.subscribeItemEvents?.({
382
- listener: handleItemControllerEvent,
383
- }) || null;
384
- const unsubscribeSection =
385
- toolkitCoordinator?.subscribeSectionLifecycleEvents?.({
386
- listener: handleSectionControllerEvent,
387
- }) || null;
388
- subscriptions.controller = () => {
389
- unsubscribeItem?.();
390
- unsubscribeSection?.();
391
- };
392
- subscriptions.activeSectionId = sectionId;
393
- subscriptions.activeAttemptId = nextAttemptId;
394
- seedFromRuntimeState(controller);
395
- }
396
-
397
- function queueEnsureControllerSubscription(): void {
398
- if (resubscribeQueued) return;
399
- resubscribeQueued = true;
400
- queueMicrotask(() => {
401
- resubscribeQueued = false;
402
- ensureControllerSubscription();
403
- });
404
- }
405
-
406
- function clearRecords() {
407
- records = [];
408
- selectedRecordId = null;
409
- }
410
-
411
- function formatTimestamp(timestamp: number): string {
412
- return new Date(timestamp).toLocaleTimeString();
413
- }
414
-
415
- const visibleRecords = $derived.by(() =>
416
- records.filter(
417
- (record) => getEventLevel(record.type) === selectedLevel,
418
- ),
419
- );
420
- const semanticCounts = $derived.by(() => {
421
- const counts = new Map<string, number>();
422
- for (const record of visibleRecords) {
423
- counts.set(
424
- record.semanticFingerprint,
425
- (counts.get(record.semanticFingerprint) || 0) + record.duplicateCount,
426
- );
427
- }
428
- return counts;
429
- });
430
- const selectedRecord = $derived.by(
431
- () => visibleRecords.find((record) => record.id === selectedRecordId) || visibleRecords[0] || null,
432
- );
433
-
434
- $effect(() => {
435
- void toolkitCoordinator;
436
- void sectionId;
437
- void attemptId;
438
- untrack(() => {
439
- ensureControllerSubscription();
440
- detachLifecycleSubscription();
441
- subscriptions.lifecycle = toolkitCoordinator?.onSectionControllerLifecycle?.(
442
- (event: {
443
- type?: "ready" | "disposed";
444
- key?: { sectionId?: string; attemptId?: string };
445
- }) => {
446
- if (
447
- !isMatchingSectionControllerLifecycleEvent(event, sectionId, attemptId)
448
- )
449
- return;
450
- if (event?.type === "disposed") {
451
- detachControllerSubscription();
452
- queueEnsureControllerSubscription();
453
- return;
454
- }
455
- const nextAttemptId = attemptId || undefined;
456
- if (
457
- subscriptions.controller &&
458
- subscriptions.activeSectionId === sectionId &&
459
- subscriptions.activeAttemptId === nextAttemptId
460
- ) {
461
- return;
462
- }
463
- queueEnsureControllerSubscription();
464
- },
465
- ) || null;
466
- });
467
- return () => {
468
- detachControllerSubscription();
469
- detachLifecycleSubscription();
470
- };
471
- });
472
-
473
- $effect(() => {
474
- void maxEvents;
475
- void maxEventsByLevel;
476
- reconcileRecordsWithLimits();
477
- });
478
-
479
- onDestroy(() => {
480
- detachControllerSubscription();
481
- detachLifecycleSubscription();
482
- });
483
- </script>
484
-
485
- <SharedFloatingPanel
486
- title="Controller Events"
487
- ariaLabel="Drag event debugger panel"
488
- minWidth={360}
489
- minHeight={280}
490
- {persistenceScope}
491
- {persistencePanelId}
492
- initialSizing={{
493
- widthRatio: 0.34,
494
- heightRatio: 0.74,
495
- minWidth: 380,
496
- maxWidth: 720,
497
- minHeight: 360,
498
- maxHeight: 860,
499
- alignX: "right",
500
- alignY: "center",
501
- paddingX: 16,
502
- paddingY: 16,
503
- }}
504
- className="pie-section-player-tools-event-debugger"
505
- bodyClass="pie-section-player-tools-event-debugger__content-shell"
506
- onClose={() => dispatch("close")}
507
- >
508
- <svelte:fragment slot="icon">
509
- <svg
510
- xmlns="http://www.w3.org/2000/svg"
511
- class="pie-section-player-tools-event-debugger__icon-sm"
512
- fill="none"
513
- viewBox="0 0 24 24"
514
- stroke="currentColor"
515
- >
516
- <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" />
517
- </svg>
518
- </svelte:fragment>
519
-
520
- <div class="pie-section-player-tools-event-debugger__toolbar">
521
- <div
522
- class="pie-section-player-tools-event-debugger__toggle-group"
523
- role="group"
524
- aria-label="Event level filter"
525
- >
526
- <button
527
- class="pie-section-player-tools-event-debugger__toggle-button"
528
- class:pie-section-player-tools-event-debugger__toggle-button--active={selectedLevel ===
529
- "item"}
530
- onclick={() => (selectedLevel = "item")}
531
- aria-pressed={selectedLevel === "item"}
532
- >
533
- item
534
- </button>
535
- <button
536
- class="pie-section-player-tools-event-debugger__toggle-button"
537
- class:pie-section-player-tools-event-debugger__toggle-button--active={selectedLevel ===
538
- "section"}
539
- onclick={() => (selectedLevel = "section")}
540
- aria-pressed={selectedLevel === "section"}
541
- >
542
- section
543
- </button>
544
- </div>
545
- <button class="pie-section-player-tools-event-debugger__button" onclick={() => (isPaused = !isPaused)}>
546
- {isPaused ? "resume" : "pause"}
547
- </button>
548
- <button class="pie-section-player-tools-event-debugger__button" onclick={clearRecords}>
549
- clear
550
- </button>
551
- {#if !controllerAvailable}
552
- <span class="pie-section-player-tools-event-debugger__status">
553
- controller unavailable
554
- </span>
555
- {/if}
556
- </div>
557
-
558
- <div class="pie-section-player-tools-event-debugger__grid">
559
- <div class="pie-section-player-tools-event-debugger__list">
560
- {#if visibleRecords.length === 0}
561
- <div class="pie-section-player-tools-event-debugger__empty">
562
- No matching events yet. Interact with an item to capture controller events.
563
- </div>
564
- {:else}
565
- {#each visibleRecords as record (record.id)}
566
- <button
567
- class="pie-section-player-tools-event-debugger__row"
568
- class:pie-section-player-tools-event-debugger__row--active={selectedRecord?.id ===
569
- record.id}
570
- onclick={() => (selectedRecordId = record.id)}
571
- >
572
- <div class="pie-section-player-tools-event-debugger__row-top">
573
- <span class="pie-section-player-tools-event-debugger__event-type">{record.type}</span>
574
- <span class="pie-section-player-tools-event-debugger__event-time">
575
- {formatTimestamp(record.timestamp)}
576
- </span>
577
- </div>
578
- <div class="pie-section-player-tools-event-debugger__row-meta">
579
- {#if record.itemId}
580
- <span>item: {record.itemId}</span>
581
- {/if}
582
- {#if record.intent}
583
- <span>intent: {record.intent}</span>
584
- {/if}
585
- {#if (semanticCounts.get(record.semanticFingerprint) || 0) > record.duplicateCount}
586
- <span>
587
- semantic repeats: {semanticCounts.get(record.semanticFingerprint)}
588
- </span>
589
- {/if}
590
- {#if record.duplicateCount > 1}
591
- <span>dupes: {record.duplicateCount}</span>
592
- {/if}
593
- </div>
594
- </button>
595
- {/each}
596
- {/if}
597
- </div>
598
- <div
599
- class="pie-section-player-tools-event-debugger__detail"
600
- role="textbox"
601
- aria-readonly="true"
602
- tabindex="0"
603
- aria-label="Controller event details"
604
- >
605
- {#if selectedRecord}
606
- <div class="pie-section-player-tools-event-debugger__detail-meta">
607
- <div><strong>Type:</strong> {selectedRecord.type}</div>
608
- <div><strong>Target:</strong> {selectedRecord.targetTag || "unknown"}</div>
609
- <div><strong>Item:</strong> {selectedRecord.itemId || "n/a"}</div>
610
- <div><strong>Canonical:</strong> {selectedRecord.canonicalItemId || "n/a"}</div>
611
- <div><strong>Intent:</strong> {selectedRecord.intent || "n/a"}</div>
612
- <div><strong>Duplicates:</strong> {selectedRecord.duplicateCount}</div>
613
- <div>
614
- <strong>Semantic Repeats:</strong>
615
- {semanticCounts.get(selectedRecord.semanticFingerprint) || selectedRecord.duplicateCount}
616
- </div>
617
- </div>
618
- <pre class="pie-section-player-tools-event-debugger__pre">{JSON.stringify(
619
- selectedRecord.payload,
620
- null,
621
- 2,
622
- )}</pre>
623
- {:else}
624
- <div class="pie-section-player-tools-event-debugger__empty">
625
- Select an event to inspect payload details.
626
- </div>
627
- {/if}
628
- </div>
629
- </div>
630
- </SharedFloatingPanel>
631
-
632
- <style>
633
- .pie-section-player-tools-event-debugger__icon-sm {
634
- width: 1rem;
635
- height: 1rem;
636
- }
637
-
638
- .pie-section-player-tools-event-debugger__toolbar {
639
- display: flex;
640
- align-items: center;
641
- gap: 8px;
642
- padding: 10px 12px;
643
- border-bottom: 1px solid var(--color-base-300, #d1d5db);
644
- flex-wrap: wrap;
645
- }
646
-
647
- .pie-section-player-tools-event-debugger__button {
648
- border: 1px solid var(--color-base-300, #d1d5db);
649
- background: var(--color-base-100, #fff);
650
- color: inherit;
651
- border-radius: 6px;
652
- font-size: 0.78rem;
653
- padding: 6px 8px;
654
- }
655
-
656
- .pie-section-player-tools-event-debugger__toggle-group {
657
- display: inline-flex;
658
- border: 1px solid var(--color-base-300, #d1d5db);
659
- border-radius: 6px;
660
- overflow: hidden;
661
- }
662
-
663
- .pie-section-player-tools-event-debugger__toggle-button {
664
- border: none;
665
- background: var(--color-base-100, #fff);
666
- color: inherit;
667
- font-size: 0.78rem;
668
- padding: 6px 10px;
669
- cursor: pointer;
670
- }
671
-
672
- .pie-section-player-tools-event-debugger__toggle-button + .pie-section-player-tools-event-debugger__toggle-button {
673
- border-left: 1px solid var(--color-base-300, #d1d5db);
674
- }
675
-
676
- .pie-section-player-tools-event-debugger__toggle-button--active {
677
- background: color-mix(in srgb, var(--color-primary, #2563eb) 18%, transparent);
678
- font-weight: 600;
679
- }
680
-
681
- .pie-section-player-tools-event-debugger__status {
682
- font-size: 0.72rem;
683
- opacity: 0.75;
684
- }
685
-
686
- .pie-section-player-tools-event-debugger__grid {
687
- display: grid;
688
- grid-template-columns: minmax(180px, 1fr) minmax(260px, 1.3fr);
689
- flex: 1;
690
- min-height: 0;
691
- }
692
-
693
- .pie-section-player-tools-event-debugger__list {
694
- border-right: 1px solid var(--color-base-300, #d1d5db);
695
- overflow: auto;
696
- }
697
-
698
- .pie-section-player-tools-event-debugger__detail {
699
- overflow: auto;
700
- }
701
-
702
- .pie-section-player-tools-event-debugger__row {
703
- display: block;
704
- width: 100%;
705
- border: 0;
706
- text-align: left;
707
- background: transparent;
708
- padding: 8px 10px;
709
- border-bottom: 1px solid var(--color-base-300, #e5e7eb);
710
- cursor: pointer;
711
- }
712
-
713
- .pie-section-player-tools-event-debugger__row--active {
714
- background: color-mix(in srgb, var(--color-primary, #2563eb) 14%, transparent);
715
- }
716
-
717
- .pie-section-player-tools-event-debugger__row-top {
718
- display: flex;
719
- justify-content: space-between;
720
- gap: 8px;
721
- font-size: 0.74rem;
722
- }
723
-
724
- .pie-section-player-tools-event-debugger__event-type {
725
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
726
- font-weight: 600;
727
- }
728
-
729
- .pie-section-player-tools-event-debugger__event-time {
730
- opacity: 0.75;
731
- }
732
-
733
- .pie-section-player-tools-event-debugger__row-meta {
734
- margin-top: 4px;
735
- display: flex;
736
- flex-wrap: wrap;
737
- gap: 8px;
738
- font-size: 0.7rem;
739
- opacity: 0.88;
740
- }
741
-
742
- .pie-section-player-tools-event-debugger__detail-meta {
743
- display: grid;
744
- gap: 3px;
745
- padding: 10px 12px;
746
- font-size: 0.78rem;
747
- border-bottom: 1px solid var(--color-base-300, #d1d5db);
748
- }
749
-
750
- .pie-section-player-tools-event-debugger__pre {
751
- margin: 0;
752
- padding: 12px;
753
- font-size: 0.74rem;
754
- line-height: 1.35;
755
- white-space: pre-wrap;
756
- word-break: break-word;
757
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
758
- }
759
-
760
- .pie-section-player-tools-event-debugger__empty {
761
- padding: 12px;
762
- font-size: 0.8rem;
763
- opacity: 0.8;
764
- }
765
-
766
- </style>