@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.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/catalog.js +18 -0
- package/dist/catalog.js.map +1 -0
- package/dist/code/code-editor-block.js +6 -0
- package/dist/code/code-editor-block.js.map +1 -0
- package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js +116 -0
- package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/code/index.js +69 -0
- package/dist/code/index.js.map +1 -0
- package/dist/code/languages.js +51 -0
- package/dist/code/languages.js.map +1 -0
- package/dist/code/view.js +255 -0
- package/dist/code/view.js.map +1 -0
- package/dist/collaboration/binding.js +381 -0
- package/dist/collaboration/binding.js.map +1 -0
- package/dist/collaboration/cursors.js +150 -0
- package/dist/collaboration/cursors.js.map +1 -0
- package/dist/collaboration/index.js +108 -0
- package/dist/collaboration/index.js.map +1 -0
- package/dist/collaboration/order.js +88 -0
- package/dist/collaboration/order.js.map +1 -0
- package/dist/collaboration/table.js +234 -0
- package/dist/collaboration/table.js.map +1 -0
- package/dist/collaboration/text.js +91 -0
- package/dist/collaboration/text.js.map +1 -0
- package/dist/components/block-handle.js +6 -0
- package/dist/components/block-handle.js.map +1 -0
- package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js +474 -0
- package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/bubble-toolbar.js +6 -0
- package/dist/components/bubble-toolbar.js.map +1 -0
- package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js +381 -0
- package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/comment-button.js +6 -0
- package/dist/components/comment-button.js.map +1 -0
- package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/comment-card.js +6 -0
- package/dist/components/comment-card.js.map +1 -0
- package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js +104 -0
- package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/menu-escape.js +25 -0
- package/dist/components/menu-escape.js.map +1 -0
- package/dist/components/search-bar.js +6 -0
- package/dist/components/search-bar.js.map +1 -0
- package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js +192 -0
- package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/slash-menu.js +6 -0
- package/dist/components/slash-menu.js.map +1 -0
- package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js +164 -0
- package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/core/align.js +94 -0
- package/dist/core/align.js.map +1 -0
- package/dist/core/block-selection.js +155 -0
- package/dist/core/block-selection.js.map +1 -0
- package/dist/core/blocks.js +132 -0
- package/dist/core/blocks.js.map +1 -0
- package/dist/core/changes.js +85 -0
- package/dist/core/changes.js.map +1 -0
- package/dist/core/colors.js +16 -0
- package/dist/core/colors.js.map +1 -0
- package/dist/core/commands.js +379 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/comments.js +186 -0
- package/dist/core/comments.js.map +1 -0
- package/dist/core/compare.js +106 -0
- package/dist/core/compare.js.map +1 -0
- package/dist/core/convert.js +28 -0
- package/dist/core/convert.js.map +1 -0
- package/dist/core/diff.js +141 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/editor.js +138 -0
- package/dist/core/editor.js.map +1 -0
- package/dist/core/extension.js +99 -0
- package/dist/core/extension.js.map +1 -0
- package/dist/core/file-drop.js +56 -0
- package/dist/core/file-drop.js.map +1 -0
- package/dist/core/highlight.js +120 -0
- package/dist/core/highlight.js.map +1 -0
- package/dist/core/input-rules.js +61 -0
- package/dist/core/input-rules.js.map +1 -0
- package/dist/core/keymap.js +38 -0
- package/dist/core/keymap.js.map +1 -0
- package/dist/core/model.js +196 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/output.js +263 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/plugins.js +102 -0
- package/dist/core/plugins.js.map +1 -0
- package/dist/core/prepare.js +102 -0
- package/dist/core/prepare.js.map +1 -0
- package/dist/core/schema.js +158 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/search.js +197 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/selection.js +16 -0
- package/dist/core/selection.js.map +1 -0
- package/dist/core/slash.js +83 -0
- package/dist/core/slash.js.map +1 -0
- package/dist/core/transaction.js +311 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/define-block.js +177 -0
- package/dist/define-block.js.map +1 -0
- package/dist/document.js +157 -0
- package/dist/document.js.map +1 -0
- package/dist/dom.js +48 -0
- package/dist/dom.js.map +1 -0
- package/dist/editor.js +33 -0
- package/dist/editor.js.map +1 -0
- package/dist/editor.vue_vue_type_script_setup_true_lang.js +564 -0
- package/dist/editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/editor2.js +6 -0
- package/dist/editor2.js.map +1 -0
- package/dist/extensions/align/align-menu.js +6 -0
- package/dist/extensions/align/align-menu.js.map +1 -0
- package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js +82 -0
- package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/align/align-sub.js +6 -0
- package/dist/extensions/align/align-sub.js.map +1 -0
- package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js +57 -0
- package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/align/commands.js +40 -0
- package/dist/extensions/align/commands.js.map +1 -0
- package/dist/extensions/align/index.js +27 -0
- package/dist/extensions/align/index.js.map +1 -0
- package/dist/extensions/attachment/attachment-block.js +6 -0
- package/dist/extensions/attachment/attachment-block.js.map +1 -0
- package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js +119 -0
- package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/attachment/files.js +66 -0
- package/dist/extensions/attachment/files.js.map +1 -0
- package/dist/extensions/attachment/index.js +132 -0
- package/dist/extensions/attachment/index.js.map +1 -0
- package/dist/extensions/blocks.js +170 -0
- package/dist/extensions/blocks.js.map +1 -0
- package/dist/extensions/callout/callout-block.js +6 -0
- package/dist/extensions/callout/callout-block.js.map +1 -0
- package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/callout/html.js +61 -0
- package/dist/extensions/callout/html.js.map +1 -0
- package/dist/extensions/callout/index.js +52 -0
- package/dist/extensions/callout/index.js.map +1 -0
- package/dist/extensions/callout/tones.js +12 -0
- package/dist/extensions/callout/tones.js.map +1 -0
- package/dist/extensions/code-block/code-block-view.js +6 -0
- package/dist/extensions/code-block/code-block-view.js.map +1 -0
- package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js +87 -0
- package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/code-block/index.js +98 -0
- package/dist/extensions/code-block/index.js.map +1 -0
- package/dist/extensions/code-block/languages.js +116 -0
- package/dist/extensions/code-block/languages.js.map +1 -0
- package/dist/extensions/colors/color-menu.js +6 -0
- package/dist/extensions/colors/color-menu.js.map +1 -0
- package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js +113 -0
- package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/colors/index.js +49 -0
- package/dist/extensions/colors/index.js.map +1 -0
- package/dist/extensions/colors/palette.js +26 -0
- package/dist/extensions/colors/palette.js.map +1 -0
- package/dist/extensions/editor-kit.js +35 -0
- package/dist/extensions/editor-kit.js.map +1 -0
- package/dist/extensions/embed/embed-block.js +6 -0
- package/dist/extensions/embed/embed-block.js.map +1 -0
- package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js +210 -0
- package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/embed/index.js +118 -0
- package/dist/extensions/embed/index.js.map +1 -0
- package/dist/extensions/embed/targets.js +79 -0
- package/dist/extensions/embed/targets.js.map +1 -0
- package/dist/extensions/formatting.js +153 -0
- package/dist/extensions/formatting.js.map +1 -0
- package/dist/extensions/image/image-block.js +6 -0
- package/dist/extensions/image/image-block.js.map +1 -0
- package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js +206 -0
- package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/image/index.js +125 -0
- package/dist/extensions/image/index.js.map +1 -0
- package/dist/extensions/image/uploads.js +96 -0
- package/dist/extensions/image/uploads.js.map +1 -0
- package/dist/extensions/list-html.js +58 -0
- package/dist/extensions/list-html.js.map +1 -0
- package/dist/extensions/lists.js +182 -0
- package/dist/extensions/lists.js.map +1 -0
- package/dist/extensions/mention/index.js +56 -0
- package/dist/extensions/mention/index.js.map +1 -0
- package/dist/extensions/mention/mention-menu.js +6 -0
- package/dist/extensions/mention/mention-menu.js.map +1 -0
- package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js +132 -0
- package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/mention/state.js +113 -0
- package/dist/extensions/mention/state.js.map +1 -0
- package/dist/extensions/table/commands.js +356 -0
- package/dist/extensions/table/commands.js.map +1 -0
- package/dist/extensions/table/html.js +134 -0
- package/dist/extensions/table/html.js.map +1 -0
- package/dist/extensions/table/index.js +56 -0
- package/dist/extensions/table/index.js.map +1 -0
- package/dist/extensions/table/plugin.js +81 -0
- package/dist/extensions/table/plugin.js.map +1 -0
- package/dist/extensions/table/schema.js +258 -0
- package/dist/extensions/table/schema.js.map +1 -0
- package/dist/extensions/table/table-controls.js +6 -0
- package/dist/extensions/table/table-controls.js.map +1 -0
- package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js +698 -0
- package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/table/view.js +19 -0
- package/dist/extensions/table/view.js.map +1 -0
- package/dist/extensions/toggle/index.js +87 -0
- package/dist/extensions/toggle/index.js.map +1 -0
- package/dist/extensions/toggle/state.js +120 -0
- package/dist/extensions/toggle/state.js.map +1 -0
- package/dist/extensions/toggle/toggle-block.js +6 -0
- package/dist/extensions/toggle/toggle-block.js.map +1 -0
- package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/locale.js +522 -0
- package/dist/locale.js.map +1 -0
- package/dist/markdown/index.js +24 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/style.d.ts +1 -0
- package/dist/style.js +8 -0
- package/dist/types/catalog.d.ts +8 -0
- package/dist/types/code/code-editor-block.vue.d.ts +17 -0
- package/dist/types/code/index.d.ts +18 -0
- package/dist/types/code/languages.d.ts +7 -0
- package/dist/types/code/types.d.ts +37 -0
- package/dist/types/code/view.d.ts +12 -0
- package/dist/types/collaboration/binding.d.ts +32 -0
- package/dist/types/collaboration/cursors.d.ts +26 -0
- package/dist/types/collaboration/index.d.ts +47 -0
- package/dist/types/collaboration/order.d.ts +20 -0
- package/dist/types/collaboration/table.d.ts +25 -0
- package/dist/types/collaboration/text.d.ts +9 -0
- package/dist/types/components/block-handle.vue.d.ts +3 -0
- package/dist/types/components/bubble-toolbar.vue.d.ts +7 -0
- package/dist/types/components/comment-button.vue.d.ts +3 -0
- package/dist/types/components/comment-card.vue.d.ts +3 -0
- package/dist/types/components/menu-escape.d.ts +7 -0
- package/dist/types/components/search-bar.vue.d.ts +22 -0
- package/dist/types/components/slash-menu.vue.d.ts +11 -0
- package/dist/types/core/align.d.ts +16 -0
- package/dist/types/core/block-selection.d.ts +22 -0
- package/dist/types/core/blocks.d.ts +52 -0
- package/dist/types/core/changes.d.ts +14 -0
- package/dist/types/core/colors.d.ts +4 -0
- package/dist/types/core/commands.d.ts +61 -0
- package/dist/types/core/comments.d.ts +44 -0
- package/dist/types/core/compare.d.ts +10 -0
- package/dist/types/core/convert.d.ts +12 -0
- package/dist/types/core/diff.d.ts +49 -0
- package/dist/types/core/editor.d.ts +64 -0
- package/dist/types/core/extension.d.ts +167 -0
- package/dist/types/core/file-drop.d.ts +19 -0
- package/dist/types/core/highlight.d.ts +32 -0
- package/dist/types/core/input-rules.d.ts +21 -0
- package/dist/types/core/keymap.d.ts +8 -0
- package/dist/types/core/model.d.ts +158 -0
- package/dist/types/core/output.d.ts +64 -0
- package/dist/types/core/plugins.d.ts +24 -0
- package/dist/types/core/prepare.d.ts +23 -0
- package/dist/types/core/schema.d.ts +38 -0
- package/dist/types/core/search.d.ts +83 -0
- package/dist/types/core/selection.d.ts +8 -0
- package/dist/types/core/slash.d.ts +21 -0
- package/dist/types/core/transaction.d.ts +97 -0
- package/dist/types/define-block.d.ts +66 -0
- package/dist/types/document.d.ts +46 -0
- package/dist/types/dom.d.ts +17 -0
- package/dist/types/editor.d.ts +252 -0
- package/dist/types/editor.vue.d.ts +210 -0
- package/dist/types/extensions/align/align-menu.vue.d.ts +3 -0
- package/dist/types/extensions/align/align-sub.vue.d.ts +7 -0
- package/dist/types/extensions/align/commands.d.ts +13 -0
- package/dist/types/extensions/align/index.d.ts +7 -0
- package/dist/types/extensions/attachment/attachment-block.vue.d.ts +17 -0
- package/dist/types/extensions/attachment/files.d.ts +6 -0
- package/dist/types/extensions/attachment/index.d.ts +20 -0
- package/dist/types/extensions/blocks.d.ts +8 -0
- package/dist/types/extensions/callout/callout-block.vue.d.ts +14 -0
- package/dist/types/extensions/callout/html.d.ts +4 -0
- package/dist/types/extensions/callout/index.d.ts +3 -0
- package/dist/types/extensions/callout/tones.d.ts +8 -0
- package/dist/types/extensions/code-block/code-block-view.vue.d.ts +16 -0
- package/dist/types/extensions/code-block/index.d.ts +19 -0
- package/dist/types/extensions/code-block/languages.d.ts +9 -0
- package/dist/types/extensions/colors/color-menu.vue.d.ts +3 -0
- package/dist/types/extensions/colors/index.d.ts +7 -0
- package/dist/types/extensions/colors/palette.d.ts +7 -0
- package/dist/types/extensions/editor-kit.d.ts +37 -0
- package/dist/types/extensions/embed/embed-block.vue.d.ts +16 -0
- package/dist/types/extensions/embed/index.d.ts +18 -0
- package/dist/types/extensions/embed/targets.d.ts +32 -0
- package/dist/types/extensions/formatting.d.ts +8 -0
- package/dist/types/extensions/image/image-block.vue.d.ts +16 -0
- package/dist/types/extensions/image/index.d.ts +16 -0
- package/dist/types/extensions/image/uploads.d.ts +35 -0
- package/dist/types/extensions/list-html.d.ts +11 -0
- package/dist/types/extensions/lists.d.ts +6 -0
- package/dist/types/extensions/mention/index.d.ts +11 -0
- package/dist/types/extensions/mention/mention-menu.vue.d.ts +7 -0
- package/dist/types/extensions/mention/state.d.ts +28 -0
- package/dist/types/extensions/table/commands.d.ts +69 -0
- package/dist/types/extensions/table/html.d.ts +4 -0
- package/dist/types/extensions/table/index.d.ts +8 -0
- package/dist/types/extensions/table/plugin.d.ts +4 -0
- package/dist/types/extensions/table/schema.d.ts +23 -0
- package/dist/types/extensions/table/table-controls.vue.d.ts +3 -0
- package/dist/types/extensions/table/view.d.ts +13 -0
- package/dist/types/extensions/toggle/index.d.ts +7 -0
- package/dist/types/extensions/toggle/state.d.ts +17 -0
- package/dist/types/extensions/toggle/toggle-block.vue.d.ts +14 -0
- package/dist/types/index.d.ts +266 -0
- package/dist/types/locale.d.ts +195 -0
- package/dist/types/markdown/index.d.ts +14 -0
- package/dist/types/use-comments.d.ts +17 -0
- package/dist/use-comments.js +141 -0
- package/dist/use-comments.js.map +1 -0
- package/package.json +98 -4
package/dist/document.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { editorKit } from "./extensions/editor-kit.js";
|
|
2
|
+
import { kitOf } from "./core/convert.js";
|
|
3
|
+
// src/document.ts
|
|
4
|
+
/** A document with no blocks, to start from when nothing is saved yet. */
|
|
5
|
+
var emptyDocument = () => ({
|
|
6
|
+
version: 1,
|
|
7
|
+
blocks: []
|
|
8
|
+
});
|
|
9
|
+
var defaultKit;
|
|
10
|
+
/**
|
|
11
|
+
* What is wrong with the shape itself: the document is not an object, or a
|
|
12
|
+
* list in it is not an array. Nothing else can be checked until it is fixed.
|
|
13
|
+
*/
|
|
14
|
+
function shapeProblems(value, lists) {
|
|
15
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return [{
|
|
16
|
+
code: "shape",
|
|
17
|
+
path: "",
|
|
18
|
+
text: "",
|
|
19
|
+
message: "The document is not an object"
|
|
20
|
+
}];
|
|
21
|
+
return lists.filter((key) => !Array.isArray(value[key])).map((key) => ({
|
|
22
|
+
code: "shape",
|
|
23
|
+
path: key,
|
|
24
|
+
text: "",
|
|
25
|
+
message: `"${key}" is not a list`
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The problems of an editor document, before it is saved or shown: an
|
|
30
|
+
* unknown version, blocks or table rows sharing an id, and block types,
|
|
31
|
+
* marks and inline nodes the extensions don't define, which the editor
|
|
32
|
+
* would show as a paragraph or leave out.
|
|
33
|
+
*/
|
|
34
|
+
function checkDocument(document, options = {}) {
|
|
35
|
+
const shape = shapeProblems(document, ["blocks"]);
|
|
36
|
+
if (shape.length) return shape;
|
|
37
|
+
const problems = [];
|
|
38
|
+
if (document.version !== 1) problems.push({
|
|
39
|
+
code: "version",
|
|
40
|
+
path: "version",
|
|
41
|
+
text: String(document.version),
|
|
42
|
+
message: `Version ${String(document.version)} is not known`
|
|
43
|
+
});
|
|
44
|
+
const { schema } = kitOf(options.extensions ?? (defaultKit ??= [editorKit()]));
|
|
45
|
+
const ids = /* @__PURE__ */ new Set();
|
|
46
|
+
const walk = (blocks, at) => blocks.forEach((block, i) => {
|
|
47
|
+
const path = `${at}[${i}]`;
|
|
48
|
+
checkBlock(block, path, schema, ids, problems);
|
|
49
|
+
if (block.children) walk(block.children, `${path}.children`);
|
|
50
|
+
});
|
|
51
|
+
walk(document.blocks, "blocks");
|
|
52
|
+
return problems;
|
|
53
|
+
}
|
|
54
|
+
function checkBlock(block, path, schema, ids, problems) {
|
|
55
|
+
const blockId = block.id;
|
|
56
|
+
if (blockId !== void 0) {
|
|
57
|
+
if (ids.has(blockId)) problems.push({
|
|
58
|
+
code: "duplicate-id",
|
|
59
|
+
blockId,
|
|
60
|
+
path,
|
|
61
|
+
text: blockId,
|
|
62
|
+
message: `The block id "${blockId}" is used more than once`
|
|
63
|
+
});
|
|
64
|
+
ids.add(blockId);
|
|
65
|
+
}
|
|
66
|
+
const type = block.type ?? "paragraph";
|
|
67
|
+
if (!schema.nodes[type]?.isInGroup("blockContent")) problems.push({
|
|
68
|
+
code: "unknown-type",
|
|
69
|
+
blockId,
|
|
70
|
+
path,
|
|
71
|
+
text: type,
|
|
72
|
+
message: `No block type "${type}" is defined; it would show as a paragraph`
|
|
73
|
+
});
|
|
74
|
+
if (Array.isArray(block.content)) block.content.forEach((item, i) => {
|
|
75
|
+
const at = `${path}.content[${i}]`;
|
|
76
|
+
if (item.type === "inline" && !schema.nodes[item.name]?.isInline) problems.push({
|
|
77
|
+
code: "unknown-inline",
|
|
78
|
+
blockId,
|
|
79
|
+
path: at,
|
|
80
|
+
text: item.name,
|
|
81
|
+
message: `No inline node "${item.name}" is defined; it would be left out`
|
|
82
|
+
});
|
|
83
|
+
if (item.type === "text") {
|
|
84
|
+
for (const mark of item.marks ?? []) if (!schema.marks[mark]) problems.push({
|
|
85
|
+
code: "unknown-mark",
|
|
86
|
+
blockId,
|
|
87
|
+
path: at,
|
|
88
|
+
text: mark,
|
|
89
|
+
message: `No mark "${mark}" is defined; it would be left out`
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (block.rows) checkRows(block.rows, `${path}.rows`, blockId, problems);
|
|
94
|
+
}
|
|
95
|
+
function checkRows(rows, path, blockId, problems) {
|
|
96
|
+
const ids = /* @__PURE__ */ new Set();
|
|
97
|
+
rows.forEach((row, i) => {
|
|
98
|
+
if (row.id === void 0) return;
|
|
99
|
+
if (ids.has(row.id)) problems.push({
|
|
100
|
+
code: "duplicate-id",
|
|
101
|
+
blockId,
|
|
102
|
+
path: `${path}[${i}]`,
|
|
103
|
+
text: row.id,
|
|
104
|
+
message: `The row id "${row.id}" is used more than once in a table`
|
|
105
|
+
});
|
|
106
|
+
ids.add(row.id);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
var objects = (value) => (Array.isArray(value) ? value : []).filter((item) => !!item && typeof item === "object");
|
|
110
|
+
/** The saved data as an object of version 1, or an error saying why it can't be. */
|
|
111
|
+
function sourceOf(input) {
|
|
112
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`[@dou-ui/editor] an editor document must be an object, not ${Array.isArray(input) ? "an array" : JSON.stringify(input) ?? String(input)}`);
|
|
113
|
+
const source = input;
|
|
114
|
+
const version = source.version ?? 1;
|
|
115
|
+
if (version !== 1) throw new Error(`[@dou-ui/editor] unknown document version ${JSON.stringify(version) ?? String(version)} (expected 1); data saved by a newer @dou-ui/editor needs that version to open`);
|
|
116
|
+
return source;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Brings a saved document up to the current version, and fills what an
|
|
120
|
+
* older or hand-written one may lack (`version`, `blocks`). Blocks that are
|
|
121
|
+
* not objects are dropped; anything else is kept as it is, for the editor
|
|
122
|
+
* to read as it reads partial blocks. Throws for data that is not an object (an array,
|
|
123
|
+
* `null`, a string) and for a version it does not know.
|
|
124
|
+
*/
|
|
125
|
+
function migrateDocument(input) {
|
|
126
|
+
const source = sourceOf(input);
|
|
127
|
+
return {
|
|
128
|
+
...source,
|
|
129
|
+
version: 1,
|
|
130
|
+
blocks: objects(source.blocks)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
var warned = /* @__PURE__ */ new Set();
|
|
134
|
+
/**
|
|
135
|
+
* The blocks of the `v-model`, as the editor reads them. A document of the
|
|
136
|
+
* wrong shape (not an object, a bare list of blocks, without `blocks` or a
|
|
137
|
+
* known `version`) gets a warning in development and is read as well as it
|
|
138
|
+
* can be: a list as the blocks, anything else missing as no blocks. What
|
|
139
|
+
* the editor writes back always carries `version: 1`. `undefined`, no
|
|
140
|
+
* `v-model` bound, is fine.
|
|
141
|
+
*/
|
|
142
|
+
function readBlocks(value) {
|
|
143
|
+
if (value === void 0) return void 0;
|
|
144
|
+
const plain = !!value && typeof value === "object" && !Array.isArray(value);
|
|
145
|
+
const source = plain ? value : {};
|
|
146
|
+
const blocks = Array.isArray(source.blocks);
|
|
147
|
+
const problem = Array.isArray(value) ? "is a list of blocks" : !plain ? `is ${JSON.stringify(value) ?? String(value)}` : !blocks ? "has no blocks array" : source.version === void 0 ? "has no version" : source.version !== 1 ? `has version ${JSON.stringify(source.version)}` : "";
|
|
148
|
+
if (problem && process.env.NODE_ENV !== "production" && !warned.has(problem)) {
|
|
149
|
+
warned.add(problem);
|
|
150
|
+
console.warn(`[@dou-ui/editor] the v-model ${problem}; expected { version: 1, blocks: [...] }. It is read as well as it can be and the version as 1; read saved data with migrateDocument().`);
|
|
151
|
+
}
|
|
152
|
+
if (Array.isArray(value)) return value;
|
|
153
|
+
return blocks ? source.blocks : [];
|
|
154
|
+
}
|
|
155
|
+
export { checkDocument, emptyDocument, migrateDocument, readBlocks };
|
|
156
|
+
|
|
157
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","names":[],"sources":["../src/document.ts"],"sourcesContent":["import type { Schema } from 'prosemirror-model'\nimport { kitOf, type EditorExtensionList } from './core/convert'\nimport type { Block, EditorDocument, PartialBlock, PartialTableRow } from './core/model'\nimport { editorKit } from './extensions/editor-kit'\n\n/** A document with no blocks, to start from when nothing is saved yet. */\nexport const emptyDocument = (): EditorDocument<Block> => ({ version: 1, blocks: [] })\n\nexport interface DocumentProblem {\n code: 'version' | 'shape' | 'duplicate-id' | 'unknown-type' | 'unknown-mark' | 'unknown-inline'\n /** The block at fault, when it has an id. */\n blockId?: string\n /**\n * Where the problem is, as a path into the document, e.g.\n * `blocks[0].children[2]` or `blocks[1].content[0]`; `version` for the version.\n */\n path: string\n /** The id, type, mark or inline node at fault, for the editor's message. */\n text: string\n /** An English description, for logs. */\n message: string\n}\n\nexport interface CheckDocumentOptions {\n /** The editor's `extensions`, for the blocks, marks and inline nodes they add; `[editorKit()]` by default, as in the editor. */\n extensions?: EditorExtensionList\n}\n\nlet defaultKit: EditorExtensionList | undefined\n\n/**\n * What is wrong with the shape itself: the document is not an object, or a\n * list in it is not an array. Nothing else can be checked until it is fixed.\n */\nfunction shapeProblems(value: unknown, lists: string[]): DocumentProblem[] {\n if (!value || typeof value !== 'object' || Array.isArray(value))\n return [{ code: 'shape', path: '', text: '', message: 'The document is not an object' }]\n return lists\n .filter((key) => !Array.isArray((value as Record<string, unknown>)[key]))\n .map((key) => ({ code: 'shape', path: key, text: '', message: `\"${key}\" is not a list` }))\n}\n\n/**\n * The problems of an editor document, before it is saved or shown: an\n * unknown version, blocks or table rows sharing an id, and block types,\n * marks and inline nodes the extensions don't define, which the editor\n * would show as a paragraph or leave out.\n */\nexport function checkDocument(\n document: EditorDocument<PartialBlock>,\n options: CheckDocumentOptions = {},\n): DocumentProblem[] {\n const shape = shapeProblems(document, ['blocks'])\n if (shape.length) return shape\n const problems: DocumentProblem[] = []\n if (document.version !== 1)\n problems.push({\n code: 'version',\n path: 'version',\n text: String(document.version),\n message: `Version ${String(document.version)} is not known`,\n })\n const { schema } = kitOf(options.extensions ?? (defaultKit ??= [editorKit()]))\n const ids = new Set<string>()\n const walk = (blocks: readonly PartialBlock[], at: string) =>\n blocks.forEach((block, i) => {\n const path = `${at}[${i}]`\n checkBlock(block, path, schema, ids, problems)\n if (block.children) walk(block.children, `${path}.children`)\n })\n walk(document.blocks, 'blocks')\n return problems\n}\n\nfunction checkBlock(\n block: PartialBlock,\n path: string,\n schema: Schema,\n ids: Set<string>,\n problems: DocumentProblem[],\n) {\n const blockId = block.id\n if (blockId !== undefined) {\n if (ids.has(blockId))\n problems.push({\n code: 'duplicate-id',\n blockId,\n path,\n text: blockId,\n message: `The block id \"${blockId}\" is used more than once`,\n })\n ids.add(blockId)\n }\n const type = block.type ?? 'paragraph'\n if (!schema.nodes[type]?.isInGroup('blockContent'))\n problems.push({\n code: 'unknown-type',\n blockId,\n path,\n text: type,\n message: `No block type \"${type}\" is defined; it would show as a paragraph`,\n })\n if (Array.isArray(block.content))\n block.content.forEach((item, i) => {\n const at = `${path}.content[${i}]`\n if (item.type === 'inline' && !schema.nodes[item.name]?.isInline)\n problems.push({\n code: 'unknown-inline',\n blockId,\n path: at,\n text: item.name,\n message: `No inline node \"${item.name}\" is defined; it would be left out`,\n })\n if (item.type === 'text')\n for (const mark of item.marks ?? [])\n if (!schema.marks[mark])\n problems.push({\n code: 'unknown-mark',\n blockId,\n path: at,\n text: mark,\n message: `No mark \"${mark}\" is defined; it would be left out`,\n })\n })\n if (block.rows) checkRows(block.rows, `${path}.rows`, blockId, problems)\n}\n\nfunction checkRows(\n rows: readonly PartialTableRow[],\n path: string,\n blockId: string | undefined,\n problems: DocumentProblem[],\n) {\n const ids = new Set<string>()\n rows.forEach((row, i) => {\n if (row.id === undefined) return\n if (ids.has(row.id))\n problems.push({\n code: 'duplicate-id',\n blockId,\n path: `${path}[${i}]`,\n text: row.id,\n message: `The row id \"${row.id}\" is used more than once in a table`,\n })\n ids.add(row.id)\n })\n}\n\nconst objects = <T>(value: unknown) =>\n (Array.isArray(value) ? value : []).filter(\n (item): item is T => !!item && typeof item === 'object',\n )\n\n/** The saved data as an object of version 1, or an error saying why it can't be. */\nfunction sourceOf(input: unknown): Record<string, unknown> {\n if (!input || typeof input !== 'object' || Array.isArray(input))\n throw new Error(\n `[@dou-ui/editor] an editor document must be an object, not ${Array.isArray(input) ? 'an array' : (JSON.stringify(input) ?? String(input))}`,\n )\n const source = input as Record<string, unknown>\n const version = source.version ?? 1\n if (version !== 1)\n throw new Error(\n `[@dou-ui/editor] unknown document version ${JSON.stringify(version) ?? String(version)} (expected 1); data saved by a newer @dou-ui/editor needs that version to open`,\n )\n return source\n}\n\n/**\n * Brings a saved document up to the current version, and fills what an\n * older or hand-written one may lack (`version`, `blocks`). Blocks that are\n * not objects are dropped; anything else is kept as it is, for the editor\n * to read as it reads partial blocks. Throws for data that is not an object (an array,\n * `null`, a string) and for a version it does not know.\n */\nexport function migrateDocument(input: unknown): EditorDocument<PartialBlock> {\n const source = sourceOf(input)\n return { ...source, version: 1, blocks: objects<PartialBlock>(source.blocks) }\n}\n\nconst warned = new Set<string>()\n\n/**\n * The blocks of the `v-model`, as the editor reads them. A document of the\n * wrong shape (not an object, a bare list of blocks, without `blocks` or a\n * known `version`) gets a warning in development and is read as well as it\n * can be: a list as the blocks, anything else missing as no blocks. What\n * the editor writes back always carries `version: 1`. `undefined`, no\n * `v-model` bound, is fine.\n */\nexport function readBlocks(value: unknown): PartialBlock[] | undefined {\n if (value === undefined) return undefined\n const plain = !!value && typeof value === 'object' && !Array.isArray(value)\n const source = (plain ? value : {}) as Partial<EditorDocument<PartialBlock>>\n const blocks = Array.isArray(source.blocks)\n const problem = Array.isArray(value)\n ? 'is a list of blocks'\n : !plain\n ? `is ${JSON.stringify(value) ?? String(value)}`\n : !blocks\n ? 'has no blocks array'\n : source.version === undefined\n ? 'has no version'\n : source.version !== 1\n ? `has version ${JSON.stringify(source.version)}`\n : ''\n if (problem && process.env.NODE_ENV !== 'production' && !warned.has(problem)) {\n warned.add(problem)\n console.warn(\n `[@dou-ui/editor] the v-model ${problem}; expected { version: 1, blocks: [...] }. It is read as well as it can be and the version as 1; read saved data with migrateDocument().`,\n )\n }\n if (Array.isArray(value)) return value as PartialBlock[]\n return blocks ? source.blocks : []\n}\n"],"mappings":"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"}
|
package/dist/dom.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/dom.ts
|
|
2
|
+
/**
|
|
3
|
+
* The content element (paragraph, heading, …) at height `y` in viewport
|
|
4
|
+
* coordinates: the last one whose top is above it. Contents follow each
|
|
5
|
+
* other from top to bottom in document order, nested ones included, so
|
|
6
|
+
* a binary search does.
|
|
7
|
+
*/
|
|
8
|
+
function contentAt(view, y) {
|
|
9
|
+
const list = view.dom.querySelectorAll(".dou-editor__content");
|
|
10
|
+
let low = 0;
|
|
11
|
+
let high = list.length - 1;
|
|
12
|
+
let found = -1;
|
|
13
|
+
while (low <= high) {
|
|
14
|
+
const middle = low + high >> 1;
|
|
15
|
+
if (list[middle].getBoundingClientRect().top <= y) {
|
|
16
|
+
found = middle;
|
|
17
|
+
low = middle + 1;
|
|
18
|
+
} else high = middle - 1;
|
|
19
|
+
}
|
|
20
|
+
return list[Math.max(found, 0)] ?? null;
|
|
21
|
+
}
|
|
22
|
+
/** The block element around a content element. */
|
|
23
|
+
function blockElementOf(content) {
|
|
24
|
+
return content.closest(".dou-editor__block");
|
|
25
|
+
}
|
|
26
|
+
/** The nearest ancestor that scrolls vertically, or `null` for the page. */
|
|
27
|
+
function scrollParent(element) {
|
|
28
|
+
for (let node = element.parentElement; node; node = node.parentElement) {
|
|
29
|
+
const { overflowY } = getComputedStyle(node);
|
|
30
|
+
if ((overflowY === "auto" || overflowY === "scroll") && node.scrollHeight > node.clientHeight) return node;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/** Where the first line of a content element sits: its top and height. */
|
|
35
|
+
function firstLine(content) {
|
|
36
|
+
const rect = content.getBoundingClientRect();
|
|
37
|
+
const style = getComputedStyle(content);
|
|
38
|
+
const lineHeight = Number.parseFloat(style.lineHeight);
|
|
39
|
+
const padding = Number.parseFloat(style.paddingTop) || 0;
|
|
40
|
+
const height = Number.isFinite(lineHeight) ? Math.min(lineHeight, rect.height) : rect.height;
|
|
41
|
+
return {
|
|
42
|
+
top: rect.top + (height < rect.height ? padding : 0),
|
|
43
|
+
height
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export { blockElementOf, contentAt, firstLine, scrollParent };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=dom.js.map
|
package/dist/dom.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.js","names":[],"sources":["../src/dom.ts"],"sourcesContent":["import type { EditorView } from 'prosemirror-view'\n\n/**\n * The content element (paragraph, heading, …) at height `y` in viewport\n * coordinates: the last one whose top is above it. Contents follow each\n * other from top to bottom in document order, nested ones included, so\n * a binary search does.\n */\nexport function contentAt(view: EditorView, y: number): HTMLElement | null {\n const list = view.dom.querySelectorAll<HTMLElement>('.dou-editor__content')\n let low = 0\n let high = list.length - 1\n let found = -1\n while (low <= high) {\n const middle = (low + high) >> 1\n if (list[middle]!.getBoundingClientRect().top <= y) {\n found = middle\n low = middle + 1\n } else high = middle - 1\n }\n return list[Math.max(found, 0)] ?? null\n}\n\n/** The block element around a content element. */\nexport function blockElementOf(content: HTMLElement): HTMLElement | null {\n return content.closest<HTMLElement>('.dou-editor__block')\n}\n\n/** The nearest ancestor that scrolls vertically, or `null` for the page. */\nexport function scrollParent(element: HTMLElement): HTMLElement | null {\n for (let node = element.parentElement; node; node = node.parentElement) {\n const { overflowY } = getComputedStyle(node)\n if ((overflowY === 'auto' || overflowY === 'scroll') && node.scrollHeight > node.clientHeight)\n return node\n }\n return null\n}\n\n/** Where the first line of a content element sits: its top and height. */\nexport function firstLine(content: HTMLElement): { top: number; height: number } {\n const rect = content.getBoundingClientRect()\n const style = getComputedStyle(content)\n const lineHeight = Number.parseFloat(style.lineHeight)\n const padding = Number.parseFloat(style.paddingTop) || 0\n const height = Number.isFinite(lineHeight) ? Math.min(lineHeight, rect.height) : rect.height\n return { top: rect.top + (height < rect.height ? padding : 0), height }\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,UAAU,MAAkB,GAA+B;CACzE,MAAM,OAAO,KAAK,IAAI,iBAA8B,sBAAsB;CAC1E,IAAI,MAAM;CACV,IAAI,OAAO,KAAK,SAAS;CACzB,IAAI,QAAQ;CACZ,OAAO,OAAO,MAAM;EAClB,MAAM,SAAU,MAAM,QAAS;EAC/B,IAAI,KAAK,OAAO,CAAE,sBAAsB,CAAC,CAAC,OAAO,GAAG;GAClD,QAAQ;GACR,MAAM,SAAS;EACjB,OAAO,OAAO,SAAS;CACzB;CACA,OAAO,KAAK,KAAK,IAAI,OAAO,CAAC,MAAM;AACrC;;AAGA,SAAgB,eAAe,SAA0C;CACvE,OAAO,QAAQ,QAAqB,oBAAoB;AAC1D;;AAGA,SAAgB,aAAa,SAA0C;CACrE,KAAK,IAAI,OAAO,QAAQ,eAAe,MAAM,OAAO,KAAK,eAAe;EACtE,MAAM,EAAE,cAAc,iBAAiB,IAAI;EAC3C,KAAK,cAAc,UAAU,cAAc,aAAa,KAAK,eAAe,KAAK,cAC/E,OAAO;CACX;CACA,OAAO;AACT;;AAGA,SAAgB,UAAU,SAAuD;CAC/E,MAAM,OAAO,QAAQ,sBAAsB;CAC3C,MAAM,QAAQ,iBAAiB,OAAO;CACtC,MAAM,aAAa,OAAO,WAAW,MAAM,UAAU;CACrD,MAAM,UAAU,OAAO,WAAW,MAAM,UAAU,KAAK;CACvD,MAAM,SAAS,OAAO,SAAS,UAAU,IAAI,KAAK,IAAI,YAAY,KAAK,MAAM,IAAI,KAAK;CACtF,OAAO;EAAE,KAAK,KAAK,OAAO,SAAS,KAAK,SAAS,UAAU;EAAI;CAAO;AACxE"}
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { inject, onBeforeUnmount, provide, shallowReactive, shallowRef } from "vue";
|
|
2
|
+
// src/editor.ts
|
|
3
|
+
var editorKey = Symbol("DouEditor");
|
|
4
|
+
var toolbarMenusKey = Symbol("DouEditorToolbarMenus");
|
|
5
|
+
/** For the formatting toolbar: the open state of the menus in it. */
|
|
6
|
+
function provideToolbarMenus() {
|
|
7
|
+
const menus = shallowReactive(/* @__PURE__ */ new Set());
|
|
8
|
+
provide(toolbarMenusKey, menus);
|
|
9
|
+
return menus;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The open state for a menu in the formatting toolbar (an extension's
|
|
13
|
+
* `toolbar` component), to bind with `v-model:open`: the toolbar stays
|
|
14
|
+
* while the menu is open, though the focus has left the editor.
|
|
15
|
+
*/
|
|
16
|
+
function useToolbarMenu() {
|
|
17
|
+
const open = shallowRef(false);
|
|
18
|
+
const menus = inject(toolbarMenusKey, null);
|
|
19
|
+
if (menus) {
|
|
20
|
+
menus.add(open);
|
|
21
|
+
onBeforeUnmount(() => menus.delete(open));
|
|
22
|
+
}
|
|
23
|
+
return open;
|
|
24
|
+
}
|
|
25
|
+
/** The editor around a block component, for its messages, state and view. */
|
|
26
|
+
function useEditor() {
|
|
27
|
+
const context = inject(editorKey, null);
|
|
28
|
+
if (!context) throw new Error("[@dou-ui/editor] useEditor() must be called inside <DouEditor>");
|
|
29
|
+
return context;
|
|
30
|
+
}
|
|
31
|
+
export { editorKey, provideToolbarMenus, useEditor, useToolbarMenu };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.js","names":[],"sources":["../src/editor.ts"],"sourcesContent":["import type { EditorState, Command, Plugin } from 'prosemirror-state'\nimport type { EditorView } from 'prosemirror-view'\nimport type { CommentAction, CommentAuthor, CommentThread } from 'dou-ui'\nimport {\n inject,\n onBeforeUnmount,\n provide,\n shallowReactive,\n shallowRef,\n type ComputedRef,\n type InjectionKey,\n type ShallowRef,\n} from 'vue'\nimport type { CommentAnchor, CommentRange } from './core/comments'\nimport type { EditorExtension, ResolvedEditorExtensions } from './core/extension'\nimport type { BlockChanges } from './core/changes'\nimport type { Block, EditorDocument, PartialBlock } from './core/model'\nimport type { EditorSearchQuery } from './core/search'\nimport type {\n BlockEdge,\n BlockPatch,\n BlockPlacement,\n EditorTransaction,\n EditorTransactionMeta,\n} from './core/transaction'\nimport type { EditorMessages, EditorMessagesOverride } from './locale'\n\nexport interface EditorProps {\n /**\n * The document, bound with `v-model`. With collaboration the shared\n * document is the source of truth: leave `v-model` out and read changes\n * from the `change` event.\n */\n modelValue?: EditorDocument<PartialBlock>\n /**\n * Blocks, marks, shortcuts and menu entries. Defaults to `[editorKit()]`;\n * read once, when the editor mounts.\n */\n extensions?: (EditorExtension | EditorExtension[])[]\n /** Show the document without editing it. Links stay clickable. */\n readonly?: boolean\n /** The built-in find panel (`Ctrl/⌘+F`). Off, the keys are left to the browser. On by default. */\n searchable?: boolean\n /** The hint in the empty block under the cursor. */\n placeholder?: string\n /** Replace any of the built-in texts. */\n messages?: EditorMessagesOverride\n /** The accessible name of the editing area. */\n ariaLabel?: string\n /** Focus the editor once it is mounted. */\n autofocus?: boolean\n /**\n * Extra ProseMirror plugins, e.g. for collaboration. They run before\n * the built-in ones, so their keys win.\n */\n plugins?: Plugin[]\n /** Leave out the built-in undo history (collaboration brings its own). */\n history?: boolean\n /** Undo and redo commands to replace the built-in ones. */\n undo?: Command\n redo?: Command\n /**\n * The comment threads, bound with `v-model:comments`. The text a thread\n * is about carries its id in the document; leave this out to turn\n * comments off.\n */\n comments?: CommentThread[]\n /** Who is commenting. Without it comments can be read but not written. */\n author?: CommentAuthor\n /** The id of the thread shown beside its text, bound with `v-model:activeComment`. */\n activeComment?: string | null\n /** Whether threads can be started and answered; also while `readonly`. Defaults to `true`. */\n commentable?: boolean\n /**\n * An older version of the document, e.g. one the app saved. The editor\n * then shows the document read-only, with what changed since marked:\n * blocks added and removed, and in each changed block the text added\n * and removed. Blocks are matched by `id`; a block without one shows as\n * removed.\n */\n compare?: EditorDocument<PartialBlock> | null\n /**\n * The block with the cursor and the selected blocks, bound with\n * `v-model:selection`. Setting it puts the cursor at the end of `block`,\n * or selects `blocks` when there are several siblings, without focusing.\n */\n selection?: EditorSelection\n}\n\n/**\n * Who made a change: `'external'` for a new `v-model` value or\n * `setBlocks`, `'remote'` for a collaborator's, `'restore'` for `restore`,\n * `'undo'` and `'redo'`, or\n * the `origin` given to `transact`. Typing and the editor's own commands\n * leave it out.\n */\nexport type EditorOrigin = 'external' | 'remote' | 'restore' | 'undo' | 'redo' | (string & {})\n\n/** What a change to the document did, as the `transaction` event hands it over. */\nexport interface EditorTransactionResult {\n /** The document after the change, as `update:modelValue` gets it next. */\n blocks: Block[]\n /** The blocks the change touched, by id. */\n changes: BlockChanges\n meta: {\n origin?: EditorOrigin\n /** Whether undo can take the change back: not for others' changes, nor for undo and redo themselves. */\n history: boolean\n }\n}\n\n/** Where the selection is, by block. */\nexport interface EditorSelection {\n /** The innermost block at the cursor (the moving end of a selection); `null` while none is. */\n block: string | null\n /**\n * The blocks the selection covers: the selected blocks, or the blocks a\n * text selection runs across, at the level they share.\n */\n blocks: string[]\n}\n\nexport interface EditorEmits {\n 'update:modelValue': [document: EditorDocument]\n /** The document changed: typing, formatting, moving blocks. */\n change: [document: EditorDocument]\n /** Every change to the document, with what it touched; `update:modelValue` follows. */\n transaction: [result: EditorTransactionResult]\n /** The cursor moved to another block, or other blocks were selected. */\n 'update:selection': [selection: EditorSelection]\n /** Before a link opens (Ctrl / ⌘ + click, or a click while read-only); `preventDefault()` keeps it from opening in a new tab. */\n linkOpen: [event: EditorLinkOpenEvent]\n focus: [event: FocusEvent]\n blur: [event: FocusEvent]\n 'update:comments': [threads: CommentThread[]]\n 'update:activeComment': [id: string | null]\n /** A change to the comments made here: to send to a server, notify people, keep a log. */\n commentAction: [action: CommentAction]\n}\n\n/**\n * Before a link is followed. `preventDefault()` stops the editor from\n * opening it in a new tab, e.g. to use the app's router or ask first.\n */\nexport interface EditorLinkOpenEvent {\n /** The address as the text's `link` holds it. */\n link: string\n /** The id of the block the link is in. */\n block: string\n defaultPrevented: boolean\n preventDefault: () => void\n}\n\nexport interface EditorExpose {\n /** The ProseMirror view, once mounted. */\n readonly view: EditorView | null\n focus: () => void\n blur: () => void\n /** The current document. */\n getBlocks: () => Block[]\n /** Replace the document. The change does not go into the undo history. */\n setBlocks: (blocks: PartialBlock[]) => void\n /** The current document as HTML, written by the extensions (see `blocksToHTML`). */\n getHTML: () => string\n /** Replace the document with HTML, read like pasted HTML. */\n setHTML: (html: string) => void\n /** Run a ProseMirror command, e.g. one of the exported ones. */\n run: (command: Command) => boolean\n /**\n * Opens the find panel (`Ctrl/⌘+F`), with a query put in; with `replace`\n * in it, the replace box opens too. `false` when `searchable` is off or\n * the editor is not mounted.\n */\n openSearch: (query?: Partial<EditorSearchQuery>) => boolean\n /** Closes the find panel; `false` when it was not open. */\n closeSearch: () => boolean\n /**\n * Make changes by blocks and ids, applied together as one undo step.\n * Returns what happened, or `null` when nothing changed. Works while\n * read-only too.\n */\n transact: (\n fn: (tr: EditorTransaction) => void,\n meta?: EditorTransactionMeta,\n ) => EditorTransactionResult | null\n /** One block, or `null` when there is none with that id. */\n getBlock: (id: string) => Block | null\n /**\n * Insert blocks, by default after the block with the cursor. Returns the\n * new blocks' ids (an id already in use is replaced). One undo step.\n */\n insertBlocks: (blocks: PartialBlock[], placement?: BlockPlacement) => string[]\n /** Change a block's type, props (merged), content or children. One undo step. */\n updateBlock: (id: string, patch: BlockPatch) => boolean\n /** Remove blocks with their children. One undo step. */\n removeBlocks: (ids: string[]) => boolean\n /** Move blocks, in the order given, before, after or into another block. One undo step. */\n moveBlocks: (ids: string[], placement: BlockPlacement) => boolean\n /** Focus the editor with the cursor at the start or end (the default) of a block, scrolled into view. */\n focusBlock: (id: string, edge?: BlockEdge) => boolean\n /** The selection by block, as `v-model:selection` has it. */\n getSelection: () => EditorSelection\n undo: () => boolean\n redo: () => boolean\n readonly canUndo: boolean\n readonly canRedo: boolean\n /**\n * Start a thread on the selected text and open the box for its first\n * comment; with the cursor on commented text, open that thread to reply.\n */\n comment: () => boolean\n /** Where each thread is anchored; `null` for a thread whose text was deleted. */\n getCommentAnchors: () => Record<string, CommentAnchor | null>\n /**\n * Replace the document with `doc`, e.g. an older version, as one\n * change undo takes back. With collaboration only the blocks that differ\n * are written, and everyone sees it. The `transaction` event has origin\n * `'restore'`. Like the other methods it works while `readonly`; it is\n * the one change allowed while comparing. Returns `false` when nothing\n * differs.\n */\n restore: (doc: EditorDocument<PartialBlock>) => boolean\n /** With `compare`: scroll to the next change after the one last shown; `false` when there is none. */\n nextChange: () => boolean\n /** With `compare`: scroll to the change before the one last shown. */\n previousChange: () => boolean\n}\n\n/** The comments, as the parts around the editing area see them. */\nexport interface EditorComments {\n /** Comments are on: there are threads to show. */\n enabled: ComputedRef<boolean>\n /** Threads can be started and answered. */\n writable: ComputedRef<boolean>\n threads: ComputedRef<readonly CommentThread[]>\n author: ComputedRef<CommentAuthor | undefined>\n /** The thread shown beside its text. */\n active: ComputedRef<string | null>\n /** The text a new thread is being started on. */\n pending: ShallowRef<CommentRange | null>\n /** Goes up when the open thread's reply box should take focus. */\n focusRequest: ShallowRef<number>\n /** The selected text, also in a read-only editor; `null` when there is none. */\n selection: () => CommentRange | null\n /** Start a thread on the selected text, or open the one the cursor is in. */\n start: () => boolean\n apply: (action: CommentAction) => void\n open: (id: string | null) => void\n /** Close the card, dropping a thread not yet started. */\n close: () => void\n}\n\n/** What the parts around the editing area (menus, handle, toolbar) share. */\nexport interface EditorContext {\n view: ShallowRef<EditorView | null>\n /** Updated on every transaction, so computeds follow the selection too. */\n state: ShallowRef<EditorState | null>\n focused: ShallowRef<boolean>\n /** Blocks are being dragged by the handle. */\n dragging: ShallowRef<boolean>\n editable: ComputedRef<boolean>\n messages: ComputedRef<EditorMessages>\n /** What the extensions contribute: schema, menu entries, toolbar toggles. */\n kit: ResolvedEditorExtensions\n /** The element the overlays are positioned against. */\n root: ShallowRef<HTMLElement | null>\n comments: EditorComments\n}\n\nexport const editorKey: InjectionKey<EditorContext> = Symbol('DouEditor')\n\nconst toolbarMenusKey: InjectionKey<Set<ShallowRef<boolean>>> = Symbol('DouEditorToolbarMenus')\n\n/** For the formatting toolbar: the open state of the menus in it. */\nexport function provideToolbarMenus(): Set<ShallowRef<boolean>> {\n const menus = shallowReactive(new Set<ShallowRef<boolean>>())\n provide(toolbarMenusKey, menus)\n return menus\n}\n\n/**\n * The open state for a menu in the formatting toolbar (an extension's\n * `toolbar` component), to bind with `v-model:open`: the toolbar stays\n * while the menu is open, though the focus has left the editor.\n */\nexport function useToolbarMenu(): ShallowRef<boolean> {\n const open = shallowRef(false)\n const menus = inject(toolbarMenusKey, null)\n if (menus) {\n menus.add(open)\n onBeforeUnmount(() => menus.delete(open))\n }\n return open\n}\n\n/** The editor around a block component, for its messages, state and view. */\nexport function useEditor(): EditorContext {\n const context = inject(editorKey, null)\n if (!context) throw new Error('[@dou-ui/editor] useEditor() must be called inside <DouEditor>')\n return context\n}\n"],"mappings":";;AA6QA,IAAa,YAAyC,OAAO,WAAW;AAExE,IAAM,kBAA0D,OAAO,uBAAuB;;AAG9F,SAAgB,sBAAgD;CAC9D,MAAM,QAAQ,gCAAgB,IAAI,IAAyB,CAAC;CAC5D,QAAQ,iBAAiB,KAAK;CAC9B,OAAO;AACT;;;;;;AAOA,SAAgB,iBAAsC;CACpD,MAAM,OAAO,WAAW,KAAK;CAC7B,MAAM,QAAQ,OAAO,iBAAiB,IAAI;CAC1C,IAAI,OAAO;EACT,MAAM,IAAI,IAAI;EACd,sBAAsB,MAAM,OAAO,IAAI,CAAC;CAC1C;CACA,OAAO;AACT;;AAGA,SAAgB,YAA2B;CACzC,MAAM,UAAU,OAAO,WAAW,IAAI;CACtC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,gEAAgE;CAC9F,OAAO;AACT"}
|