@dou-ui/editor 0.0.0-stage → 0.1.0-alpha.1

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 (324) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/catalog.js +18 -0
  4. package/dist/catalog.js.map +1 -0
  5. package/dist/code/code-editor-block.js +6 -0
  6. package/dist/code/code-editor-block.js.map +1 -0
  7. package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js +116 -0
  8. package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  9. package/dist/code/index.js +69 -0
  10. package/dist/code/index.js.map +1 -0
  11. package/dist/code/languages.js +51 -0
  12. package/dist/code/languages.js.map +1 -0
  13. package/dist/code/view.js +255 -0
  14. package/dist/code/view.js.map +1 -0
  15. package/dist/collaboration/binding.js +381 -0
  16. package/dist/collaboration/binding.js.map +1 -0
  17. package/dist/collaboration/cursors.js +150 -0
  18. package/dist/collaboration/cursors.js.map +1 -0
  19. package/dist/collaboration/index.js +108 -0
  20. package/dist/collaboration/index.js.map +1 -0
  21. package/dist/collaboration/order.js +88 -0
  22. package/dist/collaboration/order.js.map +1 -0
  23. package/dist/collaboration/table.js +234 -0
  24. package/dist/collaboration/table.js.map +1 -0
  25. package/dist/collaboration/text.js +91 -0
  26. package/dist/collaboration/text.js.map +1 -0
  27. package/dist/components/block-handle.js +6 -0
  28. package/dist/components/block-handle.js.map +1 -0
  29. package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js +474 -0
  30. package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js.map +1 -0
  31. package/dist/components/bubble-toolbar.js +6 -0
  32. package/dist/components/bubble-toolbar.js.map +1 -0
  33. package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js +381 -0
  34. package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  35. package/dist/components/comment-button.js +6 -0
  36. package/dist/components/comment-button.js.map +1 -0
  37. package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js +83 -0
  38. package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js.map +1 -0
  39. package/dist/components/comment-card.js +6 -0
  40. package/dist/components/comment-card.js.map +1 -0
  41. package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js +104 -0
  42. package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js.map +1 -0
  43. package/dist/components/menu-escape.js +25 -0
  44. package/dist/components/menu-escape.js.map +1 -0
  45. package/dist/components/search-bar.js +6 -0
  46. package/dist/components/search-bar.js.map +1 -0
  47. package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js +192 -0
  48. package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  49. package/dist/components/slash-menu.js +6 -0
  50. package/dist/components/slash-menu.js.map +1 -0
  51. package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js +164 -0
  52. package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  53. package/dist/core/align.js +94 -0
  54. package/dist/core/align.js.map +1 -0
  55. package/dist/core/block-selection.js +155 -0
  56. package/dist/core/block-selection.js.map +1 -0
  57. package/dist/core/blocks.js +132 -0
  58. package/dist/core/blocks.js.map +1 -0
  59. package/dist/core/changes.js +85 -0
  60. package/dist/core/changes.js.map +1 -0
  61. package/dist/core/colors.js +16 -0
  62. package/dist/core/colors.js.map +1 -0
  63. package/dist/core/commands.js +379 -0
  64. package/dist/core/commands.js.map +1 -0
  65. package/dist/core/comments.js +186 -0
  66. package/dist/core/comments.js.map +1 -0
  67. package/dist/core/compare.js +106 -0
  68. package/dist/core/compare.js.map +1 -0
  69. package/dist/core/convert.js +28 -0
  70. package/dist/core/convert.js.map +1 -0
  71. package/dist/core/diff.js +141 -0
  72. package/dist/core/diff.js.map +1 -0
  73. package/dist/core/editor.js +138 -0
  74. package/dist/core/editor.js.map +1 -0
  75. package/dist/core/extension.js +99 -0
  76. package/dist/core/extension.js.map +1 -0
  77. package/dist/core/file-drop.js +56 -0
  78. package/dist/core/file-drop.js.map +1 -0
  79. package/dist/core/highlight.js +120 -0
  80. package/dist/core/highlight.js.map +1 -0
  81. package/dist/core/input-rules.js +61 -0
  82. package/dist/core/input-rules.js.map +1 -0
  83. package/dist/core/keymap.js +38 -0
  84. package/dist/core/keymap.js.map +1 -0
  85. package/dist/core/model.js +196 -0
  86. package/dist/core/model.js.map +1 -0
  87. package/dist/core/output.js +263 -0
  88. package/dist/core/output.js.map +1 -0
  89. package/dist/core/plugins.js +102 -0
  90. package/dist/core/plugins.js.map +1 -0
  91. package/dist/core/prepare.js +102 -0
  92. package/dist/core/prepare.js.map +1 -0
  93. package/dist/core/schema.js +158 -0
  94. package/dist/core/schema.js.map +1 -0
  95. package/dist/core/search.js +197 -0
  96. package/dist/core/search.js.map +1 -0
  97. package/dist/core/selection.js +16 -0
  98. package/dist/core/selection.js.map +1 -0
  99. package/dist/core/slash.js +83 -0
  100. package/dist/core/slash.js.map +1 -0
  101. package/dist/core/transaction.js +311 -0
  102. package/dist/core/transaction.js.map +1 -0
  103. package/dist/define-block.js +177 -0
  104. package/dist/define-block.js.map +1 -0
  105. package/dist/document.js +157 -0
  106. package/dist/document.js.map +1 -0
  107. package/dist/dom.js +48 -0
  108. package/dist/dom.js.map +1 -0
  109. package/dist/editor.js +33 -0
  110. package/dist/editor.js.map +1 -0
  111. package/dist/editor.vue_vue_type_script_setup_true_lang.js +564 -0
  112. package/dist/editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
  113. package/dist/editor2.js +6 -0
  114. package/dist/editor2.js.map +1 -0
  115. package/dist/extensions/align/align-menu.js +6 -0
  116. package/dist/extensions/align/align-menu.js.map +1 -0
  117. package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js +82 -0
  118. package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  119. package/dist/extensions/align/align-sub.js +6 -0
  120. package/dist/extensions/align/align-sub.js.map +1 -0
  121. package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js +57 -0
  122. package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js.map +1 -0
  123. package/dist/extensions/align/commands.js +40 -0
  124. package/dist/extensions/align/commands.js.map +1 -0
  125. package/dist/extensions/align/index.js +27 -0
  126. package/dist/extensions/align/index.js.map +1 -0
  127. package/dist/extensions/attachment/attachment-block.js +6 -0
  128. package/dist/extensions/attachment/attachment-block.js.map +1 -0
  129. package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js +119 -0
  130. package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  131. package/dist/extensions/attachment/files.js +66 -0
  132. package/dist/extensions/attachment/files.js.map +1 -0
  133. package/dist/extensions/attachment/index.js +132 -0
  134. package/dist/extensions/attachment/index.js.map +1 -0
  135. package/dist/extensions/blocks.js +170 -0
  136. package/dist/extensions/blocks.js.map +1 -0
  137. package/dist/extensions/callout/callout-block.js +6 -0
  138. package/dist/extensions/callout/callout-block.js.map +1 -0
  139. package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js +71 -0
  140. package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  141. package/dist/extensions/callout/html.js +61 -0
  142. package/dist/extensions/callout/html.js.map +1 -0
  143. package/dist/extensions/callout/index.js +52 -0
  144. package/dist/extensions/callout/index.js.map +1 -0
  145. package/dist/extensions/callout/tones.js +12 -0
  146. package/dist/extensions/callout/tones.js.map +1 -0
  147. package/dist/extensions/code-block/code-block-view.js +6 -0
  148. package/dist/extensions/code-block/code-block-view.js.map +1 -0
  149. package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js +87 -0
  150. package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js.map +1 -0
  151. package/dist/extensions/code-block/index.js +98 -0
  152. package/dist/extensions/code-block/index.js.map +1 -0
  153. package/dist/extensions/code-block/languages.js +116 -0
  154. package/dist/extensions/code-block/languages.js.map +1 -0
  155. package/dist/extensions/colors/color-menu.js +6 -0
  156. package/dist/extensions/colors/color-menu.js.map +1 -0
  157. package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js +113 -0
  158. package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  159. package/dist/extensions/colors/index.js +49 -0
  160. package/dist/extensions/colors/index.js.map +1 -0
  161. package/dist/extensions/colors/palette.js +26 -0
  162. package/dist/extensions/colors/palette.js.map +1 -0
  163. package/dist/extensions/editor-kit.js +35 -0
  164. package/dist/extensions/editor-kit.js.map +1 -0
  165. package/dist/extensions/embed/embed-block.js +6 -0
  166. package/dist/extensions/embed/embed-block.js.map +1 -0
  167. package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js +210 -0
  168. package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  169. package/dist/extensions/embed/index.js +118 -0
  170. package/dist/extensions/embed/index.js.map +1 -0
  171. package/dist/extensions/embed/targets.js +79 -0
  172. package/dist/extensions/embed/targets.js.map +1 -0
  173. package/dist/extensions/formatting.js +153 -0
  174. package/dist/extensions/formatting.js.map +1 -0
  175. package/dist/extensions/image/image-block.js +6 -0
  176. package/dist/extensions/image/image-block.js.map +1 -0
  177. package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js +206 -0
  178. package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  179. package/dist/extensions/image/index.js +125 -0
  180. package/dist/extensions/image/index.js.map +1 -0
  181. package/dist/extensions/image/uploads.js +96 -0
  182. package/dist/extensions/image/uploads.js.map +1 -0
  183. package/dist/extensions/list-html.js +58 -0
  184. package/dist/extensions/list-html.js.map +1 -0
  185. package/dist/extensions/lists.js +182 -0
  186. package/dist/extensions/lists.js.map +1 -0
  187. package/dist/extensions/mention/index.js +56 -0
  188. package/dist/extensions/mention/index.js.map +1 -0
  189. package/dist/extensions/mention/mention-menu.js +6 -0
  190. package/dist/extensions/mention/mention-menu.js.map +1 -0
  191. package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js +132 -0
  192. package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  193. package/dist/extensions/mention/state.js +113 -0
  194. package/dist/extensions/mention/state.js.map +1 -0
  195. package/dist/extensions/table/commands.js +356 -0
  196. package/dist/extensions/table/commands.js.map +1 -0
  197. package/dist/extensions/table/html.js +134 -0
  198. package/dist/extensions/table/html.js.map +1 -0
  199. package/dist/extensions/table/index.js +56 -0
  200. package/dist/extensions/table/index.js.map +1 -0
  201. package/dist/extensions/table/plugin.js +81 -0
  202. package/dist/extensions/table/plugin.js.map +1 -0
  203. package/dist/extensions/table/schema.js +258 -0
  204. package/dist/extensions/table/schema.js.map +1 -0
  205. package/dist/extensions/table/table-controls.js +6 -0
  206. package/dist/extensions/table/table-controls.js.map +1 -0
  207. package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js +698 -0
  208. package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js.map +1 -0
  209. package/dist/extensions/table/view.js +19 -0
  210. package/dist/extensions/table/view.js.map +1 -0
  211. package/dist/extensions/toggle/index.js +87 -0
  212. package/dist/extensions/toggle/index.js.map +1 -0
  213. package/dist/extensions/toggle/state.js +120 -0
  214. package/dist/extensions/toggle/state.js.map +1 -0
  215. package/dist/extensions/toggle/toggle-block.js +6 -0
  216. package/dist/extensions/toggle/toggle-block.js.map +1 -0
  217. package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js +42 -0
  218. package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  219. package/dist/index.js +49 -0
  220. package/dist/index.js.map +1 -0
  221. package/dist/locale.js +522 -0
  222. package/dist/locale.js.map +1 -0
  223. package/dist/markdown/index.js +24 -0
  224. package/dist/markdown/index.js.map +1 -0
  225. package/dist/style.css +1 -0
  226. package/dist/style.d.ts +1 -0
  227. package/dist/style.js +8 -0
  228. package/dist/types/catalog.d.ts +8 -0
  229. package/dist/types/code/code-editor-block.vue.d.ts +17 -0
  230. package/dist/types/code/index.d.ts +18 -0
  231. package/dist/types/code/languages.d.ts +7 -0
  232. package/dist/types/code/types.d.ts +37 -0
  233. package/dist/types/code/view.d.ts +12 -0
  234. package/dist/types/collaboration/binding.d.ts +32 -0
  235. package/dist/types/collaboration/cursors.d.ts +26 -0
  236. package/dist/types/collaboration/index.d.ts +47 -0
  237. package/dist/types/collaboration/order.d.ts +20 -0
  238. package/dist/types/collaboration/table.d.ts +25 -0
  239. package/dist/types/collaboration/text.d.ts +9 -0
  240. package/dist/types/components/block-handle.vue.d.ts +3 -0
  241. package/dist/types/components/bubble-toolbar.vue.d.ts +7 -0
  242. package/dist/types/components/comment-button.vue.d.ts +3 -0
  243. package/dist/types/components/comment-card.vue.d.ts +3 -0
  244. package/dist/types/components/menu-escape.d.ts +7 -0
  245. package/dist/types/components/search-bar.vue.d.ts +22 -0
  246. package/dist/types/components/slash-menu.vue.d.ts +11 -0
  247. package/dist/types/core/align.d.ts +16 -0
  248. package/dist/types/core/block-selection.d.ts +22 -0
  249. package/dist/types/core/blocks.d.ts +52 -0
  250. package/dist/types/core/changes.d.ts +14 -0
  251. package/dist/types/core/colors.d.ts +4 -0
  252. package/dist/types/core/commands.d.ts +61 -0
  253. package/dist/types/core/comments.d.ts +44 -0
  254. package/dist/types/core/compare.d.ts +10 -0
  255. package/dist/types/core/convert.d.ts +12 -0
  256. package/dist/types/core/diff.d.ts +49 -0
  257. package/dist/types/core/editor.d.ts +64 -0
  258. package/dist/types/core/extension.d.ts +167 -0
  259. package/dist/types/core/file-drop.d.ts +19 -0
  260. package/dist/types/core/highlight.d.ts +32 -0
  261. package/dist/types/core/input-rules.d.ts +21 -0
  262. package/dist/types/core/keymap.d.ts +8 -0
  263. package/dist/types/core/model.d.ts +158 -0
  264. package/dist/types/core/output.d.ts +64 -0
  265. package/dist/types/core/plugins.d.ts +24 -0
  266. package/dist/types/core/prepare.d.ts +23 -0
  267. package/dist/types/core/schema.d.ts +38 -0
  268. package/dist/types/core/search.d.ts +83 -0
  269. package/dist/types/core/selection.d.ts +8 -0
  270. package/dist/types/core/slash.d.ts +21 -0
  271. package/dist/types/core/transaction.d.ts +97 -0
  272. package/dist/types/define-block.d.ts +66 -0
  273. package/dist/types/document.d.ts +46 -0
  274. package/dist/types/dom.d.ts +17 -0
  275. package/dist/types/editor.d.ts +252 -0
  276. package/dist/types/editor.vue.d.ts +210 -0
  277. package/dist/types/extensions/align/align-menu.vue.d.ts +3 -0
  278. package/dist/types/extensions/align/align-sub.vue.d.ts +7 -0
  279. package/dist/types/extensions/align/commands.d.ts +13 -0
  280. package/dist/types/extensions/align/index.d.ts +7 -0
  281. package/dist/types/extensions/attachment/attachment-block.vue.d.ts +17 -0
  282. package/dist/types/extensions/attachment/files.d.ts +6 -0
  283. package/dist/types/extensions/attachment/index.d.ts +20 -0
  284. package/dist/types/extensions/blocks.d.ts +8 -0
  285. package/dist/types/extensions/callout/callout-block.vue.d.ts +14 -0
  286. package/dist/types/extensions/callout/html.d.ts +4 -0
  287. package/dist/types/extensions/callout/index.d.ts +3 -0
  288. package/dist/types/extensions/callout/tones.d.ts +8 -0
  289. package/dist/types/extensions/code-block/code-block-view.vue.d.ts +16 -0
  290. package/dist/types/extensions/code-block/index.d.ts +19 -0
  291. package/dist/types/extensions/code-block/languages.d.ts +9 -0
  292. package/dist/types/extensions/colors/color-menu.vue.d.ts +3 -0
  293. package/dist/types/extensions/colors/index.d.ts +7 -0
  294. package/dist/types/extensions/colors/palette.d.ts +7 -0
  295. package/dist/types/extensions/editor-kit.d.ts +37 -0
  296. package/dist/types/extensions/embed/embed-block.vue.d.ts +16 -0
  297. package/dist/types/extensions/embed/index.d.ts +18 -0
  298. package/dist/types/extensions/embed/targets.d.ts +32 -0
  299. package/dist/types/extensions/formatting.d.ts +8 -0
  300. package/dist/types/extensions/image/image-block.vue.d.ts +16 -0
  301. package/dist/types/extensions/image/index.d.ts +16 -0
  302. package/dist/types/extensions/image/uploads.d.ts +35 -0
  303. package/dist/types/extensions/list-html.d.ts +11 -0
  304. package/dist/types/extensions/lists.d.ts +6 -0
  305. package/dist/types/extensions/mention/index.d.ts +11 -0
  306. package/dist/types/extensions/mention/mention-menu.vue.d.ts +7 -0
  307. package/dist/types/extensions/mention/state.d.ts +28 -0
  308. package/dist/types/extensions/table/commands.d.ts +69 -0
  309. package/dist/types/extensions/table/html.d.ts +4 -0
  310. package/dist/types/extensions/table/index.d.ts +8 -0
  311. package/dist/types/extensions/table/plugin.d.ts +4 -0
  312. package/dist/types/extensions/table/schema.d.ts +23 -0
  313. package/dist/types/extensions/table/table-controls.vue.d.ts +3 -0
  314. package/dist/types/extensions/table/view.d.ts +13 -0
  315. package/dist/types/extensions/toggle/index.d.ts +7 -0
  316. package/dist/types/extensions/toggle/state.d.ts +17 -0
  317. package/dist/types/extensions/toggle/toggle-block.vue.d.ts +14 -0
  318. package/dist/types/index.d.ts +266 -0
  319. package/dist/types/locale.d.ts +195 -0
  320. package/dist/types/markdown/index.d.ts +14 -0
  321. package/dist/types/use-comments.d.ts +17 -0
  322. package/dist/use-comments.js +141 -0
  323. package/dist/use-comments.js.map +1 -0
  324. package/package.json +98 -4
