@bendyline/squisq-editor-react 2.0.1 → 2.2.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 (357) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +40 -0
  3. package/README.md +10 -2
  4. package/dist/index.d.ts +683 -60
  5. package/dist/index.js +9648 -3750
  6. package/dist/monaco.js +0 -1
  7. package/dist/styles/index.css +1160 -241
  8. package/package.json +10 -6
  9. package/dist/index.js.map +0 -1
  10. package/dist/monaco.js.map +0 -1
  11. package/src/BlockCardView.tsx +0 -121
  12. package/src/BlockPreviewPanel.tsx +0 -69
  13. package/src/BlockPropertiesPopover.tsx +0 -222
  14. package/src/DocumentSettingsDialog.tsx +0 -272
  15. package/src/DropZoneOverlay.tsx +0 -137
  16. package/src/EditorContext.tsx +0 -1247
  17. package/src/EditorShell.tsx +0 -1334
  18. package/src/EmojiPicker.tsx +0 -332
  19. package/src/FolderView.tsx +0 -131
  20. package/src/Icon.tsx +0 -26
  21. package/src/ImageEditor.tsx +0 -395
  22. package/src/ImageNodeView.tsx +0 -284
  23. package/src/ImageViewer.tsx +0 -221
  24. package/src/InlineIcon.ts +0 -84
  25. package/src/InlinePreviewGutter.tsx +0 -590
  26. package/src/LinkDialog.tsx +0 -276
  27. package/src/MediaBin.tsx +0 -500
  28. package/src/MentionExtension.tsx +0 -261
  29. package/src/OutlinePanel.tsx +0 -547
  30. package/src/PlainHtmlPreview.tsx +0 -277
  31. package/src/PreviewControls.tsx +0 -1250
  32. package/src/PreviewPanel.tsx +0 -422
  33. package/src/RawEditor.tsx +0 -731
  34. package/src/RecorderEntry.tsx +0 -159
  35. package/src/StatusBar.tsx +0 -51
  36. package/src/TemplateAnnotation.ts +0 -167
  37. package/src/TemplateContentPreview.tsx +0 -56
  38. package/src/TemplatePicker.tsx +0 -1314
  39. package/src/ThemeCustomizerPanel.tsx +0 -304
  40. package/src/ThemePicker.tsx +0 -433
  41. package/src/TimelineBlockPreview.tsx +0 -37
  42. package/src/TimelineTrack.tsx +0 -671
  43. package/src/Toolbar.tsx +0 -2587
  44. package/src/Tooltip.tsx +0 -142
  45. package/src/TransitionPicker.tsx +0 -390
  46. package/src/VersionHistoryPanel.tsx +0 -359
  47. package/src/ViewMenuPanel.tsx +0 -189
  48. package/src/ViewSwitcher.tsx +0 -56
  49. package/src/WysiwygEditor.tsx +0 -792
  50. package/src/__tests__/blockProperties.test.ts +0 -92
  51. package/src/__tests__/blockPropertiesPopoverTheme.test.tsx +0 -33
  52. package/src/__tests__/blockRange.test.ts +0 -105
  53. package/src/__tests__/blockTagActivity.test.ts +0 -183
  54. package/src/__tests__/buildDocumentPreviewMarkdown.test.ts +0 -75
  55. package/src/__tests__/buildPreviewDocContent.test.ts +0 -48
  56. package/src/__tests__/buildPreviewDocTransition.test.ts +0 -92
  57. package/src/__tests__/canvasSurfaceTextEditing.test.tsx +0 -60
  58. package/src/__tests__/codeContextSectionView.test.tsx +0 -97
  59. package/src/__tests__/codeContextZoneManager.test.ts +0 -127
  60. package/src/__tests__/createShapeLayer.test.ts +0 -46
  61. package/src/__tests__/customLayoutManagerTheme.test.tsx +0 -34
  62. package/src/__tests__/detectMarkdown.test.ts +0 -73
  63. package/src/__tests__/diffContextSections.test.ts +0 -39
  64. package/src/__tests__/documentSettingsDialog.test.tsx +0 -197
  65. package/src/__tests__/drawingShapeRoundTrip.test.ts +0 -49
  66. package/src/__tests__/editorScrollHandoff.test.tsx +0 -102
  67. package/src/__tests__/editorScrollSync.test.ts +0 -65
  68. package/src/__tests__/editorShellCodeContext.test.tsx +0 -86
  69. package/src/__tests__/editorShellProps.test.tsx +0 -485
  70. package/src/__tests__/embeddedMedia.test.ts +0 -48
  71. package/src/__tests__/emojiPicker.test.tsx +0 -93
  72. package/src/__tests__/fileKind.test.ts +0 -112
  73. package/src/__tests__/headingTransition.test.ts +0 -176
  74. package/src/__tests__/imageEditAffordance.test.tsx +0 -272
  75. package/src/__tests__/imageEditModalContract.test.tsx +0 -119
  76. package/src/__tests__/imageEditorShell.test.tsx +0 -94
  77. package/src/__tests__/imageEditorState.test.ts +0 -171
  78. package/src/__tests__/inlinePreviewGutter.test.tsx +0 -62
  79. package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +0 -103
  80. package/src/__tests__/jsonEditor.test.tsx +0 -223
  81. package/src/__tests__/layersPanel.test.tsx +0 -167
  82. package/src/__tests__/layoutChildRoundTrip.test.ts +0 -71
  83. package/src/__tests__/linkDialogDocPicker.test.tsx +0 -137
  84. package/src/__tests__/majorApiTypes.test.ts +0 -26
  85. package/src/__tests__/mediaAttachmentFlow.test.ts +0 -110
  86. package/src/__tests__/mediaBinDrop.test.tsx +0 -90
  87. package/src/__tests__/mediaReferences.test.ts +0 -82
  88. package/src/__tests__/narrationSave.test.ts +0 -148
  89. package/src/__tests__/outlinePanel.test.tsx +0 -169
  90. package/src/__tests__/outlineSource.test.ts +0 -217
  91. package/src/__tests__/plainHtmlPreview.test.tsx +0 -125
  92. package/src/__tests__/presentationMode.test.tsx +0 -290
  93. package/src/__tests__/previewControls.test.tsx +0 -540
  94. package/src/__tests__/previewPanelAudioFailure.test.tsx +0 -46
  95. package/src/__tests__/previewPanelPresentation.test.tsx +0 -126
  96. package/src/__tests__/previewPanelTransforms.test.tsx +0 -117
  97. package/src/__tests__/propertiesPanel.test.tsx +0 -69
  98. package/src/__tests__/rawEditorModelIsolation.test.ts +0 -19
  99. package/src/__tests__/recorderFormats.test.ts +0 -146
  100. package/src/__tests__/recorderMediaInsert.test.ts +0 -86
  101. package/src/__tests__/recorderTheme.test.tsx +0 -42
  102. package/src/__tests__/recorderTimingJson.test.ts +0 -41
  103. package/src/__tests__/resolveBlockVisual.test.ts +0 -43
  104. package/src/__tests__/selectionConversions.test.ts +0 -80
  105. package/src/__tests__/statusBar.test.tsx +0 -27
  106. package/src/__tests__/teleprompter.test.tsx +0 -190
  107. package/src/__tests__/templateAnnotationRoundTrip.test.ts +0 -99
  108. package/src/__tests__/templateContentPreview.test.ts +0 -101
  109. package/src/__tests__/templatePickerMetadata.test.ts +0 -32
  110. package/src/__tests__/templatePickerPortal.test.tsx +0 -62
  111. package/src/__tests__/timelineSource.test.ts +0 -134
  112. package/src/__tests__/tiptapBridge.test.ts +0 -566
  113. package/src/__tests__/tiptapBridgeConformance.test.ts +0 -47
  114. package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +0 -109
  115. package/src/__tests__/tiptapImageRoundTrip.test.ts +0 -73
  116. package/src/__tests__/toolbarSelectionConversion.test.tsx +0 -164
  117. package/src/__tests__/tooltip.test.tsx +0 -72
  118. package/src/__tests__/transformStyleId.test.ts +0 -13
  119. package/src/__tests__/transitionCatalog.test.ts +0 -64
  120. package/src/__tests__/useBlockNavigator.test.tsx +0 -67
  121. package/src/__tests__/useImageEditor.test.tsx +0 -226
  122. package/src/__tests__/useJsonEditorTokens.test.ts +0 -59
  123. package/src/__tests__/useMediaRecorder.test.ts +0 -302
  124. package/src/__tests__/useTimelineClock.test.ts +0 -21
  125. package/src/__tests__/versionHistory.test.tsx +0 -197
  126. package/src/__tests__/viewMenuPanel.test.tsx +0 -130
  127. package/src/__tests__/wysiwygImageUpload.test.ts +0 -69
  128. package/src/__tests__/wysiwygTemplateBadgeFocus.test.tsx +0 -56
  129. package/src/asciiDiagram/AsciiDiagramExtension.ts +0 -343
  130. package/src/asciiDiagram/AsciiDiagramWidget.tsx +0 -254
  131. package/src/asciiDiagram/RepairableDiagramExtension.ts +0 -200
  132. package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +0 -145
  133. package/src/asciiDiagram/__tests__/RepairableDiagramExtension.test.ts +0 -159
  134. package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +0 -284
  135. package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +0 -114
  136. package/src/asciiDiagram/__tests__/asciiPaste.test.ts +0 -43
  137. package/src/asciiDiagram/asciiDiagramCommands.ts +0 -174
  138. package/src/asciiDiagram/asciiDiagramData.ts +0 -149
  139. package/src/asciiDiagram/asciiDiagramOps.ts +0 -238
  140. package/src/asciiDiagram/asciiPaste.ts +0 -19
  141. package/src/blockProperties.ts +0 -88
  142. package/src/blockRange.ts +0 -132
  143. package/src/blockSlice.ts +0 -75
  144. package/src/blockTagActivity.ts +0 -233
  145. package/src/buildDocumentPreviewMarkdown.ts +0 -168
  146. package/src/buildPreviewDoc.ts +0 -405
  147. package/src/codeContext/CodeContextSectionView.tsx +0 -124
  148. package/src/codeContext/CodeContextZoneManager.ts +0 -149
  149. package/src/codeContext/CodeContextZones.tsx +0 -121
  150. package/src/codeContext/diffContextSections.ts +0 -38
  151. package/src/codeContext/types.ts +0 -75
  152. package/src/customTemplates/AddBin.tsx +0 -126
  153. package/src/customTemplates/CustomLayoutManager.tsx +0 -234
  154. package/src/customTemplates/CustomTemplateContext.tsx +0 -188
  155. package/src/customTemplates/LayerToolbar.tsx +0 -580
  156. package/src/customTemplates/ShapeGlyph.tsx +0 -47
  157. package/src/customTemplates/TemplateDesigner.tsx +0 -433
  158. package/src/customTemplates/__tests__/library.test.ts +0 -96
  159. package/src/customTemplates/__tests__/normalizePositions.test.ts +0 -109
  160. package/src/customTemplates/__tests__/shapeDefs.test.ts +0 -49
  161. package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +0 -108
  162. package/src/customTemplates/designer.css +0 -732
  163. package/src/customTemplates/index.ts +0 -31
  164. package/src/customTemplates/library.ts +0 -110
  165. package/src/customTemplates/normalizePositions.ts +0 -75
  166. package/src/customTemplates/shapeDefs.ts +0 -131
  167. package/src/customTemplates/thumbnail.tsx +0 -63
  168. package/src/customTemplates/tokenDefs.ts +0 -60
  169. package/src/customTemplates/useDocCustomTemplates.ts +0 -52
  170. package/src/customTemplates/useMemoryLayerAdapter.ts +0 -129
  171. package/src/customThemes/CustomThemeContext.tsx +0 -185
  172. package/src/customThemes/CustomThemeDialog.tsx +0 -316
  173. package/src/customThemes/ImportThemeSection.tsx +0 -178
  174. package/src/customThemes/__tests__/CustomThemeContext.test.tsx +0 -64
  175. package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +0 -47
  176. package/src/customThemes/__tests__/customThemeLibrary.test.ts +0 -60
  177. package/src/customThemes/__tests__/importThemeSection.test.tsx +0 -192
  178. package/src/customThemes/customThemeLibrary.ts +0 -100
  179. package/src/customThemes/index.ts +0 -37
  180. package/src/customThemes/themeControls.tsx +0 -229
  181. package/src/customThemes/themeDraft.ts +0 -288
  182. package/src/customThemes/useDocCustomThemes.ts +0 -49
  183. package/src/detectMarkdown.ts +0 -62
  184. package/src/diagram/DiagramCanvas.tsx +0 -259
  185. package/src/diagram/DiagramMaximizedOverlay.tsx +0 -46
  186. package/src/diagram/diagramCommands.ts +0 -604
  187. package/src/diagram/diagramConstants.ts +0 -17
  188. package/src/diagram/types.ts +0 -35
  189. package/src/editorScrollSync.ts +0 -73
  190. package/src/embeddedMedia.ts +0 -78
  191. package/src/emojiData.ts +0 -1317
  192. package/src/fileKind.ts +0 -158
  193. package/src/frontmatter.ts +0 -29
  194. package/src/frontmatterSettings.ts +0 -23
  195. package/src/headingTransition.ts +0 -291
  196. package/src/hooks/useFileDrop.ts +0 -266
  197. package/src/imageEditor/CanvasSurface.tsx +0 -703
  198. package/src/imageEditor/ImageVersionHistoryDropdown.tsx +0 -396
  199. package/src/imageEditor/LayersPanel.tsx +0 -218
  200. package/src/imageEditor/PropertiesPanel.tsx +0 -474
  201. package/src/imageEditor/Toolbar.tsx +0 -459
  202. package/src/imageEditor/createShapeLayer.ts +0 -280
  203. package/src/imageEditor/icons.tsx +0 -68
  204. package/src/imageEditor/image-editor.css +0 -574
  205. package/src/imageEditor/layers/EditorImageLayer.tsx +0 -45
  206. package/src/imageEditor/layers/EditorShapeLayer.tsx +0 -62
  207. package/src/imageEditor/layers/EditorTextLayer.tsx +0 -45
  208. package/src/imageEditor/layers/SelectionHandles.tsx +0 -86
  209. package/src/imageEditor/state.ts +0 -173
  210. package/src/imageEditor/useImageEditor.ts +0 -385
  211. package/src/imageEditor/useImageEditorTokens.ts +0 -70
  212. package/src/index.ts +0 -470
  213. package/src/jsonEditor/EmbeddedRichTextField.tsx +0 -81
  214. package/src/jsonEditor/JsonEditor.tsx +0 -81
  215. package/src/jsonEditor/JsonEditorContext.tsx +0 -81
  216. package/src/jsonEditor/RenderNode.tsx +0 -87
  217. package/src/jsonEditor/editors.tsx +0 -745
  218. package/src/jsonEditor/index.ts +0 -2
  219. package/src/jsonEditor/json-editor.css +0 -463
  220. package/src/jsonEditor/useJsonEditorTokens.ts +0 -33
  221. package/src/mediaDragMime.ts +0 -32
  222. package/src/mediaEntries.ts +0 -12
  223. package/src/mediaReferences.ts +0 -299
  224. package/src/monaco.ts +0 -35
  225. package/src/monacoWorkers.ts +0 -89
  226. package/src/outlineSource.ts +0 -171
  227. package/src/presentation/PresentationMode.tsx +0 -596
  228. package/src/rawEditorIsolation.ts +0 -18
  229. package/src/recorder/RecorderButton.tsx +0 -80
  230. package/src/recorder/RecorderModal.tsx +0 -706
  231. package/src/recorder/RecorderPanel.tsx +0 -77
  232. package/src/recorder/formats.ts +0 -159
  233. package/src/recorder/hooks/useMediaRecorder.ts +0 -436
  234. package/src/recorder/hooks/useStreamPreview.ts +0 -47
  235. package/src/recorder/insertMediaBlock.ts +0 -30
  236. package/src/recorder/sources/cameraStream.ts +0 -32
  237. package/src/recorder/sources/micStream.ts +0 -25
  238. package/src/recorder/sources/screenStream.ts +0 -162
  239. package/src/recorder/timingJson.ts +0 -66
  240. package/src/resolveBlockVisual.ts +0 -39
  241. package/src/scene/Scene.tsx +0 -616
  242. package/src/scene/SceneBlockExtension.ts +0 -205
  243. package/src/scene/SceneBlockToolbar.tsx +0 -201
  244. package/src/scene/SceneBlockWidget.tsx +0 -441
  245. package/src/scene/ScenePropsBar.tsx +0 -85
  246. package/src/scene/SceneSelection.tsx +0 -111
  247. package/src/scene/SceneSideToolbar.tsx +0 -89
  248. package/src/scene/SceneViewport.tsx +0 -106
  249. package/src/scene/ShapePalette.tsx +0 -196
  250. package/src/scene/__tests__/DiagramAdapter.test.ts +0 -142
  251. package/src/scene/__tests__/SceneBlockExtension.test.ts +0 -33
  252. package/src/scene/__tests__/bezierEdit.test.ts +0 -85
  253. package/src/scene/__tests__/blockLayers.test.ts +0 -57
  254. package/src/scene/__tests__/sceneIsolation.test.tsx +0 -119
  255. package/src/scene/__tests__/shapeLayers.test.ts +0 -106
  256. package/src/scene/__tests__/useSceneHitTest.test.ts +0 -90
  257. package/src/scene/__tests__/useScenePanZoom.test.ts +0 -103
  258. package/src/scene/adapters/DiagramAdapter.ts +0 -79
  259. package/src/scene/adapters/DrawingAdapter.ts +0 -420
  260. package/src/scene/adapters/LayoutAdapter.ts +0 -313
  261. package/src/scene/adapters/blockLayers.ts +0 -159
  262. package/src/scene/commands/SceneCommand.ts +0 -84
  263. package/src/scene/commands/drawingCommands.ts +0 -318
  264. package/src/scene/commands/layoutCommands.ts +0 -301
  265. package/src/scene/hooks/useSceneHitTest.ts +0 -105
  266. package/src/scene/hooks/useScenePanZoom.ts +0 -147
  267. package/src/scene/hooks/useSceneSelection.ts +0 -62
  268. package/src/scene/index.ts +0 -90
  269. package/src/scene/layers/DiagramEdges.tsx +0 -142
  270. package/src/scene/layers/edgeGeometry.ts +0 -104
  271. package/src/scene/layers/nodeCard.tsx +0 -164
  272. package/src/scene/layers/renderLayer.tsx +0 -70
  273. package/src/scene/layers/shapeLayers.ts +0 -201
  274. package/src/scene/paths/bezierEdit.ts +0 -208
  275. package/src/scene/scene.css +0 -792
  276. package/src/scene/text/SceneTextOverlay.tsx +0 -161
  277. package/src/scene/text/sceneTextChannel.ts +0 -46
  278. package/src/scene/text/sceneTextConfig.ts +0 -31
  279. package/src/scene/text/sceneTiptap.ts +0 -36
  280. package/src/scene/text/useSceneTextEditing.ts +0 -39
  281. package/src/scene/tools/ConnectTool.ts +0 -209
  282. package/src/scene/tools/DrawingConnectTool.ts +0 -231
  283. package/src/scene/tools/PathTool.ts +0 -158
  284. package/src/scene/tools/PlaceTool.ts +0 -47
  285. package/src/scene/tools/SceneTool.ts +0 -85
  286. package/src/scene/tools/SelectTool.ts +0 -300
  287. package/src/scene/tools/ShapeTool.ts +0 -143
  288. package/src/scene/tools/TextTool.ts +0 -72
  289. package/src/scene/tools/TokenTool.ts +0 -95
  290. package/src/scene/tools/createDrawShapeTool.ts +0 -82
  291. package/src/selectionConversions.ts +0 -155
  292. package/src/styles/ascii-diagram.css +0 -79
  293. package/src/styles/ascii-timeline.css +0 -596
  294. package/src/styles/code-context.css +0 -155
  295. package/src/styles/diagram.css +0 -183
  296. package/src/styles/editor.css +0 -6811
  297. package/src/styles/folder-view.css +0 -210
  298. package/src/styles/image-edit-affordance.css +0 -201
  299. package/src/styles/index.css +0 -18
  300. package/src/styles/tree-view.css +0 -189
  301. package/src/teleprompter/TeleprompterControls.tsx +0 -218
  302. package/src/teleprompter/TeleprompterSelfView.tsx +0 -22
  303. package/src/teleprompter/TeleprompterSurface.tsx +0 -267
  304. package/src/teleprompter/TeleprompterView.tsx +0 -338
  305. package/src/teleprompter/canvasRenderer.ts +0 -161
  306. package/src/teleprompter/floatingWindow.ts +0 -352
  307. package/src/teleprompter/index.ts +0 -61
  308. package/src/teleprompter/pcmWorklet.ts +0 -62
  309. package/src/teleprompter/recording/insertPreamble.ts +0 -80
  310. package/src/teleprompter/recording/narrationSave.ts +0 -134
  311. package/src/teleprompter/recording/useNarrationRecorder.ts +0 -367
  312. package/src/teleprompter/scrollModel.ts +0 -85
  313. package/src/teleprompter/teleprompterTheme.ts +0 -303
  314. package/src/teleprompter/types.ts +0 -38
  315. package/src/teleprompter/useFloatingWindow.ts +0 -74
  316. package/src/teleprompter/useMicAnalysis.ts +0 -211
  317. package/src/teleprompter/useTeleprompter.ts +0 -421
  318. package/src/templateContentPreviewResolver.ts +0 -360
  319. package/src/timeline/TimelineEditorWidget.tsx +0 -861
  320. package/src/timeline/TimelineViewExtension.ts +0 -252
  321. package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +0 -414
  322. package/src/timeline/__tests__/TimelineViewExtension.test.ts +0 -163
  323. package/src/timeline/__tests__/timelineCommands.test.ts +0 -359
  324. package/src/timeline/__tests__/timelineOps.test.ts +0 -277
  325. package/src/timeline/__tests__/timelinePaste.test.ts +0 -34
  326. package/src/timeline/timelineCommands.ts +0 -421
  327. package/src/timeline/timelineData.ts +0 -54
  328. package/src/timeline/timelineOps.ts +0 -381
  329. package/src/timeline/timelinePaste.ts +0 -8
  330. package/src/timelineSource.ts +0 -244
  331. package/src/tiptap/TiptapAudio.tsx +0 -86
  332. package/src/tiptap/TiptapVideo.tsx +0 -119
  333. package/src/tiptap/useResolvedMediaSrc.ts +0 -47
  334. package/src/tiptapBridge.ts +0 -986
  335. package/src/tooltipPlacement.ts +0 -13
  336. package/src/transformStyleId.ts +0 -17
  337. package/src/transitionCatalog.ts +0 -159
  338. package/src/treeview/TreeOutlineWidget.tsx +0 -390
  339. package/src/treeview/TreeViewExtension.ts +0 -250
  340. package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +0 -156
  341. package/src/treeview/__tests__/TreeViewExtension.test.ts +0 -122
  342. package/src/treeview/__tests__/treeOps.test.ts +0 -177
  343. package/src/treeview/__tests__/treePaste.test.ts +0 -40
  344. package/src/treeview/__tests__/treeViewCommands.test.ts +0 -179
  345. package/src/treeview/treeOps.ts +0 -231
  346. package/src/treeview/treePaste.ts +0 -13
  347. package/src/treeview/treeViewCommands.ts +0 -99
  348. package/src/treeview/treeViewData.ts +0 -41
  349. package/src/types/monaco-shims.d.ts +0 -10
  350. package/src/useBlockNavigator.ts +0 -153
  351. package/src/useHeadingLayout.ts +0 -294
  352. package/src/useMonacoLoader.ts +0 -82
  353. package/src/useTimelineClock.ts +0 -76
  354. package/src/utils/collectInlineFontAwesomeCss.ts +0 -69
  355. package/src/utils/dropUtils.ts +0 -136
  356. package/src/utils/normalizeMalformedAssetUrl.ts +0 -22
  357. package/src/wysiwygImageUpload.ts +0 -113
