wendang-react 0.0.0-stage → 0.5.5

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 (548) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +381 -0
  3. package/README.md +469 -2
  4. package/dist/bindings/yjs.js +5 -0
  5. package/dist/chunks/AnchoredPanel-D3nte06T.js +68 -0
  6. package/dist/chunks/MindMapCanvas-VWrxTy5Q.js +2070 -0
  7. package/dist/chunks/MindMapViewer-DqKu_Kuz.js +180 -0
  8. package/dist/chunks/SpreadsheetViewer-DN_QQGud.js +157 -0
  9. package/dist/chunks/code-highlight-BmaqTe1m.js +30 -0
  10. package/dist/chunks/code-highlight-engine-BFDzDPzy.js +40 -0
  11. package/dist/chunks/comment-hint-DN7LKJAn.js +296 -0
  12. package/dist/chunks/composition-state-Bl-U1R_2.js +40 -0
  13. package/dist/chunks/extensions-B-CVVQhp.js +4210 -0
  14. package/dist/chunks/fuzzy-match-DYQR-4w2.js +89 -0
  15. package/dist/chunks/hooks-C53Dp0pr.js +35 -0
  16. package/dist/chunks/host-C-V688Rq.js +197 -0
  17. package/dist/chunks/index-Ii536CFG.js +720 -0
  18. package/dist/chunks/katex-WjrodtTU.js +71 -0
  19. package/dist/chunks/mind-map-controller-B6SxK3GZ.js +479 -0
  20. package/dist/chunks/mind-map-delete-CVsxdXfs.js +31 -0
  21. package/dist/chunks/mind-map-input-CEWRz_t6.js +43 -0
  22. package/dist/chunks/mind-topic-text-BBNTbFSe.js +446 -0
  23. package/dist/chunks/nodes-iKqTGr_N.js +291 -0
  24. package/dist/chunks/popover-placement-D3xVYdkt.js +47 -0
  25. package/dist/chunks/worksheet-engine-TJ54m0KC.js +125 -0
  26. package/dist/chunks/worksheet-preview-DsDVWxIM.js +129 -0
  27. package/dist/document/viewer.js +287 -0
  28. package/dist/document.js +3620 -0
  29. package/dist/index.js +8 -0
  30. package/dist/mindmap/viewer.js +6 -0
  31. package/dist/mindmap.js +123 -0
  32. package/dist/shared.js +52 -0
  33. package/dist/spreadsheet/viewer.js +5 -0
  34. package/dist/spreadsheet/xlsx.js +299 -0
  35. package/dist/spreadsheet.js +120 -0
  36. package/dist/styles/callout.css +34 -0
  37. package/dist/styles/code-block.css +58 -0
  38. package/dist/styles/content-style.css +619 -0
  39. package/dist/styles/editor-interaction.css +621 -0
  40. package/dist/styles/file-video.css +12 -0
  41. package/dist/styles/image-preview.css +103 -0
  42. package/dist/styles/mind-map-block.css +14 -0
  43. package/dist/styles/mind-map-canvas.css +80 -0
  44. package/dist/styles/mind-map-comments.css +93 -0
  45. package/dist/styles/mind-map-details.css +7 -0
  46. package/dist/styles/mind-map-preview.css +7 -0
  47. package/dist/styles/mind-map-topic.css +112 -0
  48. package/dist/styles/page-picker.css +47 -0
  49. package/dist/styles/renderer.css +45 -0
  50. package/dist/styles/spreadsheet-editor.css +3 -0
  51. package/dist/styles/tokens.css +153 -0
  52. package/dist/styles/video-dialog.css +41 -0
  53. package/dist/styles/video-embed.css +35 -0
  54. package/dist/styles.css +2614 -0
  55. package/dist/types/bindings/yjs.d.ts +3 -0
  56. package/dist/types/document/viewer.d.ts +3 -0
  57. package/dist/types/document.d.ts +7 -0
  58. package/dist/types/editor/BlockAffordance.d.ts +47 -0
  59. package/dist/types/editor/CalloutView.d.ts +2 -0
  60. package/dist/types/editor/CodeBlockView.d.ts +9 -0
  61. package/dist/types/editor/DocumentColorPanel.d.ts +8 -0
  62. package/dist/types/editor/DocumentEditor.d.ts +71 -0
  63. package/dist/types/editor/ImagePreview.d.ts +10 -0
  64. package/dist/types/editor/LinkPopover.d.ts +21 -0
  65. package/dist/types/editor/PagePicker.d.ts +7 -0
  66. package/dist/types/editor/SelectionToolbar.d.ts +11 -0
  67. package/dist/types/editor/SlashMenu.d.ts +30 -0
  68. package/dist/types/editor/TableControls.d.ts +9 -0
  69. package/dist/types/editor/VideoEmbedDialog.d.ts +6 -0
  70. package/dist/types/editor/document-gestures.d.ts +13 -0
  71. package/dist/types/editor/extensions.d.ts +29 -0
  72. package/dist/types/editor/nodes.d.ts +20 -0
  73. package/dist/types/editor/same-document.d.ts +2 -0
  74. package/dist/types/engine/collab/presence.d.ts +1 -0
  75. package/dist/types/engine/editor/block-commands.d.ts +35 -0
  76. package/dist/types/engine/editor/block-conversion.d.ts +12 -0
  77. package/dist/types/engine/editor/block-drag.d.ts +16 -0
  78. package/dist/types/engine/editor/block-menu-placement.d.ts +16 -0
  79. package/dist/types/engine/editor/callout-commands.d.ts +4 -0
  80. package/dist/types/engine/editor/collab-change-origin.d.ts +2 -0
  81. package/dist/types/engine/editor/delete-block.d.ts +2 -0
  82. package/dist/types/engine/editor/extensions/block-format.d.ts +19 -0
  83. package/dist/types/engine/editor/extensions/block-handle.d.ts +17 -0
  84. package/dist/types/engine/editor/extensions/block-menu-highlight.d.ts +4 -0
  85. package/dist/types/engine/editor/extensions/block-selection.d.ts +117 -0
  86. package/dist/types/engine/editor/extensions/code-highlight.d.ts +2 -0
  87. package/dist/types/engine/editor/extensions/collab-cursor.d.ts +13 -0
  88. package/dist/types/engine/editor/extensions/ime-guard.d.ts +8 -0
  89. package/dist/types/engine/editor/extensions/inline-comments.d.ts +15 -0
  90. package/dist/types/engine/editor/extensions/paste-sanitizer.d.ts +4 -0
  91. package/dist/types/engine/editor/extensions/slash.d.ts +16 -0
  92. package/dist/types/engine/editor/extensions/table-interaction.d.ts +9 -0
  93. package/dist/types/engine/editor/extensions/upload-placeholder.d.ts +48 -0
  94. package/dist/types/engine/editor/image-preview-gesture.d.ts +3 -0
  95. package/dist/types/engine/editor/ime/composition-state.d.ts +9 -0
  96. package/dist/types/engine/editor/insert-block.d.ts +6 -0
  97. package/dist/types/engine/editor/insert-picker-logic.d.ts +13 -0
  98. package/dist/types/engine/editor/link-edit.d.ts +10 -0
  99. package/dist/types/engine/editor/mind-map-controller.d.ts +63 -0
  100. package/dist/types/engine/editor/mind-map-delete.d.ts +7 -0
  101. package/dist/types/engine/editor/mind-map-drag.d.ts +16 -0
  102. package/dist/types/engine/editor/mind-map-input.d.ts +16 -0
  103. package/dist/types/engine/editor/mind-map-selection.d.ts +11 -0
  104. package/dist/types/engine/editor/nodes/columns.d.ts +6 -0
  105. package/dist/types/engine/editor/nodes/file-nodeview.d.ts +4 -0
  106. package/dist/types/engine/editor/nodes/file.d.ts +2 -0
  107. package/dist/types/engine/editor/nodes/image-gallery.d.ts +2 -0
  108. package/dist/types/engine/editor/nodes/image-nodeview.d.ts +4 -0
  109. package/dist/types/engine/editor/nodes/image.d.ts +1 -0
  110. package/dist/types/engine/editor/nodes/inline-style.d.ts +4 -0
  111. package/dist/types/engine/editor/nodes/math-nodeview.d.ts +2 -0
  112. package/dist/types/engine/editor/nodes/math.d.ts +3 -0
  113. package/dist/types/engine/editor/nodes/mention.d.ts +2 -0
  114. package/dist/types/engine/editor/nodes/mind-map-schema.d.ts +6 -0
  115. package/dist/types/engine/editor/nodes/page-link.d.ts +2 -0
  116. package/dist/types/engine/editor/nodes/table-cell.d.ts +4 -0
  117. package/dist/types/engine/editor/nodes/video-embed-schema.d.ts +2 -0
  118. package/dist/types/engine/editor/popover-placement.d.ts +35 -0
  119. package/dist/types/engine/editor/table-clipboard.d.ts +3 -0
  120. package/dist/types/engine/editor/table-commands.d.ts +20 -0
  121. package/dist/types/engine/editor/table-geometry.d.ts +34 -0
  122. package/dist/types/engine/editor/table-menu-placement.d.ts +30 -0
  123. package/dist/types/engine/editor/tracked-target.d.ts +8 -0
  124. package/dist/types/engine/editor/trailing-paragraph.d.ts +2 -0
  125. package/dist/types/engine/host.d.ts +2 -0
  126. package/dist/types/engine/renderer/attachments.d.ts +11 -0
  127. package/dist/types/engine/renderer/katex.d.ts +7 -0
  128. package/dist/types/engine/renderer/lazy-visible.d.ts +11 -0
  129. package/dist/types/engine/shared/callout.d.ts +1 -0
  130. package/dist/types/engine/shared/code-block.d.ts +1 -0
  131. package/dist/types/engine/shared/code-highlight-engine.d.ts +2 -0
  132. package/dist/types/engine/shared/code-highlight.d.ts +8 -0
  133. package/dist/types/engine/shared/comment-hint-position.d.ts +20 -0
  134. package/dist/types/engine/shared/comment-hint.d.ts +2 -0
  135. package/dist/types/engine/shared/document-colors.d.ts +1 -0
  136. package/dist/types/engine/shared/fuzzy-match.d.ts +13 -0
  137. package/dist/types/engine/shared/image-gallery.d.ts +1 -0
  138. package/dist/types/engine/shared/inline-comments.d.ts +1 -0
  139. package/dist/types/engine/shared/mind-map-phase2.d.ts +1 -0
  140. package/dist/types/engine/shared/mind-map.d.ts +1 -0
  141. package/dist/types/engine/shared/mind-topic-text.d.ts +1 -0
  142. package/dist/types/engine/shared/modal-stack.d.ts +6 -0
  143. package/dist/types/engine/shared/page-candidates.d.ts +6 -0
  144. package/dist/types/engine/shared/pinyin.d.ts +6 -0
  145. package/dist/types/engine/shared/preview-items.d.ts +10 -0
  146. package/dist/types/engine/shared/safe-href.d.ts +1 -0
  147. package/dist/types/engine/shared/table-cell-format.d.ts +1 -0
  148. package/dist/types/engine/shared/table-layout.d.ts +1 -0
  149. package/dist/types/engine/shared/video-embed.d.ts +1 -0
  150. package/dist/types/engine/shared/video-feedback.d.ts +1 -0
  151. package/dist/types/engine/shared/workbook.d.ts +1 -0
  152. package/dist/types/engine/worksheets/worksheet-engine.d.ts +16 -0
  153. package/dist/types/engine/worksheets/worksheet-format.d.ts +20 -0
  154. package/dist/types/engine/worksheets/worksheet-preview.d.ts +17 -0
  155. package/dist/types/engine/worksheets/worksheet-xlsx.d.ts +21 -0
  156. package/dist/types/engine/wui/core/dialog.d.ts +5 -0
  157. package/dist/types/engine/wui/core/listbox.d.ts +8 -0
  158. package/dist/types/engine/wui/core/placement.d.ts +40 -0
  159. package/dist/types/hooks.d.ts +7 -0
  160. package/dist/types/host-types.d.ts +58 -0
  161. package/dist/types/host.d.ts +24 -0
  162. package/dist/types/index.d.ts +5 -0
  163. package/dist/types/mindmap/Comments.d.ts +9 -0
  164. package/dist/types/mindmap/Details.d.ts +13 -0
  165. package/dist/types/mindmap/Field.d.ts +10 -0
  166. package/dist/types/mindmap/MindMapCanvas.d.ts +19 -0
  167. package/dist/types/mindmap/MindMapEditor.d.ts +30 -0
  168. package/dist/types/mindmap/MindMapViewer.d.ts +23 -0
  169. package/dist/types/mindmap/TopicLabel.d.ts +4 -0
  170. package/dist/types/mindmap/viewer.d.ts +2 -0
  171. package/dist/types/mindmap.d.ts +4 -0
  172. package/dist/types/renderer/CodeToolbar.d.ts +10 -0
  173. package/dist/types/renderer/DocumentViewer.d.ts +23 -0
  174. package/dist/types/renderer/media.d.ts +33 -0
  175. package/dist/types/shared.d.ts +12 -0
  176. package/dist/types/spreadsheet/SpreadsheetEditor.d.ts +23 -0
  177. package/dist/types/spreadsheet/SpreadsheetViewer.d.ts +13 -0
  178. package/dist/types/spreadsheet/viewer.d.ts +2 -0
  179. package/dist/types/spreadsheet/xlsx.d.ts +2 -0
  180. package/dist/types/spreadsheet.d.ts +3 -0
  181. package/dist/types/wui/AnchoredPanel.d.ts +14 -0
  182. package/dist/types/wui/Dropdown.d.ts +18 -0
  183. package/dist/types/wui/Icon.d.ts +7 -0
  184. package/dist/types/wui/Tooltip.d.ts +5 -0
  185. package/dist/types/wui/index.d.ts +69 -0
  186. package/package.json +122 -3
  187. package/third-party-notices/@fast-csv__format@4.3.5-LICENSE.txt +21 -0
  188. package/third-party-notices/@fast-csv__parse@4.3.6-LICENSE.txt +21 -0
  189. package/third-party-notices/@flatten-js__interval-tree@1.1.3-LICENSE.txt +21 -0
  190. package/third-party-notices/@floating-ui__core@1.8.0-LICENSE.txt +20 -0
  191. package/third-party-notices/@floating-ui__dom@1.8.0-LICENSE.txt +20 -0
  192. package/third-party-notices/@floating-ui__react-dom@2.1.9-LICENSE.txt +20 -0
  193. package/third-party-notices/@floating-ui__utils@0.2.12-LICENSE.txt +20 -0
  194. package/third-party-notices/@grpc__grpc-js@1.14.6-LICENSE.txt +201 -0
  195. package/third-party-notices/@grpc__proto-loader@0.8.1-LICENSE.txt +201 -0
  196. package/third-party-notices/@js-sdsl__ordered-map@4.4.2-LICENSE.txt +21 -0
  197. package/third-party-notices/@protobufjs__aspromise@1.1.2-LICENSE.txt +26 -0
  198. package/third-party-notices/@protobufjs__base64@1.1.2-LICENSE.txt +26 -0
  199. package/third-party-notices/@protobufjs__codegen@2.0.5-LICENSE.txt +26 -0
  200. package/third-party-notices/@protobufjs__eventemitter@1.1.1-LICENSE.txt +26 -0
  201. package/third-party-notices/@protobufjs__fetch@1.1.1-LICENSE.txt +26 -0
  202. package/third-party-notices/@protobufjs__float@1.0.2-LICENSE.txt +26 -0
  203. package/third-party-notices/@protobufjs__path@1.1.2-LICENSE.txt +26 -0
  204. package/third-party-notices/@protobufjs__pool@1.1.0-LICENSE.txt +26 -0
  205. package/third-party-notices/@protobufjs__utf8@1.1.2-LICENSE.txt +26 -0
  206. package/third-party-notices/@radix-ui__primitive@1.2.0-LICENSE.txt +21 -0
  207. package/third-party-notices/@radix-ui__react-arrow@1.1.17-LICENSE.txt +21 -0
  208. package/third-party-notices/@radix-ui__react-collection@1.2.0-LICENSE.txt +21 -0
  209. package/third-party-notices/@radix-ui__react-compose-refs@1.1.5-LICENSE.txt +21 -0
  210. package/third-party-notices/@radix-ui__react-context@1.3.0-LICENSE.txt +21 -0
  211. package/third-party-notices/@radix-ui__react-dialog@1.3.1-LICENSE.txt +21 -0
  212. package/third-party-notices/@radix-ui__react-direction@1.2.0-LICENSE.txt +21 -0
  213. package/third-party-notices/@radix-ui__react-dismissable-layer@1.2.1-LICENSE.txt +21 -0
  214. package/third-party-notices/@radix-ui__react-dropdown-menu@2.2.1-LICENSE.txt +21 -0
  215. package/third-party-notices/@radix-ui__react-focus-guards@1.2.0-LICENSE.txt +21 -0
  216. package/third-party-notices/@radix-ui__react-focus-scope@1.3.1-LICENSE.txt +21 -0
  217. package/third-party-notices/@radix-ui__react-hover-card@1.2.1-LICENSE.txt +21 -0
  218. package/third-party-notices/@radix-ui__react-id@1.1.4-LICENSE.txt +21 -0
  219. package/third-party-notices/@radix-ui__react-menu@2.2.1-LICENSE.txt +21 -0
  220. package/third-party-notices/@radix-ui__react-popover@1.3.1-LICENSE.txt +21 -0
  221. package/third-party-notices/@radix-ui__react-popper@1.4.0-LICENSE.txt +21 -0
  222. package/third-party-notices/@radix-ui__react-portal@1.2.0-LICENSE.txt +21 -0
  223. package/third-party-notices/@radix-ui__react-presence@1.1.11-LICENSE.txt +21 -0
  224. package/third-party-notices/@radix-ui__react-primitive@2.2.0-LICENSE.txt +21 -0
  225. package/third-party-notices/@radix-ui__react-roving-focus@1.2.1-LICENSE.txt +21 -0
  226. package/third-party-notices/@radix-ui__react-separator@1.2.0-LICENSE.txt +21 -0
  227. package/third-party-notices/@radix-ui__react-slot@1.5.0-LICENSE.txt +21 -0
  228. package/third-party-notices/@radix-ui__react-use-callback-ref@1.1.5-LICENSE.txt +21 -0
  229. package/third-party-notices/@radix-ui__react-use-controllable-state@1.3.0-LICENSE.txt +21 -0
  230. package/third-party-notices/@radix-ui__react-use-effect-event@0.0.5-LICENSE.txt +21 -0
  231. package/third-party-notices/@radix-ui__react-use-is-hydrated@0.1.3-LICENSE.txt +21 -0
  232. package/third-party-notices/@radix-ui__react-use-layout-effect@1.1.4-LICENSE.txt +21 -0
  233. package/third-party-notices/@radix-ui__react-use-rect@1.1.4-LICENSE.txt +21 -0
  234. package/third-party-notices/@radix-ui__react-use-size@1.1.5-LICENSE.txt +21 -0
  235. package/third-party-notices/@radix-ui__rect@1.1.3-LICENSE.txt +21 -0
  236. package/third-party-notices/@tiptap__core@3.31.4-LICENSE.md.txt +21 -0
  237. package/third-party-notices/@tiptap__extension-blockquote@3.31.4-LICENSE.md.txt +21 -0
  238. package/third-party-notices/@tiptap__extension-bold@3.31.4-LICENSE.md.txt +21 -0
  239. package/third-party-notices/@tiptap__extension-bullet-list@3.31.4-LICENSE.md.txt +21 -0
  240. package/third-party-notices/@tiptap__extension-code-block@3.31.4-LICENSE.md.txt +21 -0
  241. package/third-party-notices/@tiptap__extension-code@3.31.4-LICENSE.md.txt +21 -0
  242. package/third-party-notices/@tiptap__extension-collaboration@3.31.4-LICENSE.md.txt +21 -0
  243. package/third-party-notices/@tiptap__extension-document@3.31.4-LICENSE.md.txt +21 -0
  244. package/third-party-notices/@tiptap__extension-dropcursor@3.31.4-LICENSE.md.txt +21 -0
  245. package/third-party-notices/@tiptap__extension-gapcursor@3.31.4-LICENSE.md.txt +21 -0
  246. package/third-party-notices/@tiptap__extension-hard-break@3.31.4-LICENSE.md.txt +21 -0
  247. package/third-party-notices/@tiptap__extension-heading@3.31.4-LICENSE.md.txt +21 -0
  248. package/third-party-notices/@tiptap__extension-horizontal-rule@3.31.4-LICENSE.md.txt +21 -0
  249. package/third-party-notices/@tiptap__extension-image@3.31.4-LICENSE.md.txt +21 -0
  250. package/third-party-notices/@tiptap__extension-italic@3.31.4-LICENSE.md.txt +21 -0
  251. package/third-party-notices/@tiptap__extension-link@3.31.4-LICENSE.md.txt +21 -0
  252. package/third-party-notices/@tiptap__extension-list-item@3.31.4-LICENSE.md.txt +21 -0
  253. package/third-party-notices/@tiptap__extension-list-keymap@3.31.4-LICENSE.md.txt +21 -0
  254. package/third-party-notices/@tiptap__extension-list@3.31.4-LICENSE.md.txt +21 -0
  255. package/third-party-notices/@tiptap__extension-ordered-list@3.31.4-LICENSE.md.txt +21 -0
  256. package/third-party-notices/@tiptap__extension-paragraph@3.31.4-LICENSE.md.txt +21 -0
  257. package/third-party-notices/@tiptap__extension-placeholder@3.31.4-LICENSE.md.txt +21 -0
  258. package/third-party-notices/@tiptap__extension-strike@3.31.4-LICENSE.md.txt +21 -0
  259. package/third-party-notices/@tiptap__extension-table-cell@3.31.4-LICENSE.md.txt +21 -0
  260. package/third-party-notices/@tiptap__extension-table-header@3.31.4-LICENSE.md.txt +21 -0
  261. package/third-party-notices/@tiptap__extension-table-row@3.31.4-LICENSE.md.txt +21 -0
  262. package/third-party-notices/@tiptap__extension-table@3.31.4-LICENSE.md.txt +21 -0
  263. package/third-party-notices/@tiptap__extension-task-item@3.31.4-LICENSE.md.txt +21 -0
  264. package/third-party-notices/@tiptap__extension-task-list@3.31.4-LICENSE.md.txt +21 -0
  265. package/third-party-notices/@tiptap__extension-text@3.31.4-LICENSE.md.txt +21 -0
  266. package/third-party-notices/@tiptap__extension-underline@3.31.4-LICENSE.md.txt +21 -0
  267. package/third-party-notices/@tiptap__extensions@3.31.4-LICENSE.md.txt +21 -0
  268. package/third-party-notices/@tiptap__pm@3.31.4-LICENSE.txt +21 -0
  269. package/third-party-notices/@tiptap__starter-kit@3.31.4-LICENSE.md.txt +21 -0
  270. package/third-party-notices/@tiptap__y-tiptap@3.0.7-LICENSE.txt +22 -0
  271. package/third-party-notices/@types__hast@3.0.5-LICENSE.txt +21 -0
  272. package/third-party-notices/@types__node@14.18.63-LICENSE.txt +21 -0
  273. package/third-party-notices/@types__node@26.6.2-LICENSE.txt +21 -0
  274. package/third-party-notices/@types__react-dom@19.3.0-LICENSE.txt +21 -0
  275. package/third-party-notices/@types__react@19.3.0-LICENSE.txt +21 -0
  276. package/third-party-notices/@types__unist@3.0.3-LICENSE.txt +21 -0
  277. package/third-party-notices/@univerjs__core@1.1.0-LICENSE.txt +176 -0
  278. package/third-party-notices/@univerjs__design@1.1.0-LICENSE.txt +176 -0
  279. package/third-party-notices/@univerjs__docs-ui@1.1.0-LICENSE.txt +176 -0
  280. package/third-party-notices/@univerjs__docs@1.1.0-LICENSE.txt +176 -0
  281. package/third-party-notices/@univerjs__drawing@1.1.0-LICENSE.txt +176 -0
  282. package/third-party-notices/@univerjs__engine-formula@1.1.0-LICENSE.txt +176 -0
  283. package/third-party-notices/@univerjs__engine-render@1.1.0-LICENSE.txt +176 -0
  284. package/third-party-notices/@univerjs__icons@1.46.0-LICENSE.txt +21 -0
  285. package/third-party-notices/@univerjs__network@1.1.0-LICENSE.txt +176 -0
  286. package/third-party-notices/@univerjs__preset-sheets-core@1.1.0-LICENSE.txt +176 -0
  287. package/third-party-notices/@univerjs__preset-sheets-filter@1.1.0-LICENSE.txt +176 -0
  288. package/third-party-notices/@univerjs__preset-sheets-sort@1.1.0-LICENSE.txt +176 -0
  289. package/third-party-notices/@univerjs__presets@1.1.0-LICENSE.txt +176 -0
  290. package/third-party-notices/@univerjs__rpc@1.1.0-LICENSE.txt +176 -0
  291. package/third-party-notices/@univerjs__sheets-filter-ui@1.1.0-LICENSE.txt +176 -0
  292. package/third-party-notices/@univerjs__sheets-filter@1.1.0-LICENSE.txt +176 -0
  293. package/third-party-notices/@univerjs__sheets-formula-ui@1.1.0-LICENSE.txt +176 -0
  294. package/third-party-notices/@univerjs__sheets-formula@1.1.0-LICENSE.txt +176 -0
  295. package/third-party-notices/@univerjs__sheets-numfmt-ui@1.1.0-LICENSE.txt +176 -0
  296. package/third-party-notices/@univerjs__sheets-numfmt@1.1.0-LICENSE.txt +176 -0
  297. package/third-party-notices/@univerjs__sheets-sort-ui@1.1.0-LICENSE.txt +176 -0
  298. package/third-party-notices/@univerjs__sheets-sort@1.1.0-LICENSE.txt +176 -0
  299. package/third-party-notices/@univerjs__sheets-ui@1.1.0-LICENSE.txt +176 -0
  300. package/third-party-notices/@univerjs__sheets@1.1.0-LICENSE.txt +176 -0
  301. package/third-party-notices/@univerjs__telemetry@1.1.0-LICENSE.txt +176 -0
  302. package/third-party-notices/@univerjs__themes@1.1.0-LICENSE.txt +176 -0
  303. package/third-party-notices/@univerjs__ui@1.1.0-LICENSE.txt +176 -0
  304. package/third-party-notices/@wendellhu__redi@1.1.3-LICENSE.txt +7 -0
  305. package/third-party-notices/ansi-regex@5.0.1-license.txt +9 -0
  306. package/third-party-notices/ansi-styles@4.3.0-license.txt +9 -0
  307. package/third-party-notices/archiver-utils@2.1.0-LICENSE.txt +22 -0
  308. package/third-party-notices/archiver-utils@3.0.4-LICENSE.txt +22 -0
  309. package/third-party-notices/archiver@5.3.2-LICENSE.txt +22 -0
  310. package/third-party-notices/aria-hidden@1.2.6-LICENSE.txt +21 -0
  311. package/third-party-notices/array-buffer-byte-length@1.0.2-LICENSE.txt +21 -0
  312. package/third-party-notices/arraybuffer.prototype.slice@1.0.4-LICENSE.txt +21 -0
  313. package/third-party-notices/async-function@1.0.0-LICENSE.txt +21 -0
  314. package/third-party-notices/async-lock@1.4.1-LICENSE.txt +22 -0
  315. package/third-party-notices/async@3.2.6-LICENSE.txt +19 -0
  316. package/third-party-notices/available-typed-arrays@1.0.7-LICENSE.txt +21 -0
  317. package/third-party-notices/balanced-match@1.0.2-LICENSE.md.txt +21 -0
  318. package/third-party-notices/base64-js@1.5.1-LICENSE.txt +21 -0
  319. package/third-party-notices/big-integer@1.6.52-LICENSE.txt +24 -0
  320. package/third-party-notices/bl@4.1.0-LICENSE.md.txt +13 -0
  321. package/third-party-notices/bluebird@3.4.7-LICENSE.txt +21 -0
  322. package/third-party-notices/brace-expansion@1.1.21-LICENSE.txt +21 -0
  323. package/third-party-notices/brace-expansion@2.1.7-LICENSE.txt +21 -0
  324. package/third-party-notices/buffer-crc32@0.2.13-LICENSE.txt +19 -0
  325. package/third-party-notices/buffer-indexof-polyfill@1.0.2-LICENSE.txt +22 -0
  326. package/third-party-notices/buffer@5.7.1-LICENSE.txt +21 -0
  327. package/third-party-notices/call-bind-apply-helpers@1.0.2-LICENSE.txt +21 -0
  328. package/third-party-notices/call-bind@1.0.9-LICENSE.txt +21 -0
  329. package/third-party-notices/call-bound@1.0.4-LICENSE.txt +21 -0
  330. package/third-party-notices/class-variance-authority@0.7.1-LICENSE.txt +190 -0
  331. package/third-party-notices/cliui@8.0.1-LICENSE.txt.txt +14 -0
  332. package/third-party-notices/clsx@2.1.1-license.txt +9 -0
  333. package/third-party-notices/cmdk@1.1.1-LICENSE.md.txt +21 -0
  334. package/third-party-notices/collapse-white-space@2.1.0-license.txt +22 -0
  335. package/third-party-notices/color-convert@2.0.1-LICENSE.txt +21 -0
  336. package/third-party-notices/color-name@1.1.4-LICENSE.txt +8 -0
  337. package/third-party-notices/commander@15.0.0-LICENSE.txt +22 -0
  338. package/third-party-notices/compress-commons@4.1.2-LICENSE.txt +22 -0
  339. package/third-party-notices/concat-map@0.0.1-LICENSE.txt +18 -0
  340. package/third-party-notices/core-util-is@1.0.3-LICENSE.txt +19 -0
  341. package/third-party-notices/crc-32@1.2.2-LICENSE.txt +201 -0
  342. package/third-party-notices/crc32-stream@4.0.3-LICENSE.txt +22 -0
  343. package/third-party-notices/csstype@3.2.3-LICENSE.txt +19 -0
  344. package/third-party-notices/data-view-buffer@1.0.2-LICENSE.txt +21 -0
  345. package/third-party-notices/data-view-byte-length@1.0.2-LICENSE.txt +21 -0
  346. package/third-party-notices/data-view-byte-offset@1.0.1-LICENSE.txt +21 -0
  347. package/third-party-notices/dayjs@1.11.23-LICENSE.txt +21 -0
  348. package/third-party-notices/decimal.js@10.6.0-LICENCE.md.txt +23 -0
  349. package/third-party-notices/define-data-property@1.1.4-LICENSE.txt +21 -0
  350. package/third-party-notices/define-properties@1.2.1-LICENSE.txt +21 -0
  351. package/third-party-notices/dequal@2.0.3-license.txt +21 -0
  352. package/third-party-notices/detect-node-es@1.1.0-LICENSE.txt +21 -0
  353. package/third-party-notices/devlop@1.1.0-license.txt +22 -0
  354. package/third-party-notices/dunder-proto@1.0.1-LICENSE.txt +21 -0
  355. package/third-party-notices/duplexer2@0.1.4-LICENSE.md.txt +26 -0
  356. package/third-party-notices/end-of-stream@1.4.5-LICENSE.txt +21 -0
  357. package/third-party-notices/es-abstract-get@1.0.0-LICENSE.txt +21 -0
  358. package/third-party-notices/es-abstract@1.24.2-LICENSE.txt +21 -0
  359. package/third-party-notices/es-define-property@1.0.1-LICENSE.txt +21 -0
  360. package/third-party-notices/es-errors@1.3.0-LICENSE.txt +21 -0
  361. package/third-party-notices/es-object-atoms@1.1.2-LICENSE.txt +21 -0
  362. package/third-party-notices/es-set-tostringtag@2.1.0-LICENSE.txt +21 -0
  363. package/third-party-notices/es-to-primitive@1.3.4-LICENSE.txt +22 -0
  364. package/third-party-notices/escalade@3.2.0-license.txt +9 -0
  365. package/third-party-notices/exceljs@4.4.0-LICENSE.txt +22 -0
  366. package/third-party-notices/fast-csv@4.3.6-LICENSE.txt +21 -0
  367. package/third-party-notices/fast-diff@1.3.0-LICENSE.txt +201 -0
  368. package/third-party-notices/for-each@0.3.5-LICENSE.txt +22 -0
  369. package/third-party-notices/fs-constants@1.0.0-LICENSE.txt +21 -0
  370. package/third-party-notices/fs.realpath@1.0.0-LICENSE.txt +43 -0
  371. package/third-party-notices/fstream@1.0.12-LICENSE.txt +15 -0
  372. package/third-party-notices/function-bind@1.1.2-LICENSE.txt +20 -0
  373. package/third-party-notices/function.prototype.name@1.2.0-LICENSE.txt +21 -0
  374. package/third-party-notices/functions-have-names@1.2.3-LICENSE.txt +21 -0
  375. package/third-party-notices/generator-function@2.0.1-LICENSE.md.txt +7 -0
  376. package/third-party-notices/get-caller-file@2.0.5-LICENSE.md.txt +6 -0
  377. package/third-party-notices/get-intrinsic@1.3.0-LICENSE.txt +21 -0
  378. package/third-party-notices/get-nonce@1.0.1-LICENSE.txt +21 -0
  379. package/third-party-notices/get-proto@1.0.1-LICENSE.txt +21 -0
  380. package/third-party-notices/get-symbol-description@1.1.0-LICENSE.txt +21 -0
  381. package/third-party-notices/glob@7.2.3-LICENSE.txt +21 -0
  382. package/third-party-notices/globalthis@1.0.4-LICENSE.txt +21 -0
  383. package/third-party-notices/gopd@1.2.0-LICENSE.txt +21 -0
  384. package/third-party-notices/graceful-fs@4.2.11-LICENSE.txt +15 -0
  385. package/third-party-notices/has-bigints@1.1.0-LICENSE.txt +21 -0
  386. package/third-party-notices/has-property-descriptors@1.0.2-LICENSE.txt +21 -0
  387. package/third-party-notices/has-proto@1.2.0-LICENSE.txt +21 -0
  388. package/third-party-notices/has-symbols@1.1.0-LICENSE.txt +21 -0
  389. package/third-party-notices/has-tostringtag@1.0.2-LICENSE.txt +21 -0
  390. package/third-party-notices/hasown@2.0.4-LICENSE.txt +21 -0
  391. package/third-party-notices/highlight.js@11.11.2-LICENSE.txt +29 -0
  392. package/third-party-notices/ieee754@1.2.1-LICENSE.txt +11 -0
  393. package/third-party-notices/immediate@3.0.6-LICENSE.txt.txt +20 -0
  394. package/third-party-notices/inflight@1.0.6-LICENSE.txt +15 -0
  395. package/third-party-notices/inherits@2.0.4-LICENSE.txt +16 -0
  396. package/third-party-notices/internal-slot@1.1.0-LICENSE.txt +21 -0
  397. package/third-party-notices/is-array-buffer@3.0.5-LICENSE.txt +21 -0
  398. package/third-party-notices/is-async-function@2.1.1-LICENSE.txt +20 -0
  399. package/third-party-notices/is-bigint@1.1.0-LICENSE.txt +21 -0
  400. package/third-party-notices/is-boolean-object@1.2.2-LICENSE.txt +22 -0
  401. package/third-party-notices/is-callable@1.2.7-LICENSE.txt +22 -0
  402. package/third-party-notices/is-data-view@1.0.2-LICENSE.txt +21 -0
  403. package/third-party-notices/is-date-object@1.1.0-LICENSE.txt +22 -0
  404. package/third-party-notices/is-document.all@1.0.0-LICENSE.txt +21 -0
  405. package/third-party-notices/is-finalizationregistry@1.1.1-LICENSE.txt +21 -0
  406. package/third-party-notices/is-fullwidth-code-point@3.0.0-license.txt +9 -0
  407. package/third-party-notices/is-generator-function@1.1.2-LICENSE.txt +20 -0
  408. package/third-party-notices/is-map@2.0.3-LICENSE.txt +21 -0
  409. package/third-party-notices/is-negative-zero@2.0.3-LICENSE.txt +20 -0
  410. package/third-party-notices/is-number-object@1.1.1-LICENSE.txt +22 -0
  411. package/third-party-notices/is-regex@1.2.1-LICENSE.txt +20 -0
  412. package/third-party-notices/is-set@2.0.3-LICENSE.txt +21 -0
  413. package/third-party-notices/is-shared-array-buffer@1.0.4-LICENSE.txt +21 -0
  414. package/third-party-notices/is-string@1.1.1-LICENSE.txt +22 -0
  415. package/third-party-notices/is-symbol@1.1.1-LICENSE.txt +22 -0
  416. package/third-party-notices/is-typed-array@1.1.15-LICENSE.txt +22 -0
  417. package/third-party-notices/is-weakmap@2.0.2-LICENSE.txt +21 -0
  418. package/third-party-notices/is-weakref@1.1.1-LICENSE.txt +21 -0
  419. package/third-party-notices/is-weakset@2.0.4-LICENSE.txt +21 -0
  420. package/third-party-notices/isarray@2.0.5-LICENSE.txt +21 -0
  421. package/third-party-notices/isomorphic.js@0.2.5-LICENSE.txt +21 -0
  422. package/third-party-notices/jszip@3.10.2-LICENSE.markdown.txt +651 -0
  423. package/third-party-notices/katex@0.18.9-LICENSE.txt +21 -0
  424. package/third-party-notices/kdbush@4.1.0-LICENSE.txt +15 -0
  425. package/third-party-notices/lazystream@1.0.1-LICENSE.txt +23 -0
  426. package/third-party-notices/lib0@0.2.119-LICENSE.txt +21 -0
  427. package/third-party-notices/lie@3.3.0-license.md.txt +7 -0
  428. package/third-party-notices/linkifyjs@4.3.3-LICENSE.txt +19 -0
  429. package/third-party-notices/listenercount@1.0.1-LICENSE.md.txt +5 -0
  430. package/third-party-notices/lodash-es@4.18.1-LICENSE.txt +47 -0
  431. package/third-party-notices/lodash.camelcase@4.3.0-LICENSE.txt +47 -0
  432. package/third-party-notices/lodash.defaults@4.2.0-LICENSE.txt +47 -0
  433. package/third-party-notices/lodash.difference@4.5.0-LICENSE.txt +47 -0
  434. package/third-party-notices/lodash.escaperegexp@4.1.2-LICENSE.txt +47 -0
  435. package/third-party-notices/lodash.flatten@4.4.0-LICENSE.txt +47 -0
  436. package/third-party-notices/lodash.groupby@4.6.0-LICENSE.txt +47 -0
  437. package/third-party-notices/lodash.isboolean@3.0.3-LICENSE.txt +22 -0
  438. package/third-party-notices/lodash.isequal@4.5.0-LICENSE.txt +47 -0
  439. package/third-party-notices/lodash.isfunction@3.0.9-LICENSE.txt +47 -0
  440. package/third-party-notices/lodash.isnil@4.0.0-LICENSE.txt +22 -0
  441. package/third-party-notices/lodash.isplainobject@4.0.6-LICENSE.txt +47 -0
  442. package/third-party-notices/lodash.isundefined@3.0.1-LICENSE.txt.txt +22 -0
  443. package/third-party-notices/lodash.union@4.6.0-LICENSE.txt +47 -0
  444. package/third-party-notices/lodash.uniq@4.5.0-LICENSE.txt +47 -0
  445. package/third-party-notices/long@5.3.2-LICENSE.txt +202 -0
  446. package/third-party-notices/lowlight@3.3.0-license.txt +22 -0
  447. package/third-party-notices/math-intrinsics@1.1.0-LICENSE.txt +21 -0
  448. package/third-party-notices/minimatch@3.1.5-LICENSE.txt +15 -0
  449. package/third-party-notices/minimatch@5.1.9-LICENSE.txt +15 -0
  450. package/third-party-notices/minimist@1.2.8-LICENSE.txt +18 -0
  451. package/third-party-notices/mkdirp@0.5.6-LICENSE.txt +21 -0
  452. package/third-party-notices/n-gram@2.0.2-license.txt +22 -0
  453. package/third-party-notices/normalize-path@3.0.0-LICENSE.txt +21 -0
  454. package/third-party-notices/numfmt@4.0.1-LICENSE.txt +19 -0
  455. package/third-party-notices/object-inspect@1.13.4-LICENSE.txt +21 -0
  456. package/third-party-notices/object-keys@1.1.1-LICENSE.txt +21 -0
  457. package/third-party-notices/object.assign@4.1.7-LICENSE.txt +21 -0
  458. package/third-party-notices/once@1.4.0-LICENSE.txt +15 -0
  459. package/third-party-notices/orderedmap@2.1.1-LICENSE.txt +19 -0
  460. package/third-party-notices/own-keys@1.0.2-LICENSE.txt +21 -0
  461. package/third-party-notices/pako@1.0.11-LICENSE.txt +21 -0
  462. package/third-party-notices/path-is-absolute@1.0.1-license.txt +21 -0
  463. package/third-party-notices/possible-typed-array-names@1.1.0-LICENSE.txt +21 -0
  464. package/third-party-notices/process-nextick-args@2.0.1-license.md.txt +19 -0
  465. package/third-party-notices/prosemirror-changeset@2.4.4-LICENSE.txt +19 -0
  466. package/third-party-notices/prosemirror-commands@1.7.2-LICENSE.txt +19 -0
  467. package/third-party-notices/prosemirror-dropcursor@1.8.4-LICENSE.txt +19 -0
  468. package/third-party-notices/prosemirror-gapcursor@1.4.1-LICENSE.txt +19 -0
  469. package/third-party-notices/prosemirror-history@1.5.1-LICENSE.txt +19 -0
  470. package/third-party-notices/prosemirror-inputrules@1.5.1-LICENSE.txt +19 -0
  471. package/third-party-notices/prosemirror-keymap@1.2.3-LICENSE.txt +19 -0
  472. package/third-party-notices/prosemirror-model@1.25.12-LICENSE.txt +19 -0
  473. package/third-party-notices/prosemirror-schema-list@1.5.1-LICENSE.txt +19 -0
  474. package/third-party-notices/prosemirror-state@1.4.4-LICENSE.txt +19 -0
  475. package/third-party-notices/prosemirror-tables@1.8.5-LICENSE.txt +19 -0
  476. package/third-party-notices/prosemirror-transform@1.12.2-LICENSE.txt +19 -0
  477. package/third-party-notices/prosemirror-view@1.42.6-LICENSE.txt +19 -0
  478. package/third-party-notices/protobufjs@7.6.6-LICENSE.txt +39 -0
  479. package/third-party-notices/quickselect@3.0.0-LICENSE.txt +15 -0
  480. package/third-party-notices/rbush@4.0.1-LICENSE.txt +21 -0
  481. package/third-party-notices/react-dom@19.3.1-LICENSE.txt +21 -0
  482. package/third-party-notices/react-remove-scroll@2.7.2-LICENSE.txt +21 -0
  483. package/third-party-notices/react-style-singleton@2.2.3-LICENSE.txt +21 -0
  484. package/third-party-notices/react@19.3.1-LICENSE.txt +21 -0
  485. package/third-party-notices/readable-stream@2.3.8-LICENSE.txt +47 -0
  486. package/third-party-notices/readable-stream@3.6.2-LICENSE.txt +47 -0
  487. package/third-party-notices/readdir-glob@1.1.3-LICENSE.txt +201 -0
  488. package/third-party-notices/reflect.getprototypeof@1.0.10-LICENSE.txt +21 -0
  489. package/third-party-notices/regexp.prototype.flags@1.5.4-LICENSE.txt +22 -0
  490. package/third-party-notices/require-directory@2.1.1-LICENSE.txt +22 -0
  491. package/third-party-notices/rimraf@2.7.1-LICENSE.txt +15 -0
  492. package/third-party-notices/rope-sequence@1.3.4-LICENSE.txt +19 -0
  493. package/third-party-notices/rxjs@7.8.2-LICENSE.txt.txt +202 -0
  494. package/third-party-notices/safe-array-concat@1.1.4-LICENSE.txt +21 -0
  495. package/third-party-notices/safe-buffer@5.1.2-LICENSE.txt +21 -0
  496. package/third-party-notices/safe-buffer@5.2.1-LICENSE.txt +21 -0
  497. package/third-party-notices/safe-push-apply@1.0.0-LICENSE.txt +21 -0
  498. package/third-party-notices/safe-regex-test@1.1.0-LICENSE.txt +21 -0
  499. package/third-party-notices/scheduler@0.28.0-LICENSE.txt +21 -0
  500. package/third-party-notices/set-function-length@1.2.2-LICENSE.txt +21 -0
  501. package/third-party-notices/set-function-name@2.0.2-LICENSE.txt +21 -0
  502. package/third-party-notices/set-proto@1.0.0-LICENSE.txt +21 -0
  503. package/third-party-notices/setimmediate@1.0.5-LICENSE.txt.txt +20 -0
  504. package/third-party-notices/side-channel-list@1.0.1-LICENSE.txt +21 -0
  505. package/third-party-notices/side-channel-map@1.0.1-LICENSE.txt +21 -0
  506. package/third-party-notices/side-channel-weakmap@1.0.2-LICENSE.txt +21 -0
  507. package/third-party-notices/side-channel@1.1.1-LICENSE.txt +21 -0
  508. package/third-party-notices/sonner@2.0.8-LICENSE.md.txt +21 -0
  509. package/third-party-notices/stop-iteration-iterator@1.1.0-LICENSE.txt +21 -0
  510. package/third-party-notices/string-width@4.2.3-license.txt +9 -0
  511. package/third-party-notices/string.prototype.trim@1.2.11-LICENSE.txt +21 -0
  512. package/third-party-notices/string.prototype.trimend@1.0.10-LICENSE.txt +21 -0
  513. package/third-party-notices/string.prototype.trimstart@1.0.8-LICENSE.txt +21 -0
  514. package/third-party-notices/string_decoder@1.1.1-LICENSE.txt +48 -0
  515. package/third-party-notices/string_decoder@1.3.0-LICENSE.txt +48 -0
  516. package/third-party-notices/strip-ansi@6.0.1-license.txt +9 -0
  517. package/third-party-notices/tar-stream@2.2.0-LICENSE.txt +21 -0
  518. package/third-party-notices/tiny-pinyin@1.3.2-LICENSE.txt +21 -0
  519. package/third-party-notices/tmp@0.2.7-LICENSE.txt +21 -0
  520. package/third-party-notices/traverse@0.3.10-LICENSE.txt +21 -0
  521. package/third-party-notices/trigram-utils@2.0.1-license.txt +22 -0
  522. package/third-party-notices/tslib@2.8.1-LICENSE.txt.txt +12 -0
  523. package/third-party-notices/typed-array-buffer@1.0.3-LICENSE.txt +21 -0
  524. package/third-party-notices/typed-array-byte-length@1.0.3-LICENSE.txt +21 -0
  525. package/third-party-notices/typed-array-byte-offset@1.0.5-LICENSE.txt +21 -0
  526. package/third-party-notices/typed-array-length@1.0.8-LICENSE.txt +21 -0
  527. package/third-party-notices/typedarray.prototype.slice@1.0.6-LICENSE.txt +21 -0
  528. package/third-party-notices/unbox-primitive@1.1.0-LICENSE.txt +21 -0
  529. package/third-party-notices/undici-types@8.9.0-LICENSE.txt +21 -0
  530. package/third-party-notices/unzipper@0.10.14-LICENSE.txt +25 -0
  531. package/third-party-notices/use-callback-ref@1.3.3-LICENSE.txt +21 -0
  532. package/third-party-notices/use-sidecar@1.1.3-LICENSE.txt +21 -0
  533. package/third-party-notices/util-deprecate@1.0.2-LICENSE.txt +24 -0
  534. package/third-party-notices/uuid@8.3.2-LICENSE.md.txt +9 -0
  535. package/third-party-notices/w3c-keyname@2.2.8-LICENSE.txt +19 -0
  536. package/third-party-notices/which-boxed-primitive@1.1.1-LICENSE.txt +21 -0
  537. package/third-party-notices/which-builtin-type@1.2.1-LICENSE.txt +21 -0
  538. package/third-party-notices/which-collection@1.0.2-LICENSE.txt +21 -0
  539. package/third-party-notices/which-typed-array@1.1.24-LICENSE.txt +22 -0
  540. package/third-party-notices/wrap-ansi@7.0.0-license.txt +9 -0
  541. package/third-party-notices/wrappy@1.0.2-LICENSE.txt +15 -0
  542. package/third-party-notices/xmlchars@2.2.0-LICENSE.txt +18 -0
  543. package/third-party-notices/y-protocols@1.0.7-LICENSE.txt +21 -0
  544. package/third-party-notices/y18n@5.0.8-LICENSE.txt +13 -0
  545. package/third-party-notices/yargs-parser@21.1.1-LICENSE.txt.txt +14 -0
  546. package/third-party-notices/yargs@17.7.3-LICENSE.txt +21 -0
  547. package/third-party-notices/yjs@13.6.33-LICENSE.txt +23 -0
  548. package/third-party-notices/zip-stream@4.1.1-LICENSE.txt +22 -0
