@ct-player/embed 1.1.11 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import * as react from 'react';
2
- import react__default from 'react';
1
+ import * as React__default from 'react';
2
+ import React__default__default from 'react';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import Hls from 'hls.js';
5
5
 
@@ -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' | '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';
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';
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<'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'>;
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'>;
31
31
  /**
32
32
  * Mapping of event types to their data structures
33
33
  */
@@ -46,6 +46,10 @@ interface CTEventDataMap {
46
46
  whiteboard_undo: WhiteboardUndoEventData;
47
47
  excalidraw_scene: ExcalidrawSceneEventData;
48
48
  excalidraw_clear: ExcalidrawClearEventData;
49
+ excalidraw_pointer: ExcalidrawPointerEventData;
50
+ overlay_scene: ExcalidrawSceneEventData;
51
+ overlay_clear: ExcalidrawClearEventData;
52
+ overlay_pointer: ExcalidrawPointerEventData;
49
53
  document_load: DocumentLoadEventData;
50
54
  document_page: DocumentPageEventData;
51
55
  document_zoom: DocumentZoomEventData;
@@ -74,6 +78,8 @@ interface CTEventDataMap {
74
78
  editor_cursor: EditorCursorEventData;
75
79
  tool_switch: ToolSwitchEventData;
76
80
  marker: MarkerEventData;
81
+ webcam_state: WebcamStateEventData;
82
+ content_dimensions: ContentDimensionsEventData;
77
83
  tool: ToolEventData;
78
84
  whiteboard: WhiteboardEventData;
79
85
  whiteboard_point: WhiteboardPointEventData;
@@ -221,6 +227,10 @@ interface InitEventData {
221
227
  x: number;
222
228
  y: number;
223
229
  };
230
+ contentDimensions?: {
231
+ width: number;
232
+ height: number;
233
+ };
224
234
  }
225
235
  /**
226
236
  * Code content change
@@ -380,6 +390,15 @@ interface ExcalidrawSceneEventData {
380
390
  */
381
391
  interface ExcalidrawClearEventData {
382
392
  }
393
+ /**
394
+ * Excalidraw pointer update (laser pointer, tool changes)
395
+ */
396
+ interface ExcalidrawPointerEventData {
397
+ x: number;
398
+ y: number;
399
+ tool: 'pointer' | 'laser';
400
+ button: 'down' | 'up';
401
+ }
383
402
  /**
384
403
  * Load document
385
404
  */
@@ -587,7 +606,22 @@ interface ToolSwitchEventData {
587
606
  interface MarkerEventData {
588
607
  id: string;
589
608
  label: string;
590
- type: 'chapter' | 'highlight' | 'quiz' | 'note';
609
+ type: 'chapter' | 'bookmark' | 'quiz' | 'note';
610
+ }
611
+ /**
612
+ * Webcam state event for maximize/minimize during recording
613
+ */
614
+ interface WebcamStateEventData {
615
+ /** Whether the webcam is maximized (full overlay) */
616
+ maximized: boolean;
617
+ }
618
+ /**
619
+ * Content dimensions event — captures the recording container size
620
+ * for accurate cursor alignment during playback
621
+ */
622
+ interface ContentDimensionsEventData {
623
+ width: number;
624
+ height: number;
591
625
  }
592
626
  /**
593
627
  * Legacy tool event (same as tool_switch)
@@ -683,6 +717,32 @@ interface PlaybackState$1 {
683
717
  textSelection: TextSelectionState | null;
684
718
  /** Editor typing cursor (caret) position */
685
719
  editorCursor: EditorCursorState | null;
720
+ /** Whether webcam is in maximized (full overlay) mode */
721
+ webcamMaximized: boolean;
722
+ /** Content area dimensions from the recording session.
723
+ * Used to correctly position the cursor overlay during playback when
724
+ * the player container is a different size than the recording container.
725
+ * Without this, the cursor diverges from Excalidraw's absolute coordinates. */
726
+ contentDimensions: {
727
+ width: number;
728
+ height: number;
729
+ } | null;
730
+ /** Excalidraw pointer state (for laser pointer playback) */
731
+ excalidrawPointer: {
732
+ x: number;
733
+ y: number;
734
+ tool: 'pointer' | 'laser';
735
+ button: 'down' | 'up';
736
+ } | null;
737
+ /** Drawing overlay scene (floating overlay on non-whiteboard panels) */
738
+ overlayScene: ExcalidrawScene | null;
739
+ /** Drawing overlay pointer state (laser pointer on overlay) */
740
+ overlayPointer: {
741
+ x: number;
742
+ y: number;
743
+ tool: 'pointer' | 'laser';
744
+ button: 'down' | 'up';
745
+ } | null;
686
746
  }
687
747
  /**
688
748
  * State for file/folder creation UI display during playback
@@ -1071,7 +1131,7 @@ interface CTManifest {
1071
1131
  media: CTMediaInfo;
1072
1132
  /** Events reference (chunked in v1.1.0+, single file in v1.0.0) */
1073
1133
  events: CTEventsInfo;
1074
- /** Chapter/highlight markers */
1134
+ /** Chapter/bookmark markers */
1075
1135
  markers: CTMarker[];
1076
1136
  /** Optional embedded assets */
1077
1137
  assets?: CTAssets;
@@ -1144,7 +1204,7 @@ interface CTWaveformInfo {
1144
1204
  samplesPerSecond: number;
1145
1205
  }
1146
1206
  /**
1147
- * Chapter/highlight marker
1207
+ * Chapter/bookmark marker
1148
1208
  */
1149
1209
  interface CTMarker {
1150
1210
  /** Unique identifier */
@@ -1155,13 +1215,16 @@ interface CTMarker {
1155
1215
  label: string;
1156
1216
  /** Marker type */
1157
1217
  type: CTMarkerType;
1158
- /** Type-specific additional data */
1218
+ /**
1219
+ * Type-specific additional data.
1220
+ * When `type === 'quiz'`, this should conform to {@link MCQMarkerData}.
1221
+ */
1159
1222
  data?: Record<string, unknown>;
1160
1223
  }
1161
1224
  /**
1162
1225
  * Marker types
1163
1226
  */
1164
- type CTMarkerType = 'chapter' | 'highlight' | 'quiz' | 'note';
1227
+ type CTMarkerType = 'chapter' | 'bookmark' | 'quiz' | 'note';
1165
1228
  /**
1166
1229
  * Embedded assets manifest
1167
1230
  */
@@ -1551,6 +1614,21 @@ interface IDEPanelProps extends BasePanelProps {
1551
1614
  line: number;
1552
1615
  column: number;
1553
1616
  }) => void;
1617
+ /**
1618
+ * Called when user interacts with the IDE (clicks explorer, terminal, etc).
1619
+ * Use this to pause playback when user starts interacting.
1620
+ */
1621
+ onInteractionStart?: () => void;
1622
+ /** Whether to show AI inline suggestions (recording mode only). Default: false */
1623
+ aiSuggestionsEnabled?: boolean;
1624
+ /** API endpoint URL for AI completions. Default: '/api/ai/complete' */
1625
+ aiSuggestionsEndpoint?: string;
1626
+ /** Debounce delay in ms before requesting a completion. Default: 300 */
1627
+ aiSuggestionsDebounceMs?: number;
1628
+ /** Whether the AI debug feature is available. Default: false */
1629
+ aiDebugEnabled?: boolean;
1630
+ /** API endpoint for AI debug analysis. Default: '/api/ai/debug' */
1631
+ aiDebugEndpoint?: string;
1554
1632
  }
1555
1633
  /**
1556
1634
  * Excalidraw scene structure (simplified)
@@ -1570,6 +1648,22 @@ interface WhiteboardPanelProps extends BasePanelProps {
1570
1648
  onSceneChange?: (scene: WhiteboardScene) => void;
1571
1649
  /** Called when scene is cleared */
1572
1650
  onSceneClear?: () => void;
