@kungal/editor-core 0.35.0 → 0.37.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 CHANGED
@@ -1,5 +1,62 @@
1
1
  # @kungal/editor-core
2
2
 
3
+ ## 0.37.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a473708: 隐藏文本:选区里的换行不再写出读不回来的 markdown,选区气泡里也有了这颗按钮
8
+
9
+ - **`insertKunSpoilerCommand` 把选区里的换行折成空格。** `||…||` 在每一个读它的地方
10
+ 都只在同一行内配对(这里的 remark 转换、共用同一语法的服务端渲染器),而 commonmark 的
11
+ hardbreak 声明了 `leafText: () => '\n'` —— 所以选中跨行的文字点「隐藏文本」,按钮会写出
12
+ `||第一行\n第二行||`:一段它自己都读不回来、渲染出来是两条光秃秃竖线的 markdown。现在
13
+ `textBetween` 的**两个**分隔符都给 `' '`(块级的本来就是),跨行选区并成一个单行 spoiler。
14
+ - **选区里一个字都没有时返回 `false`**,而不是把选中的东西替换掉 —— 单独选中一张图片点
15
+ 「隐藏文本」不再删图。(选中的是节点、或在代码块里,原本就已经返回 `false`。)
16
+ - **选区气泡工具栏加入 `spoiler`**:`KunSelectionItem` 多一个成员,默认那一排变成
17
+ `bold / italic / strike / code / spoiler / link`。按下态走已有的 `activeMarks.spoiler`,
18
+ 光标在 spoiler 里时按钮点亮、点一下揭示,和固定工具栏完全一致。
19
+ - 顺手把气泡默认按钮表收成**一份** `DEFAULT_SELECTION_ITEMS`(`context.ts`)。原本
20
+ `<KunEditor>` 和气泡各存一份,只改气泡那份等于没改 —— 真实编辑器读的是 context 那份。
21
+
22
+ **跨行剧透不在此列**:`||…||` 是行内语法(schema `inline: true`,mdast 里开闭两个 `||`
23
+ 根本落在不同的 text 节点上),要藏整段多行内容需要的是一个块级容器语法,得全生态(渲染器
24
+ 先行)另行约定,不是把 `||…||` 拉长。
25
+
26
+ ## 0.36.0
27
+
28
+ ### Minor Changes
29
+
30
+ - f7c65c2: fix(editor): the spoiler toolbar button hides the selection instead of deleting it
31
+
32
+ `insertKunSpoilerCommand` built an EMPTY `kun-spoiler` node and dropped it over
33
+ the selection with `replaceSelectionWith`, so clicking 隐藏文本 with text selected
34
+ deleted the text and left a bare `||||` — and the next thing typed landed
35
+ outside the node, so nothing was hidden either. The JSDoc said "wraps the
36
+ current selection"; the code never wrapped anything.
37
+
38
+ The command now covers the three cases the `||…||` syntax does:
39
+
40
+ - **a selection** → its text moves inside the node. The schema is `marks: ''`
41
+ (inline formatting cannot round-trip through `||…||`), so bold is what's
42
+ dropped, never the words. Selections spanning paragraphs collapse into one
43
+ spoiler; select-all (an `AllSelection`) works too.
44
+ - **an empty selection** → a new spoiler with the caret INSIDE it: type and it
45
+ is hidden. It holds a zero-width caret anchor, because a caret cannot sit in
46
+ an inline node with no text node in it — the serializer strips anchors, and a
47
+ spoiler holding nothing else serializes to nothing, so a stray click cannot
48
+ leave `||||` behind.
49
+ - **the caret already inside a spoiler** → reveal it, leaving the text selected.
50
+ Nesting would have produced `||||text||||`, which reads back as nothing.
51
+
52
+ The button is therefore a toggle, and both toolbars (the headless one and the
53
+ KunUI one in `@kungal/editor-nuxt`) now show it pressed while the caret is
54
+ inside a spoiler — `KunToggleMark` gains a `'spoiler'` member for that, so
55
+ `activeMarks` carries one more key.
56
+
57
+ It returns `false` — no document change — inside a code block and with a node
58
+ selected (an image is not text; replacing it would delete it).
59
+
3
60
  ## 0.35.0
4
61
 
5
62
  ### Minor Changes
@@ -7,12 +7,13 @@ var listener = require('@milkdown/kit/plugin/listener');
7
7
  var clipboard = require('@milkdown/kit/plugin/clipboard');
8
8
  var indent = require('@milkdown/kit/plugin/indent');
9
9
  var trailing = require('@milkdown/kit/plugin/trailing');
10
+ var model = require('@milkdown/kit/prose/model');
10
11
  var exception = require('@milkdown/kit/exception');
11
12
  var inputrules = require('@milkdown/kit/prose/inputrules');
13
+ var state = require('@milkdown/kit/prose/state');
12
14
  var utils = require('@milkdown/kit/utils');
13
15
  var unistUtilVisit = require('unist-util-visit');
14
16
  var core = require('@milkdown/kit/core');
15
- var state = require('@milkdown/kit/prose/state');
16
17
  var commands = require('@milkdown/kit/prose/commands');
17
18
  var prose = require('@milkdown/kit/prose');
18
19
  var katex = require('katex');
