@bendyline/squisq-editor-react 1.6.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +12 -3
  2. package/dist/index.d.ts +1304 -208
  3. package/dist/index.js +22838 -14494
  4. package/dist/index.js.map +1 -1
  5. package/dist/monaco.d.ts +1 -0
  6. package/dist/monaco.js +4 -0
  7. package/dist/monaco.js.map +1 -0
  8. package/dist/styles/index.css +2384 -408
  9. package/package.json +9 -5
  10. package/src/BlockPropertiesPopover.tsx +48 -17
  11. package/src/DocumentSettingsDialog.tsx +13 -21
  12. package/src/EditorContext.tsx +199 -42
  13. package/src/EditorShell.tsx +403 -270
  14. package/src/ImageEditor.tsx +22 -1
  15. package/src/InlinePreviewGutter.tsx +16 -8
  16. package/src/MediaBin.tsx +276 -40
  17. package/src/OutlinePanel.tsx +202 -7
  18. package/src/PlainHtmlPreview.tsx +40 -1
  19. package/src/PreviewControls.tsx +466 -93
  20. package/src/PreviewPanel.tsx +313 -77
  21. package/src/RawEditor.tsx +13 -2
  22. package/src/StatusBar.tsx +5 -2
  23. package/src/TemplateAnnotation.ts +34 -10
  24. package/src/TemplateContentPreview.tsx +56 -0
  25. package/src/TemplatePicker.tsx +355 -134
  26. package/src/ThemeCustomizerPanel.tsx +30 -15
  27. package/src/ThemePicker.tsx +10 -5
  28. package/src/TimelineBlockPreview.tsx +1 -1
  29. package/src/Toolbar.tsx +719 -238
  30. package/src/TransitionPicker.tsx +44 -5
  31. package/src/ViewMenuPanel.tsx +12 -14
  32. package/src/ViewSwitcher.tsx +4 -4
  33. package/src/WysiwygEditor.tsx +167 -92
  34. package/src/__tests__/blockPropertiesPopoverTheme.test.tsx +33 -0
  35. package/src/__tests__/blockTagActivity.test.ts +183 -0
  36. package/src/__tests__/buildDocumentPreviewMarkdown.test.ts +75 -0
  37. package/src/__tests__/buildPreviewDocContent.test.ts +48 -0
  38. package/src/__tests__/buildPreviewDocTransition.test.ts +22 -3
  39. package/src/__tests__/canvasSurfaceTextEditing.test.tsx +60 -0
  40. package/src/__tests__/customLayoutManagerTheme.test.tsx +34 -0
  41. package/src/__tests__/documentSettingsDialog.test.tsx +29 -1
  42. package/src/__tests__/editorScrollHandoff.test.tsx +102 -0
  43. package/src/__tests__/editorScrollSync.test.ts +65 -0
  44. package/src/__tests__/editorShellProps.test.tsx +390 -1
  45. package/src/__tests__/emojiPicker.test.tsx +2 -42
  46. package/src/__tests__/headingTransition.test.ts +53 -15
  47. package/src/__tests__/imageEditAffordance.test.tsx +5 -1
  48. package/src/__tests__/imageEditModalContract.test.tsx +119 -0
  49. package/src/__tests__/imageEditorShell.test.tsx +38 -1
  50. package/src/__tests__/jsonEditor.test.tsx +56 -1
  51. package/src/__tests__/layersPanel.test.tsx +75 -5
  52. package/src/__tests__/majorApiTypes.test.ts +26 -0
  53. package/src/__tests__/mediaBinDrop.test.tsx +90 -0
  54. package/src/__tests__/mediaReferences.test.ts +82 -0
  55. package/src/__tests__/narrationSave.test.ts +148 -0
  56. package/src/__tests__/outlinePanel.test.tsx +93 -3
  57. package/src/__tests__/outlineSource.test.ts +217 -0
  58. package/src/__tests__/plainHtmlPreview.test.tsx +17 -1
  59. package/src/__tests__/presentationMode.test.tsx +290 -0
  60. package/src/__tests__/previewControls.test.tsx +312 -6
  61. package/src/__tests__/previewPanelAudioFailure.test.tsx +46 -0
  62. package/src/__tests__/previewPanelPresentation.test.tsx +126 -0
  63. package/src/__tests__/previewPanelTransforms.test.tsx +117 -0
  64. package/src/__tests__/rawEditorModelIsolation.test.ts +19 -0
  65. package/src/__tests__/resolveBlockVisual.test.ts +43 -0
  66. package/src/__tests__/statusBar.test.tsx +27 -0
  67. package/src/__tests__/teleprompter.test.tsx +190 -0
  68. package/src/__tests__/templateAnnotationRoundTrip.test.ts +49 -2
  69. package/src/__tests__/templateContentPreview.test.ts +101 -0
  70. package/src/__tests__/templatePickerPortal.test.tsx +62 -0
  71. package/src/__tests__/tiptapBridge.test.ts +70 -0
  72. package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +109 -0
  73. package/src/__tests__/transformStyleId.test.ts +13 -0
  74. package/src/__tests__/useImageEditor.test.tsx +67 -0
  75. package/src/__tests__/useMediaRecorder.test.ts +75 -0
  76. package/src/__tests__/viewMenuPanel.test.tsx +130 -0
  77. package/src/__tests__/wysiwygImageUpload.test.ts +69 -0
  78. package/src/__tests__/wysiwygTemplateBadgeFocus.test.tsx +56 -0
  79. package/src/asciiDiagram/AsciiDiagramExtension.ts +343 -0
  80. package/src/asciiDiagram/AsciiDiagramWidget.tsx +224 -0
  81. package/src/asciiDiagram/RepairableDiagramExtension.ts +200 -0
  82. package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +145 -0
  83. package/src/asciiDiagram/__tests__/RepairableDiagramExtension.test.ts +159 -0
  84. package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +227 -0
  85. package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +114 -0
  86. package/src/asciiDiagram/__tests__/asciiPaste.test.ts +43 -0
  87. package/src/asciiDiagram/asciiDiagramCommands.ts +148 -0
  88. package/src/asciiDiagram/asciiDiagramData.ts +116 -0
  89. package/src/asciiDiagram/asciiDiagramOps.ts +219 -0
  90. package/src/asciiDiagram/asciiPaste.ts +19 -0
  91. package/src/blockTagActivity.ts +233 -0
  92. package/src/buildDocumentPreviewMarkdown.ts +168 -0
  93. package/src/buildPreviewDoc.ts +19 -12
  94. package/src/customTemplates/CustomLayoutManager.tsx +3 -2
  95. package/src/customTemplates/CustomTemplateContext.tsx +6 -0
  96. package/src/customTemplates/TemplateDesigner.tsx +7 -4
  97. package/src/customTemplates/__tests__/library.test.ts +8 -0
  98. package/src/customTemplates/designer.css +158 -99
  99. package/src/customTemplates/library.ts +16 -3
  100. package/src/customThemes/CustomThemeContext.tsx +6 -0
  101. package/src/customThemes/CustomThemeDialog.tsx +35 -5
  102. package/src/customThemes/ImportThemeSection.tsx +178 -0
  103. package/src/customThemes/__tests__/customThemeLibrary.test.ts +9 -0
  104. package/src/customThemes/__tests__/importThemeSection.test.tsx +192 -0
  105. package/src/customThemes/customThemeLibrary.ts +7 -4
  106. package/src/customThemes/index.ts +7 -1
  107. package/src/customThemes/themeDraft.ts +23 -7
  108. package/src/diagram/DiagramCanvas.tsx +9 -4
  109. package/src/diagram/types.ts +35 -0
  110. package/src/editorScrollSync.ts +73 -0
  111. package/src/emojiData.ts +3 -23
  112. package/src/headingTransition.ts +85 -25
  113. package/src/imageEditor/CanvasSurface.tsx +29 -23
  114. package/src/imageEditor/LayersPanel.tsx +78 -3
  115. package/src/imageEditor/Toolbar.tsx +5 -1
  116. package/src/imageEditor/icons.tsx +4 -0
  117. package/src/imageEditor/image-editor.css +75 -0
  118. package/src/imageEditor/layers/SelectionHandles.tsx +1 -1
  119. package/src/imageEditor/useImageEditor.ts +71 -14
  120. package/src/index.ts +193 -20
  121. package/src/jsonEditor/JsonEditorContext.tsx +12 -6
  122. package/src/jsonEditor/RenderNode.tsx +24 -3
  123. package/src/jsonEditor/editors.tsx +86 -19
  124. package/src/mediaEntries.ts +12 -0
  125. package/src/mediaReferences.ts +299 -0
  126. package/src/monaco.ts +35 -0
  127. package/src/monacoWorkers.ts +89 -0
  128. package/src/outlineSource.ts +171 -0
  129. package/src/presentation/PresentationMode.tsx +596 -0
  130. package/src/rawEditorIsolation.ts +18 -0
  131. package/src/recorder/hooks/useMediaRecorder.ts +97 -53
  132. package/src/resolveBlockVisual.ts +10 -4
  133. package/src/scene/Scene.tsx +101 -25
  134. package/src/scene/SceneBlockExtension.ts +17 -10
  135. package/src/scene/SceneBlockWidget.tsx +11 -4
  136. package/src/scene/SceneSelection.tsx +19 -15
  137. package/src/scene/SceneSideToolbar.tsx +89 -0
  138. package/src/scene/SceneViewport.tsx +7 -3
  139. package/src/scene/ShapePalette.tsx +17 -2
  140. package/src/scene/__tests__/DiagramAdapter.test.ts +86 -0
  141. package/src/scene/__tests__/SceneBlockExtension.test.ts +33 -0
  142. package/src/scene/__tests__/sceneIsolation.test.tsx +93 -0
  143. package/src/scene/adapters/DiagramAdapter.ts +12 -101
  144. package/src/scene/commands/SceneCommand.ts +4 -1
  145. package/src/scene/index.ts +1 -6
  146. package/src/scene/layers/DiagramEdges.tsx +24 -9
  147. package/src/scene/layers/edgeGeometry.ts +31 -4
  148. package/src/scene/layers/nodeCard.tsx +27 -8
  149. package/src/scene/scene.css +145 -2
  150. package/src/scene/text/SceneTextOverlay.tsx +5 -5
  151. package/src/scene/text/sceneTextChannel.ts +26 -20
  152. package/src/scene/text/sceneTextConfig.ts +4 -0
  153. package/src/scene/tools/ConnectTool.ts +126 -28
  154. package/src/scene/tools/DrawingConnectTool.ts +86 -16
  155. package/src/scene/tools/SceneTool.ts +10 -0
  156. package/src/scene/tools/SelectTool.ts +34 -18
  157. package/src/scene/tools/ShapeTool.ts +2 -3
  158. package/src/styles/ascii-diagram.css +79 -0
  159. package/src/styles/ascii-timeline.css +499 -0
  160. package/src/styles/editor.css +1566 -219
  161. package/src/styles/index.css +3 -0
  162. package/src/styles/tree-view.css +139 -0
  163. package/src/teleprompter/TeleprompterControls.tsx +218 -0
  164. package/src/teleprompter/TeleprompterSelfView.tsx +22 -0
  165. package/src/teleprompter/TeleprompterSurface.tsx +267 -0
  166. package/src/teleprompter/TeleprompterView.tsx +338 -0
  167. package/src/teleprompter/canvasRenderer.ts +161 -0
  168. package/src/teleprompter/floatingWindow.ts +352 -0
  169. package/src/teleprompter/index.ts +61 -0
  170. package/src/teleprompter/pcmWorklet.ts +62 -0
  171. package/src/teleprompter/recording/insertPreamble.ts +80 -0
  172. package/src/teleprompter/recording/narrationSave.ts +134 -0
  173. package/src/teleprompter/recording/useNarrationRecorder.ts +367 -0
  174. package/src/teleprompter/scrollModel.ts +85 -0
  175. package/src/teleprompter/teleprompterTheme.ts +303 -0
  176. package/src/teleprompter/types.ts +38 -0
  177. package/src/teleprompter/useFloatingWindow.ts +74 -0
  178. package/src/teleprompter/useMicAnalysis.ts +211 -0
  179. package/src/teleprompter/useTeleprompter.ts +421 -0
  180. package/src/templateContentPreviewResolver.ts +360 -0
  181. package/src/timeline/TimelineEditorWidget.tsx +702 -0
  182. package/src/timeline/TimelineViewExtension.ts +252 -0
  183. package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +355 -0
  184. package/src/timeline/__tests__/TimelineViewExtension.test.ts +163 -0
  185. package/src/timeline/__tests__/timelineCommands.test.ts +327 -0
  186. package/src/timeline/__tests__/timelineOps.test.ts +222 -0
  187. package/src/timeline/__tests__/timelinePaste.test.ts +34 -0
  188. package/src/timeline/timelineCommands.ts +367 -0
  189. package/src/timeline/timelineData.ts +54 -0
  190. package/src/timeline/timelineOps.ts +277 -0
  191. package/src/timeline/timelinePaste.ts +8 -0
  192. package/src/tiptapBridge.ts +84 -49
  193. package/src/transformStyleId.ts +17 -0
  194. package/src/treeview/TreeOutlineWidget.tsx +240 -0
  195. package/src/treeview/TreeViewExtension.ts +250 -0
  196. package/src/treeview/__tests__/TreeViewExtension.test.ts +122 -0
  197. package/src/treeview/__tests__/treeOps.test.ts +125 -0
  198. package/src/treeview/__tests__/treePaste.test.ts +40 -0
  199. package/src/treeview/__tests__/treeViewCommands.test.ts +163 -0
  200. package/src/treeview/treeOps.ts +172 -0
  201. package/src/treeview/treePaste.ts +13 -0
  202. package/src/treeview/treeViewCommands.ts +94 -0
  203. package/src/treeview/treeViewData.ts +41 -0
  204. package/src/useMonacoLoader.ts +11 -34
  205. package/src/wysiwygImageUpload.ts +113 -0
  206. package/src/diagram/DiagramExtension.ts +0 -209
  207. package/src/diagram/DiagramWidget.tsx +0 -270
  208. package/src/diagram/useDiagramData.ts +0 -126
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
- import { ReactNode, CSSProperties, RefObject } from 'react';
4
- import { Doc, MediaProvider, Theme, ViewportPreset, ViewportConfig, CustomTemplateDefinition, SurfaceScheme, ImageEditDoc, ImageEditLayer } from '@bendyline/squisq/schemas';
5
- import { MarkdownDocument, HeadingAttributes } from '@bendyline/squisq/markdown';
3
+ import { ReactNode, CSSProperties, RefObject, KeyboardEvent } from 'react';
4
+ import { Doc, MediaProvider, Theme, CustomTemplateDefinition, ThemeSeedColors, ViewportPreset, ViewportConfig, Block, MediaEntry, DiagramEdgeAnchor, SurfaceScheme, ImageEditDoc, ImageEditLayer } from '@bendyline/squisq/schemas';
5
+ import { MarkdownDocument } from '@bendyline/squisq/markdown';
6
6
  import { ContentContainer } from '@bendyline/squisq/storage';
7
7
  import { DocumentVersionManager, SaveVersionOptions, SaveVersionResult, PrunePolicy } from '@bendyline/squisq/versions';
8
8
  import * as _tiptap_core from '@tiptap/core';
@@ -11,10 +11,14 @@ import * as monaco_editor from 'monaco-editor';
11
11
  import { editor } from 'monaco-editor';
12
12
  import { IconFamily } from '@bendyline/squisq/icons';
13
13
  import { DisplayMode, CaptionStyle } from '@bendyline/squisq-react';
14
- import { Editor as Editor$1 } from '@tiptap/react';
14
+ import { ConnectorRouting, AsciiDiagram, Tree, AsciiTimeline, AsciiTimelineSide, AsciiTimelineMarker } from '@bendyline/squisq/doc';
15
15
  import * as _tiptap_extension_heading from '@tiptap/extension-heading';
16
+ import { DecorationSet } from '@tiptap/pm/view';
17
+ import { Editor as Editor$1 } from '@tiptap/react';
18
+ import { PluginKey } from '@tiptap/pm/state';
16
19
  import { Node } from '@tiptap/pm/model';
17
20
  import { SquisqAnnotatedSchema, JsonFormValidator, JsonFormValidationError } from '@bendyline/squisq/jsonForm';
21
+ import { NarrationTrace, NarrationAlignment, NarrationScript, NarrationTimingJsonV3, VadConfig } from '@bendyline/squisq/narration';
18
22
  import { ImageEditExportFormat, ImageEditVersionManager } from '@bendyline/squisq/imageEdit';
19
23
 
20
24
  /**
@@ -23,24 +27,31 @@ import { ImageEditExportFormat, ImageEditVersionManager } from '@bendyline/squis
23
27
  * a given layer. The Scene owns the editing UI (overlay + positioning);
24
28
  * the host owns persistence (markdown heading vs. layout JSON blob).
25
29
  */
30
+
26
31
  type SceneTextLevel = 'inline' | 'block' | 'rich';
27
32
 
28
33
  /**
29
- * sceneTextChannel — a module singleton that bridges the canvas's inline
34
+ * sceneTextChannel — an editor-owned channel that bridges the canvas's inline
30
35
  * text editor (which renders in a **detached React root** created by the
31
36
  * Diagram/SceneBlock ProseMirror extensions, outside `<EditorProvider>`)
32
37
  * to the provider, so the top formatting toolbar can target it.
33
38
  *
34
39
  * The active textbox's `SceneTextOverlay` publishes its Tiptap editor here
35
40
  * on focus and clears it on blur/unmount; `EditorProvider` subscribes and
36
- * mirrors the handle into `activeSceneText`. Singleton because only one
37
- * canvas textbox can be focused at a time.
41
+ * mirrors the handle into `activeSceneText`. Each EditorProvider creates its
42
+ * own channel, preventing focus in one editor from mutating another editor.
38
43
  */
39
44
 