1651
+ /** Called when pointer updates (laser pointer, drawing pointer) */
1652
+ onPointerUpdate?: (payload: {
1653
+ pointer: {
1654
+ x: number;
1655
+ y: number;
1656
+ tool: 'pointer' | 'laser';
1657
+ };
1658
+ button: 'down' | 'up';
1659
+ }) => void;
1660
+ /** Excalidraw pointer state for playback (laser trail rendering) */
1661
+ pointerState?: {
1662
+ x: number;
1663
+ y: number;
1664
+ tool: 'pointer' | 'laser';
1665
+ button: 'down' | 'up';
1666
+ } | null;
1573
1667
  /** Show Excalidraw toolbar */
1574
1668
  showToolbar?: boolean;
1575
1669
  /** Background color */
@@ -1647,6 +1741,8 @@ interface DocumentPanelProps extends BasePanelProps {
1647
1741
  allowMultiple?: boolean;
1648
1742
  /** Background color */
1649
1743
  backgroundColor?: string;
1744
+ /** Base URL for asset upload endpoint (e.g. '/api/assets'). PPTX files are uploaded here to get a public URL for iframe embedding. */
1745
+ assetUploadUrl?: string;
1650
1746
  }
1651
1747
  /**
1652
1748
  * Terminal component props
@@ -1763,13 +1859,15 @@ interface ToolSidebarProps {
1763
1859
  tools?: ToolType[];
1764
1860
  /** Additional CSS classes */
1765
1861
  className?: string;
1862
+ /** Inline styles (e.g. background override) */
1863
+ style?: React.CSSProperties;
1766
1864
  }
1767
1865
 
1768
- declare function ToolSidebar({ activeTool, onToolChange, tools, className, }: ToolSidebarProps): react_jsx_runtime.JSX.Element;
1866
+ declare function ToolSidebar({ activeTool, onToolChange, tools, className, style, }: ToolSidebarProps): react_jsx_runtime.JSX.Element;
1769
1867
 
1770
- declare function WhiteboardPanel({ scene, onSceneChange, onSceneClear, mode, isPlaying, onInteract, showToolbar, backgroundColor, className, }: WhiteboardPanelProps): react_jsx_runtime.JSX.Element;
1868
+ declare function WhiteboardPanel({ scene, onSceneChange, onSceneClear, onPointerUpdate, pointerState, mode, isPlaying, onInteract, showToolbar, backgroundColor, className, }: WhiteboardPanelProps): react_jsx_runtime.JSX.Element;
1771
1869
 
1772
- 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;
1870
+ 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, assetUploadUrl, className, }: DocumentPanelProps): react_jsx_runtime.JSX.Element;
1773
1871
 
1774
1872
  interface VirtualFileSystem {
1775
1873
  getFileTree: () => FileSystemEntry;
@@ -1784,103 +1882,62 @@ interface VirtualFileSystem {
1784
1882
  notifyChanged?: () => void;
1785
1883
  mkdir?: (path: string) => void;
1786
1884
  }
1787
- 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 }: IDEPanelProps & {
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 & {
1788
1886
  fileSystem?: VirtualFileSystem;
1789
1887
  }): react_jsx_runtime.JSX.Element;
1790
1888
 
1791
1889
  declare function FileExplorer({ tree, selectedFile, expandedFolders, fileCreation, fileRenaming, mode, isPlaying, hoveredFile, onFileHover, onFileSelect, onFileCreate, onFolderCreate, onFileDelete, onFileRename, onFolderToggle, onFileCreationStart, onFileCreationInput, onFileCreationCancel, onFileRenameStart, onFileRenameInput, onFileRenameCancel, className, }: FileExplorerProps): react_jsx_runtime.JSX.Element;
1792
1890
 
1793
- declare const Terminal: react.ForwardRefExoticComponent<TerminalProps & react.RefAttributes<TerminalRef>>;
1891
+ declare const Terminal: React__default.ForwardRefExoticComponent<TerminalProps & React__default.RefAttributes<TerminalRef>>;
1794
1892
 
1795
1893
  /**
1796
- * Active tool type - matches ToolType from @ct-courses/ui
1894
+ * Learner Branch Types
1895
+ *
1896
+ * A branch is a saved snapshot of the learner's code edits at a specific
1897
+ * moment in the instructor's timeline. Branches are learner-side only —
1898
+ * they never modify the .ct file, events, or manifest.
1899
+ *
1900
+ * @packageDocumentation
1797
1901
  */
1798
- type ActiveTool$1 = ToolType;
1799
1902
  /**
1800
- * Props for the CoursePlayer component
1903
+ * A point-in-time capture of the IDE state.
1904
+ * Used for both the instructor's original state and the learner's edited state.
1801
1905
  */