@@ -1,405 +0,0 @@
1
- /**
2
- * buildPreviewDoc — Converts a markdown-derived Doc into a player-ready Doc
3
- * with TemplateBlock slides and interleaved images.
4
- *
5
- * Shared between PreviewPanel (live preview) and export flows (HTML/video).
6
- *
7
- * Pipeline:
8
- * 1. Flatten hierarchical blocks into a linear slide sequence
9
- * 2. Convert each block into a TemplateBlock-compatible object
10
- * 3. Interleave images as standalone imageWithCaption slides
11
- * 4. Synthesize a dummy audio segment for timer-based playback
12
- */
13
-
14
- import { deriveTemplateInputs, flattenRenderableBlocks, hasTemplate } from '@bendyline/squisq/doc';
15
- import { extractPlainText, KNOWN_BLOCK_META_KEYS } from '@bendyline/squisq/markdown';
16
- import { getChildren } from '@bendyline/squisq/markdown';
17
- import { iconMarker } from '@bendyline/squisq/icon-marker';
18
- import type { IconFamily } from '@bendyline/squisq/icons';
19
- import type { Block, Doc } from '@bendyline/squisq/schemas';
20
- import type { MarkdownBlockNode, MarkdownList, MarkdownNode } from '@bendyline/squisq/markdown';
21
-
22
- // ── Helpers ────────────────────────────────────────────────────────
23
-
24
- /**
25
- * Like `extractPlainText`, but preserves inline icons as encoded markers so
26
- * template text can render them as glyphs downstream (see `iconMarker` and
27
- * `TextLayer`). Mirrors `extractPlainText`'s value/child/list handling; the
28
- * only addition is the `inlineIcon` interception.
29
- */
30
- function extractRichText(node: MarkdownNode): string {
31
- if (node.type === 'inlineIcon') {
32
- const icon = node as unknown as { family: IconFamily; name: string };
33
- return iconMarker(icon.family, icon.name);
34
- }
35
- if ('value' in node && typeof (node as { value?: unknown }).value === 'string') {
36
- return (node as { value: string }).value;
37
- }
38
- const children = getChildren(node);
39
- const separator = node.type === 'list' || node.type === 'listItem' ? '\n' : '';
40
- return children.map(extractRichText).join(separator);
41
- }
42
-
43
- function extractBodyText(contents: MarkdownBlockNode[] | undefined): string {
44
- if (!contents || contents.length === 0) return '';
45
- const parts: string[] = [];
46
- for (const node of contents) {
47
- parts.push(extractRichText(node));
48
- }
49
- return parts.join('\n').trim();
50
- }
51
-
52
- interface ExtractedImage {
53
- src: string;
54
- alt: string;
55
- /** Explicit width from `<img width>` (markdown shorthand has none). */
56
- width?: number;
57
- /** Explicit height from `<img height>`. */
58
- height?: number;
59
- }
60
-
61
- function parseDim(raw: string | undefined): number | undefined {
62
- if (raw === undefined) return undefined;
63
- const n = parseFloat(raw);
64
- return Number.isFinite(n) && n > 0 ? n : undefined;
65
- }
66
-
67
- function extractBlockImages(contents: MarkdownBlockNode[] | undefined): ExtractedImage[] {
68
- if (!contents || contents.length === 0) return [];
69
- const images: ExtractedImage[] = [];
70
-
71
- function walkHtml(node: unknown): void {
72
- if (!node || typeof node !== 'object') return;
73
- const n = node as Record<string, unknown>;
74
- if (n.type === 'htmlElement' && (n.tagName as string).toLowerCase() === 'img') {
75
- const attrs = n.attributes as Record<string, string> | undefined;
76
- const src = attrs?.src;
77
- if (typeof src === 'string' && src) {
78
- images.push({
79
- src,
80
- alt: typeof attrs?.alt === 'string' ? attrs.alt : '',
81
- width: parseDim(attrs?.width),
82
- height: parseDim(attrs?.height),
83
- });
84
- }
85
- }
86
- if (Array.isArray(n.children)) {
87
- for (const child of n.children) walkHtml(child);
88
- }
89
- }
90
-
91
- function walk(node: MarkdownNode): void {
92
- if ('type' in node && node.type === 'image' && 'url' in node) {
93
- const img = node as { url: string; alt?: string };
94
- if (img.url) {
95
- images.push({ src: img.url, alt: img.alt ?? '' });
96
- }
97
- }
98
- // Resized images round-trip as raw HTML. Pick them up so feature
99
- // and slideshow previews show the actual image rather than a blank
100
- // placeholder.
101
- if ('type' in node && (node.type === 'htmlBlock' || node.type === 'htmlInline')) {
102
- const html = node as unknown as { htmlChildren?: unknown[] };
103
- for (const child of html.htmlChildren ?? []) walkHtml(child);
104
- }
105
- for (const child of getChildren(node)) {
106
- walk(child);
107
- }
108
- }
109
-
110
- for (const node of contents) {
111
- walk(node);
112
- }
113
- return images;
114
- }
115
-
116
- function collectAllDocImages(blocks: Block[]): Array<{ src: string; alt: string }> {
117
- const seen = new Set<string>();
118
- const images: Array<{ src: string; alt: string }> = [];
119
-
120
- function walkBlocks(blockList: Block[]): void {
121
- for (const block of blockList) {
122
- for (const img of extractBlockImages(block.contents)) {
123
- if (!seen.has(img.src)) {
124
- seen.add(img.src);
125
- images.push(img);
126
- }
127
- }
128
- if (block.children) {
129
- walkBlocks(block.children);
130
- }
131
- }
132
- }
133
-
134
- walkBlocks(blocks);
135
- return images;
136
- }
137
-
138
- function extractListItems(contents: MarkdownBlockNode[] | undefined): string[] {
139
- if (!contents) return [];
140
- const items: string[] = [];
141
- for (const node of contents) {
142
- if (node.type === 'list') {
143
- for (const item of (node as MarkdownList).children) {
144
- const text = extractPlainText(item).trim();
145
- if (text) items.push(text);
146
- }
147
- }
148
- }
149
- return items;
150
- }
151
-
152
- function getTemplateDefaults(
153
- templateName: string,
154
- headingText: string,
155
- block: Block,
156
- ): Record<string, unknown> {
157
- const body = extractBodyText(block.contents);
158
-
159
- switch (templateName) {
160
- case 'statHighlight':
161
- return (
162
- deriveTemplateInputs(templateName, headingText, block.contents) ?? {
163
- stat: headingText,
164
- description: body || headingText,
165
- }
166
- );
167
- case 'quote':
168
- case 'fullBleedQuote':
169
- case 'pullQuote':
170
- return { quote: body || headingText };
171
- case 'factCard':
172
- return { fact: headingText, explanation: body || headingText };
173
- case 'comparisonBar':
174
- return { leftLabel: 'A', leftValue: 60, rightLabel: 'B', rightValue: 40 };
175
- case 'list': {
176
- const items = extractListItems(block.contents);
177
- return { items: items.length > 0 ? items : ['Item 1', 'Item 2', 'Item 3'] };
178
- }
179
- case 'definitionCard':
180
- return { term: headingText, definition: body || headingText };
181
- case 'dateEvent':
182
- return { date: headingText, description: body || headingText };
183
- case 'leftFeature':
184
- case 'rightFeature': {
185
- // Feature blocks need imageSrc from body content and use the
186
- // heading text as the visible title alongside the body paragraph.
187
- const images = extractBlockImages(block.contents);
188
- const img = images[0];
189
- return {
190
- imageSrc: img?.src ?? '',
191
- imageAlt: img?.alt || headingText,
192
- imageWidth: img?.width,
193
- imageHeight: img?.height,
194
- title: headingText,
195
- body: body || headingText,
196
- };
197
- }
198
- default:
199
- return {};
200
- }
201
- }
202
-
203
- function blockToSlide(
204
- block: Block,
205
- index: number,
206
- knownTemplates?: ReadonlySet<string>,
207
- ): Record<string, unknown> {
208
- const headingText = block.sourceHeading
209
- ? extractPlainText(block.sourceHeading)
210
- : block.title || block.id || `Slide ${index + 1}`;
211
-
212
- const requestedTemplate = block.template || 'sectionHeader';
213
- // A template is recognized if it's a built-in OR a user-defined
214
- // template carried in the doc's `customTemplates` set. Without this,
215
- // an annotated `{[hero]}` heading would silently fall back to
216
- // `sectionHeader` because `hasTemplate` only knows built-ins.
217
- const isCustomTemplate = knownTemplates?.has(requestedTemplate) ?? false;
218
- const recognized = hasTemplate(requestedTemplate) || isCustomTemplate;
219
- const template = recognized ? requestedTemplate : 'sectionHeader';
220
- const defaults = getTemplateDefaults(template, headingText, block);
221
-
222
- const {
223
- id: _id,
224
- startTime: _st,
225
- duration: _d,
226
- audioSegment: _as,
227
- layers: _l,
228
- transition: _tr,
229
- template: _t,
230
- title: _ti,
231
- children: _c,
232
- contents: _co,
233
- sourceHeading: _sh,
234
- templateOverrides: _to,
235
- templateData: _td,
236
- ...extraFields
237
- } = block as unknown as Record<string, unknown>;
238
-
239
- return {
240
- id: block.id,
241
- template,
242
- duration: block.duration,
243
- audioSegment: 0,
244
- // Respect the block's authored transition (set via the toolbar / on-canvas
245
- // properties palette → `{…}` block attrs). Only fall back to a default fade
246
- // for blocks past the first when the author hasn't chosen one; the first
247
- // block has no previous slide to transition in from.
248
- transition: block.transition ?? (index > 0 ? { type: 'fade', duration: 0.5 } : undefined),
249
- title: headingText,
250
- // Custom templates need access to the source block's body content
251
- // + children so their token resolver (`{content}`, `{children}`,
252
- // `{image:N}`) substitutes against the user's prose, not just the
253
- // heading. Built-in templates don't read these fields and risk
254
- // surprising overlap with their typed inputs, so we only attach
255
- // them when the slide actually maps to a custom template.
256
- ...(isCustomTemplate && block.contents ? { contents: block.contents } : {}),
257
- ...(isCustomTemplate && block.children ? { children: block.children } : {}),
258
- ...defaults,
259
- ...extraFields,
260
- // Structured body data (```json data fences, GFM tables for dataTable)
261
- // carries typed values; `{[…]}` string overrides win last so an explicit
262
- // annotation param can still pin any field.
263
- //
264
- // Block-meta keys (transition, startTime, duration, …) are the exception:
265
- // they were already coerced to typed block fields above (e.g.
266
- // `block.transition` → `{ type, duration, direction }`). Their raw string
267
- // form also rides along in `templateData`/`templateOverrides` because the
268
- // author wrote them inside `{[…]}`; left un-stripped, that string would
269
- // spread back over the typed value here and clobber it — turning
270
- // `transition=vortex` into the string `"vortex"`, which the player can't
271
- // animate. Omit them from the content spreads so the typed fields win.
272
- ...omitBlockMeta(block.templateData),
273
- ...omitBlockMeta(block.templateOverrides),
274
- };
275
- }
276
-
277
- /** Keys coerced to typed block fields; must not be re-applied as raw strings. */
278
- const BLOCK_META_KEYS: ReadonlySet<string> = new Set(Object.keys(KNOWN_BLOCK_META_KEYS));
279
-
280
- /** Copy of a template-param record with block-meta keys removed. */
281
- function omitBlockMeta(
282
- data: Record<string, unknown> | undefined,
283
- ): Record<string, unknown> | undefined {
284
- if (!data) return data;
285
- let hit = false;
286
- const out: Record<string, unknown> = {};
287
- for (const key of Object.keys(data)) {
288
- if (BLOCK_META_KEYS.has(key)) {
289
- hit = true;
290
- continue;
291
- }
292
- out[key] = data[key];
293
- }
294
- return hit ? out : data;
295
- }
296
-
297
- const IMAGE_MOTIONS: Array<'zoomIn' | 'zoomOut' | 'panLeft' | 'panRight'> = [
298
- 'zoomIn',
299
- 'zoomOut',
300
- 'panLeft',
301
- 'panRight',
302
- ];
303
-
304
- // ── Public API ─────────────────────────────────────────────────────
305
-
306
- /**
307
- * Build a player-ready Doc from a markdown-derived Doc.
308
- *
309
- * Flattens hierarchical blocks, converts each to a TemplateBlock-compatible
310
- * slide, interleaves images, recalculates timing, and adds a synthetic
311
- * audio segment.
312
- */
313
- export function buildPreviewDoc(doc: Doc): Doc {
314
- // Container templates (`diagram`, `drawing`) render their children as
315
- // nodes/shapes, so those children must not also become preview slides.
316
- const flat = flattenRenderableBlocks(doc.blocks);
317
- const allImages = collectAllDocImages(doc.blocks);
318
- const usedImageSrcs = new Set<string>();
319
- // Names of user-defined templates carried by the doc — passed into
320
- // `blockToSlide` so heading annotations like `{[hero]}` aren't
321
- // silently downgraded to `sectionHeader` when the template doesn't
322
- // exist in the built-in registry.
323
- const knownTemplates = doc.customTemplates
324
- ? new Set(doc.customTemplates.map((d) => d.name))
325
- : undefined;
326
-
327
- const slides: Record<string, unknown>[] = [];
328
- let motionIndex = 0;
329
-
330
- for (let i = 0; i < flat.length; i++) {
331
- const block = flat[i];
332
- const blockImages = extractBlockImages(block.contents);
333
- const slide = blockToSlide(block, i, knownTemplates);
334
-
335
- if (blockImages.length > 0 && slide.template === 'sectionHeader') {
336
- const img = blockImages[0];
337
- usedImageSrcs.add(img.src);
338
- slide.template = 'imageWithCaption';
339
- slide.imageSrc = img.src;
340
- slide.imageAlt = img.alt;
341
- slide.caption = slide.title as string;
342
- slide.captionPosition = 'bottom';
343
- slide.ambientMotion = IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length];
344
- } else if (blockImages.length > 0) {
345
- const img = blockImages[0];
346
- usedImageSrcs.add(img.src);
347
- if (!slide.accentImage) {
348
- slide.accentImage = {
349
- src: img.src,
350
- alt: img.alt,
351
- position: 'left-strip',
352
- ambientMotion: IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length],
353
- };
354
- }
355
- }
356
-
357
- slides.push(slide);
358
- }
359
-
360
- // Interleave unused images
361
- const unusedImages = allImages.filter((img) => !usedImageSrcs.has(img.src));
362
- if (unusedImages.length > 0 && slides.length > 0) {
363
- const interval = Math.max(2, Math.floor(slides.length / (unusedImages.length + 1)));
364
- let insertOffset = 0;
365
- for (let imgIdx = 0; imgIdx < unusedImages.length; imgIdx++) {
366
- const insertAt = Math.min((imgIdx + 1) * interval + insertOffset, slides.length);
367
- const img = unusedImages[imgIdx];
368
- slides.splice(insertAt, 0, {
369
- id: `img-interleave-${imgIdx}`,
370
- template: 'imageWithCaption',
371
- duration: 5,
372
- audioSegment: 0,
373
- imageSrc: img.src,
374
- imageAlt: img.alt,
375
- ambientMotion: IMAGE_MOTIONS[motionIndex++ % IMAGE_MOTIONS.length],
376
- transition: { type: 'fade', duration: 0.5 },
377
- });
378
- insertOffset++;
379
- }
380
- }
381
-
382
- // Recalculate timing
383
- let t = 0;
384
- for (const slide of slides) {
385
- slide.startTime = t;
386
- t += slide.duration as number;
387
- }
388
-
389
- const audio =
390
- doc.audio?.segments?.length > 0
391
- ? doc.audio
392
- : {
393
- segments: t > 0 ? [{ src: '', name: 'preview', duration: t, startTime: 0 }] : [],
394
- };
395
-
396
- return {
397
- // Preserve document-wide capabilities (custom themes, persistent layers,
398
- // scheduled media, frontmatter, captions, and future schema fields).
399
- // Preview preparation should replace only the slide/timing projection.
400
- ...doc,
401
- duration: t,
402
- blocks: slides as unknown as Block[],
403
- audio,
404
- };
405
- }
@@ -1,124 +0,0 @@
1
- import { parseMarkdown } from '@bendyline/squisq/markdown';
2
- import { MarkdownRenderer } from '@bendyline/squisq-react';
3
- import { useCallback, useEffect, useMemo, useRef } from 'react';
4
- import type { CodeContext, CodeContextSection } from './types';
5
-
6
- /**
7
- * One context section rendered inside a Monaco view zone: a single-line
8
- * disclosure strip, plus the full markdown body while expanded. The body is
9
- * rendered lazily — a file with 100 collapsed sections parses 100 one-liners,
10
- * nothing more.
11
- */
12
- export interface CodeContextSectionViewProps {
13
- section: Omit<CodeContextSection, 'line'>;
14
- expanded: boolean;
15
- onToggle: (id: string) => void;
16
- linkSchemes?: readonly string[] | undefined;
17
- onLinkClick?: CodeContext['onLinkClick'] | undefined;
18
- /** Native `#L<n>` handling: reveal that line in the editor. */
19
- onRevealLine: (line: number) => void;
20
- /** Reports the rendered content height so the zone can be resized to fit. */
21
- onMeasure: (id: string, px: number) => void;
22
- }
23
-
24
- export function CodeContextSectionView({
25
- section,
26
- expanded,
27
- onToggle,
28
- linkSchemes,
29
- onLinkClick,
30
- onRevealLine,
31
- onMeasure,
32
- }: CodeContextSectionViewProps) {
33
- const rootRef = useRef<HTMLDivElement | null>(null);
34
-
35
- const stripNodes = useMemo(
36
- () => parseMarkdown(section.summaryMarkdown).children,
37
- [section.summaryMarkdown],
38
- );
39
- const bodyNodes = useMemo(
40
- () => (expanded && section.markdown ? parseMarkdown(section.markdown).children : null),
41
- [expanded, section.markdown],
42
- );
43
-
44
- // Height feedback loop: report the content's real height whenever it
45
- // changes. Monaco display:none's offscreen zones and ResizeObserver reports
46
- // 0×0 for those — ignore zeros so scrolled-away zones keep their height.
47
- useEffect(() => {
48
- const el = rootRef.current;
49
- if (!el || typeof ResizeObserver === 'undefined') return;
50
- const report = () => {
51
- const h = el.offsetHeight;
52
- if (h > 0) onMeasure(section.id, h);
53
- };
54
- report();
55
- const ro = new ResizeObserver(report);
56
- ro.observe(el);
57
- return () => ro.disconnect();
58
- }, [section.id, onMeasure]);
59
-
60
- // Delegated link interception. `#L<n>` reveals natively; everything else
61
- // goes to the host callback (returning false opts back into default
62
- // navigation).
63
- const handleClick = useCallback(
64
- (e: React.MouseEvent) => {
65
- const anchor = (e.target as HTMLElement).closest?.('a');
66
- if (!anchor) return;
67
- const href = anchor.getAttribute('href') ?? '';
68
- const lineMatch = /^#L(\d+)$/.exec(href);
69
- if (lineMatch) {
70
- e.preventDefault();
71
- e.stopPropagation();
72
- onRevealLine(Number(lineMatch[1]));
73
- return;
74
- }
75
- if (onLinkClick) {
76
- const handled = onLinkClick(href, { sectionId: section.id });
77
- if (handled !== false) {
78
- e.preventDefault();
79
- e.stopPropagation();
80
- }
81
- }
82
- },
83
- [onLinkClick, onRevealLine, section.id],
84
- );
85
-
86
- // Keep Monaco's container-level mousedown handler from hijacking clicks
87
- // and text selection inside the section.
88
- const stopMouseDown = useCallback((e: React.MouseEvent) => {
89
- e.stopPropagation();
90
- }, []);
91
-
92
- return (
93
- // Click handler is delegated anchor interception only; keyboard users
94
- // reach links natively and the strip itself is a real <button>.
95
- <div
96
- ref={rootRef}
97
- className={`squisq-ccx-section${expanded ? ' squisq-ccx-section--expanded' : ''}`}
98
- onClick={handleClick}
99
- onMouseDown={stopMouseDown}
100
- >
101
- <button
102
- type="button"
103
- className="squisq-ccx-strip"
104
- aria-expanded={expanded}
105
- onClick={() => onToggle(section.id)}
106
- >
107
- <span className="squisq-ccx-chevron" aria-hidden="true">
108
- {expanded ? '▾' : '▸'}
109
- </span>
110
- <span className="squisq-ccx-strip-text">
111
- <MarkdownRenderer nodes={stripNodes} {...(linkSchemes ? { linkSchemes } : {})} />
112
- </span>
113
- </button>
114
- {expanded &&
115
- (bodyNodes ? (
116
- <div className="squisq-ccx-body">
117
- <MarkdownRenderer nodes={bodyNodes} {...(linkSchemes ? { linkSchemes } : {})} />
118
- </div>
119
- ) : (
120
- <div className="squisq-ccx-body squisq-ccx-body--loading">Loading…</div>
121
- ))}
122
- </div>
123
- );
124
- }
@@ -1,149 +0,0 @@
1
- import type { editor as MonacoEditorNs } from 'monaco-editor';
2
- import { type ZoneSpec, diffContextSections } from './diffContextSections';
3
-
4
- type MonacoEditor = MonacoEditorNs.IStandaloneCodeEditor;
5
-
6
- /**
7
- * Owns the Monaco view zones behind the code-context sections. React-free —
8
- * the React layer portals content into the zone dom nodes it hands out and
9
- * feeds measured heights back via {@link setHeight}.
10
- *
11
- * Zone mechanics this leans on (monaco 0.50):
12
- * - `changeViewZones` batches adds/removes/layouts into one whitespace
13
- * re-layout.
14
- * - `layoutZone(id)` re-reads the zone delegate, so mutating our own
15
- * delegate's `afterLineNumber`/`heightInPx` then calling layoutZone both
16
- * moves and resizes without remove/re-add churn (expanded DOM survives).
17
- * - Zone dom nodes live in `.view-zones` (absolutely positioned, offscreen
18
- * ones display:none'd by Monaco). They sit BELOW `.view-lines` in hit-test
19
- * order, so the stylesheet raises `.squisq-ccx-zone` with a z-index and
20
- * `pointer-events: auto` to make section content clickable.
21
- * - Model swaps invalidate zone ids; the owner listens to onDidChangeModel
22
- * and rebuilds.
23
- */
24
- export class CodeContextZoneManager {
25
- private readonly editor: MonacoEditor;
26
- private entries = new Map<
27
- string,
28
- {
29
- zoneId: string;
30
- delegate: MonacoEditorNs.IViewZone;
31
- domNode: HTMLDivElement;
32
- spec: ZoneSpec;
33
- }
34
- >();
35
- private listeners = new Set<() => void>();
36
- private modelListener: { dispose(): void } | null = null;
37
- private disposed = false;
38
-
39
- /** Initial height estimate for a strip; the ResizeObserver corrects it. */
40
- static readonly INITIAL_HEIGHT_PX = 28;
41
-
42
- constructor(editor: MonacoEditor) {
43
- this.editor = editor;
44
- // A model swap (file change / external reset) invalidates every zone id.
45
- // Drop our bookkeeping; the next sync() recreates zones against the new
46
- // model.
47
- this.modelListener = editor.onDidChangeModel(() => {
48
- this.entries.clear();
49
- this.emit();
50
- });
51
- }
52
-
53
- /** Reconcile the live zones against `specs` in one changeViewZones batch. */
54
- sync(specs: ZoneSpec[]): void {
55
- if (this.disposed) return;
56
- const prev = [...this.entries.values()].map((e) => e.spec);
57
- const { add, remove, move } = diffContextSections(prev, specs);
58
- if (add.length === 0 && remove.length === 0 && move.length === 0) return;
59
-
60
- this.editor.changeViewZones((accessor) => {
61
- for (const id of remove) {
62
- const entry = this.entries.get(id);
63
- if (!entry) continue;
64
- accessor.removeZone(entry.zoneId);
65
- this.entries.delete(id);
66
- }
67
- for (const spec of move) {
68
- const entry = this.entries.get(spec.id);
69
- if (!entry) continue;
70
- entry.spec = spec;
71
- entry.delegate.afterLineNumber = Math.max(spec.line - 1, 0);
72
- setOrdinal(entry.delegate, spec.ordinal);
73
- accessor.layoutZone(entry.zoneId);
74
- }
75
- for (const spec of add) {
76
- const domNode = document.createElement('div');
77
- domNode.className = 'squisq-ccx-zone';
78
- domNode.dataset.sectionId = spec.id;
79
- const delegate: MonacoEditorNs.IViewZone = {
80
- afterLineNumber: Math.max(spec.line - 1, 0),
81
- heightInPx: CodeContextZoneManager.INITIAL_HEIGHT_PX,
82
- domNode,
83
- suppressMouseDown: true,
84
- };
85
- setOrdinal(delegate, spec.ordinal);
86
- const zoneId = accessor.addZone(delegate);
87
- this.entries.set(spec.id, { zoneId, delegate, domNode, spec });
88
- }
89
- });
90
- this.emit();
91
- }
92
-
93
- /** Measured-content feedback: resize the zone to fit its rendered DOM. */
94
- setHeight(id: string, px: number): void {
95
- if (this.disposed) return;
96
- const entry = this.entries.get(id);
97
- if (!entry) return;
98
- const height = Math.max(Math.ceil(px), 1);
99
- if (Math.abs((entry.delegate.heightInPx ?? 0) - height) < 1) return;
100
- entry.delegate.heightInPx = height;
101
- this.editor.changeViewZones((accessor) => {
102
- accessor.layoutZone(entry.zoneId);
103
- });
104
- }
105
-
106
- getDomNode(id: string): HTMLDivElement | undefined {
107
- return this.entries.get(id)?.domNode;
108
- }
109
-
110
- liveIds(): string[] {
111
- return [...this.entries.keys()];
112
- }
113
-
114
- /** Fires after sync/model-change so the React layer rebuilds its portals. */
115
- onDidChangeZones(cb: () => void): () => void {
116
- this.listeners.add(cb);
117
- return () => this.listeners.delete(cb);
118
- }
119
-
120
- dispose(): void {
121
- if (this.disposed) return;
122
- this.disposed = true;
123
- this.modelListener?.dispose();
124
- this.modelListener = null;
125
- if (this.entries.size > 0) {
126
- try {
127
- this.editor.changeViewZones((accessor) => {
128
- for (const entry of this.entries.values()) accessor.removeZone(entry.zoneId);
129
- });
130
- } catch {
131
- // Editor already disposed — its zones died with it.
132
- }
133
- }
134
- this.entries.clear();
135
- this.listeners.clear();
136
- }
137
-
138
- private emit(): void {
139
- for (const cb of [...this.listeners]) cb();
140
- }
141
- }
142
-
143
- /**
144
- * `ordinal` orders zones sharing an afterLineNumber (monaco 0.50 supports it
145
- * but the public IViewZone typing lags behind) — hence the structural write.
146
- */
147
- function setOrdinal(zone: MonacoEditorNs.IViewZone, ordinal: number): void {
148
- (zone as MonacoEditorNs.IViewZone & { ordinal?: number }).ordinal = ordinal;
149
- }