@bendyline/squisq-editor-react 1.4.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 (446) 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 +173 -4
  10. package/dist/EditorShell.d.ts.map +1 -1
  11. package/dist/EditorShell.js +110 -10
  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/MentionExtension.js +10 -7
  42. package/dist/MentionExtension.js.map +1 -1
  43. package/dist/OutlinePanel.d.ts +17 -0
  44. package/dist/OutlinePanel.d.ts.map +1 -0
  45. package/dist/OutlinePanel.js +167 -0
  46. package/dist/OutlinePanel.js.map +1 -0
  47. package/dist/PlainHtmlPreview.d.ts +50 -0
  48. package/dist/PlainHtmlPreview.d.ts.map +1 -0
  49. package/dist/PlainHtmlPreview.js +155 -0
  50. package/dist/PlainHtmlPreview.js.map +1 -0
  51. package/dist/PreviewControls.d.ts +15 -1
  52. package/dist/PreviewControls.d.ts.map +1 -1
  53. package/dist/PreviewControls.js +75 -18
  54. package/dist/PreviewControls.js.map +1 -1
  55. package/dist/PreviewPanel.d.ts +11 -10
  56. package/dist/PreviewPanel.d.ts.map +1 -1
  57. package/dist/PreviewPanel.js +20 -17
  58. package/dist/PreviewPanel.js.map +1 -1
  59. package/dist/RawEditor.d.ts.map +1 -1
  60. package/dist/RawEditor.js +198 -4
  61. package/dist/RawEditor.js.map +1 -1
  62. package/dist/RecorderEntry.d.ts +24 -0
  63. package/dist/RecorderEntry.d.ts.map +1 -0
  64. package/dist/RecorderEntry.js +139 -0
  65. package/dist/RecorderEntry.js.map +1 -0
  66. package/dist/TemplateAnnotation.d.ts.map +1 -1
  67. package/dist/TemplateAnnotation.js +32 -6
  68. package/dist/TemplateAnnotation.js.map +1 -1
  69. package/dist/TemplatePicker.d.ts +53 -0
  70. package/dist/TemplatePicker.d.ts.map +1 -0
  71. package/dist/TemplatePicker.js +388 -0
  72. package/dist/TemplatePicker.js.map +1 -0
  73. package/dist/ThemeCustomizerPanel.d.ts +32 -0
  74. package/dist/ThemeCustomizerPanel.d.ts.map +1 -0
  75. package/dist/ThemeCustomizerPanel.js +256 -0
  76. package/dist/ThemeCustomizerPanel.js.map +1 -0
  77. package/dist/ThemePicker.d.ts +33 -0
  78. package/dist/ThemePicker.d.ts.map +1 -0
  79. package/dist/ThemePicker.js +148 -0
  80. package/dist/ThemePicker.js.map +1 -0
  81. package/dist/Toolbar.d.ts.map +1 -1
  82. package/dist/Toolbar.js +508 -33
  83. package/dist/Toolbar.js.map +1 -1
  84. package/dist/VersionHistoryPanel.d.ts +14 -0
  85. package/dist/VersionHistoryPanel.d.ts.map +1 -0
  86. package/dist/VersionHistoryPanel.js +147 -0
  87. package/dist/VersionHistoryPanel.js.map +1 -0
  88. package/dist/ViewMenuPanel.d.ts +13 -0
  89. package/dist/ViewMenuPanel.d.ts.map +1 -0
  90. package/dist/ViewMenuPanel.js +58 -0
  91. package/dist/ViewMenuPanel.js.map +1 -0
  92. package/dist/WysiwygEditor.d.ts.map +1 -1
  93. package/dist/WysiwygEditor.js +198 -9
  94. package/dist/WysiwygEditor.js.map +1 -1
  95. package/dist/__tests__/detectMarkdown.test.js +0 -14
  96. package/dist/__tests__/detectMarkdown.test.js.map +1 -1
  97. package/dist/__tests__/documentSettingsDialog.test.d.ts +2 -0
  98. package/dist/__tests__/documentSettingsDialog.test.d.ts.map +1 -0
  99. package/dist/__tests__/documentSettingsDialog.test.js +132 -0
  100. package/dist/__tests__/documentSettingsDialog.test.js.map +1 -0
  101. package/dist/__tests__/emojiPicker.test.d.ts +2 -0
  102. package/dist/__tests__/emojiPicker.test.d.ts.map +1 -0
  103. package/dist/__tests__/emojiPicker.test.js +111 -0
  104. package/dist/__tests__/emojiPicker.test.js.map +1 -0
  105. package/dist/__tests__/fileKind.test.js +13 -0
  106. package/dist/__tests__/fileKind.test.js.map +1 -1
  107. package/dist/__tests__/imageEditAffordance.test.d.ts +2 -0
  108. package/dist/__tests__/imageEditAffordance.test.d.ts.map +1 -0
  109. package/dist/__tests__/imageEditAffordance.test.js +188 -0
  110. package/dist/__tests__/imageEditAffordance.test.js.map +1 -0
  111. package/dist/__tests__/imageEditorShell.test.d.ts +2 -0
  112. package/dist/__tests__/imageEditorShell.test.d.ts.map +1 -0
  113. package/dist/__tests__/imageEditorShell.test.js +52 -0
  114. package/dist/__tests__/imageEditorShell.test.js.map +1 -0
  115. package/dist/__tests__/imageEditorState.test.d.ts +3 -0
  116. package/dist/__tests__/imageEditorState.test.d.ts.map +1 -0
  117. package/dist/__tests__/imageEditorState.test.js +148 -0
  118. package/dist/__tests__/imageEditorState.test.js.map +1 -0
  119. package/dist/__tests__/inlinePreviewGutter.test.d.ts +2 -0
  120. package/dist/__tests__/inlinePreviewGutter.test.d.ts.map +1 -0
  121. package/dist/__tests__/inlinePreviewGutter.test.js +51 -0
  122. package/dist/__tests__/inlinePreviewGutter.test.js.map +1 -0
  123. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts +2 -0
  124. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.d.ts.map +1 -0
  125. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js +63 -0
  126. package/dist/__tests__/inlinePreviewGutterAllBlocks.test.js.map +1 -0
  127. package/dist/__tests__/jsonEditor.test.d.ts +2 -0
  128. package/dist/__tests__/jsonEditor.test.d.ts.map +1 -0
  129. package/dist/__tests__/jsonEditor.test.js +134 -0
  130. package/dist/__tests__/jsonEditor.test.js.map +1 -0
  131. package/dist/__tests__/layersPanel.test.d.ts +2 -0
  132. package/dist/__tests__/layersPanel.test.d.ts.map +1 -0
  133. package/dist/__tests__/layersPanel.test.js +84 -0
  134. package/dist/__tests__/layersPanel.test.js.map +1 -0
  135. package/dist/__tests__/linkDialogDocPicker.test.d.ts +7 -0
  136. package/dist/__tests__/linkDialogDocPicker.test.d.ts.map +1 -0
  137. package/dist/__tests__/linkDialogDocPicker.test.js +75 -0
  138. package/dist/__tests__/linkDialogDocPicker.test.js.map +1 -0
  139. package/dist/__tests__/outlinePanel.test.d.ts +2 -0
  140. package/dist/__tests__/outlinePanel.test.d.ts.map +1 -0
  141. package/dist/__tests__/outlinePanel.test.js +68 -0
  142. package/dist/__tests__/outlinePanel.test.js.map +1 -0
  143. package/dist/__tests__/plainHtmlPreview.test.d.ts +2 -0
  144. package/dist/__tests__/plainHtmlPreview.test.d.ts.map +1 -0
  145. package/dist/__tests__/plainHtmlPreview.test.js +87 -0
  146. package/dist/__tests__/plainHtmlPreview.test.js.map +1 -0
  147. package/dist/__tests__/propertiesPanel.test.d.ts +2 -0
  148. package/dist/__tests__/propertiesPanel.test.d.ts.map +1 -0
  149. package/dist/__tests__/propertiesPanel.test.js +64 -0
  150. package/dist/__tests__/propertiesPanel.test.js.map +1 -0
  151. package/dist/__tests__/recorderFormats.test.d.ts +2 -0
  152. package/dist/__tests__/recorderFormats.test.d.ts.map +1 -0
  153. package/dist/__tests__/recorderFormats.test.js +121 -0
  154. package/dist/__tests__/recorderFormats.test.js.map +1 -0
  155. package/dist/__tests__/recorderTimingJson.test.d.ts +2 -0
  156. package/dist/__tests__/recorderTimingJson.test.d.ts.map +1 -0
  157. package/dist/__tests__/recorderTimingJson.test.js +37 -0
  158. package/dist/__tests__/recorderTimingJson.test.js.map +1 -0
  159. package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts +2 -0
  160. package/dist/__tests__/templateAnnotationRoundTrip.test.d.ts.map +1 -0
  161. package/dist/__tests__/templateAnnotationRoundTrip.test.js +31 -0
  162. package/dist/__tests__/templateAnnotationRoundTrip.test.js.map +1 -0
  163. package/dist/__tests__/tiptapBridge.test.js +13 -0
  164. package/dist/__tests__/tiptapBridge.test.js.map +1 -1
  165. package/dist/__tests__/useImageEditor.test.d.ts +2 -0
  166. package/dist/__tests__/useImageEditor.test.d.ts.map +1 -0
  167. package/dist/__tests__/useImageEditor.test.js +131 -0
  168. package/dist/__tests__/useImageEditor.test.js.map +1 -0
  169. package/dist/__tests__/useMediaRecorder.test.d.ts +2 -0
  170. package/dist/__tests__/useMediaRecorder.test.d.ts.map +1 -0
  171. package/dist/__tests__/useMediaRecorder.test.js +153 -0
  172. package/dist/__tests__/useMediaRecorder.test.js.map +1 -0
  173. package/dist/__tests__/versionHistory.test.d.ts +2 -0
  174. package/dist/__tests__/versionHistory.test.d.ts.map +1 -0
  175. package/dist/__tests__/versionHistory.test.js +124 -0
  176. package/dist/__tests__/versionHistory.test.js.map +1 -0
  177. package/dist/blockSlice.d.ts +24 -0
  178. package/dist/blockSlice.d.ts.map +1 -0
  179. package/dist/blockSlice.js +63 -0
  180. package/dist/blockSlice.js.map +1 -0
  181. package/dist/buildPreviewDoc.d.ts.map +1 -1
  182. package/dist/buildPreviewDoc.js +52 -2
  183. package/dist/buildPreviewDoc.js.map +1 -1
  184. package/dist/emojiData.d.ts +81 -0
  185. package/dist/emojiData.d.ts.map +1 -0
  186. package/dist/emojiData.js +1283 -0
  187. package/dist/emojiData.js.map +1 -0
  188. package/dist/fileKind.d.ts +6 -2
  189. package/dist/fileKind.d.ts.map +1 -1
  190. package/dist/fileKind.js +25 -4
  191. package/dist/fileKind.js.map +1 -1
  192. package/dist/hooks/useFileDrop.d.ts.map +1 -1
  193. package/dist/hooks/useFileDrop.js +40 -4
  194. package/dist/hooks/useFileDrop.js.map +1 -1
  195. package/dist/imageEditor/CanvasSurface.d.ts +31 -0
  196. package/dist/imageEditor/CanvasSurface.d.ts.map +1 -0
  197. package/dist/imageEditor/CanvasSurface.js +264 -0
  198. package/dist/imageEditor/CanvasSurface.js.map +1 -0
  199. package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts +39 -0
  200. package/dist/imageEditor/ImageVersionHistoryDropdown.d.ts.map +1 -0
  201. package/dist/imageEditor/ImageVersionHistoryDropdown.js +283 -0
  202. package/dist/imageEditor/ImageVersionHistoryDropdown.js.map +1 -0
  203. package/dist/imageEditor/LayersPanel.d.ts +14 -0
  204. package/dist/imageEditor/LayersPanel.d.ts.map +1 -0
  205. package/dist/imageEditor/LayersPanel.js +43 -0
  206. package/dist/imageEditor/LayersPanel.js.map +1 -0
  207. package/dist/imageEditor/PropertiesPanel.d.ts +14 -0
  208. package/dist/imageEditor/PropertiesPanel.d.ts.map +1 -0
  209. package/dist/imageEditor/PropertiesPanel.js +97 -0
  210. package/dist/imageEditor/PropertiesPanel.js.map +1 -0
  211. package/dist/imageEditor/Toolbar.d.ts +30 -0
  212. package/dist/imageEditor/Toolbar.d.ts.map +1 -0
  213. package/dist/imageEditor/Toolbar.js +108 -0
  214. package/dist/imageEditor/Toolbar.js.map +1 -0
  215. package/dist/imageEditor/icons.d.ts +24 -0
  216. package/dist/imageEditor/icons.d.ts.map +1 -0
  217. package/dist/imageEditor/icons.js +45 -0
  218. package/dist/imageEditor/icons.js.map +1 -0
  219. package/dist/imageEditor/layers/EditorImageLayer.d.ts +16 -0
  220. package/dist/imageEditor/layers/EditorImageLayer.d.ts.map +1 -0
  221. package/dist/imageEditor/layers/EditorImageLayer.js +37 -0
  222. package/dist/imageEditor/layers/EditorImageLayer.js.map +1 -0
  223. package/dist/imageEditor/layers/EditorShapeLayer.d.ts +15 -0
  224. package/dist/imageEditor/layers/EditorShapeLayer.d.ts.map +1 -0
  225. package/dist/imageEditor/layers/EditorShapeLayer.js +20 -0
  226. package/dist/imageEditor/layers/EditorShapeLayer.js.map +1 -0
  227. package/dist/imageEditor/layers/EditorTextLayer.d.ts +18 -0
  228. package/dist/imageEditor/layers/EditorTextLayer.d.ts.map +1 -0
  229. package/dist/imageEditor/layers/EditorTextLayer.js +13 -0
  230. package/dist/imageEditor/layers/EditorTextLayer.js.map +1 -0
  231. package/dist/imageEditor/layers/SelectionHandles.d.ts +17 -0
  232. package/dist/imageEditor/layers/SelectionHandles.d.ts.map +1 -0
  233. package/dist/imageEditor/layers/SelectionHandles.js +19 -0
  234. package/dist/imageEditor/layers/SelectionHandles.js.map +1 -0
  235. package/dist/imageEditor/state.d.ts +76 -0
  236. package/dist/imageEditor/state.d.ts.map +1 -0
  237. package/dist/imageEditor/state.js +87 -0
  238. package/dist/imageEditor/state.js.map +1 -0
  239. package/dist/imageEditor/useImageEditor.d.ts +53 -0
  240. package/dist/imageEditor/useImageEditor.d.ts.map +1 -0
  241. package/dist/imageEditor/useImageEditor.js +244 -0
  242. package/dist/imageEditor/useImageEditor.js.map +1 -0
  243. package/dist/imageEditor/useImageEditorTokens.d.ts +16 -0
  244. package/dist/imageEditor/useImageEditorTokens.d.ts.map +1 -0
  245. package/dist/imageEditor/useImageEditorTokens.js +45 -0
  246. package/dist/imageEditor/useImageEditorTokens.js.map +1 -0
  247. package/dist/index.d.ts +48 -1
  248. package/dist/index.d.ts.map +1 -1
  249. package/dist/index.js +36 -0
  250. package/dist/index.js.map +1 -1
  251. package/dist/jsonEditor/EmbeddedRichTextField.d.ts +15 -0
  252. package/dist/jsonEditor/EmbeddedRichTextField.d.ts.map +1 -0
  253. package/dist/jsonEditor/EmbeddedRichTextField.js +74 -0
  254. package/dist/jsonEditor/EmbeddedRichTextField.js.map +1 -0
  255. package/dist/jsonEditor/JsonEditor.d.ts +36 -0
  256. package/dist/jsonEditor/JsonEditor.d.ts.map +1 -0
  257. package/dist/jsonEditor/JsonEditor.js +15 -0
  258. package/dist/jsonEditor/JsonEditor.js.map +1 -0
  259. package/dist/jsonEditor/JsonEditorContext.d.ts +28 -0
  260. package/dist/jsonEditor/JsonEditorContext.d.ts.map +1 -0
  261. package/dist/jsonEditor/JsonEditorContext.js +41 -0
  262. package/dist/jsonEditor/JsonEditorContext.js.map +1 -0
  263. package/dist/jsonEditor/RenderNode.d.ts +16 -0
  264. package/dist/jsonEditor/RenderNode.d.ts.map +1 -0
  265. package/dist/jsonEditor/RenderNode.js +32 -0
  266. package/dist/jsonEditor/RenderNode.js.map +1 -0
  267. package/dist/jsonEditor/editors.d.ts +36 -0
  268. package/dist/jsonEditor/editors.d.ts.map +1 -0
  269. package/dist/jsonEditor/editors.js +347 -0
  270. package/dist/jsonEditor/editors.js.map +1 -0
  271. package/dist/jsonEditor/index.d.ts +3 -0
  272. package/dist/jsonEditor/index.d.ts.map +1 -0
  273. package/dist/jsonEditor/index.js +2 -0
  274. package/dist/jsonEditor/index.js.map +1 -0
  275. package/dist/jsonEditor/useJsonEditorTokens.d.ts +13 -0
  276. package/dist/jsonEditor/useJsonEditorTokens.d.ts.map +1 -0
  277. package/dist/jsonEditor/useJsonEditorTokens.js +38 -0
  278. package/dist/jsonEditor/useJsonEditorTokens.js.map +1 -0
  279. package/dist/recorder/RecorderButton.d.ts +31 -0
  280. package/dist/recorder/RecorderButton.d.ts.map +1 -0
  281. package/dist/recorder/RecorderButton.js +24 -0
  282. package/dist/recorder/RecorderButton.js.map +1 -0
  283. package/dist/recorder/RecorderModal.d.ts +59 -0
  284. package/dist/recorder/RecorderModal.d.ts.map +1 -0
  285. package/dist/recorder/RecorderModal.js +333 -0
  286. package/dist/recorder/RecorderModal.js.map +1 -0
  287. package/dist/recorder/RecorderPanel.d.ts +25 -0
  288. package/dist/recorder/RecorderPanel.d.ts.map +1 -0
  289. package/dist/recorder/RecorderPanel.js +30 -0
  290. package/dist/recorder/RecorderPanel.js.map +1 -0
  291. package/dist/recorder/formats.d.ts +51 -0
  292. package/dist/recorder/formats.d.ts.map +1 -0
  293. package/dist/recorder/formats.js +144 -0
  294. package/dist/recorder/formats.js.map +1 -0
  295. package/dist/recorder/hooks/useMediaRecorder.d.ts +90 -0
  296. package/dist/recorder/hooks/useMediaRecorder.d.ts.map +1 -0
  297. package/dist/recorder/hooks/useMediaRecorder.js +277 -0
  298. package/dist/recorder/hooks/useMediaRecorder.js.map +1 -0
  299. package/dist/recorder/hooks/useStreamPreview.d.ts +22 -0
  300. package/dist/recorder/hooks/useStreamPreview.d.ts.map +1 -0
  301. package/dist/recorder/hooks/useStreamPreview.js +44 -0
  302. package/dist/recorder/hooks/useStreamPreview.js.map +1 -0
  303. package/dist/recorder/sources/cameraStream.d.ts +22 -0
  304. package/dist/recorder/sources/cameraStream.d.ts.map +1 -0
  305. package/dist/recorder/sources/cameraStream.js +24 -0
  306. package/dist/recorder/sources/cameraStream.js.map +1 -0
  307. package/dist/recorder/sources/micStream.d.ts +15 -0
  308. package/dist/recorder/sources/micStream.d.ts.map +1 -0
  309. package/dist/recorder/sources/micStream.js +24 -0
  310. package/dist/recorder/sources/micStream.js.map +1 -0
  311. package/dist/recorder/sources/screenStream.d.ts +53 -0
  312. package/dist/recorder/sources/screenStream.d.ts.map +1 -0
  313. package/dist/recorder/sources/screenStream.js +114 -0
  314. package/dist/recorder/sources/screenStream.js.map +1 -0
  315. package/dist/recorder/timingJson.d.ts +51 -0
  316. package/dist/recorder/timingJson.d.ts.map +1 -0
  317. package/dist/recorder/timingJson.js +42 -0
  318. package/dist/recorder/timingJson.js.map +1 -0
  319. package/dist/tiptap/TiptapAudio.d.ts +26 -0
  320. package/dist/tiptap/TiptapAudio.d.ts.map +1 -0
  321. package/dist/tiptap/TiptapAudio.js +58 -0
  322. package/dist/tiptap/TiptapAudio.js.map +1 -0
  323. package/dist/tiptap/TiptapVideo.d.ts +30 -0
  324. package/dist/tiptap/TiptapVideo.d.ts.map +1 -0
  325. package/dist/tiptap/TiptapVideo.js +66 -0
  326. package/dist/tiptap/TiptapVideo.js.map +1 -0
  327. package/dist/tiptap/useResolvedMediaSrc.d.ts +2 -0
  328. package/dist/tiptap/useResolvedMediaSrc.d.ts.map +1 -0
  329. package/dist/tiptap/useResolvedMediaSrc.js +42 -0
  330. package/dist/tiptap/useResolvedMediaSrc.js.map +1 -0
  331. package/dist/tiptapBridge.d.ts.map +1 -1
  332. package/dist/tiptapBridge.js +171 -14
  333. package/dist/tiptapBridge.js.map +1 -1
  334. package/dist/useHeadingLayout.d.ts +54 -0
  335. package/dist/useHeadingLayout.d.ts.map +1 -0
  336. package/dist/useHeadingLayout.js +260 -0
  337. package/dist/useHeadingLayout.js.map +1 -0
  338. package/dist/utils/collectInlineFontAwesomeCss.d.ts +21 -0
  339. package/dist/utils/collectInlineFontAwesomeCss.d.ts.map +1 -0
  340. package/dist/utils/collectInlineFontAwesomeCss.js +68 -0
  341. package/dist/utils/collectInlineFontAwesomeCss.js.map +1 -0
  342. package/dist/utils/dropUtils.d.ts +21 -2
  343. package/dist/utils/dropUtils.d.ts.map +1 -1
  344. package/dist/utils/dropUtils.js +43 -4
  345. package/dist/utils/dropUtils.js.map +1 -1
  346. package/dist/utils/normalizeMalformedAssetUrl.d.ts +15 -0
  347. package/dist/utils/normalizeMalformedAssetUrl.d.ts.map +1 -0
  348. package/dist/utils/normalizeMalformedAssetUrl.js +27 -0
  349. package/dist/utils/normalizeMalformedAssetUrl.js.map +1 -0
  350. package/package.json +8 -5
  351. package/src/DocumentSettingsDialog.tsx +266 -0
  352. package/src/EditorContext.tsx +534 -10
  353. package/src/EditorShell.tsx +571 -55
  354. package/src/EmojiPicker.tsx +332 -0
  355. package/src/ImageEditor.tsx +327 -0
  356. package/src/ImageNodeView.tsx +222 -21
  357. package/src/ImageViewer.tsx +221 -0
  358. package/src/InlineIcon.ts +84 -0
  359. package/src/InlinePreviewGutter.tsx +582 -0
  360. package/src/LinkDialog.tsx +276 -0
  361. package/src/MentionExtension.tsx +10 -7
  362. package/src/OutlinePanel.tsx +295 -0
  363. package/src/PlainHtmlPreview.tsx +211 -0
  364. package/src/PreviewControls.tsx +130 -24
  365. package/src/PreviewPanel.tsx +38 -21
  366. package/src/RawEditor.tsx +215 -4
  367. package/src/RecorderEntry.tsx +164 -0
  368. package/src/TemplateAnnotation.ts +32 -6
  369. package/src/TemplatePicker.tsx +818 -0
  370. package/src/ThemeCustomizerPanel.tsx +595 -0
  371. package/src/ThemePicker.tsx +319 -0
  372. package/src/Toolbar.tsx +708 -111
  373. package/src/VersionHistoryPanel.tsx +329 -0
  374. package/src/ViewMenuPanel.tsx +188 -0
  375. package/src/WysiwygEditor.tsx +229 -9
  376. package/src/__tests__/detectMarkdown.test.ts +0 -15
  377. package/src/__tests__/documentSettingsDialog.test.tsx +147 -0
  378. package/src/__tests__/emojiPicker.test.tsx +133 -0
  379. package/src/__tests__/fileKind.test.ts +16 -0
  380. package/src/__tests__/imageEditAffordance.test.tsx +268 -0
  381. package/src/__tests__/imageEditorShell.test.tsx +57 -0
  382. package/src/__tests__/imageEditorState.test.ts +171 -0
  383. package/src/__tests__/inlinePreviewGutter.test.tsx +62 -0
  384. package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +103 -0
  385. package/src/__tests__/jsonEditor.test.tsx +168 -0
  386. package/src/__tests__/layersPanel.test.tsx +97 -0
  387. package/src/__tests__/linkDialogDocPicker.test.tsx +137 -0
  388. package/src/__tests__/outlinePanel.test.tsx +79 -0
  389. package/src/__tests__/plainHtmlPreview.test.tsx +107 -0
  390. package/src/__tests__/propertiesPanel.test.tsx +69 -0
  391. package/src/__tests__/recorderFormats.test.ts +146 -0
  392. package/src/__tests__/recorderTimingJson.test.ts +41 -0
  393. package/src/__tests__/templateAnnotationRoundTrip.test.ts +34 -0
  394. package/src/__tests__/tiptapBridge.test.ts +15 -0
  395. package/src/__tests__/useImageEditor.test.tsx +159 -0
  396. package/src/__tests__/useMediaRecorder.test.ts +186 -0
  397. package/src/__tests__/versionHistory.test.tsx +197 -0
  398. package/src/blockSlice.ts +75 -0
  399. package/src/buildPreviewDoc.ts +61 -6
  400. package/src/emojiData.ts +1337 -0
  401. package/src/fileKind.ts +30 -6
  402. package/src/hooks/useFileDrop.ts +40 -4
  403. package/src/imageEditor/CanvasSurface.tsx +402 -0
  404. package/src/imageEditor/ImageVersionHistoryDropdown.tsx +396 -0
  405. package/src/imageEditor/LayersPanel.tsx +143 -0
  406. package/src/imageEditor/PropertiesPanel.tsx +428 -0
  407. package/src/imageEditor/Toolbar.tsx +242 -0
  408. package/src/imageEditor/icons.tsx +144 -0
  409. package/src/imageEditor/image-editor.css +450 -0
  410. package/src/imageEditor/layers/EditorImageLayer.tsx +45 -0
  411. package/src/imageEditor/layers/EditorShapeLayer.tsx +62 -0
  412. package/src/imageEditor/layers/EditorTextLayer.tsx +45 -0
  413. package/src/imageEditor/layers/SelectionHandles.tsx +86 -0
  414. package/src/imageEditor/state.ts +153 -0
  415. package/src/imageEditor/useImageEditor.ts +328 -0
  416. package/src/imageEditor/useImageEditorTokens.ts +70 -0
  417. package/src/index.ts +82 -0
  418. package/src/jsonEditor/EmbeddedRichTextField.tsx +81 -0
  419. package/src/jsonEditor/JsonEditor.tsx +81 -0
  420. package/src/jsonEditor/JsonEditorContext.tsx +75 -0
  421. package/src/jsonEditor/RenderNode.tsx +66 -0
  422. package/src/jsonEditor/editors.tsx +678 -0
  423. package/src/jsonEditor/index.ts +2 -0
  424. package/src/jsonEditor/json-editor.css +463 -0
  425. package/src/jsonEditor/useJsonEditorTokens.ts +63 -0
  426. package/src/recorder/RecorderButton.tsx +72 -0
  427. package/src/recorder/RecorderModal.tsx +596 -0
  428. package/src/recorder/RecorderPanel.tsx +93 -0
  429. package/src/recorder/formats.ts +159 -0
  430. package/src/recorder/hooks/useMediaRecorder.ts +378 -0
  431. package/src/recorder/hooks/useStreamPreview.ts +47 -0
  432. package/src/recorder/sources/cameraStream.ts +32 -0
  433. package/src/recorder/sources/micStream.ts +25 -0
  434. package/src/recorder/sources/screenStream.ts +162 -0
  435. package/src/recorder/timingJson.ts +66 -0
  436. package/src/styles/editor.css +2490 -51
  437. package/src/styles/image-edit-affordance.css +201 -0
  438. package/src/styles/index.css +10 -0
  439. package/src/tiptap/TiptapAudio.tsx +86 -0
  440. package/src/tiptap/TiptapVideo.tsx +119 -0
  441. package/src/tiptap/useResolvedMediaSrc.ts +47 -0
  442. package/src/tiptapBridge.ts +188 -20
  443. package/src/useHeadingLayout.ts +294 -0
  444. package/src/utils/collectInlineFontAwesomeCss.ts +69 -0
  445. package/src/utils/dropUtils.ts +54 -6
  446. package/src/utils/normalizeMalformedAssetUrl.ts +22 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * ImageVersionHistoryDropdown