@@ -39,6 +40,21 @@ var spoilerAttr = utils.$nodeAttr("kun-spoiler", () => ({
39
40
  style: "background: var(--color-default-500); border-radius: var(--radius-sm); padding: 0 4px; cursor: pointer;"
40
41
  }
41
42
  }));
43
+ var CARET_ANCHOR = "\u200B";
44
+ var withoutAnchors = (content) => {
45
+ const kept = [];
46
+ content.forEach((child) => {
47
+ if (!child.isText) {
48
+ kept.push(child);
49
+ return;
50
+ }
51
+ const text = (child.text ?? "").replaceAll(CARET_ANCHOR, "");
52
+ if (text) {
53
+ kept.push(child.type.schema.text(text, child.marks));
54
+ }
55
+ });
56
+ return model.Fragment.fromArray(kept);
57
+ };
42
58
  var spoilerSchema = utils.$nodeSchema("kun-spoiler", (ctx) => ({
43
59
  group: "inline",
44
60
  inline: true,
@@ -83,23 +99,79 @@ var spoilerSchema = utils.$nodeSchema("kun-spoiler", (ctx) => ({
83
99
  toMarkdown: {
84
100
  match: (node) => node.type.name === "kun-spoiler",
85
101
  runner: (state, node) => {
102
+ const content = withoutAnchors(node.content);
103
+ if (!content.size) {
104
+ return;
105
+ }
86
106
  state.addNode("text", void 0, "||");
87
- state.next(node.content);
107
+ state.next(content);
88
108
  state.addNode("text", void 0, "||");
89
109
  }
90
110
  }
91
111
  }));
112
+ var enclosingSpoiler = ($pos, type) => {
113
+ for (let depth = $pos.depth; depth > 0; depth--) {
114
+ const node = $pos.node(depth);
115
+ if (node.type === type) {
116
+ return { pos: $pos.before(depth), node };
117
+ }
118
+ }
119
+ return null;
120
+ };
92
121
  var insertKunSpoilerCommand = utils.$command(
93
122
  "InsertKunSpoiler",
94
- (ctx) => () => (state, dispatch) => {
95
- if (!dispatch) {
123
+ (ctx) => () => (state$1, dispatch) => {
124
+ const type = spoilerSchema.type(ctx);
125
+ const { $from, from, to, empty } = state$1.selection;
126
+ const open = enclosingSpoiler($from, type);
127
+ if (open) {
128
+ if (dispatch) {
129
+ const { pos, node: node2 } = open;
130
+ const revealed = withoutAnchors(node2.content);
131
+ const end = pos + node2.nodeSize;
132
+ const trailing2 = state$1.doc.textBetween(
133
+ end,
134
+ Math.min(end + CARET_ANCHOR.length, state$1.doc.content.size)
135
+ );
136
+ const tr2 = state$1.tr.replaceWith(
137
+ pos,
138
+ trailing2 === CARET_ANCHOR ? end + CARET_ANCHOR.length : end,
139
+ revealed
140
+ );
141
+ if (revealed.size) {
142
+ tr2.setSelection(state.TextSelection.create(tr2.doc, pos, pos + revealed.size));
143
+ }
144
+ dispatch(tr2.scrollIntoView());
145
+ }
96
146
  return true;
97
147
  }
98
- const node = spoilerSchema.type(ctx).create();
99
- if (!node) {
148
+ if (state$1.selection instanceof state.NodeSelection) {
149
+ return false;
150
+ }
151
+ if ($from.parent.isTextblock && !$from.parent.canReplaceWith($from.index(), $from.index(), type)) {
152
+ return false;
153
+ }
154
+ const text = state$1.doc.textBetween(from, to, " ", " ").replaceAll(CARET_ANCHOR, "");
155
+ if (!empty && !text.trim()) {
156
+ return false;
157
+ }
158
+ if (!dispatch) {
100
159
  return true;
101
160
  }
102
- dispatch(state.tr.replaceSelectionWith(node).scrollIntoView());
161
+ const node = type.create(null, state$1.schema.text(text || CARET_ANCHOR));
162
+ const tr = state$1.tr.replaceSelectionWith(node, false);
163
+ const after = tr.selection.to;
164
+ tr.insertText(CARET_ANCHOR, after);
165
+ dispatch(
166
+ tr.setSelection(
167
+ state.TextSelection.create(
168
+ tr.doc,
169
+ // On the anchor inside the empty node (type and it is hidden), or
170
+ // past the one after a wrap (type and it is not).
171
+ empty ? after - node.nodeSize + 1 + CARET_ANCHOR.length : after + 1
172
+ )
173
+ ).scrollIntoView()
174
+ );
103
175
  return true;
104
176
  }
105
177
  );
@@ -116,7 +188,7 @@ var insertSpoilerInputRule = utils.$inputRule(
116
188
  { revealed: false },
117
189
  schema.text(content)
118
190
  );
119
- const zeroWidthSpace = schema.text("\u200B");
191
+ const zeroWidthSpace = schema.text(CARET_ANCHOR);
120
192
  return tr.replaceWith(startPos, end, [spoilerNode, zeroWidthSpace]).setStoredMarks([]).scrollIntoView();
121
193
  })
122
194
  );