@moraya/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +344 -0
  2. package/LICENSE +85 -0
  3. package/README.md +82 -0
  4. package/dist/adapters/browser-media-resolver.d.ts +21 -0
  5. package/dist/adapters/browser-media-resolver.js +24 -0
  6. package/dist/adapters/browser-media-resolver.js.map +1 -0
  7. package/dist/commands.d.ts +35 -0
  8. package/dist/commands.js +976 -0
  9. package/dist/commands.js.map +1 -0
  10. package/dist/doc-cache.d.ts +29 -0
  11. package/dist/doc-cache.js +50 -0
  12. package/dist/doc-cache.js.map +1 -0
  13. package/dist/index.d.ts +10 -0
  14. package/dist/index.js +4534 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/markdown.d.ts +46 -0
  17. package/dist/markdown.js +1553 -0
  18. package/dist/markdown.js.map +1 -0
  19. package/dist/plugins/code-block-view.d.ts +52 -0
  20. package/dist/plugins/code-block-view.js +686 -0
  21. package/dist/plugins/code-block-view.js.map +1 -0
  22. package/dist/plugins/cursor-syntax.d.ts +27 -0
  23. package/dist/plugins/cursor-syntax.js +122 -0
  24. package/dist/plugins/cursor-syntax.js.map +1 -0
  25. package/dist/plugins/definition-list.d.ts +23 -0
  26. package/dist/plugins/definition-list.js +12 -0
  27. package/dist/plugins/definition-list.js.map +1 -0
  28. package/dist/plugins/editor-props-plugin.d.ts +36 -0
  29. package/dist/plugins/editor-props-plugin.js +1963 -0
  30. package/dist/plugins/editor-props-plugin.js.map +1 -0
  31. package/dist/plugins/emoji.d.ts +21 -0
  32. package/dist/plugins/emoji.js +42 -0
  33. package/dist/plugins/emoji.js.map +1 -0
  34. package/dist/plugins/enter-handler.d.ts +26 -0
  35. package/dist/plugins/enter-handler.js +193 -0
  36. package/dist/plugins/enter-handler.js.map +1 -0
  37. package/dist/plugins/highlight.d.ts +39 -0
  38. package/dist/plugins/highlight.js +283 -0
  39. package/dist/plugins/highlight.js.map +1 -0
  40. package/dist/plugins/inline-code-convert.d.ts +32 -0
  41. package/dist/plugins/inline-code-convert.js +173 -0
  42. package/dist/plugins/inline-code-convert.js.map +1 -0
  43. package/dist/plugins/link-text-plugin.d.ts +22 -0
  44. package/dist/plugins/link-text-plugin.js +194 -0
  45. package/dist/plugins/link-text-plugin.js.map +1 -0
  46. package/dist/plugins/mermaid-renderer.d.ts +24 -0
  47. package/dist/plugins/mermaid-renderer.js +80 -0
  48. package/dist/plugins/mermaid-renderer.js.map +1 -0
  49. package/dist/schema.d.ts +48 -0
  50. package/dist/schema.js +847 -0
  51. package/dist/schema.js.map +1 -0
  52. package/dist/setup.d.ts +104 -0
  53. package/dist/setup.js +4393 -0
  54. package/dist/setup.js.map +1 -0
  55. package/dist/types.d.ts +107 -0
  56. package/dist/types.js +10 -0
  57. package/dist/types.js.map +1 -0
  58. package/package.json +121 -0