1802
- interface CoursePlayerProps {
1803
- /** Pre-loaded recording data */
1804
- recording?: CTRecording;
1805
- /** URL to a .ct file */
1806
- recordingUrl?: string;
1807
- /** Recording ID to fetch from API */
1808
- recordingId?: string;
1809
- /** Base API URL for fetching recordings */
1810
- apiUrl?: string;
1811
- /** Authentication token */
1812
- authToken?: string;
1813
- /** Go-Judge server URL */
1814
- goJudgeUrl?: string;
1815
- /** Go-Judge API key */
1816
- goJudgeApiKey?: string;
1817
- /** Theme setting */
1818
- theme?: 'light' | 'dark' | 'system';
1819
- /** Aspect ratio of the player */
1820
- aspectRatio?: '16:9' | '4:3' | 'auto';
1821
- /** Show playback controls toolbar */
1822
- showToolbar?: boolean;
1823
- /** Show timeline below player */
1824
- showTimeline?: boolean;
1825
- /** Show chapter markers */
1826
- showChapters?: boolean;
1827
- /** Show tool sidebar */
1828
- showToolSidebar?: boolean;
1829
- /** Auto-play when loaded */
1830
- autoPlay?: boolean;
1831
- /** Default playback speed */
1832
- defaultSpeed?: number;
1833
- /** Allow fullscreen mode */
1834
- allowFullscreen?: boolean;
1835
- /** Default active tool */
1836
- defaultTool?: ActiveTool$1;
1837
- /** Called when player is ready */
1838
- onReady?: () => void;
1839
- /** Called when playback starts */
1840
- onPlay?: () => void;
1841
- /** Called when playback pauses */
1842
- onPause?: () => void;
1843
- /** Called on progress update */
1844
- onProgress?: (time: number, duration: number) => void;
1845
- /** Called when playback completes */
1846
- onComplete?: () => void;
1847
- /** Called on error */
1848
- onError?: (error: Error) => void;
1849
- /** Called when entering interactive mode */
1850
- onInteractionStart?: () => void;
1851
- /** Called when exiting interactive mode */
1852
- onInteractionEnd?: (code: string) => void;
1853
- /** Called when tool changes */
1854
- onToolChange?: (tool: string) => void;
1906
+ interface BranchSnapshot {
1907
+ /** Active file's code content */
1908
+ code: string;
1909
+ /** Programming language at snapshot time */
1910
+ language: string;
1911
+ /** Which file tab was active (null if single-file mode) */
1912
+ currentFile: string | null;
1913
+ /** Full file system snapshot path → content */
1914
+ fileSystem: Record<string, string>;
1915
+ /** Terminal output lines if any existed */
1916
+ terminalOutput?: string[];
1917
+ /** Which tool panel was open (code / terminal / whiteboard / document) */
1918
+ activeTool: string;
1919
+ /** Folder paths created by the learner during interactive mode */
1920
+ folders?: string[];
1855
1921
  }
1856
1922
  /**
1857
- * Imperative handle for CoursePlayer
1923
+ * A learner branch — a saved code snapshot at a point in the timeline.
1858
1924
  */
1859
- interface CoursePlayerRef {
1860
- play(): void;
1861
- pause(): void;
1862
- seek(timeMs: number): void;
1863
- setSpeed(speed: number): void;
1864
- getCurrentTime(): number;
1865
- getDuration(): number;
1866
- getState(): PlaybackState$1;
1867
- enterInteractiveMode(): void;
1868
- exitInteractiveMode(): void;
1869
- setActiveTool(tool: ActiveTool$1): void;
1925
+ interface LearnerBranch {
1926
+ /** Unique branch ID (crypto.randomUUID) */
1927
+ id: string;
1928
+ /** Lesson/recording this branch belongs to */
1929
+ lessonId: string;
1930
+ /** Playback time in milliseconds where the learner paused */
1931
+ timeMs: number;
1932
+ /** Instructor's state at that moment (for comparison / restoring) */
1933
+ originalState: BranchSnapshot;
1934
+ /** Learner's edited state */
1935
+ learnerState: BranchSnapshot;
1936
+ /** ISO timestamp of when the branch was created */
1937
+ createdAt: string;
1938
+ /** User-provided label, defaults to "Branch at MM:SS" */
1939
+ label?: string;
1870
1940
  }
1871
- /**
1872
- * CoursePlayer component
1873
- */
1874
- declare const CoursePlayer: react.ForwardRefExoticComponent<CoursePlayerProps & react.RefAttributes<CoursePlayerRef>>;
1875
-
1876
- /**
1877
- * CT-Courses Player - Remote Streaming Types
1878
- *
1879
- * Types for streaming playback from a remote CDN/R2 storage.
1880
- * These types match the streaming service manifest structure.
1881
- *
1882
- * @packageDocumentation
1883
- */
1884
1941
 
1885
1942
  /**
1886
1943
  * Audio quality level information
@@ -1908,6 +1965,40 @@ interface StreamingRecordingInfo {
1908
1965
  /** Creation timestamp (ISO 8601) */
1909
1966
  createdAt: string;
1910
1967
  }
1968
+ /**
1969
+ * Video quality level information
1970
+ */
1971
+ interface VideoQuality {
1972
+ /** Quality name (low, medium, high) */
1973
+ name: string;
1974
+ /** Bitrate in bits per second */
1975
+ bitrate: number;
1976
+ /** Video width in pixels */
1977
+ width: number;
1978
+ /** Video height in pixels */
1979
+ height: number;
1980
+ /** URL to the quality-specific playlist */
1981
+ playlistUrl?: string;
1982
+ }
1983
+ /**
1984
+ * Video streaming configuration (webcam overlay)
1985
+ */
1986
+ interface StreamingVideoConfig {
1987
+ /** Video type (always 'hls' for adaptive streaming) */
1988
+ type: 'hls';
1989
+ /** URL to the HLS master playlist */
1990
+ masterPlaylistUrl: string;
1991
+ /** Available quality levels */
1992
+ qualities: VideoQuality[];
1993
+ /** Video duration in seconds */
1994
+ duration: number;
1995
+ /** Video width in pixels */
1996
+ width: number;
1997
+ /** Video height in pixels */
1998
+ height: number;
1999
+ /** Video codec */
2000
+ codec: string;
2001
+ }
1911
2002
  /**
1912
2003
  * Audio streaming configuration
1913
2004
  */
@@ -1947,6 +2038,32 @@ interface StreamingConfig {
1947
2038
  /** Maximum buffer to maintain (ms) */
1948
2039
  maxBufferMs: number;
1949
2040
  }
2041
+ /**
2042
+ * An interaction point from the streaming manifest (CDN-safe).
2043
+ * Options intentionally omit `isCorrect` — answers validated server-side only.
2044
+ */
2045
+ interface InteractionPointManifest {
2046
+ /** Marker ID from the .ct manifest */
2047
+ id: string;
2048
+ /** Interaction type (only 'mcq' for now) */
2049
+ type: 'mcq';
2050
+ /** Time position in milliseconds */
2051
+ timeMs: number;
2052
+ /** Whether to pause playback when this point is reached */
2053
+ pausePlayback: boolean;
2054
+ /** MCQ question data (without isCorrect on options) */
2055
+ mcq: {
2056
+ question: string;
2057
+ options: {
2058
+ id: string;
2059
+ text: string;
2060
+ }[];
2061
+ explanation?: string;
2062
+ allowRetry: boolean;
2063
+ shuffleOptions: boolean;
2064
+ timeoutSeconds: number | null;
2065
+ };
2066
+ }
1950
2067
  /**
1951
2068
  * Complete streaming manifest structure
1952
2069
  * This is the main entry point returned by the streaming service.
@@ -1960,10 +2077,18 @@ interface StreamingManifest {
1960
2077
  recording: StreamingRecordingInfo;
1961
2078
  /** Audio streaming configuration */
1962
2079
  audio: StreamingAudioConfig;
2080
+ /** Video streaming configuration (present when webcam was recorded) */
2081
+ video?: StreamingVideoConfig;
1963
2082
  /** Events streaming configuration */
1964
2083
  events: StreamingEventsConfig;
1965
2084
  /** Streaming playback hints */
1966
2085
  streaming: StreamingConfig;
2086
+ /**
2087
+ * Interaction points extracted from .ct manifest quiz markers.
2088
+ * Options omit `isCorrect` — answers are validated server-side only.
2089
+ * Absent or empty when the recording has no quiz markers.
2090
+ */
2091
+ interactionPoints?: InteractionPointManifest[];
1967
2092
  }
1968
2093
  /**
1969
2094
  * Options for useRemoteStreamingPlayback hook
@@ -2038,8 +2163,10 @@ interface RemoteStreamingPlaybackState {
2038
2163
  isPlaying: boolean;
2039
2164
  /** Current playback speed */
2040
2165
  speed: number;
2041
- /** Whether the player is ready */
2166
+ /** Whether the player is ready (UI can render — audio+events loaded) */
2042
2167
  isReady: boolean;
2168
+ /** Whether ALL sources are loaded and synced (audio+video+events) */
2169
+ allSourcesReady: boolean;
2043
2170
  /** Whether currently buffering */
2044
2171
  isBuffering: boolean;
2045
2172
  /** Buffer health information */
@@ -2065,6 +2192,16 @@ interface RemoteStreamingPlaybackControls {
2065
2192
  setSpeed(speed: number): void;
2066
2193
  /** Get the audio element for custom controls */
2067
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;
2068
2205
  }
2069
2206
  /**
2070
2207
  * HLS player configuration
@@ -2089,81 +2226,12 @@ interface HLSQualityLevel {
2089
2226
  bitrate: number;
2090
2227
  /** Codec string */
2091
2228
  codecs: string;
2229
+ /** Video width (0 if audio-only) */
2230
+ width?: number;
2231
+ /** Video height (0 if audio-only) */
2232
+ height?: number;
2092
2233
  }
2093
2234
 
2094
- type ActiveTool = ToolType;
2095
- /**
2096
- * Props for StreamingCoursePlayer
2097
- */
2098
- interface StreamingCoursePlayerProps {
2099
- /** URL to the streaming manifest.json */
2100
- manifestUrl: string;
2101
- /** Theme setting */
2102
- theme?: 'light' | 'dark' | 'system';
2103
- /** Aspect ratio of the player */
2104
- aspectRatio?: '16:9' | '4:3' | 'auto';
2105
- /** Show playback controls toolbar */
2106
- showToolbar?: boolean;
2107
- /** Show tool sidebar */
2108
- showToolSidebar?: boolean;
2109
- /** Auto-play when loaded */
2110
- autoPlay?: boolean;
2111
- /** Default playback speed */
2112
- defaultSpeed?: number;
2113
- /** Allow fullscreen mode */
2114
- allowFullscreen?: boolean;
2115
- /** Default active tool */
2116
- defaultTool?: ActiveTool;
2117
- /** Maximum chunks to cache */
2118
- maxCacheSize?: number;
2119
- /** Additional CSS class */
2120
- className?: string;
2121
- /** Called when player is ready */
2122
- onReady?: () => void;
2123
- /** Called when playback starts */
2124
- onPlay?: () => void;
2125
- /** Called when playback pauses */
2126
- onPause?: () => void;
2127
- /** Called on progress update */
2128
- onProgress?: (time: number, duration: number) => void;
2129
- /** Called when playback completes */
2130
- onComplete?: () => void;
2131
- /** Called on error */
2132
- onError?: (error: Error) => void;
2133
- /** Called when buffering state changes */
2134
- onBuffering?: (isBuffering: boolean) => void;
2135
- /** Called when tool changes */
2136
- onToolChange?: (tool: string) => void;
2137
- /** Go-Judge server URL */
2138
- goJudgeUrl?: string;
2139
- /** Go-Judge API key */
2140
- goJudgeApiKey?: string;
2141
- /** Show chapter markers */
2142
- showChapters?: boolean;
2143
- /** Called when entering interactive mode */
2144
- onInteractionStart?: () => void;
2145
- /** Called when exiting interactive mode */
2146
- onInteractionEnd?: (code: string) => void;
2147
- }
2148
- /**
2149
- * Imperative handle for StreamingCoursePlayer
2150
- */
2151
- interface StreamingCoursePlayerRef {
2152
- play(): void;
2153
- pause(): void;
2154
- seek(timeMs: number): Promise<void>;
2155
- setSpeed(speed: number): void;
2156
- getCurrentTime(): number;
2157
- getDuration(): number;
2158
- getState(): PlaybackState$1;
2159
- enterInteractiveMode(): void;
2160
- exitInteractiveMode(): void;
2161
- setActiveTool(tool: ActiveTool): void;
2162
- getManifest(): StreamingManifest | null;
2163
- getBufferHealth(): RemoteBufferHealth;
2164
- }
2165
- declare const StreamingCoursePlayer: react.ForwardRefExoticComponent<StreamingCoursePlayerProps & react.RefAttributes<StreamingCoursePlayerRef>>;
2166
-
2167
2235
  /**
2168
2236
  * @ct-courses/player - Type Definitions
2169
2237
  */