@@ -0,0 +1,4210 @@
1
+ "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key2, value) => key2 in obj ? __defProp(obj, key2, { enumerable: true, configurable: true, writable: true, value }) : obj[key2] = value;
4
+ var __publicField = (obj, key2, value) => __defNormalProp(obj, typeof key2 !== "symbol" ? key2 + "" : key2, value);
5
+ import StarterKit from "@tiptap/starter-kit";
6
+ import Link from "@tiptap/extension-link";
7
+ import Placeholder from "@tiptap/extension-placeholder";
8
+ import { Table } from "@tiptap/extension-table";
9
+ import TableRow from "@tiptap/extension-table-row";
10
+ import TaskList from "@tiptap/extension-task-list";
11
+ import TaskItem from "@tiptap/extension-task-item";
12
+ import Collaboration from "@tiptap/extension-collaboration";
13
+ import { a as MindMapTopic, b as MindMapDescription, c as MindMapNote, d as MindMapRelation, T as TextStyle, H as Highlight, V as VideoEmbed, M as MindMap, C as Callout } from "./nodes-iKqTGr_N.js";
14
+ import { Fragment, Slice } from "@tiptap/pm/model";
15
+ import { u as unavailableAttachment, c as attachmentObjectUrl, d as downloadAttachment, f as formatBytes, e as ContentContextBridge } from "./host-C-V688Rq.js";
16
+ import { w as whenNearViewport, e as ensureKatexCss, r as renderMathAsync } from "./katex-WjrodtTU.js";
17
+ import { IMAGE_GALLERY_DEFAULT_HEIGHT, normalizeGalleryHeight, normalizeImageAspectRatio, IMAGE_GALLERY_MAX_IMAGES, imageAspectRatioFromSize } from "wendang/internal/image-gallery";
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ import { useState, useRef } from "react";
20
+ import { createRoot } from "react-dom/client";
21
+ import { calloutDOMAttributes, effectiveCallout, CALLOUT_COLORS, CALLOUT_FILLS, CALLOUT_TONES, calloutSwatchStyle, CALLOUT_ICONS } from "wendang/shared";
22
+ import { closeHistory } from "@tiptap/pm/history";
23
+ import { yUndoPluginKey, yCursorPlugin } from "@tiptap/y-tiptap";
24
+ import { validCalloutAttrs } from "wendang/internal/callout";
25
+ import { A as AnchoredPanel } from "./AnchoredPanel-D3nte06T.js";
26
+ import { W as WButton, b as WIcon } from "./index-Ii536CFG.js";
27
+ import { C as CodeToolbar, s as showCommentHint, h as hideCommentHint } from "./comment-hint-DN7LKJAn.js";
28
+ import CodeBlock from "@tiptap/extension-code-block";
29
+ import { codeBlockDisplay, codeLineNumbers, codeBlockClass } from "wendang/internal/code-block";
30
+ import { Node as Node$1, mergeAttributes, Extension } from "@tiptap/core";
31
+ import { Plugin, PluginKey, Selection, TextSelection, SelectionRange } from "@tiptap/pm/state";
32
+ import { r as resetCompositionState, e as endComposition, b as beginComposition, s as shouldIgnoreForIme } from "./composition-state-Bl-U1R_2.js";
33
+ import { Decoration, DecorationSet } from "@tiptap/pm/view";
34
+ import { e as ensureCodeHighlight, c as codeTokens } from "./code-highlight-BmaqTe1m.js";
35
+ import { resolveInlineComments } from "wendang/internal/inline-comments";
36
+ import { readableTextColor } from "wendang/internal/presence";
37
+ import { CellSelection, TableMap, addRow, addColumn, selectedRect } from "@tiptap/pm/tables";
38
+ import { cellBackground, CELL_BACKGROUNDS } from "wendang/internal/table-cell-format";
39
+ import { TABLE_CELL_MIN_WIDTH } from "wendang/internal/table-layout";
40
+ import TableCell from "@tiptap/extension-table-cell";
41
+ import TableHeader from "@tiptap/extension-table-header";
42
+ import { paletteColor } from "wendang/internal/document-colors";
43
+ import Image from "@tiptap/extension-image";
44
+ const MAX_COLUMNS = 5;
45
+ const MIN_COLUMNS = 1;
46
+ const Columns = Node$1.create({
47
+ name: "columns",
48
+ group: "block",
49
+ // 至少一栏:空行容器会让光标无处可落
50
+ content: "column+",
51
+ defining: true,
52
+ parseHTML() {
53
+ return [{ tag: "div[data-columns]" }];
54
+ },
55
+ renderHTML({ HTMLAttributes }) {
56
+ return [
57
+ "div",
58
+ // 分栏是多列结构:铺整块蓝底会把栏间空隙也染上,只加外圈
59
+ mergeAttributes(HTMLAttributes, {
60
+ "data-columns": "true",
61
+ "data-block-chrome": "",
62
+ class: "doc-columns"
63
+ }),
64
+ 0
65
+ ];
66
+ }
67
+ });
68
+ const Column = Node$1.create({
69
+ name: "column",
70
+ // 刻意不设 group:只能作为 columns 的子节点存在(见文件头说明)
71
+ content: "block+",
72
+ defining: true,
73
+ parseHTML() {
74
+ return [{ tag: "div[data-column]" }];
75
+ },
76
+ renderHTML({ HTMLAttributes }) {
77
+ return [
78
+ "div",
79
+ mergeAttributes(HTMLAttributes, { "data-column": "true", class: "doc-column" }),
80
+ 0
81
+ ];
82
+ }
83
+ });
84
+ function buildColumnsJson(count) {
85
+ const n = Math.min(Math.max(Math.round(count), MIN_COLUMNS), MAX_COLUMNS);
86
+ return {
87
+ type: "columns",
88
+ content: Array.from({ length: n }, () => ({
89
+ type: "column",
90
+ content: [{ type: "paragraph" }]
91
+ }))
92
+ };
93
+ }
94
+ const FileAttachment = Node$1.create({
95
+ name: "fileAttachment",
96
+ group: "block",
97
+ atom: true,
98
+ selectable: true,
99
+ draggable: false,
100
+ addAttributes() {
101
+ return {
102
+ attachmentId: { default: null },
103
+ filename: { default: "" },
104
+ /** MIME 类型:前端据此显示图标/是否可内嵌播放 */
105
+ mimeType: { default: "" },
106
+ /** 字节数(用于显示文件大小) */
107
+ size: { default: 0 }
108
+ };
109
+ },
110
+ parseHTML() {
111
+ return [{ tag: "div[data-file-attachment]" }];
112
+ },
113
+ renderHTML({ HTMLAttributes, node }) {
114
+ const name = node.attrs.filename || "附件";
115
+ return [
116
+ "div",
117
+ mergeAttributes(HTMLAttributes, {
118
+ "data-file-attachment": "true",
119
+ "data-attachment-id": node.attrs.attachmentId ?? "",
120
+ // 附件卡片自带外框与底色:块选中时只加外圈,不铺蓝底(同思维导图)
121
+ "data-block-chrome": "",
122
+ class: "doc-file-attachment"
123
+ }),
124
+ `[附件]${name}`
125
+ ];
126
+ },
127
+ /** 导出为文本时的降级表示(真正的 Markdown 导出由服务端序列化器处理)。 */
128
+ renderText({ node }) {
129
+ return node.attrs.filename || "附件";
130
+ }
131
+ });
132
+ const YwikiImage = Image.extend({
133
+ addAttributes() {
134
+ var _a;
135
+ return {
136
+ // 保留官方属性(src/alt/title)
137
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
138
+ attachmentId: {
139
+ default: null,
140
+ parseHTML: (el) => el.getAttribute("data-attachment-id"),
141
+ renderHTML: (attrs) => attrs.attachmentId ? { "data-attachment-id": attrs.attachmentId } : {}
142
+ },
143
+ /**
144
+ * 显示宽度(px)。null = 跟随正文宽度(默认)。
145
+ *
146
+ * 为什么只存宽度、不存高度(对齐飞书):图片缩放是**等比**的,
147
+ * 存两个数字会带来"宽高不一致"的坏数据,而高度由宽度和原始宽高比
148
+ * 天然推出。服务端属性白名单已登记 `width`(doc-schema 的 image 集合)。
149
+ */
150
+ width: {
151
+ default: null,
152
+ parseHTML: (el) => {
153
+ const raw = el.getAttribute("data-width") ?? el.getAttribute("width");
154
+ const n = Number(raw);
155
+ return Number.isFinite(n) && n > 0 ? Math.round(n) : null;
156
+ },
157
+ renderHTML: (attrs) => {
158
+ const n = Number(attrs.width);
159
+ return Number.isFinite(n) && n > 0 ? { "data-width": String(Math.round(n)), width: String(Math.round(n)) } : {};
160
+ }
161
+ },
162
+ /**
163
+ * 水平对齐:left / center / right;null = 跟随默认观感
164
+ * (未缩放的图居中、已缩放的图靠左,见两端样式)。
165
+ *
166
+ * 对应飞书选中图片后上方工具条的「左/中/右对齐」(用户口径:
167
+ * 飞书的图片编辑是两部分——四角手柄调大小 + 工具条做对齐等操作)。
168
+ * 渲染成 `data-align`,编辑端与阅读端共用同一套 margin 规则。
169
+ */
170
+ align: {
171
+ default: null,
172
+ parseHTML: (el) => {
173
+ const v = el.getAttribute("data-align");
174
+ return v === "left" || v === "center" || v === "right" ? v : null;
175
+ },
176
+ renderHTML: (attrs) => {
177
+ const v = attrs.align;
178
+ return v === "left" || v === "center" || v === "right" ? { "data-align": v } : {};
179
+ }
180
+ },
181
+ /**
182
+ * 原图宽高比(宽 / 高),供图片行按比例分配列宽。
183
+ * 不替代 `width`:独立图片仍按显式像素宽度显示。
184
+ */
185
+ aspectRatio: {
186
+ default: null,
187
+ parseHTML: (el) => {
188
+ const value = Number(el.getAttribute("data-aspect-ratio"));
189
+ return Number.isFinite(value) && value > 0 ? value : null;
190
+ },
191
+ renderHTML: (attrs) => {
192
+ const value = Number(attrs.aspectRatio);
193
+ return Number.isFinite(value) && value > 0 ? { "data-aspect-ratio": String(value) } : {};
194
+ }
195
+ },
196
+ /**
197
+ * 图片行内的**相对宽度权重**(1 = 按原始比例参与分配)。
198
+ *
199
+ * 对应飞书"在图库里拖四角调大小":不是改某张图的绝对像素,而是改它
200
+ * 相对同行的权重,整行重新归一化分配——所以同行仍然等高、也不会溢出。
201
+ * 独立图片不使用该属性(用 `width`)。
202
+ */
203
+ galleryScale: {
204
+ default: null,
205
+ parseHTML: (el) => {
206
+ const value = Number(el.getAttribute("data-gallery-scale"));
207
+ return Number.isFinite(value) && value > 0 ? value : null;
208
+ },
209
+ renderHTML: (attrs) => {
210
+ const value = Number(attrs.galleryScale);
211
+ return Number.isFinite(value) && value > 0 && value !== 1 ? { "data-gallery-scale": String(value) } : {};
212
+ }
213
+ },
214
+ /**
215
+ * 图注(显示在图片下方的一行说明文字)。
216
+ *
217
+ * 为什么放在节点属性而不是独立段落:图注与图片是**一体**的,
218
+ * 移动/删除图片时图注要跟着走;独立段落会在复制、拖拽时被拆散。
219
+ */
220
+ caption: {
221
+ default: null,
222
+ parseHTML: (el) => el.getAttribute("data-caption") || null,
223
+ renderHTML: (attrs) => attrs.caption ? { "data-caption": String(attrs.caption) } : {}
224
+ }
225
+ };
226
+ },
227
+ parseHTML() {
228
+ var _a;
229
+ return [{ tag: "img[data-attachment-id]" }, ...((_a = this.parent) == null ? void 0 : _a.call(this)) ?? []];
230
+ }
231
+ });
232
+ const ImageGallery = Node$1.create({
233
+ name: "imageGallery",
234
+ group: "block",
235
+ content: "image{2,10}",
236
+ defining: true,
237
+ addAttributes() {
238
+ return {
239
+ height: {
240
+ default: IMAGE_GALLERY_DEFAULT_HEIGHT,
241
+ parseHTML: (element) => normalizeGalleryHeight(element.getAttribute("data-gallery-height")),
242
+ renderHTML: (attrs) => {
243
+ const height = normalizeGalleryHeight(attrs.height);
244
+ return {
245
+ "data-gallery-height": String(height),
246
+ style: `--image-gallery-height:${height}px`
247
+ };
248
+ }
249
+ }
250
+ };
251
+ },
252
+ parseHTML() {
253
+ return [{ tag: "div[data-image-gallery]" }];
254
+ },
255
+ renderHTML({ HTMLAttributes }) {
256
+ return [
257
+ "div",
258
+ mergeAttributes(HTMLAttributes, {
259
+ "data-image-gallery": "true",
260
+ class: "ywiki-image-gallery"
261
+ }),
262
+ 0
263
+ ];
264
+ }
265
+ });
266
+ const MathInline = Node$1.create({
267
+ name: "mathInline",
268
+ group: "inline",
269
+ inline: true,
270
+ atom: true,
271
+ selectable: true,
272
+ addAttributes() {
273
+ return {
274
+ latex: {
275
+ default: "",
276
+ parseHTML: (el) => el.getAttribute("data-latex") ?? "",
277
+ renderHTML: (attrs) => ({ "data-latex": attrs.latex })
278
+ }
279
+ };
280
+ },
281
+ parseHTML() {
282
+ return [{ tag: "span[data-math-inline]" }];
283
+ },
284
+ renderHTML({ HTMLAttributes, node }) {
285
+ return [
286
+ "span",
287
+ mergeAttributes(HTMLAttributes, {
288
+ "data-math-inline": "true",
289
+ class: "doc-math-inline"
290
+ }),
291
+ `$${node.attrs.latex}$`
292
+ ];
293
+ },
294
+ renderText({ node }) {
295
+ return `$${node.attrs.latex}$`;
296
+ }
297
+ });
298
+ const MathBlock = Node$1.create({
299
+ name: "mathBlock",
300
+ group: "block",
301
+ atom: true,
302
+ selectable: true,
303
+ addAttributes() {
304
+ return {
305
+ latex: {
306
+ default: "",
307
+ parseHTML: (el) => el.getAttribute("data-latex") ?? "",
308
+ renderHTML: (attrs) => ({ "data-latex": attrs.latex })
309
+ }
310
+ };
311
+ },
312
+ parseHTML() {
313
+ return [{ tag: "div[data-math-block]" }];
314
+ },
315
+ renderHTML({ HTMLAttributes, node }) {
316
+ return [
317
+ "div",
318
+ mergeAttributes(HTMLAttributes, {
319
+ "data-math-block": "true",
320
+ /**
321
+ * 公式块自己是一块居中画布,选中时只加外圈、不铺蓝底。
322
+ *
323
+ * 铺蓝底会用到 `::before` 并向外扩 6px,而公式块是 `overflow-x: auto`
324
+ * 的容器——溢出的伪元素直接变成可滚动内容,选中时右侧/底部就冒出
325
+ * 一对滚动条(用户实测反馈:"公式选中为什么有滚动条了")。
326
+ */
327
+ "data-block-chrome": "",
328
+ class: "doc-math-block"
329
+ }),
330
+ `$$${node.attrs.latex}$$`
331
+ ];
332
+ },
333
+ renderText({ node }) {
334
+ return `$$
335
+ ${node.attrs.latex}
336
+ $$`;
337
+ }
338
+ });
339
+ const PageLink = Node$1.create({
340
+ name: "pageLink",
341
+ group: "inline",
342
+ inline: true,
343
+ atom: true,
344
+ selectable: true,
345
+ addAttributes() {
346
+ return {
347
+ pageId: { default: null },
348
+ label: { default: "" }
349
+ };
350
+ },
351
+ parseHTML() {
352
+ return [
353
+ {
354
+ tag: "span[data-page-link]",
355
+ getAttrs: (el) => {
356
+ const dom = el;
357
+ return {
358
+ pageId: dom.getAttribute("data-page-id") ?? null,
359
+ label: dom.textContent ?? ""
360
+ };
361
+ }
362
+ }
363
+ ];
364
+ },
365
+ renderHTML({ HTMLAttributes, node }) {
366
+ return [
367
+ "span",
368
+ mergeAttributes(HTMLAttributes, {
369
+ "data-page-link": "true",
370
+ "data-page-id": node.attrs.pageId,
371
+ class: "doc-page-link"
372
+ }),
373
+ node.attrs.label || "页面"
374
+ ];
375
+ },
376
+ renderText({ node }) {
377
+ return node.attrs.label || "页面";
378
+ }
379
+ });
380
+ const Mention = Node$1.create({
381
+ name: "mention",
382
+ group: "inline",
383
+ inline: true,
384
+ atom: true,
385
+ selectable: true,
386
+ addAttributes() {
387
+ return {
388
+ userId: { default: null },
389
+ label: { default: "" }
390
+ };
391
+ },
392
+ parseHTML() {
393
+ return [
394
+ {
395
+ tag: "span[data-mention]",
396
+ getAttrs: (el) => {
397
+ var _a;
398
+ const dom = el;
399
+ return {
400
+ userId: dom.getAttribute("data-user-id") ?? null,
401
+ label: ((_a = dom.textContent) == null ? void 0 : _a.replace(/^@/, "")) ?? ""
402
+ };
403
+ }
404
+ }
405
+ ];
406
+ },
407
+ renderHTML({ HTMLAttributes, node }) {
408
+ return [
409
+ "span",
410
+ mergeAttributes(HTMLAttributes, {
411
+ "data-mention": "true",
412
+ "data-user-id": node.attrs.userId,
413
+ class: "doc-mention"
414
+ }),
415
+ `@${node.attrs.label || "用户"}`
416
+ ];
417
+ },
418
+ renderText({ node }) {
419
+ return `@${node.attrs.label || "用户"}`;
420
+ }
421
+ });
422
+ const CELL_ALIGNS = ["left", "center", "right"];
423
+ const isCellAlign = (v) => typeof v === "string" && CELL_ALIGNS.includes(v);
424
+ const alignAttribute = {
425
+ default: null,
426
+ parseHTML: (el) => {
427
+ var _a;
428
+ const fromStyle = (_a = el.style) == null ? void 0 : _a.textAlign;
429
+ if (isCellAlign(fromStyle)) return fromStyle;
430
+ const fromData = el.getAttribute("data-align");
431
+ return isCellAlign(fromData) ? fromData : null;
432
+ },
433
+ renderHTML: (attrs) => {
434
+ return isCellAlign(attrs.align) ? { "data-align": attrs.align, style: `text-align: ${attrs.align}` } : {};
435
+ }
436
+ };
437
+ const backgroundAttribute = {
438
+ default: null,
439
+ parseHTML: (el) => paletteColor(el.getAttribute("data-cell-background") ?? el.style.backgroundColor, CELL_BACKGROUNDS),
440
+ renderHTML: (attrs) => {
441
+ const color = cellBackground(attrs.backgroundColor);
442
+ return color ? { "data-cell-background": color, style: `background-color: ${color}; color: #202124` } : {};
443
+ }
444
+ };
445
+ const AlignedTableCell = TableCell.extend({
446
+ addAttributes() {
447
+ var _a;
448
+ return {
449
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
450
+ align: alignAttribute,
451
+ backgroundColor: backgroundAttribute
452
+ };
453
+ }
454
+ });
455
+ const AlignedTableHeader = TableHeader.extend({
456
+ addAttributes() {
457
+ var _a;
458
+ return {
459
+ ...(_a = this.parent) == null ? void 0 : _a.call(this),
460
+ align: alignAttribute,
461
+ backgroundColor: backgroundAttribute
462
+ };
463
+ }
464
+ });
465
+ const MIN_WIDTH = 40;
466
+ let standaloneImageDrag = null;
467
+ function imageDragLayer() {
468
+ const portal = document.querySelector(".wendang-portal");
469
+ if (portal instanceof HTMLElement) return portal;
470
+ const shell = document.querySelector(".editor-shell");
471
+ if (shell instanceof HTMLElement) return shell;
472
+ return document.body;
473
+ }
474
+ function clearImageDropTargets() {
475
+ for (const element of document.querySelectorAll(".is-image-drop-target")) {
476
+ element.classList.remove("is-image-drop-target");
477
+ }
478
+ const indicator = document.getElementById("ywiki-image-drop-line");
479
+ indicator == null ? void 0 : indicator.remove();
480
+ }
481
+ const createImageNodeView = (fetcher = unavailableAttachment, scope) => ({ node, getPos, editor }) => {
482
+ var _a, _b, _c, _d, _e, _f, _g;
483
+ const frame = document.createElement("div");
484
+ frame.className = "ywiki-image-frame";
485
+ const inner = document.createElement("div");
486
+ inner.className = "ywiki-image-inner";
487
+ frame.appendChild(inner);
488
+ const dom = document.createElement("img");
489
+ dom.className = "ywiki-image";
490
+ dom.setAttribute("draggable", "false");
491
+ let attachmentId = String(((_a = node.attrs) == null ? void 0 : _a.attachmentId) ?? "");
492
+ const inImageGallery = () => {
493
+ const pos = typeof getPos === "function" ? getPos() : null;
494
+ if (pos === null || pos === void 0 || editor.isDestroyed || pos >= editor.state.doc.content.size) return false;
495
+ try {
496
+ return editor.state.doc.resolve(pos).parent.type.name === "imageGallery";
497
+ } catch {
498
+ return false;
499
+ }
500
+ };
501
+ let fallbackAlt = String(((_b = node.attrs) == null ? void 0 : _b.alt) ?? "");
502
+ let currentNode = node;
503
+ const aspectCloseEnough = (a, b) => {
504
+ if (!Number.isFinite(a) || !Number.isFinite(b) || b <= 0) return false;
505
+ return Math.abs(a - b) / b <= 0.02;
506
+ };
507
+ const backfillAspectRatio = () => {
508
+ var _a2, _b2;
509
+ const natural = imageAspectRatioFromSize(dom.naturalWidth, dom.naturalHeight);
510
+ if (natural === null) return;
511
+ const stored = normalizeImageAspectRatio((_a2 = currentNode.attrs) == null ? void 0 : _a2.aspectRatio);
512
+ if (stored !== null && aspectCloseEnough(stored, natural)) return;
513
+ const pos = typeof getPos === "function" ? getPos() : null;
514
+ if (pos === null || pos === void 0 || editor.isDestroyed || !editor.isEditable) return;
515
+ const current = editor.state.doc.nodeAt(pos);
516
+ if (!current || current.type.name !== "image") return;
517
+ const latest = normalizeImageAspectRatio((_b2 = current.attrs) == null ? void 0 : _b2.aspectRatio);
518
+ if (latest !== null && aspectCloseEnough(latest, natural)) return;
519
+ editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, void 0, {
520
+ ...current.attrs,
521
+ aspectRatio: natural
522
+ }));
523
+ };
524
+ const markLoaded = () => {
525
+ dom.removeAttribute("data-image-loading");
526
+ if (fallbackAlt) {
527
+ dom.alt = fallbackAlt;
528
+ dom.title = fallbackAlt;
529
+ }
530
+ backfillAspectRatio();
531
+ };
532
+ const markFailed = () => {
533
+ dom.removeAttribute("data-image-loading");
534
+ if (fallbackAlt) {
535
+ dom.alt = fallbackAlt;
536
+ dom.title = fallbackAlt;
537
+ }
538
+ dom.setAttribute("data-image-error", "true");
539
+ };
540
+ dom.setAttribute("data-image-loading", "true");
541
+ const applyWidth = (value) => {
542
+ if (inImageGallery()) {
543
+ dom.style.removeProperty("width");
544
+ frame.removeAttribute("data-sized");
545
+ return;
546
+ }
547
+ const n = Number(value);
548
+ if (Number.isFinite(n) && n > 0) {
549
+ dom.style.width = `${Math.round(n)}px`;
550
+ frame.setAttribute("data-sized", "true");
551
+ } else {
552
+ dom.style.removeProperty("width");
553
+ frame.removeAttribute("data-sized");
554
+ }
555
+ };
556
+ applyWidth((_c = node.attrs) == null ? void 0 : _c.width);
557
+ const applyAspectRatio = (value) => {
558
+ const ratio = normalizeImageAspectRatio(value);
559
+ if (ratio === null) {
560
+ frame.style.removeProperty("--ywiki-image-ratio");
561
+ frame.removeAttribute("data-aspect-ratio");
562
+ } else {
563
+ frame.style.setProperty("--ywiki-image-ratio", String(ratio));
564
+ frame.setAttribute("data-aspect-ratio", String(ratio));
565
+ }
566
+ };
567
+ const applyGalleryScale = (value) => {
568
+ const scale = Number(value);
569
+ if (Number.isFinite(scale) && scale > 0 && scale !== 1) {
570
+ frame.style.setProperty("--ywiki-image-weight", String(scale));
571
+ frame.setAttribute("data-gallery-scale", String(scale));
572
+ } else {
573
+ frame.style.removeProperty("--ywiki-image-weight");
574
+ frame.removeAttribute("data-gallery-scale");
575
+ }
576
+ };
577
+ applyAspectRatio((_d = node.attrs) == null ? void 0 : _d.aspectRatio);
578
+ applyGalleryScale((_e = node.attrs) == null ? void 0 : _e.galleryScale);
579
+ const caption = document.createElement("div");
580
+ caption.className = "ywiki-image-caption";
581
+ caption.setAttribute("contenteditable", "false");
582
+ let editingCaption = false;
583
+ const applyCaption = (value) => {
584
+ const text = typeof value === "string" ? value : "";
585
+ if (editingCaption) return;
586
+ caption.textContent = text;
587
+ caption.hidden = text.length === 0;
588
+ frame.classList.toggle("has-caption", text.length > 0);
589
+ if (text) caption.title = text;
590
+ };
591
+ applyCaption((_f = node.attrs) == null ? void 0 : _f.caption);
592
+ function commitCaption(value) {
593
+ var _a2;
594
+ editingCaption = false;
595
+ caption.removeAttribute("contenteditable");
596
+ caption.classList.remove("is-editing");
597
+ const pos = typeof getPos === "function" ? getPos() : null;
598
+ if (pos === null || pos === void 0 || editor.isDestroyed) return;
599
+ const latest = editor.state.doc.nodeAt(pos);
600
+ if (!latest || latest.type.name !== "image") return;
601
+ const next = value.trim().slice(0, 500);
602
+ if ((((_a2 = latest.attrs) == null ? void 0 : _a2.caption) ?? "") === (next || null)) {
603
+ applyCaption(next);
604
+ return;
605
+ }
606
+ editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, void 0, {
607
+ ...latest.attrs,
608
+ caption: next || null
609
+ }));
610
+ }
611
+ function startCaptionEdit() {
612
+ var _a2;
613
+ if (!editor.isEditable) return;
614
+ editingCaption = true;
615
+ caption.hidden = false;
616
+ frame.classList.add("has-caption");
617
+ caption.textContent = String(((_a2 = node.attrs) == null ? void 0 : _a2.caption) ?? "");
618
+ caption.setAttribute("contenteditable", "true");
619
+ caption.classList.add("is-editing");
620
+ caption.focus();
621
+ const range = document.createRange();
622
+ range.selectNodeContents(caption);
623
+ range.collapse(false);
624
+ const selection = window.getSelection();
625
+ selection == null ? void 0 : selection.removeAllRanges();
626
+ selection == null ? void 0 : selection.addRange(range);
627
+ }
628
+ caption.addEventListener("dblclick", (event) => {
629
+ event.preventDefault();
630
+ event.stopPropagation();
631
+ startCaptionEdit();
632
+ });
633
+ caption.addEventListener("keydown", (event) => {
634
+ if (!editingCaption) return;
635
+ event.stopPropagation();
636
+ if (event.key === "Escape") {
637
+ event.preventDefault();
638
+ commitCaption("");
639
+ } else if (event.key === "Enter") {
640
+ event.preventDefault();
641
+ commitCaption(caption.textContent ?? "");
642
+ }
643
+ });
644
+ caption.addEventListener("blur", () => {
645
+ if (editingCaption) commitCaption(caption.textContent ?? "");
646
+ });
647
+ frame.addEventListener("ywiki-caption-edit", startCaptionEdit);
648
+ const applyAlign = (value) => {
649
+ if (value === "left" || value === "center" || value === "right") {
650
+ frame.setAttribute("data-align", value);
651
+ } else {
652
+ frame.removeAttribute("data-align");
653
+ }
654
+ };
655
+ applyAlign((_g = node.attrs) == null ? void 0 : _g.align);
656
+ let stopWatch = null;
657
+ let releaseUrl;
658
+ const fetchInto = (id) => {
659
+ releaseUrl == null ? void 0 : releaseUrl();
660
+ releaseUrl = attachmentObjectUrl(id, (url) => {
661
+ if (attachmentId !== id) return;
662
+ dom.src = url;
663
+ if (dom.complete && dom.naturalWidth > 0) markLoaded();
664
+ else dom.addEventListener("load", () => markLoaded(), { once: true });
665
+ }, fetcher, () => {
666
+ if (attachmentId === id) markFailed();
667
+ }, scope);
668
+ };
669
+ const load = (id) => {
670
+ releaseUrl == null ? void 0 : releaseUrl();
671
+ stopWatch == null ? void 0 : stopWatch();
672
+ stopWatch = null;
673
+ if (!id) {
674
+ dom.removeAttribute("src");
675
+ dom.removeAttribute("data-image-loading");
676
+ dom.setAttribute("data-image-missing", "true");
677
+ return;
678
+ }
679
+ dom.removeAttribute("data-image-missing");
680
+ dom.removeAttribute("data-image-error");
681
+ if (dom.getAttribute("src")) markLoaded();
682
+ else dom.setAttribute("data-image-loading", "true");
683
+ stopWatch = whenNearViewport(dom, () => fetchInto(id));
684
+ };
685
+ load(attachmentId);
686
+ const CORNER_CURSOR = {
687
+ nw: "nwse-resize",
688
+ ne: "nesw-resize",
689
+ sw: "nesw-resize",
690
+ se: "nwse-resize"
691
+ };
692
+ const handles = [];
693
+ for (const corner of ["nw", "ne", "sw", "se"]) {
694
+ const h = document.createElement("div");
695
+ h.className = `ywiki-image-handle handle-${corner}`;
696
+ h.setAttribute("aria-hidden", "true");
697
+ h.setAttribute("contenteditable", "false");
698
+ h.addEventListener("mousedown", (e) => startResize(e, corner));
699
+ inner.appendChild(h);
700
+ handles.push(h);
701
+ }
702
+ const syncGalleryMode = () => {
703
+ const inGallery = inImageGallery();
704
+ frame.classList.toggle("is-in-gallery", inGallery);
705
+ frame.setAttribute("draggable", inGallery ? "false" : "true");
706
+ for (const h of handles) h.hidden = inGallery;
707
+ };
708
+ syncGalleryMode();
709
+ requestAnimationFrame(() => syncGalleryMode());
710
+ const setSelected = (on) => {
711
+ frame.classList.toggle("is-selected", on);
712
+ };
713
+ let galleryDrag = null;
714
+ let galleryIndicator = null;
715
+ const galleryContainerOf = (pos) => {
716
+ if (editor.isDestroyed) return null;
717
+ try {
718
+ const $pos = editor.state.doc.resolve(pos);
719
+ const parent = $pos.parent;
720
+ if (parent.type.name !== "imageGallery" || $pos.depth === 0) return null;
721
+ return { node: parent, pos: $pos.before() };
722
+ } catch {
723
+ return null;
724
+ }
725
+ };
726
+ function clearGalleryDrag() {
727
+ galleryDrag = null;
728
+ if (galleryIndicator) {
729
+ galleryIndicator.remove();
730
+ galleryIndicator = null;
731
+ }
732
+ document.removeEventListener("mousemove", onGalleryMove, true);
733
+ document.removeEventListener("mouseup", onGalleryUp, true);
734
+ frame.classList.remove("is-gallery-dragging");
735
+ document.documentElement.style.cursor = "";
736
+ }
737
+ function showIndicator(x, top, height) {
738
+ if (!galleryIndicator) {
739
+ galleryIndicator = document.createElement("div");
740
+ galleryIndicator.className = "ywiki-gallery-drop-line";
741
+ galleryIndicator.setAttribute("aria-hidden", "true");
742
+ const accent = getComputedStyle(frame).getPropertyValue("--color-accent").trim() || getComputedStyle(document.documentElement).getPropertyValue("--color-accent").trim() || "#2f6feb";
743
+ galleryIndicator.style.background = accent;
744
+ galleryIndicator.style.width = "3px";
745
+ galleryIndicator.style.borderRadius = "999px";
746
+ galleryIndicator.style.border = "1px solid rgba(255, 255, 255, 0.9)";
747
+ galleryIndicator.style.boxShadow = "0 0 0 1px rgba(0, 0, 0, 0.06), 0 2px 10px rgba(0, 0, 0, 0.18)";
748
+ imageDragLayer().appendChild(galleryIndicator);
749
+ }
750
+ galleryIndicator.style.left = `${Math.round(x) - 1}px`;
751
+ galleryIndicator.style.top = `${Math.round(top)}px`;
752
+ galleryIndicator.style.height = `${Math.round(height)}px`;
753
+ galleryIndicator.style.opacity = "1";
754
+ }
755
+ const onGalleryMove = (event) => {
756
+ const drag = galleryDrag;
757
+ if (!drag) return;
758
+ drag.lastX = event.clientX;
759
+ drag.lastY = event.clientY;
760
+ const container = frame.closest(".ywiki-image-gallery");
761
+ if (!(container instanceof HTMLElement)) return;
762
+ const rect = container.getBoundingClientRect();
763
+ const children = Array.from(container.children).filter((el) => el instanceof HTMLElement && el !== galleryIndicator);
764
+ drag.outside = event.clientX < rect.left - 24 || event.clientX > rect.right + 24 || event.clientY < rect.top - 24 || event.clientY > rect.bottom + 24;
765
+ if (drag.outside) {
766
+ if (galleryIndicator) galleryIndicator.style.opacity = "0";
767
+ return;
768
+ }
769
+ const others = children.filter((el) => el !== frame);
770
+ let dropIndex = others.length;
771
+ for (let i = 0; i < others.length; i += 1) {
772
+ const childRect = others[i].getBoundingClientRect();
773
+ if (event.clientX < childRect.left + childRect.width / 2) {
774
+ dropIndex = i;
775
+ break;
776
+ }
777
+ }
778
+ drag.dropIndex = drag.index <= dropIndex ? dropIndex + 1 : dropIndex;
779
+ const reference = others[Math.min(dropIndex, others.length - 1)];
780
+ let x = rect.left;
781
+ if (reference) {
782
+ const refRect = reference.getBoundingClientRect();
783
+ x = dropIndex >= others.length ? refRect.right : refRect.left;
784
+ }
785
+ showIndicator(x, rect.top, rect.height);
786
+ };
787
+ const onGalleryUp = () => {
788
+ const drag = galleryDrag;
789
+ clearGalleryDrag();
790
+ if (!drag || !editor.isEditable) return;
791
+ const pos = typeof getPos === "function" ? getPos() : null;
792
+ if (pos === null || pos === void 0) return;
793
+ const container = galleryContainerOf(pos);
794
+ if (!container) return;
795
+ const node2 = editor.state.doc.nodeAt(pos);
796
+ if (!node2 || node2.type.name !== "image") return;
797
+ if (!drag.outside && drag.dropIndex === drag.index) return;
798
+ const tr = editor.state.tr;
799
+ const gallery = container.node;
800
+ const imageType = editor.schema.nodes.image;
801
+ const galleryType = editor.schema.nodes.imageGallery;
802
+ if (!imageType || !galleryType) return;
803
+ const children = [];
804
+ gallery.forEach((child) => {
805
+ children.push(child);
806
+ });
807
+ if (children.length !== drag.childPositions.length) return;
808
+ const galleryStart = container.pos;
809
+ const galleryEnd = galleryStart + gallery.nodeSize;
810
+ if (drag.outside) {
811
+ const hit = imageHitTest(drag.lastX, drag.lastY);
812
+ if (hit && hit.pos !== galleryStart) {
813
+ if (hit.kind === "image") {
814
+ const merged = mergeIntoStandalone(galleryStart, gallery, node2, drag.index, hit);
815
+ if (merged) return;
816
+ } else if (moveImageIntoGalleryAt(galleryStart, gallery, node2, drag.index, hit, { x: drag.lastX, y: drag.lastY })) {
817
+ return;
818
+ }
819
+ }
820
+ children.splice(drag.index, 1);
821
+ const replacement = children.length >= 2 ? [galleryType.create(gallery.attrs, Fragment.fromArray(children)), node2] : children.length === 1 ? [children[0], node2] : [node2];
822
+ tr.replaceWith(galleryStart, galleryEnd, Fragment.fromArray(replacement));
823
+ editor.view.dispatch(tr);
824
+ editor.commands.focus();
825
+ return;
826
+ }
827
+ const from = drag.index;
828
+ const to = drag.dropIndex > from ? drag.dropIndex - 1 : drag.dropIndex;
829
+ if (to === from) return;
830
+ const [moved] = children.splice(from, 1);
831
+ children.splice(to, 0, moved);
832
+ tr.replaceWith(galleryStart, galleryEnd, galleryType.create(gallery.attrs, Fragment.fromArray(children)));
833
+ editor.view.dispatch(tr);
834
+ editor.commands.focus();
835
+ };
836
+ function startGalleryDrag(event) {
837
+ if (event.button !== 0 || !editor.isEditable || event.altKey) return;
838
+ const pos = typeof getPos === "function" ? getPos() : null;
839
+ if (pos === null || pos === void 0) return;
840
+ const container = galleryContainerOf(pos);
841
+ if (!container) return;
842
+ const childPositions = [];
843
+ container.node.forEach((child, offset) => {
844
+ childPositions.push(container.pos + 1 + offset);
845
+ });
846
+ const index = childPositions.indexOf(pos);
847
+ if (index < 0) return;
848
+ const startX = event.clientX;
849
+ const startY = event.clientY;
850
+ let armed = false;
851
+ const arm = (moveEvent) => {
852
+ if (armed) return;
853
+ const dx = moveEvent.clientX - startX;
854
+ const dy = moveEvent.clientY - startY;
855
+ if (Math.hypot(dx, dy) < 32) return;
856
+ armed = true;
857
+ document.removeEventListener("mousemove", arm, true);
858
+ frame.classList.add("is-gallery-dragging");
859
+ document.documentElement.style.cursor = "grabbing";
860
+ galleryDrag = {
861
+ galleryPos: container.pos,
862
+ galleryEnd: container.pos + container.node.nodeSize,
863
+ childPositions,
864
+ index,
865
+ dropIndex: index,
866
+ outside: false,
867
+ element: frame,
868
+ lastX: event.clientX,
869
+ lastY: event.clientY
870
+ };
871
+ document.addEventListener("mousemove", onGalleryMove, true);
872
+ document.addEventListener("mouseup", onGalleryUp, true);
873
+ };
874
+ const blockNativeDrag = (dragEvent) => {
875
+ dragEvent.preventDefault();
876
+ };
877
+ frame.addEventListener("dragstart", blockNativeDrag, true);
878
+ const stopBlockingDrag = () => {
879
+ frame.removeEventListener("dragstart", blockNativeDrag, true);
880
+ document.removeEventListener("mouseup", stopBlockingDrag, true);
881
+ };
882
+ document.addEventListener("mouseup", stopBlockingDrag, true);
883
+ document.addEventListener("mousemove", arm, true);
884
+ const cancelArm = () => {
885
+ document.removeEventListener("mousemove", arm, true);
886
+ document.removeEventListener("mouseup", cancelArm, true);
887
+ };
888
+ document.addEventListener("mouseup", cancelArm, true);
889
+ }
890
+ frame.addEventListener("mousedown", (event) => {
891
+ if (!inImageGallery()) return;
892
+ const gallery = frame.closest(".ywiki-image-gallery");
893
+ if (gallery instanceof HTMLElement && (gallery.classList.contains("is-block-selected") || gallery.classList.contains("is-block-image-selected"))) return;
894
+ startGalleryDrag(event);
895
+ });
896
+ const ownPos = () => {
897
+ const value = typeof getPos === "function" ? getPos() : null;
898
+ return typeof value === "number" ? value : null;
899
+ };
900
+ const visibleRect = () => {
901
+ const target = inner.getBoundingClientRect();
902
+ return target.width > 1 && target.height > 1 ? target : frame.getBoundingClientRect();
903
+ };
904
+ const dropAfter = (event) => {
905
+ const rect = visibleRect();
906
+ return event.clientX >= rect.left + rect.width / 2;
907
+ };
908
+ const showMergeHint = (event) => {
909
+ const rect = visibleRect();
910
+ frame.classList.add("is-image-drop-target");
911
+ let indicator = document.getElementById("ywiki-image-drop-line");
912
+ if (!indicator) {
913
+ indicator = document.createElement("div");
914
+ indicator.id = "ywiki-image-drop-line";
915
+ indicator.className = "ywiki-image-drop-line";
916
+ indicator.setAttribute("aria-hidden", "true");
917
+ const accent = getComputedStyle(frame).getPropertyValue("--color-accent").trim() || getComputedStyle(document.documentElement).getPropertyValue("--color-accent").trim() || "#2f6feb";
918
+ indicator.style.background = accent;
919
+ indicator.style.width = "3px";
920
+ indicator.style.borderRadius = "999px";
921
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.9)";
922
+ indicator.style.boxShadow = "0 0 0 1px rgba(0, 0, 0, 0.06), 0 2px 10px rgba(0, 0, 0, 0.18)";
923
+ imageDragLayer().appendChild(indicator);
924
+ }
925
+ const after = dropAfter(event);
926
+ indicator.style.top = `${Math.round(rect.top)}px`;
927
+ indicator.style.height = `${Math.round(rect.height)}px`;
928
+ indicator.style.left = `${Math.round(after ? rect.right + 4 : rect.left - 4)}px`;
929
+ indicator.style.opacity = "1";
930
+ };
931
+ const hideMergeHint = () => {
932
+ var _a2;
933
+ frame.classList.remove("is-image-drop-target");
934
+ (_a2 = document.getElementById("ywiki-image-drop-line")) == null ? void 0 : _a2.remove();
935
+ };
936
+ function imageHitTest(x, y) {
937
+ if (editor.isDestroyed) return null;
938
+ const pointerEvents = frame.style.pointerEvents;
939
+ frame.style.pointerEvents = "none";
940
+ const element = document.elementFromPoint(x, y);
941
+ frame.style.pointerEvents = pointerEvents;
942
+ if (!(element instanceof Element)) return null;
943
+ const hitFrame = element.closest(".ywiki-image-frame");
944
+ if (!(hitFrame instanceof HTMLElement) || hitFrame === frame) return null;
945
+ const ownGallery = frame.closest(".ywiki-image-gallery");
946
+ const galleryEl = hitFrame.closest(".ywiki-image-gallery");
947
+ if (galleryEl instanceof HTMLElement) {
948
+ if (galleryEl === ownGallery) return null;
949
+ let found = null;
950
+ editor.state.doc.descendants((node3, offset) => {
951
+ if (found || node3.type.name !== "imageGallery") return;
952
+ try {
953
+ if (editor.view.nodeDOM(offset) === galleryEl) found = { kind: "gallery", pos: offset, node: node3 };
954
+ } catch {
955
+ }
956
+ });
957
+ return found;
958
+ }
959
+ const imageEl = hitFrame.querySelector("img");
960
+ if (!imageEl) return null;
961
+ let pos = null;
962
+ try {
963
+ const raw = editor.view.posAtDOM(hitFrame, 0);
964
+ const candidates = [raw, raw - 1, raw + 1].filter((value) => value >= 0 && value <= editor.state.doc.content.size);
965
+ for (const candidate of candidates) {
966
+ const node3 = editor.state.doc.nodeAt(candidate);
967
+ if ((node3 == null ? void 0 : node3.type.name) === "image") {
968
+ pos = candidate;
969
+ break;
970
+ }
971
+ }
972
+ } catch (err) {
973
+ pos = null;
974
+ }
975
+ if (pos === null) return null;
976
+ const node2 = editor.state.doc.nodeAt(pos);
977
+ return node2 ? { kind: "image", pos, node: node2 } : null;
978
+ }
979
+ function moveImageIntoGalleryAt(galleryStart, gallery, moved, movedIndex, target, pointer) {
980
+ const { state } = editor;
981
+ const galleryType = state.schema.nodes.imageGallery;
982
+ if (!galleryType) return false;
983
+ if (target.pos === galleryStart) return false;
984
+ if (target.node.content.childCount >= IMAGE_GALLERY_MAX_IMAGES) return false;
985
+ const targetChildren = [];
986
+ target.node.forEach((child) => {
987
+ targetChildren.push(child);
988
+ });
989
+ const targetDom = editor.view.nodeDOM(target.pos);
990
+ const domFrames = targetDom instanceof HTMLElement ? [...targetDom.querySelectorAll(".ywiki-image-frame")] : [];
991
+ let childIndex = targetChildren.length - 1;
992
+ for (let index = 0; index < domFrames.length && index < targetChildren.length; index += 1) {
993
+ const r = domFrames[index].getBoundingClientRect();
994
+ if (pointer.x < r.left + r.width / 2) {
995
+ childIndex = index;
996
+ break;
997
+ }
998
+ childIndex = index + 1;
999
+ }
1000
+ const insertAt = Math.max(0, Math.min(childIndex, targetChildren.length));
1001
+ targetChildren.splice(insertAt, 0, moved);
1002
+ const remaining = [];
1003
+ gallery.forEach((child, _offset, index) => {
1004
+ if (index !== movedIndex) remaining.push(child);
1005
+ });
1006
+ const sourceReplacement = remaining.length >= 2 ? [galleryType.create(gallery.attrs, Fragment.fromArray(remaining))] : remaining.length === 1 ? [remaining[0]] : [];
1007
+ const sourceTop = topLevelIndexOf(galleryStart);
1008
+ const targetTop = topLevelIndexOf(target.pos);
1009
+ if (sourceTop < 0 || targetTop < 0) return false;
1010
+ return replaceTopLevelBlocks(
1011
+ sourceTop,
1012
+ sourceReplacement,
1013
+ targetTop,
1014
+ [galleryType.create(target.node.attrs, Fragment.fromArray(targetChildren))]
1015
+ );
1016
+ }
1017
+ function mergeIntoStandalone(galleryStart, gallery, moved, movedIndex, target) {
1018
+ const { state } = editor;
1019
+ const galleryType = state.schema.nodes.imageGallery;
1020
+ if (!galleryType) return false;
1021
+ if (target.pos === galleryStart) return false;
1022
+ const remaining = [];
1023
+ gallery.forEach((child, _offset, index) => {
1024
+ if (index !== movedIndex) remaining.push(child);
1025
+ });
1026
+ const sourceReplacement = remaining.length >= 2 ? [galleryType.create(gallery.attrs, Fragment.fromArray(remaining))] : remaining.length === 1 ? [remaining[0]] : [];
1027
+ const pair = galleryType.create(
1028
+ { height: IMAGE_GALLERY_DEFAULT_HEIGHT },
1029
+ Fragment.fromArray([target.node, moved])
1030
+ );
1031
+ const sourceTop = topLevelIndexOf(galleryStart);
1032
+ const targetTop = topLevelIndexOf(target.pos);
1033
+ if (sourceTop < 0 || targetTop < 0) return false;
1034
+ return replaceTopLevelBlocks(sourceTop, sourceReplacement, targetTop, [pair]);
1035
+ }
1036
+ function topLevelIndexOf(pos) {
1037
+ let cursor = 0;
1038
+ let index = 0;
1039
+ let found = -1;
1040
+ editor.state.doc.forEach((child) => {
1041
+ if (found < 0 && cursor === pos) found = index;
1042
+ cursor += child.nodeSize;
1043
+ index += 1;
1044
+ });
1045
+ return found;
1046
+ }
1047
+ function replaceTopLevelBlocks(sourceIndex, sourceNodes, targetIndex, targetNodes) {
1048
+ const { state } = editor;
1049
+ const top = [];
1050
+ state.doc.forEach((child) => {
1051
+ top.push(child);
1052
+ });
1053
+ if (sourceIndex < 0 || sourceIndex >= top.length) return false;
1054
+ if (targetIndex < 0 || targetIndex >= top.length) return false;
1055
+ if (sourceIndex === targetIndex) return false;
1056
+ const rebuilt = [];
1057
+ for (let index = 0; index < top.length; index += 1) {
1058
+ if (index === sourceIndex) rebuilt.push(...sourceNodes);
1059
+ else if (index === targetIndex) rebuilt.push(...targetNodes);
1060
+ else rebuilt.push(top[index]);
1061
+ }
1062
+ const tr = state.tr;
1063
+ try {
1064
+ const jobs = [];
1065
+ if (sourceIndex <= targetIndex) {
1066
+ jobs.push({ index: targetIndex, nodes: targetNodes });
1067
+ jobs.push({ index: sourceIndex, nodes: sourceNodes });
1068
+ } else {
1069
+ jobs.push({ index: sourceIndex, nodes: sourceNodes });
1070
+ jobs.push({ index: targetIndex, nodes: targetNodes });
1071
+ }
1072
+ let starts = [];
1073
+ let cursor = 0;
1074
+ for (const child of top) {
1075
+ starts.push(cursor);
1076
+ cursor += child.nodeSize;
1077
+ }
1078
+ for (const job of jobs) {
1079
+ const from = tr.mapping.map(starts[job.index], 1);
1080
+ const to = tr.mapping.map(starts[job.index] + top[job.index].nodeSize, -1);
1081
+ tr.replaceWith(from, Math.max(from, to), Fragment.fromArray(job.nodes));
1082
+ }
1083
+ editor.view.dispatch(tr);
1084
+ } catch {
1085
+ return false;
1086
+ }
1087
+ editor.commands.focus();
1088
+ return true;
1089
+ }
1090
+ function mergeTwoImages(sourcePos, targetPos, after) {
1091
+ const { state } = editor;
1092
+ const source = state.doc.nodeAt(sourcePos);
1093
+ const target = state.doc.nodeAt(targetPos);
1094
+ if (!source || !target || source.type.name !== "image" || target.type.name !== "image") return false;
1095
+ const galleryType = state.schema.nodes.imageGallery;
1096
+ if (!galleryType) return false;
1097
+ const pair = after ? [target, source] : [source, target];
1098
+ const earlier = Math.min(sourcePos, targetPos);
1099
+ const later = Math.max(sourcePos, targetPos);
1100
+ const earlierNode = earlier === sourcePos ? source : target;
1101
+ const laterNode = later === sourcePos ? source : target;
1102
+ const middle = state.doc.slice(earlier + earlierNode.nodeSize, later).content;
1103
+ const gallery = galleryType.create({ height: IMAGE_GALLERY_DEFAULT_HEIGHT }, Fragment.fromArray(pair));
1104
+ const tr = state.tr.replaceWith(
1105
+ earlier,
1106
+ later + laterNode.nodeSize,
1107
+ Fragment.fromArray([gallery]).append(middle)
1108
+ );
1109
+ editor.view.dispatch(tr);
1110
+ editor.commands.focus();
1111
+ return true;
1112
+ }
1113
+ function insertIntoGallery(sourcePos, galleryPos, galleryNode, dropIndex) {
1114
+ const { state } = editor;
1115
+ const source = state.doc.nodeAt(sourcePos);
1116
+ if (!source || source.type.name !== "image") return false;
1117
+ const galleryType = state.schema.nodes.imageGallery;
1118
+ if (!galleryType) return false;
1119
+ const children = galleryNode.content.content.slice();
1120
+ if (children.length >= IMAGE_GALLERY_MAX_IMAGES) return false;
1121
+ const shift = sourcePos < galleryPos ? source.nodeSize : 0;
1122
+ const children_ = [];
1123
+ galleryNode.forEach((child) => {
1124
+ children_.push(child);
1125
+ });
1126
+ children_.splice(dropIndex, 0, source);
1127
+ const tr = state.tr;
1128
+ tr.delete(sourcePos, sourcePos + source.nodeSize);
1129
+ const target = galleryPos - shift;
1130
+ tr.replaceWith(
1131
+ target,
1132
+ target + galleryNode.nodeSize,
1133
+ galleryType.create(galleryNode.attrs, Fragment.fromArray(children_))
1134
+ );
1135
+ editor.view.dispatch(tr);
1136
+ editor.commands.focus();
1137
+ return true;
1138
+ }
1139
+ frame.addEventListener("dragstart", (event) => {
1140
+ var _a2, _b2;
1141
+ if (inImageGallery()) return;
1142
+ const pos = ownPos();
1143
+ if (pos === null || !editor.isEditable) return;
1144
+ const current = editor.state.doc.nodeAt(pos);
1145
+ if (!current || current.type.name !== "image") return;
1146
+ standaloneImageDrag = { pos, node: current };
1147
+ (_a2 = event.dataTransfer) == null ? void 0 : _a2.setData("text/plain", "");
1148
+ if (event.dataTransfer) event.dataTransfer.effectAllowed = "move";
1149
+ try {
1150
+ (_b2 = event.dataTransfer) == null ? void 0 : _b2.setDragImage(dom, dom.width / 2, Math.min(dom.height / 2, 40));
1151
+ } catch {
1152
+ }
1153
+ });
1154
+ frame.addEventListener("dragend", () => {
1155
+ standaloneImageDrag = null;
1156
+ hideMergeHint();
1157
+ clearImageDropTargets();
1158
+ queueMicrotask(clearImageDropTargets);
1159
+ });
1160
+ frame.addEventListener("dragover", (event) => {
1161
+ if (!standaloneImageDrag) return;
1162
+ const pos = ownPos();
1163
+ if (pos === null || standaloneImageDrag.pos === pos) return;
1164
+ event.preventDefault();
1165
+ event.stopPropagation();
1166
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
1167
+ showMergeHint(event);
1168
+ });
1169
+ frame.addEventListener("dragleave", (event) => {
1170
+ if (!standaloneImageDrag) return;
1171
+ const next = event.relatedTarget;
1172
+ if (next instanceof Node && frame.contains(next)) return;
1173
+ hideMergeHint();
1174
+ });
1175
+ frame.addEventListener("drop", (event) => {
1176
+ var _a2;
1177
+ const source = standaloneImageDrag;
1178
+ if (!source) return;
1179
+ const pos = ownPos();
1180
+ if (pos === null) return;
1181
+ hideMergeHint();
1182
+ clearImageDropTargets();
1183
+ standaloneImageDrag = null;
1184
+ queueMicrotask(clearImageDropTargets);
1185
+ if (source.pos === pos) {
1186
+ return;
1187
+ }
1188
+ event.preventDefault();
1189
+ event.stopPropagation();
1190
+ const container = galleryContainerOf(pos);
1191
+ if (container) {
1192
+ const children_ = [];
1193
+ container.node.forEach((child) => {
1194
+ children_.push(child);
1195
+ });
1196
+ dropAfter(event) ? children_.length : 0;
1197
+ const frames = [...((_a2 = frame.parentElement) == null ? void 0 : _a2.children) ?? []].filter((el) => el instanceof HTMLElement && el.classList.contains("ywiki-image-frame"));
1198
+ let index = frames.length;
1199
+ for (let i = 0; i < frames.length; i += 1) {
1200
+ const rect = frames[i].getBoundingClientRect();
1201
+ if (event.clientX < rect.left + rect.width / 2) {
1202
+ index = i;
1203
+ break;
1204
+ }
1205
+ }
1206
+ insertIntoGallery(source.pos, container.pos, container.node, index);
1207
+ return;
1208
+ }
1209
+ mergeTwoImages(source.pos, pos, dropAfter(event));
1210
+ });
1211
+ function resizeInGallery(event, sign) {
1212
+ var _a2;
1213
+ const pos = typeof getPos === "function" ? getPos() : null;
1214
+ if (pos === null || pos === void 0) return;
1215
+ const current = editor.state.doc.nodeAt(pos);
1216
+ if (!current || current.type.name !== "image") return;
1217
+ const startX = event.clientX;
1218
+ const startWidth = frame.getBoundingClientRect().width;
1219
+ const startScale = Number((_a2 = current.attrs) == null ? void 0 : _a2.galleryScale) || 1;
1220
+ const prevCursor = document.documentElement.style.cursor;
1221
+ frame.classList.add("is-resizing");
1222
+ document.documentElement.style.cursor = CORNER_CURSOR.nw;
1223
+ document.body.style.userSelect = "none";
1224
+ const clamp = (value) => Math.max(0.25, Math.min(4, value));
1225
+ let nextScale = startScale;
1226
+ const onMove = (moveEvent) => {
1227
+ const dx = (moveEvent.clientX - startX) * sign;
1228
+ const nextWidth = Math.max(24, startWidth + dx);
1229
+ nextScale = clamp(startScale * (nextWidth / Math.max(1, startWidth)));
1230
+ frame.style.setProperty("--ywiki-image-weight", String(nextScale));
1231
+ };
1232
+ const onUp = () => {
1233
+ window.removeEventListener("mousemove", onMove);
1234
+ window.removeEventListener("mouseup", onUp);
1235
+ window.removeEventListener("blur", onUp);
1236
+ frame.classList.remove("is-resizing");
1237
+ document.documentElement.style.cursor = prevCursor;
1238
+ document.body.style.userSelect = "";
1239
+ if (editor.isDestroyed) return;
1240
+ const latest = editor.state.doc.nodeAt(pos);
1241
+ if (!latest || latest.type.name !== "image") return;
1242
+ const rounded = Math.round(nextScale * 1e3) / 1e3;
1243
+ if (Math.abs(rounded - startScale) < 1e-3) {
1244
+ frame.style.removeProperty("--ywiki-image-weight");
1245
+ return;
1246
+ }
1247
+ editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, void 0, {
1248
+ ...latest.attrs,
1249
+ galleryScale: rounded === 1 ? null : rounded
1250
+ }));
1251
+ };
1252
+ window.addEventListener("mousemove", onMove);
1253
+ window.addEventListener("mouseup", onUp);
1254
+ window.addEventListener("blur", onUp);
1255
+ }
1256
+ function startResize(event, corner) {
1257
+ const sign = corner === "nw" || corner === "sw" ? -1 : 1;
1258
+ if (event.button !== 0 || !editor.isEditable) return;
1259
+ event.preventDefault();
1260
+ event.stopPropagation();
1261
+ if (inImageGallery()) {
1262
+ resizeInGallery(event, sign);
1263
+ return;
1264
+ }
1265
+ frame.classList.add("is-resizing");
1266
+ const cursor = CORNER_CURSOR[corner];
1267
+ const prevCursor = document.documentElement.style.cursor;
1268
+ document.documentElement.style.cursor = cursor;
1269
+ const startX = event.clientX;
1270
+ const startWidth = dom.getBoundingClientRect().width;
1271
+ const maxWidth = editor.view.dom.clientWidth || Number.POSITIVE_INFINITY;
1272
+ const onMove = (e) => {
1273
+ const dx = (e.clientX - startX) * sign;
1274
+ const next = Math.max(MIN_WIDTH, Math.min(maxWidth, Math.round(startWidth + dx)));
1275
+ dom.style.width = `${next}px`;
1276
+ frame.setAttribute("data-sized", "true");
1277
+ };
1278
+ const onUp = () => {
1279
+ window.removeEventListener("mousemove", onMove);
1280
+ window.removeEventListener("mouseup", onUp);
1281
+ window.removeEventListener("blur", onUp);
1282
+ frame.classList.remove("is-resizing");
1283
+ document.documentElement.style.cursor = prevCursor;
1284
+ const finalWidth = Math.round(dom.getBoundingClientRect().width);
1285
+ const pos = typeof getPos === "function" ? getPos() : null;
1286
+ if (pos === null || pos === void 0 || editor.isDestroyed) return;
1287
+ const currentNode2 = editor.state.doc.nodeAt(pos);
1288
+ if (!currentNode2 || currentNode2.type.name !== "image") return;
1289
+ if (Number(currentNode2.attrs.width) === finalWidth) return;
1290
+ editor.view.dispatch(
1291
+ editor.state.tr.setNodeMarkup(pos, void 0, {
1292
+ ...currentNode2.attrs,
1293
+ width: finalWidth
1294
+ })
1295
+ );
1296
+ };
1297
+ window.addEventListener("mousemove", onMove);
1298
+ window.addEventListener("mouseup", onUp);
1299
+ window.addEventListener("blur", onUp);
1300
+ }
1301
+ inner.appendChild(dom);
1302
+ frame.appendChild(caption);
1303
+ return {
1304
+ dom: frame,
1305
+ update: (updated) => {
1306
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h;
1307
+ if (updated.type.name !== "image") return false;
1308
+ const nextAlt = String(((_a2 = updated.attrs) == null ? void 0 : _a2.alt) ?? "");
1309
+ if (nextAlt !== fallbackAlt) {
1310
+ fallbackAlt = nextAlt;
1311
+ if (!dom.getAttribute("data-image-loading")) {
1312
+ dom.alt = fallbackAlt;
1313
+ dom.title = fallbackAlt;
1314
+ }
1315
+ }
1316
+ const nextRatio = (_b2 = updated.attrs) == null ? void 0 : _b2.aspectRatio;
1317
+ const ratioChanged = String(nextRatio ?? "") !== String(((_c2 = currentNode.attrs) == null ? void 0 : _c2.aspectRatio) ?? "");
1318
+ currentNode = updated;
1319
+ applyAspectRatio(nextRatio);
1320
+ if (ratioChanged) {
1321
+ queueMicrotask(backfillAspectRatio);
1322
+ }
1323
+ applyGalleryScale((_d2 = updated.attrs) == null ? void 0 : _d2.galleryScale);
1324
+ applyCaption((_e2 = updated.attrs) == null ? void 0 : _e2.caption);
1325
+ syncGalleryMode();
1326
+ applyWidth((_f2 = updated.attrs) == null ? void 0 : _f2.width);
1327
+ applyAlign((_g2 = updated.attrs) == null ? void 0 : _g2.align);
1328
+ const next = String(((_h = updated.attrs) == null ? void 0 : _h.attachmentId) ?? "");
1329
+ if (next !== attachmentId) {
1330
+ attachmentId = next;
1331
+ load(next);
1332
+ }
1333
+ return true;
1334
+ },
1335
+ // 选中态:PM 在选到该节点时回调(图片是原子块,点击即 NodeSelection)
1336
+ selectNode: () => {
1337
+ setSelected(true);
1338
+ syncGalleryMode();
1339
+ },
1340
+ deselectNode: () => {
1341
+ setSelected(false);
1342
+ syncGalleryMode();
1343
+ },
1344
+ destroy: () => {
1345
+ setSelected(false);
1346
+ attachmentId = "";
1347
+ hideMergeHint();
1348
+ clearImageDropTargets();
1349
+ releaseUrl == null ? void 0 : releaseUrl();
1350
+ stopWatch == null ? void 0 : stopWatch();
1351
+ stopWatch = null;
1352
+ }
1353
+ };
1354
+ };
1355
+ const createFileAttachmentNodeView = (fetcher = unavailableAttachment, scope) => ({ node }) => {
1356
+ var _a, _b, _c;
1357
+ const dom = document.createElement("div");
1358
+ dom.className = "doc-file-attachment";
1359
+ dom.setAttribute("data-file-attachment", "true");
1360
+ dom.setAttribute("data-block-chrome", "");
1361
+ dom.setAttribute("contenteditable", "false");
1362
+ let attachmentId = String(((_a = node.attrs) == null ? void 0 : _a.attachmentId) ?? "");
1363
+ let mimeType = String(((_b = node.attrs) == null ? void 0 : _b.mimeType) ?? "");
1364
+ let filename = String(((_c = node.attrs) == null ? void 0 : _c.filename) ?? "附件");
1365
+ let stopWatch = null;
1366
+ let releaseUrl;
1367
+ const head = document.createElement("div");
1368
+ head.className = "doc-file-head";
1369
+ const icon = document.createElement("span");
1370
+ icon.className = "doc-file-icon";
1371
+ const name = document.createElement("span");
1372
+ name.className = "doc-file-name";
1373
+ const size = document.createElement("span");
1374
+ size.className = "doc-file-size";
1375
+ const download = document.createElement("button");
1376
+ download.type = "button";
1377
+ download.className = "doc-file-action";
1378
+ download.textContent = "下载";
1379
+ download.addEventListener("click", async (event) => {
1380
+ event.preventDefault();
1381
+ event.stopPropagation();
1382
+ if (!attachmentId || download.disabled) return;
1383
+ const id = attachmentId;
1384
+ download.disabled = true;
1385
+ try {
1386
+ await downloadAttachment(id, filename, fetcher);
1387
+ if (id === attachmentId) download.textContent = "下载";
1388
+ } catch {
1389
+ if (id === attachmentId) download.textContent = "下载失败,重试";
1390
+ } finally {
1391
+ if (id === attachmentId) download.disabled = false;
1392
+ }
1393
+ });
1394
+ head.append(icon, name, size, download);
1395
+ dom.append(head);
1396
+ let media = null;
1397
+ function render() {
1398
+ var _a2;
1399
+ dom.setAttribute("data-attachment-id", attachmentId);
1400
+ icon.textContent = mimeType.startsWith("video/") ? "▶" : mimeType.startsWith("image/") ? "🖼" : "📎";
1401
+ name.textContent = filename;
1402
+ name.title = filename;
1403
+ const bytes = Number(((_a2 = node.attrs) == null ? void 0 : _a2.size) ?? 0);
1404
+ size.textContent = bytes > 0 ? formatBytes(bytes) : "";
1405
+ download.disabled = !attachmentId;
1406
+ download.textContent = "下载";
1407
+ media == null ? void 0 : media.remove();
1408
+ media = null;
1409
+ releaseUrl == null ? void 0 : releaseUrl();
1410
+ stopWatch == null ? void 0 : stopWatch();
1411
+ stopWatch = null;
1412
+ if (!attachmentId) return;
1413
+ if (mimeType.startsWith("video/")) {
1414
+ const video = document.createElement("video");
1415
+ video.className = "doc-file-video";
1416
+ video.controls = true;
1417
+ video.preload = "metadata";
1418
+ media = video;
1419
+ dom.append(video);
1420
+ } else if (mimeType.startsWith("image/")) {
1421
+ const img = document.createElement("img");
1422
+ img.className = "doc-file-image";
1423
+ img.alt = filename;
1424
+ media = img;
1425
+ dom.append(img);
1426
+ } else {
1427
+ return;
1428
+ }
1429
+ const id = attachmentId;
1430
+ stopWatch = whenNearViewport(dom, () => {
1431
+ releaseUrl == null ? void 0 : releaseUrl();
1432
+ releaseUrl = attachmentObjectUrl(id, (url) => {
1433
+ if (id !== attachmentId || !media) return;
1434
+ media.setAttribute("src", url);
1435
+ }, fetcher, void 0, scope);
1436
+ });
1437
+ }
1438
+ render();
1439
+ return {
1440
+ dom,
1441
+ update: (updated) => {
1442
+ var _a2, _b2, _c2, _d;
1443
+ if (updated.type.name !== "fileAttachment") return false;
1444
+ node = updated;
1445
+ const bytes = Number(((_a2 = updated.attrs) == null ? void 0 : _a2.size) ?? 0);
1446
+ size.textContent = bytes > 0 ? formatBytes(bytes) : "";
1447
+ const nextId = String(((_b2 = updated.attrs) == null ? void 0 : _b2.attachmentId) ?? "");
1448
+ const nextMime = String(((_c2 = updated.attrs) == null ? void 0 : _c2.mimeType) ?? "");
1449
+ const nextName = String(((_d = updated.attrs) == null ? void 0 : _d.filename) ?? "附件");
1450
+ if (nextId !== attachmentId || nextMime !== mimeType || nextName !== filename) {
1451
+ attachmentId = nextId;
1452
+ mimeType = nextMime;
1453
+ filename = nextName;
1454
+ render();
1455
+ }
1456
+ return true;
1457
+ },
1458
+ destroy: () => {
1459
+ attachmentId = "";
1460
+ media = null;
1461
+ releaseUrl == null ? void 0 : releaseUrl();
1462
+ stopWatch == null ? void 0 : stopWatch();
1463
+ stopWatch = null;
1464
+ },
1465
+ stopEvent: (event) => {
1466
+ var _a2, _b2;
1467
+ return !!((_b2 = (_a2 = event.target) == null ? void 0 : _a2.closest) == null ? void 0 : _b2.call(_a2, "video, audio, button"));
1468
+ }
1469
+ };
1470
+ };
1471
+ function dispatch(editor, tr) {
1472
+ var _a;
1473
+ const manager = (_a = yUndoPluginKey.getState(editor.state)) == null ? void 0 : _a.undoManager;
1474
+ manager == null ? void 0 : manager.stopCapturing();
1475
+ editor.view.dispatch(closeHistory(tr));
1476
+ manager == null ? void 0 : manager.stopCapturing();
1477
+ }
1478
+ function patchCallout(editor, pos, attrs) {
1479
+ if (!editor.isEditable || editor.isDestroyed) return false;
1480
+ const node = editor.state.doc.nodeAt(pos);
1481
+ if ((node == null ? void 0 : node.type.name) !== "callout" || !validCalloutAttrs({ ...node.attrs, ...attrs })) return false;
1482
+ dispatch(editor, editor.state.tr.setNodeMarkup(pos, void 0, { ...node.attrs, ...attrs }));
1483
+ return true;
1484
+ }
1485
+ function unwrapCallout(editor, pos) {
1486
+ if (!editor.isEditable || editor.isDestroyed) return false;
1487
+ const node = editor.state.doc.nodeAt(pos);
1488
+ if ((node == null ? void 0 : node.type.name) !== "callout") return false;
1489
+ const resolved = editor.state.doc.resolve(pos);
1490
+ if (!resolved.parent.canReplace(resolved.index(), resolved.index() + 1, node.content)) return false;
1491
+ dispatch(editor, editor.state.tr.replaceWith(pos, pos + node.nodeSize, node.content));
1492
+ return true;
1493
+ }
1494
+ const TONE_LABELS = { info: "💡 提示", success: "✅ 成功", warning: "⚠️ 注意", danger: "❗ 警告" };
1495
+ const COLOR_NAMES = { gray: "灰", red: "红", orange: "橙", yellow: "黄", green: "绿", blue: "蓝", purple: "紫" };
1496
+ function swatchName(value, key2) {
1497
+ if (value === null) return "默认";
1498
+ if (value === "none") return "无";
1499
+ if (key2 !== "backgroundColor") return COLOR_NAMES[value] ?? value;
1500
+ return `${value.endsWith("-strong") ? "浓" : "浅"}${COLOR_NAMES[value.replace("-strong", "")] ?? value}`;
1501
+ }
1502
+ function CalloutControls({ attrs, editable, portal, onPatch, onUnwrap }) {
1503
+ const [panel, setPanel] = useState("none");
1504
+ const iconTrigger = useRef(null), colorTrigger = useRef(null), toolbarIconTrigger = useRef(null);
1505
+ const value = effectiveCallout(attrs);
1506
+ const anchor = panel === "color" ? colorTrigger.current : panel === "icon" ? iconTrigger.current ?? toolbarIconTrigger.current : null;
1507
+ const sections = [
1508
+ { key: "textColor", label: "字体颜色", values: [null, ...CALLOUT_COLORS] },
1509
+ { key: "borderColor", label: "边框颜色", values: ["none", ...CALLOUT_COLORS] },
1510
+ { key: "backgroundColor", label: "填充颜色", values: [null, "none", ...CALLOUT_FILLS] }
1511
+ ];
1512
+ const selected = (key2, swatch) => (key2 === "borderColor" ? value.borderColor : attrs[key2] ?? null) === swatch;
1513
+ return /* @__PURE__ */ jsxs("div", { className: "callout-controls", contentEditable: false, onPointerDown: (event) => event.stopPropagation(), onKeyDown: (event) => event.stopPropagation(), children: [
1514
+ value.icon && /* @__PURE__ */ jsx(
1515
+ WButton,
1516
+ {
1517
+ ref: iconTrigger,
1518
+ variant: "text",
1519
+ className: "callout-icon callout-icon-button",
1520
+ "aria-label": "更换高亮块图标",
1521
+ "aria-expanded": panel === "icon",
1522
+ disabled: !editable,
1523
+ onClick: () => setPanel(panel === "icon" ? "none" : "icon"),
1524
+ children: value.icon
1525
+ }
1526
+ ),
1527
+ editable && /* @__PURE__ */ jsxs("div", { className: `callout-toolbar${panel !== "none" ? " is-open" : ""}`, "aria-label": "高亮块操作", children: [
1528
+ /* @__PURE__ */ jsx(
1529
+ "button",
1530
+ {
1531
+ ref: colorTrigger,
1532
+ type: "button",
1533
+ className: "callout-toolbar-button",
1534
+ "aria-label": "高亮块颜色设置",
1535
+ "aria-expanded": panel === "color",
1536
+ onClick: () => setPanel(panel === "color" ? "none" : "color"),
1537
+ children: /* @__PURE__ */ jsx(WIcon, { name: "palette", size: 20 })
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsx(
1541
+ "button",
1542
+ {
1543
+ ref: toolbarIconTrigger,
1544
+ type: "button",
1545
+ className: "callout-toolbar-button",
1546
+ "aria-label": "高亮块图标设置",
1547
+ "aria-expanded": panel === "icon",
1548
+ onClick: () => setPanel(panel === "icon" ? "none" : "icon"),
1549
+ children: "图标"
1550
+ }
1551
+ ),
1552
+ /* @__PURE__ */ jsxs("select", { "aria-label": "高亮块预设", className: "callout-preset", value: "", onChange: (event) => {
1553
+ if (event.target.value) onPatch({ tone: event.target.value, icon: null, textColor: null, borderColor: null, backgroundColor: null });
1554
+ }, children: [
1555
+ /* @__PURE__ */ jsx("option", { value: "", children: "预设" }),
1556
+ CALLOUT_TONES.map((tone) => /* @__PURE__ */ jsx("option", { value: tone, children: TONE_LABELS[tone] }, tone))
1557
+ ] }),
1558
+ /* @__PURE__ */ jsx("span", { className: "callout-toolbar-divider", "aria-hidden": "true" }),
1559
+ /* @__PURE__ */ jsx("button", { type: "button", className: "callout-toolbar-button", onClick: onUnwrap, children: "取消高亮块" })
1560
+ ] }),
1561
+ panel === "color" && /* @__PURE__ */ jsx(AnchoredPanel, { anchor, portal, label: "高亮块颜色", onClose: () => setPanel("none"), align: "start", prefer: "below", gap: 6, children: /* @__PURE__ */ jsxs("div", { className: "callout-color-panel", role: "group", "aria-label": "高亮块颜色", children: [
1562
+ sections.map((section) => /* @__PURE__ */ jsxs("section", { children: [
1563
+ /* @__PURE__ */ jsx("div", { className: "callout-color-label", children: section.label }),
1564
+ /* @__PURE__ */ jsx("div", { className: "callout-color-grid", role: "group", "aria-label": section.label, children: section.values.map((swatch) => /* @__PURE__ */ jsx(
1565
+ "button",
1566
+ {
1567
+ type: "button",
1568
+ className: `callout-color-choice${selected(section.key, swatch) ? " is-selected" : ""}${swatch === "none" ? " is-none" : ""}`,
1569
+ style: calloutSwatchStyle(section.key, swatch),
1570
+ "aria-label": `${section.label}:${swatchName(swatch, section.key)}`,
1571
+ "aria-pressed": selected(section.key, swatch),
1572
+ onClick: () => onPatch({ [section.key]: swatch }),
1573
+ children: section.key === "textColor" ? /* @__PURE__ */ jsx("span", { className: "callout-font-sample", children: "A" }) : swatch === null ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "↺" }) : null
1574
+ },
1575
+ swatch ?? "default"
1576
+ )) })
1577
+ ] }, section.key)),
1578
+ /* @__PURE__ */ jsx(WButton, { variant: "outline", block: true, onClick: () => onPatch({ textColor: null, borderColor: null, backgroundColor: null }), children: "恢复默认颜色" })
1579
+ ] }) }),
1580
+ panel === "icon" && /* @__PURE__ */ jsx(AnchoredPanel, { anchor, portal, label: "高亮块图标", onClose: () => setPanel("none"), align: "center", prefer: "below", gap: 6, children: /* @__PURE__ */ jsxs("div", { className: "callout-icon-picker", role: "group", "aria-label": "高亮块图标", children: [
1581
+ /* @__PURE__ */ jsx("div", { className: "callout-icon-grid", children: CALLOUT_ICONS.map((icon) => /* @__PURE__ */ jsx(
1582
+ "button",
1583
+ {
1584
+ type: "button",
1585
+ className: "callout-icon-choice",
1586
+ "aria-label": `高亮块图标:${icon}`,
1587
+ "aria-pressed": value.icon === icon,
1588
+ onClick: () => {
1589
+ onPatch({ icon });
1590
+ setPanel("none");
1591
+ },
1592
+ children: icon
1593
+ },
1594
+ icon
1595
+ )) }),
1596
+ /* @__PURE__ */ jsxs("div", { className: "callout-icon-footer", children: [
1597
+ /* @__PURE__ */ jsx(WButton, { size: "small", variant: "text", onClick: () => {
1598
+ onPatch({ icon: "" });
1599
+ setPanel("none");
1600
+ }, children: "隐藏图标" }),
1601
+ /* @__PURE__ */ jsx(WButton, { size: "small", variant: "text", onClick: () => {
1602
+ onPatch({ icon: null });
1603
+ setPanel("none");
1604
+ }, children: "默认图标" })
1605
+ ] })
1606
+ ] }) })
1607
+ ] });
1608
+ }
1609
+ function resolvePortal(node) {
1610
+ var _a;
1611
+ const portal = document.querySelector("[data-wendang-portal]");
1612
+ if (portal instanceof HTMLElement) return portal;
1613
+ if (!node.isConnected) return null;
1614
+ const scope = document.createElement("div");
1615
+ scope.className = "wendang-portal wendang";
1616
+ scope.dataset.wendangPortal = "";
1617
+ const theme = (_a = node.closest("[data-wendang-theme]")) == null ? void 0 : _a.getAttribute("data-wendang-theme");
1618
+ if (theme) scope.dataset.wendangTheme = theme;
1619
+ document.body.append(scope);
1620
+ return scope;
1621
+ }
1622
+ const createCalloutNodeView = () => ({ node, editor, getPos }) => {
1623
+ const dom = document.createElement("div");
1624
+ const controls = document.createElement("div");
1625
+ controls.className = "callout-controls-host";
1626
+ controls.contentEditable = "false";
1627
+ const content = document.createElement("div");
1628
+ content.className = "doc-callout-content";
1629
+ content.dataset.calloutContent = "true";
1630
+ dom.append(controls, content);
1631
+ let current = node, root = createRoot(controls);
1632
+ let lastEditable = editor.isEditable;
1633
+ const position = () => {
1634
+ var _a;
1635
+ const pos = getPos();
1636
+ return typeof pos === "number" && ((_a = editor.state.doc.nodeAt(pos)) == null ? void 0 : _a.type.name) === "callout" ? pos : null;
1637
+ };
1638
+ const paint = () => {
1639
+ const attributes = calloutDOMAttributes(current.attrs);
1640
+ dom.classList.add("doc-callout");
1641
+ for (const key2 of Object.keys(dom.dataset)) if (key2.startsWith("callout") || key2 === "tone" || key2 === "iconHidden") delete dom.dataset[key2];
1642
+ for (const [key2, value] of Object.entries(attributes)) dom.setAttribute(key2, value);
1643
+ root == null ? void 0 : root.render(/* @__PURE__ */ jsx(
1644
+ CalloutControls,
1645
+ {
1646
+ attrs: current.attrs,
1647
+ editable: editor.isEditable,
1648
+ portal: () => resolvePortal(dom),
1649
+ onPatch: (attrs) => {
1650
+ const pos = position();
1651
+ if (pos !== null) patchCallout(editor, pos, attrs);
1652
+ },
1653
+ onUnwrap: () => {
1654
+ const pos = position();
1655
+ if (pos !== null) unwrapCallout(editor, pos);
1656
+ }
1657
+ }
1658
+ ));
1659
+ };
1660
+ paint();
1661
+ const syncEditable = () => {
1662
+ if (editor.isEditable !== lastEditable) {
1663
+ lastEditable = editor.isEditable;
1664
+ paint();
1665
+ }
1666
+ };
1667
+ editor.on("transaction", syncEditable);
1668
+ return {
1669
+ dom,
1670
+ contentDOM: content,
1671
+ update(updated) {
1672
+ if (updated.type.name !== "callout") return false;
1673
+ current = updated;
1674
+ paint();
1675
+ return true;
1676
+ },
1677
+ stopEvent: (event) => controls.contains(event.target),
1678
+ ignoreMutation: (mutation) => mutation.type !== "selection" && !content.contains(mutation.target),
1679
+ // ProseMirror 可能在 React 提交阶段同步销毁节点视图;此时同步 unmount 会与
1680
+ // 正在进行的渲染冲突。延后一拍卸载,容器仍由 ProseMirror 持有。
1681
+ destroy() {
1682
+ editor.off("transaction", syncEditable);
1683
+ const instance = root;
1684
+ root = null;
1685
+ queueMicrotask(() => instance == null ? void 0 : instance.unmount());
1686
+ }
1687
+ };
1688
+ };
1689
+ function parseLanguage(element) {
1690
+ var _a;
1691
+ const data = element.getAttribute("data-language");
1692
+ if (data) return data;
1693
+ const match = /(?:^|\s)language-([\w+#-]+)/.exec(((_a = element.querySelector("code")) == null ? void 0 : _a.className) || element.className);
1694
+ return match ? match[1] : null;
1695
+ }
1696
+ const createCodeBlockNodeView = (canCopy, contentContext) => ({ node, editor, getPos, extension }) => {
1697
+ const wrapper = document.createElement("div"), toolbar = document.createElement("div");
1698
+ wrapper.dataset.blockChrome = "";
1699
+ const scroll = document.createElement("div"), numbers = document.createElement("div"), pre = document.createElement("pre"), code = document.createElement("code");
1700
+ toolbar.className = "code-toolbar-host";
1701
+ toolbar.contentEditable = "false";
1702
+ scroll.className = "code-scroll";
1703
+ scroll.setAttribute("aria-label", "代码内容");
1704
+ numbers.className = "code-line-numbers";
1705
+ numbers.contentEditable = "false";
1706
+ numbers.setAttribute("aria-hidden", "true");
1707
+ pre.className = "doc-code";
1708
+ pre.append(code);
1709
+ scroll.append(numbers, pre);
1710
+ wrapper.append(toolbar, scroll);
1711
+ let display = codeBlockDisplay(), current = node, lastText = node.textContent, root = createRoot(toolbar);
1712
+ const paintClass = () => {
1713
+ const classes = ["code-block-wrap", ...codeBlockClass(display)];
1714
+ for (const name of ["code-theme-auto", "code-theme-paper", "code-theme-soft", "code-theme-night", "is-collapsed", "without-line-numbers", "is-unlimited"]) wrapper.classList.toggle(name, classes.includes(name));
1715
+ wrapper.classList.add("code-block-wrap", "ywiki-code-panel");
1716
+ scroll.hidden = display.collapsed;
1717
+ numbers.hidden = !display.lineNumbers;
1718
+ };
1719
+ const chooseLanguage = (value) => {
1720
+ if (!editor.isEditable || editor.isDestroyed) return;
1721
+ const pos = getPos();
1722
+ if (typeof pos !== "number") return;
1723
+ const latest = editor.state.doc.nodeAt(pos);
1724
+ if ((latest == null ? void 0 : latest.type) !== node.type) return;
1725
+ editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, void 0, { ...latest.attrs, language: value || null }));
1726
+ };
1727
+ const paintToolbar = () => {
1728
+ const context = contentContext == null ? void 0 : contentContext();
1729
+ const toolbar2 = /* @__PURE__ */ jsx(
1730
+ CodeToolbar,
1731
+ {
1732
+ text: current.textContent,
1733
+ language: String(current.attrs.language ?? ""),
1734
+ editable: editor.isEditable,
1735
+ canCopy: (canCopy == null ? void 0 : canCopy()) ?? false,
1736
+ display,
1737
+ onLanguage: chooseLanguage,
1738
+ onDisplay: (value) => {
1739
+ display = value;
1740
+ paintClass();
1741
+ paintToolbar();
1742
+ }
1743
+ }
1744
+ );
1745
+ root == null ? void 0 : root.render(context ? /* @__PURE__ */ jsx(ContentContextBridge, { value: context, children: toolbar2 }) : toolbar2);
1746
+ };
1747
+ numbers.textContent = codeLineNumbers(lastText);
1748
+ paintClass();
1749
+ paintToolbar();
1750
+ const syncMode = () => paintToolbar();
1751
+ editor.on("transaction", syncMode);
1752
+ editor.on("update", syncMode);
1753
+ const revealSelection = () => {
1754
+ if (!display.collapsed || !editor.isFocused) return;
1755
+ const pos = getPos();
1756
+ if (typeof pos !== "number") return;
1757
+ const { from, to } = editor.state.selection;
1758
+ if (from > pos && to < pos + current.nodeSize) {
1759
+ display = { ...display, collapsed: false };
1760
+ paintClass();
1761
+ paintToolbar();
1762
+ }
1763
+ };
1764
+ editor.on("selectionUpdate", revealSelection);
1765
+ return {
1766
+ dom: wrapper,
1767
+ contentDOM: code,
1768
+ update(updated) {
1769
+ if (updated.type.name !== "codeBlock") return false;
1770
+ current = updated;
1771
+ if (updated.textContent !== lastText) {
1772
+ lastText = updated.textContent;
1773
+ numbers.textContent = codeLineNumbers(lastText);
1774
+ }
1775
+ paintToolbar();
1776
+ return true;
1777
+ },
1778
+ stopEvent: (event) => toolbar.contains(event.target) || numbers.contains(event.target),
1779
+ ignoreMutation: (mutation) => mutation.type !== "selection" && !code.contains(mutation.target),
1780
+ // 与高亮块一致:延后一拍卸载,避免在 React 提交阶段同步 unmount。
1781
+ destroy() {
1782
+ editor.off("selectionUpdate", revealSelection);
1783
+ editor.off("transaction", syncMode);
1784
+ editor.off("update", syncMode);
1785
+ const instance = root;
1786
+ root = null;
1787
+ queueMicrotask(() => instance == null ? void 0 : instance.unmount());
1788
+ }
1789
+ };
1790
+ };
1791
+ const YwikiCodeBlock = CodeBlock.extend({
1792
+ addOptions() {
1793
+ var _a;
1794
+ return { ...(_a = this.parent) == null ? void 0 : _a.call(this), canCopy: void 0 };
1795
+ },
1796
+ addAttributes() {
1797
+ var _a;
1798
+ return { ...(_a = this.parent) == null ? void 0 : _a.call(this), language: {
1799
+ default: null,
1800
+ parseHTML: (element) => parseLanguage(element),
1801
+ renderHTML: (attributes) => attributes.language ? { "data-language": attributes.language } : {}
1802
+ } };
1803
+ },
1804
+ addNodeView() {
1805
+ return createCodeBlockNodeView(this.options.canCopy, this.options.contentContext);
1806
+ }
1807
+ });
1808
+ const imeKey = new PluginKey("ywiki-ime");
1809
+ const ImeGuard = Extension.create({
1810
+ name: "ywikiImeGuard",
1811
+ addProseMirrorPlugins() {
1812
+ return [
1813
+ new Plugin({
1814
+ key: imeKey,
1815
+ props: {
1816
+ handleDOMEvents: {
1817
+ compositionstart: () => {
1818
+ beginComposition();
1819
+ return false;
1820
+ },
1821
+ compositionend: () => {
1822
+ endComposition();
1823
+ return false;
1824
+ }
1825
+ }
1826
+ }
1827
+ })
1828
+ ];
1829
+ },
1830
+ onDestroy() {
1831
+ resetCompositionState();
1832
+ }
1833
+ });
1834
+ function setRenderedHtml(holder, html) {
1835
+ const tpl = document.createElement("template");
1836
+ tpl.innerHTML = html;
1837
+ holder.replaceChildren(tpl.content);
1838
+ }
1839
+ const mathNodeView = ({ node, editor, getPos }) => {
1840
+ var _a;
1841
+ const isBlock = node.type.name === "mathBlock";
1842
+ const dom = document.createElement(isBlock ? "div" : "span");
1843
+ dom.className = isBlock ? "doc-math-block math-node" : "doc-math-inline math-node";
1844
+ dom.setAttribute("data-math-node", isBlock ? "block" : "inline");
1845
+ if (isBlock) dom.setAttribute("data-block-chrome", "");
1846
+ const holder = document.createElement("span");
1847
+ holder.className = "math-render";
1848
+ dom.appendChild(holder);
1849
+ let latex = String(((_a = node.attrs) == null ? void 0 : _a.latex) ?? "");
1850
+ let input = null;
1851
+ let closing = false;
1852
+ let destroyed = false;
1853
+ let renderVersion = 0;
1854
+ function paint() {
1855
+ const version = ++renderVersion;
1856
+ holder.replaceChildren();
1857
+ dom.classList.remove("doc-math-error", "doc-math-pending");
1858
+ dom.removeAttribute("title");
1859
+ if (!latex) {
1860
+ holder.textContent = "空公式";
1861
+ return;
1862
+ }
1863
+ holder.textContent = isBlock ? `$$${latex}$$` : `$${latex}$`;
1864
+ dom.classList.add("doc-math-pending");
1865
+ void renderMathAsync(latex, isBlock).then((res) => {
1866
+ if (destroyed || version !== renderVersion) return;
1867
+ dom.classList.remove("doc-math-pending");
1868
+ if (res.html && !res.error) setRenderedHtml(holder, res.html);
1869
+ else {
1870
+ dom.classList.add("doc-math-error");
1871
+ dom.title = res.error || "公式渲染失败";
1872
+ holder.textContent = isBlock ? `$$${latex}$$` : `$${latex}$`;
1873
+ const hint = document.createElement("span");
1874
+ hint.className = "doc-math-error-hint";
1875
+ hint.textContent = "(公式渲染失败)";
1876
+ holder.appendChild(hint);
1877
+ }
1878
+ });
1879
+ }
1880
+ function closeEdit() {
1881
+ if (closing) return;
1882
+ closing = true;
1883
+ const field = input;
1884
+ input = null;
1885
+ field == null ? void 0 : field.remove();
1886
+ dom.classList.remove("is-editing");
1887
+ holder.style.display = "";
1888
+ closing = false;
1889
+ }
1890
+ function commit() {
1891
+ var _a2;
1892
+ if (closing) return;
1893
+ if (!input || destroyed || !editor.isEditable) {
1894
+ closeEdit();
1895
+ return;
1896
+ }
1897
+ const value = input.value;
1898
+ const pos = typeof getPos === "function" ? getPos() : void 0;
1899
+ closeEdit();
1900
+ if (value === latex) return;
1901
+ latex = value;
1902
+ if (typeof pos !== "number" || ((_a2 = editor.state.doc.nodeAt(pos)) == null ? void 0 : _a2.type) !== node.type) {
1903
+ paint();
1904
+ return;
1905
+ }
1906
+ editor.view.dispatch(
1907
+ editor.view.state.tr.setNodeMarkup(pos, void 0, { ...node.attrs, latex: value })
1908
+ );
1909
+ paint();
1910
+ }
1911
+ function beginEdit() {
1912
+ if (!editor.isEditable || input) return;
1913
+ input = document.createElement("input");
1914
+ input.type = "text";
1915
+ input.className = "math-latex-input";
1916
+ input.value = latex;
1917
+ input.spellcheck = false;
1918
+ input.setAttribute("aria-label", "编辑公式");
1919
+ input.addEventListener("keydown", (e) => {
1920
+ e.stopPropagation();
1921
+ if (shouldIgnoreForIme(e)) return;
1922
+ if (e.key === "Enter") {
1923
+ e.preventDefault();
1924
+ commit();
1925
+ } else if (e.key === "Escape") {
1926
+ e.preventDefault();
1927
+ closeEdit();
1928
+ }
1929
+ });
1930
+ input.addEventListener("blur", () => commit());
1931
+ input.addEventListener("mousedown", (e) => e.stopPropagation());
1932
+ holder.style.display = "none";
1933
+ dom.classList.add("is-editing");
1934
+ dom.appendChild(input);
1935
+ void ensureKatexCss();
1936
+ setTimeout(() => input == null ? void 0 : input.focus(), 0);
1937
+ }
1938
+ dom.addEventListener("click", (e) => {
1939
+ if (!editor.isEditable) return;
1940
+ e.preventDefault();
1941
+ beginEdit();
1942
+ });
1943
+ paint();
1944
+ void ensureKatexCss();
1945
+ return {
1946
+ dom,
1947
+ // atom 节点没有 contentDOM
1948
+ update: (updated) => {
1949
+ var _a2;
1950
+ if (updated.type.name !== node.type.name) return false;
1951
+ node = updated;
1952
+ const next = String(((_a2 = updated.attrs) == null ? void 0 : _a2.latex) ?? "");
1953
+ if (next !== latex) {
1954
+ latex = next;
1955
+ paint();
1956
+ }
1957
+ return true;
1958
+ },
1959
+ // 输入框相关的交互全部由本 nodeView 处理,不交给 ProseMirror
1960
+ stopEvent: (event) => {
1961
+ var _a2;
1962
+ const t = event.target;
1963
+ return !!((_a2 = t == null ? void 0 : t.classList) == null ? void 0 : _a2.contains("math-latex-input"));
1964
+ },
1965
+ ignoreMutation: () => true,
1966
+ destroy: () => {
1967
+ destroyed = true;
1968
+ renderVersion += 1;
1969
+ closeEdit();
1970
+ }
1971
+ };
1972
+ };
1973
+ const TEXT_ALIGNS = ["left", "center", "right", "justify"];
1974
+ const MAX_INDENT = 6;
1975
+ const INDENT_STEP_PX = 24;
1976
+ const BlockFormat = Extension.create({
1977
+ name: "blockFormat",
1978
+ addOptions() {
1979
+ return { types: ["paragraph", "heading"] };
1980
+ },
1981
+ addGlobalAttributes() {
1982
+ const types = this.options.types ?? ["paragraph", "heading"];
1983
+ return [
1984
+ {
1985
+ types,
1986
+ attributes: {
1987
+ textAlign: {
1988
+ default: null,
1989
+ /** 从 DOM 读回时同样限定取值,防止粘贴内容塞入任意值 */
1990
+ parseHTML: (el) => {
1991
+ const v = el.getAttribute("data-text-align") ?? el.style.textAlign;
1992
+ return TEXT_ALIGNS.includes(v) ? v : null;
1993
+ },
1994
+ renderHTML: (attrs) => {
1995
+ const v = attrs.textAlign;
1996
+ if (!v || !TEXT_ALIGNS.includes(v)) return {};
1997
+ return { "data-text-align": v, style: `text-align: ${v}` };
1998
+ }
1999
+ },
2000
+ indent: {
2001
+ default: 0,
2002
+ parseHTML: (el) => {
2003
+ const raw = el.getAttribute("data-indent");
2004
+ const n = Number(raw);
2005
+ return Number.isFinite(n) && n > 0 ? Math.min(Math.floor(n), MAX_INDENT) : 0;
2006
+ },
2007
+ renderHTML: (attrs) => {
2008
+ const n = Number(attrs.indent ?? 0);
2009
+ if (!Number.isFinite(n) || n <= 0) return {};
2010
+ return {
2011
+ "data-indent": String(Math.min(Math.floor(n), MAX_INDENT)),
2012
+ style: `margin-left: ${Math.min(Math.floor(n), MAX_INDENT) * INDENT_STEP_PX}px`
2013
+ };
2014
+ }
2015
+ }
2016
+ }
2017
+ }
2018
+ ];
2019
+ },
2020
+ addCommands() {
2021
+ return {
2022
+ setTextAlign: (align) => ({ tr, state, dispatch: dispatch2 }) => {
2023
+ const types = this.options.types ?? ["paragraph", "heading"];
2024
+ const { from, to } = state.selection;
2025
+ let changed = false;
2026
+ state.doc.nodesBetween(from, to, (node, pos) => {
2027
+ if (!types.includes(node.type.name)) return;
2028
+ if (dispatch2) {
2029
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, textAlign: align });
2030
+ }
2031
+ changed = true;
2032
+ });
2033
+ if (!changed) return false;
2034
+ dispatch2 == null ? void 0 : dispatch2(tr);
2035
+ return true;
2036
+ },
2037
+ setIndent: (level) => ({ tr, state, dispatch: dispatch2 }) => {
2038
+ const types = this.options.types ?? ["paragraph", "heading"];
2039
+ const next = Math.max(0, Math.min(Math.floor(level), MAX_INDENT));
2040
+ const { from, to } = state.selection;
2041
+ let changed = false;
2042
+ state.doc.nodesBetween(from, to, (node, pos) => {
2043
+ if (!types.includes(node.type.name)) return;
2044
+ const cur = Number(node.attrs.indent ?? 0);
2045
+ if (cur === next) return;
2046
+ if (dispatch2) {
2047
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, indent: next });
2048
+ }
2049
+ changed = true;
2050
+ });
2051
+ if (!changed) return false;
2052
+ dispatch2 == null ? void 0 : dispatch2(tr);
2053
+ return true;
2054
+ },
2055
+ indentBlock: () => ({ tr, state, dispatch: dispatch2 }) => {
2056
+ const types = this.options.types ?? ["paragraph", "heading"];
2057
+ const { from, to } = state.selection;
2058
+ let changed = false;
2059
+ state.doc.nodesBetween(from, to, (node, pos) => {
2060
+ if (!types.includes(node.type.name)) return;
2061
+ const cur = Number(node.attrs.indent ?? 0);
2062
+ if (cur >= MAX_INDENT) return;
2063
+ if (dispatch2) {
2064
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, indent: cur + 1 });
2065
+ }
2066
+ changed = true;
2067
+ });
2068
+ if (!changed) return false;
2069
+ dispatch2 == null ? void 0 : dispatch2(tr);
2070
+ return true;
2071
+ },
2072
+ outdentBlock: () => ({ tr, state, dispatch: dispatch2 }) => {
2073
+ const types = this.options.types ?? ["paragraph", "heading"];
2074
+ const { from, to } = state.selection;
2075
+ let changed = false;
2076
+ state.doc.nodesBetween(from, to, (node, pos) => {
2077
+ if (!types.includes(node.type.name)) return;
2078
+ const cur = Number(node.attrs.indent ?? 0);
2079
+ if (cur <= 0) return;
2080
+ if (dispatch2) {
2081
+ tr.setNodeMarkup(pos, void 0, { ...node.attrs, indent: cur - 1 });
2082
+ }
2083
+ changed = true;
2084
+ });
2085
+ if (!changed) return false;
2086
+ dispatch2 == null ? void 0 : dispatch2(tr);
2087
+ return true;
2088
+ }
2089
+ };
2090
+ }
2091
+ });
2092
+ const blockSelectionKey = new PluginKey("ywikiBlockSelection");
2093
+ const GUTTER_MIN_PX = 48;
2094
+ const SELECT_PAD_Y = 5;
2095
+ function blockSelectionBand(view) {
2096
+ const dom = view.dom;
2097
+ if (!dom || typeof dom.getBoundingClientRect !== "function") return { left: 0, right: 0 };
2098
+ const prose = dom.getBoundingClientRect();
2099
+ const rect = blockSelectionBandRect(view);
2100
+ return {
2101
+ // 取并集:宿主容器比正文还窄时(正文铺满容器)仍保留最小留白
2102
+ left: Math.min((rect == null ? void 0 : rect.left) ?? prose.left, prose.left - GUTTER_MIN_PX),
2103
+ right: Math.max((rect == null ? void 0 : rect.right) ?? prose.right, prose.right + GUTTER_MIN_PX)
2104
+ };
2105
+ }
2106
+ function blockSelectionBandRect(view) {
2107
+ var _a, _b;
2108
+ const dom = view.dom;
2109
+ if (!dom || typeof dom.getBoundingClientRect !== "function") return null;
2110
+ const marked = (_a = dom.closest) == null ? void 0 : _a.call(dom, "[data-block-select-band]");
2111
+ const host = (marked instanceof HTMLElement ? marked : null) ?? ((_b = dom.closest) == null ? void 0 : _b.call(dom, ".editor-shell")) ?? null;
2112
+ return (host == null ? void 0 : host.getBoundingClientRect()) ?? null;
2113
+ }
2114
+ function bandSide(x, proseLeft, proseRight, band) {
2115
+ if (x >= proseLeft && x <= proseRight) return null;
2116
+ if (x < proseLeft) return x >= band.left ? "left" : null;
2117
+ return x <= band.right ? "right" : null;
2118
+ }
2119
+ function scrollParentOf(element) {
2120
+ let node = (element == null ? void 0 : element.parentElement) ?? null;
2121
+ while (node) {
2122
+ const style = typeof getComputedStyle === "function" ? getComputedStyle(node) : null;
2123
+ if (/(auto|scroll)/.test((style == null ? void 0 : style.overflowY) ?? "")) return node;
2124
+ node = node.parentElement;
2125
+ }
2126
+ return null;
2127
+ }
2128
+ function autoScrollDelta(pointerY, top, bottom, edge = 60, maxSpeed = 16) {
2129
+ if (!Number.isFinite(pointerY) || bottom <= top) return 0;
2130
+ if (pointerY < top + edge) {
2131
+ const depth = Math.min(edge, top + edge - pointerY);
2132
+ return -Math.ceil(depth / edge * maxSpeed);
2133
+ }
2134
+ if (pointerY > bottom - edge) {
2135
+ const depth = Math.min(edge, pointerY - (bottom - edge));
2136
+ return Math.ceil(depth / edge * maxSpeed);
2137
+ }
2138
+ return 0;
2139
+ }
2140
+ function expandParts(doc, parts) {
2141
+ const out = [];
2142
+ for (const part of parts) {
2143
+ if (part.kind === "block") {
2144
+ out.push(new SelectionRange(doc.resolve(part.from), doc.resolve(part.to)));
2145
+ continue;
2146
+ }
2147
+ for (const range of part.images) {
2148
+ out.push(new SelectionRange(doc.resolve(range.from), doc.resolve(range.to)));
2149
+ }
2150
+ }
2151
+ return out;
2152
+ }
2153
+ class BlockRangeSelection extends Selection {
2154
+ constructor($from, $to, parts) {
2155
+ super($from, $to, (parts == null ? void 0 : parts.length) ? expandParts($from.doc, parts) : void 0);
2156
+ __publicField(this, "parts");
2157
+ __publicField(this, "visible", false);
2158
+ this.parts = parts;
2159
+ }
2160
+ content() {
2161
+ const parts = this.parts;
2162
+ if (!(parts == null ? void 0 : parts.length)) return this.$from.doc.slice(this.from, this.to);
2163
+ const doc = this.$from.doc;
2164
+ const nodes = [];
2165
+ for (const part of parts) {
2166
+ if (part.kind === "block") {
2167
+ const node = doc.nodeAt(part.from);
2168
+ if (node) nodes.push(node);
2169
+ continue;
2170
+ }
2171
+ const gallery = doc.nodeAt(part.from);
2172
+ if (!gallery) continue;
2173
+ const picked = [];
2174
+ gallery.forEach((child, offset) => {
2175
+ const abs = part.from + 1 + offset;
2176
+ if (part.images.some((range) => abs >= range.from && abs < range.to)) picked.push(child);
2177
+ });
2178
+ if (picked.length >= 2) {
2179
+ nodes.push(gallery.type.create(gallery.attrs, Fragment.fromArray(picked)));
2180
+ } else if (picked.length === 1) {
2181
+ nodes.push(picked[0]);
2182
+ }
2183
+ }
2184
+ return new Slice(Fragment.fromArray(nodes), 0, 0);
2185
+ }
2186
+ /** 删除:按容器重建,图片行剩一张时还原成独立图片,避免 PM 补空白占位图。 */
2187
+ replace(tr, content = Slice.empty) {
2188
+ var _a;
2189
+ if (!((_a = this.parts) == null ? void 0 : _a.length) || content.size > 0) {
2190
+ super.replace(tr, content);
2191
+ return;
2192
+ }
2193
+ const parts = [...this.parts].sort((a, b) => b.from - a.from);
2194
+ let caret = null;
2195
+ for (const part of parts) {
2196
+ if (part.kind === "block") {
2197
+ tr.delete(part.from, part.to);
2198
+ caret = part.from;
2199
+ continue;
2200
+ }
2201
+ const gallery = tr.doc.nodeAt(part.from);
2202
+ if (!gallery) continue;
2203
+ const remaining = [];
2204
+ gallery.forEach((child, offset) => {
2205
+ const abs = part.from + 1 + offset;
2206
+ if (!part.images.some((range) => abs >= range.from && abs < range.to)) remaining.push(child);
2207
+ });
2208
+ if (remaining.length >= 2) {
2209
+ tr.replaceWith(part.from, part.to, gallery.type.create(gallery.attrs, Fragment.fromArray(remaining)));
2210
+ } else if (remaining.length === 1) {
2211
+ tr.replaceWith(part.from, part.to, remaining[0]);
2212
+ } else {
2213
+ tr.delete(part.from, part.to);
2214
+ }
2215
+ caret = part.from;
2216
+ }
2217
+ if (caret !== null) {
2218
+ const pos = Math.max(0, Math.min(caret, tr.doc.content.size));
2219
+ tr.setSelection(Selection.near(tr.doc.resolve(pos), 1));
2220
+ }
2221
+ }
2222
+ eq(other) {
2223
+ return other.toJSON().type === "ywikiBlockRange" && other.from === this.from && other.to === this.to && JSON.stringify(other.parts ?? null) === JSON.stringify(this.parts ?? null);
2224
+ }
2225
+ map(doc, mapping) {
2226
+ return Selection.near(doc.resolve(mapping.map(this.from, 1)));
2227
+ }
2228
+ toJSON() {
2229
+ return { type: "ywikiBlockRange", from: this.from, to: this.to, parts: this.parts };
2230
+ }
2231
+ static fromJSON(doc, json) {
2232
+ return new BlockRangeSelection(doc.resolve(json.from), doc.resolve(json.to), json.parts);
2233
+ }
2234
+ getBookmark() {
2235
+ return Selection.near(this.$from).getBookmark();
2236
+ }
2237
+ }
2238
+ try {
2239
+ Selection.jsonID("ywikiBlockRange", BlockRangeSelection);
2240
+ } catch (err) {
2241
+ if (!(err instanceof RangeError) || err.message !== "Duplicate use of selection JSON ID ywikiBlockRange") throw err;
2242
+ }
2243
+ function blockAtY(view, y) {
2244
+ const candidates = [];
2245
+ view.state.doc.forEach((node, offset) => {
2246
+ const dom = view.nodeDOM(offset);
2247
+ if (!(dom instanceof HTMLElement)) return;
2248
+ const r = dom.getBoundingClientRect();
2249
+ candidates.push({ pos: offset, node, top: r.top, bottom: r.bottom });
2250
+ });
2251
+ if (candidates.length === 0) return null;
2252
+ for (const c of candidates) {
2253
+ if (y >= c.top && y <= c.bottom) {
2254
+ return { pos: c.pos, node: c.node };
2255
+ }
2256
+ }
2257
+ let best = candidates[0];
2258
+ let bestDist = Number.POSITIVE_INFINITY;
2259
+ for (const c of candidates) {
2260
+ const dist = y < c.top ? c.top - y : y - c.bottom;
2261
+ if (dist < bestDist) {
2262
+ bestDist = dist;
2263
+ best = c;
2264
+ }
2265
+ }
2266
+ return bestDist > 48 ? null : { pos: best.pos, node: best.node };
2267
+ }
2268
+ function gutterAreaHit(view, event) {
2269
+ const domRect = view.dom.getBoundingClientRect();
2270
+ if (event.clientY < domRect.top || event.clientY > domRect.bottom) return null;
2271
+ if (!bandSide(event.clientX, domRect.left, domRect.right, blockSelectionBand(view))) return null;
2272
+ return blockAtY(view, event.clientY);
2273
+ }
2274
+ function coveredBlocks(doc, aPos, bPos) {
2275
+ const lo = Math.min(aPos, bPos);
2276
+ const hi = Math.max(aPos, bPos);
2277
+ const out = [];
2278
+ doc.forEach((node, offset) => {
2279
+ const end = offset + node.nodeSize;
2280
+ if (end > lo && offset <= hi) out.push({ from: offset, to: end });
2281
+ });
2282
+ return out;
2283
+ }
2284
+ function rectsIntersect(a, b) {
2285
+ return a.right > b.left && a.left < b.right && a.bottom > b.top && a.top < b.bottom;
2286
+ }
2287
+ function marqueeImageRanges(galleryFrom, sizes, childRects, marquee) {
2288
+ const picked = [];
2289
+ let offset = 0;
2290
+ for (let index = 0; index < sizes.length; index += 1) {
2291
+ const from = galleryFrom + 1 + offset;
2292
+ offset += sizes[index];
2293
+ const rect = childRects[index];
2294
+ if (rect && rectsIntersect(marquee, rect)) picked.push({ from, to: from + sizes[index] });
2295
+ }
2296
+ return picked;
2297
+ }
2298
+ function selectionPartsForRange(view, doc, aPos, bPos, marquee) {
2299
+ const out = [];
2300
+ for (const block of coveredBlocks(doc, aPos, bPos)) {
2301
+ const node = doc.nodeAt(block.from);
2302
+ if ((node == null ? void 0 : node.type.name) === "imageGallery") {
2303
+ const galleryEl = typeof view.nodeDOM === "function" ? view.nodeDOM(block.from) : null;
2304
+ if (galleryEl instanceof HTMLElement) {
2305
+ const galleryRect = galleryEl.getBoundingClientRect();
2306
+ const enteredRow = rectsIntersect(marquee, {
2307
+ left: galleryRect.left,
2308
+ top: galleryRect.top,
2309
+ right: galleryRect.right,
2310
+ bottom: galleryRect.bottom
2311
+ });
2312
+ if (enteredRow) {
2313
+ const sizes = [];
2314
+ node.forEach((child) => sizes.push(child.nodeSize));
2315
+ const childRects = [];
2316
+ let offset = 0;
2317
+ node.forEach((child) => {
2318
+ const childPos = block.from + 1 + offset;
2319
+ offset += child.nodeSize;
2320
+ const dom = typeof view.nodeDOM === "function" ? view.nodeDOM(childPos) : null;
2321
+ if (!(dom instanceof HTMLElement)) {
2322
+ childRects.push(null);
2323
+ return;
2324
+ }
2325
+ const r = dom.getBoundingClientRect();
2326
+ childRects.push({ left: r.left, top: r.top, right: r.right, bottom: r.bottom });
2327
+ });
2328
+ const picked = marqueeImageRanges(block.from, sizes, childRects, marquee);
2329
+ if (picked.length === 0) continue;
2330
+ if (picked.length === sizes.length) out.push({ kind: "block", ...block });
2331
+ else out.push({ kind: "images", ...block, images: picked });
2332
+ continue;
2333
+ }
2334
+ }
2335
+ }
2336
+ out.push({ kind: "block", ...block });
2337
+ }
2338
+ return out;
2339
+ }
2340
+ function marqueeBox(startX, startY, x, y, hostLeft, hostTop) {
2341
+ return {
2342
+ left: Math.min(startX, x) - hostLeft,
2343
+ top: Math.min(startY, y) - hostTop,
2344
+ width: Math.abs(x - startX),
2345
+ height: Math.abs(y - startY)
2346
+ };
2347
+ }
2348
+ function clampMarqueeBand(bandLeft, bandRight, hostLeft, hostRight, viewportRight = Number.POSITIVE_INFINITY) {
2349
+ const left = Math.max(bandLeft, hostLeft);
2350
+ const right = Math.min(bandRight, hostRight, viewportRight);
2351
+ if (right > left) return { left, right };
2352
+ return { left: hostLeft, right: Math.max(hostLeft, Math.min(hostRight, viewportRight)) };
2353
+ }
2354
+ const BLOCK_SELECT_SLOP_PX = 8;
2355
+ function isBlockSelectGesture(dx, dy) {
2356
+ return Math.max(Math.abs(dx), Math.abs(dy)) >= BLOCK_SELECT_SLOP_PX;
2357
+ }
2358
+ function gutterClickCaretPos(doc, pos) {
2359
+ if (!Number.isInteger(pos) || pos < 0 || pos >= doc.content.size) return null;
2360
+ const node = doc.nodeAt(pos);
2361
+ if (!node) return null;
2362
+ return node.isTextblock ? Math.min(pos + 1, doc.content.size) : pos;
2363
+ }
2364
+ function shouldSuppressNativeHighlight(blocks) {
2365
+ return ((blocks == null ? void 0 : blocks.length) ?? 0) > 0;
2366
+ }
2367
+ const MIN_BAND_GAP = 6;
2368
+ function selectionPadding(rects) {
2369
+ if (rects.length === 0) return [];
2370
+ let smallest = Number.POSITIVE_INFINITY;
2371
+ for (let i = 1; i < rects.length; i += 1) {
2372
+ const gap = rects[i].top - rects[i - 1].bottom;
2373
+ if (gap > 0) smallest = Math.min(smallest, gap);
2374
+ }
2375
+ const budget = Number.isFinite(smallest) ? Math.max(0, (smallest - MIN_BAND_GAP) / 2) : SELECT_PAD_Y;
2376
+ const pad = Math.max(0, Math.min(SELECT_PAD_Y, budget));
2377
+ return rects.map(() => ({ top: pad, bottom: pad }));
2378
+ }
2379
+ function measureBlocksPadding(view, blocks) {
2380
+ if (blocks.length === 0) return [];
2381
+ if (typeof view.nodeDOM !== "function") return [];
2382
+ const rects = [];
2383
+ for (const block of blocks) {
2384
+ const element = view.nodeDOM(block.from);
2385
+ if (!(element instanceof HTMLElement)) return [];
2386
+ const rect = element.getBoundingClientRect();
2387
+ rects.push({ top: rect.top, bottom: rect.bottom });
2388
+ }
2389
+ return selectionPadding(rects);
2390
+ }
2391
+ function setBlockRangeSelection(view, anchorPos, targetPos, marquee) {
2392
+ const { doc } = view.state;
2393
+ const parts = marquee ? selectionPartsForRange(view, doc, anchorPos, targetPos, marquee) : coveredBlocks(doc, anchorPos, targetPos).map(
2394
+ (block) => ({ kind: "block", ...block })
2395
+ );
2396
+ if (parts.length === 0) {
2397
+ clearBlockRangeSelection(view);
2398
+ return false;
2399
+ }
2400
+ const first = parts[0];
2401
+ const last = parts[parts.length - 1];
2402
+ const sel = new BlockRangeSelection(doc.resolve(first.from), doc.resolve(last.to), parts);
2403
+ const tr = view.state.tr.setSelection(sel);
2404
+ const wholeBlocks = parts.filter(
2405
+ (part) => part.kind === "block"
2406
+ );
2407
+ const padding = measureBlocksPadding(view, wholeBlocks);
2408
+ let paddingIndex = 0;
2409
+ tr.setMeta(blockSelectionKey, {
2410
+ blocks: parts.map((part) => {
2411
+ if (part.kind === "images") return { from: part.from, to: part.to, images: part.images };
2412
+ const measured = padding[paddingIndex];
2413
+ paddingIndex += 1;
2414
+ return { from: part.from, to: part.to, padding: measured };
2415
+ })
2416
+ });
2417
+ view.dispatch(tr);
2418
+ return true;
2419
+ }
2420
+ function clearBlockRangeSelection(view) {
2421
+ const tr = view.state.tr.setMeta(blockSelectionKey, { blocks: [] });
2422
+ view.dispatch(tr);
2423
+ }
2424
+ function setImageRangeSelection(view, galleryPos, imageRanges) {
2425
+ const { doc } = view.state;
2426
+ const gallery = doc.nodeAt(galleryPos);
2427
+ if (!gallery || gallery.type.name !== "imageGallery" || imageRanges.length === 0) return false;
2428
+ const parts = imageRanges.length === gallery.childCount ? [{ kind: "block", from: galleryPos, to: galleryPos + gallery.nodeSize }] : [{
2429
+ kind: "images",
2430
+ from: galleryPos,
2431
+ to: galleryPos + gallery.nodeSize,
2432
+ images: imageRanges
2433
+ }];
2434
+ const first = parts[0];
2435
+ const last = parts[parts.length - 1];
2436
+ const sel = new BlockRangeSelection(doc.resolve(first.from), doc.resolve(last.to), parts);
2437
+ const tr = view.state.tr.setSelection(sel);
2438
+ const padding = measureBlocksPadding(
2439
+ view,
2440
+ parts.filter(
2441
+ (part) => part.kind === "block"
2442
+ )
2443
+ );
2444
+ tr.setMeta(blockSelectionKey, {
2445
+ blocks: parts.map((part) => part.kind === "images" ? { from: part.from, to: part.to, images: part.images } : { from: part.from, to: part.to, padding: padding[0] })
2446
+ });
2447
+ view.dispatch(tr);
2448
+ return true;
2449
+ }
2450
+ function getSelectedBlocks(view) {
2451
+ var _a;
2452
+ return ((_a = blockSelectionKey.getState(view.state)) == null ? void 0 : _a.blocks) ?? [];
2453
+ }
2454
+ function hasPartialImageSelection(view) {
2455
+ var _a;
2456
+ const sel = view.state.selection;
2457
+ return sel instanceof BlockRangeSelection && !!((_a = sel.parts) == null ? void 0 : _a.some((part) => part.kind === "images"));
2458
+ }
2459
+ function replacePartialWithText(view, text) {
2460
+ if (!hasPartialImageSelection(view)) return false;
2461
+ const sel = view.state.selection;
2462
+ const tr = view.state.tr;
2463
+ sel.replace(tr);
2464
+ const schema = view.state.schema;
2465
+ const insertAt = tr.selection.from;
2466
+ if (tr.selection.$from.parent.inlineContent) {
2467
+ if (text) tr.insertText(text, insertAt);
2468
+ tr.setSelection(TextSelection.create(tr.doc, insertAt + text.length));
2469
+ } else if (schema.nodes.paragraph) {
2470
+ tr.insert(
2471
+ insertAt,
2472
+ schema.nodes.paragraph.create(null, text ? schema.text(text) : void 0)
2473
+ );
2474
+ tr.setSelection(TextSelection.create(tr.doc, insertAt + 1 + text.length));
2475
+ } else {
2476
+ return false;
2477
+ }
2478
+ tr.setMeta(blockSelectionKey, { blocks: [] });
2479
+ view.dispatch(tr.scrollIntoView());
2480
+ return true;
2481
+ }
2482
+ const BlockSelection = Extension.create({
2483
+ name: "ywikiBlockSelection",
2484
+ /**
2485
+ * 高于默认(100):本扩展要在**输入/删除快捷键**之前拿到事件。
2486
+ *
2487
+ * Tiptap 的 `deleteSelection` 命令不是调用 `tr.deleteSelection()`,而是自己
2488
+ * 遍历 `selection.ranges` 逐个 `deleteRange`——不经过选区类的 `replace()`。
2489
+ * 图片行部分选中时那样删,会把行里删到只剩一张,PM 随后补一个**空白图片
2490
+ * 节点**占位(`image{2,10}` 的下限),留下看不见但真实存在的脏图(实测)。
2491
+ * 因此 Backspace/Delete 由本扩展先接管,走选区自己的重建逻辑。
2492
+ */
2493
+ priority: 1e3,
2494
+ addKeyboardShortcuts() {
2495
+ const remove = () => {
2496
+ var _a;
2497
+ const view = this.editor.view;
2498
+ const sel = view.state.selection;
2499
+ if (!(sel instanceof BlockRangeSelection) || !((_a = sel.parts) == null ? void 0 : _a.length)) return false;
2500
+ const tr = view.state.tr;
2501
+ sel.replace(tr);
2502
+ if (!tr.docChanged) return false;
2503
+ tr.setMeta(blockSelectionKey, { blocks: [] });
2504
+ view.dispatch(tr.scrollIntoView());
2505
+ return true;
2506
+ };
2507
+ return {
2508
+ Backspace: remove,
2509
+ Delete: remove,
2510
+ "Mod-Backspace": remove,
2511
+ "Mod-Delete": remove,
2512
+ "Shift-Backspace": remove,
2513
+ "Ctrl-h": remove,
2514
+ "Ctrl-d": remove,
2515
+ "Alt-Backspace": remove,
2516
+ "Alt-Delete": remove,
2517
+ "Ctrl-Alt-Backspace": remove,
2518
+ "Alt-d": remove
2519
+ };
2520
+ },
2521
+ addProseMirrorPlugins() {
2522
+ return [
2523
+ new Plugin({
2524
+ key: blockSelectionKey,
2525
+ state: {
2526
+ init: () => ({ blocks: [] }),
2527
+ apply: (tr, value) => {
2528
+ const meta = tr.getMeta(blockSelectionKey);
2529
+ if (meta) return meta;
2530
+ if (tr.selectionSet || tr.docChanged) return { blocks: [] };
2531
+ return value;
2532
+ }
2533
+ },
2534
+ props: {
2535
+ /**
2536
+ * 输入替换**图片行部分选中**。
2537
+ *
2538
+ * ## 三个入口都要接,缺一个就会多删图
2539
+ *
2540
+ * PM 对非 TextSelection 的输入只处理 `ranges[0]`,而且中英文输入走的
2541
+ * 是不同路径:
2542
+ * - 普通按键 → `handleTextInput`;
2543
+ * - 浏览器直接派发 input/beforeinput → DOM 变化被 PM 的 observer
2544
+ * 读到后按 DOM 区间替换,会连带吃掉相邻的图(实测:中文输入
2545
+ * "替换测试"删掉了 3 张,而只选中 2 张);
2546
+ * - 输入法 → composition 系列,既不经过 `handleTextInput`,
2547
+ * DOM 变化还与组合文本混在一起。
2548
+ *
2549
+ * 因此三处都收敛到 `replacePartialWithText`:先删掉被框住的子图,
2550
+ * 再把文字落成段落并把光标放在文字后面。composition 是在
2551
+ * `compositionstart` 里先删再让输入法正常输入,不再自造文本。
2552
+ */
2553
+ handleTextInput(view, _from, _to, text) {
2554
+ return replacePartialWithText(view, text);
2555
+ },
2556
+ handleDOMEvents: {
2557
+ beforeinput(view, event) {
2558
+ const input = event;
2559
+ if (input.inputType !== "insertText" || input.data === null) return false;
2560
+ if (!hasPartialImageSelection(view)) return false;
2561
+ input.preventDefault();
2562
+ replacePartialWithText(view, input.data);
2563
+ return true;
2564
+ },
2565
+ compositionstart(view) {
2566
+ if (!hasPartialImageSelection(view)) return false;
2567
+ replacePartialWithText(view, "");
2568
+ return false;
2569
+ }
2570
+ },
2571
+ decorations(state) {
2572
+ var _a;
2573
+ const value = blockSelectionKey.getState(state);
2574
+ if (!value || value.blocks.length === 0) return null;
2575
+ const decorations = [];
2576
+ for (const b of value.blocks) {
2577
+ if ((_a = b.images) == null ? void 0 : _a.length) {
2578
+ decorations.push(
2579
+ Decoration.node(b.from, b.to, { class: "is-block-image-selected" })
2580
+ );
2581
+ for (const range of b.images) {
2582
+ decorations.push(
2583
+ Decoration.node(range.from, range.to, { class: "is-block-selected-image" })
2584
+ );
2585
+ }
2586
+ continue;
2587
+ }
2588
+ decorations.push(
2589
+ Decoration.node(b.from, b.to, {
2590
+ class: "is-block-selected",
2591
+ /**
2592
+ * 外扩量按实测值写成 CSS 变量,交给样式里的 `::before` 消费。
2593
+ * 未测到(布局不可用)时留空,样式回退到 `SELECT_PAD_*` 的默认值。
2594
+ */
2595
+ style: b.padding ? `--block-select-pad-top:${b.padding.top}px;--block-select-pad-bottom:${b.padding.bottom}px` : ""
2596
+ })
2597
+ );
2598
+ }
2599
+ return DecorationSet.create(state.doc, decorations);
2600
+ }
2601
+ },
2602
+ view: (editorView) => {
2603
+ let anchorPos = null;
2604
+ const applyBlockSelection = (targetPos) => {
2605
+ if (anchorPos === null) return;
2606
+ setBlockRangeSelection(
2607
+ editorView,
2608
+ anchorPos,
2609
+ targetPos,
2610
+ marqueeRect(pointerX, pointerY)
2611
+ );
2612
+ };
2613
+ let moved = false;
2614
+ let startX = 0;
2615
+ let startY = 0;
2616
+ const marqueeHost = editorView.dom.closest(".editor-shell") ?? editorView.dom.parentElement;
2617
+ let marqueeEl = null;
2618
+ const updateMarquee = (x, y) => {
2619
+ if (!marqueeHost) return;
2620
+ if (!marqueeEl) {
2621
+ marqueeEl = document.createElement("div");
2622
+ marqueeEl.className = "block-marquee";
2623
+ marqueeHost.appendChild(marqueeEl);
2624
+ }
2625
+ const hostRect = marqueeHost.getBoundingClientRect();
2626
+ const declared = blockSelectionBand(editorView);
2627
+ const limit = blockSelectionBandRect(editorView) ?? hostRect;
2628
+ const band = clampMarqueeBand(
2629
+ declared.left,
2630
+ declared.right,
2631
+ limit.left,
2632
+ limit.right,
2633
+ typeof document !== "undefined" ? document.documentElement.clientWidth : void 0
2634
+ );
2635
+ const clampX = (v) => Math.max(band.left, Math.min(v, band.right));
2636
+ const clampY = (v) => Math.max(hostRect.top, Math.min(v, hostRect.bottom));
2637
+ const box = marqueeBox(
2638
+ clampX(startX),
2639
+ clampY(startY),
2640
+ clampX(x),
2641
+ clampY(y),
2642
+ hostRect.left,
2643
+ hostRect.top
2644
+ );
2645
+ marqueeEl.style.left = `${box.left}px`;
2646
+ marqueeEl.style.top = `${box.top}px`;
2647
+ marqueeEl.style.width = `${box.width}px`;
2648
+ marqueeEl.style.height = `${box.height}px`;
2649
+ };
2650
+ const clearMarquee = () => {
2651
+ marqueeEl == null ? void 0 : marqueeEl.remove();
2652
+ marqueeEl = null;
2653
+ };
2654
+ const marqueeRect = (x, y) => ({
2655
+ left: Math.min(startX, x),
2656
+ top: Math.min(startY, y),
2657
+ right: Math.max(startX, x),
2658
+ bottom: Math.max(startY, y)
2659
+ });
2660
+ let scrollFrame = 0;
2661
+ let pointerX = 0;
2662
+ let pointerY = 0;
2663
+ let scrollParent = null;
2664
+ let lastFrameAt = 0;
2665
+ const stopAutoScroll = () => {
2666
+ if (scrollFrame) cancelAnimationFrame(scrollFrame);
2667
+ scrollFrame = 0;
2668
+ scrollParent = null;
2669
+ lastFrameAt = 0;
2670
+ };
2671
+ const tickAutoScroll = (now = performance.now()) => {
2672
+ scrollFrame = 0;
2673
+ if (anchorPos === null || !moved) return;
2674
+ const target = scrollParent;
2675
+ if (!target) return;
2676
+ const elapsed = lastFrameAt ? Math.min(now - lastFrameAt, 50) : 16.7;
2677
+ lastFrameAt = now;
2678
+ const rect = target.getBoundingClientRect();
2679
+ const delta = autoScrollDelta(pointerY, rect.top, rect.bottom);
2680
+ if (delta === 0) {
2681
+ lastFrameAt = 0;
2682
+ return;
2683
+ }
2684
+ const scaled = Math.round(delta * elapsed / 16.7);
2685
+ const before = target.scrollTop;
2686
+ target.scrollTop = before + (scaled === 0 ? Math.sign(delta) : scaled);
2687
+ if (target.scrollTop === before) return;
2688
+ const hit = blockAtY(editorView, pointerY);
2689
+ if (hit) applyBlockSelection(hit.pos);
2690
+ updateMarquee(pointerX, pointerY);
2691
+ scrollFrame = requestAnimationFrame(tickAutoScroll);
2692
+ };
2693
+ const startAutoScroll = () => {
2694
+ if (scrollFrame) return;
2695
+ scrollFrame = requestAnimationFrame(tickAutoScroll);
2696
+ };
2697
+ const onMouseMove = (event) => {
2698
+ if (anchorPos === null) return;
2699
+ if (!moved && !isBlockSelectGesture(event.clientX - startX, event.clientY - startY)) {
2700
+ return;
2701
+ }
2702
+ pointerX = event.clientX;
2703
+ pointerY = event.clientY;
2704
+ if (!moved) {
2705
+ moved = true;
2706
+ applyBlockSelection(anchorPos);
2707
+ }
2708
+ scrollParent ?? (scrollParent = scrollParentOf(editorView.dom));
2709
+ updateMarquee(event.clientX, event.clientY);
2710
+ const target = blockAtY(editorView, event.clientY);
2711
+ if (target) applyBlockSelection(target.pos);
2712
+ if (scrollParent) {
2713
+ const rect = scrollParent.getBoundingClientRect();
2714
+ if (autoScrollDelta(event.clientY, rect.top, rect.bottom) !== 0) startAutoScroll();
2715
+ }
2716
+ event.preventDefault();
2717
+ };
2718
+ const finish = (event) => {
2719
+ const wasClick = (event == null ? void 0 : event.type) === "mouseup" && !moved && anchorPos !== null;
2720
+ const pos = anchorPos;
2721
+ anchorPos = null;
2722
+ moved = false;
2723
+ stopAutoScroll();
2724
+ clearMarquee();
2725
+ window.removeEventListener("mousemove", onMouseMove);
2726
+ window.removeEventListener("mouseup", finish, true);
2727
+ window.removeEventListener("blur", finish);
2728
+ window.removeEventListener("pointercancel", finish);
2729
+ window.removeEventListener("keydown", onKeydown, true);
2730
+ if (wasClick && pos !== null) {
2731
+ const caret = gutterClickCaretPos(editorView.state.doc, pos);
2732
+ if (caret !== null) {
2733
+ const tr = editorView.state.tr.setSelection(
2734
+ TextSelection.near(editorView.state.doc.resolve(caret), 1)
2735
+ );
2736
+ tr.setMeta(blockSelectionKey, { blocks: [] });
2737
+ editorView.dispatch(tr);
2738
+ editorView.focus();
2739
+ }
2740
+ }
2741
+ };
2742
+ const onKeydown = (event) => {
2743
+ if (event.key !== "Escape" || event.isComposing) return;
2744
+ event.preventDefault();
2745
+ finish();
2746
+ if (!editorView.isDestroyed) {
2747
+ editorView.dispatch(editorView.state.tr.setSelection(
2748
+ Selection.near(editorView.state.doc.resolve(editorView.state.selection.from))
2749
+ ));
2750
+ }
2751
+ };
2752
+ const onMouseDown = (event) => {
2753
+ var _a, _b;
2754
+ if (event.button !== 0 || !editorView.editable || editorView.composing) return;
2755
+ const target = event.target;
2756
+ const area = editorView.dom.closest(".edit-workspace") ?? marqueeHost;
2757
+ if (!target || !area || !area.contains(target) && !target.contains(area)) return;
2758
+ if ((_a = target == null ? void 0 : target.closest) == null ? void 0 : _a.call(
2759
+ target,
2760
+ 'button, input, textarea, select, a, img, video, audio, [contenteditable="false"], .math-node, .code-lang-menu, .block-grip, .block-plus, .block-chip, .insert-picker, .tbl-handle, .sel-toolbar, .tbl-menu, .tbl-ctx, .block-anchor, .slash-anchor'
2761
+ )) {
2762
+ return;
2763
+ }
2764
+ if ((_b = target == null ? void 0 : target.closest) == null ? void 0 : _b.call(target, "table")) return;
2765
+ if (target.closest(".is-block-selected") && editorView.dom.contains(target)) return;
2766
+ const hit = gutterAreaHit(editorView, event);
2767
+ if (!hit) return;
2768
+ event.preventDefault();
2769
+ event.stopPropagation();
2770
+ editorView.focus();
2771
+ anchorPos = hit.pos;
2772
+ startX = event.clientX;
2773
+ startY = event.clientY;
2774
+ moved = false;
2775
+ window.addEventListener("mousemove", onMouseMove);
2776
+ window.addEventListener("mouseup", finish, true);
2777
+ window.addEventListener("blur", finish);
2778
+ window.addEventListener("pointercancel", finish);
2779
+ window.addEventListener("keydown", onKeydown, true);
2780
+ };
2781
+ document.addEventListener("mousedown", onMouseDown, true);
2782
+ const onGlobalKeydown = (event) => {
2783
+ if (event.key !== "Escape" || event.isComposing) return;
2784
+ if (!getSelectedBlocks(editorView).length) return;
2785
+ event.preventDefault();
2786
+ event.stopPropagation();
2787
+ const { state } = editorView;
2788
+ const tr = state.tr.setSelection(
2789
+ Selection.near(state.doc.resolve(state.selection.from))
2790
+ );
2791
+ tr.setMeta(blockSelectionKey, { blocks: [] });
2792
+ editorView.dispatch(tr);
2793
+ editorView.focus();
2794
+ };
2795
+ window.addEventListener("keydown", onGlobalKeydown, true);
2796
+ const syncNativeHighlight = (view) => {
2797
+ const state = blockSelectionKey.getState(view.state);
2798
+ view.dom.classList.toggle(
2799
+ "is-block-selecting",
2800
+ shouldSuppressNativeHighlight(state == null ? void 0 : state.blocks)
2801
+ );
2802
+ };
2803
+ syncNativeHighlight(editorView);
2804
+ return {
2805
+ update: (view, prevState) => {
2806
+ if (anchorPos !== null && !view.state.doc.eq(prevState.doc)) finish();
2807
+ syncNativeHighlight(view);
2808
+ },
2809
+ destroy: () => {
2810
+ document.removeEventListener("mousedown", onMouseDown, true);
2811
+ window.removeEventListener("keydown", onGlobalKeydown, true);
2812
+ stopAutoScroll();
2813
+ clearMarquee();
2814
+ editorView.dom.classList.remove("is-block-selecting");
2815
+ finish();
2816
+ }
2817
+ };
2818
+ }
2819
+ })
2820
+ ];
2821
+ }
2822
+ });
2823
+ const blockMenuHighlightKey = new PluginKey("ywikiBlockMenuHighlight");
2824
+ const BlockMenuHighlight = Extension.create({
2825
+ name: "ywikiBlockMenuHighlight",
2826
+ addProseMirrorPlugins() {
2827
+ return [
2828
+ new Plugin({
2829
+ key: blockMenuHighlightKey,
2830
+ state: {
2831
+ init: () => null,
2832
+ apply(tr, pos) {
2833
+ const requested = tr.getMeta(blockMenuHighlightKey);
2834
+ if (requested !== void 0) return requested;
2835
+ if (pos === null || !tr.docChanged) return pos;
2836
+ const mapped = tr.mapping.mapResult(pos);
2837
+ return mapped.deleted ? null : mapped.pos;
2838
+ }
2839
+ },
2840
+ props: {
2841
+ decorations(state) {
2842
+ const pos = blockMenuHighlightKey.getState(state);
2843
+ if (pos === null || pos === void 0) return null;
2844
+ const node = state.doc.nodeAt(pos);
2845
+ if (!node) return null;
2846
+ return DecorationSet.create(state.doc, [
2847
+ Decoration.node(pos, pos + node.nodeSize, { class: "is-block-menu-target" })
2848
+ ]);
2849
+ }
2850
+ }
2851
+ })
2852
+ ];
2853
+ }
2854
+ });
2855
+ function highlightMenuBlock(view, pos) {
2856
+ if (!view.isDestroyed && blockMenuHighlightKey.getState(view.state) !== pos) {
2857
+ view.dispatch(view.state.tr.setMeta(blockMenuHighlightKey, pos));
2858
+ }
2859
+ }
2860
+ const key = new PluginKey("ywikiCodeHighlight");
2861
+ function decorate(doc) {
2862
+ const decorations = [];
2863
+ doc.descendants((node, pos) => {
2864
+ if (node.type.name !== "codeBlock") return;
2865
+ for (const token of codeTokens(node.textContent, node.attrs.language)) {
2866
+ decorations.push(Decoration.inline(pos + 1 + token.from, pos + 1 + token.to, { class: token.classes }));
2867
+ }
2868
+ return false;
2869
+ });
2870
+ return DecorationSet.create(doc, decorations);
2871
+ }
2872
+ const CodeHighlight = Extension.create({
2873
+ name: "ywikiCodeHighlight",
2874
+ addProseMirrorPlugins() {
2875
+ return [new Plugin({
2876
+ key,
2877
+ state: {
2878
+ init: (_, state) => decorate(state.doc),
2879
+ apply: (tr, value) => tr.docChanged || tr.getMeta(key) ? decorate(tr.doc) : value
2880
+ },
2881
+ props: { decorations: (state) => key.getState(state) },
2882
+ view(view) {
2883
+ let disposed = false;
2884
+ let requested = false;
2885
+ const load = () => {
2886
+ if (requested) return;
2887
+ let hasCode = false;
2888
+ view.state.doc.descendants((node) => {
2889
+ if (node.type.name === "codeBlock") hasCode = true;
2890
+ });
2891
+ if (!hasCode) return;
2892
+ requested = true;
2893
+ void ensureCodeHighlight().then((ready) => {
2894
+ if (ready && !disposed) view.dispatch(view.state.tr.setMeta(key, true).setMeta("addToHistory", false));
2895
+ });
2896
+ };
2897
+ load();
2898
+ return { update: load, destroy: () => {
2899
+ disposed = true;
2900
+ } };
2901
+ }
2902
+ })];
2903
+ }
2904
+ });
2905
+ const ALLOWED_ATTR = {
2906
+ a: ["href", "title"],
2907
+ img: ["src", "alt", "title", "width", "height", "data-width", "data-align", "data-attachment-id"],
2908
+ td: ["colspan", "rowspan", "colwidth", "data-align", "data-cell-background"],
2909
+ th: ["colspan", "rowspan", "colwidth", "data-align", "data-cell-background"],
2910
+ code: ["class"],
2911
+ pre: ["class", "data-language"],
2912
+ ol: ["start", "data-type"],
2913
+ ul: ["data-type"],
2914
+ li: ["data-type", "data-checked"],
2915
+ div: [
2916
+ "data-video-embed",
2917
+ "data-provider",
2918
+ "data-video-id",
2919
+ "data-page",
2920
+ "data-start-seconds",
2921
+ "data-topic-meta",
2922
+ "data-branch-style",
2923
+ "data-mind-map",
2924
+ "data-map-id",
2925
+ "data-root-id",
2926
+ "data-format-version",
2927
+ "data-layout",
2928
+ "data-theme",
2929
+ "data-height",
2930
+ "data-mind-topic",
2931
+ "data-topic-id",
2932
+ "data-parent-id",
2933
+ "data-order",
2934
+ "data-file-attachment",
2935
+ "data-attachment-id",
2936
+ "attachmentid",
2937
+ "filename",
2938
+ "mimetype",
2939
+ "size",
2940
+ "data-math-block",
2941
+ "data-latex",
2942
+ "data-columns",
2943
+ "data-column",
2944
+ "data-callout",
2945
+ "data-tone",
2946
+ "data-callout-content",
2947
+ "data-callout-icon",
2948
+ "data-callout-textcolor",
2949
+ "data-callout-bordercolor",
2950
+ "data-callout-backgroundcolor"
2951
+ ],
2952
+ span: [
2953
+ "data-mind-description",
2954
+ "data-mind-note",
2955
+ "data-mind-relation",
2956
+ "data-id",
2957
+ "data-source",
2958
+ "data-target",
2959
+ "data-kind",
2960
+ "data-label",
2961
+ "data-page-link",
2962
+ "data-page-id",
2963
+ "pageid",
2964
+ "label",
2965
+ "data-mention",
2966
+ "data-user-id",
2967
+ "userid",
2968
+ "data-math-inline",
2969
+ "data-latex",
2970
+ "data-text-color",
2971
+ "data-color"
2972
+ ],
2973
+ mark: ["data-highlight", "data-color"],
2974
+ p: ["data-indent", "data-text-align"],
2975
+ h1: ["data-indent", "data-text-align"],
2976
+ h2: ["data-indent", "data-text-align"],
2977
+ h3: ["data-indent", "data-text-align"],
2978
+ h4: ["data-indent", "data-text-align"],
2979
+ h5: ["data-indent", "data-text-align"],
2980
+ h6: ["data-indent", "data-text-align"]
2981
+ };
2982
+ const DROP_TAGS = /* @__PURE__ */ new Set([
2983
+ "style",
2984
+ "script",
2985
+ "meta",
2986
+ "link",
2987
+ "iframe",
2988
+ "object",
2989
+ "embed",
2990
+ "form",
2991
+ "input",
2992
+ "button"
2993
+ ]);
2994
+ function sanitizeElement(el) {
2995
+ for (const child of Array.from(el.children)) {
2996
+ if (child.hasAttribute("data-callout-decoration")) {
2997
+ child.remove();
2998
+ continue;
2999
+ }
3000
+ if (DROP_TAGS.has(child.tagName.toLowerCase())) {
3001
+ child.remove();
3002
+ continue;
3003
+ }
3004
+ child.removeAttribute("style");
3005
+ const languageClass = /(?:^|\s)(language-[\w+#-]+)/.exec(child.className);
3006
+ child.removeAttribute("class");
3007
+ if (["code", "pre"].includes(child.tagName.toLowerCase()) && languageClass) {
3008
+ child.setAttribute("class", languageClass[1]);
3009
+ }
3010
+ child.removeAttribute("id");
3011
+ for (const attr of Array.from(child.attributes)) {
3012
+ const name = attr.name.toLowerCase();
3013
+ if (name.startsWith("on")) {
3014
+ child.removeAttribute(attr.name);
3015
+ continue;
3016
+ }
3017
+ const allowed = ALLOWED_ATTR[child.tagName.toLowerCase()];
3018
+ if (!allowed || !allowed.includes(name)) {
3019
+ child.removeAttribute(attr.name);
3020
+ }
3021
+ }
3022
+ if (child.tagName.toLowerCase() === "a") {
3023
+ const href = child.getAttribute("href") ?? "";
3024
+ const low = href.trim().toLowerCase();
3025
+ if (low.startsWith("javascript:") || low.startsWith("data:") || low.startsWith("vbscript:")) {
3026
+ child.removeAttribute("href");
3027
+ }
3028
+ }
3029
+ sanitizeElement(child);
3030
+ }
3031
+ }
3032
+ const PasteSanitizer = Extension.create({
3033
+ name: "ywikiPasteSanitizer",
3034
+ addProseMirrorPlugins() {
3035
+ return [
3036
+ new Plugin({
3037
+ props: {
3038
+ transformPastedHTML(html) {
3039
+ const container = document.createElement("div");
3040
+ container.innerHTML = html;
3041
+ sanitizeElement(container);
3042
+ return container.innerHTML;
3043
+ }
3044
+ }
3045
+ })
3046
+ ];
3047
+ }
3048
+ });
3049
+ function pastedImageFiles(event) {
3050
+ var _a;
3051
+ const items = (_a = event.clipboardData) == null ? void 0 : _a.items;
3052
+ if (!items) return [];
3053
+ const files = [];
3054
+ for (const item of Array.from(items)) {
3055
+ if (item.kind === "file" && item.type.startsWith("image/")) {
3056
+ const f = item.getAsFile();
3057
+ if (f) files.push(f);
3058
+ }
3059
+ }
3060
+ return files;
3061
+ }
3062
+ function pastedRemoteImageUrls(event) {
3063
+ var _a;
3064
+ const html = ((_a = event.clipboardData) == null ? void 0 : _a.getData("text/html")) ?? "";
3065
+ if (!html) return [];
3066
+ const container = document.createElement("div");
3067
+ container.innerHTML = html;
3068
+ const urls = /* @__PURE__ */ new Set();
3069
+ for (const img of Array.from(container.querySelectorAll("img"))) {
3070
+ const src = (img.getAttribute("src") ?? "").trim();
3071
+ if (/^https?:\/\//i.test(src)) urls.add(src);
3072
+ }
3073
+ return [...urls];
3074
+ }
3075
+ const inlineCommentPluginKey = new PluginKey(
3076
+ "ywikiInlineComments"
3077
+ );
3078
+ function textNodePositions(doc) {
3079
+ const positions = [];
3080
+ doc.descendants((node, pos) => {
3081
+ var _a;
3082
+ if (node.isText && (((_a = node.text) == null ? void 0 : _a.length) ?? 0) > 0) positions.push(pos);
3083
+ });
3084
+ return positions;
3085
+ }
3086
+ function buildDecorations(doc, comments, activeId) {
3087
+ const { marks } = resolveInlineComments(
3088
+ doc.toJSON(),
3089
+ comments
3090
+ );
3091
+ if (marks.length === 0) return DecorationSet.empty;
3092
+ const positions = textNodePositions(doc);
3093
+ const decorations = [];
3094
+ for (const mark of marks) {
3095
+ const base = positions[mark.textIndex];
3096
+ if (base === void 0) continue;
3097
+ const from = base + mark.from;
3098
+ const to = base + mark.to;
3099
+ if (to <= from) {
3100
+ decorations.push(
3101
+ Decoration.widget(from, () => {
3102
+ const el = document.createElement("span");
3103
+ el.className = "doc-inline-comment is-editor is-shifted";
3104
+ el.setAttribute("data-comment-id", mark.commentId);
3105
+ el.setAttribute("data-comment-hint", "此处有讨论(原文已改动)");
3106
+ return el;
3107
+ })
3108
+ );
3109
+ continue;
3110
+ }
3111
+ const classes = ["doc-inline-comment", "is-editor"];
3112
+ if (mark.hasReplies) classes.push("has-replies");
3113
+ if (activeId === mark.commentId) classes.push("is-active");
3114
+ decorations.push(
3115
+ Decoration.inline(from, to, {
3116
+ class: classes.join(" "),
3117
+ "data-comment-id": mark.commentId,
3118
+ // 同上:不用原生 title(浏览器灰框、样式不可控)
3119
+ "data-comment-hint": "点击查看该处讨论"
3120
+ })
3121
+ );
3122
+ }
3123
+ return DecorationSet.create(doc, decorations);
3124
+ }
3125
+ const InlineCommentHighlight = Extension.create({
3126
+ name: "ywikiInlineCommentHighlight",
3127
+ addProseMirrorPlugins() {
3128
+ const options = this.options;
3129
+ return [
3130
+ new Plugin({
3131
+ key: inlineCommentPluginKey,
3132
+ state: {
3133
+ init: () => ({ comments: options.getComments() }),
3134
+ apply: (tr, value) => {
3135
+ const meta = tr.getMeta(inlineCommentPluginKey);
3136
+ return meta ?? value;
3137
+ }
3138
+ },
3139
+ props: {
3140
+ decorations(state) {
3141
+ var _a;
3142
+ const value = inlineCommentPluginKey.getState(state);
3143
+ if (!value || value.comments.length === 0) return null;
3144
+ return buildDecorations(
3145
+ state.doc,
3146
+ value.comments,
3147
+ ((_a = options.getActiveId) == null ? void 0 : _a.call(options)) ?? null
3148
+ );
3149
+ },
3150
+ /**
3151
+ * 点击高亮 → 定位下方讨论。
3152
+ * decoration 不是 mark,`resolve(pos)` 里查不到它,因此从事件目标
3153
+ * 上读 `data-comment-id`(这个属性由上面的 decoration 生成)。
3154
+ */
3155
+ handleClick(_view, _pos, event) {
3156
+ var _a;
3157
+ const target = event.target;
3158
+ const el = target == null ? void 0 : target.closest("[data-comment-id]");
3159
+ const id = el == null ? void 0 : el.getAttribute("data-comment-id");
3160
+ if (!id) return false;
3161
+ hideCommentHint();
3162
+ (_a = options.onMarkClick) == null ? void 0 : _a.call(options, id);
3163
+ return true;
3164
+ },
3165
+ /**
3166
+ * 悬停评论标记 → 自绘提示。
3167
+ *
3168
+ * 不用原生 `title`:浏览器绘制的灰框圆角在浅色文档里很突兀,
3169
+ * 也无法统一主题(用户实测反馈:"这个提示不用 Tooltip 用的啥?")。
3170
+ * decoration 是运行时插入的 DOM,Vue 的 `WTooltip` 包不住每一处
3171
+ * 标记,因此按 `data-comment-hint` 自绘(见 shared/comment-hint)。
3172
+ */
3173
+ handleDOMEvents: {
3174
+ mouseover(_view, event) {
3175
+ const mouse = event;
3176
+ const target = mouse.target;
3177
+ const el = target == null ? void 0 : target.closest("[data-comment-hint]");
3178
+ if (!(el instanceof HTMLElement)) return false;
3179
+ const text = el.getAttribute("data-comment-hint");
3180
+ if (text) showCommentHint(el, text);
3181
+ return false;
3182
+ },
3183
+ mouseout(_view, event) {
3184
+ const mouse = event;
3185
+ const target = mouse.target;
3186
+ const el = target == null ? void 0 : target.closest("[data-comment-hint]");
3187
+ if (!el) return false;
3188
+ const related = mouse.relatedTarget;
3189
+ if (related && el.contains(related)) return false;
3190
+ hideCommentHint();
3191
+ return false;
3192
+ }
3193
+ }
3194
+ }
3195
+ })
3196
+ ];
3197
+ }
3198
+ });
3199
+ function refreshInlineComments(editor, comments) {
3200
+ const tr = editor.state.tr.setMeta(inlineCommentPluginKey, { comments });
3201
+ editor.view.dispatch(tr);
3202
+ }
3203
+ new PluginKey("ywiki-collab-cursor");
3204
+ function buildCursor(user) {
3205
+ const color = user.color || "#2f6feb";
3206
+ const name = user.name || "匿名";
3207
+ const cursor = document.createElement("span");
3208
+ cursor.classList.add("ProseMirror-yjs-cursor");
3209
+ cursor.style.setProperty("--yjs-cursor-color", color);
3210
+ const caret = document.createElement("span");
3211
+ caret.classList.add("ywiki-collab-caret");
3212
+ caret.style.backgroundColor = color;
3213
+ const label = document.createElement("span");
3214
+ label.classList.add("ywiki-collab-caret-label");
3215
+ label.style.backgroundColor = color;
3216
+ label.style.color = readableTextColor(color);
3217
+ label.textContent = name;
3218
+ cursor.appendChild(caret);
3219
+ cursor.appendChild(label);
3220
+ return cursor;
3221
+ }
3222
+ const CollabCursor = Extension.create({
3223
+ name: "collabCursor",
3224
+ addOptions() {
3225
+ return { awareness: null };
3226
+ },
3227
+ addProseMirrorPlugins() {
3228
+ if (!this.options.awareness) return [];
3229
+ return [
3230
+ yCursorPlugin(this.options.awareness, {
3231
+ cursorBuilder: (user) => buildCursor(user)
3232
+ })
3233
+ ];
3234
+ }
3235
+ });
3236
+ const SlashKey = new PluginKey("ywiki-slash");
3237
+ function dismissSlash(view) {
3238
+ const { $from } = view.state.selection;
3239
+ const index = $from.parent.textBetween(0, $from.parentOffset).lastIndexOf("/");
3240
+ view.dispatch(view.state.tr.setMeta(SlashKey, {
3241
+ dismissedFrom: index >= 0 ? $from.start() + index : null
3242
+ }));
3243
+ }
3244
+ const SlashCommand = Extension.create({
3245
+ name: "ywikiSlashCommand",
3246
+ addOptions() {
3247
+ return {
3248
+ onOpen: () => void 0,
3249
+ onUpdate: () => void 0,
3250
+ onClose: () => void 0
3251
+ };
3252
+ },
3253
+ addProseMirrorPlugins() {
3254
+ const options = this.options;
3255
+ return [
3256
+ new Plugin({
3257
+ key: SlashKey,
3258
+ state: {
3259
+ init: () => ({ dismissedFrom: null }),
3260
+ apply: (tr, value) => {
3261
+ const meta = tr.getMeta(SlashKey);
3262
+ if (meta) return meta;
3263
+ if (value.dismissedFrom === null) return value;
3264
+ const result = tr.mapping.mapResult(value.dismissedFrom, 1);
3265
+ return { dismissedFrom: result.deleted ? null : result.pos };
3266
+ }
3267
+ },
3268
+ view: () => {
3269
+ let openFrom = null;
3270
+ return {
3271
+ update: (view, prevState) => {
3272
+ var _a;
3273
+ if (shouldIgnoreForIme({ isComposing: false })) return;
3274
+ if (view.state.doc.eq(prevState.doc) && view.state.selection.eq(prevState.selection) && SlashKey.getState(view.state) === SlashKey.getState(prevState)) {
3275
+ return;
3276
+ }
3277
+ const { $from, empty } = view.state.selection;
3278
+ if (!view.editable || view.composing || !empty || $from.parent.type.spec.code) {
3279
+ if (openFrom !== null) {
3280
+ openFrom = null;
3281
+ options.onClose();
3282
+ }
3283
+ return;
3284
+ }
3285
+ const textBefore = $from.parent.textBetween(
3286
+ 0,
3287
+ $from.parentOffset,
3288
+ void 0,
3289
+ ""
3290
+ );
3291
+ const slashIndex = textBefore.lastIndexOf("/");
3292
+ const isSlashAtBoundary = slashIndex >= 0 && (slashIndex === 0 || /\s/.test(textBefore[slashIndex - 1]));
3293
+ if (isSlashAtBoundary) {
3294
+ const from = $from.start() + slashIndex;
3295
+ const query = textBefore.slice(slashIndex + 1);
3296
+ if (((_a = SlashKey.getState(view.state)) == null ? void 0 : _a.dismissedFrom) === from) {
3297
+ if (openFrom !== null) options.onClose();
3298
+ openFrom = null;
3299
+ return;
3300
+ }
3301
+ if (/\s/.test(query)) {
3302
+ if (openFrom !== null) {
3303
+ openFrom = null;
3304
+ options.onClose();
3305
+ }
3306
+ return;
3307
+ }
3308
+ if (openFrom !== from) {
3309
+ openFrom = from;
3310
+ options.onOpen({ from, query });
3311
+ } else {
3312
+ options.onUpdate(query);
3313
+ }
3314
+ } else if (openFrom !== null) {
3315
+ openFrom = null;
3316
+ options.onClose();
3317
+ }
3318
+ },
3319
+ destroy: () => {
3320
+ if (openFrom !== null) {
3321
+ openFrom = null;
3322
+ options.onClose();
3323
+ }
3324
+ }
3325
+ };
3326
+ },
3327
+ props: {
3328
+ handleKeyDown(_view, event) {
3329
+ if (shouldIgnoreForIme(event)) return false;
3330
+ return false;
3331
+ }
3332
+ }
3333
+ })
3334
+ ];
3335
+ }
3336
+ });
3337
+ function isEmptyLineBlock(typeName, contentSize) {
3338
+ return contentSize === 0 && (typeName === "paragraph" || typeName === "heading");
3339
+ }
3340
+ const BlockHandleKey = new PluginKey("ywiki-block-handle");
3341
+ const HANDLE_GUTTER_PX = 64;
3342
+ function resolveTopLevelBlock(view, event, host) {
3343
+ const contentRoot = view.dom;
3344
+ let blockPos = null;
3345
+ let blockNode = null;
3346
+ let blockEl = null;
3347
+ let el = event.target instanceof HTMLElement ? event.target : null;
3348
+ while (el && el.parentElement && el.parentElement !== contentRoot) {
3349
+ el = el.parentElement;
3350
+ }
3351
+ if (el && el.parentElement === contentRoot) {
3352
+ try {
3353
+ const inside = view.posAtDOM(el, 0);
3354
+ const $inside = view.state.doc.resolve(inside);
3355
+ const start = $inside.depth > 0 ? $inside.before(1) : inside;
3356
+ const node = view.state.doc.nodeAt(start);
3357
+ if (node) {
3358
+ blockPos = start;
3359
+ blockNode = node;
3360
+ blockEl = el;
3361
+ }
3362
+ } catch {
3363
+ }
3364
+ }
3365
+ if (blockPos === null || !blockNode) {
3366
+ const coords = view.posAtCoords({ left: event.clientX, top: event.clientY });
3367
+ if (!coords) return null;
3368
+ const raw = coords.pos;
3369
+ if (typeof raw !== "number" || raw < 0) return null;
3370
+ const pos = Math.min(raw, view.state.doc.content.size);
3371
+ view.state.doc.forEach((node, offset) => {
3372
+ if (blockPos !== null) return;
3373
+ if (pos >= offset && pos < offset + node.nodeSize) {
3374
+ blockPos = offset;
3375
+ blockNode = node;
3376
+ }
3377
+ });
3378
+ if (blockPos === null && view.state.doc.childCount > 0) {
3379
+ const last = view.state.doc.lastChild;
3380
+ blockPos = view.state.doc.content.size - last.nodeSize;
3381
+ blockNode = last;
3382
+ }
3383
+ if (blockPos === null || !blockNode) return null;
3384
+ }
3385
+ const HANDLE_HEIGHT = 26;
3386
+ const hostRect = host.getBoundingClientRect();
3387
+ let top = event.clientY;
3388
+ let contentLeft = contentRoot.getBoundingClientRect().left - hostRect.left;
3389
+ try {
3390
+ const dom = blockEl ?? view.nodeDOM(blockPos);
3391
+ if (dom instanceof HTMLElement) {
3392
+ const domRect = dom.getBoundingClientRect();
3393
+ let firstTop = domRect.top;
3394
+ let firstHeight = domRect.height;
3395
+ try {
3396
+ const range = document.createRange();
3397
+ range.selectNodeContents(dom);
3398
+ const lines = Array.from(range.getClientRects()).filter(
3399
+ (r) => r.height > 0.5
3400
+ );
3401
+ if (lines.length > 0) {
3402
+ firstTop = lines[0].top;
3403
+ firstHeight = lines[0].height;
3404
+ }
3405
+ } catch {
3406
+ }
3407
+ top = firstTop + firstHeight / 2 - HANDLE_HEIGHT / 2 - hostRect.top;
3408
+ }
3409
+ } catch {
3410
+ top = event.clientY - hostRect.top;
3411
+ }
3412
+ return {
3413
+ pos: blockPos,
3414
+ type: blockNode.type.name,
3415
+ top,
3416
+ /** 块内容左缘(相对容器):供 EditorPane 把手柄精确贴在正文左侧 */
3417
+ contentLeft,
3418
+ isEmpty: isEmptyLineBlock(blockNode.type.name, blockNode.content.size)
3419
+ };
3420
+ }
3421
+ const BlockHandle = Extension.create({
3422
+ name: "ywikiBlockHandle",
3423
+ addOptions() {
3424
+ return { onHover: () => void 0 };
3425
+ },
3426
+ addProseMirrorPlugins() {
3427
+ const options = this.options;
3428
+ return [
3429
+ new Plugin({
3430
+ key: BlockHandleKey,
3431
+ view: (editorView) => {
3432
+ let lastPos = null;
3433
+ let lastTop = Number.NaN;
3434
+ const host = editorView.dom.closest(".editor-shell") ?? editorView.dom.parentElement;
3435
+ const report = (info) => {
3436
+ if (!info) {
3437
+ if (lastPos !== null) {
3438
+ lastPos = null;
3439
+ lastTop = Number.NaN;
3440
+ options.onHover(null);
3441
+ }
3442
+ return;
3443
+ }
3444
+ if (info.pos !== lastPos || Math.abs(info.top - lastTop) > 0.5) {
3445
+ lastPos = info.pos;
3446
+ lastTop = info.top;
3447
+ options.onHover(info);
3448
+ }
3449
+ };
3450
+ const onMove = (event) => {
3451
+ if (!editorView.editable) {
3452
+ report(null);
3453
+ return;
3454
+ }
3455
+ if (!host) return;
3456
+ const rect = editorView.dom.getBoundingClientRect();
3457
+ const inContent = event.clientX >= rect.left && event.clientX <= rect.right;
3458
+ const inGutter = event.clientX < rect.left && event.clientX >= rect.left - HANDLE_GUTTER_PX;
3459
+ const inRows = event.clientY >= rect.top - 8 && event.clientY <= rect.bottom + 8;
3460
+ if (!inContent && !inGutter || !inRows) {
3461
+ report(null);
3462
+ return;
3463
+ }
3464
+ let info = null;
3465
+ try {
3466
+ info = resolveTopLevelBlock(editorView, event, host);
3467
+ } catch {
3468
+ info = null;
3469
+ }
3470
+ if (inGutter && !info) return;
3471
+ report(info);
3472
+ };
3473
+ const onLeave = () => {
3474
+ report(null);
3475
+ };
3476
+ document.addEventListener("mousemove", onMove);
3477
+ document.addEventListener("mouseleave", onLeave);
3478
+ return {
3479
+ destroy: () => {
3480
+ document.removeEventListener("mousemove", onMove);
3481
+ document.removeEventListener("mouseleave", onLeave);
3482
+ }
3483
+ };
3484
+ }
3485
+ })
3486
+ ];
3487
+ }
3488
+ });
3489
+ function insertUploadedBlock(tr, pos, node) {
3490
+ const $pos = tr.doc.resolve(pos);
3491
+ if (node.isBlock && $pos.depth > 0 && $pos.parent.type.name === "paragraph" && $pos.parent.content.size === 0) {
3492
+ const from = $pos.before(), to = $pos.after();
3493
+ tr.replaceWith(from, to, node);
3494
+ return { replacedEmpty: true, nextPos: from + node.nodeSize };
3495
+ }
3496
+ tr.insert(pos, node);
3497
+ return { replacedEmpty: false, nextPos: tr.mapping.map(pos, 1) };
3498
+ }
3499
+ const uploadPlaceholderKey = new PluginKey(
3500
+ "ywikiUploadPlaceholder"
3501
+ );
3502
+ function blockStartAt(doc, index) {
3503
+ let start = 0;
3504
+ for (let i = 0; i < index && i < doc.childCount; i += 1) start += doc.child(i).nodeSize;
3505
+ return start;
3506
+ }
3507
+ function anchorAt(doc, pos) {
3508
+ const clamped = Math.max(0, Math.min(pos, doc.content.size));
3509
+ let blockIndex = -1;
3510
+ let blockStart = 0;
3511
+ doc.forEach((node, offset, index) => {
3512
+ if (blockIndex >= 0) return;
3513
+ if (clamped >= offset && clamped < offset + node.nodeSize) {
3514
+ blockIndex = index;
3515
+ blockStart = offset;
3516
+ }
3517
+ });
3518
+ if (blockIndex < 0 && doc.childCount > 0) {
3519
+ blockIndex = doc.childCount - 1;
3520
+ blockStart = doc.content.size - doc.child(doc.childCount - 1).nodeSize;
3521
+ }
3522
+ if (blockIndex < 0) {
3523
+ return { pos: clamped, blockIndex: 0, offset: 0, blockText: "", blockCount: doc.childCount, blockJSON: "", relative: 0 };
3524
+ }
3525
+ const block = doc.child(blockIndex);
3526
+ return {
3527
+ pos: clamped,
3528
+ blockIndex,
3529
+ offset: Math.max(0, clamped - (blockStart + 1)),
3530
+ blockText: block.textContent,
3531
+ blockCount: doc.childCount,
3532
+ blockJSON: JSON.stringify(block.toJSON()),
3533
+ relative: clamped - blockStart
3534
+ };
3535
+ }
3536
+ function resolveAnchor(doc, anchor) {
3537
+ if (doc.childCount === 0) return null;
3538
+ const matches = [];
3539
+ doc.forEach((node2, _offset, index) => {
3540
+ if (JSON.stringify(node2.toJSON()) === anchor.blockJSON) matches.push(index);
3541
+ });
3542
+ if (matches.length !== 1) return null;
3543
+ const start = blockStartAt(doc, matches[0]);
3544
+ const node = doc.child(matches[0]);
3545
+ return start + Math.max(0, Math.min(anchor.relative, node.nodeSize));
3546
+ }
3547
+ function advancePlaceholders(doc, items, mapping, recoverDeleted = false) {
3548
+ const next = [];
3549
+ const lost = [];
3550
+ for (const item of items) {
3551
+ const res = mapping.mapResult(item.anchor.pos, 1);
3552
+ if (!res.deleted) {
3553
+ next.push({ ...item, anchor: anchorAt(doc, res.pos) });
3554
+ continue;
3555
+ }
3556
+ const resolved = recoverDeleted ? resolveAnchor(doc, item.anchor) : null;
3557
+ if (resolved === null) {
3558
+ lost.push(item.id);
3559
+ continue;
3560
+ }
3561
+ next.push({ ...item, anchor: anchorAt(doc, resolved) });
3562
+ }
3563
+ return { items: next, lost };
3564
+ }
3565
+ const livePreviewUrls = /* @__PURE__ */ new Set();
3566
+ function releasePreview(url) {
3567
+ if (!url || !livePreviewUrls.delete(url)) return;
3568
+ if (typeof URL.revokeObjectURL === "function") URL.revokeObjectURL(url);
3569
+ }
3570
+ function releaseAllPreviews() {
3571
+ for (const url of [...livePreviewUrls]) releasePreview(url);
3572
+ }
3573
+ function createPreview(file, kind) {
3574
+ if (!file || kind !== "image" || typeof URL.createObjectURL !== "function") return void 0;
3575
+ try {
3576
+ const url = URL.createObjectURL(file);
3577
+ livePreviewUrls.add(url);
3578
+ return url;
3579
+ } catch {
3580
+ return void 0;
3581
+ }
3582
+ }
3583
+ function placeholderDom(item, options) {
3584
+ const el = document.createElement("span");
3585
+ el.className = "ywiki-upload-placeholder";
3586
+ el.setAttribute("data-upload-id", item.id);
3587
+ const spinner = document.createElement("span");
3588
+ spinner.className = "ywiki-upload-spinner";
3589
+ spinner.setAttribute("aria-hidden", "true");
3590
+ const label = document.createElement("span");
3591
+ label.className = "ywiki-upload-label";
3592
+ label.textContent = item.kind === "image" ? `正在上传图片:${item.label}` : `正在上传:${item.label}`;
3593
+ const cancel = document.createElement("button");
3594
+ cancel.type = "button";
3595
+ cancel.className = "ywiki-upload-cancel";
3596
+ cancel.title = "取消上传";
3597
+ cancel.setAttribute("aria-label", "取消上传");
3598
+ cancel.textContent = "×";
3599
+ cancel.addEventListener("mousedown", (e) => {
3600
+ e.preventDefault();
3601
+ e.stopPropagation();
3602
+ });
3603
+ cancel.addEventListener("click", (e) => {
3604
+ var _a;
3605
+ e.preventDefault();
3606
+ e.stopPropagation();
3607
+ (_a = options.onCancel) == null ? void 0 : _a.call(options, item.id);
3608
+ });
3609
+ if (item.previewUrl) {
3610
+ el.classList.add("has-preview");
3611
+ const preview = document.createElement("img");
3612
+ preview.className = "ywiki-upload-preview";
3613
+ preview.setAttribute("alt", "");
3614
+ preview.setAttribute("aria-hidden", "true");
3615
+ preview.setAttribute("draggable", "false");
3616
+ preview.src = item.previewUrl;
3617
+ el.title = item.label;
3618
+ el.append(preview, spinner, cancel);
3619
+ return el;
3620
+ }
3621
+ el.append(spinner, label, cancel);
3622
+ return el;
3623
+ }
3624
+ const UploadPlaceholderExtension = Extension.create({
3625
+ name: "ywikiUploadPlaceholder",
3626
+ addOptions() {
3627
+ return { onLost: () => void 0, onCancel: () => void 0 };
3628
+ },
3629
+ addProseMirrorPlugins() {
3630
+ const options = this.options;
3631
+ return [
3632
+ new Plugin({
3633
+ key: uploadPlaceholderKey,
3634
+ state: {
3635
+ init: () => ({ items: [] }),
3636
+ apply: (tr, value) => {
3637
+ var _a;
3638
+ const meta = tr.getMeta(uploadPlaceholderKey);
3639
+ if (meta == null ? void 0 : meta.clear) {
3640
+ for (const item of value.items) releasePreview(item.previewUrl);
3641
+ return { items: [] };
3642
+ }
3643
+ const removed = /* @__PURE__ */ new Set([...(meta == null ? void 0 : meta.remove) ? [meta.remove] : [], ...(meta == null ? void 0 : meta.removeMany) ?? []]);
3644
+ const remaining = removed.size === 0 ? value.items : value.items.filter((i) => !removed.has(i.id));
3645
+ if (removed.size > 0) {
3646
+ for (const item of value.items) {
3647
+ if (removed.has(item.id)) releasePreview(item.previewUrl);
3648
+ }
3649
+ }
3650
+ const moved = (meta == null ? void 0 : meta.reanchor) ? remaining.filter(
3651
+ (i) => i.anchor.pos === meta.reanchor.from
3652
+ ).map((i) => ({ ...i, anchor: anchorAt(tr.doc, meta.reanchor.to) })) : [];
3653
+ const unmoved = remaining.filter((i) => !(meta == null ? void 0 : meta.reanchor) || i.anchor.pos !== meta.reanchor.from);
3654
+ const advanced = tr.docChanged ? advancePlaceholders(tr.doc, unmoved, tr.mapping, !!tr.getMeta("y-sync$")) : { items: unmoved, lost: [] };
3655
+ advanced.items.push(...moved);
3656
+ for (const id of advanced.lost) {
3657
+ const lost = value.items.find((i) => i.id === id);
3658
+ releasePreview(lost == null ? void 0 : lost.previewUrl);
3659
+ (_a = options.onLost) == null ? void 0 : _a.call(options, id);
3660
+ }
3661
+ if (meta) {
3662
+ if (meta.add) return { items: [...advanced.items, meta.add] };
3663
+ }
3664
+ return { items: advanced.items };
3665
+ }
3666
+ },
3667
+ view: () => ({
3668
+ destroy: () => releaseAllPreviews()
3669
+ }),
3670
+ props: {
3671
+ decorations(state) {
3672
+ const value = uploadPlaceholderKey.getState(state);
3673
+ if (!value || value.items.length === 0) return null;
3674
+ return DecorationSet.create(
3675
+ state.doc,
3676
+ value.items.map((item) => {
3677
+ const widget = Decoration.widget(item.anchor.pos, () => placeholderDom(item, options), {
3678
+ key: item.id,
3679
+ side: 1
3680
+ });
3681
+ return widget;
3682
+ })
3683
+ );
3684
+ }
3685
+ }
3686
+ })
3687
+ ];
3688
+ }
3689
+ });
3690
+ function addUploadPlaceholder(view, item) {
3691
+ const anchor = anchorAt(view.state.doc, item.pos);
3692
+ const previewUrl = createPreview(item.file, item.kind);
3693
+ view.dispatch(
3694
+ view.state.tr.setMeta(uploadPlaceholderKey, {
3695
+ add: {
3696
+ id: item.id,
3697
+ label: item.label,
3698
+ kind: item.kind,
3699
+ anchor,
3700
+ ...previewUrl ? { previewUrl } : {}
3701
+ }
3702
+ })
3703
+ );
3704
+ }
3705
+ function removeUploadPlaceholder(view, id) {
3706
+ const value = uploadPlaceholderKey.getState(view.state);
3707
+ if (!(value == null ? void 0 : value.items.some((i) => i.id === id))) return;
3708
+ view.dispatch(view.state.tr.setMeta(uploadPlaceholderKey, { remove: id }));
3709
+ }
3710
+ function replacePlaceholderWithNode(view, id, node) {
3711
+ const pos = currentPlaceholderPos(view, id);
3712
+ if (pos === null || !view.editable || view.isDestroyed) return false;
3713
+ const nodeType = view.state.schema.nodes[node.type];
3714
+ if (!nodeType) return false;
3715
+ const created = nodeType.create(node.attrs ?? null);
3716
+ const tr = view.state.tr;
3717
+ let reanchor;
3718
+ try {
3719
+ const inserted = insertUploadedBlock(tr, pos, created);
3720
+ if (inserted.replacedEmpty) reanchor = { from: pos, to: inserted.nextPos };
3721
+ } catch {
3722
+ tr.setMeta(uploadPlaceholderKey, { remove: id });
3723
+ view.dispatch(tr);
3724
+ return false;
3725
+ }
3726
+ tr.setMeta(uploadPlaceholderKey, { remove: id, reanchor });
3727
+ view.dispatch(tr);
3728
+ return true;
3729
+ }
3730
+ function currentPlaceholderPos(view, id) {
3731
+ const value = uploadPlaceholderKey.getState(view.state);
3732
+ const item = value == null ? void 0 : value.items.find((i) => i.id === id);
3733
+ return item ? item.anchor.pos : null;
3734
+ }
3735
+ function selectTableCell(view, pos) {
3736
+ var _a, _b;
3737
+ if (pos < 0 || pos >= view.state.doc.content.size || !["cell", "header_cell"].includes(((_a = view.state.doc.nodeAt(pos)) == null ? void 0 : _a.type.spec.tableRole) ?? "")) return;
3738
+ const sel = view.state.selection;
3739
+ let included = false;
3740
+ (_b = sel.forEachCell) == null ? void 0 : _b.call(sel, (_node, cellPos) => {
3741
+ if (cellPos === pos) included = true;
3742
+ });
3743
+ if (!included) view.dispatch(view.state.tr.setSelection(
3744
+ Selection.near(view.state.doc.resolve(pos + 1))
3745
+ ));
3746
+ }
3747
+ function cellPositionAtDOM(view, cell) {
3748
+ try {
3749
+ const $pos = view.state.doc.resolve(view.posAtDOM(cell, 0));
3750
+ for (let d = $pos.depth; d > 0; d -= 1) {
3751
+ if (["cell", "header_cell"].includes($pos.node(d).type.spec.tableRole ?? "")) {
3752
+ return $pos.before(d);
3753
+ }
3754
+ }
3755
+ } catch {
3756
+ }
3757
+ return null;
3758
+ }
3759
+ function selectTableAxis(view, cellPos, axis, index) {
3760
+ if (!view.editable) return false;
3761
+ try {
3762
+ const $cell = view.state.doc.resolve(cellPos);
3763
+ const table = $cell.node(-1);
3764
+ if (table.type.spec.tableRole !== "table") return false;
3765
+ const start = $cell.start(-1);
3766
+ const map = TableMap.get(table);
3767
+ const rect = map.findCell(cellPos - start);
3768
+ if (index !== void 0) {
3769
+ const limit = axis === "row" ? map.height : map.width;
3770
+ if (!Number.isInteger(index) || index < 0 || index >= limit) return false;
3771
+ if (axis === "row") {
3772
+ rect.top = index;
3773
+ rect.bottom = index + 1;
3774
+ }
3775
+ if (axis === "column") {
3776
+ rect.left = index;
3777
+ rect.right = index + 1;
3778
+ }
3779
+ }
3780
+ const first = axis === "row" ? rect.top * map.width : axis === "column" ? rect.left : 0;
3781
+ const last = axis === "row" ? (rect.bottom - 1) * map.width + map.width - 1 : axis === "column" ? (map.height - 1) * map.width + rect.right - 1 : map.map.length - 1;
3782
+ const selection = CellSelection.create(view.state.doc, start + map.map[first], start + map.map[last]);
3783
+ view.dispatch(view.state.tr.setSelection(selection));
3784
+ view.focus();
3785
+ return true;
3786
+ } catch {
3787
+ return false;
3788
+ }
3789
+ }
3790
+ function tablePositionAtCell(view, cellPos) {
3791
+ try {
3792
+ const $cell = view.state.doc.resolve(cellPos);
3793
+ return $cell.node(-1).type.spec.tableRole === "table" ? $cell.before(-1) : null;
3794
+ } catch {
3795
+ return null;
3796
+ }
3797
+ }
3798
+ function currentTableCellPosition(view) {
3799
+ const selection = view.state.selection;
3800
+ if (selection instanceof CellSelection) return selection.$anchorCell.pos;
3801
+ for (let depth = selection.$from.depth; depth > 0; depth -= 1) {
3802
+ if (["cell", "header_cell"].includes(selection.$from.node(depth).type.spec.tableRole ?? "")) {
3803
+ return selection.$from.before(depth);
3804
+ }
3805
+ }
3806
+ return null;
3807
+ }
3808
+ function insertTableBoundary(editor, cellPos, axis, index) {
3809
+ if (!editor.isEditable) return false;
3810
+ const pos = tablePositionAtCell(editor.view, cellPos);
3811
+ if (pos === null) return false;
3812
+ const table = editor.state.doc.nodeAt(pos);
3813
+ const map = TableMap.get(table);
3814
+ const limit = axis === "row" ? map.height : map.width;
3815
+ if (!Number.isInteger(index) || index < 0 || index > limit) return false;
3816
+ const rect = { map, table, tableStart: pos + 1, left: 0, top: 0, right: map.width, bottom: map.height };
3817
+ const tr = editor.state.tr.setSelection(Selection.near(editor.state.doc.resolve(cellPos + 1)));
3818
+ if (axis === "row") addRow(tr, rect, index);
3819
+ else addColumn(tr, rect, index);
3820
+ editor.view.dispatch(tr);
3821
+ editor.view.focus();
3822
+ return true;
3823
+ }
3824
+ function tableSelectionMarkActive(editor, name) {
3825
+ const mark = editor.state.schema.marks[name];
3826
+ const selection = editor.state.selection;
3827
+ if (!mark || !(selection instanceof CellSelection)) return false;
3828
+ let hasText = false;
3829
+ let allMarked = true;
3830
+ selection.forEachCell((cell) => cell.descendants((node) => {
3831
+ if (node.isText) {
3832
+ hasText = true;
3833
+ if (!mark.isInSet(node.marks)) allMarked = false;
3834
+ }
3835
+ }));
3836
+ return hasText && allMarked;
3837
+ }
3838
+ function setTableSelectionMark(editor, name, attrs) {
3839
+ const mark = editor.state.schema.marks[name];
3840
+ const selection = editor.state.selection;
3841
+ if (!editor.isEditable || !mark || !(selection instanceof CellSelection)) return false;
3842
+ const tr = editor.state.tr;
3843
+ selection.forEachCell((cell, pos) => {
3844
+ const from = pos + 1, to = pos + cell.nodeSize - 1;
3845
+ if (attrs === null) tr.removeMark(from, to, mark);
3846
+ else tr.addMark(from, to, mark.create(attrs));
3847
+ });
3848
+ if (!tr.docChanged) return false;
3849
+ editor.view.dispatch(tr);
3850
+ return true;
3851
+ }
3852
+ function findTable(editor) {
3853
+ const { $from } = editor.state.selection;
3854
+ for (let d = $from.depth; d > 0; d -= 1) {
3855
+ const node = $from.node(d);
3856
+ if (node.type.spec.tableRole === "table") {
3857
+ return { node, pos: $from.before(d) };
3858
+ }
3859
+ }
3860
+ return null;
3861
+ }
3862
+ function tableHeaderActive(editor, axis) {
3863
+ const table = findTable(editor);
3864
+ if (!table) return false;
3865
+ const map = TableMap.get(table.node);
3866
+ const positions = map.cellsInRect({
3867
+ left: 0,
3868
+ top: 0,
3869
+ right: axis === "row" ? map.width : 1,
3870
+ bottom: axis === "column" ? map.height : 1
3871
+ });
3872
+ return positions.length > 0 && positions.every((pos) => {
3873
+ var _a;
3874
+ return ((_a = table.node.nodeAt(pos)) == null ? void 0 : _a.type.spec.tableRole) === "header_cell";
3875
+ });
3876
+ }
3877
+ function distributeTableColumns(editor, availableWidth) {
3878
+ const table = findTable(editor);
3879
+ if (!editor.isEditable || !table || !Number.isFinite(availableWidth) || availableWidth <= 0) return false;
3880
+ const map = TableMap.get(table.node);
3881
+ const width = Math.max(TABLE_CELL_MIN_WIDTH, Math.min(2e3, Math.floor(availableWidth / map.width)));
3882
+ const tr = editor.state.tr;
3883
+ for (const relative of new Set(map.map)) {
3884
+ const pos = table.pos + 1 + relative;
3885
+ const cell = editor.state.doc.nodeAt(pos);
3886
+ tr.setNodeMarkup(pos, void 0, {
3887
+ ...cell.attrs,
3888
+ colwidth: Array(Number(cell.attrs.colspan)).fill(width)
3889
+ });
3890
+ }
3891
+ editor.view.dispatch(tr);
3892
+ return true;
3893
+ }
3894
+ function setTableHeaderBackground(editor, backgroundColor) {
3895
+ const table = findTable(editor);
3896
+ const header = Object.values(editor.state.schema.nodes).find((type) => type.spec.tableRole === "header_cell");
3897
+ if (!editor.isEditable || !table || !header || !(backgroundColor === null || CELL_BACKGROUNDS.some((color) => color.value === backgroundColor))) return false;
3898
+ const map = TableMap.get(table.node);
3899
+ const tr = editor.state.tr;
3900
+ for (const relative of new Set(map.map.slice(0, map.width))) {
3901
+ const pos = table.pos + 1 + relative;
3902
+ const cell = editor.state.doc.nodeAt(pos);
3903
+ tr.setNodeMarkup(pos, header, { ...cell.attrs, backgroundColor });
3904
+ }
3905
+ editor.view.dispatch(tr);
3906
+ return true;
3907
+ }
3908
+ function currentCellRect(editor) {
3909
+ try {
3910
+ const rect = selectedRect(editor.state);
3911
+ return { row: rect.top, col: rect.left };
3912
+ } catch {
3913
+ return null;
3914
+ }
3915
+ }
3916
+ function setColumnWidth(editor, colIndex, px) {
3917
+ const table = findTable(editor);
3918
+ if (!table) return false;
3919
+ const map = TableMap.get(table.node);
3920
+ if (colIndex < 0 || colIndex >= map.width) return false;
3921
+ const { state, view } = editor;
3922
+ if (!editor.isEditable || !Number.isFinite(px) || px < 40 || px > 2e3) return false;
3923
+ const tr = state.tr;
3924
+ const seen = /* @__PURE__ */ new Set();
3925
+ let touched = 0;
3926
+ for (let row = 0; row < map.height; row += 1) {
3927
+ const relative = map.map[row * map.width + colIndex];
3928
+ if (seen.has(relative)) continue;
3929
+ seen.add(relative);
3930
+ const cellPos = table.pos + 1 + relative;
3931
+ const cell = state.doc.nodeAt(cellPos);
3932
+ if (!cell) continue;
3933
+ const widths = [...cell.attrs.colwidth ?? Array(Number(cell.attrs.colspan ?? 1)).fill(0)];
3934
+ widths[colIndex - map.findCell(relative).left] = px;
3935
+ tr.setNodeMarkup(cellPos, void 0, { ...cell.attrs, colwidth: widths });
3936
+ touched += 1;
3937
+ }
3938
+ if (touched > 0) view.dispatch(tr);
3939
+ return touched > 0;
3940
+ }
3941
+ function autoFitColumn(editor, colIndex) {
3942
+ var _a;
3943
+ const table = findTable(editor);
3944
+ if (!table) return false;
3945
+ const map = TableMap.get(table.node);
3946
+ if (colIndex < 0 || colIndex >= map.width) return false;
3947
+ const { state, view } = editor;
3948
+ if (!editor.isEditable) return false;
3949
+ const tr = state.tr;
3950
+ const seen = /* @__PURE__ */ new Set();
3951
+ let touched = 0;
3952
+ for (let row = 0; row < map.height; row += 1) {
3953
+ const relative = map.map[row * map.width + colIndex];
3954
+ if (seen.has(relative)) continue;
3955
+ seen.add(relative);
3956
+ const cellPos = table.pos + 1 + relative;
3957
+ const cell = state.doc.nodeAt(cellPos);
3958
+ if (!cell) continue;
3959
+ if (((_a = cell.attrs) == null ? void 0 : _a.colwidth) == null) continue;
3960
+ const widths = [...cell.attrs.colwidth];
3961
+ widths[colIndex - map.findCell(relative).left] = 0;
3962
+ tr.setNodeMarkup(cellPos, void 0, {
3963
+ ...cell.attrs,
3964
+ colwidth: widths.some((w) => w > 0) ? widths : null
3965
+ });
3966
+ touched += 1;
3967
+ }
3968
+ if (touched > 0) view.dispatch(tr);
3969
+ return touched > 0;
3970
+ }
3971
+ function readTableGeometry(view, cellPos, pointer) {
3972
+ var _a, _b;
3973
+ try {
3974
+ const $cell = view.state.doc.resolve(cellPos);
3975
+ const node = $cell.node(-1);
3976
+ if (node.type.spec.tableRole !== "table") return null;
3977
+ const start = $cell.start(-1);
3978
+ const map = TableMap.get(node);
3979
+ const element = view.nodeDOM(cellPos);
3980
+ if (!(element instanceof HTMLElement)) return null;
3981
+ const table = element.closest("table");
3982
+ const shell = view.dom.closest(".editor-shell");
3983
+ if (!table || !shell) return null;
3984
+ const hostRect = shell.getBoundingClientRect();
3985
+ const tableRect = table.getBoundingClientRect();
3986
+ const clip = (table.closest(".tableWrapper") ?? table).getBoundingClientRect();
3987
+ const left = Math.max(tableRect.left, clip.left);
3988
+ const right = Math.min(tableRect.right, clip.right);
3989
+ if (right <= left) return null;
3990
+ const rows = Array.from(table.rows).map((row, index) => {
3991
+ const rect = row.getBoundingClientRect();
3992
+ return {
3993
+ index,
3994
+ cellPos: start + map.map[index * map.width],
3995
+ start: rect.top - hostRect.top,
3996
+ size: rect.height
3997
+ };
3998
+ });
3999
+ const cols = Array.from(table.querySelectorAll(":scope > colgroup > col"));
4000
+ const columns = cols.map((col, index) => {
4001
+ const rect = col.getBoundingClientRect();
4002
+ return {
4003
+ index,
4004
+ cellPos: start + map.map[index],
4005
+ start: rect.left - hostRect.left,
4006
+ size: rect.width
4007
+ };
4008
+ });
4009
+ if (rows.length !== map.height || columns.length !== map.width) return null;
4010
+ const cellRect = map.findCell(cellPos - start);
4011
+ const rowIndex = pointer ? ((_a = rows.find((row) => pointer.y >= row.start + hostRect.top && pointer.y < row.start + hostRect.top + row.size)) == null ? void 0 : _a.index) ?? cellRect.top : cellRect.top;
4012
+ const colIndex = pointer ? ((_b = columns.find((col) => pointer.x >= col.start + hostRect.left && pointer.x < col.start + hostRect.left + col.size)) == null ? void 0 : _b.index) ?? cellRect.left : cellRect.left;
4013
+ return {
4014
+ cellPos,
4015
+ rowIndex,
4016
+ colIndex,
4017
+ rowTop: rows[rowIndex].start,
4018
+ rowHeight: rows[rowIndex].size,
4019
+ colLeft: columns[colIndex].start,
4020
+ colWidth: columns[colIndex].size,
4021
+ tableWidth: right - left,
4022
+ tableHeight: tableRect.height,
4023
+ tableTop: tableRect.top - hostRect.top,
4024
+ tableLeft: left - hostRect.left,
4025
+ contentLeft: view.dom.getBoundingClientRect().left - hostRect.left,
4026
+ rows,
4027
+ columns
4028
+ };
4029
+ } catch {
4030
+ return null;
4031
+ }
4032
+ }
4033
+ function clippedTableSelection(rects, clip) {
4034
+ if (!rects.length) return null;
4035
+ const left = Math.max(clip.left, Math.min(...rects.map((rect) => rect.left)));
4036
+ const right = Math.min(clip.right, Math.max(...rects.map((rect) => rect.right)));
4037
+ const top = Math.min(...rects.map((rect) => rect.top));
4038
+ const bottom = Math.max(...rects.map((rect) => rect.bottom));
4039
+ return right > left && bottom > top ? { left, right, top, bottom } : null;
4040
+ }
4041
+ const TableInteractionKey = new PluginKey("ywiki-table-interaction");
4042
+ const TableInteraction = Extension.create({
4043
+ name: "ywikiTableInteraction",
4044
+ addOptions() {
4045
+ return { onHover: () => void 0 };
4046
+ },
4047
+ addProseMirrorPlugins() {
4048
+ const options = this.options;
4049
+ return [new Plugin({
4050
+ key: TableInteractionKey,
4051
+ view: (view) => {
4052
+ const shell = view.dom.closest(".editor-shell") ?? view.dom;
4053
+ let cell = null;
4054
+ let pointer;
4055
+ let lastKey = "";
4056
+ let frame = 0;
4057
+ const clear = () => {
4058
+ cell = null;
4059
+ pointer = void 0;
4060
+ if (lastKey) options.onHover(null);
4061
+ lastKey = "";
4062
+ };
4063
+ const report = () => {
4064
+ if (!(cell == null ? void 0 : cell.isConnected) || !view.editable) {
4065
+ clear();
4066
+ return;
4067
+ }
4068
+ const pos = cellPositionAtDOM(view, cell);
4069
+ const geometry = pos === null ? null : readTableGeometry(view, pos, pointer);
4070
+ if (!geometry) {
4071
+ clear();
4072
+ return;
4073
+ }
4074
+ const key2 = JSON.stringify(geometry);
4075
+ if (key2 !== lastKey) {
4076
+ lastKey = key2;
4077
+ options.onHover(geometry);
4078
+ }
4079
+ };
4080
+ const schedule = () => {
4081
+ if (!frame) frame = requestAnimationFrame(() => {
4082
+ frame = 0;
4083
+ report();
4084
+ });
4085
+ };
4086
+ const move = (event) => {
4087
+ var _a;
4088
+ const mouse = event;
4089
+ const target = mouse.target instanceof Element ? mouse.target : null;
4090
+ if (target == null ? void 0 : target.closest(".tbl-ui-host")) return;
4091
+ const next = target == null ? void 0 : target.closest("td,th");
4092
+ if (next && view.dom.contains(next)) {
4093
+ cell = next;
4094
+ pointer = { x: mouse.clientX, y: mouse.clientY };
4095
+ schedule();
4096
+ return;
4097
+ }
4098
+ const rect = (_a = cell == null ? void 0 : cell.closest("table")) == null ? void 0 : _a.getBoundingClientRect();
4099
+ if (rect && mouse.clientX >= rect.left - 64 && mouse.clientX <= rect.right + 24 && mouse.clientY >= rect.top - 32 && mouse.clientY <= rect.bottom + 24) return;
4100
+ clear();
4101
+ };
4102
+ shell.addEventListener("mousemove", move);
4103
+ shell.addEventListener("mouseleave", clear);
4104
+ window.addEventListener("scroll", schedule, true);
4105
+ window.addEventListener("resize", schedule);
4106
+ const observer = new ResizeObserver(schedule);
4107
+ observer.observe(view.dom);
4108
+ return {
4109
+ update: schedule,
4110
+ destroy: () => {
4111
+ cancelAnimationFrame(frame);
4112
+ observer.disconnect();
4113
+ shell.removeEventListener("mousemove", move);
4114
+ shell.removeEventListener("mouseleave", clear);
4115
+ window.removeEventListener("scroll", schedule, true);
4116
+ window.removeEventListener("resize", schedule);
4117
+ }
4118
+ };
4119
+ }
4120
+ })];
4121
+ }
4122
+ });
4123
+ const COLLAB_FIELD = "default";
4124
+ function coreExtensions(options = {}) {
4125
+ const list = [
4126
+ StarterKit.configure({ heading: { levels: [1, 2, 3, 4, 5, 6] }, undoRedo: options.collabDoc ? false : {}, link: false, codeBlock: false, trailingNode: false, dropcursor: false }),
4127
+ YwikiCodeBlock.configure({ canCopy: () => {
4128
+ var _a;
4129
+ return ((_a = options.canCopyCode) == null ? void 0 : _a.call(options)) ?? false;
4130
+ }, contentContext: options.contentContext }),
4131
+ CodeHighlight,
4132
+ Link.configure({ openOnClick: false, protocols: ["http", "https", "mailto"], HTMLAttributes: { rel: "noopener noreferrer nofollow" } }),
4133
+ YwikiImage.configure({ inline: false, allowBase64: false }).extend({ addNodeView: () => createImageNodeView(options.attachmentFetcher, options.attachmentScope) }),
4134
+ FileAttachment.extend({ addNodeView: () => createFileAttachmentNodeView(options.attachmentFetcher, options.attachmentScope) }),
4135
+ ImageGallery,
4136
+ Table.configure({ resizable: true, cellMinWidth: 120, allowTableNodeSelection: true }),
4137
+ TableRow,
4138
+ AlignedTableCell,
4139
+ AlignedTableHeader,
4140
+ TaskList,
4141
+ TaskItem.configure({ nested: true }),
4142
+ Columns,
4143
+ Column,
4144
+ PageLink,
4145
+ Mention,
4146
+ MathInline.extend({ addNodeView: () => mathNodeView }),
4147
+ MathBlock.extend({ addNodeView: () => mathNodeView }),
4148
+ VideoEmbed.configure({ renderer: options.videoRenderer }),
4149
+ MindMap.configure({ renderer: options.mindMapRenderer }),
4150
+ Callout.configure({ renderer: createCalloutNodeView() }),
4151
+ MindMapTopic,
4152
+ MindMapDescription,
4153
+ MindMapNote,
4154
+ MindMapRelation,
4155
+ TextStyle,
4156
+ Highlight,
4157
+ BlockFormat,
4158
+ BlockSelection,
4159
+ BlockMenuHighlight,
4160
+ ImeGuard,
4161
+ PasteSanitizer
4162
+ ];
4163
+ if (options.placeholder) list.push(Placeholder.configure({ placeholder: options.placeholder }));
4164
+ if (options.inlineComments) list.push(InlineCommentHighlight.configure(options.inlineComments));
4165
+ if (options.slash) list.push(SlashCommand.configure(options.slash));
4166
+ if (options.blockHandle) list.push(BlockHandle.configure(options.blockHandle));
4167
+ if (options.tableInteraction) list.push(TableInteraction.configure(options.tableInteraction));
4168
+ if (options.uploadPlaceholder) list.push(UploadPlaceholderExtension.configure(options.uploadPlaceholder));
4169
+ if (options.collabDoc) list.push(Collaboration.configure({ document: options.collabDoc, field: options.collabField ?? COLLAB_FIELD }));
4170
+ if (options.collabDoc && options.collabAwareness) list.push(CollabCursor.configure({ awareness: options.collabAwareness }));
4171
+ return list;
4172
+ }
4173
+ export {
4174
+ coreExtensions as A,
4175
+ BlockRangeSelection as B,
4176
+ COLLAB_FIELD as C,
4177
+ refreshInlineComments as D,
4178
+ addUploadPlaceholder as E,
4179
+ currentPlaceholderPos as F,
4180
+ insertUploadedBlock as G,
4181
+ removeUploadPlaceholder as H,
4182
+ replacePlaceholderWithNode as I,
4183
+ TEXT_ALIGNS as T,
4184
+ tablePositionAtCell as a,
4185
+ buildColumnsJson as b,
4186
+ currentTableCellPosition as c,
4187
+ dismissSlash as d,
4188
+ CELL_ALIGNS as e,
4189
+ cellPositionAtDOM as f,
4190
+ selectTableCell as g,
4191
+ highlightMenuBlock as h,
4192
+ insertTableBoundary as i,
4193
+ clippedTableSelection as j,
4194
+ tableSelectionMarkActive as k,
4195
+ setTableSelectionMark as l,
4196
+ setTableHeaderBackground as m,
4197
+ currentCellRect as n,
4198
+ setColumnWidth as o,
4199
+ autoFitColumn as p,
4200
+ distributeTableColumns as q,
4201
+ readTableGeometry as r,
4202
+ selectTableAxis as s,
4203
+ tableHeaderActive as t,
4204
+ unwrapCallout as u,
4205
+ getSelectedBlocks as v,
4206
+ setBlockRangeSelection as w,
4207
+ setImageRangeSelection as x,
4208
+ pastedImageFiles as y,
4209
+ pastedRemoteImageUrls as z
4210
+ };