@ct-player/embed 1.0.0
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/README.md +269 -0
- package/dist/index.cjs +181 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2716 -0
- package/dist/index.d.ts +2716 -0
- package/dist/index.js +181 -0
- package/dist/index.js.map +1 -0
- package/package.json +86 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2716 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import react__default from 'react';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
import Hls from 'hls.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CT Format Event Types
|
|
8
|
+
*
|
|
9
|
+
* Defines all event types and their data structures for the .ct format.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* All possible event types in the .ct format
|
|
13
|
+
*/
|
|
14
|
+
type CTEventType = 'init' | 'code' | 'code_edit' | 'language' | 'terminal' | 'terminal_input' | 'stroke_start' | 'whiteboard_points' | 'stroke_end' | 'whiteboard_stroke' | 'whiteboard_clear' | 'whiteboard_undo' | 'whiteboard' | 'whiteboard_point' | 'excalidraw_scene' | 'excalidraw_clear' | 'document_load' | 'document_page' | 'document_zoom' | 'document_scroll' | 'document_clear' | 'file_create' | 'file_update' | 'file_delete' | 'file_rename' | 'file_switch' | 'file_select' | 'file_change' | 'file_creation_start' | 'file_creation_input' | 'file_creation_cancel' | 'cursor_move' | 'cursor_click' | 'cursor_enter' | 'cursor_leave' | 'tool_switch' | 'tool' | 'marker';
|
|
15
|
+
/**
|
|
16
|
+
* Base event structure with generic type parameter
|
|
17
|
+
*/
|
|
18
|
+
interface CTEventBase<T extends CTEventType = CTEventType> {
|
|
19
|
+
/** Event type identifier */
|
|
20
|
+
type: T;
|
|
21
|
+
/** Time in milliseconds from recording start */
|
|
22
|
+
time: number;
|
|
23
|
+
/** Event-specific data payload */
|
|
24
|
+
data: CTEventDataMap[T];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Discriminated union of all event types.
|
|
28
|
+
* This enables proper type narrowing in switch statements.
|
|
29
|
+
*/
|
|
30
|
+
type CTEvent = CTEventBase<'init'> | CTEventBase<'code'> | CTEventBase<'code_edit'> | CTEventBase<'language'> | CTEventBase<'terminal'> | CTEventBase<'terminal_input'> | CTEventBase<'stroke_start'> | CTEventBase<'whiteboard_points'> | CTEventBase<'stroke_end'> | CTEventBase<'whiteboard_stroke'> | CTEventBase<'whiteboard_clear'> | CTEventBase<'whiteboard_undo'> | CTEventBase<'whiteboard'> | CTEventBase<'whiteboard_point'> | CTEventBase<'excalidraw_scene'> | CTEventBase<'excalidraw_clear'> | CTEventBase<'document_load'> | CTEventBase<'document_page'> | CTEventBase<'document_zoom'> | CTEventBase<'document_scroll'> | CTEventBase<'document_clear'> | CTEventBase<'file_create'> | CTEventBase<'file_update'> | CTEventBase<'file_delete'> | CTEventBase<'file_rename'> | CTEventBase<'file_switch'> | CTEventBase<'file_select'> | CTEventBase<'file_change'> | CTEventBase<'file_creation_start'> | CTEventBase<'file_creation_input'> | CTEventBase<'file_creation_cancel'> | CTEventBase<'cursor_move'> | CTEventBase<'cursor_click'> | CTEventBase<'cursor_enter'> | CTEventBase<'cursor_leave'> | CTEventBase<'tool_switch'> | CTEventBase<'tool'> | CTEventBase<'marker'>;
|
|
31
|
+
/**
|
|
32
|
+
* Mapping of event types to their data structures
|
|
33
|
+
*/
|
|
34
|
+
interface CTEventDataMap {
|
|
35
|
+
init: InitEventData;
|
|
36
|
+
code: CodeEventData;
|
|
37
|
+
code_edit: CodeEditEventData;
|
|
38
|
+
language: LanguageEventData;
|
|
39
|
+
terminal: TerminalEventData;
|
|
40
|
+
terminal_input: TerminalInputEventData;
|
|
41
|
+
stroke_start: StrokeStartEventData;
|
|
42
|
+
whiteboard_points: WhiteboardPointsEventData;
|
|
43
|
+
stroke_end: StrokeEndEventData;
|
|
44
|
+
whiteboard_stroke: WhiteboardStrokeEventData;
|
|
45
|
+
whiteboard_clear: WhiteboardClearEventData;
|
|
46
|
+
whiteboard_undo: WhiteboardUndoEventData;
|
|
47
|
+
excalidraw_scene: ExcalidrawSceneEventData;
|
|
48
|
+
excalidraw_clear: ExcalidrawClearEventData;
|
|
49
|
+
document_load: DocumentLoadEventData;
|
|
50
|
+
document_page: DocumentPageEventData;
|
|
51
|
+
document_zoom: DocumentZoomEventData;
|
|
52
|
+
document_scroll: DocumentScrollEventData;
|
|
53
|
+
document_clear: DocumentClearEventData;
|
|
54
|
+
file_create: FileCreateEventData;
|
|
55
|
+
file_update: FileUpdateEventData;
|
|
56
|
+
file_delete: FileDeleteEventData;
|
|
57
|
+
file_rename: FileRenameEventData;
|
|
58
|
+
file_switch: FileSwitchEventData;
|
|
59
|
+
file_creation_start: FileCreationStartEventData;
|
|
60
|
+
file_creation_input: FileCreationInputEventData;
|
|
61
|
+
file_creation_cancel: FileCreationCancelEventData;
|
|
62
|
+
cursor_move: CursorMoveEventData;
|
|
63
|
+
cursor_click: CursorClickEventData;
|
|
64
|
+
cursor_enter: CursorEnterEventData;
|
|
65
|
+
cursor_leave: CursorLeaveEventData;
|
|
66
|
+
tool_switch: ToolSwitchEventData;
|
|
67
|
+
marker: MarkerEventData;
|
|
68
|
+
tool: ToolEventData;
|
|
69
|
+
whiteboard: WhiteboardEventData;
|
|
70
|
+
whiteboard_point: WhiteboardPointEventData;
|
|
71
|
+
file_select: FileSelectEventData;
|
|
72
|
+
file_change: FileChangeEventData;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Tool types for tool switching
|
|
76
|
+
*/
|
|
77
|
+
type Tool = 'code' | 'terminal' | 'whiteboard' | 'document';
|
|
78
|
+
/**
|
|
79
|
+
* Terminal line entry
|
|
80
|
+
*/
|
|
81
|
+
interface TerminalLine$1 {
|
|
82
|
+
type: 'input' | 'output' | 'error' | 'system';
|
|
83
|
+
text: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 2D point with optional pressure
|
|
87
|
+
*/
|
|
88
|
+
interface Point {
|
|
89
|
+
x: number;
|
|
90
|
+
y: number;
|
|
91
|
+
pressure?: number;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Drawing stroke
|
|
95
|
+
*/
|
|
96
|
+
interface Stroke {
|
|
97
|
+
id: string;
|
|
98
|
+
color: string;
|
|
99
|
+
width: number;
|
|
100
|
+
opacity: number;
|
|
101
|
+
tool: StrokeTool;
|
|
102
|
+
points: Point[];
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Stroke tool types
|
|
106
|
+
*/
|
|
107
|
+
type StrokeTool = 'pen' | 'eraser' | 'highlighter';
|
|
108
|
+
/**
|
|
109
|
+
* Document reference data
|
|
110
|
+
*/
|
|
111
|
+
interface DocumentData {
|
|
112
|
+
id: string;
|
|
113
|
+
name: string;
|
|
114
|
+
type: string;
|
|
115
|
+
data?: string;
|
|
116
|
+
assetRef?: string;
|
|
117
|
+
numPages: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Excalidraw element types we support
|
|
121
|
+
*/
|
|
122
|
+
type ExcalidrawElementType = 'rectangle' | 'ellipse' | 'diamond' | 'line' | 'arrow' | 'freedraw' | 'text' | 'image' | 'frame' | 'group';
|
|
123
|
+
/**
|
|
124
|
+
* Excalidraw fill style
|
|
125
|
+
*/
|
|
126
|
+
type ExcalidrawFillStyle = 'solid' | 'hachure' | 'cross-hatch' | 'none';
|
|
127
|
+
/**
|
|
128
|
+
* Excalidraw stroke style
|
|
129
|
+
*/
|
|
130
|
+
type ExcalidrawStrokeStyle = 'solid' | 'dashed' | 'dotted';
|
|
131
|
+
/**
|
|
132
|
+
* Strongly typed Excalidraw element (v1.1.0+)
|
|
133
|
+
* This replaces the previous `unknown[]` for type safety
|
|
134
|
+
*/
|
|
135
|
+
interface CTExcalidrawElement {
|
|
136
|
+
id: string;
|
|
137
|
+
type: ExcalidrawElementType | string;
|
|
138
|
+
x: number;
|
|
139
|
+
y: number;
|
|
140
|
+
width: number;
|
|
141
|
+
height: number;
|
|
142
|
+
angle: number;
|
|
143
|
+
strokeColor: string;
|
|
144
|
+
backgroundColor: string;
|
|
145
|
+
fillStyle: ExcalidrawFillStyle;
|
|
146
|
+
strokeWidth: number;
|
|
147
|
+
strokeStyle: ExcalidrawStrokeStyle;
|
|
148
|
+
roughness: number;
|
|
149
|
+
opacity: number;
|
|
150
|
+
points?: number[][];
|
|
151
|
+
text?: string;
|
|
152
|
+
fontSize?: number;
|
|
153
|
+
fontFamily?: number;
|
|
154
|
+
textAlign?: 'left' | 'center' | 'right';
|
|
155
|
+
groupIds?: string[];
|
|
156
|
+
frameId?: string | null;
|
|
157
|
+
isDeleted: boolean;
|
|
158
|
+
locked: boolean;
|
|
159
|
+
fileId?: string;
|
|
160
|
+
[key: string]: unknown;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Excalidraw app state we care about
|
|
164
|
+
*/
|
|
165
|
+
interface CTExcalidrawAppState {
|
|
166
|
+
viewBackgroundColor: string;
|
|
167
|
+
zoom: {
|
|
168
|
+
value: number;
|
|
169
|
+
};
|
|
170
|
+
scrollX: number;
|
|
171
|
+
scrollY: number;
|
|
172
|
+
selectedElementIds?: Record<string, boolean>;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Excalidraw file (for images, etc.)
|
|
176
|
+
*/
|
|
177
|
+
interface CTExcalidrawFile {
|
|
178
|
+
mimeType: string;
|
|
179
|
+
id: string;
|
|
180
|
+
dataURL?: string;
|
|
181
|
+
assetRef?: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Excalidraw scene structure (v1.1.0 with strong typing)
|
|
185
|
+
*/
|
|
186
|
+
interface ExcalidrawScene {
|
|
187
|
+
elements: CTExcalidrawElement[];
|
|
188
|
+
appState?: CTExcalidrawAppState;
|
|
189
|
+
files?: Record<string, CTExcalidrawFile>;
|
|
190
|
+
/** Excalidraw library version for migrations (v1.1.0+) */
|
|
191
|
+
excalidrawVersion?: string;
|
|
192
|
+
/** Our schema version (v1.1.0+) */
|
|
193
|
+
ctSchemaVersion?: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Full state snapshot (used at recording start or after edits)
|
|
197
|
+
*/
|
|
198
|
+
interface InitEventData {
|
|
199
|
+
code: string;
|
|
200
|
+
language: string;
|
|
201
|
+
terminal: TerminalLine$1[];
|
|
202
|
+
terminalInput: string;
|
|
203
|
+
strokes: Stroke[];
|
|
204
|
+
excalidrawScene?: ExcalidrawScene;
|
|
205
|
+
tool: Tool;
|
|
206
|
+
currentFile?: string;
|
|
207
|
+
fileSystem: Record<string, string>;
|
|
208
|
+
document?: DocumentData;
|
|
209
|
+
documentPage: number;
|
|
210
|
+
documentZoom: number;
|
|
211
|
+
documentScroll: {
|
|
212
|
+
x: number;
|
|
213
|
+
y: number;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Code content change
|
|
218
|
+
*/
|
|
219
|
+
interface CodeEventData {
|
|
220
|
+
content: string;
|
|
221
|
+
language?: string;
|
|
222
|
+
cursorPosition?: {
|
|
223
|
+
line: number;
|
|
224
|
+
column: number;
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Granular code edit event - for smooth typing playback
|
|
229
|
+
* Records individual text operations (insert/delete) at specific positions.
|
|
230
|
+
*/
|
|
231
|
+
interface CodeEditEventData {
|
|
232
|
+
/** Type of edit operation */
|
|
233
|
+
operation: 'insert' | 'delete';
|
|
234
|
+
/** Start position of the edit */
|
|
235
|
+
position: {
|
|
236
|
+
line: number;
|
|
237
|
+
column: number;
|
|
238
|
+
/** Absolute character offset from start of file */
|
|
239
|
+
offset: number;
|
|
240
|
+
};
|
|
241
|
+
/** Text being inserted (for 'insert' operations) */
|
|
242
|
+
text?: string;
|
|
243
|
+
/** Number of characters deleted (for 'delete' operations) */
|
|
244
|
+
deleteCount?: number;
|
|
245
|
+
/** Direction of deletion: 'forward' (Delete key) or 'backward' (Backspace) */
|
|
246
|
+
deleteDirection?: 'forward' | 'backward';
|
|
247
|
+
/** Current cursor position after the edit */
|
|
248
|
+
cursorPosition?: {
|
|
249
|
+
line: number;
|
|
250
|
+
column: number;
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Language change
|
|
255
|
+
*/
|
|
256
|
+
interface LanguageEventData {
|
|
257
|
+
language: string;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Terminal output (batched) - v1.1.0 optimized format
|
|
261
|
+
*/
|
|
262
|
+
interface TerminalBatchedOutput {
|
|
263
|
+
/** Full output text (may be multi-line) */
|
|
264
|
+
text: string;
|
|
265
|
+
/** Output stream */
|
|
266
|
+
stream: 'stdout' | 'stderr';
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Terminal cursor position
|
|
270
|
+
*/
|
|
271
|
+
interface TerminalCursorPosition {
|
|
272
|
+
row: number;
|
|
273
|
+
col: number;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Terminal output event
|
|
277
|
+
*
|
|
278
|
+
* v1.1.0 adds batched output support for efficiency.
|
|
279
|
+
* The `output` field is preferred over `lines` for new recordings.
|
|
280
|
+
*/
|
|
281
|
+
interface TerminalEventData {
|
|
282
|
+
/** Legacy line-by-line format (v1.0.0) */
|
|
283
|
+
lines?: TerminalLine$1[];
|
|
284
|
+
/** Batched output format (v1.1.0+) - preferred */
|
|
285
|
+
output?: TerminalBatchedOutput | string;
|
|
286
|
+
/** Whether terminal was cleared */
|
|
287
|
+
cleared?: boolean;
|
|
288
|
+
/** Cursor position change (v1.1.0+) */
|
|
289
|
+
cursorPosition?: TerminalCursorPosition;
|
|
290
|
+
/** Legacy: append lines to existing (v1.0.0) */
|
|
291
|
+
addedLines?: boolean;
|
|
292
|
+
/** Legacy: command that was executed (v1.0.0) */
|
|
293
|
+
command?: string;
|
|
294
|
+
/** Legacy: whether terminal input was cleared (v1.0.0) */
|
|
295
|
+
inputCleared?: boolean;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Keystroke timing for realistic playback animation
|
|
299
|
+
*/
|
|
300
|
+
interface TerminalKeystroke {
|
|
301
|
+
/** Character typed */
|
|
302
|
+
char: string;
|
|
303
|
+
/** Delay in ms since previous keystroke */
|
|
304
|
+
delay: number;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Terminal input event (user typing)
|
|
308
|
+
*
|
|
309
|
+
* v1.1.0 adds keystroke timing for realistic playback.
|
|
310
|
+
*/
|
|
311
|
+
interface TerminalInputEventData {
|
|
312
|
+
/** Final input string (what was typed) */
|
|
313
|
+
input?: string;
|
|
314
|
+
/** Legacy: input value (v1.0.0) */
|
|
315
|
+
value?: string;
|
|
316
|
+
/** Keystroke timing for animation (v1.1.0+) */
|
|
317
|
+
keystrokes?: TerminalKeystroke[];
|
|
318
|
+
/** Whether Enter was pressed (command submitted) (v1.1.0+) */
|
|
319
|
+
submitted?: boolean;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Begin drawing stroke
|
|
323
|
+
*/
|
|
324
|
+
interface StrokeStartEventData {
|
|
325
|
+
id: string;
|
|
326
|
+
color: string;
|
|
327
|
+
width: number;
|
|
328
|
+
opacity?: number;
|
|
329
|
+
tool: StrokeTool;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Batch of points in current stroke
|
|
333
|
+
*/
|
|
334
|
+
interface WhiteboardPointsEventData {
|
|
335
|
+
strokeId: string;
|
|
336
|
+
points: Point[];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Finish stroke
|
|
340
|
+
*/
|
|
341
|
+
interface StrokeEndEventData {
|
|
342
|
+
strokeId: string;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Complete stroke at once
|
|
346
|
+
*/
|
|
347
|
+
interface WhiteboardStrokeEventData {
|
|
348
|
+
stroke: Stroke;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Clear entire whiteboard
|
|
352
|
+
*/
|
|
353
|
+
interface WhiteboardClearEventData {
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Undo last stroke
|
|
357
|
+
*/
|
|
358
|
+
interface WhiteboardUndoEventData {
|
|
359
|
+
strokeId?: string;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Excalidraw full scene update
|
|
363
|
+
*/
|
|
364
|
+
interface ExcalidrawSceneEventData {
|
|
365
|
+
elements: unknown[];
|
|
366
|
+
appState?: unknown;
|
|
367
|
+
files?: Record<string, unknown>;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Clear Excalidraw scene
|
|
371
|
+
*/
|
|
372
|
+
interface ExcalidrawClearEventData {
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Load document
|
|
376
|
+
*/
|
|
377
|
+
interface DocumentLoadEventData {
|
|
378
|
+
id: string;
|
|
379
|
+
name: string;
|
|
380
|
+
type: string;
|
|
381
|
+
data?: string;
|
|
382
|
+
assetRef?: string;
|
|
383
|
+
numPages: number;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Document page change
|
|
387
|
+
*/
|
|
388
|
+
interface DocumentPageEventData {
|
|
389
|
+
page: number;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Document zoom change
|
|
393
|
+
*/
|
|
394
|
+
interface DocumentZoomEventData {
|
|
395
|
+
zoom: number;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Document scroll position
|
|
399
|
+
*/
|
|
400
|
+
interface DocumentScrollEventData {
|
|
401
|
+
x: number;
|
|
402
|
+
y: number;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Remove document
|
|
406
|
+
*/
|
|
407
|
+
interface DocumentClearEventData {
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Create new file
|
|
411
|
+
*/
|
|
412
|
+
interface FileCreateEventData {
|
|
413
|
+
path: string;
|
|
414
|
+
content: string;
|
|
415
|
+
language?: string;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Update file content
|
|
419
|
+
*/
|
|
420
|
+
interface FileUpdateEventData {
|
|
421
|
+
path: string;
|
|
422
|
+
content: string;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Delete file
|
|
426
|
+
*/
|
|
427
|
+
interface FileDeleteEventData {
|
|
428
|
+
path: string;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Rename file
|
|
432
|
+
*/
|
|
433
|
+
interface FileRenameEventData {
|
|
434
|
+
oldPath: string;
|
|
435
|
+
newPath: string;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Switch active file
|
|
439
|
+
*/
|
|
440
|
+
interface FileSwitchEventData {
|
|
441
|
+
path: string;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* File/folder creation UI started - shows the inline input
|
|
445
|
+
*/
|
|
446
|
+
interface FileCreationStartEventData {
|
|
447
|
+
/** Type of item being created */
|
|
448
|
+
type: 'file' | 'folder';
|
|
449
|
+
/** Parent folder path where the item is being created */
|
|
450
|
+
parentPath: string;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* File/folder creation input changed - for typing animation
|
|
454
|
+
*/
|
|
455
|
+
interface FileCreationInputEventData {
|
|
456
|
+
/** Current value of the input field */
|
|
457
|
+
value: string;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* File/folder creation cancelled - hides the inline input
|
|
461
|
+
*/
|
|
462
|
+
interface FileCreationCancelEventData {
|
|
463
|
+
/** Reason for cancellation (optional) */
|
|
464
|
+
reason?: 'escape' | 'blur' | 'other';
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Cursor position update
|
|
468
|
+
*/
|
|
469
|
+
interface CursorMoveEventData {
|
|
470
|
+
x: number;
|
|
471
|
+
y: number;
|
|
472
|
+
visible: boolean;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Click event
|
|
476
|
+
*/
|
|
477
|
+
interface CursorClickEventData {
|
|
478
|
+
x: number;
|
|
479
|
+
y: number;
|
|
480
|
+
button: 'left' | 'right' | 'middle';
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Cursor entered content area
|
|
484
|
+
*/
|
|
485
|
+
interface CursorEnterEventData {
|
|
486
|
+
area: string;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Cursor left content area
|
|
490
|
+
*/
|
|
491
|
+
interface CursorLeaveEventData {
|
|
492
|
+
area: string;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Switch active tool
|
|
496
|
+
*/
|
|
497
|
+
interface ToolSwitchEventData {
|
|
498
|
+
tool: Tool;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Marker event (metadata only)
|
|
502
|
+
*/
|
|
503
|
+
interface MarkerEventData {
|
|
504
|
+
id: string;
|
|
505
|
+
label: string;
|
|
506
|
+
type: 'chapter' | 'highlight' | 'quiz' | 'note';
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Legacy tool event (same as tool_switch)
|
|
510
|
+
*/
|
|
511
|
+
interface ToolEventData {
|
|
512
|
+
tool: Tool;
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Legacy whiteboard full strokes replacement
|
|
516
|
+
*/
|
|
517
|
+
interface WhiteboardEventData {
|
|
518
|
+
strokes: Stroke[];
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Legacy single point add to current stroke
|
|
522
|
+
*/
|
|
523
|
+
interface WhiteboardPointEventData {
|
|
524
|
+
point: Point;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Legacy file select event
|
|
528
|
+
*/
|
|
529
|
+
interface FileSelectEventData {
|
|
530
|
+
path: string;
|
|
531
|
+
content?: string;
|
|
532
|
+
language?: string;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Legacy file change event
|
|
536
|
+
*/
|
|
537
|
+
interface FileChangeEventData {
|
|
538
|
+
path: string;
|
|
539
|
+
content: string;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* CT Format State Types
|
|
544
|
+
*
|
|
545
|
+
* Defines the playback state structure used by the state engine.
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Complete playback state at any point in time
|
|
550
|
+
*/
|
|
551
|
+
interface PlaybackState$1 {
|
|
552
|
+
/** Current code content */
|
|
553
|
+
code: string;
|
|
554
|
+
/** Current programming language */
|
|
555
|
+
language: string;
|
|
556
|
+
/** Terminal output history */
|
|
557
|
+
terminalLines: TerminalLine$1[];
|
|
558
|
+
/** Current terminal input buffer */
|
|
559
|
+
terminalInput: string;
|
|
560
|
+
/** Completed strokes on whiteboard (legacy) */
|
|
561
|
+
strokes: Stroke[];
|
|
562
|
+
/** Current Excalidraw scene (modern whiteboard) */
|
|
563
|
+
excalidrawScene: ExcalidrawScene | null;
|
|
564
|
+
/** Currently active tool */
|
|
565
|
+
activeTool: Tool;
|
|
566
|
+
/** Currently active file path */
|
|
567
|
+
currentFile: string | null;
|
|
568
|
+
/** Virtual file system (path → content) */
|
|
569
|
+
fileSystem: Record<string, string>;
|
|
570
|
+
/** Currently loaded document */
|
|
571
|
+
documentData: DocumentData | null;
|
|
572
|
+
/** Current document page number */
|
|
573
|
+
documentPage: number;
|
|
574
|
+
/** Current document zoom level */
|
|
575
|
+
documentZoom: number;
|
|
576
|
+
/** Current document scroll position */
|
|
577
|
+
documentScroll: {
|
|
578
|
+
x: number;
|
|
579
|
+
y: number;
|
|
580
|
+
};
|
|
581
|
+
/** Cursor position and visibility */
|
|
582
|
+
cursorPosition: {
|
|
583
|
+
x: number;
|
|
584
|
+
y: number;
|
|
585
|
+
visible: boolean;
|
|
586
|
+
};
|
|
587
|
+
/** File creation UI state - for showing inline input during playback */
|
|
588
|
+
fileCreation: FileCreationState | null;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* State for file/folder creation UI display during playback
|
|
592
|
+
*/
|
|
593
|
+
interface FileCreationState {
|
|
594
|
+
/** Type of item being created */
|
|
595
|
+
type: 'file' | 'folder';
|
|
596
|
+
/** Parent folder path */
|
|
597
|
+
parentPath: string;
|
|
598
|
+
/** Current input value (filename being typed) */
|
|
599
|
+
inputValue: string;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* CT Format Chunked Events Types
|
|
604
|
+
*
|
|
605
|
+
* Defines types for the chunked events architecture in v1.1.0.
|
|
606
|
+
* Events are split into time-indexed chunks for:
|
|
607
|
+
* - Fast seeking (load only needed chunks)
|
|
608
|
+
* - Memory efficiency (unload distant chunks)
|
|
609
|
+
* - Progressive loading (start playback before full load)
|
|
610
|
+
*/
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* A single chunk of events covering a time range
|
|
614
|
+
*/
|
|
615
|
+
interface CTEventChunk {
|
|
616
|
+
/** Chunk identifier (0, 1, 2, ...) */
|
|
617
|
+
id: number;
|
|
618
|
+
/** First event time in this chunk (milliseconds) */
|
|
619
|
+
startTimeMs: number;
|
|
620
|
+
/** Last event time in this chunk (milliseconds) */
|
|
621
|
+
endTimeMs: number;
|
|
622
|
+
/** Events in this chunk */
|
|
623
|
+
events: CTEvent[];
|
|
624
|
+
/** Optional state snapshot at the start of this chunk for fast seeking */
|
|
625
|
+
snapshotAtStart?: PlaybackState$1;
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Individual chunk metadata in the index
|
|
629
|
+
*/
|
|
630
|
+
interface CTChunkInfo {
|
|
631
|
+
/** Chunk index (0, 1, 2, ...) */
|
|
632
|
+
id: number;
|
|
633
|
+
/** Filename (e.g., "chunk-000.json" or "chunk-000.json.gz") */
|
|
634
|
+
file: string;
|
|
635
|
+
/** First event time in this chunk (milliseconds) */
|
|
636
|
+
startTimeMs: number;
|
|
637
|
+
/** Last event time in this chunk (milliseconds) */
|
|
638
|
+
endTimeMs: number;
|
|
639
|
+
/** Number of events in this chunk */
|
|
640
|
+
eventCount: number;
|
|
641
|
+
/** SHA-256 checksum for integrity verification */
|
|
642
|
+
checksum: string;
|
|
643
|
+
/** File size in bytes (for progress reporting) */
|
|
644
|
+
sizeBytes: number;
|
|
645
|
+
/** Whether this chunk has a state snapshot at its start */
|
|
646
|
+
hasSnapshot: boolean;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* State snapshot reference
|
|
650
|
+
*/
|
|
651
|
+
interface CTStateSnapshotRef {
|
|
652
|
+
/** Chunk ID where snapshot is stored */
|
|
653
|
+
chunkId: number;
|
|
654
|
+
/** Snapshot timestamp (milliseconds) */
|
|
655
|
+
timeMs: number;
|
|
656
|
+
/** Snapshot file path (e.g., "snapshots/snap-003.json") */
|
|
657
|
+
file: string;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Event Chunk Index (events/index.json)
|
|
661
|
+
*
|
|
662
|
+
* This is the first file loaded when opening a .ct file.
|
|
663
|
+
* It tells the player which chunks exist and their time ranges.
|
|
664
|
+
*/
|
|
665
|
+
interface CTEventChunkIndex {
|
|
666
|
+
/** Format version for the index itself */
|
|
667
|
+
formatVersion: string;
|
|
668
|
+
/** All chunks in this recording */
|
|
669
|
+
chunks: CTChunkInfo[];
|
|
670
|
+
/** State snapshots for fast seeking (every N chunks) */
|
|
671
|
+
snapshots?: CTStateSnapshotRef[];
|
|
672
|
+
/** Total event count across all chunks */
|
|
673
|
+
totalEventCount: number;
|
|
674
|
+
/** Total number of chunks */
|
|
675
|
+
totalChunks: number;
|
|
676
|
+
/** Total duration in milliseconds */
|
|
677
|
+
totalDuration: number;
|
|
678
|
+
/** Default chunk duration in milliseconds */
|
|
679
|
+
chunkDurationMs: number;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Chunk load result
|
|
683
|
+
*/
|
|
684
|
+
interface ChunkLoadResult {
|
|
685
|
+
/** The loaded chunk (undefined if failed) */
|
|
686
|
+
chunk?: CTEventChunk;
|
|
687
|
+
/** Load time in milliseconds */
|
|
688
|
+
loadTimeMs: number;
|
|
689
|
+
/** Whether this was a cache hit */
|
|
690
|
+
fromCache: boolean;
|
|
691
|
+
/** Error message if load failed */
|
|
692
|
+
error?: string;
|
|
693
|
+
}
|
|
694
|
+
/** State snapshot stored at chunk boundary for fast seeking */
|
|
695
|
+
interface StateSnapshot {
|
|
696
|
+
/** Chunk ID where this snapshot applies */
|
|
697
|
+
chunkId: number;
|
|
698
|
+
/** Timestamp in milliseconds */
|
|
699
|
+
timeMs: number;
|
|
700
|
+
/** Complete playback state at this point */
|
|
701
|
+
state: PlaybackState$1;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* CT Format Access Control Types
|
|
706
|
+
*
|
|
707
|
+
* Defines types for content protection, licensing, and DRM in v1.1.0.
|
|
708
|
+
* Supports multiple licensing models from free to enterprise.
|
|
709
|
+
*/
|
|
710
|
+
/**
|
|
711
|
+
* License type for content access
|
|
712
|
+
*/
|
|
713
|
+
type CTLicenseType = 'public' | 'unlisted' | 'private' | 'paid';
|
|
714
|
+
/**
|
|
715
|
+
* DRM provider options
|
|
716
|
+
*/
|
|
717
|
+
type CTDRMProvider = 'widevine' | 'fairplay' | 'playready' | 'custom';
|
|
718
|
+
/**
|
|
719
|
+
* Watermark type
|
|
720
|
+
*/
|
|
721
|
+
type CTWatermarkType = 'visible' | 'invisible' | 'both';
|
|
722
|
+
/**
|
|
723
|
+
* Encryption configuration for protected content
|
|
724
|
+
*/
|
|
725
|
+
interface CTEncryptionConfig {
|
|
726
|
+
/** Encryption algorithm (only AES-256-GCM supported) */
|
|
727
|
+
algorithm: 'AES-256-GCM';
|
|
728
|
+
/** Key derivation function */
|
|
729
|
+
keyDerivation: 'PBKDF2';
|
|
730
|
+
/** Key identifier for key server */
|
|
731
|
+
keyId: string;
|
|
732
|
+
/** Whether event chunks are encrypted */
|
|
733
|
+
encryptedChunks: boolean;
|
|
734
|
+
/** Whether video/audio is encrypted */
|
|
735
|
+
encryptedMedia: boolean;
|
|
736
|
+
/** Initialization vector (base64 encoded) */
|
|
737
|
+
iv: string;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Access restrictions for content
|
|
741
|
+
*/
|
|
742
|
+
interface CTAccessRestrictions {
|
|
743
|
+
/** ISO 8601 expiration date (for rentals) */
|
|
744
|
+
expiresAt?: string;
|
|
745
|
+
/** Maximum number of plays allowed */
|
|
746
|
+
maxPlays?: number;
|
|
747
|
+
/** Whether user can download for offline viewing */
|
|
748
|
+
allowDownload: boolean;
|
|
749
|
+
/** Whether content can be embedded on other sites */
|
|
750
|
+
allowEmbed: boolean;
|
|
751
|
+
/** Whitelist of allowed embedding domains */
|
|
752
|
+
allowedDomains?: string[];
|
|
753
|
+
/** Whether authentication is required */
|
|
754
|
+
requireAuth: boolean;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* DRM configuration for premium content
|
|
758
|
+
*/
|
|
759
|
+
interface CTDRMConfig {
|
|
760
|
+
/** DRM provider to use */
|
|
761
|
+
provider: CTDRMProvider;
|
|
762
|
+
/** License acquisition URL */
|
|
763
|
+
licenseServerUrl: string;
|
|
764
|
+
/** Certificate URL (required for FairPlay) */
|
|
765
|
+
certificateUrl?: string;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Watermark configuration for content tracing
|
|
769
|
+
*/
|
|
770
|
+
interface CTWatermarkConfig {
|
|
771
|
+
/** Whether watermarking is enabled */
|
|
772
|
+
enabled: boolean;
|
|
773
|
+
/** Type of watermark */
|
|
774
|
+
type: CTWatermarkType;
|
|
775
|
+
/** Watermark template (e.g., "{userId} - {timestamp}") */
|
|
776
|
+
data: string;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Main Access Control Configuration
|
|
780
|
+
*/
|
|
781
|
+
interface CTAccessControl {
|
|
782
|
+
/** License type */
|
|
783
|
+
license: CTLicenseType;
|
|
784
|
+
/** Encryption settings (for private/paid content) */
|
|
785
|
+
encryption?: CTEncryptionConfig;
|
|
786
|
+
/** Access restrictions */
|
|
787
|
+
restrictions?: CTAccessRestrictions;
|
|
788
|
+
/** DRM configuration (for premium content) */
|
|
789
|
+
drm?: CTDRMConfig;
|
|
790
|
+
/** Watermark configuration (for content tracing) */
|
|
791
|
+
watermark?: CTWatermarkConfig;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* CT Format Streaming Types
|
|
796
|
+
*
|
|
797
|
+
* Defines types for streaming playback configuration in v1.1.0.
|
|
798
|
+
* Goal: Start playback within 2 seconds by loading minimal data first.
|
|
799
|
+
*/
|
|
800
|
+
/**
|
|
801
|
+
* Priority loading configuration
|
|
802
|
+
*/
|
|
803
|
+
interface CTStreamingPriority {
|
|
804
|
+
/** Files to load FIRST (before playback can start) */
|
|
805
|
+
critical: string[];
|
|
806
|
+
/** Files to load SECOND (for smooth early playback) */
|
|
807
|
+
high: string[];
|
|
808
|
+
/** Files to prefetch in background */
|
|
809
|
+
prefetch: string[];
|
|
810
|
+
}
|
|
811
|
+
/**
|
|
812
|
+
* Buffering strategy configuration
|
|
813
|
+
*/
|
|
814
|
+
interface CTBufferStrategy {
|
|
815
|
+
/** Minimum buffer before play starts (ms, default: 5000) */
|
|
816
|
+
minBufferMs: number;
|
|
817
|
+
/** Maximum buffer to maintain (ms, default: 30000) */
|
|
818
|
+
maxBufferMs: number;
|
|
819
|
+
/** Number of chunks to prefetch ahead (default: 2) */
|
|
820
|
+
prefetchChunks: number;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Seek optimization configuration
|
|
824
|
+
*/
|
|
825
|
+
interface CTSeekConfig {
|
|
826
|
+
/** State snapshot interval in ms (default: 60000 = 1 min) */
|
|
827
|
+
snapshotIntervalMs: number;
|
|
828
|
+
/** Whether to align seeks to video keyframes */
|
|
829
|
+
keyframeAligned: boolean;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Main Streaming Configuration
|
|
833
|
+
*/
|
|
834
|
+
interface CTStreamingConfig {
|
|
835
|
+
/** Priority loading order */
|
|
836
|
+
priority: CTStreamingPriority;
|
|
837
|
+
/** Buffering strategy */
|
|
838
|
+
buffer: CTBufferStrategy;
|
|
839
|
+
/** Seek optimization */
|
|
840
|
+
seeking: CTSeekConfig;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Recovery information for corrupted files
|
|
844
|
+
*/
|
|
845
|
+
interface CTRecoveryInfo {
|
|
846
|
+
/** Last chunk that was fully written */
|
|
847
|
+
lastGoodChunk: number;
|
|
848
|
+
/** Safe seek points (chunk boundaries) */
|
|
849
|
+
recoveryPoints: number[];
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* File integrity information
|
|
853
|
+
*/
|
|
854
|
+
interface CTIntegrityInfo {
|
|
855
|
+
/** Hash algorithm used */
|
|
856
|
+
algorithm: 'SHA-256';
|
|
857
|
+
/** ISO 8601 timestamp when hashes were generated */
|
|
858
|
+
generatedAt: string;
|
|
859
|
+
/** Map of file paths to their SHA-256 hashes */
|
|
860
|
+
files: Record<string, string>;
|
|
861
|
+
/** Recovery information */
|
|
862
|
+
recovery?: CTRecoveryInfo;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Chunked events reference (v1.1.0+)
|
|
867
|
+
* Events are stored in time-indexed chunks for efficient streaming.
|
|
868
|
+
*/
|
|
869
|
+
interface CTEventsReference {
|
|
870
|
+
/** Path to chunk index file (e.g., "events/index.json") */
|
|
871
|
+
indexFile: string;
|
|
872
|
+
/** Total events across all chunks */
|
|
873
|
+
totalCount: number;
|
|
874
|
+
/** Number of chunk files */
|
|
875
|
+
chunkCount: number;
|
|
876
|
+
/** Duration per chunk in milliseconds (default: 300000 = 5 min) */
|
|
877
|
+
chunkDurationMs: number;
|
|
878
|
+
/** Whether chunks are gzip compressed */
|
|
879
|
+
compressed: boolean;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Legacy events reference (v1.0.0)
|
|
883
|
+
* Single events.json file - kept for backwards compatibility
|
|
884
|
+
*/
|
|
885
|
+
interface CTEventsInfoLegacy {
|
|
886
|
+
/** Relative path in archive (typically "events.json") */
|
|
887
|
+
file: string;
|
|
888
|
+
/** Total number of events */
|
|
889
|
+
count: number;
|
|
890
|
+
/** Whether events file is gzip compressed */
|
|
891
|
+
compressed?: boolean;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
894
|
+
* Events info - supports both legacy and chunked formats
|
|
895
|
+
*/
|
|
896
|
+
type CTEventsInfo = CTEventsReference | CTEventsInfoLegacy;
|
|
897
|
+
/**
|
|
898
|
+
* Author information
|
|
899
|
+
*/
|
|
900
|
+
interface CTAuthorInfo {
|
|
901
|
+
/** Author's unique ID */
|
|
902
|
+
id: string;
|
|
903
|
+
/** Display name */
|
|
904
|
+
name: string;
|
|
905
|
+
/** Email address */
|
|
906
|
+
email?: string;
|
|
907
|
+
/** Website URL */
|
|
908
|
+
url?: string;
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Main manifest structure for a .ct file (v1.1.0)
|
|
912
|
+
*/
|
|
913
|
+
interface CTManifest {
|
|
914
|
+
/** Format version following semver (e.g., "1.1.0") */
|
|
915
|
+
formatVersion: string;
|
|
916
|
+
/** Generator application identifier (e.g., "CT-Courses Recorder v2.1.0") */
|
|
917
|
+
generator: string;
|
|
918
|
+
/** Unique identifier for this recording (UUID) */
|
|
919
|
+
id: string;
|
|
920
|
+
/** Display title of the recording */
|
|
921
|
+
name: string;
|
|
922
|
+
/** Optional description */
|
|
923
|
+
description?: string;
|
|
924
|
+
/** Author information (v1.1.0+) */
|
|
925
|
+
author?: CTAuthorInfo;
|
|
926
|
+
/** Total duration in seconds */
|
|
927
|
+
duration: number;
|
|
928
|
+
/** ISO 8601 timestamp of creation */
|
|
929
|
+
createdAt: string;
|
|
930
|
+
/** ISO 8601 timestamp of last modification */
|
|
931
|
+
modifiedAt: string;
|
|
932
|
+
/** Access control and licensing (v1.1.0+) */
|
|
933
|
+
access?: CTAccessControl;
|
|
934
|
+
/** Streaming configuration (v1.1.0+) */
|
|
935
|
+
streaming?: CTStreamingConfig;
|
|
936
|
+
/** Integrity checksums (v1.1.0+) */
|
|
937
|
+
integrity?: CTIntegrityInfo;
|
|
938
|
+
/** Media file references */
|
|
939
|
+
media: CTMediaInfo;
|
|
940
|
+
/** Events reference (chunked in v1.1.0+, single file in v1.0.0) */
|
|
941
|
+
events: CTEventsInfo;
|
|
942
|
+
/** Chapter/highlight markers */
|
|
943
|
+
markers: CTMarker[];
|
|
944
|
+
/** Optional embedded assets */
|
|
945
|
+
assets?: CTAssets;
|
|
946
|
+
/** Edit history (for edited recordings) */
|
|
947
|
+
editHistory?: CTEditHistory;
|
|
948
|
+
/** Playback configuration hints */
|
|
949
|
+
playback?: CTPlaybackHints;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* Video segment information for streaming
|
|
953
|
+
*/
|
|
954
|
+
interface CTVideoSegments {
|
|
955
|
+
/** Segment duration in seconds */
|
|
956
|
+
duration: number;
|
|
957
|
+
/** Segment filename pattern (e.g., "media/seg-{index}.webm") */
|
|
958
|
+
pattern: string;
|
|
959
|
+
/** Total segment count */
|
|
960
|
+
count: number;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Media file references
|
|
964
|
+
*/
|
|
965
|
+
interface CTMediaInfo {
|
|
966
|
+
video?: CTVideoInfo;
|
|
967
|
+
audio?: CTAudioInfo;
|
|
968
|
+
/** Pre-generated waveform for timeline UI (v1.1.0+) */
|
|
969
|
+
waveform?: CTWaveformInfo;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* Video file metadata
|
|
973
|
+
*/
|
|
974
|
+
interface CTVideoInfo {
|
|
975
|
+
/** Relative path in archive (e.g., "media/video.webm") */
|
|
976
|
+
file: string;
|
|
977
|
+
/** MIME type (e.g., "video/webm", "video/mp4") */
|
|
978
|
+
mimeType: string;
|
|
979
|
+
/** Duration in seconds */
|
|
980
|
+
duration: number;
|
|
981
|
+
/** Video width in pixels */
|
|
982
|
+
width: number;
|
|
983
|
+
/** Video height in pixels */
|
|
984
|
+
height: number;
|
|
985
|
+
/** Codec identifier (e.g., "vp9", "h264") */
|
|
986
|
+
codec?: string;
|
|
987
|
+
/** Video segments for streaming (v1.1.0+) */
|
|
988
|
+
segments?: CTVideoSegments;
|
|
989
|
+
/** Keyframe positions in ms for fast seeking (v1.1.0+) */
|
|
990
|
+
keyframes?: number[];
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Audio file metadata (when separate from video)
|
|
994
|
+
*/
|
|
995
|
+
interface CTAudioInfo {
|
|
996
|
+
/** Relative path in archive */
|
|
997
|
+
file: string;
|
|
998
|
+
/** MIME type (e.g., "audio/webm", "audio/mp3") */
|
|
999
|
+
mimeType: string;
|
|
1000
|
+
/** Duration in seconds */
|
|
1001
|
+
duration: number;
|
|
1002
|
+
/** Sample rate in Hz */
|
|
1003
|
+
sampleRate?: number;
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Pre-generated waveform data reference
|
|
1007
|
+
*/
|
|
1008
|
+
interface CTWaveformInfo {
|
|
1009
|
+
/** Path to waveform file */
|
|
1010
|
+
file: string;
|
|
1011
|
+
/** Samples per second */
|
|
1012
|
+
samplesPerSecond: number;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Chapter/highlight marker
|
|
1016
|
+
*/
|
|
1017
|
+
interface CTMarker {
|
|
1018
|
+
/** Unique identifier */
|
|
1019
|
+
id: string;
|
|
1020
|
+
/** Time position in milliseconds */
|
|
1021
|
+
time: number;
|
|
1022
|
+
/** Display label */
|
|
1023
|
+
label: string;
|
|
1024
|
+
/** Marker type */
|
|
1025
|
+
type: CTMarkerType;
|
|
1026
|
+
/** Type-specific additional data */
|
|
1027
|
+
data?: Record<string, unknown>;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Marker types
|
|
1031
|
+
*/
|
|
1032
|
+
type CTMarkerType = 'chapter' | 'highlight' | 'quiz' | 'note';
|
|
1033
|
+
/**
|
|
1034
|
+
* Embedded assets manifest
|
|
1035
|
+
*/
|
|
1036
|
+
interface CTAssets {
|
|
1037
|
+
documents?: CTAssetReference[];
|
|
1038
|
+
images?: CTAssetReference[];
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Reference to an embedded asset
|
|
1042
|
+
*/
|
|
1043
|
+
interface CTAssetReference {
|
|
1044
|
+
/** Unique identifier */
|
|
1045
|
+
id: string;
|
|
1046
|
+
/** Relative path in archive */
|
|
1047
|
+
file: string;
|
|
1048
|
+
/** Display name */
|
|
1049
|
+
name: string;
|
|
1050
|
+
/** MIME type */
|
|
1051
|
+
mimeType: string;
|
|
1052
|
+
/** File size in bytes */
|
|
1053
|
+
size: number;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Edit history for modified recordings
|
|
1057
|
+
*/
|
|
1058
|
+
interface CTEditHistory {
|
|
1059
|
+
/** Original recording ID */
|
|
1060
|
+
sourceRecordingId: string;
|
|
1061
|
+
/** List of edit operations applied */
|
|
1062
|
+
operations: CTEditOperation[];
|
|
1063
|
+
/** ISO 8601 timestamp of last edit */
|
|
1064
|
+
editedAt: string;
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Edit operation record
|
|
1068
|
+
*/
|
|
1069
|
+
interface CTEditOperation {
|
|
1070
|
+
/** Unique identifier */
|
|
1071
|
+
id: string;
|
|
1072
|
+
/** Operation type */
|
|
1073
|
+
type: 'cut' | 'trim_start' | 'trim_end' | 'speed';
|
|
1074
|
+
/** Start time in milliseconds (for cut/speed) */
|
|
1075
|
+
startTime?: number;
|
|
1076
|
+
/** End time in milliseconds (for cut/speed) */
|
|
1077
|
+
endTime?: number;
|
|
1078
|
+
/** Amount trimmed in milliseconds (for trims) */
|
|
1079
|
+
trimAmount?: number;
|
|
1080
|
+
/** Speed factor (for speed changes) */
|
|
1081
|
+
factor?: number;
|
|
1082
|
+
}
|
|
1083
|
+
/**
|
|
1084
|
+
* Playback configuration hints
|
|
1085
|
+
*/
|
|
1086
|
+
interface CTPlaybackHints {
|
|
1087
|
+
/** Default playback speed (1.0 = normal) */
|
|
1088
|
+
defaultSpeed?: number;
|
|
1089
|
+
/** Whether viewer can edit code when paused */
|
|
1090
|
+
allowInteraction?: boolean;
|
|
1091
|
+
/** Whether to start playing immediately */
|
|
1092
|
+
autoPlay?: boolean;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Complete recording data structure
|
|
1096
|
+
*/
|
|
1097
|
+
interface CTRecording {
|
|
1098
|
+
/** Manifest metadata */
|
|
1099
|
+
manifest: CTManifest;
|
|
1100
|
+
/** All events in the recording (flat array) */
|
|
1101
|
+
events: CTEvent[];
|
|
1102
|
+
/** Pre-computed event chunks (v1.1.0+, optional) */
|
|
1103
|
+
chunks?: CTEventChunk[];
|
|
1104
|
+
/** Pre-computed chunk index (v1.1.0+, optional) */
|
|
1105
|
+
chunkIndex?: CTEventChunkIndex;
|
|
1106
|
+
/** State snapshots for fast seeking (v1.1.0+, optional) */
|
|
1107
|
+
snapshots?: StateSnapshot[];
|
|
1108
|
+
/** Video/audio media blob */
|
|
1109
|
+
mediaBlob?: Blob;
|
|
1110
|
+
/** Embedded assets by ID */
|
|
1111
|
+
assets?: Map<string, Blob>;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* CT Format Reader
|
|
1116
|
+
*
|
|
1117
|
+
* Parses .ct files (ZIP archives) and extracts all components.
|
|
1118
|
+
* Supports both v1.0.0 (single events.json) and v1.1.0 (chunked events/).
|
|
1119
|
+
*/
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Options for reading a CT file
|
|
1123
|
+
*/
|
|
1124
|
+
interface CTReadOptions {
|
|
1125
|
+
/** Whether to load all events immediately (false = lazy loading for chunked)
|
|
1126
|
+
* WARNING: Setting to true can cause memory exhaustion for large files (>100MB)
|
|
1127
|
+
* Use streaming API (streamCTEvents) or lazy loading instead
|
|
1128
|
+
*/
|
|
1129
|
+
loadAllEvents?: boolean;
|
|
1130
|
+
/** Validate events after loading */
|
|
1131
|
+
validateEvents?: boolean;
|
|
1132
|
+
/** Whether to load media blobs */
|
|
1133
|
+
loadMedia?: boolean;
|
|
1134
|
+
/** Whether to load assets */
|
|
1135
|
+
loadAssets?: boolean;
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Extended recording type for chunked format
|
|
1139
|
+
*/
|
|
1140
|
+
interface CTRecordingWithChunks extends CTRecording {
|
|
1141
|
+
/** Chunk index for lazy loading (v1.1.0+) */
|
|
1142
|
+
chunkIndex?: CTEventChunkIndex;
|
|
1143
|
+
/** Function to load a specific chunk (v1.1.0+) */
|
|
1144
|
+
loadChunk?: (chunkNumber: number) => Promise<ChunkLoadResult>;
|
|
1145
|
+
/** Whether events are stored in chunked format */
|
|
1146
|
+
isChunked: boolean;
|
|
1147
|
+
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Read and parse a .ct file
|
|
1150
|
+
*
|
|
1151
|
+
* @param file - The .ct file as a File, Blob, or ArrayBuffer
|
|
1152
|
+
* @param options - Reading options
|
|
1153
|
+
* @returns Parsed recording data
|
|
1154
|
+
*/
|
|
1155
|
+
declare function readCTFile(file: File | Blob | ArrayBuffer, options?: CTReadOptions): Promise<CTRecordingWithChunks>;
|
|
1156
|
+
/**
|
|
1157
|
+
* Read only the manifest from a .ct file
|
|
1158
|
+
* Faster than full read when you just need metadata
|
|
1159
|
+
*/
|
|
1160
|
+
declare function readCTManifest(file: File | Blob | ArrayBuffer): Promise<CTManifest>;
|
|
1161
|
+
|
|
1162
|
+
/**
|
|
1163
|
+
* CT Format Writer
|
|
1164
|
+
*
|
|
1165
|
+
* Creates .ct files (ZIP archives) from recording data.
|
|
1166
|
+
* Supports both v1.0.0 (single events.json) and v1.1.0 (chunked events/).
|
|
1167
|
+
*/
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* Options for writing a .ct file
|
|
1171
|
+
*/
|
|
1172
|
+
interface WriteCTOptions {
|
|
1173
|
+
/** Compression level (0-9, default: 6) */
|
|
1174
|
+
compressionLevel?: number;
|
|
1175
|
+
/** Whether to validate before writing (default: true) */
|
|
1176
|
+
validate?: boolean;
|
|
1177
|
+
/** Skip media embedding (useful for draft saves) */
|
|
1178
|
+
skipMedia?: boolean;
|
|
1179
|
+
/** Use chunked events format (v1.1.0+, default: true for large recordings) */
|
|
1180
|
+
useChunkedEvents?: boolean;
|
|
1181
|
+
/** Target events per chunk (default: 1000) */
|
|
1182
|
+
eventsPerChunk?: number;
|
|
1183
|
+
/** Target chunk duration in ms (default: 300000 = 5 minutes) */
|
|
1184
|
+
chunkDurationMs?: number;
|
|
1185
|
+
/** Generate checksums for chunks (default: true) */
|
|
1186
|
+
generateChecksums?: boolean;
|
|
1187
|
+
/** Generate integrity.json for file verification (default: true) */
|
|
1188
|
+
generateIntegrity?: boolean;
|
|
1189
|
+
/** Use SHA-256 for checksums (default: true, falls back to simple hash if unavailable) */
|
|
1190
|
+
useSHA256?: boolean;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Create a .ct file from recording data
|
|
1194
|
+
*
|
|
1195
|
+
* @param recording - The recording data to package
|
|
1196
|
+
* @param options - Write options
|
|
1197
|
+
* @returns The .ct file as a Blob
|
|
1198
|
+
*/
|
|
1199
|
+
declare function writeCTFile(recording: CTRecording, options?: WriteCTOptions): Promise<Blob>;
|
|
1200
|
+
/**
|
|
1201
|
+
* Create a new empty recording with default manifest
|
|
1202
|
+
*/
|
|
1203
|
+
declare function createEmptyRecording(name?: string): CTRecording;
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* CT Format Validator
|
|
1207
|
+
*
|
|
1208
|
+
* Validates .ct file structure and content.
|
|
1209
|
+
* Supports both v1.0.0 (single events) and v1.1.0 (chunked events) formats.
|
|
1210
|
+
*/
|
|
1211
|
+
|
|
1212
|
+
/**
|
|
1213
|
+
* Validation result
|
|
1214
|
+
*/
|
|
1215
|
+
interface ValidationResult$1 {
|
|
1216
|
+
/** Whether the validation passed */
|
|
1217
|
+
valid: boolean;
|
|
1218
|
+
/** Error messages (if invalid) */
|
|
1219
|
+
errors?: string[];
|
|
1220
|
+
/** Warning messages (valid but concerning) */
|
|
1221
|
+
warnings?: string[];
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Validate a complete CT recording
|
|
1225
|
+
*/
|
|
1226
|
+
declare function validateCTFile(recording: CTRecording): ValidationResult$1;
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* CT-Courses UI Types
|
|
1230
|
+
*
|
|
1231
|
+
* Shared type definitions for all UI components.
|
|
1232
|
+
*/
|
|
1233
|
+
/**
|
|
1234
|
+
* Component operation mode
|
|
1235
|
+
*/
|
|
1236
|
+
type PanelMode = 'recording' | 'playback' | 'interactive';
|
|
1237
|
+
/**
|
|
1238
|
+
* Base props shared by all panels
|
|
1239
|
+
*/
|
|
1240
|
+
interface BasePanelProps {
|
|
1241
|
+
/** Current operation mode */
|
|
1242
|
+
mode: PanelMode;
|
|
1243
|
+
/** Whether playback is currently active (playback mode only) */
|
|
1244
|
+
isPlaying?: boolean;
|
|
1245
|
+
/** Called when user interacts during playback */
|
|
1246
|
+
onInteract?: () => void;
|
|
1247
|
+
/** Additional CSS classes */
|
|
1248
|
+
className?: string;
|
|
1249
|
+
}
|
|
1250
|
+
/**
|
|
1251
|
+
* File system representation
|
|
1252
|
+
*/
|
|
1253
|
+
interface FileSystemEntry {
|
|
1254
|
+
name: string;
|
|
1255
|
+
path: string;
|
|
1256
|
+
type: 'file' | 'folder' | 'directory';
|
|
1257
|
+
language?: string;
|
|
1258
|
+
children?: FileSystemEntry[];
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Cursor position in editor
|
|
1262
|
+
*/
|
|
1263
|
+
interface CursorPosition$1 {
|
|
1264
|
+
line: number;
|
|
1265
|
+
column: number;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Terminal line structure
|
|
1269
|
+
*/
|
|
1270
|
+
interface TerminalLineData {
|
|
1271
|
+
type: 'input' | 'output' | 'system' | 'error' | 'success' | 'warning' | 'info' | 'header' | 'divider' | 'execution_header' | 'execution_result';
|
|
1272
|
+
text: string;
|
|
1273
|
+
status?: string;
|
|
1274
|
+
time?: number;
|
|
1275
|
+
memory?: number;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* IDE Panel props
|
|
1279
|
+
*/
|
|
1280
|
+
interface IDEPanelProps extends BasePanelProps {
|
|
1281
|
+
/** Current code content */
|
|
1282
|
+
code?: string;
|
|
1283
|
+
/** Current language */
|
|
1284
|
+
language?: string;
|
|
1285
|
+
/** Current file path */
|
|
1286
|
+
currentFile?: string;
|
|
1287
|
+
/** Cursor position */
|
|
1288
|
+
cursorPosition?: CursorPosition$1;
|
|
1289
|
+
/** Terminal output lines */
|
|
1290
|
+
terminalLines?: TerminalLineData[];
|
|
1291
|
+
/** Current terminal input */
|
|
1292
|
+
terminalInput?: string;
|
|
1293
|
+
/** Called when code changes */
|
|
1294
|
+
onCodeChange?: (code: string, language: string) => void;
|
|
1295
|
+
/** Called when cursor moves */
|
|
1296
|
+
onCursorChange?: (position: CursorPosition$1) => void;
|
|
1297
|
+
/** Called when language changes */
|
|
1298
|
+
onLanguageChange?: (language: string) => void;
|
|
1299
|
+
/** Called when a file is selected */
|
|
1300
|
+
onFileSelect?: (path: string, content: string, language: string) => void;
|
|
1301
|
+
/** Called when file content changes */
|
|
1302
|
+
onFileChange?: (path: string, content: string, language?: string) => void;
|
|
1303
|
+
/** Called when a file is created */
|
|
1304
|
+
onFileCreate?: (path: string, content: string) => void;
|
|
1305
|
+
/** Called when a file is deleted */
|
|
1306
|
+
onFileDelete?: (path: string) => void;
|
|
1307
|
+
/** Called when a file is renamed */
|
|
1308
|
+
onFileRename?: (oldPath: string, newPath: string) => void;
|
|
1309
|
+
/**
|
|
1310
|
+
* File creation state during playback - shows inline input for typing animation.
|
|
1311
|
+
* When set, displays the file creation UI with the inputValue.
|
|
1312
|
+
*/
|
|
1313
|
+
fileCreationState?: {
|
|
1314
|
+
type: 'file' | 'folder';
|
|
1315
|
+
parentPath: string;
|
|
1316
|
+
inputValue: string;
|
|
1317
|
+
} | null;
|
|
1318
|
+
/** Called when file creation starts (user clicks New File/Folder button) */
|
|
1319
|
+
onFileCreationStart?: (type: 'file' | 'folder', parentPath: string) => void;
|
|
1320
|
+
/** Called when file creation input changes (user types in the file name input) */
|
|
1321
|
+
onFileCreationInput?: (value: string) => void;
|
|
1322
|
+
/** Called when file creation is cancelled */
|
|
1323
|
+
onFileCreationCancel?: (reason?: string) => void;
|
|
1324
|
+
/** Called when terminal input changes */
|
|
1325
|
+
onTerminalInputChange?: (input: string) => void;
|
|
1326
|
+
/** Called when a command is executed */
|
|
1327
|
+
onTerminalCommand?: (command: string, output: string) => void;
|
|
1328
|
+
/** Called to add lines to terminal */
|
|
1329
|
+
onTerminalAddLines?: (lines: TerminalLineData[]) => void;
|
|
1330
|
+
/** Called when terminal is cleared */
|
|
1331
|
+
onTerminalClear?: () => void;
|
|
1332
|
+
/** Initial file system state */
|
|
1333
|
+
initialFiles?: Record<string, string>;
|
|
1334
|
+
/** Show run code button */
|
|
1335
|
+
showRunButton?: boolean;
|
|
1336
|
+
/** Show file explorer */
|
|
1337
|
+
showFileExplorer?: boolean;
|
|
1338
|
+
/** Default show terminal */
|
|
1339
|
+
defaultShowTerminal?: boolean;
|
|
1340
|
+
/** Default show explorer */
|
|
1341
|
+
defaultShowExplorer?: boolean;
|
|
1342
|
+
/** Read-only editor */
|
|
1343
|
+
readOnly?: boolean;
|
|
1344
|
+
/** Initial active file */
|
|
1345
|
+
initialActiveFile?: string;
|
|
1346
|
+
/** Interactive label text */
|
|
1347
|
+
interactiveLabel?: string;
|
|
1348
|
+
/** Execute code handler */
|
|
1349
|
+
onExecute?: (path: string, language: string) => Promise<TerminalLineData[] | undefined>;
|
|
1350
|
+
/** Execute terminal command handler (uses TerminalService for full command support) */
|
|
1351
|
+
onTerminalExecute?: (command: string) => Promise<TerminalLineData[]>;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Excalidraw scene structure (simplified)
|
|
1355
|
+
*/
|
|
1356
|
+
interface WhiteboardScene {
|
|
1357
|
+
elements: unknown[];
|
|
1358
|
+
appState?: Record<string, unknown>;
|
|
1359
|
+
files?: Record<string, unknown>;
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Whiteboard Panel props
|
|
1363
|
+
*/
|
|
1364
|
+
interface WhiteboardPanelProps extends BasePanelProps {
|
|
1365
|
+
/** Current Excalidraw scene */
|
|
1366
|
+
scene?: WhiteboardScene | null;
|
|
1367
|
+
/** Called when scene changes */
|
|
1368
|
+
onSceneChange?: (scene: WhiteboardScene) => void;
|
|
1369
|
+
/** Called when scene is cleared */
|
|
1370
|
+
onSceneClear?: () => void;
|
|
1371
|
+
/** Show Excalidraw toolbar */
|
|
1372
|
+
showToolbar?: boolean;
|
|
1373
|
+
/** Background color */
|
|
1374
|
+
backgroundColor?: string;
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Document data structure
|
|
1378
|
+
*/
|
|
1379
|
+
interface DocumentDataInput {
|
|
1380
|
+
/** Unique document ID (for multi-document support) */
|
|
1381
|
+
id?: string;
|
|
1382
|
+
/** Document content (base64, ArrayBuffer, or URL) */
|
|
1383
|
+
data: string | ArrayBuffer;
|
|
1384
|
+
/** Document filename */
|
|
1385
|
+
name: string;
|
|
1386
|
+
/** MIME type */
|
|
1387
|
+
type: string;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Scroll position
|
|
1391
|
+
*/
|
|
1392
|
+
interface ScrollPosition {
|
|
1393
|
+
x: number;
|
|
1394
|
+
y: number;
|
|
1395
|
+
}
|
|
1396
|
+
/**
|
|
1397
|
+
* Document info returned on load
|
|
1398
|
+
*/
|
|
1399
|
+
interface DocumentInfo {
|
|
1400
|
+
id?: string;
|
|
1401
|
+
data: string;
|
|
1402
|
+
name: string;
|
|
1403
|
+
numPages: number;
|
|
1404
|
+
type: string;
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Document Panel props
|
|
1408
|
+
*/
|
|
1409
|
+
interface DocumentPanelProps extends BasePanelProps {
|
|
1410
|
+
/** Document data (single document - for backwards compatibility) */
|
|
1411
|
+
documentData?: DocumentDataInput | null;
|
|
1412
|
+
/** Multiple documents (new multi-document support) */
|
|
1413
|
+
documents?: DocumentDataInput[];
|
|
1414
|
+
/** Currently active document ID (for multi-document mode) */
|
|
1415
|
+
activeDocumentId?: string;
|
|
1416
|
+
/** Current page number (1-indexed) */
|
|
1417
|
+
currentPage?: number;
|
|
1418
|
+
/** Zoom level (1.0 = 100%) */
|
|
1419
|
+
zoom?: number;
|
|
1420
|
+
/** Scroll position within page */
|
|
1421
|
+
scrollPosition?: ScrollPosition;
|
|
1422
|
+
/** Called when document is loaded */
|
|
1423
|
+
onDocumentLoad?: (info: DocumentInfo) => void;
|
|
1424
|
+
/** Called when active document changes (multi-document mode) */
|
|
1425
|
+
onDocumentSelect?: (documentId: string) => void;
|
|
1426
|
+
/** Called when a document is removed (multi-document mode) */
|
|
1427
|
+
onDocumentRemove?: (documentId: string) => void;
|
|
1428
|
+
/** Called when page changes */
|
|
1429
|
+
onPageChange?: (page: number) => void;
|
|
1430
|
+
/** Called when zoom changes */
|
|
1431
|
+
onZoomChange?: (zoom: number) => void;
|
|
1432
|
+
/** Called when scroll position changes */
|
|
1433
|
+
onScrollChange?: (position: ScrollPosition) => void;
|
|
1434
|
+
/** Called when document is cleared */
|
|
1435
|
+
onDocumentClear?: () => void;
|
|
1436
|
+
/** Show document tab bar (for multiple documents) */
|
|
1437
|
+
showTabBar?: boolean;
|
|
1438
|
+
/** Show navigation toolbar */
|
|
1439
|
+
showToolbar?: boolean;
|
|
1440
|
+
/** Show page indicator */
|
|
1441
|
+
showPageIndicator?: boolean;
|
|
1442
|
+
/** Allow file upload */
|
|
1443
|
+
allowUpload?: boolean;
|
|
1444
|
+
/** Allow multiple document uploads */
|
|
1445
|
+
allowMultiple?: boolean;
|
|
1446
|
+
/** Background color */
|
|
1447
|
+
backgroundColor?: string;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Terminal component props
|
|
1451
|
+
*/
|
|
1452
|
+
interface TerminalProps {
|
|
1453
|
+
/** Output lines */
|
|
1454
|
+
lines?: TerminalLineData[];
|
|
1455
|
+
/** Current input value */
|
|
1456
|
+
input?: string;
|
|
1457
|
+
/** Called when input changes */
|
|
1458
|
+
onInputChange?: (value: string) => void;
|
|
1459
|
+
/** Called when command is submitted */
|
|
1460
|
+
onCommand?: (command: string) => void;
|
|
1461
|
+
/** Called when terminal is cleared (Ctrl+L) */
|
|
1462
|
+
onClear?: () => void;
|
|
1463
|
+
/** Is code currently executing */
|
|
1464
|
+
isExecuting?: boolean;
|
|
1465
|
+
/** Read-only mode */
|
|
1466
|
+
isReadOnly?: boolean;
|
|
1467
|
+
/** Input placeholder */
|
|
1468
|
+
placeholder?: string;
|
|
1469
|
+
/** Additional CSS classes */
|
|
1470
|
+
className?: string;
|
|
1471
|
+
/** Show prompt */
|
|
1472
|
+
showPrompt?: boolean;
|
|
1473
|
+
/** Auto-focus input */
|
|
1474
|
+
autoFocus?: boolean;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Terminal ref interface
|
|
1478
|
+
*/
|
|
1479
|
+
interface TerminalRef {
|
|
1480
|
+
focus: () => void;
|
|
1481
|
+
scrollToBottom: () => void;
|
|
1482
|
+
}
|
|
1483
|
+
/**
|
|
1484
|
+
* File Explorer props
|
|
1485
|
+
*/
|
|
1486
|
+
interface FileExplorerProps {
|
|
1487
|
+
/** Currently selected file path */
|
|
1488
|
+
selectedFile?: string | null;
|
|
1489
|
+
/** Called when file is selected */
|
|
1490
|
+
onFileSelect: (path: string) => void;
|
|
1491
|
+
/** Called when file is created */
|
|
1492
|
+
onFileCreate?: (path: string, content: string) => void;
|
|
1493
|
+
/** Called when folder is created */
|
|
1494
|
+
onFolderCreate?: (path: string) => void;
|
|
1495
|
+
/** Called when file is deleted */
|
|
1496
|
+
onFileDelete?: (path: string) => void;
|
|
1497
|
+
/** Called when file is renamed */
|
|
1498
|
+
onFileRename?: (oldPath: string, newPath: string) => void;
|
|
1499
|
+
/** Called when user starts creating a file/folder (clicks New File/Folder button) */
|
|
1500
|
+
onFileCreationStart?: (type: 'file' | 'folder', parentPath: string) => void;
|
|
1501
|
+
/** Called when user types in the file creation input */
|
|
1502
|
+
onFileCreationInput?: (value: string) => void;
|
|
1503
|
+
/** Called when user cancels file creation (Escape or blur) */
|
|
1504
|
+
onFileCreationCancel?: (reason?: string) => void;
|
|
1505
|
+
/**
|
|
1506
|
+
* Playback file creation state - for showing the file creation input during playback.
|
|
1507
|
+
* When set, displays a read-only input showing the filename being typed.
|
|
1508
|
+
*/
|
|
1509
|
+
playbackFileCreation?: {
|
|
1510
|
+
type: 'file' | 'folder';
|
|
1511
|
+
parentPath: string;
|
|
1512
|
+
inputValue: string;
|
|
1513
|
+
} | null;
|
|
1514
|
+
/** Additional CSS classes */
|
|
1515
|
+
className?: string;
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Available tool types
|
|
1519
|
+
*/
|
|
1520
|
+
type ToolType = 'code' | 'whiteboard' | 'document';
|
|
1521
|
+
/**
|
|
1522
|
+
* Tool Sidebar props
|
|
1523
|
+
*/
|
|
1524
|
+
interface ToolSidebarProps {
|
|
1525
|
+
/** Currently active tool */
|
|
1526
|
+
activeTool: ToolType;
|
|
1527
|
+
/** Called when tool changes */
|
|
1528
|
+
onToolChange: (tool: ToolType) => void;
|
|
1529
|
+
/** Available tools */
|
|
1530
|
+
tools?: ToolType[];
|
|
1531
|
+
/** Additional CSS classes */
|
|
1532
|
+
className?: string;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
declare function ToolSidebar({ activeTool, onToolChange, tools, className, }: ToolSidebarProps): react_jsx_runtime.JSX.Element;
|
|
1536
|
+
|
|
1537
|
+
declare function WhiteboardPanel({ scene, onSceneChange, onSceneClear, mode, isPlaying, onInteract, showToolbar, backgroundColor, className, }: WhiteboardPanelProps): react_jsx_runtime.JSX.Element;
|
|
1538
|
+
|
|
1539
|
+
declare function DocumentPanel({ documentData, documents: externalDocuments, activeDocumentId: externalActiveId, currentPage, zoom, scrollPosition, onDocumentLoad, onDocumentSelect, onDocumentRemove, onPageChange, onZoomChange, onScrollChange, onDocumentClear, mode, isPlaying, onInteract, showTabBar, showToolbar, showPageIndicator, allowUpload, allowMultiple, backgroundColor, className, }: DocumentPanelProps): react_jsx_runtime.JSX.Element;
|
|
1540
|
+
|
|
1541
|
+
interface VirtualFileSystem {
|
|
1542
|
+
getFileTree: () => FileSystemEntry;
|
|
1543
|
+
subscribe: (callback: () => void) => () => void;
|
|
1544
|
+
writeFile: (path: string, content: string) => void;
|
|
1545
|
+
/** Optional: Read file content by path */
|
|
1546
|
+
readFile?: (path: string) => string;
|
|
1547
|
+
/** Optional: Get all files as a record */
|
|
1548
|
+
getFiles?: () => Record<string, string>;
|
|
1549
|
+
}
|
|
1550
|
+
declare function FileExplorer({ fileSystem, selectedFile, onFileSelect, onFileCreate, onFolderCreate, onFileDelete, onFileRename, onFileCreationStart, onFileCreationInput, onFileCreationCancel, playbackFileCreation, className }: FileExplorerProps & {
|
|
1551
|
+
fileSystem: VirtualFileSystem;
|
|
1552
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1553
|
+
|
|
1554
|
+
declare function IDEPanel({ fileSystem, code: externalCode, language: externalLanguage, currentFile: externalCurrentFile, onCodeChange, onFileChange, onFileCreate, onFileDelete, onFileRename, onFileSelect, onTerminalCommand, onTerminalClear, onTerminalAddLines, fileCreationState, onFileCreationStart, onFileCreationInput, onFileCreationCancel, terminalLines: externalTerminalLines, terminalInput: externalTerminalInput, onTerminalInputChange, onExecute, onTerminalExecute, mode, isPlaying, defaultShowExplorer, defaultShowTerminal, showRunButton, interactiveLabel, initialFiles, initialActiveFile }: IDEPanelProps & {
|
|
1555
|
+
fileSystem: VirtualFileSystem;
|
|
1556
|
+
}): react_jsx_runtime.JSX.Element;
|
|
1557
|
+
|
|
1558
|
+
declare const Terminal: react.ForwardRefExoticComponent<TerminalProps & react.RefAttributes<TerminalRef>>;
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* Active tool type - matches ToolType from @ct-courses/ui
|
|
1562
|
+
*/
|
|
1563
|
+
type ActiveTool$1 = ToolType;
|
|
1564
|
+
/**
|
|
1565
|
+
* Props for the CoursePlayer component
|
|
1566
|
+
*/
|
|
1567
|
+
interface CoursePlayerProps {
|
|
1568
|
+
/** Pre-loaded recording data */
|
|
1569
|
+
recording?: CTRecording;
|
|
1570
|
+
/** URL to a .ct file */
|
|
1571
|
+
recordingUrl?: string;
|
|
1572
|
+
/** Recording ID to fetch from API */
|
|
1573
|
+
recordingId?: string;
|
|
1574
|
+
/** Base API URL for fetching recordings */
|
|
1575
|
+
apiUrl?: string;
|
|
1576
|
+
/** Authentication token */
|
|
1577
|
+
authToken?: string;
|
|
1578
|
+
/** Go-Judge server URL */
|
|
1579
|
+
goJudgeUrl?: string;
|
|
1580
|
+
/** Go-Judge API key */
|
|
1581
|
+
goJudgeApiKey?: string;
|
|
1582
|
+
/** Theme setting */
|
|
1583
|
+
theme?: 'light' | 'dark' | 'system';
|
|
1584
|
+
/** Aspect ratio of the player */
|
|
1585
|
+
aspectRatio?: '16:9' | '4:3' | 'auto';
|
|
1586
|
+
/** Show playback controls toolbar */
|
|
1587
|
+
showToolbar?: boolean;
|
|
1588
|
+
/** Show timeline below player */
|
|
1589
|
+
showTimeline?: boolean;
|
|
1590
|
+
/** Show chapter markers */
|
|
1591
|
+
showChapters?: boolean;
|
|
1592
|
+
/** Show tool sidebar */
|
|
1593
|
+
showToolSidebar?: boolean;
|
|
1594
|
+
/** Auto-play when loaded */
|
|
1595
|
+
autoPlay?: boolean;
|
|
1596
|
+
/** Default playback speed */
|
|
1597
|
+
defaultSpeed?: number;
|
|
1598
|
+
/** Allow fullscreen mode */
|
|
1599
|
+
allowFullscreen?: boolean;
|
|
1600
|
+
/** Default active tool */
|
|
1601
|
+
defaultTool?: ActiveTool$1;
|
|
1602
|
+
/** Called when player is ready */
|
|
1603
|
+
onReady?: () => void;
|
|
1604
|
+
/** Called when playback starts */
|
|
1605
|
+
onPlay?: () => void;
|
|
1606
|
+
/** Called when playback pauses */
|
|
1607
|
+
onPause?: () => void;
|
|
1608
|
+
/** Called on progress update */
|
|
1609
|
+
onProgress?: (time: number, duration: number) => void;
|
|
1610
|
+
/** Called when playback completes */
|
|
1611
|
+
onComplete?: () => void;
|
|
1612
|
+
/** Called on error */
|
|
1613
|
+
onError?: (error: Error) => void;
|
|
1614
|
+
/** Called when entering interactive mode */
|
|
1615
|
+
onInteractionStart?: () => void;
|
|
1616
|
+
/** Called when exiting interactive mode */
|
|
1617
|
+
onInteractionEnd?: (code: string) => void;
|
|
1618
|
+
/** Called when tool changes */
|
|
1619
|
+
onToolChange?: (tool: string) => void;
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Imperative handle for CoursePlayer
|
|
1623
|
+
*/
|
|
1624
|
+
interface CoursePlayerRef {
|
|
1625
|
+
play(): void;
|
|
1626
|
+
pause(): void;
|
|
1627
|
+
seek(timeMs: number): void;
|
|
1628
|
+
setSpeed(speed: number): void;
|
|
1629
|
+
getCurrentTime(): number;
|
|
1630
|
+
getDuration(): number;
|
|
1631
|
+
getState(): PlaybackState$1;
|
|
1632
|
+
enterInteractiveMode(): void;
|
|
1633
|
+
exitInteractiveMode(): void;
|
|
1634
|
+
setActiveTool(tool: ActiveTool$1): void;
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* CoursePlayer component
|
|
1638
|
+
*/
|
|
1639
|
+
declare const CoursePlayer: react.ForwardRefExoticComponent<CoursePlayerProps & react.RefAttributes<CoursePlayerRef>>;
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* CT-Courses Player - Remote Streaming Types
|
|
1643
|
+
*
|
|
1644
|
+
* Types for streaming playback from a remote CDN/R2 storage.
|
|
1645
|
+
* These types match the streaming service manifest structure.
|
|
1646
|
+
*
|
|
1647
|
+
* @packageDocumentation
|
|
1648
|
+
*/
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* Audio quality level information
|
|
1652
|
+
*/
|
|
1653
|
+
interface AudioQuality {
|
|
1654
|
+
/** Quality name (low, medium, high) */
|
|
1655
|
+
name: string;
|
|
1656
|
+
/** Bitrate in bits per second */
|
|
1657
|
+
bitrate: number;
|
|
1658
|
+
/** URL to the quality-specific playlist */
|
|
1659
|
+
playlistUrl: string;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Recording metadata from the streaming manifest
|
|
1663
|
+
*/
|
|
1664
|
+
interface StreamingRecordingInfo {
|
|
1665
|
+
/** Recording ID */
|
|
1666
|
+
id: string;
|
|
1667
|
+
/** Recording name/title */
|
|
1668
|
+
name: string;
|
|
1669
|
+
/** Optional description */
|
|
1670
|
+
description?: string;
|
|
1671
|
+
/** Duration in milliseconds */
|
|
1672
|
+
duration: number;
|
|
1673
|
+
/** Creation timestamp (ISO 8601) */
|
|
1674
|
+
createdAt: string;
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Audio streaming configuration
|
|
1678
|
+
*/
|
|
1679
|
+
interface StreamingAudioConfig {
|
|
1680
|
+
/** Audio type (always 'hls' for adaptive streaming) */
|
|
1681
|
+
type: 'hls';
|
|
1682
|
+
/** URL to the HLS master playlist */
|
|
1683
|
+
masterPlaylistUrl: string;
|
|
1684
|
+
/** Available quality levels */
|
|
1685
|
+
qualities: AudioQuality[];
|
|
1686
|
+
/** Audio duration in seconds */
|
|
1687
|
+
duration: number;
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Events streaming configuration
|
|
1691
|
+
*/
|
|
1692
|
+
interface StreamingEventsConfig {
|
|
1693
|
+
/** URL to the events chunk index */
|
|
1694
|
+
indexUrl: string;
|
|
1695
|
+
/** Total number of event chunks */
|
|
1696
|
+
totalChunks: number;
|
|
1697
|
+
/** Total event count */
|
|
1698
|
+
totalEvents: number;
|
|
1699
|
+
/** Duration of each chunk in milliseconds */
|
|
1700
|
+
chunkDurationMs: number;
|
|
1701
|
+
/** Whether chunks are gzip compressed */
|
|
1702
|
+
compressed: boolean;
|
|
1703
|
+
}
|
|
1704
|
+
/**
|
|
1705
|
+
* Streaming playback configuration hints
|
|
1706
|
+
*/
|
|
1707
|
+
interface StreamingConfig {
|
|
1708
|
+
/** Number of chunks to prefetch ahead */
|
|
1709
|
+
prefetchChunks: number;
|
|
1710
|
+
/** Minimum buffer before play starts (ms) */
|
|
1711
|
+
minBufferMs: number;
|
|
1712
|
+
/** Maximum buffer to maintain (ms) */
|
|
1713
|
+
maxBufferMs: number;
|
|
1714
|
+
}
|
|
1715
|
+
/**
|
|
1716
|
+
* Complete streaming manifest structure
|
|
1717
|
+
* This is the main entry point returned by the streaming service.
|
|
1718
|
+
*/
|
|
1719
|
+
interface StreamingManifest {
|
|
1720
|
+
/** Manifest version */
|
|
1721
|
+
version: string;
|
|
1722
|
+
/** Content ID */
|
|
1723
|
+
contentId: string;
|
|
1724
|
+
/** Recording metadata */
|
|
1725
|
+
recording: StreamingRecordingInfo;
|
|
1726
|
+
/** Audio streaming configuration */
|
|
1727
|
+
audio: StreamingAudioConfig;
|
|
1728
|
+
/** Events streaming configuration */
|
|
1729
|
+
events: StreamingEventsConfig;
|
|
1730
|
+
/** Streaming playback hints */
|
|
1731
|
+
streaming: StreamingConfig;
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Options for useRemoteStreamingPlayback hook
|
|
1735
|
+
*/
|
|
1736
|
+
interface UseRemoteStreamingPlaybackOptions {
|
|
1737
|
+
/** URL to the streaming manifest.json */
|
|
1738
|
+
manifestUrl: string;
|
|
1739
|
+
/** Auto-play when ready */
|
|
1740
|
+
autoPlay?: boolean;
|
|
1741
|
+
/** Initial playback speed (default: 1.0) */
|
|
1742
|
+
defaultSpeed?: number;
|
|
1743
|
+
/** Maximum chunks to keep in memory (default: 5) */
|
|
1744
|
+
maxCacheSize?: number;
|
|
1745
|
+
/** Called when streaming is ready to play */
|
|
1746
|
+
onReady?: () => void;
|
|
1747
|
+
/** Called when playback starts */
|
|
1748
|
+
onPlay?: () => void;
|
|
1749
|
+
/** Called when playback pauses */
|
|
1750
|
+
onPause?: () => void;
|
|
1751
|
+
/** Called on playback progress */
|
|
1752
|
+
onProgress?: (timeMs: number, duration: number) => void;
|
|
1753
|
+
/** Called when playback completes */
|
|
1754
|
+
onComplete?: () => void;
|
|
1755
|
+
/** Called when buffering state changes */
|
|
1756
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
1757
|
+
/** Called on error */
|
|
1758
|
+
onError?: (error: Error) => void;
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Buffer health status for remote streaming
|
|
1762
|
+
*/
|
|
1763
|
+
interface RemoteBufferHealth {
|
|
1764
|
+
/** Current buffer status */
|
|
1765
|
+
status: 'healthy' | 'buffering' | 'critical' | 'empty';
|
|
1766
|
+
/** Number of chunks currently cached */
|
|
1767
|
+
cachedChunks: number;
|
|
1768
|
+
/** Total number of chunks */
|
|
1769
|
+
totalChunks: number;
|
|
1770
|
+
/** Current chunk being played */
|
|
1771
|
+
currentChunk: number;
|
|
1772
|
+
/** Total buffered time in milliseconds */
|
|
1773
|
+
bufferedTimeMs: number;
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Performance metrics for remote streaming
|
|
1777
|
+
*/
|
|
1778
|
+
interface RemoteStreamingMetrics {
|
|
1779
|
+
/** Average chunk fetch time in ms */
|
|
1780
|
+
avgChunkFetchTimeMs: number;
|
|
1781
|
+
/** Cache hit rate (0-1) */
|
|
1782
|
+
cacheHitRate: number;
|
|
1783
|
+
/** Number of times playback had to wait for buffering */
|
|
1784
|
+
bufferUnderrunCount: number;
|
|
1785
|
+
/** Average seek time in ms */
|
|
1786
|
+
avgSeekTimeMs: number;
|
|
1787
|
+
/** Total bytes downloaded */
|
|
1788
|
+
totalBytesLoaded: number;
|
|
1789
|
+
/** Peak number of chunks in cache */
|
|
1790
|
+
peakChunksCached: number;
|
|
1791
|
+
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Complete state returned by the remote streaming hook
|
|
1794
|
+
*/
|
|
1795
|
+
interface RemoteStreamingPlaybackState {
|
|
1796
|
+
/** Current playback state (code, terminal, etc.) */
|
|
1797
|
+
state: PlaybackState$1;
|
|
1798
|
+
/** Current playback time in milliseconds */
|
|
1799
|
+
currentTime: number;
|
|
1800
|
+
/** Total duration in milliseconds */
|
|
1801
|
+
duration: number;
|
|
1802
|
+
/** Whether playback is active */
|
|
1803
|
+
isPlaying: boolean;
|
|
1804
|
+
/** Current playback speed */
|
|
1805
|
+
speed: number;
|
|
1806
|
+
/** Whether the player is ready */
|
|
1807
|
+
isReady: boolean;
|
|
1808
|
+
/** Whether currently buffering */
|
|
1809
|
+
isBuffering: boolean;
|
|
1810
|
+
/** Buffer health information */
|
|
1811
|
+
bufferHealth: RemoteBufferHealth;
|
|
1812
|
+
/** Streaming metrics */
|
|
1813
|
+
metrics: RemoteStreamingMetrics;
|
|
1814
|
+
/** Current error (if any) */
|
|
1815
|
+
error: Error | null;
|
|
1816
|
+
/** The loaded manifest */
|
|
1817
|
+
manifest: StreamingManifest | null;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Controls for remote streaming playback
|
|
1821
|
+
*/
|
|
1822
|
+
interface RemoteStreamingPlaybackControls {
|
|
1823
|
+
/** Start playback */
|
|
1824
|
+
play(): void;
|
|
1825
|
+
/** Pause playback */
|
|
1826
|
+
pause(): void;
|
|
1827
|
+
/** Seek to a specific time (milliseconds) */
|
|
1828
|
+
seek(timeMs: number): Promise<void>;
|
|
1829
|
+
/** Set playback speed */
|
|
1830
|
+
setSpeed(speed: number): void;
|
|
1831
|
+
/** Get the audio element for custom controls */
|
|
1832
|
+
getAudioElement(): HTMLAudioElement | null;
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* HLS player configuration
|
|
1836
|
+
*/
|
|
1837
|
+
interface HLSPlayerConfig {
|
|
1838
|
+
/** Max buffer length in seconds */
|
|
1839
|
+
maxBufferLength?: number;
|
|
1840
|
+
/** Max max buffer length in seconds */
|
|
1841
|
+
maxMaxBufferLength?: number;
|
|
1842
|
+
/** Start level (-1 for auto) */
|
|
1843
|
+
startLevel?: number;
|
|
1844
|
+
/** Auto-start load */
|
|
1845
|
+
autoStartLoad?: boolean;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* HLS quality level info
|
|
1849
|
+
*/
|
|
1850
|
+
interface HLSQualityLevel {
|
|
1851
|
+
/** Level index */
|
|
1852
|
+
index: number;
|
|
1853
|
+
/** Bitrate */
|
|
1854
|
+
bitrate: number;
|
|
1855
|
+
/** Codec string */
|
|
1856
|
+
codecs: string;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
type ActiveTool = ToolType;
|
|
1860
|
+
/**
|
|
1861
|
+
* Props for StreamingCoursePlayer
|
|
1862
|
+
*/
|
|
1863
|
+
interface StreamingCoursePlayerProps {
|
|
1864
|
+
/** URL to the streaming manifest.json */
|
|
1865
|
+
manifestUrl: string;
|
|
1866
|
+
/** Theme setting */
|
|
1867
|
+
theme?: 'light' | 'dark' | 'system';
|
|
1868
|
+
/** Aspect ratio of the player */
|
|
1869
|
+
aspectRatio?: '16:9' | '4:3' | 'auto';
|
|
1870
|
+
/** Show playback controls toolbar */
|
|
1871
|
+
showToolbar?: boolean;
|
|
1872
|
+
/** Show tool sidebar */
|
|
1873
|
+
showToolSidebar?: boolean;
|
|
1874
|
+
/** Auto-play when loaded */
|
|
1875
|
+
autoPlay?: boolean;
|
|
1876
|
+
/** Default playback speed */
|
|
1877
|
+
defaultSpeed?: number;
|
|
1878
|
+
/** Allow fullscreen mode */
|
|
1879
|
+
allowFullscreen?: boolean;
|
|
1880
|
+
/** Default active tool */
|
|
1881
|
+
defaultTool?: ActiveTool;
|
|
1882
|
+
/** Maximum chunks to cache */
|
|
1883
|
+
maxCacheSize?: number;
|
|
1884
|
+
/** Additional CSS class */
|
|
1885
|
+
className?: string;
|
|
1886
|
+
/** Called when player is ready */
|
|
1887
|
+
onReady?: () => void;
|
|
1888
|
+
/** Called when playback starts */
|
|
1889
|
+
onPlay?: () => void;
|
|
1890
|
+
/** Called when playback pauses */
|
|
1891
|
+
onPause?: () => void;
|
|
1892
|
+
/** Called on progress update */
|
|
1893
|
+
onProgress?: (time: number, duration: number) => void;
|
|
1894
|
+
/** Called when playback completes */
|
|
1895
|
+
onComplete?: () => void;
|
|
1896
|
+
/** Called on error */
|
|
1897
|
+
onError?: (error: Error) => void;
|
|
1898
|
+
/** Called when buffering state changes */
|
|
1899
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
1900
|
+
/** Called when tool changes */
|
|
1901
|
+
onToolChange?: (tool: string) => void;
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* Imperative handle for StreamingCoursePlayer
|
|
1905
|
+
*/
|
|
1906
|
+
interface StreamingCoursePlayerRef {
|
|
1907
|
+
play(): void;
|
|
1908
|
+
pause(): void;
|
|
1909
|
+
seek(timeMs: number): Promise<void>;
|
|
1910
|
+
setSpeed(speed: number): void;
|
|
1911
|
+
getCurrentTime(): number;
|
|
1912
|
+
getDuration(): number;
|
|
1913
|
+
getState(): PlaybackState$1;
|
|
1914
|
+
setActiveTool(tool: ActiveTool): void;
|
|
1915
|
+
getManifest(): StreamingManifest | null;
|
|
1916
|
+
getBufferHealth(): RemoteBufferHealth;
|
|
1917
|
+
}
|
|
1918
|
+
declare const StreamingCoursePlayer: react.ForwardRefExoticComponent<StreamingCoursePlayerProps & react.RefAttributes<StreamingCoursePlayerRef>>;
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* @ct-courses/player - Type Definitions
|
|
1922
|
+
*/
|
|
1923
|
+
|
|
1924
|
+
/**
|
|
1925
|
+
* Backend adapter interface for loading recordings
|
|
1926
|
+
*/
|
|
1927
|
+
interface BackendAdapter {
|
|
1928
|
+
getRecording(id: string): Promise<InternalRecording | null>;
|
|
1929
|
+
getMediaUrl?(recordingId: string): Promise<string | null>;
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Internal recording structure (from backend)
|
|
1933
|
+
*/
|
|
1934
|
+
interface InternalRecording {
|
|
1935
|
+
id: string;
|
|
1936
|
+
name?: string;
|
|
1937
|
+
duration: number;
|
|
1938
|
+
events: InternalEvent[];
|
|
1939
|
+
markers?: RecordingMarker[];
|
|
1940
|
+
hasMedia?: boolean;
|
|
1941
|
+
mediaUrl?: string;
|
|
1942
|
+
createdAt?: number;
|
|
1943
|
+
}
|
|
1944
|
+
/**
|
|
1945
|
+
* Internal event structure (from backend)
|
|
1946
|
+
*/
|
|
1947
|
+
interface InternalEvent {
|
|
1948
|
+
type: string;
|
|
1949
|
+
time: number;
|
|
1950
|
+
data: Record<string, unknown>;
|
|
1951
|
+
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Recording marker for chapters
|
|
1954
|
+
*/
|
|
1955
|
+
interface RecordingMarker {
|
|
1956
|
+
id: string;
|
|
1957
|
+
time: number;
|
|
1958
|
+
label: string;
|
|
1959
|
+
color?: string;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Cursor position for replay
|
|
1963
|
+
*/
|
|
1964
|
+
interface CursorPosition {
|
|
1965
|
+
x: number;
|
|
1966
|
+
y: number;
|
|
1967
|
+
visible: boolean;
|
|
1968
|
+
}
|
|
1969
|
+
/**
|
|
1970
|
+
* File system snapshot
|
|
1971
|
+
*/
|
|
1972
|
+
interface FileSystemSnapshot {
|
|
1973
|
+
[path: string]: string;
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Document data with content (extends DocumentInfo with raw data)
|
|
1977
|
+
*/
|
|
1978
|
+
interface PlayerDocumentData extends DocumentInfo {
|
|
1979
|
+
/** Raw document data (base64 for PDFs, etc.) */
|
|
1980
|
+
data: string;
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Playback engine configuration
|
|
1984
|
+
*/
|
|
1985
|
+
interface PlaybackEngineConfig {
|
|
1986
|
+
/** Recording ID to load */
|
|
1987
|
+
recordingId?: string;
|
|
1988
|
+
/** Backend adapter for loading */
|
|
1989
|
+
backendAdapter?: BackendAdapter;
|
|
1990
|
+
/** Pre-loaded recording */
|
|
1991
|
+
recording?: InternalRecording;
|
|
1992
|
+
/** CT file blob to load */
|
|
1993
|
+
ctFile?: Blob;
|
|
1994
|
+
/** Auto-play on load */
|
|
1995
|
+
autoPlay?: boolean;
|
|
1996
|
+
/** Initial playback speed */
|
|
1997
|
+
initialSpeed?: number;
|
|
1998
|
+
/** Initial volume (0-1) */
|
|
1999
|
+
initialVolume?: number;
|
|
2000
|
+
onReady?: () => void;
|
|
2001
|
+
onPlay?: () => void;
|
|
2002
|
+
onPause?: () => void;
|
|
2003
|
+
onEnded?: () => void;
|
|
2004
|
+
onTimeUpdate?: (time: number) => void;
|
|
2005
|
+
onError?: (error: Error) => void;
|
|
2006
|
+
}
|
|
2007
|
+
/**
|
|
2008
|
+
* Playback engine state
|
|
2009
|
+
*/
|
|
2010
|
+
interface PlaybackState {
|
|
2011
|
+
loading: boolean;
|
|
2012
|
+
error: string | null;
|
|
2013
|
+
recording: InternalRecording | null;
|
|
2014
|
+
isPlaying: boolean;
|
|
2015
|
+
currentTime: number;
|
|
2016
|
+
duration: number;
|
|
2017
|
+
playbackSpeed: number;
|
|
2018
|
+
volume: number;
|
|
2019
|
+
muted: boolean;
|
|
2020
|
+
code: string;
|
|
2021
|
+
language: string;
|
|
2022
|
+
terminalLines: TerminalLineData[];
|
|
2023
|
+
terminalInput: string;
|
|
2024
|
+
strokes: unknown[];
|
|
2025
|
+
excalidrawScene: ExcalidrawScene | null;
|
|
2026
|
+
activeTool: ToolType;
|
|
2027
|
+
markers: RecordingMarker[];
|
|
2028
|
+
currentFile: string | null;
|
|
2029
|
+
fileSystemSnapshot: FileSystemSnapshot;
|
|
2030
|
+
cursorPosition: CursorPosition;
|
|
2031
|
+
documentData: PlayerDocumentData | null;
|
|
2032
|
+
documentPage: number;
|
|
2033
|
+
documentZoom: number;
|
|
2034
|
+
documentScroll: ScrollPosition;
|
|
2035
|
+
mediaUrl: string | null;
|
|
2036
|
+
isInteracting: boolean;
|
|
2037
|
+
}
|
|
2038
|
+
/**
|
|
2039
|
+
* Playback controls
|
|
2040
|
+
*/
|
|
2041
|
+
interface PlaybackControls$1 {
|
|
2042
|
+
setAudioElement: (element: HTMLAudioElement | null) => void;
|
|
2043
|
+
setVolume: (volume: number) => void;
|
|
2044
|
+
setMuted: (muted: boolean) => void;
|
|
2045
|
+
play: () => void;
|
|
2046
|
+
pause: () => void;
|
|
2047
|
+
togglePlay: () => void;
|
|
2048
|
+
seekTo: (timeSeconds: number) => void;
|
|
2049
|
+
skip: (seconds: number) => void;
|
|
2050
|
+
restart: () => void;
|
|
2051
|
+
setSpeed: (speed: number) => void;
|
|
2052
|
+
enterInteractiveMode: () => void;
|
|
2053
|
+
interactiveSetCode: (code: string) => void;
|
|
2054
|
+
interactiveSetLanguage: (language: string) => void;
|
|
2055
|
+
interactiveSetTerminalInput: (input: string) => void;
|
|
2056
|
+
interactiveSetTerminalLines: (lines: TerminalLineData[]) => void;
|
|
2057
|
+
interactiveAddTerminalLines: (lines: TerminalLineData[]) => void;
|
|
2058
|
+
interactiveSetStrokes: (strokes: unknown[]) => void;
|
|
2059
|
+
interactiveSetExcalidrawScene: (scene: ExcalidrawScene | null) => void;
|
|
2060
|
+
interactiveSetDocumentPage: (page: number) => void;
|
|
2061
|
+
interactiveSetDocumentZoom: (zoom: number) => void;
|
|
2062
|
+
interactiveSetDocumentScroll: (scroll: ScrollPosition) => void;
|
|
2063
|
+
setActiveTool: (tool: ToolType) => void;
|
|
2064
|
+
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Full playback engine result
|
|
2067
|
+
*/
|
|
2068
|
+
type UsePlaybackEngineResult = PlaybackState & PlaybackControls$1;
|
|
2069
|
+
/**
|
|
2070
|
+
* Player component props
|
|
2071
|
+
*/
|
|
2072
|
+
interface PlayerProps {
|
|
2073
|
+
ctFile?: Blob;
|
|
2074
|
+
recording?: CTRecording;
|
|
2075
|
+
recordingId?: string;
|
|
2076
|
+
backendAdapter?: BackendAdapter;
|
|
2077
|
+
autoPlay?: boolean;
|
|
2078
|
+
showControls?: boolean;
|
|
2079
|
+
showTimeline?: boolean;
|
|
2080
|
+
showToolSidebar?: boolean;
|
|
2081
|
+
showAudio?: boolean;
|
|
2082
|
+
defaultSpeed?: number;
|
|
2083
|
+
allowInteraction?: boolean;
|
|
2084
|
+
theme?: 'light' | 'dark' | 'system';
|
|
2085
|
+
className?: string;
|
|
2086
|
+
onReady?: () => void;
|
|
2087
|
+
onPlay?: () => void;
|
|
2088
|
+
onPause?: () => void;
|
|
2089
|
+
onEnded?: () => void;
|
|
2090
|
+
onTimeUpdate?: (time: number) => void;
|
|
2091
|
+
onError?: (error: Error) => void;
|
|
2092
|
+
onInteractionStart?: () => void;
|
|
2093
|
+
onInteractionEnd?: (code: string) => void;
|
|
2094
|
+
}
|
|
2095
|
+
/**
|
|
2096
|
+
* Player imperative handle
|
|
2097
|
+
*/
|
|
2098
|
+
interface PlayerRef {
|
|
2099
|
+
play(): void;
|
|
2100
|
+
pause(): void;
|
|
2101
|
+
seek(timeMs: number): void;
|
|
2102
|
+
setSpeed(speed: number): void;
|
|
2103
|
+
getCurrentTime(): number;
|
|
2104
|
+
getDuration(): number;
|
|
2105
|
+
enterInteractiveMode(): void;
|
|
2106
|
+
exitInteractiveMode(): void;
|
|
2107
|
+
setActiveTool(tool: ToolType): void;
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* PlayerControls props
|
|
2111
|
+
*/
|
|
2112
|
+
interface PlayerControlsProps$1 {
|
|
2113
|
+
isPlaying: boolean;
|
|
2114
|
+
currentTime: number;
|
|
2115
|
+
duration: number;
|
|
2116
|
+
playbackSpeed: number;
|
|
2117
|
+
volume: number;
|
|
2118
|
+
muted: boolean;
|
|
2119
|
+
isInteracting: boolean;
|
|
2120
|
+
onPlay: () => void;
|
|
2121
|
+
onPause: () => void;
|
|
2122
|
+
onSeek: (time: number) => void;
|
|
2123
|
+
onSpeedChange: (speed: number) => void;
|
|
2124
|
+
onVolumeChange: (volume: number) => void;
|
|
2125
|
+
onMuteToggle: () => void;
|
|
2126
|
+
className?: string;
|
|
2127
|
+
}
|
|
2128
|
+
/**
|
|
2129
|
+
* Timeline props
|
|
2130
|
+
*/
|
|
2131
|
+
interface TimelineProps$1 {
|
|
2132
|
+
currentTime: number;
|
|
2133
|
+
duration: number;
|
|
2134
|
+
markers?: RecordingMarker[];
|
|
2135
|
+
buffered?: Array<{
|
|
2136
|
+
start: number;
|
|
2137
|
+
end: number;
|
|
2138
|
+
}>;
|
|
2139
|
+
onSeek: (time: number) => void;
|
|
2140
|
+
onMarkerClick?: (marker: RecordingMarker) => void;
|
|
2141
|
+
className?: string;
|
|
2142
|
+
}
|
|
2143
|
+
/**
|
|
2144
|
+
* Speed selector props
|
|
2145
|
+
*/
|
|
2146
|
+
interface SpeedSelectorProps {
|
|
2147
|
+
speed: number;
|
|
2148
|
+
onSpeedChange: (speed: number) => void;
|
|
2149
|
+
speeds?: number[];
|
|
2150
|
+
className?: string;
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Volume control props
|
|
2154
|
+
*/
|
|
2155
|
+
interface VolumeControlProps {
|
|
2156
|
+
volume: number;
|
|
2157
|
+
muted: boolean;
|
|
2158
|
+
onVolumeChange: (volume: number) => void;
|
|
2159
|
+
onMuteToggle: () => void;
|
|
2160
|
+
className?: string;
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
/**
|
|
2164
|
+
* usePlayback Hook
|
|
2165
|
+
*
|
|
2166
|
+
* Core playback engine hook using @ct-courses/core StateEngine.
|
|
2167
|
+
* Supports audio synchronization as master clock (no video/webcam).
|
|
2168
|
+
*
|
|
2169
|
+
* CRITICAL SYNC ARCHITECTURE:
|
|
2170
|
+
* - AUDIO is the MASTER CLOCK when media exists
|
|
2171
|
+
* - Events are applied based on audio.currentTime
|
|
2172
|
+
* - Without media, use requestAnimationFrame timing
|
|
2173
|
+
*/
|
|
2174
|
+
|
|
2175
|
+
/**
|
|
2176
|
+
* Playback state returned by the hook
|
|
2177
|
+
*/
|
|
2178
|
+
interface PlaybackHookState {
|
|
2179
|
+
/** Current playback state */
|
|
2180
|
+
state: PlaybackState$1;
|
|
2181
|
+
/** Current time in milliseconds */
|
|
2182
|
+
currentTime: number;
|
|
2183
|
+
/** Total duration in milliseconds */
|
|
2184
|
+
duration: number;
|
|
2185
|
+
/** Whether currently playing */
|
|
2186
|
+
isPlaying: boolean;
|
|
2187
|
+
/** Current playback speed */
|
|
2188
|
+
speed: number;
|
|
2189
|
+
/** Whether recording is loaded */
|
|
2190
|
+
isLoaded: boolean;
|
|
2191
|
+
/** Volume level (0-1) */
|
|
2192
|
+
volume: number;
|
|
2193
|
+
/** Whether audio is muted */
|
|
2194
|
+
muted: boolean;
|
|
2195
|
+
/** Whether user is interacting (paused with edits) */
|
|
2196
|
+
isInteracting: boolean;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* Playback controls
|
|
2200
|
+
*/
|
|
2201
|
+
interface PlaybackControls {
|
|
2202
|
+
play(): void;
|
|
2203
|
+
pause(): void;
|
|
2204
|
+
seek(timeMs: number): void;
|
|
2205
|
+
setSpeed(speed: number): void;
|
|
2206
|
+
setVolume(volume: number): void;
|
|
2207
|
+
setMuted(muted: boolean): void;
|
|
2208
|
+
setAudioElement(audio: HTMLAudioElement | null): void;
|
|
2209
|
+
enterInteractiveMode(): void;
|
|
2210
|
+
exitInteractiveMode(): void;
|
|
2211
|
+
setInteractiveCode(code: string): void;
|
|
2212
|
+
setInteractiveLanguage(language: string): void;
|
|
2213
|
+
setInteractiveTerminalLines(lines: PlaybackState$1['terminalLines']): void;
|
|
2214
|
+
addInteractiveTerminalLines(lines: PlaybackState$1['terminalLines']): void;
|
|
2215
|
+
setInteractiveExcalidrawScene(scene: PlaybackState$1['excalidrawScene']): void;
|
|
2216
|
+
setInteractiveDocumentPage(page: number): void;
|
|
2217
|
+
setInteractiveDocumentZoom(zoom: number): void;
|
|
2218
|
+
setInteractiveDocumentScroll(scroll: {
|
|
2219
|
+
x: number;
|
|
2220
|
+
y: number;
|
|
2221
|
+
}): void;
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Options for usePlayback hook
|
|
2225
|
+
*/
|
|
2226
|
+
interface UsePlaybackOptions {
|
|
2227
|
+
recording: CTRecording | null;
|
|
2228
|
+
autoPlay?: boolean;
|
|
2229
|
+
defaultSpeed?: number;
|
|
2230
|
+
defaultVolume?: number;
|
|
2231
|
+
onPlay?: () => void;
|
|
2232
|
+
onPause?: () => void;
|
|
2233
|
+
onProgress?: (time: number, duration: number) => void;
|
|
2234
|
+
onComplete?: () => void;
|
|
2235
|
+
onError?: (error: Error) => void;
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Hook for managing playback of a CT recording
|
|
2239
|
+
* Supports audio synchronization as master clock
|
|
2240
|
+
*/
|
|
2241
|
+
declare function usePlayback(options: UsePlaybackOptions): PlaybackHookState & {
|
|
2242
|
+
controls: PlaybackControls;
|
|
2243
|
+
};
|
|
2244
|
+
|
|
2245
|
+
/**
|
|
2246
|
+
* useStreamingPlayback Hook
|
|
2247
|
+
*
|
|
2248
|
+
* Streaming playback engine hook for large recordings.
|
|
2249
|
+
* Supports lazy chunk loading, adaptive buffering, and efficient seeking.
|
|
2250
|
+
*
|
|
2251
|
+
* @packageDocumentation
|
|
2252
|
+
*/
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* Buffer health status
|
|
2256
|
+
*/
|
|
2257
|
+
interface BufferHealth {
|
|
2258
|
+
status: 'healthy' | 'buffering' | 'critical' | 'empty';
|
|
2259
|
+
loadedChunks: number;
|
|
2260
|
+
totalChunks: number;
|
|
2261
|
+
currentChunk: number;
|
|
2262
|
+
bufferedTimeMs: number;
|
|
2263
|
+
percentLoaded: number;
|
|
2264
|
+
}
|
|
2265
|
+
/**
|
|
2266
|
+
* Streaming metrics for performance monitoring
|
|
2267
|
+
*/
|
|
2268
|
+
interface StreamingMetrics {
|
|
2269
|
+
avgChunkLoadTimeMs: number;
|
|
2270
|
+
chunkCacheHitRate: number;
|
|
2271
|
+
bufferUnderrunCount: number;
|
|
2272
|
+
avgBufferHealthPercent: number;
|
|
2273
|
+
avgSeekTimeMs: number;
|
|
2274
|
+
seekCacheHitRate: number;
|
|
2275
|
+
peakChunksCached: number;
|
|
2276
|
+
totalBytesLoaded: number;
|
|
2277
|
+
}
|
|
2278
|
+
/**
|
|
2279
|
+
* Streaming playback state
|
|
2280
|
+
*/
|
|
2281
|
+
interface StreamingPlaybackState {
|
|
2282
|
+
state: PlaybackState$1;
|
|
2283
|
+
currentTime: number;
|
|
2284
|
+
duration: number;
|
|
2285
|
+
isPlaying: boolean;
|
|
2286
|
+
speed: number;
|
|
2287
|
+
isLoaded: boolean;
|
|
2288
|
+
isBuffering: boolean;
|
|
2289
|
+
bufferHealth: BufferHealth;
|
|
2290
|
+
isStreaming: boolean;
|
|
2291
|
+
metrics: StreamingMetrics;
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Streaming playback controls
|
|
2295
|
+
*/
|
|
2296
|
+
interface StreamingPlaybackControls {
|
|
2297
|
+
play(): void;
|
|
2298
|
+
pause(): void;
|
|
2299
|
+
seek(timeMs: number): Promise<void>;
|
|
2300
|
+
setSpeed(speed: number): void;
|
|
2301
|
+
preloadRange(startMs: number, endMs: number): Promise<void>;
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* Options for useStreamingPlayback hook
|
|
2305
|
+
*/
|
|
2306
|
+
interface UseStreamingPlaybackOptions {
|
|
2307
|
+
recording: CTRecording | null;
|
|
2308
|
+
chunkIndex?: CTEventChunkIndex | null;
|
|
2309
|
+
loadChunk?: (chunkNumber: number) => Promise<ChunkLoadResult>;
|
|
2310
|
+
autoPlay?: boolean;
|
|
2311
|
+
defaultSpeed?: number;
|
|
2312
|
+
bufferAhead?: number;
|
|
2313
|
+
bufferBehind?: number;
|
|
2314
|
+
maxChunksInMemory?: number;
|
|
2315
|
+
onPlay?: () => void;
|
|
2316
|
+
onPause?: () => void;
|
|
2317
|
+
onProgress?: (time: number, duration: number) => void;
|
|
2318
|
+
onComplete?: () => void;
|
|
2319
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
2320
|
+
onChunkLoad?: (chunkNumber: number) => void;
|
|
2321
|
+
onError?: (error: Error) => void;
|
|
2322
|
+
}
|
|
2323
|
+
/**
|
|
2324
|
+
* Hook for streaming playback of large CT recordings
|
|
2325
|
+
*/
|
|
2326
|
+
declare function useStreamingPlayback(options: UseStreamingPlaybackOptions): StreamingPlaybackState & {
|
|
2327
|
+
controls: StreamingPlaybackControls;
|
|
2328
|
+
};
|
|
2329
|
+
|
|
2330
|
+
/**
|
|
2331
|
+
* useRemoteStreamingPlayback Hook
|
|
2332
|
+
*
|
|
2333
|
+
* React hook for streaming playback from a remote CDN/R2 storage.
|
|
2334
|
+
* Handles HLS audio streaming and lazy-loading of event chunks.
|
|
2335
|
+
*
|
|
2336
|
+
* This is designed for the streaming service architecture where:
|
|
2337
|
+
* - Audio is served via HLS adaptive bitrate streaming
|
|
2338
|
+
* - Events are stored in gzip-compressed chunks on CDN
|
|
2339
|
+
* - The manifest provides URLs for all resources
|
|
2340
|
+
*
|
|
2341
|
+
* @packageDocumentation
|
|
2342
|
+
*/
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* Hook for remote streaming playback from CDN
|
|
2346
|
+
*/
|
|
2347
|
+
declare function useRemoteStreamingPlayback(options: UseRemoteStreamingPlaybackOptions): RemoteStreamingPlaybackState & {
|
|
2348
|
+
controls: RemoteStreamingPlaybackControls;
|
|
2349
|
+
};
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* useInteractiveMode Hook
|
|
2353
|
+
*
|
|
2354
|
+
* Manages interactive mode where users can edit code while paused.
|
|
2355
|
+
*/
|
|
2356
|
+
|
|
2357
|
+
/**
|
|
2358
|
+
* Interactive mode state
|
|
2359
|
+
*/
|
|
2360
|
+
interface InteractiveModeState {
|
|
2361
|
+
/** Whether interactive mode is active */
|
|
2362
|
+
isActive: boolean;
|
|
2363
|
+
/** Modified code (user edits) */
|
|
2364
|
+
modifiedCode: string;
|
|
2365
|
+
/** Whether code has been modified from original */
|
|
2366
|
+
hasChanges: boolean;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* Interactive mode controls
|
|
2370
|
+
*/
|
|
2371
|
+
interface InteractiveModeControls {
|
|
2372
|
+
/** Enter interactive mode */
|
|
2373
|
+
enter(): void;
|
|
2374
|
+
/** Exit interactive mode */
|
|
2375
|
+
exit(): void;
|
|
2376
|
+
/** Update the modified code */
|
|
2377
|
+
updateCode(code: string): void;
|
|
2378
|
+
/** Reset code to original */
|
|
2379
|
+
resetCode(): void;
|
|
2380
|
+
/** Run the modified code */
|
|
2381
|
+
runCode(): Promise<string>;
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Options for useInteractiveMode hook
|
|
2385
|
+
*/
|
|
2386
|
+
interface UseInteractiveModeOptions {
|
|
2387
|
+
/** Current playback state */
|
|
2388
|
+
playbackState: PlaybackState$1;
|
|
2389
|
+
/** Callback when entering interactive mode */
|
|
2390
|
+
onEnter?: () => void;
|
|
2391
|
+
/** Callback when exiting interactive mode */
|
|
2392
|
+
onExit?: (code: string, hasChanges: boolean) => void;
|
|
2393
|
+
/** Code execution function */
|
|
2394
|
+
executeCode?: (code: string, language: string) => Promise<string>;
|
|
2395
|
+
}
|
|
2396
|
+
/**
|
|
2397
|
+
* Hook for managing interactive mode
|
|
2398
|
+
*/
|
|
2399
|
+
declare function useInteractiveMode(options: UseInteractiveModeOptions): InteractiveModeState & {
|
|
2400
|
+
controls: InteractiveModeControls;
|
|
2401
|
+
};
|
|
2402
|
+
|
|
2403
|
+
/**
|
|
2404
|
+
* useAudioSync Hook
|
|
2405
|
+
*
|
|
2406
|
+
* Synchronizes audio playback with event state.
|
|
2407
|
+
* Handles audio/event drift and maintains sync within tolerance.
|
|
2408
|
+
*
|
|
2409
|
+
* @packageDocumentation
|
|
2410
|
+
*/
|
|
2411
|
+
/**
|
|
2412
|
+
* Audio sync state
|
|
2413
|
+
*/
|
|
2414
|
+
interface AudioSyncState {
|
|
2415
|
+
/** Current audio time in ms */
|
|
2416
|
+
audioTime: number;
|
|
2417
|
+
/** Current event time in ms */
|
|
2418
|
+
eventTime: number;
|
|
2419
|
+
/** Drift between audio and events (ms) */
|
|
2420
|
+
drift: number;
|
|
2421
|
+
/** Is sync healthy (within tolerance) */
|
|
2422
|
+
isSynced: boolean;
|
|
2423
|
+
/** Is audio loaded */
|
|
2424
|
+
isAudioReady: boolean;
|
|
2425
|
+
}
|
|
2426
|
+
/**
|
|
2427
|
+
* Audio sync controls
|
|
2428
|
+
*/
|
|
2429
|
+
interface AudioSyncControls {
|
|
2430
|
+
/** Sync events to audio time */
|
|
2431
|
+
syncToAudio(): void;
|
|
2432
|
+
/** Sync audio to event time */
|
|
2433
|
+
syncAudioTo(timeMs: number): void;
|
|
2434
|
+
/** Force resync */
|
|
2435
|
+
forceResync(): void;
|
|
2436
|
+
}
|
|
2437
|
+
/**
|
|
2438
|
+
* Options for useAudioSync hook
|
|
2439
|
+
*/
|
|
2440
|
+
interface UseAudioSyncOptions {
|
|
2441
|
+
/** Reference to audio element */
|
|
2442
|
+
audioRef: React.RefObject<HTMLAudioElement>;
|
|
2443
|
+
/** Current event time */
|
|
2444
|
+
eventTime: number;
|
|
2445
|
+
/** Is currently playing */
|
|
2446
|
+
isPlaying: boolean;
|
|
2447
|
+
/** Playback speed */
|
|
2448
|
+
speed: number;
|
|
2449
|
+
/** Maximum allowed drift in ms before resync (default: 500ms) */
|
|
2450
|
+
driftTolerance?: number;
|
|
2451
|
+
/** Callback when audio time updates */
|
|
2452
|
+
onAudioTimeUpdate?: (timeMs: number) => void;
|
|
2453
|
+
/** Callback when sync is lost */
|
|
2454
|
+
onSyncLost?: (drift: number) => void;
|
|
2455
|
+
/** Callback when sync is restored */
|
|
2456
|
+
onSyncRestored?: () => void;
|
|
2457
|
+
}
|
|
2458
|
+
/**
|
|
2459
|
+
* Hook for synchronizing audio playback with event state
|
|
2460
|
+
*/
|
|
2461
|
+
declare function useAudioSync(options: UseAudioSyncOptions): AudioSyncState & {
|
|
2462
|
+
controls: AudioSyncControls;
|
|
2463
|
+
};
|
|
2464
|
+
/**
|
|
2465
|
+
* Hook for managing audio element lifecycle
|
|
2466
|
+
*/
|
|
2467
|
+
declare function useAudioElement(src: string | null): {
|
|
2468
|
+
audioRef: react.MutableRefObject<HTMLAudioElement | null>;
|
|
2469
|
+
isLoaded: boolean;
|
|
2470
|
+
error: Error | null;
|
|
2471
|
+
duration: number;
|
|
2472
|
+
};
|
|
2473
|
+
/** @deprecated Use AudioSyncState instead */
|
|
2474
|
+
type VideoSyncState = AudioSyncState;
|
|
2475
|
+
/** @deprecated Use AudioSyncControls instead */
|
|
2476
|
+
type VideoSyncControls = AudioSyncControls;
|
|
2477
|
+
/** @deprecated Use UseAudioSyncOptions instead */
|
|
2478
|
+
type UseVideoSyncOptions = UseAudioSyncOptions;
|
|
2479
|
+
/** @deprecated Use useAudioSync instead */
|
|
2480
|
+
declare const useVideoSync: typeof useAudioSync;
|
|
2481
|
+
/** @deprecated Use useAudioElement instead */
|
|
2482
|
+
declare const useVideoElement: typeof useAudioElement;
|
|
2483
|
+
|
|
2484
|
+
/**
|
|
2485
|
+
* HLS Loader Utility
|
|
2486
|
+
*
|
|
2487
|
+
* Wrapper around HLS.js for adaptive audio streaming.
|
|
2488
|
+
* Handles both native HLS support (Safari) and HLS.js polyfill.
|
|
2489
|
+
*
|
|
2490
|
+
* @packageDocumentation
|
|
2491
|
+
*/
|
|
2492
|
+
|
|
2493
|
+
interface HLSPlayerInstance {
|
|
2494
|
+
/** The HLS.js instance (null if using native HLS) */
|
|
2495
|
+
hls: Hls | null;
|
|
2496
|
+
/** The audio element */
|
|
2497
|
+
audioElement: HTMLAudioElement;
|
|
2498
|
+
/** Whether this is using native HLS (Safari) */
|
|
2499
|
+
isNative: boolean;
|
|
2500
|
+
/** Destroy and cleanup */
|
|
2501
|
+
destroy: () => void;
|
|
2502
|
+
/** Get current quality levels */
|
|
2503
|
+
getQualityLevels: () => HLSQualityLevel[];
|
|
2504
|
+
/** Set quality level (-1 for auto) */
|
|
2505
|
+
setQualityLevel: (index: number) => void;
|
|
2506
|
+
/** Get current quality level */
|
|
2507
|
+
getCurrentQualityLevel: () => number;
|
|
2508
|
+
/** Add event listener for HLS events */
|
|
2509
|
+
on: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
2510
|
+
/** Remove event listener */
|
|
2511
|
+
off: (event: string, callback: (...args: unknown[]) => void) => void;
|
|
2512
|
+
}
|
|
2513
|
+
interface HLSEvents {
|
|
2514
|
+
/** Manifest has been parsed and is ready */
|
|
2515
|
+
MANIFEST_PARSED: 'hlsManifestParsed';
|
|
2516
|
+
/** Level has switched */
|
|
2517
|
+
LEVEL_SWITCHED: 'hlsLevelSwitched';
|
|
2518
|
+
/** Error occurred */
|
|
2519
|
+
ERROR: 'hlsError';
|
|
2520
|
+
/** Buffering started */
|
|
2521
|
+
BUFFER_CREATED: 'hlsBufferCreated';
|
|
2522
|
+
/** Fragment loaded */
|
|
2523
|
+
FRAG_LOADED: 'hlsFragLoaded';
|
|
2524
|
+
}
|
|
2525
|
+
declare const HLS_EVENTS: HLSEvents;
|
|
2526
|
+
/**
|
|
2527
|
+
* Check if the browser supports HLS.js
|
|
2528
|
+
*/
|
|
2529
|
+
declare function isHlsSupported(): boolean;
|
|
2530
|
+
/**
|
|
2531
|
+
* Check if the browser has native HLS support (Safari)
|
|
2532
|
+
*/
|
|
2533
|
+
declare function hasNativeHlsSupport(): boolean;
|
|
2534
|
+
/**
|
|
2535
|
+
* Check if any form of HLS playback is possible
|
|
2536
|
+
*/
|
|
2537
|
+
declare function canPlayHls(): boolean;
|
|
2538
|
+
/**
|
|
2539
|
+
* Create an HLS player instance
|
|
2540
|
+
*
|
|
2541
|
+
* @param audioElement - The audio element to attach to
|
|
2542
|
+
* @param masterPlaylistUrl - URL to the HLS master playlist
|
|
2543
|
+
* @param config - Optional configuration
|
|
2544
|
+
* @returns HLS player instance
|
|
2545
|
+
* @throws Error if HLS is not supported
|
|
2546
|
+
*/
|
|
2547
|
+
declare function createHlsPlayer(audioElement: HTMLAudioElement, masterPlaylistUrl: string, config?: HLSPlayerConfig): HLSPlayerInstance;
|
|
2548
|
+
/**
|
|
2549
|
+
* Destroy an HLS player instance safely
|
|
2550
|
+
*/
|
|
2551
|
+
declare function destroyHlsPlayer(player: HLSPlayerInstance | null): void;
|
|
2552
|
+
|
|
2553
|
+
interface Marker$1 {
|
|
2554
|
+
time: number;
|
|
2555
|
+
label?: string;
|
|
2556
|
+
type?: 'chapter' | 'highlight';
|
|
2557
|
+
}
|
|
2558
|
+
interface PlayerControlsProps {
|
|
2559
|
+
isPlaying: boolean;
|
|
2560
|
+
currentTime: number;
|
|
2561
|
+
duration: number;
|
|
2562
|
+
volume: number;
|
|
2563
|
+
muted: boolean;
|
|
2564
|
+
playbackSpeed: number;
|
|
2565
|
+
markers?: Marker$1[];
|
|
2566
|
+
isFullscreen?: boolean;
|
|
2567
|
+
onPlayPause: () => void;
|
|
2568
|
+
onSeek: (timeMs: number) => void;
|
|
2569
|
+
onSeekStart?: () => void;
|
|
2570
|
+
onSeekEnd?: () => void;
|
|
2571
|
+
onSkip: (seconds: number) => void;
|
|
2572
|
+
onRestart: () => void;
|
|
2573
|
+
onVolumeChange: (volume: number) => void;
|
|
2574
|
+
onMuteToggle: () => void;
|
|
2575
|
+
onSpeedChange: (speed: number) => void;
|
|
2576
|
+
onFullscreenToggle?: () => void;
|
|
2577
|
+
showMarkers?: boolean;
|
|
2578
|
+
compact?: boolean;
|
|
2579
|
+
containerRef?: react__default.RefObject<HTMLElement>;
|
|
2580
|
+
}
|
|
2581
|
+
declare function PlayerControls({ isPlaying, currentTime, duration, volume, muted, playbackSpeed, markers, isFullscreen, onPlayPause, onSeek, onSeekStart, onSeekEnd, onSkip, onRestart, onVolumeChange, onMuteToggle, onSpeedChange, onFullscreenToggle, showMarkers, compact, }: PlayerControlsProps): react_jsx_runtime.JSX.Element;
|
|
2582
|
+
|
|
2583
|
+
/**
|
|
2584
|
+
* Timeline Component
|
|
2585
|
+
*
|
|
2586
|
+
* Enhanced visual timeline with markers, chunk indicators, and chapter navigation.
|
|
2587
|
+
* Supports streaming playback visualization and buffer status.
|
|
2588
|
+
*
|
|
2589
|
+
* @packageDocumentation
|
|
2590
|
+
*/
|
|
2591
|
+
/**
|
|
2592
|
+
* Timeline marker
|
|
2593
|
+
*/
|
|
2594
|
+
interface Marker {
|
|
2595
|
+
id: string;
|
|
2596
|
+
time: number;
|
|
2597
|
+
label: string;
|
|
2598
|
+
type: 'chapter' | 'highlight' | 'quiz' | 'note' | 'bookmark' | 'error';
|
|
2599
|
+
description?: string;
|
|
2600
|
+
}
|
|
2601
|
+
/**
|
|
2602
|
+
* Chunk info for streaming visualization
|
|
2603
|
+
*/
|
|
2604
|
+
interface ChunkInfo {
|
|
2605
|
+
id: number;
|
|
2606
|
+
startTime: number;
|
|
2607
|
+
endTime: number;
|
|
2608
|
+
loaded: boolean;
|
|
2609
|
+
loading?: boolean;
|
|
2610
|
+
size?: number;
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* Buffered range
|
|
2614
|
+
*/
|
|
2615
|
+
interface BufferedRange {
|
|
2616
|
+
start: number;
|
|
2617
|
+
end: number;
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Props for Timeline component
|
|
2621
|
+
*/
|
|
2622
|
+
interface TimelineProps {
|
|
2623
|
+
/** Current time in ms */
|
|
2624
|
+
currentTime: number;
|
|
2625
|
+
/** Total duration in ms */
|
|
2626
|
+
duration: number;
|
|
2627
|
+
/** Timeline markers */
|
|
2628
|
+
markers?: Marker[];
|
|
2629
|
+
/** Chunk info for streaming visualization */
|
|
2630
|
+
chunks?: ChunkInfo[];
|
|
2631
|
+
/** Buffered ranges */
|
|
2632
|
+
buffered?: BufferedRange[];
|
|
2633
|
+
/** Seek callback */
|
|
2634
|
+
onSeek: (timeMs: number) => void;
|
|
2635
|
+
/** Marker click callback */
|
|
2636
|
+
onMarkerClick?: (marker: Marker) => void;
|
|
2637
|
+
/** Theme */
|
|
2638
|
+
theme?: 'light' | 'dark';
|
|
2639
|
+
/** Show chapter labels */
|
|
2640
|
+
showChapterLabels?: boolean;
|
|
2641
|
+
/** Show buffer status */
|
|
2642
|
+
showBufferStatus?: boolean;
|
|
2643
|
+
/** Show chunk indicators */
|
|
2644
|
+
showChunkIndicators?: boolean;
|
|
2645
|
+
/** Compact mode */
|
|
2646
|
+
compact?: boolean;
|
|
2647
|
+
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Timeline component with markers and chunk indicators
|
|
2650
|
+
*/
|
|
2651
|
+
declare function Timeline({ currentTime, duration, markers, chunks, buffered, onSeek, onMarkerClick, theme, showChapterLabels, showBufferStatus, showChunkIndicators, compact, }: TimelineProps): JSX.Element;
|
|
2652
|
+
|
|
2653
|
+
/**
|
|
2654
|
+
* AudioOverlay Component
|
|
2655
|
+
*
|
|
2656
|
+
* Audio playback overlay with controls and visualization.
|
|
2657
|
+
* Replaces WebcamOverlay for audio-only playback.
|
|
2658
|
+
*
|
|
2659
|
+
* @packageDocumentation
|
|
2660
|
+
*/
|
|
2661
|
+
/**
|
|
2662
|
+
* Overlay position presets
|
|
2663
|
+
*/
|
|
2664
|
+
type OverlayPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'custom';
|
|
2665
|
+
/**
|
|
2666
|
+
* Custom position
|
|
2667
|
+
*/
|
|
2668
|
+
interface CustomPosition {
|
|
2669
|
+
x: number;
|
|
2670
|
+
y: number;
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Props for AudioOverlay component
|
|
2674
|
+
*/
|
|
2675
|
+
interface AudioOverlayProps {
|
|
2676
|
+
/** Audio source URL */
|
|
2677
|
+
src: string | null;
|
|
2678
|
+
/** Current time to sync to (ms) */
|
|
2679
|
+
currentTime: number;
|
|
2680
|
+
/** Whether audio should be playing */
|
|
2681
|
+
isPlaying: boolean;
|
|
2682
|
+
/** Playback rate */
|
|
2683
|
+
playbackRate?: number;
|
|
2684
|
+
/** Overlay position preset */
|
|
2685
|
+
position?: OverlayPosition;
|
|
2686
|
+
/** Custom position (when position='custom') */
|
|
2687
|
+
customPosition?: CustomPosition;
|
|
2688
|
+
/** Whether to show the overlay */
|
|
2689
|
+
visible?: boolean;
|
|
2690
|
+
/** Enable dragging */
|
|
2691
|
+
draggable?: boolean;
|
|
2692
|
+
/** Show controls on hover */
|
|
2693
|
+
showControls?: boolean;
|
|
2694
|
+
/** Opacity (0-1) */
|
|
2695
|
+
opacity?: number;
|
|
2696
|
+
/** On position change callback */
|
|
2697
|
+
onPositionChange?: (position: CustomPosition) => void;
|
|
2698
|
+
/** Callback to pass audio element reference for sync */
|
|
2699
|
+
onAudioRef?: (audio: HTMLAudioElement | null) => void;
|
|
2700
|
+
/** Whether audio is muted */
|
|
2701
|
+
muted?: boolean;
|
|
2702
|
+
/** Volume level (0-1) */
|
|
2703
|
+
volume?: number;
|
|
2704
|
+
/** Sync tolerance in ms */
|
|
2705
|
+
syncTolerance?: number;
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Audio overlay for instructor audio
|
|
2709
|
+
*
|
|
2710
|
+
* NOTE: Audio playback is now controlled entirely by parent (usePlayback).
|
|
2711
|
+
* Props like currentTime, isPlaying, playbackRate, muted, volume, syncTolerance
|
|
2712
|
+
* are kept for backward compatibility but are no longer used.
|
|
2713
|
+
*/
|
|
2714
|
+
declare function AudioOverlay({ src, currentTime: _currentTime, isPlaying: _isPlaying, playbackRate: _playbackRate, position, customPosition, visible, draggable, showControls, opacity, onPositionChange, onAudioRef, muted: _muted, volume: _volume, syncTolerance: _syncTolerance, }: AudioOverlayProps): JSX.Element | null;
|
|
2715
|
+
|
|
2716
|
+
export { AudioOverlay, type AudioOverlayProps, type AudioQuality, type AudioSyncControls, type AudioSyncState, type BackendAdapter, type BufferHealth, type CTEvent, type CTManifest, type CTMarker, type CTReadOptions, type CTRecording, CoursePlayer, type CoursePlayerProps, type CoursePlayerRef, type CursorPosition, type CustomPosition, DocumentPanel, type DocumentPanelProps, FileExplorer, type FileExplorerProps, type FileSystemSnapshot, type HLSPlayerInstance, HLS_EVENTS, IDEPanel, type IDEPanelProps, type InternalEvent, type InternalRecording, type OverlayPosition, type PanelMode, type PlaybackControls$1 as PlaybackControls, type PlaybackEngineConfig, type PlaybackHookState, type PlaybackState, PlayerControls, type PlayerControlsProps$1 as PlayerControlsProps, type PlayerProps, type PlayerRef, type RecordingMarker, type RemoteBufferHealth, type RemoteStreamingMetrics, type RemoteStreamingPlaybackControls, type RemoteStreamingPlaybackState, type SpeedSelectorProps, type StreamingAudioConfig, type StreamingConfig, StreamingCoursePlayer, type StreamingCoursePlayerProps, type StreamingCoursePlayerRef, type StreamingEventsConfig, type StreamingManifest, type StreamingMetrics, type StreamingPlaybackControls, type StreamingPlaybackState, type StreamingRecordingInfo, Terminal, type TerminalLineData, type TerminalProps, Timeline, type TimelineProps$1 as TimelineProps, ToolSidebar, type ToolSidebarProps, type ToolType, type UseAudioSyncOptions, type UsePlaybackEngineResult, type UsePlaybackOptions, type UseRemoteStreamingPlaybackOptions, type UseVideoSyncOptions, type ValidationResult$1 as ValidationResult, type VideoSyncControls, type VideoSyncState, type VolumeControlProps, AudioOverlay as WebcamOverlay, type AudioOverlayProps as WebcamOverlayProps, WhiteboardPanel, type WhiteboardPanelProps, type WriteCTOptions, canPlayHls, createEmptyRecording, createHlsPlayer, destroyHlsPlayer, hasNativeHlsSupport, isHlsSupported, readCTFile, readCTManifest, useAudioElement, useAudioSync, useInteractiveMode, usePlayback, usePlayback as usePlaybackEngine, useRemoteStreamingPlayback, useStreamingPlayback, useVideoElement, useVideoSync, validateCTFile, writeCTFile };
|