3
+ *
4
+ * Toolbar-anchored popover that lists image-edit version snapshots from
5
+ * the sidecar and lets the user revert to any of them.
6
+ *
7
+ * For each snapshot we render a small thumbnail (rasterized via
8
+ * `exportImageEditDoc` at a thumbnail-sized scale) plus a one-line diff
9
+ * summary describing what changed relative to the *previous* (older)
10
+ * snapshot — e.g. "Resized 4800×920 → 1200×920", "Added text layer",
11
+ * "Removed Background". This makes it possible to identify the right
12
+ * snapshot to revert to without having to revert blindly.
13
+ *
14
+ * Thumbnails and diffs are computed lazily and cached by version path.
15
+ * Loads happen in the background after the popover opens.
16
+ */
17
+
18
+ import { useCallback, useEffect, useRef, useState } from 'react';
19
+ import {
20
+ exportImageEditDoc,
21
+ type ImageEditDoc,
22
+ type ImageEditLayer,
23
+ type ImageEditVersionManager,
24
+ } from '@bendyline/squisq/imageEdit';
25
+ import type { ContentContainer } from '@bendyline/squisq/storage';
26
+ import type { Version } from '@bendyline/squisq/versions';
27
+
28
+ interface Props {
29
+ versioning: ImageEditVersionManager;
30
+ /**
31
+ * Sidecar container backing the snapshots — same one passed to the
32
+ * `<ImageEditor>`. Required for thumbnail rendering since
33
+ * `exportImageEditDoc` reads asset bytes from the container.
34
+ */
35
+ container: ContentContainer;
36
+ /** Called when the user picks a snapshot to load into the editor. */
37
+ onRevert: (version: Version) => void | Promise<void>;
38
+ /**
39
+ * Persistence revision counter. When the host saves a new snapshot
40
+ * (e.g. on flush), bumping this triggers a re-list so the popover
41
+ * stays in sync without polling.
42
+ */
43
+ refreshKey?: number;
44
+ }
45
+
46
+ interface VersionMeta {
47
+ doc: ImageEditDoc;
48
+ thumbUrl: string | null;
49
+ }
50
+
51
+ const THUMB_MAX_DIM = 96;
52
+
53
+ export function ImageVersionHistoryDropdown({
54
+ versioning,
55
+ container,
56
+ onRevert,
57
+ refreshKey,
58
+ }: Props) {
59
+ const [open, setOpen] = useState(false);
60
+ const [versions, setVersions] = useState<Version[]>([]);
61
+ const [loading, setLoading] = useState(false);
62
+ const [busyTimestamp, setBusyTimestamp] = useState<number | null>(null);
63
+ // Per-version metadata (doc + thumbnail). Keyed by version.path.
64
+ const [meta, setMeta] = useState<Record<string, VersionMeta>>({});
65
+ const popoverRef = useRef<HTMLDivElement | null>(null);
66
+ const triggerRef = useRef<HTMLButtonElement | null>(null);
67
+ // Track object URLs so we can revoke them on unmount / cache reset.
68
+ const urlsRef = useRef<Set<string>>(new Set());
69
+
70
+ // Re-load the list when the popover opens or when the host bumps refreshKey.
71
+ useEffect(() => {
72
+ if (!open) return;
73
+ let cancelled = false;
74
+ setLoading(true);
75
+ versioning
76
+ .listVersions()
77
+ .then((list) => {
78
+ if (cancelled) return;
79
+ // Newest first.
80
+ const sorted = [...list].sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
81
+ setVersions(sorted);
82
+ })
83
+ .catch(() => {
84
+ if (cancelled) return;
85
+ setVersions([]);
86
+ })
87
+ .finally(() => {
88
+ if (!cancelled) setLoading(false);
89
+ });
90
+ return () => {
91
+ cancelled = true;
92
+ };
93
+ }, [open, versioning, refreshKey]);
94
+
95
+ // Lazily load doc + thumbnail for any version we don't have cached yet.
96
+ // We do this sequentially to avoid hammering the canvas pipeline.
97
+ useEffect(() => {
98
+ if (!open) return;
99
+ if (versions.length === 0) return;
100
+ let cancelled = false;
101
+ (async () => {
102
+ for (const v of versions) {
103
+ if (cancelled) return;
104
+ if (meta[v.path]) continue;
105
+ try {
106
+ const doc = await versioning.readVersion(v);
107
+ if (cancelled) return;
108
+ if (!doc) {
109
+ setMeta((m) => ({ ...m, [v.path]: { doc: emptyDoc(), thumbUrl: null } }));
110
+ continue;
111
+ }
112
+ let thumbUrl: string | null = null;
113
+ try {
114
+ const scale = computeThumbScale(doc.canvas.width, doc.canvas.height);
115
+ const blob = await exportImageEditDoc(doc, container, {
116
+ format: 'png',
117
+ scale,
118
+ });
119
+ if (cancelled) return;
120
+ thumbUrl = URL.createObjectURL(blob);
121
+ urlsRef.current.add(thumbUrl);
122
+ } catch {
123
+ // Best-effort thumbnail — fall back to a placeholder.
124
+ thumbUrl = null;
125
+ }
126
+ setMeta((m) => ({ ...m, [v.path]: { doc, thumbUrl } }));
127
+ } catch {
128
+ // Skip on read error; the row will render without a thumb.
129
+ }
130
+ }
131
+ })();
132
+ return () => {
133
+ cancelled = true;
134
+ };
135
+ }, [open, versions, versioning, container, meta]);
136
+
137
+ // Revoke object URLs on unmount.
138
+ useEffect(() => {
139
+ const urls = urlsRef.current;
140
+ return () => {
141
+ for (const url of urls) URL.revokeObjectURL(url);
142
+ urls.clear();
143
+ };
144
+ }, []);
145
+
146
+ // When refreshKey changes, blow away the cache so newly-saved
147
+ // snapshots are re-thumbnailed (and old thumbs aren't stale).
148
+ useEffect(() => {
149
+ for (const url of urlsRef.current) URL.revokeObjectURL(url);
150
+ urlsRef.current.clear();
151
+ setMeta({});
152
+ }, [refreshKey]);
153
+
154
+ // Close on outside click / Escape.
155
+ useEffect(() => {
156
+ if (!open) return;
157
+ function onDocClick(e: MouseEvent) {
158
+ const t = e.target as Node | null;
159
+ if (!t) return;
160
+ if (popoverRef.current?.contains(t)) return;
161
+ if (triggerRef.current?.contains(t)) return;
162
+ setOpen(false);
163
+ }
164
+ function onKey(e: KeyboardEvent) {
165
+ if (e.key === 'Escape') setOpen(false);
166
+ }
167
+ document.addEventListener('mousedown', onDocClick);
168
+ document.addEventListener('keydown', onKey);
169
+ return () => {
170
+ document.removeEventListener('mousedown', onDocClick);
171
+ document.removeEventListener('keydown', onKey);
172
+ };
173
+ }, [open]);
174
+
175
+ const handleRevert = useCallback(
176
+ async (v: Version) => {
177
+ setBusyTimestamp(v.timestamp.getTime());
178
+ try {
179
+ await onRevert(v);
180
+ setOpen(false);
181
+ } catch (err: unknown) {
182
+ console.warn(
183
+ '[squisq-editor] image-edit revert failed:',
184
+ err instanceof Error ? err.message : err,
185
+ );
186
+ } finally {
187
+ setBusyTimestamp(null);
188
+ }
189
+ },
190
+ [onRevert],
191
+ );
192
+
193
+ return (
194
+ <div className="squisq-image-editor-version-dropdown">
195
+ <button
196
+ ref={triggerRef}
197
+ type="button"
198
+ className="squisq-image-editor-tool-button squisq-image-editor-tool-button--with-label"
199
+ onClick={() => setOpen((o) => !o)}
200
+ aria-expanded={open}
201
+ aria-haspopup="menu"
202
+ title="Version history"
203
+ data-testid="image-editor-history-trigger"
204
+ >
205
+ <span>History</span>
206
+ <span aria-hidden="true" style={{ fontSize: '0.8em' }}>
207
+
208
+ </span>
209
+ </button>
210
+ {open && (
211
+ <div
212
+ ref={popoverRef}
213
+ className="squisq-image-editor-version-popover"
214
+ role="menu"
215
+ data-testid="image-editor-history-popover"
216
+ >
217
+ <div className="squisq-image-editor-version-popover__title">Version history</div>
218
+ {loading && <div className="squisq-image-editor-version-popover__empty">Loading…</div>}
219
+ {!loading && versions.length === 0 && (
220
+ <div className="squisq-image-editor-version-popover__empty">No snapshots yet</div>
221
+ )}
222
+ {!loading && versions.length > 0 && (
223
+ <ul className="squisq-image-editor-version-popover__list">
224
+ {versions.map((v, i) => {
225
+ const ts = v.timestamp.getTime();
226
+ const m = meta[v.path];
227
+ const isCurrent = i === 0;
228
+ const isOriginal = i + 1 >= versions.length;
229
+ // Diff is computed against the *next* (older) version in
230
+ // the newest-first list. The oldest snapshot has no
231
+ // older neighbor, so we label it "Original".
232
+ const olderMeta = i + 1 < versions.length ? meta[versions[i + 1]!.path] : undefined;
233
+ const summary = isOriginal
234
+ ? 'Original'
235
+ : m && olderMeta
236
+ ? summarizeDiff(olderMeta.doc, m.doc)
237
+ : '';
238
+ return (
239
+ <li
240
+ key={ts}
241
+ className={
242
+ 'squisq-image-editor-version-popover__row' +
243
+ (isCurrent ? ' squisq-image-editor-version-popover__row--current' : '')
244
+ }
245
+ >
246
+ <div className="squisq-image-editor-version-popover__thumb">
247
+ {m?.thumbUrl ? (
248
+ <img
249
+ src={m.thumbUrl}
250
+ alt=""
251
+ className="squisq-image-editor-version-popover__thumb-img"
252
+ />
253
+ ) : (
254
+ <div
255
+ className="squisq-image-editor-version-popover__thumb-placeholder"
256
+ aria-hidden="true"
257
+ />
258
+ )}
259
+ </div>
260
+ <div className="squisq-image-editor-version-popover__info">
261
+ <div className="squisq-image-editor-version-popover__when">
262
+ {isCurrent && (
263
+ <span className="squisq-image-editor-version-popover__badge">
264
+ Current
265
+ </span>
266
+ )}
267
+ {formatTimestamp(v.timestamp)}
268
+ </div>
269
+ <div className="squisq-image-editor-version-popover__summary" title={summary}>
270
+ {summary || (m ? '' : 'Loading…')}
271
+ </div>
272
+ </div>
273
+ <button
274
+ type="button"
275
+ className="squisq-image-editor-tool-button"
276
+ onClick={() => handleRevert(v)}
277
+ disabled={isCurrent || busyTimestamp === ts}
278
+ title={isCurrent ? 'This is the current version' : 'Revert to this version'}
279
+ >
280
+ {busyTimestamp === ts ? 'Loading…' : 'Revert'}
281
+ </button>
282
+ </li>
283
+ );
284
+ })}
285
+ </ul>
286
+ )}
287
+ </div>
288
+ )}
289
+ </div>
290
+ );
291
+ }
292
+
293
+ /**
294
+ * Pick a `scale` factor for `exportImageEditDoc` so the rendered
295
+ * thumbnail's longest side is at most `THUMB_MAX_DIM` pixels. Always
296
+ * ≤1 to avoid up-rendering small canvases.
297
+ */
298
+ function computeThumbScale(width: number, height: number): number {
299
+ const longest = Math.max(width, height);
300
+ if (longest <= THUMB_MAX_DIM) return 1;
301
+ return THUMB_MAX_DIM / longest;
302
+ }
303
+
304
+ /**
305
+ * Convert a Date to a human-readable local time.
306
+ */
307
+ function formatTimestamp(stamp: Date): string {
308
+ if (Number.isNaN(stamp.getTime())) return String(stamp);
309
+ return stamp.toLocaleString(undefined, {
310
+ month: 'short',
311
+ day: 'numeric',
312
+ hour: 'numeric',
313
+ minute: '2-digit',
314
+ second: '2-digit',
315
+ });
316
+ }
317
+
318
+ /**
319
+ * Build a one-line, human-readable summary of what changed between
320
+ * two snapshots. Reports (in priority order):
321
+ *
322
+ * 1. Canvas size change — "Resized 4800×920 → 1200×920"
323
+ * 2. Layer additions/removals — "Added text", "Removed Background"
324
+ * 3. Per-layer position/size — "Moved Text", "Resized Image"
325
+ * 4. Per-layer content/style — "Edited Text"
326
+ *
327
+ * Returns "No changes" if the two docs are byte-equivalent in the
328
+ * fields we compare.
329
+ */
330
+ function summarizeDiff(prev: ImageEditDoc, next: ImageEditDoc): string {
331
+ const parts: string[] = [];
332
+
333
+ // 1. Canvas size.
334
+ if (prev.canvas.width !== next.canvas.width || prev.canvas.height !== next.canvas.height) {
335
+ parts.push(
336
+ `Resized ${prev.canvas.width}×${prev.canvas.height} → ${next.canvas.width}×${next.canvas.height}`,
337
+ );
338
+ } else if (prev.canvas.background !== next.canvas.background) {
339
+ parts.push('Changed background');
340
+ }
341
+
342
+ // 2. Layer add / remove (by id).
343
+ const prevIds = new Set(prev.layers.map((l) => l.id));
344
+ const nextIds = new Set(next.layers.map((l) => l.id));
345
+ const added = next.layers.filter((l) => !prevIds.has(l.id));
346
+ const removed = prev.layers.filter((l) => !nextIds.has(l.id));
347
+ for (const l of added) parts.push(`Added ${describeLayer(l)}`);
348
+ for (const l of removed) parts.push(`Removed ${describeLayer(l)}`);
349
+
350
+ // 3 & 4. Per-layer changes for layers present in both.
351
+ for (const n of next.layers) {
352
+ const p = prev.layers.find((l) => l.id === n.id);
353
+ if (!p) continue;
354
+ const change = describeLayerChange(p, n);
355
+ if (change) parts.push(change);
356
+ }
357
+
358
+ if (parts.length === 0) return 'No changes';
359
+ // Cap to a sane length — the popover row is one line.
360
+ return parts.slice(0, 3).join(' · ') + (parts.length > 3 ? ` (+${parts.length - 3})` : '');
361
+ }
362
+
363
+ function describeLayer(layer: ImageEditLayer): string {
364
+ const name = layer.name?.trim();
365
+ return name && name.length > 0 ? `“${name}”` : layer.type;
366
+ }
367
+
368
+ function describeLayerChange(p: ImageEditLayer, n: ImageEditLayer): string | null {
369
+ const label = describeLayer(n);
370
+ const posChanged =
371
+ p.position.x !== n.position.x ||
372
+ p.position.y !== n.position.y ||
373
+ p.position.width !== n.position.width ||
374
+ p.position.height !== n.position.height;
375
+ const sizeChanged =
376
+ p.position.width !== n.position.width || p.position.height !== n.position.height;
377
+ // Treat content + style as a single "edited" bucket.
378
+ const contentChanged = JSON.stringify(p.content) !== JSON.stringify(n.content);
379
+
380
+ if (sizeChanged && !contentChanged) return `Resized ${label}`;
381
+ if (posChanged && !contentChanged) return `Moved ${label}`;
382
+ if (contentChanged) return `Edited ${label}`;
383
+ return null;
384
+ }
385
+
386
+ /**
387
+ * Fallback used when a snapshot fails to load — keeps render code
388
+ * branch-free without polluting the diff summary with errors.
389
+ */
390
+ function emptyDoc(): ImageEditDoc {
391
+ return {
392
+ version: 1,
393
+ canvas: { width: 0, height: 0, background: 'transparent' },
394
+ layers: [],
395
+ };
396
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * LayersPanel — list of layers with visibility / lock / reorder / delete
3
+ * controls. Newest layer is rendered last (top of the SVG stack), so the
4
+ * panel shows the array reversed so "top" appears at the top of the list.
5
+ */
6
+
7
+ import type { ImageEditDoc } from '@bendyline/squisq/schemas';
8
+ import type { ImageEditorAction } from './state.js';
9
+ import {
10
+ ChevronDownIcon,
11
+ ChevronUpIcon,
12
+ CloseIcon,
13
+ EyeIcon,
14
+ EyeOffIcon,
15
+ LockIcon,
16
+ UnlockIcon,
17
+ } from './icons.js';
18
+
19
+ export interface LayersPanelProps {
20
+ doc: ImageEditDoc;
21
+ selectedLayerId: string | null;
22
+ dispatch: (action: ImageEditorAction) => void;
23
+ }
24
+
25
+ export function LayersPanel({ doc, selectedLayerId, dispatch }: LayersPanelProps) {
26
+ // Visual order: top of stack first.
27
+ const ordered = doc.layers.slice().reverse();
28
+
29
+ return (
30
+ <div className="squisq-image-editor-layers" data-testid="image-editor-layers">
31
+ <div className="squisq-image-editor-panel-header">Layers</div>
32
+ <ul className="squisq-image-editor-layer-list">
33
+ {ordered.length === 0 && <li className="squisq-image-editor-layer-empty">No layers yet</li>}
34
+ {ordered.map((layer) => {
35
+ const visible = layer.visible !== false;
36
+ const locked = !!layer.locked;
37
+ const stackIndex = doc.layers.findIndex((l) => l.id === layer.id);
38
+ const canMoveUp = stackIndex < doc.layers.length - 1;
39
+ const canMoveDown = stackIndex > 0;
40
+ const isSelected = selectedLayerId === layer.id;
41
+
42
+ return (
43
+ <li
44
+ key={layer.id}
45
+ className={[
46
+ 'squisq-image-editor-layer-item',
47
+ isSelected ? 'is-selected' : '',
48
+ visible ? '' : 'is-hidden',
49
+ ]
50
+ .filter(Boolean)
51
+ .join(' ')}
52
+ >
53
+ <button
54
+ type="button"
55
+ className="squisq-image-editor-layer-toggle"
56
+ onClick={() =>
57
+ dispatch({
58
+ type: 'update-layer',
59
+ layerId: layer.id,
60
+ patch: { visible: !visible },
61
+ })
62
+ }
63
+ aria-label={visible ? 'Hide layer' : 'Show layer'}
64
+ title={visible ? 'Hide layer' : 'Show layer'}
65
+ >
66
+ {visible ? <EyeIcon /> : <EyeOffIcon />}
67
+ </button>
68
+ <button
69
+ type="button"
70
+ className="squisq-image-editor-layer-toggle"
71
+ onClick={() =>
72
+ dispatch({
73
+ type: 'update-layer',
74
+ layerId: layer.id,
75
+ patch: { locked: !locked },
76
+ })
77
+ }
78
+ aria-label={locked ? 'Unlock layer' : 'Lock layer'}
79
+ title={locked ? 'Unlock layer' : 'Lock layer'}
80
+ >
81
+ {locked ? <LockIcon /> : <UnlockIcon />}
82
+ </button>
83
+ <button
84
+ type="button"
85
+ className="squisq-image-editor-layer-name"
86
+ onClick={() => dispatch({ type: 'select', layerId: layer.id })}
87
+ >
88
+ {layer.name ?? defaultLayerName(layer)}
89
+ <span className="squisq-image-editor-layer-kind">{layer.type}</span>
90
+ </button>
91
+ <button
92
+ type="button"
93
+ className="squisq-image-editor-layer-toggle"
94
+ disabled={!canMoveUp}
95
+ onClick={() =>
96
+ dispatch({ type: 'reorder-layer', layerId: layer.id, toIndex: stackIndex + 1 })
97
+ }
98
+ aria-label="Move layer up"
99
+ title="Move layer up"
100
+ >
101
+ <ChevronUpIcon />
102
+ </button>
103
+ <button
104
+ type="button"
105
+ className="squisq-image-editor-layer-toggle"
106
+ disabled={!canMoveDown}
107
+ onClick={() =>
108
+ dispatch({ type: 'reorder-layer', layerId: layer.id, toIndex: stackIndex - 1 })
109
+ }
110
+ aria-label="Move layer down"
111
+ title="Move layer down"
112
+ >
113
+ <ChevronDownIcon />
114
+ </button>
115
+ <button
116
+ type="button"
117
+ className="squisq-image-editor-layer-toggle"
118
+ onClick={() => dispatch({ type: 'remove-layer', layerId: layer.id })}
119
+ aria-label="Delete layer"
120
+ title="Delete layer"
121
+ >
122
+ <CloseIcon />
123
+ </button>
124
+ </li>
125
+ );
126
+ })}
127
+ </ul>
128
+ </div>
129
+ );
130
+ }
131
+
132
+ function defaultLayerName(layer: { type: string; content?: unknown }): string {
133
+ if (layer.type === 'text') {
134
+ const c = layer.content as { text?: string } | undefined;
135
+ return c?.text?.split('\n')[0]?.slice(0, 24) || 'Text';
136
+ }
137
+ if (layer.type === 'image') return 'Image';
138
+ if (layer.type === 'shape') {
139
+ const c = layer.content as { shape?: string } | undefined;
140
+ return c?.shape ? c.shape[0]!.toUpperCase() + c.shape.slice(1) : 'Shape';
141
+ }
142
+ return layer.type;
143
+ }