package/dist/index.js ADDED
@@ -0,0 +1,4534 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/plugins/highlight.ts
12
+ var highlight_exports = {};
13
+ __export(highlight_exports, {
14
+ createHighlightPlugin: () => createHighlightPlugin
15
+ });
16
+ import { Plugin as Plugin6, PluginKey as PluginKey6 } from "prosemirror-state";
17
+ import { Decoration as Decoration4, DecorationSet as DecorationSet4 } from "prosemirror-view";
18
+ import hljs from "highlight.js/lib/core";
19
+ import javascript from "highlight.js/lib/languages/javascript";
20
+ import typescript from "highlight.js/lib/languages/typescript";
21
+ import python from "highlight.js/lib/languages/python";
22
+ import rust from "highlight.js/lib/languages/rust";
23
+ import css from "highlight.js/lib/languages/css";
24
+ import xml from "highlight.js/lib/languages/xml";
25
+ import json from "highlight.js/lib/languages/json";
26
+ import bash from "highlight.js/lib/languages/bash";
27
+ import sql from "highlight.js/lib/languages/sql";
28
+ import java from "highlight.js/lib/languages/java";
29
+ import cpp from "highlight.js/lib/languages/cpp";
30
+ import c from "highlight.js/lib/languages/c";
31
+ import go from "highlight.js/lib/languages/go";
32
+ import ruby from "highlight.js/lib/languages/ruby";
33
+ import php from "highlight.js/lib/languages/php";
34
+ import swift from "highlight.js/lib/languages/swift";
35
+ import kotlin from "highlight.js/lib/languages/kotlin";
36
+ import yaml from "highlight.js/lib/languages/yaml";
37
+ import markdown from "highlight.js/lib/languages/markdown";
38
+ import diff from "highlight.js/lib/languages/diff";
39
+ import lua from "highlight.js/lib/languages/lua";
40
+ import scss from "highlight.js/lib/languages/scss";
41
+ import csharp from "highlight.js/lib/languages/csharp";
42
+ import dart from "highlight.js/lib/languages/dart";
43
+ import r from "highlight.js/lib/languages/r";
44
+ import perl from "highlight.js/lib/languages/perl";
45
+ import scala from "highlight.js/lib/languages/scala";
46
+ import objectivec from "highlight.js/lib/languages/objectivec";
47
+ import dockerfile from "highlight.js/lib/languages/dockerfile";
48
+ import ini from "highlight.js/lib/languages/ini";
49
+ import powershell from "highlight.js/lib/languages/powershell";
50
+ import makefile from "highlight.js/lib/languages/makefile";
51
+ import groovy from "highlight.js/lib/languages/groovy";
52
+ import elixir from "highlight.js/lib/languages/elixir";
53
+ import haskell from "highlight.js/lib/languages/haskell";
54
+ import protobuf from "highlight.js/lib/languages/protobuf";
55
+ import graphql from "highlight.js/lib/languages/graphql";
56
+ import latex from "highlight.js/lib/languages/latex";
57
+ import nginx from "highlight.js/lib/languages/nginx";
58
+ import shell from "highlight.js/lib/languages/shell";
59
+ function scopeToClasses(scope) {
60
+ const parts = scope.split(".");
61
+ const classes = [`hljs-${parts[0]}`];
62
+ for (let i = 1; i < parts.length; i++) {
63
+ classes.push(`${parts[i]}_`);
64
+ }
65
+ return classes;
66
+ }
67
+ function flattenHljsTree(nodes, parentClasses = []) {
68
+ const result = [];
69
+ for (const node of nodes) {
70
+ if (typeof node === "string") {
71
+ if (node.length > 0) {
72
+ result.push({ text: node, classes: parentClasses });
73
+ }
74
+ } else {
75
+ const classes = node.scope ? [...parentClasses, ...scopeToClasses(node.scope)] : parentClasses;
76
+ if (node.children) {
77
+ result.push(...flattenHljsTree(node.children, classes));
78
+ }
79
+ }
80
+ }
81
+ return result;
82
+ }
83
+ function hljsCacheKey(language, code2) {
84
+ return language + "\0" + code2;
85
+ }
86
+ function getDecorations(doc2) {
87
+ const decorations = [];
88
+ doc2.descendants((node, pos) => {
89
+ if (node.type.name !== "code_block") return;
90
+ const language = node.attrs.language || "";
91
+ const code2 = node.textContent;
92
+ if (!code2) return;
93
+ if (!language) return;
94
+ if (!hljs.getLanguage(language)) return;
95
+ const cKey = hljsCacheKey(language, code2);
96
+ const blockStart = pos + 1;
97
+ const cachedSpans = hljsCache.get(cKey);
98
+ if (cachedSpans) {
99
+ for (const span of cachedSpans) {
100
+ const from = blockStart + span.relOffset;
101
+ const to = from + span.length;
102
+ if (from < to) {
103
+ decorations.push(Decoration4.inline(from, to, { class: span.classes }));
104
+ }
105
+ }
106
+ return;
107
+ }
108
+ let result;
109
+ try {
110
+ result = hljs.highlight(code2, { language, ignoreIllegals: true });
111
+ } catch {
112
+ return;
113
+ }
114
+ const emitter = result;
115
+ const rootNode = emitter._emitter?.rootNode ?? emitter._emitter?.root;
116
+ if (!rootNode?.children) return;
117
+ const spans = flattenHljsTree(rootNode.children);
118
+ const toCache = [];
119
+ let offset = 0;
120
+ for (const span of spans) {
121
+ const relOffset = offset;
122
+ const length = span.text.length;
123
+ offset += length;
124
+ if (span.classes.length > 0 && length > 0) {
125
+ const classes = span.classes.join(" ");
126
+ toCache.push({ relOffset, length, classes });
127
+ decorations.push(
128
+ Decoration4.inline(blockStart + relOffset, blockStart + relOffset + length, { class: classes })
129
+ );
130
+ }
131
+ }
132
+ if (hljsCache.size >= HLJS_CACHE_MAX) {
133
+ const oldest = hljsCache.keys().next().value;
134
+ if (oldest !== void 0) hljsCache.delete(oldest);
135
+ }
136
+ hljsCache.set(cKey, toCache);
137
+ });
138
+ return DecorationSet4.create(doc2, decorations);
139
+ }
140
+ function createHighlightPlugin() {
141
+ let debounceTimer = null;
142
+ let needsRefresh = false;
143
+ let currentView = null;
144
+ return new Plugin6({
145
+ key: highlightPluginKey,
146
+ state: {
147
+ init(_, state) {
148
+ return getDecorations(state.doc);
149
+ },
150
+ apply(tr, decorationSet, _oldState, newState) {
151
+ if (!tr.docChanged) {
152
+ if (needsRefresh) {
153
+ needsRefresh = false;
154
+ return getDecorations(newState.doc);
155
+ }
156
+ return decorationSet;
157
+ }
158
+ if (tr.getMeta("file-switch")) {
159
+ if (debounceTimer !== null) {
160
+ clearTimeout(debounceTimer);
161
+ debounceTimer = null;
162
+ }
163
+ return getDecorations(newState.doc);
164
+ }
165
+ if (tr.getMeta("full-delete")) {
166
+ if (debounceTimer !== null) {
167
+ clearTimeout(debounceTimer);
168
+ debounceTimer = null;
169
+ }
170
+ needsRefresh = false;
171
+ return getDecorations(newState.doc);
172
+ }
173
+ const mapped = decorationSet.map(tr.mapping, newState.doc);
174
+ let affectsCodeBlock = false;
175
+ const docSize = newState.doc.content.size;
176
+ tr.mapping.maps.forEach((stepMap) => {
177
+ if (affectsCodeBlock) return;
178
+ stepMap.forEach((from, to) => {
179
+ if (affectsCodeBlock) return;
180
+ newState.doc.nodesBetween(
181
+ Math.max(0, from),
182
+ Math.min(to, docSize),
183
+ (node) => {
184
+ if (node.type.name === "code_block") affectsCodeBlock = true;
185
+ return !affectsCodeBlock;
186
+ }
187
+ );
188
+ });
189
+ });
190
+ if (!affectsCodeBlock) return mapped;
191
+ if (debounceTimer !== null) clearTimeout(debounceTimer);
192
+ debounceTimer = setTimeout(() => {
193
+ debounceTimer = null;
194
+ needsRefresh = true;
195
+ try {
196
+ if (currentView && !currentView.isDestroyed) {
197
+ currentView.dispatch(currentView.state.tr.setMeta("highlight-refresh", true));
198
+ }
199
+ } catch {
200
+ }
201
+ }, 300);
202
+ return mapped;
203
+ }
204
+ },
205
+ props: {
206
+ decorations(state) {
207
+ return this.getState(state);
208
+ }
209
+ },
210
+ view(editorView) {
211
+ currentView = editorView;
212
+ return {
213
+ destroy() {
214
+ currentView = null;
215
+ if (debounceTimer !== null) {
216
+ clearTimeout(debounceTimer);
217
+ debounceTimer = null;
218
+ }
219
+ }
220
+ };
221
+ }
222
+ });
223
+ }
224
+ var highlightPluginKey, HLJS_CACHE_MAX, hljsCache;
225
+ var init_highlight = __esm({
226
+ "src/plugins/highlight.ts"() {
227
+ "use strict";
228
+ hljs.registerLanguage("javascript", javascript);
229
+ hljs.registerLanguage("js", javascript);
230
+ hljs.registerLanguage("typescript", typescript);
231
+ hljs.registerLanguage("ts", typescript);
232
+ hljs.registerLanguage("python", python);
233
+ hljs.registerLanguage("py", python);
234
+ hljs.registerLanguage("rust", rust);
235
+ hljs.registerLanguage("rs", rust);
236
+ hljs.registerLanguage("css", css);
237
+ hljs.registerLanguage("html", xml);
238
+ hljs.registerLanguage("xml", xml);
239
+ hljs.registerLanguage("json", json);
240
+ hljs.registerLanguage("bash", bash);
241
+ hljs.registerLanguage("sh", bash);
242
+ hljs.registerLanguage("sql", sql);
243
+ hljs.registerLanguage("java", java);
244
+ hljs.registerLanguage("cpp", cpp);
245
+ hljs.registerLanguage("c", c);
246
+ hljs.registerLanguage("go", go);
247
+ hljs.registerLanguage("ruby", ruby);
248
+ hljs.registerLanguage("rb", ruby);
249
+ hljs.registerLanguage("php", php);
250
+ hljs.registerLanguage("swift", swift);
251
+ hljs.registerLanguage("kotlin", kotlin);
252
+ hljs.registerLanguage("kt", kotlin);
253
+ hljs.registerLanguage("yaml", yaml);
254
+ hljs.registerLanguage("yml", yaml);
255
+ hljs.registerLanguage("markdown", markdown);
256
+ hljs.registerLanguage("md", markdown);
257
+ hljs.registerLanguage("diff", diff);
258
+ hljs.registerLanguage("lua", lua);
259
+ hljs.registerLanguage("scss", scss);
260
+ hljs.registerLanguage("svelte", xml);
261
+ hljs.registerLanguage("jsx", javascript);
262
+ hljs.registerLanguage("tsx", typescript);
263
+ hljs.registerLanguage("csharp", csharp);
264
+ hljs.registerLanguage("cs", csharp);
265
+ hljs.registerLanguage("dart", dart);
266
+ hljs.registerLanguage("r", r);
267
+ hljs.registerLanguage("perl", perl);
268
+ hljs.registerLanguage("pl", perl);
269
+ hljs.registerLanguage("scala", scala);
270
+ hljs.registerLanguage("objectivec", objectivec);
271
+ hljs.registerLanguage("objc", objectivec);
272
+ hljs.registerLanguage("dockerfile", dockerfile);
273
+ hljs.registerLanguage("docker", dockerfile);
274
+ hljs.registerLanguage("ini", ini);
275
+ hljs.registerLanguage("toml", ini);
276
+ hljs.registerLanguage("powershell", powershell);
277
+ hljs.registerLanguage("ps", powershell);
278
+ hljs.registerLanguage("ps1", powershell);
279
+ hljs.registerLanguage("makefile", makefile);
280
+ hljs.registerLanguage("make", makefile);
281
+ hljs.registerLanguage("groovy", groovy);
282
+ hljs.registerLanguage("elixir", elixir);
283
+ hljs.registerLanguage("ex", elixir);
284
+ hljs.registerLanguage("haskell", haskell);
285
+ hljs.registerLanguage("hs", haskell);
286
+ hljs.registerLanguage("protobuf", protobuf);
287
+ hljs.registerLanguage("proto", protobuf);
288
+ hljs.registerLanguage("graphql", graphql);
289
+ hljs.registerLanguage("gql", graphql);
290
+ hljs.registerLanguage("latex", latex);
291
+ hljs.registerLanguage("tex", latex);
292
+ hljs.registerLanguage("nginx", nginx);
293
+ hljs.registerLanguage("nginxconf", nginx);
294
+ hljs.registerLanguage("shell", shell);
295
+ highlightPluginKey = new PluginKey6("moraya-syntax-highlight");
296
+ HLJS_CACHE_MAX = 100;
297
+ hljsCache = /* @__PURE__ */ new Map();
298
+ }
299
+ });
300
+
301
+ // src/plugins/emoji.ts
302
+ var emoji_exports = {};
303
+ __export(emoji_exports, {
304
+ createEmojiPlugin: () => createEmojiPlugin
305
+ });
306
+ import { Plugin as Plugin7, PluginKey as PluginKey7 } from "prosemirror-state";
307
+ import { get as getEmoji } from "node-emoji";
308
+ function createEmojiPlugin() {
309
+ return new Plugin7({
310
+ key: emojiPluginKey,
311
+ props: {
312
+ handleTextInput(view, from, to, text2) {
313
+ if (text2 !== ":") return false;
314
+ const { state } = view;
315
+ const $pos = state.doc.resolve(from);
316
+ const textBefore = $pos.parent.textBetween(
317
+ 0,
318
+ $pos.parentOffset,
319
+ void 0,
320
+ "\uFFFC"
321
+ );
322
+ const lastColon = textBefore.lastIndexOf(":");
323
+ if (lastColon === -1) return false;
324
+ const shortcode = textBefore.slice(lastColon + 1);
325
+ if (!shortcode || !/^[a-zA-Z0-9_+-]+$/.test(shortcode)) return false;
326
+ const emoji = getEmoji(shortcode);
327
+ if (!emoji) return false;
328
+ const openColonOffset = textBefore.length - lastColon;
329
+ const replaceFrom = from - openColonOffset;
330
+ const tr = state.tr.replaceWith(
331
+ replaceFrom,
332
+ to,
333
+ // `to` is where the closing ":" would be inserted
334
+ state.schema.text(emoji)
335
+ );
336
+ view.dispatch(tr);
337
+ return true;
338
+ }
339
+ }
340
+ });
341
+ }
342
+ var emojiPluginKey;
343
+ var init_emoji = __esm({
344
+ "src/plugins/emoji.ts"() {
345
+ "use strict";
346
+ emojiPluginKey = new PluginKey7("moraya-emoji");
347
+ }
348
+ });
349
+
350
+ // src/plugins/mermaid-renderer.ts
351
+ var mermaid_renderer_exports = {};
352
+ __export(mermaid_renderer_exports, {
353
+ ensureMermaidLoaded: () => ensureMermaidLoaded,
354
+ renderMermaid: () => renderMermaid,
355
+ updateMermaidTheme: () => updateMermaidTheme
356
+ });
357
+ function isDark() {
358
+ if (typeof document === "undefined") return false;
359
+ const dt = document.documentElement.getAttribute("data-theme");
360
+ if (dt === "dark") return true;
361
+ if (dt === "light") return false;
362
+ if (typeof window === "undefined" || !window.matchMedia) return false;
363
+ return window.matchMedia("(prefers-color-scheme: dark)").matches;
364
+ }
365
+ function resolveThemeColors() {
366
+ if (typeof document === "undefined" || typeof getComputedStyle === "undefined") {
367
+ return {
368
+ primaryColor: "#4a90d9",
369
+ primaryTextColor: "#333",
370
+ primaryBorderColor: "#ccc",
371
+ lineColor: "#666",
372
+ secondaryColor: "#f5f5f5",
373
+ tertiaryColor: "#eee"
374
+ };
375
+ }
376
+ const s = getComputedStyle(document.documentElement);
377
+ return {
378
+ primaryColor: s.getPropertyValue("--accent-color").trim() || "#4a90d9",
379
+ primaryTextColor: s.getPropertyValue("--text-primary").trim() || "#333",
380
+ primaryBorderColor: s.getPropertyValue("--border-color").trim() || "#ccc",
381
+ lineColor: s.getPropertyValue("--text-secondary").trim() || "#666",
382
+ secondaryColor: s.getPropertyValue("--bg-secondary").trim() || "#f5f5f5",
383
+ tertiaryColor: s.getPropertyValue("--bg-hover").trim() || "#eee"
384
+ };
385
+ }
386
+ async function ensureMermaidLoaded() {
387
+ if (mermaidModule) return;
388
+ if (loadingPromise) return loadingPromise;
389
+ loadingPromise = (async () => {
390
+ const mod = await import(
391
+ /* @vite-ignore */
392
+ "mermaid"
393
+ );
394
+ mermaidModule = mod.default;
395
+ mermaidModule.initialize({
396
+ startOnLoad: false,
397
+ theme: isDark() ? "dark" : "default",
398
+ themeVariables: resolveThemeColors()
399
+ });
400
+ })();
401
+ return loadingPromise;
402
+ }
403
+ async function renderMermaid(code2) {
404
+ await ensureMermaidLoaded();
405
+ const result = new Promise((resolve) => {
406
+ renderQueue = renderQueue.then(async () => {
407
+ const id = `mermaid-${++renderCounter}`;
408
+ try {
409
+ const { svg } = await mermaidModule.render(id, code2);
410
+ resolve({ svg });
411
+ } catch (e) {
412
+ resolve({ error: e instanceof Error ? e.message : "Render failed" });
413
+ }
414
+ });
415
+ });
416
+ return result;
417
+ }
418
+ function updateMermaidTheme() {
419
+ if (!mermaidModule) return;
420
+ mermaidModule.initialize({
421
+ startOnLoad: false,
422
+ theme: isDark() ? "dark" : "default",
423
+ themeVariables: resolveThemeColors()
424
+ });
425
+ }
426
+ var mermaidModule, loadingPromise, renderCounter, renderQueue;
427
+ var init_mermaid_renderer = __esm({
428
+ "src/plugins/mermaid-renderer.ts"() {
429
+ "use strict";
430
+ mermaidModule = null;
431
+ loadingPromise = null;
432
+ renderCounter = 0;
433
+ renderQueue = Promise.resolve();
434
+ }
435
+ });
436
+
437
+ // src/plugins/code-block-view.ts
438
+ var code_block_view_exports = {};
439
+ __export(code_block_view_exports, {
440
+ createCodeBlockNodeViewFactory: () => createCodeBlockNodeViewFactory
441
+ });
442
+ function loadMermaidApi() {
443
+ if (mermaidApi) return Promise.resolve(mermaidApi);
444
+ if (mermaidLoading) return mermaidLoading;
445
+ mermaidLoading = Promise.resolve().then(() => (init_mermaid_renderer(), mermaid_renderer_exports)).then((mod) => {
446
+ mermaidApi = mod;
447
+ return mermaidApi;
448
+ });
449
+ return mermaidLoading;
450
+ }
451
+ function installThemeObserver() {
452
+ if (themeObserverInstalled) return;
453
+ themeObserverInstalled = true;
454
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") return;
455
+ const observer = new MutationObserver(() => {
456
+ if (mermaidApi) mermaidApi.updateMermaidTheme();
457
+ for (const cb of mermaidReRenderCallbacks) cb();
458
+ });
459
+ observer.observe(document.documentElement, {
460
+ attributes: true,
461
+ attributeFilter: ["data-theme"]
462
+ });
463
+ }
464
+ function buildLanguageLists(registry) {
465
+ const rendererLangIds = registry ? new Set(Object.keys(registry.versions)) : /* @__PURE__ */ new Set();
466
+ const rendererPlugins = registry ? Object.keys(registry.versions).sort().map((id) => ({
467
+ id,
468
+ label: id.charAt(0).toUpperCase() + id.slice(1),
469
+ aliases: []
470
+ })) : [];
471
+ const all = [
472
+ ...POPULAR_LANGUAGES,
473
+ ...BASE_OTHER_LANGUAGES,
474
+ ...rendererPlugins
475
+ ].sort((a, b) => a.label.localeCompare(b.label));
476
+ return { popular: POPULAR_LANGUAGES, rendererPlugins, all, rendererLangIds };
477
+ }
478
+ function findLanguageLabel(langId, all) {
479
+ if (!langId) return "text";
480
+ const entry = all.find(
481
+ (l) => l.id === langId || l.aliases.includes(langId)
482
+ );
483
+ return entry ? entry.label : langId;
484
+ }
485
+ async function getAutoDetect() {
486
+ if (hljsAutoDetect) return hljsAutoDetect;
487
+ try {
488
+ const hljs2 = (await import("highlight.js/lib/core")).default;
489
+ hljsAutoDetect = (code2) => {
490
+ if (!code2.trim() || code2.length < 10) return null;
491
+ try {
492
+ const result = hljs2.highlightAuto(code2);
493
+ if (result.language && result.relevance > 5) {
494
+ return result.language;
495
+ }
496
+ } catch {
497
+ }
498
+ return null;
499
+ };
500
+ } catch {
501
+ hljsAutoDetect = () => null;
502
+ }
503
+ return hljsAutoDetect;
504
+ }
505
+ function createLanguagePicker(container, anchor, currentLang, codeContent, langLists, onSelect, onDismiss) {
506
+ const { popular, rendererPlugins, all } = langLists;
507
+ const picker = document.createElement("div");
508
+ picker.className = "code-lang-picker";
509
+ picker.setAttribute("contenteditable", "false");
510
+ picker.addEventListener("mousedown", (e) => {
511
+ e.stopPropagation();
512
+ });
513
+ picker.addEventListener("click", (e) => {
514
+ e.stopPropagation();
515
+ });
516
+ const searchWrap = document.createElement("div");
517
+ searchWrap.className = "code-lang-search";
518
+ const searchInput = document.createElement("input");
519
+ searchInput.type = "text";
520
+ searchInput.className = "code-lang-search-input";
521
+ searchInput.placeholder = "Search language...";
522
+ searchInput.autocomplete = "off";
523
+ searchInput.setAttribute("autocorrect", "off");
524
+ searchInput.setAttribute("autocapitalize", "off");
525
+ searchInput.spellcheck = false;
526
+ searchWrap.appendChild(searchInput);
527
+ picker.appendChild(searchWrap);
528
+ const listEl = document.createElement("div");
529
+ listEl.className = "code-lang-list";
530
+ picker.appendChild(listEl);
531
+ let detectedLang = null;
532
+ function renderList(filter) {
533
+ listEl.innerHTML = "";
534
+ const lowerFilter = filter.toLowerCase();
535
+ const matchesFilter = (entry) => {
536
+ if (!lowerFilter) return true;
537
+ return entry.id.includes(lowerFilter) || entry.label.toLowerCase().includes(lowerFilter) || entry.aliases.some((a) => a.includes(lowerFilter));
538
+ };
539
+ if (detectedLang && !lowerFilter && detectedLang !== currentLang) {
540
+ const label = findLanguageLabel(detectedLang, all);
541
+ const suggestEl = document.createElement("div");
542
+ suggestEl.className = "code-lang-suggestion";
543
+ suggestEl.innerHTML = `<span class="suggestion-icon">\u2726</span> ${label} <span class="suggestion-hint">detected</span>`;
544
+ suggestEl.addEventListener("mousedown", (e) => {
545
+ e.preventDefault();
546
+ e.stopPropagation();
547
+ onSelect(detectedLang);
548
+ destroy();
549
+ });
550
+ listEl.appendChild(suggestEl);
551
+ const divider = document.createElement("div");
552
+ divider.className = "code-lang-divider";
553
+ listEl.appendChild(divider);
554
+ }
555
+ const popularMatches = popular.filter(matchesFilter);
556
+ if (popularMatches.length > 0 && !lowerFilter) {
557
+ const groupLabel = document.createElement("div");
558
+ groupLabel.className = "code-lang-group-label";
559
+ groupLabel.textContent = "Popular";
560
+ listEl.appendChild(groupLabel);
561
+ for (const lang of popularMatches) {
562
+ listEl.appendChild(createOption(lang));
563
+ }
564
+ const rendererIds = new Set(rendererPlugins.map((l) => l.id));
565
+ const others = all.filter(
566
+ (l) => !POPULAR_IDS.has(l.id) && !rendererIds.has(l.id) && matchesFilter(l)
567
+ );
568
+ if (others.length > 0) {
569
+ const divider = document.createElement("div");
570
+ divider.className = "code-lang-divider";
571
+ listEl.appendChild(divider);
572
+ const allLabel = document.createElement("div");
573
+ allLabel.className = "code-lang-group-label";
574
+ allLabel.textContent = "All";
575
+ listEl.appendChild(allLabel);
576
+ for (const lang of others) {
577
+ listEl.appendChild(createOption(lang));
578
+ }
579
+ }
580
+ const rendererMatches = rendererPlugins.filter(matchesFilter);
581
+ if (rendererMatches.length > 0) {
582
+ const divider2 = document.createElement("div");
583
+ divider2.className = "code-lang-divider";
584
+ listEl.appendChild(divider2);
585
+ const rendererLabel = document.createElement("div");
586
+ rendererLabel.className = "code-lang-group-label";
587
+ rendererLabel.textContent = "Renderer Plugins";
588
+ listEl.appendChild(rendererLabel);
589
+ for (const lang of rendererMatches) {
590
+ listEl.appendChild(createOption(lang));
591
+ }
592
+ }
593
+ } else {
594
+ const matches = all.filter(matchesFilter);
595
+ for (const lang of matches) {
596
+ listEl.appendChild(createOption(lang));
597
+ }
598
+ if (matches.length === 0) {
599
+ const empty = document.createElement("div");
600
+ empty.className = "code-lang-empty";
601
+ empty.textContent = "No matches";
602
+ listEl.appendChild(empty);
603
+ }
604
+ }
605
+ }
606
+ function createOption(lang) {
607
+ const option = document.createElement("div");
608
+ option.className = "code-lang-option";
609
+ if (lang.id === currentLang) option.classList.add("selected");
610
+ option.textContent = lang.label;
611
+ option.addEventListener("mousedown", (e) => {
612
+ e.preventDefault();
613
+ e.stopPropagation();
614
+ onSelect(lang.id);
615
+ destroy();
616
+ });
617
+ return option;
618
+ }
619
+ renderList("");
620
+ searchInput.addEventListener("input", () => {
621
+ renderList(searchInput.value);
622
+ });
623
+ searchInput.addEventListener("keydown", (e) => {
624
+ e.stopPropagation();
625
+ if (e.key === "Escape") {
626
+ destroy();
627
+ }
628
+ });
629
+ const pickerHost = container.closest(".editor-wrapper") ?? document.body;
630
+ pickerHost.appendChild(picker);
631
+ (function positionPicker() {
632
+ const rect = anchor.getBoundingClientRect();
633
+ picker.style.position = "fixed";
634
+ picker.style.top = `${rect.bottom + 2}px`;
635
+ picker.style.left = `${rect.left}px`;
636
+ })();
637
+ requestAnimationFrame(() => searchInput.focus());
638
+ getAutoDetect().then((detect) => {
639
+ detectedLang = detect(codeContent);
640
+ if (detectedLang && !searchInput.value) {
641
+ renderList("");
642
+ }
643
+ });
644
+ function handleOutsideClick(e) {
645
+ if (!picker.contains(e.target) && !anchor.contains(e.target)) {
646
+ destroy();
647
+ }
648
+ }
649
+ function handleKeydown(e) {
650
+ if (e.key === "Escape") {
651
+ destroy();
652
+ }
653
+ }
654
+ setTimeout(() => {
655
+ document.addEventListener("mousedown", handleOutsideClick);
656
+ document.addEventListener("keydown", handleKeydown, true);
657
+ }, 0);
658
+ function destroy() {
659
+ document.removeEventListener("mousedown", handleOutsideClick);
660
+ document.removeEventListener("keydown", handleKeydown, true);
661
+ picker.remove();
662
+ onDismiss?.();
663
+ }
664
+ return { destroy };
665
+ }
666
+ function escapeText(str) {
667
+ const d = document.createElement("div");
668
+ d.textContent = str;
669
+ return d.innerHTML;
670
+ }
671
+ function handleCopy(btn, codeEl) {
672
+ const text2 = codeEl.textContent || "";
673
+ navigator.clipboard.writeText(text2).then(() => {
674
+ btn.classList.add("copied");
675
+ btn.title = "Copied!";
676
+ setTimeout(() => {
677
+ btn.classList.remove("copied");
678
+ btn.title = "Copy";
679
+ }, 1500);
680
+ });
681
+ }
682
+ function createCodeBlockNodeViewFactory(opts = {}) {
683
+ const { rendererRegistry } = opts;
684
+ const langLists = buildLanguageLists(rendererRegistry);
685
+ const { rendererLangIds, all: allLanguages } = langLists;
686
+ return function createCodeBlockNodeView(nodeArg, view, getPos) {
687
+ let node = nodeArg;
688
+ const wrapper = document.createElement("div");
689
+ wrapper.className = "code-block-wrapper";
690
+ const toolbar = document.createElement("div");
691
+ toolbar.className = "code-block-toolbar";
692
+ toolbar.setAttribute("contenteditable", "false");
693
+ const langLabel = document.createElement("span");
694
+ langLabel.className = "code-lang-label";
695
+ langLabel.textContent = findLanguageLabel(node.attrs.language || "", allLanguages);
696
+ langLabel.title = "Change language";
697
+ const toggleBtn = document.createElement("button");
698
+ toggleBtn.className = "mermaid-toggle-btn";
699
+ toggleBtn.type = "button";
700
+ const copyBtn = document.createElement("button");
701
+ copyBtn.className = "code-copy-btn";
702
+ copyBtn.title = "Copy";
703
+ copyBtn.type = "button";
704
+ copyBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg><svg class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>';
705
+ const toolbarRight = document.createElement("div");
706
+ toolbarRight.className = "code-toolbar-right";
707
+ toolbarRight.appendChild(toggleBtn);
708
+ toolbarRight.appendChild(copyBtn);
709
+ toolbar.appendChild(langLabel);
710
+ toolbar.appendChild(toolbarRight);
711
+ const pre = document.createElement("pre");
712
+ pre.className = "code-block-pre";
713
+ const code2 = document.createElement("code");
714
+ code2.className = "code-block-code";
715
+ pre.appendChild(code2);
716
+ const mermaidPreview = document.createElement("div");
717
+ mermaidPreview.className = "mermaid-preview";
718
+ mermaidPreview.setAttribute("contenteditable", "false");
719
+ mermaidPreview.style.display = "none";
720
+ const rendererPreview = document.createElement("div");
721
+ rendererPreview.className = "renderer-preview";
722
+ rendererPreview.setAttribute("contenteditable", "false");
723
+ rendererPreview.style.display = "none";
724
+ wrapper.appendChild(toolbar);
725
+ wrapper.appendChild(pre);
726
+ wrapper.appendChild(mermaidPreview);
727
+ wrapper.appendChild(rendererPreview);
728
+ let isEditing = false;
729
+ let isMermaid = node.attrs.language === "mermaid";
730
+ let lastRenderedCode = "";
731
+ let renderTimer = null;
732
+ let isRenderer = rendererLangIds.has(node.attrs.language || "");
733
+ let rendererEditing = false;
734
+ let lastRendererCode = "";
735
+ let rendererTimer = null;
736
+ let currentRendererModule = null;
737
+ function syncMermaidMode() {
738
+ const showPreview = isMermaid && !isEditing;
739
+ pre.style.display = showPreview || isRenderer && !rendererEditing ? "none" : "";
740
+ mermaidPreview.style.display = showPreview ? "flex" : "none";
741
+ toggleBtn.style.display = isMermaid || isRenderer ? "inline-flex" : "none";
742
+ wrapper.classList.toggle("mermaid-preview-mode", showPreview);
743
+ if (isMermaid) {
744
+ toggleBtn.textContent = isEditing ? "\u{1F441} Preview" : "\u270F\uFE0F Edit";
745
+ if (showPreview) triggerMermaidRender();
746
+ }
747
+ }
748
+ function triggerMermaidRender() {
749
+ const codeText = code2.textContent || "";
750
+ if (!codeText.trim()) {
751
+ mermaidPreview.innerHTML = '<div class="mermaid-empty">Empty diagram</div>';
752
+ lastRenderedCode = "";
753
+ return;
754
+ }
755
+ if (codeText === lastRenderedCode) return;
756
+ lastRenderedCode = codeText;
757
+ if (renderTimer) clearTimeout(renderTimer);
758
+ renderTimer = setTimeout(async () => {
759
+ mermaidPreview.innerHTML = '<div class="mermaid-loading"><div class="mermaid-spinner"></div>Loading diagram...</div>';
760
+ try {
761
+ const api = await loadMermaidApi();
762
+ if (!api) return;
763
+ const result = await api.renderMermaid(codeText);
764
+ if (code2.textContent !== codeText) return;
765
+ if ("svg" in result) {
766
+ mermaidPreview.innerHTML = result.svg;
767
+ } else {
768
+ mermaidPreview.innerHTML = `<div class="mermaid-error">${escapeText(result.error)}</div>`;
769
+ }
770
+ } catch {
771
+ mermaidPreview.innerHTML = '<div class="mermaid-error">Render failed</div>';
772
+ }
773
+ }, 150);
774
+ }
775
+ function syncRendererMode() {
776
+ const showPreview = isRenderer && !rendererEditing;
777
+ pre.style.display = showPreview || isMermaid && !isEditing ? "none" : "";
778
+ rendererPreview.style.display = showPreview ? "block" : "none";
779
+ toggleBtn.style.display = isMermaid || isRenderer ? "inline-flex" : "none";
780
+ wrapper.classList.toggle("renderer-preview-mode", showPreview);
781
+ if (isRenderer) {
782
+ toggleBtn.textContent = rendererEditing ? "\u{1F441} Preview" : "\u270F\uFE0F Edit";
783
+ if (showPreview) triggerRendererRender();
784
+ }
785
+ }
786
+ function triggerRendererRender() {
787
+ const source = code2.textContent || "";
788
+ const lang = node.attrs.language || "";
789
+ if (!rendererRegistry || !rendererRegistry.has(lang)) return;
790
+ if (!source.trim()) {
791
+ rendererPreview.innerHTML = '<div class="renderer-empty">Empty block</div>';
792
+ lastRendererCode = "";
793
+ return;
794
+ }
795
+ if (source === lastRendererCode) return;
796
+ lastRendererCode = source;
797
+ if (rendererTimer) clearTimeout(rendererTimer);
798
+ rendererTimer = setTimeout(async () => {
799
+ rendererPreview.innerHTML = '<div class="renderer-loading"><div class="renderer-spinner"></div>Rendering...</div>';
800
+ try {
801
+ const module = await rendererRegistry.load(lang);
802
+ if (code2.textContent !== source) return;
803
+ if (currentRendererModule?.destroy) {
804
+ try {
805
+ currentRendererModule.destroy(rendererPreview);
806
+ } catch {
807
+ }
808
+ }
809
+ currentRendererModule = module;
810
+ rendererPreview.innerHTML = "";
811
+ try {
812
+ await module.render(source, rendererPreview);
813
+ } catch (e) {
814
+ rendererPreview.innerHTML = `<div class="renderer-error" data-language="${escapeText(lang)}" data-error="${escapeText(String(e))}">[Renderer ${escapeText(lang)} failed]</div>`;
815
+ }
816
+ } catch (e) {
817
+ rendererPreview.innerHTML = `<div class="renderer-error" data-language="${escapeText(lang)}" data-error="${escapeText(String(e))}">[Renderer ${escapeText(lang)} failed]</div>`;
818
+ }
819
+ }, 150);
820
+ }
821
+ function onThemeChange() {
822
+ if (isMermaid && !isEditing) {
823
+ lastRenderedCode = "";
824
+ triggerMermaidRender();
825
+ }
826
+ }
827
+ if (isMermaid) {
828
+ installThemeObserver();
829
+ mermaidReRenderCallbacks.add(onThemeChange);
830
+ requestAnimationFrame(() => syncMermaidMode());
831
+ } else if (isRenderer) {
832
+ syncRendererMode();
833
+ requestAnimationFrame(() => syncRendererMode());
834
+ } else {
835
+ syncMermaidMode();
836
+ }
837
+ let activePicker = null;
838
+ langLabel.addEventListener("mousedown", (e) => {
839
+ e.preventDefault();
840
+ e.stopPropagation();
841
+ if (activePicker) {
842
+ activePicker.destroy();
843
+ activePicker = null;
844
+ wrapper.classList.remove("picker-open");
845
+ return;
846
+ }
847
+ const currentLang = node.attrs.language || "";
848
+ const codeContent = code2.textContent || "";
849
+ wrapper.classList.add("picker-open");
850
+ activePicker = createLanguagePicker(
851
+ wrapper,
852
+ langLabel,
853
+ currentLang,
854
+ codeContent,
855
+ langLists,
856
+ (newLang) => {
857
+ activePicker = null;
858
+ wrapper.classList.remove("picker-open");
859
+ const pos = getPos();
860
+ if (pos === void 0) return;
861
+ view.dispatch(
862
+ view.state.tr.setNodeMarkup(pos, void 0, {
863
+ ...node.attrs,
864
+ language: newLang
865
+ })
866
+ );
867
+ view.focus();
868
+ },
869
+ () => {
870
+ activePicker = null;
871
+ wrapper.classList.remove("picker-open");
872
+ }
873
+ );
874
+ });
875
+ toggleBtn.addEventListener("mousedown", (e) => {
876
+ e.preventDefault();
877
+ e.stopPropagation();
878
+ if (isMermaid) {
879
+ isEditing = !isEditing;
880
+ syncMermaidMode();
881
+ } else if (isRenderer) {
882
+ rendererEditing = !rendererEditing;
883
+ syncRendererMode();
884
+ }
885
+ if (isEditing || rendererEditing) view.focus();
886
+ });
887
+ mermaidPreview.addEventListener("mousedown", (e) => {
888
+ e.preventDefault();
889
+ e.stopPropagation();
890
+ isEditing = true;
891
+ syncMermaidMode();
892
+ view.focus();
893
+ });
894
+ copyBtn.addEventListener("mousedown", (e) => {
895
+ e.preventDefault();
896
+ e.stopPropagation();
897
+ handleCopy(copyBtn, code2);
898
+ });
899
+ return {
900
+ dom: wrapper,
901
+ contentDOM: code2,
902
+ stopEvent(event) {
903
+ const target = event.target;
904
+ return !code2.contains(target) && wrapper.contains(target) && target !== code2;
905
+ },
906
+ ignoreMutation(mutation) {
907
+ return !code2.contains(mutation.target);
908
+ },
909
+ update(updatedNode) {
910
+ if (updatedNode.type.name !== "code_block") return false;
911
+ node = updatedNode;
912
+ langLabel.textContent = findLanguageLabel(updatedNode.attrs.language || "", allLanguages);
913
+ const wasMermaid = isMermaid;
914
+ isMermaid = updatedNode.attrs.language === "mermaid";
915
+ if (isMermaid !== wasMermaid) {
916
+ isEditing = false;
917
+ if (isMermaid) {
918
+ installThemeObserver();
919
+ mermaidReRenderCallbacks.add(onThemeChange);
920
+ } else {
921
+ mermaidReRenderCallbacks.delete(onThemeChange);
922
+ }
923
+ }
924
+ const wasRenderer = isRenderer;
925
+ isRenderer = rendererLangIds.has(updatedNode.attrs.language || "");
926
+ if (isRenderer !== wasRenderer) {
927
+ rendererEditing = false;
928
+ lastRendererCode = "";
929
+ rendererPreview.innerHTML = "";
930
+ if (!isRenderer && currentRendererModule?.destroy) {
931
+ try {
932
+ currentRendererModule.destroy(rendererPreview);
933
+ } catch {
934
+ }
935
+ currentRendererModule = null;
936
+ }
937
+ }
938
+ if (isRenderer) {
939
+ syncRendererMode();
940
+ } else {
941
+ rendererPreview.style.display = "none";
942
+ wrapper.classList.remove("renderer-preview-mode");
943
+ syncMermaidMode();
944
+ }
945
+ return true;
946
+ },
947
+ selectNode() {
948
+ wrapper.classList.add("ProseMirror-selectednode");
949
+ },
950
+ deselectNode() {
951
+ wrapper.classList.remove("ProseMirror-selectednode");
952
+ },
953
+ destroy() {
954
+ if (activePicker) {
955
+ activePicker.destroy();
956
+ activePicker = null;
957
+ }
958
+ if (renderTimer) clearTimeout(renderTimer);
959
+ if (rendererTimer) clearTimeout(rendererTimer);
960
+ if (currentRendererModule?.destroy) {
961
+ try {
962
+ currentRendererModule.destroy(rendererPreview);
963
+ } catch {
964
+ }
965
+ }
966
+ currentRendererModule = null;
967
+ mermaidReRenderCallbacks.delete(onThemeChange);
968
+ }
969
+ };
970
+ };
971
+ }
972
+ var mermaidApi, mermaidLoading, themeObserverInstalled, mermaidReRenderCallbacks, POPULAR_LANGUAGES, BASE_OTHER_LANGUAGES, POPULAR_IDS, hljsAutoDetect;
973
+ var init_code_block_view = __esm({
974
+ "src/plugins/code-block-view.ts"() {
975
+ "use strict";
976
+ mermaidApi = null;
977
+ mermaidLoading = null;
978
+ themeObserverInstalled = false;
979
+ mermaidReRenderCallbacks = /* @__PURE__ */ new Set();
980
+ POPULAR_LANGUAGES = [
981
+ { id: "javascript", label: "JavaScript", aliases: ["js"] },
982
+ { id: "typescript", label: "TypeScript", aliases: ["ts"] },
983
+ { id: "python", label: "Python", aliases: ["py"] },
984
+ { id: "java", label: "Java", aliases: [] },
985
+ { id: "go", label: "Go", aliases: ["golang"] },
986
+ { id: "rust", label: "Rust", aliases: ["rs"] },
987
+ { id: "c", label: "C", aliases: [] },
988
+ { id: "cpp", label: "C++", aliases: ["c++"] },
989
+ { id: "ruby", label: "Ruby", aliases: ["rb"] },
990
+ { id: "php", label: "PHP", aliases: [] },
991
+ { id: "swift", label: "Swift", aliases: [] },
992
+ { id: "kotlin", label: "Kotlin", aliases: ["kt"] },
993
+ { id: "sql", label: "SQL", aliases: [] },
994
+ { id: "bash", label: "Bash", aliases: ["sh", "shell"] },
995
+ { id: "json", label: "JSON", aliases: [] },
996
+ { id: "yaml", label: "YAML", aliases: ["yml"] },
997
+ { id: "html", label: "HTML", aliases: ["xml"] },
998
+ { id: "css", label: "CSS", aliases: [] },
999
+ { id: "csharp", label: "C#", aliases: ["cs"] },
1000
+ { id: "dart", label: "Dart", aliases: [] },
1001
+ { id: "r", label: "R", aliases: [] },
1002
+ { id: "dockerfile", label: "Dockerfile", aliases: ["docker"] },
1003
+ { id: "graphql", label: "GraphQL", aliases: ["gql"] },
1004
+ { id: "markdown", label: "Markdown", aliases: ["md"] },
1005
+ { id: "text", label: "Plain Text", aliases: ["plaintext", "txt"] },
1006
+ { id: "prompt", label: "Prompt", aliases: ["image-prompts", "image-prompt"] },
1007
+ { id: "system", label: "System Prompt", aliases: ["system-prompt"] }
1008
+ ];
1009
+ BASE_OTHER_LANGUAGES = [
1010
+ { id: "scss", label: "SCSS", aliases: [] },
1011
+ { id: "lua", label: "Lua", aliases: [] },
1012
+ { id: "diff", label: "Diff", aliases: [] },
1013
+ { id: "perl", label: "Perl", aliases: ["pl"] },
1014
+ { id: "scala", label: "Scala", aliases: [] },
1015
+ { id: "objectivec", label: "Objective-C", aliases: ["objc"] },
1016
+ { id: "ini", label: "TOML / INI", aliases: ["toml"] },
1017
+ { id: "powershell", label: "PowerShell", aliases: ["ps", "ps1"] },
1018
+ { id: "makefile", label: "Makefile", aliases: ["make"] },
1019
+ { id: "groovy", label: "Groovy", aliases: [] },
1020
+ { id: "elixir", label: "Elixir", aliases: ["ex"] },
1021
+ { id: "haskell", label: "Haskell", aliases: ["hs"] },
1022
+ { id: "protobuf", label: "Protobuf", aliases: ["proto"] },
1023
+ { id: "latex", label: "LaTeX", aliases: ["tex"] },
1024
+ { id: "nginx", label: "Nginx", aliases: ["nginxconf"] },
1025
+ { id: "shell", label: "Shell Session", aliases: [] },
1026
+ { id: "mermaid", label: "Mermaid", aliases: [] }
1027
+ ];
1028
+ POPULAR_IDS = new Set(POPULAR_LANGUAGES.map((l) => l.id));
1029
+ hljsAutoDetect = null;
1030
+ }
1031
+ });
1032
+
1033
+ // src/schema.ts
1034
+ import { Schema, Fragment } from "prosemirror-model";
1035
+ import katex from "katex";
1036
+
1037
+ // src/types.ts
1038
+ var NULL_MEDIA_RESOLVER_SENTINEL = /* @__PURE__ */ Symbol("@moraya/core:null-media-resolver");
1039
+ function isNullMediaResolver(r2) {
1040
+ return r2[NULL_MEDIA_RESOLVER_SENTINEL] === true;
1041
+ }
1042
+
1043
+ // src/schema.ts
1044
+ function extractHtmlAttr(html, name) {
1045
+ const re = new RegExp(`${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))`, "i");
1046
+ const m = html.match(re);
1047
+ return m ? m[1] ?? m[2] ?? m[3] ?? null : null;
1048
+ }
1049
+ function extractAllHtmlAttrs(html) {
1050
+ const attrs = {};
1051
+ const re = /([a-zA-Z_][\w:.-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
1052
+ let m;
1053
+ while ((m = re.exec(html)) !== null) {
1054
+ const name = m[1];
1055
+ if (!name) continue;
1056
+ attrs[name.toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
1057
+ }
1058
+ return attrs;
1059
+ }
1060
+ function showBrokenImage(container, sourceText) {
1061
+ container.textContent = "";
1062
+ container.className = (container.className.replace(/\bhtml-img-wrapper\b|\bimage-node\b/, "").trim() + " broken-image").trim();
1063
+ const icon = document.createElement("span");
1064
+ icon.className = "broken-image-icon";
1065
+ container.appendChild(icon);
1066
+ const code2 = document.createElement("code");
1067
+ code2.className = "broken-image-src";
1068
+ code2.textContent = sourceText;
1069
+ container.appendChild(code2);
1070
+ }
1071
+ function htmlTagToStyle(openTag) {
1072
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
1073
+ if (!tagMatch || !tagMatch[1]) return "";
1074
+ const tagName = tagMatch[1].toLowerCase();
1075
+ switch (tagName) {
1076
+ case "font": {
1077
+ const parts = [];
1078
+ const color = extractHtmlAttr(openTag, "color");
1079
+ if (color) parts.push(`color: ${color}`);
1080
+ const size = extractHtmlAttr(openTag, "size");
1081
+ if (size) {
1082
+ const sizeMap = {
1083
+ "1": "0.63em",
1084
+ "2": "0.82em",
1085
+ "3": "1em",
1086
+ "4": "1.13em",
1087
+ "5": "1.5em",
1088
+ "6": "2em",
1089
+ "7": "3em"
1090
+ };
1091
+ parts.push(`font-size: ${sizeMap[size] || size}`);
1092
+ }
1093
+ const face = extractHtmlAttr(openTag, "face");
1094
+ if (face) parts.push(`font-family: ${face}`);
1095
+ return parts.join("; ");
1096
+ }
1097
+ case "span":
1098
+ case "div":
1099
+ return extractHtmlAttr(openTag, "style") || "";
1100
+ default:
1101
+ return "";
1102
+ }
1103
+ }
1104
+ var documentBaseDir = "";
1105
+ function setDocumentBaseDir(dir) {
1106
+ documentBaseDir = dir;
1107
+ }
1108
+ function getDocumentBaseDir() {
1109
+ return documentBaseDir;
1110
+ }
1111
+ function isAbsoluteFilePath(src) {
1112
+ if (!src) return false;
1113
+ if (src.startsWith("/") && !src.startsWith("//")) return true;
1114
+ if (/^[A-Z]:[\\/]/i.test(src)) return true;
1115
+ return false;
1116
+ }
1117
+ function isRelativePath(src) {
1118
+ if (!src) return false;
1119
+ if (/^(https?:|data:|blob:|javascript:|vbscript:|tauri:|\/\/)/i.test(src)) return false;
1120
+ if (src.startsWith("/") || /^[A-Z]:[\\/]/i.test(src)) return false;
1121
+ return true;
1122
+ }
1123
+ function resolveRelativePath(src) {
1124
+ if (!documentBaseDir) return src;
1125
+ let rel = src.replace(/^\.\//, "");
1126
+ const sep = documentBaseDir.includes("\\") ? "\\" : "/";
1127
+ let base = documentBaseDir.endsWith(sep) ? documentBaseDir.slice(0, -1) : documentBaseDir;
1128
+ while (rel.startsWith("../") || rel.startsWith("..\\")) {
1129
+ rel = rel.slice(3);
1130
+ const lastSep = base.lastIndexOf(sep);
1131
+ if (lastSep > 0) base = base.slice(0, lastSep);
1132
+ }
1133
+ return `${base}${sep}${rel}`;
1134
+ }
1135
+ function loadLocalImageSrc(img, src, mediaResolver) {
1136
+ let path;
1137
+ try {
1138
+ path = decodeURIComponent(src);
1139
+ } catch {
1140
+ path = src;
1141
+ }
1142
+ mediaResolver.loadLocalImage(path).then((url) => {
1143
+ if (url) img.src = url;
1144
+ else img.dispatchEvent(new Event("error"));
1145
+ }).catch(() => {
1146
+ img.dispatchEvent(new Event("error"));
1147
+ });
1148
+ }
1149
+ function setMediaSrc(el, src, mediaResolver) {
1150
+ if (isAbsoluteFilePath(src)) {
1151
+ mediaResolver.loadLocalMedia(src).then((url) => {
1152
+ if (!url) return;
1153
+ el.src = url;
1154
+ if (el instanceof HTMLMediaElement) el.load();
1155
+ }).catch(() => {
1156
+ });
1157
+ } else if (isRelativePath(src)) {
1158
+ mediaResolver.loadLocalMedia(resolveRelativePath(src)).then((url) => {
1159
+ if (!url) return;
1160
+ el.src = url;
1161
+ if (el instanceof HTMLMediaElement) el.load();
1162
+ }).catch(() => {
1163
+ });
1164
+ } else if (/^https?:\/\//i.test(src)) {
1165
+ if (el instanceof HTMLVideoElement) {
1166
+ el.src = src;
1167
+ el.load();
1168
+ } else {
1169
+ mediaResolver.loadRemoteMedia(src).then((url) => {
1170
+ if (!url) return;
1171
+ el.src = url;
1172
+ if (el instanceof HTMLMediaElement) el.load();
1173
+ }).catch(() => {
1174
+ });
1175
+ }
1176
+ } else {
1177
+ el.src = src;
1178
+ }
1179
+ }
1180
+ function createMediaElement(tagName, value, mediaResolver) {
1181
+ const wrapper = document.createElement("span");
1182
+ wrapper.dataset.type = "html-inline";
1183
+ wrapper.dataset.value = value;
1184
+ wrapper.className = "html-media-wrapper";
1185
+ wrapper.contentEditable = "false";
1186
+ const el = document.createElement(tagName);
1187
+ const stopForControls = (ev) => ev.stopPropagation();
1188
+ el.addEventListener("mousedown", stopForControls);
1189
+ el.addEventListener("click", stopForControls);
1190
+ el.addEventListener("pointerdown", stopForControls);
1191
+ const openTagMatch = value.match(new RegExp(`^<${tagName}\\b[^>]*>`, "i"));
1192
+ const openTag = openTagMatch ? openTagMatch[0] : "";
1193
+ const attrs = extractAllHtmlAttrs(openTag);
1194
+ for (const [key, val] of Object.entries(attrs)) {
1195
+ if (key === "src") continue;
1196
+ if (key.startsWith("on")) continue;
1197
+ el.setAttribute(key, val);
1198
+ }
1199
+ const strippedTag = openTag.replace(/=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/g, "");
1200
+ const boolAttrs = ["controls", "autoplay", "loop", "muted", "playsinline"];
1201
+ for (const attr of boolAttrs) {
1202
+ if (!(attr in attrs) && new RegExp(`\\b${attr}\\b`, "i").test(strippedTag)) {
1203
+ el.setAttribute(attr, "");
1204
+ }
1205
+ }
1206
+ if (tagName === "audio" && !attrs.preload) {
1207
+ el.setAttribute("preload", "auto");
1208
+ }
1209
+ const sourceRe = /<source\b[^>]*\/?>/gi;
1210
+ let srcMatch;
1211
+ while ((srcMatch = sourceRe.exec(value)) !== null) {
1212
+ const srcAttrs = extractAllHtmlAttrs(srcMatch[0]);
1213
+ if (!srcAttrs.src) continue;
1214
+ const source = document.createElement("source");
1215
+ if (srcAttrs.type) source.type = srcAttrs.type;
1216
+ setMediaSrc(source, srcAttrs.src, mediaResolver);
1217
+ el.appendChild(source);
1218
+ }
1219
+ if (attrs.src) {
1220
+ setMediaSrc(el, attrs.src, mediaResolver);
1221
+ }
1222
+ wrapper.appendChild(el);
1223
+ return wrapper;
1224
+ }
1225
+ var doc = {
1226
+ content: "block+"
1227
+ };
1228
+ var text = { group: "inline" };
1229
+ var paragraph = {
1230
+ content: "inline*",
1231
+ group: "block",
1232
+ parseDOM: [{ tag: "p" }],
1233
+ toDOM() {
1234
+ return ["p", 0];
1235
+ }
1236
+ };
1237
+ var heading = {
1238
+ attrs: {
1239
+ id: { default: "" },
1240
+ level: { default: 1 }
1241
+ },
1242
+ content: "inline*",
1243
+ group: "block",
1244
+ defining: true,
1245
+ parseDOM: [1, 2, 3, 4, 5, 6].map((level) => ({
1246
+ tag: `h${level}`,
1247
+ getAttrs(dom) {
1248
+ return { level, id: dom.getAttribute("id") || "" };
1249
+ }
1250
+ })),
1251
+ toDOM(node) {
1252
+ const attrs = {};
1253
+ if (node.attrs.id) attrs.id = node.attrs.id;
1254
+ return [`h${node.attrs.level}`, attrs, 0];
1255
+ }
1256
+ };
1257
+ var blockquote = {
1258
+ content: "block+",
1259
+ group: "block",
1260
+ defining: true,
1261
+ parseDOM: [{ tag: "blockquote" }],
1262
+ toDOM() {
1263
+ return ["blockquote", 0];
1264
+ }
1265
+ };
1266
+ var code_block = {
1267
+ content: "text*",
1268
+ group: "block",
1269
+ marks: "",
1270
+ defining: true,
1271
+ code: true,
1272
+ attrs: {
1273
+ language: { default: "text" }
1274
+ },
1275
+ parseDOM: [{
1276
+ tag: "pre",
1277
+ preserveWhitespace: "full",
1278
+ getAttrs(dom) {
1279
+ return { language: dom.dataset.language || "text" };
1280
+ }
1281
+ }],
1282
+ toDOM(node) {
1283
+ return ["pre", { "data-language": node.attrs.language || void 0 }, ["code", 0]];
1284
+ }
1285
+ };
1286
+ var horizontal_rule = {
1287
+ group: "block",
1288
+ parseDOM: [{ tag: "hr" }],
1289
+ toDOM() {
1290
+ return ["hr"];
1291
+ }
1292
+ };
1293
+ var bullet_list = {
1294
+ content: "list_item+",
1295
+ group: "block",
1296
+ parseDOM: [{ tag: "ul" }],
1297
+ toDOM() {
1298
+ return ["ul", 0];
1299
+ }
1300
+ };
1301
+ var ordered_list = {
1302
+ content: "list_item+",
1303
+ group: "block",
1304
+ attrs: {
1305
+ order: { default: 1 }
1306
+ },
1307
+ parseDOM: [{
1308
+ tag: "ol",
1309
+ getAttrs(dom) {
1310
+ return { order: dom.hasAttribute("start") ? +(dom.getAttribute("start") || 1) : 1 };
1311
+ }
1312
+ }],
1313
+ toDOM(node) {
1314
+ return node.attrs.order === 1 ? ["ol", 0] : ["ol", { start: node.attrs.order }, 0];
1315
+ }
1316
+ };
1317
+ var list_item = {
1318
+ content: "paragraph block*",
1319
+ group: "listItem",
1320
+ defining: true,
1321
+ attrs: {
1322
+ label: { default: "\u2022" },
1323
+ listType: { default: "bullet" },
1324
+ spread: { default: "true" },
1325
+ checked: { default: null }
1326
+ },
1327
+ parseDOM: [
1328
+ {
1329
+ tag: 'li[data-item-type="task"]',
1330
+ getAttrs(dom) {
1331
+ return {
1332
+ label: dom.dataset.label,
1333
+ listType: dom.dataset.listType,
1334
+ spread: dom.dataset.spread,
1335
+ checked: dom.dataset.checked ? dom.dataset.checked === "true" : null
1336
+ };
1337
+ }
1338
+ },
1339
+ {
1340
+ tag: "li",
1341
+ getAttrs(dom) {
1342
+ return {
1343
+ label: dom.dataset.label || "\u2022",
1344
+ listType: dom.dataset.listType || "bullet",
1345
+ spread: dom.dataset.spread || "true"
1346
+ };
1347
+ }
1348
+ }
1349
+ ],
1350
+ toDOM(node) {
1351
+ if (node.attrs.checked != null) {
1352
+ return ["li", {
1353
+ "data-item-type": "task",
1354
+ "data-label": node.attrs.label,
1355
+ "data-list-type": node.attrs.listType,
1356
+ "data-spread": node.attrs.spread,
1357
+ "data-checked": String(node.attrs.checked)
1358
+ }, 0];
1359
+ }
1360
+ return ["li", {
1361
+ "data-label": node.attrs.label,
1362
+ "data-list-type": node.attrs.listType,
1363
+ "data-spread": node.attrs.spread
1364
+ }, 0];
1365
+ }
1366
+ };
1367
+ var hardbreak = {
1368
+ inline: true,
1369
+ group: "inline",
1370
+ selectable: false,
1371
+ attrs: {
1372
+ isInline: { default: false }
1373
+ },
1374
+ parseDOM: [
1375
+ { tag: "br" },
1376
+ {
1377
+ tag: 'span[data-type="hardbreak"]',
1378
+ getAttrs() {
1379
+ return { isInline: true };
1380
+ }
1381
+ }
1382
+ ],
1383
+ toDOM() {
1384
+ return ["span", { "data-type": "hardbreak", "class": "hardbreak-marker" }, "\n"];
1385
+ },
1386
+ leafText() {
1387
+ return "\n";
1388
+ }
1389
+ };
1390
+ var html_block = {
1391
+ content: "text*",
1392
+ group: "block",
1393
+ marks: "",
1394
+ code: true,
1395
+ defining: true,
1396
+ parseDOM: [{
1397
+ tag: 'div[data-type="html"]',
1398
+ preserveWhitespace: "full"
1399
+ }],
1400
+ toDOM() {
1401
+ return ["div", { "data-type": "html" }, ["pre", 0]];
1402
+ }
1403
+ };
1404
+ var table = {
1405
+ content: "table_header_row table_row+",
1406
+ group: "block",
1407
+ tableRole: "table",
1408
+ isolating: true,
1409
+ parseDOM: [{ tag: "table" }],
1410
+ toDOM() {
1411
+ return ["table", ["tbody", 0]];
1412
+ }
1413
+ };
1414
+ var table_header_row = {
1415
+ content: "(table_header)*",
1416
+ tableRole: "row",
1417
+ parseDOM: [
1418
+ { tag: "tr[data-is-header]" },
1419
+ {
1420
+ tag: "tr",
1421
+ getAttrs(dom) {
1422
+ const hasHeader = dom.querySelector("th");
1423
+ return hasHeader ? {} : false;
1424
+ }
1425
+ }
1426
+ ],
1427
+ toDOM() {
1428
+ return ["tr", { "data-is-header": "true" }, 0];
1429
+ }
1430
+ };
1431
+ var table_row = {
1432
+ content: "(table_cell)*",
1433
+ tableRole: "row",
1434
+ parseDOM: [{ tag: "tr" }],
1435
+ toDOM() {
1436
+ return ["tr", 0];
1437
+ }
1438
+ };
1439
+ var table_header = {
1440
+ content: "paragraph+",
1441
+ tableRole: "header_cell",
1442
+ attrs: {
1443
+ alignment: { default: "left" },
1444
+ colspan: { default: 1 },
1445
+ rowspan: { default: 1 },
1446
+ colwidth: { default: null }
1447
+ },
1448
+ isolating: true,
1449
+ parseDOM: [{
1450
+ tag: "th",
1451
+ getAttrs(dom) {
1452
+ return {
1453
+ alignment: dom.style.textAlign || "left",
1454
+ colspan: Number(dom.getAttribute("colspan") || 1),
1455
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
1456
+ colwidth: null
1457
+ };
1458
+ }
1459
+ }],
1460
+ toDOM(node) {
1461
+ return ["th", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
1462
+ }
1463
+ };
1464
+ var table_cell = {
1465
+ content: "paragraph+",
1466
+ tableRole: "cell",
1467
+ attrs: {
1468
+ alignment: { default: "left" },
1469
+ colspan: { default: 1 },
1470
+ rowspan: { default: 1 },
1471
+ colwidth: { default: null }
1472
+ },
1473
+ isolating: true,
1474
+ parseDOM: [{
1475
+ tag: "td",
1476
+ getAttrs(dom) {
1477
+ return {
1478
+ alignment: dom.style.textAlign || "left",
1479
+ colspan: Number(dom.getAttribute("colspan") || 1),
1480
+ rowspan: Number(dom.getAttribute("rowspan") || 1),
1481
+ colwidth: null
1482
+ };
1483
+ }
1484
+ }],
1485
+ toDOM(node) {
1486
+ return ["td", { style: `text-align: ${node.attrs.alignment || "left"}` }, 0];
1487
+ }
1488
+ };
1489
+ var math_inline = {
1490
+ group: "inline",
1491
+ content: "text*",
1492
+ inline: true,
1493
+ atom: true,
1494
+ parseDOM: [{
1495
+ tag: 'span[data-type="math_inline"]',
1496
+ getContent(dom, schema2) {
1497
+ if (!(dom instanceof HTMLElement)) return Fragment.empty;
1498
+ const value = dom.dataset.value ?? "";
1499
+ if (!value) return Fragment.empty;
1500
+ return Fragment.from(schema2.text(value));
1501
+ }
1502
+ }],
1503
+ toDOM(node) {
1504
+ const code2 = node.textContent;
1505
+ const dom = document.createElement("span");
1506
+ dom.dataset.type = "math_inline";
1507
+ dom.dataset.value = code2;
1508
+ try {
1509
+ katex.render(code2, dom);
1510
+ } catch {
1511
+ dom.textContent = code2;
1512
+ dom.classList.add("math-error");
1513
+ dom.setAttribute("data-math-type", "inline");
1514
+ }
1515
+ return dom;
1516
+ }
1517
+ };
1518
+ var math_block = {
1519
+ content: "text*",
1520
+ group: "block",
1521
+ marks: "",
1522
+ defining: true,
1523
+ atom: true,
1524
+ isolating: true,
1525
+ attrs: {
1526
+ value: { default: "" }
1527
+ },
1528
+ parseDOM: [{
1529
+ tag: 'div[data-type="math_block"]',
1530
+ preserveWhitespace: "full",
1531
+ getAttrs(dom) {
1532
+ return { value: dom.dataset.value ?? "" };
1533
+ }
1534
+ }],
1535
+ toDOM(node) {
1536
+ const code2 = node.attrs.value;
1537
+ const dom = document.createElement("div");
1538
+ dom.dataset.type = "math_block";
1539
+ dom.dataset.value = code2;
1540
+ try {
1541
+ katex.render(code2, dom, { displayMode: true });
1542
+ } catch {
1543
+ dom.textContent = code2;
1544
+ dom.classList.add("math-error");
1545
+ dom.setAttribute("data-math-type", "block");
1546
+ }
1547
+ return dom;
1548
+ }
1549
+ };
1550
+ var defList = {
1551
+ content: "(defListTerm | defListDescription)+",
1552
+ group: "block",
1553
+ defining: true,
1554
+ parseDOM: [{ tag: "dl" }],
1555
+ toDOM() {
1556
+ return ["dl", { class: "definition-list" }, 0];
1557
+ }
1558
+ };
1559
+ var defListTerm = {
1560
+ content: "inline*",
1561
+ group: "block",
1562
+ defining: true,
1563
+ parseDOM: [{ tag: "dt" }],
1564
+ toDOM() {
1565
+ return ["dt", 0];
1566
+ }
1567
+ };
1568
+ var defListDescription = {
1569
+ content: "block+",
1570
+ group: "block",
1571
+ defining: true,
1572
+ parseDOM: [{ tag: "dd" }],
1573
+ toDOM() {
1574
+ return ["dd", 0];
1575
+ }
1576
+ };
1577
+ var strong = {
1578
+ parseDOM: [
1579
+ {
1580
+ tag: "b",
1581
+ getAttrs(dom) {
1582
+ return dom.style.fontWeight !== "normal" && null;
1583
+ }
1584
+ },
1585
+ { tag: "strong" },
1586
+ {
1587
+ style: "font-weight",
1588
+ getAttrs(value) {
1589
+ return /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null;
1590
+ }
1591
+ }
1592
+ ],
1593
+ toDOM() {
1594
+ return ["strong", 0];
1595
+ }
1596
+ };
1597
+ var em = {
1598
+ parseDOM: [
1599
+ { tag: "i" },
1600
+ { tag: "em" },
1601
+ {
1602
+ style: "font-style",
1603
+ getAttrs(value) {
1604
+ return value === "italic" && null;
1605
+ }
1606
+ }
1607
+ ],
1608
+ toDOM() {
1609
+ return ["em", 0];
1610
+ }
1611
+ };
1612
+ var code = {
1613
+ priority: 100,
1614
+ code: true,
1615
+ inclusive: false,
1616
+ parseDOM: [{ tag: "code" }],
1617
+ toDOM() {
1618
+ return ["code", 0];
1619
+ }
1620
+ };
1621
+ var link = {
1622
+ attrs: {
1623
+ href: {},
1624
+ title: { default: null }
1625
+ },
1626
+ inclusive: false,
1627
+ parseDOM: [{
1628
+ tag: "a[href]",
1629
+ getAttrs(dom) {
1630
+ return {
1631
+ href: dom.getAttribute("href"),
1632
+ title: dom.getAttribute("title")
1633
+ };
1634
+ }
1635
+ }],
1636
+ toDOM(mark) {
1637
+ const attrs = { href: mark.attrs.href };
1638
+ if (mark.attrs.title) attrs.title = mark.attrs.title;
1639
+ return ["a", attrs, 0];
1640
+ }
1641
+ };
1642
+ var strike_through = {
1643
+ parseDOM: [
1644
+ { tag: "del" },
1645
+ { tag: "s" },
1646
+ {
1647
+ style: "text-decoration",
1648
+ getAttrs(value) {
1649
+ return value === "line-through" && null;
1650
+ }
1651
+ }
1652
+ ],
1653
+ toDOM() {
1654
+ return ["del", 0];
1655
+ }
1656
+ };
1657
+ var html_mark = {
1658
+ attrs: {
1659
+ openTag: { default: "" },
1660
+ closeTag: { default: "" }
1661
+ },
1662
+ excludes: "",
1663
+ // Allow nesting multiple html_marks (e.g., <font><u>text</u></font>)
1664
+ parseDOM: [{
1665
+ tag: '[data-type="html-mark"]',
1666
+ getAttrs(dom) {
1667
+ return {
1668
+ openTag: dom.dataset.openTag ?? "",
1669
+ closeTag: dom.dataset.closeTag ?? ""
1670
+ };
1671
+ }
1672
+ }],
1673
+ toDOM(mark) {
1674
+ const openTag = mark.attrs.openTag;
1675
+ const tagMatch = openTag.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
1676
+ const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : "span";
1677
+ const attrs = {
1678
+ "data-type": "html-mark",
1679
+ "data-open-tag": openTag,
1680
+ "data-close-tag": mark.attrs.closeTag
1681
+ };
1682
+ const semanticTags = ["sub", "sup", "u", "ins", "mark", "small", "big", "kbd", "abbr"];
1683
+ if (semanticTags.includes(tagName)) {
1684
+ return [tagName, attrs, 0];
1685
+ }
1686
+ const style = htmlTagToStyle(openTag);
1687
+ if (style) attrs.style = style;
1688
+ return ["span", attrs, 0];
1689
+ }
1690
+ };
1691
+ function buildImageNodeSpec(mediaResolver) {
1692
+ return {
1693
+ inline: true,
1694
+ group: "inline",
1695
+ selectable: true,
1696
+ draggable: true,
1697
+ marks: "",
1698
+ atom: true,
1699
+ defining: true,
1700
+ isolating: true,
1701
+ attrs: {
1702
+ src: { default: "" },
1703
+ alt: { default: "" },
1704
+ title: { default: "" }
1705
+ },
1706
+ parseDOM: [{
1707
+ tag: "img[src]",
1708
+ getAttrs(dom) {
1709
+ return {
1710
+ src: dom.getAttribute("src") || "",
1711
+ alt: dom.getAttribute("alt") || "",
1712
+ title: dom.getAttribute("title") || dom.getAttribute("alt") || ""
1713
+ };
1714
+ }
1715
+ }],
1716
+ toDOM(node) {
1717
+ const container = document.createElement("span");
1718
+ container.className = "image-node";
1719
+ const img = document.createElement("img");
1720
+ if (node.attrs.alt) img.alt = node.attrs.alt;
1721
+ if (node.attrs.title) img.title = node.attrs.title;
1722
+ const titleStr = node.attrs.title || "";
1723
+ const widthMatch = titleStr.match(/^width=(\d+%?)$/);
1724
+ const widthVal = widthMatch?.[1];
1725
+ if (widthVal) {
1726
+ img.style.width = widthVal.includes("%") ? widthVal : `${widthVal}px`;
1727
+ img.style.maxWidth = "none";
1728
+ }
1729
+ img.onerror = () => {
1730
+ const alt = node.attrs.alt ? `![${node.attrs.alt}]` : "![]";
1731
+ const title = node.attrs.title ? ` "${node.attrs.title}"` : "";
1732
+ showBrokenImage(container, `${alt}(${node.attrs.src}${title})`);
1733
+ };
1734
+ const src = node.attrs.src;
1735
+ if (isAbsoluteFilePath(src)) {
1736
+ loadLocalImageSrc(img, src, mediaResolver);
1737
+ } else if (isRelativePath(src)) {
1738
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
1739
+ } else {
1740
+ img.src = src;
1741
+ }
1742
+ container.appendChild(img);
1743
+ return container;
1744
+ }
1745
+ };
1746
+ }
1747
+ function buildHtmlInlineNodeSpec(mediaResolver) {
1748
+ return {
1749
+ group: "inline",
1750
+ inline: true,
1751
+ atom: true,
1752
+ attrs: {
1753
+ value: { default: "" }
1754
+ },
1755
+ parseDOM: [{
1756
+ tag: 'span[data-type="html-inline"]',
1757
+ getAttrs(dom) {
1758
+ return { value: dom.dataset.value ?? "" };
1759
+ }
1760
+ }],
1761
+ toDOM(node) {
1762
+ const value = node.attrs.value;
1763
+ if (/^<img\s/i.test(value)) {
1764
+ const wrapper = document.createElement("span");
1765
+ wrapper.dataset.type = "html-inline";
1766
+ wrapper.dataset.value = value;
1767
+ wrapper.className = "html-img-wrapper";
1768
+ const attrs = extractAllHtmlAttrs(value);
1769
+ const src = attrs.src || "";
1770
+ if (src) {
1771
+ const img = document.createElement("img");
1772
+ for (const [key, val] of Object.entries(attrs)) {
1773
+ if (key === "src") continue;
1774
+ if (key === "onerror" || key === "onload" || key.startsWith("on")) continue;
1775
+ img.setAttribute(key, val);
1776
+ }
1777
+ img.onerror = () => {
1778
+ showBrokenImage(wrapper, value);
1779
+ };
1780
+ if (isAbsoluteFilePath(src)) {
1781
+ loadLocalImageSrc(img, src, mediaResolver);
1782
+ } else if (isRelativePath(src)) {
1783
+ loadLocalImageSrc(img, resolveRelativePath(src), mediaResolver);
1784
+ } else {
1785
+ img.src = src;
1786
+ }
1787
+ wrapper.appendChild(img);
1788
+ } else {
1789
+ showBrokenImage(wrapper, value);
1790
+ }
1791
+ return wrapper;
1792
+ }
1793
+ if (/^<video\b/i.test(value)) return createMediaElement("video", value, mediaResolver);
1794
+ if (/^<audio\b/i.test(value)) return createMediaElement("audio", value, mediaResolver);
1795
+ return ["span", { "data-type": "html-inline", "data-value": value }];
1796
+ }
1797
+ };
1798
+ }
1799
+ function buildNodes(mediaResolver) {
1800
+ return {
1801
+ doc,
1802
+ text,
1803
+ paragraph,
1804
+ heading,
1805
+ blockquote,
1806
+ code_block,
1807
+ horizontal_rule,
1808
+ bullet_list,
1809
+ ordered_list,
1810
+ list_item,
1811
+ image: buildImageNodeSpec(mediaResolver),
1812
+ hardbreak,
1813
+ html_block,
1814
+ html_inline: buildHtmlInlineNodeSpec(mediaResolver),
1815
+ table,
1816
+ table_header_row,
1817
+ table_row,
1818
+ table_header,
1819
+ table_cell,
1820
+ math_inline,
1821
+ math_block,
1822
+ defList,
1823
+ defListTerm,
1824
+ defListDescription
1825
+ };
1826
+ }
1827
+ var marks = {
1828
+ html_mark,
1829
+ strong,
1830
+ em,
1831
+ code,
1832
+ link,
1833
+ strike_through
1834
+ };
1835
+ var nullMediaResolver = {
1836
+ [NULL_MEDIA_RESOLVER_SENTINEL]: true,
1837
+ async loadLocalImage() {
1838
+ return "";
1839
+ },
1840
+ async loadLocalMedia() {
1841
+ return "";
1842
+ },
1843
+ async loadRemoteMedia(url) {
1844
+ return url;
1845
+ }
1846
+ };
1847
+ var defaultSchema = new Schema({
1848
+ nodes: buildNodes(nullMediaResolver),
1849
+ marks
1850
+ });
1851
+ var schemaCache = /* @__PURE__ */ new WeakMap();
1852
+ function createSchema(config) {
1853
+ if (!config || typeof config !== "object") {
1854
+ throw new TypeError("@moraya/core: createSchema() requires a config object with a MediaResolver");
1855
+ }
1856
+ if (!config.mediaResolver) {
1857
+ throw new TypeError("@moraya/core: createSchema() requires a MediaResolver");
1858
+ }
1859
+ if (isNullMediaResolver(config.mediaResolver)) {
1860
+ throw new TypeError(
1861
+ "@moraya/core: do not pass nullMediaResolver to createSchema(). That instance is reserved for parseMarkdown/serializeMarkdown internal use only. Provide a real MediaResolver implementation (e.g. BrowserMediaResolver from '@moraya/core/adapters/browser-media-resolver')."
1862
+ );
1863
+ }
1864
+ const cached = schemaCache.get(config.mediaResolver);
1865
+ if (cached) return cached;
1866
+ const schema2 = new Schema({
1867
+ nodes: buildNodes(config.mediaResolver),
1868
+ marks
1869
+ });
1870
+ schemaCache.set(config.mediaResolver, schema2);
1871
+ return schema2;
1872
+ }
1873
+
1874
+ // src/markdown.ts
1875
+ import MarkdownIt from "markdown-it";
1876
+ import deflistPlugin from "markdown-it-deflist";
1877
+ import texmathPlugin from "markdown-it-texmath";
1878
+ import { MarkdownParser, MarkdownSerializer } from "prosemirror-markdown";
1879
+ var md = new MarkdownIt({
1880
+ html: true,
1881
+ linkify: false,
1882
+ typographer: false
1883
+ }).enable(["table", "strikethrough"]).use(deflistPlugin).use(texmathPlugin);
1884
+ function tagPairedHtmlInline(tokens) {
1885
+ const VOID_RE = /^<(?:br|hr|img|input|wbr|area|base|col|embed|link|meta|param|source|track)[\s/>]/i;
1886
+ for (const token of tokens) {
1887
+ if (token.type !== "inline" || !token.children) continue;
1888
+ const children = token.children;
1889
+ const stack = [];
1890
+ for (let i = 0; i < children.length; i++) {
1891
+ const child = children[i];
1892
+ if (!child || child.type !== "html_inline") continue;
1893
+ const content = child.content;
1894
+ if (VOID_RE.test(content) || /\/>$/.test(content) || /^<!--/.test(content)) continue;
1895
+ const closeMatch = content.match(/^<\/([a-zA-Z][a-zA-Z0-9]*)\s*>$/);
1896
+ if (closeMatch && closeMatch[1]) {
1897
+ const tagName = closeMatch[1].toLowerCase();
1898
+ for (let j = stack.length - 1; j >= 0; j--) {
1899
+ const entry = stack[j];
1900
+ if (!entry) continue;
1901
+ if (entry.tagName === tagName) {
1902
+ const opener = children[entry.index];
1903
+ if (opener) {
1904
+ opener.meta = { ...opener.meta || {}, htmlPaired: true };
1905
+ }
1906
+ child.meta = { ...child.meta || {}, htmlPaired: true };
1907
+ stack.splice(j, 1);
1908
+ break;
1909
+ }
1910
+ }
1911
+ continue;
1912
+ }
1913
+ const openMatch = content.match(/^<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>$/);
1914
+ if (openMatch && openMatch[1]) {
1915
+ stack.push({ tagName: openMatch[1].toLowerCase(), index: i });
1916
+ }
1917
+ }
1918
+ }
1919
+ }
1920
+ function preserveBlankLines(tokens) {
1921
+ function mkToken(type, tag, nesting, extra) {
1922
+ return {
1923
+ type,
1924
+ tag,
1925
+ nesting,
1926
+ content: "",
1927
+ children: null,
1928
+ attrs: null,
1929
+ info: "",
1930
+ meta: null,
1931
+ map: null,
1932
+ block: true,
1933
+ hidden: false,
1934
+ level: 0,
1935
+ markup: "",
1936
+ ...extra
1937
+ };
1938
+ }
1939
+ const result = [];
1940
+ let lastTopBlockEndLine = 0;
1941
+ for (let i = 0; i < tokens.length; i++) {
1942
+ const tok = tokens[i];
1943
+ if (!tok) continue;
1944
+ if (tok.map && tok.level === 0 && (tok.nesting === 1 || tok.nesting === 0)) {
1945
+ const startLine = tok.map[0];
1946
+ const gap = startLine - lastTopBlockEndLine;
1947
+ if (gap > 1 && lastTopBlockEndLine > 0) {
1948
+ const extra = gap - 1;
1949
+ for (let j = 0; j < extra; j++) {
1950
+ result.push(
1951
+ mkToken("paragraph_open", "p", 1),
1952
+ mkToken("inline", "", 0, { level: 1, block: false, children: [] }),
1953
+ mkToken("paragraph_close", "p", -1)
1954
+ );
1955
+ }
1956
+ }
1957
+ lastTopBlockEndLine = tok.map[1];
1958
+ }
1959
+ result.push(tok);
1960
+ }
1961
+ return result;
1962
+ }
1963
+ var _origMdParse = md.parse.bind(md);
1964
+ md.parse = function(src, env) {
1965
+ let tokens = _origMdParse(src, env);
1966
+ tagPairedHtmlInline(tokens);
1967
+ tokens = preserveBlankLines(tokens);
1968
+ return tokens;
1969
+ };
1970
+ var parserTokens = {
1971
+ // ── Block tokens ──
1972
+ paragraph: { block: "paragraph" },
1973
+ blockquote: { block: "blockquote" },
1974
+ heading: {
1975
+ block: "heading",
1976
+ getAttrs(token) {
1977
+ return { level: Number(token.tag.slice(1)) };
1978
+ }
1979
+ },
1980
+ hr: { node: "horizontal_rule" },
1981
+ bullet_list: { block: "bullet_list" },
1982
+ ordered_list: {
1983
+ block: "ordered_list",
1984
+ getAttrs(token) {
1985
+ return { order: Number(token.attrGet("start") || 1) };
1986
+ }
1987
+ },
1988
+ list_item: {
1989
+ block: "list_item",
1990
+ getAttrs(_token, tokens, index) {
1991
+ let checked = null;
1992
+ for (let i = index + 1; i < tokens.length; i++) {
1993
+ const t = tokens[i];
1994
+ if (!t) continue;
1995
+ if (t.type === "inline" && t.content) {
1996
+ const match = t.content.match(/^\[( |x|X)\]\s?/);
1997
+ if (match) {
1998
+ checked = match[1] !== " ";
1999
+ t.content = t.content.slice(match[0].length);
2000
+ const children = t.children;
2001
+ if (children && children.length > 0) {
2002
+ const firstChild = children[0];
2003
+ if (firstChild.type === "text") {
2004
+ firstChild.content = firstChild.content.slice(match[0].length);
2005
+ if (!firstChild.content) {
2006
+ children.shift();
2007
+ }
2008
+ }
2009
+ }
2010
+ }
2011
+ break;
2012
+ }
2013
+ if (t.type === "list_item_close") break;
2014
+ }
2015
+ return { checked };
2016
+ }
2017
+ },
2018
+ code_block: {
2019
+ block: "code_block",
2020
+ getAttrs() {
2021
+ return { language: "text" };
2022
+ },
2023
+ noCloseToken: true
2024
+ },
2025
+ fence: {
2026
+ block: "code_block",
2027
+ getAttrs(token) {
2028
+ return { language: token.info.trim() || "text" };
2029
+ },
2030
+ noCloseToken: true
2031
+ },
2032
+ html_block: {
2033
+ block: "html_block",
2034
+ noCloseToken: true
2035
+ },
2036
+ html_inline: {
2037
+ // markdown-it emits this token for inline HTML like <br>, <span>, <sup>,
2038
+ // and HTML comments <!-- ... -->. Store raw HTML in the `value` attr.
2039
+ node: "html_inline",
2040
+ noCloseToken: true,
2041
+ getAttrs(token) {
2042
+ return { value: token.content };
2043
+ }
2044
+ },
2045
+ // ── Table tokens ──
2046
+ // NOTE: tr/th/td are NOT listed here — they are handled by custom tokenHandler
2047
+ // overrides in MorayaMarkdownParser below. The `block:` spec alone can't
2048
+ // handle (a) thead-row → table_header_row vs table_row dispatch, or
2049
+ // (b) wrapping inline content in the required paragraph child of each cell.
2050
+ table: { block: "table" },
2051
+ thead: { ignore: true },
2052
+ tbody: { ignore: true },
2053
+ // ── Definition list tokens ──
2054
+ dl: { block: "defList" },
2055
+ dt: { block: "defListTerm" },
2056
+ dd: { block: "defListDescription" },
2057
+ // ── Math tokens (from markdown-it-texmath) ──
2058
+ // Use block: spec (not node:) so token.content is added as text children,
2059
+ // correctly filling math_inline's `content: 'text*'`.
2060
+ math_inline: {
2061
+ block: "math_inline",
2062
+ noCloseToken: true
2063
+ },
2064
+ // markdown-it-texmath emits math_inline_double for $$...$$ in inline context.
2065
+ // Map to math_inline to prevent "Token type not supported" crash.
2066
+ math_inline_double: {
2067
+ block: "math_inline",
2068
+ noCloseToken: true
2069
+ },
2070
+ math_block: {
2071
+ node: "math_block",
2072
+ noCloseToken: true,
2073
+ getAttrs(token) {
2074
+ return { value: token.content.trim() };
2075
+ }
2076
+ },
2077
+ // ── Inline tokens ──
2078
+ image: {
2079
+ node: "image",
2080
+ getAttrs(token) {
2081
+ let src = token.attrGet("src") || "";
2082
+ try {
2083
+ src = decodeURIComponent(src);
2084
+ } catch {
2085
+ }
2086
+ return {
2087
+ src,
2088
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2089
+ alt: (token.children || []).map((c2) => c2.content).join("") || "",
2090
+ title: token.attrGet("title") || ""
2091
+ };
2092
+ }
2093
+ },
2094
+ hardbreak: { node: "hardbreak" },
2095
+ softbreak: { node: "hardbreak", attrs: { isInline: true } },
2096
+ // ── Mark tokens ──
2097
+ em: { mark: "em" },
2098
+ strong: { mark: "strong" },
2099
+ s: { mark: "strike_through" },
2100
+ code_inline: { mark: "code", noCloseToken: true },
2101
+ link: {
2102
+ mark: "link",
2103
+ getAttrs(token) {
2104
+ let href = token.attrGet("href") || "";
2105
+ href = href.replace(
2106
+ /%[C-F][0-9A-F](?:%[89AB][0-9A-F])+/gi,
2107
+ (m) => {
2108
+ try {
2109
+ return decodeURIComponent(m);
2110
+ } catch {
2111
+ return m;
2112
+ }
2113
+ }
2114
+ );
2115
+ return {
2116
+ href,
2117
+ title: token.attrGet("title") || null
2118
+ };
2119
+ }
2120
+ }
2121
+ };
2122
+ var MorayaMarkdownParser = class extends MarkdownParser {
2123
+ /**
2124
+ * The schema this parser instance is bound to. Captured for use in
2125
+ * tokenHandler overrides (tr_open / th_open / etc.) so they reference the
2126
+ * caller-provided schema rather than the module-level defaultSchema.
2127
+ */
2128
+ schema;
2129
+ constructor(schemaArg = defaultSchema) {
2130
+ super(schemaArg, md, parserTokens);
2131
+ this.schema = schemaArg;
2132
+ const h = (
2133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2134
+ this.tokenHandlers
2135
+ );
2136
+ function cellAlignment(tok) {
2137
+ const style = tok.attrGet("style") || "";
2138
+ const m = style.match(/text-align:\s*(\w+)/);
2139
+ return m && m[1] ? m[1] : "left";
2140
+ }
2141
+ h["tr_open"] = (state, _tok, tokens, i) => {
2142
+ let inThead = false;
2143
+ for (let j = i - 1; j >= 0; j--) {
2144
+ if (tokens[j].type === "thead_open") {
2145
+ inThead = true;
2146
+ break;
2147
+ }
2148
+ if (tokens[j].type === "thead_close" || tokens[j].type === "tbody_open") break;
2149
+ }
2150
+ state.openNode(inThead ? schemaArg.nodes.table_header_row : schemaArg.nodes.table_row, null);
2151
+ };
2152
+ h["tr_close"] = (state) => state.closeNode();
2153
+ h["th_open"] = (state, tok) => {
2154
+ state.openNode(schemaArg.nodes.table_header, { alignment: cellAlignment(tok) });
2155
+ state.openNode(schemaArg.nodes.paragraph, null);
2156
+ };
2157
+ h["th_close"] = (state) => {
2158
+ state.closeNode();
2159
+ state.closeNode();
2160
+ };
2161
+ h["td_open"] = (state, tok) => {
2162
+ state.openNode(schemaArg.nodes.table_cell, { alignment: cellAlignment(tok) });
2163
+ state.openNode(schemaArg.nodes.paragraph, null);
2164
+ };
2165
+ h["td_close"] = (state) => {
2166
+ state.closeNode();
2167
+ state.closeNode();
2168
+ };
2169
+ const defaultLinkOpen = h["link_open"];
2170
+ const defaultLinkClose = h["link_close"];
2171
+ h["link_open"] = (state, tok, tokens, i) => {
2172
+ let hasContent = false;
2173
+ for (let j = i + 1; j < tokens.length; j++) {
2174
+ if (tokens[j].type === "link_close") break;
2175
+ if (tokens[j].type === "text" && tokens[j].content) {
2176
+ hasContent = true;
2177
+ break;
2178
+ }
2179
+ if (["image", "code_inline", "softbreak", "hardbreak", "html_inline"].includes(tokens[j].type)) {
2180
+ hasContent = true;
2181
+ break;
2182
+ }
2183
+ }
2184
+ if (!hasContent) {
2185
+ let href = tok.attrGet("href") || "";
2186
+ href = href.replace(
2187
+ /%[C-F][0-9A-F](?:%[89AB][0-9A-F])+/gi,
2188
+ (m) => {
2189
+ try {
2190
+ return decodeURIComponent(m);
2191
+ } catch {
2192
+ return m;
2193
+ }
2194
+ }
2195
+ );
2196
+ const title = tok.attrGet("title");
2197
+ let literal = `[](${href}`;
2198
+ if (title) literal += ` "${title}"`;
2199
+ literal += ")";
2200
+ state.addText(literal);
2201
+ for (let j = i + 1; j < tokens.length; j++) {
2202
+ if (tokens[j].type === "link_close") {
2203
+ tokens[j].meta = { ...tokens[j].meta || {}, skipClose: true };
2204
+ break;
2205
+ }
2206
+ }
2207
+ return;
2208
+ }
2209
+ defaultLinkOpen(state, tok, tokens, i);
2210
+ };
2211
+ h["link_close"] = (state, tok, tokens, i) => {
2212
+ if (tok.meta?.skipClose) return;
2213
+ defaultLinkClose(state, tok, tokens, i);
2214
+ };
2215
+ const defaultTextHandler = h["text"];
2216
+ h["text"] = (state, tok, toks, ii) => {
2217
+ if (tok.meta?.mediaSkip) return;
2218
+ defaultTextHandler(state, tok, toks, ii);
2219
+ };
2220
+ h["html_inline"] = (state, tok, tokens, i) => {
2221
+ if (tok.meta?.mediaSkip) return;
2222
+ const content = tok.content;
2223
+ const mediaMatch = content.match(/^<(audio|video)\b/i);
2224
+ if (mediaMatch && mediaMatch[1]) {
2225
+ const tagName = mediaMatch[1].toLowerCase();
2226
+ const closeRe = new RegExp(`^</${tagName}\\s*>$`, "i");
2227
+ let fullHtml = content;
2228
+ for (let j = i + 1; j < tokens.length; j++) {
2229
+ const t = tokens[j];
2230
+ if (t.type === "html_inline" && closeRe.test(t.content.trim())) {
2231
+ fullHtml += t.content;
2232
+ t.meta = { ...t.meta || {}, mediaSkip: true };
2233
+ break;
2234
+ }
2235
+ if (t.content) fullHtml += t.content;
2236
+ t.meta = { ...t.meta || {}, mediaSkip: true };
2237
+ }
2238
+ state.addNode(schemaArg.nodes.html_inline, { value: fullHtml });
2239
+ return;
2240
+ }
2241
+ if (tok.meta?.htmlPaired) {
2242
+ const htmlMark = schemaArg.marks.html_mark;
2243
+ if (!htmlMark) {
2244
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
2245
+ return;
2246
+ }
2247
+ if (!content.startsWith("</")) {
2248
+ const tagMatch = content.match(/^<([a-zA-Z][a-zA-Z0-9]*)/);
2249
+ const tagName = tagMatch && tagMatch[1] ? tagMatch[1].toLowerCase() : "";
2250
+ state.openMark(htmlMark.create({
2251
+ openTag: content,
2252
+ closeTag: `</${tagName}>`
2253
+ }));
2254
+ } else {
2255
+ state.closeMark(htmlMark);
2256
+ }
2257
+ return;
2258
+ }
2259
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
2260
+ };
2261
+ const defaultHtmlBlock = h["html_block"];
2262
+ h["html_block"] = (state, tok, tokens, i) => {
2263
+ const content = tok.content.trim();
2264
+ if (/^<img\s/i.test(content)) {
2265
+ const imgPattern = /<img\s[^>]*\/?>/gi;
2266
+ const imgs = content.match(imgPattern);
2267
+ state.openNode(schemaArg.nodes.paragraph, null);
2268
+ if (imgs && imgs.length > 0) {
2269
+ for (let j = 0; j < imgs.length; j++) {
2270
+ if (j > 0) {
2271
+ state.addNode(schemaArg.nodes.hardbreak, { isInline: true });
2272
+ }
2273
+ state.addNode(schemaArg.nodes.html_inline, { value: imgs[j] });
2274
+ }
2275
+ } else {
2276
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
2277
+ }
2278
+ state.closeNode();
2279
+ } else if (/^<(video|audio)\b/i.test(content)) {
2280
+ state.openNode(schemaArg.nodes.paragraph, null);
2281
+ state.addNode(schemaArg.nodes.html_inline, { value: content });
2282
+ state.closeNode();
2283
+ } else {
2284
+ defaultHtmlBlock(state, tok, tokens, i);
2285
+ }
2286
+ };
2287
+ }
2288
+ };
2289
+ var defaultParser = new MorayaMarkdownParser(defaultSchema);
2290
+ var parserCache = /* @__PURE__ */ new WeakMap();
2291
+ parserCache.set(defaultSchema, defaultParser);
2292
+ function getParserFor(schema2) {
2293
+ if (!schema2 || schema2 === defaultSchema) return defaultParser;
2294
+ let p = parserCache.get(schema2);
2295
+ if (!p) {
2296
+ p = new MorayaMarkdownParser(schema2);
2297
+ parserCache.set(schema2, p);
2298
+ }
2299
+ return p;
2300
+ }
2301
+ var serializer = new MarkdownSerializer(
2302
+ {
2303
+ // ── Block nodes ──
2304
+ doc(state, node) {
2305
+ state.renderContent(node);
2306
+ },
2307
+ paragraph(state, node) {
2308
+ if (node.content.size === 0) {
2309
+ state.write("");
2310
+ } else {
2311
+ state.renderInline(node);
2312
+ }
2313
+ state.closeBlock(node);
2314
+ },
2315
+ heading(state, node) {
2316
+ state.write(`${"#".repeat(node.attrs.level)} `);
2317
+ state.renderInline(node, false);
2318
+ state.closeBlock(node);
2319
+ },
2320
+ blockquote(state, node) {
2321
+ state.wrapBlock("> ", null, node, () => state.renderContent(node));
2322
+ },
2323
+ code_block(state, node) {
2324
+ const lang = node.attrs.language || "";
2325
+ const fenceLang = lang === "text" ? "" : lang;
2326
+ state.write(`\`\`\`${fenceLang}
2327
+ `);
2328
+ state.text(node.textContent, false);
2329
+ state.ensureNewLine();
2330
+ state.write("```");
2331
+ state.closeBlock(node);
2332
+ },
2333
+ horizontal_rule(state, node) {
2334
+ state.write("---");
2335
+ state.closeBlock(node);
2336
+ },
2337
+ bullet_list(state, node) {
2338
+ state.renderList(node, " ", () => "- ");
2339
+ },
2340
+ ordered_list(state, node) {
2341
+ const start = node.attrs.order || 1;
2342
+ state.renderList(node, " ", (i) => `${start + i}. `);
2343
+ },
2344
+ list_item(state, node) {
2345
+ if (node.attrs.checked != null) {
2346
+ const checkbox = node.attrs.checked ? "[x] " : "[ ] ";
2347
+ state.write(checkbox);
2348
+ }
2349
+ state.renderContent(node);
2350
+ },
2351
+ image(state, node) {
2352
+ const alt = state.esc(node.attrs.alt || "", false);
2353
+ const src = node.attrs.src || "";
2354
+ const title = node.attrs.title;
2355
+ if (title) {
2356
+ state.write(`![${alt}](${src} "${state.esc(title, false)}")`);
2357
+ } else {
2358
+ state.write(`![${alt}](${src})`);
2359
+ }
2360
+ },
2361
+ hardbreak(state) {
2362
+ state.write(" \n");
2363
+ },
2364
+ html_block(state, node) {
2365
+ state.text(node.textContent, false);
2366
+ state.closeBlock(node);
2367
+ },
2368
+ html_inline(state, node) {
2369
+ state.text(node.attrs.value, false);
2370
+ },
2371
+ // ── Table nodes ──
2372
+ table(state, node) {
2373
+ const alignments = [];
2374
+ const headerRow = node.child(0);
2375
+ headerRow.forEach((cell) => {
2376
+ alignments.push(cell.attrs.alignment || "left");
2377
+ });
2378
+ renderTableRow(state, headerRow);
2379
+ const sep = alignments.map((a) => {
2380
+ switch (a) {
2381
+ case "center":
2382
+ return ":---:";
2383
+ case "right":
2384
+ return "---:";
2385
+ default:
2386
+ return "---";
2387
+ }
2388
+ });
2389
+ state.write(`| ${sep.join(" | ")} |`);
2390
+ state.ensureNewLine();
2391
+ for (let i = 1; i < node.childCount; i++) {
2392
+ renderTableRow(state, node.child(i));
2393
+ }
2394
+ state.closeBlock(node);
2395
+ },
2396
+ table_header_row() {
2397
+ },
2398
+ table_row() {
2399
+ },
2400
+ table_header(state, node) {
2401
+ state.renderInline(node.firstChild);
2402
+ },
2403
+ table_cell(state, node) {
2404
+ state.renderInline(node.firstChild);
2405
+ },
2406
+ // ── Math nodes ──
2407
+ math_inline(state, node) {
2408
+ state.write(`$${node.textContent}$`);
2409
+ },
2410
+ math_block(state, node) {
2411
+ state.write("$$\n");
2412
+ state.text(node.attrs.value || node.textContent, false);
2413
+ state.ensureNewLine();
2414
+ state.write("$$");
2415
+ state.closeBlock(node);
2416
+ },
2417
+ // ── Definition list nodes ──
2418
+ defList(state, node) {
2419
+ state.renderContent(node);
2420
+ },
2421
+ defListTerm(state, node) {
2422
+ state.renderInline(node);
2423
+ state.closeBlock(node);
2424
+ },
2425
+ defListDescription(state, node) {
2426
+ state.write(": ");
2427
+ state.renderContent(node);
2428
+ },
2429
+ // ── Fallback for text node (shouldn't be needed but safe) ──
2430
+ text(state, node) {
2431
+ state.text(node.text || "");
2432
+ }
2433
+ },
2434
+ {
2435
+ // ── Mark serializers ──
2436
+ strong: {
2437
+ open: "**",
2438
+ close: "**",
2439
+ mixable: true,
2440
+ expelEnclosingWhitespace: true
2441
+ },
2442
+ em: {
2443
+ open: "*",
2444
+ close: "*",
2445
+ mixable: true,
2446
+ expelEnclosingWhitespace: true
2447
+ },
2448
+ code: {
2449
+ open(_state, mark, parent, index) {
2450
+ return isPlainURL(mark, parent, index, 1) ? "" : "`";
2451
+ },
2452
+ close(_state, mark, parent, index) {
2453
+ return isPlainURL(mark, parent, index, -1) ? "" : "`";
2454
+ },
2455
+ escape: false
2456
+ },
2457
+ link: {
2458
+ open(_state, mark, parent, index) {
2459
+ return isPlainURL(mark, parent, index, 1) ? "<" : "[";
2460
+ },
2461
+ close(state, mark, parent, index) {
2462
+ const href = mark.attrs.href;
2463
+ const title = mark.attrs.title;
2464
+ if (isPlainURL(mark, parent, index, -1)) {
2465
+ return ">";
2466
+ }
2467
+ return title ? `](${href} "${state.esc(title, false)}")` : `](${href})`;
2468
+ },
2469
+ mixable: false
2470
+ },
2471
+ strike_through: {
2472
+ open: "~~",
2473
+ close: "~~",
2474
+ mixable: true,
2475
+ expelEnclosingWhitespace: true
2476
+ },
2477
+ html_mark: {
2478
+ open(_state, mark) {
2479
+ return mark.attrs.openTag;
2480
+ },
2481
+ close(_state, mark) {
2482
+ return mark.attrs.closeTag;
2483
+ }
2484
+ }
2485
+ },
2486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2487
+ {
2488
+ hardBreakNodeName: "hardbreak",
2489
+ strict: false
2490
+ }
2491
+ );
2492
+ function renderTableRow(state, row) {
2493
+ const cells = [];
2494
+ const s = state;
2495
+ row.forEach((cell) => {
2496
+ const parts = [];
2497
+ cell.forEach((para) => {
2498
+ if (para.type.name !== "paragraph") return;
2499
+ const savedOut = s.out;
2500
+ const savedClosed = s.closed;
2501
+ s.out = "";
2502
+ s.closed = null;
2503
+ state.renderInline(para);
2504
+ const piece = s.out.replace(/\n/g, " ").trim();
2505
+ s.out = savedOut;
2506
+ s.closed = savedClosed;
2507
+ parts.push(piece);
2508
+ });
2509
+ cells.push(parts.join(" "));
2510
+ });
2511
+ state.write(`| ${cells.join(" | ")} |`);
2512
+ state.ensureNewLine();
2513
+ }
2514
+ function isPlainURL(mark, parent, index, side) {
2515
+ if (mark.attrs.title || !/^\w+:/.test(mark.attrs.href)) return false;
2516
+ const content = parent.child(index + (side < 0 ? -1 : 0));
2517
+ if (!content.isText || content.text !== mark.attrs.href || content.marks[content.marks.length - 1] !== mark) {
2518
+ return false;
2519
+ }
2520
+ if (index === (side < 0 ? 1 : parent.childCount - 1)) return true;
2521
+ const next = parent.child(index + (side < 0 ? -2 : 1));
2522
+ return !mark.isInSet(next.marks);
2523
+ }
2524
+ function normalizeMathBlocks(text2) {
2525
+ if (!text2.includes("$$")) return text2;
2526
+ const lines = text2.split("\n");
2527
+ const result = [];
2528
+ let inFence = false;
2529
+ let inMathBlock = false;
2530
+ for (let i = 0; i < lines.length; i++) {
2531
+ const line = lines[i] ?? "";
2532
+ const trimmed = line.trim();
2533
+ if (!inMathBlock && /^(`{3,}|~{3,})/.test(trimmed)) {
2534
+ inFence = !inFence;
2535
+ result.push(line);
2536
+ continue;
2537
+ }
2538
+ if (inFence) {
2539
+ result.push(line);
2540
+ continue;
2541
+ }
2542
+ if (trimmed === "$$") {
2543
+ if (!inMathBlock) {
2544
+ const last = result[result.length - 1];
2545
+ if (result.length > 0 && last !== void 0 && last.trim() !== "") {
2546
+ result.push("");
2547
+ }
2548
+ result.push(line);
2549
+ inMathBlock = true;
2550
+ } else {
2551
+ result.push(line);
2552
+ inMathBlock = false;
2553
+ const next = lines[i + 1];
2554
+ if (next !== void 0 && next.trim() !== "") {
2555
+ result.push("");
2556
+ }
2557
+ }
2558
+ } else {
2559
+ result.push(line);
2560
+ }
2561
+ }
2562
+ return result.join("\n");
2563
+ }
2564
+ function normalizeSmartQuotes(text2) {
2565
+ if (!/[“”„‟‘’‚‛]/.test(text2)) return text2;
2566
+ return text2.replace(
2567
+ /(\]\([^\n)]*\s)“([^”\n]*)”(\s*\))/g,
2568
+ (_m, pre, title, post) => `${pre}"${title}"${post}`
2569
+ ).replace(
2570
+ /(\]\([^\n)]*\s)“([^”\n]*)”(\s*\))/g,
2571
+ (_m, pre, title, post) => `${pre}"${title}"${post}`
2572
+ ).replace(
2573
+ /(\]\([^\n)]*\s)‘([^’\n]*)’(\s*\))/g,
2574
+ (_m, pre, title, post) => `${pre}'${title}'${post}`
2575
+ );
2576
+ }
2577
+ function parseMarkdown(markdown2, schemaArg) {
2578
+ const p = getParserFor(schemaArg);
2579
+ try {
2580
+ return p.parse(normalizeSmartQuotes(normalizeMathBlocks(markdown2)));
2581
+ } catch (err) {
2582
+ if (typeof console !== "undefined" && console.warn) {
2583
+ console.warn("[parseMarkdown] best-effort fallback for malformed input:", err);
2584
+ }
2585
+ return p.schema.topNodeType.createAndFill();
2586
+ }
2587
+ }
2588
+ var ASYNC_PARSE_THRESHOLD = 5e4;
2589
+ function parseMarkdownAsync(markdown2, schemaArg) {
2590
+ const p = getParserFor(schemaArg);
2591
+ const normalized = normalizeSmartQuotes(normalizeMathBlocks(markdown2));
2592
+ if (normalized.length < ASYNC_PARSE_THRESHOLD) {
2593
+ return Promise.resolve(parseMarkdown(normalized, schemaArg));
2594
+ }
2595
+ return new Promise((resolve) => setTimeout(() => {
2596
+ try {
2597
+ resolve(p.parse(normalized));
2598
+ } catch {
2599
+ resolve(p.schema.topNodeType.createAndFill());
2600
+ }
2601
+ }, 0));
2602
+ }
2603
+ function serializeMarkdown(doc2) {
2604
+ let result = serializer.serialize(doc2, { tightLists: true });
2605
+ result = result.replace(/\\\[([^\\\[\]]*)\\\]\(([^)]*)\)/g, "[$1]($2)");
2606
+ result = result.replace(/​/g, "");
2607
+ return result;
2608
+ }
2609
+
2610
+ // src/setup.ts
2611
+ import {
2612
+ AllSelection as AllSelection2,
2613
+ EditorState,
2614
+ NodeSelection,
2615
+ Plugin as Plugin8,
2616
+ PluginKey as PluginKey8,
2617
+ Selection,
2618
+ TextSelection as TextSelection4
2619
+ } from "prosemirror-state";
2620
+ import { Decoration as Decoration5, DecorationSet as DecorationSet5, EditorView } from "prosemirror-view";
2621
+ import { keymap } from "prosemirror-keymap";
2622
+ import { history, redo, undo } from "prosemirror-history";
2623
+ import {
2624
+ baseKeymap,
2625
+ joinForward,
2626
+ setBlockType as setBlockType2,
2627
+ toggleMark as toggleMark2,
2628
+ wrapIn as wrapIn2
2629
+ } from "prosemirror-commands";
2630
+ import {
2631
+ inputRules,
2632
+ textblockTypeInputRule,
2633
+ wrappingInputRule as wrappingInputRule2,
2634
+ InputRule
2635
+ } from "prosemirror-inputrules";
2636
+ import {
2637
+ liftListItem as liftListItem2,
2638
+ sinkListItem,
2639
+ splitListItem
2640
+ } from "prosemirror-schema-list";
2641
+ import { dropCursor } from "prosemirror-dropcursor";
2642
+ import { columnResizing } from "prosemirror-tables";
2643
+
2644
+ // src/commands.ts
2645
+ import {
2646
+ toggleMark,
2647
+ setBlockType,
2648
+ wrapIn,
2649
+ lift
2650
+ } from "prosemirror-commands";
2651
+ import { wrapInList, liftListItem } from "prosemirror-schema-list";
2652
+ var schema = defaultSchema;
2653
+ function markType(name) {
2654
+ const m = schema.marks[name];
2655
+ if (!m) throw new Error(`[@moraya/core] mark "${name}" not in schema`);
2656
+ return m;
2657
+ }
2658
+ function nodeType(name) {
2659
+ const n = schema.nodes[name];
2660
+ if (!n) throw new Error(`[@moraya/core] node "${name}" not in schema`);
2661
+ return n;
2662
+ }
2663
+ var toggleBold = (state, dispatch) => toggleMark(markType("strong"))(state, dispatch);
2664
+ var toggleItalic = (state, dispatch) => toggleMark(markType("em"))(state, dispatch);
2665
+ var toggleStrikethrough = (state, dispatch) => toggleMark(markType("strike_through"))(state, dispatch);
2666
+ var toggleCode = (state, dispatch) => toggleMark(markType("code"))(state, dispatch);
2667
+ function setHeading(level) {
2668
+ return (state, dispatch) => setBlockType(nodeType("heading"), { level })(state, dispatch);
2669
+ }
2670
+ var toggleBlockquote = (state, dispatch) => {
2671
+ const $from = state.selection.$from;
2672
+ for (let d = $from.depth; d > 0; d--) {
2673
+ if ($from.node(d).type.name === "blockquote") {
2674
+ return lift(state, dispatch);
2675
+ }
2676
+ }
2677
+ return wrapIn(nodeType("blockquote"))(state, dispatch);
2678
+ };
2679
+ var toggleOrderedList = (state, dispatch) => wrapInList(nodeType("ordered_list"))(state, dispatch);
2680
+ var toggleBulletList = (state, dispatch) => wrapInList(nodeType("bullet_list"))(state, dispatch);
2681
+ function makeToggleList(typeName) {
2682
+ return (state, dispatch, view) => {
2683
+ const listType = state.schema.nodes[typeName];
2684
+ const listItemType = state.schema.nodes.list_item;
2685
+ if (!listType || !listItemType) return false;
2686
+ const { $from } = state.selection;
2687
+ for (let d = $from.depth; d >= 0; d--) {
2688
+ if ($from.node(d).type === listType) {
2689
+ return liftListItem(listItemType)(state, dispatch, view);
2690
+ }
2691
+ }
2692
+ return wrapInList(listType)(state, dispatch, view);
2693
+ };
2694
+ }
2695
+ var wrapInBulletList = makeToggleList("bullet_list");
2696
+ var wrapInOrderedList = makeToggleList("ordered_list");
2697
+ var wrapInTaskList = (state, dispatch) => {
2698
+ const bulletListType = state.schema.nodes.bullet_list;
2699
+ const listItemType = state.schema.nodes.list_item;
2700
+ if (!bulletListType || !listItemType) return false;
2701
+ if (!wrapInList(bulletListType)(state)) return false;
2702
+ if (!dispatch) return true;
2703
+ let listTr;
2704
+ wrapInList(bulletListType)(state, (tr) => {
2705
+ listTr = tr;
2706
+ });
2707
+ if (!listTr) return false;
2708
+ const { from, to } = listTr.selection;
2709
+ const updates = [];
2710
+ listTr.doc.nodesBetween(
2711
+ Math.max(0, from - 200),
2712
+ Math.min(listTr.doc.content.size, to + 200),
2713
+ (node, pos) => {
2714
+ if (node.type === listItemType && node.attrs.checked === null) {
2715
+ updates.push({ pos, attrs: { ...node.attrs, checked: false } });
2716
+ }
2717
+ }
2718
+ );
2719
+ for (let i = updates.length - 1; i >= 0; i--) {
2720
+ const u = updates[i];
2721
+ listTr.setNodeMarkup(u.pos, void 0, u.attrs);
2722
+ }
2723
+ dispatch(listTr.scrollIntoView());
2724
+ return true;
2725
+ };
2726
+ var toggleCodeBlock = (state, dispatch) => {
2727
+ const cb = nodeType("code_block");
2728
+ if (state.selection.$from.parent.type === cb) {
2729
+ return setBlockType(nodeType("paragraph"))(state, dispatch);
2730
+ }
2731
+ return setBlockType(cb)(state, dispatch);
2732
+ };
2733
+ var insertHorizontalRule = (state, dispatch) => {
2734
+ if (dispatch) {
2735
+ dispatch(state.tr.replaceSelectionWith(nodeType("horizontal_rule").create()));
2736
+ }
2737
+ return true;
2738
+ };
2739
+ var insertTable = (state, dispatch) => {
2740
+ const tableType = schema.nodes.table;
2741
+ if (!tableType) {
2742
+ if (dispatch) {
2743
+ const text2 = "\n\n| Col 1 | Col 2 | Col 3 |\n|---|---|---|\n| | | |\n| | | |\n\n";
2744
+ dispatch(state.tr.insertText(text2));
2745
+ }
2746
+ return true;
2747
+ }
2748
+ return false;
2749
+ };
2750
+ var insertMathBlock = (state, dispatch) => {
2751
+ const mathBlock = schema.nodes.math_block;
2752
+ if (!mathBlock) {
2753
+ if (dispatch) {
2754
+ dispatch(state.tr.insertText("\n$$\n\\\n$$\n"));
2755
+ }
2756
+ return true;
2757
+ }
2758
+ if (dispatch) {
2759
+ const node = mathBlock.create({ value: "" });
2760
+ dispatch(state.tr.replaceSelectionWith(node));
2761
+ }
2762
+ return true;
2763
+ };
2764
+ function toggleLink(href) {
2765
+ return (state, dispatch) => {
2766
+ const link2 = markType("link");
2767
+ const { from, to } = state.selection;
2768
+ if (state.doc.rangeHasMark(from, to, link2)) {
2769
+ if (dispatch) dispatch(state.tr.removeMark(from, to, link2));
2770
+ return true;
2771
+ }
2772
+ if (!href) return false;
2773
+ if (dispatch) {
2774
+ dispatch(state.tr.addMark(from, to, link2.create({ href })));
2775
+ }
2776
+ return true;
2777
+ };
2778
+ }
2779
+ function insertImage(src, alt) {
2780
+ return (state, dispatch) => {
2781
+ const img = nodeType("image").create({ src, alt: alt ?? null });
2782
+ if (dispatch) {
2783
+ dispatch(state.tr.replaceSelectionWith(img));
2784
+ }
2785
+ return true;
2786
+ };
2787
+ }
2788
+
2789
+ // src/plugins/definition-list.ts
2790
+ import { wrappingInputRule } from "prosemirror-inputrules";
2791
+ function createDefListInputRule(schema2) {
2792
+ return wrappingInputRule(
2793
+ /^:\s{3}$/,
2794
+ schema2.nodes.defListDescription
2795
+ );
2796
+ }
2797
+
2798
+ // src/plugins/enter-handler.ts
2799
+ import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
2800
+ import {
2801
+ splitBlock,
2802
+ chainCommands,
2803
+ newlineInCode,
2804
+ createParagraphNear,
2805
+ liftEmptyBlock
2806
+ } from "prosemirror-commands";
2807
+ import { addRowAfter } from "prosemirror-tables";
2808
+ var enterHandlerKey = new PluginKey("moraya-enter-handler");
2809
+ function parsePipeTableHeader(text2) {
2810
+ if (!/^\|(.+\|)+\s*$/.test(text2)) return null;
2811
+ const cells = text2.split("|").slice(1, -1).map((s) => s.trim());
2812
+ if (cells.length < 2) return null;
2813
+ if (cells.every((c2) => /^:?-+:?$/.test(c2))) return null;
2814
+ return cells;
2815
+ }
2816
+ function buildTableFromHeaders(schema2, headers) {
2817
+ const tableType = schema2.nodes.table;
2818
+ const headerRowType = schema2.nodes.table_header_row;
2819
+ const dataRowType = schema2.nodes.table_row;
2820
+ const headerCellType = schema2.nodes.table_header;
2821
+ const dataCellType = schema2.nodes.table_cell;
2822
+ const paragraphType = schema2.nodes.paragraph;
2823
+ if (!tableType || !headerRowType || !dataRowType || !headerCellType || !dataCellType || !paragraphType) {
2824
+ return null;
2825
+ }
2826
+ const headerCells = headers.map((text2) => {
2827
+ const para = text2 ? paragraphType.create(null, schema2.text(text2)) : paragraphType.create();
2828
+ return headerCellType.create({ alignment: "left" }, para);
2829
+ });
2830
+ const emptyCells = headers.map(
2831
+ () => dataCellType.createAndFill({ alignment: "left" })
2832
+ );
2833
+ const headerRow = headerRowType.create(null, headerCells);
2834
+ const dataRow = dataRowType.create(null, emptyCells);
2835
+ return tableType.create(null, [headerRow, dataRow]);
2836
+ }
2837
+ function createEnterHandlerPlugin() {
2838
+ const enterCommand = chainCommands(
2839
+ newlineInCode,
2840
+ createParagraphNear,
2841
+ liftEmptyBlock,
2842
+ splitBlock
2843
+ );
2844
+ return new Plugin({
2845
+ key: enterHandlerKey,
2846
+ props: {
2847
+ handleKeyDown(view, event) {
2848
+ if (event.isComposing || event.key !== "Enter") return false;
2849
+ const { $from } = view.state.selection;
2850
+ let inTable = false;
2851
+ let cellDepth = -1;
2852
+ let inListItem = false;
2853
+ for (let d = $from.depth; d > 0; d--) {
2854
+ const nodeName = $from.node(d).type.name;
2855
+ if (nodeName === "table_cell" || nodeName === "table_header") {
2856
+ inTable = true;
2857
+ cellDepth = d;
2858
+ break;
2859
+ }
2860
+ if (nodeName === "list_item") {
2861
+ inListItem = true;
2862
+ break;
2863
+ }
2864
+ }
2865
+ if (inTable) {
2866
+ if ((event.ctrlKey || event.metaKey) && !event.shiftKey) {
2867
+ event.preventDefault();
2868
+ addRowAfter(view.state, view.dispatch);
2869
+ const { $from: $cur } = view.state.selection;
2870
+ for (let d = $cur.depth; d > 0; d--) {
2871
+ const name = $cur.node(d).type.name;
2872
+ if (name === "table_row" || name === "table_header_row") {
2873
+ try {
2874
+ const rowEnd = $cur.after(d);
2875
+ const $newRow = view.state.doc.resolve(rowEnd + 1);
2876
+ view.dispatch(
2877
+ view.state.tr.setSelection(TextSelection.near($newRow)).scrollIntoView()
2878
+ );
2879
+ } catch {
2880
+ }
2881
+ break;
2882
+ }
2883
+ }
2884
+ return true;
2885
+ }
2886
+ if (event.shiftKey && !event.ctrlKey && !event.metaKey) {
2887
+ event.preventDefault();
2888
+ const hardbreak2 = view.state.schema.nodes.hardbreak;
2889
+ if (hardbreak2) {
2890
+ const tr = view.state.tr.replaceSelectionWith(hardbreak2.create({ isInline: false }));
2891
+ view.dispatch(tr.scrollIntoView());
2892
+ }
2893
+ return true;
2894
+ }
2895
+ if (!event.shiftKey && !event.ctrlKey && !event.metaKey) {
2896
+ event.preventDefault();
2897
+ if (cellDepth < 2) return true;
2898
+ const rowDepth = cellDepth - 1;
2899
+ const tableDepth = cellDepth - 2;
2900
+ const colIndex = $from.index(rowDepth);
2901
+ const rowIndex = $from.index(tableDepth);
2902
+ const tableNode = $from.node(tableDepth);
2903
+ const tableStart = $from.start(tableDepth);
2904
+ if (rowIndex === tableNode.childCount - 1) {
2905
+ const tableEnd = $from.after(tableDepth);
2906
+ const afterNode = view.state.doc.nodeAt(tableEnd);
2907
+ if (afterNode) {
2908
+ const $target = view.state.doc.resolve(tableEnd + 1);
2909
+ view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView());
2910
+ } else {
2911
+ const paragraph2 = view.state.schema.nodes.paragraph;
2912
+ if (paragraph2) {
2913
+ const tr = view.state.tr.insert(tableEnd, paragraph2.create());
2914
+ const $target = tr.doc.resolve(tableEnd + 1);
2915
+ tr.setSelection(TextSelection.near($target));
2916
+ view.dispatch(tr.scrollIntoView());
2917
+ }
2918
+ }
2919
+ } else {
2920
+ const nextRow = tableNode.child(rowIndex + 1);
2921
+ const safeCol = Math.min(colIndex, nextRow.childCount - 1);
2922
+ let targetPos = tableStart;
2923
+ for (let r2 = 0; r2 <= rowIndex; r2++) {
2924
+ targetPos += tableNode.child(r2).nodeSize;
2925
+ }
2926
+ targetPos += 1;
2927
+ for (let c2 = 0; c2 < safeCol; c2++) {
2928
+ targetPos += nextRow.child(c2).nodeSize;
2929
+ }
2930
+ targetPos += 1;
2931
+ const $target = view.state.doc.resolve(targetPos);
2932
+ view.dispatch(view.state.tr.setSelection(TextSelection.near($target)).scrollIntoView());
2933
+ }
2934
+ return true;
2935
+ }
2936
+ return false;
2937
+ }
2938
+ if (inListItem) return false;
2939
+ if (!event.shiftKey && !event.metaKey && !event.ctrlKey) {
2940
+ if ($from.parent.type.name === "paragraph") {
2941
+ const text2 = $from.parent.textContent;
2942
+ const match = $from.parentOffset === text2.length ? text2.match(/^```(\S*)\s*$/) : null;
2943
+ if (match) {
2944
+ const language = match[1] ?? "";
2945
+ const codeBlockType = view.state.schema.nodes.code_block;
2946
+ if (codeBlockType) {
2947
+ const pos = $from.before();
2948
+ const end = $from.after();
2949
+ const tr = view.state.tr;
2950
+ tr.replaceWith(pos, end, codeBlockType.create({ language }));
2951
+ view.dispatch(tr);
2952
+ return true;
2953
+ }
2954
+ }
2955
+ const headers = $from.parentOffset === text2.length ? parsePipeTableHeader(text2) : null;
2956
+ if (headers) {
2957
+ const $para = view.state.doc.resolve($from.before());
2958
+ const parentNode = $para.node($para.depth);
2959
+ const tableType = view.state.schema.nodes.table;
2960
+ if (tableType && parentNode.type.contentMatch.matchType(tableType)) {
2961
+ const tableNode = buildTableFromHeaders(view.state.schema, headers);
2962
+ if (tableNode) {
2963
+ const pos = $from.before();
2964
+ const end = $from.after();
2965
+ const tr = view.state.tr;
2966
+ tr.replaceWith(pos, end, tableNode);
2967
+ const inserted = tr.doc.nodeAt(pos);
2968
+ if (inserted && inserted.childCount >= 2) {
2969
+ const headerRowSize = inserted.child(0).nodeSize;
2970
+ const $dataRow = tr.doc.resolve(pos + 1 + headerRowSize + 1);
2971
+ tr.setSelection(TextSelection.near($dataRow));
2972
+ }
2973
+ tr.scrollIntoView();
2974
+ view.dispatch(tr);
2975
+ return true;
2976
+ }
2977
+ }
2978
+ }
2979
+ }
2980
+ return enterCommand(view.state, view.dispatch, view);
2981
+ }
2982
+ return false;
2983
+ }
2984
+ }
2985
+ });
2986
+ }
2987
+
2988
+ // src/plugins/cursor-syntax.ts
2989
+ import { Plugin as Plugin2, PluginKey as PluginKey2 } from "prosemirror-state";
2990
+ import { Decoration, DecorationSet } from "prosemirror-view";
2991
+ var pluginKey = new PluginKey2("moraya-cursor-syntax");
2992
+ var HEADING_PREFIX = {
2993
+ 1: "# ",
2994
+ 2: "## ",
2995
+ 3: "### ",
2996
+ 4: "#### ",
2997
+ 5: "##### ",
2998
+ 6: "###### "
2999
+ };
3000
+ var MARK_DELIMITERS = {
3001
+ strong: { open: "**", close: "**" },
3002
+ em: { open: "*", close: "*" },
3003
+ code: { open: "`", close: "`" },
3004
+ strike_through: { open: "~~", close: "~~" }
3005
+ };
3006
+ function makeWidget(text2, className) {
3007
+ return () => {
3008
+ const span = document.createElement("span");
3009
+ span.className = className;
3010
+ span.textContent = text2;
3011
+ return span;
3012
+ };
3013
+ }
3014
+ function getMarkRange(state, pos, markType2) {
3015
+ const $pos = state.doc.resolve(pos);
3016
+ const parent = $pos.parent;
3017
+ if (!parent.isTextblock) return null;
3018
+ const base = $pos.start();
3019
+ const runs = [];
3020
+ let runFrom = -1;
3021
+ let nodePos = base;
3022
+ for (let i = 0; i < parent.childCount; i++) {
3023
+ const child = parent.child(i);
3024
+ const childEnd = nodePos + child.nodeSize;
3025
+ if (markType2.isInSet(child.marks)) {
3026
+ if (runFrom === -1) runFrom = nodePos;
3027
+ } else {
3028
+ if (runFrom !== -1) {
3029
+ runs.push({ from: runFrom, to: nodePos });
3030
+ runFrom = -1;
3031
+ }
3032
+ }
3033
+ nodePos = childEnd;
3034
+ }
3035
+ if (runFrom !== -1) runs.push({ from: runFrom, to: nodePos });
3036
+ return runs.find((r2) => pos >= r2.from && pos < r2.to) ?? null;
3037
+ }
3038
+ function buildDecorations(state) {
3039
+ const { selection } = state;
3040
+ if (!selection.empty) return DecorationSet.empty;
3041
+ const $from = selection.$from;
3042
+ const decorations = [];
3043
+ const pos = $from.pos;
3044
+ const depth = $from.depth;
3045
+ const parent = $from.parent;
3046
+ if (parent.type === state.schema.nodes.heading) {
3047
+ const level = parent.attrs.level;
3048
+ const prefix = HEADING_PREFIX[level] ?? "# ";
3049
+ const contentStart = $from.start(depth);
3050
+ decorations.push(
3051
+ Decoration.widget(contentStart, makeWidget(prefix, "syntax-md-prefix"), {
3052
+ side: -1,
3053
+ key: "heading-prefix"
3054
+ })
3055
+ );
3056
+ }
3057
+ for (let d = depth - 1; d >= 1; d--) {
3058
+ if ($from.node(d).type === state.schema.nodes.blockquote) {
3059
+ const contentStart = $from.start(depth);
3060
+ decorations.push(
3061
+ Decoration.widget(contentStart, makeWidget("> ", "syntax-md-prefix"), {
3062
+ side: -1,
3063
+ key: "bq-prefix"
3064
+ })
3065
+ );
3066
+ break;
3067
+ }
3068
+ }
3069
+ for (const [markName, delim] of Object.entries(MARK_DELIMITERS)) {
3070
+ const markType2 = state.schema.marks[markName];
3071
+ if (!markType2) continue;
3072
+ const range = getMarkRange(state, pos, markType2);
3073
+ if (!range) continue;
3074
+ decorations.push(
3075
+ Decoration.widget(range.from, makeWidget(delim.open, "syntax-md-mark"), {
3076
+ side: -1,
3077
+ key: `${markName}-open`
3078
+ }),
3079
+ Decoration.widget(range.to, makeWidget(delim.close, "syntax-md-mark"), {
3080
+ side: 1,
3081
+ key: `${markName}-close`
3082
+ })
3083
+ );
3084
+ }
3085
+ return DecorationSet.create(state.doc, decorations);
3086
+ }
3087
+ function createCursorSyntaxPlugin() {
3088
+ return new Plugin2({
3089
+ key: pluginKey,
3090
+ state: {
3091
+ init(_, state) {
3092
+ return buildDecorations(state);
3093
+ },
3094
+ apply(tr, old, _, newState) {
3095
+ if (!tr.selectionSet && !tr.docChanged) return old;
3096
+ return buildDecorations(newState);
3097
+ }
3098
+ },
3099
+ props: {
3100
+ decorations(state) {
3101
+ return this.getState(state);
3102
+ }
3103
+ }
3104
+ });
3105
+ }
3106
+
3107
+ // src/plugins/link-text-plugin.ts
3108
+ import { Plugin as Plugin3, PluginKey as PluginKey3, TextSelection as TextSelection2 } from "prosemirror-state";
3109
+ import { Decoration as Decoration2, DecorationSet as DecorationSet2 } from "prosemirror-view";
3110
+ var pluginKey2 = new PluginKey3("moraya-link-text");
3111
+ var LINK_PATTERN_DECO = /\[([^\]]*)\]\(([^)]*)\)/g;
3112
+ var LINK_PATTERN_CONVERT = /\[([^\]]+)\]\(([^)]+)\)/g;
3113
+ function findLinkPatterns(state, regex) {
3114
+ const matches = [];
3115
+ const linkType = state.schema.marks.link;
3116
+ state.doc.descendants((node, pos) => {
3117
+ if (!node.isText || !node.text) return;
3118
+ if (linkType && linkType.isInSet(node.marks)) return;
3119
+ regex.lastIndex = 0;
3120
+ let m;
3121
+ while ((m = regex.exec(node.text)) !== null) {
3122
+ matches.push({
3123
+ from: pos + m.index,
3124
+ to: pos + m.index + m[0].length,
3125
+ text: m[1] ?? "",
3126
+ url: m[2] ?? ""
3127
+ });
3128
+ }
3129
+ });
3130
+ return matches;
3131
+ }
3132
+ function findLinkPatternsInBlock(state, pos, regex) {
3133
+ const matches = [];
3134
+ const linkType = state.schema.marks.link;
3135
+ let resolved;
3136
+ try {
3137
+ resolved = state.doc.resolve(pos);
3138
+ } catch {
3139
+ return matches;
3140
+ }
3141
+ const parent = resolved.parent;
3142
+ if (!parent.isTextblock) return matches;
3143
+ const base = resolved.start();
3144
+ let nodePos = base;
3145
+ for (let i = 0; i < parent.childCount; i++) {
3146
+ const child = parent.child(i);
3147
+ if (child.isText && child.text && !(linkType && linkType.isInSet(child.marks))) {
3148
+ regex.lastIndex = 0;
3149
+ let m;
3150
+ while ((m = regex.exec(child.text)) !== null) {
3151
+ matches.push({
3152
+ from: nodePos + m.index,
3153
+ to: nodePos + m.index + m[0].length,
3154
+ text: m[1] ?? "",
3155
+ url: m[2] ?? ""
3156
+ });
3157
+ }
3158
+ }
3159
+ nodePos += child.nodeSize;
3160
+ }
3161
+ return matches;
3162
+ }
3163
+ function buildDecorations2(state) {
3164
+ const matches = findLinkPatterns(state, LINK_PATTERN_DECO);
3165
+ if (matches.length === 0) return DecorationSet2.empty;
3166
+ const decorations = matches.map(
3167
+ (m) => Decoration2.inline(m.from, m.to, { class: "link-text-syntax" })
3168
+ );
3169
+ return DecorationSet2.create(state.doc, decorations);
3170
+ }
3171
+ function cursorInsidePattern(pos, matches) {
3172
+ return matches.some((m) => pos >= m.from && pos <= m.to);
3173
+ }
3174
+ function findLinkMarkAtPos(state, pos) {
3175
+ const linkType = state.schema.marks.link;
3176
+ if (!linkType) return null;
3177
+ let resolved;
3178
+ try {
3179
+ resolved = state.doc.resolve(pos);
3180
+ } catch {
3181
+ return null;
3182
+ }
3183
+ const parent = resolved.parent;
3184
+ if (!parent.isTextblock) return null;
3185
+ const base = resolved.start();
3186
+ let runFrom = -1;
3187
+ let runTo = -1;
3188
+ let href = "";
3189
+ const textParts = [];
3190
+ let nodePos = base;
3191
+ for (let i = 0; i < parent.childCount; i++) {
3192
+ const child = parent.child(i);
3193
+ const childEnd = nodePos + child.nodeSize;
3194
+ const lm = linkType.isInSet(child.marks);
3195
+ if (lm) {
3196
+ if (runFrom === -1) {
3197
+ runFrom = nodePos;
3198
+ href = lm.attrs.href || "";
3199
+ textParts.length = 0;
3200
+ }
3201
+ textParts.push(child.text || "");
3202
+ runTo = childEnd;
3203
+ } else {
3204
+ if (runFrom !== -1 && pos >= runFrom && pos <= runTo) {
3205
+ return { from: runFrom, to: runTo, text: textParts.join(""), href };
3206
+ }
3207
+ runFrom = -1;
3208
+ runTo = -1;
3209
+ href = "";
3210
+ textParts.length = 0;
3211
+ }
3212
+ nodePos = childEnd;
3213
+ }
3214
+ if (runFrom !== -1 && pos >= runFrom && pos <= runTo) {
3215
+ return { from: runFrom, to: runTo, text: textParts.join(""), href };
3216
+ }
3217
+ return null;
3218
+ }
3219
+ function createLinkTextPlugin() {
3220
+ return new Plugin3({
3221
+ key: pluginKey2,
3222
+ state: {
3223
+ init(_, state) {
3224
+ return buildDecorations2(state);
3225
+ },
3226
+ apply(tr, old, _, newState) {
3227
+ if (!tr.docChanged) return old;
3228
+ if (tr.getMeta("full-delete")) return DecorationSet2.empty;
3229
+ return buildDecorations2(newState);
3230
+ }
3231
+ },
3232
+ props: {
3233
+ decorations(state) {
3234
+ return this.getState(state);
3235
+ }
3236
+ },
3237
+ appendTransaction(transactions, oldState, newState) {
3238
+ if (transactions.some((tr2) => tr2.getMeta(pluginKey2))) return null;
3239
+ if (transactions.some((tr2) => tr2.getMeta("full-delete"))) return null;
3240
+ const selChanged = transactions.some((tr2) => tr2.selectionSet);
3241
+ const docChanged = transactions.some((tr2) => tr2.docChanged);
3242
+ if (!selChanged && !docChanged) return null;
3243
+ const linkType = newState.schema.marks.link;
3244
+ if (!linkType) return null;
3245
+ if (!newState.selection.empty) return null;
3246
+ const newPos = newState.selection.from;
3247
+ const oldPos = oldState.selection.from;
3248
+ const linkInfo = findLinkMarkAtPos(newState, newPos);
3249
+ if (linkInfo) {
3250
+ const oldLinkInfo = findLinkMarkAtPos(oldState, oldPos);
3251
+ if (!oldLinkInfo) {
3252
+ const { from, to, text: text2, href } = linkInfo;
3253
+ const literal = `[${text2}](${href})`;
3254
+ const textNode = newState.schema.text(literal);
3255
+ const tr2 = newState.tr.replaceWith(from, to, textNode);
3256
+ tr2.setMeta(pluginKey2, "expand");
3257
+ tr2.setMeta("addToHistory", false);
3258
+ const relPos = Math.max(0, Math.min(newPos - from, text2.length));
3259
+ const cursorPos = from + 1 + relPos;
3260
+ try {
3261
+ tr2.setSelection(TextSelection2.create(tr2.doc, cursorPos));
3262
+ } catch {
3263
+ }
3264
+ return tr2;
3265
+ }
3266
+ return null;
3267
+ }
3268
+ const oldBlockMatches = findLinkPatternsInBlock(oldState, oldPos, LINK_PATTERN_CONVERT);
3269
+ if (oldBlockMatches.length === 0) return null;
3270
+ const wasIn = oldBlockMatches.find((m) => oldPos >= m.from && oldPos <= m.to);
3271
+ if (!wasIn) return null;
3272
+ let target;
3273
+ if (docChanged) {
3274
+ let mappedFrom = wasIn.from;
3275
+ for (const t of transactions) {
3276
+ mappedFrom = t.mapping.map(mappedFrom);
3277
+ }
3278
+ if (mappedFrom < 0 || mappedFrom > newState.doc.content.size) return null;
3279
+ const newBlockMatches = findLinkPatternsInBlock(newState, mappedFrom, LINK_PATTERN_CONVERT);
3280
+ if (cursorInsidePattern(newPos, newBlockMatches)) return null;
3281
+ target = newBlockMatches.find((m) => Math.abs(m.from - mappedFrom) < 3);
3282
+ } else {
3283
+ const newBlockMatches = findLinkPatternsInBlock(newState, wasIn.from, LINK_PATTERN_CONVERT);
3284
+ if (cursorInsidePattern(newPos, newBlockMatches)) return null;
3285
+ target = newBlockMatches.find((m) => m.from === wasIn.from && m.to === wasIn.to);
3286
+ }
3287
+ if (!target || !target.text || !target.url) return null;
3288
+ const mark = linkType.create({ href: target.url });
3289
+ const linkNode = newState.schema.text(target.text, [mark]);
3290
+ const tr = newState.tr.replaceWith(target.from, target.to, linkNode);
3291
+ tr.setMeta(pluginKey2, "collapse");
3292
+ tr.setMeta("addToHistory", false);
3293
+ return tr;
3294
+ }
3295
+ });
3296
+ }
3297
+
3298
+ // src/plugins/inline-code-convert.ts
3299
+ import { Plugin as Plugin4, PluginKey as PluginKey4 } from "prosemirror-state";
3300
+ var pluginKey3 = new PluginKey4("moraya-inline-code-convert");
3301
+ var ZWSP = "\u200B";
3302
+ var ZWSP_MARK_NAMES = ["code", "strong", "em", "strike_through"];
3303
+ function hasZwspTargetMark(marks2, state) {
3304
+ return ZWSP_MARK_NAMES.some((name) => {
3305
+ const mt = state.schema.marks[name];
3306
+ return mt && mt.isInSet(marks2);
3307
+ });
3308
+ }
3309
+ var CODE_PATTERN = /`([^`]+)`/g;
3310
+ function findCodePatternsInBlock(state, pos) {
3311
+ const matches = [];
3312
+ const codeType = state.schema.marks.code;
3313
+ let resolved;
3314
+ try {
3315
+ resolved = state.doc.resolve(pos);
3316
+ } catch {
3317
+ return matches;
3318
+ }
3319
+ const parent = resolved.parent;
3320
+ if (!parent.isTextblock) return matches;
3321
+ if (parent.type.spec.code) return matches;
3322
+ const base = resolved.start();
3323
+ let nodePos = base;
3324
+ for (let i = 0; i < parent.childCount; i++) {
3325
+ const child = parent.child(i);
3326
+ if (child.isText && child.text && !(codeType && codeType.isInSet(child.marks))) {
3327
+ CODE_PATTERN.lastIndex = 0;
3328
+ let m;
3329
+ while ((m = CODE_PATTERN.exec(child.text)) !== null) {
3330
+ matches.push({
3331
+ from: nodePos + m.index,
3332
+ to: nodePos + m.index + m[0].length,
3333
+ content: m[1] ?? ""
3334
+ });
3335
+ }
3336
+ }
3337
+ nodePos += child.nodeSize;
3338
+ }
3339
+ return matches;
3340
+ }
3341
+ function needsCursorTarget(state) {
3342
+ const { $head } = state.selection;
3343
+ if (!$head) return -1;
3344
+ const parent = $head.parent;
3345
+ if (!parent.isTextblock || parent.type.spec.code || parent.childCount === 0) return -1;
3346
+ const lastChild = parent.lastChild;
3347
+ if (!lastChild?.isText) return -1;
3348
+ if (!hasZwspTargetMark(lastChild.marks, state) && lastChild.text?.endsWith(ZWSP)) return -1;
3349
+ for (let i = parent.childCount - 1; i >= 0; i--) {
3350
+ const child = parent.child(i);
3351
+ if (child.isText && !hasZwspTargetMark(child.marks, state) && child.text === ZWSP) continue;
3352
+ if (child.isText && hasZwspTargetMark(child.marks, state)) {
3353
+ return $head.start() + parent.content.size;
3354
+ }
3355
+ break;
3356
+ }
3357
+ return -1;
3358
+ }
3359
+ function createInlineCodeConvertPlugin() {
3360
+ return new Plugin4({
3361
+ key: pluginKey3,
3362
+ appendTransaction(transactions, oldState, newState) {
3363
+ if (transactions.some((tr) => tr.getMeta(pluginKey3))) return null;
3364
+ if (transactions.some((tr) => tr.getMeta("full-delete"))) return null;
3365
+ const selChanged = transactions.some((tr) => tr.selectionSet);
3366
+ const docChanged = transactions.some((tr) => tr.docChanged);
3367
+ if (!selChanged && !docChanged) return null;
3368
+ if (!newState.selection.empty) return null;
3369
+ const newPos = newState.selection.from;
3370
+ const oldPos = oldState.selection.from;
3371
+ const codeType = newState.schema.marks.code;
3372
+ const oldMatches = findCodePatternsInBlock(oldState, oldPos);
3373
+ const wasIn = oldMatches.find((m) => oldPos > m.from && oldPos < m.to);
3374
+ if (wasIn && codeType) {
3375
+ let mappedFrom = wasIn.from;
3376
+ if (docChanged) {
3377
+ for (const t of transactions) {
3378
+ mappedFrom = t.mapping.map(mappedFrom);
3379
+ }
3380
+ if (mappedFrom < 0 || mappedFrom > newState.doc.content.size) return null;
3381
+ }
3382
+ const newMatches = findCodePatternsInBlock(newState, mappedFrom);
3383
+ const isStillIn = newMatches.find((m) => newPos > m.from && newPos < m.to);
3384
+ if (!isStillIn) {
3385
+ const target = newMatches.find(
3386
+ (m) => Math.abs(m.from - mappedFrom) < 3
3387
+ );
3388
+ if (target?.content) {
3389
+ const codeNode = newState.schema.text(target.content, [codeType.create()]);
3390
+ const tr = newState.tr.replaceWith(target.from, target.to, codeNode);
3391
+ tr.setMeta(pluginKey3, "collapse");
3392
+ tr.setMeta("addToHistory", false);
3393
+ return tr;
3394
+ }
3395
+ }
3396
+ }
3397
+ const insertPos = needsCursorTarget(newState);
3398
+ if (insertPos >= 0) {
3399
+ const tr = newState.tr.insertText(ZWSP, insertPos);
3400
+ tr.setMeta(pluginKey3, "cursor-target");
3401
+ tr.setMeta("addToHistory", false);
3402
+ if (newState.selection.from === insertPos) {
3403
+ const { $head: $h } = newState.selection;
3404
+ if ($h?.nodeBefore) {
3405
+ const hasInclusive = ZWSP_MARK_NAMES.filter((n) => n !== "code").some((name) => {
3406
+ const mt = newState.schema.marks[name];
3407
+ return mt && $h.nodeBefore.marks.some((m) => m.type === mt);
3408
+ });
3409
+ if (hasInclusive) {
3410
+ const filtered = $h.marks().filter(
3411
+ (m) => !ZWSP_MARK_NAMES.filter((n) => n !== "code").some((name) => {
3412
+ const mt = newState.schema.marks[name];
3413
+ return mt && m.type === mt;
3414
+ })
3415
+ );
3416
+ tr.setStoredMarks(filtered);
3417
+ }
3418
+ }
3419
+ }
3420
+ return tr;
3421
+ }
3422
+ if (transactions.some((tr) => tr.getMeta("code-escape"))) return null;
3423
+ const { $head } = newState.selection;
3424
+ if ($head && newState.selection.empty && codeType) {
3425
+ const nodeBefore = $head.nodeBefore;
3426
+ const nodeAfter = $head.nodeAfter;
3427
+ if (nodeBefore?.marks.some((m) => m.type === codeType) && nodeAfter?.isText && !codeType.isInSet(nodeAfter.marks) && nodeAfter.text?.startsWith(ZWSP)) {
3428
+ const stored = newState.storedMarks;
3429
+ if (stored && stored.some((m) => m.type === codeType)) return null;
3430
+ const marks2 = [...$head.marks(), codeType.create()];
3431
+ const tr = newState.tr.setStoredMarks(marks2);
3432
+ tr.setMeta(pluginKey3, "boundary-marks");
3433
+ tr.setMeta("addToHistory", false);
3434
+ return tr;
3435
+ }
3436
+ const inclusiveMarkNames = ZWSP_MARK_NAMES.filter((n) => n !== "code");
3437
+ const hasInclusiveBefore = nodeBefore != null && inclusiveMarkNames.some((name) => {
3438
+ const mt = newState.schema.marks[name];
3439
+ return mt && nodeBefore.marks.some((m) => m.type === mt);
3440
+ });
3441
+ if (hasInclusiveBefore && nodeAfter?.isText && nodeAfter.text?.startsWith(ZWSP)) {
3442
+ const stored = newState.storedMarks;
3443
+ const storedHasInclusive = stored?.some(
3444
+ (m) => inclusiveMarkNames.some((name) => {
3445
+ const mt = newState.schema.marks[name];
3446
+ return mt && m.type === mt;
3447
+ })
3448
+ );
3449
+ if (stored !== null && !storedHasInclusive) return null;
3450
+ const filtered = $head.marks().filter(
3451
+ (m) => !inclusiveMarkNames.some((name) => {
3452
+ const mt = newState.schema.marks[name];
3453
+ return mt && m.type === mt;
3454
+ })
3455
+ );
3456
+ const tr = newState.tr.setStoredMarks(filtered);
3457
+ tr.setMeta(pluginKey3, "boundary-marks-inclusive");
3458
+ tr.setMeta("addToHistory", false);
3459
+ return tr;
3460
+ }
3461
+ }
3462
+ return null;
3463
+ }
3464
+ });
3465
+ }
3466
+
3467
+ // src/plugins/editor-props-plugin.ts
3468
+ import { Fragment as Fragment2, Slice } from "prosemirror-model";
3469
+ import { AllSelection, Plugin as Plugin5, PluginKey as PluginKey5, TextSelection as TextSelection3 } from "prosemirror-state";
3470
+ import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "prosemirror-view";
3471
+ var editorPropsKey = new PluginKey5("moraya-editor-props");
3472
+ function isLocalFilePath(href) {
3473
+ if (href.startsWith("/")) return true;
3474
+ if (href.startsWith("./") || href.startsWith("../")) return true;
3475
+ if (/^[A-Za-z]:[/\\]/.test(href)) return true;
3476
+ if (href.startsWith("file://")) return true;
3477
+ return false;
3478
+ }
3479
+ function resolveLocalPath(href, platform) {
3480
+ let path = href;
3481
+ if (path.startsWith("file:///")) {
3482
+ path = path.slice(7);
3483
+ try {
3484
+ path = decodeURIComponent(path);
3485
+ } catch {
3486
+ }
3487
+ } else if (path.startsWith("file://")) {
3488
+ path = path.slice(5);
3489
+ try {
3490
+ path = decodeURIComponent(path);
3491
+ } catch {
3492
+ }
3493
+ }
3494
+ if (path.startsWith("/") || /^[A-Za-z]:[/\\]/.test(path)) return path;
3495
+ const currentFile = platform.getCurrentFilePath();
3496
+ if (currentFile) {
3497
+ const dir = currentFile.replace(/[/\\][^/\\]*$/, "");
3498
+ return dir + "/" + path;
3499
+ }
3500
+ return path;
3501
+ }
3502
+ function createEditorPropsPlugin(opts) {
3503
+ const { platform, linkOpener } = opts;
3504
+ const isMacOS = platform.isMacOS;
3505
+ let pendingPaste = false;
3506
+ return new Plugin5({
3507
+ key: editorPropsKey,
3508
+ props: {
3509
+ /**
3510
+ * Parse pasted plain text as Markdown so syntax renders instead of
3511
+ * being inserted as escaped literal text.
3512
+ */
3513
+ clipboardTextParser(text2, $context, plain) {
3514
+ if (plain || $context.parent.type.spec.code) return void 0;
3515
+ const doc2 = parseMarkdown(text2);
3516
+ if (doc2.textContent.length === 0 && doc2.content.size <= 2) return void 0;
3517
+ const content = doc2.content;
3518
+ if (content.childCount === 1 && content.firstChild.type.name === "paragraph") {
3519
+ return new Slice(content.firstChild.content, 0, 0);
3520
+ }
3521
+ return new Slice(content, 0, 0);
3522
+ },
3523
+ /**
3524
+ * Safety net for degenerate pastes (empty markdown link, empty <a>, etc.).
3525
+ * Also routes pasted markdown image syntax through the markdown parser.
3526
+ */
3527
+ handlePaste(view, event, slice) {
3528
+ const plain = event.clipboardData?.getData("text/plain");
3529
+ if (!plain) return false;
3530
+ const trimmed = plain.trim();
3531
+ if (/^!\[/.test(trimmed)) {
3532
+ const doc2 = parseMarkdown(trimmed);
3533
+ if (doc2.content.size > 2) {
3534
+ const content = doc2.content;
3535
+ const inner = content.childCount === 1 && content.firstChild.type.name === "paragraph" ? content.firstChild.content : content;
3536
+ view.dispatch(
3537
+ view.state.tr.replaceSelection(new Slice(inner, 0, 0))
3538
+ );
3539
+ pendingPaste = true;
3540
+ return true;
3541
+ }
3542
+ }
3543
+ const linkMatch = /^\[([^\]]*)\]\(([^)]*)\)$/.exec(trimmed);
3544
+ if (linkMatch && (!linkMatch[1] || !linkMatch[2])) {
3545
+ const textNode = view.state.schema.text(plain);
3546
+ view.dispatch(
3547
+ view.state.tr.replaceSelection(new Slice(Fragment2.from(textNode), 0, 0))
3548
+ );
3549
+ pendingPaste = true;
3550
+ return true;
3551
+ }
3552
+ try {
3553
+ const sliceText = slice.content.textBetween(0, slice.content.size, "", "");
3554
+ if (sliceText.trim().length === 0 && trimmed.length > 0) {
3555
+ const textNode = view.state.schema.text(plain);
3556
+ view.dispatch(
3557
+ view.state.tr.replaceSelection(new Slice(Fragment2.from(textNode), 0, 0))
3558
+ );
3559
+ pendingPaste = true;
3560
+ return true;
3561
+ }
3562
+ } catch {
3563
+ }
3564
+ return false;
3565
+ },
3566
+ /**
3567
+ * Paste language normalization:
3568
+ * Copy class="language-xxx" from <code> to data-language on parent <pre>.
3569
+ */
3570
+ transformPastedHTML(html) {
3571
+ if (!html.includes("language-")) return html;
3572
+ try {
3573
+ const template = document.createElement("template");
3574
+ template.innerHTML = html;
3575
+ const fragment = template.content;
3576
+ for (const pre of fragment.querySelectorAll("pre")) {
3577
+ if (pre.dataset.language) continue;
3578
+ const code2 = pre.querySelector("code");
3579
+ if (!code2) continue;
3580
+ const match = code2.className.match(/(?:language|lang)-(\S+)/);
3581
+ if (match && match[1]) {
3582
+ pre.dataset.language = match[1];
3583
+ }
3584
+ }
3585
+ return template.innerHTML;
3586
+ } catch {
3587
+ return html;
3588
+ }
3589
+ },
3590
+ handleDOMEvents: {
3591
+ /**
3592
+ * Safety: prevent WebView navigation on any remaining <a> clicks.
3593
+ * (Most <a> tags get expanded to literal text on mousedown, but this
3594
+ * is a fallback in case the click fires before the expand.)
3595
+ */
3596
+ click(_view, event) {
3597
+ const me = event;
3598
+ const target = me.target;
3599
+ if (!target) return false;
3600
+ const anchor = target.closest("a[href]");
3601
+ if (anchor) {
3602
+ me.preventDefault();
3603
+ }
3604
+ return false;
3605
+ },
3606
+ mousedown(view, event) {
3607
+ const me = event;
3608
+ if (me.button !== 0) return false;
3609
+ const target = me.target;
3610
+ if (!target) return false;
3611
+ if (me.metaKey || me.ctrlKey) {
3612
+ const anchor = target.closest("a[href]");
3613
+ if (anchor) {
3614
+ const href = anchor.getAttribute("href");
3615
+ if (href) {
3616
+ me.preventDefault();
3617
+ const targetHref = isLocalFilePath(href) ? resolveLocalPath(href, platform) : href;
3618
+ try {
3619
+ linkOpener.open(targetHref);
3620
+ } catch (e) {
3621
+ console.warn("[opener] failed:", targetHref, e);
3622
+ }
3623
+ return true;
3624
+ }
3625
+ }
3626
+ }
3627
+ const mathBlock = target.closest('div[data-type="math_block"]');
3628
+ if (!mathBlock) return false;
3629
+ me.preventDefault();
3630
+ try {
3631
+ const pos = view.posAtDOM(mathBlock, 0);
3632
+ const $pos = view.state.doc.resolve(pos);
3633
+ let beforePos = pos;
3634
+ for (let d = $pos.depth; d > 0; d--) {
3635
+ if ($pos.node(d).type.name === "math_block") {
3636
+ beforePos = $pos.before(d);
3637
+ break;
3638
+ }
3639
+ }
3640
+ const $before = view.state.doc.resolve(beforePos);
3641
+ if (!$before.nodeAfter || $before.nodeAfter.type.name !== "math_block") {
3642
+ if ($pos.nodeAfter?.type.name === "math_block") {
3643
+ beforePos = pos;
3644
+ }
3645
+ }
3646
+ const sel = TextSelection3.near(view.state.doc.resolve(beforePos), -1);
3647
+ view.dispatch(view.state.tr.setSelection(sel));
3648
+ } catch {
3649
+ }
3650
+ view.focus();
3651
+ return true;
3652
+ },
3653
+ /**
3654
+ * Cmd/Ctrl held → add 'link-hover' class for pointer cursor on links.
3655
+ * Also handles fast AllSelection delete + WKWebView end-of-textblock
3656
+ * Backspace fix at the highest priority interception point.
3657
+ */
3658
+ keydown(view, event) {
3659
+ if (event.isComposing) return false;
3660
+ if (event.key === "Meta" || event.key === "Control") {
3661
+ view.dom.classList.add("link-hover");
3662
+ }
3663
+ if ((event.key === "Backspace" || event.key === "Delete") && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) {
3664
+ try {
3665
+ ;
3666
+ view.domObserver?.flush?.();
3667
+ } catch {
3668
+ }
3669
+ const docSize = view.state.doc.content.size;
3670
+ let isAllSelected = false;
3671
+ const sel = view.state.selection;
3672
+ if (sel instanceof AllSelection || docSize > 0 && sel.from <= 1 && sel.to >= docSize - 1) {
3673
+ isAllSelected = true;
3674
+ }
3675
+ if (!isAllSelected && docSize > 0) {
3676
+ try {
3677
+ const domSel = window.getSelection();
3678
+ if (domSel && !domSel.isCollapsed && domSel.rangeCount > 0) {
3679
+ const range = domSel.getRangeAt(0);
3680
+ const editorRange = document.createRange();
3681
+ editorRange.selectNodeContents(view.dom);
3682
+ if (range.compareBoundaryPoints(Range.START_TO_START, editorRange) <= 0 && range.compareBoundaryPoints(Range.END_TO_END, editorRange) >= 0) {
3683
+ isAllSelected = true;
3684
+ }
3685
+ }
3686
+ } catch {
3687
+ }
3688
+ }
3689
+ if (!isAllSelected && docSize > 0) {
3690
+ try {
3691
+ const domSel = window.getSelection();
3692
+ if (domSel && !domSel.isCollapsed) {
3693
+ const selectedText = domSel.toString();
3694
+ const fullText = view.dom.textContent || "";
3695
+ if (selectedText.length > 0 && fullText.length > 0 && selectedText.length >= fullText.length * 0.9) {
3696
+ isAllSelected = true;
3697
+ }
3698
+ }
3699
+ } catch {
3700
+ }
3701
+ }
3702
+ if (isAllSelected) {
3703
+ event.preventDefault();
3704
+ const paragraphType = view.state.schema.nodes.paragraph;
3705
+ if (!paragraphType) return false;
3706
+ const emptyParagraph = paragraphType.create();
3707
+ const tr = view.state.tr.replaceWith(0, docSize, emptyParagraph);
3708
+ tr.setSelection(TextSelection3.create(tr.doc, 1));
3709
+ tr.setMeta("full-delete", true);
3710
+ view.dispatch(tr);
3711
+ return true;
3712
+ }
3713
+ if (event.key === "Backspace") {
3714
+ if (sel instanceof TextSelection3 && sel.empty && sel.$cursor) {
3715
+ const { parent, parentOffset } = sel.$cursor;
3716
+ if (parent.isTextblock && parentOffset === parent.content.size && parentOffset > 0) {
3717
+ const nb = sel.$cursor.nodeBefore;
3718
+ if (nb) {
3719
+ event.preventDefault();
3720
+ if (nb.isText && nb.text) {
3721
+ const code2 = nb.text.charCodeAt(nb.text.length - 1);
3722
+ const delLen = code2 >= 56320 && code2 <= 57343 ? 2 : 1;
3723
+ view.dispatch(view.state.tr.delete(sel.from - delLen, sel.from).scrollIntoView());
3724
+ } else {
3725
+ view.dispatch(view.state.tr.delete(sel.from - nb.nodeSize, sel.from).scrollIntoView());
3726
+ }
3727
+ return true;
3728
+ }
3729
+ }
3730
+ }
3731
+ }
3732
+ }
3733
+ return false;
3734
+ },
3735
+ keyup(view, event) {
3736
+ if (event.key === "Meta" || event.key === "Control") {
3737
+ view.dom.classList.remove("link-hover");
3738
+ }
3739
+ return false;
3740
+ }
3741
+ },
3742
+ /**
3743
+ * Click below content: append a paragraph and place cursor there when
3744
+ * the last node is a code_block / table / etc. and user clicks below it.
3745
+ */
3746
+ handleClick(view, _pos, event) {
3747
+ if (event.button !== 0) return false;
3748
+ const { doc: doc2 } = view.state;
3749
+ const lastNode = doc2.lastChild;
3750
+ if (!lastNode || lastNode.type.name === "paragraph") return false;
3751
+ const lastNodePos = doc2.content.size - lastNode.nodeSize;
3752
+ const lastDOM = view.nodeDOM(lastNodePos);
3753
+ if (!lastDOM) return false;
3754
+ const rect = lastDOM.getBoundingClientRect();
3755
+ if (event.clientY <= rect.bottom) return false;
3756
+ const paragraphType = view.state.schema.nodes.paragraph;
3757
+ if (!paragraphType) return false;
3758
+ const endPos = doc2.content.size;
3759
+ const paragraph2 = paragraphType.create();
3760
+ const tr = view.state.tr.insert(endPos, paragraph2);
3761
+ tr.setSelection(TextSelection3.create(tr.doc, endPos + 1));
3762
+ view.dispatch(tr);
3763
+ view.focus();
3764
+ return true;
3765
+ },
3766
+ /**
3767
+ * Image click: prevent NodeSelection blue highlight, place TextSelection
3768
+ * after the image instead. (math_block is handled in mousedown above.)
3769
+ */
3770
+ handleClickOn(view, _pos, node, nodePos, event) {
3771
+ if (node.type.name !== "image") return false;
3772
+ if (event.button !== 0) return false;
3773
+ const $pos = view.state.doc.resolve(nodePos + node.nodeSize);
3774
+ const sel = TextSelection3.near($pos);
3775
+ view.dispatch(view.state.tr.setSelection(sel));
3776
+ return true;
3777
+ },
3778
+ /**
3779
+ * Keyboard shortcuts (after keymap plugins):
3780
+ * - ArrowRight: escape formatting mark boundary
3781
+ * - Backspace/Delete on AllSelection: fast full-doc deletion
3782
+ */
3783
+ handleKeyDown(view, event) {
3784
+ if (event.isComposing) return false;
3785
+ if (event.key === "ArrowRight" && !event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
3786
+ const sel = view.state.selection;
3787
+ if (sel.empty && sel instanceof TextSelection3 && sel.$cursor) {
3788
+ const $cursor = sel.$cursor;
3789
+ const ZWSP_MARK_NAMES2 = ["code", "strong", "em", "strike_through"];
3790
+ const nodeBefore = $cursor.nodeBefore;
3791
+ const nodeAfter = $cursor.nodeAfter;
3792
+ const hasTargetMarkBefore = nodeBefore != null && ZWSP_MARK_NAMES2.some((name) => {
3793
+ const mt = view.state.schema.marks[name];
3794
+ return mt && nodeBefore.marks.some((m) => m.type === mt);
3795
+ });
3796
+ if (hasTargetMarkBefore && nodeAfter?.isText && nodeAfter.text?.startsWith("\u200B")) {
3797
+ const nextPos = $cursor.pos + nodeAfter.nodeSize;
3798
+ const $next = view.state.doc.resolve(Math.min(nextPos, view.state.doc.content.size));
3799
+ const nextSel = TextSelection3.near($next, 1);
3800
+ const tr = view.state.tr.setSelection(nextSel);
3801
+ tr.setStoredMarks([]);
3802
+ tr.setMeta("code-escape", true);
3803
+ tr.scrollIntoView();
3804
+ view.dispatch(tr);
3805
+ return true;
3806
+ }
3807
+ }
3808
+ }
3809
+ if (event.key === "Backspace" || event.key === "Delete") {
3810
+ const sel = view.state.selection;
3811
+ const docSize = view.state.doc.content.size;
3812
+ const isAllSelected = sel instanceof AllSelection || docSize > 0 && sel.from <= 1 && sel.to >= docSize - 1;
3813
+ if (isAllSelected) {
3814
+ event.preventDefault();
3815
+ const paragraphType = view.state.schema.nodes.paragraph;
3816
+ if (!paragraphType) return false;
3817
+ const emptyParagraph = paragraphType.create();
3818
+ const tr = view.state.tr.replaceWith(0, docSize, emptyParagraph);
3819
+ tr.setSelection(TextSelection3.create(tr.doc, 1));
3820
+ tr.setMeta("full-delete", true);
3821
+ view.dispatch(tr);
3822
+ return true;
3823
+ }
3824
+ }
3825
+ return false;
3826
+ },
3827
+ /**
3828
+ * WKWebView caret fix: add 'caret-empty-para' decoration to empty
3829
+ * paragraph under cursor on macOS.
3830
+ */
3831
+ decorations(state) {
3832
+ if (!isMacOS) return DecorationSet3.empty;
3833
+ const { selection } = state;
3834
+ if (!selection.empty) return DecorationSet3.empty;
3835
+ const { $from } = selection;
3836
+ const parent = $from.parent;
3837
+ if (parent.type.name === "paragraph" && parent.content.size === 0) {
3838
+ const pos = $from.before();
3839
+ return DecorationSet3.create(state.doc, [
3840
+ Decoration3.node(pos, pos + parent.nodeSize, { class: "caret-empty-para" })
3841
+ ]);
3842
+ }
3843
+ return DecorationSet3.empty;
3844
+ }
3845
+ },
3846
+ /**
3847
+ * Scroll-after-paste + empty-doc focus recovery.
3848
+ */
3849
+ view(editorView) {
3850
+ function onPaste() {
3851
+ pendingPaste = true;
3852
+ }
3853
+ editorView.dom.addEventListener("paste", onPaste, true);
3854
+ function onBlur() {
3855
+ editorView.dom.classList.remove("link-hover");
3856
+ }
3857
+ window.addEventListener("blur", onBlur);
3858
+ return {
3859
+ update(view, prevState) {
3860
+ if (isMacOS && view.state.doc !== prevState.doc) {
3861
+ const docSize = view.state.doc.content.size;
3862
+ const prevDocSize = prevState.doc.content.size;
3863
+ if (docSize <= 4 && prevDocSize > 4) {
3864
+ requestAnimationFrame(() => {
3865
+ try {
3866
+ if (!view.hasFocus()) view.focus();
3867
+ } catch {
3868
+ }
3869
+ });
3870
+ }
3871
+ }
3872
+ if (!pendingPaste || view.state.doc.eq(prevState.doc)) return;
3873
+ pendingPaste = false;
3874
+ requestAnimationFrame(() => {
3875
+ try {
3876
+ const { from } = view.state.selection;
3877
+ const coords = view.coordsAtPos(from);
3878
+ const wrapper = view.dom.closest(".editor-wrapper");
3879
+ if (!wrapper) return;
3880
+ const rect = wrapper.getBoundingClientRect();
3881
+ if (coords.top < rect.top || coords.bottom > rect.bottom) {
3882
+ wrapper.scrollTop += coords.top - rect.top - rect.height / 2;
3883
+ }
3884
+ } catch {
3885
+ }
3886
+ });
3887
+ },
3888
+ destroy() {
3889
+ editorView.dom.removeEventListener("paste", onPaste, true);
3890
+ window.removeEventListener("blur", onBlur);
3891
+ editorView.dom.classList.remove("link-hover");
3892
+ }
3893
+ };
3894
+ }
3895
+ });
3896
+ }
3897
+
3898
+ // src/doc-cache.ts
3899
+ var LRUDocCache = class {
3900
+ constructor(maxEntries) {
3901
+ this.maxEntries = maxEntries;
3902
+ if (maxEntries < 1) throw new RangeError("docCache maxEntries must be \u2265 1");
3903
+ }
3904
+ maxEntries;
3905
+ map = /* @__PURE__ */ new Map();
3906
+ get size() {
3907
+ return this.map.size;
3908
+ }
3909
+ get(hash) {
3910
+ const v = this.map.get(hash);
3911
+ if (v !== void 0) {
3912
+ this.map.delete(hash);
3913
+ this.map.set(hash, v);
3914
+ }
3915
+ return v;
3916
+ }
3917
+ set(hash, doc2) {
3918
+ if (this.map.has(hash)) {
3919
+ this.map.delete(hash);
3920
+ }
3921
+ this.map.set(hash, doc2);
3922
+ if (this.map.size > this.maxEntries) {
3923
+ const firstKey = this.map.keys().next().value;
3924
+ if (firstKey !== void 0) {
3925
+ this.map.delete(firstKey);
3926
+ }
3927
+ }
3928
+ }
3929
+ clear() {
3930
+ this.map.clear();
3931
+ }
3932
+ };
3933
+ function createDocCache(maxEntries = 10) {
3934
+ return new LRUDocCache(maxEntries);
3935
+ }
3936
+ function djb2Hash(str) {
3937
+ let hash = 5381;
3938
+ for (let i = 0; i < str.length; i++) {
3939
+ hash = hash * 33 ^ str.charCodeAt(i);
3940
+ }
3941
+ return hash >>> 0;
3942
+ }
3943
+
3944
+ // src/setup.ts
3945
+ var tier1Cache = null;
3946
+ var tier1Loading = null;
3947
+ function preloadEnhancementPlugins(schema2, rendererRegistry) {
3948
+ if (tier1Cache && tier1Cache.key.schema === schema2 && tier1Cache.key.rendererRegistry === rendererRegistry) {
3949
+ return Promise.resolve(tier1Cache.plugins);
3950
+ }
3951
+ if (tier1Loading) return tier1Loading;
3952
+ tier1Loading = Promise.allSettled([
3953
+ Promise.resolve().then(() => (init_highlight(), highlight_exports)),
3954
+ Promise.resolve().then(() => (init_emoji(), emoji_exports)),
3955
+ Promise.resolve().then(() => (init_code_block_view(), code_block_view_exports))
3956
+ ]).then(([hl, em2, cbv]) => {
3957
+ const plugins = {};
3958
+ if (hl.status === "fulfilled") {
3959
+ plugins.highlight = hl.value.createHighlightPlugin();
3960
+ }
3961
+ if (em2.status === "fulfilled") {
3962
+ plugins.emoji = em2.value.createEmojiPlugin();
3963
+ }
3964
+ if (cbv.status === "fulfilled") {
3965
+ plugins.codeBlockView = cbv.value.createCodeBlockNodeViewFactory({
3966
+ ...rendererRegistry ? { rendererRegistry } : {}
3967
+ });
3968
+ }
3969
+ plugins.defListInputRule = createDefListInputRule(schema2);
3970
+ tier1Cache = { key: { schema: schema2, ...rendererRegistry ? { rendererRegistry } : {} }, plugins };
3971
+ tier1Loading = null;
3972
+ return plugins;
3973
+ });
3974
+ return tier1Loading;
3975
+ }
3976
+ function createImageSelectionPlugin() {
3977
+ return new Plugin8({
3978
+ key: new PluginKey8("moraya-image-selection"),
3979
+ props: {
3980
+ decorations(state) {
3981
+ const { from, to } = state.selection;
3982
+ if (from === to) return DecorationSet5.empty;
3983
+ const decos = [];
3984
+ state.doc.nodesBetween(from, to, (node, pos) => {
3985
+ if (node.type.name === "image") {
3986
+ decos.push(Decoration5.node(pos, pos + node.nodeSize, { class: "image-in-selection" }));
3987
+ } else if (node.type.name === "html_inline" && /^<img\s/i.test(node.attrs.value || "")) {
3988
+ decos.push(Decoration5.node(pos, pos + node.nodeSize, { class: "image-in-selection" }));
3989
+ }
3990
+ });
3991
+ return decos.length ? DecorationSet5.create(state.doc, decos) : DecorationSet5.empty;
3992
+ }
3993
+ }
3994
+ });
3995
+ }
3996
+ function buildInputRules(schema2, tier1) {
3997
+ const rules = [];
3998
+ const N = schema2.nodes;
3999
+ const M = schema2.marks;
4000
+ if (N.code_block) {
4001
+ rules.push(textblockTypeInputRule(
4002
+ /^```(?<language>[a-zA-Z][a-zA-Z0-9_+#.\-]*)?[\s\n]$/,
4003
+ N.code_block,
4004
+ (match) => ({ language: match.groups?.language ?? "" })
4005
+ ));
4006
+ }
4007
+ if (N.blockquote) {
4008
+ rules.push(wrappingInputRule2(/^\s*>\s$/, N.blockquote));
4009
+ }
4010
+ if (N.bullet_list) {
4011
+ rules.push(wrappingInputRule2(/^\s*[-*]\s$/, N.bullet_list));
4012
+ }
4013
+ if (N.ordered_list) {
4014
+ rules.push(wrappingInputRule2(
4015
+ /^\s*(\d+)\.\s$/,
4016
+ N.ordered_list,
4017
+ (match) => ({ order: +(match[1] ?? "1") }),
4018
+ (match, node) => node.childCount + node.attrs.order === +(match[1] ?? "1")
4019
+ ));
4020
+ }
4021
+ if (N.heading) {
4022
+ for (let level = 1; level <= 6; level++) {
4023
+ const pattern = new RegExp(`^#{${level}}\\s$`);
4024
+ rules.push(textblockTypeInputRule(pattern, N.heading, { level }));
4025
+ }
4026
+ }
4027
+ if (N.horizontal_rule) {
4028
+ rules.push(new InputRule(/^---$/, (state, _match, start, end) => {
4029
+ const hr = N.horizontal_rule.create();
4030
+ return state.tr.replaceWith(start - 1, end, hr);
4031
+ }));
4032
+ }
4033
+ if (N.math_block) {
4034
+ rules.push(new InputRule(/^\$\$\s$/, (state, _match, start, end) => {
4035
+ const $start = state.doc.resolve(start);
4036
+ if (!$start.node(-1).canReplaceWith(
4037
+ $start.index(-1),
4038
+ $start.indexAfter(-1),
4039
+ N.math_block
4040
+ )) return null;
4041
+ return state.tr.delete(start, end).setBlockType(start, start, N.math_block);
4042
+ }));
4043
+ }
4044
+ if (N.math_inline) {
4045
+ rules.push(new InputRule(/(?:\$)([^$]+)(?:\$)$/, (state, match, start, end) => {
4046
+ const content = match[1];
4047
+ if (!content) return null;
4048
+ const node = N.math_inline.create(null, schema2.text(content));
4049
+ return state.tr.replaceWith(start, end, node);
4050
+ }));
4051
+ }
4052
+ if (M.strong) {
4053
+ rules.push(new InputRule(
4054
+ /(?<![\w:/])(?:\*\*|__)([^*_]+?)(?:\*\*|__)(?![\w/])$/,
4055
+ (state, match, start, end) => {
4056
+ const tr = state.tr;
4057
+ const captured = match[1];
4058
+ if (captured) {
4059
+ const textStart = start + match[0].indexOf(captured);
4060
+ const textEnd = textStart + captured.length;
4061
+ if (textEnd < end) tr.delete(textEnd, end);
4062
+ if (textStart > start) tr.delete(start, textStart);
4063
+ const markFrom = start;
4064
+ const markTo = markFrom + captured.length;
4065
+ tr.addMark(markFrom, markTo, M.strong.create());
4066
+ }
4067
+ return tr;
4068
+ }
4069
+ ));
4070
+ }
4071
+ if (M.em) {
4072
+ rules.push(new InputRule(
4073
+ /(?<![\w:/*])(?:\*|_)([^*_]+?)(?:\*|_)(?![\w/])$/,
4074
+ (state, match, start, end) => {
4075
+ const tr = state.tr;
4076
+ const captured = match[1];
4077
+ if (captured) {
4078
+ const textStart = start + match[0].indexOf(captured);
4079
+ const textEnd = textStart + captured.length;
4080
+ if (textEnd < end) tr.delete(textEnd, end);
4081
+ if (textStart > start) tr.delete(start, textStart);
4082
+ const markFrom = start;
4083
+ const markTo = markFrom + captured.length;
4084
+ tr.addMark(markFrom, markTo, M.em.create());
4085
+ }
4086
+ return tr;
4087
+ }
4088
+ ));
4089
+ }
4090
+ if (M.code) {
4091
+ rules.push(new InputRule(
4092
+ /(?:`)([^`]+)(?:`)$/,
4093
+ (state, match, start, end) => {
4094
+ const tr = state.tr;
4095
+ const captured = match[1];
4096
+ if (captured) {
4097
+ const textStart = start + match[0].indexOf(captured);
4098
+ const textEnd = textStart + captured.length;
4099
+ if (textEnd < end) tr.delete(textEnd, end);
4100
+ if (textStart > start) tr.delete(start, textStart);
4101
+ const markFrom = start;
4102
+ const markTo = markFrom + captured.length;
4103
+ tr.addMark(markFrom, markTo, M.code.create());
4104
+ }
4105
+ return tr;
4106
+ }
4107
+ ));
4108
+ }
4109
+ if (M.strike_through) {
4110
+ rules.push(new InputRule(
4111
+ /~~([^~]+)~~$/,
4112
+ (state, match, start, end) => {
4113
+ const tr = state.tr;
4114
+ const captured = match[1];
4115
+ if (captured) {
4116
+ const textStart = start + match[0].indexOf(captured);
4117
+ const textEnd = textStart + captured.length;
4118
+ if (textEnd < end) tr.delete(textEnd, end);
4119
+ if (textStart > start) tr.delete(start, textStart);
4120
+ const markFrom = start;
4121
+ const markTo = markFrom + captured.length;
4122
+ tr.addMark(markFrom, markTo, M.strike_through.create());
4123
+ }
4124
+ return tr;
4125
+ }
4126
+ ));
4127
+ }
4128
+ rules.push(new InputRule(
4129
+ /^\[(?<checked>\s|x)\]\s$/,
4130
+ (state, match, start, end) => {
4131
+ const pos = state.doc.resolve(start);
4132
+ let depth = 0;
4133
+ let node = pos.node(depth);
4134
+ while (node && node.type.name !== "list_item") {
4135
+ depth--;
4136
+ try {
4137
+ node = pos.node(depth);
4138
+ } catch {
4139
+ node = null;
4140
+ }
4141
+ }
4142
+ if (!node || node.attrs.checked != null) return null;
4143
+ const checked = Boolean(match.groups?.checked === "x");
4144
+ const finPos = pos.before(depth);
4145
+ return state.tr.deleteRange(start, end).setNodeMarkup(finPos, void 0, {
4146
+ ...node.attrs,
4147
+ checked
4148
+ });
4149
+ }
4150
+ ));
4151
+ if (M.link) {
4152
+ rules.push(new InputRule(
4153
+ /\[([^\]]+)\]\(([^)]+)\)$/,
4154
+ (state, match, start, end) => {
4155
+ const text2 = match[1];
4156
+ const url = match[2];
4157
+ if (!text2 || !url) return null;
4158
+ const linkMark = M.link.create({ href: url });
4159
+ return state.tr.replaceWith(start, end, schema2.text(text2, [linkMark]));
4160
+ }
4161
+ ));
4162
+ }
4163
+ if (tier1.defListInputRule) {
4164
+ rules.push(tier1.defListInputRule);
4165
+ }
4166
+ return inputRules({ rules });
4167
+ }
4168
+ function buildKeymap(schema2) {
4169
+ const N = schema2.nodes;
4170
+ const M = schema2.marks;
4171
+ const listItemType = N.list_item;
4172
+ const bindings = {
4173
+ // History
4174
+ "Mod-z": undo,
4175
+ "Mod-y": redo,
4176
+ "Mod-Shift-z": redo,
4177
+ // Marks
4178
+ ...M.strong ? { "Mod-b": toggleMark2(M.strong) } : {},
4179
+ ...M.em ? { "Mod-i": toggleMark2(M.em) } : {},
4180
+ ...M.code ? { "Mod-e": toggleMark2(M.code) } : {},
4181
+ ...M.strike_through ? { "Mod-Shift-x": toggleMark2(M.strike_through) } : {}
4182
+ };
4183
+ if (listItemType) {
4184
+ bindings["Enter"] = splitListItem(listItemType);
4185
+ bindings["Tab"] = (state, dispatch) => {
4186
+ if (sinkListItem(listItemType)(state)) return sinkListItem(listItemType)(state, dispatch);
4187
+ if (dispatch) dispatch(state.tr.insertText(" "));
4188
+ return true;
4189
+ };
4190
+ bindings["Mod-]"] = sinkListItem(listItemType);
4191
+ bindings["Shift-Tab"] = liftListItem2(listItemType);
4192
+ bindings["Mod-["] = liftListItem2(listItemType);
4193
+ }
4194
+ if (N.paragraph) bindings["Mod-Alt-0"] = setBlockType2(N.paragraph);
4195
+ if (N.heading) {
4196
+ for (let level = 1; level <= 6; level++) {
4197
+ bindings[`Mod-Alt-${level}`] = setBlockType2(N.heading, { level });
4198
+ }
4199
+ }
4200
+ if (N.code_block) bindings["Mod-Alt-c"] = setBlockType2(N.code_block);
4201
+ if (N.blockquote) bindings["Mod-Shift-b"] = wrapIn2(N.blockquote);
4202
+ bindings["Mod-a"] = (state, dispatch) => {
4203
+ const { $from } = state.selection;
4204
+ for (let d = $from.depth; d > 0; d--) {
4205
+ if ($from.node(d).type.name === "code_block") {
4206
+ if (dispatch) {
4207
+ dispatch(state.tr.setSelection(TextSelection4.create(state.doc, $from.start(d), $from.end(d))));
4208
+ }
4209
+ return true;
4210
+ }
4211
+ }
4212
+ if (dispatch) {
4213
+ dispatch(state.tr.setSelection(new AllSelection2(state.doc)));
4214
+ }
4215
+ return true;
4216
+ };
4217
+ if (N.hardbreak) {
4218
+ bindings["Shift-Enter"] = (state, dispatch) => {
4219
+ if (dispatch) {
4220
+ dispatch(state.tr.replaceSelectionWith(N.hardbreak.create({ isInline: false })).scrollIntoView());
4221
+ }
4222
+ return true;
4223
+ };
4224
+ }
4225
+ bindings["Backspace"] = (state, dispatch) => {
4226
+ const sel = state.selection;
4227
+ {
4228
+ const docSize = state.doc.content.size;
4229
+ const isAllSelected = sel instanceof AllSelection2 || docSize > 0 && sel.from <= 1 && sel.to >= docSize - 1;
4230
+ if (isAllSelected && dispatch) {
4231
+ const paragraphType = state.schema.nodes.paragraph;
4232
+ if (!paragraphType) return false;
4233
+ const emptyParagraph = paragraphType.create();
4234
+ const tr = state.tr.replaceWith(0, docSize, emptyParagraph);
4235
+ tr.setSelection(TextSelection4.create(tr.doc, 1));
4236
+ tr.setMeta("full-delete", true);
4237
+ dispatch(tr);
4238
+ return true;
4239
+ }
4240
+ if (isAllSelected) return true;
4241
+ }
4242
+ if (sel instanceof NodeSelection && sel.node.isBlock && sel.node.type.spec.atom) {
4243
+ const before = Selection.findFrom(state.doc.resolve(sel.from), -1, true);
4244
+ if (before && dispatch) {
4245
+ dispatch(state.tr.setSelection(before).scrollIntoView());
4246
+ }
4247
+ return true;
4248
+ }
4249
+ if (!sel.empty || !(sel instanceof TextSelection4)) return false;
4250
+ const $cursor = sel.$cursor;
4251
+ if (!$cursor) return false;
4252
+ const { parent, parentOffset } = $cursor;
4253
+ if (parent.isTextblock && parentOffset === parent.content.size && parent.content.size > 0) {
4254
+ const afterPos = $cursor.after();
4255
+ if (afterPos < state.doc.content.size) {
4256
+ const nextNode = state.doc.resolve(afterPos).nodeAfter;
4257
+ if (nextNode && nextNode.isBlock && nextNode.type.spec.atom) {
4258
+ if (dispatch) {
4259
+ const before = $cursor.nodeBefore;
4260
+ if (before) {
4261
+ const delSize = before.isText ? 1 : before.nodeSize;
4262
+ dispatch(state.tr.delete(sel.from - delSize, sel.from).scrollIntoView());
4263
+ }
4264
+ }
4265
+ return true;
4266
+ }
4267
+ }
4268
+ }
4269
+ if (parent.isTextblock && parentOffset === 0) {
4270
+ const beforePos = $cursor.before();
4271
+ if (beforePos > 0) {
4272
+ const prevNode = state.doc.resolve(beforePos).nodeBefore;
4273
+ if (prevNode && prevNode.isBlock && prevNode.type.spec.atom) {
4274
+ const target = Selection.findFrom(
4275
+ state.doc.resolve(beforePos - prevNode.nodeSize),
4276
+ -1,
4277
+ true
4278
+ );
4279
+ if (target && dispatch) {
4280
+ dispatch(state.tr.setSelection(target).scrollIntoView());
4281
+ }
4282
+ return true;
4283
+ }
4284
+ }
4285
+ }
4286
+ if (parent.type.name === "paragraph" && parentOffset === parent.content.size) {
4287
+ const nodeBeforeAtom = $cursor.nodeBefore;
4288
+ if (nodeBeforeAtom && nodeBeforeAtom.isAtom) {
4289
+ const afterPos2 = $cursor.after();
4290
+ if (afterPos2 < state.doc.content.size) {
4291
+ const nextNode2 = state.doc.resolve(afterPos2).nodeAfter;
4292
+ if (nextNode2 && nextNode2.isBlock) {
4293
+ return joinForward(state, dispatch);
4294
+ }
4295
+ }
4296
+ }
4297
+ }
4298
+ if (parent.isTextblock && parentOffset === parent.content.size && parentOffset > 0) {
4299
+ if (dispatch) {
4300
+ const nb = $cursor.nodeBefore;
4301
+ if (nb && nb.isText && nb.text) {
4302
+ const code2 = nb.text.charCodeAt(nb.text.length - 1);
4303
+ const delLen = code2 >= 56320 && code2 <= 57343 ? 2 : 1;
4304
+ dispatch(state.tr.delete(sel.from - delLen, sel.from).scrollIntoView());
4305
+ } else if (nb) {
4306
+ dispatch(state.tr.delete(sel.from - nb.nodeSize, sel.from).scrollIntoView());
4307
+ }
4308
+ }
4309
+ return true;
4310
+ }
4311
+ return false;
4312
+ };
4313
+ bindings["Delete"] = (state, dispatch) => {
4314
+ const sel = state.selection;
4315
+ if (sel instanceof NodeSelection && sel.node.isBlock && sel.node.type.spec.atom) {
4316
+ const after = Selection.findFrom(state.doc.resolve(sel.to), 1, true);
4317
+ if (after && dispatch) {
4318
+ dispatch(state.tr.setSelection(after).scrollIntoView());
4319
+ }
4320
+ return true;
4321
+ }
4322
+ if (sel.empty && sel instanceof TextSelection4 && sel.$cursor) {
4323
+ const $c = sel.$cursor;
4324
+ if ($c.parent.isTextblock && $c.parentOffset === $c.parent.content.size) {
4325
+ const ap = $c.after();
4326
+ if (ap < state.doc.content.size) {
4327
+ const nn = state.doc.resolve(ap).nodeAfter;
4328
+ if (nn && nn.isBlock && nn.type.spec.atom) {
4329
+ return true;
4330
+ }
4331
+ }
4332
+ }
4333
+ }
4334
+ return false;
4335
+ };
4336
+ return keymap(bindings);
4337
+ }
4338
+ function createDirtyTrackPlugin(onDocChanged) {
4339
+ return new Plugin8({
4340
+ key: new PluginKey8("moraya-dirty-track"),
4341
+ view: () => ({
4342
+ update: (view, prevState) => {
4343
+ if (!prevState || view.state.doc.eq(prevState.doc)) return;
4344
+ onDocChanged(view.state.doc.textContent);
4345
+ }
4346
+ })
4347
+ });
4348
+ }
4349
+ function createLazyChangePlugin(onChange, debounceMs = 500) {
4350
+ let changeTimer = null;
4351
+ return new Plugin8({
4352
+ key: new PluginKey8("moraya-lazy-change"),
4353
+ view: () => ({
4354
+ update: (view, prevState) => {
4355
+ if (!prevState || view.state.doc.eq(prevState.doc)) return;
4356
+ if (changeTimer !== null) clearTimeout(changeTimer);
4357
+ changeTimer = setTimeout(() => {
4358
+ try {
4359
+ const markdown2 = serializeMarkdown(view.state.doc);
4360
+ onChange(markdown2);
4361
+ } catch {
4362
+ }
4363
+ changeTimer = null;
4364
+ }, debounceMs);
4365
+ },
4366
+ destroy: () => {
4367
+ if (changeTimer !== null) {
4368
+ clearTimeout(changeTimer);
4369
+ changeTimer = null;
4370
+ }
4371
+ }
4372
+ })
4373
+ });
4374
+ }
4375
+ var defaultPlatform = () => ({
4376
+ getCurrentFilePath: () => null,
4377
+ isMacOS: typeof navigator !== "undefined" && /Mac/i.test(navigator.platform ?? "")
4378
+ });
4379
+ async function createEditorPlugins(opts, schemaArg) {
4380
+ if (!opts.mediaResolver) {
4381
+ throw new TypeError(
4382
+ "@moraya/core: createEditorPlugins() requires a MediaResolver in opts.mediaResolver"
4383
+ );
4384
+ }
4385
+ const platform = opts.platform ?? defaultPlatform();
4386
+ void platform;
4387
+ const schemaConfig = {
4388
+ mediaResolver: opts.mediaResolver,
4389
+ ...opts.rendererRegistry ? { rendererRegistry: opts.rendererRegistry } : {},
4390
+ ...opts.linkOpener ? { linkOpener: opts.linkOpener } : {}
4391
+ };
4392
+ const schema2 = schemaArg ?? createSchema(schemaConfig);
4393
+ const linkOpener = opts.linkOpener ?? {
4394
+ open(url) {
4395
+ if (typeof window !== "undefined") {
4396
+ window.open(url, "_blank", "noopener,noreferrer");
4397
+ }
4398
+ }
4399
+ };
4400
+ const tier1 = await preloadEnhancementPlugins(schema2, opts.rendererRegistry);
4401
+ const plugins = [
4402
+ // List shortcuts using event.code (reliable on macOS where Option+key
4403
+ // produces special chars). Must come before keymap so this handler has
4404
+ // highest priority.
4405
+ new Plugin8({
4406
+ key: new PluginKey8("moraya-list-shortcuts"),
4407
+ props: {
4408
+ handleKeyDown(view, event) {
4409
+ const mod = event.metaKey || event.ctrlKey;
4410
+ if (!mod || !event.altKey || event.shiftKey) return false;
4411
+ if (event.code === "KeyO") return wrapInOrderedList(view.state, view.dispatch, view);
4412
+ if (event.code === "KeyU") return wrapInBulletList(view.state, view.dispatch, view);
4413
+ if (event.code === "KeyX") return wrapInTaskList(view.state, view.dispatch, view);
4414
+ return false;
4415
+ }
4416
+ }
4417
+ }),
4418
+ // Input rules (must come before keymaps)
4419
+ buildInputRules(schema2, tier1),
4420
+ // Custom Enter handler MUST come before keymaps so pipe-table and
4421
+ // code-fence detection run before baseKeymap's splitBlock intercepts Enter.
4422
+ createEnterHandlerPlugin(),
4423
+ // Keymaps
4424
+ buildKeymap(schema2),
4425
+ keymap(baseKeymap)
4426
+ ];
4427
+ if (opts.enableHistory !== false) {
4428
+ plugins.push(history());
4429
+ }
4430
+ plugins.push(dropCursor());
4431
+ if (opts.enableTableResize !== false) {
4432
+ plugins.push(columnResizing());
4433
+ }
4434
+ plugins.push(createEditorPropsPlugin({ platform, linkOpener }));
4435
+ plugins.push(createCursorSyntaxPlugin());
4436
+ plugins.push(createLinkTextPlugin());
4437
+ plugins.push(createInlineCodeConvertPlugin());
4438
+ if (opts.enableImageSelection !== false) {
4439
+ plugins.push(createImageSelectionPlugin());
4440
+ }
4441
+ if (opts.onChange) {
4442
+ plugins.push(createLazyChangePlugin(opts.onChange, opts.changeDebounceMs));
4443
+ } else if (opts.onDocChanged) {
4444
+ plugins.push(createDirtyTrackPlugin(opts.onDocChanged));
4445
+ }
4446
+ if (tier1.highlight) plugins.push(tier1.highlight);
4447
+ if (tier1.emoji) plugins.push(tier1.emoji);
4448
+ return plugins;
4449
+ }
4450
+ async function createEditor(opts) {
4451
+ if (!opts.container) {
4452
+ throw new TypeError(
4453
+ "@moraya/core: createEditor() requires opts.container (HTMLElement)"
4454
+ );
4455
+ }
4456
+ if (!opts.mediaResolver) {
4457
+ throw new TypeError(
4458
+ "@moraya/core: createEditor() requires opts.mediaResolver"
4459
+ );
4460
+ }
4461
+ const schemaConfig = {
4462
+ mediaResolver: opts.mediaResolver,
4463
+ ...opts.rendererRegistry ? { rendererRegistry: opts.rendererRegistry } : {},
4464
+ ...opts.linkOpener ? { linkOpener: opts.linkOpener } : {}
4465
+ };
4466
+ const schema2 = createSchema(schemaConfig);
4467
+ const docCache = opts.docCache ?? createDocCache(10);
4468
+ void docCache;
4469
+ const plugins = await createEditorPlugins(opts, schema2);
4470
+ const tier1 = await preloadEnhancementPlugins(schema2, opts.rendererRegistry);
4471
+ const nodeViews = {};
4472
+ if (tier1.codeBlockView) {
4473
+ nodeViews.code_block = tier1.codeBlockView;
4474
+ }
4475
+ const initialDoc = opts.initialContent ? parseMarkdown(opts.initialContent, schema2) : schema2.topNodeType.createAndFill();
4476
+ const state = EditorState.create({ schema: schema2, doc: initialDoc, plugins });
4477
+ const view = new EditorView(opts.container, {
4478
+ state,
4479
+ nodeViews,
4480
+ attributes: {
4481
+ class: "moraya-editor",
4482
+ spellcheck: "true"
4483
+ }
4484
+ });
4485
+ if (opts.onFocus || opts.onBlur) {
4486
+ const editorDom = opts.container.querySelector(".ProseMirror");
4487
+ if (editorDom) {
4488
+ if (opts.onFocus) editorDom.addEventListener("focus", opts.onFocus);
4489
+ if (opts.onBlur) editorDom.addEventListener("blur", opts.onBlur);
4490
+ }
4491
+ }
4492
+ return {
4493
+ view,
4494
+ getMarkdown() {
4495
+ return serializeMarkdown(view.state.doc);
4496
+ },
4497
+ setContent(md2) {
4498
+ const newDoc = parseMarkdown(md2, schema2);
4499
+ const tr = view.state.tr.replaceWith(0, view.state.doc.content.size, newDoc.content);
4500
+ view.dispatch(tr);
4501
+ },
4502
+ destroy() {
4503
+ view.destroy();
4504
+ }
4505
+ };
4506
+ }
4507
+ export {
4508
+ createDocCache,
4509
+ createEditor,
4510
+ createEditorPlugins,
4511
+ createSchema,
4512
+ djb2Hash,
4513
+ getDocumentBaseDir,
4514
+ insertHorizontalRule,
4515
+ insertImage,
4516
+ insertMathBlock,
4517
+ insertTable,
4518
+ parseMarkdown,
4519
+ parseMarkdownAsync,
4520
+ preloadEnhancementPlugins,
4521
+ serializeMarkdown,
4522
+ setDocumentBaseDir,
4523
+ setHeading,
4524
+ toggleBlockquote,
4525
+ toggleBold,
4526
+ toggleBulletList,
4527
+ toggleCode,
4528
+ toggleCodeBlock,
4529
+ toggleItalic,
4530
+ toggleLink,
4531
+ toggleOrderedList,
4532
+ toggleStrikethrough
4533
+ };
4534
+ //# sourceMappingURL=index.js.map