@bendyline/squisq-editor-react 0.1.1 → 1.0.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/dist/EditorContext.d.ts +75 -0
- package/dist/EditorContext.d.ts.map +1 -0
- package/dist/EditorContext.js +158 -0
- package/dist/EditorContext.js.map +1 -0
- package/dist/EditorShell.d.ts +34 -0
- package/dist/EditorShell.d.ts.map +1 -0
- package/dist/EditorShell.js +59 -0
- package/dist/EditorShell.js.map +1 -0
- package/dist/PreviewPanel.d.ts +33 -0
- package/dist/PreviewPanel.d.ts.map +1 -0
- package/dist/PreviewPanel.js +385 -0
- package/dist/PreviewPanel.js.map +1 -0
- package/dist/RawEditor.d.ts +25 -0
- package/dist/RawEditor.d.ts.map +1 -0
- package/dist/RawEditor.js +100 -0
- package/dist/RawEditor.js.map +1 -0
- package/dist/StatusBar.d.ts +15 -0
- package/dist/StatusBar.d.ts.map +1 -0
- package/dist/StatusBar.js +24 -0
- package/dist/StatusBar.js.map +1 -0
- package/dist/TemplateAnnotation.d.ts +20 -0
- package/dist/TemplateAnnotation.d.ts.map +1 -0
- package/dist/TemplateAnnotation.js +69 -0
- package/dist/TemplateAnnotation.js.map +1 -0
- package/dist/Toolbar.d.ts +19 -0
- package/dist/Toolbar.d.ts.map +1 -0
- package/dist/Toolbar.js +350 -0
- package/dist/Toolbar.js.map +1 -0
- package/dist/ViewSwitcher.d.ts +14 -0
- package/dist/ViewSwitcher.d.ts.map +1 -0
- package/dist/ViewSwitcher.js +20 -0
- package/dist/ViewSwitcher.js.map +1 -0
- package/dist/WysiwygEditor.d.ts +28 -0
- package/dist/WysiwygEditor.d.ts.map +1 -0
- package/dist/WysiwygEditor.js +111 -0
- package/dist/WysiwygEditor.js.map +1 -0
- package/dist/index.d.ts +36 -268
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -3824
- package/dist/index.js.map +1 -1
- package/dist/tiptapBridge.d.ts +24 -0
- package/dist/tiptapBridge.d.ts.map +1 -0
- package/dist/tiptapBridge.js +358 -0
- package/dist/tiptapBridge.js.map +1 -0
- package/package.json +4 -5
- package/src/Toolbar.tsx +50 -45
- package/src/styles/editor.css +21 -0
package/dist/index.js
CHANGED
|
@@ -1,3825 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const [activeView, setActiveView] = useState(initialView);
|
|
36
|
-
const [parseError, setParseError] = useState(null);
|
|
37
|
-
const [isParsing, setIsParsing] = useState(false);
|
|
38
|
-
const [theme, setTheme] = useState(initialTheme);
|
|
39
|
-
const [tiptapEditor, setTiptapEditor] = useState(null);
|
|
40
|
-
const [monacoEditor, setMonacoEditor] = useState(null);
|
|
41
|
-
const articleIdRef = useRef(articleId);
|
|
42
|
-
articleIdRef.current = articleId;
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
setTheme(initialTheme);
|
|
45
|
-
}, [initialTheme]);
|
|
46
|
-
const parseTimeoutRef = useRef(null);
|
|
47
|
-
const doParse = useCallback((source) => {
|
|
48
|
-
setIsParsing(true);
|
|
49
|
-
try {
|
|
50
|
-
const parsed = parseMarkdown(source);
|
|
51
|
-
setMarkdownDocState(parsed);
|
|
52
|
-
setParseError(null);
|
|
53
|
-
try {
|
|
54
|
-
const generatedDoc = markdownToDoc(parsed, {
|
|
55
|
-
articleId: articleIdRef.current
|
|
56
|
-
});
|
|
57
|
-
setDoc(generatedDoc);
|
|
58
|
-
} catch (docErr) {
|
|
59
|
-
setDoc(null);
|
|
60
|
-
console.warn("Doc generation failed:", docErr instanceof Error ? docErr.message : docErr);
|
|
61
|
-
}
|
|
62
|
-
} catch (err) {
|
|
63
|
-
setParseError(err instanceof Error ? err.message : "Parse error");
|
|
64
|
-
setMarkdownDocState(null);
|
|
65
|
-
setDoc(null);
|
|
66
|
-
} finally {
|
|
67
|
-
setIsParsing(false);
|
|
68
|
-
}
|
|
69
|
-
}, []);
|
|
70
|
-
useEffect(() => {
|
|
71
|
-
if (parseTimeoutRef.current) {
|
|
72
|
-
clearTimeout(parseTimeoutRef.current);
|
|
73
|
-
}
|
|
74
|
-
parseTimeoutRef.current = setTimeout(() => {
|
|
75
|
-
doParse(markdownSource);
|
|
76
|
-
}, 150);
|
|
77
|
-
return () => {
|
|
78
|
-
if (parseTimeoutRef.current) {
|
|
79
|
-
clearTimeout(parseTimeoutRef.current);
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
}, [markdownSource, doParse]);
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (initialMarkdown) {
|
|
85
|
-
doParse(initialMarkdown);
|
|
86
|
-
}
|
|
87
|
-
}, []);
|
|
88
|
-
const setMarkdownSource = useCallback((source) => {
|
|
89
|
-
setMarkdownSourceRaw(source);
|
|
90
|
-
}, []);
|
|
91
|
-
const setMarkdownDoc = useCallback((newDoc) => {
|
|
92
|
-
setMarkdownDocState(newDoc);
|
|
93
|
-
try {
|
|
94
|
-
const newSource = stringifyMarkdown(newDoc);
|
|
95
|
-
setMarkdownSourceRaw(newSource);
|
|
96
|
-
setParseError(null);
|
|
97
|
-
try {
|
|
98
|
-
const generatedDoc = markdownToDoc(newDoc, {
|
|
99
|
-
articleId: articleIdRef.current
|
|
100
|
-
});
|
|
101
|
-
setDoc(generatedDoc);
|
|
102
|
-
} catch (docErr) {
|
|
103
|
-
setDoc(null);
|
|
104
|
-
console.warn("Doc generation failed:", docErr instanceof Error ? docErr.message : docErr);
|
|
105
|
-
}
|
|
106
|
-
} catch (err) {
|
|
107
|
-
setParseError(err instanceof Error ? err.message : "Stringify error");
|
|
108
|
-
}
|
|
109
|
-
}, []);
|
|
110
|
-
const value = useMemo(
|
|
111
|
-
() => ({
|
|
112
|
-
markdownSource,
|
|
113
|
-
markdownDoc,
|
|
114
|
-
doc,
|
|
115
|
-
activeView,
|
|
116
|
-
parseError,
|
|
117
|
-
isParsing,
|
|
118
|
-
theme,
|
|
119
|
-
tiptapEditor,
|
|
120
|
-
monacoEditor,
|
|
121
|
-
setMarkdownSource,
|
|
122
|
-
setMarkdownDoc,
|
|
123
|
-
setActiveView,
|
|
124
|
-
setTiptapEditor,
|
|
125
|
-
setMonacoEditor,
|
|
126
|
-
setTheme
|
|
127
|
-
}),
|
|
128
|
-
[
|
|
129
|
-
markdownSource,
|
|
130
|
-
markdownDoc,
|
|
131
|
-
doc,
|
|
132
|
-
activeView,
|
|
133
|
-
parseError,
|
|
134
|
-
isParsing,
|
|
135
|
-
theme,
|
|
136
|
-
tiptapEditor,
|
|
137
|
-
monacoEditor,
|
|
138
|
-
setMarkdownSource,
|
|
139
|
-
setMarkdownDoc,
|
|
140
|
-
setActiveView,
|
|
141
|
-
setTiptapEditor,
|
|
142
|
-
setMonacoEditor,
|
|
143
|
-
setTheme
|
|
144
|
-
]
|
|
145
|
-
);
|
|
146
|
-
return /* @__PURE__ */ jsx(EditorContext.Provider, { value, children });
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// src/Toolbar.tsx
|
|
150
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useReducer } from "react";
|
|
151
|
-
import { getAvailableTemplates } from "@bendyline/squisq/doc";
|
|
152
|
-
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
153
|
-
var VIEWS = [
|
|
154
|
-
{ id: "wysiwyg", label: "Editor", shortcut: "\u23181" },
|
|
155
|
-
{ id: "raw", label: "Raw", shortcut: "\u23182" },
|
|
156
|
-
{ id: "preview", label: "Preview", shortcut: "\u23183" }
|
|
157
|
-
];
|
|
158
|
-
var BUTTONS = [
|
|
159
|
-
// Format group
|
|
160
|
-
{
|
|
161
|
-
id: "bold",
|
|
162
|
-
label: "B",
|
|
163
|
-
icon: "B",
|
|
164
|
-
title: "Bold (Ctrl+B)",
|
|
165
|
-
group: "format",
|
|
166
|
-
iconStyle: { fontWeight: 700 }
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
id: "italic",
|
|
170
|
-
label: "I",
|
|
171
|
-
icon: "I",
|
|
172
|
-
title: "Italic (Ctrl+I)",
|
|
173
|
-
group: "format",
|
|
174
|
-
iconStyle: { fontStyle: "italic" }
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
id: "strikethrough",
|
|
178
|
-
label: "S",
|
|
179
|
-
icon: "S",
|
|
180
|
-
title: "Strikethrough",
|
|
181
|
-
group: "format",
|
|
182
|
-
iconStyle: { textDecoration: "line-through" }
|
|
183
|
-
},
|
|
184
|
-
{ id: "code", label: "<>", icon: "`", title: "Inline code", group: "format" },
|
|
185
|
-
// Structure group
|
|
186
|
-
{ id: "h1", label: "H1", icon: "H1", title: "Heading 1", group: "structure" },
|
|
187
|
-
{ id: "h2", label: "H2", icon: "H2", title: "Heading 2", group: "structure" },
|
|
188
|
-
{ id: "h3", label: "H3", icon: "H3", title: "Heading 3", group: "structure" },
|
|
189
|
-
{ id: "quote", label: "\u275D", icon: "\u275D", title: "Blockquote", group: "structure" },
|
|
190
|
-
// Insert group
|
|
191
|
-
{ id: "ul", label: "\u2022", icon: "\u2022", title: "Bullet list", group: "insert" },
|
|
192
|
-
{ id: "ol", label: "1.", icon: "1.", title: "Numbered list", group: "insert" },
|
|
193
|
-
{ id: "codeblock", label: "{ }", icon: "{ }", title: "Code block", group: "insert" },
|
|
194
|
-
{ id: "hr", label: "\u2014", icon: "\u2014", title: "Horizontal rule", group: "insert" },
|
|
195
|
-
{ id: "link", label: "\u{1F517}", icon: "\u{1F517}", title: "Insert link", group: "insert" }
|
|
196
|
-
];
|
|
197
|
-
function isTiptapActive(editor, id) {
|
|
198
|
-
if (!editor) return false;
|
|
199
|
-
switch (id) {
|
|
200
|
-
case "bold":
|
|
201
|
-
return editor.isActive("bold");
|
|
202
|
-
case "italic":
|
|
203
|
-
return editor.isActive("italic");
|
|
204
|
-
case "strikethrough":
|
|
205
|
-
return editor.isActive("strike");
|
|
206
|
-
case "code":
|
|
207
|
-
return editor.isActive("code");
|
|
208
|
-
case "h1":
|
|
209
|
-
return editor.isActive("heading", { level: 1 });
|
|
210
|
-
case "h2":
|
|
211
|
-
return editor.isActive("heading", { level: 2 });
|
|
212
|
-
case "h3":
|
|
213
|
-
return editor.isActive("heading", { level: 3 });
|
|
214
|
-
case "quote":
|
|
215
|
-
return editor.isActive("blockquote");
|
|
216
|
-
case "ul":
|
|
217
|
-
return editor.isActive("bulletList");
|
|
218
|
-
case "ol":
|
|
219
|
-
return editor.isActive("orderedList");
|
|
220
|
-
case "codeblock":
|
|
221
|
-
return editor.isActive("codeBlock");
|
|
222
|
-
default:
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
function Toolbar({ className }) {
|
|
227
|
-
const {
|
|
228
|
-
activeView,
|
|
229
|
-
setActiveView,
|
|
230
|
-
markdownSource,
|
|
231
|
-
setMarkdownSource,
|
|
232
|
-
tiptapEditor,
|
|
233
|
-
monacoEditor
|
|
234
|
-
} = useEditorContext();
|
|
235
|
-
const [, forceUpdate] = useReducer((c) => c + 1, 0);
|
|
236
|
-
useEffect2(() => {
|
|
237
|
-
if (!tiptapEditor) return;
|
|
238
|
-
tiptapEditor.on("transaction", forceUpdate);
|
|
239
|
-
return () => {
|
|
240
|
-
tiptapEditor.off("transaction", forceUpdate);
|
|
241
|
-
};
|
|
242
|
-
}, [tiptapEditor]);
|
|
243
|
-
const handleTiptap = useCallback2(
|
|
244
|
-
(id) => {
|
|
245
|
-
if (!tiptapEditor) return;
|
|
246
|
-
const chain = tiptapEditor.chain().focus();
|
|
247
|
-
switch (id) {
|
|
248
|
-
case "bold":
|
|
249
|
-
chain.toggleBold().run();
|
|
250
|
-
break;
|
|
251
|
-
case "italic":
|
|
252
|
-
chain.toggleItalic().run();
|
|
253
|
-
break;
|
|
254
|
-
case "strikethrough":
|
|
255
|
-
chain.toggleStrike().run();
|
|
256
|
-
break;
|
|
257
|
-
case "code":
|
|
258
|
-
chain.toggleCode().run();
|
|
259
|
-
break;
|
|
260
|
-
case "h1":
|
|
261
|
-
chain.toggleHeading({ level: 1 }).run();
|
|
262
|
-
break;
|
|
263
|
-
case "h2":
|
|
264
|
-
chain.toggleHeading({ level: 2 }).run();
|
|
265
|
-
break;
|
|
266
|
-
case "h3":
|
|
267
|
-
chain.toggleHeading({ level: 3 }).run();
|
|
268
|
-
break;
|
|
269
|
-
case "quote":
|
|
270
|
-
chain.toggleBlockquote().run();
|
|
271
|
-
break;
|
|
272
|
-
case "ul":
|
|
273
|
-
chain.toggleBulletList().run();
|
|
274
|
-
break;
|
|
275
|
-
case "ol":
|
|
276
|
-
chain.toggleOrderedList().run();
|
|
277
|
-
break;
|
|
278
|
-
case "codeblock":
|
|
279
|
-
chain.toggleCodeBlock().run();
|
|
280
|
-
break;
|
|
281
|
-
case "hr":
|
|
282
|
-
chain.setHorizontalRule().run();
|
|
283
|
-
break;
|
|
284
|
-
case "link": {
|
|
285
|
-
const url = window.prompt("URL:");
|
|
286
|
-
if (url) {
|
|
287
|
-
chain.setLink?.({ href: url }).run();
|
|
288
|
-
}
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
},
|
|
293
|
-
[tiptapEditor]
|
|
294
|
-
);
|
|
295
|
-
const handleRaw = useCallback2(
|
|
296
|
-
(id) => {
|
|
297
|
-
if (monacoEditor) {
|
|
298
|
-
const selection = monacoEditor.getSelection();
|
|
299
|
-
const model = monacoEditor.getModel();
|
|
300
|
-
if (!selection || !model) return;
|
|
301
|
-
const selectedText = model.getValueInRange(selection);
|
|
302
|
-
const hasSelection = selectedText.length > 0;
|
|
303
|
-
let replacement = "";
|
|
304
|
-
let newCursorOffset = 0;
|
|
305
|
-
const wrapInline = (before, after, placeholder) => {
|
|
306
|
-
if (hasSelection) {
|
|
307
|
-
replacement = before + selectedText + after;
|
|
308
|
-
} else {
|
|
309
|
-
replacement = before + placeholder + after;
|
|
310
|
-
newCursorOffset = before.length;
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
const prefixLines = (prefix, placeholder) => {
|
|
314
|
-
if (hasSelection) {
|
|
315
|
-
replacement = selectedText.split("\n").map((line) => prefix + line).join("\n");
|
|
316
|
-
} else {
|
|
317
|
-
replacement = prefix + placeholder;
|
|
318
|
-
newCursorOffset = prefix.length;
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
switch (id) {
|
|
322
|
-
case "bold":
|
|
323
|
-
wrapInline("**", "**", "bold text");
|
|
324
|
-
break;
|
|
325
|
-
case "italic":
|
|
326
|
-
wrapInline("*", "*", "italic text");
|
|
327
|
-
break;
|
|
328
|
-
case "strikethrough":
|
|
329
|
-
wrapInline("~~", "~~", "strikethrough");
|
|
330
|
-
break;
|
|
331
|
-
case "code":
|
|
332
|
-
wrapInline("`", "`", "code");
|
|
333
|
-
break;
|
|
334
|
-
case "h1":
|
|
335
|
-
prefixLines("# ", "Heading 1");
|
|
336
|
-
break;
|
|
337
|
-
case "h2":
|
|
338
|
-
prefixLines("## ", "Heading 2");
|
|
339
|
-
break;
|
|
340
|
-
case "h3":
|
|
341
|
-
prefixLines("### ", "Heading 3");
|
|
342
|
-
break;
|
|
343
|
-
case "quote":
|
|
344
|
-
prefixLines("> ", "Quote");
|
|
345
|
-
break;
|
|
346
|
-
case "ul":
|
|
347
|
-
prefixLines("- ", "Item");
|
|
348
|
-
break;
|
|
349
|
-
case "ol":
|
|
350
|
-
prefixLines("1. ", "Item");
|
|
351
|
-
break;
|
|
352
|
-
case "codeblock": {
|
|
353
|
-
const inner = hasSelection ? selectedText : "code";
|
|
354
|
-
replacement = "```\n" + inner + "\n```";
|
|
355
|
-
if (!hasSelection) newCursorOffset = 4;
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
358
|
-
case "hr": {
|
|
359
|
-
replacement = "\n---\n";
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
case "link": {
|
|
363
|
-
if (hasSelection) {
|
|
364
|
-
replacement = "[" + selectedText + "](url)";
|
|
365
|
-
} else {
|
|
366
|
-
replacement = "[link text](url)";
|
|
367
|
-
newCursorOffset = 1;
|
|
368
|
-
}
|
|
369
|
-
break;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const range = selection;
|
|
373
|
-
monacoEditor.executeEdits("toolbar", [{ range, text: replacement }]);
|
|
374
|
-
if (!hasSelection && newCursorOffset > 0) {
|
|
375
|
-
const startPos = model.getPositionAt(
|
|
376
|
-
model.getOffsetAt(range.getStartPosition()) + newCursorOffset
|
|
377
|
-
);
|
|
378
|
-
const _placeholderLen = replacement.length - newCursorOffset - (replacement.length - replacement.lastIndexOf(replacement.charAt(replacement.length - 1)));
|
|
379
|
-
monacoEditor.setPosition(startPos);
|
|
380
|
-
}
|
|
381
|
-
monacoEditor.focus();
|
|
382
|
-
} else {
|
|
383
|
-
let insertion = "";
|
|
384
|
-
switch (id) {
|
|
385
|
-
case "bold":
|
|
386
|
-
insertion = "**bold text**";
|
|
387
|
-
break;
|
|
388
|
-
case "italic":
|
|
389
|
-
insertion = "*italic text*";
|
|
390
|
-
break;
|
|
391
|
-
case "strikethrough":
|
|
392
|
-
insertion = "~~strikethrough~~";
|
|
393
|
-
break;
|
|
394
|
-
case "code":
|
|
395
|
-
insertion = "`code`";
|
|
396
|
-
break;
|
|
397
|
-
case "h1":
|
|
398
|
-
insertion = "\n# Heading 1\n";
|
|
399
|
-
break;
|
|
400
|
-
case "h2":
|
|
401
|
-
insertion = "\n## Heading 2\n";
|
|
402
|
-
break;
|
|
403
|
-
case "h3":
|
|
404
|
-
insertion = "\n### Heading 3\n";
|
|
405
|
-
break;
|
|
406
|
-
case "quote":
|
|
407
|
-
insertion = "\n> Quote\n";
|
|
408
|
-
break;
|
|
409
|
-
case "ul":
|
|
410
|
-
insertion = "\n- Item\n";
|
|
411
|
-
break;
|
|
412
|
-
case "ol":
|
|
413
|
-
insertion = "\n1. Item\n";
|
|
414
|
-
break;
|
|
415
|
-
case "codeblock":
|
|
416
|
-
insertion = "\n```\ncode\n```\n";
|
|
417
|
-
break;
|
|
418
|
-
case "hr":
|
|
419
|
-
insertion = "\n---\n";
|
|
420
|
-
break;
|
|
421
|
-
case "link":
|
|
422
|
-
insertion = "[link text](url)";
|
|
423
|
-
break;
|
|
424
|
-
}
|
|
425
|
-
if (insertion) {
|
|
426
|
-
setMarkdownSource(markdownSource + insertion);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
[monacoEditor, markdownSource, setMarkdownSource]
|
|
431
|
-
);
|
|
432
|
-
const handleAction = useCallback2(
|
|
433
|
-
(id) => {
|
|
434
|
-
if (activeView === "wysiwyg" && tiptapEditor) {
|
|
435
|
-
handleTiptap(id);
|
|
436
|
-
} else {
|
|
437
|
-
handleRaw(id);
|
|
438
|
-
}
|
|
439
|
-
},
|
|
440
|
-
[activeView, tiptapEditor, handleTiptap, handleRaw]
|
|
441
|
-
);
|
|
442
|
-
const groups = ["format", "structure", "insert"];
|
|
443
|
-
const isWysiwyg = activeView === "wysiwyg" && tiptapEditor;
|
|
444
|
-
const isPreview = activeView === "preview";
|
|
445
|
-
const currentTemplate = isWysiwyg ? tiptapEditor.isActive("heading") ? tiptapEditor.getAttributes("heading")?.dataTemplate ?? "" : null : null;
|
|
446
|
-
const handleTemplatePick = (value) => {
|
|
447
|
-
if (!tiptapEditor) return;
|
|
448
|
-
if (value === "") {
|
|
449
|
-
tiptapEditor.chain().focus().updateAttributes("heading", { dataTemplate: null, dataTemplateParams: null }).run();
|
|
450
|
-
} else {
|
|
451
|
-
tiptapEditor.chain().focus().updateAttributes("heading", { dataTemplate: value }).run();
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
const templateNames = getAvailableTemplates();
|
|
455
|
-
return /* @__PURE__ */ jsxs(
|
|
456
|
-
"div",
|
|
457
|
-
{
|
|
458
|
-
className: `squisq-toolbar ${className || ""}`,
|
|
459
|
-
role: "toolbar",
|
|
460
|
-
"aria-label": "Formatting toolbar",
|
|
461
|
-
children: [
|
|
462
|
-
/* @__PURE__ */ jsx2("div", { className: "squisq-toolbar-view-tabs", role: "tablist", "aria-label": "Editor view", children: VIEWS.map((view) => /* @__PURE__ */ jsx2(
|
|
463
|
-
"button",
|
|
464
|
-
{
|
|
465
|
-
role: "tab",
|
|
466
|
-
"data-view": view.id,
|
|
467
|
-
"aria-selected": activeView === view.id,
|
|
468
|
-
className: `squisq-toolbar-view-tab${activeView === view.id ? " squisq-toolbar-view-tab--active" : ""}`,
|
|
469
|
-
onClick: () => setActiveView(view.id),
|
|
470
|
-
title: `${view.label} (${view.shortcut})`,
|
|
471
|
-
children: view.label
|
|
472
|
-
},
|
|
473
|
-
view.id
|
|
474
|
-
)) }),
|
|
475
|
-
!isPreview && /* @__PURE__ */ jsx2("div", { className: "squisq-toolbar-separator" }),
|
|
476
|
-
!isPreview && groups.map((group, gi) => /* @__PURE__ */ jsxs("div", { className: "squisq-toolbar-group", children: [
|
|
477
|
-
gi > 0 && /* @__PURE__ */ jsx2("div", { className: "squisq-toolbar-separator" }),
|
|
478
|
-
BUTTONS.filter((b) => b.group === group).map((btn) => {
|
|
479
|
-
const active = isWysiwyg ? isTiptapActive(tiptapEditor, btn.id) : false;
|
|
480
|
-
return /* @__PURE__ */ jsx2(
|
|
481
|
-
"button",
|
|
482
|
-
{
|
|
483
|
-
className: `squisq-toolbar-button${active ? " squisq-toolbar-button--active" : ""}`,
|
|
484
|
-
title: btn.title,
|
|
485
|
-
onClick: () => handleAction(btn.id),
|
|
486
|
-
"aria-label": btn.title,
|
|
487
|
-
"aria-pressed": active,
|
|
488
|
-
style: btn.iconStyle,
|
|
489
|
-
children: btn.icon
|
|
490
|
-
},
|
|
491
|
-
btn.id
|
|
492
|
-
);
|
|
493
|
-
})
|
|
494
|
-
] }, group)),
|
|
495
|
-
!isPreview && currentTemplate !== null && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
496
|
-
/* @__PURE__ */ jsx2("div", { className: "squisq-toolbar-separator" }),
|
|
497
|
-
/* @__PURE__ */ jsx2("div", { className: "squisq-toolbar-group squisq-template-picker", children: /* @__PURE__ */ jsxs("label", { className: "squisq-template-picker-label", title: "Block template for this heading", children: [
|
|
498
|
-
"Template:",
|
|
499
|
-
/* @__PURE__ */ jsxs(
|
|
500
|
-
"select",
|
|
501
|
-
{
|
|
502
|
-
className: "squisq-template-picker-select",
|
|
503
|
-
value: currentTemplate,
|
|
504
|
-
onChange: (e) => handleTemplatePick(e.target.value),
|
|
505
|
-
children: [
|
|
506
|
-
/* @__PURE__ */ jsx2("option", { value: "", children: "\u2014 none \u2014" }),
|
|
507
|
-
templateNames.map((name) => /* @__PURE__ */ jsx2("option", { value: name, children: name }, name))
|
|
508
|
-
]
|
|
509
|
-
}
|
|
510
|
-
)
|
|
511
|
-
] }) })
|
|
512
|
-
] })
|
|
513
|
-
]
|
|
514
|
-
}
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// src/StatusBar.tsx
|
|
519
|
-
import { useMemo as useMemo2 } from "react";
|
|
520
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
521
|
-
function StatusBar({ className }) {
|
|
522
|
-
const { markdownSource, doc, parseError, isParsing } = useEditorContext();
|
|
523
|
-
const stats = useMemo2(() => {
|
|
524
|
-
const chars = markdownSource.length;
|
|
525
|
-
const words = markdownSource.trim() ? markdownSource.trim().split(/\s+/).length : 0;
|
|
526
|
-
const lines = markdownSource.split("\n").length;
|
|
527
|
-
const blocks = doc?.blocks.length ?? 0;
|
|
528
|
-
return { chars, words, lines, blocks };
|
|
529
|
-
}, [markdownSource, doc]);
|
|
530
|
-
return /* @__PURE__ */ jsxs2("div", { className: `squisq-status-bar ${className || ""}`, children: [
|
|
531
|
-
/* @__PURE__ */ jsxs2("span", { className: "squisq-status-item", children: [
|
|
532
|
-
stats.words,
|
|
533
|
-
" words"
|
|
534
|
-
] }),
|
|
535
|
-
/* @__PURE__ */ jsxs2("span", { className: "squisq-status-item", children: [
|
|
536
|
-
stats.chars,
|
|
537
|
-
" chars"
|
|
538
|
-
] }),
|
|
539
|
-
/* @__PURE__ */ jsxs2("span", { className: "squisq-status-item", children: [
|
|
540
|
-
stats.lines,
|
|
541
|
-
" lines"
|
|
542
|
-
] }),
|
|
543
|
-
/* @__PURE__ */ jsxs2("span", { className: "squisq-status-item", children: [
|
|
544
|
-
stats.blocks,
|
|
545
|
-
" blocks"
|
|
546
|
-
] }),
|
|
547
|
-
/* @__PURE__ */ jsx3("span", { className: "squisq-status-spacer" }),
|
|
548
|
-
isParsing && /* @__PURE__ */ jsx3("span", { className: "squisq-status-item squisq-status-parsing", children: "Parsing\u2026" }),
|
|
549
|
-
parseError && /* @__PURE__ */ jsx3("span", { className: "squisq-status-item squisq-status-error", title: parseError, children: "\u26A0 Error" }),
|
|
550
|
-
!isParsing && !parseError && /* @__PURE__ */ jsx3("span", { className: "squisq-status-item squisq-status-ok", children: "\u2713 OK" })
|
|
551
|
-
] });
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// src/RawEditor.tsx
|
|
555
|
-
import { useRef as useRef2, useCallback as useCallback3, useEffect as useEffect3 } from "react";
|
|
556
|
-
import Editor, { loader } from "@monaco-editor/react";
|
|
557
|
-
import * as monaco from "monaco-editor";
|
|
558
|
-
import { getAvailableTemplates as getAvailableTemplates2 } from "@bendyline/squisq/doc";
|
|
559
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
560
|
-
loader.config({ monaco });
|
|
561
|
-
function RawEditor({
|
|
562
|
-
theme = "vs",
|
|
563
|
-
minimap = false,
|
|
564
|
-
fontSize = 14,
|
|
565
|
-
wordWrap = "on",
|
|
566
|
-
className
|
|
567
|
-
}) {
|
|
568
|
-
const { markdownSource, setMarkdownSource, setMonacoEditor } = useEditorContext();
|
|
569
|
-
const editorRef = useRef2(null);
|
|
570
|
-
const isExternalUpdate = useRef2(false);
|
|
571
|
-
const completionDisposable = useRef2(null);
|
|
572
|
-
const handleMount = useCallback3(
|
|
573
|
-
(editor, monaco2) => {
|
|
574
|
-
editorRef.current = editor;
|
|
575
|
-
setMonacoEditor(editor);
|
|
576
|
-
editor.focus();
|
|
577
|
-
completionDisposable.current?.dispose();
|
|
578
|
-
const templates = getAvailableTemplates2();
|
|
579
|
-
completionDisposable.current = monaco2.languages.registerCompletionItemProvider("markdown", {
|
|
580
|
-
triggerCharacters: ["["],
|
|
581
|
-
provideCompletionItems(model, position) {
|
|
582
|
-
const lineContent = model.getLineContent(position.lineNumber);
|
|
583
|
-
if (!/^#{1,6}\s/.test(lineContent)) return { suggestions: [] };
|
|
584
|
-
const textBeforeCursor = lineContent.substring(0, position.column - 1);
|
|
585
|
-
const bracketIdx = textBeforeCursor.lastIndexOf("{[");
|
|
586
|
-
if (bracketIdx === -1) return { suggestions: [] };
|
|
587
|
-
const startCol = bracketIdx + 3;
|
|
588
|
-
const range = new monaco2.Range(
|
|
589
|
-
position.lineNumber,
|
|
590
|
-
startCol,
|
|
591
|
-
position.lineNumber,
|
|
592
|
-
position.column
|
|
593
|
-
);
|
|
594
|
-
const suggestions = templates.map((name) => ({
|
|
595
|
-
label: name,
|
|
596
|
-
kind: monaco2.languages.CompletionItemKind.Value,
|
|
597
|
-
insertText: name + "]}",
|
|
598
|
-
range,
|
|
599
|
-
detail: "Block template",
|
|
600
|
-
sortText: name
|
|
601
|
-
}));
|
|
602
|
-
return { suggestions };
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
},
|
|
606
|
-
[setMonacoEditor]
|
|
607
|
-
);
|
|
608
|
-
useEffect3(() => {
|
|
609
|
-
return () => {
|
|
610
|
-
setMonacoEditor(null);
|
|
611
|
-
completionDisposable.current?.dispose();
|
|
612
|
-
completionDisposable.current = null;
|
|
613
|
-
};
|
|
614
|
-
}, [setMonacoEditor]);
|
|
615
|
-
const handleChange = useCallback3(
|
|
616
|
-
(value) => {
|
|
617
|
-
if (isExternalUpdate.current) return;
|
|
618
|
-
if (value !== void 0) {
|
|
619
|
-
setMarkdownSource(value);
|
|
620
|
-
}
|
|
621
|
-
},
|
|
622
|
-
[setMarkdownSource]
|
|
623
|
-
);
|
|
624
|
-
useEffect3(() => {
|
|
625
|
-
const editor = editorRef.current;
|
|
626
|
-
if (editor) {
|
|
627
|
-
const currentValue = editor.getValue();
|
|
628
|
-
if (currentValue !== markdownSource) {
|
|
629
|
-
isExternalUpdate.current = true;
|
|
630
|
-
editor.setValue(markdownSource);
|
|
631
|
-
isExternalUpdate.current = false;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}, [markdownSource]);
|
|
635
|
-
return /* @__PURE__ */ jsx4("div", { className, style: { width: "100%", height: "100%" }, "data-testid": "raw-editor", children: /* @__PURE__ */ jsx4(
|
|
636
|
-
Editor,
|
|
637
|
-
{
|
|
638
|
-
defaultLanguage: "markdown",
|
|
639
|
-
value: markdownSource,
|
|
640
|
-
theme,
|
|
641
|
-
onMount: handleMount,
|
|
642
|
-
onChange: handleChange,
|
|
643
|
-
options: {
|
|
644
|
-
fontSize,
|
|
645
|
-
wordWrap,
|
|
646
|
-
minimap: { enabled: minimap },
|
|
647
|
-
lineNumbers: "on",
|
|
648
|
-
scrollBeyondLastLine: false,
|
|
649
|
-
automaticLayout: true,
|
|
650
|
-
tabSize: 2,
|
|
651
|
-
renderWhitespace: "selection",
|
|
652
|
-
bracketPairColorization: { enabled: true },
|
|
653
|
-
guides: { indentation: true },
|
|
654
|
-
padding: { top: 12, bottom: 12 }
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
) });
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
// src/WysiwygEditor.tsx
|
|
661
|
-
import { useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
662
|
-
import { useEditor, EditorContent } from "@tiptap/react";
|
|
663
|
-
import StarterKit from "@tiptap/starter-kit";
|
|
664
|
-
import Table from "@tiptap/extension-table";
|
|
665
|
-
import TableRow from "@tiptap/extension-table-row";
|
|
666
|
-
import TableCell from "@tiptap/extension-table-cell";
|
|
667
|
-
import TableHeader from "@tiptap/extension-table-header";
|
|
668
|
-
import TaskList from "@tiptap/extension-task-list";
|
|
669
|
-
import TaskItem from "@tiptap/extension-task-item";
|
|
670
|
-
import Placeholder from "@tiptap/extension-placeholder";
|
|
671
|
-
|
|
672
|
-
// ../../node_modules/@tiptap/core/dist/index.js
|
|
673
|
-
import { Plugin, PluginKey, TextSelection, Selection, AllSelection, NodeSelection, EditorState } from "@tiptap/pm/state";
|
|
674
|
-
import { EditorView } from "@tiptap/pm/view";
|
|
675
|
-
import { keymap } from "@tiptap/pm/keymap";
|
|
676
|
-
import { Schema, DOMSerializer, Fragment as Fragment2, Node as Node$1, DOMParser, Slice } from "@tiptap/pm/model";
|
|
677
|
-
import { liftTarget, ReplaceStep, ReplaceAroundStep, joinPoint, Transform, canSplit, canJoin, findWrapping } from "@tiptap/pm/transform";
|
|
678
|
-
import { createParagraphNear as createParagraphNear$1, deleteSelection as deleteSelection$1, exitCode as exitCode$1, joinUp as joinUp$1, joinDown as joinDown$1, joinBackward as joinBackward$1, joinForward as joinForward$1, joinTextblockBackward as joinTextblockBackward$1, joinTextblockForward as joinTextblockForward$1, lift as lift$1, liftEmptyBlock as liftEmptyBlock$1, newlineInCode as newlineInCode$1, selectNodeBackward as selectNodeBackward$1, selectNodeForward as selectNodeForward$1, selectParentNode as selectParentNode$1, selectTextblockEnd as selectTextblockEnd$1, selectTextblockStart as selectTextblockStart$1, setBlockType, wrapIn as wrapIn$1 } from "@tiptap/pm/commands";
|
|
679
|
-
import { liftListItem as liftListItem$1, sinkListItem as sinkListItem$1, wrapInList as wrapInList$1 } from "@tiptap/pm/schema-list";
|
|
680
|
-
function createChainableState(config) {
|
|
681
|
-
const { state, transaction } = config;
|
|
682
|
-
let { selection } = transaction;
|
|
683
|
-
let { doc } = transaction;
|
|
684
|
-
let { storedMarks } = transaction;
|
|
685
|
-
return {
|
|
686
|
-
...state,
|
|
687
|
-
apply: state.apply.bind(state),
|
|
688
|
-
applyTransaction: state.applyTransaction.bind(state),
|
|
689
|
-
plugins: state.plugins,
|
|
690
|
-
schema: state.schema,
|
|
691
|
-
reconfigure: state.reconfigure.bind(state),
|
|
692
|
-
toJSON: state.toJSON.bind(state),
|
|
693
|
-
get storedMarks() {
|
|
694
|
-
return storedMarks;
|
|
695
|
-
},
|
|
696
|
-
get selection() {
|
|
697
|
-
return selection;
|
|
698
|
-
},
|
|
699
|
-
get doc() {
|
|
700
|
-
return doc;
|
|
701
|
-
},
|
|
702
|
-
get tr() {
|
|
703
|
-
selection = transaction.selection;
|
|
704
|
-
doc = transaction.doc;
|
|
705
|
-
storedMarks = transaction.storedMarks;
|
|
706
|
-
return transaction;
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
var CommandManager = class {
|
|
711
|
-
constructor(props) {
|
|
712
|
-
this.editor = props.editor;
|
|
713
|
-
this.rawCommands = this.editor.extensionManager.commands;
|
|
714
|
-
this.customState = props.state;
|
|
715
|
-
}
|
|
716
|
-
get hasCustomState() {
|
|
717
|
-
return !!this.customState;
|
|
718
|
-
}
|
|
719
|
-
get state() {
|
|
720
|
-
return this.customState || this.editor.state;
|
|
721
|
-
}
|
|
722
|
-
get commands() {
|
|
723
|
-
const { rawCommands, editor, state } = this;
|
|
724
|
-
const { view } = editor;
|
|
725
|
-
const { tr } = state;
|
|
726
|
-
const props = this.buildProps(tr);
|
|
727
|
-
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
728
|
-
const method = (...args) => {
|
|
729
|
-
const callback = command2(...args)(props);
|
|
730
|
-
if (!tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
|
731
|
-
view.dispatch(tr);
|
|
732
|
-
}
|
|
733
|
-
return callback;
|
|
734
|
-
};
|
|
735
|
-
return [name, method];
|
|
736
|
-
}));
|
|
737
|
-
}
|
|
738
|
-
get chain() {
|
|
739
|
-
return () => this.createChain();
|
|
740
|
-
}
|
|
741
|
-
get can() {
|
|
742
|
-
return () => this.createCan();
|
|
743
|
-
}
|
|
744
|
-
createChain(startTr, shouldDispatch = true) {
|
|
745
|
-
const { rawCommands, editor, state } = this;
|
|
746
|
-
const { view } = editor;
|
|
747
|
-
const callbacks = [];
|
|
748
|
-
const hasStartTransaction = !!startTr;
|
|
749
|
-
const tr = startTr || state.tr;
|
|
750
|
-
const run = () => {
|
|
751
|
-
if (!hasStartTransaction && shouldDispatch && !tr.getMeta("preventDispatch") && !this.hasCustomState) {
|
|
752
|
-
view.dispatch(tr);
|
|
753
|
-
}
|
|
754
|
-
return callbacks.every((callback) => callback === true);
|
|
755
|
-
};
|
|
756
|
-
const chain = {
|
|
757
|
-
...Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
758
|
-
const chainedCommand = (...args) => {
|
|
759
|
-
const props = this.buildProps(tr, shouldDispatch);
|
|
760
|
-
const callback = command2(...args)(props);
|
|
761
|
-
callbacks.push(callback);
|
|
762
|
-
return chain;
|
|
763
|
-
};
|
|
764
|
-
return [name, chainedCommand];
|
|
765
|
-
})),
|
|
766
|
-
run
|
|
767
|
-
};
|
|
768
|
-
return chain;
|
|
769
|
-
}
|
|
770
|
-
createCan(startTr) {
|
|
771
|
-
const { rawCommands, state } = this;
|
|
772
|
-
const dispatch = false;
|
|
773
|
-
const tr = startTr || state.tr;
|
|
774
|
-
const props = this.buildProps(tr, dispatch);
|
|
775
|
-
const formattedCommands = Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
776
|
-
return [name, (...args) => command2(...args)({ ...props, dispatch: void 0 })];
|
|
777
|
-
}));
|
|
778
|
-
return {
|
|
779
|
-
...formattedCommands,
|
|
780
|
-
chain: () => this.createChain(tr, dispatch)
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
|
-
buildProps(tr, shouldDispatch = true) {
|
|
784
|
-
const { rawCommands, editor, state } = this;
|
|
785
|
-
const { view } = editor;
|
|
786
|
-
const props = {
|
|
787
|
-
tr,
|
|
788
|
-
editor,
|
|
789
|
-
view,
|
|
790
|
-
state: createChainableState({
|
|
791
|
-
state,
|
|
792
|
-
transaction: tr
|
|
793
|
-
}),
|
|
794
|
-
dispatch: shouldDispatch ? () => void 0 : void 0,
|
|
795
|
-
chain: () => this.createChain(tr, shouldDispatch),
|
|
796
|
-
can: () => this.createCan(tr),
|
|
797
|
-
get commands() {
|
|
798
|
-
return Object.fromEntries(Object.entries(rawCommands).map(([name, command2]) => {
|
|
799
|
-
return [name, (...args) => command2(...args)(props)];
|
|
800
|
-
}));
|
|
801
|
-
}
|
|
802
|
-
};
|
|
803
|
-
return props;
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
function getExtensionField(extension, field, context) {
|
|
807
|
-
if (extension.config[field] === void 0 && extension.parent) {
|
|
808
|
-
return getExtensionField(extension.parent, field, context);
|
|
809
|
-
}
|
|
810
|
-
if (typeof extension.config[field] === "function") {
|
|
811
|
-
const value = extension.config[field].bind({
|
|
812
|
-
...context,
|
|
813
|
-
parent: extension.parent ? getExtensionField(extension.parent, field, context) : null
|
|
814
|
-
});
|
|
815
|
-
return value;
|
|
816
|
-
}
|
|
817
|
-
return extension.config[field];
|
|
818
|
-
}
|
|
819
|
-
function splitExtensions(extensions) {
|
|
820
|
-
const baseExtensions = extensions.filter((extension) => extension.type === "extension");
|
|
821
|
-
const nodeExtensions = extensions.filter((extension) => extension.type === "node");
|
|
822
|
-
const markExtensions = extensions.filter((extension) => extension.type === "mark");
|
|
823
|
-
return {
|
|
824
|
-
baseExtensions,
|
|
825
|
-
nodeExtensions,
|
|
826
|
-
markExtensions
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
function getNodeType(nameOrType, schema) {
|
|
830
|
-
if (typeof nameOrType === "string") {
|
|
831
|
-
if (!schema.nodes[nameOrType]) {
|
|
832
|
-
throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
833
|
-
}
|
|
834
|
-
return schema.nodes[nameOrType];
|
|
835
|
-
}
|
|
836
|
-
return nameOrType;
|
|
837
|
-
}
|
|
838
|
-
function mergeAttributes(...objects) {
|
|
839
|
-
return objects.filter((item) => !!item).reduce((items, item) => {
|
|
840
|
-
const mergedAttributes = { ...items };
|
|
841
|
-
Object.entries(item).forEach(([key, value]) => {
|
|
842
|
-
const exists = mergedAttributes[key];
|
|
843
|
-
if (!exists) {
|
|
844
|
-
mergedAttributes[key] = value;
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
if (key === "class") {
|
|
848
|
-
const valueClasses = value ? String(value).split(" ") : [];
|
|
849
|
-
const existingClasses = mergedAttributes[key] ? mergedAttributes[key].split(" ") : [];
|
|
850
|
-
const insertClasses = valueClasses.filter((valueClass) => !existingClasses.includes(valueClass));
|
|
851
|
-
mergedAttributes[key] = [...existingClasses, ...insertClasses].join(" ");
|
|
852
|
-
} else if (key === "style") {
|
|
853
|
-
const newStyles = value ? value.split(";").map((style) => style.trim()).filter(Boolean) : [];
|
|
854
|
-
const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(";").map((style) => style.trim()).filter(Boolean) : [];
|
|
855
|
-
const styleMap = /* @__PURE__ */ new Map();
|
|
856
|
-
existingStyles.forEach((style) => {
|
|
857
|
-
const [property, val] = style.split(":").map((part) => part.trim());
|
|
858
|
-
styleMap.set(property, val);
|
|
859
|
-
});
|
|
860
|
-
newStyles.forEach((style) => {
|
|
861
|
-
const [property, val] = style.split(":").map((part) => part.trim());
|
|
862
|
-
styleMap.set(property, val);
|
|
863
|
-
});
|
|
864
|
-
mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join("; ");
|
|
865
|
-
} else {
|
|
866
|
-
mergedAttributes[key] = value;
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
return mergedAttributes;
|
|
870
|
-
}, {});
|
|
871
|
-
}
|
|
872
|
-
function isFunction(value) {
|
|
873
|
-
return typeof value === "function";
|
|
874
|
-
}
|
|
875
|
-
function callOrReturn(value, context = void 0, ...props) {
|
|
876
|
-
if (isFunction(value)) {
|
|
877
|
-
if (context) {
|
|
878
|
-
return value.bind(context)(...props);
|
|
879
|
-
}
|
|
880
|
-
return value(...props);
|
|
881
|
-
}
|
|
882
|
-
return value;
|
|
883
|
-
}
|
|
884
|
-
function isRegExp(value) {
|
|
885
|
-
return Object.prototype.toString.call(value) === "[object RegExp]";
|
|
886
|
-
}
|
|
887
|
-
var InputRule = class {
|
|
888
|
-
constructor(config) {
|
|
889
|
-
this.find = config.find;
|
|
890
|
-
this.handler = config.handler;
|
|
891
|
-
}
|
|
892
|
-
};
|
|
893
|
-
function getType(value) {
|
|
894
|
-
return Object.prototype.toString.call(value).slice(8, -1);
|
|
895
|
-
}
|
|
896
|
-
function isPlainObject(value) {
|
|
897
|
-
if (getType(value) !== "Object") {
|
|
898
|
-
return false;
|
|
899
|
-
}
|
|
900
|
-
return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype;
|
|
901
|
-
}
|
|
902
|
-
function mergeDeep(target, source) {
|
|
903
|
-
const output = { ...target };
|
|
904
|
-
if (isPlainObject(target) && isPlainObject(source)) {
|
|
905
|
-
Object.keys(source).forEach((key) => {
|
|
906
|
-
if (isPlainObject(source[key]) && isPlainObject(target[key])) {
|
|
907
|
-
output[key] = mergeDeep(target[key], source[key]);
|
|
908
|
-
} else {
|
|
909
|
-
output[key] = source[key];
|
|
910
|
-
}
|
|
911
|
-
});
|
|
912
|
-
}
|
|
913
|
-
return output;
|
|
914
|
-
}
|
|
915
|
-
var Extension = class _Extension {
|
|
916
|
-
constructor(config = {}) {
|
|
917
|
-
this.type = "extension";
|
|
918
|
-
this.name = "extension";
|
|
919
|
-
this.parent = null;
|
|
920
|
-
this.child = null;
|
|
921
|
-
this.config = {
|
|
922
|
-
name: this.name,
|
|
923
|
-
defaultOptions: {}
|
|
924
|
-
};
|
|
925
|
-
this.config = {
|
|
926
|
-
...this.config,
|
|
927
|
-
...config
|
|
928
|
-
};
|
|
929
|
-
this.name = this.config.name;
|
|
930
|
-
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
931
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
932
|
-
}
|
|
933
|
-
this.options = this.config.defaultOptions;
|
|
934
|
-
if (this.config.addOptions) {
|
|
935
|
-
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
|
936
|
-
name: this.name
|
|
937
|
-
}));
|
|
938
|
-
}
|
|
939
|
-
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
|
940
|
-
name: this.name,
|
|
941
|
-
options: this.options
|
|
942
|
-
})) || {};
|
|
943
|
-
}
|
|
944
|
-
static create(config = {}) {
|
|
945
|
-
return new _Extension(config);
|
|
946
|
-
}
|
|
947
|
-
configure(options = {}) {
|
|
948
|
-
const extension = this.extend({
|
|
949
|
-
...this.config,
|
|
950
|
-
addOptions: () => {
|
|
951
|
-
return mergeDeep(this.options, options);
|
|
952
|
-
}
|
|
953
|
-
});
|
|
954
|
-
extension.name = this.name;
|
|
955
|
-
extension.parent = this.parent;
|
|
956
|
-
return extension;
|
|
957
|
-
}
|
|
958
|
-
extend(extendedConfig = {}) {
|
|
959
|
-
const extension = new _Extension({ ...this.config, ...extendedConfig });
|
|
960
|
-
extension.parent = this;
|
|
961
|
-
this.child = extension;
|
|
962
|
-
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
963
|
-
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
|
964
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
965
|
-
}
|
|
966
|
-
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
|
967
|
-
name: extension.name
|
|
968
|
-
}));
|
|
969
|
-
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
|
970
|
-
name: extension.name,
|
|
971
|
-
options: extension.options
|
|
972
|
-
}));
|
|
973
|
-
return extension;
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
function getTextBetween(startNode, range, options) {
|
|
977
|
-
const { from, to } = range;
|
|
978
|
-
const { blockSeparator = "\n\n", textSerializers = {} } = options || {};
|
|
979
|
-
let text = "";
|
|
980
|
-
startNode.nodesBetween(from, to, (node, pos, parent, index) => {
|
|
981
|
-
var _a;
|
|
982
|
-
if (node.isBlock && pos > from) {
|
|
983
|
-
text += blockSeparator;
|
|
984
|
-
}
|
|
985
|
-
const textSerializer = textSerializers === null || textSerializers === void 0 ? void 0 : textSerializers[node.type.name];
|
|
986
|
-
if (textSerializer) {
|
|
987
|
-
if (parent) {
|
|
988
|
-
text += textSerializer({
|
|
989
|
-
node,
|
|
990
|
-
pos,
|
|
991
|
-
parent,
|
|
992
|
-
index,
|
|
993
|
-
range
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
return false;
|
|
997
|
-
}
|
|
998
|
-
if (node.isText) {
|
|
999
|
-
text += (_a = node === null || node === void 0 ? void 0 : node.text) === null || _a === void 0 ? void 0 : _a.slice(Math.max(from, pos) - pos, to - pos);
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
return text;
|
|
1003
|
-
}
|
|
1004
|
-
function getTextSerializersFromSchema(schema) {
|
|
1005
|
-
return Object.fromEntries(Object.entries(schema.nodes).filter(([, node]) => node.spec.toText).map(([name, node]) => [name, node.spec.toText]));
|
|
1006
|
-
}
|
|
1007
|
-
var ClipboardTextSerializer = Extension.create({
|
|
1008
|
-
name: "clipboardTextSerializer",
|
|
1009
|
-
addOptions() {
|
|
1010
|
-
return {
|
|
1011
|
-
blockSeparator: void 0
|
|
1012
|
-
};
|
|
1013
|
-
},
|
|
1014
|
-
addProseMirrorPlugins() {
|
|
1015
|
-
return [
|
|
1016
|
-
new Plugin({
|
|
1017
|
-
key: new PluginKey("clipboardTextSerializer"),
|
|
1018
|
-
props: {
|
|
1019
|
-
clipboardTextSerializer: () => {
|
|
1020
|
-
const { editor } = this;
|
|
1021
|
-
const { state, schema } = editor;
|
|
1022
|
-
const { doc, selection } = state;
|
|
1023
|
-
const { ranges } = selection;
|
|
1024
|
-
const from = Math.min(...ranges.map((range2) => range2.$from.pos));
|
|
1025
|
-
const to = Math.max(...ranges.map((range2) => range2.$to.pos));
|
|
1026
|
-
const textSerializers = getTextSerializersFromSchema(schema);
|
|
1027
|
-
const range = { from, to };
|
|
1028
|
-
return getTextBetween(doc, range, {
|
|
1029
|
-
...this.options.blockSeparator !== void 0 ? { blockSeparator: this.options.blockSeparator } : {},
|
|
1030
|
-
textSerializers
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
})
|
|
1035
|
-
];
|
|
1036
|
-
}
|
|
1037
|
-
});
|
|
1038
|
-
var blur = () => ({ editor, view }) => {
|
|
1039
|
-
requestAnimationFrame(() => {
|
|
1040
|
-
var _a;
|
|
1041
|
-
if (!editor.isDestroyed) {
|
|
1042
|
-
view.dom.blur();
|
|
1043
|
-
(_a = window === null || window === void 0 ? void 0 : window.getSelection()) === null || _a === void 0 ? void 0 : _a.removeAllRanges();
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
return true;
|
|
1047
|
-
};
|
|
1048
|
-
var clearContent = (emitUpdate = false) => ({ commands: commands2 }) => {
|
|
1049
|
-
return commands2.setContent("", emitUpdate);
|
|
1050
|
-
};
|
|
1051
|
-
var clearNodes = () => ({ state, tr, dispatch }) => {
|
|
1052
|
-
const { selection } = tr;
|
|
1053
|
-
const { ranges } = selection;
|
|
1054
|
-
if (!dispatch) {
|
|
1055
|
-
return true;
|
|
1056
|
-
}
|
|
1057
|
-
ranges.forEach(({ $from, $to }) => {
|
|
1058
|
-
state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
|
|
1059
|
-
if (node.type.isText) {
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
const { doc, mapping } = tr;
|
|
1063
|
-
const $mappedFrom = doc.resolve(mapping.map(pos));
|
|
1064
|
-
const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize));
|
|
1065
|
-
const nodeRange = $mappedFrom.blockRange($mappedTo);
|
|
1066
|
-
if (!nodeRange) {
|
|
1067
|
-
return;
|
|
1068
|
-
}
|
|
1069
|
-
const targetLiftDepth = liftTarget(nodeRange);
|
|
1070
|
-
if (node.type.isTextblock) {
|
|
1071
|
-
const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index());
|
|
1072
|
-
tr.setNodeMarkup(nodeRange.start, defaultType);
|
|
1073
|
-
}
|
|
1074
|
-
if (targetLiftDepth || targetLiftDepth === 0) {
|
|
1075
|
-
tr.lift(nodeRange, targetLiftDepth);
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
|
-
});
|
|
1079
|
-
return true;
|
|
1080
|
-
};
|
|
1081
|
-
var command = (fn) => (props) => {
|
|
1082
|
-
return fn(props);
|
|
1083
|
-
};
|
|
1084
|
-
var createParagraphNear = () => ({ state, dispatch }) => {
|
|
1085
|
-
return createParagraphNear$1(state, dispatch);
|
|
1086
|
-
};
|
|
1087
|
-
var cut = (originRange, targetPos) => ({ editor, tr }) => {
|
|
1088
|
-
const { state } = editor;
|
|
1089
|
-
const contentSlice = state.doc.slice(originRange.from, originRange.to);
|
|
1090
|
-
tr.deleteRange(originRange.from, originRange.to);
|
|
1091
|
-
const newPos = tr.mapping.map(targetPos);
|
|
1092
|
-
tr.insert(newPos, contentSlice.content);
|
|
1093
|
-
tr.setSelection(new TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))));
|
|
1094
|
-
return true;
|
|
1095
|
-
};
|
|
1096
|
-
var deleteCurrentNode = () => ({ tr, dispatch }) => {
|
|
1097
|
-
const { selection } = tr;
|
|
1098
|
-
const currentNode = selection.$anchor.node();
|
|
1099
|
-
if (currentNode.content.size > 0) {
|
|
1100
|
-
return false;
|
|
1101
|
-
}
|
|
1102
|
-
const $pos = tr.selection.$anchor;
|
|
1103
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
1104
|
-
const node = $pos.node(depth);
|
|
1105
|
-
if (node.type === currentNode.type) {
|
|
1106
|
-
if (dispatch) {
|
|
1107
|
-
const from = $pos.before(depth);
|
|
1108
|
-
const to = $pos.after(depth);
|
|
1109
|
-
tr.delete(from, to).scrollIntoView();
|
|
1110
|
-
}
|
|
1111
|
-
return true;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return false;
|
|
1115
|
-
};
|
|
1116
|
-
var deleteNode = (typeOrName) => ({ tr, state, dispatch }) => {
|
|
1117
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1118
|
-
const $pos = tr.selection.$anchor;
|
|
1119
|
-
for (let depth = $pos.depth; depth > 0; depth -= 1) {
|
|
1120
|
-
const node = $pos.node(depth);
|
|
1121
|
-
if (node.type === type) {
|
|
1122
|
-
if (dispatch) {
|
|
1123
|
-
const from = $pos.before(depth);
|
|
1124
|
-
const to = $pos.after(depth);
|
|
1125
|
-
tr.delete(from, to).scrollIntoView();
|
|
1126
|
-
}
|
|
1127
|
-
return true;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
return false;
|
|
1131
|
-
};
|
|
1132
|
-
var deleteRange = (range) => ({ tr, dispatch }) => {
|
|
1133
|
-
const { from, to } = range;
|
|
1134
|
-
if (dispatch) {
|
|
1135
|
-
tr.delete(from, to);
|
|
1136
|
-
}
|
|
1137
|
-
return true;
|
|
1138
|
-
};
|
|
1139
|
-
var deleteSelection = () => ({ state, dispatch }) => {
|
|
1140
|
-
return deleteSelection$1(state, dispatch);
|
|
1141
|
-
};
|
|
1142
|
-
var enter = () => ({ commands: commands2 }) => {
|
|
1143
|
-
return commands2.keyboardShortcut("Enter");
|
|
1144
|
-
};
|
|
1145
|
-
var exitCode = () => ({ state, dispatch }) => {
|
|
1146
|
-
return exitCode$1(state, dispatch);
|
|
1147
|
-
};
|
|
1148
|
-
function objectIncludes(object1, object2, options = { strict: true }) {
|
|
1149
|
-
const keys = Object.keys(object2);
|
|
1150
|
-
if (!keys.length) {
|
|
1151
|
-
return true;
|
|
1152
|
-
}
|
|
1153
|
-
return keys.every((key) => {
|
|
1154
|
-
if (options.strict) {
|
|
1155
|
-
return object2[key] === object1[key];
|
|
1156
|
-
}
|
|
1157
|
-
if (isRegExp(object2[key])) {
|
|
1158
|
-
return object2[key].test(object1[key]);
|
|
1159
|
-
}
|
|
1160
|
-
return object2[key] === object1[key];
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
function findMarkInSet(marks, type, attributes = {}) {
|
|
1164
|
-
return marks.find((item) => {
|
|
1165
|
-
return item.type === type && objectIncludes(
|
|
1166
|
-
// Only check equality for the attributes that are provided
|
|
1167
|
-
Object.fromEntries(Object.keys(attributes).map((k) => [k, item.attrs[k]])),
|
|
1168
|
-
attributes
|
|
1169
|
-
);
|
|
1170
|
-
});
|
|
1171
|
-
}
|
|
1172
|
-
function isMarkInSet(marks, type, attributes = {}) {
|
|
1173
|
-
return !!findMarkInSet(marks, type, attributes);
|
|
1174
|
-
}
|
|
1175
|
-
function getMarkRange($pos, type, attributes) {
|
|
1176
|
-
var _a;
|
|
1177
|
-
if (!$pos || !type) {
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
let start = $pos.parent.childAfter($pos.parentOffset);
|
|
1181
|
-
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
|
1182
|
-
start = $pos.parent.childBefore($pos.parentOffset);
|
|
1183
|
-
}
|
|
1184
|
-
if (!start.node || !start.node.marks.some((mark2) => mark2.type === type)) {
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1187
|
-
attributes = attributes || ((_a = start.node.marks[0]) === null || _a === void 0 ? void 0 : _a.attrs);
|
|
1188
|
-
const mark = findMarkInSet([...start.node.marks], type, attributes);
|
|
1189
|
-
if (!mark) {
|
|
1190
|
-
return;
|
|
1191
|
-
}
|
|
1192
|
-
let startIndex = start.index;
|
|
1193
|
-
let startPos = $pos.start() + start.offset;
|
|
1194
|
-
let endIndex = startIndex + 1;
|
|
1195
|
-
let endPos = startPos + start.node.nodeSize;
|
|
1196
|
-
while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {
|
|
1197
|
-
startIndex -= 1;
|
|
1198
|
-
startPos -= $pos.parent.child(startIndex).nodeSize;
|
|
1199
|
-
}
|
|
1200
|
-
while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {
|
|
1201
|
-
endPos += $pos.parent.child(endIndex).nodeSize;
|
|
1202
|
-
endIndex += 1;
|
|
1203
|
-
}
|
|
1204
|
-
return {
|
|
1205
|
-
from: startPos,
|
|
1206
|
-
to: endPos
|
|
1207
|
-
};
|
|
1208
|
-
}
|
|
1209
|
-
function getMarkType(nameOrType, schema) {
|
|
1210
|
-
if (typeof nameOrType === "string") {
|
|
1211
|
-
if (!schema.marks[nameOrType]) {
|
|
1212
|
-
throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`);
|
|
1213
|
-
}
|
|
1214
|
-
return schema.marks[nameOrType];
|
|
1215
|
-
}
|
|
1216
|
-
return nameOrType;
|
|
1217
|
-
}
|
|
1218
|
-
var extendMarkRange = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1219
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1220
|
-
const { doc, selection } = tr;
|
|
1221
|
-
const { $from, from, to } = selection;
|
|
1222
|
-
if (dispatch) {
|
|
1223
|
-
const range = getMarkRange($from, type, attributes);
|
|
1224
|
-
if (range && range.from <= from && range.to >= to) {
|
|
1225
|
-
const newSelection = TextSelection.create(doc, range.from, range.to);
|
|
1226
|
-
tr.setSelection(newSelection);
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return true;
|
|
1230
|
-
};
|
|
1231
|
-
var first = (commands2) => (props) => {
|
|
1232
|
-
const items = typeof commands2 === "function" ? commands2(props) : commands2;
|
|
1233
|
-
for (let i = 0; i < items.length; i += 1) {
|
|
1234
|
-
if (items[i](props)) {
|
|
1235
|
-
return true;
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
return false;
|
|
1239
|
-
};
|
|
1240
|
-
function isTextSelection(value) {
|
|
1241
|
-
return value instanceof TextSelection;
|
|
1242
|
-
}
|
|
1243
|
-
function minMax(value = 0, min = 0, max = 0) {
|
|
1244
|
-
return Math.min(Math.max(value, min), max);
|
|
1245
|
-
}
|
|
1246
|
-
function resolveFocusPosition(doc, position = null) {
|
|
1247
|
-
if (!position) {
|
|
1248
|
-
return null;
|
|
1249
|
-
}
|
|
1250
|
-
const selectionAtStart = Selection.atStart(doc);
|
|
1251
|
-
const selectionAtEnd = Selection.atEnd(doc);
|
|
1252
|
-
if (position === "start" || position === true) {
|
|
1253
|
-
return selectionAtStart;
|
|
1254
|
-
}
|
|
1255
|
-
if (position === "end") {
|
|
1256
|
-
return selectionAtEnd;
|
|
1257
|
-
}
|
|
1258
|
-
const minPos = selectionAtStart.from;
|
|
1259
|
-
const maxPos = selectionAtEnd.to;
|
|
1260
|
-
if (position === "all") {
|
|
1261
|
-
return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos));
|
|
1262
|
-
}
|
|
1263
|
-
return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos));
|
|
1264
|
-
}
|
|
1265
|
-
function isAndroid() {
|
|
1266
|
-
return navigator.platform === "Android" || /android/i.test(navigator.userAgent);
|
|
1267
|
-
}
|
|
1268
|
-
function isiOS() {
|
|
1269
|
-
return [
|
|
1270
|
-
"iPad Simulator",
|
|
1271
|
-
"iPhone Simulator",
|
|
1272
|
-
"iPod Simulator",
|
|
1273
|
-
"iPad",
|
|
1274
|
-
"iPhone",
|
|
1275
|
-
"iPod"
|
|
1276
|
-
].includes(navigator.platform) || navigator.userAgent.includes("Mac") && "ontouchend" in document;
|
|
1277
|
-
}
|
|
1278
|
-
function isSafari() {
|
|
1279
|
-
return typeof navigator !== "undefined" ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false;
|
|
1280
|
-
}
|
|
1281
|
-
var focus = (position = null, options = {}) => ({ editor, view, tr, dispatch }) => {
|
|
1282
|
-
options = {
|
|
1283
|
-
scrollIntoView: true,
|
|
1284
|
-
...options
|
|
1285
|
-
};
|
|
1286
|
-
const delayedFocus = () => {
|
|
1287
|
-
if (isiOS() || isAndroid()) {
|
|
1288
|
-
view.dom.focus();
|
|
1289
|
-
}
|
|
1290
|
-
requestAnimationFrame(() => {
|
|
1291
|
-
if (!editor.isDestroyed) {
|
|
1292
|
-
view.focus();
|
|
1293
|
-
if (isSafari() && !isiOS() && !isAndroid()) {
|
|
1294
|
-
view.dom.focus({ preventScroll: true });
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
});
|
|
1298
|
-
};
|
|
1299
|
-
if (view.hasFocus() && position === null || position === false) {
|
|
1300
|
-
return true;
|
|
1301
|
-
}
|
|
1302
|
-
if (dispatch && position === null && !isTextSelection(editor.state.selection)) {
|
|
1303
|
-
delayedFocus();
|
|
1304
|
-
return true;
|
|
1305
|
-
}
|
|
1306
|
-
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection;
|
|
1307
|
-
const isSameSelection = editor.state.selection.eq(selection);
|
|
1308
|
-
if (dispatch) {
|
|
1309
|
-
if (!isSameSelection) {
|
|
1310
|
-
tr.setSelection(selection);
|
|
1311
|
-
}
|
|
1312
|
-
if (isSameSelection && tr.storedMarks) {
|
|
1313
|
-
tr.setStoredMarks(tr.storedMarks);
|
|
1314
|
-
}
|
|
1315
|
-
delayedFocus();
|
|
1316
|
-
}
|
|
1317
|
-
return true;
|
|
1318
|
-
};
|
|
1319
|
-
var forEach = (items, fn) => (props) => {
|
|
1320
|
-
return items.every((item, index) => fn(item, { ...props, index }));
|
|
1321
|
-
};
|
|
1322
|
-
var insertContent = (value, options) => ({ tr, commands: commands2 }) => {
|
|
1323
|
-
return commands2.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options);
|
|
1324
|
-
};
|
|
1325
|
-
var removeWhitespaces = (node) => {
|
|
1326
|
-
const children = node.childNodes;
|
|
1327
|
-
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
1328
|
-
const child = children[i];
|
|
1329
|
-
if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
|
|
1330
|
-
node.removeChild(child);
|
|
1331
|
-
} else if (child.nodeType === 1) {
|
|
1332
|
-
removeWhitespaces(child);
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
return node;
|
|
1336
|
-
};
|
|
1337
|
-
function elementFromString(value) {
|
|
1338
|
-
const wrappedValue = `<body>${value}</body>`;
|
|
1339
|
-
const html = new window.DOMParser().parseFromString(wrappedValue, "text/html").body;
|
|
1340
|
-
return removeWhitespaces(html);
|
|
1341
|
-
}
|
|
1342
|
-
function createNodeFromContent(content, schema, options) {
|
|
1343
|
-
if (content instanceof Node$1 || content instanceof Fragment2) {
|
|
1344
|
-
return content;
|
|
1345
|
-
}
|
|
1346
|
-
options = {
|
|
1347
|
-
slice: true,
|
|
1348
|
-
parseOptions: {},
|
|
1349
|
-
...options
|
|
1350
|
-
};
|
|
1351
|
-
const isJSONContent = typeof content === "object" && content !== null;
|
|
1352
|
-
const isTextContent = typeof content === "string";
|
|
1353
|
-
if (isJSONContent) {
|
|
1354
|
-
try {
|
|
1355
|
-
const isArrayContent = Array.isArray(content) && content.length > 0;
|
|
1356
|
-
if (isArrayContent) {
|
|
1357
|
-
return Fragment2.fromArray(content.map((item) => schema.nodeFromJSON(item)));
|
|
1358
|
-
}
|
|
1359
|
-
const node = schema.nodeFromJSON(content);
|
|
1360
|
-
if (options.errorOnInvalidContent) {
|
|
1361
|
-
node.check();
|
|
1362
|
-
}
|
|
1363
|
-
return node;
|
|
1364
|
-
} catch (error) {
|
|
1365
|
-
if (options.errorOnInvalidContent) {
|
|
1366
|
-
throw new Error("[tiptap error]: Invalid JSON content", { cause: error });
|
|
1367
|
-
}
|
|
1368
|
-
console.warn("[tiptap warn]: Invalid content.", "Passed value:", content, "Error:", error);
|
|
1369
|
-
return createNodeFromContent("", schema, options);
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
if (isTextContent) {
|
|
1373
|
-
if (options.errorOnInvalidContent) {
|
|
1374
|
-
let hasInvalidContent = false;
|
|
1375
|
-
let invalidContent = "";
|
|
1376
|
-
const contentCheckSchema = new Schema({
|
|
1377
|
-
topNode: schema.spec.topNode,
|
|
1378
|
-
marks: schema.spec.marks,
|
|
1379
|
-
// Prosemirror's schemas are executed such that: the last to execute, matches last
|
|
1380
|
-
// This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle
|
|
1381
|
-
nodes: schema.spec.nodes.append({
|
|
1382
|
-
__tiptap__private__unknown__catch__all__node: {
|
|
1383
|
-
content: "inline*",
|
|
1384
|
-
group: "block",
|
|
1385
|
-
parseDOM: [
|
|
1386
|
-
{
|
|
1387
|
-
tag: "*",
|
|
1388
|
-
getAttrs: (e) => {
|
|
1389
|
-
hasInvalidContent = true;
|
|
1390
|
-
invalidContent = typeof e === "string" ? e : e.outerHTML;
|
|
1391
|
-
return null;
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
]
|
|
1395
|
-
}
|
|
1396
|
-
})
|
|
1397
|
-
});
|
|
1398
|
-
if (options.slice) {
|
|
1399
|
-
DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions);
|
|
1400
|
-
} else {
|
|
1401
|
-
DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions);
|
|
1402
|
-
}
|
|
1403
|
-
if (options.errorOnInvalidContent && hasInvalidContent) {
|
|
1404
|
-
throw new Error("[tiptap error]: Invalid HTML content", { cause: new Error(`Invalid element found: ${invalidContent}`) });
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
const parser = DOMParser.fromSchema(schema);
|
|
1408
|
-
if (options.slice) {
|
|
1409
|
-
return parser.parseSlice(elementFromString(content), options.parseOptions).content;
|
|
1410
|
-
}
|
|
1411
|
-
return parser.parse(elementFromString(content), options.parseOptions);
|
|
1412
|
-
}
|
|
1413
|
-
return createNodeFromContent("", schema, options);
|
|
1414
|
-
}
|
|
1415
|
-
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
1416
|
-
const last = tr.steps.length - 1;
|
|
1417
|
-
if (last < startLen) {
|
|
1418
|
-
return;
|
|
1419
|
-
}
|
|
1420
|
-
const step = tr.steps[last];
|
|
1421
|
-
if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) {
|
|
1422
|
-
return;
|
|
1423
|
-
}
|
|
1424
|
-
const map = tr.mapping.maps[last];
|
|
1425
|
-
let end = 0;
|
|
1426
|
-
map.forEach((_from, _to, _newFrom, newTo) => {
|
|
1427
|
-
if (end === 0) {
|
|
1428
|
-
end = newTo;
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
|
|
1432
|
-
}
|
|
1433
|
-
var isFragment = (nodeOrFragment) => {
|
|
1434
|
-
return !("type" in nodeOrFragment);
|
|
1435
|
-
};
|
|
1436
|
-
var insertContentAt = (position, value, options) => ({ tr, dispatch, editor }) => {
|
|
1437
|
-
var _a;
|
|
1438
|
-
if (dispatch) {
|
|
1439
|
-
options = {
|
|
1440
|
-
parseOptions: editor.options.parseOptions,
|
|
1441
|
-
updateSelection: true,
|
|
1442
|
-
applyInputRules: false,
|
|
1443
|
-
applyPasteRules: false,
|
|
1444
|
-
...options
|
|
1445
|
-
};
|
|
1446
|
-
let content;
|
|
1447
|
-
const emitContentError = (error) => {
|
|
1448
|
-
editor.emit("contentError", {
|
|
1449
|
-
editor,
|
|
1450
|
-
error,
|
|
1451
|
-
disableCollaboration: () => {
|
|
1452
|
-
if (editor.storage.collaboration) {
|
|
1453
|
-
editor.storage.collaboration.isDisabled = true;
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
});
|
|
1457
|
-
};
|
|
1458
|
-
const parseOptions = {
|
|
1459
|
-
preserveWhitespace: "full",
|
|
1460
|
-
...options.parseOptions
|
|
1461
|
-
};
|
|
1462
|
-
if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {
|
|
1463
|
-
try {
|
|
1464
|
-
createNodeFromContent(value, editor.schema, {
|
|
1465
|
-
parseOptions,
|
|
1466
|
-
errorOnInvalidContent: true
|
|
1467
|
-
});
|
|
1468
|
-
} catch (e) {
|
|
1469
|
-
emitContentError(e);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
try {
|
|
1473
|
-
content = createNodeFromContent(value, editor.schema, {
|
|
1474
|
-
parseOptions,
|
|
1475
|
-
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
|
1476
|
-
});
|
|
1477
|
-
} catch (e) {
|
|
1478
|
-
emitContentError(e);
|
|
1479
|
-
return false;
|
|
1480
|
-
}
|
|
1481
|
-
let { from, to } = typeof position === "number" ? { from: position, to: position } : { from: position.from, to: position.to };
|
|
1482
|
-
let isOnlyTextContent = true;
|
|
1483
|
-
let isOnlyBlockContent = true;
|
|
1484
|
-
const nodes = isFragment(content) ? content : [content];
|
|
1485
|
-
nodes.forEach((node) => {
|
|
1486
|
-
node.check();
|
|
1487
|
-
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false;
|
|
1488
|
-
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;
|
|
1489
|
-
});
|
|
1490
|
-
if (from === to && isOnlyBlockContent) {
|
|
1491
|
-
const { parent } = tr.doc.resolve(from);
|
|
1492
|
-
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount;
|
|
1493
|
-
if (isEmptyTextBlock) {
|
|
1494
|
-
from -= 1;
|
|
1495
|
-
to += 1;
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
let newContent;
|
|
1499
|
-
if (isOnlyTextContent) {
|
|
1500
|
-
if (Array.isArray(value)) {
|
|
1501
|
-
newContent = value.map((v) => v.text || "").join("");
|
|
1502
|
-
} else if (value instanceof Fragment2) {
|
|
1503
|
-
let text = "";
|
|
1504
|
-
value.forEach((node) => {
|
|
1505
|
-
if (node.text) {
|
|
1506
|
-
text += node.text;
|
|
1507
|
-
}
|
|
1508
|
-
});
|
|
1509
|
-
newContent = text;
|
|
1510
|
-
} else if (typeof value === "object" && !!value && !!value.text) {
|
|
1511
|
-
newContent = value.text;
|
|
1512
|
-
} else {
|
|
1513
|
-
newContent = value;
|
|
1514
|
-
}
|
|
1515
|
-
tr.insertText(newContent, from, to);
|
|
1516
|
-
} else {
|
|
1517
|
-
newContent = content;
|
|
1518
|
-
tr.replaceWith(from, to, newContent);
|
|
1519
|
-
}
|
|
1520
|
-
if (options.updateSelection) {
|
|
1521
|
-
selectionToInsertionEnd(tr, tr.steps.length - 1, -1);
|
|
1522
|
-
}
|
|
1523
|
-
if (options.applyInputRules) {
|
|
1524
|
-
tr.setMeta("applyInputRules", { from, text: newContent });
|
|
1525
|
-
}
|
|
1526
|
-
if (options.applyPasteRules) {
|
|
1527
|
-
tr.setMeta("applyPasteRules", { from, text: newContent });
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
return true;
|
|
1531
|
-
};
|
|
1532
|
-
var joinUp = () => ({ state, dispatch }) => {
|
|
1533
|
-
return joinUp$1(state, dispatch);
|
|
1534
|
-
};
|
|
1535
|
-
var joinDown = () => ({ state, dispatch }) => {
|
|
1536
|
-
return joinDown$1(state, dispatch);
|
|
1537
|
-
};
|
|
1538
|
-
var joinBackward = () => ({ state, dispatch }) => {
|
|
1539
|
-
return joinBackward$1(state, dispatch);
|
|
1540
|
-
};
|
|
1541
|
-
var joinForward = () => ({ state, dispatch }) => {
|
|
1542
|
-
return joinForward$1(state, dispatch);
|
|
1543
|
-
};
|
|
1544
|
-
var joinItemBackward = () => ({ state, dispatch, tr }) => {
|
|
1545
|
-
try {
|
|
1546
|
-
const point = joinPoint(state.doc, state.selection.$from.pos, -1);
|
|
1547
|
-
if (point === null || point === void 0) {
|
|
1548
|
-
return false;
|
|
1549
|
-
}
|
|
1550
|
-
tr.join(point, 2);
|
|
1551
|
-
if (dispatch) {
|
|
1552
|
-
dispatch(tr);
|
|
1553
|
-
}
|
|
1554
|
-
return true;
|
|
1555
|
-
} catch {
|
|
1556
|
-
return false;
|
|
1557
|
-
}
|
|
1558
|
-
};
|
|
1559
|
-
var joinItemForward = () => ({ state, dispatch, tr }) => {
|
|
1560
|
-
try {
|
|
1561
|
-
const point = joinPoint(state.doc, state.selection.$from.pos, 1);
|
|
1562
|
-
if (point === null || point === void 0) {
|
|
1563
|
-
return false;
|
|
1564
|
-
}
|
|
1565
|
-
tr.join(point, 2);
|
|
1566
|
-
if (dispatch) {
|
|
1567
|
-
dispatch(tr);
|
|
1568
|
-
}
|
|
1569
|
-
return true;
|
|
1570
|
-
} catch {
|
|
1571
|
-
return false;
|
|
1572
|
-
}
|
|
1573
|
-
};
|
|
1574
|
-
var joinTextblockBackward = () => ({ state, dispatch }) => {
|
|
1575
|
-
return joinTextblockBackward$1(state, dispatch);
|
|
1576
|
-
};
|
|
1577
|
-
var joinTextblockForward = () => ({ state, dispatch }) => {
|
|
1578
|
-
return joinTextblockForward$1(state, dispatch);
|
|
1579
|
-
};
|
|
1580
|
-
function isMacOS() {
|
|
1581
|
-
return typeof navigator !== "undefined" ? /Mac/.test(navigator.platform) : false;
|
|
1582
|
-
}
|
|
1583
|
-
function normalizeKeyName(name) {
|
|
1584
|
-
const parts = name.split(/-(?!$)/);
|
|
1585
|
-
let result = parts[parts.length - 1];
|
|
1586
|
-
if (result === "Space") {
|
|
1587
|
-
result = " ";
|
|
1588
|
-
}
|
|
1589
|
-
let alt;
|
|
1590
|
-
let ctrl;
|
|
1591
|
-
let shift;
|
|
1592
|
-
let meta;
|
|
1593
|
-
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
1594
|
-
const mod = parts[i];
|
|
1595
|
-
if (/^(cmd|meta|m)$/i.test(mod)) {
|
|
1596
|
-
meta = true;
|
|
1597
|
-
} else if (/^a(lt)?$/i.test(mod)) {
|
|
1598
|
-
alt = true;
|
|
1599
|
-
} else if (/^(c|ctrl|control)$/i.test(mod)) {
|
|
1600
|
-
ctrl = true;
|
|
1601
|
-
} else if (/^s(hift)?$/i.test(mod)) {
|
|
1602
|
-
shift = true;
|
|
1603
|
-
} else if (/^mod$/i.test(mod)) {
|
|
1604
|
-
if (isiOS() || isMacOS()) {
|
|
1605
|
-
meta = true;
|
|
1606
|
-
} else {
|
|
1607
|
-
ctrl = true;
|
|
1608
|
-
}
|
|
1609
|
-
} else {
|
|
1610
|
-
throw new Error(`Unrecognized modifier name: ${mod}`);
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
if (alt) {
|
|
1614
|
-
result = `Alt-${result}`;
|
|
1615
|
-
}
|
|
1616
|
-
if (ctrl) {
|
|
1617
|
-
result = `Ctrl-${result}`;
|
|
1618
|
-
}
|
|
1619
|
-
if (meta) {
|
|
1620
|
-
result = `Meta-${result}`;
|
|
1621
|
-
}
|
|
1622
|
-
if (shift) {
|
|
1623
|
-
result = `Shift-${result}`;
|
|
1624
|
-
}
|
|
1625
|
-
return result;
|
|
1626
|
-
}
|
|
1627
|
-
var keyboardShortcut = (name) => ({ editor, view, tr, dispatch }) => {
|
|
1628
|
-
const keys = normalizeKeyName(name).split(/-(?!$)/);
|
|
1629
|
-
const key = keys.find((item) => !["Alt", "Ctrl", "Meta", "Shift"].includes(item));
|
|
1630
|
-
const event = new KeyboardEvent("keydown", {
|
|
1631
|
-
key: key === "Space" ? " " : key,
|
|
1632
|
-
altKey: keys.includes("Alt"),
|
|
1633
|
-
ctrlKey: keys.includes("Ctrl"),
|
|
1634
|
-
metaKey: keys.includes("Meta"),
|
|
1635
|
-
shiftKey: keys.includes("Shift"),
|
|
1636
|
-
bubbles: true,
|
|
1637
|
-
cancelable: true
|
|
1638
|
-
});
|
|
1639
|
-
const capturedTransaction = editor.captureTransaction(() => {
|
|
1640
|
-
view.someProp("handleKeyDown", (f) => f(view, event));
|
|
1641
|
-
});
|
|
1642
|
-
capturedTransaction === null || capturedTransaction === void 0 ? void 0 : capturedTransaction.steps.forEach((step) => {
|
|
1643
|
-
const newStep = step.map(tr.mapping);
|
|
1644
|
-
if (newStep && dispatch) {
|
|
1645
|
-
tr.maybeStep(newStep);
|
|
1646
|
-
}
|
|
1647
|
-
});
|
|
1648
|
-
return true;
|
|
1649
|
-
};
|
|
1650
|
-
function isNodeActive(state, typeOrName, attributes = {}) {
|
|
1651
|
-
const { from, to, empty } = state.selection;
|
|
1652
|
-
const type = typeOrName ? getNodeType(typeOrName, state.schema) : null;
|
|
1653
|
-
const nodeRanges = [];
|
|
1654
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1655
|
-
if (node.isText) {
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
const relativeFrom = Math.max(from, pos);
|
|
1659
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
1660
|
-
nodeRanges.push({
|
|
1661
|
-
node,
|
|
1662
|
-
from: relativeFrom,
|
|
1663
|
-
to: relativeTo
|
|
1664
|
-
});
|
|
1665
|
-
});
|
|
1666
|
-
const selectionRange = to - from;
|
|
1667
|
-
const matchedNodeRanges = nodeRanges.filter((nodeRange) => {
|
|
1668
|
-
if (!type) {
|
|
1669
|
-
return true;
|
|
1670
|
-
}
|
|
1671
|
-
return type.name === nodeRange.node.type.name;
|
|
1672
|
-
}).filter((nodeRange) => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }));
|
|
1673
|
-
if (empty) {
|
|
1674
|
-
return !!matchedNodeRanges.length;
|
|
1675
|
-
}
|
|
1676
|
-
const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0);
|
|
1677
|
-
return range >= selectionRange;
|
|
1678
|
-
}
|
|
1679
|
-
var lift = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
1680
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1681
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
1682
|
-
if (!isActive) {
|
|
1683
|
-
return false;
|
|
1684
|
-
}
|
|
1685
|
-
return lift$1(state, dispatch);
|
|
1686
|
-
};
|
|
1687
|
-
var liftEmptyBlock = () => ({ state, dispatch }) => {
|
|
1688
|
-
return liftEmptyBlock$1(state, dispatch);
|
|
1689
|
-
};
|
|
1690
|
-
var liftListItem = (typeOrName) => ({ state, dispatch }) => {
|
|
1691
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
1692
|
-
return liftListItem$1(type)(state, dispatch);
|
|
1693
|
-
};
|
|
1694
|
-
var newlineInCode = () => ({ state, dispatch }) => {
|
|
1695
|
-
return newlineInCode$1(state, dispatch);
|
|
1696
|
-
};
|
|
1697
|
-
function getSchemaTypeNameByName(name, schema) {
|
|
1698
|
-
if (schema.nodes[name]) {
|
|
1699
|
-
return "node";
|
|
1700
|
-
}
|
|
1701
|
-
if (schema.marks[name]) {
|
|
1702
|
-
return "mark";
|
|
1703
|
-
}
|
|
1704
|
-
return null;
|
|
1705
|
-
}
|
|
1706
|
-
function deleteProps(obj, propOrProps) {
|
|
1707
|
-
const props = typeof propOrProps === "string" ? [propOrProps] : propOrProps;
|
|
1708
|
-
return Object.keys(obj).reduce((newObj, prop) => {
|
|
1709
|
-
if (!props.includes(prop)) {
|
|
1710
|
-
newObj[prop] = obj[prop];
|
|
1711
|
-
}
|
|
1712
|
-
return newObj;
|
|
1713
|
-
}, {});
|
|
1714
|
-
}
|
|
1715
|
-
var resetAttributes = (typeOrName, attributes) => ({ tr, state, dispatch }) => {
|
|
1716
|
-
let nodeType = null;
|
|
1717
|
-
let markType = null;
|
|
1718
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
|
1719
|
-
if (!schemaType) {
|
|
1720
|
-
return false;
|
|
1721
|
-
}
|
|
1722
|
-
if (schemaType === "node") {
|
|
1723
|
-
nodeType = getNodeType(typeOrName, state.schema);
|
|
1724
|
-
}
|
|
1725
|
-
if (schemaType === "mark") {
|
|
1726
|
-
markType = getMarkType(typeOrName, state.schema);
|
|
1727
|
-
}
|
|
1728
|
-
if (dispatch) {
|
|
1729
|
-
tr.selection.ranges.forEach((range) => {
|
|
1730
|
-
state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
|
|
1731
|
-
if (nodeType && nodeType === node.type) {
|
|
1732
|
-
tr.setNodeMarkup(pos, void 0, deleteProps(node.attrs, attributes));
|
|
1733
|
-
}
|
|
1734
|
-
if (markType && node.marks.length) {
|
|
1735
|
-
node.marks.forEach((mark) => {
|
|
1736
|
-
if (markType === mark.type) {
|
|
1737
|
-
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)));
|
|
1738
|
-
}
|
|
1739
|
-
});
|
|
1740
|
-
}
|
|
1741
|
-
});
|
|
1742
|
-
});
|
|
1743
|
-
}
|
|
1744
|
-
return true;
|
|
1745
|
-
};
|
|
1746
|
-
var scrollIntoView = () => ({ tr, dispatch }) => {
|
|
1747
|
-
if (dispatch) {
|
|
1748
|
-
tr.scrollIntoView();
|
|
1749
|
-
}
|
|
1750
|
-
return true;
|
|
1751
|
-
};
|
|
1752
|
-
var selectAll = () => ({ tr, dispatch }) => {
|
|
1753
|
-
if (dispatch) {
|
|
1754
|
-
const selection = new AllSelection(tr.doc);
|
|
1755
|
-
tr.setSelection(selection);
|
|
1756
|
-
}
|
|
1757
|
-
return true;
|
|
1758
|
-
};
|
|
1759
|
-
var selectNodeBackward = () => ({ state, dispatch }) => {
|
|
1760
|
-
return selectNodeBackward$1(state, dispatch);
|
|
1761
|
-
};
|
|
1762
|
-
var selectNodeForward = () => ({ state, dispatch }) => {
|
|
1763
|
-
return selectNodeForward$1(state, dispatch);
|
|
1764
|
-
};
|
|
1765
|
-
var selectParentNode = () => ({ state, dispatch }) => {
|
|
1766
|
-
return selectParentNode$1(state, dispatch);
|
|
1767
|
-
};
|
|
1768
|
-
var selectTextblockEnd = () => ({ state, dispatch }) => {
|
|
1769
|
-
return selectTextblockEnd$1(state, dispatch);
|
|
1770
|
-
};
|
|
1771
|
-
var selectTextblockStart = () => ({ state, dispatch }) => {
|
|
1772
|
-
return selectTextblockStart$1(state, dispatch);
|
|
1773
|
-
};
|
|
1774
|
-
function createDocument(content, schema, parseOptions = {}, options = {}) {
|
|
1775
|
-
return createNodeFromContent(content, schema, {
|
|
1776
|
-
slice: false,
|
|
1777
|
-
parseOptions,
|
|
1778
|
-
errorOnInvalidContent: options.errorOnInvalidContent
|
|
1779
|
-
});
|
|
1780
|
-
}
|
|
1781
|
-
var setContent = (content, emitUpdate = false, parseOptions = {}, options = {}) => ({ editor, tr, dispatch, commands: commands2 }) => {
|
|
1782
|
-
var _a, _b;
|
|
1783
|
-
const { doc } = tr;
|
|
1784
|
-
if (parseOptions.preserveWhitespace !== "full") {
|
|
1785
|
-
const document2 = createDocument(content, editor.schema, parseOptions, {
|
|
1786
|
-
errorOnInvalidContent: (_a = options.errorOnInvalidContent) !== null && _a !== void 0 ? _a : editor.options.enableContentCheck
|
|
1787
|
-
});
|
|
1788
|
-
if (dispatch) {
|
|
1789
|
-
tr.replaceWith(0, doc.content.size, document2).setMeta("preventUpdate", !emitUpdate);
|
|
1790
|
-
}
|
|
1791
|
-
return true;
|
|
1792
|
-
}
|
|
1793
|
-
if (dispatch) {
|
|
1794
|
-
tr.setMeta("preventUpdate", !emitUpdate);
|
|
1795
|
-
}
|
|
1796
|
-
return commands2.insertContentAt({ from: 0, to: doc.content.size }, content, {
|
|
1797
|
-
parseOptions,
|
|
1798
|
-
errorOnInvalidContent: (_b = options.errorOnInvalidContent) !== null && _b !== void 0 ? _b : editor.options.enableContentCheck
|
|
1799
|
-
});
|
|
1800
|
-
};
|
|
1801
|
-
function getMarkAttributes(state, typeOrName) {
|
|
1802
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1803
|
-
const { from, to, empty } = state.selection;
|
|
1804
|
-
const marks = [];
|
|
1805
|
-
if (empty) {
|
|
1806
|
-
if (state.storedMarks) {
|
|
1807
|
-
marks.push(...state.storedMarks);
|
|
1808
|
-
}
|
|
1809
|
-
marks.push(...state.selection.$head.marks());
|
|
1810
|
-
} else {
|
|
1811
|
-
state.doc.nodesBetween(from, to, (node) => {
|
|
1812
|
-
marks.push(...node.marks);
|
|
1813
|
-
});
|
|
1814
|
-
}
|
|
1815
|
-
const mark = marks.find((markItem) => markItem.type.name === type.name);
|
|
1816
|
-
if (!mark) {
|
|
1817
|
-
return {};
|
|
1818
|
-
}
|
|
1819
|
-
return { ...mark.attrs };
|
|
1820
|
-
}
|
|
1821
|
-
function defaultBlockAt(match) {
|
|
1822
|
-
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
1823
|
-
const { type } = match.edge(i);
|
|
1824
|
-
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
1825
|
-
return type;
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
return null;
|
|
1829
|
-
}
|
|
1830
|
-
function findParentNodeClosestToPos($pos, predicate) {
|
|
1831
|
-
for (let i = $pos.depth; i > 0; i -= 1) {
|
|
1832
|
-
const node = $pos.node(i);
|
|
1833
|
-
if (predicate(node)) {
|
|
1834
|
-
return {
|
|
1835
|
-
pos: i > 0 ? $pos.before(i) : 0,
|
|
1836
|
-
start: $pos.start(i),
|
|
1837
|
-
depth: i,
|
|
1838
|
-
node
|
|
1839
|
-
};
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
function findParentNode(predicate) {
|
|
1844
|
-
return (selection) => findParentNodeClosestToPos(selection.$from, predicate);
|
|
1845
|
-
}
|
|
1846
|
-
function getSplittedAttributes(extensionAttributes, typeName, attributes) {
|
|
1847
|
-
return Object.fromEntries(Object.entries(attributes).filter(([name]) => {
|
|
1848
|
-
const extensionAttribute = extensionAttributes.find((item) => {
|
|
1849
|
-
return item.type === typeName && item.name === name;
|
|
1850
|
-
});
|
|
1851
|
-
if (!extensionAttribute) {
|
|
1852
|
-
return false;
|
|
1853
|
-
}
|
|
1854
|
-
return extensionAttribute.attribute.keepOnSplit;
|
|
1855
|
-
}));
|
|
1856
|
-
}
|
|
1857
|
-
function isMarkActive(state, typeOrName, attributes = {}) {
|
|
1858
|
-
const { empty, ranges } = state.selection;
|
|
1859
|
-
const type = typeOrName ? getMarkType(typeOrName, state.schema) : null;
|
|
1860
|
-
if (empty) {
|
|
1861
|
-
return !!(state.storedMarks || state.selection.$from.marks()).filter((mark) => {
|
|
1862
|
-
if (!type) {
|
|
1863
|
-
return true;
|
|
1864
|
-
}
|
|
1865
|
-
return type.name === mark.type.name;
|
|
1866
|
-
}).find((mark) => objectIncludes(mark.attrs, attributes, { strict: false }));
|
|
1867
|
-
}
|
|
1868
|
-
let selectionRange = 0;
|
|
1869
|
-
const markRanges = [];
|
|
1870
|
-
ranges.forEach(({ $from, $to }) => {
|
|
1871
|
-
const from = $from.pos;
|
|
1872
|
-
const to = $to.pos;
|
|
1873
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
1874
|
-
if (!node.isText && !node.marks.length) {
|
|
1875
|
-
return;
|
|
1876
|
-
}
|
|
1877
|
-
const relativeFrom = Math.max(from, pos);
|
|
1878
|
-
const relativeTo = Math.min(to, pos + node.nodeSize);
|
|
1879
|
-
const range2 = relativeTo - relativeFrom;
|
|
1880
|
-
selectionRange += range2;
|
|
1881
|
-
markRanges.push(...node.marks.map((mark) => ({
|
|
1882
|
-
mark,
|
|
1883
|
-
from: relativeFrom,
|
|
1884
|
-
to: relativeTo
|
|
1885
|
-
})));
|
|
1886
|
-
});
|
|
1887
|
-
});
|
|
1888
|
-
if (selectionRange === 0) {
|
|
1889
|
-
return false;
|
|
1890
|
-
}
|
|
1891
|
-
const matchedRange = markRanges.filter((markRange) => {
|
|
1892
|
-
if (!type) {
|
|
1893
|
-
return true;
|
|
1894
|
-
}
|
|
1895
|
-
return type.name === markRange.mark.type.name;
|
|
1896
|
-
}).filter((markRange) => objectIncludes(markRange.mark.attrs, attributes, { strict: false })).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
1897
|
-
const excludedRange = markRanges.filter((markRange) => {
|
|
1898
|
-
if (!type) {
|
|
1899
|
-
return true;
|
|
1900
|
-
}
|
|
1901
|
-
return markRange.mark.type !== type && markRange.mark.type.excludes(type);
|
|
1902
|
-
}).reduce((sum, markRange) => sum + markRange.to - markRange.from, 0);
|
|
1903
|
-
const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange;
|
|
1904
|
-
return range >= selectionRange;
|
|
1905
|
-
}
|
|
1906
|
-
function isList(name, extensions) {
|
|
1907
|
-
const { nodeExtensions } = splitExtensions(extensions);
|
|
1908
|
-
const extension = nodeExtensions.find((item) => item.name === name);
|
|
1909
|
-
if (!extension) {
|
|
1910
|
-
return false;
|
|
1911
|
-
}
|
|
1912
|
-
const context = {
|
|
1913
|
-
name: extension.name,
|
|
1914
|
-
options: extension.options,
|
|
1915
|
-
storage: extension.storage
|
|
1916
|
-
};
|
|
1917
|
-
const group = callOrReturn(getExtensionField(extension, "group", context));
|
|
1918
|
-
if (typeof group !== "string") {
|
|
1919
|
-
return false;
|
|
1920
|
-
}
|
|
1921
|
-
return group.split(" ").includes("list");
|
|
1922
|
-
}
|
|
1923
|
-
function isNodeEmpty(node, { checkChildren = true, ignoreWhitespace = false } = {}) {
|
|
1924
|
-
var _a;
|
|
1925
|
-
if (ignoreWhitespace) {
|
|
1926
|
-
if (node.type.name === "hardBreak") {
|
|
1927
|
-
return true;
|
|
1928
|
-
}
|
|
1929
|
-
if (node.isText) {
|
|
1930
|
-
return /^\s*$/m.test((_a = node.text) !== null && _a !== void 0 ? _a : "");
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
if (node.isText) {
|
|
1934
|
-
return !node.text;
|
|
1935
|
-
}
|
|
1936
|
-
if (node.isAtom || node.isLeaf) {
|
|
1937
|
-
return false;
|
|
1938
|
-
}
|
|
1939
|
-
if (node.content.childCount === 0) {
|
|
1940
|
-
return true;
|
|
1941
|
-
}
|
|
1942
|
-
if (checkChildren) {
|
|
1943
|
-
let isContentEmpty = true;
|
|
1944
|
-
node.content.forEach((childNode) => {
|
|
1945
|
-
if (isContentEmpty === false) {
|
|
1946
|
-
return;
|
|
1947
|
-
}
|
|
1948
|
-
if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {
|
|
1949
|
-
isContentEmpty = false;
|
|
1950
|
-
}
|
|
1951
|
-
});
|
|
1952
|
-
return isContentEmpty;
|
|
1953
|
-
}
|
|
1954
|
-
return false;
|
|
1955
|
-
}
|
|
1956
|
-
function canSetMark(state, tr, newMarkType) {
|
|
1957
|
-
var _a;
|
|
1958
|
-
const { selection } = tr;
|
|
1959
|
-
let cursor = null;
|
|
1960
|
-
if (isTextSelection(selection)) {
|
|
1961
|
-
cursor = selection.$cursor;
|
|
1962
|
-
}
|
|
1963
|
-
if (cursor) {
|
|
1964
|
-
const currentMarks = (_a = state.storedMarks) !== null && _a !== void 0 ? _a : cursor.marks();
|
|
1965
|
-
return !!newMarkType.isInSet(currentMarks) || !currentMarks.some((mark) => mark.type.excludes(newMarkType));
|
|
1966
|
-
}
|
|
1967
|
-
const { ranges } = selection;
|
|
1968
|
-
return ranges.some(({ $from, $to }) => {
|
|
1969
|
-
let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false;
|
|
1970
|
-
state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
|
|
1971
|
-
if (someNodeSupportsMark) {
|
|
1972
|
-
return false;
|
|
1973
|
-
}
|
|
1974
|
-
if (node.isInline) {
|
|
1975
|
-
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType);
|
|
1976
|
-
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some((otherMark) => otherMark.type.excludes(newMarkType));
|
|
1977
|
-
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType;
|
|
1978
|
-
}
|
|
1979
|
-
return !someNodeSupportsMark;
|
|
1980
|
-
});
|
|
1981
|
-
return someNodeSupportsMark;
|
|
1982
|
-
});
|
|
1983
|
-
}
|
|
1984
|
-
var setMark = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
1985
|
-
const { selection } = tr;
|
|
1986
|
-
const { empty, ranges } = selection;
|
|
1987
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
1988
|
-
if (dispatch) {
|
|
1989
|
-
if (empty) {
|
|
1990
|
-
const oldAttributes = getMarkAttributes(state, type);
|
|
1991
|
-
tr.addStoredMark(type.create({
|
|
1992
|
-
...oldAttributes,
|
|
1993
|
-
...attributes
|
|
1994
|
-
}));
|
|
1995
|
-
} else {
|
|
1996
|
-
ranges.forEach((range) => {
|
|
1997
|
-
const from = range.$from.pos;
|
|
1998
|
-
const to = range.$to.pos;
|
|
1999
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2000
|
-
const trimmedFrom = Math.max(pos, from);
|
|
2001
|
-
const trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
2002
|
-
const someHasMark = node.marks.find((mark) => mark.type === type);
|
|
2003
|
-
if (someHasMark) {
|
|
2004
|
-
node.marks.forEach((mark) => {
|
|
2005
|
-
if (type === mark.type) {
|
|
2006
|
-
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
|
2007
|
-
...mark.attrs,
|
|
2008
|
-
...attributes
|
|
2009
|
-
}));
|
|
2010
|
-
}
|
|
2011
|
-
});
|
|
2012
|
-
} else {
|
|
2013
|
-
tr.addMark(trimmedFrom, trimmedTo, type.create(attributes));
|
|
2014
|
-
}
|
|
2015
|
-
});
|
|
2016
|
-
});
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
return canSetMark(state, tr, type);
|
|
2020
|
-
};
|
|
2021
|
-
var setMeta = (key, value) => ({ tr }) => {
|
|
2022
|
-
tr.setMeta(key, value);
|
|
2023
|
-
return true;
|
|
2024
|
-
};
|
|
2025
|
-
var setNode = (typeOrName, attributes = {}) => ({ state, dispatch, chain }) => {
|
|
2026
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2027
|
-
let attributesToCopy;
|
|
2028
|
-
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
|
2029
|
-
attributesToCopy = state.selection.$anchor.parent.attrs;
|
|
2030
|
-
}
|
|
2031
|
-
if (!type.isTextblock) {
|
|
2032
|
-
console.warn('[tiptap warn]: Currently "setNode()" only supports text block nodes.');
|
|
2033
|
-
return false;
|
|
2034
|
-
}
|
|
2035
|
-
return chain().command(({ commands: commands2 }) => {
|
|
2036
|
-
const canSetBlock = setBlockType(type, { ...attributesToCopy, ...attributes })(state);
|
|
2037
|
-
if (canSetBlock) {
|
|
2038
|
-
return true;
|
|
2039
|
-
}
|
|
2040
|
-
return commands2.clearNodes();
|
|
2041
|
-
}).command(({ state: updatedState }) => {
|
|
2042
|
-
return setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch);
|
|
2043
|
-
}).run();
|
|
2044
|
-
};
|
|
2045
|
-
var setNodeSelection = (position) => ({ tr, dispatch }) => {
|
|
2046
|
-
if (dispatch) {
|
|
2047
|
-
const { doc } = tr;
|
|
2048
|
-
const from = minMax(position, 0, doc.content.size);
|
|
2049
|
-
const selection = NodeSelection.create(doc, from);
|
|
2050
|
-
tr.setSelection(selection);
|
|
2051
|
-
}
|
|
2052
|
-
return true;
|
|
2053
|
-
};
|
|
2054
|
-
var setTextSelection = (position) => ({ tr, dispatch }) => {
|
|
2055
|
-
if (dispatch) {
|
|
2056
|
-
const { doc } = tr;
|
|
2057
|
-
const { from, to } = typeof position === "number" ? { from: position, to: position } : position;
|
|
2058
|
-
const minPos = TextSelection.atStart(doc).from;
|
|
2059
|
-
const maxPos = TextSelection.atEnd(doc).to;
|
|
2060
|
-
const resolvedFrom = minMax(from, minPos, maxPos);
|
|
2061
|
-
const resolvedEnd = minMax(to, minPos, maxPos);
|
|
2062
|
-
const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd);
|
|
2063
|
-
tr.setSelection(selection);
|
|
2064
|
-
}
|
|
2065
|
-
return true;
|
|
2066
|
-
};
|
|
2067
|
-
var sinkListItem = (typeOrName) => ({ state, dispatch }) => {
|
|
2068
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2069
|
-
return sinkListItem$1(type)(state, dispatch);
|
|
2070
|
-
};
|
|
2071
|
-
function ensureMarks(state, splittableMarks) {
|
|
2072
|
-
const marks = state.storedMarks || state.selection.$to.parentOffset && state.selection.$from.marks();
|
|
2073
|
-
if (marks) {
|
|
2074
|
-
const filteredMarks = marks.filter((mark) => splittableMarks === null || splittableMarks === void 0 ? void 0 : splittableMarks.includes(mark.type.name));
|
|
2075
|
-
state.tr.ensureMarks(filteredMarks);
|
|
2076
|
-
}
|
|
2077
|
-
}
|
|
2078
|
-
var splitBlock = ({ keepMarks = true } = {}) => ({ tr, state, dispatch, editor }) => {
|
|
2079
|
-
const { selection, doc } = tr;
|
|
2080
|
-
const { $from, $to } = selection;
|
|
2081
|
-
const extensionAttributes = editor.extensionManager.attributes;
|
|
2082
|
-
const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs);
|
|
2083
|
-
if (selection instanceof NodeSelection && selection.node.isBlock) {
|
|
2084
|
-
if (!$from.parentOffset || !canSplit(doc, $from.pos)) {
|
|
2085
|
-
return false;
|
|
2086
|
-
}
|
|
2087
|
-
if (dispatch) {
|
|
2088
|
-
if (keepMarks) {
|
|
2089
|
-
ensureMarks(state, editor.extensionManager.splittableMarks);
|
|
2090
|
-
}
|
|
2091
|
-
tr.split($from.pos).scrollIntoView();
|
|
2092
|
-
}
|
|
2093
|
-
return true;
|
|
2094
|
-
}
|
|
2095
|
-
if (!$from.parent.isBlock) {
|
|
2096
|
-
return false;
|
|
2097
|
-
}
|
|
2098
|
-
const atEnd = $to.parentOffset === $to.parent.content.size;
|
|
2099
|
-
const deflt = $from.depth === 0 ? void 0 : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)));
|
|
2100
|
-
let types = atEnd && deflt ? [
|
|
2101
|
-
{
|
|
2102
|
-
type: deflt,
|
|
2103
|
-
attrs: newAttributes
|
|
2104
|
-
}
|
|
2105
|
-
] : void 0;
|
|
2106
|
-
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types);
|
|
2107
|
-
if (!types && !can && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : void 0)) {
|
|
2108
|
-
can = true;
|
|
2109
|
-
types = deflt ? [
|
|
2110
|
-
{
|
|
2111
|
-
type: deflt,
|
|
2112
|
-
attrs: newAttributes
|
|
2113
|
-
}
|
|
2114
|
-
] : void 0;
|
|
2115
|
-
}
|
|
2116
|
-
if (dispatch) {
|
|
2117
|
-
if (can) {
|
|
2118
|
-
if (selection instanceof TextSelection) {
|
|
2119
|
-
tr.deleteSelection();
|
|
2120
|
-
}
|
|
2121
|
-
tr.split(tr.mapping.map($from.pos), 1, types);
|
|
2122
|
-
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
|
2123
|
-
const first2 = tr.mapping.map($from.before());
|
|
2124
|
-
const $first = tr.doc.resolve(first2);
|
|
2125
|
-
if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {
|
|
2126
|
-
tr.setNodeMarkup(tr.mapping.map($from.before()), deflt);
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
if (keepMarks) {
|
|
2131
|
-
ensureMarks(state, editor.extensionManager.splittableMarks);
|
|
2132
|
-
}
|
|
2133
|
-
tr.scrollIntoView();
|
|
2134
|
-
}
|
|
2135
|
-
return can;
|
|
2136
|
-
};
|
|
2137
|
-
var splitListItem = (typeOrName, overrideAttrs = {}) => ({ tr, state, dispatch, editor }) => {
|
|
2138
|
-
var _a;
|
|
2139
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2140
|
-
const { $from, $to } = state.selection;
|
|
2141
|
-
const node = state.selection.node;
|
|
2142
|
-
if (node && node.isBlock || $from.depth < 2 || !$from.sameParent($to)) {
|
|
2143
|
-
return false;
|
|
2144
|
-
}
|
|
2145
|
-
const grandParent = $from.node(-1);
|
|
2146
|
-
if (grandParent.type !== type) {
|
|
2147
|
-
return false;
|
|
2148
|
-
}
|
|
2149
|
-
const extensionAttributes = editor.extensionManager.attributes;
|
|
2150
|
-
if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {
|
|
2151
|
-
if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {
|
|
2152
|
-
return false;
|
|
2153
|
-
}
|
|
2154
|
-
if (dispatch) {
|
|
2155
|
-
let wrap = Fragment2.empty;
|
|
2156
|
-
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3;
|
|
2157
|
-
for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {
|
|
2158
|
-
wrap = Fragment2.from($from.node(d).copy(wrap));
|
|
2159
|
-
}
|
|
2160
|
-
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3;
|
|
2161
|
-
const newNextTypeAttributes2 = {
|
|
2162
|
-
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
|
2163
|
-
...overrideAttrs
|
|
2164
|
-
};
|
|
2165
|
-
const nextType2 = ((_a = type.contentMatch.defaultType) === null || _a === void 0 ? void 0 : _a.createAndFill(newNextTypeAttributes2)) || void 0;
|
|
2166
|
-
wrap = wrap.append(Fragment2.from(type.createAndFill(null, nextType2) || void 0));
|
|
2167
|
-
const start = $from.before($from.depth - (depthBefore - 1));
|
|
2168
|
-
tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0));
|
|
2169
|
-
let sel = -1;
|
|
2170
|
-
tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {
|
|
2171
|
-
if (sel > -1) {
|
|
2172
|
-
return false;
|
|
2173
|
-
}
|
|
2174
|
-
if (n.isTextblock && n.content.size === 0) {
|
|
2175
|
-
sel = pos + 1;
|
|
2176
|
-
}
|
|
2177
|
-
});
|
|
2178
|
-
if (sel > -1) {
|
|
2179
|
-
tr.setSelection(TextSelection.near(tr.doc.resolve(sel)));
|
|
2180
|
-
}
|
|
2181
|
-
tr.scrollIntoView();
|
|
2182
|
-
}
|
|
2183
|
-
return true;
|
|
2184
|
-
}
|
|
2185
|
-
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null;
|
|
2186
|
-
const newTypeAttributes = {
|
|
2187
|
-
...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),
|
|
2188
|
-
...overrideAttrs
|
|
2189
|
-
};
|
|
2190
|
-
const newNextTypeAttributes = {
|
|
2191
|
-
...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),
|
|
2192
|
-
...overrideAttrs
|
|
2193
|
-
};
|
|
2194
|
-
tr.delete($from.pos, $to.pos);
|
|
2195
|
-
const types = nextType ? [
|
|
2196
|
-
{ type, attrs: newTypeAttributes },
|
|
2197
|
-
{ type: nextType, attrs: newNextTypeAttributes }
|
|
2198
|
-
] : [{ type, attrs: newTypeAttributes }];
|
|
2199
|
-
if (!canSplit(tr.doc, $from.pos, 2)) {
|
|
2200
|
-
return false;
|
|
2201
|
-
}
|
|
2202
|
-
if (dispatch) {
|
|
2203
|
-
const { selection, storedMarks } = state;
|
|
2204
|
-
const { splittableMarks } = editor.extensionManager;
|
|
2205
|
-
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
|
2206
|
-
tr.split($from.pos, 2, types).scrollIntoView();
|
|
2207
|
-
if (!marks || !dispatch) {
|
|
2208
|
-
return true;
|
|
2209
|
-
}
|
|
2210
|
-
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
|
2211
|
-
tr.ensureMarks(filteredMarks);
|
|
2212
|
-
}
|
|
2213
|
-
return true;
|
|
2214
|
-
};
|
|
2215
|
-
var joinListBackwards = (tr, listType) => {
|
|
2216
|
-
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
|
2217
|
-
if (!list) {
|
|
2218
|
-
return true;
|
|
2219
|
-
}
|
|
2220
|
-
const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth);
|
|
2221
|
-
if (before === void 0) {
|
|
2222
|
-
return true;
|
|
2223
|
-
}
|
|
2224
|
-
const nodeBefore = tr.doc.nodeAt(before);
|
|
2225
|
-
const canJoinBackwards = list.node.type === (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.type) && canJoin(tr.doc, list.pos);
|
|
2226
|
-
if (!canJoinBackwards) {
|
|
2227
|
-
return true;
|
|
2228
|
-
}
|
|
2229
|
-
tr.join(list.pos);
|
|
2230
|
-
return true;
|
|
2231
|
-
};
|
|
2232
|
-
var joinListForwards = (tr, listType) => {
|
|
2233
|
-
const list = findParentNode((node) => node.type === listType)(tr.selection);
|
|
2234
|
-
if (!list) {
|
|
2235
|
-
return true;
|
|
2236
|
-
}
|
|
2237
|
-
const after = tr.doc.resolve(list.start).after(list.depth);
|
|
2238
|
-
if (after === void 0) {
|
|
2239
|
-
return true;
|
|
2240
|
-
}
|
|
2241
|
-
const nodeAfter = tr.doc.nodeAt(after);
|
|
2242
|
-
const canJoinForwards = list.node.type === (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.type) && canJoin(tr.doc, after);
|
|
2243
|
-
if (!canJoinForwards) {
|
|
2244
|
-
return true;
|
|
2245
|
-
}
|
|
2246
|
-
tr.join(after);
|
|
2247
|
-
return true;
|
|
2248
|
-
};
|
|
2249
|
-
var toggleList = (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) => ({ editor, tr, state, dispatch, chain, commands: commands2, can }) => {
|
|
2250
|
-
const { extensions, splittableMarks } = editor.extensionManager;
|
|
2251
|
-
const listType = getNodeType(listTypeOrName, state.schema);
|
|
2252
|
-
const itemType = getNodeType(itemTypeOrName, state.schema);
|
|
2253
|
-
const { selection, storedMarks } = state;
|
|
2254
|
-
const { $from, $to } = selection;
|
|
2255
|
-
const range = $from.blockRange($to);
|
|
2256
|
-
const marks = storedMarks || selection.$to.parentOffset && selection.$from.marks();
|
|
2257
|
-
if (!range) {
|
|
2258
|
-
return false;
|
|
2259
|
-
}
|
|
2260
|
-
const parentList = findParentNode((node) => isList(node.type.name, extensions))(selection);
|
|
2261
|
-
if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {
|
|
2262
|
-
if (parentList.node.type === listType) {
|
|
2263
|
-
return commands2.liftListItem(itemType);
|
|
2264
|
-
}
|
|
2265
|
-
if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {
|
|
2266
|
-
return chain().command(() => {
|
|
2267
|
-
tr.setNodeMarkup(parentList.pos, listType);
|
|
2268
|
-
return true;
|
|
2269
|
-
}).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
if (!keepMarks || !marks || !dispatch) {
|
|
2273
|
-
return chain().command(() => {
|
|
2274
|
-
const canWrapInList = can().wrapInList(listType, attributes);
|
|
2275
|
-
if (canWrapInList) {
|
|
2276
|
-
return true;
|
|
2277
|
-
}
|
|
2278
|
-
return commands2.clearNodes();
|
|
2279
|
-
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
2280
|
-
}
|
|
2281
|
-
return chain().command(() => {
|
|
2282
|
-
const canWrapInList = can().wrapInList(listType, attributes);
|
|
2283
|
-
const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name));
|
|
2284
|
-
tr.ensureMarks(filteredMarks);
|
|
2285
|
-
if (canWrapInList) {
|
|
2286
|
-
return true;
|
|
2287
|
-
}
|
|
2288
|
-
return commands2.clearNodes();
|
|
2289
|
-
}).wrapInList(listType, attributes).command(() => joinListBackwards(tr, listType)).command(() => joinListForwards(tr, listType)).run();
|
|
2290
|
-
};
|
|
2291
|
-
var toggleMark = (typeOrName, attributes = {}, options = {}) => ({ state, commands: commands2 }) => {
|
|
2292
|
-
const { extendEmptyMarkRange = false } = options;
|
|
2293
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
2294
|
-
const isActive = isMarkActive(state, type, attributes);
|
|
2295
|
-
if (isActive) {
|
|
2296
|
-
return commands2.unsetMark(type, { extendEmptyMarkRange });
|
|
2297
|
-
}
|
|
2298
|
-
return commands2.setMark(type, attributes);
|
|
2299
|
-
};
|
|
2300
|
-
var toggleNode = (typeOrName, toggleTypeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
|
2301
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2302
|
-
const toggleType = getNodeType(toggleTypeOrName, state.schema);
|
|
2303
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
2304
|
-
let attributesToCopy;
|
|
2305
|
-
if (state.selection.$anchor.sameParent(state.selection.$head)) {
|
|
2306
|
-
attributesToCopy = state.selection.$anchor.parent.attrs;
|
|
2307
|
-
}
|
|
2308
|
-
if (isActive) {
|
|
2309
|
-
return commands2.setNode(toggleType, attributesToCopy);
|
|
2310
|
-
}
|
|
2311
|
-
return commands2.setNode(type, { ...attributesToCopy, ...attributes });
|
|
2312
|
-
};
|
|
2313
|
-
var toggleWrap = (typeOrName, attributes = {}) => ({ state, commands: commands2 }) => {
|
|
2314
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2315
|
-
const isActive = isNodeActive(state, type, attributes);
|
|
2316
|
-
if (isActive) {
|
|
2317
|
-
return commands2.lift(type);
|
|
2318
|
-
}
|
|
2319
|
-
return commands2.wrapIn(type, attributes);
|
|
2320
|
-
};
|
|
2321
|
-
var undoInputRule = () => ({ state, dispatch }) => {
|
|
2322
|
-
const plugins = state.plugins;
|
|
2323
|
-
for (let i = 0; i < plugins.length; i += 1) {
|
|
2324
|
-
const plugin = plugins[i];
|
|
2325
|
-
let undoable;
|
|
2326
|
-
if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {
|
|
2327
|
-
if (dispatch) {
|
|
2328
|
-
const tr = state.tr;
|
|
2329
|
-
const toUndo = undoable.transform;
|
|
2330
|
-
for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {
|
|
2331
|
-
tr.step(toUndo.steps[j].invert(toUndo.docs[j]));
|
|
2332
|
-
}
|
|
2333
|
-
if (undoable.text) {
|
|
2334
|
-
const marks = tr.doc.resolve(undoable.from).marks();
|
|
2335
|
-
tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks));
|
|
2336
|
-
} else {
|
|
2337
|
-
tr.delete(undoable.from, undoable.to);
|
|
2338
|
-
}
|
|
2339
|
-
}
|
|
2340
|
-
return true;
|
|
2341
|
-
}
|
|
2342
|
-
}
|
|
2343
|
-
return false;
|
|
2344
|
-
};
|
|
2345
|
-
var unsetAllMarks = () => ({ tr, dispatch }) => {
|
|
2346
|
-
const { selection } = tr;
|
|
2347
|
-
const { empty, ranges } = selection;
|
|
2348
|
-
if (empty) {
|
|
2349
|
-
return true;
|
|
2350
|
-
}
|
|
2351
|
-
if (dispatch) {
|
|
2352
|
-
ranges.forEach((range) => {
|
|
2353
|
-
tr.removeMark(range.$from.pos, range.$to.pos);
|
|
2354
|
-
});
|
|
2355
|
-
}
|
|
2356
|
-
return true;
|
|
2357
|
-
};
|
|
2358
|
-
var unsetMark = (typeOrName, options = {}) => ({ tr, state, dispatch }) => {
|
|
2359
|
-
var _a;
|
|
2360
|
-
const { extendEmptyMarkRange = false } = options;
|
|
2361
|
-
const { selection } = tr;
|
|
2362
|
-
const type = getMarkType(typeOrName, state.schema);
|
|
2363
|
-
const { $from, empty, ranges } = selection;
|
|
2364
|
-
if (!dispatch) {
|
|
2365
|
-
return true;
|
|
2366
|
-
}
|
|
2367
|
-
if (empty && extendEmptyMarkRange) {
|
|
2368
|
-
let { from, to } = selection;
|
|
2369
|
-
const attrs = (_a = $from.marks().find((mark) => mark.type === type)) === null || _a === void 0 ? void 0 : _a.attrs;
|
|
2370
|
-
const range = getMarkRange($from, type, attrs);
|
|
2371
|
-
if (range) {
|
|
2372
|
-
from = range.from;
|
|
2373
|
-
to = range.to;
|
|
2374
|
-
}
|
|
2375
|
-
tr.removeMark(from, to, type);
|
|
2376
|
-
} else {
|
|
2377
|
-
ranges.forEach((range) => {
|
|
2378
|
-
tr.removeMark(range.$from.pos, range.$to.pos, type);
|
|
2379
|
-
});
|
|
2380
|
-
}
|
|
2381
|
-
tr.removeStoredMark(type);
|
|
2382
|
-
return true;
|
|
2383
|
-
};
|
|
2384
|
-
var updateAttributes = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
|
2385
|
-
let nodeType = null;
|
|
2386
|
-
let markType = null;
|
|
2387
|
-
const schemaType = getSchemaTypeNameByName(typeof typeOrName === "string" ? typeOrName : typeOrName.name, state.schema);
|
|
2388
|
-
if (!schemaType) {
|
|
2389
|
-
return false;
|
|
2390
|
-
}
|
|
2391
|
-
if (schemaType === "node") {
|
|
2392
|
-
nodeType = getNodeType(typeOrName, state.schema);
|
|
2393
|
-
}
|
|
2394
|
-
if (schemaType === "mark") {
|
|
2395
|
-
markType = getMarkType(typeOrName, state.schema);
|
|
2396
|
-
}
|
|
2397
|
-
if (dispatch) {
|
|
2398
|
-
tr.selection.ranges.forEach((range) => {
|
|
2399
|
-
const from = range.$from.pos;
|
|
2400
|
-
const to = range.$to.pos;
|
|
2401
|
-
let lastPos;
|
|
2402
|
-
let lastNode;
|
|
2403
|
-
let trimmedFrom;
|
|
2404
|
-
let trimmedTo;
|
|
2405
|
-
if (tr.selection.empty) {
|
|
2406
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2407
|
-
if (nodeType && nodeType === node.type) {
|
|
2408
|
-
trimmedFrom = Math.max(pos, from);
|
|
2409
|
-
trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
2410
|
-
lastPos = pos;
|
|
2411
|
-
lastNode = node;
|
|
2412
|
-
}
|
|
2413
|
-
});
|
|
2414
|
-
} else {
|
|
2415
|
-
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2416
|
-
if (pos < from && nodeType && nodeType === node.type) {
|
|
2417
|
-
trimmedFrom = Math.max(pos, from);
|
|
2418
|
-
trimmedTo = Math.min(pos + node.nodeSize, to);
|
|
2419
|
-
lastPos = pos;
|
|
2420
|
-
lastNode = node;
|
|
2421
|
-
}
|
|
2422
|
-
if (pos >= from && pos <= to) {
|
|
2423
|
-
if (nodeType && nodeType === node.type) {
|
|
2424
|
-
tr.setNodeMarkup(pos, void 0, {
|
|
2425
|
-
...node.attrs,
|
|
2426
|
-
...attributes
|
|
2427
|
-
});
|
|
2428
|
-
}
|
|
2429
|
-
if (markType && node.marks.length) {
|
|
2430
|
-
node.marks.forEach((mark) => {
|
|
2431
|
-
if (markType === mark.type) {
|
|
2432
|
-
const trimmedFrom2 = Math.max(pos, from);
|
|
2433
|
-
const trimmedTo2 = Math.min(pos + node.nodeSize, to);
|
|
2434
|
-
tr.addMark(trimmedFrom2, trimmedTo2, markType.create({
|
|
2435
|
-
...mark.attrs,
|
|
2436
|
-
...attributes
|
|
2437
|
-
}));
|
|
2438
|
-
}
|
|
2439
|
-
});
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
});
|
|
2443
|
-
}
|
|
2444
|
-
if (lastNode) {
|
|
2445
|
-
if (lastPos !== void 0) {
|
|
2446
|
-
tr.setNodeMarkup(lastPos, void 0, {
|
|
2447
|
-
...lastNode.attrs,
|
|
2448
|
-
...attributes
|
|
2449
|
-
});
|
|
2450
|
-
}
|
|
2451
|
-
if (markType && lastNode.marks.length) {
|
|
2452
|
-
lastNode.marks.forEach((mark) => {
|
|
2453
|
-
if (markType === mark.type) {
|
|
2454
|
-
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
|
2455
|
-
...mark.attrs,
|
|
2456
|
-
...attributes
|
|
2457
|
-
}));
|
|
2458
|
-
}
|
|
2459
|
-
});
|
|
2460
|
-
}
|
|
2461
|
-
}
|
|
2462
|
-
});
|
|
2463
|
-
}
|
|
2464
|
-
return true;
|
|
2465
|
-
};
|
|
2466
|
-
var wrapIn = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2467
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2468
|
-
return wrapIn$1(type, attributes)(state, dispatch);
|
|
2469
|
-
};
|
|
2470
|
-
var wrapInList = (typeOrName, attributes = {}) => ({ state, dispatch }) => {
|
|
2471
|
-
const type = getNodeType(typeOrName, state.schema);
|
|
2472
|
-
return wrapInList$1(type, attributes)(state, dispatch);
|
|
2473
|
-
};
|
|
2474
|
-
var commands = /* @__PURE__ */ Object.freeze({
|
|
2475
|
-
__proto__: null,
|
|
2476
|
-
blur,
|
|
2477
|
-
clearContent,
|
|
2478
|
-
clearNodes,
|
|
2479
|
-
command,
|
|
2480
|
-
createParagraphNear,
|
|
2481
|
-
cut,
|
|
2482
|
-
deleteCurrentNode,
|
|
2483
|
-
deleteNode,
|
|
2484
|
-
deleteRange,
|
|
2485
|
-
deleteSelection,
|
|
2486
|
-
enter,
|
|
2487
|
-
exitCode,
|
|
2488
|
-
extendMarkRange,
|
|
2489
|
-
first,
|
|
2490
|
-
focus,
|
|
2491
|
-
forEach,
|
|
2492
|
-
insertContent,
|
|
2493
|
-
insertContentAt,
|
|
2494
|
-
joinBackward,
|
|
2495
|
-
joinDown,
|
|
2496
|
-
joinForward,
|
|
2497
|
-
joinItemBackward,
|
|
2498
|
-
joinItemForward,
|
|
2499
|
-
joinTextblockBackward,
|
|
2500
|
-
joinTextblockForward,
|
|
2501
|
-
joinUp,
|
|
2502
|
-
keyboardShortcut,
|
|
2503
|
-
lift,
|
|
2504
|
-
liftEmptyBlock,
|
|
2505
|
-
liftListItem,
|
|
2506
|
-
newlineInCode,
|
|
2507
|
-
resetAttributes,
|
|
2508
|
-
scrollIntoView,
|
|
2509
|
-
selectAll,
|
|
2510
|
-
selectNodeBackward,
|
|
2511
|
-
selectNodeForward,
|
|
2512
|
-
selectParentNode,
|
|
2513
|
-
selectTextblockEnd,
|
|
2514
|
-
selectTextblockStart,
|
|
2515
|
-
setContent,
|
|
2516
|
-
setMark,
|
|
2517
|
-
setMeta,
|
|
2518
|
-
setNode,
|
|
2519
|
-
setNodeSelection,
|
|
2520
|
-
setTextSelection,
|
|
2521
|
-
sinkListItem,
|
|
2522
|
-
splitBlock,
|
|
2523
|
-
splitListItem,
|
|
2524
|
-
toggleList,
|
|
2525
|
-
toggleMark,
|
|
2526
|
-
toggleNode,
|
|
2527
|
-
toggleWrap,
|
|
2528
|
-
undoInputRule,
|
|
2529
|
-
unsetAllMarks,
|
|
2530
|
-
unsetMark,
|
|
2531
|
-
updateAttributes,
|
|
2532
|
-
wrapIn,
|
|
2533
|
-
wrapInList
|
|
2534
|
-
});
|
|
2535
|
-
var Commands = Extension.create({
|
|
2536
|
-
name: "commands",
|
|
2537
|
-
addCommands() {
|
|
2538
|
-
return {
|
|
2539
|
-
...commands
|
|
2540
|
-
};
|
|
2541
|
-
}
|
|
2542
|
-
});
|
|
2543
|
-
var Drop = Extension.create({
|
|
2544
|
-
name: "drop",
|
|
2545
|
-
addProseMirrorPlugins() {
|
|
2546
|
-
return [
|
|
2547
|
-
new Plugin({
|
|
2548
|
-
key: new PluginKey("tiptapDrop"),
|
|
2549
|
-
props: {
|
|
2550
|
-
handleDrop: (_, e, slice, moved) => {
|
|
2551
|
-
this.editor.emit("drop", {
|
|
2552
|
-
editor: this.editor,
|
|
2553
|
-
event: e,
|
|
2554
|
-
slice,
|
|
2555
|
-
moved
|
|
2556
|
-
});
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
})
|
|
2560
|
-
];
|
|
2561
|
-
}
|
|
2562
|
-
});
|
|
2563
|
-
var Editable = Extension.create({
|
|
2564
|
-
name: "editable",
|
|
2565
|
-
addProseMirrorPlugins() {
|
|
2566
|
-
return [
|
|
2567
|
-
new Plugin({
|
|
2568
|
-
key: new PluginKey("editable"),
|
|
2569
|
-
props: {
|
|
2570
|
-
editable: () => this.editor.options.editable
|
|
2571
|
-
}
|
|
2572
|
-
})
|
|
2573
|
-
];
|
|
2574
|
-
}
|
|
2575
|
-
});
|
|
2576
|
-
var focusEventsPluginKey = new PluginKey("focusEvents");
|
|
2577
|
-
var FocusEvents = Extension.create({
|
|
2578
|
-
name: "focusEvents",
|
|
2579
|
-
addProseMirrorPlugins() {
|
|
2580
|
-
const { editor } = this;
|
|
2581
|
-
return [
|
|
2582
|
-
new Plugin({
|
|
2583
|
-
key: focusEventsPluginKey,
|
|
2584
|
-
props: {
|
|
2585
|
-
handleDOMEvents: {
|
|
2586
|
-
focus: (view, event) => {
|
|
2587
|
-
editor.isFocused = true;
|
|
2588
|
-
const transaction = editor.state.tr.setMeta("focus", { event }).setMeta("addToHistory", false);
|
|
2589
|
-
view.dispatch(transaction);
|
|
2590
|
-
return false;
|
|
2591
|
-
},
|
|
2592
|
-
blur: (view, event) => {
|
|
2593
|
-
editor.isFocused = false;
|
|
2594
|
-
const transaction = editor.state.tr.setMeta("blur", { event }).setMeta("addToHistory", false);
|
|
2595
|
-
view.dispatch(transaction);
|
|
2596
|
-
return false;
|
|
2597
|
-
}
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
})
|
|
2601
|
-
];
|
|
2602
|
-
}
|
|
2603
|
-
});
|
|
2604
|
-
var Keymap = Extension.create({
|
|
2605
|
-
name: "keymap",
|
|
2606
|
-
addKeyboardShortcuts() {
|
|
2607
|
-
const handleBackspace = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
2608
|
-
() => commands2.undoInputRule(),
|
|
2609
|
-
// maybe convert first text block node to default node
|
|
2610
|
-
() => commands2.command(({ tr }) => {
|
|
2611
|
-
const { selection, doc } = tr;
|
|
2612
|
-
const { empty, $anchor } = selection;
|
|
2613
|
-
const { pos, parent } = $anchor;
|
|
2614
|
-
const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor;
|
|
2615
|
-
const parentIsIsolating = $parentPos.parent.type.spec.isolating;
|
|
2616
|
-
const parentPos = $anchor.pos - $anchor.parentOffset;
|
|
2617
|
-
const isAtStart = parentIsIsolating && $parentPos.parent.childCount === 1 ? parentPos === $anchor.pos : Selection.atStart(doc).from === pos;
|
|
2618
|
-
if (!empty || !parent.type.isTextblock || parent.textContent.length || !isAtStart || isAtStart && $anchor.parent.type.name === "paragraph") {
|
|
2619
|
-
return false;
|
|
2620
|
-
}
|
|
2621
|
-
return commands2.clearNodes();
|
|
2622
|
-
}),
|
|
2623
|
-
() => commands2.deleteSelection(),
|
|
2624
|
-
() => commands2.joinBackward(),
|
|
2625
|
-
() => commands2.selectNodeBackward()
|
|
2626
|
-
]);
|
|
2627
|
-
const handleDelete = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
2628
|
-
() => commands2.deleteSelection(),
|
|
2629
|
-
() => commands2.deleteCurrentNode(),
|
|
2630
|
-
() => commands2.joinForward(),
|
|
2631
|
-
() => commands2.selectNodeForward()
|
|
2632
|
-
]);
|
|
2633
|
-
const handleEnter = () => this.editor.commands.first(({ commands: commands2 }) => [
|
|
2634
|
-
() => commands2.newlineInCode(),
|
|
2635
|
-
() => commands2.createParagraphNear(),
|
|
2636
|
-
() => commands2.liftEmptyBlock(),
|
|
2637
|
-
() => commands2.splitBlock()
|
|
2638
|
-
]);
|
|
2639
|
-
const baseKeymap = {
|
|
2640
|
-
Enter: handleEnter,
|
|
2641
|
-
"Mod-Enter": () => this.editor.commands.exitCode(),
|
|
2642
|
-
Backspace: handleBackspace,
|
|
2643
|
-
"Mod-Backspace": handleBackspace,
|
|
2644
|
-
"Shift-Backspace": handleBackspace,
|
|
2645
|
-
Delete: handleDelete,
|
|
2646
|
-
"Mod-Delete": handleDelete,
|
|
2647
|
-
"Mod-a": () => this.editor.commands.selectAll()
|
|
2648
|
-
};
|
|
2649
|
-
const pcKeymap = {
|
|
2650
|
-
...baseKeymap
|
|
2651
|
-
};
|
|
2652
|
-
const macKeymap = {
|
|
2653
|
-
...baseKeymap,
|
|
2654
|
-
"Ctrl-h": handleBackspace,
|
|
2655
|
-
"Alt-Backspace": handleBackspace,
|
|
2656
|
-
"Ctrl-d": handleDelete,
|
|
2657
|
-
"Ctrl-Alt-Backspace": handleDelete,
|
|
2658
|
-
"Alt-Delete": handleDelete,
|
|
2659
|
-
"Alt-d": handleDelete,
|
|
2660
|
-
"Ctrl-a": () => this.editor.commands.selectTextblockStart(),
|
|
2661
|
-
"Ctrl-e": () => this.editor.commands.selectTextblockEnd()
|
|
2662
|
-
};
|
|
2663
|
-
if (isiOS() || isMacOS()) {
|
|
2664
|
-
return macKeymap;
|
|
2665
|
-
}
|
|
2666
|
-
return pcKeymap;
|
|
2667
|
-
},
|
|
2668
|
-
addProseMirrorPlugins() {
|
|
2669
|
-
return [
|
|
2670
|
-
// With this plugin we check if the whole document was selected and deleted.
|
|
2671
|
-
// In this case we will additionally call `clearNodes()` to convert e.g. a heading
|
|
2672
|
-
// to a paragraph if necessary.
|
|
2673
|
-
// This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
|
|
2674
|
-
// with many other commands.
|
|
2675
|
-
new Plugin({
|
|
2676
|
-
key: new PluginKey("clearDocument"),
|
|
2677
|
-
appendTransaction: (transactions, oldState, newState) => {
|
|
2678
|
-
if (transactions.some((tr2) => tr2.getMeta("composition"))) {
|
|
2679
|
-
return;
|
|
2680
|
-
}
|
|
2681
|
-
const docChanges = transactions.some((transaction) => transaction.docChanged) && !oldState.doc.eq(newState.doc);
|
|
2682
|
-
const ignoreTr = transactions.some((transaction) => transaction.getMeta("preventClearDocument"));
|
|
2683
|
-
if (!docChanges || ignoreTr) {
|
|
2684
|
-
return;
|
|
2685
|
-
}
|
|
2686
|
-
const { empty, from, to } = oldState.selection;
|
|
2687
|
-
const allFrom = Selection.atStart(oldState.doc).from;
|
|
2688
|
-
const allEnd = Selection.atEnd(oldState.doc).to;
|
|
2689
|
-
const allWasSelected = from === allFrom && to === allEnd;
|
|
2690
|
-
if (empty || !allWasSelected) {
|
|
2691
|
-
return;
|
|
2692
|
-
}
|
|
2693
|
-
const isEmpty = isNodeEmpty(newState.doc);
|
|
2694
|
-
if (!isEmpty) {
|
|
2695
|
-
return;
|
|
2696
|
-
}
|
|
2697
|
-
const tr = newState.tr;
|
|
2698
|
-
const state = createChainableState({
|
|
2699
|
-
state: newState,
|
|
2700
|
-
transaction: tr
|
|
2701
|
-
});
|
|
2702
|
-
const { commands: commands2 } = new CommandManager({
|
|
2703
|
-
editor: this.editor,
|
|
2704
|
-
state
|
|
2705
|
-
});
|
|
2706
|
-
commands2.clearNodes();
|
|
2707
|
-
if (!tr.steps.length) {
|
|
2708
|
-
return;
|
|
2709
|
-
}
|
|
2710
|
-
return tr;
|
|
2711
|
-
}
|
|
2712
|
-
})
|
|
2713
|
-
];
|
|
2714
|
-
}
|
|
2715
|
-
});
|
|
2716
|
-
var Paste = Extension.create({
|
|
2717
|
-
name: "paste",
|
|
2718
|
-
addProseMirrorPlugins() {
|
|
2719
|
-
return [
|
|
2720
|
-
new Plugin({
|
|
2721
|
-
key: new PluginKey("tiptapPaste"),
|
|
2722
|
-
props: {
|
|
2723
|
-
handlePaste: (_view, e, slice) => {
|
|
2724
|
-
this.editor.emit("paste", {
|
|
2725
|
-
editor: this.editor,
|
|
2726
|
-
event: e,
|
|
2727
|
-
slice
|
|
2728
|
-
});
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
})
|
|
2732
|
-
];
|
|
2733
|
-
}
|
|
2734
|
-
});
|
|
2735
|
-
var Tabindex = Extension.create({
|
|
2736
|
-
name: "tabindex",
|
|
2737
|
-
addProseMirrorPlugins() {
|
|
2738
|
-
return [
|
|
2739
|
-
new Plugin({
|
|
2740
|
-
key: new PluginKey("tabindex"),
|
|
2741
|
-
props: {
|
|
2742
|
-
attributes: () => this.editor.isEditable ? { tabindex: "0" } : {}
|
|
2743
|
-
}
|
|
2744
|
-
})
|
|
2745
|
-
];
|
|
2746
|
-
}
|
|
2747
|
-
});
|
|
2748
|
-
function textblockTypeInputRule(config) {
|
|
2749
|
-
return new InputRule({
|
|
2750
|
-
find: config.find,
|
|
2751
|
-
handler: ({ state, range, match }) => {
|
|
2752
|
-
const $start = state.doc.resolve(range.from);
|
|
2753
|
-
const attributes = callOrReturn(config.getAttributes, void 0, match) || {};
|
|
2754
|
-
if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {
|
|
2755
|
-
return null;
|
|
2756
|
-
}
|
|
2757
|
-
state.tr.delete(range.from, range.to).setBlockType(range.from, range.from, config.type, attributes);
|
|
2758
|
-
}
|
|
2759
|
-
});
|
|
2760
|
-
}
|
|
2761
|
-
var Node = class _Node {
|
|
2762
|
-
constructor(config = {}) {
|
|
2763
|
-
this.type = "node";
|
|
2764
|
-
this.name = "node";
|
|
2765
|
-
this.parent = null;
|
|
2766
|
-
this.child = null;
|
|
2767
|
-
this.config = {
|
|
2768
|
-
name: this.name,
|
|
2769
|
-
defaultOptions: {}
|
|
2770
|
-
};
|
|
2771
|
-
this.config = {
|
|
2772
|
-
...this.config,
|
|
2773
|
-
...config
|
|
2774
|
-
};
|
|
2775
|
-
this.name = this.config.name;
|
|
2776
|
-
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
2777
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`);
|
|
2778
|
-
}
|
|
2779
|
-
this.options = this.config.defaultOptions;
|
|
2780
|
-
if (this.config.addOptions) {
|
|
2781
|
-
this.options = callOrReturn(getExtensionField(this, "addOptions", {
|
|
2782
|
-
name: this.name
|
|
2783
|
-
}));
|
|
2784
|
-
}
|
|
2785
|
-
this.storage = callOrReturn(getExtensionField(this, "addStorage", {
|
|
2786
|
-
name: this.name,
|
|
2787
|
-
options: this.options
|
|
2788
|
-
})) || {};
|
|
2789
|
-
}
|
|
2790
|
-
static create(config = {}) {
|
|
2791
|
-
return new _Node(config);
|
|
2792
|
-
}
|
|
2793
|
-
configure(options = {}) {
|
|
2794
|
-
const extension = this.extend({
|
|
2795
|
-
...this.config,
|
|
2796
|
-
addOptions: () => {
|
|
2797
|
-
return mergeDeep(this.options, options);
|
|
2798
|
-
}
|
|
2799
|
-
});
|
|
2800
|
-
extension.name = this.name;
|
|
2801
|
-
extension.parent = this.parent;
|
|
2802
|
-
return extension;
|
|
2803
|
-
}
|
|
2804
|
-
extend(extendedConfig = {}) {
|
|
2805
|
-
const extension = new _Node(extendedConfig);
|
|
2806
|
-
extension.parent = this;
|
|
2807
|
-
this.child = extension;
|
|
2808
|
-
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name;
|
|
2809
|
-
if (extendedConfig.defaultOptions && Object.keys(extendedConfig.defaultOptions).length > 0) {
|
|
2810
|
-
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`);
|
|
2811
|
-
}
|
|
2812
|
-
extension.options = callOrReturn(getExtensionField(extension, "addOptions", {
|
|
2813
|
-
name: extension.name
|
|
2814
|
-
}));
|
|
2815
|
-
extension.storage = callOrReturn(getExtensionField(extension, "addStorage", {
|
|
2816
|
-
name: extension.name,
|
|
2817
|
-
options: extension.options
|
|
2818
|
-
}));
|
|
2819
|
-
return extension;
|
|
2820
|
-
}
|
|
2821
|
-
};
|
|
2822
|
-
|
|
2823
|
-
// ../../node_modules/@tiptap/extension-heading/dist/index.js
|
|
2824
|
-
var Heading = Node.create({
|
|
2825
|
-
name: "heading",
|
|
2826
|
-
addOptions() {
|
|
2827
|
-
return {
|
|
2828
|
-
levels: [1, 2, 3, 4, 5, 6],
|
|
2829
|
-
HTMLAttributes: {}
|
|
2830
|
-
};
|
|
2831
|
-
},
|
|
2832
|
-
content: "inline*",
|
|
2833
|
-
group: "block",
|
|
2834
|
-
defining: true,
|
|
2835
|
-
addAttributes() {
|
|
2836
|
-
return {
|
|
2837
|
-
level: {
|
|
2838
|
-
default: 1,
|
|
2839
|
-
rendered: false
|
|
2840
|
-
}
|
|
2841
|
-
};
|
|
2842
|
-
},
|
|
2843
|
-
parseHTML() {
|
|
2844
|
-
return this.options.levels.map((level) => ({
|
|
2845
|
-
tag: `h${level}`,
|
|
2846
|
-
attrs: { level }
|
|
2847
|
-
}));
|
|
2848
|
-
},
|
|
2849
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
2850
|
-
const hasLevel = this.options.levels.includes(node.attrs.level);
|
|
2851
|
-
const level = hasLevel ? node.attrs.level : this.options.levels[0];
|
|
2852
|
-
return [`h${level}`, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
|
|
2853
|
-
},
|
|
2854
|
-
addCommands() {
|
|
2855
|
-
return {
|
|
2856
|
-
setHeading: (attributes) => ({ commands: commands2 }) => {
|
|
2857
|
-
if (!this.options.levels.includes(attributes.level)) {
|
|
2858
|
-
return false;
|
|
2859
|
-
}
|
|
2860
|
-
return commands2.setNode(this.name, attributes);
|
|
2861
|
-
},
|
|
2862
|
-
toggleHeading: (attributes) => ({ commands: commands2 }) => {
|
|
2863
|
-
if (!this.options.levels.includes(attributes.level)) {
|
|
2864
|
-
return false;
|
|
2865
|
-
}
|
|
2866
|
-
return commands2.toggleNode(this.name, "paragraph", attributes);
|
|
2867
|
-
}
|
|
2868
|
-
};
|
|
2869
|
-
},
|
|
2870
|
-
addKeyboardShortcuts() {
|
|
2871
|
-
return this.options.levels.reduce((items, level) => ({
|
|
2872
|
-
...items,
|
|
2873
|
-
...{
|
|
2874
|
-
[`Mod-Alt-${level}`]: () => this.editor.commands.toggleHeading({ level })
|
|
2875
|
-
}
|
|
2876
|
-
}), {});
|
|
2877
|
-
},
|
|
2878
|
-
addInputRules() {
|
|
2879
|
-
return this.options.levels.map((level) => {
|
|
2880
|
-
return textblockTypeInputRule({
|
|
2881
|
-
find: new RegExp(`^(#{${Math.min(...this.options.levels)},${level}})\\s$`),
|
|
2882
|
-
type: this.type,
|
|
2883
|
-
getAttributes: {
|
|
2884
|
-
level
|
|
2885
|
-
}
|
|
2886
|
-
});
|
|
2887
|
-
});
|
|
2888
|
-
}
|
|
2889
|
-
});
|
|
2890
|
-
|
|
2891
|
-
// src/TemplateAnnotation.ts
|
|
2892
|
-
var HeadingWithTemplate = Heading.extend({
|
|
2893
|
-
addAttributes() {
|
|
2894
|
-
return {
|
|
2895
|
-
...this.parent?.(),
|
|
2896
|
-
dataTemplate: {
|
|
2897
|
-
default: null,
|
|
2898
|
-
parseHTML: (element) => element.getAttribute("data-template") || null,
|
|
2899
|
-
renderHTML: (attributes) => {
|
|
2900
|
-
if (!attributes.dataTemplate) return {};
|
|
2901
|
-
return { "data-template": attributes.dataTemplate };
|
|
2902
|
-
}
|
|
2903
|
-
},
|
|
2904
|
-
dataTemplateParams: {
|
|
2905
|
-
default: null,
|
|
2906
|
-
parseHTML: (element) => element.getAttribute("data-template-params") || null,
|
|
2907
|
-
renderHTML: (attributes) => {
|
|
2908
|
-
if (!attributes.dataTemplateParams) return {};
|
|
2909
|
-
return { "data-template-params": attributes.dataTemplateParams };
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
};
|
|
2913
|
-
},
|
|
2914
|
-
renderHTML({ node, HTMLAttributes }) {
|
|
2915
|
-
const level = node.attrs.level;
|
|
2916
|
-
const tag = `h${level}`;
|
|
2917
|
-
const templateName = HTMLAttributes["data-template"];
|
|
2918
|
-
if (templateName) {
|
|
2919
|
-
return [
|
|
2920
|
-
tag,
|
|
2921
|
-
HTMLAttributes,
|
|
2922
|
-
["span", { class: "squisq-heading-content" }, 0],
|
|
2923
|
-
[
|
|
2924
|
-
"span",
|
|
2925
|
-
{
|
|
2926
|
-
class: "squisq-template-badge",
|
|
2927
|
-
contenteditable: "false",
|
|
2928
|
-
"data-template": templateName
|
|
2929
|
-
},
|
|
2930
|
-
templateName
|
|
2931
|
-
]
|
|
2932
|
-
];
|
|
2933
|
-
}
|
|
2934
|
-
return [tag, HTMLAttributes, 0];
|
|
2935
|
-
}
|
|
2936
|
-
});
|
|
2937
|
-
|
|
2938
|
-
// src/tiptapBridge.ts
|
|
2939
|
-
var RE_BOLD_STAR = /\*\*(.+?)\*\*/g;
|
|
2940
|
-
var RE_BOLD_UNDER = /__(.+?)__/g;
|
|
2941
|
-
var RE_ITALIC_STAR = /\*(.+?)\*/g;
|
|
2942
|
-
var RE_ITALIC_UNDER = /_(.+?)_/g;
|
|
2943
|
-
var RE_STRIKETHROUGH = /~~(.+?)~~/g;
|
|
2944
|
-
var RE_INLINE_CODE = /`(.+?)`/g;
|
|
2945
|
-
var RE_LINK = /\[(.+?)\]\((.+?)\)/g;
|
|
2946
|
-
var RE_IMAGE = /!\[(.+?)\]\((.+?)\)/g;
|
|
2947
|
-
var RE_STRONG_TAG = /<strong>(.*?)<\/strong>/g;
|
|
2948
|
-
var RE_B_TAG = /<b>(.*?)<\/b>/g;
|
|
2949
|
-
var RE_EM_TAG = /<em>(.*?)<\/em>/g;
|
|
2950
|
-
var RE_I_TAG = /<i>(.*?)<\/i>/g;
|
|
2951
|
-
var RE_S_TAG = /<s>(.*?)<\/s>/g;
|
|
2952
|
-
var RE_DEL_TAG = /<del>(.*?)<\/del>/g;
|
|
2953
|
-
var RE_CODE_TAG = /<code>(.*?)<\/code>/g;
|
|
2954
|
-
var RE_A_TAG = /<a[^>]+href="([^"]*)"[^>]*>(.*?)<\/a>/g;
|
|
2955
|
-
var RE_IMG_TAG = /<img[^>]+alt="([^"]*)"[^>]+src="([^"]*)"[^>]*>/g;
|
|
2956
|
-
var RE_STRIP_TAGS = /<[^>]+>/g;
|
|
2957
|
-
function markdownToTiptap(markdown) {
|
|
2958
|
-
if (!markdown.trim()) return "<p></p>";
|
|
2959
|
-
const html = markdown;
|
|
2960
|
-
const lines = html.split("\n");
|
|
2961
|
-
const outputBlocks = [];
|
|
2962
|
-
let inCodeBlock = false;
|
|
2963
|
-
let codeBlockLang = "";
|
|
2964
|
-
let codeBlockLines = [];
|
|
2965
|
-
let inList = false;
|
|
2966
|
-
let listItems = [];
|
|
2967
|
-
let listType = "ul";
|
|
2968
|
-
const flushList = () => {
|
|
2969
|
-
if (inList && listItems.length > 0) {
|
|
2970
|
-
const tag = listType === "ol" ? "ol" : "ul";
|
|
2971
|
-
const attr = listType === "task" ? ' data-type="taskList"' : "";
|
|
2972
|
-
outputBlocks.push(`<${tag}${attr}>${listItems.join("")}</${tag}>`);
|
|
2973
|
-
listItems = [];
|
|
2974
|
-
inList = false;
|
|
2975
|
-
}
|
|
2976
|
-
};
|
|
2977
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2978
|
-
const line = lines[i];
|
|
2979
|
-
if (line.startsWith("```")) {
|
|
2980
|
-
if (!inCodeBlock) {
|
|
2981
|
-
flushList();
|
|
2982
|
-
inCodeBlock = true;
|
|
2983
|
-
codeBlockLang = line.slice(3).trim();
|
|
2984
|
-
codeBlockLines = [];
|
|
2985
|
-
continue;
|
|
2986
|
-
} else {
|
|
2987
|
-
const langAttr = codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : "";
|
|
2988
|
-
outputBlocks.push(
|
|
2989
|
-
`<pre><code${langAttr}>${escapeHtml(codeBlockLines.join("\n"))}</code></pre>`
|
|
2990
|
-
);
|
|
2991
|
-
inCodeBlock = false;
|
|
2992
|
-
codeBlockLang = "";
|
|
2993
|
-
codeBlockLines = [];
|
|
2994
|
-
continue;
|
|
2995
|
-
}
|
|
2996
|
-
}
|
|
2997
|
-
if (inCodeBlock) {
|
|
2998
|
-
codeBlockLines.push(line);
|
|
2999
|
-
continue;
|
|
3000
|
-
}
|
|
3001
|
-
if (line.trim() === "") {
|
|
3002
|
-
flushList();
|
|
3003
|
-
continue;
|
|
3004
|
-
}
|
|
3005
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
3006
|
-
if (headingMatch) {
|
|
3007
|
-
flushList();
|
|
3008
|
-
const level = headingMatch[1].length;
|
|
3009
|
-
let text = headingMatch[2];
|
|
3010
|
-
let attrs = "";
|
|
3011
|
-
const annotMatch = text.match(/\s*\{\[([^\]]+)\]\}\s*$/);
|
|
3012
|
-
if (annotMatch) {
|
|
3013
|
-
text = text.slice(0, annotMatch.index).trimEnd();
|
|
3014
|
-
const tokens = annotMatch[1].trim().split(/\s+/);
|
|
3015
|
-
attrs = ` data-template="${escapeHtml(tokens[0])}"`;
|
|
3016
|
-
const params = tokens.slice(1).filter((t) => t.includes("="));
|
|
3017
|
-
if (params.length > 0) {
|
|
3018
|
-
attrs += ` data-template-params="${escapeHtml(params.join(" "))}"`;
|
|
3019
|
-
}
|
|
3020
|
-
}
|
|
3021
|
-
outputBlocks.push(`<h${level}${attrs}>${inlineToHtml(text)}</h${level}>`);
|
|
3022
|
-
continue;
|
|
3023
|
-
}
|
|
3024
|
-
if (/^(---|\*\*\*|___)(\s*)$/.test(line.trim())) {
|
|
3025
|
-
flushList();
|
|
3026
|
-
outputBlocks.push("<hr>");
|
|
3027
|
-
continue;
|
|
3028
|
-
}
|
|
3029
|
-
if (line.startsWith("> ")) {
|
|
3030
|
-
flushList();
|
|
3031
|
-
outputBlocks.push(`<blockquote><p>${inlineToHtml(line.slice(2))}</p></blockquote>`);
|
|
3032
|
-
continue;
|
|
3033
|
-
}
|
|
3034
|
-
const taskMatch = line.match(/^[-*+]\s+\[([xX ])\]\s+(.+)$/);
|
|
3035
|
-
if (taskMatch) {
|
|
3036
|
-
if (!inList || listType !== "task") {
|
|
3037
|
-
flushList();
|
|
3038
|
-
inList = true;
|
|
3039
|
-
listType = "task";
|
|
3040
|
-
}
|
|
3041
|
-
const checked = taskMatch[1].toLowerCase() === "x" ? ' data-checked="true"' : "";
|
|
3042
|
-
listItems.push(
|
|
3043
|
-
`<li data-type="taskItem"${checked}><label><input type="checkbox"${checked ? " checked" : ""}>${inlineToHtml(taskMatch[2])}</label></li>`
|
|
3044
|
-
);
|
|
3045
|
-
continue;
|
|
3046
|
-
}
|
|
3047
|
-
const ulMatch = line.match(/^[-*+]\s+(.+)$/);
|
|
3048
|
-
if (ulMatch) {
|
|
3049
|
-
if (!inList || listType !== "ul") {
|
|
3050
|
-
flushList();
|
|
3051
|
-
inList = true;
|
|
3052
|
-
listType = "ul";
|
|
3053
|
-
}
|
|
3054
|
-
listItems.push(`<li><p>${inlineToHtml(ulMatch[1])}</p></li>`);
|
|
3055
|
-
continue;
|
|
3056
|
-
}
|
|
3057
|
-
const olMatch = line.match(/^\d+\.\s+(.+)$/);
|
|
3058
|
-
if (olMatch) {
|
|
3059
|
-
if (!inList || listType !== "ol") {
|
|
3060
|
-
flushList();
|
|
3061
|
-
inList = true;
|
|
3062
|
-
listType = "ol";
|
|
3063
|
-
}
|
|
3064
|
-
listItems.push(`<li><p>${inlineToHtml(olMatch[1])}</p></li>`);
|
|
3065
|
-
continue;
|
|
3066
|
-
}
|
|
3067
|
-
flushList();
|
|
3068
|
-
outputBlocks.push(`<p>${inlineToHtml(line)}</p>`);
|
|
3069
|
-
}
|
|
3070
|
-
if (inCodeBlock) {
|
|
3071
|
-
const langAttr = codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : "";
|
|
3072
|
-
outputBlocks.push(
|
|
3073
|
-
`<pre><code${langAttr}>${escapeHtml(codeBlockLines.join("\n"))}</code></pre>`
|
|
3074
|
-
);
|
|
3075
|
-
}
|
|
3076
|
-
flushList();
|
|
3077
|
-
return outputBlocks.join("") || "<p></p>";
|
|
3078
|
-
}
|
|
3079
|
-
function tiptapToMarkdown(html) {
|
|
3080
|
-
if (!html || html === "<p></p>") return "";
|
|
3081
|
-
const lines = [];
|
|
3082
|
-
let remaining = html;
|
|
3083
|
-
while (remaining.length > 0) {
|
|
3084
|
-
const headingMatch = remaining.match(/^<h([1-6])([^>]*)>(.*?)<\/h\1>/s);
|
|
3085
|
-
if (headingMatch) {
|
|
3086
|
-
const level = parseInt(headingMatch[1], 10);
|
|
3087
|
-
const attrs = headingMatch[2];
|
|
3088
|
-
let text = htmlToInline(headingMatch[3]);
|
|
3089
|
-
const tmplMatch = attrs.match(/data-template="([^"]+)"/);
|
|
3090
|
-
if (tmplMatch) {
|
|
3091
|
-
let annotation = tmplMatch[1];
|
|
3092
|
-
const paramsMatch = attrs.match(/data-template-params="([^"]+)"/);
|
|
3093
|
-
if (paramsMatch) {
|
|
3094
|
-
annotation += " " + unescapeHtml(paramsMatch[1]);
|
|
3095
|
-
}
|
|
3096
|
-
text += ` {[${annotation}]}`;
|
|
3097
|
-
}
|
|
3098
|
-
lines.push("#".repeat(level) + " " + text);
|
|
3099
|
-
lines.push("");
|
|
3100
|
-
remaining = remaining.slice(headingMatch[0].length);
|
|
3101
|
-
continue;
|
|
3102
|
-
}
|
|
3103
|
-
const codeMatch = remaining.match(
|
|
3104
|
-
/^<pre><code(?:\s+class="language-([^"]*)")?>(.*?)<\/code><\/pre>/s
|
|
3105
|
-
);
|
|
3106
|
-
if (codeMatch) {
|
|
3107
|
-
const lang = codeMatch[1] || "";
|
|
3108
|
-
const code = unescapeHtml(codeMatch[2]);
|
|
3109
|
-
lines.push("```" + lang);
|
|
3110
|
-
lines.push(code);
|
|
3111
|
-
lines.push("```");
|
|
3112
|
-
lines.push("");
|
|
3113
|
-
remaining = remaining.slice(codeMatch[0].length);
|
|
3114
|
-
continue;
|
|
3115
|
-
}
|
|
3116
|
-
const bqMatch = remaining.match(/^<blockquote>(.*?)<\/blockquote>/s);
|
|
3117
|
-
if (bqMatch) {
|
|
3118
|
-
const inner = htmlToInline(bqMatch[1].replace(/<\/?p>/g, ""));
|
|
3119
|
-
lines.push("> " + inner);
|
|
3120
|
-
lines.push("");
|
|
3121
|
-
remaining = remaining.slice(bqMatch[0].length);
|
|
3122
|
-
continue;
|
|
3123
|
-
}
|
|
3124
|
-
if (remaining.startsWith("<hr>") || remaining.startsWith("<hr/>") || remaining.startsWith("<hr />")) {
|
|
3125
|
-
const hrMatch = remaining.match(/^<hr\s*\/?>/);
|
|
3126
|
-
lines.push("---");
|
|
3127
|
-
lines.push("");
|
|
3128
|
-
remaining = remaining.slice(hrMatch[0].length);
|
|
3129
|
-
continue;
|
|
3130
|
-
}
|
|
3131
|
-
const taskListMatch = remaining.match(/^<ul[^>]*data-type="taskList"[^>]*>(.*?)<\/ul>/s);
|
|
3132
|
-
if (taskListMatch) {
|
|
3133
|
-
const items = taskListMatch[1].matchAll(
|
|
3134
|
-
/<li[^>]*data-type="taskItem"[^>]*(data-checked="true")?[^>]*>.*?<\/li>/gs
|
|
3135
|
-
);
|
|
3136
|
-
for (const item of items) {
|
|
3137
|
-
const checked = item[0].includes('data-checked="true"') || item[0].includes("checked");
|
|
3138
|
-
const textMatch2 = item[0].match(/<label>.*?<\/label>|<p>(.*?)<\/p>/s);
|
|
3139
|
-
const text = textMatch2 ? htmlToInline(textMatch2[0].replace(/<[^>]+>/g, "")) : "";
|
|
3140
|
-
lines.push(`- [${checked ? "x" : " "}] ${text}`);
|
|
3141
|
-
}
|
|
3142
|
-
lines.push("");
|
|
3143
|
-
remaining = remaining.slice(taskListMatch[0].length);
|
|
3144
|
-
continue;
|
|
3145
|
-
}
|
|
3146
|
-
const ulMatch = remaining.match(/^<ul>(.*?)<\/ul>/s);
|
|
3147
|
-
if (ulMatch) {
|
|
3148
|
-
const items = ulMatch[1].matchAll(/<li>(.*?)<\/li>/gs);
|
|
3149
|
-
for (const item of items) {
|
|
3150
|
-
lines.push("- " + htmlToInline(item[1].replace(/<\/?p>/g, "")));
|
|
3151
|
-
}
|
|
3152
|
-
lines.push("");
|
|
3153
|
-
remaining = remaining.slice(ulMatch[0].length);
|
|
3154
|
-
continue;
|
|
3155
|
-
}
|
|
3156
|
-
const olMatch = remaining.match(/^<ol[^>]*>(.*?)<\/ol>/s);
|
|
3157
|
-
if (olMatch) {
|
|
3158
|
-
const items = [...olMatch[1].matchAll(/<li>(.*?)<\/li>/gs)];
|
|
3159
|
-
items.forEach((item, idx) => {
|
|
3160
|
-
lines.push(`${idx + 1}. ` + htmlToInline(item[1].replace(/<\/?p>/g, "")));
|
|
3161
|
-
});
|
|
3162
|
-
lines.push("");
|
|
3163
|
-
remaining = remaining.slice(olMatch[0].length);
|
|
3164
|
-
continue;
|
|
3165
|
-
}
|
|
3166
|
-
const pMatch = remaining.match(/^<p>(.*?)<\/p>/s);
|
|
3167
|
-
if (pMatch) {
|
|
3168
|
-
const text = htmlToInline(pMatch[1]);
|
|
3169
|
-
if (text.trim()) {
|
|
3170
|
-
lines.push(text);
|
|
3171
|
-
lines.push("");
|
|
3172
|
-
}
|
|
3173
|
-
remaining = remaining.slice(pMatch[0].length);
|
|
3174
|
-
continue;
|
|
3175
|
-
}
|
|
3176
|
-
const skipMatch = remaining.match(/^(<[^>]+>|\s+)/);
|
|
3177
|
-
if (skipMatch) {
|
|
3178
|
-
remaining = remaining.slice(skipMatch[0].length);
|
|
3179
|
-
continue;
|
|
3180
|
-
}
|
|
3181
|
-
const textMatch = remaining.match(/^([^<]+)/);
|
|
3182
|
-
if (textMatch) {
|
|
3183
|
-
lines.push(unescapeHtml(textMatch[1]));
|
|
3184
|
-
remaining = remaining.slice(textMatch[0].length);
|
|
3185
|
-
continue;
|
|
3186
|
-
}
|
|
3187
|
-
remaining = remaining.slice(1);
|
|
3188
|
-
}
|
|
3189
|
-
return lines.join("\n").replace(/\n{3,}/g, "\n\n").trim() + "\n";
|
|
3190
|
-
}
|
|
3191
|
-
function escapeHtml(text) {
|
|
3192
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3193
|
-
}
|
|
3194
|
-
function unescapeHtml(text) {
|
|
3195
|
-
return text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&");
|
|
3196
|
-
}
|
|
3197
|
-
function inlineToHtml(text) {
|
|
3198
|
-
let result = escapeHtml(text);
|
|
3199
|
-
result = result.replace(RE_BOLD_STAR, "<strong>$1</strong>");
|
|
3200
|
-
result = result.replace(RE_BOLD_UNDER, "<strong>$1</strong>");
|
|
3201
|
-
result = result.replace(RE_ITALIC_STAR, "<em>$1</em>");
|
|
3202
|
-
result = result.replace(RE_ITALIC_UNDER, "<em>$1</em>");
|
|
3203
|
-
result = result.replace(RE_STRIKETHROUGH, "<s>$1</s>");
|
|
3204
|
-
result = result.replace(RE_INLINE_CODE, "<code>$1</code>");
|
|
3205
|
-
result = result.replace(RE_LINK, '<a href="$2">$1</a>');
|
|
3206
|
-
result = result.replace(RE_IMAGE, '<img alt="$1" src="$2">');
|
|
3207
|
-
return result;
|
|
3208
|
-
}
|
|
3209
|
-
function htmlToInline(html) {
|
|
3210
|
-
let result = html;
|
|
3211
|
-
result = result.replace(RE_STRONG_TAG, "**$1**");
|
|
3212
|
-
result = result.replace(RE_B_TAG, "**$1**");
|
|
3213
|
-
result = result.replace(RE_EM_TAG, "*$1*");
|
|
3214
|
-
result = result.replace(RE_I_TAG, "*$1*");
|
|
3215
|
-
result = result.replace(RE_S_TAG, "~~$1~~");
|
|
3216
|
-
result = result.replace(RE_DEL_TAG, "~~$1~~");
|
|
3217
|
-
result = result.replace(RE_CODE_TAG, "`$1`");
|
|
3218
|
-
result = result.replace(RE_A_TAG, "[$2]($1)");
|
|
3219
|
-
result = result.replace(RE_IMG_TAG, "");
|
|
3220
|
-
result = result.replace(RE_STRIP_TAGS, "");
|
|
3221
|
-
return unescapeHtml(result);
|
|
3222
|
-
}
|
|
3223
|
-
|
|
3224
|
-
// src/WysiwygEditor.tsx
|
|
3225
|
-
import { useEditor as useEditor2 } from "@tiptap/react";
|
|
3226
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
3227
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
3228
|
-
function stripFrontmatter(md) {
|
|
3229
|
-
const m = md.match(FRONTMATTER_RE);
|
|
3230
|
-
if (!m) return { body: md, frontmatter: "" };
|
|
3231
|
-
return { body: md.slice(m[0].length), frontmatter: m[0] };
|
|
3232
|
-
}
|
|
3233
|
-
function WysiwygEditor({
|
|
3234
|
-
placeholder = "Start typing your markdown\u2026",
|
|
3235
|
-
className
|
|
3236
|
-
}) {
|
|
3237
|
-
const { markdownSource, setMarkdownSource, setTiptapEditor } = useEditorContext();
|
|
3238
|
-
const isExternalUpdate = useRef3(false);
|
|
3239
|
-
const lastSourceRef = useRef3(markdownSource);
|
|
3240
|
-
const frontmatterRef = useRef3(stripFrontmatter(markdownSource).frontmatter);
|
|
3241
|
-
const editor = useEditor({
|
|
3242
|
-
extensions: [
|
|
3243
|
-
StarterKit.configure({
|
|
3244
|
-
// Disable built-in heading; we use HeadingWithTemplate instead
|
|
3245
|
-
heading: false,
|
|
3246
|
-
codeBlock: {
|
|
3247
|
-
HTMLAttributes: { class: "squisq-code-block" }
|
|
3248
|
-
}
|
|
3249
|
-
}),
|
|
3250
|
-
HeadingWithTemplate.configure({ levels: [1, 2, 3, 4, 5, 6] }),
|
|
3251
|
-
Table.configure({ resizable: true }),
|
|
3252
|
-
TableRow,
|
|
3253
|
-
TableCell,
|
|
3254
|
-
TableHeader,
|
|
3255
|
-
TaskList,
|
|
3256
|
-
TaskItem.configure({ nested: true }),
|
|
3257
|
-
Placeholder.configure({ placeholder })
|
|
3258
|
-
],
|
|
3259
|
-
content: markdownToTiptap(stripFrontmatter(markdownSource).body),
|
|
3260
|
-
onUpdate: ({ editor: ed }) => {
|
|
3261
|
-
if (isExternalUpdate.current) return;
|
|
3262
|
-
const html = ed.getHTML();
|
|
3263
|
-
const bodyMd = tiptapToMarkdown(html);
|
|
3264
|
-
const newSource = frontmatterRef.current + bodyMd;
|
|
3265
|
-
lastSourceRef.current = newSource;
|
|
3266
|
-
setMarkdownSource(newSource);
|
|
3267
|
-
},
|
|
3268
|
-
editorProps: {
|
|
3269
|
-
attributes: {
|
|
3270
|
-
class: "squisq-wysiwyg-editor",
|
|
3271
|
-
"data-testid": "wysiwyg-editor"
|
|
3272
|
-
}
|
|
3273
|
-
}
|
|
3274
|
-
});
|
|
3275
|
-
useEffect4(() => {
|
|
3276
|
-
if (editor) {
|
|
3277
|
-
setTiptapEditor(editor);
|
|
3278
|
-
}
|
|
3279
|
-
return () => setTiptapEditor(null);
|
|
3280
|
-
}, [editor, setTiptapEditor]);
|
|
3281
|
-
useEffect4(() => {
|
|
3282
|
-
if (!editor) return;
|
|
3283
|
-
if (markdownSource !== lastSourceRef.current) {
|
|
3284
|
-
isExternalUpdate.current = true;
|
|
3285
|
-
const { body, frontmatter } = stripFrontmatter(markdownSource);
|
|
3286
|
-
frontmatterRef.current = frontmatter;
|
|
3287
|
-
const content = markdownToTiptap(body);
|
|
3288
|
-
editor.commands.setContent(content);
|
|
3289
|
-
lastSourceRef.current = markdownSource;
|
|
3290
|
-
isExternalUpdate.current = false;
|
|
3291
|
-
}
|
|
3292
|
-
}, [markdownSource, editor]);
|
|
3293
|
-
return /* @__PURE__ */ jsx5(
|
|
3294
|
-
"div",
|
|
3295
|
-
{
|
|
3296
|
-
className,
|
|
3297
|
-
style: { width: "100%", height: "100%", overflow: "auto" },
|
|
3298
|
-
"data-testid": "wysiwyg-container",
|
|
3299
|
-
children: /* @__PURE__ */ jsx5(EditorContent, { editor, style: { height: "100%" } })
|
|
3300
|
-
}
|
|
3301
|
-
);
|
|
3302
|
-
}
|
|
3303
|
-
|
|
3304
|
-
// src/PreviewPanel.tsx
|
|
3305
|
-
import { useMemo as useMemo3, useState as useState2, useEffect as useEffect5 } from "react";
|
|
3306
|
-
import { DocPlayer, LinearDocView } from "@bendyline/squisq-react";
|
|
3307
|
-
import { flattenBlocks } from "@bendyline/squisq/doc";
|
|
3308
|
-
import { hasTemplate } from "@bendyline/squisq/doc";
|
|
3309
|
-
import { extractPlainText } from "@bendyline/squisq/markdown";
|
|
3310
|
-
import { VIEWPORT_PRESETS } from "@bendyline/squisq/schemas";
|
|
3311
|
-
import { getThemeSummaries, resolveTheme } from "@bendyline/squisq/schemas";
|
|
3312
|
-
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
3313
|
-
function extractBodyText(contents) {
|
|
3314
|
-
if (!contents || contents.length === 0) return "";
|
|
3315
|
-
const parts = [];
|
|
3316
|
-
for (const node of contents) {
|
|
3317
|
-
parts.push(extractPlainText(node));
|
|
3318
|
-
}
|
|
3319
|
-
return parts.join("\n").trim();
|
|
3320
|
-
}
|
|
3321
|
-
function extractListItems(contents) {
|
|
3322
|
-
if (!contents) return [];
|
|
3323
|
-
const items = [];
|
|
3324
|
-
for (const node of contents) {
|
|
3325
|
-
if (node.type === "list") {
|
|
3326
|
-
for (const item of node.children) {
|
|
3327
|
-
const text = extractPlainText(item).trim();
|
|
3328
|
-
if (text) items.push(text);
|
|
3329
|
-
}
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
return items;
|
|
3333
|
-
}
|
|
3334
|
-
function getTemplateDefaults(templateName, headingText, block) {
|
|
3335
|
-
const body = extractBodyText(block.contents);
|
|
3336
|
-
switch (templateName) {
|
|
3337
|
-
case "statHighlight":
|
|
3338
|
-
return {
|
|
3339
|
-
stat: headingText,
|
|
3340
|
-
description: body || headingText
|
|
3341
|
-
};
|
|
3342
|
-
case "quoteBlock":
|
|
3343
|
-
case "fullBleedQuote":
|
|
3344
|
-
case "pullQuote":
|
|
3345
|
-
return {
|
|
3346
|
-
quote: body || headingText
|
|
3347
|
-
};
|
|
3348
|
-
case "factCard":
|
|
3349
|
-
return {
|
|
3350
|
-
fact: headingText,
|
|
3351
|
-
explanation: body || headingText
|
|
3352
|
-
};
|
|
3353
|
-
case "comparisonBar":
|
|
3354
|
-
return {
|
|
3355
|
-
leftLabel: "A",
|
|
3356
|
-
leftValue: 60,
|
|
3357
|
-
rightLabel: "B",
|
|
3358
|
-
rightValue: 40
|
|
3359
|
-
};
|
|
3360
|
-
case "listBlock":
|
|
3361
|
-
return {
|
|
3362
|
-
items: extractListItems(block.contents) || ["Item 1", "Item 2", "Item 3"]
|
|
3363
|
-
};
|
|
3364
|
-
case "definitionCard":
|
|
3365
|
-
return {
|
|
3366
|
-
term: headingText,
|
|
3367
|
-
definition: body || headingText
|
|
3368
|
-
};
|
|
3369
|
-
case "dateEvent":
|
|
3370
|
-
return {
|
|
3371
|
-
date: headingText,
|
|
3372
|
-
description: body || headingText
|
|
3373
|
-
};
|
|
3374
|
-
default:
|
|
3375
|
-
return {};
|
|
3376
|
-
}
|
|
3377
|
-
}
|
|
3378
|
-
function blockToSlide(block, index) {
|
|
3379
|
-
const headingText = block.sourceHeading ? extractPlainText(block.sourceHeading) : block.id || `Slide ${index + 1}`;
|
|
3380
|
-
const requestedTemplate = block.template || "sectionHeader";
|
|
3381
|
-
const template = hasTemplate(requestedTemplate) ? requestedTemplate : "sectionHeader";
|
|
3382
|
-
const defaults = getTemplateDefaults(template, headingText, block);
|
|
3383
|
-
return {
|
|
3384
|
-
id: block.id,
|
|
3385
|
-
template,
|
|
3386
|
-
duration: block.duration,
|
|
3387
|
-
audioSegment: 0,
|
|
3388
|
-
transition: index > 0 ? { type: "fade", duration: 0.5 } : void 0,
|
|
3389
|
-
// Provide heading text as title — consumed by sectionHeader, titleBlock, etc.
|
|
3390
|
-
title: headingText,
|
|
3391
|
-
// Template-specific defaults (safe fallbacks for required fields)
|
|
3392
|
-
...defaults,
|
|
3393
|
-
// Spread annotation overrides last so explicit values win
|
|
3394
|
-
...block.templateOverrides
|
|
3395
|
-
};
|
|
3396
|
-
}
|
|
3397
|
-
function buildPreviewDoc(doc) {
|
|
3398
|
-
const flat = flattenBlocks(doc.blocks);
|
|
3399
|
-
const slides = flat.map(blockToSlide);
|
|
3400
|
-
let t = 0;
|
|
3401
|
-
for (const slide of slides) {
|
|
3402
|
-
slide.startTime = t;
|
|
3403
|
-
t += slide.duration;
|
|
3404
|
-
}
|
|
3405
|
-
return {
|
|
3406
|
-
articleId: doc.articleId,
|
|
3407
|
-
duration: t,
|
|
3408
|
-
blocks: slides,
|
|
3409
|
-
audio: {
|
|
3410
|
-
// Synthetic segment — audio will fail to load and DocPlayer will use
|
|
3411
|
-
// its fallback timer to advance currentTime via requestAnimationFrame.
|
|
3412
|
-
segments: t > 0 ? [{ src: "", name: "preview", duration: t, startTime: 0 }] : []
|
|
3413
|
-
}
|
|
3414
|
-
};
|
|
3415
|
-
}
|
|
3416
|
-
var VIEWPORT_OPTIONS = [
|
|
3417
|
-
{ key: "landscape", label: "16:9 Landscape" },
|
|
3418
|
-
{ key: "portrait", label: "9:16 Portrait" },
|
|
3419
|
-
{ key: "square", label: "1:1 Square" },
|
|
3420
|
-
{ key: "standard", label: "4:3 Standard" }
|
|
3421
|
-
];
|
|
3422
|
-
function resolveRenderAs(value) {
|
|
3423
|
-
if (typeof value !== "string") return null;
|
|
3424
|
-
const v = value.trim().toLowerCase();
|
|
3425
|
-
const mapping = {
|
|
3426
|
-
landscape: "landscape",
|
|
3427
|
-
"16:9": "landscape",
|
|
3428
|
-
widescreen: "landscape",
|
|
3429
|
-
portrait: "portrait",
|
|
3430
|
-
"9:16": "portrait",
|
|
3431
|
-
vertical: "portrait",
|
|
3432
|
-
stories: "portrait",
|
|
3433
|
-
square: "square",
|
|
3434
|
-
"1:1": "square",
|
|
3435
|
-
standard: "standard",
|
|
3436
|
-
"4:3": "standard"
|
|
3437
|
-
};
|
|
3438
|
-
return mapping[v] ?? null;
|
|
3439
|
-
}
|
|
3440
|
-
var DISPLAY_MODE_OPTIONS = [
|
|
3441
|
-
{ key: "video", label: "Video" },
|
|
3442
|
-
{ key: "slideshow", label: "Slideshow" },
|
|
3443
|
-
{ key: "linear", label: "Document" }
|
|
3444
|
-
];
|
|
3445
|
-
var THEME_OPTIONS = getThemeSummaries().map((s) => ({ key: s.id, label: s.name }));
|
|
3446
|
-
var VALID_THEME_IDS = new Set(THEME_OPTIONS.map((o) => o.key));
|
|
3447
|
-
function resolveFrontmatterTheme(value) {
|
|
3448
|
-
if (typeof value !== "string") return null;
|
|
3449
|
-
const v = value.trim().toLowerCase();
|
|
3450
|
-
if (VALID_THEME_IDS.has(v)) return v;
|
|
3451
|
-
const normalized = v.replace(/\s+/g, "-");
|
|
3452
|
-
if (VALID_THEME_IDS.has(normalized)) return normalized;
|
|
3453
|
-
return null;
|
|
3454
|
-
}
|
|
3455
|
-
function resolveDisplayMode(value) {
|
|
3456
|
-
if (typeof value !== "string") return null;
|
|
3457
|
-
const v = value.trim().toLowerCase();
|
|
3458
|
-
if (v === "video" || v === "slideshow" || v === "linear") return v;
|
|
3459
|
-
if (v === "slides" || v === "presentation" || v === "deck") return "slideshow";
|
|
3460
|
-
if (v === "document" || v === "scroll" || v === "page") return "linear";
|
|
3461
|
-
return null;
|
|
3462
|
-
}
|
|
3463
|
-
function PreviewPanel({ basePath = "/", className }) {
|
|
3464
|
-
const { doc, parseError, isParsing } = useEditorContext();
|
|
3465
|
-
const frontmatterPreset = useMemo3(() => {
|
|
3466
|
-
if (!doc?.frontmatter) return null;
|
|
3467
|
-
return resolveRenderAs(doc.frontmatter["document-render-as"]);
|
|
3468
|
-
}, [doc?.frontmatter]);
|
|
3469
|
-
const [selectedPreset, setSelectedPreset] = useState2(null);
|
|
3470
|
-
useEffect5(() => {
|
|
3471
|
-
setSelectedPreset(null);
|
|
3472
|
-
}, [frontmatterPreset]);
|
|
3473
|
-
const activePreset = selectedPreset ?? frontmatterPreset ?? "landscape";
|
|
3474
|
-
const activeViewport = VIEWPORT_PRESETS[activePreset];
|
|
3475
|
-
const frontmatterDisplayMode = useMemo3(() => {
|
|
3476
|
-
if (!doc?.frontmatter) return null;
|
|
3477
|
-
return resolveDisplayMode(doc.frontmatter["display-mode"]);
|
|
3478
|
-
}, [doc?.frontmatter]);
|
|
3479
|
-
const [selectedDisplayMode, setSelectedDisplayMode] = useState2(null);
|
|
3480
|
-
useEffect5(() => {
|
|
3481
|
-
setSelectedDisplayMode(null);
|
|
3482
|
-
}, [frontmatterDisplayMode]);
|
|
3483
|
-
const activeDisplayMode = selectedDisplayMode ?? frontmatterDisplayMode ?? "video";
|
|
3484
|
-
const frontmatterThemeId = useMemo3(() => {
|
|
3485
|
-
if (!doc?.frontmatter) return null;
|
|
3486
|
-
return resolveFrontmatterTheme(doc.frontmatter["theme"]);
|
|
3487
|
-
}, [doc?.frontmatter]);
|
|
3488
|
-
const [selectedThemeId, setSelectedThemeId] = useState2(null);
|
|
3489
|
-
useEffect5(() => {
|
|
3490
|
-
setSelectedThemeId(null);
|
|
3491
|
-
}, [frontmatterThemeId]);
|
|
3492
|
-
const activeThemeId = selectedThemeId ?? frontmatterThemeId ?? "documentary";
|
|
3493
|
-
const activeTheme = useMemo3(() => resolveTheme(activeThemeId), [activeThemeId]);
|
|
3494
|
-
const previewDoc = useMemo3(() => {
|
|
3495
|
-
if (!doc || !doc.blocks.length) return null;
|
|
3496
|
-
return buildPreviewDoc(doc);
|
|
3497
|
-
}, [doc]);
|
|
3498
|
-
if (isParsing) {
|
|
3499
|
-
return /* @__PURE__ */ jsx6("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx6("p", { children: "Parsing\u2026" }) });
|
|
3500
|
-
}
|
|
3501
|
-
if (parseError) {
|
|
3502
|
-
return /* @__PURE__ */ jsxs3("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: [
|
|
3503
|
-
/* @__PURE__ */ jsx6("h3", { children: "Parse Error" }),
|
|
3504
|
-
/* @__PURE__ */ jsx6("pre", { children: parseError })
|
|
3505
|
-
] });
|
|
3506
|
-
}
|
|
3507
|
-
if (!previewDoc) {
|
|
3508
|
-
return /* @__PURE__ */ jsx6("div", { className: `squisq-preview-status ${className || ""}`, "data-testid": "preview-panel", children: /* @__PURE__ */ jsx6("p", { children: "No content to preview. Start typing in the editor." }) });
|
|
3509
|
-
}
|
|
3510
|
-
return /* @__PURE__ */ jsxs3(
|
|
3511
|
-
"div",
|
|
3512
|
-
{
|
|
3513
|
-
className: `squisq-preview-container ${className || ""}`,
|
|
3514
|
-
"data-testid": "preview-panel",
|
|
3515
|
-
style: {
|
|
3516
|
-
width: "100%",
|
|
3517
|
-
height: "100%",
|
|
3518
|
-
display: "flex",
|
|
3519
|
-
flexDirection: "column",
|
|
3520
|
-
overflow: "hidden",
|
|
3521
|
-
background: "var(--squisq-bg, #f5f5f5)"
|
|
3522
|
-
},
|
|
3523
|
-
children: [
|
|
3524
|
-
/* @__PURE__ */ jsxs3(
|
|
3525
|
-
"div",
|
|
3526
|
-
{
|
|
3527
|
-
className: "squisq-preview-toolbar",
|
|
3528
|
-
style: {
|
|
3529
|
-
display: "flex",
|
|
3530
|
-
alignItems: "center",
|
|
3531
|
-
gap: "8px",
|
|
3532
|
-
padding: "6px 12px",
|
|
3533
|
-
borderBottom: "1px solid var(--squisq-border, #e0e0e0)",
|
|
3534
|
-
flexShrink: 0,
|
|
3535
|
-
fontSize: "13px"
|
|
3536
|
-
},
|
|
3537
|
-
children: [
|
|
3538
|
-
/* @__PURE__ */ jsx6("label", { htmlFor: "viewport-preset", style: { color: "var(--squisq-text-muted, #6b7280)" }, children: "Format:" }),
|
|
3539
|
-
/* @__PURE__ */ jsx6(
|
|
3540
|
-
"select",
|
|
3541
|
-
{
|
|
3542
|
-
id: "viewport-preset",
|
|
3543
|
-
value: activePreset,
|
|
3544
|
-
onChange: (e) => setSelectedPreset(e.target.value),
|
|
3545
|
-
style: {
|
|
3546
|
-
padding: "3px 8px",
|
|
3547
|
-
borderRadius: "4px",
|
|
3548
|
-
border: "1px solid var(--squisq-border, #d1d5db)",
|
|
3549
|
-
background: "var(--squisq-input-bg, #fff)",
|
|
3550
|
-
color: "var(--squisq-text, #1f2937)",
|
|
3551
|
-
fontSize: "13px",
|
|
3552
|
-
cursor: "pointer"
|
|
3553
|
-
},
|
|
3554
|
-
children: VIEWPORT_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.key, children: opt.label }, opt.key))
|
|
3555
|
-
}
|
|
3556
|
-
),
|
|
3557
|
-
frontmatterPreset && selectedPreset === null && /* @__PURE__ */ jsx6(
|
|
3558
|
-
"span",
|
|
3559
|
-
{
|
|
3560
|
-
style: {
|
|
3561
|
-
fontSize: "11px",
|
|
3562
|
-
color: "var(--squisq-text-muted, #9ca3af)",
|
|
3563
|
-
fontStyle: "italic"
|
|
3564
|
-
},
|
|
3565
|
-
children: "(from frontmatter)"
|
|
3566
|
-
}
|
|
3567
|
-
),
|
|
3568
|
-
/* @__PURE__ */ jsx6(
|
|
3569
|
-
"span",
|
|
3570
|
-
{
|
|
3571
|
-
style: {
|
|
3572
|
-
width: "1px",
|
|
3573
|
-
height: "18px",
|
|
3574
|
-
background: "var(--squisq-border, #d1d5db)",
|
|
3575
|
-
margin: "0 4px"
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
),
|
|
3579
|
-
/* @__PURE__ */ jsx6("label", { htmlFor: "display-mode", style: { color: "var(--squisq-text-muted, #6b7280)" }, children: "Mode:" }),
|
|
3580
|
-
/* @__PURE__ */ jsx6(
|
|
3581
|
-
"select",
|
|
3582
|
-
{
|
|
3583
|
-
id: "display-mode",
|
|
3584
|
-
value: activeDisplayMode,
|
|
3585
|
-
onChange: (e) => setSelectedDisplayMode(e.target.value),
|
|
3586
|
-
style: {
|
|
3587
|
-
padding: "3px 8px",
|
|
3588
|
-
borderRadius: "4px",
|
|
3589
|
-
border: "1px solid var(--squisq-border, #d1d5db)",
|
|
3590
|
-
background: "var(--squisq-input-bg, #fff)",
|
|
3591
|
-
color: "var(--squisq-text, #1f2937)",
|
|
3592
|
-
fontSize: "13px",
|
|
3593
|
-
cursor: "pointer"
|
|
3594
|
-
},
|
|
3595
|
-
children: DISPLAY_MODE_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.key, children: opt.label }, opt.key))
|
|
3596
|
-
}
|
|
3597
|
-
),
|
|
3598
|
-
frontmatterDisplayMode && selectedDisplayMode === null && /* @__PURE__ */ jsx6(
|
|
3599
|
-
"span",
|
|
3600
|
-
{
|
|
3601
|
-
style: {
|
|
3602
|
-
fontSize: "11px",
|
|
3603
|
-
color: "var(--squisq-text-muted, #9ca3af)",
|
|
3604
|
-
fontStyle: "italic"
|
|
3605
|
-
},
|
|
3606
|
-
children: "(from frontmatter)"
|
|
3607
|
-
}
|
|
3608
|
-
),
|
|
3609
|
-
/* @__PURE__ */ jsx6(
|
|
3610
|
-
"span",
|
|
3611
|
-
{
|
|
3612
|
-
style: {
|
|
3613
|
-
width: "1px",
|
|
3614
|
-
height: "18px",
|
|
3615
|
-
background: "var(--squisq-border, #d1d5db)",
|
|
3616
|
-
margin: "0 4px"
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
),
|
|
3620
|
-
/* @__PURE__ */ jsx6("label", { htmlFor: "theme-select", style: { color: "var(--squisq-text-muted, #6b7280)" }, children: "Theme:" }),
|
|
3621
|
-
/* @__PURE__ */ jsx6(
|
|
3622
|
-
"select",
|
|
3623
|
-
{
|
|
3624
|
-
id: "theme-select",
|
|
3625
|
-
value: activeThemeId,
|
|
3626
|
-
onChange: (e) => setSelectedThemeId(e.target.value),
|
|
3627
|
-
style: {
|
|
3628
|
-
padding: "3px 8px",
|
|
3629
|
-
borderRadius: "4px",
|
|
3630
|
-
border: "1px solid var(--squisq-border, #d1d5db)",
|
|
3631
|
-
background: "var(--squisq-input-bg, #fff)",
|
|
3632
|
-
color: "var(--squisq-text, #1f2937)",
|
|
3633
|
-
fontSize: "13px",
|
|
3634
|
-
cursor: "pointer"
|
|
3635
|
-
},
|
|
3636
|
-
children: THEME_OPTIONS.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.key, children: opt.label }, opt.key))
|
|
3637
|
-
}
|
|
3638
|
-
),
|
|
3639
|
-
frontmatterThemeId && selectedThemeId === null && /* @__PURE__ */ jsx6(
|
|
3640
|
-
"span",
|
|
3641
|
-
{
|
|
3642
|
-
style: {
|
|
3643
|
-
fontSize: "11px",
|
|
3644
|
-
color: "var(--squisq-text-muted, #9ca3af)",
|
|
3645
|
-
fontStyle: "italic"
|
|
3646
|
-
},
|
|
3647
|
-
children: "(from frontmatter)"
|
|
3648
|
-
}
|
|
3649
|
-
)
|
|
3650
|
-
]
|
|
3651
|
-
}
|
|
3652
|
-
),
|
|
3653
|
-
/* @__PURE__ */ jsx6(
|
|
3654
|
-
"div",
|
|
3655
|
-
{
|
|
3656
|
-
className: "squisq-preview-player",
|
|
3657
|
-
style: {
|
|
3658
|
-
flex: 1,
|
|
3659
|
-
display: "flex",
|
|
3660
|
-
alignItems: activeDisplayMode === "linear" ? "stretch" : "center",
|
|
3661
|
-
justifyContent: "center",
|
|
3662
|
-
overflow: "hidden",
|
|
3663
|
-
minHeight: 0
|
|
3664
|
-
},
|
|
3665
|
-
children: activeDisplayMode === "linear" ? /* @__PURE__ */ jsx6(
|
|
3666
|
-
LinearDocView,
|
|
3667
|
-
{
|
|
3668
|
-
doc,
|
|
3669
|
-
basePath,
|
|
3670
|
-
viewport: activeViewport,
|
|
3671
|
-
theme: activeTheme
|
|
3672
|
-
}
|
|
3673
|
-
) : /* @__PURE__ */ jsx6(
|
|
3674
|
-
DocPlayer,
|
|
3675
|
-
{
|
|
3676
|
-
script: previewDoc,
|
|
3677
|
-
basePath,
|
|
3678
|
-
showControls: true,
|
|
3679
|
-
muted: true,
|
|
3680
|
-
forceViewport: activeViewport,
|
|
3681
|
-
displayMode: activeDisplayMode,
|
|
3682
|
-
theme: activeTheme
|
|
3683
|
-
}
|
|
3684
|
-
)
|
|
3685
|
-
}
|
|
3686
|
-
)
|
|
3687
|
-
]
|
|
3688
|
-
}
|
|
3689
|
-
);
|
|
3690
|
-
}
|
|
3691
|
-
|
|
3692
|
-
// src/EditorShell.tsx
|
|
3693
|
-
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
3694
|
-
function EditorShell({
|
|
3695
|
-
initialMarkdown = "",
|
|
3696
|
-
initialView = "wysiwyg",
|
|
3697
|
-
articleId = "untitled",
|
|
3698
|
-
basePath = "/",
|
|
3699
|
-
onChange,
|
|
3700
|
-
theme = "light",
|
|
3701
|
-
className,
|
|
3702
|
-
height = "100vh"
|
|
3703
|
-
}) {
|
|
3704
|
-
return /* @__PURE__ */ jsx7(
|
|
3705
|
-
EditorProvider,
|
|
3706
|
-
{
|
|
3707
|
-
initialMarkdown,
|
|
3708
|
-
initialView,
|
|
3709
|
-
articleId,
|
|
3710
|
-
theme,
|
|
3711
|
-
children: /* @__PURE__ */ jsx7(
|
|
3712
|
-
EditorShellInner,
|
|
3713
|
-
{
|
|
3714
|
-
basePath,
|
|
3715
|
-
onChange,
|
|
3716
|
-
className,
|
|
3717
|
-
height
|
|
3718
|
-
}
|
|
3719
|
-
)
|
|
3720
|
-
}
|
|
3721
|
-
);
|
|
3722
|
-
}
|
|
3723
|
-
function EditorShellInner({ basePath, onChange, className, height }) {
|
|
3724
|
-
const { activeView, markdownSource, theme } = useEditorContext();
|
|
3725
|
-
useEffect6(() => {
|
|
3726
|
-
onChange?.(markdownSource);
|
|
3727
|
-
}, [markdownSource, onChange]);
|
|
3728
|
-
useEffect6(() => {
|
|
3729
|
-
const handler = (e) => {
|
|
3730
|
-
if (e.ctrlKey || e.metaKey) {
|
|
3731
|
-
switch (e.key) {
|
|
3732
|
-
case "1":
|
|
3733
|
-
e.preventDefault();
|
|
3734
|
-
document.querySelector('[data-view="wysiwyg"]')?.click();
|
|
3735
|
-
break;
|
|
3736
|
-
case "2":
|
|
3737
|
-
e.preventDefault();
|
|
3738
|
-
document.querySelector('[data-view="raw"]')?.click();
|
|
3739
|
-
break;
|
|
3740
|
-
case "3":
|
|
3741
|
-
e.preventDefault();
|
|
3742
|
-
document.querySelector('[data-view="preview"]')?.click();
|
|
3743
|
-
break;
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
};
|
|
3747
|
-
window.addEventListener("keydown", handler);
|
|
3748
|
-
return () => window.removeEventListener("keydown", handler);
|
|
3749
|
-
}, []);
|
|
3750
|
-
return /* @__PURE__ */ jsxs4(
|
|
3751
|
-
"div",
|
|
3752
|
-
{
|
|
3753
|
-
className: `squisq-editor-shell ${className || ""}`,
|
|
3754
|
-
"data-theme": theme,
|
|
3755
|
-
style: {
|
|
3756
|
-
display: "flex",
|
|
3757
|
-
flexDirection: "column",
|
|
3758
|
-
height,
|
|
3759
|
-
overflow: "hidden"
|
|
3760
|
-
},
|
|
3761
|
-
children: [
|
|
3762
|
-
/* @__PURE__ */ jsx7("div", { className: "squisq-editor-header", children: /* @__PURE__ */ jsx7(Toolbar, {}) }),
|
|
3763
|
-
/* @__PURE__ */ jsxs4(
|
|
3764
|
-
"div",
|
|
3765
|
-
{
|
|
3766
|
-
className: "squisq-editor-content",
|
|
3767
|
-
style: { flex: 1, overflow: "hidden", position: "relative" },
|
|
3768
|
-
children: [
|
|
3769
|
-
activeView === "raw" && /* @__PURE__ */ jsx7(RawEditor, { theme: theme === "dark" ? "vs-dark" : "vs" }),
|
|
3770
|
-
activeView === "wysiwyg" && /* @__PURE__ */ jsx7(WysiwygEditor, {}),
|
|
3771
|
-
activeView === "preview" && /* @__PURE__ */ jsx7(PreviewPanel, { basePath })
|
|
3772
|
-
]
|
|
3773
|
-
}
|
|
3774
|
-
),
|
|
3775
|
-
/* @__PURE__ */ jsx7(StatusBar, {})
|
|
3776
|
-
]
|
|
3777
|
-
}
|
|
3778
|
-
);
|
|
3779
|
-
}
|
|
3780
|
-
|
|
3781
|
-
// src/ViewSwitcher.tsx
|
|
3782
|
-
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
3783
|
-
var VIEWS2 = [
|
|
3784
|
-
{ id: "raw", label: "Raw", shortcut: "\u23181" },
|
|
3785
|
-
{ id: "wysiwyg", label: "Editor", shortcut: "\u23182" },
|
|
3786
|
-
{ id: "preview", label: "Preview", shortcut: "\u23183" }
|
|
3787
|
-
];
|
|
3788
|
-
function ViewSwitcher({ className }) {
|
|
3789
|
-
const { activeView, setActiveView } = useEditorContext();
|
|
3790
|
-
return /* @__PURE__ */ jsx8(
|
|
3791
|
-
"div",
|
|
3792
|
-
{
|
|
3793
|
-
className: `squisq-view-switcher ${className || ""}`,
|
|
3794
|
-
role: "tablist",
|
|
3795
|
-
"aria-label": "Editor view",
|
|
3796
|
-
children: VIEWS2.map((view) => /* @__PURE__ */ jsx8(
|
|
3797
|
-
"button",
|
|
3798
|
-
{
|
|
3799
|
-
role: "tab",
|
|
3800
|
-
"aria-selected": activeView === view.id,
|
|
3801
|
-
className: `squisq-view-tab ${activeView === view.id ? "squisq-view-tab--active" : ""}`,
|
|
3802
|
-
onClick: () => setActiveView(view.id),
|
|
3803
|
-
title: `${view.label} (${view.shortcut})`,
|
|
3804
|
-
children: view.label
|
|
3805
|
-
},
|
|
3806
|
-
view.id
|
|
3807
|
-
))
|
|
3808
|
-
}
|
|
3809
|
-
);
|
|
3810
|
-
}
|
|
3811
|
-
export {
|
|
3812
|
-
EditorProvider,
|
|
3813
|
-
EditorShell,
|
|
3814
|
-
HeadingWithTemplate,
|
|
3815
|
-
PreviewPanel,
|
|
3816
|
-
RawEditor,
|
|
3817
|
-
StatusBar,
|
|
3818
|
-
Toolbar,
|
|
3819
|
-
ViewSwitcher,
|
|
3820
|
-
WysiwygEditor,
|
|
3821
|
-
markdownToTiptap,
|
|
3822
|
-
tiptapToMarkdown,
|
|
3823
|
-
useEditorContext
|
|
3824
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* @bendyline/squisq-editor-react
|
|
3
|
+
*
|
|
4
|
+
* React component library for editing markdown content with three views:
|
|
5
|
+
* - Raw (Monaco) — Full markdown source editing
|
|
6
|
+
* - WYSIWYG (Tiptap) — Rich text editing
|
|
7
|
+
* - Preview — Rendered block preview
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { EditorShell } from '@bendyline/squisq-editor-react';
|
|
12
|
+
* import '@bendyline/squisq-editor-react/styles';
|
|
13
|
+
*
|
|
14
|
+
* function App() {
|
|
15
|
+
* return <EditorShell initialMarkdown="# Hello World" />;
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
// Shell (top-level component)
|
|
20
|
+
export { EditorShell } from './EditorShell.js';
|
|
21
|
+
// Context
|
|
22
|
+
export { EditorProvider, useEditorContext } from './EditorContext.js';
|
|
23
|
+
// Individual editors (for custom layouts)
|
|
24
|
+
export { RawEditor } from './RawEditor.js';
|
|
25
|
+
export { WysiwygEditor } from './WysiwygEditor.js';
|
|
26
|
+
export { PreviewPanel } from './PreviewPanel.js';
|
|
27
|
+
// Chrome (for custom layouts)
|
|
28
|
+
export { ViewSwitcher } from './ViewSwitcher.js';
|
|
29
|
+
export { Toolbar } from './Toolbar.js';
|
|
30
|
+
export { StatusBar } from './StatusBar.js';
|
|
31
|
+
// Bridge utilities
|
|
32
|
+
export { markdownToTiptap, tiptapToMarkdown } from './tiptapBridge.js';
|
|
33
|
+
// Tiptap extension: Heading with template annotation support
|
|
34
|
+
export { HeadingWithTemplate } from './TemplateAnnotation.js';
|
|
3825
35
|
//# sourceMappingURL=index.js.map
|