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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,94 @@
1
+ import { warnOnce } from "./model.js";
2
+ // src/core/align.ts
3
+ var ALIGNS = [
4
+ "left",
5
+ "center",
6
+ "right"
7
+ ];
8
+ var isAlign = (value) => ALIGNS.includes(value);
9
+ /**
10
+ * The alignment written on an element: `data-align`, `style="text-align"`
11
+ * or `align`. Left is the reading direction: `null`, as it is stored.
12
+ */
13
+ function alignOf(dom) {
14
+ const value = (dom.dataset?.align || dom.style?.textAlign || dom.getAttribute("align") || "").toLowerCase().replace("start", "left").replace("end", "right");
15
+ return isAlign(value) && value !== "left" ? value : null;
16
+ }
17
+ function withAttrs(spec, attrs) {
18
+ if (!Array.isArray(spec)) return spec;
19
+ const [tag, second, ...rest] = spec;
20
+ if (second && typeof second === "object" && !Array.isArray(second) && !("nodeType" in second)) {
21
+ const own = second;
22
+ const style = [own.style, attrs.style].filter(Boolean).join("; ");
23
+ return [
24
+ tag,
25
+ {
26
+ ...own,
27
+ ...attrs,
28
+ ...style ? { style } : {}
29
+ },
30
+ ...rest
31
+ ];
32
+ }
33
+ return [
34
+ tag,
35
+ attrs,
36
+ ...second === void 0 ? [] : [second],
37
+ ...rest
38
+ ];
39
+ }
40
+ /**
41
+ * A text block that can be aligned: adds the `align` attribute, reads it
42
+ * from pasted HTML and draws it. Used by paragraphs, headings and quotes;
43
+ * custom text blocks can use it too.
44
+ */
45
+ function alignable(spec) {
46
+ const toDOM = spec.toDOM;
47
+ const normalize = spec.normalizeProps;
48
+ return {
49
+ ...spec,
50
+ normalizeProps: (attrs) => {
51
+ const next = normalize ? normalize(attrs) : attrs;
52
+ if (next.align === void 0 || next.align === null || isAlign(next.align)) return next.align === "left" ? {
53
+ ...next,
54
+ align: null
55
+ } : next;
56
+ warnOnce(`align "${String(next.align)}" is not one of left, center, right; dropped`);
57
+ return {
58
+ ...next,
59
+ align: null
60
+ };
61
+ },
62
+ attrs: {
63
+ ...spec.attrs,
64
+ align: { default: null }
65
+ },
66
+ optionalProps: [...spec.optionalProps ?? [], "align"],
67
+ parseDOM: spec.parseDOM?.map((rule) => {
68
+ if (!("tag" in rule)) return rule;
69
+ const tagRule = rule;
70
+ return {
71
+ ...tagRule,
72
+ getAttrs: (dom) => {
73
+ const own = tagRule.getAttrs ? tagRule.getAttrs(dom) : tagRule.attrs ?? null;
74
+ if (own === false) return false;
75
+ return {
76
+ ...own ?? tagRule.attrs,
77
+ align: alignOf(dom)
78
+ };
79
+ }
80
+ };
81
+ }),
82
+ toDOM: toDOM ? (node) => {
83
+ const out = toDOM(node);
84
+ const align = node.attrs.align;
85
+ return isAlign(align) ? withAttrs(out, {
86
+ "data-align": align,
87
+ style: `text-align: ${align}`
88
+ }) : out;
89
+ } : void 0
90
+ };
91
+ }
92
+ export { ALIGNS, alignOf, alignable, isAlign };
93
+
94
+ //# sourceMappingURL=align.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"align.js","names":[],"sources":["../../src/core/align.ts"],"sourcesContent":["import { warnOnce } from './model'\nimport type { DOMOutputSpec, Node as PMNode, NodeSpec, TagParseRule } from 'prosemirror-model'\n\n/** How a block's text lines up; `null` (left out of `props`) follows the reading direction. */\nexport type BlockAlign = 'left' | 'center' | 'right'\n\nexport const ALIGNS: readonly BlockAlign[] = ['left', 'center', 'right']\n\nexport const isAlign = (value: unknown): value is BlockAlign => ALIGNS.includes(value as BlockAlign)\n\n/**\n * The alignment written on an element: `data-align`, `style=\"text-align\"`\n * or `align`. Left is the reading direction: `null`, as it is stored.\n */\nexport function alignOf(dom: HTMLElement): BlockAlign | null {\n const value = (dom.dataset?.align || dom.style?.textAlign || dom.getAttribute('align') || '')\n .toLowerCase()\n .replace('start', 'left')\n .replace('end', 'right')\n return isAlign(value) && value !== 'left' ? value : null\n}\n\nfunction withAttrs(spec: DOMOutputSpec, attrs: Record<string, string>): DOMOutputSpec {\n if (!Array.isArray(spec)) return spec\n const [tag, second, ...rest] = spec as unknown as [string, unknown, ...unknown[]]\n if (second && typeof second === 'object' && !Array.isArray(second) && !('nodeType' in second)) {\n const own = second as Record<string, string>\n const style = [own.style, attrs.style].filter(Boolean).join('; ')\n return [tag, { ...own, ...attrs, ...(style ? { style } : {}) }, ...rest] as never\n }\n return [tag, attrs, ...(second === undefined ? [] : [second]), ...rest] as never\n}\n\n/**\n * A text block that can be aligned: adds the `align` attribute, reads it\n * from pasted HTML and draws it. Used by paragraphs, headings and quotes;\n * custom text blocks can use it too.\n */\nexport function alignable(spec: NodeSpec): NodeSpec {\n const toDOM = spec.toDOM\n const normalize = spec.normalizeProps as\n ((attrs: Record<string, unknown>) => Record<string, unknown>) | undefined\n return {\n ...spec,\n // one way to say \"left\": nothing set; anything else is dropped\n normalizeProps: (attrs: Record<string, unknown>) => {\n const next = normalize ? normalize(attrs) : attrs\n if (next.align === undefined || next.align === null || isAlign(next.align)) {\n return next.align === 'left' ? { ...next, align: null } : next\n }\n warnOnce(`align \"${String(next.align)}\" is not one of left, center, right; dropped`)\n return { ...next, align: null }\n },\n attrs: { ...spec.attrs, align: { default: null } },\n optionalProps: [...((spec.optionalProps as string[] | undefined) ?? []), 'align'],\n parseDOM: spec.parseDOM?.map((rule) => {\n if (!('tag' in rule)) return rule\n const tagRule = rule as TagParseRule\n return {\n ...tagRule,\n getAttrs: (dom: HTMLElement) => {\n const own = tagRule.getAttrs ? tagRule.getAttrs(dom) : (tagRule.attrs ?? null)\n if (own === false) return false\n return { ...(own ?? tagRule.attrs), align: alignOf(dom) }\n },\n }\n }),\n toDOM: toDOM\n ? (node: PMNode) => {\n const out = toDOM(node)\n const align = node.attrs.align as string | null\n return isAlign(align)\n ? withAttrs(out, { 'data-align': align, style: `text-align: ${align}` })\n : out\n }\n : undefined,\n }\n}\n"],"mappings":";;AAMA,IAAa,SAAgC;CAAC;CAAQ;CAAU;AAAO;AAEvE,IAAa,WAAW,UAAwC,OAAO,SAAS,KAAmB;;;;;AAMnG,SAAgB,QAAQ,KAAqC;CAC3D,MAAM,SAAS,IAAI,SAAS,SAAS,IAAI,OAAO,aAAa,IAAI,aAAa,OAAO,KAAK,GAAA,CACvF,YAAY,CAAC,CACb,QAAQ,SAAS,MAAM,CAAC,CACxB,QAAQ,OAAO,OAAO;CACzB,OAAO,QAAQ,KAAK,KAAK,UAAU,SAAS,QAAQ;AACtD;AAEA,SAAS,UAAU,MAAqB,OAA8C;CACpF,IAAI,CAAC,MAAM,QAAQ,IAAI,GAAG,OAAO;CACjC,MAAM,CAAC,KAAK,QAAQ,GAAG,QAAQ;CAC/B,IAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,KAAK,EAAE,cAAc,SAAS;EAC7F,MAAM,MAAM;EACZ,MAAM,QAAQ,CAAC,IAAI,OAAO,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI;EAChE,OAAO;GAAC;GAAK;IAAE,GAAG;IAAK,GAAG;IAAO,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;GAAG;GAAG,GAAG;EAAI;CACzE;CACA,OAAO;EAAC;EAAK;EAAO,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,CAAC,MAAM;EAAI,GAAG;CAAI;AACxE;;;;;;AAOA,SAAgB,UAAU,MAA0B;CAClD,MAAM,QAAQ,KAAK;CACnB,MAAM,YAAY,KAAK;CAEvB,OAAO;EACL,GAAG;EAEH,iBAAiB,UAAmC;GAClD,MAAM,OAAO,YAAY,UAAU,KAAK,IAAI;GAC5C,IAAI,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,QAAQ,QAAQ,KAAK,KAAK,GACvE,OAAO,KAAK,UAAU,SAAS;IAAE,GAAG;IAAM,OAAO;GAAK,IAAI;GAE5D,SAAS,UAAU,OAAO,KAAK,KAAK,EAAE,6CAA6C;GACnF,OAAO;IAAE,GAAG;IAAM,OAAO;GAAK;EAChC;EACA,OAAO;GAAE,GAAG,KAAK;GAAO,OAAO,EAAE,SAAS,KAAK;EAAE;EACjD,eAAe,CAAC,GAAK,KAAK,iBAA0C,CAAC,GAAI,OAAO;EAChF,UAAU,KAAK,UAAU,KAAK,SAAS;GACrC,IAAI,EAAE,SAAS,OAAO,OAAO;GAC7B,MAAM,UAAU;GAChB,OAAO;IACL,GAAG;IACH,WAAW,QAAqB;KAC9B,MAAM,MAAM,QAAQ,WAAW,QAAQ,SAAS,GAAG,IAAK,QAAQ,SAAS;KACzE,IAAI,QAAQ,OAAO,OAAO;KAC1B,OAAO;MAAE,GAAI,OAAO,QAAQ;MAAQ,OAAO,QAAQ,GAAG;KAAE;IAC1D;GACF;EACF,CAAC;EACD,OAAO,SACF,SAAiB;GAChB,MAAM,MAAM,MAAM,IAAI;GACtB,MAAM,QAAQ,KAAK,MAAM;GACzB,OAAO,QAAQ,KAAK,IAChB,UAAU,KAAK;IAAE,cAAc;IAAO,OAAO,eAAe;GAAQ,CAAC,IACrE;EACN,IACA,KAAA;CACN;AACF"}
@@ -0,0 +1,155 @@
1
+ import { blocksIn, findBlockById, selectedRange, siblingRangeBetween } from "./blocks.js";
2
+ import { deleteBlocks, duplicateBlocks, liftBlocks, sinkBlocks, swapBlocks } from "./commands.js";
3
+ import { keydownHandler } from "prosemirror-keymap";
4
+ import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
5
+ import { Decoration, DecorationSet } from "prosemirror-view";
6
+ // src/core/block-selection.ts
7
+ var blockSelectionKey = new PluginKey("dou-editor-block-selection");
8
+ function getBlockSelection(state) {
9
+ return blockSelectionKey.getState(state) ?? null;
10
+ }
11
+ /** The blocks of a block selection, or `null` when they can't be found. */
12
+ function blockSelectionRange(state) {
13
+ const current = getBlockSelection(state);
14
+ if (!current) return null;
15
+ const anchor = findBlockById(state.doc, current.anchor);
16
+ const head = findBlockById(state.doc, current.head);
17
+ if (!anchor || !head) return null;
18
+ const [first, last] = anchor.pos <= head.pos ? [anchor, head] : [head, anchor];
19
+ const range = siblingRangeBetween(state.doc, first.pos + 1, last.pos + 1);
20
+ return range && range.depth === anchor.depth ? range : null;
21
+ }
22
+ /** Put the editor's own selection over the text of the selected blocks. */
23
+ function spanSelection(tr, range) {
24
+ const blocks = blocksIn(range);
25
+ const first = blocks[0];
26
+ const last = blocks.at(-1);
27
+ const $from = tr.doc.resolve(first.contentPos + 1);
28
+ const $to = tr.doc.resolve(last.pos + last.node.nodeSize - 1);
29
+ tr.setSelection(TextSelection.between($from, $to));
30
+ }
31
+ /** Select whole blocks, from `anchor` to `head` (siblings). */
32
+ function selectBlocks(anchor, head = anchor) {
33
+ return (state, dispatch) => {
34
+ const tr = state.tr.setMeta(blockSelectionKey, {
35
+ anchor,
36
+ head
37
+ });
38
+ const range = blockSelectionRange(state.apply(tr));
39
+ if (!range) return false;
40
+ if (dispatch) {
41
+ spanSelection(tr, range);
42
+ tr.setMeta(blockSelectionKey, {
43
+ anchor,
44
+ head
45
+ });
46
+ dispatch(tr.scrollIntoView());
47
+ }
48
+ return true;
49
+ };
50
+ }
51
+ var clearBlockSelection = (state, dispatch) => {
52
+ if (!getBlockSelection(state)) return false;
53
+ if (dispatch) dispatch(state.tr.setMeta(blockSelectionKey, null));
54
+ return true;
55
+ };
56
+ /** `Esc`: select the blocks the cursor or text selection is in. */
57
+ var selectCurrentBlocks = (state, dispatch) => {
58
+ if (getBlockSelection(state)) return false;
59
+ const range = selectedRange(state);
60
+ if (!range) return false;
61
+ const blocks = blocksIn(range);
62
+ return selectBlocks(blocks[0].node.attrs.id, blocks.at(-1).node.attrs.id)(state, dispatch);
63
+ };
64
+ /** Leave the block selection with the cursor at the end of a block. */
65
+ function placeCursor(view, block, atEnd = true) {
66
+ const tr = view.state.tr.setMeta(blockSelectionKey, null);
67
+ const pos = atEnd ? block.contentPos + block.content.nodeSize - 1 : block.contentPos + 1;
68
+ tr.setSelection(TextSelection.near(tr.doc.resolve(pos), atEnd ? -1 : 1));
69
+ view.dispatch(tr.scrollIntoView());
70
+ }
71
+ function moveHead(view, direction, extend) {
72
+ const state = view.state;
73
+ const current = getBlockSelection(state);
74
+ const range = blockSelectionRange(state);
75
+ if (!range) return false;
76
+ const head = findBlockById(state.doc, current.head);
77
+ const index = state.doc.resolve(head.pos).index() + direction;
78
+ if (index < 0 || index >= range.parent.childCount) return true;
79
+ const id = range.parent.child(index).attrs.id;
80
+ return selectBlocks(extend ? current.anchor : id, id)(state, view.dispatch);
81
+ }
82
+ /** Run a block command, then select the same blocks again. */
83
+ function keepSelected(command) {
84
+ return (view) => {
85
+ const current = getBlockSelection(view.state);
86
+ if (!current || !command(view.state, view.dispatch)) return false;
87
+ selectBlocks(current.anchor, current.head)(view.state, view.dispatch);
88
+ return true;
89
+ };
90
+ }
91
+ var removeSelected = (view) => {
92
+ const range = blockSelectionRange(view.state);
93
+ if (!range) return false;
94
+ const tr = deleteBlocks(view.state.tr, blocksIn(range)).setMeta(blockSelectionKey, null);
95
+ view.dispatch(tr.scrollIntoView());
96
+ return true;
97
+ };
98
+ var blockKeys = keydownHandler({
99
+ ArrowUp: (_state, _dispatch, view) => moveHead(view, -1, false),
100
+ ArrowDown: (_state, _dispatch, view) => moveHead(view, 1, false),
101
+ "Shift-ArrowUp": (_state, _dispatch, view) => moveHead(view, -1, true),
102
+ "Shift-ArrowDown": (_state, _dispatch, view) => moveHead(view, 1, true),
103
+ Backspace: (_state, _dispatch, view) => removeSelected(view),
104
+ Delete: (_state, _dispatch, view) => removeSelected(view),
105
+ "Mod-d": (_state, _dispatch, view) => keepSelected(duplicateBlocks)(view),
106
+ "Mod-Shift-ArrowUp": (_state, _dispatch, view) => keepSelected(swapBlocks(-1))(view),
107
+ "Mod-Shift-ArrowDown": (_state, _dispatch, view) => keepSelected(swapBlocks(1))(view),
108
+ Tab: (_state, _dispatch, view) => keepSelected(sinkBlocks)(view),
109
+ "Shift-Tab": (_state, _dispatch, view) => keepSelected(liftBlocks)(view),
110
+ Escape: (state, dispatch) => clearBlockSelection(state, dispatch),
111
+ Enter: (state, _dispatch, view) => {
112
+ const current = getBlockSelection(state);
113
+ const head = findBlockById(state.doc, current.head);
114
+ if (head) placeCursor(view, head);
115
+ return true;
116
+ }
117
+ });
118
+ function blockSelectionPlugin() {
119
+ return new Plugin({
120
+ key: blockSelectionKey,
121
+ state: {
122
+ init: () => null,
123
+ apply(tr, value) {
124
+ const meta = tr.getMeta(blockSelectionKey);
125
+ if (meta !== void 0) return meta;
126
+ if (value && tr.selectionSet && !tr.docChanged) return null;
127
+ return value;
128
+ }
129
+ },
130
+ props: {
131
+ decorations(state) {
132
+ const range = blockSelectionRange(state);
133
+ if (!range) return null;
134
+ return DecorationSet.create(state.doc, blocksIn(range).map((block) => Decoration.node(block.pos, block.pos + block.node.nodeSize, { class: "is-selected" })));
135
+ },
136
+ attributes(state) {
137
+ return getBlockSelection(state) ? { class: "is-block-selecting" } : {};
138
+ },
139
+ handleKeyDown(view, event) {
140
+ const current = getBlockSelection(view.state);
141
+ if (!current) return false;
142
+ if (blockKeys(view, event)) return true;
143
+ if (event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {
144
+ const head = findBlockById(view.state.doc, current.head);
145
+ if (head) placeCursor(view, head);
146
+ return false;
147
+ }
148
+ return false;
149
+ }
150
+ }
151
+ });
152
+ }
153
+ export { blockSelectionKey, blockSelectionPlugin, blockSelectionRange, clearBlockSelection, getBlockSelection, selectBlocks, selectCurrentBlocks };
154
+
155
+ //# sourceMappingURL=block-selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-selection.js","names":[],"sources":["../../src/core/block-selection.ts"],"sourcesContent":["import { keydownHandler } from 'prosemirror-keymap'\nimport {\n Plugin,\n PluginKey,\n TextSelection,\n type Command,\n type EditorState,\n type Transaction,\n} from 'prosemirror-state'\nimport { Decoration, DecorationSet, type EditorView } from 'prosemirror-view'\nimport {\n blocksIn,\n findBlockById,\n selectedRange,\n siblingRangeBetween,\n type BlockInfo,\n type SiblingRange,\n} from './blocks'\nimport { duplicateBlocks, liftBlocks, swapBlocks, deleteBlocks, sinkBlocks } from './commands'\n\n/**\n * Whole blocks selected with `Esc` or from the block handle. The selected\n * blocks are siblings, from `anchor` to `head`. Underneath, the editor's\n * own selection spans their text, so copy and cut work as usual; the text\n * highlight is hidden and the blocks are tinted instead.\n */\nexport interface BlockSelection {\n anchor: string\n head: string\n}\n\nexport const blockSelectionKey = new PluginKey<BlockSelection | null>('dou-editor-block-selection')\n\nexport function getBlockSelection(state: EditorState): BlockSelection | null {\n return blockSelectionKey.getState(state) ?? null\n}\n\n/** The blocks of a block selection, or `null` when they can't be found. */\nexport function blockSelectionRange(state: EditorState): SiblingRange | null {\n const current = getBlockSelection(state)\n if (!current) return null\n const anchor = findBlockById(state.doc, current.anchor)\n const head = findBlockById(state.doc, current.head)\n if (!anchor || !head) return null\n const [first, last] = anchor.pos <= head.pos ? [anchor, head] : [head, anchor]\n const range = siblingRangeBetween(state.doc, first.pos + 1, last.pos + 1)\n return range && range.depth === anchor.depth ? range : null\n}\n\n/** Put the editor's own selection over the text of the selected blocks. */\nfunction spanSelection(tr: Transaction, range: SiblingRange) {\n const blocks = blocksIn(range)\n const first = blocks[0]!\n const last = blocks.at(-1)!\n const $from = tr.doc.resolve(first.contentPos + 1)\n const $to = tr.doc.resolve(last.pos + last.node.nodeSize - 1)\n tr.setSelection(TextSelection.between($from, $to))\n}\n\n/** Select whole blocks, from `anchor` to `head` (siblings). */\nexport function selectBlocks(anchor: string, head = anchor): Command {\n return (state, dispatch) => {\n const tr = state.tr.setMeta(blockSelectionKey, { anchor, head })\n const preview = state.apply(tr)\n const range = blockSelectionRange(preview)\n if (!range) return false\n if (dispatch) {\n spanSelection(tr, range)\n tr.setMeta(blockSelectionKey, { anchor, head })\n dispatch(tr.scrollIntoView())\n }\n return true\n }\n}\n\nexport const clearBlockSelection: Command = (state, dispatch) => {\n if (!getBlockSelection(state)) return false\n if (dispatch) dispatch(state.tr.setMeta(blockSelectionKey, null))\n return true\n}\n\n/** `Esc`: select the blocks the cursor or text selection is in. */\nexport const selectCurrentBlocks: Command = (state, dispatch) => {\n if (getBlockSelection(state)) return false\n const range = selectedRange(state)\n if (!range) return false\n const blocks = blocksIn(range)\n return selectBlocks(blocks[0]!.node.attrs.id, blocks.at(-1)!.node.attrs.id)(state, dispatch)\n}\n\n/** Leave the block selection with the cursor at the end of a block. */\nfunction placeCursor(view: EditorView, block: BlockInfo, atEnd = true) {\n const tr = view.state.tr.setMeta(blockSelectionKey, null)\n const pos = atEnd ? block.contentPos + block.content.nodeSize - 1 : block.contentPos + 1\n tr.setSelection(TextSelection.near(tr.doc.resolve(pos), atEnd ? -1 : 1))\n view.dispatch(tr.scrollIntoView())\n}\n\nfunction moveHead(view: EditorView, direction: -1 | 1, extend: boolean) {\n const state = view.state\n const current = getBlockSelection(state)!\n const range = blockSelectionRange(state)\n if (!range) return false\n const head = findBlockById(state.doc, current.head)!\n const $head = state.doc.resolve(head.pos)\n const index = $head.index() + direction\n if (index < 0 || index >= range.parent.childCount) return true\n const id = range.parent.child(index).attrs.id as string\n return selectBlocks(extend ? current.anchor : id, id)(state, view.dispatch)\n}\n\n/** Run a block command, then select the same blocks again. */\nfunction keepSelected(command: Command): (view: EditorView) => boolean {\n return (view) => {\n const current = getBlockSelection(view.state)\n if (!current || !command(view.state, view.dispatch)) return false\n selectBlocks(current.anchor, current.head)(view.state, view.dispatch)\n return true\n }\n}\n\nconst removeSelected = (view: EditorView) => {\n const range = blockSelectionRange(view.state)\n if (!range) return false\n const tr = deleteBlocks(view.state.tr, blocksIn(range)).setMeta(blockSelectionKey, null)\n view.dispatch(tr.scrollIntoView())\n return true\n}\n\nconst blockKeys = keydownHandler({\n ArrowUp: (_state, _dispatch, view) => moveHead(view!, -1, false),\n ArrowDown: (_state, _dispatch, view) => moveHead(view!, 1, false),\n 'Shift-ArrowUp': (_state, _dispatch, view) => moveHead(view!, -1, true),\n 'Shift-ArrowDown': (_state, _dispatch, view) => moveHead(view!, 1, true),\n Backspace: (_state, _dispatch, view) => removeSelected(view!),\n Delete: (_state, _dispatch, view) => removeSelected(view!),\n 'Mod-d': (_state, _dispatch, view) => keepSelected(duplicateBlocks)(view!),\n 'Mod-Shift-ArrowUp': (_state, _dispatch, view) => keepSelected(swapBlocks(-1))(view!),\n 'Mod-Shift-ArrowDown': (_state, _dispatch, view) => keepSelected(swapBlocks(1))(view!),\n Tab: (_state, _dispatch, view) => keepSelected(sinkBlocks)(view!),\n 'Shift-Tab': (_state, _dispatch, view) => keepSelected(liftBlocks)(view!),\n Escape: (state, dispatch) => clearBlockSelection(state, dispatch),\n Enter: (state, _dispatch, view) => {\n const current = getBlockSelection(state)!\n const head = findBlockById(state.doc, current.head)\n if (head) placeCursor(view!, head)\n return true\n },\n})\n\nexport function blockSelectionPlugin(): Plugin<BlockSelection | null> {\n return new Plugin<BlockSelection | null>({\n key: blockSelectionKey,\n state: {\n init: () => null,\n apply(tr, value) {\n const meta = tr.getMeta(blockSelectionKey) as BlockSelection | null | undefined\n if (meta !== undefined) return meta\n // any other selection change (a click, typing) ends it\n if (value && tr.selectionSet && !tr.docChanged) return null\n return value\n },\n },\n props: {\n decorations(state) {\n const range = blockSelectionRange(state)\n if (!range) return null\n return DecorationSet.create(\n state.doc,\n blocksIn(range).map((block) =>\n Decoration.node(block.pos, block.pos + block.node.nodeSize, { class: 'is-selected' }),\n ),\n )\n },\n attributes(state): Record<string, string> {\n return getBlockSelection(state) ? { class: 'is-block-selecting' } : {}\n },\n handleKeyDown(view, event) {\n const current = getBlockSelection(view.state)\n if (!current) return false\n if (blockKeys(view, event)) return true\n // typing a character: back to text editing at the end of the head block\n if (event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey) {\n const head = findBlockById(view.state.doc, current.head)\n if (head) placeCursor(view, head)\n return false\n }\n return false\n },\n },\n })\n}\n"],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ // src/core/blocks.ts
2
+ function infoAt(node, pos, depth) {
3
+ return {
4
+ node,
5
+ pos,
6
+ depth,
7
+ content: node.firstChild,
8
+ contentPos: pos + 1,
9
+ group: node.childCount > 1 ? node.child(1) : null
10
+ };
11
+ }
12
+ /** The innermost block around a position, or `null` between blocks. */
13
+ function blockAround($pos) {
14
+ for (let depth = $pos.depth; depth > 0; depth--) {
15
+ const node = $pos.node(depth);
16
+ if (node.type.name === "block") return infoAt(node, $pos.before(depth), depth);
17
+ }
18
+ const after = $pos.nodeAfter;
19
+ if (after?.type.name === "block") return infoAt(after, $pos.pos, $pos.depth + 1);
20
+ return null;
21
+ }
22
+ /** The block with an id, by scanning the document. */
23
+ function findBlockById(doc, id) {
24
+ let found = null;
25
+ doc.descendants((node, pos) => {
26
+ if (found) return false;
27
+ if (node.type.name === "block" && node.attrs.id === id) {
28
+ found = infoAt(node, pos, doc.resolve(pos).depth + 1);
29
+ return false;
30
+ }
31
+ return node.type.name === "block" || node.type.name === "blockGroup";
32
+ });
33
+ return found;
34
+ }
35
+ function rangeOf(doc, a, b) {
36
+ const $a = doc.resolve(a.pos);
37
+ const $b = doc.resolve(b.pos);
38
+ return {
39
+ parent: $a.parent,
40
+ parentStart: $a.start(),
41
+ depth: a.depth,
42
+ from: $a.index(),
43
+ to: $b.index(),
44
+ start: a.pos,
45
+ end: b.pos + b.node.nodeSize
46
+ };
47
+ }
48
+ /** The outer block at `depth` that contains `info`. */
49
+ function climb(doc, info, depth) {
50
+ if (info.depth === depth) return info;
51
+ const $pos = doc.resolve(info.pos + 1);
52
+ return infoAt($pos.node(depth), $pos.before(depth), depth);
53
+ }
54
+ /**
55
+ * The sibling blocks a selection spans. When its ends sit at different
56
+ * levels, the range climbs to the level they share.
57
+ */
58
+ function siblingRangeBetween(doc, from, to) {
59
+ let a = blockAround(doc.resolve(from));
60
+ let b = blockAround(doc.resolve(to));
61
+ if (!a || !b) return null;
62
+ let depth = Math.min(a.depth, b.depth);
63
+ for (;;) {
64
+ a = climb(doc, a, depth);
65
+ b = climb(doc, b, depth);
66
+ if (doc.resolve(a.pos).start() === doc.resolve(b.pos).start()) break;
67
+ depth -= 2;
68
+ }
69
+ return rangeOf(doc, a, b);
70
+ }
71
+ function selectedRange(state) {
72
+ const { from, to } = state.selection;
73
+ return siblingRangeBetween(state.doc, from, to);
74
+ }
75
+ /** The blocks of a range, in order. */
76
+ function blocksIn(range) {
77
+ const list = [];
78
+ let pos = range.parentStart;
79
+ range.parent.forEach((node, offset, index) => {
80
+ if (index >= range.from && index <= range.to) list.push(infoAt(node, pos + offset, range.depth));
81
+ });
82
+ return list;
83
+ }
84
+ /** Every block in document order, outer before inner. */
85
+ function allBlocks(doc) {
86
+ const list = [];
87
+ const visit = (container, start, depth) => {
88
+ container.forEach((block, offset) => {
89
+ const pos = start + offset;
90
+ list.push(infoAt(block, pos, depth));
91
+ if (block.childCount > 1) visit(block.child(1), pos + 1 + block.firstChild.nodeSize + 1, depth + 2);
92
+ });
93
+ };
94
+ visit(doc, 0, 1);
95
+ return list;
96
+ }
97
+ /** The deepest last block inside a block (itself when it has no children). */
98
+ function lastDescendant(info) {
99
+ let current = info;
100
+ while (current.group) {
101
+ const group = current.group;
102
+ const lastPos = current.pos + 1 + current.content.nodeSize + 1 + group.content.size - group.lastChild.nodeSize;
103
+ current = infoAt(group.lastChild, lastPos, current.depth + 2);
104
+ }
105
+ return current;
106
+ }
107
+ /** The block that comes before `info` when reading top to bottom. */
108
+ function previousInDocument(doc, info) {
109
+ const $pos = doc.resolve(info.pos);
110
+ const index = $pos.index();
111
+ if (index > 0) {
112
+ const before = $pos.parent.child(index - 1);
113
+ return lastDescendant(infoAt(before, info.pos - before.nodeSize, info.depth));
114
+ }
115
+ if (info.depth > 1) return infoAt($pos.node(info.depth - 2), $pos.before(info.depth - 2), info.depth - 2);
116
+ return null;
117
+ }
118
+ /** The block that comes after `info` when reading top to bottom. */
119
+ function nextInDocument(doc, info) {
120
+ if (info.group) return infoAt(info.group.firstChild, info.pos + 1 + info.content.nodeSize + 1, info.depth + 2);
121
+ let current = info;
122
+ for (;;) {
123
+ const $pos = doc.resolve(current.pos);
124
+ const end = current.pos + current.node.nodeSize;
125
+ if ($pos.index() < $pos.parent.childCount - 1) return infoAt($pos.parent.child($pos.index() + 1), end, current.depth);
126
+ if (current.depth <= 1) return null;
127
+ current = infoAt($pos.node(current.depth - 2), $pos.before(current.depth - 2), current.depth - 2);
128
+ }
129
+ }
130
+ export { allBlocks, blockAround, blocksIn, findBlockById, infoAt, lastDescendant, nextInDocument, previousInDocument, selectedRange, siblingRangeBetween };
131
+
132
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","names":[],"sources":["../../src/core/blocks.ts"],"sourcesContent":["import type { Node as PMNode, ResolvedPos } from 'prosemirror-model'\nimport type { EditorState } from 'prosemirror-state'\n\n/** A `block` node and where it sits. */\nexport interface BlockInfo {\n node: PMNode\n /** The position right before the block. */\n pos: number\n /** Its depth in the document; top-level blocks are at depth 1. */\n depth: number\n /** The block's content node (paragraph, heading, …). */\n content: PMNode\n /** The position right before the content node. */\n contentPos: number\n /** The children, if any. */\n group: PMNode | null\n}\n\nexport function infoAt(node: PMNode, pos: number, depth: number): BlockInfo {\n return {\n node,\n pos,\n depth,\n content: node.firstChild!,\n contentPos: pos + 1,\n group: node.childCount > 1 ? node.child(1) : null,\n }\n}\n\n/** The innermost block around a position, or `null` between blocks. */\nexport function blockAround($pos: ResolvedPos): BlockInfo | null {\n for (let depth = $pos.depth; depth > 0; depth--) {\n const node = $pos.node(depth)\n if (node.type.name === 'block') return infoAt(node, $pos.before(depth), depth)\n }\n // right before a block, e.g. a node selection of it\n const after = $pos.nodeAfter\n if (after?.type.name === 'block') return infoAt(after, $pos.pos, $pos.depth + 1)\n return null\n}\n\n/** The block with an id, by scanning the document. */\nexport function findBlockById(doc: PMNode, id: string): BlockInfo | null {\n let found: BlockInfo | null = null\n doc.descendants((node, pos) => {\n if (found) return false\n if (node.type.name === 'block' && node.attrs.id === id) {\n found = infoAt(node, pos, doc.resolve(pos).depth + 1)\n return false\n }\n // blocks only live in the doc and in groups; skip inline content\n return node.type.name === 'block' || node.type.name === 'blockGroup'\n })\n return found\n}\n\n/** A run of sibling blocks inside one container (the doc or a group). */\nexport interface SiblingRange {\n /** The container: the doc or a `blockGroup`. */\n parent: PMNode\n /** The position where the container's content starts. */\n parentStart: number\n /** Depth of the blocks. */\n depth: number\n /** First and last index, inclusive. */\n from: number\n to: number\n /** Positions before the first and after the last block. */\n start: number\n end: number\n}\n\nfunction rangeOf(doc: PMNode, a: BlockInfo, b: BlockInfo): SiblingRange {\n const $a = doc.resolve(a.pos)\n const $b = doc.resolve(b.pos)\n const parent = $a.parent\n return {\n parent,\n parentStart: $a.start(),\n depth: a.depth,\n from: $a.index(),\n to: $b.index(),\n start: a.pos,\n end: b.pos + b.node.nodeSize,\n }\n}\n\n/** The outer block at `depth` that contains `info`. */\nfunction climb(doc: PMNode, info: BlockInfo, depth: number): BlockInfo {\n if (info.depth === depth) return info\n const $pos = doc.resolve(info.pos + 1)\n return infoAt($pos.node(depth), $pos.before(depth), depth)\n}\n\n/**\n * The sibling blocks a selection spans. When its ends sit at different\n * levels, the range climbs to the level they share.\n */\nexport function siblingRangeBetween(doc: PMNode, from: number, to: number): SiblingRange | null {\n let a = blockAround(doc.resolve(from))\n let b = blockAround(doc.resolve(to))\n if (!a || !b) return null\n // climb two levels at a time (block > group > block) until both share a parent\n let depth = Math.min(a.depth, b.depth)\n for (;;) {\n a = climb(doc, a, depth)\n b = climb(doc, b, depth)\n if (doc.resolve(a.pos).start() === doc.resolve(b.pos).start()) break\n depth -= 2\n }\n return rangeOf(doc, a, b)\n}\n\nexport function selectedRange(state: EditorState): SiblingRange | null {\n const { from, to } = state.selection\n return siblingRangeBetween(state.doc, from, to)\n}\n\n/** The blocks of a range, in order. */\nexport function blocksIn(range: SiblingRange): BlockInfo[] {\n const list: BlockInfo[] = []\n let pos = range.parentStart\n range.parent.forEach((node, offset, index) => {\n if (index >= range.from && index <= range.to) list.push(infoAt(node, pos + offset, range.depth))\n })\n return list\n}\n\n/** Every block in document order, outer before inner. */\nexport function allBlocks(doc: PMNode): BlockInfo[] {\n const list: BlockInfo[] = []\n // walked by hand: resolving every position would scan the document again each time\n const visit = (container: PMNode, start: number, depth: number) => {\n container.forEach((block, offset) => {\n const pos = start + offset\n list.push(infoAt(block, pos, depth))\n if (block.childCount > 1)\n visit(block.child(1), pos + 1 + block.firstChild!.nodeSize + 1, depth + 2)\n })\n }\n visit(doc, 0, 1)\n return list\n}\n\n/** The deepest last block inside a block (itself when it has no children). */\nexport function lastDescendant(info: BlockInfo): BlockInfo {\n let current = info\n while (current.group) {\n const group = current.group\n const lastPos =\n current.pos +\n 1 +\n current.content.nodeSize +\n 1 +\n group.content.size -\n group.lastChild!.nodeSize\n current = infoAt(group.lastChild!, lastPos, current.depth + 2)\n }\n return current\n}\n\n/** The block that comes before `info` when reading top to bottom. */\nexport function previousInDocument(doc: PMNode, info: BlockInfo): BlockInfo | null {\n const $pos = doc.resolve(info.pos)\n const index = $pos.index()\n if (index > 0) {\n const before = $pos.parent.child(index - 1)\n return lastDescendant(infoAt(before, info.pos - before.nodeSize, info.depth))\n }\n // the first child: the previous is its parent block\n if (info.depth > 1)\n return infoAt($pos.node(info.depth - 2), $pos.before(info.depth - 2), info.depth - 2)\n return null\n}\n\n/** The block that comes after `info` when reading top to bottom. */\nexport function nextInDocument(doc: PMNode, info: BlockInfo): BlockInfo | null {\n if (info.group)\n return infoAt(info.group.firstChild!, info.pos + 1 + info.content.nodeSize + 1, info.depth + 2)\n let current = info\n for (;;) {\n const $pos = doc.resolve(current.pos)\n const end = current.pos + current.node.nodeSize\n if ($pos.index() < $pos.parent.childCount - 1)\n return infoAt($pos.parent.child($pos.index() + 1), end, current.depth)\n if (current.depth <= 1) return null\n current = infoAt(\n $pos.node(current.depth - 2),\n $pos.before(current.depth - 2),\n current.depth - 2,\n )\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ // src/core/changes.ts
2
+ var indexes = /* @__PURE__ */ new WeakMap();
3
+ function indexOf(doc) {
4
+ const cached = indexes.get(doc);
5
+ if (cached) return cached;
6
+ const blocks = /* @__PURE__ */ new Map();
7
+ const children = /* @__PURE__ */ new Map();
8
+ const visit = (container, parent) => {
9
+ const ids = [];
10
+ container.forEach((block) => {
11
+ const id = block.attrs.id;
12
+ ids.push(id);
13
+ blocks.set(id, {
14
+ content: block.firstChild,
15
+ parent
16
+ });
17
+ if (block.childCount > 1) visit(block.child(1), id);
18
+ });
19
+ children.set(parent ?? "", ids);
20
+ };
21
+ visit(doc, null);
22
+ const index = {
23
+ blocks,
24
+ children
25
+ };
26
+ indexes.set(doc, index);
27
+ return index;
28
+ }
29
+ /** The positions that are not part of a longest increasing run: the fewest that moved. */
30
+ function outOfOrder(order) {
31
+ const tails = [];
32
+ const previous = Array.from({ length: order.length });
33
+ for (let i = 0; i < order.length; i++) {
34
+ let low = 0;
35
+ let high = tails.length;
36
+ while (low < high) {
37
+ const mid = low + high >> 1;
38
+ if (order[tails[mid]] < order[i]) low = mid + 1;
39
+ else high = mid;
40
+ }
41
+ previous[i] = low > 0 ? tails[low - 1] : -1;
42
+ tails[low] = i;
43
+ }
44
+ const kept = /* @__PURE__ */ new Set();
45
+ for (let i = tails.at(-1) ?? -1; i >= 0; i = previous[i]) kept.add(i);
46
+ const moved = /* @__PURE__ */ new Set();
47
+ for (let i = 0; i < order.length; i++) if (!kept.has(i)) moved.add(i);
48
+ return moved;
49
+ }
50
+ /** What changed between two documents, block by block. */
51
+ function diffBlocks(before, after) {
52
+ const changes = {
53
+ added: [],
54
+ removed: [],
55
+ updated: [],
56
+ moved: []
57
+ };
58
+ if (before === after) return changes;
59
+ const old = indexOf(before);
60
+ const next = indexOf(after);
61
+ for (const [id, placed] of next.blocks) {
62
+ const was = old.blocks.get(id);
63
+ if (!was) {
64
+ changes.added.push(id);
65
+ continue;
66
+ }
67
+ if (was.content !== placed.content && !was.content.eq(placed.content)) changes.updated.push(id);
68
+ if (was.parent !== placed.parent) changes.moved.push(id);
69
+ }
70
+ for (const id of old.blocks.keys()) if (!next.blocks.has(id)) changes.removed.push(id);
71
+ for (const [parent, ids] of next.children) {
72
+ const oldIds = old.children.get(parent);
73
+ if (!oldIds) continue;
74
+ if (oldIds === ids) continue;
75
+ const rank = new Map(oldIds.map((id, i) => [id, i]));
76
+ const stayed = ids.filter((id) => rank.has(id));
77
+ const order = stayed.map((id) => rank.get(id));
78
+ if (order.every((value, i) => i === 0 || order[i - 1] < value)) continue;
79
+ for (const i of outOfOrder(order)) changes.moved.push(stayed[i]);
80
+ }
81
+ return changes;
82
+ }
83
+ export { diffBlocks };
84
+
85
+ //# sourceMappingURL=changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.js","names":[],"sources":["../../src/core/changes.ts"],"sourcesContent":["import type { Node as PMNode } from 'prosemirror-model'\n\n/** Which blocks a change touched, by id. */\nexport interface BlockChanges {\n /** New blocks, including those pasted or split off. */\n added: string[]\n /** Blocks that are gone. */\n removed: string[]\n /** Blocks whose type, props, text or rows changed (not their children). */\n updated: string[]\n /** Blocks that got another parent or another place among their siblings. */\n moved: string[]\n}\n\ninterface Placed {\n content: PMNode\n parent: string | null\n}\n\ninterface DocIndex {\n blocks: Map<string, Placed>\n /** Child ids in order, by parent id (`''` for the top level). */\n children: Map<string, string[]>\n}\n\nconst indexes = new WeakMap<PMNode, DocIndex>()\n\nfunction indexOf(doc: PMNode): DocIndex {\n const cached = indexes.get(doc)\n if (cached) return cached\n const blocks = new Map<string, Placed>()\n const children = new Map<string, string[]>()\n const visit = (container: PMNode, parent: string | null) => {\n const ids: string[] = []\n container.forEach((block) => {\n const id = block.attrs.id as string\n ids.push(id)\n blocks.set(id, { content: block.firstChild!, parent })\n if (block.childCount > 1) visit(block.child(1), id)\n })\n children.set(parent ?? '', ids)\n }\n visit(doc, null)\n const index = { blocks, children }\n indexes.set(doc, index)\n return index\n}\n\n/** The positions that are not part of a longest increasing run: the fewest that moved. */\nfunction outOfOrder(order: number[]): Set<number> {\n // patience sorting, keeping back links to rebuild the run\n const tails: number[] = []\n const previous = Array.from<number>({ length: order.length })\n for (let i = 0; i < order.length; i++) {\n let low = 0\n let high = tails.length\n while (low < high) {\n const mid = (low + high) >> 1\n if (order[tails[mid]!]! < order[i]!) low = mid + 1\n else high = mid\n }\n previous[i] = low > 0 ? tails[low - 1]! : -1\n tails[low] = i\n }\n const kept = new Set<number>()\n for (let i = tails.at(-1) ?? -1; i >= 0; i = previous[i]!) kept.add(i)\n const moved = new Set<number>()\n for (let i = 0; i < order.length; i++) if (!kept.has(i)) moved.add(i)\n return moved\n}\n\n/** What changed between two documents, block by block. */\nexport function diffBlocks(before: PMNode, after: PMNode): BlockChanges {\n const changes: BlockChanges = { added: [], removed: [], updated: [], moved: [] }\n if (before === after) return changes\n const old = indexOf(before)\n const next = indexOf(after)\n for (const [id, placed] of next.blocks) {\n const was = old.blocks.get(id)\n if (!was) {\n changes.added.push(id)\n continue\n }\n if (was.content !== placed.content && !was.content.eq(placed.content)) changes.updated.push(id)\n if (was.parent !== placed.parent) changes.moved.push(id)\n }\n for (const id of old.blocks.keys()) if (!next.blocks.has(id)) changes.removed.push(id)\n // among siblings that stayed with the same parent, the fewest that changed place\n for (const [parent, ids] of next.children) {\n const oldIds = old.children.get(parent)\n if (!oldIds) continue\n if (oldIds === ids) continue\n const rank = new Map(oldIds.map((id, i) => [id, i]))\n const stayed = ids.filter((id) => rank.has(id))\n const order = stayed.map((id) => rank.get(id)!)\n // usually nothing changed place: skip the search\n if (order.every((value, i) => i === 0 || order[i - 1]! < value)) continue\n for (const i of outOfOrder(order)) changes.moved.push(stayed[i]!)\n }\n return changes\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ // src/core/colors.ts
2
+ /** The palette names; the colors follow the theme and the light or dark scheme. */
3
+ var textColors = [
4
+ "gray",
5
+ "red",
6
+ "orange",
7
+ "yellow",
8
+ "green",
9
+ "blue",
10
+ "purple",
11
+ "pink"
12
+ ];
13
+ var isColor = (value) => textColors.includes(value);
14
+ export { isColor, textColors };
15
+
16
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","names":[],"sources":["../../src/core/colors.ts"],"sourcesContent":["/** The palette names; the colors follow the theme and the light or dark scheme. */\nexport const textColors = [\n 'gray',\n 'red',\n 'orange',\n 'yellow',\n 'green',\n 'blue',\n 'purple',\n 'pink',\n] as const\n\nexport type TextColor = (typeof textColors)[number]\n\nexport const isColor = (value: unknown): value is TextColor =>\n textColors.includes(value as TextColor)\n"],"mappings":";;AACA,IAAa,aAAa;CACxB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAIA,IAAa,WAAW,UACtB,WAAW,SAAS,KAAkB"}