@pie-players/pie-section-player-tools-session-debugger 0.1.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/SectionSessionPanel.svelte +280 -0
- package/dist/SectionSessionPanel.svelte.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/section-player-tools-session-debugger.js +2709 -0
- package/dist/vite.config.d.ts +3 -0
- package/dist/vite.config.d.ts.map +1 -0
- package/index.ts +10 -0
- package/package.json +67 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<svelte:options
|
|
2
|
+
customElement={{
|
|
3
|
+
tag: 'pie-section-player-tools-session-debugger',
|
|
4
|
+
shadow: 'open',
|
|
5
|
+
props: {
|
|
6
|
+
sectionId: { type: 'String', attribute: 'section-id' },
|
|
7
|
+
attemptId: { type: 'String', attribute: 'attempt-id' },
|
|
8
|
+
toolkitCoordinator: { type: 'Object', attribute: 'toolkit-coordinator' }
|
|
9
|
+
}
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { createEventDispatcher } from 'svelte';
|
|
15
|
+
import { onMount } from 'svelte';
|
|
16
|
+
const dispatch = createEventDispatcher<{ close: undefined }>();
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
type SessionPanelSnapshot = {
|
|
20
|
+
currentItemIndex: number | null;
|
|
21
|
+
currentItemId: string | null;
|
|
22
|
+
visitedItemIdentifiers: string[];
|
|
23
|
+
updatedAt: number | null;
|
|
24
|
+
lastChangedItemId: string | null;
|
|
25
|
+
itemSessions: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type SectionAttemptSliceLike = {
|
|
29
|
+
currentItemIndex?: number;
|
|
30
|
+
currentItemId?: string;
|
|
31
|
+
visitedItemIdentifiers?: string[];
|
|
32
|
+
itemSessions?: Record<string, unknown>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type SectionControllerLike = {
|
|
36
|
+
getCurrentSectionAttemptSlice?: () => SectionAttemptSliceLike | null;
|
|
37
|
+
subscribe?: (listener: (event: { itemId?: string; timestamp?: number }) => void) => () => void;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type ToolkitCoordinatorLike = {
|
|
41
|
+
getSectionController?: (args: { sectionId: string; attemptId?: string }) => SectionControllerLike | undefined;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
let {
|
|
45
|
+
toolkitCoordinator = null,
|
|
46
|
+
sectionId = '',
|
|
47
|
+
attemptId = undefined
|
|
48
|
+
}: {
|
|
49
|
+
toolkitCoordinator?: ToolkitCoordinatorLike | null;
|
|
50
|
+
sectionId: string;
|
|
51
|
+
attemptId?: string;
|
|
52
|
+
} = $props();
|
|
53
|
+
|
|
54
|
+
let isSessionMinimized = $state(false);
|
|
55
|
+
let sessionWindowX = $state(24);
|
|
56
|
+
let sessionWindowY = $state(100);
|
|
57
|
+
let sessionWindowWidth = $state(220);
|
|
58
|
+
let sessionWindowHeight = $state(600);
|
|
59
|
+
let isSessionDragging = $state(false);
|
|
60
|
+
let isSessionResizing = $state(false);
|
|
61
|
+
|
|
62
|
+
let dragStartX = 0;
|
|
63
|
+
let dragStartY = 0;
|
|
64
|
+
let dragStartWindowX = 0;
|
|
65
|
+
let dragStartWindowY = 0;
|
|
66
|
+
let resizeStartX = 0;
|
|
67
|
+
let resizeStartY = 0;
|
|
68
|
+
let resizeStartWidth = 0;
|
|
69
|
+
let resizeStartHeight = 0;
|
|
70
|
+
|
|
71
|
+
let sessionPanelSnapshot = $state<SessionPanelSnapshot>({
|
|
72
|
+
currentItemIndex: null,
|
|
73
|
+
currentItemId: null,
|
|
74
|
+
visitedItemIdentifiers: [],
|
|
75
|
+
updatedAt: null,
|
|
76
|
+
lastChangedItemId: null,
|
|
77
|
+
itemSessions: {}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function cloneSessionSnapshot<T>(value: T): T {
|
|
81
|
+
try {
|
|
82
|
+
return structuredClone(value);
|
|
83
|
+
} catch {
|
|
84
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getController(): SectionControllerLike | undefined {
|
|
89
|
+
if (!toolkitCoordinator || !sectionId) return undefined;
|
|
90
|
+
return toolkitCoordinator.getSectionController?.({ sectionId, attemptId });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function refreshFromController(meta?: { itemId?: string; updatedAt?: number }) {
|
|
94
|
+
const controller = getController();
|
|
95
|
+
const sectionSlice = controller?.getCurrentSectionAttemptSlice?.() || null;
|
|
96
|
+
sessionPanelSnapshot = {
|
|
97
|
+
currentItemIndex:
|
|
98
|
+
typeof sectionSlice?.currentItemIndex === 'number' ? sectionSlice.currentItemIndex : null,
|
|
99
|
+
currentItemId:
|
|
100
|
+
typeof sectionSlice?.currentItemId === 'string' && sectionSlice.currentItemId
|
|
101
|
+
? sectionSlice.currentItemId
|
|
102
|
+
: null,
|
|
103
|
+
visitedItemIdentifiers: cloneSessionSnapshot(sectionSlice?.visitedItemIdentifiers || []),
|
|
104
|
+
updatedAt: meta?.updatedAt || Date.now(),
|
|
105
|
+
lastChangedItemId: meta?.itemId || null,
|
|
106
|
+
itemSessions: cloneSessionSnapshot(sectionSlice?.itemSessions || {})
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
$effect(() => {
|
|
111
|
+
if (!toolkitCoordinator || !sectionId) return;
|
|
112
|
+
refreshFromController();
|
|
113
|
+
const controller = getController();
|
|
114
|
+
const unsubscribe = controller?.subscribe?.((detail) => {
|
|
115
|
+
refreshFromController({
|
|
116
|
+
itemId: detail?.itemId,
|
|
117
|
+
updatedAt: detail?.timestamp || Date.now()
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
unsubscribe?.();
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
onMount(() => {
|
|
126
|
+
const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(value, max));
|
|
127
|
+
const viewportWidth = window.innerWidth;
|
|
128
|
+
const viewportHeight = window.innerHeight;
|
|
129
|
+
sessionWindowWidth = clamp(Math.round(viewportWidth * 0.29), 280, 560);
|
|
130
|
+
sessionWindowHeight = clamp(Math.round(viewportHeight * 0.72), 360, 860);
|
|
131
|
+
sessionWindowX = Math.max(16, Math.round(viewportWidth * 0.08));
|
|
132
|
+
sessionWindowY = Math.max(16, Math.round((viewportHeight - sessionWindowHeight) / 2));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
function startSessionDrag(e: MouseEvent) {
|
|
136
|
+
isSessionDragging = true;
|
|
137
|
+
dragStartX = e.clientX;
|
|
138
|
+
dragStartY = e.clientY;
|
|
139
|
+
dragStartWindowX = sessionWindowX;
|
|
140
|
+
dragStartWindowY = sessionWindowY;
|
|
141
|
+
|
|
142
|
+
document.addEventListener('mousemove', onSessionDrag);
|
|
143
|
+
document.addEventListener('mouseup', stopSessionDrag);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onSessionDrag(e: MouseEvent) {
|
|
147
|
+
if (!isSessionDragging) return;
|
|
148
|
+
const deltaX = e.clientX - dragStartX;
|
|
149
|
+
const deltaY = e.clientY - dragStartY;
|
|
150
|
+
sessionWindowX = dragStartWindowX + deltaX;
|
|
151
|
+
sessionWindowY = dragStartWindowY + deltaY;
|
|
152
|
+
sessionWindowX = Math.max(0, Math.min(sessionWindowX, window.innerWidth - sessionWindowWidth));
|
|
153
|
+
sessionWindowY = Math.max(0, Math.min(sessionWindowY, window.innerHeight - 100));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function stopSessionDrag() {
|
|
157
|
+
isSessionDragging = false;
|
|
158
|
+
document.removeEventListener('mousemove', onSessionDrag);
|
|
159
|
+
document.removeEventListener('mouseup', stopSessionDrag);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function startSessionResize(e: MouseEvent) {
|
|
163
|
+
isSessionResizing = true;
|
|
164
|
+
resizeStartX = e.clientX;
|
|
165
|
+
resizeStartY = e.clientY;
|
|
166
|
+
resizeStartWidth = sessionWindowWidth;
|
|
167
|
+
resizeStartHeight = sessionWindowHeight;
|
|
168
|
+
document.addEventListener('mousemove', onSessionResize);
|
|
169
|
+
document.addEventListener('mouseup', stopSessionResize);
|
|
170
|
+
e.stopPropagation();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function onSessionResize(e: MouseEvent) {
|
|
174
|
+
if (!isSessionResizing) return;
|
|
175
|
+
const deltaX = e.clientX - resizeStartX;
|
|
176
|
+
const deltaY = e.clientY - resizeStartY;
|
|
177
|
+
sessionWindowWidth = Math.max(300, Math.min(resizeStartWidth + deltaX, window.innerWidth - sessionWindowX));
|
|
178
|
+
sessionWindowHeight = Math.max(
|
|
179
|
+
200,
|
|
180
|
+
Math.min(resizeStartHeight + deltaY, window.innerHeight - sessionWindowY)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function stopSessionResize() {
|
|
185
|
+
isSessionResizing = false;
|
|
186
|
+
document.removeEventListener('mousemove', onSessionResize);
|
|
187
|
+
document.removeEventListener('mouseup', stopSessionResize);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
$effect(() => {
|
|
191
|
+
return () => {
|
|
192
|
+
stopSessionDrag();
|
|
193
|
+
stopSessionResize();
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
</script>
|
|
197
|
+
|
|
198
|
+
<div
|
|
199
|
+
class="fixed z-100 bg-base-100 rounded-lg shadow-2xl border-2 border-base-300"
|
|
200
|
+
style="left: {sessionWindowX}px; top: {sessionWindowY}px; width: {sessionWindowWidth}px; {isSessionMinimized ? 'height: auto;' : `height: ${sessionWindowHeight}px;`}"
|
|
201
|
+
>
|
|
202
|
+
<div
|
|
203
|
+
class="flex items-center justify-between px-4 py-2 bg-base-200 rounded-t-lg cursor-move select-none border-b border-base-300"
|
|
204
|
+
onmousedown={startSessionDrag}
|
|
205
|
+
role="button"
|
|
206
|
+
tabindex="0"
|
|
207
|
+
aria-label="Drag session panel"
|
|
208
|
+
>
|
|
209
|
+
<div class="flex items-center gap-2">
|
|
210
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
211
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
212
|
+
</svg>
|
|
213
|
+
<h3 class="font-bold text-sm">Session Data</h3>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="flex gap-1">
|
|
216
|
+
<button
|
|
217
|
+
class="btn btn-xs btn-ghost btn-circle"
|
|
218
|
+
onclick={() => (isSessionMinimized = !isSessionMinimized)}
|
|
219
|
+
title={isSessionMinimized ? 'Maximize' : 'Minimize'}
|
|
220
|
+
>
|
|
221
|
+
{#if isSessionMinimized}
|
|
222
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
223
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7" />
|
|
224
|
+
</svg>
|
|
225
|
+
{:else}
|
|
226
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
227
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
|
228
|
+
</svg>
|
|
229
|
+
{/if}
|
|
230
|
+
</button>
|
|
231
|
+
<button class="btn btn-xs btn-ghost btn-circle" onclick={() => dispatch('close')} title="Close">
|
|
232
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
233
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
234
|
+
</svg>
|
|
235
|
+
</button>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
{#if !isSessionMinimized}
|
|
240
|
+
<div class="p-4 flex flex-col min-h-0 overflow-hidden" style="height: {sessionWindowHeight - 60}px;">
|
|
241
|
+
<div class="space-y-3 flex-1 min-h-0 flex flex-col">
|
|
242
|
+
<div class="mb-2">
|
|
243
|
+
<div class="text-sm font-bold mb-2">PIE Session Data (Persistent)</div>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{#if Object.keys(sessionPanelSnapshot.itemSessions || {}).length === 0}
|
|
247
|
+
<div class="alert alert-info">
|
|
248
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-5 w-5" fill="none" viewBox="0 0 24 24">
|
|
249
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
250
|
+
</svg>
|
|
251
|
+
<span class="text-xs">No session data yet. Interact with the questions to see updates.</span>
|
|
252
|
+
</div>
|
|
253
|
+
{:else}
|
|
254
|
+
<div class="bg-base-200 rounded p-3 flex-1 min-h-0 flex flex-col">
|
|
255
|
+
<div class="text-xs font-semibold mb-2">
|
|
256
|
+
Item Sessions Snapshot
|
|
257
|
+
</div>
|
|
258
|
+
<pre class="bg-base-300 p-2 rounded text-xs overflow-auto flex-1 min-h-0">{JSON.stringify(sessionPanelSnapshot, null, 2)}</pre>
|
|
259
|
+
</div>
|
|
260
|
+
{/if}
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
{/if}
|
|
264
|
+
|
|
265
|
+
{#if !isSessionMinimized}
|
|
266
|
+
<div
|
|
267
|
+
class="absolute bottom-0 right-0 w-4 h-4 cursor-se-resize"
|
|
268
|
+
onmousedown={startSessionResize}
|
|
269
|
+
role="button"
|
|
270
|
+
tabindex="0"
|
|
271
|
+
title="Resize window"
|
|
272
|
+
>
|
|
273
|
+
<svg class="w-full h-full text-base-content/30" viewBox="0 0 16 16" fill="currentColor">
|
|
274
|
+
<path d="M16 16V14H14V16H16Z" />
|
|
275
|
+
<path d="M16 11V9H14V11H16Z" />
|
|
276
|
+
<path d="M13 16V14H11V16H13Z" />
|
|
277
|
+
</svg>
|
|
278
|
+
</div>
|
|
279
|
+
{/if}
|
|
280
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SvelteComponent as default } from 'svelte';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,8BAA8B,CAAC;AAEtC,YAAY,EAAE,CAAC"}
|