@bendyline/squisq-editor-react 1.3.0 → 1.5.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 (461) hide show
  1. package/dist/DocumentSettingsDialog.d.ts +26 -0
  2. package/dist/DocumentSettingsDialog.d.ts.map +1 -0
  3. package/dist/DocumentSettingsDialog.js +115 -0
  4. package/dist/DocumentSettingsDialog.js.map +1 -0
  5. package/dist/EditorContext.d.ts +248 -4
  6. package/dist/EditorContext.d.ts.map +1 -1
  7. package/dist/EditorContext.js +248 -10
  8. package/dist/EditorContext.js.map +1 -1
  9. package/dist/EditorShell.d.ts +184 -4
  10. package/dist/EditorShell.d.ts.map +1 -1
  11. package/dist/EditorShell.js +184 -12
  12. package/dist/EditorShell.js.map +1 -1
  13. package/dist/EmojiPicker.d.ts +50 -0
  14. package/dist/EmojiPicker.d.ts.map +1 -0
  15. package/dist/EmojiPicker.js +182 -0
  16. package/dist/EmojiPicker.js.map +1 -0
  17. package/dist/ImageEditor.d.ts +68 -0
  18. package/dist/ImageEditor.d.ts.map +1 -0
  19. package/dist/ImageEditor.js +166 -0
  20. package/dist/ImageEditor.js.map +1 -0
  21. package/dist/ImageNodeView.d.ts +13 -1
  22. package/dist/ImageNodeView.d.ts.map +1 -1
  23. package/dist/ImageNodeView.js +172 -19
  24. package/dist/ImageNodeView.js.map +1 -1
  25. package/dist/ImageViewer.d.ts +26 -0
  26. package/dist/ImageViewer.d.ts.map +1 -0
  27. package/dist/ImageViewer.js +119 -0
  28. package/dist/ImageViewer.js.map +1 -0
  29. package/dist/InlineIcon.d.ts +17 -0
  30. package/dist/InlineIcon.d.ts.map +1 -0
  31. package/dist/InlineIcon.js +72 -0
  32. package/dist/InlineIcon.js.map +1 -0
  33. package/dist/InlinePreviewGutter.d.ts +52 -0
  34. package/dist/InlinePreviewGutter.d.ts.map +1 -0
  35. package/dist/InlinePreviewGutter.js +397 -0
  36. package/dist/InlinePreviewGutter.js.map +1 -0
  37. package/dist/LinkDialog.d.ts +43 -0
  38. package/dist/LinkDialog.d.ts.map +1 -0
  39. package/dist/LinkDialog.js +102 -0
  40. package/dist/LinkDialog.js.map +1 -0
  41. package/dist/MediaBin.d.ts +12 -1
  42. package/dist/MediaBin.d.ts.map +1 -1
  43. package/dist/MediaBin.js +13 -3
  44. package/dist/MediaBin.js.map +1 -1
  45. package/dist/MentionExtension.js +10 -7
  46. package/dist/MentionExtension.js.map +1 -1
  47. package/dist/OutlinePanel.d.ts +17 -0
  48. package/dist/OutlinePanel.d.ts.map +1 -0
  49. package/dist/OutlinePanel.js +167 -0
  50. package/dist/OutlinePanel.js.map +1 -0
  51. package/dist/PlainHtmlPreview.d.ts +50 -0
  52. package/dist/PlainHtmlPreview.d.ts.map +1 -0
  53. package/dist/PlainHtmlPreview.js +155 -0
  54. package/dist/PlainHtmlPreview.js.map +1 -0
  55. package/dist/PreviewControls.d.ts +15 -1
  56. package/dist/PreviewControls.d.ts.map +1 -1
  57. package/dist/PreviewControls.js +75 -18
  58. package/dist/PreviewControls.js.map +1 -1
  59. package/dist/PreviewPanel.d.ts +11 -10
  60. package/dist/PreviewPanel.d.ts.map +1 -1
  61. package/dist/PreviewPanel.js +20 -17
  62. package/dist/PreviewPanel.js.map +1 -1
  63. package/dist/RawEditor.d.ts.map +1 -1
  64. package/dist/RawEditor.js +198 -4
  65. package/dist/RawEditor.js.map +1 -1
  66. package/dist/RecorderEntry.d.ts +24 -0
  67. package/dist/RecorderEntry.d.ts.map +1 -0
  68. package/dist/RecorderEntry.js +139 -0
  69. package/dist/RecorderEntry.js.map +1 -0
  70. package/dist/TemplateAnnotation.d.ts.map +1 -1
  71. package/dist/TemplateAnnotation.js +32 -6
  72. package/dist/TemplateAnnotation.js.map +1 -1
  73. package/dist/TemplatePicker.d.ts +53 -0
  74. package/dist/TemplatePicker.d.ts.map +1 -0
  75. package/dist/TemplatePicker.js +388 -0
  76. package/dist/TemplatePicker.js.map +1 -0
  77. package/dist/ThemeCustomizerPanel.d.ts +32 -0
  78. package/dist/ThemeCustomizerPanel.d.ts.map +1 -0
  79. package/dist/ThemeCustomizerPanel.js +256 -0
  80. package/dist/ThemeCustomizerPanel.js.map +1 -0
  81. package/dist/ThemePicker.d.ts +33 -0
  82. package/dist/ThemePicker.d.ts.map +1 -0
  83. package/dist/ThemePicker.js +148 -0
  84. package/dist/ThemePicker.js.map +1 -0
  85. package/dist/Toolbar.d.ts.map +1 -1
  86. package/dist/Toolbar.js +508 -33
  87. package/dist/Toolbar.js.map +1 -1
  88. package/dist/VersionHistoryPanel.d.ts +14 -0
  89. package/dist/VersionHistoryPanel.d.ts.map +1 -0
  90. package/dist/VersionHistoryPanel.js +147 -0
  91. package/dist/VersionHistoryPanel.js.map +1 -0
  92. package/dist/ViewMenuPanel.d.ts +13 -0
  93. package/dist/ViewMenuPanel.d.ts.map +1 -0
  94. package/dist/ViewMenuPanel.js +58 -0
  95. package/dist/ViewMenuPanel.js.map +1 -0
  96. package/dist/WysiwygEditor.d.ts.map +1 -1
  97. package/dist/WysiwygEditor.js +198 -9
  98. package/dist/WysiwygEditor.js.map +1 -1
  99. package/dist/__tests__/detectMarkdown.test.js +0 -14
  100. package/dist/__tests__/detectMarkdown.test.js.map +1 -1
  101. package/dist/__tests__/documentSettingsDialog.test.d.ts +2 -0
  102. package/dist/__tests__/documentSettingsDialog.test.d.ts.map +1 -0
  103. package/dist/__tests__/documentSettingsDialog.test.js +132 -0
  104. package/dist/__tests__/documentSettingsDialog.test.js.map +1 -0
  105. package/dist/__tests__/emojiPicker.test.d.ts +2 -0
  106. package/dist/__tests__/emojiPicker.test.d.ts.map +1 -0
  107. package/dist/__tests__/emojiPicker.test.js +111 -0
  108. package/dist/__tests__/emojiPicker.test.js.map +1 -0
  109. package/dist/__tests__/fileKind.test.js +13 -0
  110. package/dist/__tests__/fileKind.test.js.map +1 -1
  111. package/dist/__tests__/imageEditAffordance.test.d.ts +2 -0
  112. package/dist/__tests__/imageEditAffordance.test.d.ts.map +1 -0
  113. package/dist/__tests__/imageEditAffordance.test.js +188 -0
  114. package/dist/__tests__/imageEditAffordance.test.js.map +1 -0
  115. package/dist/__tests__/imageEditorShell.test.d.ts +2 -0
  116. package/dist/__tests__/imageEditorShell.test.d.ts.map +1 -0
  117. package/dist/__tests__/imageEditorShell.test.js +52 -0
  118. package/dist/__tests__/imageEditorShell.test.js.map +1 -0
  119. package/dist/__tests__/imageEditorState.test.d.ts +3 -0
  120. package/dist/__tests__/imageEditorState.test.d.ts.map +1 -0
  121. package/dist/__tests__/imageEditorState.test.js +148 -0
  122. package/dist/__tests__/imageEditorState.test.js.map +1 -0
  123. package/dist/__tests__/inlinePreviewGutter.test.d.ts +2 -0
  124. package/dist/__tests__/inlinePreviewGutter.test.d.ts.map +1 -0
  125. package/dist/__tests__/inlinePreviewGutter.test.js +51 -0
  126. package/dist/__tests__/inlinePreviewGutter.test.js.map +1 -0
  127. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts +2 -0
  128. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts.map +1 -0
  129. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js +63 -0
  130. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js.map +1 -0
  131. package/dist/__tests__/jsonEditor.test.d.ts +2 -0
  132. package/dist/__tests__/jsonEditor.test.d.ts.map +1 -0
  133. package/dist/__tests__/jsonEditor.test.js +134 -0
  134. package/dist/__tests__/jsonEditor.test.js.map +1 -0
  135. package/dist/__tests__/layersPanel.test.d.ts +2 -0
  136. package/dist/__tests__/layersPanel.test.d.ts.map +1 -0
  137. package/dist/__tests__/layersPanel.test.js +84 -0
  138. package/dist/__tests__/layersPanel.test.js.map +1 -0
  139. package/dist/__tests__/linkDialogDocPicker.test.d.ts +7 -0
  140. package/dist/__tests__/linkDialogDocPicker.test.d.ts.map +1 -0
  141. package/dist/__tests__/linkDialogDocPicker.test.js +75 -0
  142. package/dist/__tests__/linkDialogDocPicker.test.js.map +1 -0
  143. package/dist/__tests__/mediaAttachmentFlow.test.d.ts +2 -0
  144. package/dist/__tests__/mediaAttachmentFlow.test.d.ts.map +1 -0
  145. package/dist/__tests__/mediaAttachmentFlow.test.js +99 -0
  146. package/dist/__tests__/mediaAttachmentFlow.test.js.map +1 -0
  147. package/dist/__tests__/outlinePanel.test.d.ts +2 -0
  148. package/dist/__tests__/outlinePanel.test.d.ts.map +1 -0
  149. package/dist/__tests__/outlinePanel.test.js +68 -0
  150. package/dist/__tests__/outlinePanel.test.js.map +1 -0
  151. package/dist/__tests__/plainHtmlPreview.test.d.ts +2 -0
  152. package/dist/__tests__/plainHtmlPreview.test.d.ts.map +1 -0
  153. package/dist/__tests__/plainHtmlPreview.test.js +87 -0
  154. package/dist/__tests__/plainHtmlPreview.test.js.map +1 -0
  155. package/dist/__tests__/propertiesPanel.test.d.ts +2 -0
  156. package/dist/__tests__/propertiesPanel.test.d.ts.map +1 -0
  157. package/dist/__tests__/propertiesPanel.test.js +64 -0
  158. package/dist/__tests__/propertiesPanel.test.js.map +1 -0
  159. package/dist/__tests__/recorderFormats.test.d.ts +2 -0
  160. package/dist/__tests__/recorderFormats.test.d.ts.map +1 -0
  161. package/dist/__tests__/recorderFormats.test.js +121 -0
  162. package/dist/__tests__/recorderFormats.test.js.map +1 -0
  163. package/dist/__tests__/recorderTimingJson.test.d.ts +2 -0
  164. package/dist/__tests__/recorderTimingJson.test.d.ts.map +1 -0
  165. package/dist/__tests__/recorderTimingJson.test.js +37 -0
  166. package/dist/__tests__/recorderTimingJson.test.js.map +1 -0
  167. package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts +2 -0
  168. package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts.map +1 -0
  169. package/dist/__tests__/templateAnnotationRoundTrip.test.js +31 -0
  170. package/dist/__tests__/templateAnnotationRoundTrip.test.js.map +1 -0
  171. package/dist/__tests__/tiptapBridge.test.js +26 -0
  172. package/dist/__tests__/tiptapBridge.test.js.map +1 -1
  173. package/dist/__tests__/tiptapImageRoundTrip.test.d.ts +2 -0
  174. package/dist/__tests__/tiptapImageRoundTrip.test.d.ts.map +1 -0
  175. package/dist/__tests__/tiptapImageRoundTrip.test.js +68 -0
  176. package/dist/__tests__/tiptapImageRoundTrip.test.js.map +1 -0
  177. package/dist/__tests__/useImageEditor.test.d.ts +2 -0
  178. package/dist/__tests__/useImageEditor.test.d.ts.map +1 -0
  179. package/dist/__tests__/useImageEditor.test.js +131 -0
  180. package/dist/__tests__/useImageEditor.test.js.map +1 -0
  181. package/dist/__tests__/useMediaRecorder.test.d.ts +2 -0
  182. package/dist/__tests__/useMediaRecorder.test.d.ts.map +1 -0
  183. package/dist/__tests__/useMediaRecorder.test.js +153 -0
  184. package/dist/__tests__/useMediaRecorder.test.js.map +1 -0
  185. package/dist/__tests__/versionHistory.test.d.ts +2 -0
  186. package/dist/__tests__/versionHistory.test.d.ts.map +1 -0
  187. package/dist/__tests__/versionHistory.test.js +124 -0
  188. package/dist/__tests__/versionHistory.test.js.map +1 -0
  189. package/dist/blockSlice.d.ts +24 -0
  190. package/dist/blockSlice.d.ts.map +1 -0
  191. package/dist/blockSlice.js +63 -0
  192. package/dist/blockSlice.js.map +1 -0
  193. package/dist/buildPreviewDoc.d.ts.map +1 -1
  194. package/dist/buildPreviewDoc.js +52 -2
  195. package/dist/buildPreviewDoc.js.map +1 -1
  196. package/dist/emojiData.d.ts +81 -0
  197. package/dist/emojiData.d.ts.map +1 -0
  198. package/dist/emojiData.js +1283 -0
  199. package/dist/emojiData.js.map +1 -0
  200. package/dist/fileKind.d.ts +6 -2
  201. package/dist/fileKind.d.ts.map +1 -1
  202. package/dist/fileKind.js +25 -4
  203. package/dist/fileKind.js.map +1 -1
  204. package/dist/hooks/useFileDrop.d.ts.map +1 -1
  205. package/dist/hooks/useFileDrop.js +40 -4
  206. package/dist/hooks/useFileDrop.js.map +1 -1
  207. package/dist/imageEditor/CanvasSurface.d.ts +31 -0
  208. package/dist/imageEditor/CanvasSurface.d.ts.map +1 -0
  209. package/dist/imageEditor/CanvasSurface.js +264 -0
  210. package/dist/imageEditor/CanvasSurface.js.map +1 -0
  211. package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts +39 -0
  212. package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts.map +1 -0
  213. package/dist/imageEditor/ImageVersionHistoryDropdown.js +283 -0
  214. package/dist/imageEditor/ImageVersionHistoryDropdown.js.map +1 -0
  215. package/dist/imageEditor/LayersPanel.d.ts +14 -0
  216. package/dist/imageEditor/LayersPanel.d.ts.map +1 -0
  217. package/dist/imageEditor/LayersPanel.js +43 -0
  218. package/dist/imageEditor/LayersPanel.js.map +1 -0
  219. package/dist/imageEditor/PropertiesPanel.d.ts +14 -0
  220. package/dist/imageEditor/PropertiesPanel.d.ts.map +1 -0
  221. package/dist/imageEditor/PropertiesPanel.js +97 -0
  222. package/dist/imageEditor/PropertiesPanel.js.map +1 -0
  223. package/dist/imageEditor/Toolbar.d.ts +30 -0
  224. package/dist/imageEditor/Toolbar.d.ts.map +1 -0
  225. package/dist/imageEditor/Toolbar.js +108 -0
  226. package/dist/imageEditor/Toolbar.js.map +1 -0
  227. package/dist/imageEditor/icons.d.ts +24 -0
  228. package/dist/imageEditor/icons.d.ts.map +1 -0
  229. package/dist/imageEditor/icons.js +45 -0
  230. package/dist/imageEditor/icons.js.map +1 -0
  231. package/dist/imageEditor/layers/EditorImageLayer.d.ts +16 -0
  232. package/dist/imageEditor/layers/EditorImageLayer.d.ts.map +1 -0
  233. package/dist/imageEditor/layers/EditorImageLayer.js +37 -0
  234. package/dist/imageEditor/layers/EditorImageLayer.js.map +1 -0
  235. package/dist/imageEditor/layers/EditorShapeLayer.d.ts +15 -0
  236. package/dist/imageEditor/layers/EditorShapeLayer.d.ts.map +1 -0
  237. package/dist/imageEditor/layers/EditorShapeLayer.js +20 -0
  238. package/dist/imageEditor/layers/EditorShapeLayer.js.map +1 -0
  239. package/dist/imageEditor/layers/EditorTextLayer.d.ts +18 -0
  240. package/dist/imageEditor/layers/EditorTextLayer.d.ts.map +1 -0
  241. package/dist/imageEditor/layers/EditorTextLayer.js +13 -0
  242. package/dist/imageEditor/layers/EditorTextLayer.js.map +1 -0
  243. package/dist/imageEditor/layers/SelectionHandles.d.ts +17 -0
  244. package/dist/imageEditor/layers/SelectionHandles.d.ts.map +1 -0
  245. package/dist/imageEditor/layers/SelectionHandles.js +19 -0
  246. package/dist/imageEditor/layers/SelectionHandles.js.map +1 -0
  247. package/dist/imageEditor/state.d.ts +76 -0
  248. package/dist/imageEditor/state.d.ts.map +1 -0
  249. package/dist/imageEditor/state.js +87 -0
  250. package/dist/imageEditor/state.js.map +1 -0
  251. package/dist/imageEditor/useImageEditor.d.ts +53 -0
  252. package/dist/imageEditor/useImageEditor.d.ts.map +1 -0
  253. package/dist/imageEditor/useImageEditor.js +244 -0
  254. package/dist/imageEditor/useImageEditor.js.map +1 -0
  255. package/dist/imageEditor/useImageEditorTokens.d.ts +16 -0
  256. package/dist/imageEditor/useImageEditorTokens.d.ts.map +1 -0
  257. package/dist/imageEditor/useImageEditorTokens.js +45 -0
  258. package/dist/imageEditor/useImageEditorTokens.js.map +1 -0
  259. package/dist/index.d.ts +48 -1
  260. package/dist/index.d.ts.map +1 -1
  261. package/dist/index.js +36 -0
  262. package/dist/index.js.map +1 -1
  263. package/dist/jsonEditor/EmbeddedRichTextField.d.ts +15 -0
  264. package/dist/jsonEditor/EmbeddedRichTextField.d.ts.map +1 -0
  265. package/dist/jsonEditor/EmbeddedRichTextField.js +74 -0
  266. package/dist/jsonEditor/EmbeddedRichTextField.js.map +1 -0
  267. package/dist/jsonEditor/JsonEditor.d.ts +36 -0
  268. package/dist/jsonEditor/JsonEditor.d.ts.map +1 -0
  269. package/dist/jsonEditor/JsonEditor.js +15 -0
  270. package/dist/jsonEditor/JsonEditor.js.map +1 -0
  271. package/dist/jsonEditor/JsonEditorContext.d.ts +28 -0
  272. package/dist/jsonEditor/JsonEditorContext.d.ts.map +1 -0
  273. package/dist/jsonEditor/JsonEditorContext.js +41 -0
  274. package/dist/jsonEditor/JsonEditorContext.js.map +1 -0
  275. package/dist/jsonEditor/RenderNode.d.ts +16 -0
  276. package/dist/jsonEditor/RenderNode.d.ts.map +1 -0
  277. package/dist/jsonEditor/RenderNode.js +32 -0
  278. package/dist/jsonEditor/RenderNode.js.map +1 -0
  279. package/dist/jsonEditor/editors.d.ts +36 -0
  280. package/dist/jsonEditor/editors.d.ts.map +1 -0
  281. package/dist/jsonEditor/editors.js +347 -0
  282. package/dist/jsonEditor/editors.js.map +1 -0
  283. package/dist/jsonEditor/index.d.ts +3 -0
  284. package/dist/jsonEditor/index.d.ts.map +1 -0
  285. package/dist/jsonEditor/index.js +2 -0
  286. package/dist/jsonEditor/index.js.map +1 -0
  287. package/dist/jsonEditor/useJsonEditorTokens.d.ts +13 -0
  288. package/dist/jsonEditor/useJsonEditorTokens.d.ts.map +1 -0
  289. package/dist/jsonEditor/useJsonEditorTokens.js +38 -0
  290. package/dist/jsonEditor/useJsonEditorTokens.js.map +1 -0
  291. package/dist/recorder/RecorderButton.d.ts +31 -0
  292. package/dist/recorder/RecorderButton.d.ts.map +1 -0
  293. package/dist/recorder/RecorderButton.js +24 -0
  294. package/dist/recorder/RecorderButton.js.map +1 -0
  295. package/dist/recorder/RecorderModal.d.ts +59 -0
  296. package/dist/recorder/RecorderModal.d.ts.map +1 -0
  297. package/dist/recorder/RecorderModal.js +333 -0
  298. package/dist/recorder/RecorderModal.js.map +1 -0
  299. package/dist/recorder/RecorderPanel.d.ts +25 -0
  300. package/dist/recorder/RecorderPanel.d.ts.map +1 -0
  301. package/dist/recorder/RecorderPanel.js +30 -0
  302. package/dist/recorder/RecorderPanel.js.map +1 -0
  303. package/dist/recorder/formats.d.ts +51 -0
  304. package/dist/recorder/formats.d.ts.map +1 -0
  305. package/dist/recorder/formats.js +144 -0
  306. package/dist/recorder/formats.js.map +1 -0
  307. package/dist/recorder/hooks/useMediaRecorder.d.ts +90 -0
  308. package/dist/recorder/hooks/useMediaRecorder.d.ts.map +1 -0
  309. package/dist/recorder/hooks/useMediaRecorder.js +277 -0
  310. package/dist/recorder/hooks/useMediaRecorder.js.map +1 -0
  311. package/dist/recorder/hooks/useStreamPreview.d.ts +22 -0
  312. package/dist/recorder/hooks/useStreamPreview.d.ts.map +1 -0
  313. package/dist/recorder/hooks/useStreamPreview.js +44 -0
  314. package/dist/recorder/hooks/useStreamPreview.js.map +1 -0
  315. package/dist/recorder/sources/cameraStream.d.ts +22 -0
  316. package/dist/recorder/sources/cameraStream.d.ts.map +1 -0
  317. package/dist/recorder/sources/cameraStream.js +24 -0
  318. package/dist/recorder/sources/cameraStream.js.map +1 -0
  319. package/dist/recorder/sources/micStream.d.ts +15 -0
  320. package/dist/recorder/sources/micStream.d.ts.map +1 -0
  321. package/dist/recorder/sources/micStream.js +24 -0
  322. package/dist/recorder/sources/micStream.js.map +1 -0
  323. package/dist/recorder/sources/screenStream.d.ts +53 -0
  324. package/dist/recorder/sources/screenStream.d.ts.map +1 -0
  325. package/dist/recorder/sources/screenStream.js +114 -0
  326. package/dist/recorder/sources/screenStream.js.map +1 -0
  327. package/dist/recorder/timingJson.d.ts +51 -0
  328. package/dist/recorder/timingJson.d.ts.map +1 -0
  329. package/dist/recorder/timingJson.js +42 -0
  330. package/dist/recorder/timingJson.js.map +1 -0
  331. package/dist/tiptap/TiptapAudio.d.ts +26 -0
  332. package/dist/tiptap/TiptapAudio.d.ts.map +1 -0
  333. package/dist/tiptap/TiptapAudio.js +58 -0
  334. package/dist/tiptap/TiptapAudio.js.map +1 -0
  335. package/dist/tiptap/TiptapVideo.d.ts +30 -0
  336. package/dist/tiptap/TiptapVideo.d.ts.map +1 -0
  337. package/dist/tiptap/TiptapVideo.js +66 -0
  338. package/dist/tiptap/TiptapVideo.js.map +1 -0
  339. package/dist/tiptap/useResolvedMediaSrc.d.ts +2 -0
  340. package/dist/tiptap/useResolvedMediaSrc.d.ts.map +1 -0
  341. package/dist/tiptap/useResolvedMediaSrc.js +42 -0
  342. package/dist/tiptap/useResolvedMediaSrc.js.map +1 -0
  343. package/dist/tiptapBridge.d.ts.map +1 -1
  344. package/dist/tiptapBridge.js +210 -16
  345. package/dist/tiptapBridge.js.map +1 -1
  346. package/dist/useHeadingLayout.d.ts +54 -0
  347. package/dist/useHeadingLayout.d.ts.map +1 -0
  348. package/dist/useHeadingLayout.js +260 -0
  349. package/dist/useHeadingLayout.js.map +1 -0
  350. package/dist/utils/collectInlineFontAwesomeCss.d.ts +21 -0
  351. package/dist/utils/collectInlineFontAwesomeCss.d.ts.map +1 -0
  352. package/dist/utils/collectInlineFontAwesomeCss.js +68 -0
  353. package/dist/utils/collectInlineFontAwesomeCss.js.map +1 -0
  354. package/dist/utils/dropUtils.d.ts +21 -2
  355. package/dist/utils/dropUtils.d.ts.map +1 -1
  356. package/dist/utils/dropUtils.js +43 -4
  357. package/dist/utils/dropUtils.js.map +1 -1
  358. package/dist/utils/normalizeMalformedAssetUrl.d.ts +15 -0
  359. package/dist/utils/normalizeMalformedAssetUrl.d.ts.map +1 -0
  360. package/dist/utils/normalizeMalformedAssetUrl.js +27 -0
  361. package/dist/utils/normalizeMalformedAssetUrl.js.map +1 -0
  362. package/package.json +8 -5
  363. package/src/DocumentSettingsDialog.tsx +266 -0
  364. package/src/EditorContext.tsx +534 -10
  365. package/src/EditorShell.tsx +691 -63
  366. package/src/EmojiPicker.tsx +332 -0
  367. package/src/ImageEditor.tsx +327 -0
  368. package/src/ImageNodeView.tsx +222 -21
  369. package/src/ImageViewer.tsx +221 -0
  370. package/src/InlineIcon.ts +84 -0
  371. package/src/InlinePreviewGutter.tsx +582 -0
  372. package/src/LinkDialog.tsx +276 -0
  373. package/src/MediaBin.tsx +22 -3
  374. package/src/MentionExtension.tsx +10 -7
  375. package/src/OutlinePanel.tsx +295 -0
  376. package/src/PlainHtmlPreview.tsx +211 -0
  377. package/src/PreviewControls.tsx +130 -24
  378. package/src/PreviewPanel.tsx +38 -21
  379. package/src/RawEditor.tsx +215 -4
  380. package/src/RecorderEntry.tsx +164 -0
  381. package/src/TemplateAnnotation.ts +32 -6
  382. package/src/TemplatePicker.tsx +818 -0
  383. package/src/ThemeCustomizerPanel.tsx +595 -0
  384. package/src/ThemePicker.tsx +319 -0
  385. package/src/Toolbar.tsx +708 -111
  386. package/src/VersionHistoryPanel.tsx +329 -0
  387. package/src/ViewMenuPanel.tsx +188 -0
  388. package/src/WysiwygEditor.tsx +229 -9
  389. package/src/__tests__/detectMarkdown.test.ts +0 -15
  390. package/src/__tests__/documentSettingsDialog.test.tsx +147 -0
  391. package/src/__tests__/emojiPicker.test.tsx +133 -0
  392. package/src/__tests__/fileKind.test.ts +16 -0
  393. package/src/__tests__/imageEditAffordance.test.tsx +268 -0
  394. package/src/__tests__/imageEditorShell.test.tsx +57 -0
  395. package/src/__tests__/imageEditorState.test.ts +171 -0
  396. package/src/__tests__/inlinePreviewGutter.test.tsx +62 -0
  397. package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +103 -0
  398. package/src/__tests__/jsonEditor.test.tsx +168 -0
  399. package/src/__tests__/layersPanel.test.tsx +97 -0
  400. package/src/__tests__/linkDialogDocPicker.test.tsx +137 -0
  401. package/src/__tests__/mediaAttachmentFlow.test.ts +110 -0
  402. package/src/__tests__/outlinePanel.test.tsx +79 -0
  403. package/src/__tests__/plainHtmlPreview.test.tsx +107 -0
  404. package/src/__tests__/propertiesPanel.test.tsx +69 -0
  405. package/src/__tests__/recorderFormats.test.ts +146 -0
  406. package/src/__tests__/recorderTimingJson.test.ts +41 -0
  407. package/src/__tests__/templateAnnotationRoundTrip.test.ts +34 -0
  408. package/src/__tests__/tiptapBridge.test.ts +29 -0
  409. package/src/__tests__/tiptapImageRoundTrip.test.ts +73 -0
  410. package/src/__tests__/useImageEditor.test.tsx +159 -0
  411. package/src/__tests__/useMediaRecorder.test.ts +186 -0
  412. package/src/__tests__/versionHistory.test.tsx +197 -0
  413. package/src/blockSlice.ts +75 -0
  414. package/src/buildPreviewDoc.ts +61 -6
  415. package/src/emojiData.ts +1337 -0
  416. package/src/fileKind.ts +30 -6
  417. package/src/hooks/useFileDrop.ts +40 -4
  418. package/src/imageEditor/CanvasSurface.tsx +402 -0
  419. package/src/imageEditor/ImageVersionHistoryDropdown.tsx +396 -0
  420. package/src/imageEditor/LayersPanel.tsx +143 -0
  421. package/src/imageEditor/PropertiesPanel.tsx +428 -0
  422. package/src/imageEditor/Toolbar.tsx +242 -0
  423. package/src/imageEditor/icons.tsx +144 -0
  424. package/src/imageEditor/image-editor.css +450 -0
  425. package/src/imageEditor/layers/EditorImageLayer.tsx +45 -0
  426. package/src/imageEditor/layers/EditorShapeLayer.tsx +62 -0
  427. package/src/imageEditor/layers/EditorTextLayer.tsx +45 -0
  428. package/src/imageEditor/layers/SelectionHandles.tsx +86 -0
  429. package/src/imageEditor/state.ts +153 -0
  430. package/src/imageEditor/useImageEditor.ts +328 -0
  431. package/src/imageEditor/useImageEditorTokens.ts +70 -0
  432. package/src/index.ts +82 -0
  433. package/src/jsonEditor/EmbeddedRichTextField.tsx +81 -0
  434. package/src/jsonEditor/JsonEditor.tsx +81 -0
  435. package/src/jsonEditor/JsonEditorContext.tsx +75 -0
  436. package/src/jsonEditor/RenderNode.tsx +66 -0
  437. package/src/jsonEditor/editors.tsx +678 -0
  438. package/src/jsonEditor/index.ts +2 -0
  439. package/src/jsonEditor/json-editor.css +463 -0
  440. package/src/jsonEditor/useJsonEditorTokens.ts +63 -0
  441. package/src/recorder/RecorderButton.tsx +72 -0
  442. package/src/recorder/RecorderModal.tsx +596 -0
  443. package/src/recorder/RecorderPanel.tsx +93 -0
  444. package/src/recorder/formats.ts +159 -0
  445. package/src/recorder/hooks/useMediaRecorder.ts +378 -0
  446. package/src/recorder/hooks/useStreamPreview.ts +47 -0
  447. package/src/recorder/sources/cameraStream.ts +32 -0
  448. package/src/recorder/sources/micStream.ts +25 -0
  449. package/src/recorder/sources/screenStream.ts +162 -0
  450. package/src/recorder/timingJson.ts +66 -0
  451. package/src/styles/editor.css +2490 -51
  452. package/src/styles/image-edit-affordance.css +201 -0
  453. package/src/styles/index.css +10 -0
  454. package/src/tiptap/TiptapAudio.tsx +86 -0
  455. package/src/tiptap/TiptapVideo.tsx +119 -0
  456. package/src/tiptap/useResolvedMediaSrc.ts +47 -0
  457. package/src/tiptapBridge.ts +227 -22
  458. package/src/useHeadingLayout.ts +294 -0
  459. package/src/utils/collectInlineFontAwesomeCss.ts +69 -0
  460. package/src/utils/dropUtils.ts +54 -6
  461. package/src/utils/normalizeMalformedAssetUrl.ts +22 -0