40
45
  interface SceneTextHandle {
41
46
  editor: Editor;
42
47
  level: SceneTextLevel;
43
48
  }
49
+ type Listener = (handle: SceneTextHandle | null) => void;
50
+ interface SceneTextChannel {
51
+ set(handle: SceneTextHandle | null): void;
52
+ get(): SceneTextHandle | null;
53
+ subscribe(listener: Listener): () => void;
54
+ }
44
55
 
45
56
  /** Monaco standalone code editor instance type */
46
57
  type MonacoEditor = editor.IStandaloneCodeEditor;
@@ -114,6 +125,11 @@ type LayoutMode = 'document' | 'block' | 'timeline';
114
125
  * colors. `'none'` opts out completely.
115
126
  */
116
127
  type ThemeInheritance = 'none' | 'fonts' | 'fonts-colors';
128
+ /**
129
+ * When inline block-template tags are shown in the WYSIWYG surface.
130
+ * `'active'` shows tags for the cursor's block and the block under the pointer.
131
+ */
132
+ type BlockTagVisibility = 'none' | 'active' | 'always';
117
133
  /**
118
134
  * Editor operating mode. `markdown` is the full experience (WYSIWYG +
119
135
  * Preview tabs, formatting toolbar). `code` is a Monaco-only view used
@@ -158,12 +174,11 @@ interface EditorState {
158
174
  * the toolbar can toggle it at runtime.
159
175
  */
160
176
  outlineVisible: boolean;
177
+ /** When inline block-template tags are shown in the WYSIWYG view. */
178
+ blockTagVisibility: BlockTagVisibility;
161
179
  /**
162
- * Whether inline block-template tags (the chip next to each templated
163
- * heading in the WYSIWYG view, plus the subtle affordance chip on plain
164
- * headings) are currently visible. Initialized from the EditorShell
165
- * `blockTags` prop (default true); the View menu in the toolbar can
166
- * toggle it at runtime.
180
+ * Whether inline block-template tags can currently be visible.
181
+ * Kept for compatibility; prefer {@link blockTagVisibility}.
167
182
  */
168
183
  blockTagsVisible: boolean;
169
184
  /**
@@ -195,6 +210,13 @@ interface EditorState {
195
210
  * shell.
196
211
  */
197
212
  allowRecording: boolean;
213
+ /**
214
+ * Whether the Narrate (teleprompter) display mode is offered under the
215
+ * Use tab. Orthogonal to `allowRecording`: the teleprompter is useful
216
+ * without any capture (reading for external recording software), and a
217
+ * host may allow the recorder modal but not want a prompter surface.
218
+ */
219
+ allowNarrate: boolean;
198
220
  /**
199
221
  * Document layout mode. `'document'` (default) edits the whole document;
200
222
  * `'block'` activates the block-at-a-time card view. Initialized from the
@@ -254,6 +276,8 @@ interface EditorActions {
254
276
  setOutlineVisible: (visible: boolean) => void;
255
277
  /** Show or hide inline block-template tags at runtime (driven by the View menu). */
256
278
  setBlockTagsVisible: (visible: boolean) => void;
279
+ /** Choose when inline block-template tags are shown. */
280
+ setBlockTagVisibility: (visibility: BlockTagVisibility) => void;
257
281
  /** Change how much of the active Squisq theme the WYSIWYG surface mirrors. */
258
282
  setThemeInheritance: (mode: ThemeInheritance) => void;
259
283
  /** Insert text at the current cursor position in the active editor */
@@ -289,6 +313,8 @@ interface EditorContextValue extends EditorState, EditorActions {
289
313
  * buttons apply (`inline` = marks only; `rich` = headings/lists too).
290
314
  */
291
315
  activeSceneText: SceneTextHandle | null;
316
+ /** Instance-owned bridge used by detached scene widget roots. */
317
+ sceneTextChannel: SceneTextChannel;
292
318
  /**
293
319
  * Workspace-scoped `ContentContainer` for this document — the folder
294
320
  * holding the doc, its `_files/` sidecar, sibling documents, and any
@@ -405,6 +431,12 @@ interface EditorProviderProps {
405
431
  * surfaces where camera/screen prompts would be jarring).
406
432
  */
407
433
  allowRecording?: boolean;
434
+ /**
435
+ * Whether the Narrate (teleprompter) display mode is offered under the
436
+ * Use tab. Defaults to true. When false the mode button is hidden and a
437
+ * frontmatter-forced `display-mode: narrate` clamps back to video.
438
+ */
439
+ allowNarrate?: boolean;
408
440
  /**
409
441
  * File name (e.g. `foo.ts`) or bare extension — used to pick a Monaco
410
442
  * language and decide between markdown vs. code mode.
@@ -428,10 +460,16 @@ interface EditorProviderProps {
428
460
  */
429
461
  outline?: boolean;
430
462
  /**
431
- * Initial visibility of inline block-template tags on headings.
432
- * Defaults to true. The toolbar's View menu can toggle it at runtime.
463
+ * Legacy initial visibility of inline block-template tags on headings.
464
+ * `true` maps to always visible and `false` maps to hidden. When omitted,
465
+ * {@link blockTagVisibility} defaults to `'active'`.
433
466
  */
434
467
  blockTags?: boolean;
468
+ /**
469
+ * Initial block-tag visibility mode. When set, this takes precedence over
470
+ * the legacy boolean {@link blockTags} prop. Defaults to `'active'`.
471
+ */
472
+ blockTagVisibility?: BlockTagVisibility;
435
473
  /**
436
474
  * Initial value for how much of the active Squisq theme the WYSIWYG
437
475
  * editing surface should mirror. Defaults to `'fonts'` — the
@@ -449,9 +487,9 @@ interface EditorProviderProps {
449
487
  * Bundled view preferences — a serializable JSON blob covering all
450
488
  * runtime-toggleable view options. When provided, individual values
451
489
  * here override the matching individual props (`inlinePreview`,
452
- * `showStatusBar`, `outline`). Hosts wiring this up typically load
453
- * the blob from their own preferences storage and pair it with
454
- * {@link onViewPreferencesChange}.
490
+ * `showStatusBar`, `outline`, `blockTagVisibility`, `blockTags`). Hosts
491
+ * wiring this up typically load the blob from their own preferences storage
492
+ * and pair it with {@link onViewPreferencesChange}.
455
493
  */
456
494
  viewPreferences?: ViewPreferences;
457
495
  /**
@@ -478,12 +516,14 @@ interface ViewPreferences {
478
516
  showStatusBar?: boolean;
479
517
  /** Whether inline block-template tags on headings are visible. */
480
518
  blockTags?: boolean;
519
+ /** When inline block-template tags are shown. Takes precedence over `blockTags`. */
520
+ blockTagVisibility?: BlockTagVisibility;
481
521
  /** How much of the active Squisq theme the WYSIWYG surface mirrors. */
482
522
  themeInheritance?: ThemeInheritance;
483
523
  /** Document vs. block-at-a-time layout. */
484
524
  layoutMode?: LayoutMode;
485
525
  }
486
- declare function EditorProvider({ initialMarkdown, initialView, articleId, colorScheme: initialColorScheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, allowRecording, fileName, language, inlinePreview, showStatusBar, outline, blockTags, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
526
+ declare function EditorProvider({ initialMarkdown, initialView, articleId, colorScheme: initialColorScheme, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, mediaProvider, imageDisplayMode, mentionProvider, documentLinkProvider, allowRecording, allowNarrate, fileName, language, inlinePreview, showStatusBar, outline, blockTags, blockTagVisibility: initialBlockTagVisibility, themeInheritance, layoutMode, viewPreferences, onViewPreferencesChange, children, }: EditorProviderProps): react_jsx_runtime.JSX.Element;
487
527
 
488
528
  /**
489
529
  * Host-supplied context sections rendered INSIDE the Monaco (raw/code)
@@ -611,12 +651,6 @@ interface EditorShellProps {
611
651
  * this container via `createMediaProviderFromContainer`.
612
652
  */
613
653
  workspaceContainer?: ContentContainer | null;
614
- /**
615
- * @deprecated Renamed to `workspaceContainer` to make the workspace-
616
- * vs. doc-scoped distinction explicit. Still accepted as a fallback
617
- * for now; remove in the next breaking release.
618
- */
619
- container?: ContentContainer | null;
620
654
  /**
621
655
  * Enable version history. Snapshots are stored at
622
656
  * `.versions/<basename>.<timestamp>.md` inside the same
@@ -768,6 +802,14 @@ interface EditorShellProps {
768
802
  * the button is hidden regardless of this prop.
769
803
  */
770
804
  allowRecording?: boolean;
805
+ /**
806
+ * Whether the Narrate (teleprompter) display mode is offered under the
807
+ * Use tab. Defaults to true. Orthogonal to `allowRecording` — the
808
+ * prompter is useful without capture (reading for external recording
809
+ * software), and the in-mode Record affordance additionally requires
810
+ * `allowRecording` + a `mediaProvider`.
811
+ */
812
+ allowNarrate?: boolean;
771
813
  /**
772
814
  * Placeholder text shown in the WYSIWYG editor while the document is
773
815
  * empty. When omitted, the editor rotates through its own generic
@@ -847,12 +889,16 @@ interface EditorShellProps {
847
889
  */
848
890
  outlineWidth?: number;
849
891
  /**
850
- * Initial visibility of inline block-template tags on headings — the
851
- * chip rendered next to each heading in the WYSIWYG view that opens
852
- * the block-template picker. Defaults to true; the View menu can
853
- * toggle it at runtime regardless of the initial value.
892
+ * Legacy initial visibility of inline block-template tags on headings.
893
+ * `true` maps to always visible and `false` maps to hidden. When omitted,
894
+ * {@link blockTagVisibility} defaults to `'active'`.
854
895
  */
855
896
  blockTags?: boolean;
897
+ /**
898
+ * Initial block-tag visibility mode. Takes precedence over `blockTags`.
899
+ * Defaults to `'active'` (selected/hovered block only).
900
+ */
901
+ blockTagVisibility?: BlockTagVisibility;
856
902
  /**
857
903
  * How much of the active Squisq theme the WYSIWYG editing surface
858
904
  * mirrors. Defaults to `'fonts'` — the historical behavior of
@@ -864,7 +910,8 @@ interface EditorShellProps {
864
910
  * Bundled view preferences — a serializable JSON blob covering the
865
911
  * runtime-toggleable view options surfaced in the View menu. When
866
912
  * provided, fields here override the corresponding individual props
867
- * (`outline`, `inlinePreview`, `showStatusBar`). Pair with
913
+ * (`outline`, `inlinePreview`, `showStatusBar`, `blockTagVisibility`,
914
+ * `blockTags`). Pair with
868
915
  * {@link onViewPreferencesChange} to externalize storage of these
869
916
  * preferences in the host.
870
917
  */
@@ -888,7 +935,7 @@ interface EditorShellProps {
888
935
  * Complete markdown editor shell with toolbar, view switcher, and three
889
936
  * editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
890
937
  */
891
- declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, colorScheme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, container, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, showPlayTab, submitOnEnter, codeContext, fullWidth, uxFont, thinMargins, showStatusBar, imageDisplayMode, fileName, language, mentionProvider, documentLinkProvider, allowRecording, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
938
+ declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, colorScheme, className, height, minHeight, maxHeight, mediaProvider, workspaceContainer, allowVersioning, versionBasename, versioningPrunePolicy, versioningAutoSaveIdleMs, onSaveVersion, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, showPlayTab, submitOnEnter, codeContext, fullWidth, uxFont, thinMargins, showStatusBar, imageDisplayMode, fileName, language, mentionProvider, documentLinkProvider, allowRecording, allowNarrate, placeholder, readOnly, imageSrc, imageAlt, imageMode, imageEditorContainer, onImageExport, inlinePreview, inlinePreviewWidth, outline, outlineWidth, blockTags, blockTagVisibility, themeInheritance, viewPreferences, onViewPreferencesChange, themeOverride, }: EditorShellProps): react_jsx_runtime.JSX.Element;
892
939
 
893
940
  /**
894
941
  * Bridges a host-supplied {@link CodeContext} onto the live Monaco editor:
@@ -1299,8 +1346,10 @@ interface PlainHtmlPreviewProps {
1299
1346
  theme?: Theme;
1300
1347
  className?: string;
1301
1348
  style?: CSSProperties;
1349
+ /** Let unmodified Up/Down arrows scroll the preview without requiring iframe focus. */
1350
+ globalKeyboardShortcuts?: boolean;
1302
1351
  }
1303
- declare function PlainHtmlPreview({ markdown, title, images, mediaProvider, mediaRevision, theme, className, style, }: PlainHtmlPreviewProps): react_jsx_runtime.JSX.Element;
1352
+ declare function PlainHtmlPreview({ markdown, title, images, mediaProvider, mediaRevision, theme, className, style, globalKeyboardShortcuts, }: PlainHtmlPreviewProps): react_jsx_runtime.JSX.Element;
1304
1353
 
1305
1354
  /**
1306
1355
  * Emoji Dataset
@@ -1316,29 +1365,6 @@ declare function PlainHtmlPreview({ markdown, title, images, mediaProvider, medi
1316
1365
  * because the picker is meant for quick insertion, not exhaustive
1317
1366
  * lookup; the user can paste anything we don't have.
1318
1367
  */
1319
- interface EmojiEntry {
1320
- /** The actual emoji glyph (may be multi-codepoint, e.g. ZWJ sequences). */
1321
- char: string;
1322
- /** Display name shown in the tooltip. */
1323
- name: string;
1324
- /** Lowercase keywords for the search index — name words plus aliases. */
1325
- keywords: string;
1326
- }
1327
- interface EmojiCategory {
1328
- id: string;
1329
- label: string;
1330
- /** Single-glyph icon shown on the tab. */
1331
- icon: string;
1332
- emojis: EmojiEntry[];
1333
- }
1334
- declare const EMOJI_CATEGORIES: EmojiCategory[];
1335
- /**
1336
- * Flat list of every emoji across all categories — used as the search
1337
- * corpus and to expose a `getEmojiByChar` lookup.
1338
- */
1339
- declare const ALL_EMOJIS: EmojiEntry[];
1340
- /** Search for emoji whose name or keywords contain the query (case-insensitive). */
1341
- declare function searchEmojis(query: string, limit?: number): EmojiEntry[];
1342
1368
 
1343
1369
  /**
1344
1370
  * Discriminated picker entry. The picker UI dispatches on `kind` to
@@ -1359,6 +1385,29 @@ type PickerEntry = {
1359
1385
  /** Canonical token used in the markdown source (bare or qualified). */
1360
1386
  token: string;
1361
1387
  };
1388
+ interface PickerCategory {
1389
+ id: string;
1390
+ label: string;
1391
+ /** What to render on the category tab. Emoji glyph or FA class. */
1392
+ tab: {
1393
+ kind: 'emoji';
1394
+ char: string;
1395
+ } | {
1396
+ kind: 'icon';
1397
+ family: IconFamily;
1398
+ name: string;
1399
+ };
1400
+ entries: PickerEntry[];
1401
+ }
1402
+ /**
1403
+ * The full picker category list — emoji buckets followed by the three
1404
+ * FontAwesome families. Drives the EmojiPicker tabs.
1405
+ */
1406
+ declare const PICKER_CATEGORIES: PickerCategory[];
1407
+ /** Flat corpus for the picker search index. */
1408
+ declare const ALL_PICKER_ENTRIES: PickerEntry[];
1409
+ /** Lowercase substring search across emoji + icon entries. */
1410
+ declare function searchPickerEntries(query: string, limit?: number): PickerEntry[];
1362
1411
 