@@ -2406,33 +2474,275 @@ interface VolumeControlProps {
2406
2474
  onMuteToggle: () => void;
2407
2475
  className?: string;
2408
2476
  }
2409
-
2410
2477
  /**
2411
- * usePlayback Hook
2412
- *
2413
- * Core playback engine hook using @ct-courses/core StateEngine.
2414
- * Supports audio synchronization as master clock (no video/webcam).
2415
- *
2416
- * CRITICAL SYNC ARCHITECTURE:
2417
- * - AUDIO is the MASTER CLOCK when media exists
2418
- * - Events are applied based on audio.currentTime
2419
- * - Without media, use requestAnimationFrame timing
2478
+ * Client-side interaction point (no isCorrect — answers validated server-side).
2479
+ * Mirrors InteractionPointManifest from streaming.ts but usable without a manifest.
2480
+ */
2481
+ interface InteractionPoint {
2482
+ /** Unique identifier */
2483
+ id: string;
2484
+ /** Interaction type */
2485
+ type: 'mcq';
2486
+ /** Time position in milliseconds */
2487
+ timeMs: number;
2488
+ /** Whether to pause playback when this point is reached */
2489
+ pausePlayback: boolean;
2490
+ /** MCQ question data */
2491
+ mcq: {
2492
+ question: string;
2493
+ options: {
2494
+ id: string;
2495
+ text: string;
2496
+ }[];
2497
+ explanation?: string;
2498
+ allowRetry: boolean;
2499
+ shuffleOptions: boolean;
2500
+ timeoutSeconds: number | null;
2501
+ };
2502
+ }
2503
+ /**
2504
+ * MCQ answer submission from the player
2505
+ */
2506
+ interface MCQSubmission {
2507
+ /** The interaction point ID */
2508
+ pointId: string;
2509
+ /** Selected option IDs */
2510
+ selectedOptionIds: string[];
2511
+ }
2512
+ /**
2513
+ * MCQ answer result returned from the host app's callback
2420
2514
  */
2515
+ interface MCQResult {
2516
+ /** Whether the answer was correct */
2517
+ correct: boolean;
2518
+ /** Optional explanation text */
2519
+ explanation?: string;
2520
+ /** Correct option IDs (revealed after answering) */
2521
+ correctOptionIds?: string[];
2522
+ }
2421
2523
 
2422
2524
  /**
2423
- * Playback state returned by the hook
2525
+ * Active tool type - matches ToolType from @ct-courses/ui
2424
2526
  */