@@ -0,0 +1,818 @@
1
+ /**
2
+ * TemplatePicker
3
+ *
4
+ * A custom popover that replaces the plain <select> for block templates.
5
+ * Each template entry shows a mini wireframe SVG, a human-readable label,
6
+ * and a one-sentence description so authors can quickly find the right layout.
7
+ */
8
+
9
+ import { useEffect, useRef, useState } from 'react';
10
+ import { createPortal } from 'react-dom';
11
+
12
+ // ── Template metadata ─────────────────────────────────────────────
13
+
14
+ interface TemplateEntry {
15
+ name: string;
16
+ label: string;
17
+ description: string;
18
+ icon: JSX.Element;
19
+ }
20
+
21
+ const W = 56;
22
+ const H = 40;
23
+
24
+ /** Neutral fill for structural shapes */
25
+ const F1 = '#d1d5db';
26
+ /** Slightly darker fill for important / featured elements */
27
+ const F2 = '#9ca3af';
28
+ /** Accent fill (stat number, image, play button) */
29
+ const FA = '#818cf8';
30
+
31
+ function TemplateIcon({ children }: { children: React.ReactNode }) {
32
+ return (
33
+ <svg
34
+ width={W}
35
+ height={H}
36
+ viewBox={`0 0 ${W} ${H}`}
37
+ aria-hidden="true"
38
+ className="squisq-template-picker-icon"
39
+ >
40
+ {children}
41
+ </svg>
42
+ );
43
+ }
44
+
45
+ const NONE_ENTRY: TemplateEntry = {
46
+ name: '',
47
+ label: '— none —',
48
+ description: 'Plain heading block with no visual template.',
49
+ icon: (
50
+ <TemplateIcon>
51
+ <rect
52
+ x={4}
53
+ y={4}
54
+ width={48}
55
+ height={32}
56
+ rx={2}
57
+ fill="none"
58
+ stroke={F1}
59
+ strokeWidth={1.5}
60
+ strokeDasharray="3 2"
61
+ />
62
+ <rect x={12} y={15} width={32} height={4} rx={1} fill={F1} />
63
+ <rect x={16} y={22} width={24} height={3} rx={1} fill={F1} opacity={0.6} />
64
+ </TemplateIcon>
65
+ ),
66
+ };
67
+
68
+ const TEMPLATE_ENTRIES: TemplateEntry[] = [
69
+ {
70
+ name: 'title',
71
+ label: 'Title',
72
+ description: 'A bold opening slide with large title text, perfect for covers and chapters.',
73
+ icon: (
74
+ <TemplateIcon>
75
+ <rect x={4} y={4} width={48} height={32} rx={2} fill={F1} opacity={0.3} />
76
+ <rect x={8} y={11} width={40} height={8} rx={1} fill={FA} />
77
+ <rect x={14} y={23} width={28} height={3} rx={1} fill={F2} />
78
+ <rect x={20} y={29} width={16} height={2} rx={1} fill={F1} />
79
+ </TemplateIcon>
80
+ ),
81
+ },
82
+ {
83
+ name: 'sectionHeader',
84
+ label: 'Section Header',
85
+ description: 'A clean section break with a prominent title and optional subtitle.',
86
+ icon: (
87
+ <TemplateIcon>
88
+ <rect x={4} y={4} width={3} height={32} rx={1} fill={FA} />
89
+ <rect x={11} y={8} width={36} height={6} rx={1} fill={F2} />
90
+ <rect x={11} y={18} width={28} height={3} rx={1} fill={F1} />
91
+ <rect x={11} y={24} width={20} height={2.5} rx={1} fill={F1} opacity={0.7} />
92
+ </TemplateIcon>
93
+ ),
94
+ },
95
+ {
96
+ name: 'statHighlight',
97
+ label: 'Stat Highlight',
98
+ description: 'Showcases a single key number or metric with supporting context.',
99
+ icon: (
100
+ <TemplateIcon>
101
+ <rect x={14} y={4} width={28} height={16} rx={2} fill={FA} />
102
+ <rect x={10} y={24} width={36} height={3.5} rx={1} fill={F2} />
103
+ <rect x={16} y={30} width={24} height={2.5} rx={1} fill={F1} />
104
+ </TemplateIcon>
105
+ ),
106
+ },
107
+ {
108
+ name: 'quote',
109
+ label: 'Quote',
110
+ description: 'Displays a stylized pull quote with decorative marks and attribution.',
111
+ icon: (
112
+ <TemplateIcon>
113
+ <text
114
+ x={5}
115
+ y={18}
116
+ fontSize={18}
117
+ fill={FA}
118
+ fontFamily="serif"
119
+ fontWeight="bold"
120
+ opacity={0.7}
121
+ >
122
+ "
123
+ </text>
124
+ <rect x={16} y={8} width={36} height={4} rx={1} fill={F2} />
125
+ <rect x={16} y={15} width={32} height={4} rx={1} fill={F2} />
126
+ <rect x={16} y={22} width={24} height={4} rx={1} fill={F2} />
127
+ <rect x={16} y={31} width={18} height={2.5} rx={1} fill={F1} />
128
+ </TemplateIcon>
129
+ ),
130
+ },
131
+ {
132
+ name: 'factCard',
133
+ label: 'Fact Card',
134
+ description: 'Presents a focused fact or insight with a labeled header and body text.',
135
+ icon: (
136
+ <TemplateIcon>
137
+ <rect x={4} y={4} width={20} height={6} rx={3} fill={FA} opacity={0.8} />
138
+ <rect x={4} y={14} width={48} height={5} rx={1} fill={F2} />
139
+ <rect x={4} y={22} width={44} height={3.5} rx={1} fill={F1} />
140
+ <rect x={4} y={28} width={36} height={3} rx={1} fill={F1} opacity={0.7} />
141
+ </TemplateIcon>
142
+ ),
143
+ },
144
+ {
145
+ name: 'twoColumn',
146
+ label: 'Two Column',
147
+ description: 'Divides the slide into two equal side-by-side content columns.',
148
+ icon: (
149
+ <TemplateIcon>
150
+ <rect x={3} y={4} width={23} height={32} rx={2} fill={F1} opacity={0.4} />
151
+ <rect x={3} y={4} width={23} height={7} rx={2} fill={F2} opacity={0.6} />
152
+ <rect x={6} y={15} width={17} height={3} rx={1} fill={F1} />
153
+ <rect x={6} y={21} width={14} height={2.5} rx={1} fill={F1} opacity={0.7} />
154
+ <rect x={30} y={4} width={23} height={32} rx={2} fill={F1} opacity={0.4} />
155
+ <rect x={30} y={4} width={23} height={7} rx={2} fill={F2} opacity={0.6} />
156
+ <rect x={33} y={15} width={17} height={3} rx={1} fill={F1} />
157
+ <rect x={33} y={21} width={14} height={2.5} rx={1} fill={F1} opacity={0.7} />
158
+ </TemplateIcon>
159
+ ),
160
+ },
161
+ {
162
+ name: 'dateEvent',
163
+ label: 'Date Event',
164
+ description: 'Highlights a date-based event or milestone with a prominent date display.',
165
+ icon: (
166
+ <TemplateIcon>
167
+ <rect x={3} y={4} width={18} height={18} rx={3} fill={FA} opacity={0.85} />
168
+ <rect x={6} y={8} width={12} height={2} rx={1} fill="white" opacity={0.7} />
169
+ <rect x={6} y={13} width={12} height={6} rx={1} fill="white" opacity={0.5} />
170
+ <rect x={25} y={6} width={27} height={5} rx={1} fill={F2} />
171
+ <rect x={25} y={15} width={22} height={3} rx={1} fill={F1} />
172
+ <rect x={25} y={21} width={16} height={2.5} rx={1} fill={F1} opacity={0.7} />
173
+ <rect x={3} y={27} width={50} height={2.5} rx={1} fill={F1} opacity={0.5} />
174
+ <rect x={3} y={32} width={40} height={2} rx={1} fill={F1} opacity={0.4} />
175
+ </TemplateIcon>
176
+ ),
177
+ },
178
+ {
179
+ name: 'imageWithCaption',
180
+ label: 'Image with Caption',
181
+ description: 'Displays a full-bleed image with an optional caption below.',
182
+ icon: (
183
+ <TemplateIcon>
184
+ <rect x={3} y={3} width={50} height={28} rx={2} fill={F2} opacity={0.5} />
185
+ <line x1={3} y1={14} x2={53} y2={14} stroke="white" strokeWidth={0.5} opacity={0.3} />
186
+ <polygon points="22,10 22,20 32,15" fill="white" opacity={0.4} />
187
+ <rect x={10} y={35} width={36} height={2.5} rx={1} fill={F1} />
188
+ </TemplateIcon>
189
+ ),
190
+ },
191
+ {
192
+ name: 'leftFeature',
193
+ label: 'Left Feature',
194
+ description: 'Image on the left, title and body text stacked on the right.',
195
+ icon: (
196
+ <TemplateIcon>
197
+ <rect x={3} y={4} width={24} height={32} rx={2} fill={F2} opacity={0.55} />
198
+ <polygon points="11,16 11,24 19,20" fill="white" opacity={0.5} />
199
+ <rect x={31} y={9} width={20} height={4} rx={1} fill={FA} />
200
+ <rect x={31} y={17} width={22} height={2.5} rx={1} fill={F1} />
201
+ <rect x={31} y={22} width={22} height={2.5} rx={1} fill={F1} opacity={0.75} />
202
+ <rect x={31} y={27} width={16} height={2.5} rx={1} fill={F1} opacity={0.6} />
203
+ </TemplateIcon>
204
+ ),
205
+ },
206
+ {
207
+ name: 'rightFeature',
208
+ label: 'Right Feature',
209
+ description: 'Image on the right, title and body text stacked on the left.',
210
+ icon: (
211
+ <TemplateIcon>
212
+ <rect x={29} y={4} width={24} height={32} rx={2} fill={F2} opacity={0.55} />
213
+ <polygon points="37,16 37,24 45,20" fill="white" opacity={0.5} />
214
+ <rect x={3} y={9} width={20} height={4} rx={1} fill={FA} />
215
+ <rect x={3} y={17} width={22} height={2.5} rx={1} fill={F1} />
216
+ <rect x={3} y={22} width={22} height={2.5} rx={1} fill={F1} opacity={0.75} />
217
+ <rect x={3} y={27} width={16} height={2.5} rx={1} fill={F1} opacity={0.6} />
218
+ </TemplateIcon>
219
+ ),
220
+ },
221
+ {
222
+ name: 'map',
223
+ label: 'Map',
224
+ description: 'Embeds an interactive map centered on a geographic location.',
225
+ icon: (
226
+ <TemplateIcon>
227
+ <rect x={3} y={3} width={50} height={34} rx={2} fill={F1} opacity={0.3} />
228
+ <line x1={3} y1={13} x2={53} y2={13} stroke={F1} strokeWidth={1} />
229
+ <line x1={3} y1={23} x2={53} y2={23} stroke={F1} strokeWidth={1} />
230
+ <line x1={18} y1={3} x2={18} y2={37} stroke={F1} strokeWidth={1} />
231
+ <line x1={38} y1={3} x2={38} y2={37} stroke={F1} strokeWidth={1} />
232
+ <circle cx={28} cy={18} r={4} fill={FA} opacity={0.8} />
233
+ <line x1={28} y1={22} x2={28} y2={26} stroke={FA} strokeWidth={1.5} opacity={0.6} />
234
+ </TemplateIcon>
235
+ ),
236
+ },
237
+ {
238
+ name: 'fullBleedQuote',
239
+ label: 'Full Bleed Quote',
240
+ description: 'A full-width quote that spans the entire slide for maximum impact.',
241
+ icon: (
242
+ <TemplateIcon>
243
+ <rect x={4} y={4} width={48} height={32} rx={2} fill={F2} opacity={0.2} />
244
+ <text
245
+ x={5}
246
+ y={15}
247
+ fontSize={16}
248
+ fill={FA}
249
+ fontFamily="serif"
250
+ fontWeight="bold"
251
+ opacity={0.8}
252
+ >
253
+ "
254
+ </text>
255
+ <rect x={4} y={14} width={48} height={5} rx={1} fill={F2} />
256
+ <rect x={4} y={22} width={44} height={5} rx={1} fill={F2} />
257
+ <rect x={4} y={30} width={30} height={4} rx={1} fill={F2} opacity={0.7} />
258
+ </TemplateIcon>
259
+ ),
260
+ },
261
+ {
262
+ name: 'list',
263
+ label: 'List',
264
+ description: 'Renders a bulleted or numbered list in a clean, card-style layout.',
265
+ icon: (
266
+ <TemplateIcon>
267
+ <rect x={4} y={5} width={6} height={5} rx={3} fill={FA} opacity={0.8} />
268
+ <rect x={14} y={6} width={38} height={4} rx={1} fill={F2} />
269
+ <rect x={4} y={17} width={6} height={5} rx={3} fill={FA} opacity={0.8} />
270
+ <rect x={14} y={18} width={34} height={4} rx={1} fill={F2} />
271
+ <rect x={4} y={29} width={6} height={5} rx={3} fill={FA} opacity={0.8} />
272
+ <rect x={14} y={30} width={36} height={4} rx={1} fill={F2} />
273
+ </TemplateIcon>
274
+ ),
275
+ },
276
+ {
277
+ name: 'photoGrid',
278
+ label: 'Photo Grid',
279
+ description: 'Arranges multiple photos in a 2×2 or 3×3 mosaic grid.',
280
+ icon: (
281
+ <TemplateIcon>
282
+ <rect x={3} y={3} width={23} height={16} rx={1.5} fill={F2} opacity={0.55} />
283
+ <rect x={30} y={3} width={23} height={16} rx={1.5} fill={F2} opacity={0.7} />
284
+ <rect x={3} y={22} width={23} height={16} rx={1.5} fill={F2} opacity={0.8} />
285
+ <rect x={30} y={22} width={23} height={16} rx={1.5} fill={F2} opacity={0.5} />
286
+ </TemplateIcon>
287
+ ),
288
+ },
289
+ {
290
+ name: 'definitionCard',
291
+ label: 'Definition Card',
292
+ description: 'Shows a term and its definition in a structured, dictionary-style card.',
293
+ icon: (
294
+ <TemplateIcon>
295
+ <rect x={4} y={6} width={32} height={6} rx={1} fill={FA} opacity={0.8} />
296
+ <rect x={4} y={17} width={48} height={3.5} rx={1} fill={F1} />
297
+ <rect x={4} y={23} width={44} height={3} rx={1} fill={F1} opacity={0.8} />
298
+ <rect x={4} y={29} width={36} height={3} rx={1} fill={F1} opacity={0.6} />
299
+ </TemplateIcon>
300
+ ),
301
+ },
302
+ {
303
+ name: 'comparisonBar',
304
+ label: 'Comparison Bar',
305
+ description: 'Visualizes two or more values side-by-side with labeled horizontal bars.',
306
+ icon: (
307
+ <TemplateIcon>
308
+ <rect x={4} y={5} width={14} height={3} rx={1} fill={F1} />
309
+ <rect x={20} y={4} width={32} height={5} rx={1} fill={FA} opacity={0.85} />
310
+ <rect x={4} y={16} width={14} height={3} rx={1} fill={F1} />
311
+ <rect x={20} y={15} width={22} height={5} rx={1} fill={F2} opacity={0.7} />
312
+ <rect x={4} y={27} width={14} height={3} rx={1} fill={F1} />
313
+ <rect x={20} y={26} width={28} height={5} rx={1} fill={F2} opacity={0.5} />
314
+ </TemplateIcon>
315
+ ),
316
+ },
317
+ {
318
+ name: 'pullQuote',
319
+ label: 'Pull Quote',
320
+ description: 'A stylized pull quote with large decorative marks and centered text.',
321
+ icon: (
322
+ <TemplateIcon>
323
+ <text x={3} y={20} fontSize={26} fill={FA} fontFamily="serif" opacity={0.5}>
324
+ "
325
+ </text>
326
+ <rect x={16} y={7} width={36} height={4.5} rx={1} fill={F2} />
327
+ <rect x={16} y={15} width={34} height={4.5} rx={1} fill={F2} />
328
+ <rect x={16} y={23} width={28} height={4.5} rx={1} fill={F2} />
329
+ <rect x={32} y={31} width={20} height={3} rx={1} fill={F1} />
330
+ <text x={44} y={42} fontSize={26} fill={FA} fontFamily="serif" opacity={0.3}>
331
+ "
332
+ </text>
333
+ </TemplateIcon>
334
+ ),
335
+ },
336
+ {
337
+ name: 'videoWithCaption',
338
+ label: 'Video with Caption',
339
+ description: 'Embeds a video player with an optional caption below.',
340
+ icon: (
341
+ <TemplateIcon>
342
+ <rect x={3} y={3} width={50} height={28} rx={2} fill={F1} opacity={0.4} />
343
+ <rect x={3} y={3} width={50} height={28} rx={2} fill={F2} opacity={0.25} />
344
+ <circle cx={28} cy={17} r={8} fill={FA} opacity={0.7} />
345
+ <polygon points="25,13 25,21 33,17" fill="white" />
346
+ <rect x={10} y={35} width={36} height={2.5} rx={1} fill={F1} />
347
+ </TemplateIcon>
348
+ ),
349
+ },
350
+ {
351
+ name: 'videoPullQuote',
352
+ label: 'Video Pull Quote',
353
+ description: 'Combines a video panel with a highlighted pull quote side-by-side.',
354
+ icon: (
355
+ <TemplateIcon>
356
+ <rect x={3} y={4} width={23} height={32} rx={2} fill={F2} opacity={0.35} />
357
+ <circle cx={14.5} cy={20} r={6} fill={FA} opacity={0.65} />
358
+ <polygon points="12,17 12,23 18,20" fill="white" />
359
+ <rect x={30} y={4} width={23} height={32} rx={2} fill={F1} opacity={0.3} />
360
+ <text x={32} y={16} fontSize={12} fill={FA} fontFamily="serif" opacity={0.6}>
361
+ "
362
+ </text>
363
+ <rect x={30} y={17} width={20} height={3.5} rx={1} fill={F2} />
364
+ <rect x={30} y={23} width={18} height={3} rx={1} fill={F1} />
365
+ <rect x={30} y={29} width={14} height={2.5} rx={1} fill={F1} opacity={0.7} />
366
+ </TemplateIcon>
367
+ ),
368
+ },
369
+ {
370
+ name: 'dataTable',
371
+ label: 'Data Table',
372
+ description: 'Renders tabular data in a clean, styled table with a header row.',
373
+ icon: (
374
+ <TemplateIcon>
375
+ <rect x={3} y={3} width={50} height={8} rx={1.5} fill={FA} opacity={0.7} />
376
+ <rect x={3} y={13} width={50} height={6} rx={1} fill={F1} opacity={0.5} />
377
+ <rect x={3} y={21} width={50} height={6} rx={1} fill={F1} opacity={0.35} />
378
+ <rect x={3} y={29} width={50} height={6} rx={1} fill={F1} opacity={0.5} />
379
+ <line x1={20} y1={3} x2={20} y2={35} stroke="white" strokeWidth={1} opacity={0.4} />
380
+ <line x1={37} y1={3} x2={37} y2={35} stroke="white" strokeWidth={1} opacity={0.4} />
381
+ </TemplateIcon>
382
+ ),
383
+ },
384
+ ];
385
+
386
+ /**
387
+ * Canonical template names known to the picker, in the order they
388
+ * appear in the gallery. Exported so callers can hand the same list to
389
+ * `recommendTemplatesForBlock()` and stay in sync with the visual
390
+ * order.
391
+ */
392
+ export const TEMPLATE_NAMES: readonly string[] = TEMPLATE_ENTRIES.map((e) => e.name);
393
+
394
+ /**
395
+ * Convert a camelCase template id to a human-readable label. Accepts both
396
+ * the canonical short ids (`title`, `quote`, `map`, `list`) and the
397
+ * legacy long ones (`titleBlock`, `quoteBlock`, `mapBlock`, `listBlock`)
398
+ * so existing documents keep showing a friendly label without first
399
+ * normalizing their annotations.
400
+ */
401
+ // eslint-disable-next-line react-refresh/only-export-components
402
+ export function templateLabel(name: string): string {
403
+ if (!name) return '— none —';
404
+ const resolved = TEMPLATE_NAME_ALIASES[name] ?? name;
405
+ const entry = TEMPLATE_ENTRIES.find((e) => e.name === resolved);
406
+ if (entry) return entry.label;
407
+ // Fallback: split camelCase
408
+ return resolved.replace(/([A-Z])/g, ' $1').replace(/^./, (s) => s.toUpperCase());
409
+ }
410
+
411
+ /**
412
+ * Legacy template id → canonical short id. Kept inline to avoid pulling
413
+ * the core package's runtime registry into the picker's bundle — the
414
+ * picker only needs this for label resolution.
415
+ */
416
+ const TEMPLATE_NAME_ALIASES: Readonly<Record<string, string>> = {
417
+ titleBlock: 'title',
418
+ quoteBlock: 'quote',
419
+ mapBlock: 'map',
420
+ listBlock: 'list',
421
+ };
422
+
423
+ // ── Component ─────────────────────────────────────────────────────
424
+
425
+ export interface TemplatePickerProps {
426
+ value: string;
427
+ onChange: (name: string) => void;
428
+ /** When true, shows only the trigger button (no popover) — used in the overflow menu. */
429
+ compact?: boolean;
430
+ /**
431
+ * Template names to surface in a "Recommended for this block" section
432
+ * above the full list. When omitted or empty, the gallery renders as a
433
+ * single ungrouped grid (legacy behavior).
434
+ */
435
+ recommended?: readonly string[];
436
+ }
437
+
438
+ export function TemplatePicker({ value, onChange, compact, recommended }: TemplatePickerProps) {
439
+ const [open, setOpen] = useState(false);
440
+ const [popoverStyle, setPopoverStyle] = useState<React.CSSProperties>({});
441
+ const containerRef = useRef<HTMLDivElement>(null);
442
+ const triggerRef = useRef<HTMLButtonElement>(null);
443
+
444
+ // Position the portal popover below the trigger button. Clamp the
445
+ // left edge so the gallery (up to 780px wide) doesn't overflow the
446
+ // viewport when the toolbar trigger sits near the right edge of the
447
+ // window — previously the popover was left-aligned to the trigger,
448
+ // which pushed half the cards off-screen at narrow widths.
449
+ const updatePosition = () => {
450
+ if (!triggerRef.current) return;
451
+ const rect = triggerRef.current.getBoundingClientRect();
452
+ const popoverEl = document.getElementById('squisq-template-gallery-portal');
453
+ // Use the actual rendered width if the popover is already mounted;
454
+ // otherwise fall back to the CSS-defined max so the first paint
455
+ // doesn't overflow either. Measure the portal element directly —
456
+ // its `firstElementChild` is the (small) "(none)" option, not the
457
+ // gallery itself.
458
+ const popoverWidth = popoverEl?.getBoundingClientRect().width ?? 780;
459
+ const margin = 8;
460
+ const maxLeft = Math.max(margin, window.innerWidth - popoverWidth - margin);
461
+ const left = Math.min(Math.max(margin, rect.left), maxLeft);
462
+ setPopoverStyle({
463
+ position: 'fixed',
464
+ top: rect.bottom + 6,
465
+ left,
466
+ zIndex: 9999,
467
+ });
468
+ };
469
+
470
+ const handleOpen = () => {
471
+ updatePosition();
472
+ setOpen((v) => !v);
473
+ };
474
+
475
+ // Close on outside click
476
+ useEffect(() => {
477
+ if (!open) return;
478
+ const handler = (e: MouseEvent) => {
479
+ const target = e.target as Node;
480
+ const inTrigger = triggerRef.current?.contains(target);
481
+ const inPopover = document.getElementById('squisq-template-gallery-portal')?.contains(target);
482
+ if (!inTrigger && !inPopover) {
483
+ setOpen(false);
484
+ }
485
+ };
486
+ document.addEventListener('mousedown', handler);
487
+ return () => document.removeEventListener('mousedown', handler);
488
+ }, [open]);
489
+
490
+ // Close on Escape
491
+ useEffect(() => {
492
+ if (!open) return;
493
+ const handler = (e: KeyboardEvent) => {
494
+ if (e.key === 'Escape') setOpen(false);
495
+ };
496
+ document.addEventListener('keydown', handler);
497
+ return () => document.removeEventListener('keydown', handler);
498
+ }, [open]);
499
+
500
+ // Reposition on scroll/resize while open
501
+ useEffect(() => {
502
+ if (!open) return;
503
+ // Reposition once on the next frame so the clamp uses the actual
504
+ // rendered popover width (the initial open() runs before mount).
505
+ requestAnimationFrame(updatePosition);
506
+ const handler = () => updatePosition();
507
+ window.addEventListener('scroll', handler, true);
508
+ window.addEventListener('resize', handler);
509
+ return () => {
510
+ window.removeEventListener('scroll', handler, true);
511
+ window.removeEventListener('resize', handler);
512
+ };
513
+ }, [open]);
514
+
515
+ const handleSelect = (name: string) => {
516
+ onChange(name);
517
+ setOpen(false);
518
+ };
519
+
520
+ const currentLabel = templateLabel(value);
521
+ const currentEntry: TemplateEntry =
522
+ (value && TEMPLATE_ENTRIES.find((e) => e.name === value)) || NONE_ENTRY;
523
+
524
+ if (compact) {
525
+ // In overflow menu, use a simple select for space efficiency
526
+ const all: TemplateEntry[] = [NONE_ENTRY, ...TEMPLATE_ENTRIES];
527
+ return (
528
+ <select
529
+ className="squisq-template-picker-select"
530
+ value={value}
531
+ onChange={(e) => onChange(e.target.value)}
532
+ >
533
+ {all.map((e) => (
534
+ <option key={e.name} value={e.name}>
535
+ {e.label}
536
+ </option>
537
+ ))}
538
+ </select>
539
+ );
540
+ }
541
+
542
+ const gallery = open
543
+ ? createPortal(
544
+ <TemplateGalleryBody
545
+ value={value}
546
+ onSelect={handleSelect}
547
+ style={popoverStyle}
548
+ recommended={recommended}
549
+ />,
550
+ document.body,
551
+ )
552
+ : null;
553
+
554
+ return (
555
+ <div className="squisq-template-picker-popover-host" ref={containerRef}>
556
+ <button
557
+ ref={triggerRef}
558
+ type="button"
559
+ className={`squisq-template-picker-trigger${open ? ' squisq-template-picker-trigger--open' : ''}`}
560
+ onClick={handleOpen}
561
+ aria-haspopup="listbox"
562
+ aria-expanded={open}
563
+ title="Choose block template"
564
+ >
565
+ <span className="squisq-template-picker-trigger-label">Block:</span>
566
+ <span className="squisq-template-picker-trigger-thumb" aria-hidden="true">
567
+ {currentEntry.icon}
568
+ </span>
569
+ <span className="squisq-template-picker-trigger-value">
570
+ {value ? currentLabel : '(No visual)'}
571
+ </span>
572
+ <svg
573
+ className="squisq-template-picker-trigger-caret"
574
+ width="10"
575
+ height="10"
576
+ viewBox="0 0 10 10"
577
+ fill="currentColor"
578
+ aria-hidden="true"
579
+ >
580
+ <path
581
+ d="M2 3.5l3 3 3-3"
582
+ stroke="currentColor"
583
+ strokeWidth="1.5"
584
+ strokeLinecap="round"
585
+ fill="none"
586
+ />
587
+ </svg>
588
+ </button>
589
+ {gallery}
590
+ </div>
591
+ );
592
+ }
593
+
594
+ // ── Reusable gallery body ──────────────────────────────────────────
595
+
596
+ /**
597
+ * The popover/grid markup shared by the toolbar `TemplatePicker` and the
598
+ * inline `TemplateBadgeMenu`. Renders all template cards plus the
599
+ * "(none)" option; no positioning logic — callers supply `style` (typically
600
+ * a `position: fixed` rect from `getBoundingClientRect()`).
601
+ */
602
+ function TemplateCard({
603
+ entry,
604
+ value,
605
+ onSelect,
606
+ }: {
607
+ entry: TemplateEntry;
608
+ value: string;
609
+ onSelect: (name: string) => void;
610
+ }) {
611
+ return (
612
+ <button
613
+ type="button"
614
+ role="option"
615
+ aria-selected={value === entry.name}
616
+ className={`squisq-template-gallery-card${value === entry.name ? ' squisq-template-gallery-card--selected' : ''}`}
617
+ onClick={() => onSelect(entry.name)}
618
+ title={entry.description}
619
+ >
620
+ <div className="squisq-template-gallery-card-icon">{entry.icon}</div>
621
+ <div className="squisq-template-gallery-card-body">
622
+ <span className="squisq-template-gallery-card-name">{entry.label}</span>
623
+ <span className="squisq-template-gallery-card-desc">{entry.description}</span>
624
+ </div>
625
+ </button>
626
+ );
627
+ }
628
+
629
+ function TemplateGalleryBody({
630
+ value,
631
+ onSelect,
632
+ style,
633
+ recommended,
634
+ }: {
635
+ value: string;
636
+ onSelect: (name: string) => void;
637
+ style: React.CSSProperties;
638
+ recommended?: readonly string[];
639
+ }) {
640
+ const recommendedSet = recommended && recommended.length > 0 ? new Set(recommended) : null;
641
+ const recommendedEntries = recommendedSet
642
+ ? TEMPLATE_ENTRIES.filter((e) => recommendedSet.has(e.name))
643
+ : [];
644
+ const restEntries = recommendedSet
645
+ ? TEMPLATE_ENTRIES.filter((e) => !recommendedSet.has(e.name))
646
+ : TEMPLATE_ENTRIES;
647
+ const segmented = recommendedEntries.length > 0;
648
+
649
+ return (
650
+ <div
651
+ id="squisq-template-gallery-portal"
652
+ className={`squisq-template-gallery${segmented ? ' squisq-template-gallery--segmented' : ''}`}
653
+ role="listbox"
654
+ aria-label="Block templates"
655
+ style={style}
656
+ >
657
+ <button
658
+ type="button"
659
+ role="option"
660
+ aria-selected={value === ''}
661
+ className={`squisq-template-gallery-none${value === '' ? ' squisq-template-gallery-card--selected' : ''}`}
662
+ onClick={() => onSelect('')}
663
+ >
664
+ {NONE_ENTRY.icon}
665
+ <span className="squisq-template-gallery-none-label">{NONE_ENTRY.label}</span>
666
+ <span className="squisq-template-gallery-none-desc">{NONE_ENTRY.description}</span>
667
+ </button>
668
+
669
+ {segmented && (
670
+ <div className="squisq-template-gallery-section">
671
+ <h3 className="squisq-template-gallery-section-title">Recommended for this block</h3>
672
+ <div className="squisq-template-gallery-grid">
673
+ {recommendedEntries.map((entry) => (
674
+ <TemplateCard key={entry.name} entry={entry} value={value} onSelect={onSelect} />
675
+ ))}
676
+ </div>
677
+ </div>
678
+ )}
679
+
680
+ {segmented ? (
681
+ <div className="squisq-template-gallery-section">
682
+ <h3 className="squisq-template-gallery-section-title">All templates</h3>
683
+ <div className="squisq-template-gallery-grid">
684
+ {restEntries.map((entry) => (
685
+ <TemplateCard key={entry.name} entry={entry} value={value} onSelect={onSelect} />
686
+ ))}
687
+ </div>
688
+ </div>
689
+ ) : (
690
+ <div className="squisq-template-gallery-grid">
691
+ {restEntries.map((entry) => (
692
+ <TemplateCard key={entry.name} entry={entry} value={value} onSelect={onSelect} />
693
+ ))}
694
+ </div>
695
+ )}
696
+ </div>
697
+ );
698
+ }
699
+
700
+ // ── Inline badge popover (anchored at a heading badge) ─────────────
701
+
702
+ export interface TemplateBadgePopoverProps {
703
+ /** DOMRect of the badge that triggered the popover (in viewport coords). */
704
+ anchorRect: DOMRect;
705
+ /** Currently active template name (empty string for none). */
706
+ value: string;
707
+ onChange: (name: string) => void;
708
+ onClose: () => void;
709
+ /** Optional list of template names to surface as "Recommended for this block". */
710
+ recommended?: readonly string[];
711
+ }
712
+
713
+ /**
714
+ * Standalone popover that mirrors the toolbar `TemplatePicker`'s gallery,
715
+ * but is anchored to a caller-supplied DOM rect (typically a clicked
716
+ * `.squisq-template-badge` span). Handles its own positioning, outside
717
+ * clicks, Escape, and viewport-edge clamping.
718
+ */
719
+ export function TemplateBadgePopover({
720
+ anchorRect,
721
+ value,
722
+ onChange,
723
+ onClose,
724
+ recommended,
725
+ }: TemplateBadgePopoverProps) {
726
+ const [style, setStyle] = useState<React.CSSProperties>(() => computePopoverStyle(anchorRect));
727
+
728
+ // Reposition once after mount using the actual rendered popover width.
729
+ useEffect(() => {
730
+ requestAnimationFrame(() => setStyle(computePopoverStyle(anchorRect)));
731
+ }, [anchorRect]);
732
+
733
+ // Outside click + Escape close
734
+ useEffect(() => {
735
+ const onKey = (e: KeyboardEvent) => {
736
+ if (e.key === 'Escape') onClose();
737
+ };
738
+ const onMouse = (e: MouseEvent) => {
739
+ const target = e.target as Node;
740
+ const inPopover = document.getElementById('squisq-template-gallery-portal')?.contains(target);
741
+ if (!inPopover) onClose();
742
+ };
743
+ // Defer the mousedown listener by one frame so the click that opened
744
+ // us doesn't immediately close us.
745
+ const id = requestAnimationFrame(() => {
746
+ document.addEventListener('mousedown', onMouse);
747
+ });
748
+ document.addEventListener('keydown', onKey);
749
+ return () => {
750
+ cancelAnimationFrame(id);
751
+ document.removeEventListener('mousedown', onMouse);
752
+ document.removeEventListener('keydown', onKey);
753
+ };
754
+ }, [onClose]);
755
+
756
+ const handleSelect = (name: string) => {
757
+ onChange(name);
758
+ onClose();
759
+ };
760
+
761
+ return createPortal(
762
+ <TemplateGalleryBody
763
+ value={value}
764
+ onSelect={handleSelect}
765
+ style={style}
766
+ recommended={recommended}
767
+ />,
768
+ document.body,
769
+ );
770
+ }
771
+
772
+ function computePopoverStyle(rect: DOMRect): React.CSSProperties {
773
+ // The portal *is* the gallery (`#squisq-template-gallery-portal` is the
774
+ // outer `.squisq-template-gallery` div). Measure it directly — using
775
+ // `firstElementChild` returns the "(none)" option, which is ~50px tall
776
+ // and made the "fits below" check incorrectly pass on tall galleries.
777
+ const popoverEl = document.getElementById('squisq-template-gallery-portal');
778
+ const popoverRect = popoverEl?.getBoundingClientRect();
779
+ const popoverWidth = popoverRect?.width ?? 780;
780
+ const popoverHeight = popoverRect?.height ?? 520;
781
+ const margin = 8;
782
+ const gap = 6;
783
+ const vw = window.innerWidth;
784
+ const vh = window.innerHeight;
785
+
786
+ // Vertical placement: prefer below the badge, fall back to above.
787
+ // If neither fits, center the popover in the viewport (dialog-style)
788
+ // so the gallery is always fully visible no matter where the chip
789
+ // sits in the editor (top, middle, bottom).
790
+ const spaceBelow = vh - rect.bottom - margin;
791
+ const spaceAbove = rect.top - margin;
792
+ let top: number;
793
+ let left: number;
794
+ if (popoverHeight + gap <= spaceBelow) {
795
+ top = rect.bottom + gap;
796
+ const maxLeft = Math.max(margin, vw - popoverWidth - margin);
797
+ left = Math.min(Math.max(margin, rect.left), maxLeft);
798
+ } else if (popoverHeight + gap <= spaceAbove) {
799
+ top = rect.top - popoverHeight - gap;
800
+ const maxLeft = Math.max(margin, vw - popoverWidth - margin);
801
+ left = Math.min(Math.max(margin, rect.left), maxLeft);
802
+ } else {
803
+ // Center it.
804
+ top = Math.max(margin, Math.floor((vh - popoverHeight) / 2));
805
+ left = Math.max(margin, Math.floor((vw - popoverWidth) / 2));
806
+ }
807
+
808
+ return {
809
+ position: 'fixed',
810
+ top,
811
+ left,
812
+ // Cap height so an oversized gallery still fits and scrolls
813
+ // gracefully instead of pushing past the viewport.
814
+ maxHeight: `${vh - 2 * margin}px`,
815
+ overflowY: 'auto',
816
+ zIndex: 9999,
817
+ };
818
+ }