@milkdown/plugin-emoji 6.5.4 → 7.0.0-next.1

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/lib/index.d.ts CHANGED
@@ -1,4 +1,15 @@
1
- import { AtomList } from '@milkdown/utils';
2
- export * from './node';
3
- export declare const emoji: AtomList<import("@milkdown/utils").Metadata<import("@milkdown/utils").GetPlugin<string, import("./node").EmojiOptions>> & import("@milkdown/core").MilkdownPlugin>;
1
+ import type { MilkdownPlugin } from '@milkdown/ctx';
2
+ import type Twemoji from 'twemoji';
3
+ type TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>;
4
+ export interface EmojiConfig {
5
+ twemojiOptions?: TwemojiOptions;
6
+ }
7
+ export declare const emojiConfig: import("@milkdown/utils").$Ctx<EmojiConfig, "emojiConfig">;
8
+ export declare const emojiAttr: import("@milkdown/utils").$NodeAttr;
9
+ export declare const emojiSchema: import("@milkdown/utils").$NodeSchema<"emoji">;
10
+ export declare const insertEmojiInputRule: import("@milkdown/utils").$InputRule;
11
+ export declare const remarkEmojiPlugin: import("@milkdown/utils").$Remark;
12
+ export declare const remarkTwemojiPlugin: import("@milkdown/utils").$Remark;
13
+ export declare const emoji: MilkdownPlugin[];
14
+ export {};
4
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,cAAc,QAAQ,CAAA;AAEtB,eAAO,MAAM,KAAK,oKAAiC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAOnD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAKlC,KAAK,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAA;AAGxF,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAGD,eAAO,MAAM,WAAW,4DAAsD,CAAA;AAG9E,eAAO,MAAM,SAAS,qCAGnB,CAAA;AAGH,eAAO,MAAM,WAAW,gDA+CrB,CAAA;AAIH,eAAO,MAAM,oBAAoB,sCAc9B,CAAA;AAGH,eAAO,MAAM,iBAAiB,mCAA6C,CAAA;AAG3E,eAAO,MAAM,mBAAmB,mCAAyE,CAAA;AAGzG,eAAO,MAAM,KAAK,EAAE,cAAc,EAO1B,CAAA"}
package/lib/index.es.js CHANGED
@@ -1,289 +1,114 @@
1
- import { createNode as _, AtomList as O } from "@milkdown/utils";
2
- import { missingRootElement as N, expectDomTypeError as H } from "@milkdown/exception";
3
- import { InputRule as F } from "@milkdown/prose/inputrules";
4
- import A from "node-emoji";
5
- import I from "remark-emoji";
6
- import { calculateNodePosition as $ } from "@milkdown/prose";
7
- import { PluginKey as R, Plugin as B } from "@milkdown/prose/state";
8
- import K from "twemoji";
9
- import { ThemeBorder as W, ThemeShadow as z, ThemeScrollbar as U, ThemeSize as q, ThemeFont as J, ThemeColor as G } from "@milkdown/core";
10
- import Q from "emoji-regex";
11
- const X = /:\+1|:-1|:[\w-]+/, Y = /:\+1:|:-1:|:[\w-]+:/, Z = /(:([^:\s]+):)$/, V = (n) => ({ title: n }), C = (n, s) => K.parse(n, { attributes: V, ...s }), ee = (n, s, d, e, l, o) => {
12
- if (n.composing)
13
- return !1;
14
- const { state: r } = n, i = r.doc.resolve(s);
15
- if (i.parent.type.spec.code)
16
- return !1;
17
- const a = (i.parent.textBetween(Math.max(0, i.parentOffset - 10), i.parentOffset, void 0, "\uFFFC") + e).toLowerCase();
18
- if (Y.test(a))
19
- return !1;
20
- const t = X.exec(a);
21
- if (t && t[0] && a.endsWith(t[0])) {
22
- const m = t[0];
23
- return l(s - (m.length - e.length), d), o(m), !0;
24
- }
25
- return !1;
26
- }, te = (n, s, d, e, l) => {
27
- for (; s.firstChild; )
28
- s.firstChild.remove();
29
- n.forEach(({ emoji: o, key: r }, i) => {
30
- const a = document.createElement("div");
31
- a.className = "milkdown-emoji-filter_item";
32
- const t = document.createElement("span");
33
- t.innerHTML = C(o, l), t.className = "milkdown-emoji-filter_item-emoji";
34
- const m = document.createElement("span");
35
- m.textContent = `:${r}:`, m.className = "milkdown-emoji-filter_item-key", a.appendChild(t), a.appendChild(m), s.appendChild(a), i === 0 && e(a);
36
- const c = (u) => {
37
- const { target: j } = u;
38
- j instanceof HTMLElement && e(j);
39
- }, w = (u) => {
40
- u.preventDefault(), d();
41
- };
42
- a.addEventListener("mouseenter", c), a.addEventListener("mousedown", w);
43
- });
44
- }, ne = (n, { css: s, cx: d }) => {
45
- const e = n.get(W, void 0), l = n.get(z, void 0), o = n.get(U, void 0), r = n.get(q, "radius"), i = n.get(J, "typography"), a = (m, c = 1) => n.get(G, [m, c]), t = s`
46
- min-height: 36px;
47
- max-height: 320px;
48
- overflow-y: auto;
49
- border-radius: ${r};
50
- position: absolute;
51
- background: ${a("surface")};
52
-
53
- &.hide {
54
- display: none;
55
- }
56
-
57
- .milkdown-emoji-filter_item {
58
- display: flex;
59
- gap: 8px;
60
- height: 36px;
61
- padding: 0 14px;
62
- align-items: center;
63
- justify-content: flex-start;
64
- cursor: pointer;
65
- line-height: 2;
66
- font-family: ${i};
67
- font-size: 14px;
68
- &.active {
69
- background: ${a("secondary", 0.12)};
70
- color: ${a("primary")};
71
- }
72
- }
73
-
74
- .emoji {
75
- height: 14px;
76
- width: 14px;
77
- margin: 0 1px 0 1.5px;
78
- vertical-align: -1.5px;
79
- }
80
- `;
81
- return d(e, l, o, t);
82
- }, oe = new R("MILKDOWN_EMOJI_FILTER"), re = (n, s, d) => {
83
- let e = !1, l = 0, o = "", r = null;
84
- const i = () => {
85
- e = !1, l = 0, o = "", r = null;
86
- }, a = (t) => {
87
- r && r.classList.remove("active"), t && t.classList.add("active"), r = t;
88
- };
89
- return new B({
90
- key: oe,
91
- props: {
92
- handleKeyDown(t, m) {
93
- return ["Delete", "Backspace"].includes(m.key) ? (o = o.slice(0, -1), o.length <= 1 && i(), !1) : !(!e || !["ArrowUp", "ArrowDown", "Enter"].includes(m.key));
94
- },
95
- handleTextInput(t, m, c, w) {
96
- return e = ee(
97
- t,
98
- m,
99
- c,
100
- w,
101
- (u) => {
102
- l = u;
103
- },
104
- (u) => {
105
- o = u;
106
- }
107
- ), e || i(), !1;
108
- }
109
- },
110
- view: (t) => {
111
- const { parentNode: m } = t.dom;
112
- if (!m)
113
- throw N();
114
- const c = document.createElement("div");
115
- c.classList.add("milkdown-emoji-filter", "hide"), n.themeManager.onFlush(() => {
116
- const h = c.className.split(" ").filter((f) => ["hide", "milkdown-emoji-filter"].includes(f));
117
- c.className = h.join(" ");
118
- const p = n.getStyle((f) => ne(n.themeManager, f));
119
- p && p.split(" ").forEach((f) => c.classList.add(f));
120
- });
121
- const w = () => {
122
- var f;
123
- if (!r)
124
- return;
125
- const { tr: h } = t.state, p = t.state.schema.node("emoji", { html: (f = r.firstElementChild) == null ? void 0 : f.innerHTML });
126
- t.dispatch(h.delete(l, l + o.length).insert(l, p)), i(), c.classList.add("hide");
127
- };
128
- m.appendChild(c);
129
- const u = (h) => {
130
- if (!e || !(h instanceof KeyboardEvent))
131
- return;
132
- const { key: p } = h;
133
- if (p === "Enter") {
134
- w();
135
- return;
136
- }
137
- if (["ArrowDown", "ArrowUp"].includes(p)) {
138
- const f = p === "ArrowDown" ? (r == null ? void 0 : r.nextElementSibling) || c.firstElementChild : (r == null ? void 0 : r.previousElementSibling) || c.lastElementChild;
139
- if (!f)
140
- return;
141
- a(f);
142
- }
143
- }, j = (h) => {
144
- !e || (h.stopPropagation(), i(), c.classList.add("hide"));
145
- };
146
- return m.addEventListener("keydown", u), m.addEventListener("mousedown", j), {
147
- update: (h) => {
148
- const { selection: p } = h.state;
149
- if (p.from - p.to !== 0 || !e)
150
- return i(), c.classList.add("hide"), null;
151
- const f = A.search(o).slice(0, s), { node: D } = h.domAtPos(l);
152
- return f.length === 0 || !D ? (c.classList.add("hide"), null) : (c.style.maxHeight = "", c.classList.remove("hide"), te(f, c, w, a, d), $(h, c, (me, v, y) => {
153
- const k = c.parentElement;
154
- if (!k)
155
- throw N();
156
- const x = h.coordsAtPos(l);
157
- let E = x.left - y.left;
158
- E < 0 && (E = 0);
159
- let L, g;
160
- const T = x.top - y.top, b = y.height + y.top - x.bottom;
161
- b >= v.height + 28 ? L = "bottom" : T >= v.height + 28 ? L = "top" : b >= T ? (L = "bottom", g = b - 28) : (L = "top", g = T - 28), (T < 0 || b < 0) && (g = y.height - (x.bottom - x.top) - 28, g > v.height && (g = void 0));
162
- const P = L === "top" ? x.top - y.top - (g != null ? g : v.height) - 14 + k.scrollTop : x.bottom - y.top + 14 + k.scrollTop;
163
- c.style.maxHeight = g !== void 0 && g > 0 ? `${g}px` : "";
164
- const M = k.clientWidth - (c.offsetWidth + 4);
165
- return E > M && (E = M), [P, E];
166
- }), null);
167
- },
168
- destroy: () => {
169
- m.removeEventListener("keydown", u), m.removeEventListener("mousedown", j), c.remove();
170
- }
171
- };
172
- }
173
- });
174
- }, S = Q(), ie = (n) => !!n.children, se = (n) => !!n.value;
175
- function le(n, s) {
176
- return d(n, 0, null)[0];
177
- function d(e, l, o) {
178
- if (ie(e)) {
179
- const r = [];
180
- for (let i = 0, a = e.children.length; i < a; i++) {
181
- const t = e.children[i];
182
- if (t) {
183
- const m = d(t, i, e);
184
- if (m)
185
- for (let c = 0, w = m.length; c < w; c++) {
186
- const u = m[c];
187
- u && r.push(u);
1
+ import { expectDomTypeError as E } from "@milkdown/exception";
2
+ import { InputRule as M } from "@milkdown/prose/inputrules";
3
+ import { $ctx as x, $nodeAttr as y, $nodeSchema as k, $inputRule as T, $remark as j } from "@milkdown/utils";
4
+ import L from "node-emoji";
5
+ import $ from "remark-emoji";
6
+ import A from "twemoji";
7
+ import H from "emoji-regex";
8
+ const O = (n) => ({ title: n }), g = (n, e) => A.parse(n, { attributes: O, base: "https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/", ...e }), h = H(), R = (n) => !!n.children, b = (n) => !!n.value;
9
+ function C(n, e) {
10
+ return s(n, 0, null)[0];
11
+ function s(t, r, o) {
12
+ if (R(t)) {
13
+ const m = [];
14
+ for (let i = 0, c = t.children.length; i < c; i++) {
15
+ const l = t.children[i];
16
+ if (l) {
17
+ const a = s(l, i, t);
18
+ if (a)
19
+ for (let u = 0, v = a.length; u < v; u++) {
20
+ const f = a[u];
21
+ f && m.push(f);
188
22
  }
189
23
  }
190
24
  }
191
- e.children = r;
25
+ t.children = m;
192
26
  }
193
- return s(e, l, o);
27
+ return e(t, r, o);
194
28
  }
195
29
  }
196
- const ce = (n) => () => {
197
- function s(d) {
198
- le(d, (e) => {
199
- if (!se(e))
200
- return [e];
201
- const l = e.value, o = [];
202
- let r, i = l;
203
- for (; r = S.exec(i); ) {
204
- const { index: a } = r, t = r[0];
205
- t && (a > 0 && o.push({ ...e, value: i.slice(0, a) }), o.push({ ...e, value: C(t, n), type: "emoji" }), i = i.slice(a + t.length)), S.lastIndex = 0;
30
+ const I = (n) => () => {
31
+ function e(s) {
32
+ C(s, (t) => {
33
+ if (!b(t))
34
+ return [t];
35
+ const r = t.value, o = [];
36
+ let m, i = r;
37
+ for (; m = h.exec(i); ) {
38
+ const { index: c } = m, l = m[0];
39
+ l && (c > 0 && o.push({ ...t, value: i.slice(0, c) }), o.push({ ...t, value: g(l, n), type: "emoji" }), i = i.slice(c + l.length)), h.lastIndex = 0;
206
40
  }
207
- return i.length && o.push({ ...e, value: i }), o;
41
+ return i.length && o.push({ ...t, value: i }), o;
208
42
  });
209
43
  }
210
- return s;
211
- }, ae = _((n, s) => {
212
- const d = () => n.getStyle(
213
- ({ css: e }) => e`
214
- .emoji {
215
- height: 1em;
216
- width: 1em;
217
- margin: 0 1px 0 1.5px;
218
- vertical-align: -1.5px;
219
- }
220
- `
221
- );
222
- return {
223
- id: "emoji",
224
- schema: () => ({
225
- group: "inline",
226
- inline: !0,
227
- atom: !0,
228
- attrs: {
229
- html: {
230
- default: ""
231
- }
232
- },
233
- parseDOM: [
234
- {
235
- tag: 'span[data-type="emoji"]',
236
- getAttrs: (e) => {
237
- if (!(e instanceof HTMLElement))
238
- throw H(e);
239
- return { html: e.innerHTML };
240
- }
241
- }
242
- ],
243
- toDOM: (e) => {
244
- const l = document.createElement("span");
245
- return l.classList.add("emoji-wrapper"), l.dataset.type = "emoji", n.themeManager.onFlush(() => {
246
- const o = d();
247
- o && l.classList.add(o);
248
- }), l.innerHTML = e.attrs.html, { dom: l };
249
- },
250
- parseMarkdown: {
251
- match: ({ type: e }) => e === "emoji",
252
- runner: (e, l, o) => {
253
- e.addNode(o, { html: l.value });
254
- }
255
- },
256
- toMarkdown: {
257
- match: (e) => e.type.name === "emoji",
258
- runner: (e, l) => {
259
- const o = document.createElement("span");
260
- o.innerHTML = l.attrs.html;
261
- const r = o.querySelector("img"), i = r == null ? void 0 : r.title;
262
- o.remove(), e.addNode("text", void 0, i);
263
- }
44
+ return e;
45
+ }, p = x({}, "emojiConfig"), d = y("emoji", () => ({
46
+ span: {},
47
+ img: {}
48
+ })), w = k("emoji", (n) => ({
49
+ group: "inline",
50
+ inline: !0,
51
+ attrs: {
52
+ html: {
53
+ default: ""
54
+ }
55
+ },
56
+ parseDOM: [
57
+ {
58
+ tag: 'span[data-type="emoji"]',
59
+ getAttrs: (e) => {
60
+ if (!(e instanceof HTMLElement))
61
+ throw E(e);
62
+ return { html: e.innerHTML };
264
63
  }
265
- }),
266
- inputRules: (e) => [
267
- new F(Z, (l, o, r, i) => {
268
- const a = o[0];
269
- if (!a)
270
- return null;
271
- const t = A.get(a);
272
- if (!t || a.includes(t))
273
- return null;
274
- const m = C(t, s == null ? void 0 : s.twemojiOptions);
275
- return l.tr.setMeta("emoji", !0).replaceRangeWith(r, i, e.create({ html: m })).scrollIntoView();
276
- })
277
- ],
278
- remarkPlugins: () => [I, ce(s == null ? void 0 : s.twemojiOptions)],
279
- prosePlugins: () => {
280
- var e;
281
- return [re(n, (e = s == null ? void 0 : s.maxListSize) != null ? e : 6, s == null ? void 0 : s.twemojiOptions)];
282
64
  }
283
- };
284
- }), Ee = O.create([ae()]);
65
+ ],
66
+ toDOM: (e) => {
67
+ var o;
68
+ const s = n.get(d.key)(e), t = document.createElement("span");
69
+ t.innerHTML = e.attrs.html;
70
+ const r = (o = t.firstElementChild) == null ? void 0 : o.cloneNode();
71
+ return t.remove(), r && r instanceof HTMLElement && Object.entries(s.img).forEach(([m, i]) => r.setAttribute(m, i)), ["span", { ...s.container, "data-type": "emoji" }, r];
72
+ },
73
+ parseMarkdown: {
74
+ match: ({ type: e }) => e === "emoji",
75
+ runner: (e, s, t) => {
76
+ e.addNode(t, { html: s.value });
77
+ }
78
+ },
79
+ toMarkdown: {
80
+ match: (e) => e.type.name === "emoji",
81
+ runner: (e, s) => {
82
+ const t = document.createElement("span");
83
+ t.innerHTML = s.attrs.html;
84
+ const r = t.querySelector("img"), o = (r == null ? void 0 : r.title) || (r == null ? void 0 : r.alt);
85
+ t.remove(), e.addNode("text", void 0, o);
86
+ }
87
+ }
88
+ })), P = T((n) => new M(/(:([^:\s]+):)$/, (e, s, t, r) => {
89
+ const o = s[0];
90
+ if (!o)
91
+ return null;
92
+ const m = L.get(o);
93
+ if (!m || o.includes(m))
94
+ return null;
95
+ const i = g(m, n.get(p.key).twemojiOptions);
96
+ return e.tr.setMeta("emoji", !0).replaceRangeWith(t, r, w.type().create({ html: i })).scrollIntoView();
97
+ })), D = j(() => $), N = j((n) => I(n.get(p.key).twemojiOptions)), G = [
98
+ d,
99
+ p,
100
+ D,
101
+ N,
102
+ w,
103
+ P
104
+ ].flat();
285
105
  export {
286
- Ee as emoji,
287
- ae as emojiNode
106
+ G as emoji,
107
+ d as emojiAttr,
108
+ p as emojiConfig,
109
+ w as emojiSchema,
110
+ P as insertEmojiInputRule,
111
+ D as remarkEmojiPlugin,
112
+ N as remarkTwemojiPlugin
288
113
  };
289
114
  //# sourceMappingURL=index.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/constant.ts","../src/parse.ts","../src/filter/helper.ts","../src/filter/style.ts","../src/filter/index.ts","../src/remark-twemoji.ts","../src/node.ts","../src/index.ts"],"sourcesContent":["/* Copyright 2021, Milkdown by Mirone. */\nexport const part = /:\\+1|:-1|:[\\w-]+/\nexport const full = /:\\+1:|:-1:|:[\\w-]+:/\nexport const input = /(:([^:\\s]+):)$/\n","/* Copyright 2021, Milkdown by Mirone. */\nimport twemoji from 'twemoji'\n\nconst setAttr = (text: string) => ({ title: text })\n\nexport const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>\n twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport type { EditorView } from '@milkdown/prose/view'\nimport type { Emoji } from 'node-emoji'\n\nimport { full, part } from '../constant'\nimport { parse } from '../parse'\n\nexport const checkTrigger = (\n view: EditorView,\n from: number,\n to: number,\n text: string,\n setRange: (from: number, to: number) => void,\n setSearch: (words: string) => void,\n) => {\n if (view.composing)\n return false\n const { state } = view\n const $from = state.doc.resolve(from)\n if ($from.parent.type.spec.code)\n return false\n const textBefore = (\n $from.parent.textBetween(Math.max(0, $from.parentOffset - 10), $from.parentOffset, undefined, '\\uFFFC') + text\n ).toLowerCase()\n if (full.test(textBefore))\n return false\n\n const regex = part.exec(textBefore)\n if (regex && regex[0] && textBefore.endsWith(regex[0])) {\n const match = regex[0]\n setRange(from - (match.length - text.length), to)\n setSearch(match)\n return true\n }\n return false\n}\n\nexport const renderDropdownList = (\n list: Emoji[],\n dropDown: HTMLElement,\n onConfirm: () => void,\n setActive: (active: HTMLElement | null) => void,\n twemojiOptions?: TwemojiOptions,\n) => {\n while (dropDown.firstChild)\n dropDown.firstChild.remove()\n\n list.forEach(({ emoji, key }, i) => {\n const container = document.createElement('div')\n container.className = 'milkdown-emoji-filter_item'\n\n const emojiSpan = document.createElement('span')\n emojiSpan.innerHTML = parse(emoji, twemojiOptions)\n\n emojiSpan.className = 'milkdown-emoji-filter_item-emoji'\n const keySpan = document.createElement('span')\n keySpan.textContent = `:${key}:`\n keySpan.className = 'milkdown-emoji-filter_item-key'\n\n container.appendChild(emojiSpan)\n container.appendChild(keySpan)\n dropDown.appendChild(container)\n\n if (i === 0)\n setActive(container)\n\n const onEnter = (e: MouseEvent) => {\n const { target } = e\n if (!(target instanceof HTMLElement))\n return\n setActive(target)\n }\n\n const onClick = (e: MouseEvent) => {\n e.preventDefault()\n onConfirm()\n }\n\n container.addEventListener('mouseenter', onEnter)\n container.addEventListener('mousedown', onClick)\n })\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type {\n Color,\n Emotion,\n ThemeManager,\n} from '@milkdown/core'\nimport {\n ThemeBorder,\n ThemeColor,\n ThemeFont,\n ThemeScrollbar,\n ThemeShadow,\n ThemeSize,\n} from '@milkdown/core'\n\nexport const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {\n const border = themeManager.get(ThemeBorder, undefined)\n const shadow = themeManager.get(ThemeShadow, undefined)\n const scrollbar = themeManager.get(ThemeScrollbar, undefined)\n const radius = themeManager.get(ThemeSize, 'radius')\n const typography = themeManager.get(ThemeFont, 'typography')\n const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity])\n\n const style = css`\n min-height: 36px;\n max-height: 320px;\n overflow-y: auto;\n border-radius: ${radius};\n position: absolute;\n background: ${palette('surface')};\n\n &.hide {\n display: none;\n }\n\n .milkdown-emoji-filter_item {\n display: flex;\n gap: 8px;\n height: 36px;\n padding: 0 14px;\n align-items: center;\n justify-content: flex-start;\n cursor: pointer;\n line-height: 2;\n font-family: ${typography};\n font-size: 14px;\n &.active {\n background: ${palette('secondary', 0.12)};\n color: ${palette('primary')};\n }\n }\n\n .emoji {\n height: 14px;\n width: 14px;\n margin: 0 1px 0 1.5px;\n vertical-align: -1.5px;\n }\n `\n return cx(border, shadow, scrollbar, style)\n}\n","/* Copyright 2021, Milkdown by Mirone. */\n\nimport { missingRootElement } from '@milkdown/exception'\nimport { calculateNodePosition } from '@milkdown/prose'\nimport { Plugin, PluginKey } from '@milkdown/prose/state'\nimport type { ThemeUtils } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\n\nimport { checkTrigger, renderDropdownList } from './helper'\nimport { injectStyle } from './style'\n\nexport const key = new PluginKey('MILKDOWN_EMOJI_FILTER')\n\nexport const filter = (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => {\n let trigger = false\n let _from = 0\n let _search = ''\n let $active: null | HTMLElement = null\n\n const off = () => {\n trigger = false\n _from = 0\n _search = ''\n $active = null\n }\n\n const setActive = (active: HTMLElement | null) => {\n if ($active)\n $active.classList.remove('active')\n\n if (active)\n active.classList.add('active')\n\n $active = active\n }\n\n return new Plugin({\n key,\n props: {\n handleKeyDown(_, event) {\n if (['Delete', 'Backspace'].includes(event.key)) {\n _search = _search.slice(0, -1)\n if (_search.length <= 1)\n off()\n\n return false\n }\n if (!trigger)\n return false\n if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key))\n return false\n\n return true\n },\n handleTextInput(view, from, to, text) {\n trigger = checkTrigger(\n view,\n from,\n to,\n text,\n (from) => {\n _from = from\n },\n (search) => {\n _search = search\n },\n )\n if (!trigger)\n off()\n\n return false\n },\n },\n view: (editorView) => {\n const { parentNode } = editorView.dom\n if (!parentNode)\n throw missingRootElement()\n\n const dropDown = document.createElement('div')\n\n dropDown.classList.add('milkdown-emoji-filter', 'hide')\n\n utils.themeManager.onFlush(() => {\n const className = dropDown.className\n .split(' ')\n .filter(x => ['hide', 'milkdown-emoji-filter'].includes(x))\n dropDown.className = className.join(' ')\n const style = utils.getStyle(emotion => injectStyle(utils.themeManager, emotion))\n if (style)\n style.split(' ').forEach(x => dropDown.classList.add(x))\n })\n\n const replace = () => {\n if (!$active)\n return\n\n const { tr } = editorView.state\n const node = editorView.state.schema.node('emoji', { html: $active.firstElementChild?.innerHTML })\n\n editorView.dispatch(tr.delete(_from, _from + _search.length).insert(_from, node))\n off()\n dropDown.classList.add('hide')\n }\n\n parentNode.appendChild(dropDown)\n const onKeydown = (e: Event) => {\n if (!trigger || !(e instanceof KeyboardEvent))\n return\n\n const { key } = e\n\n if (key === 'Enter') {\n replace()\n return\n }\n\n if (['ArrowDown', 'ArrowUp'].includes(key)) {\n const next\n = key === 'ArrowDown'\n ? $active?.nextElementSibling || dropDown.firstElementChild\n : $active?.previousElementSibling || dropDown.lastElementChild\n if (!next)\n return\n setActive(next as HTMLElement)\n }\n }\n const onClick = (e: Event) => {\n if (!trigger)\n return\n\n e.stopPropagation()\n off()\n dropDown.classList.add('hide')\n }\n parentNode.addEventListener('keydown', onKeydown)\n parentNode.addEventListener('mousedown', onClick)\n\n return {\n update: (view) => {\n const { selection } = view.state\n\n if (selection.from - selection.to !== 0 || !trigger) {\n off()\n dropDown.classList.add('hide')\n return null\n }\n const result = nodeEmoji.search(_search).slice(0, maxListSize)\n const { node } = view.domAtPos(_from)\n if (result.length === 0 || !node) {\n dropDown.classList.add('hide')\n return null\n }\n\n dropDown.style.maxHeight = ''\n dropDown.classList.remove('hide')\n renderDropdownList(result, dropDown, replace, setActive, twemojiOptions)\n calculateNodePosition(view, dropDown, (_selected, target, parent) => {\n const $editor = dropDown.parentElement\n if (!$editor)\n throw missingRootElement()\n\n const start = view.coordsAtPos(_from)\n let left = start.left - parent.left\n\n if (left < 0)\n left = 0\n\n let direction: 'top' | 'bottom'\n let maxHeight: number | undefined\n const startToTop = start.top - parent.top\n const startToBottom = parent.height + parent.top - start.bottom\n if (startToBottom >= target.height + 28) {\n direction = 'bottom'\n }\n else if (startToTop >= target.height + 28) {\n direction = 'top'\n }\n else if (startToBottom >= startToTop) {\n direction = 'bottom'\n maxHeight = startToBottom - 28\n }\n else {\n direction = 'top'\n maxHeight = startToTop - 28\n }\n if (startToTop < 0 || startToBottom < 0) {\n maxHeight = parent.height - (start.bottom - start.top) - 28\n if (maxHeight > target.height)\n maxHeight = undefined\n }\n\n const top\n = direction === 'top'\n ? start.top - parent.top - (maxHeight ?? target.height) - 14 + $editor.scrollTop\n : start.bottom - parent.top + 14 + $editor.scrollTop\n\n dropDown.style.maxHeight = maxHeight !== undefined && maxHeight > 0 ? `${maxHeight}px` : ''\n\n const maxLeft = $editor.clientWidth - (dropDown.offsetWidth + 4)\n if (left > maxLeft)\n left = maxLeft\n\n return [top, left]\n })\n\n return null\n },\n\n destroy: () => {\n parentNode.removeEventListener('keydown', onKeydown)\n parentNode.removeEventListener('mousedown', onClick)\n dropDown.remove()\n },\n }\n },\n })\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/core'\nimport emojiRegex from 'emoji-regex'\nimport type { Literal, Node, Parent } from 'unist'\n\nimport { parse } from './parse'\n\nconst regex = emojiRegex()\n\nconst isParent = (node: Node): node is Parent => !!(node as Parent).children\nconst isLiteral = (node: Node): node is Literal => !!(node as Literal).value\n\nfunction flatMap(ast: Node, fn: (node: Node, index: number, parent: Node | null) => Node[]) {\n return transform(ast, 0, null)[0]\n\n function transform(node: Node, index: number, parent: Node | null) {\n if (isParent(node)) {\n const out = []\n for (let i = 0, n = node.children.length; i < n; i++) {\n const nthChild = node.children[i]\n if (nthChild) {\n const xs = transform(nthChild, i, node)\n if (xs) {\n for (let j = 0, m = xs.length; j < m; j++) {\n const item = xs[j]\n if (item)\n out.push(item)\n }\n }\n }\n }\n node.children = out\n }\n\n return fn(node, index, parent)\n }\n}\n\nexport const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = twemojiOptions => () => {\n function transformer(tree: Node) {\n flatMap(tree, (node) => {\n if (!isLiteral(node))\n return [node]\n\n const value = node.value as string\n const output: Literal<string>[] = []\n let match\n let str = value\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(str))) {\n const { index } = match\n const emoji = match[0]\n if (emoji) {\n if (index > 0)\n output.push({ ...node, value: str.slice(0, index) })\n\n output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' })\n str = str.slice(index + emoji.length)\n }\n regex.lastIndex = 0\n }\n if (str.length)\n output.push({ ...node, value: str })\n\n return output\n })\n }\n return transformer\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/core'\nimport { expectDomTypeError } from '@milkdown/exception'\nimport { InputRule } from '@milkdown/prose/inputrules'\nimport { createNode } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\nimport remarkEmoji from 'remark-emoji'\n\nimport { input } from './constant'\nimport { filter } from './filter'\nimport { parse } from './parse'\nimport { twemojiPlugin } from './remark-twemoji'\n\nexport interface EmojiOptions {\n maxListSize: number\n twemojiOptions: TwemojiOptions\n}\n\nexport const emojiNode = createNode<string, EmojiOptions>((utils, options) => {\n const getStyle = () =>\n utils.getStyle(\n ({ css }) => css`\n .emoji {\n height: 1em;\n width: 1em;\n margin: 0 1px 0 1.5px;\n vertical-align: -1.5px;\n }\n `,\n )\n return {\n id: 'emoji',\n schema: () => ({\n group: 'inline',\n inline: true,\n atom: true,\n attrs: {\n html: {\n default: '',\n },\n },\n parseDOM: [\n {\n tag: 'span[data-type=\"emoji\"]',\n getAttrs: (dom) => {\n if (!(dom instanceof HTMLElement))\n throw expectDomTypeError(dom)\n\n return { html: dom.innerHTML }\n },\n },\n ],\n toDOM: (node) => {\n const span = document.createElement('span')\n span.classList.add('emoji-wrapper')\n span.dataset.type = 'emoji'\n utils.themeManager.onFlush(() => {\n const style = getStyle()\n if (style)\n span.classList.add(style)\n })\n span.innerHTML = node.attrs.html\n return { dom: span }\n },\n parseMarkdown: {\n match: ({ type }) => type === 'emoji',\n runner: (state, node, type) => {\n state.addNode(type, { html: node.value as string })\n },\n },\n toMarkdown: {\n match: node => node.type.name === 'emoji',\n runner: (state, node) => {\n const span = document.createElement('span')\n span.innerHTML = node.attrs.html\n const img = span.querySelector('img')\n const title = img?.title\n span.remove()\n state.addNode('text', undefined, title)\n },\n },\n }),\n inputRules: nodeType => [\n new InputRule(input, (state, match, start, end) => {\n const content = match[0]\n if (!content)\n return null\n const got = nodeEmoji.get(content)\n if (!got || content.includes(got))\n return null\n\n const html = parse(got, options?.twemojiOptions)\n\n return state.tr\n .setMeta('emoji', true)\n .replaceRangeWith(start, end, nodeType.create({ html }))\n .scrollIntoView()\n }),\n ],\n remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],\n prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],\n }\n})\n","/* Copyright 2021, Milkdown by Mirone. */\nimport { AtomList } from '@milkdown/utils'\n\nimport { emojiNode } from './node'\nexport * from './node'\n\nexport const emoji = AtomList.create([emojiNode()])\n"],"names":["part","full","input","setAttr","text","parse","emoji","twemojiOptions","twemoji","checkTrigger","view","from","to","setRange","setSearch","state","$from","textBefore","regex","match","renderDropdownList","list","dropDown","onConfirm","setActive","key","container","emojiSpan","keySpan","onEnter","e","target","onClick","injectStyle","themeManager","css","cx","border","ThemeBorder","shadow","ThemeShadow","scrollbar","ThemeScrollbar","radius","ThemeSize","typography","ThemeFont","palette","color","opacity","ThemeColor","style","PluginKey","filter","utils","maxListSize","trigger","_from","_search","$active","off","active","Plugin","_","event","search","editorView","parentNode","missingRootElement","className","x","emotion","replace","tr","node","_a","onKeydown","next","selection","result","nodeEmoji","calculateNodePosition","_selected","parent","$editor","start","left","direction","maxHeight","startToTop","startToBottom","top","maxLeft","emojiRegex","isParent","isLiteral","flatMap","ast","fn","transform","index","out","n","nthChild","xs","j","m","item","twemojiPlugin","transformer","tree","value","output","str","emojiNode","createNode","options","getStyle","dom","expectDomTypeError","span","type","img","title","nodeType","InputRule","end","content","got","html","remarkEmoji","AtomList"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/parse.ts","../src/remark-twemoji.ts","../src/index.ts"],"sourcesContent":["/* Copyright 2021, Milkdown by Mirone. */\nimport twemoji from 'twemoji'\n\nconst setAttr = (text: string) => ({ title: text })\n\nexport const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>\n twemoji.parse(emoji, { attributes: setAttr, base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/', ...twemojiOptions }) as unknown as string\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { RemarkPlugin } from '@milkdown/transformer'\nimport emojiRegex from 'emoji-regex'\nimport type { Literal, Node, Parent } from 'unist'\n\nimport { parse } from './parse'\n\nconst regex = emojiRegex()\n\nconst isParent = (node: Node): node is Parent => !!(node as Parent).children\nconst isLiteral = (node: Node): node is Literal => !!(node as Literal).value\n\nfunction flatMap(ast: Node, fn: (node: Node, index: number, parent: Node | null) => Node[]) {\n return transform(ast, 0, null)[0]\n\n function transform(node: Node, index: number, parent: Node | null) {\n if (isParent(node)) {\n const out = []\n for (let i = 0, n = node.children.length; i < n; i++) {\n const nthChild = node.children[i]\n if (nthChild) {\n const xs = transform(nthChild, i, node)\n if (xs) {\n for (let j = 0, m = xs.length; j < m; j++) {\n const item = xs[j]\n if (item)\n out.push(item)\n }\n }\n }\n }\n node.children = out\n }\n\n return fn(node, index, parent)\n }\n}\n\nexport const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin = twemojiOptions => () => {\n function transformer(tree: Node) {\n flatMap(tree, (node) => {\n if (!isLiteral(node))\n return [node]\n\n const value = node.value as string\n const output: Literal<string>[] = []\n let match\n let str = value\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(str))) {\n const { index } = match\n const emoji = match[0]\n if (emoji) {\n if (index > 0)\n output.push({ ...node, value: str.slice(0, index) })\n\n output.push({ ...node, value: parse(emoji, twemojiOptions), type: 'emoji' })\n str = str.slice(index + emoji.length)\n }\n regex.lastIndex = 0\n }\n if (str.length)\n output.push({ ...node, value: str })\n\n return output\n })\n }\n return transformer\n}\n","/* Copyright 2021, Milkdown by Mirone. */\nimport type { MilkdownPlugin } from '@milkdown/ctx'\nimport type { RemarkPlugin } from '@milkdown/transformer'\nimport { expectDomTypeError } from '@milkdown/exception'\nimport { InputRule } from '@milkdown/prose/inputrules'\nimport { $ctx, $inputRule, $nodeAttr, $nodeSchema, $remark } from '@milkdown/utils'\nimport nodeEmoji from 'node-emoji'\nimport remarkEmoji from 'remark-emoji'\nimport type Twemoji from 'twemoji'\n\nimport { parse } from './parse'\nimport { twemojiPlugin } from './remark-twemoji'\n\ntype TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>\n\n/// @internal\nexport interface EmojiConfig {\n twemojiOptions?: TwemojiOptions\n}\n\n/// A slice that contains [options for twemoji](https://github.com/twitter/twemoji#object-as-parameter).\nexport const emojiConfig = $ctx<EmojiConfig, 'emojiConfig'>({}, 'emojiConfig')\n\n/// HTML attributes for emoji node.\nexport const emojiAttr = $nodeAttr('emoji', () => ({\n span: {},\n img: {},\n}))\n\n/// Schema for emoji node.\nexport const emojiSchema = $nodeSchema('emoji', ctx => ({\n group: 'inline',\n inline: true,\n attrs: {\n html: {\n default: '',\n },\n },\n parseDOM: [\n {\n tag: 'span[data-type=\"emoji\"]',\n getAttrs: (dom) => {\n if (!(dom instanceof HTMLElement))\n throw expectDomTypeError(dom)\n\n return { html: dom.innerHTML }\n },\n },\n ],\n toDOM: (node) => {\n const attrs = ctx.get(emojiAttr.key)(node)\n const tmp = document.createElement('span')\n tmp.innerHTML = node.attrs.html\n const dom = tmp.firstElementChild?.cloneNode()\n tmp.remove()\n if (dom && dom instanceof HTMLElement)\n Object.entries<string>(attrs.img).forEach(([key, value]) => dom.setAttribute(key, value))\n\n return ['span', { ...attrs.container, 'data-type': 'emoji' }, dom]\n },\n parseMarkdown: {\n match: ({ type }) => type === 'emoji',\n runner: (state, node, type) => {\n state.addNode(type, { html: node.value as string })\n },\n },\n toMarkdown: {\n match: node => node.type.name === 'emoji',\n runner: (state, node) => {\n const span = document.createElement('span')\n span.innerHTML = node.attrs.html\n const img = span.querySelector('img')\n const title = img?.title || img?.alt\n span.remove()\n state.addNode('text', undefined, title)\n },\n },\n}))\n\n/// Input rule for inserting emoji.\n/// For example, `:smile:` will be replaced with `😄`.\nexport const insertEmojiInputRule = $inputRule(ctx => new InputRule(/(:([^:\\s]+):)$/, (state, match, start, end) => {\n const content = match[0]\n if (!content)\n return null\n const got = nodeEmoji.get(content)\n if (!got || content.includes(got))\n return null\n\n const html = parse(got, ctx.get(emojiConfig.key).twemojiOptions)\n\n return state.tr\n .setMeta('emoji', true)\n .replaceRangeWith(start, end, emojiSchema.type().create({ html }))\n .scrollIntoView()\n}))\n\n/// This plugin wraps [remark-emoji](https://github.com/rhysd/remark-emoji).\nexport const remarkEmojiPlugin = $remark(() => remarkEmoji as RemarkPlugin)\n\n/// This plugin is used for transforming emoji to twemoji.\nexport const remarkTwemojiPlugin = $remark(ctx => twemojiPlugin(ctx.get(emojiConfig.key).twemojiOptions))\n\n/// All plugins exported by this package.\nexport const emoji: MilkdownPlugin[] = [\n emojiAttr,\n emojiConfig,\n remarkEmojiPlugin,\n remarkTwemojiPlugin,\n emojiSchema,\n insertEmojiInputRule,\n].flat()\n"],"names":["setAttr","text","parse","emoji","twemojiOptions","twemoji","regex","emojiRegex","isParent","node","isLiteral","flatMap","ast","fn","transform","index","parent","out","n","nthChild","xs","j","m","item","twemojiPlugin","transformer","tree","value","output","match","str","emojiConfig","$ctx","emojiAttr","$nodeAttr","emojiSchema","$nodeSchema","ctx","dom","expectDomTypeError","attrs","tmp","_a","key","type","state","span","img","title","insertEmojiInputRule","$inputRule","InputRule","start","end","content","got","nodeEmoji","html","remarkEmojiPlugin","$remark","remarkEmoji","remarkTwemojiPlugin"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,mBAAmB,cAAc,KAAG,MACgB,CAAA"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,KAAK,UAAW,MAAM,mBAAmB,cAAc,KAAG,MAC6E,CAAA"}
@@ -1,3 +1,3 @@
1
- import type { RemarkPlugin } from '@milkdown/core';
1
+ import type { RemarkPlugin } from '@milkdown/transformer';
2
2
  export declare const twemojiPlugin: (twemojiOptions?: TwemojiOptions) => RemarkPlugin;
3
3
  //# sourceMappingURL=remark-twemoji.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAqClD,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,cAAc,KAAK,YA8BhE,CAAA"}
1
+ {"version":3,"file":"remark-twemoji.d.ts","sourceRoot":"","sources":["../src/remark-twemoji.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAqCzD,eAAO,MAAM,aAAa,EAAE,CAAC,cAAc,CAAC,EAAE,cAAc,KAAK,YA8BhE,CAAA"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@milkdown/plugin-emoji",
3
3
  "type": "module",
4
- "version": "6.5.4",
4
+ "version": "7.0.0-next.1",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,8 +20,10 @@
20
20
  "src"
21
21
  ],
22
22
  "peerDependencies": {
23
- "@milkdown/core": "^6.0.1",
24
- "@milkdown/prose": "^6.0.1"
23
+ "@milkdown/core": "^7.0.0-next.0",
24
+ "@milkdown/ctx": "^7.0.0-next.0",
25
+ "@milkdown/prose": "^7.0.0-next.0",
26
+ "@milkdown/transformer": "^7.0.0-next.0"
25
27
  },
26
28
  "dependencies": {
27
29
  "@types/node-emoji": "^1.8.1",
@@ -31,13 +33,15 @@
31
33
  "tslib": "^2.4.0",
32
34
  "twemoji": "^14.0.1",
33
35
  "unist-util-visit": "^4.0.0",
34
- "@milkdown/exception": "6.5.4",
35
- "@milkdown/utils": "6.5.4"
36
+ "@milkdown/exception": "7.0.0-next.1",
37
+ "@milkdown/utils": "7.0.0-next.1"
36
38
  },
37
39
  "devDependencies": {
38
40
  "@types/unist": "^2.0.6",
39
- "@milkdown/core": "6.5.4",
40
- "@milkdown/prose": "6.5.4"
41
+ "@milkdown/core": "7.0.0-next.1",
42
+ "@milkdown/ctx": "7.0.0-next.1",
43
+ "@milkdown/prose": "7.0.0-next.1",
44
+ "@milkdown/transformer": "7.0.0-next.1"
41
45
  },
42
46
  "nx": {
43
47
  "targets": {
package/src/index.ts CHANGED
@@ -1,7 +1,112 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { AtomList } from '@milkdown/utils'
2
+ import type { MilkdownPlugin } from '@milkdown/ctx'
3
+ import type { RemarkPlugin } from '@milkdown/transformer'
4
+ import { expectDomTypeError } from '@milkdown/exception'
5
+ import { InputRule } from '@milkdown/prose/inputrules'
6
+ import { $ctx, $inputRule, $nodeAttr, $nodeSchema, $remark } from '@milkdown/utils'
7
+ import nodeEmoji from 'node-emoji'
8
+ import remarkEmoji from 'remark-emoji'
9
+ import type Twemoji from 'twemoji'
3
10
 
4
- import { emojiNode } from './node'
5
- export * from './node'
11
+ import { parse } from './parse'
12
+ import { twemojiPlugin } from './remark-twemoji'
6
13
 
7
- export const emoji = AtomList.create([emojiNode()])
14
+ type TwemojiOptions = Exclude<Parameters<typeof Twemoji.parse>[1], Function | undefined>
15
+
16
+ /// @internal
17
+ export interface EmojiConfig {
18
+ twemojiOptions?: TwemojiOptions
19
+ }
20
+
21
+ /// A slice that contains [options for twemoji](https://github.com/twitter/twemoji#object-as-parameter).
22
+ export const emojiConfig = $ctx<EmojiConfig, 'emojiConfig'>({}, 'emojiConfig')
23
+
24
+ /// HTML attributes for emoji node.
25
+ export const emojiAttr = $nodeAttr('emoji', () => ({
26
+ span: {},
27
+ img: {},
28
+ }))
29
+
30
+ /// Schema for emoji node.
31
+ export const emojiSchema = $nodeSchema('emoji', ctx => ({
32
+ group: 'inline',
33
+ inline: true,
34
+ attrs: {
35
+ html: {
36
+ default: '',
37
+ },
38
+ },
39
+ parseDOM: [
40
+ {
41
+ tag: 'span[data-type="emoji"]',
42
+ getAttrs: (dom) => {
43
+ if (!(dom instanceof HTMLElement))
44
+ throw expectDomTypeError(dom)
45
+
46
+ return { html: dom.innerHTML }
47
+ },
48
+ },
49
+ ],
50
+ toDOM: (node) => {
51
+ const attrs = ctx.get(emojiAttr.key)(node)
52
+ const tmp = document.createElement('span')
53
+ tmp.innerHTML = node.attrs.html
54
+ const dom = tmp.firstElementChild?.cloneNode()
55
+ tmp.remove()
56
+ if (dom && dom instanceof HTMLElement)
57
+ Object.entries<string>(attrs.img).forEach(([key, value]) => dom.setAttribute(key, value))
58
+
59
+ return ['span', { ...attrs.container, 'data-type': 'emoji' }, dom]
60
+ },
61
+ parseMarkdown: {
62
+ match: ({ type }) => type === 'emoji',
63
+ runner: (state, node, type) => {
64
+ state.addNode(type, { html: node.value as string })
65
+ },
66
+ },
67
+ toMarkdown: {
68
+ match: node => node.type.name === 'emoji',
69
+ runner: (state, node) => {
70
+ const span = document.createElement('span')
71
+ span.innerHTML = node.attrs.html
72
+ const img = span.querySelector('img')
73
+ const title = img?.title || img?.alt
74
+ span.remove()
75
+ state.addNode('text', undefined, title)
76
+ },
77
+ },
78
+ }))
79
+
80
+ /// Input rule for inserting emoji.
81
+ /// For example, `:smile:` will be replaced with `😄`.
82
+ export const insertEmojiInputRule = $inputRule(ctx => new InputRule(/(:([^:\s]+):)$/, (state, match, start, end) => {
83
+ const content = match[0]
84
+ if (!content)
85
+ return null
86
+ const got = nodeEmoji.get(content)
87
+ if (!got || content.includes(got))
88
+ return null
89
+
90
+ const html = parse(got, ctx.get(emojiConfig.key).twemojiOptions)
91
+
92
+ return state.tr
93
+ .setMeta('emoji', true)
94
+ .replaceRangeWith(start, end, emojiSchema.type().create({ html }))
95
+ .scrollIntoView()
96
+ }))
97
+
98
+ /// This plugin wraps [remark-emoji](https://github.com/rhysd/remark-emoji).
99
+ export const remarkEmojiPlugin = $remark(() => remarkEmoji as RemarkPlugin)
100
+
101
+ /// This plugin is used for transforming emoji to twemoji.
102
+ export const remarkTwemojiPlugin = $remark(ctx => twemojiPlugin(ctx.get(emojiConfig.key).twemojiOptions))
103
+
104
+ /// All plugins exported by this package.
105
+ export const emoji: MilkdownPlugin[] = [
106
+ emojiAttr,
107
+ emojiConfig,
108
+ remarkEmojiPlugin,
109
+ remarkTwemojiPlugin,
110
+ emojiSchema,
111
+ insertEmojiInputRule,
112
+ ].flat()
package/src/parse.ts CHANGED
@@ -4,4 +4,4 @@ import twemoji from 'twemoji'
4
4
  const setAttr = (text: string) => ({ title: text })
5
5
 
6
6
  export const parse = (emoji: string, twemojiOptions?: TwemojiOptions): string =>
7
- twemoji.parse(emoji, { attributes: setAttr, ...twemojiOptions }) as unknown as string
7
+ twemoji.parse(emoji, { attributes: setAttr, base: 'https://cdn.jsdelivr.net/gh/twitter/twemoji/assets/', ...twemojiOptions }) as unknown as string
@@ -1,5 +1,5 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import type { RemarkPlugin } from '@milkdown/core'
2
+ import type { RemarkPlugin } from '@milkdown/transformer'
3
3
  import emojiRegex from 'emoji-regex'
4
4
  import type { Literal, Node, Parent } from 'unist'
5
5
 
package/lib/constant.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export declare const part: RegExp;
2
- export declare const full: RegExp;
3
- export declare const input: RegExp;
4
- //# sourceMappingURL=constant.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constant.d.ts","sourceRoot":"","sources":["../src/constant.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,IAAI,QAAqB,CAAA;AACtC,eAAO,MAAM,IAAI,QAAwB,CAAA;AACzC,eAAO,MAAM,KAAK,QAAmB,CAAA"}
@@ -1,5 +0,0 @@
1
- import type { EditorView } from '@milkdown/prose/view';
2
- import type { Emoji } from 'node-emoji';
3
- export declare const checkTrigger: (view: EditorView, from: number, to: number, text: string, setRange: (from: number, to: number) => void, setSearch: (words: string) => void) => boolean;
4
- export declare const renderDropdownList: (list: Emoji[], dropDown: HTMLElement, onConfirm: () => void, setActive: (active: HTMLElement | null) => void, twemojiOptions?: TwemojiOptions) => void;
5
- //# sourceMappingURL=helper.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/filter/helper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKvC,eAAO,MAAM,YAAY,SACjB,UAAU,QACV,MAAM,MACR,MAAM,QACJ,MAAM,mBACK,MAAM,MAAM,MAAM,KAAK,IAAI,qBACzB,MAAM,KAAK,IAAI,YAsBnC,CAAA;AAED,eAAO,MAAM,kBAAkB,SACvB,KAAK,EAAE,YACH,WAAW,aACV,MAAM,IAAI,sBACD,WAAW,GAAG,IAAI,KAAK,IAAI,mBAC9B,cAAc,SAuChC,CAAA"}
@@ -1,5 +0,0 @@
1
- import { Plugin, PluginKey } from '@milkdown/prose/state';
2
- import type { ThemeUtils } from '@milkdown/utils';
3
- export declare const key: PluginKey<any>;
4
- export declare const filter: (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => Plugin<any>;
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/filter/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAMjD,eAAO,MAAM,GAAG,gBAAyC,CAAA;AAEzD,eAAO,MAAM,MAAM,UAAW,UAAU,eAAe,MAAM,mBAAmB,cAAc,gBA2M7F,CAAA"}
@@ -1,3 +0,0 @@
1
- import type { Emotion, ThemeManager } from '@milkdown/core';
2
- export declare const injectStyle: (themeManager: ThemeManager, { css, cx }: Emotion) => string;
3
- //# sourceMappingURL=style.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/filter/style.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,OAAO,EACP,YAAY,EACb,MAAM,gBAAgB,CAAA;AAUvB,eAAO,MAAM,WAAW,iBAAkB,YAAY,eAAe,OAAO,WA6C3E,CAAA"}
package/lib/node.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export interface EmojiOptions {
2
- maxListSize: number;
3
- twemojiOptions: TwemojiOptions;
4
- }
5
- export declare const emojiNode: import("@milkdown/utils").NodeCreator<string, EmojiOptions>;
6
- //# sourceMappingURL=node.d.ts.map
package/lib/node.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,cAAc,CAAA;CAC/B;AAED,eAAO,MAAM,SAAS,6DAoFpB,CAAA"}
package/src/constant.ts DELETED
@@ -1,4 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- export const part = /:\+1|:-1|:[\w-]+/
3
- export const full = /:\+1:|:-1:|:[\w-]+:/
4
- export const input = /(:([^:\s]+):)$/
@@ -1,83 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
-
3
- import type { EditorView } from '@milkdown/prose/view'
4
- import type { Emoji } from 'node-emoji'
5
-
6
- import { full, part } from '../constant'
7
- import { parse } from '../parse'
8
-
9
- export const checkTrigger = (
10
- view: EditorView,
11
- from: number,
12
- to: number,
13
- text: string,
14
- setRange: (from: number, to: number) => void,
15
- setSearch: (words: string) => void,
16
- ) => {
17
- if (view.composing)
18
- return false
19
- const { state } = view
20
- const $from = state.doc.resolve(from)
21
- if ($from.parent.type.spec.code)
22
- return false
23
- const textBefore = (
24
- $from.parent.textBetween(Math.max(0, $from.parentOffset - 10), $from.parentOffset, undefined, '\uFFFC') + text
25
- ).toLowerCase()
26
- if (full.test(textBefore))
27
- return false
28
-
29
- const regex = part.exec(textBefore)
30
- if (regex && regex[0] && textBefore.endsWith(regex[0])) {
31
- const match = regex[0]
32
- setRange(from - (match.length - text.length), to)
33
- setSearch(match)
34
- return true
35
- }
36
- return false
37
- }
38
-
39
- export const renderDropdownList = (
40
- list: Emoji[],
41
- dropDown: HTMLElement,
42
- onConfirm: () => void,
43
- setActive: (active: HTMLElement | null) => void,
44
- twemojiOptions?: TwemojiOptions,
45
- ) => {
46
- while (dropDown.firstChild)
47
- dropDown.firstChild.remove()
48
-
49
- list.forEach(({ emoji, key }, i) => {
50
- const container = document.createElement('div')
51
- container.className = 'milkdown-emoji-filter_item'
52
-
53
- const emojiSpan = document.createElement('span')
54
- emojiSpan.innerHTML = parse(emoji, twemojiOptions)
55
-
56
- emojiSpan.className = 'milkdown-emoji-filter_item-emoji'
57
- const keySpan = document.createElement('span')
58
- keySpan.textContent = `:${key}:`
59
- keySpan.className = 'milkdown-emoji-filter_item-key'
60
-
61
- container.appendChild(emojiSpan)
62
- container.appendChild(keySpan)
63
- dropDown.appendChild(container)
64
-
65
- if (i === 0)
66
- setActive(container)
67
-
68
- const onEnter = (e: MouseEvent) => {
69
- const { target } = e
70
- if (!(target instanceof HTMLElement))
71
- return
72
- setActive(target)
73
- }
74
-
75
- const onClick = (e: MouseEvent) => {
76
- e.preventDefault()
77
- onConfirm()
78
- }
79
-
80
- container.addEventListener('mouseenter', onEnter)
81
- container.addEventListener('mousedown', onClick)
82
- })
83
- }
@@ -1,217 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
-
3
- import { missingRootElement } from '@milkdown/exception'
4
- import { calculateNodePosition } from '@milkdown/prose'
5
- import { Plugin, PluginKey } from '@milkdown/prose/state'
6
- import type { ThemeUtils } from '@milkdown/utils'
7
- import nodeEmoji from 'node-emoji'
8
-
9
- import { checkTrigger, renderDropdownList } from './helper'
10
- import { injectStyle } from './style'
11
-
12
- export const key = new PluginKey('MILKDOWN_EMOJI_FILTER')
13
-
14
- export const filter = (utils: ThemeUtils, maxListSize: number, twemojiOptions?: TwemojiOptions) => {
15
- let trigger = false
16
- let _from = 0
17
- let _search = ''
18
- let $active: null | HTMLElement = null
19
-
20
- const off = () => {
21
- trigger = false
22
- _from = 0
23
- _search = ''
24
- $active = null
25
- }
26
-
27
- const setActive = (active: HTMLElement | null) => {
28
- if ($active)
29
- $active.classList.remove('active')
30
-
31
- if (active)
32
- active.classList.add('active')
33
-
34
- $active = active
35
- }
36
-
37
- return new Plugin({
38
- key,
39
- props: {
40
- handleKeyDown(_, event) {
41
- if (['Delete', 'Backspace'].includes(event.key)) {
42
- _search = _search.slice(0, -1)
43
- if (_search.length <= 1)
44
- off()
45
-
46
- return false
47
- }
48
- if (!trigger)
49
- return false
50
- if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(event.key))
51
- return false
52
-
53
- return true
54
- },
55
- handleTextInput(view, from, to, text) {
56
- trigger = checkTrigger(
57
- view,
58
- from,
59
- to,
60
- text,
61
- (from) => {
62
- _from = from
63
- },
64
- (search) => {
65
- _search = search
66
- },
67
- )
68
- if (!trigger)
69
- off()
70
-
71
- return false
72
- },
73
- },
74
- view: (editorView) => {
75
- const { parentNode } = editorView.dom
76
- if (!parentNode)
77
- throw missingRootElement()
78
-
79
- const dropDown = document.createElement('div')
80
-
81
- dropDown.classList.add('milkdown-emoji-filter', 'hide')
82
-
83
- utils.themeManager.onFlush(() => {
84
- const className = dropDown.className
85
- .split(' ')
86
- .filter(x => ['hide', 'milkdown-emoji-filter'].includes(x))
87
- dropDown.className = className.join(' ')
88
- const style = utils.getStyle(emotion => injectStyle(utils.themeManager, emotion))
89
- if (style)
90
- style.split(' ').forEach(x => dropDown.classList.add(x))
91
- })
92
-
93
- const replace = () => {
94
- if (!$active)
95
- return
96
-
97
- const { tr } = editorView.state
98
- const node = editorView.state.schema.node('emoji', { html: $active.firstElementChild?.innerHTML })
99
-
100
- editorView.dispatch(tr.delete(_from, _from + _search.length).insert(_from, node))
101
- off()
102
- dropDown.classList.add('hide')
103
- }
104
-
105
- parentNode.appendChild(dropDown)
106
- const onKeydown = (e: Event) => {
107
- if (!trigger || !(e instanceof KeyboardEvent))
108
- return
109
-
110
- const { key } = e
111
-
112
- if (key === 'Enter') {
113
- replace()
114
- return
115
- }
116
-
117
- if (['ArrowDown', 'ArrowUp'].includes(key)) {
118
- const next
119
- = key === 'ArrowDown'
120
- ? $active?.nextElementSibling || dropDown.firstElementChild
121
- : $active?.previousElementSibling || dropDown.lastElementChild
122
- if (!next)
123
- return
124
- setActive(next as HTMLElement)
125
- }
126
- }
127
- const onClick = (e: Event) => {
128
- if (!trigger)
129
- return
130
-
131
- e.stopPropagation()
132
- off()
133
- dropDown.classList.add('hide')
134
- }
135
- parentNode.addEventListener('keydown', onKeydown)
136
- parentNode.addEventListener('mousedown', onClick)
137
-
138
- return {
139
- update: (view) => {
140
- const { selection } = view.state
141
-
142
- if (selection.from - selection.to !== 0 || !trigger) {
143
- off()
144
- dropDown.classList.add('hide')
145
- return null
146
- }
147
- const result = nodeEmoji.search(_search).slice(0, maxListSize)
148
- const { node } = view.domAtPos(_from)
149
- if (result.length === 0 || !node) {
150
- dropDown.classList.add('hide')
151
- return null
152
- }
153
-
154
- dropDown.style.maxHeight = ''
155
- dropDown.classList.remove('hide')
156
- renderDropdownList(result, dropDown, replace, setActive, twemojiOptions)
157
- calculateNodePosition(view, dropDown, (_selected, target, parent) => {
158
- const $editor = dropDown.parentElement
159
- if (!$editor)
160
- throw missingRootElement()
161
-
162
- const start = view.coordsAtPos(_from)
163
- let left = start.left - parent.left
164
-
165
- if (left < 0)
166
- left = 0
167
-
168
- let direction: 'top' | 'bottom'
169
- let maxHeight: number | undefined
170
- const startToTop = start.top - parent.top
171
- const startToBottom = parent.height + parent.top - start.bottom
172
- if (startToBottom >= target.height + 28) {
173
- direction = 'bottom'
174
- }
175
- else if (startToTop >= target.height + 28) {
176
- direction = 'top'
177
- }
178
- else if (startToBottom >= startToTop) {
179
- direction = 'bottom'
180
- maxHeight = startToBottom - 28
181
- }
182
- else {
183
- direction = 'top'
184
- maxHeight = startToTop - 28
185
- }
186
- if (startToTop < 0 || startToBottom < 0) {
187
- maxHeight = parent.height - (start.bottom - start.top) - 28
188
- if (maxHeight > target.height)
189
- maxHeight = undefined
190
- }
191
-
192
- const top
193
- = direction === 'top'
194
- ? start.top - parent.top - (maxHeight ?? target.height) - 14 + $editor.scrollTop
195
- : start.bottom - parent.top + 14 + $editor.scrollTop
196
-
197
- dropDown.style.maxHeight = maxHeight !== undefined && maxHeight > 0 ? `${maxHeight}px` : ''
198
-
199
- const maxLeft = $editor.clientWidth - (dropDown.offsetWidth + 4)
200
- if (left > maxLeft)
201
- left = maxLeft
202
-
203
- return [top, left]
204
- })
205
-
206
- return null
207
- },
208
-
209
- destroy: () => {
210
- parentNode.removeEventListener('keydown', onKeydown)
211
- parentNode.removeEventListener('mousedown', onClick)
212
- dropDown.remove()
213
- },
214
- }
215
- },
216
- })
217
- }
@@ -1,61 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type {
3
- Color,
4
- Emotion,
5
- ThemeManager,
6
- } from '@milkdown/core'
7
- import {
8
- ThemeBorder,
9
- ThemeColor,
10
- ThemeFont,
11
- ThemeScrollbar,
12
- ThemeShadow,
13
- ThemeSize,
14
- } from '@milkdown/core'
15
-
16
- export const injectStyle = (themeManager: ThemeManager, { css, cx }: Emotion) => {
17
- const border = themeManager.get(ThemeBorder, undefined)
18
- const shadow = themeManager.get(ThemeShadow, undefined)
19
- const scrollbar = themeManager.get(ThemeScrollbar, undefined)
20
- const radius = themeManager.get(ThemeSize, 'radius')
21
- const typography = themeManager.get(ThemeFont, 'typography')
22
- const palette = (color: Color, opacity = 1) => themeManager.get(ThemeColor, [color, opacity])
23
-
24
- const style = css`
25
- min-height: 36px;
26
- max-height: 320px;
27
- overflow-y: auto;
28
- border-radius: ${radius};
29
- position: absolute;
30
- background: ${palette('surface')};
31
-
32
- &.hide {
33
- display: none;
34
- }
35
-
36
- .milkdown-emoji-filter_item {
37
- display: flex;
38
- gap: 8px;
39
- height: 36px;
40
- padding: 0 14px;
41
- align-items: center;
42
- justify-content: flex-start;
43
- cursor: pointer;
44
- line-height: 2;
45
- font-family: ${typography};
46
- font-size: 14px;
47
- &.active {
48
- background: ${palette('secondary', 0.12)};
49
- color: ${palette('primary')};
50
- }
51
- }
52
-
53
- .emoji {
54
- height: 14px;
55
- width: 14px;
56
- margin: 0 1px 0 1.5px;
57
- vertical-align: -1.5px;
58
- }
59
- `
60
- return cx(border, shadow, scrollbar, style)
61
- }
package/src/node.ts DELETED
@@ -1,103 +0,0 @@
1
- /* Copyright 2021, Milkdown by Mirone. */
2
- import type { RemarkPlugin } from '@milkdown/core'
3
- import { expectDomTypeError } from '@milkdown/exception'
4
- import { InputRule } from '@milkdown/prose/inputrules'
5
- import { createNode } from '@milkdown/utils'
6
- import nodeEmoji from 'node-emoji'
7
- import remarkEmoji from 'remark-emoji'
8
-
9
- import { input } from './constant'
10
- import { filter } from './filter'
11
- import { parse } from './parse'
12
- import { twemojiPlugin } from './remark-twemoji'
13
-
14
- export interface EmojiOptions {
15
- maxListSize: number
16
- twemojiOptions: TwemojiOptions
17
- }
18
-
19
- export const emojiNode = createNode<string, EmojiOptions>((utils, options) => {
20
- const getStyle = () =>
21
- utils.getStyle(
22
- ({ css }) => css`
23
- .emoji {
24
- height: 1em;
25
- width: 1em;
26
- margin: 0 1px 0 1.5px;
27
- vertical-align: -1.5px;
28
- }
29
- `,
30
- )
31
- return {
32
- id: 'emoji',
33
- schema: () => ({
34
- group: 'inline',
35
- inline: true,
36
- atom: true,
37
- attrs: {
38
- html: {
39
- default: '',
40
- },
41
- },
42
- parseDOM: [
43
- {
44
- tag: 'span[data-type="emoji"]',
45
- getAttrs: (dom) => {
46
- if (!(dom instanceof HTMLElement))
47
- throw expectDomTypeError(dom)
48
-
49
- return { html: dom.innerHTML }
50
- },
51
- },
52
- ],
53
- toDOM: (node) => {
54
- const span = document.createElement('span')
55
- span.classList.add('emoji-wrapper')
56
- span.dataset.type = 'emoji'
57
- utils.themeManager.onFlush(() => {
58
- const style = getStyle()
59
- if (style)
60
- span.classList.add(style)
61
- })
62
- span.innerHTML = node.attrs.html
63
- return { dom: span }
64
- },
65
- parseMarkdown: {
66
- match: ({ type }) => type === 'emoji',
67
- runner: (state, node, type) => {
68
- state.addNode(type, { html: node.value as string })
69
- },
70
- },
71
- toMarkdown: {
72
- match: node => node.type.name === 'emoji',
73
- runner: (state, node) => {
74
- const span = document.createElement('span')
75
- span.innerHTML = node.attrs.html
76
- const img = span.querySelector('img')
77
- const title = img?.title
78
- span.remove()
79
- state.addNode('text', undefined, title)
80
- },
81
- },
82
- }),
83
- inputRules: nodeType => [
84
- new InputRule(input, (state, match, start, end) => {
85
- const content = match[0]
86
- if (!content)
87
- return null
88
- const got = nodeEmoji.get(content)
89
- if (!got || content.includes(got))
90
- return null
91
-
92
- const html = parse(got, options?.twemojiOptions)
93
-
94
- return state.tr
95
- .setMeta('emoji', true)
96
- .replaceRangeWith(start, end, nodeType.create({ html }))
97
- .scrollIntoView()
98
- }),
99
- ],
100
- remarkPlugins: () => [remarkEmoji as RemarkPlugin, twemojiPlugin(options?.twemojiOptions)],
101
- prosePlugins: () => [filter(utils, options?.maxListSize ?? 6, options?.twemojiOptions)],
102
- }
103
- })