@@ -0,0 +1,158 @@
1
+ import { alignable } from "./align.js";
2
+ import { Schema } from "prosemirror-model";
3
+ // src/core/schema.ts
4
+ /**
5
+ * The document is a list of blocks. Each block is a `block` node holding
6
+ * one piece of content (a paragraph, a heading, a list item…) and,
7
+ * optionally, a `blockGroup` of child blocks:
8
+ *
9
+ * doc
10
+ * └─ block#id
11
+ * ├─ paragraph "Hello"
12
+ * └─ blockGroup
13
+ * └─ block#id
14
+ * └─ checkItem "Nested"
15
+ *
16
+ * The frame (doc, block, group, paragraph, text, line break) is fixed;
17
+ * every other content type and mark comes from an extension. Every piece
18
+ * of state lives in node attributes (never in a node view), so a
19
+ * collaborative binding can sync it like any other change.
20
+ */
21
+ /** The DOM of a content node: `div.dou-editor__content--<type>[data-content-type]`. */
22
+ var contentDOM = (type, extra = {}) => [
23
+ "div",
24
+ {
25
+ class: `dou-editor__content dou-editor__content--${type}`,
26
+ "data-content-type": type,
27
+ ...extra
28
+ },
29
+ 0
30
+ ];
31
+ /** A content type holding formatted text, parsed back from its own DOM. */
32
+ var textBlock = (type, spec = {}) => ({
33
+ content: "inline*",
34
+ defining: true,
35
+ parseDOM: [{ tag: `div[data-content-type="${type}"]` }],
36
+ toDOM: () => contentDOM(type),
37
+ ...spec
38
+ });
39
+ var frame = {
40
+ doc: { content: "block+" },
41
+ block: {
42
+ attrs: { id: { default: null } },
43
+ content: "blockContent blockGroup?",
44
+ defining: true,
45
+ parseDOM: [{
46
+ tag: "div[data-block-id]",
47
+ getAttrs: (dom) => ({ id: dom.dataset.blockId ?? null })
48
+ }],
49
+ toDOM: (node) => [
50
+ "div",
51
+ {
52
+ class: "dou-editor__block",
53
+ "data-block-id": node.attrs.id
54
+ },
55
+ 0
56
+ ]
57
+ },
58
+ blockGroup: {
59
+ content: "block+",
60
+ parseDOM: [{ tag: "div.dou-editor__group" }],
61
+ toDOM: () => [
62
+ "div",
63
+ {
64
+ class: "dou-editor__group",
65
+ role: "group"
66
+ },
67
+ 0
68
+ ]
69
+ },
70
+ paragraph: alignable(textBlock("paragraph", {
71
+ group: "blockContent",
72
+ parseDOM: [{ tag: "div[data-content-type=\"paragraph\"]" }, { tag: "p" }]
73
+ }))
74
+ };
75
+ var inlineFrame = {
76
+ text: { group: "inline" },
77
+ hardBreak: {
78
+ inline: true,
79
+ linebreakReplacement: true,
80
+ group: "inline",
81
+ selectable: false,
82
+ parseDOM: [{ tag: "br" }],
83
+ toDOM: () => ["br"]
84
+ }
85
+ };
86
+ /**
87
+ * Where a comment thread is anchored: the text it is about carries the
88
+ * thread's id. A text can be in several threads, so the mark excludes
89
+ * nothing, not even itself; typing at its edge doesn't extend it.
90
+ */
91
+ var commentMark = {
92
+ attrs: { id: {} },
93
+ excludes: "",
94
+ inclusive: false,
95
+ parseDOM: [{
96
+ tag: "span[data-comment]",
97
+ getAttrs: (dom) => ({ id: dom.dataset.comment })
98
+ }],
99
+ toDOM: (mark) => [
100
+ "span",
101
+ {
102
+ class: "dou-editor__comment",
103
+ "data-comment": mark.attrs.id
104
+ },
105
+ 0
106
+ ]
107
+ };
108
+ /** The schema for a set of extensions' blocks, inline nodes and marks. */
109
+ function createSchema({ blocks, nodes: inner = {}, inlines, marks }) {
110
+ const nodes = { ...frame };
111
+ for (const [name, spec] of Object.entries(blocks)) {
112
+ if (name in nodes) throw new Error(`[@dou-ui/editor] "${name}" is a built-in node`);
113
+ nodes[name] = {
114
+ ...spec,
115
+ group: spec.group ? `blockContent ${spec.group}` : "blockContent"
116
+ };
117
+ }
118
+ for (const [name, spec] of Object.entries(inner)) {
119
+ if (name in nodes) throw new Error(`[@dou-ui/editor] "${name}" is a built-in node`);
120
+ nodes[name] = spec;
121
+ }
122
+ Object.assign(nodes, inlineFrame);
123
+ for (const [name, spec] of Object.entries(inlines)) {
124
+ if (name in nodes) throw new Error(`[@dou-ui/editor] "${name}" is a built-in node`);
125
+ nodes[name] = {
126
+ inline: true,
127
+ ...spec,
128
+ group: spec.group ? `inline ${spec.group}` : "inline"
129
+ };
130
+ }
131
+ if ("comment" in marks) throw new Error("[@dou-ui/editor] \"comment\" is a built-in mark");
132
+ const others = Object.keys(marks).join(" ");
133
+ const specs = Object.fromEntries(Object.entries(marks).map(([name, spec]) => [name, spec.excludes === "_" ? {
134
+ ...spec,
135
+ excludes: others
136
+ } : spec]));
137
+ return new Schema({
138
+ nodes,
139
+ marks: {
140
+ comment: commentMark,
141
+ ...specs
142
+ }
143
+ });
144
+ }
145
+ /** Links may only point to web pages, mail, phone numbers or relative paths. */
146
+ function isSafeHref(href) {
147
+ const value = href.replace(/[\u0000- ]/g, "");
148
+ const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value)?.[1]?.toLowerCase();
149
+ return !scheme || [
150
+ "http",
151
+ "https",
152
+ "mailto",
153
+ "tel"
154
+ ].includes(scheme);
155
+ }
156
+ export { contentDOM, createSchema, isSafeHref, textBlock };
157
+
158
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../src/core/schema.ts"],"sourcesContent":["import { alignable } from './align'\nimport { Schema, type DOMOutputSpec, type MarkSpec, type NodeSpec } from 'prosemirror-model'\n\n/**\n * The document is a list of blocks. Each block is a `block` node holding\n * one piece of content (a paragraph, a heading, a list item…) and,\n * optionally, a `blockGroup` of child blocks:\n *\n * doc\n * └─ block#id\n * ├─ paragraph \"Hello\"\n * └─ blockGroup\n * └─ block#id\n * └─ checkItem \"Nested\"\n *\n * The frame (doc, block, group, paragraph, text, line break) is fixed;\n * every other content type and mark comes from an extension. Every piece\n * of state lives in node attributes (never in a node view), so a\n * collaborative binding can sync it like any other change.\n */\n\n/** The DOM of a content node: `div.dou-editor__content--<type>[data-content-type]`. */\nexport const contentDOM = (type: string, extra: Record<string, string> = {}): DOMOutputSpec => [\n 'div',\n {\n class: `dou-editor__content dou-editor__content--${type}`,\n 'data-content-type': type,\n ...extra,\n },\n 0,\n]\n\n/** A content type holding formatted text, parsed back from its own DOM. */\nexport const textBlock = (type: string, spec: Partial<NodeSpec> = {}): NodeSpec => ({\n content: 'inline*',\n defining: true,\n parseDOM: [{ tag: `div[data-content-type=\"${type}\"]` }],\n toDOM: () => contentDOM(type),\n ...spec,\n})\n\nconst frame: Record<string, NodeSpec> = {\n doc: { content: 'block+' },\n\n block: {\n attrs: { id: { default: null } },\n content: 'blockContent blockGroup?',\n defining: true,\n parseDOM: [\n {\n tag: 'div[data-block-id]',\n getAttrs: (dom) => ({ id: (dom as HTMLElement).dataset.blockId ?? null }),\n },\n ],\n toDOM: (node) => ['div', { class: 'dou-editor__block', 'data-block-id': node.attrs.id }, 0],\n },\n\n blockGroup: {\n content: 'block+',\n parseDOM: [{ tag: 'div.dou-editor__group' }],\n toDOM: () => ['div', { class: 'dou-editor__group', role: 'group' }, 0],\n },\n\n paragraph: alignable(\n textBlock('paragraph', {\n group: 'blockContent',\n parseDOM: [{ tag: 'div[data-content-type=\"paragraph\"]' }, { tag: 'p' }],\n }),\n ),\n}\n\nconst inlineFrame: Record<string, NodeSpec> = {\n text: { group: 'inline' },\n\n hardBreak: {\n inline: true,\n // newlines from a code block become line breaks when it turns into text, and back\n linebreakReplacement: true,\n group: 'inline',\n selectable: false,\n parseDOM: [{ tag: 'br' }],\n toDOM: () => ['br'],\n },\n}\n\n/**\n * Where a comment thread is anchored: the text it is about carries the\n * thread's id. A text can be in several threads, so the mark excludes\n * nothing, not even itself; typing at its edge doesn't extend it.\n */\nconst commentMark: MarkSpec = {\n attrs: { id: {} },\n excludes: '',\n inclusive: false,\n parseDOM: [\n {\n tag: 'span[data-comment]',\n getAttrs: (dom) => ({ id: (dom as HTMLElement).dataset.comment }),\n },\n ],\n toDOM: (mark) => ['span', { class: 'dou-editor__comment', 'data-comment': mark.attrs.id }, 0],\n}\n\nexport interface SchemaParts {\n blocks: Record<string, NodeSpec>\n /** Nodes inside blocks, e.g. table rows and cells. */\n nodes?: Record<string, NodeSpec>\n inlines: Record<string, NodeSpec>\n marks: Record<string, MarkSpec>\n}\n\n/** The schema for a set of extensions' blocks, inline nodes and marks. */\nexport function createSchema({ blocks, nodes: inner = {}, inlines, marks }: SchemaParts): Schema {\n const nodes: Record<string, NodeSpec> = { ...frame }\n for (const [name, spec] of Object.entries(blocks)) {\n if (name in nodes) throw new Error(`[@dou-ui/editor] \"${name}\" is a built-in node`)\n nodes[name] = { ...spec, group: spec.group ? `blockContent ${spec.group}` : 'blockContent' }\n }\n for (const [name, spec] of Object.entries(inner)) {\n if (name in nodes) throw new Error(`[@dou-ui/editor] \"${name}\" is a built-in node`)\n nodes[name] = spec\n }\n Object.assign(nodes, inlineFrame)\n for (const [name, spec] of Object.entries(inlines)) {\n if (name in nodes) throw new Error(`[@dou-ui/editor] \"${name}\" is a built-in node`)\n nodes[name] = { inline: true, ...spec, group: spec.group ? `inline ${spec.group}` : 'inline' }\n }\n if ('comment' in marks) throw new Error('[@dou-ui/editor] \"comment\" is a built-in mark')\n // any text can be commented on, also text whose mark excludes all others (inline code)\n const others = Object.keys(marks).join(' ')\n const specs = Object.fromEntries(\n Object.entries(marks).map(([name, spec]) => [\n name,\n spec.excludes === '_' ? { ...spec, excludes: others } : spec,\n ]),\n )\n // first, so a comment wraps the formatting of the text it is about\n return new Schema({ nodes, marks: { comment: commentMark, ...specs } })\n}\n\n/** Links may only point to web pages, mail, phone numbers or relative paths. */\nexport function isSafeHref(href: string): boolean {\n // browsers ignore whitespace and control characters inside the scheme\n // eslint-disable-next-line no-control-regex\n const value = href.replace(/[\\u0000- ]/g, '')\n const scheme = /^([a-z][a-z0-9+.-]*):/i.exec(value)?.[1]?.toLowerCase()\n return !scheme || ['http', 'https', 'mailto', 'tel'].includes(scheme)\n}\n\n/** The content types that come with `editorKit`; extensions add more. */\nexport type BuiltinContentType =\n | 'paragraph'\n | 'heading'\n | 'bulletItem'\n | 'numberedItem'\n | 'checkItem'\n | 'quote'\n | 'codeBlock'\n | 'divider'\n | 'toggle'\n | 'callout'\n | 'table'\n\nexport type ContentType = BuiltinContentType | (string & {})\n\nexport type BuiltinMarkType = 'bold' | 'italic' | 'underline' | 'strike' | 'code'\n\nexport type MarkType = BuiltinMarkType | (string & {})\n"],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { originKey } from "./editor.js";
2
+ import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
3
+ import { Decoration, DecorationSet } from "prosemirror-view";
4
+ // src/core/search.ts
5
+ var searchKey = new PluginKey("douSearch");
6
+ /** At most this many matches are counted and marked. */
7
+ var LIMIT = 1e4;
8
+ var WORD = /[\p{L}\p{N}_]/u;
9
+ var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
+ /** The query as one global regular expression, or the reason it can't be one. */
11
+ function queryRegExp(query) {
12
+ const flags = "gu" + (query.caseSensitive ? "" : "i");
13
+ try {
14
+ return new RegExp(query.regexp ? query.search : escapeRegExp(query.search), flags);
15
+ } catch (error) {
16
+ return error.message;
17
+ }
18
+ }
19
+ /**
20
+ * The text of a textblock, one character per position: text as it is, and
21
+ * `` for an inline node such as a mention or a line break, so an
22
+ * offset in the text is an offset in the block.
23
+ */
24
+ function blockText(node) {
25
+ let text = "";
26
+ node.forEach((child) => {
27
+ text += child.isText ? child.text : "".repeat(child.nodeSize);
28
+ });
29
+ return text;
30
+ }
31
+ /**
32
+ * Every match of the query in the document's text, in order, at most
33
+ * `limit` of them. A match stays inside one block; blocks for which
34
+ * `skip` says yes, such as ones that draw their own text, are left out.
35
+ */
36
+ function searchDoc(doc, query, options = {}) {
37
+ const matches = [];
38
+ if (!query.search) return {
39
+ matches,
40
+ capped: false
41
+ };
42
+ const re = queryRegExp(query);
43
+ if (typeof re === "string") return {
44
+ matches,
45
+ capped: false,
46
+ error: re
47
+ };
48
+ const limit = options.limit ?? LIMIT;
49
+ let capped = false;
50
+ doc.descendants((node, pos) => {
51
+ if (capped) return false;
52
+ if (options.skip?.(node)) return false;
53
+ if (!node.isTextblock) return true;
54
+ const text = blockText(node);
55
+ const start = pos + 1;
56
+ re.lastIndex = 0;
57
+ let found;
58
+ while (found = re.exec(text)) {
59
+ const from = found.index;
60
+ const to = from + found[0].length;
61
+ if (to === from) {
62
+ re.lastIndex = from + 1;
63
+ continue;
64
+ }
65
+ if (query.wholeWord && (WORD.test(text[from - 1] ?? "") || WORD.test(text[to] ?? ""))) continue;
66
+ if (matches.length >= limit) {
67
+ capped = true;
68
+ return false;
69
+ }
70
+ matches.push({
71
+ from: start + from,
72
+ to: start + to
73
+ });
74
+ }
75
+ return false;
76
+ });
77
+ return {
78
+ matches,
79
+ capped
80
+ };
81
+ }
82
+ /** The text that replaces one match: `query.replace`, with groups filled in for a regular expression. */
83
+ function replacementFor(doc, match, query) {
84
+ const replace = query.replace ?? "";
85
+ if (!query.regexp) return replace;
86
+ const re = queryRegExp(query);
87
+ if (typeof re === "string") return replace;
88
+ const text = doc.textBetween(match.from, match.to, void 0, "");
89
+ re.lastIndex = 0;
90
+ return text.replace(re, replace);
91
+ }
92
+ /** The index of the match the selection is on, or `-1`. */
93
+ function currentMatch(state) {
94
+ const s = searchKey.getState(state);
95
+ return s?.current ? s.matches.indexOf(s.current) : -1;
96
+ }
97
+ /** The index of the first match from `pos` on, wrapping round to the first. */
98
+ function matchAfter(matches, pos) {
99
+ if (!matches.length) return -1;
100
+ let lo = 0;
101
+ let hi = matches.length;
102
+ while (lo < hi) {
103
+ const mid = lo + hi >> 1;
104
+ if (matches[mid].from < pos) lo = mid + 1;
105
+ else hi = mid;
106
+ }
107
+ return lo === matches.length ? 0 : lo;
108
+ }
109
+ function decorate(doc, matches, current) {
110
+ return DecorationSet.create(doc, matches.map((m) => Decoration.inline(m.from, m.to, { class: m === current ? "dou-editor__match is-current" : "dou-editor__match" })));
111
+ }
112
+ /**
113
+ * Keeps the matches of the query in step with the document and marks
114
+ * them; the match the selection is on stands out.
115
+ */
116
+ function searchPlugin(options = {}) {
117
+ const find = (doc, query) => searchDoc(doc, query, options);
118
+ return new Plugin({
119
+ key: searchKey,
120
+ state: {
121
+ init: () => ({
122
+ query: { search: "" },
123
+ active: false,
124
+ matches: [],
125
+ capped: false,
126
+ decorations: DecorationSet.empty
127
+ }),
128
+ apply(tr, value, _old, state) {
129
+ const meta = tr.getMeta(searchKey);
130
+ if (!meta && !tr.docChanged && !tr.selectionSet) return value;
131
+ const query = meta?.query ?? value.query;
132
+ const active = meta?.active ?? value.active;
133
+ if (!active || !query.search) return {
134
+ query,
135
+ active,
136
+ matches: [],
137
+ capped: false,
138
+ decorations: DecorationSet.empty
139
+ };
140
+ const again = !!(meta?.query || meta?.active || tr.docChanged);
141
+ const found = again ? find(state.doc, query) : {
142
+ matches: value.matches,
143
+ capped: value.capped,
144
+ error: value.error
145
+ };
146
+ const { from, to } = state.selection;
147
+ const i = matchAfter(found.matches, from);
148
+ const at = found.matches[i];
149
+ const current = at && at.from === from && at.to === to ? at : void 0;
150
+ if (!again && current === value.current) return value;
151
+ return {
152
+ query,
153
+ active,
154
+ ...found,
155
+ current,
156
+ decorations: decorate(state.doc, found.matches, current)
157
+ };
158
+ }
159
+ },
160
+ props: { decorations: (state) => searchKey.getState(state)?.decorations }
161
+ });
162
+ }
163
+ /** Sets the query, or shows or hides the matches, in a transaction. */
164
+ function setSearch(tr, meta) {
165
+ return tr.setMeta(searchKey, meta);
166
+ }
167
+ /**
168
+ * Selects the first match from the cursor on, as a preview while the
169
+ * search is typed; `null` when the selection is on a match already or
170
+ * there is none.
171
+ */
172
+ function previewMatch(state) {
173
+ const s = searchKey.getState(state);
174
+ if (!s?.active || !s.matches.length || s.current) return null;
175
+ return selectMatch(state, s.matches[matchAfter(s.matches, state.selection.from)]);
176
+ }
177
+ /** Selects a match and scrolls it into view. */
178
+ function selectMatch(state, match) {
179
+ return state.tr.setSelection(TextSelection.create(state.doc, match.from, match.to)).scrollIntoView();
180
+ }
181
+ /**
182
+ * Replaces the matches given, last first so the positions stay right, in
183
+ * one transaction: one step to undo.
184
+ */
185
+ function replaceMatches(state, matches, query) {
186
+ const tr = state.tr.setMeta(originKey, "replace");
187
+ for (let i = matches.length - 1; i >= 0; i--) {
188
+ const m = matches[i];
189
+ const text = replacementFor(state.doc, m, query);
190
+ if (text) tr.insertText(text, m.from, m.to);
191
+ else tr.delete(m.from, m.to);
192
+ }
193
+ return tr;
194
+ }
195
+ export { currentMatch, matchAfter, previewMatch, queryRegExp, replaceMatches, replacementFor, searchDoc, searchKey, searchPlugin, selectMatch, setSearch };
196
+
197
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","names":[],"sources":["../../src/core/search.ts"],"sourcesContent":["import type { Node as PMNode } from 'prosemirror-model'\nimport {\n Plugin,\n PluginKey,\n TextSelection,\n type EditorState,\n type Transaction,\n} from 'prosemirror-state'\nimport { Decoration, DecorationSet } from 'prosemirror-view'\nimport { originKey } from './editor'\n\n/** What to look for in the editor's text. */\nexport interface EditorSearchQuery {\n /** The text, or a regular expression with `regexp`. */\n search: string\n /** What replacing puts in; with `regexp`, `$1` and `$<name>` refer to groups. */\n replace?: string\n /** Tells upper and lower case apart. */\n caseSensitive?: boolean\n /** Only matches that are whole words. */\n wholeWord?: boolean\n /** `search` is a regular expression (JavaScript syntax). */\n regexp?: boolean\n}\n\n/** A match, as document positions. */\nexport interface EditorMatch {\n from: number\n to: number\n}\n\nexport interface EditorSearchState {\n query: EditorSearchQuery\n /** Whether matches are shown; a query is kept while the panel is closed. */\n active: boolean\n matches: EditorMatch[]\n /** More matches exist than were counted. */\n capped: boolean\n /** Why the regular expression can't be read, when it can't. */\n error?: string\n /** The match the selection is on. */\n current?: EditorMatch\n decorations: DecorationSet\n}\n\nexport const searchKey = new PluginKey<EditorSearchState>('douSearch')\n\n/** At most this many matches are counted and marked. */\nconst LIMIT = 10_000\nconst WORD = /[\\p{L}\\p{N}_]/u\nconst escapeRegExp = (text: string) => text.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n/** The query as one global regular expression, or the reason it can't be one. */\nexport function queryRegExp(query: EditorSearchQuery): RegExp | string {\n const flags = 'gu' + (query.caseSensitive ? '' : 'i')\n try {\n return new RegExp(query.regexp ? query.search : escapeRegExp(query.search), flags)\n } catch (error) {\n return (error as Error).message\n }\n}\n\n/**\n * The text of a textblock, one character per position: text as it is, and\n * `` for an inline node such as a mention or a line break, so an\n * offset in the text is an offset in the block.\n */\nfunction blockText(node: PMNode) {\n let text = ''\n node.forEach((child) => {\n text += child.isText ? child.text! : ''.repeat(child.nodeSize)\n })\n return text\n}\n\n/**\n * Every match of the query in the document's text, in order, at most\n * `limit` of them. A match stays inside one block; blocks for which\n * `skip` says yes, such as ones that draw their own text, are left out.\n */\nexport function searchDoc(\n doc: PMNode,\n query: EditorSearchQuery,\n options: { limit?: number; skip?: (node: PMNode) => boolean } = {},\n): { matches: EditorMatch[]; capped: boolean; error?: string } {\n const matches: EditorMatch[] = []\n if (!query.search) return { matches, capped: false }\n const re = queryRegExp(query)\n if (typeof re === 'string') return { matches, capped: false, error: re }\n const limit = options.limit ?? LIMIT\n let capped = false\n doc.descendants((node, pos) => {\n if (capped) return false\n if (options.skip?.(node)) return false\n if (!node.isTextblock) return true\n const text = blockText(node)\n const start = pos + 1\n re.lastIndex = 0\n let found: RegExpExecArray | null\n while ((found = re.exec(text))) {\n const from = found.index\n const to = from + found[0].length\n if (to === from) {\n re.lastIndex = from + 1\n continue\n }\n if (query.wholeWord && (WORD.test(text[from - 1] ?? '') || WORD.test(text[to] ?? '')))\n continue\n if (matches.length >= limit) {\n capped = true\n return false\n }\n matches.push({ from: start + from, to: start + to })\n }\n return false\n })\n return { matches, capped }\n}\n\n/** The text that replaces one match: `query.replace`, with groups filled in for a regular expression. */\nexport function replacementFor(doc: PMNode, match: EditorMatch, query: EditorSearchQuery): string {\n const replace = query.replace ?? ''\n if (!query.regexp) return replace\n const re = queryRegExp(query)\n if (typeof re === 'string') return replace\n const text = doc.textBetween(match.from, match.to, undefined, '')\n // match again on the matched text alone (lookarounds see only it), then expand `$1`, `$<name>`\n re.lastIndex = 0\n return text.replace(re, replace)\n}\n\n/** The index of the match the selection is on, or `-1`. */\nexport function currentMatch(state: EditorState): number {\n const s = searchKey.getState(state)\n return s?.current ? s.matches.indexOf(s.current) : -1\n}\n\n/** The index of the first match from `pos` on, wrapping round to the first. */\nexport function matchAfter(matches: readonly EditorMatch[], pos: number): number {\n if (!matches.length) return -1\n let lo = 0\n let hi = matches.length\n while (lo < hi) {\n const mid = (lo + hi) >> 1\n if (matches[mid]!.from < pos) lo = mid + 1\n else hi = mid\n }\n return lo === matches.length ? 0 : lo\n}\n\ntype SearchMeta = { query?: EditorSearchQuery; active?: boolean }\n\nfunction decorate(doc: PMNode, matches: readonly EditorMatch[], current: EditorMatch | undefined) {\n return DecorationSet.create(\n doc,\n matches.map((m) =>\n Decoration.inline(m.from, m.to, {\n class: m === current ? 'dou-editor__match is-current' : 'dou-editor__match',\n }),\n ),\n )\n}\n\n/**\n * Keeps the matches of the query in step with the document and marks\n * them; the match the selection is on stands out.\n */\nexport function searchPlugin(options: { skip?: (node: PMNode) => boolean } = {}): Plugin {\n const find = (doc: PMNode, query: EditorSearchQuery) => searchDoc(doc, query, options)\n return new Plugin<EditorSearchState>({\n key: searchKey,\n state: {\n init: () => ({\n query: { search: '' },\n active: false,\n matches: [],\n capped: false,\n decorations: DecorationSet.empty,\n }),\n apply(tr, value, _old, state) {\n const meta = tr.getMeta(searchKey) as SearchMeta | undefined\n if (!meta && !tr.docChanged && !tr.selectionSet) return value\n const query = meta?.query ?? value.query\n const active = meta?.active ?? value.active\n if (!active || !query.search)\n return { query, active, matches: [], capped: false, decorations: DecorationSet.empty }\n const again = !!(meta?.query || meta?.active || tr.docChanged)\n const found = again\n ? find(state.doc, query)\n : { matches: value.matches, capped: value.capped, error: value.error }\n const { from, to } = state.selection\n const i = matchAfter(found.matches, from)\n const at = found.matches[i]\n const current = at && at.from === from && at.to === to ? at : undefined\n // only the selection moved, and not onto or off a match: the marks stay\n if (!again && current === value.current) return value\n return {\n query,\n active,\n ...found,\n current,\n decorations: decorate(state.doc, found.matches, current),\n }\n },\n },\n props: {\n decorations: (state) => searchKey.getState(state)?.decorations,\n },\n })\n}\n\n/** Sets the query, or shows or hides the matches, in a transaction. */\nexport function setSearch(tr: Transaction, meta: SearchMeta) {\n return tr.setMeta(searchKey, meta)\n}\n\n/**\n * Selects the first match from the cursor on, as a preview while the\n * search is typed; `null` when the selection is on a match already or\n * there is none.\n */\nexport function previewMatch(state: EditorState): Transaction | null {\n const s = searchKey.getState(state)\n if (!s?.active || !s.matches.length || s.current) return null\n return selectMatch(state, s.matches[matchAfter(s.matches, state.selection.from)]!)\n}\n\n/** Selects a match and scrolls it into view. */\nexport function selectMatch(state: EditorState, match: EditorMatch): Transaction {\n return state.tr\n .setSelection(TextSelection.create(state.doc, match.from, match.to))\n .scrollIntoView()\n}\n\n/**\n * Replaces the matches given, last first so the positions stay right, in\n * one transaction: one step to undo.\n */\nexport function replaceMatches(\n state: EditorState,\n matches: readonly EditorMatch[],\n query: EditorSearchQuery,\n): Transaction {\n // the `transaction` event tells a replacement from typing\n const tr = state.tr.setMeta(originKey, 'replace')\n for (let i = matches.length - 1; i >= 0; i--) {\n const m = matches[i]!\n const text = replacementFor(state.doc, m, query)\n if (text) tr.insertText(text, m.from, m.to)\n else tr.delete(m.from, m.to)\n }\n return tr\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { blockAround, blocksIn, selectedRange } from "./blocks.js";
2
+ import { blockSelectionRange } from "./block-selection.js";
3
+ // src/core/selection.ts
4
+ function selectionOf(state) {
5
+ const range = blockSelectionRange(state) ?? selectedRange(state);
6
+ return {
7
+ block: blockAround(state.selection.$head)?.node.attrs.id ?? null,
8
+ blocks: range ? blocksIn(range).map((info) => info.node.attrs.id) : []
9
+ };
10
+ }
11
+ function sameSelection(a, b) {
12
+ return a.block === b.block && a.blocks.length === b.blocks.length && a.blocks.every((id, i) => b.blocks[i] === id);
13
+ }
14
+ export { sameSelection, selectionOf };
15
+
16
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","names":[],"sources":["../../src/core/selection.ts"],"sourcesContent":["import type { EditorState } from 'prosemirror-state'\nimport { blockAround, blocksIn, selectedRange } from './blocks'\nimport { blockSelectionRange } from './block-selection'\n\n/** Where the selection is, by block. */\nexport interface BlockSelectionInfo {\n block: string | null\n blocks: string[]\n}\n\nexport function selectionOf(state: EditorState): BlockSelectionInfo {\n const range = blockSelectionRange(state) ?? selectedRange(state)\n const head = blockAround(state.selection.$head)\n return {\n block: (head?.node.attrs.id as string | undefined) ?? null,\n blocks: range ? blocksIn(range).map((info) => info.node.attrs.id as string) : [],\n }\n}\n\nexport function sameSelection(a: BlockSelectionInfo, b: BlockSelectionInfo): boolean {\n return (\n a.block === b.block &&\n a.blocks.length === b.blocks.length &&\n a.blocks.every((id, i) => b.blocks[i] === id)\n )\n}\n"],"mappings":";;;AAUA,SAAgB,YAAY,OAAwC;CAClE,MAAM,QAAQ,oBAAoB,KAAK,KAAK,cAAc,KAAK;CAE/D,OAAO;EACL,OAFW,YAAY,MAAM,UAAU,KAE/B,CAAA,EAAM,KAAK,MAAM,MAA6B;EACtD,QAAQ,QAAQ,SAAS,KAAK,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,MAAM,EAAY,IAAI,CAAC;CACjF;AACF;AAEA,SAAgB,cAAc,GAAuB,GAAgC;CACnF,OACE,EAAE,UAAU,EAAE,SACd,EAAE,OAAO,WAAW,EAAE,OAAO,UAC7B,EAAE,OAAO,OAAO,IAAI,MAAM,EAAE,OAAO,OAAO,EAAE;AAEhD"}
@@ -0,0 +1,83 @@
1
+ import { Plugin, PluginKey } from "prosemirror-state";
2
+ // src/core/slash.ts
3
+ var slashKey = new PluginKey("dou-editor-slash");
4
+ var TRIGGERS = ["/", "、"];
5
+ function getSlash(state) {
6
+ return slashKey.getState(state) ?? null;
7
+ }
8
+ function closeSlash(view) {
9
+ if (getSlash(view.state)) view.dispatch(view.state.tr.setMeta(slashKey, null));
10
+ }
11
+ function slashPlugin(options) {
12
+ let opening = null;
13
+ return new Plugin({
14
+ key: slashKey,
15
+ appendTransaction(_trs, _old, state) {
16
+ if (opening === null) return null;
17
+ const from = opening;
18
+ opening = null;
19
+ if (!TRIGGERS.includes(state.doc.textBetween(from, from + 1))) return null;
20
+ return state.tr.setMeta(slashKey, {
21
+ from,
22
+ query: ""
23
+ });
24
+ },
25
+ state: {
26
+ init: () => null,
27
+ apply(tr, value, _old, state) {
28
+ const meta = tr.getMeta(slashKey);
29
+ if (meta !== void 0) return meta;
30
+ if (!value) return null;
31
+ const from = tr.mapping.map(value.from);
32
+ const { $head, empty } = state.selection;
33
+ if (!empty || $head.pos <= from || $head.pos - from > 40) return null;
34
+ if (!state.doc.resolve(from).sameParent($head)) return null;
35
+ const text = state.doc.textBetween(from, $head.pos);
36
+ if (!TRIGGERS.includes(text[0] ?? "")) return null;
37
+ const query = text.slice(1);
38
+ if (/\s\s/.test(query)) return null;
39
+ return {
40
+ from,
41
+ query
42
+ };
43
+ }
44
+ },
45
+ props: {
46
+ handleTextInput(view, from, to, text) {
47
+ if (!TRIGGERS.includes(text) || getSlash(view.state)) return false;
48
+ const $from = view.state.doc.resolve(from);
49
+ if (!$from.parent.isTextblock || $from.parent.type.spec.code) return false;
50
+ if ($from.node($from.depth - 1).type.name !== "block") return false;
51
+ const before = $from.parent.textBetween(0, $from.parentOffset);
52
+ if (/[\w:/.]$/.test(before)) return false;
53
+ opening = from;
54
+ return false;
55
+ },
56
+ handleKeyDown(view, event) {
57
+ if (!getSlash(view.state)) return false;
58
+ if (event.key === "Escape") {
59
+ closeSlash(view);
60
+ return true;
61
+ }
62
+ return options.onKey(event);
63
+ }
64
+ },
65
+ view() {
66
+ let last = null;
67
+ return {
68
+ update(view) {
69
+ const current = getSlash(view.state);
70
+ if (current?.from === last?.from && current?.query === last?.query) return;
71
+ last = current;
72
+ options.onChange(current, view);
73
+ },
74
+ destroy() {
75
+ last = null;
76
+ }
77
+ };
78
+ }
79
+ });
80
+ }
81
+ export { closeSlash, getSlash, slashKey, slashPlugin };
82
+
83
+ //# sourceMappingURL=slash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash.js","names":[],"sources":["../../src/core/slash.ts"],"sourcesContent":["import { Plugin, PluginKey, type EditorState } from 'prosemirror-state'\nimport type { EditorView } from 'prosemirror-view'\n\n/**\n * The slash menu's state: open from the `/` at `from` to the cursor,\n * with the text typed after it as the query. A Chinese keyboard types\n * `、` for the same key, so that opens it too.\n */\nexport interface SlashState {\n from: number\n query: string\n}\n\nexport const slashKey = new PluginKey<SlashState | null>('dou-editor-slash')\n\nconst TRIGGERS = ['/', '、']\n\nexport interface SlashOptions {\n /** The menu reads the state; called whenever it changes. */\n onChange: (state: SlashState | null, view: EditorView) => void\n /** Keys go to the open menu first: arrows, `Enter`, `Tab`. Return whether it took the key. */\n onKey: (event: KeyboardEvent) => boolean\n}\n\nexport function getSlash(state: EditorState): SlashState | null {\n return slashKey.getState(state) ?? null\n}\n\nexport function closeSlash(view: EditorView) {\n if (getSlash(view.state)) view.dispatch(view.state.tr.setMeta(slashKey, null))\n}\n\nexport function slashPlugin(options: SlashOptions): Plugin<SlashState | null> {\n let opening: number | null = null\n return new Plugin<SlashState | null>({\n key: slashKey,\n appendTransaction(_trs, _old, state) {\n if (opening === null) return null\n const from = opening\n opening = null\n if (!TRIGGERS.includes(state.doc.textBetween(from, from + 1))) return null\n return state.tr.setMeta(slashKey, { from, query: '' })\n },\n state: {\n init: () => null,\n apply(tr, value, _old, state) {\n const meta = tr.getMeta(slashKey) as SlashState | null | undefined\n if (meta !== undefined) return meta\n if (!value) return null\n const from = tr.mapping.map(value.from)\n const { $head, empty } = state.selection\n // close when the cursor leaves the query or the trigger is deleted\n if (!empty || $head.pos <= from || $head.pos - from > 40) return null\n const $from = state.doc.resolve(from)\n if (!$from.sameParent($head)) return null\n const text = state.doc.textBetween(from, $head.pos)\n if (!TRIGGERS.includes(text[0] ?? '')) return null\n const query = text.slice(1)\n // two spaces in a row: the user is typing prose, not searching\n if (/\\s\\s/.test(query)) return null\n return { from, query }\n },\n },\n props: {\n handleTextInput(view, from, to, text) {\n if (!TRIGGERS.includes(text) || getSlash(view.state)) return false\n const $from = view.state.doc.resolve(from)\n if (!$from.parent.isTextblock || $from.parent.type.spec.code) return false\n // not inside a block's inner text, e.g. a table cell\n if ($from.node($from.depth - 1).type.name !== 'block') return false\n const before = $from.parent.textBetween(0, $from.parentOffset)\n // not right after a letter, digit or URL punctuation: `a/b`, `https://`\n if (/[\\w:/.]$/.test(before)) return false\n // let the browser type the `/` itself and open the menu once it is\n // in: replacing the typed text redraws it, and a key pressed right\n // after could land where the browser put the cursor meanwhile\n opening = from\n return false\n },\n handleKeyDown(view, event) {\n if (!getSlash(view.state)) return false\n if (event.key === 'Escape') {\n closeSlash(view)\n return true\n }\n return options.onKey(event)\n },\n },\n view() {\n let last: SlashState | null = null\n return {\n update(view) {\n const current = getSlash(view.state)\n if (current?.from === last?.from && current?.query === last?.query) return\n last = current\n options.onChange(current, view)\n },\n destroy() {\n last = null\n },\n }\n },\n })\n}\n"],"mappings":";;AAaA,IAAa,WAAW,IAAI,UAA6B,kBAAkB;AAE3E,IAAM,WAAW,CAAC,KAAK,GAAG;AAS1B,SAAgB,SAAS,OAAuC;CAC9D,OAAO,SAAS,SAAS,KAAK,KAAK;AACrC;AAEA,SAAgB,WAAW,MAAkB;CAC3C,IAAI,SAAS,KAAK,KAAK,GAAG,KAAK,SAAS,KAAK,MAAM,GAAG,QAAQ,UAAU,IAAI,CAAC;AAC/E;AAEA,SAAgB,YAAY,SAAkD;CAC5E,IAAI,UAAyB;CAC7B,OAAO,IAAI,OAA0B;EACnC,KAAK;EACL,kBAAkB,MAAM,MAAM,OAAO;GACnC,IAAI,YAAY,MAAM,OAAO;GAC7B,MAAM,OAAO;GACb,UAAU;GACV,IAAI,CAAC,SAAS,SAAS,MAAM,IAAI,YAAY,MAAM,OAAO,CAAC,CAAC,GAAG,OAAO;GACtE,OAAO,MAAM,GAAG,QAAQ,UAAU;IAAE;IAAM,OAAO;GAAG,CAAC;EACvD;EACA,OAAO;GACL,YAAY;GACZ,MAAM,IAAI,OAAO,MAAM,OAAO;IAC5B,MAAM,OAAO,GAAG,QAAQ,QAAQ;IAChC,IAAI,SAAS,KAAA,GAAW,OAAO;IAC/B,IAAI,CAAC,OAAO,OAAO;IACnB,MAAM,OAAO,GAAG,QAAQ,IAAI,MAAM,IAAI;IACtC,MAAM,EAAE,OAAO,UAAU,MAAM;IAE/B,IAAI,CAAC,SAAS,MAAM,OAAO,QAAQ,MAAM,MAAM,OAAO,IAAI,OAAO;IAEjE,IAAI,CADU,MAAM,IAAI,QAAQ,IAC3B,CAAA,CAAM,WAAW,KAAK,GAAG,OAAO;IACrC,MAAM,OAAO,MAAM,IAAI,YAAY,MAAM,MAAM,GAAG;IAClD,IAAI,CAAC,SAAS,SAAS,KAAK,MAAM,EAAE,GAAG,OAAO;IAC9C,MAAM,QAAQ,KAAK,MAAM,CAAC;IAE1B,IAAI,OAAO,KAAK,KAAK,GAAG,OAAO;IAC/B,OAAO;KAAE;KAAM;IAAM;GACvB;EACF;EACA,OAAO;GACL,gBAAgB,MAAM,MAAM,IAAI,MAAM;IACpC,IAAI,CAAC,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK,KAAK,GAAG,OAAO;IAC7D,MAAM,QAAQ,KAAK,MAAM,IAAI,QAAQ,IAAI;IACzC,IAAI,CAAC,MAAM,OAAO,eAAe,MAAM,OAAO,KAAK,KAAK,MAAM,OAAO;IAErE,IAAI,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK,SAAS,SAAS,OAAO;IAC9D,MAAM,SAAS,MAAM,OAAO,YAAY,GAAG,MAAM,YAAY;IAE7D,IAAI,WAAW,KAAK,MAAM,GAAG,OAAO;IAIpC,UAAU;IACV,OAAO;GACT;GACA,cAAc,MAAM,OAAO;IACzB,IAAI,CAAC,SAAS,KAAK,KAAK,GAAG,OAAO;IAClC,IAAI,MAAM,QAAQ,UAAU;KAC1B,WAAW,IAAI;KACf,OAAO;IACT;IACA,OAAO,QAAQ,MAAM,KAAK;GAC5B;EACF;EACA,OAAO;GACL,IAAI,OAA0B;GAC9B,OAAO;IACL,OAAO,MAAM;KACX,MAAM,UAAU,SAAS,KAAK,KAAK;KACnC,IAAI,SAAS,SAAS,MAAM,QAAQ,SAAS,UAAU,MAAM,OAAO;KACpE,OAAO;KACP,QAAQ,SAAS,SAAS,IAAI;IAChC;IACA,UAAU;KACR,OAAO;IACT;GACF;EACF;CACF,CAAC;AACH"}