@kungal/editor-vue 0.4.0 → 0.5.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 +22 -0
- package/README.md +8 -3
- package/dist/components/EditorToolbar.vue.d.ts +4 -0
- package/dist/components/EditorToolbar.vue.d.ts.map +1 -0
- package/dist/components/KunEditor.vue.d.ts.map +1 -1
- package/dist/index.js +269 -90
- package/dist/style.css +1 -1
- package/dist/toolbar-icons.d.ts +18 -0
- package/dist/toolbar-icons.d.ts.map +1 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @kungal/editor-vue
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 53a2769: P3 — add the formatting toolbar to `<KunEditor>`.
|
|
8
|
+
|
|
9
|
+
A self-contained `EditorToolbar` (WYSIWYG mode, hidden when read-only) with:
|
|
10
|
+
bold / italic / strikethrough / inline code, H1–H3, bullet & ordered lists,
|
|
11
|
+
blockquote, code block, divider, spoiler and LaTeX — plus an image-upload button
|
|
12
|
+
that appears only when the host supplies `uploadImage`.
|
|
13
|
+
|
|
14
|
+
Ported from the forum's `Menu.vue` + `_buttonList.ts`, but kept dependency-free:
|
|
15
|
+
inline SVG icons (no `@kungal/ui-vue`), themed via CSS variables so a KunUI host
|
|
16
|
+
still gets matching colours. It reaches the editor with `useInstance()` and fires
|
|
17
|
+
commands via `callCommand`; command keys are read at click time (they're only
|
|
18
|
+
populated once the editor is created). Verified end-to-end in a browser via
|
|
19
|
+
`apps/playground`.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- @kungal/editor-core@0.5.0
|
|
24
|
+
|
|
3
25
|
## 0.4.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -54,14 +54,19 @@ editor**: a WYSIWYG view built from `@kungal/editor-core/preset`
|
|
|
54
54
|
…), plus a markdown-source view, over one `v-model`. The prop/emit surface
|
|
55
55
|
(`v-model`, `adapters`, `features`, `locale`, `readonly`) is stable.
|
|
56
56
|
|
|
57
|
+
A **formatting toolbar** is wired: bold / italic / strikethrough / inline code,
|
|
58
|
+
H1–H3, bullet & ordered lists, blockquote, code block, divider, spoiler, LaTeX —
|
|
59
|
+
plus an image-upload button when `uploadImage` is supplied. It's self-contained
|
|
60
|
+
(inline SVG, no `@kungal/ui-vue` dependency), themed through CSS variables so a
|
|
61
|
+
KunUI host still gets matching colours.
|
|
62
|
+
|
|
57
63
|
The **`@mention` autocomplete dropdown** is wired: type `@`, and — when the host
|
|
58
64
|
supplies `searchMentionUsers` — a debounced, keyboard-navigable dropdown offers
|
|
59
65
|
users and inserts a mention chip (`[@name](kungal-user:id)`). Without the adapter
|
|
60
66
|
the mention schema still round-trips; there's just no autocomplete.
|
|
61
67
|
|
|
62
|
-
Still to come in P3
|
|
63
|
-
|
|
64
|
-
view (currently a `<textarea>`). See
|
|
68
|
+
Still to come in P3: the sticker/emoji picker (over `stickerSource`) and the
|
|
69
|
+
CodeMirror markdown-source view (currently a `<textarea>`). See
|
|
65
70
|
[`../../docs/architecture.md`](../../docs/architecture.md) § migration.
|
|
66
71
|
|
|
67
72
|
## Build
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
4
|
+
//# sourceMappingURL=EditorToolbar.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EditorToolbar.vue.d.ts","sourceRoot":"","sources":["../../src/components/EditorToolbar.vue"],"names":[],"mappings":"AA0ZA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KunEditor.vue.d.ts","sourceRoot":"","sources":["../../src/components/KunEditor.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"KunEditor.vue.d.ts","sourceRoot":"","sources":["../../src/components/KunEditor.vue"],"names":[],"mappings":"AAiNA,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAK5B,KAAK,WAAW,GAAG;IACf,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,qDAAqD;IACrD,MAAM,CAAC,EAAE,eAAe,CAAA;IACxB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAC;AA4LJ,QAAA,MAAM,YAAY;;;;;cAnMH,iBAAiB;cAEjB,iBAAiB;YAEnB,eAAe;cAEb,OAAO;6EAiMpB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
package/dist/index.js
CHANGED
|
@@ -1,33 +1,35 @@
|
|
|
1
|
-
import { Fragment as e, computed as t, createBlock as n,
|
|
2
|
-
import { Milkdown as
|
|
3
|
-
import { ProsemirrorAdapterProvider as
|
|
4
|
-
import { Editor as
|
|
5
|
-
import { listenerCtx as
|
|
6
|
-
import { SlashProvider as
|
|
7
|
-
import { replaceAll as
|
|
8
|
-
import { createKunEditorPlugins as
|
|
9
|
-
import { TextSelection as
|
|
1
|
+
import { Fragment as e, computed as t, createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createVNode as o, defineComponent as s, inject as c, onBeforeUnmount as l, onMounted as u, openBlock as d, provide as f, ref as p, renderList as m, toDisplayString as h, unref as g, vShow as _, watch as v, withCtx as y, withDirectives as b, withModifiers as x } from "vue";
|
|
2
|
+
import { Milkdown as S, MilkdownProvider as C, useEditor as w, useInstance as T } from "@milkdown/vue";
|
|
3
|
+
import { ProsemirrorAdapterProvider as E, usePluginViewContext as D, usePluginViewFactory as O } from "@prosemirror-adapter/vue";
|
|
4
|
+
import { Editor as k, defaultValueCtx as A, editorViewOptionsCtx as j, rootCtx as M } from "@milkdown/kit/core";
|
|
5
|
+
import { listenerCtx as N } from "@milkdown/kit/plugin/listener";
|
|
6
|
+
import { SlashProvider as P, slashFactory as F } from "@milkdown/kit/plugin/slash";
|
|
7
|
+
import { callCommand as I, replaceAll as L } from "@milkdown/kit/utils";
|
|
8
|
+
import { createKunEditorPlugins as R, insertKunSpoilerCommand as ee, mentionId as te, toggleLatexCommand as ne } from "@kungal/editor-core/preset";
|
|
9
|
+
import { TextSelection as re } from "@milkdown/kit/prose/state";
|
|
10
|
+
import { createCodeBlockCommand as ie, insertHrCommand as ae, insertImageCommand as z, toggleEmphasisCommand as B, toggleInlineCodeCommand as V, toggleStrongCommand as H, wrapInBlockquoteCommand as U, wrapInBulletListCommand as W, wrapInHeadingCommand as G, wrapInOrderedListCommand as K } from "@milkdown/kit/preset/commonmark";
|
|
11
|
+
import { toggleStrikethroughCommand as q } from "@milkdown/kit/preset/gfm";
|
|
10
12
|
//#region src/context.ts
|
|
11
|
-
var
|
|
13
|
+
var J = Symbol("kun-editor-context"), oe = [
|
|
12
14
|
"data-active",
|
|
13
15
|
"onMousedown",
|
|
14
16
|
"onMouseenter"
|
|
15
|
-
],
|
|
17
|
+
], se = ["src", "alt"], ce = {
|
|
16
18
|
key: 1,
|
|
17
19
|
class: "kun-mention-dropdown__avatar kun-mention-dropdown__avatar--fallback"
|
|
18
|
-
},
|
|
20
|
+
}, le = { class: "kun-mention-dropdown__name" }, ue = {
|
|
19
21
|
key: 1,
|
|
20
22
|
class: "kun-mention-dropdown__hint"
|
|
21
|
-
},
|
|
23
|
+
}, de = {
|
|
22
24
|
key: 2,
|
|
23
25
|
class: "kun-mention-dropdown__hint"
|
|
24
|
-
},
|
|
26
|
+
}, fe = {
|
|
25
27
|
key: 3,
|
|
26
28
|
class: "kun-mention-dropdown__hint"
|
|
27
|
-
},
|
|
29
|
+
}, pe = /* @__PURE__ */ s({
|
|
28
30
|
__name: "MentionDropdown",
|
|
29
31
|
setup(n) {
|
|
30
|
-
let
|
|
32
|
+
let r = /(?:^|\s)@([^\s@]{0,30})$/, o = c(J), s = o?.adapters.searchMentionUsers, f = t(() => (o?.locale ?? "zh-cn").toLowerCase().startsWith("en")), g = t(() => f.value ? {
|
|
31
33
|
searching: "Searching…",
|
|
32
34
|
empty: "No matching users",
|
|
33
35
|
prompt: "Type a username to search"
|
|
@@ -35,62 +37,62 @@ var R = Symbol("kun-editor-context"), z = [
|
|
|
35
37
|
searching: "搜索中…",
|
|
36
38
|
empty: "无匹配用户",
|
|
37
39
|
prompt: "输入用户名以搜索"
|
|
38
|
-
}), { view:
|
|
39
|
-
j &&= (clearTimeout(j), null), w.value = "",
|
|
40
|
-
},
|
|
41
|
-
if (j &&= (clearTimeout(j), null), !e || !
|
|
42
|
-
|
|
40
|
+
}), { view: _, prevState: y } = D(), b = p(null), S, C = p(!1), w = p(""), T = p([]), E = p(0), O = p(!1), k = null, A = 0, j = null, M = () => {
|
|
41
|
+
j &&= (clearTimeout(j), null), w.value = "", T.value = [], E.value = 0, O.value = !1, k = null;
|
|
42
|
+
}, N = (e) => {
|
|
43
|
+
if (j &&= (clearTimeout(j), null), !e || !s) {
|
|
44
|
+
T.value = [], O.value = !1;
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
O.value = !0, j = setTimeout(async () => {
|
|
46
48
|
let t = ++A, n = [];
|
|
47
49
|
try {
|
|
48
|
-
n = await
|
|
50
|
+
n = await s(e);
|
|
49
51
|
} catch {
|
|
50
52
|
n = [];
|
|
51
53
|
}
|
|
52
|
-
t === A && (
|
|
54
|
+
t === A && (T.value = n ?? [], E.value = 0, O.value = !1);
|
|
53
55
|
}, 180);
|
|
54
56
|
}, F = (e) => {
|
|
55
57
|
let { selection: t } = e.state;
|
|
56
58
|
if (!t.empty) return null;
|
|
57
|
-
let { $from: n } = t,
|
|
58
|
-
return
|
|
59
|
-
},
|
|
59
|
+
let { $from: n } = t, i = n.parent.textBetween(Math.max(0, n.parentOffset - 100), n.parentOffset, void 0, "").match(r);
|
|
60
|
+
return i ? i[1] ?? "" : null;
|
|
61
|
+
}, I = (e) => {
|
|
60
62
|
let t = F(e);
|
|
61
63
|
if (t === null) return !1;
|
|
62
64
|
let n = e.state.selection.$from.pos;
|
|
63
65
|
return k = {
|
|
64
66
|
from: n - (t.length + 1),
|
|
65
67
|
to: n
|
|
66
|
-
}, t !== w.value && (w.value = t,
|
|
67
|
-
},
|
|
68
|
-
let t =
|
|
68
|
+
}, t !== w.value && (w.value = t, N(t)), !0;
|
|
69
|
+
}, L = (e) => {
|
|
70
|
+
let t = _.value;
|
|
69
71
|
if (!t || !k) return;
|
|
70
|
-
let n = t.state.schema.nodes[
|
|
72
|
+
let n = t.state.schema.nodes[te];
|
|
71
73
|
if (!n) return;
|
|
72
74
|
let r = n.create({
|
|
73
75
|
userId: e.id,
|
|
74
76
|
name: e.name
|
|
75
77
|
}), { from: i, to: a } = k, o = t.state.tr.replaceWith(i, a, r), s = i + r.nodeSize;
|
|
76
|
-
o.insertText(" ", s), o.setSelection(
|
|
77
|
-
},
|
|
78
|
-
if (!C.value || !
|
|
78
|
+
o.insertText(" ", s), o.setSelection(re.create(o.doc, s + 1)), t.dispatch(o.scrollIntoView()), t.focus(), S.hide();
|
|
79
|
+
}, R = (e) => {
|
|
80
|
+
if (!C.value || !T.value.length) {
|
|
79
81
|
C.value && e.key === "Escape" && (e.preventDefault(), S.hide());
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
|
-
let t =
|
|
84
|
+
let t = T.value.length - 1;
|
|
83
85
|
switch (e.key) {
|
|
84
86
|
case "ArrowDown":
|
|
85
|
-
e.preventDefault(), e.stopPropagation(),
|
|
87
|
+
e.preventDefault(), e.stopPropagation(), E.value = E.value >= t ? 0 : E.value + 1;
|
|
86
88
|
break;
|
|
87
89
|
case "ArrowUp":
|
|
88
|
-
e.preventDefault(), e.stopPropagation(),
|
|
90
|
+
e.preventDefault(), e.stopPropagation(), E.value = E.value <= 0 ? t : E.value - 1;
|
|
89
91
|
break;
|
|
90
92
|
case "Enter":
|
|
91
93
|
case "Tab": {
|
|
92
|
-
let t =
|
|
93
|
-
t && (e.preventDefault(), e.stopPropagation(),
|
|
94
|
+
let t = T.value[E.value];
|
|
95
|
+
t && (e.preventDefault(), e.stopPropagation(), L(t));
|
|
94
96
|
break;
|
|
95
97
|
}
|
|
96
98
|
case "Escape":
|
|
@@ -98,41 +100,41 @@ var R = Symbol("kun-editor-context"), z = [
|
|
|
98
100
|
break;
|
|
99
101
|
}
|
|
100
102
|
};
|
|
101
|
-
return
|
|
102
|
-
S = new
|
|
103
|
-
content:
|
|
103
|
+
return u(() => {
|
|
104
|
+
S = new P({
|
|
105
|
+
content: b.value,
|
|
104
106
|
trigger: "@",
|
|
105
|
-
shouldShow:
|
|
107
|
+
shouldShow: I,
|
|
106
108
|
debounce: 0
|
|
107
109
|
}), S.onShow = () => {
|
|
108
110
|
C.value = !0;
|
|
109
111
|
}, S.onHide = () => {
|
|
110
|
-
C.value = !1,
|
|
111
|
-
}, S.update(
|
|
112
|
-
}),
|
|
113
|
-
S?.update(
|
|
114
|
-
}),
|
|
115
|
-
j && clearTimeout(j), window.removeEventListener("keydown",
|
|
116
|
-
}), (t, n) => (
|
|
112
|
+
C.value = !1, M();
|
|
113
|
+
}, S.update(_.value, y.value), window.addEventListener("keydown", R, !0);
|
|
114
|
+
}), v([_, y], () => {
|
|
115
|
+
S?.update(_.value, y.value);
|
|
116
|
+
}), l(() => {
|
|
117
|
+
j && clearTimeout(j), window.removeEventListener("keydown", R, !0), S?.destroy();
|
|
118
|
+
}), (t, n) => (d(), i("div", {
|
|
117
119
|
ref_key: "divRef",
|
|
118
|
-
ref:
|
|
120
|
+
ref: b,
|
|
119
121
|
class: "kun-mention-dropdown",
|
|
120
122
|
"data-show": "false"
|
|
121
|
-
}, [
|
|
123
|
+
}, [T.value.length ? (d(!0), i(e, { key: 0 }, m(T.value, (e, t) => (d(), i("button", {
|
|
122
124
|
key: e.id,
|
|
123
125
|
type: "button",
|
|
124
126
|
class: "kun-mention-dropdown__item",
|
|
125
|
-
"data-active": t ===
|
|
126
|
-
onMousedown:
|
|
127
|
-
onMouseenter: (e) =>
|
|
128
|
-
}, [e.avatar ? (
|
|
127
|
+
"data-active": t === E.value,
|
|
128
|
+
onMousedown: x((t) => L(e), ["prevent"]),
|
|
129
|
+
onMouseenter: (e) => E.value = t
|
|
130
|
+
}, [e.avatar ? (d(), i("img", {
|
|
129
131
|
key: 0,
|
|
130
132
|
src: e.avatar,
|
|
131
133
|
alt: e.name,
|
|
132
134
|
class: "kun-mention-dropdown__avatar"
|
|
133
|
-
}, null, 8,
|
|
135
|
+
}, null, 8, se)) : (d(), i("span", ce, h(e.name.charAt(0)), 1)), a("span", le, h(e.name), 1)], 40, oe))), 128)) : O.value ? (d(), i("div", ue, h(g.value.searching), 1)) : w.value ? (d(), i("div", de, h(g.value.empty), 1)) : (d(), i("div", fe, h(g.value.prompt), 1))], 512));
|
|
134
136
|
}
|
|
135
|
-
}),
|
|
137
|
+
}), me = /* @__PURE__ */ s({
|
|
136
138
|
__name: "MilkdownEditor",
|
|
137
139
|
props: {
|
|
138
140
|
modelValue: {},
|
|
@@ -143,32 +145,209 @@ var R = Symbol("kun-editor-context"), z = [
|
|
|
143
145
|
},
|
|
144
146
|
emits: ["update:modelValue"],
|
|
145
147
|
setup(e, { emit: t }) {
|
|
146
|
-
let r = e, i = t, a = r.modelValue, o =
|
|
147
|
-
let t =
|
|
148
|
-
t.set(
|
|
148
|
+
let r = e, i = t, a = r.modelValue, o = O(), s = F("kunMention"), c = r.features.mention !== !1 && !!r.adapters.searchMentionUsers && !!o, l = w((e) => {
|
|
149
|
+
let t = k.make().config((t) => {
|
|
150
|
+
t.set(M, e), t.set(A, r.modelValue), t.get(N).markdownUpdated((e, t, n) => {
|
|
149
151
|
t !== n && (a = t, i("update:modelValue", t));
|
|
150
|
-
}), t.update(
|
|
152
|
+
}), t.update(j, (e) => ({
|
|
151
153
|
...e,
|
|
152
154
|
editable: () => !r.readonly
|
|
153
|
-
})), c && t.set(s.key, { view: o({ component:
|
|
154
|
-
}).use(
|
|
155
|
+
})), c && t.set(s.key, { view: o({ component: pe }) });
|
|
156
|
+
}).use(R(r.adapters, r.features, { locale: r.locale }));
|
|
155
157
|
return c && t.use(s), t;
|
|
156
158
|
});
|
|
157
|
-
return
|
|
158
|
-
e !== a && (a = e, l.get()?.action(
|
|
159
|
-
}), (e, t) => (
|
|
159
|
+
return v(() => r.modelValue, (e) => {
|
|
160
|
+
e !== a && (a = e, l.get()?.action(L(e)));
|
|
161
|
+
}), (e, t) => (d(), n(g(S)));
|
|
160
162
|
}
|
|
161
|
-
}),
|
|
163
|
+
}), Y = (e) => `<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${e}</svg>`, X = (e, t = 13) => `<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><text x="12" y="12" dominant-baseline="central" text-anchor="middle" font-size="${t}" font-weight="700" font-family="ui-sans-serif, system-ui, sans-serif" fill="currentColor">${e}</text></svg>`, Z = {
|
|
164
|
+
bold: Y("<path d=\"M6 4h8a4 4 0 0 1 0 8H6z\"/><path d=\"M6 12h9a4 4 0 0 1 0 8H6z\"/>"),
|
|
165
|
+
italic: Y("<line x1=\"19\" y1=\"4\" x2=\"10\" y2=\"4\"/><line x1=\"14\" y1=\"20\" x2=\"5\" y2=\"20\"/><line x1=\"15\" y1=\"4\" x2=\"9\" y2=\"20\"/>"),
|
|
166
|
+
strike: Y("<path d=\"M16 4H9a3 3 0 0 0-2.83 4\"/><path d=\"M14 12a4 4 0 0 1 0 8H6\"/><line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/>"),
|
|
167
|
+
code: Y("<path d=\"m18 16 4-4-4-4\"/><path d=\"m6 8-4 4 4 4\"/><path d=\"m14.5 4-5 16\"/>"),
|
|
168
|
+
h1: X("H1", 11),
|
|
169
|
+
h2: X("H2", 11),
|
|
170
|
+
h3: X("H3", 11),
|
|
171
|
+
bulletList: Y("<line x1=\"8\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"8\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"8\" y1=\"18\" x2=\"21\" y2=\"18\"/><circle cx=\"3.5\" cy=\"6\" r=\"1.2\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"3.5\" cy=\"12\" r=\"1.2\" fill=\"currentColor\" stroke=\"none\"/><circle cx=\"3.5\" cy=\"18\" r=\"1.2\" fill=\"currentColor\" stroke=\"none\"/>"),
|
|
172
|
+
orderedList: Y("<line x1=\"10\" y1=\"6\" x2=\"21\" y2=\"6\"/><line x1=\"10\" y1=\"12\" x2=\"21\" y2=\"12\"/><line x1=\"10\" y1=\"18\" x2=\"21\" y2=\"18\"/><path d=\"M4 6h1v4\"/><path d=\"M4 10h2\"/><path d=\"M6 18H4c0-1 2-1.5 2-2.5S5 14 4 14.5\"/>"),
|
|
173
|
+
quote: Y("<path d=\"M6 17h3l2-4V7H5v6h3zm8 0h3l2-4V7h-6v6h3z\" fill=\"currentColor\" stroke=\"none\"/>"),
|
|
174
|
+
codeBlock: Y("<rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\"/><path d=\"m9 10-2 2 2 2\"/><path d=\"m15 10 2 2-2 2\"/>"),
|
|
175
|
+
hr: Y("<line x1=\"4\" y1=\"12\" x2=\"20\" y2=\"12\"/>"),
|
|
176
|
+
image: Y("<rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/><circle cx=\"9\" cy=\"9\" r=\"1.8\" fill=\"currentColor\" stroke=\"none\"/><path d=\"m21 15-4.5-4.5a2 2 0 0 0-2.8 0L5 19\"/>"),
|
|
177
|
+
spoiler: Y("<path d=\"M9.9 4.24A9.1 9.1 0 0 1 12 4c7 0 10 8 10 8a13.2 13.2 0 0 1-1.67 2.68\"/><path d=\"M6.6 6.6A13.5 13.5 0 0 0 2 12s3 8 10 8a9.7 9.7 0 0 0 5.4-1.6\"/><line x1=\"2\" y1=\"2\" x2=\"22\" y2=\"22\"/>"),
|
|
178
|
+
latex: X("Σ", 15)
|
|
179
|
+
}, he = {
|
|
180
|
+
class: "kun-editor__format-toolbar",
|
|
181
|
+
role: "toolbar"
|
|
182
|
+
}, ge = {
|
|
183
|
+
key: 0,
|
|
184
|
+
class: "kun-editor__toolbar-divider",
|
|
185
|
+
"aria-hidden": "true"
|
|
186
|
+
}, _e = [
|
|
187
|
+
"title",
|
|
188
|
+
"aria-label",
|
|
189
|
+
"onClick"
|
|
190
|
+
], ve = ["innerHTML"], ye = ["title", "aria-label"], be = ["innerHTML"], xe = /* @__PURE__ */ s({
|
|
191
|
+
__name: "EditorToolbar",
|
|
192
|
+
setup(n) {
|
|
193
|
+
let [, o] = T(), s = c(J), l = t(() => s?.adapters.uploadImage), u = () => s?.adapters.notify, f = t(() => (s?.locale ?? "zh-cn").toLowerCase().startsWith("en")), h = (e, t) => {
|
|
194
|
+
o()?.action(I(e, t));
|
|
195
|
+
}, _ = t(() => {
|
|
196
|
+
let e = f.value;
|
|
197
|
+
return {
|
|
198
|
+
bold: e ? "Bold" : "加粗",
|
|
199
|
+
italic: e ? "Italic" : "斜体",
|
|
200
|
+
strike: e ? "Strikethrough" : "删除线",
|
|
201
|
+
code: e ? "Inline code" : "行内代码",
|
|
202
|
+
h1: e ? "Heading 1" : "一级标题",
|
|
203
|
+
h2: e ? "Heading 2" : "二级标题",
|
|
204
|
+
h3: e ? "Heading 3" : "三级标题",
|
|
205
|
+
bulletList: e ? "Bullet list" : "无序列表",
|
|
206
|
+
orderedList: e ? "Ordered list" : "有序列表",
|
|
207
|
+
quote: e ? "Blockquote" : "引用块",
|
|
208
|
+
codeBlock: e ? "Code block" : "代码块",
|
|
209
|
+
hr: e ? "Divider" : "分隔线",
|
|
210
|
+
spoiler: e ? "Spoiler" : "隐藏文本",
|
|
211
|
+
latex: e ? "Math (LaTeX)" : "公式",
|
|
212
|
+
image: e ? "Upload image" : "上传图片"
|
|
213
|
+
};
|
|
214
|
+
}), v = t(() => [
|
|
215
|
+
[
|
|
216
|
+
{
|
|
217
|
+
svg: Z.bold,
|
|
218
|
+
title: _.value.bold,
|
|
219
|
+
run: () => h(H.key)
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
svg: Z.italic,
|
|
223
|
+
title: _.value.italic,
|
|
224
|
+
run: () => h(B.key)
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
svg: Z.strike,
|
|
228
|
+
title: _.value.strike,
|
|
229
|
+
run: () => h(q.key)
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
svg: Z.code,
|
|
233
|
+
title: _.value.code,
|
|
234
|
+
run: () => h(V.key)
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
[
|
|
238
|
+
{
|
|
239
|
+
svg: Z.h1,
|
|
240
|
+
title: _.value.h1,
|
|
241
|
+
run: () => h(G.key, 1)
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
svg: Z.h2,
|
|
245
|
+
title: _.value.h2,
|
|
246
|
+
run: () => h(G.key, 2)
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
svg: Z.h3,
|
|
250
|
+
title: _.value.h3,
|
|
251
|
+
run: () => h(G.key, 3)
|
|
252
|
+
}
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
{
|
|
256
|
+
svg: Z.bulletList,
|
|
257
|
+
title: _.value.bulletList,
|
|
258
|
+
run: () => h(W.key)
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
svg: Z.orderedList,
|
|
262
|
+
title: _.value.orderedList,
|
|
263
|
+
run: () => h(K.key)
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
svg: Z.quote,
|
|
267
|
+
title: _.value.quote,
|
|
268
|
+
run: () => h(U.key)
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
svg: Z.codeBlock,
|
|
272
|
+
title: _.value.codeBlock,
|
|
273
|
+
run: () => h(ie.key, "")
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
svg: Z.hr,
|
|
277
|
+
title: _.value.hr,
|
|
278
|
+
run: () => h(ae.key)
|
|
279
|
+
}
|
|
280
|
+
],
|
|
281
|
+
[{
|
|
282
|
+
svg: Z.spoiler,
|
|
283
|
+
title: _.value.spoiler,
|
|
284
|
+
run: () => h(ee.key)
|
|
285
|
+
}, {
|
|
286
|
+
svg: Z.latex,
|
|
287
|
+
title: _.value.latex,
|
|
288
|
+
run: () => h(ne.key)
|
|
289
|
+
}]
|
|
290
|
+
]), y = p(null), b = () => y.value?.click(), x = async (e) => {
|
|
291
|
+
let t = e.target, n = t.files?.[0];
|
|
292
|
+
t.value = "";
|
|
293
|
+
let r = l.value;
|
|
294
|
+
if (!(!n || !r)) try {
|
|
295
|
+
let e = await r(n);
|
|
296
|
+
h(z.key, {
|
|
297
|
+
src: e,
|
|
298
|
+
title: n.name,
|
|
299
|
+
alt: n.name
|
|
300
|
+
});
|
|
301
|
+
} catch {
|
|
302
|
+
u()?.(f.value ? "Image upload failed" : "图片上传失败", "error");
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
return (t, n) => (d(), i("div", he, [(d(!0), i(e, null, m(v.value, (t, n) => (d(), i(e, { key: n }, [n > 0 ? (d(), i("span", ge)) : r("", !0), (d(!0), i(e, null, m(t, (e, t) => (d(), i("button", {
|
|
306
|
+
key: t,
|
|
307
|
+
type: "button",
|
|
308
|
+
class: "kun-editor__tool",
|
|
309
|
+
title: e.title,
|
|
310
|
+
"aria-label": e.title,
|
|
311
|
+
onClick: (t) => e.run()
|
|
312
|
+
}, [a("span", {
|
|
313
|
+
class: "kun-editor__icon",
|
|
314
|
+
innerHTML: e.svg
|
|
315
|
+
}, null, 8, ve)], 8, _e))), 128))], 64))), 128)), l.value ? (d(), i(e, { key: 0 }, [
|
|
316
|
+
n[0] ||= a("span", {
|
|
317
|
+
class: "kun-editor__toolbar-divider",
|
|
318
|
+
"aria-hidden": "true"
|
|
319
|
+
}, null, -1),
|
|
320
|
+
a("button", {
|
|
321
|
+
type: "button",
|
|
322
|
+
class: "kun-editor__tool",
|
|
323
|
+
title: _.value.image,
|
|
324
|
+
"aria-label": _.value.image,
|
|
325
|
+
onClick: b
|
|
326
|
+
}, [a("span", {
|
|
327
|
+
class: "kun-editor__icon",
|
|
328
|
+
innerHTML: g(Z).image
|
|
329
|
+
}, null, 8, be)], 8, ye),
|
|
330
|
+
a("input", {
|
|
331
|
+
ref_key: "fileInput",
|
|
332
|
+
ref: y,
|
|
333
|
+
type: "file",
|
|
334
|
+
accept: ".jpg,.jpeg,.png,.webp,.gif",
|
|
335
|
+
hidden: "",
|
|
336
|
+
onChange: x
|
|
337
|
+
}, null, 544)
|
|
338
|
+
], 64)) : r("", !0)]));
|
|
339
|
+
}
|
|
340
|
+
}), Q = {
|
|
162
341
|
class: "kun-editor",
|
|
163
342
|
"data-kun-editor": ""
|
|
164
|
-
},
|
|
343
|
+
}, Se = {
|
|
165
344
|
class: "kun-editor__toolbar",
|
|
166
345
|
role: "tablist"
|
|
167
|
-
},
|
|
346
|
+
}, Ce = ["aria-selected", "data-active"], we = ["aria-selected", "data-active"], Te = { class: "kun-editor__wysiwyg" }, Ee = [
|
|
168
347
|
"value",
|
|
169
348
|
"readonly",
|
|
170
349
|
"lang"
|
|
171
|
-
], $ = /* @__PURE__ */
|
|
350
|
+
], $ = /* @__PURE__ */ s({
|
|
172
351
|
__name: "KunEditor",
|
|
173
352
|
props: {
|
|
174
353
|
modelValue: {},
|
|
@@ -182,68 +361,68 @@ var R = Symbol("kun-editor-context"), z = [
|
|
|
182
361
|
},
|
|
183
362
|
emits: ["update:modelValue"],
|
|
184
363
|
setup(e, { emit: n }) {
|
|
185
|
-
let
|
|
186
|
-
|
|
364
|
+
let r = e, s = n;
|
|
365
|
+
f(J, {
|
|
187
366
|
get adapters() {
|
|
188
|
-
return
|
|
367
|
+
return r.adapters;
|
|
189
368
|
},
|
|
190
369
|
get locale() {
|
|
191
|
-
return
|
|
370
|
+
return r.locale;
|
|
192
371
|
}
|
|
193
372
|
});
|
|
194
|
-
let c =
|
|
373
|
+
let c = p("wysiwyg"), l = t(() => r.locale.toLowerCase().startsWith("en")), u = t(() => l.value ? {
|
|
195
374
|
wysiwyg: "Preview",
|
|
196
375
|
source: "Markdown"
|
|
197
376
|
} : {
|
|
198
377
|
wysiwyg: "预览",
|
|
199
378
|
source: "Markdown"
|
|
200
|
-
}),
|
|
201
|
-
return (t, n) => (
|
|
202
|
-
|
|
379
|
+
}), m = (e) => s("update:modelValue", e), v = (e) => s("update:modelValue", e.target.value);
|
|
380
|
+
return (t, n) => (d(), i("div", Q, [
|
|
381
|
+
a("div", Se, [a("button", {
|
|
203
382
|
type: "button",
|
|
204
383
|
class: "kun-editor__tab",
|
|
205
384
|
"aria-selected": c.value === "wysiwyg",
|
|
206
385
|
"data-active": c.value === "wysiwyg",
|
|
207
386
|
onClick: n[0] ||= (e) => c.value = "wysiwyg"
|
|
208
|
-
},
|
|
387
|
+
}, h(u.value.wysiwyg), 9, Ce), a("button", {
|
|
209
388
|
type: "button",
|
|
210
389
|
class: "kun-editor__tab",
|
|
211
390
|
"aria-selected": c.value === "source",
|
|
212
391
|
"data-active": c.value === "source",
|
|
213
392
|
onClick: n[1] ||= (e) => c.value = "source"
|
|
214
|
-
},
|
|
215
|
-
|
|
216
|
-
default:
|
|
217
|
-
default:
|
|
393
|
+
}, h(u.value.source), 9, we)]),
|
|
394
|
+
o(g(C), null, {
|
|
395
|
+
default: y(() => [o(g(E), null, {
|
|
396
|
+
default: y(() => [b(o(xe, null, null, 512), [[_, c.value === "wysiwyg" && !e.readonly]]), b(a("div", Te, [o(me, {
|
|
218
397
|
"model-value": e.modelValue,
|
|
219
398
|
adapters: e.adapters,
|
|
220
399
|
features: e.features,
|
|
221
400
|
locale: e.locale,
|
|
222
401
|
readonly: e.readonly,
|
|
223
|
-
"onUpdate:modelValue":
|
|
402
|
+
"onUpdate:modelValue": m
|
|
224
403
|
}, null, 8, [
|
|
225
404
|
"model-value",
|
|
226
405
|
"adapters",
|
|
227
406
|
"features",
|
|
228
407
|
"locale",
|
|
229
408
|
"readonly"
|
|
230
|
-
])], 512), [[
|
|
409
|
+
])], 512), [[_, c.value === "wysiwyg"]])]),
|
|
231
410
|
_: 1
|
|
232
411
|
})]),
|
|
233
412
|
_: 1
|
|
234
413
|
}),
|
|
235
|
-
|
|
414
|
+
b(a("textarea", {
|
|
236
415
|
class: "kun-editor__source",
|
|
237
416
|
value: e.modelValue,
|
|
238
417
|
readonly: e.readonly,
|
|
239
418
|
lang: e.locale,
|
|
240
419
|
spellcheck: "false",
|
|
241
|
-
onInput:
|
|
242
|
-
}, null, 40,
|
|
420
|
+
onInput: v
|
|
421
|
+
}, null, 40, Ee), [[_, c.value === "source"]])
|
|
243
422
|
]));
|
|
244
423
|
}
|
|
245
|
-
}),
|
|
424
|
+
}), De = { install(e) {
|
|
246
425
|
e.component("KunEditor", $);
|
|
247
426
|
} };
|
|
248
427
|
//#endregion
|
|
249
|
-
export { $ as KunEditor,
|
|
428
|
+
export { $ as KunEditor, De as KunEditorPlugin };
|
package/dist/style.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.kun-mention-dropdown{z-index:30;background:var(--color-background,#fff);border:1px solid var(--color-default-200,#e4e4e7);border-radius:.5rem;min-width:12rem;max-width:20rem;max-height:16rem;padding:.25rem;display:none;position:absolute;overflow-y:auto;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a}.kun-mention-dropdown[data-show=true]{display:block}.kun-mention-dropdown__item{width:100%;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:.375rem;align-items:center;gap:.5rem;padding:.375rem .5rem;display:flex}.kun-mention-dropdown__item:hover,.kun-mention-dropdown__item[data-active=true]{background:var(--color-default-100,#f4f4f5)}.kun-mention-dropdown__avatar{object-fit:cover;border-radius:9999px;flex-shrink:0;width:1.5rem;height:1.5rem}.kun-mention-dropdown__avatar--fallback{background:var(--color-default-200,#e4e4e7);color:var(--color-default-600,#52525b);justify-content:center;align-items:center;font-size:.75rem;display:flex}.kun-mention-dropdown__name{text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;overflow:hidden}.kun-mention-dropdown__hint{color:var(--color-default-400,#a1a1aa);padding:.375rem .5rem;font-size:.875rem}.kun-editor{flex-direction:column;gap:.5rem;display:flex}.kun-editor__toolbar{gap:.25rem;display:flex}.kun-editor__tab{border:1px solid var(--color-default-200,#e4e4e7);font:inherit;cursor:pointer;background:0 0;border-radius:.5rem;padding:.25rem .75rem}.kun-editor__tab[data-active=true]{background:var(--color-primary,#006fee);border-color:var(--color-primary,#006fee);color:#fff}.kun-editor__wysiwyg .milkdown{outline:none}.kun-editor__wysiwyg .ProseMirror{border:1px solid var(--color-default-200,#e4e4e7);border-radius:.5rem;outline:none;min-height:8rem;padding:.75rem}.kun-editor__source{border:1px solid var(--color-default-200,#e4e4e7);resize:vertical;border-radius:.5rem;width:100%;min-height:8rem;padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.875rem}
|
|
1
|
+
.kun-mention-dropdown{z-index:30;background:var(--color-background,#fff);border:1px solid var(--color-default-200,#e4e4e7);border-radius:.5rem;min-width:12rem;max-width:20rem;max-height:16rem;padding:.25rem;display:none;position:absolute;overflow-y:auto;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -2px #0000001a}.kun-mention-dropdown[data-show=true]{display:block}.kun-mention-dropdown__item{width:100%;font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:.375rem;align-items:center;gap:.5rem;padding:.375rem .5rem;display:flex}.kun-mention-dropdown__item:hover,.kun-mention-dropdown__item[data-active=true]{background:var(--color-default-100,#f4f4f5)}.kun-mention-dropdown__avatar{object-fit:cover;border-radius:9999px;flex-shrink:0;width:1.5rem;height:1.5rem}.kun-mention-dropdown__avatar--fallback{background:var(--color-default-200,#e4e4e7);color:var(--color-default-600,#52525b);justify-content:center;align-items:center;font-size:.75rem;display:flex}.kun-mention-dropdown__name{text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;overflow:hidden}.kun-mention-dropdown__hint{color:var(--color-default-400,#a1a1aa);padding:.375rem .5rem;font-size:.875rem}.kun-editor__format-toolbar{flex-wrap:wrap;align-items:center;gap:.125rem;display:flex}.kun-editor__toolbar-divider{background:var(--color-default-200,#e4e4e7);align-self:stretch;width:1px;margin:.25rem}.kun-editor__tool{width:1.75rem;height:1.75rem;color:var(--color-foreground,#27272a);cursor:pointer;background:0 0;border:none;border-radius:.375rem;justify-content:center;align-items:center;padding:0;display:inline-flex}.kun-editor__tool:hover{background:var(--color-default-100,#f4f4f5)}.kun-editor__icon{display:inline-flex}.kun-editor{flex-direction:column;gap:.5rem;display:flex}.kun-editor__toolbar{gap:.25rem;display:flex}.kun-editor__tab{border:1px solid var(--color-default-200,#e4e4e7);font:inherit;cursor:pointer;background:0 0;border-radius:.5rem;padding:.25rem .75rem}.kun-editor__tab[data-active=true]{background:var(--color-primary,#006fee);border-color:var(--color-primary,#006fee);color:#fff}.kun-editor__wysiwyg .milkdown{outline:none}.kun-editor__wysiwyg .ProseMirror{border:1px solid var(--color-default-200,#e4e4e7);white-space:pre-wrap;word-wrap:break-word;border-radius:.5rem;outline:none;min-height:8rem;padding:.75rem}.kun-editor__source{border:1px solid var(--color-default-200,#e4e4e7);resize:vertical;border-radius:.5rem;width:100%;min-height:8rem;padding:.75rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.875rem}
|
|
2
2
|
/*$vite$:1*/
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const TOOLBAR_ICONS: {
|
|
2
|
+
readonly bold: string;
|
|
3
|
+
readonly italic: string;
|
|
4
|
+
readonly strike: string;
|
|
5
|
+
readonly code: string;
|
|
6
|
+
readonly h1: string;
|
|
7
|
+
readonly h2: string;
|
|
8
|
+
readonly h3: string;
|
|
9
|
+
readonly bulletList: string;
|
|
10
|
+
readonly orderedList: string;
|
|
11
|
+
readonly quote: string;
|
|
12
|
+
readonly codeBlock: string;
|
|
13
|
+
readonly hr: string;
|
|
14
|
+
readonly image: string;
|
|
15
|
+
readonly spoiler: string;
|
|
16
|
+
readonly latex: string;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=toolbar-icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar-icons.d.ts","sourceRoot":"","sources":["../src/toolbar-icons.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;CAgBhB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungal/editor-vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "KunEditor Vue 3 layer — the <KunEditor> component (dual WYSIWYG / markdown-source), toolbar and plugin views. Consumes @kungal/editor-core; pairs with @kungal/ui-vue.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@prosemirror-adapter/vue": "^0.4.6",
|
|
47
|
-
"@kungal/editor-core": "0.
|
|
47
|
+
"@kungal/editor-core": "0.5.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@kungal/ui-vue": "^2.7.0",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"vite": "^8.0.16",
|
|
64
64
|
"vue": "^3.5.35",
|
|
65
65
|
"vue-tsc": "^3.3.3",
|
|
66
|
-
"@kungal/editor-core": "0.
|
|
66
|
+
"@kungal/editor-core": "0.5.0"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "vite build && vue-tsc -p tsconfig.build.json",
|