@kungal/editor-core 0.34.0 → 0.36.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/CHANGELOG.md +71 -0
- package/dist/chunk-IIVFLZTN.js +50 -0
- package/dist/chunk-IIVFLZTN.js.map +1 -0
- package/dist/index.cjs +21 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +1 -1
- package/dist/preset/index.cjs +100 -9
- package/dist/preset/index.cjs.map +1 -1
- package/dist/preset/index.d.cts +34 -4
- package/dist/preset/index.d.ts +34 -4
- package/dist/preset/index.js +78 -9
- package/dist/preset/index.js.map +1 -1
- package/dist/{types-Dui0tGlh.d.cts → types-f8Szrgvw.d.cts} +4 -0
- package/dist/{types-Dui0tGlh.d.ts → types-f8Szrgvw.d.ts} +4 -0
- package/package.json +1 -1
- package/dist/chunk-PT42ZQGO.js +0 -30
- package/dist/chunk-PT42ZQGO.js.map +0 -1
package/dist/preset/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MilkdownPlugin, Ctx } from '@milkdown/kit/ctx';
|
|
2
2
|
import { KatexOptions } from 'katex';
|
|
3
|
-
import { b as KunEditorLocale, N as Notify, U as UploadImage, K as KunEditorAdapters, a as KunEditorFeatures } from '../types-
|
|
3
|
+
import { b as KunEditorLocale, N as Notify, U as UploadImage, K as KunEditorAdapters, a as KunEditorFeatures } from '../types-f8Szrgvw.cjs';
|
|
4
4
|
import * as _milkdown_kit_utils from '@milkdown/kit/utils';
|
|
5
5
|
import { Extension } from '@codemirror/state';
|
|
6
6
|
import { Uploader } from '@milkdown/kit/plugin/upload';
|
|
@@ -13,8 +13,30 @@ declare const spoilerAttr: _milkdown_kit_utils.$NodeAttr;
|
|
|
13
13
|
/** The `kun-spoiler` inline node: parses/serializes `||text||` and renders a
|
|
14
14
|
* `<span data-type="kun-spoiler">` the render layer toggles on click. */
|
|
15
15
|
declare const spoilerSchema: _milkdown_kit_utils.$NodeSchema<"kun-spoiler">;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
16
|
+
/**
|
|
17
|
+
* Toolbar entry point ("隐藏文本"): hide the selection, or reveal the spoiler the
|
|
18
|
+
* caret is in. The only way into a spoiler other than typing `||…||`, so it has
|
|
19
|
+
* to cover the same cases the syntax does:
|
|
20
|
+
*
|
|
21
|
+
* - a selection → its TEXT moves INSIDE the node. It has to be moved, not
|
|
22
|
+
* replaced: `replaceSelectionWith` on its own deletes what you selected. The
|
|
23
|
+
* schema is `marks: ''` (bold inside `||…||` cannot round-trip), so the
|
|
24
|
+
* formatting is what's dropped, never the words.
|
|
25
|
+
* - an empty selection → an empty spoiler with the caret inside it: type, and
|
|
26
|
+
* what you type is hidden. "Empty" means holding a caret anchor — a caret
|
|
27
|
+
* cannot sit in an inline node with no text node in it — and a spoiler with
|
|
28
|
+
* nothing but an anchor serializes to nothing at all, so an unused one cannot
|
|
29
|
+
* rot into the document.
|
|
30
|
+
* - the caret already inside a spoiler → reveal it, leaving the text selected.
|
|
31
|
+
* Nesting would serialize to `||||text||||`, which reads back as nothing at
|
|
32
|
+
* all, so this button has to toggle.
|
|
33
|
+
*
|
|
34
|
+
* Returns false where there is nothing to hide or nowhere to put it — inside a
|
|
35
|
+
* code block, or with a NODE selected (an image is not text; replacing it would
|
|
36
|
+
* delete it) — so a host can grey the button out. A spoiler holds text, so any
|
|
37
|
+
* other inline node caught in a text selection (an image, inline math) does not
|
|
38
|
+
* survive the move: `||…||` cannot carry one.
|
|
39
|
+
*/
|
|
18
40
|
declare const insertKunSpoilerCommand: _milkdown_kit_utils.$Command<unknown>;
|
|
19
41
|
/** Typing `||text||` in the editor turns it into a spoiler node in place. The
|
|
20
42
|
* trailing zero-width space gives the caret a stable anchor after the atom. */
|
|
@@ -39,7 +61,11 @@ declare const linkCustomKeymap: _milkdown_kit_utils.$UserKeymap<"linkCustomKeyma
|
|
|
39
61
|
declare const createStopLinkPlugin: () => MilkdownPlugin[];
|
|
40
62
|
|
|
41
63
|
interface InsertLinkPayload {
|
|
42
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* The URL. Required — a blank href is a no-op. Normalized on the way in, so a
|
|
66
|
+
* schemeless host gets one: `www.a.com/x` → `https://www.a.com/x`. See
|
|
67
|
+
* {@link normalizeLinkHref} for the exact rules.
|
|
68
|
+
*/
|
|
43
69
|
href: string;
|
|
44
70
|
/** Text to insert + link when the selection is empty. Defaults to the href. */
|
|
45
71
|
text?: string;
|
|
@@ -48,6 +74,10 @@ interface InsertLinkPayload {
|
|
|
48
74
|
* Apply the `link` mark with `href` to the selection; on an EMPTY selection,
|
|
49
75
|
* insert `text` (or the href) and link that. Clears the stored mark afterwards so
|
|
50
76
|
* typing past the link isn't linked (same intent as the stop-link keymap).
|
|
77
|
+
*
|
|
78
|
+
* The href is normalized here — this command is the single funnel every link
|
|
79
|
+
* entry point (bubble, toolbar, host `linkPrompt`) goes through, so a UI never
|
|
80
|
+
* has to, and every one of them behaves the same.
|
|
51
81
|
*/
|
|
52
82
|
declare const insertLinkCommand: _milkdown_kit_utils.$Command<InsertLinkPayload>;
|
|
53
83
|
/** The link plugin bundle: the insert-link command. */
|
package/dist/preset/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MilkdownPlugin, Ctx } from '@milkdown/kit/ctx';
|
|
2
2
|
import { KatexOptions } from 'katex';
|
|
3
|
-
import { b as KunEditorLocale, N as Notify, U as UploadImage, K as KunEditorAdapters, a as KunEditorFeatures } from '../types-
|
|
3
|
+
import { b as KunEditorLocale, N as Notify, U as UploadImage, K as KunEditorAdapters, a as KunEditorFeatures } from '../types-f8Szrgvw.js';
|
|
4
4
|
import * as _milkdown_kit_utils from '@milkdown/kit/utils';
|
|
5
5
|
import { Extension } from '@codemirror/state';
|
|
6
6
|
import { Uploader } from '@milkdown/kit/plugin/upload';
|
|
@@ -13,8 +13,30 @@ declare const spoilerAttr: _milkdown_kit_utils.$NodeAttr;
|
|
|
13
13
|
/** The `kun-spoiler` inline node: parses/serializes `||text||` and renders a
|
|
14
14
|
* `<span data-type="kun-spoiler">` the render layer toggles on click. */
|
|
15
15
|
declare const spoilerSchema: _milkdown_kit_utils.$NodeSchema<"kun-spoiler">;
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
16
|
+
/**
|
|
17
|
+
* Toolbar entry point ("隐藏文本"): hide the selection, or reveal the spoiler the
|
|
18
|
+
* caret is in. The only way into a spoiler other than typing `||…||`, so it has
|
|
19
|
+
* to cover the same cases the syntax does:
|
|
20
|
+
*
|
|
21
|
+
* - a selection → its TEXT moves INSIDE the node. It has to be moved, not
|
|
22
|
+
* replaced: `replaceSelectionWith` on its own deletes what you selected. The
|
|
23
|
+
* schema is `marks: ''` (bold inside `||…||` cannot round-trip), so the
|
|
24
|
+
* formatting is what's dropped, never the words.
|
|
25
|
+
* - an empty selection → an empty spoiler with the caret inside it: type, and
|
|
26
|
+
* what you type is hidden. "Empty" means holding a caret anchor — a caret
|
|
27
|
+
* cannot sit in an inline node with no text node in it — and a spoiler with
|
|
28
|
+
* nothing but an anchor serializes to nothing at all, so an unused one cannot
|
|
29
|
+
* rot into the document.
|
|
30
|
+
* - the caret already inside a spoiler → reveal it, leaving the text selected.
|
|
31
|
+
* Nesting would serialize to `||||text||||`, which reads back as nothing at
|
|
32
|
+
* all, so this button has to toggle.
|
|
33
|
+
*
|
|
34
|
+
* Returns false where there is nothing to hide or nowhere to put it — inside a
|
|
35
|
+
* code block, or with a NODE selected (an image is not text; replacing it would
|
|
36
|
+
* delete it) — so a host can grey the button out. A spoiler holds text, so any
|
|
37
|
+
* other inline node caught in a text selection (an image, inline math) does not
|
|
38
|
+
* survive the move: `||…||` cannot carry one.
|
|
39
|
+
*/
|
|
18
40
|
declare const insertKunSpoilerCommand: _milkdown_kit_utils.$Command<unknown>;
|
|
19
41
|
/** Typing `||text||` in the editor turns it into a spoiler node in place. The
|
|
20
42
|
* trailing zero-width space gives the caret a stable anchor after the atom. */
|
|
@@ -39,7 +61,11 @@ declare const linkCustomKeymap: _milkdown_kit_utils.$UserKeymap<"linkCustomKeyma
|
|
|
39
61
|
declare const createStopLinkPlugin: () => MilkdownPlugin[];
|
|
40
62
|
|
|
41
63
|
interface InsertLinkPayload {
|
|
42
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* The URL. Required — a blank href is a no-op. Normalized on the way in, so a
|
|
66
|
+
* schemeless host gets one: `www.a.com/x` → `https://www.a.com/x`. See
|
|
67
|
+
* {@link normalizeLinkHref} for the exact rules.
|
|
68
|
+
*/
|
|
43
69
|
href: string;
|
|
44
70
|
/** Text to insert + link when the selection is empty. Defaults to the href. */
|
|
45
71
|
text?: string;
|
|
@@ -48,6 +74,10 @@ interface InsertLinkPayload {
|
|
|
48
74
|
* Apply the `link` mark with `href` to the selection; on an EMPTY selection,
|
|
49
75
|
* insert `text` (or the href) and link that. Clears the stored mark afterwards so
|
|
50
76
|
* typing past the link isn't linked (same intent as the stop-link keymap).
|
|
77
|
+
*
|
|
78
|
+
* The href is normalized here — this command is the single funnel every link
|
|
79
|
+
* entry point (bubble, toolbar, host `linkPrompt`) goes through, so a UI never
|
|
80
|
+
* has to, and every one of them behaves the same.
|
|
51
81
|
*/
|
|
52
82
|
declare const insertLinkCommand: _milkdown_kit_utils.$Command<InsertLinkPayload>;
|
|
53
83
|
/** The link plugin bundle: the insert-link command. */
|
package/dist/preset/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QUOTE_SCHEME, MENTION_SCHEME } from '../chunk-
|
|
1
|
+
import { normalizeLinkHref, QUOTE_SCHEME, MENTION_SCHEME } from '../chunk-IIVFLZTN.js';
|
|
2
2
|
import { linkSchema, headingSchema, paragraphSchema, codeBlockSchema, commonmark } from '@milkdown/kit/preset/commonmark';
|
|
3
3
|
import { gfm } from '@milkdown/kit/preset/gfm';
|
|
4
4
|
import { history } from '@milkdown/kit/plugin/history';
|
|
@@ -6,12 +6,13 @@ import { listener } from '@milkdown/kit/plugin/listener';
|
|
|
6
6
|
import { clipboard } from '@milkdown/kit/plugin/clipboard';
|
|
7
7
|
import { indent } from '@milkdown/kit/plugin/indent';
|
|
8
8
|
import { trailing } from '@milkdown/kit/plugin/trailing';
|
|
9
|
+
import { Fragment } from '@milkdown/kit/prose/model';
|
|
9
10
|
import { expectDomTypeError } from '@milkdown/kit/exception';
|
|
10
11
|
import { InputRule, textblockTypeInputRule } from '@milkdown/kit/prose/inputrules';
|
|
12
|
+
import { TextSelection, NodeSelection, PluginKey, Plugin } from '@milkdown/kit/prose/state';
|
|
11
13
|
import { $nodeAttr, $nodeSchema, $command, $inputRule, $remark, $useKeymap, $prose } from '@milkdown/kit/utils';
|
|
12
14
|
import { visit } from 'unist-util-visit';
|
|
13
15
|
import { commandsCtx, editorViewOptionsCtx } from '@milkdown/kit/core';
|
|
14
|
-
import { PluginKey, Plugin, NodeSelection, TextSelection } from '@milkdown/kit/prose/state';
|
|
15
16
|
import { setBlockType } from '@milkdown/kit/prose/commands';
|
|
16
17
|
import { findNodeInSelection, nodeRule } from '@milkdown/kit/prose';
|
|
17
18
|
import katex from 'katex';
|
|
@@ -32,6 +33,21 @@ var spoilerAttr = $nodeAttr("kun-spoiler", () => ({
|
|
|
32
33
|
style: "background: var(--color-default-500); border-radius: var(--radius-sm); padding: 0 4px; cursor: pointer;"
|
|
33
34
|
}
|
|
34
35
|
}));
|
|
36
|
+
var CARET_ANCHOR = "\u200B";
|
|
37
|
+
var withoutAnchors = (content) => {
|
|
38
|
+
const kept = [];
|
|
39
|
+
content.forEach((child) => {
|
|
40
|
+
if (!child.isText) {
|
|
41
|
+
kept.push(child);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const text = (child.text ?? "").replaceAll(CARET_ANCHOR, "");
|
|
45
|
+
if (text) {
|
|
46
|
+
kept.push(child.type.schema.text(text, child.marks));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return Fragment.fromArray(kept);
|
|
50
|
+
};
|
|
35
51
|
var spoilerSchema = $nodeSchema("kun-spoiler", (ctx) => ({
|
|
36
52
|
group: "inline",
|
|
37
53
|
inline: true,
|
|
@@ -76,23 +92,76 @@ var spoilerSchema = $nodeSchema("kun-spoiler", (ctx) => ({
|
|
|
76
92
|
toMarkdown: {
|
|
77
93
|
match: (node) => node.type.name === "kun-spoiler",
|
|
78
94
|
runner: (state, node) => {
|
|
95
|
+
const content = withoutAnchors(node.content);
|
|
96
|
+
if (!content.size) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
79
99
|
state.addNode("text", void 0, "||");
|
|
80
|
-
state.next(
|
|
100
|
+
state.next(content);
|
|
81
101
|
state.addNode("text", void 0, "||");
|
|
82
102
|
}
|
|
83
103
|
}
|
|
84
104
|
}));
|
|
105
|
+
var enclosingSpoiler = ($pos, type) => {
|
|
106
|
+
for (let depth = $pos.depth; depth > 0; depth--) {
|
|
107
|
+
const node = $pos.node(depth);
|
|
108
|
+
if (node.type === type) {
|
|
109
|
+
return { pos: $pos.before(depth), node };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
};
|
|
85
114
|
var insertKunSpoilerCommand = $command(
|
|
86
115
|
"InsertKunSpoiler",
|
|
87
116
|
(ctx) => () => (state, dispatch) => {
|
|
88
|
-
|
|
117
|
+
const type = spoilerSchema.type(ctx);
|
|
118
|
+
const { $from, from, to, empty } = state.selection;
|
|
119
|
+
const open = enclosingSpoiler($from, type);
|
|
120
|
+
if (open) {
|
|
121
|
+
if (dispatch) {
|
|
122
|
+
const { pos, node: node2 } = open;
|
|
123
|
+
const revealed = withoutAnchors(node2.content);
|
|
124
|
+
const end = pos + node2.nodeSize;
|
|
125
|
+
const trailing2 = state.doc.textBetween(
|
|
126
|
+
end,
|
|
127
|
+
Math.min(end + CARET_ANCHOR.length, state.doc.content.size)
|
|
128
|
+
);
|
|
129
|
+
const tr2 = state.tr.replaceWith(
|
|
130
|
+
pos,
|
|
131
|
+
trailing2 === CARET_ANCHOR ? end + CARET_ANCHOR.length : end,
|
|
132
|
+
revealed
|
|
133
|
+
);
|
|
134
|
+
if (revealed.size) {
|
|
135
|
+
tr2.setSelection(TextSelection.create(tr2.doc, pos, pos + revealed.size));
|
|
136
|
+
}
|
|
137
|
+
dispatch(tr2.scrollIntoView());
|
|
138
|
+
}
|
|
89
139
|
return true;
|
|
90
140
|
}
|
|
91
|
-
|
|
92
|
-
|
|
141
|
+
if (state.selection instanceof NodeSelection) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
if ($from.parent.isTextblock && !$from.parent.canReplaceWith($from.index(), $from.index(), type)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (!dispatch) {
|
|
93
148
|
return true;
|
|
94
149
|
}
|
|
95
|
-
|
|
150
|
+
const text = state.doc.textBetween(from, to, " ").replaceAll(CARET_ANCHOR, "");
|
|
151
|
+
const node = type.create(null, state.schema.text(text || CARET_ANCHOR));
|
|
152
|
+
const tr = state.tr.replaceSelectionWith(node, false);
|
|
153
|
+
const after = tr.selection.to;
|
|
154
|
+
tr.insertText(CARET_ANCHOR, after);
|
|
155
|
+
dispatch(
|
|
156
|
+
tr.setSelection(
|
|
157
|
+
TextSelection.create(
|
|
158
|
+
tr.doc,
|
|
159
|
+
// On the anchor inside the empty node (type and it is hidden), or
|
|
160
|
+
// past the one after a wrap (type and it is not).
|
|
161
|
+
empty ? after - node.nodeSize + 1 + CARET_ANCHOR.length : after + 1
|
|
162
|
+
)
|
|
163
|
+
).scrollIntoView()
|
|
164
|
+
);
|
|
96
165
|
return true;
|
|
97
166
|
}
|
|
98
167
|
);
|
|
@@ -109,7 +178,7 @@ var insertSpoilerInputRule = $inputRule(
|
|
|
109
178
|
{ revealed: false },
|
|
110
179
|
schema.text(content)
|
|
111
180
|
);
|
|
112
|
-
const zeroWidthSpace = schema.text(
|
|
181
|
+
const zeroWidthSpace = schema.text(CARET_ANCHOR);
|
|
113
182
|
return tr.replaceWith(startPos, end, [spoilerNode, zeroWidthSpace]).setStoredMarks([]).scrollIntoView();
|
|
114
183
|
})
|
|
115
184
|
);
|
|
@@ -214,7 +283,7 @@ var createStopLinkPlugin = () => [stopLinkCommand, linkCustomKeymap, clearOrphan
|
|
|
214
283
|
var insertLinkCommand = $command(
|
|
215
284
|
"InsertKunLink",
|
|
216
285
|
(ctx) => (payload) => (state, dispatch) => {
|
|
217
|
-
const href = payload?.href
|
|
286
|
+
const href = normalizeLinkHref(payload?.href ?? "");
|
|
218
287
|
if (!href) {
|
|
219
288
|
return false;
|
|
220
289
|
}
|