1363
1412
  interface EmojiPickerProps {
1364
1413
  /** Whether the picker is visible. */
@@ -1527,6 +1576,97 @@ declare function useDocCustomThemes(): DocCustomThemes;
1527
1576
 
1528
1577
  /** Where a saved theme lands — mirrors `DesignerSaveTarget` for templates. */
1529
1578
  type ThemeSaveTarget = 'doc' | 'library';
1579
+ /** Extra assets riding along with a theme save (file-import byproducts). */
1580
+ interface ThemeSaveExtras {
1581
+ /** Custom layout templates inferred from an imported PPTX. */
1582
+ templates?: CustomTemplateDefinition[];
1583
+ }
1584
+
1585
+ /**
1586
+ * themeDraft — the editable subset of the Theme schema, shared by the
1587
+ * ThemeCustomizerPanel popover and the CustomThemeDialog.
1588
+ *
1589
+ * A `Draft` mirrors the handful of fields a user actually edits (name, seed
1590
+ * colors, N accents, fonts, style presets, and a base theme to inherit from).
1591
+ * `compileDraft` turns it into a full validated `Theme` via `compileTheme`;
1592
+ * everything the draft doesn't mention inherits from the chosen base (or the
1593
+ * compiler's neutral STARTER_THEME when no base is picked).
1594
+ */
1595
+
1596
+ declare const BORDER_RADIUS_PRESETS: {
1597
+ readonly sharp: 0;
1598
+ readonly soft: 6;
1599
+ readonly rounded: 16;
1600
+ };
1601
+ type BorderRadiusPreset = keyof typeof BORDER_RADIUS_PRESETS;
1602
+ declare const ANIMATION_SPEED_PRESETS: {
1603
+ readonly static: 0;
1604
+ readonly subtle: 1.4;
1605
+ readonly normal: 1;
1606
+ readonly expressive: 0.7;
1607
+ };
1608
+ type AnimationSpeedPreset = keyof typeof ANIMATION_SPEED_PRESETS;
1609
+ declare const TEXT_SHADOW_PRESETS: {
1610
+ readonly off: false;
1611
+ readonly on: true;
1612
+ };
1613
+ type TextShadowPreset = keyof typeof TEXT_SHADOW_PRESETS;
1614
+ declare const CONTRAST_PRESETS: readonly ["subtle", "balanced", "high"];
1615
+ type ContrastPreset = (typeof CONTRAST_PRESETS)[number];
1616
+ declare const IMAGE_TREATMENT_PRESETS: readonly ["none", "mono", "duotone", "warm", "cool"];
1617
+ type ImageTreatmentPreset = (typeof IMAGE_TREATMENT_PRESETS)[number];
1618
+ declare const FALLBACK_OPTIONS: readonly ["sans-serif", "serif", "monospace", "system-ui"];
1619
+ type FallbackOption = (typeof FALLBACK_OPTIONS)[number];
1620
+ interface CustomFontInput {
1621
+ kind: 'curated' | 'custom';
1622
+ stackId?: string;
1623
+ customName?: string;
1624
+ customFallback?: FallbackOption;
1625
+ }
1626
+ /** One editable accent: a color plus the colorScheme key it maps to. */
1627
+ interface AccentInput {
1628
+ key: string;
1629
+ color: string;
1630
+ }
1631
+ interface Draft {
1632
+ name: string;
1633
+ /** Id of the base theme this draft inherits render style / layout from. */
1634
+ baseId?: string;
1635
+ seeds: ThemeSeedColors;
1636
+ /** The "N accents" — each becomes a `colorSchemes` entry. */
1637
+ accents: AccentInput[];
1638
+ /** True once the user has touched the accent list; gates wholesale replace. */
1639
+ accentsEdited: boolean;
1640
+ titleFont: CustomFontInput;
1641
+ bodyFont: CustomFontInput;
1642
+ borderRadius: BorderRadiusPreset;
1643
+ animationSpeed: AnimationSpeedPreset;
1644
+ textShadow: TextShadowPreset;
1645
+ contrast: ContrastPreset;
1646
+ imageTreatment: ImageTreatmentPreset;
1647
+ }
1648
+ /**
1649
+ * Draft patch from a theme inferred from an imported office file: name,
1650
+ * seeds, accents (marked authored so the file's schemes survive a re-save),
1651
+ * and fonts. Deliberately leaves `baseId` and the style presets untouched —
1652
+ * the user's in-progress choices there aren't the file's to overwrite.
1653
+ */
1654
+ declare function draftPatchFromImportedTheme(theme: Theme): Partial<Draft>;
1655
+
1656
+ interface ImportedThemeResult {
1657
+ theme: Theme;
1658
+ /** PPTX only, when `allowLayouts`: custom templates derived from slide layouts. */
1659
+ layouts?: CustomTemplateDefinition[];
1660
+ warnings: string[];
1661
+ fileName: string;
1662
+ }
1663
+ interface ImportThemeSectionProps {
1664
+ /** Fired on successful inference with the draft patch + full result. */
1665
+ onImported: (patch: Partial<Draft>, result: ImportedThemeResult) => void;
1666
+ /** Also derive PPTX slide layouts as custom templates. Default false. */
1667
+ allowLayouts?: boolean;
1668
+ }
1669
+ declare function ImportThemeSection({ onImported, allowLayouts }: ImportThemeSectionProps): react_jsx_runtime.JSX.Element;
1530
1670
 
1531
1671
  /** Caption selection: off, or one of the two enabled styles. */
1532
1672
  type CaptionMode = 'off' | CaptionStyle;
@@ -1549,6 +1689,10 @@ interface PreviewSettings {
1549
1689
  * that style. The single entry point so the toggle buttons persist in one
1550
1690
  * frontmatter write. */
1551
1691
  setCaptionMode: (mode: CaptionMode) => void;
1692
+ /** Whether Squisq should synthesize and show its managed cover slide. */
1693
+ activeCoverSlide: boolean;
1694
+ /** Enable/disable the managed cover slide. */
1695
+ setCoverSlideEnabled: (enabled: boolean) => void;
1552
1696
  /** User-authored themes (doc + browser library) for the picker's "Custom" group. */
1553
1697
  customThemes: Theme[];
1554
1698
  /** Open the custom-theme designer for a theme (or null to create a new one). */
@@ -1563,7 +1707,7 @@ interface PreviewSettings {
1563
1707
  interface ThemeDesignerConfig {
1564
1708
  value: Theme | null;
1565
1709
  onChange: (theme: Theme) => void;
1566
- onSave: (theme: Theme, target: ThemeSaveTarget) => void;
1710
+ onSave: (theme: Theme, target: ThemeSaveTarget, extras?: ThemeSaveExtras) => void;
1567
1711
  onClose: () => void;
1568
1712
  }
1569
1713
  declare function usePreviewSettings(): PreviewSettings;
@@ -1586,31 +1730,39 @@ declare function PreviewSettingsProvider({ doc, children, themeOverride, }: Prev
1586
1730
  * whole row in and out at a fixed window-width breakpoint, the controls
1587
1731
  * measure how many of them actually fit in the width the toolbar gives them
1588
1732
  * and keep that many inline, folding the rest — from the low-priority end of
1589
- * {@link CONTROL_KEYS} — into a single settings (gear) button's popover. As
1733
+ * {@link CONTROL_KEYS} — into a single ellipsis button's popover. As
1590
1734
  * the toolbar widens or narrows, controls migrate one at a time between the
1591
1735
  * inline row and the menu, so the available space is always well used and the
1592
1736
  * row never wraps onto a second line.
1593
1737
  */
1594
1738
  declare function PreviewToolbarControls(): react_jsx_runtime.JSX.Element;
1595
1739
  /**
1596
- * Segmented display-mode switch (Video / Slideshow / Document / Page) rendered
1597
- * as four connected buttons on the left of the Play toolbar — the prominent,
1598
- * one-click counterpart to the old "Mode:" dropdown. Reads and writes the same
1599
- * `activeDisplayMode` in preview settings.
1740
+ * Segmented display-mode switch retained as a public, embeddable control.
1741
+ * The editor shell uses {@link PreviewModeMenu} beside its Use tab instead.
1742
+ * Narrate is hidden when the host disables `allowNarrate`.
1600
1743
  */
1601
1744
  declare function PreviewModeSwitch(): react_jsx_runtime.JSX.Element;
1602
1745
  /**
1603
- * Segmented aspect-ratio switch (16:9 / 1:1 / 9:16 / 4:3) rendered as connected
1604
- * icon buttons on the left of the Play toolbar, next to the mode switch — the
1605
- * one-click counterpart to the old "Format:" dropdown. Reads and writes the
1606
- * same `activePreset` in preview settings.
1746
+ * Dropdown trigger rendered directly beside the Use tab. Selecting a mode
1747
+ * also enters the Use view, so the menu works from Write and Source as well
1748
+ * as from an already-active preview.
1749
+ */
1750
+ interface PreviewModeMenuProps {
1751
+ /** Incremented by the parent to open the menu from another control. */
1752
+ openRequest?: number;
1753
+ }
1754
+ declare function PreviewModeMenu({ openRequest }: PreviewModeMenuProps): react_jsx_runtime.JSX.Element;
1755
+ /**
1756
+ * Segmented aspect-ratio switch (16:9 / 1:1 / 9:16 / 4:3), used inline in the
1757
+ * Use toolbar and inside its overflow popover. Reads and writes the same
1758
+ * `activePreset` in preview settings.
1607
1759
  */
1608
1760
  declare function PreviewFormatSwitch(): react_jsx_runtime.JSX.Element;
1609
1761
 
1610
1762
  /**
1611
1763
  * ViewSwitcher
1612
1764
  *
1613
- * Tab bar for switching between Raw, WYSIWYG, and Preview editor views.
1765
+ * Tab bar for switching between Write, Source, and Use editor views.
1614
1766
  */
1615
1767
  interface ViewSwitcherProps {
1616
1768
  /** Additional class name */
@@ -1626,13 +1778,15 @@ interface ToolbarProps {
1626
1778
  className?: string;
1627
1779
  /** Whether the Files panel is currently shown */
1628
1780
  showFiles?: boolean;
1781
+ /** Number of files currently available through the MediaProvider. */
1782
+ fileCount?: number;
1629
1783
  /** Toggle the Files panel. When provided, a "Files" button appears in the toolbar. */
1630
1784
  onToggleFiles?: () => void;
1631
1785
  /** Content rendered at the left edge of the toolbar, before the view tabs. */
1632
1786
  slotLeft?: ReactNode;
1633
1787
  /** Content rendered immediately after the view tabs, on the left side of the
1634
1788
  * toolbar (before the formatting controls). Used for the preview mode
1635
- * switch in Play view. */
1789
+ * switch in Use view. */
1636
1790
  slotAfterTabs?: ReactNode;
1637
1791
  /** Content rendered after the formatting controls (in the middle area). */
1638
1792
  slotAfterActions?: ReactNode;
@@ -1650,7 +1804,7 @@ interface ToolbarProps {
1650
1804
  * - WYSIWYG: calls Tiptap chain commands (toggleBold, etc.)
1651
1805
  * - Raw: appends markdown syntax to the source
1652
1806
  */
1653
- declare function Toolbar({ className, showFiles, onToggleFiles, slotLeft, slotAfterTabs, slotAfterActions, slotRight, showPlayTab, }: ToolbarProps): react_jsx_runtime.JSX.Element;
1807
+ declare function Toolbar({ className, showFiles, fileCount, onToggleFiles, slotLeft, slotAfterTabs, slotAfterActions, slotRight, showPlayTab, }: ToolbarProps): react_jsx_runtime.JSX.Element;
1654
1808
 
1655
1809
  /**
1656
1810
  * VersionHistoryPanel
@@ -1688,8 +1842,10 @@ interface OutlinePanelProps {
1688
1842
  width?: number;
1689
1843
  /** Optional CSS class for the outer container. */
1690
1844
  className?: string;
1845
+ /** Disable heading-level changes and section reordering. */
1846
+ readOnly?: boolean;
1691
1847
  }
1692
- declare function OutlinePanel({ width, className }: OutlinePanelProps): react_jsx_runtime.JSX.Element;
1848
+ declare function OutlinePanel({ width, className, readOnly }: OutlinePanelProps): react_jsx_runtime.JSX.Element;
1693
1849
 
1694
1850
  interface ThemeCustomizerPanelProps {
1695
1851
  /** Current custom theme (or null to start from defaults). */
@@ -1700,8 +1856,19 @@ interface ThemeCustomizerPanelProps {
1700
1856
  onSave?: (theme: Theme, json: string) => void;
1701
1857
  /** Fired when the user clicks Reset. Host typically clears its persistent storage. */
1702
1858
  onReset?: () => void;
1859
+ /** Optional text trigger. Omit to use the compact icon trigger. */
1860
+ triggerLabel?: string;
1861
+ }
1862
+ declare function ThemeCustomizerPanel({ value, onChange, onSave, onReset, triggerLabel, }: ThemeCustomizerPanelProps): react_jsx_runtime.JSX.Element;
1863
+
1864
+ interface TemplatePreviewSource {
1865
+ block: Block;
1866
+ theme: Theme;
1867
+ viewport: ViewportConfig;
1868
+ basePath?: string;
1869
+ mediaProvider?: MediaProvider | null;
1870
+ customTemplates?: readonly CustomTemplateDefinition[];
1703
1871
  }
1704
- declare function ThemeCustomizerPanel({ value, onChange, onSave, onReset, }: ThemeCustomizerPanelProps): react_jsx_runtime.JSX.Element;
1705
1872
 
1706
1873
  /**
1707
1874
  * Convert a camelCase template id to a human-readable label. Accepts both
@@ -1714,6 +1881,8 @@ declare function templateLabel(name: string, customTemplates?: readonly CustomTe
1714
1881
  interface TemplatePickerProps {
1715
1882
  value: string;
1716
1883
  onChange: (name: string) => void;
1884
+ /** Active editor chrome theme, used by the portaled dialog. */
1885
+ colorScheme?: 'light' | 'dark';
1717
1886
  /** When true, shows only the trigger button (no popover) — used in the overflow menu. */
1718
1887
  compact?: boolean;
1719
1888
  /**
@@ -1729,8 +1898,14 @@ interface TemplatePickerProps {
1729
1898
  * hidden.
1730
1899
  */
1731
1900
  onOpenDesigner?: () => void;
1901
+ /**
1902
+ * Active block content used to render live template thumbnails. When omitted,
1903
+ * or when a template cannot be meaningfully derived from the block, cards use
1904
+ * their static wireframe icons.
1905
+ */
1906
+ previewSource?: TemplatePreviewSource;
1732
1907
  }
1733
- declare function TemplatePicker({ value, onChange, compact, recommended, onOpenDesigner, }: TemplatePickerProps): react_jsx_runtime.JSX.Element;
1908
+ declare function TemplatePicker({ value, onChange, colorScheme, compact, recommended, onOpenDesigner, previewSource, }: TemplatePickerProps): react_jsx_runtime.JSX.Element;
1734
1909
 
1735
1910
  /**
1736
1911
  * headingTransition
@@ -1740,16 +1915,13 @@ declare function TemplatePicker({ value, onChange, compact, recommended, onOpenD
1740
1915
  *
1741
1916
  * - Markdown (Monaco): operate on the raw heading line string.
1742
1917
  * - WYSIWYG (Tiptap): operate on the heading node's `dataBlockAttrs` string
1743
- * (the inner of the Pandoc `{…}` block, no braces matching how
1744
- * `tiptapBridge` stores and re-emits it).
1918
+ * (Pandoc `{…}` inner) and `dataTemplateParams` string (the params inside
1919
+ * the squisq-native `{[…]}` annotation).
1745
1920
  *
1746
- * Transitions are stored in the Pandoc `{#id .class key=value}` attribute
1747
- * block, NOT the `{[template …]}` annotation. That mirrors the canonical
1748
- * serializer (`core/doc/docToMarkdown.ts` `ensureTransitionAttributes`,
1749
- * which always emits the `{…}` form) and `diagram/diagramCommands.ts`, so a
1750
- * value set here round-trips through a Doc render without being duplicated
1751
- * or moved. Reads still look at the `{[…]}` params too, so a hand-typed
1752
- * `{[title transition=fade]}` shows up in the picker.
1921
+ * The editor writes transitions to the squisq-native `{[…]}` annotation by
1922
+ * default. It still reads legacy Pandoc `{transition=…}` attributes and
1923
+ * removes/migrates those keys when rewriting, so the two channels cannot drift
1924
+ * after a toolbar edit.
1753
1925
  *
1754
1926
  * All the brace-matching / tokenizing / serializing is delegated to the
1755
1927
  * shared core helpers so this stays in lockstep with the parser by import
@@ -1767,35 +1939,47 @@ interface TransitionFields {
1767
1939
  declare const EMPTY_TRANSITION: TransitionFields;
1768
1940
  /**
1769
1941
  * Read the transition fields off a heading line. Looks in both the Pandoc
1770
- * `{…}` block (canonical) and the `{[…]}` template params (hand-typed),
1942
+ * `{…}` block (legacy) and the `{[…]}` template params (canonical),
1771
1943
  * with the Pandoc block taking precedence. Returns the empty transition for
1772
1944
  * non-heading lines.
1773
1945
  */
1774
1946
  declare function readHeadingLineTransition(line: string): TransitionFields;
1775
1947
  /**
1776
1948
  * Return `line` with its transition rewritten from `next`, writing into the
1777
- * Pandoc `{…}` block and leaving the `{[…]}` template annotation untouched.
1778
- * Non-heading lines are returned unchanged.
1949
+ * squisq-native `{[]}` annotation. Legacy Pandoc transition keys are removed
1950
+ * while preserving ids, classes, and other Pandoc params. Non-heading lines are
1951
+ * returned unchanged.
1779
1952
  */
1780
1953
  declare function setHeadingLineTransition(line: string, next: TransitionFields): string;
1781
1954
  /**
1782
- * Read the transition fields from a heading node's `dataBlockAttrs` (Pandoc
1783
- * inner) plus `dataTemplateParams` (the `{[…]}` params). Pandoc wins.
1955
+ * Read the transition fields from a heading node's `dataBlockAttrs` (legacy
1956
+ * Pandoc inner) plus `dataTemplateParams` (canonical `{[…]}` params). Pandoc
1957
+ * wins so the picker mirrors the value that `markdownToDoc` will render when
1958
+ * both channels are present.
1784
1959
  */
1785
1960
  declare function readBlockAttrsTransition(blockAttrsInner: string | null | undefined, templateParams: string | null | undefined): TransitionFields;
1961
+ interface HeadingTransitionAttrs {
1962
+ /** Inner of the Pandoc `{…}` block, without braces. */
1963
+ blockAttrsInner: string | null;
1964
+ /** Param string inside the `{[…]}` annotation, without the template token. */
1965
+ templateParams: string | null;
1966
+ }
1786
1967
  /**
1787
- * Rewrite the transition in a heading node's `dataBlockAttrs` inner string.
1788
- * Returns the new inner (no braces), or null when the block carries no
1789
- * attributes at all — matching how `tiptapBridge` stores `dataBlockAttrs`
1790
- * (absent attribute → null, not `{}`).
1968
+ * Rewrite a heading node's transition for Tiptap, writing the transition
1969
+ * family into `dataTemplateParams` and removing any legacy transition keys
1970
+ * from `dataBlockAttrs`.
1791
1971
  */
1792
- declare function setBlockAttrsTransition(blockAttrsInner: string | null | undefined, next: TransitionFields): string | null;
1972
+ declare function setHeadingAttrsTransition(blockAttrsInner: string | null | undefined, templateParams: string | null | undefined, next: TransitionFields): HeadingTransitionAttrs;
1793
1973
 
1794
1974
  interface TransitionPickerProps {
1795
1975
  value: TransitionFields;
1796
1976
  onChange: (next: TransitionFields) => void;
1977
+ /** Surface scheme copied onto the portaled flyout so it can style independently of the shell. */
1978
+ colorScheme?: 'light' | 'dark';
1979
+ /** Active document-theme accent used for open and selected states. */
1980
+ accentColor?: string;
1797
1981
  }
1798
- declare function TransitionPicker({ value, onChange }: TransitionPickerProps): react_jsx_runtime.JSX.Element;
1982
+ declare function TransitionPicker({ value, onChange, colorScheme, accentColor, }: TransitionPickerProps): react_jsx_runtime.JSX.Element;
1799
1983
 
1800
1984
  /**
1801
1985
  * transitionCatalog
@@ -1883,12 +2067,11 @@ declare function summarizeBlockProps(blockAttrs: string | null | undefined, temp
1883
2067
  *
1884
2068
  * The on-canvas "block properties" palette — the sibling of the block-template
1885
2069
  * badge. Anchored at the `.squisq-props-badge` chip on a heading, it edits the
1886
- * block's playback/animation metadata, all stored in the heading's Pandoc `{…}`
1887
- * attribute block (`dataBlockAttrs`):
2070
+ * block's playback/animation metadata:
1888
2071
  *
1889
- * - Transition (type / direction / duration) — reuses `TransitionPicker`
1890
- * - Duration (`duration`) — how long the block is shown
1891
- * - Start time (`startTime`) — timeline position
2072
+ * - Transition (type / direction / duration) — stored in `{[…]}` params
2073
+ * - Duration (`duration`) — stored in Pandoc `dataBlockAttrs`
2074
+ * - Start time (`startTime`) — stored in Pandoc `dataBlockAttrs`
1892
2075
  *
1893
2076
  * The popover holds the `dataBlockAttrs` inner string as working state and
1894
2077
  * re-derives each control from it, so successive edits compose. Every change
@@ -1905,9 +2088,18 @@ interface BlockPropertiesPopoverProps {
1905
2088
  templateParams: string | null;
1906
2089
  /** Apply a new `dataBlockAttrs` inner to the heading (null clears it). */
1907
2090
  onChange: (nextInner: string | null) => void;
2091
+ /** Apply a paired `dataBlockAttrs` / `dataTemplateParams` transition rewrite. */
2092
+ onAnnotationChange: (next: {
2093
+ blockAttrsInner: string | null;
2094
+ templateParams: string | null;
2095
+ }) => void;
2096
+ /** Editor surface scheme. Required explicitly because this popover is portaled to `<body>`. */
2097
+ colorScheme?: 'light' | 'dark';
2098
+ /** Active document-theme accent used for focus and selected-state highlights. */
2099
+ accentColor?: string;
1908
2100
  onClose: () => void;
1909
2101
  }
1910
- declare function BlockPropertiesPopover({ anchorRect, blockAttrs, templateParams, onChange, onClose, }: BlockPropertiesPopoverProps): react.ReactPortal;
2102
+ declare function BlockPropertiesPopover({ anchorRect, blockAttrs, templateParams, onChange, onAnnotationChange, colorScheme, accentColor, onClose, }: BlockPropertiesPopoverProps): react.ReactPortal;
1911
2103
 
1912
2104
  interface InlinePreviewGutterProps {
1913
2105
  /** Width of the gutter in pixels (default: 320). */
@@ -1939,9 +2131,11 @@ interface MediaBinProps {
1939
2131
  isDark: boolean;
1940
2132
  /** Incremented externally to signal a re-scan of the media list */
1941
2133
  refreshKey?: number;
2134
+ /** Relative media paths currently referenced by the document. */
2135
+ usedMediaPaths?: ReadonlySet<string>;
1942
2136
  /**
1943
- * Fired after a successful upload via the MediaBin's own "+ Upload"
1944
- * button. `relativePath` is what the provider returned (the same
2137
+ * Fired after a successful upload via the MediaBin's "+ Upload"
2138
+ * button or image drop target. `relativePath` is what the provider returned (the same
1945
2139
  * value embedded in markdown refs, e.g. `attachments/xyz.png`);
1946
2140
  * `name` is the uploader-chosen filename before storage renamed
1947
2141
  * it. Consumers typically use this to insert a markdown image ref
@@ -1950,8 +2144,15 @@ interface MediaBinProps {
1950
2144
  * else, leaving the message body empty when the user hit Send.
1951
2145
  */
1952
2146
  onMediaUploaded?: (relativePath: string, name: string, mimeType: string) => void | Promise<void>;
2147
+ /**
2148
+ * Fired after a file is removed through the MediaBin context menu.
2149
+ * Hosts use this to remove matching markdown refs from the document.
2150
+ */
2151
+ onMediaRemoved?: (relativePath: string, entry: MediaEntry) => void | Promise<void>;
2152
+ /** Fired whenever the panel scans media and knows the current entry count. */
2153
+ onCountChange?: (count: number) => void;
1953
2154
  }
1954
- declare function MediaBin({ mediaProvider, isDark, refreshKey, onMediaUploaded }: MediaBinProps): react_jsx_runtime.JSX.Element;
2155
+ declare function MediaBin({ mediaProvider, isDark, refreshKey, usedMediaPaths, onMediaUploaded, onMediaRemoved, onCountChange, }: MediaBinProps): react_jsx_runtime.JSX.Element;
1955
2156
 
1956
2157
  /**
1957
2158
  * StatusBar
@@ -2118,6 +2319,63 @@ interface UseMonacoLoaderResult {
2118
2319
  */
2119
2320
  declare function useMonacoLoader(): UseMonacoLoaderResult;
2120
2321
 
2322
+ /**
2323
+ * Monaco language-service worker wiring.
2324
+ *
2325
+ * Monaco offloads its heavy language services — css / html / json / typescript
2326
+ * IntelliSense — plus a base editor service (word-based completions, link
2327
+ * detection, diffing) to web workers. Those worker bundles must be produced by
2328
+ * the HOST application's bundler: the mechanisms for it (Vite's `?worker`
2329
+ * import suffix, `new Worker(new URL(...))`, webpack loaders) are all
2330
+ * bundler-specific and cannot live inside this tsup-built library.
2331
+ *
2332
+ * So the division of labor is: the host supplies the five worker constructors
2333
+ * (one line each with Vite's `?worker`), and this helper owns the
2334
+ * `label → worker` mapping — the part that's fiddly and easy to get wrong.
2335
+ *
2336
+ * Call once, before the first editor mounts (typically in the app entry):
2337
+ *
2338
+ * ```ts
2339
+ * import { configureMonacoWorkers } from '@bendyline/squisq-editor-react';
2340
+ * import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
2341
+ * import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
2342
+ * import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
2343
+ * import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
2344
+ * import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
2345
+ *
2346
+ * configureMonacoWorkers({
2347
+ * editor: EditorWorker, json: JsonWorker, css: CssWorker,
2348
+ * html: HtmlWorker, ts: TsWorker,
2349
+ * });
2350
+ * ```
2351
+ *
2352
+ * This is purely additive: without it, highlighting, editing, and custom
2353
+ * completion providers (e.g. the `{[template]}` typeahead) still work — they
2354
+ * run on the main thread. Only the language-service IntelliSense is dormant
2355
+ * until the workers are wired.
2356
+ */
2357
+ /** Zero-arg worker constructor, as produced by Vite's `?worker` import. */
2358
+ type MonacoWorkerConstructor = new () => Worker;
2359
+ interface MonacoWorkerConstructors {
2360
+ /** Base editor worker (word completions, links, diff). Required. */
2361
+ editor: MonacoWorkerConstructor;
2362
+ /** JSON language service. */
2363
+ json?: MonacoWorkerConstructor;
2364
+ /** CSS/SCSS/LESS language service. */
2365
+ css?: MonacoWorkerConstructor;
2366
+ /** HTML/Handlebars/Razor language service. */
2367
+ html?: MonacoWorkerConstructor;
2368
+ /** TypeScript service — also handles JavaScript. */
2369
+ ts?: MonacoWorkerConstructor;
2370
+ }
2371
+ /**
2372
+ * Install `globalThis.MonacoEnvironment.getWorker` so Monaco routes each
2373
+ * language to the matching worker, falling back to the base editor worker for
2374
+ * any label without a dedicated service (which is every plain language — its
2375
+ * grammar-based highlighting needs no worker).
2376
+ */
2377
+ declare function configureMonacoWorkers(workers: MonacoWorkerConstructors): void;
2378
+
2121
2379
  interface CustomTemplateContextValue {
2122
2380
  /** Templates inlined into the current doc's frontmatter. */
2123
2381
  docTemplates: CustomTemplateDefinition[];
@@ -2193,20 +2451,9 @@ interface DocCustomTemplates {
2193
2451
  }
2194
2452
  declare function useDocCustomTemplates(): DocCustomTemplates;
2195
2453
 
2196
- /**
2197
- * Read diagram nodes + edges from the live Tiptap state.
2198
- *
2199
- * For a given parent heading position, walks the diagram section's child
2200
- * headings, builds synthetic `Block` objects from their text + Pandoc
2201
- * attributes, runs `computeDiagramLayout` from core to fill in missing
2202
- * positions, and returns the result in the shape React Flow consumes.
2203
- *
2204
- * The hook re-derives on every editor transaction — no caching layer
2205
- * means there's nothing to invalidate when the user types or the markdown
2206
- * is reloaded from disk.
2207
- */
2454
+ /** Data shapes consumed by `DiagramCanvas` / `buildDiagramScene`. */
2208
2455
 
2209
- interface DiagramRFNode {
2456
+ interface DiagramNode {
2210
2457
  id: string;
2211
2458
  position: {
2212
2459
  x: number;
@@ -2216,23 +2463,29 @@ interface DiagramRFNode {
2216
2463
  label: string;
2217
2464
  };
2218
2465
  type?: string;
2219
- /** Per-node width override (from the heading's `w=` Pandoc param). */
2466
+ /** Per-node width override in canvas units. */
2220
2467
  width?: number;
2221
- /** Per-node height override (from the heading's `h=` Pandoc param). */
2468
+ /** Per-node height override in canvas units. */
2222
2469
  height?: number;
2470
+ /** Render as a background container card (top-anchored label, muted fill). */
2471
+ kind?: 'container';
2223
2472
  }
2224
- interface DiagramRFEdge {
2473
+ interface DiagramEdge {
2225
2474
  id: string;
2226
2475
  source: string;
2227
2476
  target: string;
2228
2477
  label?: string;
2478
+ /** False → no end arrowhead. Undefined keeps the diagram default (arrow). */
2479
+ directed?: boolean;
2480
+ sourceAnchor?: DiagramEdgeAnchor;
2481
+ targetAnchor?: DiagramEdgeAnchor;
2482
+ routing?: ConnectorRouting;
2229
2483
  }
2230
2484
  interface DiagramData {
2231
- nodes: DiagramRFNode[];
2232
- edges: DiagramRFEdge[];
2485
+ nodes: DiagramNode[];
2486
+ edges: DiagramEdge[];
2233
2487
  warnings: string[];
2234
2488
  }
2235
- declare function useDiagramData(editor: Editor$1, parentPos: number): DiagramData;
2236
2489
 
2237
2490
  /**
2238
2491
  * Tiptap Bridge
@@ -2283,9 +2536,10 @@ declare function buildPreviewDoc(doc: Doc): Doc;
2283
2536
  /**
2284
2537
  * TemplateAnnotation — Tiptap Heading Extension
2285
2538
  *
2286
- * Extends Tiptap's built-in Heading node to support `data-template` and
2287
- * `data-template-params` HTML attributes. These attributes store which block
2288
- * template should be used for a heading section.
2539
+ * Extends Tiptap's built-in Heading node to support `data-template`,
2540
+ * `data-template-params`, and `data-template-empty` HTML attributes. These
2541
+ * attributes store the authored template annotation without exposing its raw
2542
+ * `{[…]}` syntax in the Write view.
2289
2543
  *
2290
2544
  * When present, the heading renders a visible badge (styled CSS chip)
2291
2545
  * showing the template name, e.g. `[chart]`.
@@ -2300,27 +2554,6 @@ declare function buildPreviewDoc(doc: Doc): Doc;
2300
2554
  */
2301
2555
  declare const HeadingWithTemplate: _tiptap_core.Node<_tiptap_extension_heading.HeadingOptions, any>;
2302
2556
 
2303
- /**
2304
- * DiagramExtension — Tiptap/ProseMirror plugin that:
2305
- *
2306
- * 1. Mounts a React-Flow canvas (`DiagramWidget`) immediately after every
2307
- * heading whose `dataTemplate === 'diagram'`.
2308
- * 2. Hides the direct sub-headings of each diagram parent (until the next
2309
- * equal-or-shallower heading) by tagging them with a `data-squisq-diagram-child`
2310
- * attribute — CSS in `styles/diagram.css` does the actual hiding.
2311
- *
2312
- * Widgets are rendered as plain DOM nodes attached to a ProseMirror
2313
- * `Decoration.widget`. React is mounted into the widget DOM with
2314
- * `react-dom/client`'s `createRoot`, and unmounted on the widget's
2315
- * `destroy` hook.
2316
- */
2317
-
2318
- interface DiagramExtensionOptions {
2319
- /** When false, the extension is inert (no widgets, no decorations). */
2320
- enabled?: boolean;
2321
- }
2322
- declare const DiagramExtension: Extension<DiagramExtensionOptions, any>;
2323
-
2324
2557
  type DiagramCommand = {
2325
2558
  kind: 'moveNode';
2326
2559
  nodeId: string;
@@ -2354,8 +2587,8 @@ type DiagramCommand = {
2354
2587
  nodeId: string;
2355
2588
  };
2356
2589
  interface DiagramCanvasProps {
2357
- nodes: DiagramRFNode[];
2358
- edges: DiagramRFEdge[];
2590
+ nodes: DiagramNode[];
2591
+ edges: DiagramEdge[];
2359
2592
  onCommand: (cmd: DiagramCommand) => void;
2360
2593
  /** When true, render the maximize button. Click toggles `onToggleMaximize`. */
2361
2594
  showMaximize?: boolean;
@@ -2372,99 +2605,525 @@ interface DiagramCanvasProps {
2372
2605
  onActiveToolIdChange?: (id: string) => void;
2373
2606
  /** Forwarded to the Scene so the host can drive a Delete action. */
2374
2607
  onSelectionChange?: (ids: ReadonlySet<string>) => void;
2608
+ /** Per-editor toolbar bridge for detached scene roots. */
2609
+ textChannel?: SceneTextChannel;
2610
+ }
2611
+ declare function DiagramCanvas({ nodes: incomingNodes, edges: incomingEdges, onCommand, showMaximize, maximized, onToggleMaximize, activeToolId: controlledToolId, onActiveToolIdChange, onSelectionChange, textChannel, }: DiagramCanvasProps): react_jsx_runtime.JSX.Element;
2612
+
2613
+ /**
2614
+ * AsciiDiagramExtension — Tiptap/ProseMirror plugin that turns code blocks
2615
+ * containing ASCII box-and-line diagrams into interactive diagram canvases.
2616
+ *
2617
+ * The FENCE TEXT REMAINS THE SOURCE OF TRUTH: the plugin never converts
2618
+ * the document. For each qualifying codeBlock it:
2619
+ * 1. hides the `<pre>` via a node decoration (or tags it visible-monospace
2620
+ * when the user toggles "source" mode), and
2621
+ * 2. mounts an `AsciiDiagramWidget` after it via a widget decoration.
2622
+ *
2623
+ * Identity: codeBlocks have no persisted id and their content changes on
2624
+ * every canvas edit (self-inflicted fence rewrite), so neither a content
2625
+ * hash nor a raw position can key the widget. Instead the plugin keeps a
2626
+ * POSITION REGISTRY in its state: each qualifying block gets a synthetic
2627
+ * session id (`ascii-N`); on every doc change old positions are remapped
2628
+ * through `tr.mapping` and matched against the new doc walk. Same id →
2629
+ * same widget decoration key → ProseMirror keeps the DOM + React root
2630
+ * alive (pan/zoom survives), exactly like the heading-key trick the
2631
+ * legacy DiagramExtension used.
2632
+ *
2633
+ * Hysteresis: a NEW block must pass full detection (`detectAsciiDiagram`,
2634
+ * ≥2 boxes + thresholds); an already-registered block stays interactive
2635
+ * while its fence still parses to ≥1 box, so deleting down to one node
2636
+ * doesn't kick the user back to a raw code block mid-edit.
2637
+ */
2638
+
2639
+ interface AsciiDiagramBlockEntry {
2640
+ /** Synthetic session id (`ascii-N`), stable across edits for one block. */
2641
+ id: string;
2642
+ /** Document position of the codeBlock node at the current state. */
2643
+ pos: number;
2644
+ }
2645
+ interface AsciiDiagramPluginState {
2646
+ entries: AsciiDiagramBlockEntry[];
2647
+ decorations: DecorationSet;
2648
+ sourceVisible: ReadonlySet<string>;
2649
+ /** Monotonic id allocator. */
2650
+ seq: number;
2651
+ }
2652
+ /** Resolve a registered block's current doc position (null when gone). */
2653
+ declare function findAsciiDiagramBlockPos(editor: Editor$1, blockId: string): number | null;
2654
+ declare function isAsciiSourceVisible(editor: Editor$1, blockId: string): boolean;
2655
+ /** Toggle the raw-fence view for one diagram block (meta-only transaction). */
2656
+ declare function toggleAsciiSource(editor: Editor$1, blockId: string): void;
2657
+ interface AsciiDiagramExtensionOptions {
2658
+ /** When false, the extension is inert (no widgets, no decorations). */
2659
+ enabled?: boolean;
2660
+ textChannel?: SceneTextChannel;
2375
2661
  }
2376
- declare function DiagramCanvas({ nodes: incomingNodes, edges: incomingEdges, onCommand, showMaximize, maximized, onToggleMaximize, activeToolId: controlledToolId, onActiveToolIdChange, onSelectionChange, }: DiagramCanvasProps): react_jsx_runtime.JSX.Element;
2662
+ declare const AsciiDiagramExtension: Extension<AsciiDiagramExtensionOptions, any>;
2377
2663
 
2378
- interface DiagramWidgetProps {
2664
+ interface AsciiDiagramWidgetProps {
2379
2665
  editor: Editor$1;
2380
- /** Stable id derived from the parent heading (slug / `#id`). */
2381
- headingKey: string;
2382
- /** Position of the parent heading at widget-creation time. Used as a
2383
- * fallback when the dynamic lookup fails (e.g. before the first
2384
- * transaction). */
2385
- fallbackParentPos: number;
2666
+ /** Session id from the extension's position registry. */
2667
+ blockId: string;
2668
+ /** Position at widget-creation time display-only fallback. */
2669
+ fallbackPos: number;
2386
2670
  /** Host element used for portal targeting by the maximize overlay. */
2387
2671
  host?: HTMLElement | null;
2672
+ textChannel?: SceneTextChannel;
2388
2673
  }
2389
- declare function DiagramWidget({ editor, headingKey, fallbackParentPos, host }: DiagramWidgetProps): react_jsx_runtime.JSX.Element;
2674
+ declare function AsciiDiagramWidget({ editor, blockId, host, textChannel, }: AsciiDiagramWidgetProps): react_jsx_runtime.JSX.Element | null;
2675
+
2676
+ /**
2677
+ * Read direction of the ASCII diagram loop: fence text → canvas data.
2678
+ *
2679
+ * `useAsciiDiagramData` re-derives on every editor transaction (mirroring
2680
+ * the philosophy of the old heading-based `useDiagramData`: no store, so
2681
+ * nothing to invalidate). Parsing is memoized per PMNode instance by the
2682
+ * extension's WeakMap caches, so untouched fences cost nothing.
2683
+ */
2684
+
2685
+ interface AsciiDiagramView {
2686
+ /** Canvas nodes, containers ordered first so their cards paint behind. */
2687
+ nodes: DiagramNode[];
2688
+ edges: DiagramEdge[];
2689
+ warnings: string[];
2690
+ style: 'unicode' | 'ascii';
2691
+ /** The current fence text. */
2692
+ text: string;
2693
+ /** The parse behind `nodes`/`edges` (grid units) — ops operate on this. */
2694
+ diagram: AsciiDiagram;
2695
+ }
2696
+ /** Grid model → canvas model, containers-first for paint order. */
2697
+ declare function asciiDiagramToCanvas(diagram: AsciiDiagram): Pick<AsciiDiagramView, 'nodes' | 'edges'>;
2698
+ /**
2699
+ * Live view of one registered ASCII diagram block. Returns null when the
2700
+ * block no longer exists or no longer parses (the extension will drop the
2701
+ * widget on its next pass).
2702
+ */
2703
+ declare function useAsciiDiagramData(editor: Editor$1, blockId: string): AsciiDiagramView | null;
2390
2704
 
2391
2705
  /**
2392
- * Tiptap commands for diagram editing.
2706
+ * Write direction of the ASCII diagram loop: canvas command → fence rewrite.
2393
2707
  *
2394
- * Each command finds the relevant heading inside a diagram section (parent
2395
- * heading + its direct sub-headings until the next equal-or-shallower
2396
- * heading) and mutates either its `data-block-attrs` attribute or its
2397
- * text content. All edits flow back into markdown via the existing
2398
- * `tiptapBridge` round-trip no parallel data store.
2708
+ * Every semantic edit is one functional transformation of the fence text
2709
+ * (`renderAscii(applyOp(parseAscii(text)))`) committed as a single
2710
+ * ProseMirror transaction (= a single undo step). Before committing, the
2711
+ * rendered art is re-parsed and verified if the renderer ever produced
2712
+ * something the parser disagrees with, the command aborts instead of
2713
+ * corrupting the fence.
2399
2714
  */
2400
2715
 
2401
- interface HeadingLocation {
2402
- /** Node start position in the doc (absolute). */
2716
+ /**
2717
+ * Replace the TEXT inside the codeBlock at `pos` and, when `ensureLanguage`
2718
+ * is given, promote its `language` attribute in the SAME transaction (one
2719
+ * undo step). Promoting the language to the explicit `diagram`/`tree` tag is
2720
+ * how a semantic edit makes the block's identity "sticky" — the language
2721
+ * class survives markdown ↔ Tiptap round-trips, so a once-edited diagram/tree
2722
+ * is re-recognized even after it's flattened. No-op (returns false) only when
2723
+ * BOTH the text is already identical AND the language already matches, so
2724
+ * history stays clean on a true no-op.
2725
+ */
2726
+ declare function replaceAsciiFenceText(editor: Editor$1, pos: number, nextText: string, ensureLanguage?: string): boolean;
2727
+ /**
2728
+ * Reconstruct a broken box-art fence into clean, `diagram`-tagged art in a
2729
+ * single transaction (one undo step). The AsciiDiagramExtension then claims
2730
+ * the tagged fence and mounts the interactive canvas. Returns false when the
2731
+ * block is gone or nothing recoverable (the button simply no-ops).
2732
+ */
2733
+ declare function applyRepairCommand(editor: Editor$1, blockId: string): boolean;
2734
+ /** Full pipeline for a canvas command against a registered fence block. */
2735
+ declare function applyAsciiDiagramCommand(editor: Editor$1, blockId: string, cmd: DiagramCommand): boolean;
2736
+
2737
+ /**
2738
+ * RepairableDiagramExtension — mounts an inline "Repair as diagram" button on
2739
+ * code fences that hold BROKEN box-and-line art.
2740
+ *
2741
+ * The three states of a box-drawing fence partition cleanly:
2742
+ * - a clean diagram → AsciiDiagramExtension mounts the canvas;
2743
+ * - a file tree / outline → TreeViewExtension mounts the outline;
2744
+ * - broken box art → neither claims it (it renders as a faithful
2745
+ * code block), and THIS extension offers a button
2746
+ * to reconstruct it via `repairAsciiDiagram`.
2747
+ *
2748
+ * Unlike the diagram/tree extensions it does NOT hide the fence or mount a
2749
+ * persistent interactive widget — the code stays visible and editable, and a
2750
+ * single click rewrites the fence to clean `diagram`-tagged art (which the
2751
+ * AsciiDiagramExtension then turns into a canvas). So the position registry
2752
+ * only needs to survive until the click; it mirrors the other extensions'
2753
+ * mapping so the button always resolves the right fence.
2754
+ */
2755
+
2756
+ interface RepairableBlockEntry {
2757
+ /** Synthetic session id (`repair-N`), stable across edits for one block. */
2758
+ id: string;
2759
+ /** Document position of the codeBlock node at the current state. */
2403
2760
  pos: number;
2404
- /** The heading PMNode. */
2405
- node: Node;
2406
- /** Parsed attributes derived from the heading's `data-block-attrs` (always defined). */
2407
- attrs: HeadingAttributes;
2408
- /** Computed id: explicit `#id` if set, otherwise the slugified heading text. */
2761
+ }
2762
+ interface RepairablePluginState {
2763
+ entries: RepairableBlockEntry[];
2764
+ decorations: DecorationSet;
2765
+ seq: number;
2766
+ }
2767
+ declare const REPAIRABLE_KEY: PluginKey<RepairablePluginState>;
2768
+ /** True when a codeBlock holds broken box art worth offering to repair. */
2769
+ declare function isRepairableFence(node: Node): boolean;
2770
+ /** Resolve a registered block's current doc position (null when gone). */
2771
+ declare function findRepairableBlockPos(editor: Editor$1, blockId: string): number | null;
2772
+ /** Handler the mounted button invokes; wired by the host via extension options. */
2773
+ interface RepairableDiagramExtensionOptions {
2774
+ enabled?: boolean;
2775
+ /** Invoked when a repair button is clicked, with the block's synthetic id. */
2776
+ onRepair?: (editor: Editor$1, blockId: string) => void;
2777
+ }
2778
+ declare const RepairableDiagramExtension: Extension<RepairableDiagramExtensionOptions, any>;
2779
+
2780
+ /**
2781
+ * Pure data operations over the ASCII diagram model — the write-direction
2782
+ * half of the fence-is-source-of-truth loop:
2783
+ *
2784
+ * fence text → parseAsciiDiagram → op → renderAsciiDiagram → fence text
2785
+ *
2786
+ * Every op returns a new AsciiDiagram (inputs are never mutated) and
2787
+ * maintains the invariants the renderer/parser fixpoint relies on:
2788
+ * container moves translate whole subtrees, removals promote children and
2789
+ * drop incident edges, sizes never fall below what a label needs.
2790
+ */
2791
+
2792
+ /**
2793
+ * Make arbitrary (rename-dialog) input safe as a box label: box-drawing /
2794
+ * arrow glyphs would corrupt the art, and the Scene's inline editor is
2795
+ * single-line, so newlines collapse to spaces.
2796
+ */
2797
+ declare function sanitizeAsciiLabel(label: string): string;
2798
+ /** Move a node to (col, row); a container drags its whole subtree along. */
2799
+ declare function moveNodeOp(diagram: AsciiDiagram, nodeId: string, col: number, row: number): AsciiDiagram;
2800
+ /** Resize a node (grid cells). Clamped so the label always fits. */
2801
+ declare function resizeNodeOp(diagram: AsciiDiagram, nodeId: string, wCols: number, hRows: number): AsciiDiagram;
2802
+ /** Add a directed edge; duplicates (same source/target/label) are no-ops. */
2803
+ declare function addEdgeOp(diagram: AsciiDiagram, source: string, target: string, label?: string): AsciiDiagram;
2804
+ /**
2805
+ * Remove an edge. A label-qualified call removes the exact match; without
2806
+ * a label the first (source, target) match goes — mirroring the legacy
2807
+ * `removeConnection` semantics.
2808
+ */
2809
+ declare function removeEdgeOp(diagram: AsciiDiagram, source: string, target: string, label?: string): AsciiDiagram;
2810
+ /**
2811
+ * Rename a node. The label drives the node's parse-derived id, so edges
2812
+ * and containment references are rewritten to the id the next parse will
2813
+ * produce. (Selection in the canvas drops after a rename — documented.)
2814
+ */
2815
+ declare function renameNodeOp(diagram: AsciiDiagram, nodeId: string, label: string): AsciiDiagram;
2816
+ /** Add a node at (col, row). Inherits a container when placed inside one. */
2817
+ declare function addNodeOp(diagram: AsciiDiagram, opts: {
2818
+ col: number;
2819
+ row: number;
2820
+ label?: string;
2821
+ }): {
2822
+ diagram: AsciiDiagram;
2823
+ label: string;
2824
+ };
2825
+ /**
2826
+ * Remove a node: incident edges (both directions) drop, direct children
2827
+ * are promoted to the removed node's own container (or top level).
2828
+ */
2829
+ declare function removeNodeOp(diagram: AsciiDiagram, nodeId: string): AsciiDiagram;
2830
+
2831
+ /**
2832
+ * Paste gate for bare (unfenced) ASCII diagram art.
2833
+ *
2834
+ * `+--+ | |` art pasted as plain text is otherwise mangled: its `| x |`
2835
+ * rows match the GFM table-row pattern in `detectMarkdown`, so the paste
2836
+ * handler routes it through the markdown converter, fails table
2837
+ * validation, and emits one broken paragraph per line. When this gate
2838
+ * matches, the paste handler inserts the text verbatim into a fresh code
2839
+ * block instead — which the AsciiDiagramExtension then picks up as an
2840
+ * interactive diagram.
2841
+ */
2842
+ declare function shouldPasteAsAsciiFence(text: string): boolean;
2843
+
2844
+ /**
2845
+ * TreeViewExtension — Tiptap/ProseMirror plugin that turns code blocks
2846
+ * containing ASCII file-tree / outline art into interactive outline editors.
2847
+ *
2848
+ * Structurally identical to AsciiDiagramExtension (position registry, stable
2849
+ * synthetic ids, hysteresis, self-rewrite-survival via the decoration key);
2850
+ * only the detect/parse gates and class names differ. The fence text stays
2851
+ * the source of truth — every edit re-renders the tree art.
2852
+ *
2853
+ * Mutual exclusion with the diagram extension: a fence with ≥2 closed boxes
2854
+ * is a diagram, never a tree (checked in both gates), so at most one
2855
+ * extension claims any given fence.
2856
+ */
2857
+
2858
+ interface TreeBlockEntry {
2859
+ /** Synthetic session id (`tree-N`), stable across edits for one block. */
2409
2860
  id: string;
2861
+ /** Document position of the codeBlock node at the current state. */
2862
+ pos: number;
2863
+ }
2864
+ interface TreeViewPluginState {
2865
+ entries: TreeBlockEntry[];
2866
+ decorations: DecorationSet;
2867
+ seq: number;
2868
+ }
2869
+ declare function findTreeBlockPos(editor: Editor$1, blockId: string): number | null;
2870
+ interface TreeViewExtensionOptions {
2871
+ /** When false, the extension is inert (no widgets, no decorations). */
2872
+ enabled?: boolean;
2873
+ }
2874
+ declare const TreeViewExtension: Extension<TreeViewExtensionOptions, any>;
2875
+
2876
+ interface TreeOutlineWidgetProps {
2877
+ editor: Editor$1;
2878
+ blockId: string;
2879
+ fallbackPos: number;
2880
+ host?: HTMLElement | null;
2881
+ }
2882
+ declare function TreeOutlineWidget({ editor, blockId }: TreeOutlineWidgetProps): react_jsx_runtime.JSX.Element | null;
2883
+
2884
+ /**
2885
+ * Read direction of the tree loop: fence text → outline view.
2886
+ *
2887
+ * `useTreeViewData` re-derives on every editor transaction; parsing is
2888
+ * memoized per PMNode by the extension's WeakMap caches, so untouched
2889
+ * fences cost nothing.
2890
+ */
2891
+
2892
+ interface TreeViewData {
2893
+ tree: Tree;
2894
+ text: string;
2895
+ warnings: string[];
2410
2896
  }
2897
+ /** Live view of one registered tree block; null when it no longer parses. */
2898
+ declare function useTreeViewData(editor: Editor$1, blockId: string): TreeViewData | null;
2899
+
2411
2900
  /**
2412
- * Find the diagram section that starts at `parentPos` (the position of the
2413
- * parent heading with `dataTemplate === 'diagram'`). Returns the headings
2414
- * that should appear as diagram nodes — defined as every heading at the
2415
- * **shallowest** depth greater than the parent within the section, until
2416
- * the next equal-or-shallower heading.
2901
+ * Write direction of the tree loop: outline command fence rewrite.
2417
2902
  *
2418
- * Using the shallowest deeper depth (rather than a strict parentDepth + 1)
2419
- * mirrors `markdownToDoc`'s stack behavior: when authors skip a level
2420
- * (e.g. `# parent` + `### child`), those `###` headings are still treated
2421
- * as direct children of the `#` parent. Any headings deeper than the
2422
- * detected child depth are sub-sections of a node and are not surfaced as
2423
- * separate diagram nodes.
2903
+ * parse op render verify-reparse single-transaction fence replace
2904
+ * (one undo step). Reuses the generic `replaceAsciiFenceText` (it only
2905
+ * touches codeBlock text, attributes untouched).
2424
2906
  */
2425
- declare function listDiagramChildren(editor: Editor$1, parentPos: number): HeadingLocation[];
2907
+
2908
+ type TreeCommand = {
2909
+ kind: 'addItem';
2910
+ targetId: string;
2911
+ position: 'child' | 'siblingAfter';
2912
+ label?: string;
2913
+ isDir?: boolean;
2914
+ } | {
2915
+ kind: 'renameItem';
2916
+ id: string;
2917
+ label: string;
2918
+ } | {
2919
+ kind: 'indentItem';
2920
+ id: string;
2921
+ } | {
2922
+ kind: 'outdentItem';
2923
+ id: string;
2924
+ } | {
2925
+ kind: 'moveItemUp';
2926
+ id: string;
2927
+ } | {
2928
+ kind: 'moveItemDown';
2929
+ id: string;
2930
+ } | {
2931
+ kind: 'removeItem';
2932
+ id: string;
2933
+ } | {
2934
+ kind: 'toggleDir';
2935
+ id: string;
2936
+ };
2937
+
2938
+ declare function applyTreeCommand(editor: Editor$1, blockId: string, cmd: TreeCommand): boolean;
2939
+
2426
2940
  /**
2427
- * Update a node's `x` / `y` attributes from a drag.
2941
+ * Pure outline operations over the tree model the write-direction half of
2942
+ * the fence-is-source-of-truth loop:
2943
+ *
2944
+ * fence text → parseTree → op → renderTree → fence text
2428
2945
  *
2429
- * Before writing the moved node, this also "freezes" any siblings that
2430
- * lack an explicit position by snapshotting their currently-displayed
2431
- * (auto-laid) coordinates. Without that, `computeDiagramLayout`'s grid
2432
- * auto-placement is relative to the bounding box of pinned nodes — so
2433
- * dragging one node would pull every unpinned sibling along behind it.
2434
- * Freezing converts the implicit layout into explicit per-node
2435
- * positions on the first interaction, after which each node moves
2436
- * independently.
2946
+ * Every op returns a NEW Tree (inputs untouched). These are the genuinely
2947
+ * new operations with no diagram analog: indent / outdent (re-parent a node
2948
+ * carrying its subtree), move-up / move-down (reorder siblings), plus
2949
+ * add / rename / remove / toggle-directory. Connector rails re-derive in
2950
+ * render, so ops only touch structure + labels.
2437
2951
  */
2438
- declare function moveNode(editor: Editor$1, parentPos: number, nodeId: string, x: number, y: number): boolean;
2952
+
2953
+ /** Make an arbitrary rename safe as a tree label: no connector glyphs / newlines. */
2954
+ declare function sanitizeTreeLabel(label: string): string;
2955
+ declare function renameItemOp(tree: Tree, id: string, label: string): Tree;
2956
+ declare function addItemOp(tree: Tree, targetId: string, position: 'child' | 'siblingAfter', label?: string, isDir?: boolean): Tree;
2957
+ declare function removeItemOp(tree: Tree, id: string): Tree;
2958
+ /** Node becomes the last child of its immediate preceding sibling. */
2959
+ declare function indentItemOp(tree: Tree, id: string): Tree;
2960
+ /** Node becomes a sibling of its parent, inserted directly after it (subtree carried). */
2961
+ declare function outdentItemOp(tree: Tree, id: string): Tree;
2962
+ declare function moveItemUpOp(tree: Tree, id: string): Tree;
2963
+ declare function moveItemDownOp(tree: Tree, id: string): Tree;
2964
+ /** Toggle the trailing-slash directory marker on a node's label. */
2965
+ declare function toggleDirOp(tree: Tree, id: string): Tree;
2966
+
2439
2967
  /**
2440
- * Add a connection from `sourceId` to `targetId` (optionally typed). No-op
2441
- * if the same connection already exists.
2968
+ * Paste gate for bare (unfenced) ASCII tree art. `├──`/`└──` file-tree lines
2969
+ * would otherwise route through the markdown converter and mangle; when this
2970
+ * matches, the paste handler drops the text verbatim into a fresh code block
2971
+ * that the TreeViewExtension picks up as an interactive outline.
2442
2972
  */
2443
- declare function addConnection(editor: Editor$1, parentPos: number, sourceId: string, targetId: string, type?: string): boolean;
2973
+ declare function shouldPasteAsTreeFence(text: string): boolean;
2974
+
2444
2975
  /**
2445
- * Remove a connection from `sourceId` to `targetId`. If `type` is provided,
2446
- * only the matching-typed entry is removed; otherwise the first match
2447
- * (regardless of type) is removed.
2976
+ * TimelineViewExtension mounts a WYSIWYG timeline editor over qualifying
2977
+ * authored timeline code fences while keeping the fence text as source of
2978
+ * truth. Every semantic edit is rendered back through the core codec.
2448
2979
  */
2449
- declare function removeConnection(editor: Editor$1, parentPos: number, sourceId: string, targetId: string, type?: string): boolean;
2980
+
2981
+ interface TimelineBlockEntry {
2982
+ /** Synthetic session id, stable across edits to one fence. */
2983
+ id: string;
2984
+ /** Current document position of the codeBlock node. */
2985
+ pos: number;
2986
+ }
2987
+ interface TimelineViewPluginState {
2988
+ entries: TimelineBlockEntry[];
2989
+ decorations: DecorationSet;
2990
+ seq: number;
2991
+ }
2992
+ declare const TIMELINE_VIEW_KEY: PluginKey<TimelineViewPluginState>;
2993
+ /** Conservative full-detection gate used when first claiming a code fence. */
2994
+ declare function getTimelineForNode(node: Node): AsciiTimeline | null;
2995
+ /** Relaxed/sticky gate for a block already registered by this extension. */
2996
+ declare function parseTimelineForNode(node: Node): AsciiTimeline | null;
2997
+ declare function findTimelineBlockPos(editor: Editor$1, blockId: string): number | null;
2998
+ interface TimelineViewExtensionOptions {
2999
+ /** When false, the extension does not claim fences or mount widgets. */
3000
+ enabled?: boolean;
3001
+ }
3002
+ declare const TimelineViewExtension: Extension<TimelineViewExtensionOptions, any>;
3003
+
3004
+ interface TimelineEditorWidgetProps {
3005
+ editor: Editor$1;
3006
+ blockId: string;
3007
+ }
3008
+ declare function TimelineEditorWidget({ editor, blockId }: TimelineEditorWidgetProps): react_jsx_runtime.JSX.Element | null;
3009
+
3010
+ /** Live read-direction adapter from a registered timeline fence to React. */
3011
+
3012
+ interface TimelineViewData {
3013
+ timeline: AsciiTimeline;
3014
+ text: string;
3015
+ warnings: string[];
3016
+ }
3017
+ declare function useTimelineData(editor: Editor$1, blockId: string): TimelineViewData | null;
3018
+
3019
+ /**
3020
+ * Pure edits over the authored ASCII timeline model.
3021
+ *
3022
+ * The code fence remains the source of truth. Canvas coordinates are
3023
+ * normalized to the same global, shared scale used by
3024
+ * `asciiTimelineToTemplateData`; commands render the edited model back into
3025
+ * canonical fence text after applying one of these operations.
3026
+ */
3027
+
3028
+ interface TimelineEventPatch {
3029
+ label?: string;
3030
+ /** `null` clears the optional description. */
3031
+ description?: string | null;
3032
+ side?: AsciiTimelineSide;
3033
+ /** `null` restores the label side/default placement. */
3034
+ descriptionSide?: AsciiTimelineSide | null;
3035
+ callout?: boolean;
3036
+ marker?: AsciiTimelineMarker;
3037
+ /** Normalized position on the global timeline scale. */
3038
+ position?: number;
3039
+ }
3040
+ interface AddTimelineEventOptions {
3041
+ id?: string;
3042
+ label?: string;
3043
+ description?: string;
3044
+ side?: AsciiTimelineSide;
3045
+ descriptionSide?: AsciiTimelineSide;
3046
+ callout?: boolean;
3047
+ marker?: AsciiTimelineMarker;
3048
+ }
3049
+ interface AddTimelineEventResult {
3050
+ timeline: AsciiTimeline;
3051
+ eventId: string;
3052
+ }
2450
3053
  /**
2451
- * Replace a heading's text content (used when the user renames a node
2452
- * via a double-click in the canvas).
3054
+ * Normalize editable one-line prose exactly as the canonical core renderer
3055
+ * does. This prevents the verified command path from accepting a value that
3056
+ * is silently reinterpreted as timeline syntax on its first render.
2453
3057
  */
2454
- declare function renameNode(editor: Editor$1, parentPos: number, nodeId: string, newText: string): boolean;
3058
+ declare function sanitizeTimelineText(value: string): string;
3059
+ /** Return a globally unique, renderer-safe event id. */
3060
+ declare function nextTimelineEventId(timeline: AsciiTimeline, base?: string): string;
2455
3061
  /**
2456
- * Insert a new heading node at the end of the diagram section. The new
2457
- * heading carries `data-block-attrs` with the supplied id and position,
2458
- * so the freshly-inserted node appears in React Flow at the expected
2459
- * coordinates.
3062
+ * Add a visible point to `trackId` at a normalized global rail position.
3063
+ * Returns the stable id so the canvas can select/focus the new point after
3064
+ * the fence rewrite.
2460
3065
  */
2461
- declare function addNode(editor: Editor$1, parentPos: number, id: string, label: string, x: number, y: number): boolean;
3066
+ declare function addTimelineEventOp(timeline: AsciiTimeline, trackId: string, position: number, options?: AddTimelineEventOptions): AddTimelineEventResult | null;
3067
+ /** Update one point without changing its stable id or branch endpoints. */
3068
+ declare function updateTimelineEventOp(timeline: AsciiTimeline, eventId: string, patch: TimelineEventPatch): AsciiTimeline;
2462
3069
  /**
2463
- * Remove a child node's heading (and any body content under it up to the
2464
- * next heading). Also strips inbound `connectsTo` references on remaining
2465
- * siblings so the diagram doesn't carry dangling targets.
3070
+ * Remove a point and every incident branch. Empty tracks are removed because
3071
+ * the canonical renderer cannot represent them. The last point in the whole
3072
+ * timeline is retained so an edit cannot erase its own source fence/widget.
2466
3073
  */
2467
- declare function removeNode(editor: Editor$1, parentPos: number, nodeId: string): boolean;
3074
+ declare function removeTimelineEventOp(timeline: AsciiTimeline, eventId: string): AsciiTimeline;
3075
+
3076
+ /**
3077
+ * Write direction for the WYSIWYG timeline editor:
3078
+ *
3079
+ * fence text -> parse -> pure op -> render -> verify -> one fence rewrite
3080
+ *
3081
+ * The original code block remains the source of truth and every successful
3082
+ * edit is a single ProseMirror transaction/undo step.
3083
+ */
3084
+
3085
+ type TimelineCommand = {
3086
+ kind: 'addEvent';
3087
+ trackId: string;
3088
+ /** Normalized position on the global timeline rail. */
3089
+ position: number;
3090
+ id?: string;
3091
+ label?: string;
3092
+ description?: string;
3093
+ side?: AsciiTimelineSide;
3094
+ descriptionSide?: AsciiTimelineSide;
3095
+ callout?: boolean;
3096
+ marker?: AsciiTimelineMarker;
3097
+ } | {
3098
+ kind: 'updateEvent';
3099
+ eventId: string;
3100
+ patch: TimelineEventPatch;
3101
+ } | {
3102
+ kind: 'removeEvent';
3103
+ eventId: string;
3104
+ };
3105
+ interface TimelineCommandResult {
3106
+ applied: boolean;
3107
+ /** Present after add so the widget can select/focus the new marker. */
3108
+ eventId?: string;
3109
+ /** Why an otherwise valid semantic edit was deliberately blocked. */
3110
+ reason?: 'read-only' | 'unsafe-source';
3111
+ }
3112
+ /**
3113
+ * Semantic operations replace the whole fence with canonical rendered art.
3114
+ * Fail closed when that would discard source the timeline model cannot
3115
+ * represent. Warnings catch unresolved/unlabeled constructs; the semantic
3116
+ * round-trip catches renderer normalization that changes modeled content;
3117
+ * exact metadata validation rejects partially consumed attrs; and a linear
3118
+ * semantic-residue comparison catches ignored prose/symbol rows without
3119
+ * reparsing a successively shorter document for every source line.
3120
+ */
3121
+ declare function isTimelineSourceSafeForSemanticEdit(source: string, timeline: AsciiTimeline): boolean;
3122
+ /** Apply one semantic timeline command to a registered fence block. */
3123
+ declare function applyTimelineCommand(editor: Editor$1, blockId: string, command: TimelineCommand): TimelineCommandResult;
3124
+
3125
+ /** Paste gate for bare, high-confidence Unicode timeline art. */
3126
+ declare function shouldPasteAsTimelineFence(text: string): boolean;
2468
3127
 
2469
3128
  interface JsonEditorProps {
2470
3129
  /** Schema describing the value's shape (with optional `squisq` hints). */
@@ -2869,6 +3528,443 @@ declare function encodeTimingJson(timing: TimingJson): Uint8Array;
2869
3528
  */
2870
3529
  declare function timingPathFor(audioRelativePath: string): string;
2871
3530
 
3531
+ /** Editor plumbing the recording flow needs; omit for prompter-only use. */
3532
+ interface TeleprompterRecordingDeps {
3533
+ mediaProvider: MediaProvider;
3534
+ container: ContentContainer | null;
3535
+ markdownSource: string;
3536
+ setMarkdownSource: (next: string) => void;
3537
+ bumpMediaRevision: () => void;
3538
+ }
3539
+ interface TeleprompterViewProps {
3540
+ doc: Doc | null;
3541
+ theme: Theme;
3542
+ /** Kept for API symmetry with PreviewPanel; recording uses `recording.container`. */
3543
+ workspaceContainer?: ContentContainer | null;
3544
+ /** Media base path (reserved for future preview integrations). */
3545
+ basePath?: string;
3546
+ /**
3547
+ * Optional audience-window portal owned by the editor's Presentation mode.
3548
+ * The main controller remains authoritative; only this live surface is
3549
+ * mirrored into the target.
3550
+ */
3551
+ presentationTarget?: HTMLElement | null;
3552
+ /** Recording deps; null/omitted disables the Record affordance. */
3553
+ recording?: TeleprompterRecordingDeps | null;
3554
+ }
3555
+ declare function TeleprompterView(props: TeleprompterViewProps): react_jsx_runtime.JSX.Element;
3556
+
3557
+ /**
3558
+ * Mic capture + PCM transport for the teleprompter.
3559
+ *
3560
+ * getUserMedia → AudioContext → AudioWorklet tap → subscriber callbacks
3561
+ * with 1024-sample Float32Array hops on the main thread. The DSP itself
3562
+ * lives in core (`@bendyline/squisq/narration`) — this hook only moves
3563
+ * samples. Falls back to a ScriptProcessorNode when `audioWorklet` is
3564
+ * unavailable (same push model, so pacing still isn't tied to rAF).
3565
+ */
3566
+ type MicAnalysisStatus = 'idle' | 'starting' | 'live' | 'error';
3567
+ type PcmHopListener = (pcm: Float32Array, audioTimeSec: number) => void;
3568
+ interface MicAnalysisHandle {
3569
+ status: MicAnalysisStatus;
3570
+ error: Error | null;
3571
+ /** The live mic stream (feeds the narration recorder too), or null. */
3572
+ stream: MediaStream | null;
3573
+ /** AudioContext sample rate once live. */
3574
+ sampleRate: number | null;
3575
+ /** Known audio inputs (labels appear after the first grant). */
3576
+ devices: MediaDeviceInfo[];
3577
+ /**
3578
+ * Start (or restart) capture. Resolves with the live stream — callers
3579
+ * that need it immediately must use the return value, not the `stream`
3580
+ * state field, which only updates on the NEXT render (stale-closure
3581
+ * hazard right after an await). Null on failure/supersession.
3582
+ */
3583
+ start: (deviceId: string | null) => Promise<MediaStream | null>;
3584
+ stop: () => void;
3585
+ /** Subscribe to PCM hops; returns an unsubscribe. */
3586
+ subscribeHop: (listener: PcmHopListener) => () => void;
3587
+ }
3588
+ declare function useMicAnalysis(): MicAnalysisHandle;
3589
+
3590
+ /**
3591
+ * Narration recorder for the teleprompter — an in-place (no modal)
3592
+ * capture flow.
3593
+ *
3594
+ * Audio records THE MIC ANALYSIS STREAM (what paces the prompter is
3595
+ * exactly what lands in the take); the optional camera is a separate
3596
+ * video-only capture whose start skew vs the audio recorder is measured
3597
+ * and persisted (`cameraOffsetSec`) — the audio file is the doc clock,
3598
+ * so camera skew never affects narration timing. While recording, a
3599
+ * sparse live trace of the prompter position is sampled; after stop,
3600
+ * the take is decoded and run through core's offline aligner
3601
+ * (`alignNarration`) to produce word/block timestamps for the sidecar.
3602
+ * Decode/alignment failure degrades gracefully: saving still works,
3603
+ * just without re-timing.
3604
+ */
3605
+
3606
+ type NarrationRecorderState = 'idle' | 'starting' | 'recording' | 'processing' | 'review' | 'saving' | 'error';
3607
+ interface NarrationTake {
3608
+ audioBlob: Blob;
3609
+ audioMime: string;
3610
+ audioExt: string;
3611
+ cameraBlob: Blob | null;
3612
+ cameraMime: string | null;
3613
+ cameraExt: string | null;
3614
+ durationSec: number;
3615
+ cameraOffsetSec: number | undefined;
3616
+ trace: NarrationTrace;
3617
+ alignment: NarrationAlignment | null;
3618
+ script: NarrationScript;
3619
+ }
3620
+ interface UseNarrationRecorderOptions {
3621
+ mic: MicAnalysisHandle;
3622
+ getScript: () => NarrationScript | null;
3623
+ /** Live prompter position, sampled into the trace while recording. */
3624
+ getWordPos: () => number;
3625
+ getMicDeviceId: () => string | null;
3626
+ /** Fired when capture actually starts (View starts the prompter). */
3627
+ onRecordingStart?: () => void;
3628
+ onRecordingStop?: () => void;
3629
+ }
3630
+ interface NarrationRecorderController {
3631
+ state: NarrationRecorderState;
3632
+ error: Error | null;
3633
+ withCamera: boolean;
3634
+ setWithCamera: (on: boolean) => void;
3635
+ /** Live camera stream for the self-view while recording. */
3636
+ cameraStream: MediaStream | null;
3637
+ take: NarrationTake | null;
3638
+ start: () => Promise<void>;
3639
+ stop: () => Promise<void>;
3640
+ retake: () => void;
3641
+ discard: () => void;
3642
+ /** Transition into/out of 'saving'; the View owns the actual I/O. */
3643
+ beginSave: () => void;
3644
+ finishSave: (ok: boolean, error?: Error) => void;
3645
+ }
3646
+ declare function useNarrationRecorder(options: UseNarrationRecorderOptions): NarrationRecorderController;
3647
+
3648
+ /**
3649
+ * Pure save-plan builder for a narration take, plus its executor.
3650
+ *
3651
+ * Follows the recorder's conventions (`RecorderModal.handleSave`):
3652
+ * audio at `audio/<narration-…>` via `MediaProvider.addMedia` (the
3653
+ * RETURNED path is authoritative — providers may rename), the timing
3654
+ * sidecar at `<audio>.timing.json` via `container.writeFile` (falling
3655
+ * back to `addMedia` with a warning), and ONE `setMarkdownSource` write
3656
+ * composing every markdown edit from a single snapshot (the single-write
3657
+ * rule — successive writes each derive from stale source and clobber
3658
+ * each other).
3659
+ *
3660
+ * Block timings deliberately do NOT get baked into heading `duration=`
3661
+ * pins: pins outrank narration in `applyNarrationTiming`, so baking
3662
+ * them would shadow every retake. The sidecar owns the timing.
3663
+ */
3664
+
3665
+ interface NarrationSavePlanArgs {
3666
+ script: NarrationScript;
3667
+ /** Null when decode/alignment failed — the sidecar still saves (empty timing). */
3668
+ alignment: NarrationAlignment | null;
3669
+ durationSec: number;
3670
+ audioExt: string;
3671
+ cameraExt: string | null;
3672
+ baseWpm: number;
3673
+ cameraOffsetSec?: number;
3674
+ }
3675
+ interface NarrationSavePlan {
3676
+ audioRelativeName: string;
3677
+ cameraRelativeName: string | null;
3678
+ sidecarPayload: NarrationTimingJsonV3;
3679
+ /** Sidecar path for a given (possibly renamed) saved audio path. */
3680
+ sidecarPathFor: (savedAudioPath: string) => string;
3681
+ /** Compose ALL markdown edits from one source snapshot. */
3682
+ nextMarkdown: (currentSource: string, savedAudioPath: string, savedCameraPath: string | null) => string;
3683
+ }
3684
+ declare function buildNarrationSavePlan(args: NarrationSavePlanArgs): NarrationSavePlan;
3685
+ interface ExecuteNarrationSaveDeps {
3686
+ mediaProvider: MediaProvider;
3687
+ container: ContentContainer | null;
3688
+ markdownSource: string;
3689
+ setMarkdownSource: (next: string) => void;
3690
+ bumpMediaRevision: () => void;
3691
+ }
3692
+ interface NarrationSaveResult {
3693
+ audioPath: string;
3694
+ cameraPath: string | null;
3695
+ sidecarPath: string;
3696
+ }
3697
+ /** Execute a save plan: media writes, sidecar, then the single markdown write. */
3698
+ declare function executeNarrationSave(plan: NarrationSavePlan, take: {
3699
+ audioBlob: Blob;
3700
+ audioMime: string;
3701
+ cameraBlob: Blob | null;
3702
+ cameraMime: string | null;
3703
+ }, deps: ExecuteNarrationSaveDeps): Promise<NarrationSaveResult>;
3704
+
3705
+ /**
3706
+ * Pure markdown edit: insert (or replace) the narration reference at the
3707
+ * start of the document.
3708
+ *
3709
+ * The narration take is referenced by a preamble media annotation —
3710
+ * `{[audio src=audio/narration-….webm anchor=document]}` — the canonical
3711
+ * authored form `markdownToDoc` lifts into `doc.documentMedia`. A prior
3712
+ * teleprompter take's line (matched by the `audio/narration-` prefix +
3713
+ * `anchor=document`) is replaced, so retakes swap the reference instead
3714
+ * of stacking narrations. The optional camera companion rides on the
3715
+ * next line as an inline `<video>` (deliberately NOT doc-anchored:
3716
+ * document-anchored video renders full-bleed behind every block and is
3717
+ * excluded from export audio — not a talking head).
3718
+ */
3719
+ declare function narrationAnnotationLine(audioPath: string): string;
3720
+ declare function cameraVideoLine(cameraPath: string): string;
3721
+ /**
3722
+ * Insert the narration preamble after the frontmatter (or at the top),
3723
+ * replacing any previous teleprompter take's lines.
3724
+ */
3725
+ declare function insertNarrationPreamble(source: string, audioPath: string, cameraPath: string | null): string;
3726
+
3727
+ interface TeleprompterSurfaceProps {
3728
+ script: NarrationScript;
3729
+ /** Fractional word position; floor is the active token. */
3730
+ wordPos: number;
3731
+ fontSizePx: number;
3732
+ mirrored: boolean;
3733
+ lineGuide: boolean;
3734
+ countdownRemaining: number | null;
3735
+ recordingIndicator: boolean;
3736
+ theme: Theme;
3737
+ /** Hide block markers / tighten padding for small float windows. */
3738
+ compact?: boolean;
3739
+ /** Click a word to jump the prompter there. */
3740
+ onSeekToken?: (tokenIndex: number) => void;
3741
+ }
3742
+ declare function TeleprompterSurface({ script, wordPos, fontSizePx, mirrored, lineGuide, countdownRemaining, recordingIndicator, theme, compact, onSeekToken, }: TeleprompterSurfaceProps): react_jsx_runtime.JSX.Element;
3743
+
3744
+ /**
3745
+ * Shared types for the Narrate (teleprompter) display mode.
3746
+ */
3747
+ interface TeleprompterPrefs {
3748
+ /** Prompter type size in px (28–96). */
3749
+ fontSizePx: number;
3750
+ /** Beam-splitter mirror flip. */
3751
+ mirrored: boolean;
3752
+ /** Base speaking rate in words per minute (80–260). */
3753
+ baseWpm: number;
3754
+ /** Voice-adaptive pacing on/off; off = constant-rate manual mode. */
3755
+ voiceTracking: boolean;
3756
+ /** VAD sensitivity 0–1 (0.5 = engine defaults). */
3757
+ vadSensitivity: number;
3758
+ /** Countdown before the prompter starts rolling. */
3759
+ countdownSec: 0 | 3 | 5 | 10;
3760
+ /** Eye-line chevrons + focus band. */
3761
+ lineGuide: boolean;
3762
+ /** Preferred mic device id (null = system default). */
3763
+ micDeviceId: string | null;
3764
+ }
3765
+ declare const DEFAULT_TELEPROMPTER_PREFS: TeleprompterPrefs;
3766
+ type PrompterTransport = 'stopped' | 'countdown' | 'rolling' | 'paused' | 'finished';
3767
+ /** Floating-surface tier, best first. */
3768
+ type FloatTier = 'document-pip' | 'video-pip' | 'popup' | 'docked';
3769
+
3770
+ /**
3771
+ * The teleprompter controller — single source of truth, always in the
3772
+ * MAIN window (floats are render targets only).
3773
+ *
3774
+ * Loop-ownership rule: **the audio worklet owns time; the main window
3775
+ * owns state; the visible surface owns pixels.** Voice-mode position
3776
+ * advances on worklet PCM hops (immune to rAF/timer throttling while
3777
+ * the browser is occluded by recording software); manual constant-rate
3778
+ * mode uses rAF and is documented as best-effort under occlusion. React
3779
+ * state publishes at ~15 Hz; per-hop subscribers (`subscribeTick`)
3780
+ * exist for the video-PiP canvas pump.
3781
+ */
3782
+
3783
+ interface TeleprompterController {
3784
+ script: NarrationScript | null;
3785
+ transport: PrompterTransport;
3786
+ countdownRemaining: number | null;
3787
+ /** Fractional word position (published ~15 Hz). */
3788
+ wordPos: number;
3789
+ /** Smoothed mic level 0–1 for the meter. */
3790
+ micLevel: number;
3791
+ /** VAD flag for the meter tint. */
3792
+ voiceActive: boolean;
3793
+ mic: MicAnalysisHandle;
3794
+ prefs: TeleprompterPrefs;
3795
+ setPrefs: (patch: Partial<TeleprompterPrefs>) => void;
3796
+ play: () => void;
3797
+ pause: () => void;
3798
+ restart: () => void;
3799
+ /** Move the prompter by whole tokens and re-anchor voice tracking. */
3800
+ nudge: (deltaTokens: number) => void;
3801
+ /** Jump to an absolute token index (e.g. click a block marker). */
3802
+ seekToToken: (tokenIndex: number) => void;
3803
+ /** Per-analysis-tick subscription (video-PiP pump). Not throttled. */
3804
+ subscribeTick: (cb: (wordPos: number) => void) => () => void;
3805
+ handleKeyDown: (event: KeyboardEvent<HTMLElement>) => void;
3806
+ }
3807
+ /** Map the 0–1 sensitivity pref onto VAD thresholds (0.5 = engine defaults). */
3808
+ declare function vadConfigForSensitivity(sensitivity: number): Partial<VadConfig>;
3809
+ declare function useTeleprompter(opts: {
3810
+ doc: Doc | null;
3811
+ }): TeleprompterController;
3812
+
3813
+ /**
3814
+ * Floating-teleprompter window manager — framework-free.
3815
+ *
3816
+ * Capability ladder, best first:
3817
+ * 1. `document-pip` — Document Picture-in-Picture (Chromium 116+,
3818
+ * Firefox 151+): a true always-on-top window hosting live DOM; the
3819
+ * React surface portals into it.
3820
+ * 2. `video-pip` — canvas → `captureStream(0)` → `<video>` →
3821
+ * `requestPictureInPicture()` (Safari's only always-on-top path;
3822
+ * `webkitSetPresentationMode` fallback). Read-only: the main
3823
+ * window draws frames and calls `requestFrame()` on analysis
3824
+ * ticks — never rAF, which throttles under occlusion.
3825
+ * 3. `popup` — `window.open` (positionable, not always-on-top).
3826
+ * 4. `docked` — no float.
3827
+ *
3828
+ * Every tier is feature-detected at open time and falls through to the
3829
+ * next on ANY failure. All interactive state stays in the main window;
3830
+ * floats are render targets only.
3831
+ */
3832
+
3833
+ interface FloatOpenOptions {
3834
+ width: number;
3835
+ height: number;
3836
+ /** Try this tier first; the ladder continues below it on failure. */
3837
+ preferredTier?: FloatTier;
3838
+ title: string;
3839
+ }
3840
+ interface CanvasSink {
3841
+ canvas: HTMLCanvasElement;
3842
+ width: number;
3843
+ height: number;
3844
+ /** Push the freshly drawn canvas frame into the PiP video. */
3845
+ requestFrame: () => void;
3846
+ }
3847
+ type FloatEvent = 'closed' | 'tierchange';
3848
+ interface FloatingWindowManager {
3849
+ readonly tier: FloatTier;
3850
+ readonly isOpen: boolean;
3851
+ /** Resolves with the tier that actually opened ('docked' if none could). */
3852
+ open(opts: FloatOpenOptions): Promise<FloatTier>;
3853
+ /** Idempotent; restores docked and emits 'closed'. */
3854
+ close(): void;
3855
+ /** Portal container for 'document-pip' | 'popup'; null otherwise. */
3856
+ getPortalTarget(): HTMLElement | null;
3857
+ /** Canvas sink for 'video-pip'; null otherwise. */
3858
+ getCanvasSink(): CanvasSink | null;
3859
+ on(event: FloatEvent, cb: (tier: FloatTier) => void): () => void;
3860
+ /** Tear everything down (unmount); like close() but silent-safe. */
3861
+ dispose(): void;
3862
+ }
3863
+ /** Feature-detect the available float tiers, best first. */
3864
+ declare function detectFloatTiers(): FloatTier[];
3865
+ declare function createFloatingWindowManager(deps: {
3866
+ styleCss: string;
3867
+ }): FloatingWindowManager;
3868
+
3869
+ /**
3870
+ * React binding over {@link createFloatingWindowManager}: exposes the
3871
+ * current tier + portal target as state and guarantees the float closes
3872
+ * when the owning view unmounts (mode switch, Use-tab exit, shell
3873
+ * teardown).
3874
+ */
3875
+
3876
+ interface FloatingWindowHandle {
3877
+ tier: FloatTier;
3878
+ isOpen: boolean;
3879
+ /** Non-docked tiers this browser supports, best first. */
3880
+ supportedTiers: FloatTier[];
3881
+ portalTarget: HTMLElement | null;
3882
+ canvasSink: CanvasSink | null;
3883
+ open: (preferredTier?: FloatTier) => Promise<void>;
3884
+ close: () => void;
3885
+ }
3886
+ declare function useFloatingWindow(styleCss: string): FloatingWindowHandle;
3887
+
3888
+ interface TeleprompterControlsProps {
3889
+ controller: TeleprompterController;
3890
+ float: FloatingWindowHandle;
3891
+ /** Phase B mounts the narration Record button here. */
3892
+ recordSlot?: ReactNode;
3893
+ }
3894
+ declare function TeleprompterControls({ controller, float, recordSlot }: TeleprompterControlsProps): react_jsx_runtime.JSX.Element;
3895
+
3896
+ /**
3897
+ * The PCM tap AudioWorklet, shipped as an inline source string.
3898
+ *
3899
+ * Why inline → Blob URL instead of a bundled asset: `audioWorklet
3900
+ * .addModule(url)` resolves relative to the host DOCUMENT, so a
3901
+ * dist-relative asset would need every consumer's bundler to rewrite
3902
+ * and serve it (editor-react ships zero runtime assets today). The
3903
+ * worklet is ~30 lines with no imports; inlining follows the
3904
+ * `PLAYER_BUNDLE` source-string precedent and needs zero bundler
3905
+ * config. The worklet does NO DSP — it batches 128-sample render
3906
+ * quanta into 1024-sample frames and posts them (transferably) to the
3907
+ * main thread, where the pure engine in `@bendyline/squisq/narration`
3908
+ * runs. The audio render thread never throttles, so pacing keeps
3909
+ * working while the browser window is fully occluded by recording
3910
+ * software — the whole point of the teleprompter's scenario 2.
3911
+ */
3912
+ declare const PCM_WORKLET_NAME = "squisq-pcm-tap";
3913
+ declare const PCM_WORKLET_SOURCE = "\nclass SquisqPcmTap extends AudioWorkletProcessor {\n constructor() {\n super();\n this._buf = new Float32Array(1024);\n this._len = 0;\n }\n process(inputs) {\n const channel = inputs[0] && inputs[0][0];\n if (channel && channel.length > 0) {\n let i = 0;\n while (i < channel.length) {\n const n = Math.min(channel.length - i, this._buf.length - this._len);\n this._buf.set(channel.subarray(i, i + n), this._len);\n this._len += n;\n i += n;\n if (this._len === this._buf.length) {\n const out = this._buf;\n this._buf = new Float32Array(1024);\n this._len = 0;\n // Clone, don't transfer: Chrome recycles buffers transferred out\n // of the worklet scope once the receiving handler returns, so a\n // transferred hop reads as zeros if anything retains it. A 4 KB\n // structured clone at ~47 Hz is negligible.\n this.port.postMessage({ pcm: out, audioTime: currentTime });\n }\n }\n }\n return true;\n }\n}\nregisterProcessor('squisq-pcm-tap', SquisqPcmTap);\n";
3914
+ /** Register the tap on an AudioContext via a same-origin Blob URL. */
3915
+ declare function registerPcmWorklet(ctx: AudioContext): Promise<void>;
3916
+
3917
+ /**
3918
+ * Pure scroll math for the teleprompter surface.
3919
+ *
3920
+ * The surface renders the whole script into one absolutely-positioned
3921
+ * column and translates it so the active word sits at the eye-line
3922
+ * (~35% from the top). These helpers are DOM-free except for the
3923
+ * measurement entry point, which only reads layout.
3924
+ */
3925
+ interface TokenLineMap {
3926
+ /** Top offset (px, within the scroll column) of each token span. */
3927
+ tokenTops: number[];
3928
+ /** Height of each token's line box (px). */
3929
+ tokenHeights: number[];
3930
+ }
3931
+ /** Fraction of the viewport height where the active line is held. */
3932
+ declare const EYE_LINE_FRACTION = 0.35;
3933
+ /**
3934
+ * Measure the top offset of every `[data-token-idx]` span inside the
3935
+ * scroll column. Layout read only — call on mount, resize, and font-size
3936
+ * changes, not per frame.
3937
+ */
3938
+ declare function measureTokenLines(scrollColumn: HTMLElement): TokenLineMap;
3939
+ /**
3940
+ * The translateY offset (px) that puts the (fractional) word position on
3941
+ * the eye-line. Interpolates between the current and next token tops so
3942
+ * the column glides instead of stepping line by line.
3943
+ */
3944
+ declare function targetOffsetFor(wordPos: number, lines: TokenLineMap, viewportHeightPx: number, eyeLine?: number): number;
3945
+ /**
3946
+ * Advance the current scroll offset toward the target: exponential
3947
+ * approach (critically-damped feel) with an absolute speed clamp so a
3948
+ * hard resync glides rather than teleporting.
3949
+ */
3950
+ declare function stepScroll(currentPx: number, targetPx: number, dtMs: number, maxPxPerSec?: number): number;
3951
+
3952
+ /**
3953
+ * Self-contained styling for the teleprompter.
3954
+ *
3955
+ * Everything the prompter surface needs ships as one CSS string plus a
3956
+ * set of inline `--squisq-prompter-*` custom properties derived from the
3957
+ * doc Theme. The docked surface, a Document-PiP window, and a popup all
3958
+ * inject the SAME string, so the surface never depends on the host
3959
+ * page's cascade (the VideoExportModal self-theming strategy).
3960
+ */
3961
+
3962
+ /** Inline CSS vars for the surface root, derived from the doc theme. */
3963
+ declare function prompterVarsFromTheme(theme: Theme): Record<string, string>;
3964
+ /** Inject {@link TELEPROMPTER_CSS} into a document once (main doc or a float window). */
3965
+ declare function ensureTeleprompterStyles(doc: Document): void;
3966
+ declare const TELEPROMPTER_CSS = "\n.squisq-teleprompter-root {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n min-height: 0;\n background: var(--squisq-bg, #f5f5f5);\n outline: none;\n}\n.squisq-teleprompter-stage {\n position: relative;\n flex: 1;\n min-height: 0;\n display: flex;\n}\n\n.squisq-teleprompter-surface {\n position: relative;\n flex: 1;\n min-height: 0;\n overflow: hidden;\n background: var(--squisq-prompter-bg, #101014);\n color: var(--squisq-prompter-text, #f5f5f2);\n font-family: var(--squisq-prompter-font, system-ui, sans-serif);\n user-select: none;\n}\n.squisq-teleprompter-flip {\n position: absolute;\n inset: 0;\n}\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-flip {\n transform: scaleX(-1);\n}\n.squisq-teleprompter-scroll {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n will-change: transform;\n padding: 40vh 8% 60vh;\n box-sizing: border-box;\n}\n.squisq-teleprompter-line-guide {\n position: absolute;\n left: 0;\n right: 0;\n pointer-events: none;\n z-index: 2;\n}\n.squisq-teleprompter-line-guide::before,\n.squisq-teleprompter-line-guide::after {\n content: '';\n position: absolute;\n top: 50%;\n border: 9px solid transparent;\n transform: translateY(-50%);\n}\n.squisq-teleprompter-line-guide::before {\n left: 6px;\n border-left-color: var(--squisq-prompter-accent, #e8b64c);\n}\n.squisq-teleprompter-line-guide::after {\n right: 6px;\n border-right-color: var(--squisq-prompter-accent, #e8b64c);\n}\n.squisq-teleprompter-guide-band {\n position: absolute;\n left: 0;\n right: 0;\n background: color-mix(in srgb, var(--squisq-prompter-accent, #e8b64c) 9%, transparent);\n pointer-events: none;\n z-index: 1;\n}\n\n.squisq-teleprompter-block-marker {\n display: block;\n margin: 1.1em 0 0.35em;\n font-size: 0.38em;\n font-weight: 600;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n color: var(--squisq-prompter-muted, #9a9aa0);\n border-top: 1px solid color-mix(in srgb, var(--squisq-prompter-muted, #9a9aa0) 35%, transparent);\n padding-top: 0.6em;\n}\n.squisq-teleprompter-para {\n margin: 0 0 0.55em;\n line-height: 1.4;\n font-weight: 500;\n}\n.squisq-teleprompter-word {\n opacity: 0.45;\n transition: color 0.1s linear, opacity 0.1s linear;\n}\n.squisq-teleprompter-word--read {\n opacity: 0.9;\n}\n.squisq-teleprompter-word--active {\n opacity: 1;\n font-weight: 800;\n color: var(--squisq-prompter-accent, #e8b64c);\n}\n\n.squisq-teleprompter-countdown {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 3;\n background: color-mix(in srgb, var(--squisq-prompter-bg, #101014) 72%, transparent);\n}\n.squisq-teleprompter-countdown-digit {\n font-size: 18vmin;\n font-weight: 800;\n color: var(--squisq-prompter-accent, #e8b64c);\n animation: squisq-prompter-pulse 1s ease-in-out infinite;\n}\n.squisq-teleprompter-recdot {\n position: absolute;\n top: 14px;\n right: 16px;\n z-index: 4;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: #e5484d;\n box-shadow: 0 0 0 3px color-mix(in srgb, #e5484d 30%, transparent);\n animation: squisq-prompter-pulse 1.4s ease-in-out infinite;\n}\n/* Counter-flip overlays so they stay readable in mirror mode. */\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-countdown,\n.squisq-teleprompter-surface--mirrored .squisq-teleprompter-recdot {\n transform: scaleX(-1);\n}\n\n.squisq-teleprompter-selfview {\n position: absolute;\n right: 14px;\n bottom: 14px;\n width: 160px;\n border-radius: 8px;\n border: 2px solid color-mix(in srgb, #e5484d 60%, transparent);\n z-index: 5;\n background: #000;\n}\n\n.squisq-teleprompter-review {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 10px;\n padding: 8px 12px;\n border-top: 1px solid var(--squisq-border, #e5e7eb);\n background: var(--squisq-surface, var(--squisq-bg, #fff));\n color: var(--squisq-text, #111827);\n font-size: 12.5px;\n}\n.squisq-teleprompter-review audio {\n height: 28px;\n max-width: 260px;\n}\n.squisq-teleprompter-review button {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n padding: 4px 10px;\n cursor: pointer;\n}\n\n.squisq-teleprompter-float-note {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n color: var(--squisq-text-muted, #6b7280);\n font-size: 14px;\n}\n\n.squisq-teleprompter-controls {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 10px;\n padding: 8px 12px;\n border-top: 1px solid var(--squisq-border, #e5e7eb);\n background: var(--squisq-surface, var(--squisq-bg, #fff));\n color: var(--squisq-text, #111827);\n font-size: 12.5px;\n}\n.squisq-teleprompter-controls .squisq-teleprompter-group {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n white-space: nowrap;\n}\n.squisq-teleprompter-controls button {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.squisq-teleprompter-controls button:hover {\n background: var(--squisq-surface-hover, #f3f4f6);\n}\n.squisq-teleprompter-controls button[aria-pressed='true'] {\n background: var(--squisq-text, #111827);\n color: var(--squisq-bg, #fff);\n border-color: var(--squisq-text, #111827);\n}\n.squisq-teleprompter-controls select,\n.squisq-teleprompter-controls input[type='range'] {\n font: inherit;\n color: inherit;\n background: var(--squisq-input-bg, #fff);\n border: 1px solid var(--squisq-border, #d1d5db);\n border-radius: 6px;\n max-width: 170px;\n}\n.squisq-teleprompter-controls input[type='range'] {\n border: none;\n background: transparent;\n width: 110px;\n}\n.squisq-teleprompter-meter {\n position: relative;\n width: 64px;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n background: var(--squisq-border, #e5e7eb);\n}\n.squisq-teleprompter-meter-fill {\n position: absolute;\n inset: 0 auto 0 0;\n background: #9ca3af;\n transition: width 0.08s linear;\n}\n.squisq-teleprompter-meter--voice .squisq-teleprompter-meter-fill {\n background: #30a46c;\n}\n\n@keyframes squisq-prompter-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.45; }\n}\n@media (prefers-reduced-motion: reduce) {\n .squisq-teleprompter-countdown-digit,\n .squisq-teleprompter-recdot {\n animation: none;\n }\n .squisq-teleprompter-word {\n transition: none;\n }\n}\n";
3967
+
2872
3968
  interface ImageEditorProps {
2873
3969
  /**
2874
3970
  * Scoped sidecar container for this image — typically
@@ -3057,4 +4153,4 @@ interface UseImageEditorReturn {
3057
4153
  }
3058
4154
  declare function useImageEditor(options: UseImageEditorOptions): UseImageEditorReturn;
3059
4155
 
3060
- export { ALL_EMOJIS, BlockCardView, type BlockCardViewProps, type BlockNavigator, BlockPropertiesPopover, type BlockPropertiesPopoverProps, type BlockRange, type BlockSlice, type CameraStreamOptions, type CanvasRect, type CaptureKind, type CodeContext, type CodeContextSection, CodeContextZones, type CustomTemplateContextValue, CustomTemplateProvider, type CustomTemplateProviderProps, type CustomThemeContextValue, CustomThemeProvider, type CustomThemeProviderProps, DiagramCanvas, type DiagramCommand, type DiagramData, DiagramExtension, type DiagramRFEdge, type DiagramRFNode, DiagramWidget, type DirectionModel, type DocCustomTemplates, type DocCustomThemes, type DocumentLinkCandidate, type DocumentLinkProvider, DocumentSettingsDialog, type DocumentSettingsDialogProps, type DragContentType, type DropTarget, DropZoneOverlay, type DropZoneOverlayProps, EMOJI_CATEGORIES, EMPTY_TRANSITION, type EditorActions, type EditorColorScheme, type EditorContextValue, type EditorMode, EditorProvider, type EditorProviderProps, EditorShell, type EditorShellProps, type EditorState, type EditorView, type EmojiCategory, type EmojiEntry, EmojiPicker, type EmojiPickerProps, type FileCategory, type FileKind, type FolderEntry, FolderView, type FolderViewProps, HeadingWithTemplate, type ImageDisplayMode, ImageEditor, type ImageEditorAction, type ImageEditorProps, type ImageEditorState, type ImageEditorTool, ImageViewer, type ImageViewerProps, InlinePreviewGutter, type InlinePreviewGutterProps, JsonEditor, type JsonEditorProps, type LayoutMode, MediaBin, type MediaBinProps, type MediaClipPatch, type MentionCandidate, type MentionProvider, OutlinePanel, type OutlinePanelProps, PlainHtmlPreview, type PlainHtmlPreviewProps, PreviewFormatSwitch, PreviewModeSwitch, PreviewPanel, type PreviewPanelProps, type PreviewSettings, PreviewSettingsProvider, PreviewToolbarControls, RawEditor, type RawEditorProps, type RecordedBookmark, RecorderButton, type RecorderButtonProps, RecorderModal, type RecorderModalProps, RecorderPanel, type RecorderPanelProps, type RecorderSaveResult, type RecorderSource, type RecorderState, type ResolvedFormat, type ScreenStreamHandle, type ScreenStreamOptions, StatusBar, type StatusBarProps, TRANSITION_ENTRIES, TRANSITION_GROUPS, TemplatePicker, ThemeCustomizerPanel, type ThemeCustomizerPanelProps, type ThemeInheritance, ThemePicker, type ThemePickerProps, TimelineTrack, type TimelineTrackProps, type TimingJson, Toolbar, type ToolbarProps, TooltipLayer, type TransitionCatalogEntry, type TransitionFields, type TransitionGroup, TransitionPicker, type TransitionPickerProps, type UseBlockNavigatorOptions, type UseFileDropOptions, type UseFileDropResult, type UseImageEditorOptions, type UseImageEditorReturn, type UseMediaRecorderOptions, type UseMediaRecorderResult, type UseMonacoLoaderResult, VersionHistoryPanel, ViewMenuPanel, type ViewPreferences, ViewSwitcher, type ViewSwitcherProps, WysiwygEditor, type WysiwygEditorProps, addConnection, addNode, buildFilename, buildPreviewDoc, buildTimingJson, classifyFile, detectLanguageFromFileName, encodeTimingJson, findTransitionEntry, formatSeconds, getBlockSlices, getCaptureKind, imageEditorReducer, initialImageEditorState, lineToOffset, listDiagramChildren, markdownToTiptap, moveNode, offsetToLine, partitionFiles, processMediaFiles, processTextFile, processTextFiles, readBlockAttrsParams, readBlockAttrsTransition, readBlockAttrsValue, readHeadingLineTransition, removeConnection, removeNode, renameNode, requestCameraStream, requestMicStream, requestScreenStream, resolveFileKind, resolveFormat, searchEmojis, setBlockAttrsTransition, setBlockAttrsValue, setBlockDurationInSource, setHeadingLineTransition, setMediaClipInSource, sliceIndexAtOffset, spliceBlock, summarizeBlockProps, supportsDisplayMedia, supportsMediaRecorder, supportsUserMedia, templateLabel, timingPathFor, tiptapToMarkdown, transitionLabel, useBlockNavigator, useCustomTemplates, useCustomThemes, useDiagramData, useDocCustomTemplates, useDocCustomThemes, useEditorContext, useFileDrop, useImageEditor, useMediaRecorder, useMonacoLoader, usePreviewSettings, useStreamPreview };
4156
+ export { ALL_PICKER_ENTRIES, type AddTimelineEventOptions, type AddTimelineEventResult, type AsciiDiagramBlockEntry, AsciiDiagramExtension, type AsciiDiagramExtensionOptions, type AsciiDiagramPluginState, type AsciiDiagramView, AsciiDiagramWidget, BlockCardView, type BlockCardViewProps, type BlockNavigator, BlockPropertiesPopover, type BlockPropertiesPopoverProps, type BlockRange, type BlockSlice, type BlockTagVisibility, type CameraStreamOptions, type CanvasRect, type CanvasSink, type CaptureKind, type CodeContext, type CodeContextSection, CodeContextZones, type CustomTemplateContextValue, CustomTemplateProvider, type CustomTemplateProviderProps, type CustomThemeContextValue, CustomThemeProvider, type CustomThemeProviderProps, DEFAULT_TELEPROMPTER_PREFS, DiagramCanvas, type DiagramCommand, type DiagramData, type DiagramEdge, type DiagramNode, type DirectionModel, type DocCustomTemplates, type DocCustomThemes, type DocumentLinkCandidate, type DocumentLinkProvider, DocumentSettingsDialog, type DocumentSettingsDialogProps, type DragContentType, type DropTarget, DropZoneOverlay, type DropZoneOverlayProps, EMPTY_TRANSITION, EYE_LINE_FRACTION, type EditorActions, type EditorColorScheme, type EditorContextValue, type EditorMode, EditorProvider, type EditorProviderProps, EditorShell, type EditorShellProps, type EditorState, type EditorView, EmojiPicker, type EmojiPickerProps, type FileCategory, type FileKind, type FloatOpenOptions, type FloatTier, type FloatingWindowHandle, type FloatingWindowManager, type FolderEntry, FolderView, type FolderViewProps, type HeadingTransitionAttrs, HeadingWithTemplate, type ImageDisplayMode, ImageEditor, type ImageEditorAction, type ImageEditorProps, type ImageEditorState, type ImageEditorTool, ImageViewer, type ImageViewerProps, ImportThemeSection, type ImportThemeSectionProps, type ImportedThemeResult, InlinePreviewGutter, type InlinePreviewGutterProps, JsonEditor, type JsonEditorProps, type LayoutMode, MediaBin, type MediaBinProps, type MediaClipPatch, type MentionCandidate, type MentionProvider, type MicAnalysisHandle, type MicAnalysisStatus, type MonacoWorkerConstructor, type MonacoWorkerConstructors, type NarrationRecorderController, type NarrationRecorderState, type NarrationSavePlan, type NarrationSaveResult, type NarrationTake, OutlinePanel, type OutlinePanelProps, PCM_WORKLET_NAME, PCM_WORKLET_SOURCE, PICKER_CATEGORIES, type PickerCategory, type PickerEntry, PlainHtmlPreview, type PlainHtmlPreviewProps, PreviewFormatSwitch, PreviewModeMenu, PreviewModeSwitch, PreviewPanel, type PreviewPanelProps, type PreviewSettings, PreviewSettingsProvider, PreviewToolbarControls, type PrompterTransport, REPAIRABLE_KEY, RawEditor, type RawEditorProps, type RecordedBookmark, RecorderButton, type RecorderButtonProps, RecorderModal, type RecorderModalProps, RecorderPanel, type RecorderPanelProps, type RecorderSaveResult, type RecorderSource, type RecorderState, type RepairableBlockEntry, RepairableDiagramExtension, type RepairableDiagramExtensionOptions, type RepairablePluginState, type ResolvedFormat, type ScreenStreamHandle, type ScreenStreamOptions, StatusBar, type StatusBarProps, TELEPROMPTER_CSS, TIMELINE_VIEW_KEY, TRANSITION_ENTRIES, TRANSITION_GROUPS, type TeleprompterController, TeleprompterControls, type TeleprompterControlsProps, type TeleprompterPrefs, type TeleprompterRecordingDeps, TeleprompterSurface, type TeleprompterSurfaceProps, TeleprompterView, type TeleprompterViewProps, TemplatePicker, ThemeCustomizerPanel, type ThemeCustomizerPanelProps, type ThemeInheritance, ThemePicker, type ThemePickerProps, type ThemeSaveExtras, type TimelineBlockEntry, type TimelineCommand, type TimelineCommandResult, TimelineEditorWidget, type TimelineEditorWidgetProps, type TimelineEventPatch, TimelineTrack, type TimelineTrackProps, type TimelineViewData, TimelineViewExtension, type TimelineViewExtensionOptions, type TimelineViewPluginState, type TimingJson, type TokenLineMap, Toolbar, type ToolbarProps, TooltipLayer, type TransitionCatalogEntry, type TransitionFields, type TransitionGroup, TransitionPicker, type TransitionPickerProps, type TreeBlockEntry, type TreeCommand, TreeOutlineWidget, type TreeViewData, TreeViewExtension, type TreeViewExtensionOptions, type TreeViewPluginState, type UseBlockNavigatorOptions, type UseFileDropOptions, type UseFileDropResult, type UseImageEditorOptions, type UseImageEditorReturn, type UseMediaRecorderOptions, type UseMediaRecorderResult, type UseMonacoLoaderResult, VersionHistoryPanel, ViewMenuPanel, type ViewPreferences, ViewSwitcher, type ViewSwitcherProps, WysiwygEditor, type WysiwygEditorProps, addEdgeOp, addItemOp, addNodeOp, addTimelineEventOp, applyAsciiDiagramCommand, applyRepairCommand, applyTimelineCommand, applyTreeCommand, asciiDiagramToCanvas, buildFilename, buildNarrationSavePlan, buildPreviewDoc, buildTimingJson, cameraVideoLine, classifyFile, configureMonacoWorkers, createFloatingWindowManager, detectFloatTiers, detectLanguageFromFileName, draftPatchFromImportedTheme, encodeTimingJson, ensureTeleprompterStyles, executeNarrationSave, findAsciiDiagramBlockPos, findRepairableBlockPos, findTimelineBlockPos, findTransitionEntry, findTreeBlockPos, formatSeconds, getBlockSlices, getCaptureKind, getTimelineForNode, imageEditorReducer, indentItemOp, initialImageEditorState, insertNarrationPreamble, isAsciiSourceVisible, isRepairableFence, isTimelineSourceSafeForSemanticEdit, lineToOffset, markdownToTiptap, measureTokenLines, moveItemDownOp, moveItemUpOp, moveNodeOp, narrationAnnotationLine, nextTimelineEventId, offsetToLine, outdentItemOp, parseTimelineForNode, partitionFiles, processMediaFiles, processTextFile, processTextFiles, prompterVarsFromTheme, readBlockAttrsParams, readBlockAttrsTransition, readBlockAttrsValue, readHeadingLineTransition, registerPcmWorklet, removeEdgeOp, removeItemOp, removeNodeOp, removeTimelineEventOp, renameItemOp, renameNodeOp, replaceAsciiFenceText, replaceAsciiFenceText as replaceTreeFenceText, requestCameraStream, requestMicStream, requestScreenStream, resizeNodeOp, resolveFileKind, resolveFormat, sanitizeAsciiLabel, sanitizeTimelineText, sanitizeTreeLabel, searchPickerEntries, setBlockAttrsValue, setBlockDurationInSource, setHeadingAttrsTransition, setHeadingLineTransition, setMediaClipInSource, shouldPasteAsAsciiFence, shouldPasteAsTimelineFence, shouldPasteAsTreeFence, sliceIndexAtOffset, spliceBlock, stepScroll, summarizeBlockProps, supportsDisplayMedia, supportsMediaRecorder, supportsUserMedia, targetOffsetFor, templateLabel, timingPathFor, tiptapToMarkdown, toggleAsciiSource, toggleDirOp, transitionLabel, updateTimelineEventOp, useAsciiDiagramData, useBlockNavigator, useCustomTemplates, useCustomThemes, useDocCustomTemplates, useDocCustomThemes, useEditorContext, useFileDrop, useFloatingWindow, useImageEditor, useMediaRecorder, useMicAnalysis, useMonacoLoader, useNarrationRecorder, usePreviewSettings, useStreamPreview, useTeleprompter, useTimelineData, useTreeViewData, vadConfigForSensitivity };