2425
- interface PlaybackHookState {
2426
- /** Current playback state */
2427
- state: PlaybackState$1;
2428
- /** Current time in milliseconds */
2429
- currentTime: number;
2430
- /** Total duration in milliseconds */
2431
- duration: number;
2432
- /** Whether currently playing */
2433
- isPlaying: boolean;
2434
- /** Current playback speed */
2435
- speed: number;
2527
+ type ActiveTool$1 = ToolType;
2528
+ /**
2529
+ * Props for the CoursePlayer component
2530
+ */
2531
+ interface CoursePlayerProps {
2532
+ /** Pre-loaded recording data */
2533
+ recording?: CTRecording;
2534
+ /** URL to a .ct file */
2535
+ recordingUrl?: string;
2536
+ /** Recording ID to fetch from API */
2537
+ recordingId?: string;
2538
+ /** Base API URL for fetching recordings */
2539
+ apiUrl?: string;
2540
+ /** Authentication token */
2541
+ authToken?: string;
2542
+ /** Go-Judge server URL */
2543
+ goJudgeUrl?: string;
2544
+ /** Go-Judge API key */
2545
+ goJudgeApiKey?: string;
2546
+ /** Theme setting */
2547
+ theme?: "light" | "dark" | "system";
2548
+ /** Aspect ratio of the player */
2549
+ aspectRatio?: "16:9" | "4:3" | "auto";
2550
+ /** Show playback controls toolbar */
2551
+ showToolbar?: boolean;
2552
+ /** Show timeline below player */
2553
+ showTimeline?: boolean;
2554
+ /** Show chapter markers */
2555
+ showChapters?: boolean;
2556
+ /** Show tool sidebar */
2557
+ showToolSidebar?: boolean;
2558
+ /** Auto-play when loaded */
2559
+ autoPlay?: boolean;
2560
+ /** Default playback speed */
2561
+ defaultSpeed?: number;
2562
+ /** Allow fullscreen mode */
2563
+ allowFullscreen?: boolean;
2564
+ /** Default active tool */
2565
+ defaultTool?: ActiveTool$1;
2566
+ /** Called when player is ready */
2567
+ onReady?: () => void;
2568
+ /** Called when playback starts */
2569
+ onPlay?: () => void;
2570
+ /** Called when playback pauses */
2571
+ onPause?: () => void;
2572
+ /** Called on progress update */
2573
+ onProgress?: (time: number, duration: number) => void;
2574
+ /** Called when playback completes */
2575
+ onComplete?: () => void;
2576
+ /** Called on error */
2577
+ onError?: (error: Error) => void;
2578
+ /** Called when entering interactive mode */
2579
+ onInteractionStart?: () => void;
2580
+ /** Called when exiting interactive mode */
2581
+ onInteractionEnd?: (code: string) => void;
2582
+ /** Called when tool changes */
2583
+ onToolChange?: (tool: string) => void;
2584
+ /** Interaction points (MCQs) to display during playback */
2585
+ interactionPoints?: InteractionPoint[];
2586
+ /** Pre-answered point IDs (skip these MCQs) */
2587
+ answeredPointIds?: Set<string>;
2588
+ /** Called when a learner submits an MCQ answer. Host app validates server-side. */
2589
+ onSubmitAnswer?: (pointId: string, selectedOptionIds: string[]) => Promise<MCQResult>;
2590
+ /** Info about the next lesson for auto-advance overlay */
2591
+ nextLesson?: {
2592
+ id: string;
2593
+ title: string;
2594
+ } | null;
2595
+ /** Called when the player wants to navigate to the next lesson */
2596
+ onNavigateToLesson?: (lessonId: string) => void;
2597
+ /** Called when the current lesson completes (before transition) */
2598
+ onLessonComplete?: () => void;
2599
+ /** Lesson ID used to scope saved branches in localStorage */
2600
+ lessonId?: string;
2601
+ /** Enable the branch save feature (default: true) */
2602
+ enableBranches?: boolean;
2603
+ /** Called after a branch is saved */
2604
+ onBranchSave?: (branch: LearnerBranch) => void;
2605
+ /** Called when a learner restores a saved branch */
2606
+ onBranchRestore?: (branch: LearnerBranch) => void;
2607
+ /** Called after a branch is deleted */
2608
+ onBranchDelete?: (branchId: string) => void;
2609
+ /** Maximum branches per lesson (default: 50) */
2610
+ maxBranches?: number;
2611
+ }
2612
+ /**
2613
+ * Imperative handle for CoursePlayer
2614
+ */
2615
+ interface CoursePlayerRef {
2616
+ play(): void;
2617
+ pause(): void;
2618
+ seek(timeMs: number): void;
2619
+ setSpeed(speed: number): void;
2620
+ getCurrentTime(): number;
2621
+ getDuration(): number;
2622
+ getState(): PlaybackState$1;
2623
+ enterInteractiveMode(): void;
2624
+ exitInteractiveMode(): void;
2625
+ setActiveTool(tool: ActiveTool$1): void;
2626
+ }
2627
+ /**
2628
+ * CoursePlayer component
2629
+ */
2630
+ declare const CoursePlayer: React__default.ForwardRefExoticComponent<CoursePlayerProps & React__default.RefAttributes<CoursePlayerRef>>;
2631
+
2632
+ type ActiveTool = ToolType;
2633
+ /**
2634
+ * Props for StreamingCoursePlayer
2635
+ */
2636
+ interface StreamingCoursePlayerProps {
2637
+ /** URL to the streaming manifest.json */
2638
+ manifestUrl: string;
2639
+ /** Theme setting */
2640
+ theme?: "light" | "dark" | "system";
2641
+ /** Aspect ratio of the player */
2642
+ aspectRatio?: "16:9" | "4:3" | "auto";
2643
+ /** Show playback controls toolbar */
2644
+ showToolbar?: boolean;
2645
+ /** Show tool sidebar */
2646
+ showToolSidebar?: boolean;
2647
+ /** Auto-play when loaded */
2648
+ autoPlay?: boolean;
2649
+ /** Default playback speed */
2650
+ defaultSpeed?: number;
2651
+ /** Allow fullscreen mode */
2652
+ allowFullscreen?: boolean;
2653
+ /** Default active tool */
2654
+ defaultTool?: ActiveTool;
2655
+ /** Maximum chunks to cache */
2656
+ maxCacheSize?: number;
2657
+ /** Additional CSS class */
2658
+ className?: string;
2659
+ /** Called when player is ready */
2660
+ onReady?: () => void;
2661
+ /** Called when playback starts */
2662
+ onPlay?: () => void;
2663
+ /** Called when playback pauses */
2664
+ onPause?: () => void;
2665
+ /** Called on progress update */
2666
+ onProgress?: (time: number, duration: number) => void;
2667
+ /** Called when playback completes */
2668
+ onComplete?: () => void;
2669
+ /** Called on error */
2670
+ onError?: (error: Error) => void;
2671
+ /** Called when buffering state changes */
2672
+ onBuffering?: (isBuffering: boolean) => void;
2673
+ /** Called when tool changes */
2674
+ onToolChange?: (tool: string) => void;
2675
+ /** Go-Judge server URL */
2676
+ goJudgeUrl?: string;
2677
+ /** Go-Judge API key */
2678
+ goJudgeApiKey?: string;
2679
+ /** Show chapter markers */
2680
+ showChapters?: boolean;
2681
+ /** Called when entering interactive mode */
2682
+ onInteractionStart?: () => void;
2683
+ /** Called when exiting interactive mode */
2684
+ onInteractionEnd?: (code: string) => void;
2685
+ /** Interaction points (MCQs) to display during playback */
2686
+ interactionPoints?: InteractionPoint[];
2687
+ /** Pre-answered point IDs (skip these MCQs) */
2688
+ answeredPointIds?: Set<string>;
2689
+ /** Called when a learner submits an MCQ answer. Host app validates server-side. */
2690
+ onSubmitAnswer?: (pointId: string, selectedOptionIds: string[]) => Promise<MCQResult>;
2691
+ /** Info about the next lesson for auto-advance overlay */
2692
+ nextLesson?: {
2693
+ id: string;
2694
+ title: string;
2695
+ } | null;
2696
+ /** Called when the player wants to navigate to the next lesson */
2697
+ onNavigateToLesson?: (lessonId: string) => void;
2698
+ /** Called when the current lesson completes (before transition) */
2699
+ onLessonComplete?: () => void;
2700
+ }
2701
+ /**
2702
+ * Imperative handle for StreamingCoursePlayer
2703
+ */
2704
+ interface StreamingCoursePlayerRef {
2705
+ play(): void;
2706
+ pause(): void;
2707
+ seek(timeMs: number): Promise<void>;
2708
+ setSpeed(speed: number): void;
2709
+ getCurrentTime(): number;
2710
+ getDuration(): number;
2711
+ getState(): PlaybackState$1;
2712
+ enterInteractiveMode(): void;
2713
+ exitInteractiveMode(): void;
2714
+ setActiveTool(tool: ActiveTool): void;
2715
+ getManifest(): StreamingManifest | null;
2716
+ getBufferHealth(): RemoteBufferHealth;
2717
+ }
2718
+ declare const StreamingCoursePlayer: React__default.ForwardRefExoticComponent<StreamingCoursePlayerProps & React__default.RefAttributes<StreamingCoursePlayerRef>>;
2719
+
2720
+ /**
2721
+ * usePlayback Hook
2722
+ *
2723
+ * Core playback engine hook using @ct-courses/core StateEngine.
2724
+ * Supports audio synchronization as master clock (no video/webcam).
2725
+ *
2726
+ * CRITICAL SYNC ARCHITECTURE:
2727
+ * - AUDIO is the MASTER CLOCK when media exists
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;
2436
2746
  /** Whether recording is loaded */
2437
2747
  isLoaded: boolean;
2438
2748
  /** Volume level (0-1) */
@@ -2453,6 +2763,7 @@ interface PlaybackControls {
2453
2763
  setVolume(volume: number): void;
2454
2764
  setMuted(muted: boolean): void;
2455
2765
  setAudioElement(audio: HTMLAudioElement | null): void;
2766
+ setVideoElement(video: HTMLVideoElement | null): void;
2456
2767
  enterInteractiveMode(): void;
2457
2768
  exitInteractiveMode(): void;
2458
2769
  setInteractiveCode(code: string): void;
@@ -2466,6 +2777,10 @@ interface PlaybackControls {
2466
2777
  x: number;
2467
2778
  y: number;
2468
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;
2469
2784
  }
2470
2785
  /**
2471
2786
  * Options for usePlayback hook
@@ -2712,21 +3027,233 @@ declare function useAudioSync(options: UseAudioSyncOptions): AudioSyncState & {
2712
3027
  * Hook for managing audio element lifecycle
2713
3028
  */
2714
3029
  declare function useAudioElement(src: string | null): {
2715
- audioRef: react.MutableRefObject<HTMLAudioElement | 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;
3069
+ }
3070
+ /**
3071
+ * Options for useVideoSync hook
3072
+ */
3073
+ interface UseVideoSyncOptions {
3074
+ /** Reference to video element */
3075
+ videoRef: React.RefObject<HTMLVideoElement>;
3076
+ /** Current event time */
3077
+ eventTime: number;
3078
+ /** Is currently playing */
3079
+ isPlaying: boolean;
3080
+ /** Playback speed */
3081
+ speed: number;
3082
+ /** Maximum allowed drift in ms before resync (default: 500ms) */
3083
+ driftTolerance?: number;
3084
+ /** Callback when video time updates */
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;
3090
+ }
3091
+ /**
3092
+ * Hook for synchronizing video playback with event state
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>;
2716
3102
  isLoaded: boolean;
2717
3103
  error: Error | null;
2718
3104
  duration: number;
2719
3105
  };
2720
- /** @deprecated Use AudioSyncState instead */
2721
- type VideoSyncState = AudioSyncState;
2722
- /** @deprecated Use AudioSyncControls instead */
2723
- type VideoSyncControls = AudioSyncControls;
2724
- /** @deprecated Use UseAudioSyncOptions instead */
2725
- type UseVideoSyncOptions = UseAudioSyncOptions;
2726
- /** @deprecated Use useAudioSync instead */
2727
- declare const useVideoSync: typeof useAudioSync;
2728
- /** @deprecated Use useAudioElement instead */
2729
- declare const useVideoElement: typeof useAudioElement;
3106
+
3107
+ /**
3108
+ * useInteractionPoints monitors playback time and triggers MCQ pauses.
3109
+ *
3110
+ * The hook is player-mode agnostic: it only needs the current time (ms),
3111
+ * a pause callback, and a play callback. Both CoursePlayer and
3112
+ * StreamingCoursePlayer can use it.
3113
+ *
3114
+ * Edge cases handled:
3115
+ * - Seek past unanswered MCQ → still triggers (first unanswered point ≤ currentTime)
3116
+ * - Seek backwards past a triggered-but-unanswered point → re-evaluates
3117
+ * - Multiple MCQs at exact same time → triggers in array order
3118
+ * - Race: activePoint takes priority — no duplicate triggers while overlay is open
3119
+ *
3120
+ * @packageDocumentation
3121
+ */
3122
+
3123
+ interface UseInteractionPointsOptions {
3124
+ /** Sorted interaction points (ascending by timeMs) */
3125
+ interactionPoints: InteractionPoint[];
3126
+ /** Current playback time in milliseconds */
3127
+ currentTimeMs: number;
3128
+ /** Callback to pause playback */
3129
+ pause: () => void;
3130
+ /** Callback to resume playback */
3131
+ play: () => void;
3132
+ /** Pre-loaded set of already-answered point IDs */
3133
+ initialAnsweredPointIds?: Set<string>;
3134
+ }
3135
+ interface UseInteractionPointsResult {
3136
+ /** The interaction point currently requiring attention, or null */
3137
+ activePoint: InteractionPoint | null;
3138
+ /** Dismiss the active point (adds to answered, resumes playback) */
3139
+ dismissPoint: () => void;
3140
+ /** Mark a point as answered without dismissing (for pre-loading state) */
3141
+ markAnswered: (pointId: string) => void;
3142
+ /** Set of all answered point IDs */
3143
+ answeredPointIds: Set<string>;
3144
+ }
3145
+ declare function useInteractionPoints({ interactionPoints, currentTimeMs, pause, play, initialAnsweredPointIds, }: UseInteractionPointsOptions): UseInteractionPointsResult;
3146
+
3147
+ /**
3148
+ * useBranching — manages lesson-to-lesson transition with auto-advance countdown.
3149
+ *
3150
+ * @packageDocumentation
3151
+ */
3152
+ interface UseBranchingOptions {
3153
+ /** Next lesson info (null = course complete) */
3154
+ nextLesson: {
3155
+ id: string;
3156
+ title: string;
3157
+ } | null;
3158
+ /** Seconds before auto-navigating (default 5) */
3159
+ autoAdvanceSeconds?: number;
3160
+ /** Called when auto-advance or manual skip triggers navigation */
3161
+ onNavigate: (lessonId: string) => void;
3162
+ }
3163
+ interface UseBranchingResult {
3164
+ /** Whether the transition overlay is active */
3165
+ isTransitioning: boolean;
3166
+ /** Current countdown value (seconds remaining, 0 = done) */
3167
+ countdown: number;
3168
+ /** Start the transition overlay + countdown */
3169
+ startTransition: () => void;
3170
+ /** Skip the countdown and navigate immediately */
3171
+ skipToNext: () => void;
3172
+ /** Pause the auto-advance but keep the overlay visible */
3173
+ cancelAutoAdvance: () => void;
3174
+ }
3175
+ declare function useBranching({ nextLesson, autoAdvanceSeconds, onNavigate, }: UseBranchingOptions): UseBranchingResult;
3176
+
3177
+ /**
3178
+ * useBranches — manages learner branch state for a single lesson.
3179
+ *
3180
+ * Handles save / delete / rename / restore of learner code snapshots,
3181
+ * persisted to localStorage via branchStorage utilities.
3182
+ *
3183
+ * @packageDocumentation
3184
+ */
3185
+
3186
+ interface UseBranchesOptions {
3187
+ /** Lesson/recording ID — branches are scoped to this. Undefined = feature disabled. */
3188
+ lessonId: string | undefined;
3189
+ /** Returns current playback time in milliseconds */
3190
+ getCurrentTimeMs: () => number;
3191
+ /** Captures the learner's current IDE state (interactive mode state) */
3192
+ getCurrentState: () => BranchSnapshot;
3193
+ /** Captures the instructor's state at the current time from the engine */
3194
+ getOriginalState: () => BranchSnapshot;
3195
+ /** Called when the learner wants to restore a branch (parent orchestrates seek + inject) */
3196
+ onRestoreBranch?: (branch: LearnerBranch) => void;
3197
+ /** Called after a branch is successfully saved (for analytics / callbacks) */
3198
+ onBranchSaved?: (branch: LearnerBranch) => void;
3199
+ /** Called after a branch is deleted */
3200
+ onBranchDeleted?: (branchId: string) => void;
3201
+ /** Maximum branches per lesson (default 50) */
3202
+ maxBranches?: number;
3203
+ }
3204
+ interface UseBranchesResult {
3205
+ /** All branches for the current lesson, sorted oldest-first */
3206
+ branches: LearnerBranch[];
3207
+ /** Number of saved branches */
3208
+ branchCount: number;
3209
+ /** Whether the feature is enabled (lessonId is defined) */
3210
+ enabled: boolean;
3211
+ /** Save a new branch from the current interactive state. Returns the created branch, or null if disabled / duplicate. */
3212
+ saveBranch: (label?: string) => LearnerBranch | null;
3213
+ /** Delete a branch by ID */
3214
+ deleteBranch: (branchId: string) => void;
3215
+ /** Rename a branch */
3216
+ renameBranch: (branchId: string, label: string) => void;
3217
+ /** Restore a branch — delegates to parent via onRestoreBranch callback */
3218
+ restoreBranch: (branchId: string) => void;
3219
+ /** Check if a branch at this timeMs with this code already exists (duplicate detection) */
3220
+ isDuplicate: (timeMs: number, code: string) => boolean;
3221
+ }
3222
+ declare function useBranches(options: UseBranchesOptions): UseBranchesResult;
3223
+
3224
+ /**
3225
+ * Branch Storage — localStorage CRUD helpers
3226
+ *
3227
+ * Persists learner branches as JSON arrays keyed by lesson ID.
3228
+ * Key format: `ct-branches:<lessonId>`
3229
+ *
3230
+ * Pure functions — no React, no hooks.
3231
+ *
3232
+ * @packageDocumentation
3233
+ */
3234
+
3235
+ /**
3236
+ * Load all branches for a lesson from localStorage.
3237
+ * Returns empty array if nothing stored or on parse error.
3238
+ * Sorted by `createdAt` ascending (oldest first).
3239
+ */
3240
+ declare function loadBranches(lessonId: string): LearnerBranch[];
3241
+ /**
3242
+ * Persist a new branch. Caps at `maxBranches` per lesson (drops oldest).
3243
+ */
3244
+ declare function saveBranch(lessonId: string, branch: LearnerBranch, maxBranches?: number): void;
3245
+ /**
3246
+ * Delete a single branch by ID.
3247
+ */
3248
+ declare function deleteBranch(lessonId: string, branchId: string): void;
3249
+ /**
3250
+ * Update the label of an existing branch.
3251
+ */
3252
+ declare function updateBranchLabel(lessonId: string, branchId: string, label: string): void;
3253
+ /**
3254
+ * Format milliseconds as "MM:SS" for default branch labels.
3255
+ */
3256
+ declare function formatBranchTime(ms: number): string;
2730
3257
 
2731
3258
  /**
2732
3259
  * HLS Loader Utility
@@ -2757,6 +3284,29 @@ interface HLSPlayerInstance {
2757
3284
  /** Remove event listener */
2758
3285
  off: (event: string, callback: (...args: unknown[]) => void) => void;
2759
3286
  }
3287
+ /**
3288
+ * HLS video player instance (mirrors HLSPlayerInstance but for video elements)
3289
+ */
3290
+ interface HLSVideoPlayerInstance {
3291
+ /** The HLS.js instance (null if using native HLS) */
3292
+ hls: Hls | null;
3293
+ /** The video element */
3294
+ videoElement: HTMLVideoElement;
3295
+ /** Whether this is using native HLS (Safari) */
3296
+ isNative: boolean;
3297
+ /** Destroy and cleanup */
3298
+ destroy: () => void;
3299
+ /** Get current quality levels */
3300
+ getQualityLevels: () => HLSQualityLevel[];
3301
+ /** Set quality level (-1 for auto) */
3302
+ setQualityLevel: (index: number) => void;
3303
+ /** Get current quality level */
3304
+ getCurrentQualityLevel: () => number;
3305
+ /** Add event listener for HLS events */
3306
+ on: (event: string, callback: (...args: unknown[]) => void) => void;
3307
+ /** Remove event listener */
3308
+ off: (event: string, callback: (...args: unknown[]) => void) => void;
3309
+ }
2760
3310
  interface HLSEvents {
2761
3311
  /** Manifest has been parsed and is ready */
2762
3312
  MANIFEST_PARSED: 'hlsManifestParsed';
@@ -2795,12 +3345,19 @@ declare function createHlsPlayer(audioElement: HTMLAudioElement, masterPlaylistU
2795
3345
  /**
2796
3346
  * Destroy an HLS player instance safely
2797
3347
  */
2798
- declare function destroyHlsPlayer(player: HLSPlayerInstance | null): void;
3348
+ declare function destroyHlsPlayer(player: HLSPlayerInstance | HLSVideoPlayerInstance | null): void;
2799
3349
 
2800
3350
  interface Marker$1 {
2801
3351
  time: number;
2802
3352
  label?: string;
2803
- type?: 'chapter' | 'highlight';
3353
+ type?: "chapter" | "bookmark" | "quiz";
3354
+ }
3355
+ interface VideoQualityLevel {
3356
+ index: number;
3357
+ bitrate: number;
3358
+ codecs: string;
3359
+ width?: number;
3360
+ height?: number;
2804
3361
  }
2805
3362
  interface PlayerControlsProps {
2806
3363
  isPlaying: boolean;
@@ -2811,6 +3368,9 @@ interface PlayerControlsProps {
2811
3368
  playbackSpeed: number;
2812
3369
  markers?: Marker$1[];
2813
3370
  isFullscreen?: boolean;
3371
+ videoQualityLevels?: VideoQualityLevel[];
3372
+ videoQualityLevel?: number;
3373
+ onVideoQualityChange?: (index: number) => void;
2814
3374
  onPlayPause: () => void;
2815
3375
  onSeek: (timeMs: number) => void;
2816
3376
  onSeekStart?: () => void;
@@ -2821,11 +3381,28 @@ interface PlayerControlsProps {
2821
3381
  onMuteToggle: () => void;
2822
3382
  onSpeedChange: (speed: number) => void;
2823
3383
  onFullscreenToggle?: () => void;
3384
+ /** Called when the user interacts with the code editor area while playing.
3385
+ * The parent should pause playback when this fires. */
3386
+ onEditorInteraction?: () => void;
3387
+ /** Interaction points — purple dots rendered on the seek bar */
3388
+ interactionPoints?: InteractionPoint[];
3389
+ /** Saved learner branches — green diamond markers on the progress bar */
3390
+ branches?: LearnerBranch[];
3391
+ /** Callback when a branch marker is clicked */
3392
+ onBranchClick?: (branch: LearnerBranch) => void;
3393
+ /** Number of saved branches (for badge) */
3394
+ branchCount?: number;
3395
+ /** Whether the branch panel is currently open */
3396
+ isBranchPanelOpen?: boolean;
3397
+ /** Toggle the branch panel */
3398
+ onBranchPanelToggle?: () => void;
3399
+ /** Whether the learner is in interactive/editing mode */
3400
+ isInteractive?: boolean;
2824
3401
  showMarkers?: boolean;
2825
3402
  compact?: boolean;
2826
- containerRef?: react__default.RefObject<HTMLElement>;
3403
+ containerRef?: React__default__default.RefObject<HTMLElement>;
2827
3404
  }
2828
- 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;
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;
2829
3406
 
2830
3407
  /**
2831
3408
  * Timeline Component
@@ -2842,7 +3419,7 @@ interface Marker {
2842
3419
  id: string;
2843
3420
  time: number;
2844
3421
  label: string;
2845
- type: 'chapter' | 'highlight' | 'quiz' | 'note' | 'bookmark' | 'error';
3422
+ type: 'chapter' | 'bookmark' | 'quiz' | 'note' | 'error';
2846
3423
  description?: string;
2847
3424
  }
2848
3425
  /**
@@ -2897,6 +3474,128 @@ interface TimelineProps {
2897
3474
  */
2898
3475
  declare function Timeline({ currentTime, duration, markers, chunks, buffered, onSeek, onMarkerClick, theme, showChapterLabels, showBufferStatus, showChunkIndicators, compact, }: TimelineProps): JSX.Element;
2899
3476
 
3477
+ /**
3478
+ * MCQOverlay — full-area overlay that renders a multiple-choice question
3479
+ * on top of the player content.
3480
+ *
3481
+ * Phases: Question → Loading → Feedback (with optional retry).
3482
+ * Handles timeout countdown, focus trapping, and keyboard navigation.
3483
+ *
3484
+ * @packageDocumentation
3485
+ */
3486
+
3487
+ interface MCQOverlayProps {
3488
+ /** The interaction point to display */
3489
+ point: InteractionPoint;
3490
+ /** Called when the learner submits an answer. Returns result from host app. */
3491
+ onSubmit: (selectedOptionIds: string[]) => Promise<MCQResult>;
3492
+ /** Called when the learner is done (correct answer, or gave up) */
3493
+ onDismiss: () => void;
3494
+ /** Optional extra class name for the outer container */
3495
+ className?: string;
3496
+ }
3497
+ declare const MCQOverlay: React__default__default.FC<MCQOverlayProps>;
3498
+
3499
+ /**
3500
+ * BranchTransition — end-of-lesson overlay with auto-advance countdown.
3501
+ *
3502
+ * Shows "Next: [title]" with countdown + skip/cancel, or a "Course Complete"
3503
+ * message when there's no next lesson.
3504
+ *
3505
+ * @packageDocumentation
3506
+ */
3507
+
3508
+ interface BranchTransitionProps {
3509
+ /** Title of the next lesson */
3510
+ nextLessonTitle: string;
3511
+ /** Countdown seconds remaining */
3512
+ countdown: number;
3513
+ /** Skip countdown and navigate immediately */
3514
+ onSkip: () => void;
3515
+ /** Stop auto-advance (stay on current lesson) */
3516
+ onCancel: () => void;
3517
+ /** If true, show "Course Complete" instead of next lesson */
3518
+ isComplete?: boolean;
3519
+ }
3520
+ declare const BranchTransition: React__default__default.FC<BranchTransitionProps>;
3521
+
3522
+ /**
3523
+ * SaveBranchButton — floating pill button shown in the top-right of the IDE
3524
+ * panel when the learner is in interactive mode and has made code changes.
3525
+ *
3526
+ * @packageDocumentation
3527
+ */
3528
+
3529
+ interface SaveBranchButtonProps {
3530
+ /** Whether the button should be visible */
3531
+ visible: boolean;
3532
+ /** Called when the learner clicks "Save Branch" */
3533
+ onSave: () => void;
3534
+ /** Whether the max branch limit has been reached */
3535
+ limitReached?: boolean;
3536
+ }
3537
+ declare const SaveBranchButton: React__default__default.FC<SaveBranchButtonProps>;
3538
+
3539
+ /**
3540
+ * BranchSavedToast — brief confirmation toast shown after saving a branch.
3541
+ *
3542
+ * Slides up, holds, then fades out automatically.
3543
+ *
3544
+ * @packageDocumentation
3545
+ */
3546
+
3547
+ interface BranchSavedToastProps {
3548
+ /** Whether to show the toast */
3549
+ visible: boolean;
3550
+ /** Called when the toast fully disappears */
3551
+ onDismiss: () => void;
3552
+ /** Optional message override (defaults to "Branch saved") */
3553
+ message?: string;
3554
+ /** Show as a duplicate/error toast instead of success */
3555
+ variant?: "success" | "duplicate";
3556
+ }
3557
+ declare const BranchSavedToast: React__default__default.FC<BranchSavedToastProps>;
3558
+
3559
+ /**
3560
+ * UnsavedBranchPrompt — tooltip-style popup that appears near the play button
3561
+ * when the learner exits interactive mode with unsaved code changes.
3562
+ *
3563
+ * Offers "Save & Resume" or "Discard & Resume". Auto-dismisses after 5 seconds.
3564
+ *
3565
+ * @packageDocumentation
3566
+ */
3567
+
3568
+ interface UnsavedBranchPromptProps {
3569
+ /** Whether to show the prompt */
3570
+ visible: boolean;
3571
+ /** Called when the user chooses "Save & Resume" */
3572
+ onSaveAndResume: () => void;
3573
+ /** Called when the user chooses "Discard & Resume" or prompt auto-dismisses */
3574
+ onDiscardAndResume: () => void;
3575
+ }
3576
+ declare const UnsavedBranchPrompt: React__default__default.FC<UnsavedBranchPromptProps>;
3577
+
3578
+ interface BranchPanelProps {
3579
+ /** Whether the panel is visible */
3580
+ visible: boolean;
3581
+ /** All branches for the current lesson */
3582
+ branches: LearnerBranch[];
3583
+ /** Close the panel */
3584
+ onClose: () => void;
3585
+ /** Restore a branch (seek + interactive + inject) */
3586
+ onRestore: (branch: LearnerBranch) => void;
3587
+ /** Delete a branch by id */
3588
+ onDelete: (branchId: string) => void;
3589
+ /** Rename a branch */
3590
+ onRename: (branchId: string, newLabel: string) => void;
3591
+ /** Maximum branches per lesson (for limit warning) */
3592
+ maxBranches?: number;
3593
+ /** Ref to the anchor element for fixed positioning */
3594
+ anchorRef?: React__default__default.RefObject<HTMLDivElement>;
3595
+ }
3596
+ declare function BranchPanelInner({ visible, branches, onClose, onRestore, onDelete, onRename, maxBranches, anchorRef, }: BranchPanelProps): react_jsx_runtime.JSX.Element | null;
3597
+ declare const BranchPanel: React__default__default.MemoExoticComponent<typeof BranchPanelInner>;
3598
+
2900
3599
  /**
2901
3600
  * AudioOverlay Component
2902
3601
  *
@@ -2908,11 +3607,11 @@ declare function Timeline({ currentTime, duration, markers, chunks, buffered, on
2908
3607
  /**
2909
3608
  * Overlay position presets
2910
3609
  */
2911
- type OverlayPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'custom';
3610
+ type OverlayPosition$1 = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'custom';
2912
3611
  /**
2913
3612
  * Custom position
2914
3613
  */
2915
- interface CustomPosition {
3614
+ interface CustomPosition$1 {
2916
3615
  x: number;
2917
3616
  y: number;
2918
3617
  }
@@ -2929,9 +3628,9 @@ interface AudioOverlayProps {
2929
3628
  /** Playback rate */
2930
3629
  playbackRate?: number;
2931
3630
  /** Overlay position preset */
2932
- position?: OverlayPosition;
3631
+ position?: OverlayPosition$1;
2933
3632
  /** Custom position (when position='custom') */
2934
- customPosition?: CustomPosition;
3633
+ customPosition?: CustomPosition$1;
2935
3634
  /** Whether to show the overlay */
2936
3635
  visible?: boolean;
2937
3636
  /** Enable dragging */
@@ -2941,7 +3640,7 @@ interface AudioOverlayProps {
2941
3640
  /** Opacity (0-1) */
2942
3641
  opacity?: number;
2943
3642
  /** On position change callback */
2944
- onPositionChange?: (position: CustomPosition) => void;
3643
+ onPositionChange?: (position: CustomPosition$1) => void;
2945
3644
  /** Callback to pass audio element reference for sync */
2946
3645
  onAudioRef?: (audio: HTMLAudioElement | null) => void;
2947
3646
  /** Whether audio is muted */
@@ -2960,4 +3659,55 @@ interface AudioOverlayProps {
2960
3659
  */
2961
3660
  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;
2962
3661
 
2963
- 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 };
3662
+ /**
3663
+ * WebcamOverlay Component v3
3664
+ *
3665
+ * Clean webcam video overlay with:
3666
+ * - Draggable circular bubble
3667
+ * - Maximize on hover click
3668
+ * - Smooth animations
3669
+ *
3670
+ * @packageDocumentation
3671
+ */
3672
+ type OverlayPosition = "top-right" | "top-left" | "bottom-right" | "bottom-left" | "custom";
3673
+ type OverlaySize = "small" | "medium" | "large" | "custom";
3674
+ interface CustomPosition {
3675
+ x: number;
3676
+ y: number;
3677
+ }
3678
+ interface CustomSize {
3679
+ width: number;
3680
+ height: number;
3681
+ }
3682
+ interface WebcamOverlayProps {
3683
+ src: string | null;
3684
+ currentTime: number;
3685
+ isPlaying: boolean;
3686
+ playbackRate?: number;
3687
+ position?: OverlayPosition;
3688
+ customPosition?: CustomPosition;
3689
+ size?: OverlaySize;
3690
+ customSize?: CustomSize;
3691
+ visible?: boolean;
3692
+ draggable?: boolean;
3693
+ resizable?: boolean;
3694
+ showControls?: boolean;
3695
+ borderRadius?: number | "circle";
3696
+ opacity?: number;
3697
+ onPositionChange?: (position: CustomPosition) => void;
3698
+ onSizeChange?: (size: CustomSize) => void;
3699
+ onVisibilityToggle?: () => void;
3700
+ onVideoRef?: (video: HTMLVideoElement | null) => void;
3701
+ muted?: boolean;
3702
+ volume?: number;
3703
+ syncTolerance?: number;
3704
+ /** Whether webcam is in maximized (full overlay) mode - from playback events */
3705
+ maximized?: boolean;
3706
+ /** Callback when user clicks minimize button */
3707
+ onMinimize?: () => void;
3708
+ /** Callback when user clicks maximize button */
3709
+ onMaximize?: () => void;
3710
+ }
3711
+ declare function WebcamOverlay({ src, currentTime: _currentTime, isPlaying: _isPlaying, playbackRate: _playbackRate, position, customPosition, size, customSize, visible, draggable, resizable: _resizable, showControls: _showControls, borderRadius, opacity, onPositionChange, onSizeChange: _onSizeChange, onVisibilityToggle: _onVisibilityToggle, onVideoRef, muted: _muted, volume: _volume, syncTolerance: _syncTolerance, maximized, onMinimize, onMaximize, }: WebcamOverlayProps): JSX.Element | null;
3712
+
3713
+ export { AudioOverlay, type AudioOverlayProps, type AudioQuality, type AudioSyncControls, type AudioSyncState, type BackendAdapter, BranchPanel, type BranchPanelProps, BranchSavedToast, type BranchSavedToastProps, type BranchSnapshot, BranchTransition, type BranchTransitionProps, type BufferHealth, type CTEvent, type CTManifest, type CTMarker, 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, IDEPanel, type IDEPanelProps, type InteractionPoint, type InternalEvent, type InternalRecording, type LearnerBranch, MCQOverlay, type MCQOverlayProps, type MCQResult, type MCQSubmission, type OverlayPosition$1 as 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, SaveBranchButton, type SaveBranchButtonProps, 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, UnsavedBranchPrompt, type UnsavedBranchPromptProps, type UseAudioSyncOptions, type UseBranchesOptions, type UseBranchesResult, type UseBranchingOptions, type UseBranchingResult, type UseInteractionPointsOptions, type UseInteractionPointsResult, type UsePlaybackEngineResult, type UsePlaybackOptions, type UseRemoteStreamingPlaybackOptions, type UseVideoSyncOptions, type ValidationResult$1 as ValidationResult, type VideoSyncControls, type VideoSyncState, 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, useAudioElement, useAudioSync, useBranches, useBranching, useInteractionPoints, useInteractiveMode, usePlayback, usePlayback as usePlaybackEngine, useRemoteStreamingPlayback, useStreamingPlayback, useVideoElement, useVideoSync, validateCTFile, writeCTFile };