@ct-player/embed 1.2.1 → 1.2.3
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/dist/index.cjs +110 -118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +874 -677
- package/dist/index.d.ts +874 -677
- package/dist/index.js +110 -118
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +2 -3
- package/README.md +0 -284
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import Hls from 'hls.js';
|
|
1
2
|
import * as React__default from 'react';
|
|
2
3
|
import React__default__default from 'react';
|
|
3
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
-
import Hls from 'hls.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* CT Format Event Types
|
|
@@ -11,7 +11,7 @@ import Hls from 'hls.js';
|
|
|
11
11
|
/**
|
|
12
12
|
* All possible event types in the .ct format
|
|
13
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' | 'excalidraw_pointer' | 'overlay_scene' | 'overlay_clear' | 'overlay_pointer' | 'document_load' | 'document_page' | 'document_zoom' | 'document_scroll' | 'document_clear' | 'file_create' | 'file_update' | 'file_delete' | 'file_rename' | 'file_switch' | 'folder_create' | 'folder_delete' | 'file_select' | 'file_change' | 'file_creation_start' | 'file_creation_input' | 'file_creation_cancel' | 'file_rename_start' | 'file_rename_input' | 'file_rename_cancel' | 'folder_toggle' | 'cursor_move' | 'cursor_click' | 'cursor_enter' | 'cursor_leave' | 'file_hover' | 'text_select' | 'editor_cursor' | 'tool_switch' | 'tool' | 'marker' | 'webcam_state' | 'content_dimensions';
|
|
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' | 'excalidraw_pointer' | 'overlay_scene' | 'overlay_clear' | 'overlay_pointer' | 'document_load' | 'document_page' | 'document_zoom' | 'document_scroll' | 'document_clear' | 'db_seed' | 'db_query' | 'db_result' | 'db_error' | 'db_schema_change' | 'db_snapshot' | 'file_create' | 'file_update' | 'file_delete' | 'file_rename' | 'file_switch' | 'folder_create' | 'folder_delete' | 'file_select' | 'file_change' | 'file_creation_start' | 'file_creation_input' | 'file_creation_cancel' | 'file_rename_start' | 'file_rename_input' | 'file_rename_cancel' | 'folder_toggle' | 'cursor_move' | 'cursor_click' | 'cursor_enter' | 'cursor_leave' | 'file_hover' | 'text_select' | 'editor_cursor' | 'editor_scroll' | 'tool_switch' | 'tool' | 'marker' | 'webcam_state' | 'content_dimensions' | 'panel_resize';
|
|
15
15
|
/**
|
|
16
16
|
* Base event structure with generic type parameter
|
|
17
17
|
*/
|
|
@@ -27,7 +27,7 @@ interface CTEventBase<T extends CTEventType = CTEventType> {
|
|
|
27
27
|
* Discriminated union of all event types.
|
|
28
28
|
* This enables proper type narrowing in switch statements.
|
|
29
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<'excalidraw_pointer'> | CTEventBase<'overlay_scene'> | CTEventBase<'overlay_clear'> | CTEventBase<'overlay_pointer'> | 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<'folder_create'> | CTEventBase<'folder_delete'> | CTEventBase<'file_select'> | CTEventBase<'file_change'> | CTEventBase<'file_creation_start'> | CTEventBase<'file_creation_input'> | CTEventBase<'file_creation_cancel'> | CTEventBase<'file_rename_start'> | CTEventBase<'file_rename_input'> | CTEventBase<'file_rename_cancel'> | CTEventBase<'folder_toggle'> | CTEventBase<'cursor_move'> | CTEventBase<'cursor_click'> | CTEventBase<'cursor_enter'> | CTEventBase<'cursor_leave'> | CTEventBase<'file_hover'> | CTEventBase<'text_select'> | CTEventBase<'editor_cursor'> | CTEventBase<'tool_switch'> | CTEventBase<'tool'> | CTEventBase<'marker'> | CTEventBase<'webcam_state'> | CTEventBase<'content_dimensions'>;
|
|
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<'excalidraw_pointer'> | CTEventBase<'overlay_scene'> | CTEventBase<'overlay_clear'> | CTEventBase<'overlay_pointer'> | CTEventBase<'document_load'> | CTEventBase<'document_page'> | CTEventBase<'document_zoom'> | CTEventBase<'document_scroll'> | CTEventBase<'document_clear'> | CTEventBase<'db_seed'> | CTEventBase<'db_query'> | CTEventBase<'db_result'> | CTEventBase<'db_error'> | CTEventBase<'db_schema_change'> | CTEventBase<'db_snapshot'> | CTEventBase<'file_create'> | CTEventBase<'file_update'> | CTEventBase<'file_delete'> | CTEventBase<'file_rename'> | CTEventBase<'file_switch'> | CTEventBase<'folder_create'> | CTEventBase<'folder_delete'> | CTEventBase<'file_select'> | CTEventBase<'file_change'> | CTEventBase<'file_creation_start'> | CTEventBase<'file_creation_input'> | CTEventBase<'file_creation_cancel'> | CTEventBase<'file_rename_start'> | CTEventBase<'file_rename_input'> | CTEventBase<'file_rename_cancel'> | CTEventBase<'folder_toggle'> | CTEventBase<'cursor_move'> | CTEventBase<'cursor_click'> | CTEventBase<'cursor_enter'> | CTEventBase<'cursor_leave'> | CTEventBase<'file_hover'> | CTEventBase<'text_select'> | CTEventBase<'editor_cursor'> | CTEventBase<'editor_scroll'> | CTEventBase<'tool_switch'> | CTEventBase<'tool'> | CTEventBase<'marker'> | CTEventBase<'webcam_state'> | CTEventBase<'content_dimensions'> | CTEventBase<'panel_resize'>;
|
|
31
31
|
/**
|
|
32
32
|
* Mapping of event types to their data structures
|
|
33
33
|
*/
|
|
@@ -55,6 +55,12 @@ interface CTEventDataMap {
|
|
|
55
55
|
document_zoom: DocumentZoomEventData;
|
|
56
56
|
document_scroll: DocumentScrollEventData;
|
|
57
57
|
document_clear: DocumentClearEventData;
|
|
58
|
+
db_seed: DbSeedEventData;
|
|
59
|
+
db_query: DbQueryEventData;
|
|
60
|
+
db_result: DbResultEventData;
|
|
61
|
+
db_error: DbErrorEventData;
|
|
62
|
+
db_schema_change: DbSchemaChangeEventData;
|
|
63
|
+
db_snapshot: DbSnapshotEventData;
|
|
58
64
|
file_create: FileCreateEventData;
|
|
59
65
|
file_update: FileUpdateEventData;
|
|
60
66
|
file_delete: FileDeleteEventData;
|
|
@@ -76,10 +82,12 @@ interface CTEventDataMap {
|
|
|
76
82
|
file_hover: FileHoverEventData;
|
|
77
83
|
text_select: TextSelectEventData;
|
|
78
84
|
editor_cursor: EditorCursorEventData;
|
|
85
|
+
editor_scroll: EditorScrollEventData;
|
|
79
86
|
tool_switch: ToolSwitchEventData;
|
|
80
87
|
marker: MarkerEventData;
|
|
81
88
|
webcam_state: WebcamStateEventData;
|
|
82
89
|
content_dimensions: ContentDimensionsEventData;
|
|
90
|
+
panel_resize: PanelResizeEventData;
|
|
83
91
|
tool: ToolEventData;
|
|
84
92
|
whiteboard: WhiteboardEventData;
|
|
85
93
|
whiteboard_point: WhiteboardPointEventData;
|
|
@@ -231,6 +239,8 @@ interface InitEventData {
|
|
|
231
239
|
width: number;
|
|
232
240
|
height: number;
|
|
233
241
|
};
|
|
242
|
+
dbSeedSql?: string;
|
|
243
|
+
dbSnapshot?: string;
|
|
234
244
|
}
|
|
235
245
|
/**
|
|
236
246
|
* Code content change
|
|
@@ -434,6 +444,65 @@ interface DocumentScrollEventData {
|
|
|
434
444
|
*/
|
|
435
445
|
interface DocumentClearEventData {
|
|
436
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Instructor loads seed SQL (CREATE TABLE + INSERT).
|
|
449
|
+
* Fired once at the start of a DB session.
|
|
450
|
+
*/
|
|
451
|
+
interface DbSeedEventData {
|
|
452
|
+
/** The seed SQL text */
|
|
453
|
+
sql: string;
|
|
454
|
+
/** List of table names created by the seed */
|
|
455
|
+
tables: string[];
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* A SQL query was executed (instructor or student).
|
|
459
|
+
*/
|
|
460
|
+
interface DbQueryEventData {
|
|
461
|
+
/** The raw SQL string that was executed */
|
|
462
|
+
sql: string;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* The result of a successfully executed query.
|
|
466
|
+
*/
|
|
467
|
+
interface DbResultEventData {
|
|
468
|
+
/** Column headers */
|
|
469
|
+
columns: string[];
|
|
470
|
+
/** Row data — each inner array is one row, values are primitives */
|
|
471
|
+
rows: (string | number | null)[][];
|
|
472
|
+
/** Total rows returned */
|
|
473
|
+
rowCount: number;
|
|
474
|
+
/** Wall-clock execution time in milliseconds */
|
|
475
|
+
executionTimeMs: number;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* A query failed.
|
|
479
|
+
*/
|
|
480
|
+
interface DbErrorEventData {
|
|
481
|
+
/** The query that failed */
|
|
482
|
+
sql: string;
|
|
483
|
+
/** The error message */
|
|
484
|
+
message: string;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* The schema changed (after DDL). Contains current schema state.
|
|
488
|
+
*/
|
|
489
|
+
interface DbSchemaChangeEventData {
|
|
490
|
+
/** Current schema — output of SELECT name, sql FROM sqlite_master */
|
|
491
|
+
tables: {
|
|
492
|
+
name: string;
|
|
493
|
+
sql: string;
|
|
494
|
+
}[];
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Full database binary snapshot (base64-encoded).
|
|
498
|
+
* Used for seeking.
|
|
499
|
+
*/
|
|
500
|
+
interface DbSnapshotEventData {
|
|
501
|
+
/** The full database serialized as a base64 string */
|
|
502
|
+
snapshot: string;
|
|
503
|
+
/** Current table names for quick reference */
|
|
504
|
+
tables: string[];
|
|
505
|
+
}
|
|
437
506
|
/**
|
|
438
507
|
* Create new file
|
|
439
508
|
*/
|
|
@@ -594,6 +663,13 @@ interface EditorCursorEventData {
|
|
|
594
663
|
/** Column number (1-based) */
|
|
595
664
|
column: number;
|
|
596
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Editor scroll position (code editor)
|
|
668
|
+
*/
|
|
669
|
+
interface EditorScrollEventData {
|
|
670
|
+
/** Vertical scroll offset in pixels */
|
|
671
|
+
scrollTop: number;
|
|
672
|
+
}
|
|
597
673
|
/**
|
|
598
674
|
* Switch active tool
|
|
599
675
|
*/
|
|
@@ -623,6 +699,15 @@ interface ContentDimensionsEventData {
|
|
|
623
699
|
width: number;
|
|
624
700
|
height: number;
|
|
625
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Panel resize event — captures explorer width and terminal height changes
|
|
704
|
+
*/
|
|
705
|
+
interface PanelResizeEventData {
|
|
706
|
+
/** Which panel was resized */
|
|
707
|
+
panel: 'explorer' | 'terminal';
|
|
708
|
+
/** New size in pixels (width for explorer, height for terminal) */
|
|
709
|
+
size: number;
|
|
710
|
+
}
|
|
626
711
|
/**
|
|
627
712
|
* Legacy tool event (same as tool_switch)
|
|
628
713
|
*/
|
|
@@ -704,6 +789,7 @@ interface PlaybackState$1 {
|
|
|
704
789
|
y: number;
|
|
705
790
|
visible: boolean;
|
|
706
791
|
clicking?: boolean;
|
|
792
|
+
clickTimestamp?: number;
|
|
707
793
|
};
|
|
708
794
|
/** File creation UI state - for showing inline input during playback */
|
|
709
795
|
fileCreation: FileCreationState | null;
|
|
@@ -717,6 +803,8 @@ interface PlaybackState$1 {
|
|
|
717
803
|
textSelection: TextSelectionState | null;
|
|
718
804
|
/** Editor typing cursor (caret) position */
|
|
719
805
|
editorCursor: EditorCursorState | null;
|
|
806
|
+
/** Editor scroll position (pixels from top) */
|
|
807
|
+
editorScrollTop: number;
|
|
720
808
|
/** Whether webcam is in maximized (full overlay) mode */
|
|
721
809
|
webcamMaximized: boolean;
|
|
722
810
|
/** Content area dimensions from the recording session.
|
|
@@ -743,6 +831,32 @@ interface PlaybackState$1 {
|
|
|
743
831
|
tool: 'pointer' | 'laser';
|
|
744
832
|
button: 'down' | 'up';
|
|
745
833
|
} | null;
|
|
834
|
+
/** Whether a sql.js database is active in this recording */
|
|
835
|
+
dbInitialized: boolean;
|
|
836
|
+
/** The seed SQL that was loaded (null if no DB session) */
|
|
837
|
+
dbSeedSql: string | null;
|
|
838
|
+
/** The most recently executed SQL query */
|
|
839
|
+
dbLastQuery: string | null;
|
|
840
|
+
/** The most recently returned query result */
|
|
841
|
+
dbLastResult: DbResultEventData | null;
|
|
842
|
+
/** The most recent error message (null if last query succeeded) */
|
|
843
|
+
dbLastError: string | null;
|
|
844
|
+
/** Current schema (tables + their CREATE statements) */
|
|
845
|
+
dbSchema: {
|
|
846
|
+
name: string;
|
|
847
|
+
sql: string;
|
|
848
|
+
}[];
|
|
849
|
+
/** Most recent base64 snapshot (for seeking) */
|
|
850
|
+
dbSnapshot: string | null;
|
|
851
|
+
/** History of queries executed so far (for query history panel) */
|
|
852
|
+
dbQueryHistory: {
|
|
853
|
+
sql: string;
|
|
854
|
+
time: number;
|
|
855
|
+
}[];
|
|
856
|
+
/** File explorer panel width in pixels (null = use default) */
|
|
857
|
+
explorerWidth: number | null;
|
|
858
|
+
/** Terminal panel height in pixels (null = use default) */
|
|
859
|
+
terminalHeight: number | null;
|
|
746
860
|
}
|
|
747
861
|
/**
|
|
748
862
|
* State for file/folder creation UI display during playback
|
|
@@ -1614,6 +1728,10 @@ interface IDEPanelProps extends BasePanelProps {
|
|
|
1614
1728
|
line: number;
|
|
1615
1729
|
column: number;
|
|
1616
1730
|
}) => void;
|
|
1731
|
+
/** Editor scroll position in pixels from top (playback display) */
|
|
1732
|
+
editorScrollTop?: number;
|
|
1733
|
+
/** Editor scroll change callback (recording) */
|
|
1734
|
+
onEditorScrollChange?: (scrollTop: number) => void;
|
|
1617
1735
|
/**
|
|
1618
1736
|
* Called when user interacts with the IDE (clicks explorer, terminal, etc).
|
|
1619
1737
|
* Use this to pause playback when user starts interacting.
|
|
@@ -1629,6 +1747,41 @@ interface IDEPanelProps extends BasePanelProps {
|
|
|
1629
1747
|
aiDebugEnabled?: boolean;
|
|
1630
1748
|
/** API endpoint for AI debug analysis. Default: '/api/ai/debug' */
|
|
1631
1749
|
aiDebugEndpoint?: string;
|
|
1750
|
+
/** Whether a sql.js database is active in this recording */
|
|
1751
|
+
dbInitialized?: boolean;
|
|
1752
|
+
/** Current database schema */
|
|
1753
|
+
dbSchema?: {
|
|
1754
|
+
name: string;
|
|
1755
|
+
sql: string;
|
|
1756
|
+
}[];
|
|
1757
|
+
/** Most recently executed SQL query */
|
|
1758
|
+
dbLastQuery?: string | null;
|
|
1759
|
+
/** Most recent query result */
|
|
1760
|
+
dbLastResult?: {
|
|
1761
|
+
columns: string[];
|
|
1762
|
+
rows: (string | number | null)[][];
|
|
1763
|
+
rowCount: number;
|
|
1764
|
+
executionTimeMs: number;
|
|
1765
|
+
} | null;
|
|
1766
|
+
/** Most recent error message */
|
|
1767
|
+
dbLastError?: string | null;
|
|
1768
|
+
/** Query execution history */
|
|
1769
|
+
dbQueryHistory?: {
|
|
1770
|
+
sql: string;
|
|
1771
|
+
time: number;
|
|
1772
|
+
}[];
|
|
1773
|
+
/** Execute a SQL query (recording/interactive) */
|
|
1774
|
+
onDbQueryExecute?: (sql: string) => void | Promise<void>;
|
|
1775
|
+
/** Load seed SQL (recording) */
|
|
1776
|
+
onDbSeedLoad?: (sql: string) => void | Promise<void>;
|
|
1777
|
+
/** Reset database (recording) */
|
|
1778
|
+
onDbReset?: () => void;
|
|
1779
|
+
/** External explorer width (for playback — overrides local state) */
|
|
1780
|
+
explorerWidthState?: number | null;
|
|
1781
|
+
/** External terminal height (for playback — overrides local state) */
|
|
1782
|
+
terminalHeightState?: number | null;
|
|
1783
|
+
/** Called when a panel is resized (for recording) */
|
|
1784
|
+
onPanelResize?: (panel: 'explorer' | 'terminal', size: number) => void;
|
|
1632
1785
|
}
|
|
1633
1786
|
/**
|
|
1634
1787
|
* Excalidraw scene structure (simplified)
|
|
@@ -1882,7 +2035,7 @@ interface VirtualFileSystem {
|
|
|
1882
2035
|
notifyChanged?: () => void;
|
|
1883
2036
|
mkdir?: (path: string) => void;
|
|
1884
2037
|
}
|
|
1885
|
-
declare function IDEPanel({ fileSystem, tree: externalTree, files: externalFiles, code: externalCode, language: externalLanguage, currentFile: externalCurrentFile, onCodeChange, onFileChange, onFileCreate, onFileDelete, onFileRename, onFileSelect, onFolderCreate, onFolderDelete, onTerminalCommand, onTerminalClear, onTerminalAddLines, fileCreationState, onFileCreationStart, onFileCreationInput, onFileCreationCancel, fileRenamingState, onFileRenameStart, onFileRenameInput, onFileRenameCancel, expandedFoldersState, onFolderToggle, terminalLines: externalTerminalLines, terminalInput: externalTerminalInput, onTerminalInputChange, onExecute, onTerminalExecute, mode, isPlaying, defaultShowExplorer, defaultShowTerminal, showRunButton, interactiveLabel, initialFiles, initialActiveFile, hoveredFile, onFileHover, textSelection, onTextSelect, editorCursor, onEditorCursorChange, onInteractionStart, aiSuggestionsEnabled, aiSuggestionsEndpoint, aiSuggestionsDebounceMs, aiDebugEnabled, aiDebugEndpoint, }: IDEPanelProps & {
|
|
2038
|
+
declare function IDEPanel({ fileSystem, tree: externalTree, files: externalFiles, code: externalCode, language: externalLanguage, currentFile: externalCurrentFile, onCodeChange, onFileChange, onFileCreate, onFileDelete, onFileRename, onFileSelect, onFolderCreate, onFolderDelete, onTerminalCommand, onTerminalClear, onTerminalAddLines, fileCreationState, onFileCreationStart, onFileCreationInput, onFileCreationCancel, fileRenamingState, onFileRenameStart, onFileRenameInput, onFileRenameCancel, expandedFoldersState, onFolderToggle, terminalLines: externalTerminalLines, terminalInput: externalTerminalInput, onTerminalInputChange, onExecute, onTerminalExecute, mode, isPlaying, defaultShowExplorer, defaultShowTerminal, showRunButton, interactiveLabel, initialFiles, initialActiveFile, hoveredFile, onFileHover, textSelection, onTextSelect, editorCursor, onEditorCursorChange, editorScrollTop, onEditorScrollChange, onInteractionStart, aiSuggestionsEnabled, aiSuggestionsEndpoint, aiSuggestionsDebounceMs, aiDebugEnabled, aiDebugEndpoint, dbInitialized, dbSchema, dbLastQuery, dbLastResult, dbLastError, dbQueryHistory, onDbQueryExecute, onDbSeedLoad, onDbReset, explorerWidthState, terminalHeightState, onPanelResize, }: IDEPanelProps & {
|
|
1886
2039
|
fileSystem?: VirtualFileSystem;
|
|
1887
2040
|
}): react_jsx_runtime.JSX.Element;
|
|
1888
2041
|
|
|
@@ -1891,52 +2044,100 @@ declare function FileExplorer({ tree, selectedFile, expandedFolders, fileCreatio
|
|
|
1891
2044
|
declare const Terminal: React__default.ForwardRefExoticComponent<TerminalProps & React__default.RefAttributes<TerminalRef>>;
|
|
1892
2045
|
|
|
1893
2046
|
/**
|
|
1894
|
-
*
|
|
2047
|
+
* PlaybackStateMachine
|
|
1895
2048
|
*
|
|
1896
|
-
*
|
|
1897
|
-
*
|
|
1898
|
-
*
|
|
2049
|
+
* Pure TypeScript finite state machine for player state management.
|
|
2050
|
+
* No React, no DOM, no side effects — only state and transitions.
|
|
2051
|
+
*
|
|
2052
|
+
* Replaces the 13 booleans scattered across useRemoteStreamingPlayback
|
|
2053
|
+
* with a single enum value and enforced valid transitions.
|
|
1899
2054
|
*
|
|
1900
2055
|
* @packageDocumentation
|
|
1901
2056
|
*/
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
/**
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2057
|
+
type PlayerState = 'IDLE' | 'LOADING' | 'READY' | 'PLAYING' | 'PAUSED' | 'BUFFERING' | 'SEEKING' | 'ENDED' | 'ERROR';
|
|
2058
|
+
type PlayerAction = 'LOAD' | 'SOURCES_READY' | 'PLAY' | 'PAUSE' | 'BUFFER_EMPTY' | 'BUFFER_READY' | 'SEEK' | 'SEEK_COMPLETE' | 'COMPLETE' | 'ERROR' | 'RESET' | 'DESTROY';
|
|
2059
|
+
interface Transition {
|
|
2060
|
+
from: PlayerState;
|
|
2061
|
+
to: PlayerState;
|
|
2062
|
+
action: PlayerAction;
|
|
2063
|
+
}
|
|
2064
|
+
type TransitionListener = (transition: Transition) => void;
|
|
2065
|
+
declare class PlaybackStateMachine {
|
|
2066
|
+
private _state;
|
|
2067
|
+
private _listeners;
|
|
2068
|
+
private _preSeekState;
|
|
2069
|
+
private _destroyed;
|
|
2070
|
+
get state(): PlayerState;
|
|
2071
|
+
get preSeekState(): PlayerState | null;
|
|
2072
|
+
/**
|
|
2073
|
+
* Dispatch an action. Returns true if the transition was valid, false otherwise.
|
|
2074
|
+
* Invalid transitions are logged as warnings — they are programmer errors.
|
|
2075
|
+
*/
|
|
2076
|
+
dispatch(action: PlayerAction): boolean;
|
|
2077
|
+
/**
|
|
2078
|
+
* Subscribe to state transitions. Returns an unsubscribe function.
|
|
2079
|
+
*/
|
|
2080
|
+
onTransition(fn: TransitionListener): () => void;
|
|
2081
|
+
/**
|
|
2082
|
+
* Check if the current state is one of the given states.
|
|
2083
|
+
*/
|
|
2084
|
+
is(...states: PlayerState[]): boolean;
|
|
1921
2085
|
}
|
|
2086
|
+
|
|
1922
2087
|
/**
|
|
1923
|
-
*
|
|
2088
|
+
* MasterClock
|
|
2089
|
+
*
|
|
2090
|
+
* The single source of time for the entire player. Nothing else reads
|
|
2091
|
+
* `audio.currentTime` directly — everything goes through this clock.
|
|
2092
|
+
*
|
|
2093
|
+
* Two tick sources work together:
|
|
2094
|
+
* 1. `timeupdate` event (primary) — fires ~4x/sec from audio element.
|
|
2095
|
+
* Reliable in background tabs. Source of truth for "what time is it."
|
|
2096
|
+
* 2. `requestAnimationFrame` (interpolation) — fires ~60fps when visible.
|
|
2097
|
+
* Between timeupdate ticks, interpolates linearly for smooth UI.
|
|
2098
|
+
*
|
|
2099
|
+
* No React, no DOM ownership — pure TypeScript.
|
|
2100
|
+
*
|
|
2101
|
+
* @packageDocumentation
|
|
1924
2102
|
*/
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2103
|
+
type ClockTickCallback = (timeMs: number) => void;
|
|
2104
|
+
interface ClockSource {
|
|
2105
|
+
getCurrentTimeMs(): number;
|
|
2106
|
+
onTick(fn: ClockTickCallback): () => void;
|
|
2107
|
+
setPlaybackRate(rate: number): void;
|
|
2108
|
+
getPlaybackRate(): number;
|
|
2109
|
+
}
|
|
2110
|
+
declare class MasterClock {
|
|
2111
|
+
private source;
|
|
2112
|
+
private sourceUnsub;
|
|
2113
|
+
private subscribers;
|
|
2114
|
+
private rafId;
|
|
2115
|
+
private lastTickTimeMs;
|
|
2116
|
+
private lastTickTimestamp;
|
|
2117
|
+
private playbackRate;
|
|
2118
|
+
private running;
|
|
2119
|
+
/** Attach a clock source (e.g., audio element wrapper). */
|
|
2120
|
+
setSource(source: ClockSource): void;
|
|
2121
|
+
/**
|
|
2122
|
+
* Get current time in ms.
|
|
2123
|
+
* Between timeupdate ticks, interpolates linearly using elapsed wall-clock time.
|
|
2124
|
+
*/
|
|
2125
|
+
getCurrentTimeMs(): number;
|
|
2126
|
+
/** Start the RAF interpolation loop for smooth UI updates. */
|
|
2127
|
+
start(): void;
|
|
2128
|
+
/** Stop the RAF loop. timeupdate events still arrive from the source. */
|
|
2129
|
+
stop(): void;
|
|
2130
|
+
/** Subscribe to clock ticks (called on every RAF frame + every timeupdate). */
|
|
2131
|
+
subscribe(fn: ClockTickCallback): () => void;
|
|
2132
|
+
setPlaybackRate(rate: number): void;
|
|
2133
|
+
getPlaybackRate(): number;
|
|
2134
|
+
/** Force-set the clock position (used after seek). */
|
|
2135
|
+
seekTo(timeMs: number): void;
|
|
2136
|
+
/** Whether the RAF loop is running. */
|
|
2137
|
+
isRunning(): boolean;
|
|
2138
|
+
private rafLoop;
|
|
2139
|
+
private notify;
|
|
2140
|
+
destroy(): void;
|
|
1940
2141
|
}
|
|
1941
2142
|
|
|
1942
2143
|
/**
|
|
@@ -2090,33 +2291,6 @@ interface StreamingManifest {
|
|
|
2090
2291
|
*/
|
|
2091
2292
|
interactionPoints?: InteractionPointManifest[];
|
|
2092
2293
|
}
|
|
2093
|
-
/**
|
|
2094
|
-
* Options for useRemoteStreamingPlayback hook
|
|
2095
|
-
*/
|
|
2096
|
-
interface UseRemoteStreamingPlaybackOptions {
|
|
2097
|
-
/** URL to the streaming manifest.json */
|
|
2098
|
-
manifestUrl: string;
|
|
2099
|
-
/** Auto-play when ready */
|
|
2100
|
-
autoPlay?: boolean;
|
|
2101
|
-
/** Initial playback speed (default: 1.0) */
|
|
2102
|
-
defaultSpeed?: number;
|
|
2103
|
-
/** Maximum chunks to keep in memory (default: 5) */
|
|
2104
|
-
maxCacheSize?: number;
|
|
2105
|
-
/** Called when streaming is ready to play */
|
|
2106
|
-
onReady?: () => void;
|
|
2107
|
-
/** Called when playback starts */
|
|
2108
|
-
onPlay?: () => void;
|
|
2109
|
-
/** Called when playback pauses */
|
|
2110
|
-
onPause?: () => void;
|
|
2111
|
-
/** Called on playback progress */
|
|
2112
|
-
onProgress?: (timeMs: number, duration: number) => void;
|
|
2113
|
-
/** Called when playback completes */
|
|
2114
|
-
onComplete?: () => void;
|
|
2115
|
-
/** Called when buffering state changes */
|
|
2116
|
-
onBuffering?: (isBuffering: boolean) => void;
|
|
2117
|
-
/** Called on error */
|
|
2118
|
-
onError?: (error: Error) => void;
|
|
2119
|
-
}
|
|
2120
2294
|
/**
|
|
2121
2295
|
* Buffer health status for remote streaming
|
|
2122
2296
|
*/
|
|
@@ -2132,77 +2306,6 @@ interface RemoteBufferHealth {
|
|
|
2132
2306
|
/** Total buffered time in milliseconds */
|
|
2133
2307
|
bufferedTimeMs: number;
|
|
2134
2308
|
}
|
|
2135
|
-
/**
|
|
2136
|
-
* Performance metrics for remote streaming
|
|
2137
|
-
*/
|
|
2138
|
-
interface RemoteStreamingMetrics {
|
|
2139
|
-
/** Average chunk fetch time in ms */
|
|
2140
|
-
avgChunkFetchTimeMs: number;
|
|
2141
|
-
/** Cache hit rate (0-1) */
|
|
2142
|
-
cacheHitRate: number;
|
|
2143
|
-
/** Number of times playback had to wait for buffering */
|
|
2144
|
-
bufferUnderrunCount: number;
|
|
2145
|
-
/** Average seek time in ms */
|
|
2146
|
-
avgSeekTimeMs: number;
|
|
2147
|
-
/** Total bytes downloaded */
|
|
2148
|
-
totalBytesLoaded: number;
|
|
2149
|
-
/** Peak number of chunks in cache */
|
|
2150
|
-
peakChunksCached: number;
|
|
2151
|
-
}
|
|
2152
|
-
/**
|
|
2153
|
-
* Complete state returned by the remote streaming hook
|
|
2154
|
-
*/
|
|
2155
|
-
interface RemoteStreamingPlaybackState {
|
|
2156
|
-
/** Current playback state (code, terminal, etc.) */
|
|
2157
|
-
state: PlaybackState$1;
|
|
2158
|
-
/** Current playback time in milliseconds */
|
|
2159
|
-
currentTime: number;
|
|
2160
|
-
/** Total duration in milliseconds */
|
|
2161
|
-
duration: number;
|
|
2162
|
-
/** Whether playback is active */
|
|
2163
|
-
isPlaying: boolean;
|
|
2164
|
-
/** Current playback speed */
|
|
2165
|
-
speed: number;
|
|
2166
|
-
/** Whether the player is ready (UI can render — audio+events loaded) */
|
|
2167
|
-
isReady: boolean;
|
|
2168
|
-
/** Whether ALL sources are loaded and synced (audio+video+events) */
|
|
2169
|
-
allSourcesReady: boolean;
|
|
2170
|
-
/** Whether currently buffering */
|
|
2171
|
-
isBuffering: boolean;
|
|
2172
|
-
/** Buffer health information */
|
|
2173
|
-
bufferHealth: RemoteBufferHealth;
|
|
2174
|
-
/** Streaming metrics */
|
|
2175
|
-
metrics: RemoteStreamingMetrics;
|
|
2176
|
-
/** Current error (if any) */
|
|
2177
|
-
error: Error | null;
|
|
2178
|
-
/** The loaded manifest */
|
|
2179
|
-
manifest: StreamingManifest | null;
|
|
2180
|
-
}
|
|
2181
|
-
/**
|
|
2182
|
-
* Controls for remote streaming playback
|
|
2183
|
-
*/
|
|
2184
|
-
interface RemoteStreamingPlaybackControls {
|
|
2185
|
-
/** Start playback */
|
|
2186
|
-
play(): void;
|
|
2187
|
-
/** Pause playback */
|
|
2188
|
-
pause(): void;
|
|
2189
|
-
/** Seek to a specific time (milliseconds) */
|
|
2190
|
-
seek(timeMs: number): Promise<void>;
|
|
2191
|
-
/** Set playback speed */
|
|
2192
|
-
setSpeed(speed: number): void;
|
|
2193
|
-
/** Get the audio element for custom controls */
|
|
2194
|
-
getAudioElement(): HTMLAudioElement | null;
|
|
2195
|
-
/** Get the video element for webcam overlay (null if no video) */
|
|
2196
|
-
getVideoElement(): HTMLVideoElement | null;
|
|
2197
|
-
/** Attach HLS video player to a video element (from WebcamOverlay onVideoRef) */
|
|
2198
|
-
initVideoHls(videoElement: HTMLVideoElement | null): void;
|
|
2199
|
-
/** Get available webcam video quality levels */
|
|
2200
|
-
getVideoQualityLevels(): HLSQualityLevel[];
|
|
2201
|
-
/** Set webcam video quality level (-1 for auto) */
|
|
2202
|
-
setVideoQualityLevel(index: number): void;
|
|
2203
|
-
/** Get current webcam video quality level (-1 = auto) */
|
|
2204
|
-
getVideoQualityLevel(): number;
|
|
2205
|
-
}
|
|
2206
2309
|
/**
|
|
2207
2310
|
* HLS player configuration
|
|
2208
2311
|
*/
|
|
@@ -2215,6 +2318,8 @@ interface HLSPlayerConfig {
|
|
|
2215
2318
|
startLevel?: number;
|
|
2216
2319
|
/** Auto-start load */
|
|
2217
2320
|
autoStartLoad?: boolean;
|
|
2321
|
+
/** Cap quality level to the video element's display dimensions */
|
|
2322
|
+
capLevelToPlayerSize?: boolean;
|
|
2218
2323
|
}
|
|
2219
2324
|
/**
|
|
2220
2325
|
* HLS quality level info
|
|
@@ -2233,96 +2338,579 @@ interface HLSQualityLevel {
|
|
|
2233
2338
|
}
|
|
2234
2339
|
|
|
2235
2340
|
/**
|
|
2236
|
-
*
|
|
2341
|
+
* Remote Chunk Fetcher
|
|
2342
|
+
*
|
|
2343
|
+
* Handles fetching and decompressing event chunks from CDN.
|
|
2344
|
+
* Supports gzip compressed chunks and provides retry logic.
|
|
2345
|
+
*
|
|
2346
|
+
* @packageDocumentation
|
|
2237
2347
|
*/
|
|
2238
2348
|
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
getMediaUrl?(recordingId: string): Promise<string | null>;
|
|
2245
|
-
}
|
|
2246
|
-
/**
|
|
2247
|
-
* Internal recording structure (from backend)
|
|
2248
|
-
*/
|
|
2249
|
-
interface InternalRecording {
|
|
2250
|
-
id: string;
|
|
2251
|
-
name?: string;
|
|
2252
|
-
duration: number;
|
|
2253
|
-
events: InternalEvent[];
|
|
2254
|
-
markers?: RecordingMarker[];
|
|
2255
|
-
hasMedia?: boolean;
|
|
2256
|
-
mediaUrl?: string;
|
|
2257
|
-
createdAt?: number;
|
|
2258
|
-
}
|
|
2259
|
-
/**
|
|
2260
|
-
* Internal event structure (from backend)
|
|
2261
|
-
*/
|
|
2262
|
-
interface InternalEvent {
|
|
2263
|
-
type: string;
|
|
2264
|
-
time: number;
|
|
2265
|
-
data: Record<string, unknown>;
|
|
2349
|
+
interface FetchResult<T> {
|
|
2350
|
+
data: T | null;
|
|
2351
|
+
error: string | null;
|
|
2352
|
+
durationMs: number;
|
|
2353
|
+
bytesLoaded: number;
|
|
2266
2354
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
*/
|
|
2270
|
-
interface RecordingMarker {
|
|
2271
|
-
id: string;
|
|
2272
|
-
time: number;
|
|
2273
|
-
label: string;
|
|
2274
|
-
color?: string;
|
|
2355
|
+
interface ChunkFetchResult extends FetchResult<CTEventChunk> {
|
|
2356
|
+
chunkId: number;
|
|
2275
2357
|
}
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2358
|
+
interface FetcherConfig {
|
|
2359
|
+
/** Maximum retry attempts */
|
|
2360
|
+
maxRetries: number;
|
|
2361
|
+
/** Base delay between retries (ms) */
|
|
2362
|
+
retryDelayMs: number;
|
|
2363
|
+
/** Request timeout (ms) */
|
|
2364
|
+
timeoutMs: number;
|
|
2365
|
+
}
|
|
2366
|
+
/**
|
|
2367
|
+
* Fetcher for streaming manifest and event chunks
|
|
2368
|
+
*/
|
|
2369
|
+
declare class ChunkFetcher {
|
|
2370
|
+
private config;
|
|
2371
|
+
private chunkIndex;
|
|
2372
|
+
private eventsBaseUrl;
|
|
2373
|
+
private compressed;
|
|
2374
|
+
private pendingFetches;
|
|
2375
|
+
private abortController;
|
|
2376
|
+
private metrics;
|
|
2377
|
+
constructor(config?: Partial<FetcherConfig>);
|
|
2378
|
+
/**
|
|
2379
|
+
* Fetch the streaming manifest
|
|
2380
|
+
*/
|
|
2381
|
+
fetchManifest(manifestUrl: string): Promise<FetchResult<StreamingManifest>>;
|
|
2382
|
+
/**
|
|
2383
|
+
* Fetch the events chunk index
|
|
2384
|
+
*/
|
|
2385
|
+
fetchChunkIndex(indexUrl: string): Promise<FetchResult<CTEventChunkIndex>>;
|
|
2386
|
+
/**
|
|
2387
|
+
* Initialize the fetcher with a manifest
|
|
2388
|
+
*/
|
|
2389
|
+
initialize(manifest: StreamingManifest, chunkIndex: CTEventChunkIndex): void;
|
|
2390
|
+
/**
|
|
2391
|
+
* Fetch a specific chunk by ID
|
|
2392
|
+
*/
|
|
2393
|
+
fetchChunk(chunkId: number): Promise<ChunkFetchResult>;
|
|
2394
|
+
/**
|
|
2395
|
+
* Prefetch multiple chunks
|
|
2396
|
+
*/
|
|
2397
|
+
prefetchChunks(chunkIds: number[]): Promise<void>;
|
|
2398
|
+
/**
|
|
2399
|
+
* Get fetcher metrics
|
|
2400
|
+
*/
|
|
2401
|
+
getMetrics(): {
|
|
2402
|
+
totalFetches: number;
|
|
2403
|
+
totalBytes: number;
|
|
2404
|
+
avgFetchTimeMs: number;
|
|
2405
|
+
errorRate: number;
|
|
2406
|
+
};
|
|
2407
|
+
/**
|
|
2408
|
+
* Get chunk info by ID
|
|
2409
|
+
*/
|
|
2410
|
+
getChunkInfo(chunkId: number): CTChunkInfo | null;
|
|
2411
|
+
/**
|
|
2412
|
+
* Get total chunk count
|
|
2413
|
+
*/
|
|
2414
|
+
getTotalChunks(): number;
|
|
2415
|
+
/**
|
|
2416
|
+
* Reset fetcher state
|
|
2417
|
+
*/
|
|
2418
|
+
reset(): void;
|
|
2283
2419
|
}
|
|
2420
|
+
|
|
2284
2421
|
/**
|
|
2285
|
-
*
|
|
2422
|
+
* MediaSourceAdapter — Interface for pluggable media sources.
|
|
2423
|
+
*
|
|
2424
|
+
* Abstracts how the PlayerEngine obtains audio, video, and event data.
|
|
2425
|
+
* Implementations:
|
|
2426
|
+
* - HlsStreamingAdapter: CDN/HLS streaming via manifest URL
|
|
2427
|
+
* - LocalFileAdapter: Local .ct file playback from a CTRecording blob
|
|
2428
|
+
*
|
|
2429
|
+
* @packageDocumentation
|
|
2286
2430
|
*/
|
|
2287
|
-
|
|
2288
|
-
|
|
2431
|
+
|
|
2432
|
+
type AudioSourceResult = {
|
|
2433
|
+
type: 'hls';
|
|
2434
|
+
url: string;
|
|
2435
|
+
} | {
|
|
2436
|
+
type: 'url';
|
|
2437
|
+
url: string;
|
|
2438
|
+
};
|
|
2439
|
+
type VideoSourceResult = {
|
|
2440
|
+
type: 'hls';
|
|
2441
|
+
url: string;
|
|
2442
|
+
} | {
|
|
2443
|
+
type: 'url';
|
|
2444
|
+
url: string;
|
|
2445
|
+
} | null;
|
|
2446
|
+
type EventsSourceResult = {
|
|
2447
|
+
type: 'chunked';
|
|
2448
|
+
manifestUrl: string;
|
|
2449
|
+
} | {
|
|
2450
|
+
type: 'inline';
|
|
2451
|
+
events: CTEvent[];
|
|
2452
|
+
duration: number;
|
|
2453
|
+
};
|
|
2454
|
+
interface RecordingInfo {
|
|
2455
|
+
duration: number;
|
|
2456
|
+
name?: string;
|
|
2457
|
+
hasVideo: boolean;
|
|
2458
|
+
}
|
|
2459
|
+
interface MediaSourceAdapter {
|
|
2460
|
+
/** Get the audio source configuration */
|
|
2461
|
+
getAudioSource(): Promise<AudioSourceResult>;
|
|
2462
|
+
/** Get the events source configuration */
|
|
2463
|
+
getEventsSource(): Promise<EventsSourceResult>;
|
|
2464
|
+
/** Get basic recording metadata */
|
|
2465
|
+
getRecordingInfo(): RecordingInfo;
|
|
2466
|
+
/** Get the video source configuration (null if no video) */
|
|
2467
|
+
getVideoSource(): Promise<VideoSourceResult>;
|
|
2468
|
+
/** Get the streaming manifest (only available for chunked sources) */
|
|
2469
|
+
getManifest(): StreamingManifest | null;
|
|
2470
|
+
/** Cleanup any resources (e.g., revoke blob URLs) */
|
|
2471
|
+
dispose(): void;
|
|
2289
2472
|
}
|
|
2473
|
+
|
|
2290
2474
|
/**
|
|
2291
|
-
*
|
|
2475
|
+
* Engine-only types — no React dependencies.
|
|
2476
|
+
*
|
|
2477
|
+
* @packageDocumentation
|
|
2292
2478
|
*/
|
|
2293
|
-
|
|
2294
|
-
/** Raw document data (base64 for PDFs, etc.) */
|
|
2295
|
-
data: string;
|
|
2296
|
-
}
|
|
2479
|
+
|
|
2297
2480
|
/**
|
|
2298
|
-
*
|
|
2481
|
+
* Configuration for creating a PlayerEngine instance.
|
|
2482
|
+
* No React types — this is framework-agnostic.
|
|
2299
2483
|
*/
|
|
2300
|
-
interface
|
|
2301
|
-
/**
|
|
2302
|
-
|
|
2303
|
-
/**
|
|
2304
|
-
|
|
2305
|
-
/**
|
|
2306
|
-
|
|
2307
|
-
/**
|
|
2308
|
-
ctFile?: Blob;
|
|
2309
|
-
/** Auto-play on load */
|
|
2484
|
+
interface PlayerEngineConfig {
|
|
2485
|
+
/** URL to the streaming manifest.json (used for HLS streaming) */
|
|
2486
|
+
manifestUrl?: string;
|
|
2487
|
+
/** Pluggable media source adapter (takes precedence over manifestUrl) */
|
|
2488
|
+
source?: MediaSourceAdapter;
|
|
2489
|
+
/** Unique content ID for resume position storage (defaults to manifestUrl hash) */
|
|
2490
|
+
contentId?: string;
|
|
2491
|
+
/** Auto-play when ready */
|
|
2310
2492
|
autoPlay?: boolean;
|
|
2311
|
-
/** Initial playback speed */
|
|
2312
|
-
|
|
2313
|
-
/**
|
|
2314
|
-
|
|
2493
|
+
/** Initial playback speed (default: 1.0) */
|
|
2494
|
+
defaultSpeed?: number;
|
|
2495
|
+
/** Maximum chunks to keep in memory (default: 10) */
|
|
2496
|
+
maxCacheSize?: number;
|
|
2315
2497
|
onReady?: () => void;
|
|
2316
2498
|
onPlay?: () => void;
|
|
2317
2499
|
onPause?: () => void;
|
|
2318
|
-
|
|
2319
|
-
|
|
2500
|
+
onProgress?: (timeMs: number, duration: number) => void;
|
|
2501
|
+
onComplete?: () => void;
|
|
2502
|
+
onBuffering?: (isBuffering: boolean) => void;
|
|
2320
2503
|
onError?: (error: Error) => void;
|
|
2321
2504
|
}
|
|
2322
2505
|
/**
|
|
2323
|
-
*
|
|
2506
|
+
* Immutable snapshot of the entire player state.
|
|
2507
|
+
* React subscribes to this via `onSnapshot()`.
|
|
2508
|
+
*
|
|
2509
|
+
* One object instead of 12 separate useState calls.
|
|
2510
|
+
* Pattern used by Zustand, Redux, XState, TanStack Query.
|
|
2324
2511
|
*/
|
|
2325
|
-
interface
|
|
2512
|
+
interface PlayerSnapshot {
|
|
2513
|
+
/** Current state machine state */
|
|
2514
|
+
machineState: PlayerState;
|
|
2515
|
+
/** Current playback state (code, terminal, whiteboard, etc.) */
|
|
2516
|
+
playbackState: PlaybackState$1;
|
|
2517
|
+
/** Current playback time in milliseconds */
|
|
2518
|
+
currentTime: number;
|
|
2519
|
+
/** Total duration in milliseconds */
|
|
2520
|
+
duration: number;
|
|
2521
|
+
/** Current playback speed */
|
|
2522
|
+
speed: number;
|
|
2523
|
+
/** Buffer health information */
|
|
2524
|
+
bufferHealth: RemoteBufferHealth;
|
|
2525
|
+
/** Current error (if any) */
|
|
2526
|
+
error: Error | null;
|
|
2527
|
+
/** The loaded manifest */
|
|
2528
|
+
manifest: StreamingManifest | null;
|
|
2529
|
+
/** Saved resume position in ms (null if none or past 95%) */
|
|
2530
|
+
resumePosition: number | null;
|
|
2531
|
+
/** Whether playback is active (derived from machineState) */
|
|
2532
|
+
isPlaying: boolean;
|
|
2533
|
+
/** Whether the player is ready (audio + events loaded, UI can render) */
|
|
2534
|
+
isReady: boolean;
|
|
2535
|
+
/** Whether ALL sources are loaded and synced (audio + video + events) */
|
|
2536
|
+
allSourcesReady: boolean;
|
|
2537
|
+
/** Whether currently buffering */
|
|
2538
|
+
isBuffering: boolean;
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
type SnapshotListener = (snapshot: PlayerSnapshot) => void;
|
|
2542
|
+
declare class PlayerEngine {
|
|
2543
|
+
private machine;
|
|
2544
|
+
private clock;
|
|
2545
|
+
private scheduler;
|
|
2546
|
+
private media;
|
|
2547
|
+
private buffer;
|
|
2548
|
+
private config;
|
|
2549
|
+
private resumeKey;
|
|
2550
|
+
private sourceAdapter;
|
|
2551
|
+
private _snapshot;
|
|
2552
|
+
private _listeners;
|
|
2553
|
+
private _lastTimeEmitTs;
|
|
2554
|
+
private audioElement;
|
|
2555
|
+
private videoElement;
|
|
2556
|
+
private _destroyed;
|
|
2557
|
+
private audioReady;
|
|
2558
|
+
private eventsReady;
|
|
2559
|
+
private videoReady;
|
|
2560
|
+
private hasVideo;
|
|
2561
|
+
private pendingPlay;
|
|
2562
|
+
private seekGeneration;
|
|
2563
|
+
private lastBufferHealthCheckTs;
|
|
2564
|
+
private bufferingDebounceTimer;
|
|
2565
|
+
private videoListenersCleanup;
|
|
2566
|
+
private clockUnsub;
|
|
2567
|
+
private schedulerUnsub;
|
|
2568
|
+
private machineUnsub;
|
|
2569
|
+
private audioListenersCleanup;
|
|
2570
|
+
private tickActive;
|
|
2571
|
+
constructor(config: PlayerEngineConfig);
|
|
2572
|
+
/**
|
|
2573
|
+
* Load media and events via the configured source adapter.
|
|
2574
|
+
* Supports both HLS streaming (chunked) and local file (inline) sources.
|
|
2575
|
+
* Transitions: IDLE → LOADING → READY (or ERROR).
|
|
2576
|
+
*/
|
|
2577
|
+
load(): Promise<void>;
|
|
2578
|
+
/**
|
|
2579
|
+
* Start playback. Gates on allSourcesReady.
|
|
2580
|
+
*/
|
|
2581
|
+
play(): void;
|
|
2582
|
+
/**
|
|
2583
|
+
* Pause playback.
|
|
2584
|
+
*/
|
|
2585
|
+
pause(): void;
|
|
2586
|
+
/**
|
|
2587
|
+
* Seek to a specific time in milliseconds.
|
|
2588
|
+
*/
|
|
2589
|
+
seek(timeMs: number): Promise<void>;
|
|
2590
|
+
/**
|
|
2591
|
+
* Set playback speed.
|
|
2592
|
+
*/
|
|
2593
|
+
setSpeed(speed: number): void;
|
|
2594
|
+
/**
|
|
2595
|
+
* Set volume level (0–2+, values > 1 amplify via Web Audio).
|
|
2596
|
+
*/
|
|
2597
|
+
setVolume(level: number): void;
|
|
2598
|
+
/**
|
|
2599
|
+
* Set muted state (uses GainNode, not audio.muted).
|
|
2600
|
+
*/
|
|
2601
|
+
setMuted(muted: boolean): void;
|
|
2602
|
+
/**
|
|
2603
|
+
* Initialize video for a given video element.
|
|
2604
|
+
* Supports both HLS (streaming) and direct URL (local file) sources.
|
|
2605
|
+
* Called from React when the <video> element mounts.
|
|
2606
|
+
*/
|
|
2607
|
+
initVideo(element: HTMLVideoElement | null): Promise<void>;
|
|
2608
|
+
/**
|
|
2609
|
+
* Toggle webcam visibility — NEVER destroys HLS.
|
|
2610
|
+
*/
|
|
2611
|
+
setVideoVisible(visible: boolean): void;
|
|
2612
|
+
getVideoQualityLevels(): HLSQualityLevel[];
|
|
2613
|
+
setVideoQualityLevel(index: number): void;
|
|
2614
|
+
getVideoQualityLevel(): number;
|
|
2615
|
+
getAudioElement(): HTMLAudioElement | null;
|
|
2616
|
+
getVideoElement(): HTMLVideoElement | null;
|
|
2617
|
+
/**
|
|
2618
|
+
* Get buffer/fetch metrics for diagnostics (polled by hook for metrics state).
|
|
2619
|
+
*/
|
|
2620
|
+
getBufferMetrics(): {
|
|
2621
|
+
avgFetchTimeMs: number;
|
|
2622
|
+
cacheHitRate: number;
|
|
2623
|
+
peakCache: number;
|
|
2624
|
+
fetcherMetrics: ReturnType<ChunkFetcher["getMetrics"]>;
|
|
2625
|
+
};
|
|
2626
|
+
/**
|
|
2627
|
+
* Get the source adapter (for adapter-specific operations like LocalFileAdapter.getRecording()).
|
|
2628
|
+
*/
|
|
2629
|
+
getSourceAdapter(): MediaSourceAdapter | null;
|
|
2630
|
+
/**
|
|
2631
|
+
* Get the current snapshot.
|
|
2632
|
+
*/
|
|
2633
|
+
getSnapshot(): PlayerSnapshot;
|
|
2634
|
+
/**
|
|
2635
|
+
* Subscribe to snapshot changes. Returns an unsubscribe function.
|
|
2636
|
+
* Time-only updates are throttled to ~4/sec.
|
|
2637
|
+
* State transitions emit immediately.
|
|
2638
|
+
*/
|
|
2639
|
+
onSnapshot(fn: SnapshotListener): () => void;
|
|
2640
|
+
resumeAudioContext(): void;
|
|
2641
|
+
destroy(): void;
|
|
2642
|
+
private handleTransition;
|
|
2643
|
+
private handleSchedulerEvent;
|
|
2644
|
+
private startTickLoop;
|
|
2645
|
+
private stopTickLoop;
|
|
2646
|
+
private wireAudioEvents;
|
|
2647
|
+
private wireVideoEvents;
|
|
2648
|
+
private cleanupVideoListeners;
|
|
2649
|
+
private checkAllSourcesReady;
|
|
2650
|
+
private syncVideoToClock;
|
|
2651
|
+
private rebuildStateAtTime;
|
|
2652
|
+
private waitForVideoSeek;
|
|
2653
|
+
private loadResumePosition;
|
|
2654
|
+
private saveResumePosition;
|
|
2655
|
+
private clearBufferingDebounce;
|
|
2656
|
+
/**
|
|
2657
|
+
* Update snapshot and notify listeners.
|
|
2658
|
+
* This is the ONLY way to mutate the snapshot.
|
|
2659
|
+
*/
|
|
2660
|
+
private updateSnapshot;
|
|
2661
|
+
private emitSnapshot;
|
|
2662
|
+
}
|
|
2663
|
+
|
|
2664
|
+
/**
|
|
2665
|
+
* usePlayerEngine — Thin React binding for PlayerEngine
|
|
2666
|
+
*
|
|
2667
|
+
* 1 ref, 1 state, 1 effect, 1 memo. That's the entire React layer.
|
|
2668
|
+
* Replaces 65 refs, 120 effects from the old hooks.
|
|
2669
|
+
*
|
|
2670
|
+
* @packageDocumentation
|
|
2671
|
+
*/
|
|
2672
|
+
|
|
2673
|
+
interface PlayerControls$1 {
|
|
2674
|
+
play(): void;
|
|
2675
|
+
pause(): void;
|
|
2676
|
+
seek(timeMs: number): Promise<void>;
|
|
2677
|
+
setSpeed(speed: number): void;
|
|
2678
|
+
setVolume(level: number): void;
|
|
2679
|
+
setMuted(muted: boolean): void;
|
|
2680
|
+
setVideoVisible(visible: boolean): void;
|
|
2681
|
+
initVideo(element: HTMLVideoElement | null): void;
|
|
2682
|
+
getAudioElement(): HTMLAudioElement | null;
|
|
2683
|
+
getVideoElement(): HTMLVideoElement | null;
|
|
2684
|
+
getVideoQualityLevels(): HLSQualityLevel[];
|
|
2685
|
+
setVideoQualityLevel(index: number): void;
|
|
2686
|
+
getVideoQualityLevel(): number;
|
|
2687
|
+
resumeAudioContext(): void;
|
|
2688
|
+
getSourceAdapter(): MediaSourceAdapter | null;
|
|
2689
|
+
}
|
|
2690
|
+
interface UsePlayerEngineResult {
|
|
2691
|
+
snapshot: PlayerSnapshot;
|
|
2692
|
+
controls: PlayerControls$1;
|
|
2693
|
+
}
|
|
2694
|
+
declare function usePlayerEngine(config: PlayerEngineConfig): UsePlayerEngineResult;
|
|
2695
|
+
|
|
2696
|
+
/**
|
|
2697
|
+
* ErrorBoundary — catches render errors in the player component tree.
|
|
2698
|
+
* Class component because React error boundaries require getDerivedStateFromError.
|
|
2699
|
+
*/
|
|
2700
|
+
|
|
2701
|
+
interface ErrorBoundaryProps {
|
|
2702
|
+
/** Fallback UI to render on error */
|
|
2703
|
+
fallback: React__default__default.ReactNode | ((error: Error) => React__default__default.ReactNode);
|
|
2704
|
+
/** Optional error callback */
|
|
2705
|
+
onError?: (error: Error, errorInfo: React__default__default.ErrorInfo) => void;
|
|
2706
|
+
children: React__default__default.ReactNode;
|
|
2707
|
+
}
|
|
2708
|
+
interface ErrorBoundaryState {
|
|
2709
|
+
error: Error | null;
|
|
2710
|
+
}
|
|
2711
|
+
declare class PlayerErrorBoundary extends React__default__default.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
|
2712
|
+
state: ErrorBoundaryState;
|
|
2713
|
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
2714
|
+
componentDidCatch(error: Error, info: React__default__default.ErrorInfo): void;
|
|
2715
|
+
render(): React__default__default.ReactNode;
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
/**
|
|
2719
|
+
* HlsStreamingAdapter — MediaSourceAdapter for CDN/HLS streaming.
|
|
2720
|
+
*
|
|
2721
|
+
* Wraps a manifest URL and provides HLS audio, chunked events,
|
|
2722
|
+
* and (optionally) HLS video sources to the PlayerEngine.
|
|
2723
|
+
*
|
|
2724
|
+
* @packageDocumentation
|
|
2725
|
+
*/
|
|
2726
|
+
|
|
2727
|
+
declare class HlsStreamingAdapter implements MediaSourceAdapter {
|
|
2728
|
+
private manifestUrl;
|
|
2729
|
+
private manifest;
|
|
2730
|
+
constructor(manifestUrl: string);
|
|
2731
|
+
/** Inject a pre-fetched manifest (avoids double fetch when engine already loaded it). */
|
|
2732
|
+
setManifest(manifest: StreamingManifest): void;
|
|
2733
|
+
private ensureManifest;
|
|
2734
|
+
getAudioSource(): Promise<AudioSourceResult>;
|
|
2735
|
+
getEventsSource(): Promise<EventsSourceResult>;
|
|
2736
|
+
getRecordingInfo(): RecordingInfo;
|
|
2737
|
+
getVideoSource(): Promise<VideoSourceResult>;
|
|
2738
|
+
getManifest(): StreamingManifest | null;
|
|
2739
|
+
dispose(): void;
|
|
2740
|
+
}
|
|
2741
|
+
|
|
2742
|
+
/**
|
|
2743
|
+
* LocalFileAdapter — MediaSourceAdapter for local .ct file playback.
|
|
2744
|
+
*
|
|
2745
|
+
* Wraps a CTRecording, exposing its mediaBlob as a blob-URL audio source
|
|
2746
|
+
* and its events as inline events (no chunk fetching needed).
|
|
2747
|
+
*
|
|
2748
|
+
* @packageDocumentation
|
|
2749
|
+
*/
|
|
2750
|
+
|
|
2751
|
+
declare class LocalFileAdapter implements MediaSourceAdapter {
|
|
2752
|
+
private recording;
|
|
2753
|
+
private blobUrl;
|
|
2754
|
+
constructor(recording: CTRecording);
|
|
2755
|
+
getAudioSource(): Promise<AudioSourceResult>;
|
|
2756
|
+
getEventsSource(): Promise<EventsSourceResult>;
|
|
2757
|
+
getRecordingInfo(): RecordingInfo;
|
|
2758
|
+
getVideoSource(): Promise<VideoSourceResult>;
|
|
2759
|
+
getManifest(): StreamingManifest | null;
|
|
2760
|
+
/** Get the underlying recording for interactive mode features. */
|
|
2761
|
+
getRecording(): CTRecording;
|
|
2762
|
+
dispose(): void;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
/**
|
|
2766
|
+
* Learner Branch Types
|
|
2767
|
+
*
|
|
2768
|
+
* A branch is a saved snapshot of the learner's code edits at a specific
|
|
2769
|
+
* moment in the instructor's timeline. Branches are learner-side only —
|
|
2770
|
+
* they never modify the .ct file, events, or manifest.
|
|
2771
|
+
*
|
|
2772
|
+
* @packageDocumentation
|
|
2773
|
+
*/
|
|
2774
|
+
/**
|
|
2775
|
+
* A point-in-time capture of the IDE state.
|
|
2776
|
+
* Used for both the instructor's original state and the learner's edited state.
|
|
2777
|
+
*/
|
|
2778
|
+
interface BranchSnapshot {
|
|
2779
|
+
/** Active file's code content */
|
|
2780
|
+
code: string;
|
|
2781
|
+
/** Programming language at snapshot time */
|
|
2782
|
+
language: string;
|
|
2783
|
+
/** Which file tab was active (null if single-file mode) */
|
|
2784
|
+
currentFile: string | null;
|
|
2785
|
+
/** Full file system snapshot — path → content */
|
|
2786
|
+
fileSystem: Record<string, string>;
|
|
2787
|
+
/** Terminal output lines if any existed */
|
|
2788
|
+
terminalOutput?: string[];
|
|
2789
|
+
/** Which tool panel was open (code / terminal / whiteboard / document) */
|
|
2790
|
+
activeTool: string;
|
|
2791
|
+
/** Folder paths created by the learner during interactive mode */
|
|
2792
|
+
folders?: string[];
|
|
2793
|
+
/** Base64 snapshot of the sql.js database (learner's modified DB) */
|
|
2794
|
+
dbSnapshot?: string;
|
|
2795
|
+
/** The seed SQL used to initialize the database */
|
|
2796
|
+
dbSeedSql?: string;
|
|
2797
|
+
/** Current database schema at snapshot time */
|
|
2798
|
+
dbSchema?: {
|
|
2799
|
+
name: string;
|
|
2800
|
+
sql: string;
|
|
2801
|
+
}[];
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* A learner branch — a saved code snapshot at a point in the timeline.
|
|
2805
|
+
*/
|
|
2806
|
+
interface LearnerBranch {
|
|
2807
|
+
/** Unique branch ID (crypto.randomUUID) */
|
|
2808
|
+
id: string;
|
|
2809
|
+
/** Lesson/recording this branch belongs to */
|
|
2810
|
+
lessonId: string;
|
|
2811
|
+
/** Playback time in milliseconds where the learner paused */
|
|
2812
|
+
timeMs: number;
|
|
2813
|
+
/** Instructor's state at that moment (for comparison / restoring) */
|
|
2814
|
+
originalState: BranchSnapshot;
|
|
2815
|
+
/** Learner's edited state */
|
|
2816
|
+
learnerState: BranchSnapshot;
|
|
2817
|
+
/** ISO timestamp of when the branch was created */
|
|
2818
|
+
createdAt: string;
|
|
2819
|
+
/** User-provided label, defaults to "Branch at MM:SS" */
|
|
2820
|
+
label?: string;
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2823
|
+
/**
|
|
2824
|
+
* @ct-courses/player - Type Definitions
|
|
2825
|
+
*/
|
|
2826
|
+
|
|
2827
|
+
/**
|
|
2828
|
+
* Backend adapter interface for loading recordings
|
|
2829
|
+
*/
|
|
2830
|
+
interface BackendAdapter {
|
|
2831
|
+
getRecording(id: string): Promise<InternalRecording | null>;
|
|
2832
|
+
getMediaUrl?(recordingId: string): Promise<string | null>;
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Internal recording structure (from backend)
|
|
2836
|
+
*/
|
|
2837
|
+
interface InternalRecording {
|
|
2838
|
+
id: string;
|
|
2839
|
+
name?: string;
|
|
2840
|
+
duration: number;
|
|
2841
|
+
events: InternalEvent[];
|
|
2842
|
+
markers?: RecordingMarker[];
|
|
2843
|
+
hasMedia?: boolean;
|
|
2844
|
+
mediaUrl?: string;
|
|
2845
|
+
createdAt?: number;
|
|
2846
|
+
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Internal event structure (from backend)
|
|
2849
|
+
*/
|
|
2850
|
+
interface InternalEvent {
|
|
2851
|
+
type: string;
|
|
2852
|
+
time: number;
|
|
2853
|
+
data: Record<string, unknown>;
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Recording marker for chapters
|
|
2857
|
+
*/
|
|
2858
|
+
interface RecordingMarker {
|
|
2859
|
+
id: string;
|
|
2860
|
+
time: number;
|
|
2861
|
+
label: string;
|
|
2862
|
+
color?: string;
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Cursor position for replay
|
|
2866
|
+
*/
|
|
2867
|
+
interface CursorPosition {
|
|
2868
|
+
x: number;
|
|
2869
|
+
y: number;
|
|
2870
|
+
visible: boolean;
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* File system snapshot
|
|
2874
|
+
*/
|
|
2875
|
+
interface FileSystemSnapshot {
|
|
2876
|
+
[path: string]: string;
|
|
2877
|
+
}
|
|
2878
|
+
/**
|
|
2879
|
+
* Document data with content (extends DocumentInfo with raw data)
|
|
2880
|
+
*/
|
|
2881
|
+
interface PlayerDocumentData extends DocumentInfo {
|
|
2882
|
+
/** Raw document data (base64 for PDFs, etc.) */
|
|
2883
|
+
data: string;
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Playback engine configuration
|
|
2887
|
+
*/
|
|
2888
|
+
interface PlaybackEngineConfig {
|
|
2889
|
+
/** Recording ID to load */
|
|
2890
|
+
recordingId?: string;
|
|
2891
|
+
/** Backend adapter for loading */
|
|
2892
|
+
backendAdapter?: BackendAdapter;
|
|
2893
|
+
/** Pre-loaded recording */
|
|
2894
|
+
recording?: InternalRecording;
|
|
2895
|
+
/** CT file blob to load */
|
|
2896
|
+
ctFile?: Blob;
|
|
2897
|
+
/** Auto-play on load */
|
|
2898
|
+
autoPlay?: boolean;
|
|
2899
|
+
/** Initial playback speed */
|
|
2900
|
+
initialSpeed?: number;
|
|
2901
|
+
/** Initial volume (0-1) */
|
|
2902
|
+
initialVolume?: number;
|
|
2903
|
+
onReady?: () => void;
|
|
2904
|
+
onPlay?: () => void;
|
|
2905
|
+
onPause?: () => void;
|
|
2906
|
+
onEnded?: () => void;
|
|
2907
|
+
onTimeUpdate?: (time: number) => void;
|
|
2908
|
+
onError?: (error: Error) => void;
|
|
2909
|
+
}
|
|
2910
|
+
/**
|
|
2911
|
+
* Playback engine state
|
|
2912
|
+
*/
|
|
2913
|
+
interface PlaybackState {
|
|
2326
2914
|
loading: boolean;
|
|
2327
2915
|
error: string | null;
|
|
2328
2916
|
recording: InternalRecording | null;
|
|
@@ -2353,7 +2941,7 @@ interface PlaybackState {
|
|
|
2353
2941
|
/**
|
|
2354
2942
|
* Playback controls
|
|
2355
2943
|
*/
|
|
2356
|
-
interface PlaybackControls
|
|
2944
|
+
interface PlaybackControls {
|
|
2357
2945
|
setAudioElement: (element: HTMLAudioElement | null) => void;
|
|
2358
2946
|
setVolume: (volume: number) => void;
|
|
2359
2947
|
setMuted: (muted: boolean) => void;
|
|
@@ -2380,7 +2968,7 @@ interface PlaybackControls$1 {
|
|
|
2380
2968
|
/**
|
|
2381
2969
|
* Full playback engine result
|
|
2382
2970
|
*/
|
|
2383
|
-
type UsePlaybackEngineResult = PlaybackState & PlaybackControls
|
|
2971
|
+
type UsePlaybackEngineResult = PlaybackState & PlaybackControls;
|
|
2384
2972
|
/**
|
|
2385
2973
|
* Player component props
|
|
2386
2974
|
*/
|
|
@@ -2521,93 +3109,53 @@ interface MCQResult {
|
|
|
2521
3109
|
correctOptionIds?: string[];
|
|
2522
3110
|
}
|
|
2523
3111
|
|
|
2524
|
-
|
|
2525
|
-
* Active tool type - matches ToolType from @ct-courses/ui
|
|
2526
|
-
*/
|
|
2527
|
-
type ActiveTool$1 = ToolType;
|
|
3112
|
+
type ActiveTool$2 = ToolType;
|
|
2528
3113
|
/**
|
|
2529
3114
|
* Props for the CoursePlayer component
|
|
2530
3115
|
*/
|
|
2531
3116
|
interface CoursePlayerProps {
|
|
2532
|
-
/** Pre-loaded recording data */
|
|
2533
3117
|
recording?: CTRecording;
|
|
2534
|
-
/** URL to a .ct file */
|
|
2535
3118
|
recordingUrl?: string;
|
|
2536
|
-
/** Recording ID to fetch from API */
|
|
2537
3119
|
recordingId?: string;
|
|
2538
|
-
/** Base API URL for fetching recordings */
|
|
2539
3120
|
apiUrl?: string;
|
|
2540
|
-
/** Authentication token */
|
|
2541
3121
|
authToken?: string;
|
|
2542
|
-
/** Go-Judge server URL */
|
|
2543
3122
|
goJudgeUrl?: string;
|
|
2544
|
-
/** Go-Judge API key */
|
|
2545
3123
|
goJudgeApiKey?: string;
|
|
2546
|
-
/** Theme setting */
|
|
2547
3124
|
theme?: "light" | "dark" | "system";
|
|
2548
|
-
/** Aspect ratio of the player */
|
|
2549
3125
|
aspectRatio?: "16:9" | "4:3" | "auto";
|
|
2550
|
-
/** Show playback controls toolbar */
|
|
2551
3126
|
showToolbar?: boolean;
|
|
2552
|
-
/** Show timeline below player */
|
|
2553
3127
|
showTimeline?: boolean;
|
|
2554
|
-
/** Show chapter markers */
|
|
2555
3128
|
showChapters?: boolean;
|
|
2556
|
-
/** Show tool sidebar */
|
|
2557
3129
|
showToolSidebar?: boolean;
|
|
2558
|
-
/** Auto-play when loaded */
|
|
2559
3130
|
autoPlay?: boolean;
|
|
2560
|
-
/** Default playback speed */
|
|
2561
3131
|
defaultSpeed?: number;
|
|
2562
|
-
/** Allow fullscreen mode */
|
|
2563
3132
|
allowFullscreen?: boolean;
|
|
2564
|
-
|
|
2565
|
-
defaultTool?: ActiveTool$1;
|
|
2566
|
-
/** Called when player is ready */
|
|
3133
|
+
defaultTool?: ActiveTool$2;
|
|
2567
3134
|
onReady?: () => void;
|
|
2568
|
-
/** Called when playback starts */
|
|
2569
3135
|
onPlay?: () => void;
|
|
2570
|
-
/** Called when playback pauses */
|
|
2571
3136
|
onPause?: () => void;
|
|
2572
|
-
/** Called on progress update */
|
|
2573
3137
|
onProgress?: (time: number, duration: number) => void;
|
|
2574
|
-
/** Called when playback completes */
|
|
2575
3138
|
onComplete?: () => void;
|
|
2576
|
-
/** Called on error */
|
|
2577
3139
|
onError?: (error: Error) => void;
|
|
2578
|
-
/** Called when entering interactive mode */
|
|
2579
3140
|
onInteractionStart?: () => void;
|
|
2580
|
-
/** Called when exiting interactive mode */
|
|
2581
3141
|
onInteractionEnd?: (code: string) => void;
|
|
2582
|
-
/** Called when tool changes */
|
|
2583
3142
|
onToolChange?: (tool: string) => void;
|
|
2584
|
-
/** Interaction points (MCQs) to display during playback */
|
|
2585
3143
|
interactionPoints?: InteractionPoint[];
|
|
2586
|
-
/** Pre-answered point IDs (skip these MCQs) */
|
|
2587
3144
|
answeredPointIds?: Set<string>;
|
|
2588
|
-
/** Called when a learner submits an MCQ answer. Host app validates server-side. */
|
|
2589
3145
|
onSubmitAnswer?: (pointId: string, selectedOptionIds: string[]) => Promise<MCQResult>;
|
|
2590
|
-
/** Info about the next lesson for auto-advance overlay */
|
|
2591
3146
|
nextLesson?: {
|
|
2592
3147
|
id: string;
|
|
2593
3148
|
title: string;
|
|
2594
3149
|
} | null;
|
|
2595
|
-
/** Called when the player wants to navigate to the next lesson */
|
|
2596
3150
|
onNavigateToLesson?: (lessonId: string) => void;
|
|
2597
|
-
/** Called when the current lesson completes (before transition) */
|
|
2598
3151
|
onLessonComplete?: () => void;
|
|
2599
|
-
/** Lesson ID used to scope saved branches in localStorage */
|
|
2600
3152
|
lessonId?: string;
|
|
2601
|
-
/** Enable the branch save feature (default: true) */
|
|
2602
3153
|
enableBranches?: boolean;
|
|
2603
|
-
/** Called after a branch is saved */
|
|
2604
3154
|
onBranchSave?: (branch: LearnerBranch) => void;
|
|
2605
|
-
/** Called when a learner restores a saved branch */
|
|
2606
3155
|
onBranchRestore?: (branch: LearnerBranch) => void;
|
|
2607
|
-
/** Called after a branch is deleted */
|
|
2608
3156
|
onBranchDelete?: (branchId: string) => void;
|
|
2609
|
-
/** Maximum branches per lesson (default: 50) */
|
|
2610
3157
|
maxBranches?: number;
|
|
3158
|
+
sqlJsWasmUrl?: string;
|
|
2611
3159
|
}
|
|
2612
3160
|
/**
|
|
2613
3161
|
* Imperative handle for CoursePlayer
|
|
@@ -2622,81 +3170,58 @@ interface CoursePlayerRef {
|
|
|
2622
3170
|
getState(): PlaybackState$1;
|
|
2623
3171
|
enterInteractiveMode(): void;
|
|
2624
3172
|
exitInteractiveMode(): void;
|
|
2625
|
-
setActiveTool(tool: ActiveTool$
|
|
3173
|
+
setActiveTool(tool: ActiveTool$2): void;
|
|
2626
3174
|
}
|
|
2627
|
-
/**
|
|
2628
|
-
* CoursePlayer component
|
|
2629
|
-
*/
|
|
2630
3175
|
declare const CoursePlayer: React__default.ForwardRefExoticComponent<CoursePlayerProps & React__default.RefAttributes<CoursePlayerRef>>;
|
|
2631
3176
|
|
|
2632
|
-
type ActiveTool = ToolType;
|
|
3177
|
+
type ActiveTool$1 = ToolType;
|
|
2633
3178
|
/**
|
|
2634
3179
|
* Props for StreamingCoursePlayer
|
|
2635
3180
|
*/
|
|
2636
3181
|
interface StreamingCoursePlayerProps {
|
|
2637
3182
|
/** URL to the streaming manifest.json */
|
|
2638
3183
|
manifestUrl: string;
|
|
2639
|
-
/** Theme setting */
|
|
2640
3184
|
theme?: "light" | "dark" | "system";
|
|
2641
|
-
/** Aspect ratio of the player */
|
|
2642
3185
|
aspectRatio?: "16:9" | "4:3" | "auto";
|
|
2643
|
-
/** Show playback controls toolbar */
|
|
2644
3186
|
showToolbar?: boolean;
|
|
2645
|
-
/** Show tool sidebar */
|
|
2646
3187
|
showToolSidebar?: boolean;
|
|
2647
|
-
/** Auto-play when loaded */
|
|
2648
3188
|
autoPlay?: boolean;
|
|
2649
|
-
/** Default playback speed */
|
|
2650
3189
|
defaultSpeed?: number;
|
|
2651
|
-
/** Allow fullscreen mode */
|
|
2652
3190
|
allowFullscreen?: boolean;
|
|
2653
|
-
|
|
2654
|
-
defaultTool?: ActiveTool;
|
|
2655
|
-
/** Maximum chunks to cache */
|
|
3191
|
+
defaultTool?: ActiveTool$1;
|
|
2656
3192
|
maxCacheSize?: number;
|
|
2657
|
-
|
|
3193
|
+
showWebcam?: boolean;
|
|
2658
3194
|
className?: string;
|
|
2659
|
-
/** Called when player is ready */
|
|
2660
3195
|
onReady?: () => void;
|
|
2661
|
-
/** Called when playback starts */
|
|
2662
3196
|
onPlay?: () => void;
|
|
2663
|
-
/** Called when playback pauses */
|
|
2664
3197
|
onPause?: () => void;
|
|
2665
|
-
/** Called on progress update */
|
|
2666
3198
|
onProgress?: (time: number, duration: number) => void;
|
|
2667
|
-
/** Called when playback completes */
|
|
2668
3199
|
onComplete?: () => void;
|
|
2669
|
-
/** Called on error */
|
|
2670
3200
|
onError?: (error: Error) => void;
|
|
2671
|
-
/** Called when buffering state changes */
|
|
2672
3201
|
onBuffering?: (isBuffering: boolean) => void;
|
|
2673
|
-
/** Called when tool changes */
|
|
2674
3202
|
onToolChange?: (tool: string) => void;
|
|
2675
|
-
/** Go-Judge server URL */
|
|
2676
3203
|
goJudgeUrl?: string;
|
|
2677
|
-
/** Go-Judge API key */
|
|
2678
3204
|
goJudgeApiKey?: string;
|
|
2679
|
-
/** Show chapter markers */
|
|
2680
3205
|
showChapters?: boolean;
|
|
2681
|
-
|
|
3206
|
+
showTimeline?: boolean;
|
|
2682
3207
|
onInteractionStart?: () => void;
|
|
2683
|
-
/** Called when exiting interactive mode */
|
|
2684
3208
|
onInteractionEnd?: (code: string) => void;
|
|
2685
|
-
/** Interaction points (MCQs) to display during playback */
|
|
2686
3209
|
interactionPoints?: InteractionPoint[];
|
|
2687
|
-
/** Pre-answered point IDs (skip these MCQs) */
|
|
2688
3210
|
answeredPointIds?: Set<string>;
|
|
2689
|
-
/** Called when a learner submits an MCQ answer. Host app validates server-side. */
|
|
2690
3211
|
onSubmitAnswer?: (pointId: string, selectedOptionIds: string[]) => Promise<MCQResult>;
|
|
2691
|
-
/** Info about the next lesson for auto-advance overlay */
|
|
2692
3212
|
nextLesson?: {
|
|
2693
3213
|
id: string;
|
|
2694
3214
|
title: string;
|
|
2695
3215
|
} | null;
|
|
2696
|
-
/** Called when the player wants to navigate to the next lesson */
|
|
2697
3216
|
onNavigateToLesson?: (lessonId: string) => void;
|
|
2698
|
-
/** Called when the current lesson completes (before transition) */
|
|
2699
3217
|
onLessonComplete?: () => void;
|
|
3218
|
+
lessonId?: string;
|
|
3219
|
+
enableBranches?: boolean;
|
|
3220
|
+
onBranchSave?: (branch: LearnerBranch) => void;
|
|
3221
|
+
onBranchRestore?: (branch: LearnerBranch) => void;
|
|
3222
|
+
onBranchDelete?: (branchId: string) => void;
|
|
3223
|
+
maxBranches?: number;
|
|
3224
|
+
sqlJsWasmUrl?: string;
|
|
2700
3225
|
}
|
|
2701
3226
|
/**
|
|
2702
3227
|
* Imperative handle for StreamingCoursePlayer
|
|
@@ -2711,398 +3236,72 @@ interface StreamingCoursePlayerRef {
|
|
|
2711
3236
|
getState(): PlaybackState$1;
|
|
2712
3237
|
enterInteractiveMode(): void;
|
|
2713
3238
|
exitInteractiveMode(): void;
|
|
2714
|
-
setActiveTool(tool: ActiveTool): void;
|
|
3239
|
+
setActiveTool(tool: ActiveTool$1): void;
|
|
2715
3240
|
getManifest(): StreamingManifest | null;
|
|
2716
3241
|
getBufferHealth(): RemoteBufferHealth;
|
|
2717
3242
|
}
|
|
2718
3243
|
declare const StreamingCoursePlayer: React__default.ForwardRefExoticComponent<StreamingCoursePlayerProps & React__default.RefAttributes<StreamingCoursePlayerRef>>;
|
|
2719
3244
|
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
* - Events are applied based on audio.currentTime
|
|
2729
|
-
* - Without media, use requestAnimationFrame timing
|
|
2730
|
-
*/
|
|
2731
|
-
|
|
2732
|
-
/**
|
|
2733
|
-
* Playback state returned by the hook
|
|
2734
|
-
*/
|
|
2735
|
-
interface PlaybackHookState {
|
|
2736
|
-
/** Current playback state */
|
|
2737
|
-
state: PlaybackState$1;
|
|
2738
|
-
/** Current time in milliseconds */
|
|
2739
|
-
currentTime: number;
|
|
2740
|
-
/** Total duration in milliseconds */
|
|
2741
|
-
duration: number;
|
|
2742
|
-
/** Whether currently playing */
|
|
2743
|
-
isPlaying: boolean;
|
|
2744
|
-
/** Current playback speed */
|
|
2745
|
-
speed: number;
|
|
2746
|
-
/** Whether recording is loaded */
|
|
2747
|
-
isLoaded: boolean;
|
|
2748
|
-
/** Volume level (0-1) */
|
|
2749
|
-
volume: number;
|
|
2750
|
-
/** Whether audio is muted */
|
|
2751
|
-
muted: boolean;
|
|
2752
|
-
/** Whether user is interacting (paused with edits) */
|
|
2753
|
-
isInteracting: boolean;
|
|
2754
|
-
}
|
|
2755
|
-
/**
|
|
2756
|
-
* Playback controls
|
|
2757
|
-
*/
|
|
2758
|
-
interface PlaybackControls {
|
|
2759
|
-
play(): void;
|
|
2760
|
-
pause(): void;
|
|
2761
|
-
seek(timeMs: number): void;
|
|
2762
|
-
setSpeed(speed: number): void;
|
|
2763
|
-
setVolume(volume: number): void;
|
|
2764
|
-
setMuted(muted: boolean): void;
|
|
2765
|
-
setAudioElement(audio: HTMLAudioElement | null): void;
|
|
2766
|
-
setVideoElement(video: HTMLVideoElement | null): void;
|
|
2767
|
-
enterInteractiveMode(): void;
|
|
2768
|
-
exitInteractiveMode(): void;
|
|
2769
|
-
setInteractiveCode(code: string): void;
|
|
2770
|
-
setInteractiveLanguage(language: string): void;
|
|
2771
|
-
setInteractiveTerminalLines(lines: PlaybackState$1['terminalLines']): void;
|
|
2772
|
-
addInteractiveTerminalLines(lines: PlaybackState$1['terminalLines']): void;
|
|
2773
|
-
setInteractiveExcalidrawScene(scene: PlaybackState$1['excalidrawScene']): void;
|
|
2774
|
-
setInteractiveDocumentPage(page: number): void;
|
|
2775
|
-
setInteractiveDocumentZoom(zoom: number): void;
|
|
2776
|
-
setInteractiveDocumentScroll(scroll: {
|
|
2777
|
-
x: number;
|
|
2778
|
-
y: number;
|
|
2779
|
-
}): void;
|
|
2780
|
-
/** Inject a full branch snapshot into the interactive state. Only works when isInteracting is true. */
|
|
2781
|
-
injectInteractiveState(snapshot: BranchSnapshot): void;
|
|
2782
|
-
/** Get the engine's (instructor's) state at a given time, without affecting playback position. */
|
|
2783
|
-
getEngineStateAtTime(timeMs: number): PlaybackState$1 | null;
|
|
2784
|
-
}
|
|
2785
|
-
/**
|
|
2786
|
-
* Options for usePlayback hook
|
|
2787
|
-
*/
|
|
2788
|
-
interface UsePlaybackOptions {
|
|
2789
|
-
recording: CTRecording | null;
|
|
3245
|
+
interface SharedProps {
|
|
3246
|
+
theme?: "light" | "dark" | "system";
|
|
3247
|
+
aspectRatio?: "16:9" | "4:3" | "auto";
|
|
3248
|
+
showToolbar?: boolean;
|
|
3249
|
+
showTimeline?: boolean;
|
|
3250
|
+
showChapters?: boolean;
|
|
3251
|
+
showToolSidebar?: boolean;
|
|
3252
|
+
className?: string;
|
|
2790
3253
|
autoPlay?: boolean;
|
|
2791
3254
|
defaultSpeed?: number;
|
|
2792
|
-
|
|
3255
|
+
allowFullscreen?: boolean;
|
|
3256
|
+
defaultTool?: ToolType;
|
|
3257
|
+
onReady?: () => void;
|
|
2793
3258
|
onPlay?: () => void;
|
|
2794
3259
|
onPause?: () => void;
|
|
2795
3260
|
onProgress?: (time: number, duration: number) => void;
|
|
2796
3261
|
onComplete?: () => void;
|
|
2797
3262
|
onError?: (error: Error) => void;
|
|
2798
|
-
}
|
|
2799
|
-
/**
|
|
2800
|
-
* Hook for managing playback of a CT recording
|
|
2801
|
-
* Supports audio synchronization as master clock
|
|
2802
|
-
*/
|
|
2803
|
-
declare function usePlayback(options: UsePlaybackOptions): PlaybackHookState & {
|
|
2804
|
-
controls: PlaybackControls;
|
|
2805
|
-
};
|
|
2806
|
-
|
|
2807
|
-
/**
|
|
2808
|
-
* useStreamingPlayback Hook
|
|
2809
|
-
*
|
|
2810
|
-
* Streaming playback engine hook for large recordings.
|
|
2811
|
-
* Supports lazy chunk loading, adaptive buffering, and efficient seeking.
|
|
2812
|
-
*
|
|
2813
|
-
* @packageDocumentation
|
|
2814
|
-
*/
|
|
2815
|
-
|
|
2816
|
-
/**
|
|
2817
|
-
* Buffer health status
|
|
2818
|
-
*/
|
|
2819
|
-
interface BufferHealth {
|
|
2820
|
-
status: 'healthy' | 'buffering' | 'critical' | 'empty';
|
|
2821
|
-
loadedChunks: number;
|
|
2822
|
-
totalChunks: number;
|
|
2823
|
-
currentChunk: number;
|
|
2824
|
-
bufferedTimeMs: number;
|
|
2825
|
-
percentLoaded: number;
|
|
2826
|
-
}
|
|
2827
|
-
/**
|
|
2828
|
-
* Streaming metrics for performance monitoring
|
|
2829
|
-
*/
|
|
2830
|
-
interface StreamingMetrics {
|
|
2831
|
-
avgChunkLoadTimeMs: number;
|
|
2832
|
-
chunkCacheHitRate: number;
|
|
2833
|
-
bufferUnderrunCount: number;
|
|
2834
|
-
avgBufferHealthPercent: number;
|
|
2835
|
-
avgSeekTimeMs: number;
|
|
2836
|
-
seekCacheHitRate: number;
|
|
2837
|
-
peakChunksCached: number;
|
|
2838
|
-
totalBytesLoaded: number;
|
|
2839
|
-
}
|
|
2840
|
-
/**
|
|
2841
|
-
* Streaming playback state
|
|
2842
|
-
*/
|
|
2843
|
-
interface StreamingPlaybackState {
|
|
2844
|
-
state: PlaybackState$1;
|
|
2845
|
-
currentTime: number;
|
|
2846
|
-
duration: number;
|
|
2847
|
-
isPlaying: boolean;
|
|
2848
|
-
speed: number;
|
|
2849
|
-
isLoaded: boolean;
|
|
2850
|
-
isBuffering: boolean;
|
|
2851
|
-
bufferHealth: BufferHealth;
|
|
2852
|
-
isStreaming: boolean;
|
|
2853
|
-
metrics: StreamingMetrics;
|
|
2854
|
-
}
|
|
2855
|
-
/**
|
|
2856
|
-
* Streaming playback controls
|
|
2857
|
-
*/
|
|
2858
|
-
interface StreamingPlaybackControls {
|
|
2859
|
-
play(): void;
|
|
2860
|
-
pause(): void;
|
|
2861
|
-
seek(timeMs: number): Promise<void>;
|
|
2862
|
-
setSpeed(speed: number): void;
|
|
2863
|
-
preloadRange(startMs: number, endMs: number): Promise<void>;
|
|
2864
|
-
}
|
|
2865
|
-
/**
|
|
2866
|
-
* Options for useStreamingPlayback hook
|
|
2867
|
-
*/
|
|
2868
|
-
interface UseStreamingPlaybackOptions {
|
|
2869
|
-
recording: CTRecording | null;
|
|
2870
|
-
chunkIndex?: CTEventChunkIndex | null;
|
|
2871
|
-
loadChunk?: (chunkNumber: number) => Promise<ChunkLoadResult>;
|
|
2872
|
-
autoPlay?: boolean;
|
|
2873
|
-
defaultSpeed?: number;
|
|
2874
|
-
bufferAhead?: number;
|
|
2875
|
-
bufferBehind?: number;
|
|
2876
|
-
maxChunksInMemory?: number;
|
|
2877
|
-
onPlay?: () => void;
|
|
2878
|
-
onPause?: () => void;
|
|
2879
|
-
onProgress?: (time: number, duration: number) => void;
|
|
2880
|
-
onComplete?: () => void;
|
|
2881
3263
|
onBuffering?: (isBuffering: boolean) => void;
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
* @packageDocumentation
|
|
2904
|
-
*/
|
|
2905
|
-
|
|
2906
|
-
/**
|
|
2907
|
-
* Hook for remote streaming playback from CDN
|
|
2908
|
-
*/
|
|
2909
|
-
declare function useRemoteStreamingPlayback(options: UseRemoteStreamingPlaybackOptions): RemoteStreamingPlaybackState & {
|
|
2910
|
-
controls: RemoteStreamingPlaybackControls;
|
|
2911
|
-
};
|
|
2912
|
-
|
|
2913
|
-
/**
|
|
2914
|
-
* useInteractiveMode Hook
|
|
2915
|
-
*
|
|
2916
|
-
* Manages interactive mode where users can edit code while paused.
|
|
2917
|
-
*/
|
|
2918
|
-
|
|
2919
|
-
/**
|
|
2920
|
-
* Interactive mode state
|
|
2921
|
-
*/
|
|
2922
|
-
interface InteractiveModeState {
|
|
2923
|
-
/** Whether interactive mode is active */
|
|
2924
|
-
isActive: boolean;
|
|
2925
|
-
/** Modified code (user edits) */
|
|
2926
|
-
modifiedCode: string;
|
|
2927
|
-
/** Whether code has been modified from original */
|
|
2928
|
-
hasChanges: boolean;
|
|
2929
|
-
}
|
|
2930
|
-
/**
|
|
2931
|
-
* Interactive mode controls
|
|
2932
|
-
*/
|
|
2933
|
-
interface InteractiveModeControls {
|
|
2934
|
-
/** Enter interactive mode */
|
|
2935
|
-
enter(): void;
|
|
2936
|
-
/** Exit interactive mode */
|
|
2937
|
-
exit(): void;
|
|
2938
|
-
/** Update the modified code */
|
|
2939
|
-
updateCode(code: string): void;
|
|
2940
|
-
/** Reset code to original */
|
|
2941
|
-
resetCode(): void;
|
|
2942
|
-
/** Run the modified code */
|
|
2943
|
-
runCode(): Promise<string>;
|
|
2944
|
-
}
|
|
2945
|
-
/**
|
|
2946
|
-
* Options for useInteractiveMode hook
|
|
2947
|
-
*/
|
|
2948
|
-
interface UseInteractiveModeOptions {
|
|
2949
|
-
/** Current playback state */
|
|
2950
|
-
playbackState: PlaybackState$1;
|
|
2951
|
-
/** Callback when entering interactive mode */
|
|
2952
|
-
onEnter?: () => void;
|
|
2953
|
-
/** Callback when exiting interactive mode */
|
|
2954
|
-
onExit?: (code: string, hasChanges: boolean) => void;
|
|
2955
|
-
/** Code execution function */
|
|
2956
|
-
executeCode?: (code: string, language: string) => Promise<string>;
|
|
2957
|
-
}
|
|
2958
|
-
/**
|
|
2959
|
-
* Hook for managing interactive mode
|
|
2960
|
-
*/
|
|
2961
|
-
declare function useInteractiveMode(options: UseInteractiveModeOptions): InteractiveModeState & {
|
|
2962
|
-
controls: InteractiveModeControls;
|
|
2963
|
-
};
|
|
2964
|
-
|
|
2965
|
-
/**
|
|
2966
|
-
* useAudioSync Hook
|
|
2967
|
-
*
|
|
2968
|
-
* Synchronizes audio playback with event state.
|
|
2969
|
-
* Handles audio/event drift and maintains sync within tolerance.
|
|
2970
|
-
*
|
|
2971
|
-
* @packageDocumentation
|
|
2972
|
-
*/
|
|
2973
|
-
/**
|
|
2974
|
-
* Audio sync state
|
|
2975
|
-
*/
|
|
2976
|
-
interface AudioSyncState {
|
|
2977
|
-
/** Current audio time in ms */
|
|
2978
|
-
audioTime: number;
|
|
2979
|
-
/** Current event time in ms */
|
|
2980
|
-
eventTime: number;
|
|
2981
|
-
/** Drift between audio and events (ms) */
|
|
2982
|
-
drift: number;
|
|
2983
|
-
/** Is sync healthy (within tolerance) */
|
|
2984
|
-
isSynced: boolean;
|
|
2985
|
-
/** Is audio loaded */
|
|
2986
|
-
isAudioReady: boolean;
|
|
2987
|
-
}
|
|
2988
|
-
/**
|
|
2989
|
-
* Audio sync controls
|
|
2990
|
-
*/
|
|
2991
|
-
interface AudioSyncControls {
|
|
2992
|
-
/** Sync events to audio time */
|
|
2993
|
-
syncToAudio(): void;
|
|
2994
|
-
/** Sync audio to event time */
|
|
2995
|
-
syncAudioTo(timeMs: number): void;
|
|
2996
|
-
/** Force resync */
|
|
2997
|
-
forceResync(): void;
|
|
2998
|
-
}
|
|
2999
|
-
/**
|
|
3000
|
-
* Options for useAudioSync hook
|
|
3001
|
-
*/
|
|
3002
|
-
interface UseAudioSyncOptions {
|
|
3003
|
-
/** Reference to audio element */
|
|
3004
|
-
audioRef: React.RefObject<HTMLAudioElement>;
|
|
3005
|
-
/** Current event time */
|
|
3006
|
-
eventTime: number;
|
|
3007
|
-
/** Is currently playing */
|
|
3008
|
-
isPlaying: boolean;
|
|
3009
|
-
/** Playback speed */
|
|
3010
|
-
speed: number;
|
|
3011
|
-
/** Maximum allowed drift in ms before resync (default: 500ms) */
|
|
3012
|
-
driftTolerance?: number;
|
|
3013
|
-
/** Callback when audio time updates */
|
|
3014
|
-
onAudioTimeUpdate?: (timeMs: number) => void;
|
|
3015
|
-
/** Callback when sync is lost */
|
|
3016
|
-
onSyncLost?: (drift: number) => void;
|
|
3017
|
-
/** Callback when sync is restored */
|
|
3018
|
-
onSyncRestored?: () => void;
|
|
3019
|
-
}
|
|
3020
|
-
/**
|
|
3021
|
-
* Hook for synchronizing audio playback with event state
|
|
3022
|
-
*/
|
|
3023
|
-
declare function useAudioSync(options: UseAudioSyncOptions): AudioSyncState & {
|
|
3024
|
-
controls: AudioSyncControls;
|
|
3025
|
-
};
|
|
3026
|
-
/**
|
|
3027
|
-
* Hook for managing audio element lifecycle
|
|
3028
|
-
*/
|
|
3029
|
-
declare function useAudioElement(src: string | null): {
|
|
3030
|
-
audioRef: React__default.MutableRefObject<HTMLAudioElement | null>;
|
|
3031
|
-
isLoaded: boolean;
|
|
3032
|
-
error: Error | null;
|
|
3033
|
-
duration: number;
|
|
3034
|
-
};
|
|
3035
|
-
|
|
3036
|
-
/**
|
|
3037
|
-
* useVideoSync Hook
|
|
3038
|
-
*
|
|
3039
|
-
* Synchronizes video playback with event state.
|
|
3040
|
-
* Handles video/event drift and maintains sync within tolerance.
|
|
3041
|
-
*
|
|
3042
|
-
* @packageDocumentation
|
|
3043
|
-
*/
|
|
3044
|
-
/**
|
|
3045
|
-
* Video sync state
|
|
3046
|
-
*/
|
|
3047
|
-
interface VideoSyncState {
|
|
3048
|
-
/** Current video time in ms */
|
|
3049
|
-
videoTime: number;
|
|
3050
|
-
/** Current event time in ms */
|
|
3051
|
-
eventTime: number;
|
|
3052
|
-
/** Drift between video and events (ms) */
|
|
3053
|
-
drift: number;
|
|
3054
|
-
/** Is sync healthy (within tolerance) */
|
|
3055
|
-
isSynced: boolean;
|
|
3056
|
-
/** Is video loaded */
|
|
3057
|
-
isVideoReady: boolean;
|
|
3058
|
-
}
|
|
3059
|
-
/**
|
|
3060
|
-
* Video sync controls
|
|
3061
|
-
*/
|
|
3062
|
-
interface VideoSyncControls {
|
|
3063
|
-
/** Sync events to video time */
|
|
3064
|
-
syncToVideo(): void;
|
|
3065
|
-
/** Sync video to event time */
|
|
3066
|
-
syncVideoTo(timeMs: number): void;
|
|
3067
|
-
/** Force resync */
|
|
3068
|
-
forceResync(): void;
|
|
3264
|
+
onToolChange?: (tool: string) => void;
|
|
3265
|
+
onInteractionStart?: () => void;
|
|
3266
|
+
onInteractionEnd?: (code: string) => void;
|
|
3267
|
+
goJudgeUrl?: string;
|
|
3268
|
+
goJudgeApiKey?: string;
|
|
3269
|
+
interactionPoints?: InteractionPoint[];
|
|
3270
|
+
answeredPointIds?: Set<string>;
|
|
3271
|
+
onSubmitAnswer?: (pointId: string, selectedOptionIds: string[]) => Promise<MCQResult>;
|
|
3272
|
+
nextLesson?: {
|
|
3273
|
+
id: string;
|
|
3274
|
+
title: string;
|
|
3275
|
+
} | null;
|
|
3276
|
+
onNavigateToLesson?: (lessonId: string) => void;
|
|
3277
|
+
onLessonComplete?: () => void;
|
|
3278
|
+
lessonId?: string;
|
|
3279
|
+
enableBranches?: boolean;
|
|
3280
|
+
onBranchSave?: (branch: LearnerBranch) => void;
|
|
3281
|
+
onBranchRestore?: (branch: LearnerBranch) => void;
|
|
3282
|
+
onBranchDelete?: (branchId: string) => void;
|
|
3283
|
+
maxBranches?: number;
|
|
3284
|
+
sqlJsWasmUrl?: string;
|
|
3069
3285
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
/**
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
onVideoTimeUpdate?: (timeMs: number) => void;
|
|
3086
|
-
/** Callback when sync is lost */
|
|
3087
|
-
onSyncLost?: (drift: number) => void;
|
|
3088
|
-
/** Callback when sync is restored */
|
|
3089
|
-
onSyncRestored?: () => void;
|
|
3286
|
+
interface CTPlayerProps extends SharedProps {
|
|
3287
|
+
/**
|
|
3288
|
+
* Content source URL.
|
|
3289
|
+
* - Ending in `.json` or containing `/manifest` → HLS streaming mode
|
|
3290
|
+
* - Ending in `.ct` or any other URL → local file mode
|
|
3291
|
+
* - Omit if passing `recording` directly
|
|
3292
|
+
*/
|
|
3293
|
+
src?: string;
|
|
3294
|
+
/** Pre-loaded CTRecording object (skips fetch). */
|
|
3295
|
+
recording?: CTRecording;
|
|
3296
|
+
maxCacheSize?: number;
|
|
3297
|
+
showWebcam?: boolean;
|
|
3298
|
+
apiUrl?: string;
|
|
3299
|
+
authToken?: string;
|
|
3300
|
+
recordingId?: string;
|
|
3090
3301
|
}
|
|
3091
|
-
/**
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
declare function useVideoSync(options: UseVideoSyncOptions): VideoSyncState & {
|
|
3095
|
-
controls: VideoSyncControls;
|
|
3096
|
-
};
|
|
3097
|
-
/**
|
|
3098
|
-
* Hook for managing video element lifecycle
|
|
3099
|
-
*/
|
|
3100
|
-
declare function useVideoElement(src: string | null): {
|
|
3101
|
-
videoRef: React__default.MutableRefObject<HTMLVideoElement | null>;
|
|
3102
|
-
isLoaded: boolean;
|
|
3103
|
-
error: Error | null;
|
|
3104
|
-
duration: number;
|
|
3105
|
-
};
|
|
3302
|
+
/** Union ref — exposes whichever component's ref is active. */
|
|
3303
|
+
type CTPlayerRef = CoursePlayerRef | StreamingCoursePlayerRef;
|
|
3304
|
+
declare const CTPlayer: React__default.ForwardRefExoticComponent<CTPlayerProps & React__default.RefAttributes<CTPlayerRef>>;
|
|
3106
3305
|
|
|
3107
3306
|
/**
|
|
3108
3307
|
* useInteractionPoints — monitors playback time and triggers MCQ pauses.
|
|
@@ -3398,11 +3597,15 @@ interface PlayerControlsProps {
|
|
|
3398
3597
|
onBranchPanelToggle?: () => void;
|
|
3399
3598
|
/** Whether the learner is in interactive/editing mode */
|
|
3400
3599
|
isInteractive?: boolean;
|
|
3600
|
+
/** Whether webcam overlay is visible */
|
|
3601
|
+
webcamVisible?: boolean;
|
|
3602
|
+
/** Toggle webcam visibility */
|
|
3603
|
+
onWebcamToggle?: () => void;
|
|
3401
3604
|
showMarkers?: boolean;
|
|
3402
3605
|
compact?: boolean;
|
|
3403
3606
|
containerRef?: React__default__default.RefObject<HTMLElement>;
|
|
3404
3607
|
}
|
|
3405
|
-
declare function PlayerControls({ isPlaying, currentTime, duration, volume, muted, playbackSpeed, markers, isFullscreen, videoQualityLevels, videoQualityLevel, onVideoQualityChange, onPlayPause, onSeek, onSeekStart, onSeekEnd, onSkip, onRestart, onVolumeChange, onMuteToggle, onSpeedChange, onFullscreenToggle, onEditorInteraction, interactionPoints, branches, onBranchClick, branchCount, isBranchPanelOpen, onBranchPanelToggle, isInteractive, showMarkers, compact, }: PlayerControlsProps): react_jsx_runtime.JSX.Element;
|
|
3608
|
+
declare function PlayerControls({ isPlaying, currentTime, duration, volume, muted, playbackSpeed, markers, isFullscreen, videoQualityLevels, videoQualityLevel, onVideoQualityChange, onPlayPause, onSeek, onSeekStart, onSeekEnd, onSkip, onRestart, onVolumeChange, onMuteToggle, onSpeedChange, onFullscreenToggle, onEditorInteraction, interactionPoints, branches, onBranchClick, branchCount, isBranchPanelOpen, onBranchPanelToggle, isInteractive, webcamVisible, onWebcamToggle, showMarkers, compact, }: PlayerControlsProps): react_jsx_runtime.JSX.Element;
|
|
3406
3609
|
|
|
3407
3610
|
/**
|
|
3408
3611
|
* Timeline Component
|
|
@@ -3681,9 +3884,6 @@ interface CustomSize {
|
|
|
3681
3884
|
}
|
|
3682
3885
|
interface WebcamOverlayProps {
|
|
3683
3886
|
src: string | null;
|
|
3684
|
-
currentTime: number;
|
|
3685
|
-
isPlaying: boolean;
|
|
3686
|
-
playbackRate?: number;
|
|
3687
3887
|
position?: OverlayPosition;
|
|
3688
3888
|
customPosition?: CustomPosition;
|
|
3689
3889
|
size?: OverlaySize;
|
|
@@ -3698,9 +3898,6 @@ interface WebcamOverlayProps {
|
|
|
3698
3898
|
onSizeChange?: (size: CustomSize) => void;
|
|
3699
3899
|
onVisibilityToggle?: () => void;
|
|
3700
3900
|
onVideoRef?: (video: HTMLVideoElement | null) => void;
|
|
3701
|
-
muted?: boolean;
|
|
3702
|
-
volume?: number;
|
|
3703
|
-
syncTolerance?: number;
|
|
3704
3901
|
/** Whether webcam is in maximized (full overlay) mode - from playback events */
|
|
3705
3902
|
maximized?: boolean;
|
|
3706
3903
|
/** Callback when user clicks minimize button */
|
|
@@ -3708,6 +3905,6 @@ interface WebcamOverlayProps {
|
|
|
3708
3905
|
/** Callback when user clicks maximize button */
|
|
3709
3906
|
onMaximize?: () => void;
|
|
3710
3907
|
}
|
|
3711
|
-
declare function WebcamOverlay({ src,
|
|
3908
|
+
declare function WebcamOverlay({ src, position, customPosition, size, customSize, visible, draggable, resizable: _resizable, showControls: _showControls, borderRadius, opacity, onPositionChange, onSizeChange: _onSizeChange, onVisibilityToggle: _onVisibilityToggle, onVideoRef, maximized, onMinimize, onMaximize, }: WebcamOverlayProps): JSX.Element;
|
|
3712
3909
|
|
|
3713
|
-
export { AudioOverlay, type AudioOverlayProps, type AudioQuality, type
|
|
3910
|
+
export { AudioOverlay, type AudioOverlayProps, type AudioQuality, type BackendAdapter, BranchPanel, type BranchPanelProps, BranchSavedToast, type BranchSavedToastProps, type BranchSnapshot, BranchTransition, type BranchTransitionProps, type CTEvent, type CTManifest, type CTMarker, CTPlayer, type CTPlayerProps, type CTPlayerRef, type CTReadOptions, type CTRecording, CoursePlayer, type CoursePlayerProps, type CoursePlayerRef, type CursorPosition, type CustomPosition$1 as CustomPosition, DocumentPanel, type DocumentPanelProps, FileExplorer, type FileExplorerProps, type FileSystemSnapshot, type HLSPlayerInstance, HLS_EVENTS, HlsStreamingAdapter, IDEPanel, type IDEPanelProps, type InteractionPoint, type InternalEvent, type InternalRecording, type LearnerBranch, LocalFileAdapter, MCQOverlay, type MCQOverlayProps, type MCQResult, type MCQSubmission, MasterClock, type MediaSourceAdapter, type OverlayPosition$1 as OverlayPosition, type PanelMode, type PlaybackControls, type PlaybackEngineConfig, type PlaybackState, PlaybackStateMachine, type PlayerAction, PlayerControls, type PlayerControlsProps$1 as PlayerControlsProps, PlayerEngine, type PlayerEngineConfig, PlayerErrorBoundary, type PlayerProps, type PlayerRef, type PlayerSnapshot, type PlayerState, type RecordingMarker, type RemoteBufferHealth, SaveBranchButton, type SaveBranchButtonProps, type SpeedSelectorProps, type StreamingAudioConfig, type StreamingConfig, StreamingCoursePlayer, type StreamingCoursePlayerProps, type StreamingCoursePlayerRef, type StreamingEventsConfig, type StreamingManifest, type StreamingRecordingInfo, Terminal, type TerminalLineData, type TerminalProps, Timeline, type TimelineProps$1 as TimelineProps, ToolSidebar, type ToolSidebarProps, type ToolType, UnsavedBranchPrompt, type UnsavedBranchPromptProps, type UseBranchesOptions, type UseBranchesResult, type UseBranchingOptions, type UseBranchingResult, type UseInteractionPointsOptions, type UseInteractionPointsResult, type UsePlaybackEngineResult, type UsePlayerEngineResult, type ValidationResult$1 as ValidationResult, type VolumeControlProps, WebcamOverlay, type WebcamOverlayProps, WhiteboardPanel, type WhiteboardPanelProps, type WriteCTOptions, canPlayHls, createEmptyRecording, createHlsPlayer, deleteBranch as deleteBranchFromStorage, destroyHlsPlayer, formatBranchTime, hasNativeHlsSupport, isHlsSupported, loadBranches, readCTFile, readCTManifest, saveBranch as saveBranchToStorage, updateBranchLabel, useBranches, useBranching, useInteractionPoints, usePlayerEngine, validateCTFile, writeCTFile };
|