@duskmoon-dev/el-markdown-input 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +999 -556
- package/dist/cjs/index.js.map +12 -9
- package/dist/cjs/register.js +989 -545
- package/dist/cjs/register.js.map +11 -8
- package/dist/esm/index.js +995 -547
- package/dist/esm/index.js.map +12 -9
- package/dist/esm/register.js +994 -543
- package/dist/esm/register.js.map +11 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/autocomplete.d.ts.map +1 -1
- package/dist/types/css.d.ts.map +1 -1
- package/dist/types/element.d.ts +30 -1
- package/dist/types/element.d.ts.map +1 -1
- package/dist/types/highlight.d.ts +1 -0
- package/dist/types/highlight.d.ts.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/pairs.d.ts +48 -0
- package/dist/types/pairs.d.ts.map +1 -0
- package/dist/types/render.d.ts +22 -0
- package/dist/types/render.d.ts.map +1 -0
- package/dist/types/sanitize-schema.d.ts +3 -0
- package/dist/types/sanitize-schema.d.ts.map +1 -0
- package/dist/types/upload.d.ts +12 -2
- package/dist/types/upload.d.ts.map +1 -1
- package/package.json +19 -6
package/dist/cjs/register.js
CHANGED
|
@@ -37,11 +37,172 @@ var __export = (target, all) => {
|
|
|
37
37
|
};
|
|
38
38
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
39
39
|
|
|
40
|
+
// src/sanitize-schema.ts
|
|
41
|
+
function deepMergeSchemas(base, extension) {
|
|
42
|
+
const result = { ...base };
|
|
43
|
+
if (extension.attributes) {
|
|
44
|
+
result.attributes = { ...base.attributes };
|
|
45
|
+
for (const [tag, attrs] of Object.entries(extension.attributes)) {
|
|
46
|
+
const existing = result.attributes[tag];
|
|
47
|
+
if (Array.isArray(existing)) {
|
|
48
|
+
result.attributes[tag] = [...existing, ...attrs];
|
|
49
|
+
} else {
|
|
50
|
+
result.attributes[tag] = attrs;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (extension.tagNames) {
|
|
55
|
+
result.tagNames = [...base.tagNames ?? [], ...extension.tagNames];
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
var import_rehype_sanitize, sanitizeSchema;
|
|
60
|
+
var init_sanitize_schema = __esm(() => {
|
|
61
|
+
import_rehype_sanitize = require("rehype-sanitize");
|
|
62
|
+
sanitizeSchema = deepMergeSchemas(import_rehype_sanitize.defaultSchema, {
|
|
63
|
+
attributes: {
|
|
64
|
+
span: ["className", "style"],
|
|
65
|
+
code: ["className"],
|
|
66
|
+
input: ["type", "checked", "disabled"],
|
|
67
|
+
math: ["xmlns", "display"],
|
|
68
|
+
annotation: ["encoding"],
|
|
69
|
+
mi: ["mathvariant"],
|
|
70
|
+
mo: ["stretchy", "lspace", "rspace"],
|
|
71
|
+
mpadded: ["height", "depth", "width", "lspace", "voffset"],
|
|
72
|
+
mspace: ["height", "depth", "width"],
|
|
73
|
+
mstyle: ["mathsize", "mathcolor", "mathbackground", "displaystyle"]
|
|
74
|
+
},
|
|
75
|
+
tagNames: [
|
|
76
|
+
"math",
|
|
77
|
+
"semantics",
|
|
78
|
+
"mrow",
|
|
79
|
+
"mi",
|
|
80
|
+
"mo",
|
|
81
|
+
"mn",
|
|
82
|
+
"msup",
|
|
83
|
+
"msub",
|
|
84
|
+
"mfrac",
|
|
85
|
+
"mover",
|
|
86
|
+
"munder",
|
|
87
|
+
"msqrt",
|
|
88
|
+
"mtable",
|
|
89
|
+
"mtr",
|
|
90
|
+
"mtd",
|
|
91
|
+
"annotation",
|
|
92
|
+
"mtext",
|
|
93
|
+
"mpadded",
|
|
94
|
+
"mspace",
|
|
95
|
+
"merror",
|
|
96
|
+
"mstyle",
|
|
97
|
+
"ms",
|
|
98
|
+
"mphantom",
|
|
99
|
+
"mmultiscripts",
|
|
100
|
+
"mprescripts"
|
|
101
|
+
]
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// src/render.ts
|
|
106
|
+
var exports_render = {};
|
|
107
|
+
__export(exports_render, {
|
|
108
|
+
renderMermaidBlocks: () => renderMermaidBlocks,
|
|
109
|
+
renderMarkdown: () => renderMarkdown
|
|
110
|
+
});
|
|
111
|
+
async function buildProcessor() {
|
|
112
|
+
const [
|
|
113
|
+
{ unified },
|
|
114
|
+
{ default: remarkParse },
|
|
115
|
+
{ default: remarkGfm },
|
|
116
|
+
{ default: remarkMath },
|
|
117
|
+
{ default: remarkRehype },
|
|
118
|
+
{ default: rehypeKatex },
|
|
119
|
+
{ default: rehypePrismPlus },
|
|
120
|
+
{ default: rehypeSanitize },
|
|
121
|
+
{ default: rehypeStringify }
|
|
122
|
+
] = await Promise.all([
|
|
123
|
+
import("unified"),
|
|
124
|
+
import("remark-parse"),
|
|
125
|
+
import("remark-gfm"),
|
|
126
|
+
import("remark-math"),
|
|
127
|
+
import("remark-rehype"),
|
|
128
|
+
import("rehype-katex"),
|
|
129
|
+
import("rehype-prism-plus"),
|
|
130
|
+
import("rehype-sanitize"),
|
|
131
|
+
import("rehype-stringify")
|
|
132
|
+
]);
|
|
133
|
+
return unified().use(remarkParse).use(remarkGfm).use(remarkMath).use(remarkRehype, { allowDangerousHtml: false }).use(rehypeKatex).use(rehypePrismPlus, { ignoreMissing: true }).use(rehypeSanitize, sanitizeSchema).use(rehypeStringify);
|
|
134
|
+
}
|
|
135
|
+
function getProcessor() {
|
|
136
|
+
if (!processorPromise) {
|
|
137
|
+
processorPromise = buildProcessor().catch((err) => {
|
|
138
|
+
processorPromise = null;
|
|
139
|
+
throw err;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return processorPromise;
|
|
143
|
+
}
|
|
144
|
+
async function renderMarkdown(source) {
|
|
145
|
+
const proc = await getProcessor();
|
|
146
|
+
const file = await proc.process(source);
|
|
147
|
+
return String(file);
|
|
148
|
+
}
|
|
149
|
+
function getCurrentTheme() {
|
|
150
|
+
if (typeof document === "undefined")
|
|
151
|
+
return "default";
|
|
152
|
+
return document.documentElement.getAttribute("data-theme") ?? "default";
|
|
153
|
+
}
|
|
154
|
+
async function renderMermaidBlocks(container, mermaidSrc) {
|
|
155
|
+
const blocks = container.querySelectorAll("pre > code.language-mermaid");
|
|
156
|
+
if (blocks.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
if (mermaidSrc !== undefined && !/^https:\/\//i.test(mermaidSrc)) {
|
|
159
|
+
console.warn(`[el-dm-markdown-input] mermaid-src "${mermaidSrc}" rejected — only https: URLs are allowed. Falling back to bundled mermaid.`);
|
|
160
|
+
mermaidSrc = undefined;
|
|
161
|
+
}
|
|
162
|
+
let mermaidModule;
|
|
163
|
+
try {
|
|
164
|
+
mermaidModule = mermaidSrc ? await import(mermaidSrc) : await import("mermaid");
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.error("[el-dm-markdown-input] Failed to load mermaid: %o", err);
|
|
167
|
+
blocks.forEach((block) => block.parentElement?.classList.add("mermaid-error"));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const mermaid = mermaidModule.default ?? mermaidModule;
|
|
171
|
+
mermaid.initialize({
|
|
172
|
+
startOnLoad: false,
|
|
173
|
+
theme: getCurrentTheme() === "moonlight" ? "dark" : "default",
|
|
174
|
+
fontFamily: "inherit"
|
|
175
|
+
});
|
|
176
|
+
for (const [i, block] of [...blocks].entries()) {
|
|
177
|
+
const pre = block.parentElement;
|
|
178
|
+
if (!pre)
|
|
179
|
+
continue;
|
|
180
|
+
const id = `mermaid-${++mermaidIdCounter}-${i}`;
|
|
181
|
+
try {
|
|
182
|
+
const { svg } = await mermaid.render(id, block.textContent ?? "");
|
|
183
|
+
const wrapper = document.createElement("div");
|
|
184
|
+
wrapper.className = "mermaid-diagram";
|
|
185
|
+
wrapper.innerHTML = svg;
|
|
186
|
+
pre.replaceWith(wrapper);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
console.error(`[el-dm-markdown-input] mermaid.render failed for block %s: %o
|
|
189
|
+
Source: %s`, id, err, block.textContent?.slice(0, 200));
|
|
190
|
+
pre.classList.add("mermaid-error");
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
var processorPromise = null, mermaidIdCounter = 0;
|
|
195
|
+
var init_render = __esm(() => {
|
|
196
|
+
init_sanitize_schema();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// src/element.ts
|
|
200
|
+
var import_el_base2 = require("@duskmoon-dev/el-base");
|
|
201
|
+
var import_markdown_body = require("@duskmoon-dev/core/components/markdown-body");
|
|
202
|
+
|
|
40
203
|
// src/css.ts
|
|
41
|
-
var import_el_base
|
|
42
|
-
var
|
|
43
|
-
import_el_base = require("@duskmoon-dev/el-base");
|
|
44
|
-
elementStyles = import_el_base.css`
|
|
204
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
205
|
+
var elementStyles = import_el_base.css`
|
|
45
206
|
/* ── Custom property defaults with design-system fallbacks ─────────── */
|
|
46
207
|
:host {
|
|
47
208
|
--md-border: var(--color-outline, #d0d7de);
|
|
@@ -90,6 +251,7 @@ var init_css = __esm(() => {
|
|
|
90
251
|
background: var(--md-bg);
|
|
91
252
|
color: var(--md-text);
|
|
92
253
|
overflow: hidden;
|
|
254
|
+
height: inherit;
|
|
93
255
|
}
|
|
94
256
|
|
|
95
257
|
.editor:focus-within {
|
|
@@ -139,74 +301,62 @@ var init_css = __esm(() => {
|
|
|
139
301
|
border-radius: 3px;
|
|
140
302
|
}
|
|
141
303
|
|
|
142
|
-
/* ── Write area (
|
|
304
|
+
/* ── Write area (render-layer + textarea overlay) ──────────────────── */
|
|
305
|
+
/*
|
|
306
|
+
* CodeMirror-style render model: .render-layer sits in normal flow and
|
|
307
|
+
* drives the container height; the textarea is absolutely positioned on
|
|
308
|
+
* top. No scroll sync required — both layers always share the same size.
|
|
309
|
+
*/
|
|
143
310
|
.write-area {
|
|
144
311
|
position: relative;
|
|
145
312
|
min-height: 12rem;
|
|
146
|
-
flex: 1;
|
|
313
|
+
flex: 1 1 auto;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.write-area[hidden] {
|
|
317
|
+
display: none;
|
|
147
318
|
}
|
|
148
319
|
|
|
149
320
|
/*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
321
|
+
* Render layer: highlighted HTML in normal flow. Drives container height.
|
|
322
|
+
* pointer-events: none lets clicks pass through to the textarea underneath.
|
|
323
|
+
* Font metrics MUST match the textarea exactly for pixel-aligned overlay.
|
|
152
324
|
*/
|
|
153
|
-
.
|
|
154
|
-
position:
|
|
155
|
-
|
|
325
|
+
.render-layer {
|
|
326
|
+
position: relative;
|
|
327
|
+
z-index: 1;
|
|
156
328
|
pointer-events: none;
|
|
157
|
-
|
|
158
|
-
* Use overflow: auto (not overflow: hidden) so the backdrop reserves
|
|
159
|
-
* the same scrollbar gutter as the textarea when content overflows.
|
|
160
|
-
* Without this, the textarea scrollbar narrows its text area but the
|
|
161
|
-
* backdrop stays full-width — lines wrap at different points — causing
|
|
162
|
-
* the cursor to appear misaligned with the highlighted text.
|
|
163
|
-
*/
|
|
164
|
-
overflow: auto;
|
|
165
|
-
scrollbar-width: none; /* Firefox */
|
|
166
|
-
border: none;
|
|
167
|
-
background: transparent;
|
|
168
|
-
/*
|
|
169
|
-
* Do NOT put white-space: pre-wrap here. The backdrop div contains a
|
|
170
|
-
* backdrop-content child, and the HTML template has whitespace text
|
|
171
|
-
* nodes (newline + indent) between them. With pre-wrap on the parent
|
|
172
|
-
* those text nodes render as a visible leading newline, shifting all
|
|
173
|
-
* content down by one line and misaligning the cursor vertically.
|
|
174
|
-
* pre-wrap lives on .backdrop-content instead.
|
|
175
|
-
*/
|
|
176
|
-
|
|
329
|
+
min-height: 12rem;
|
|
177
330
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
178
331
|
font-size: 0.875rem;
|
|
179
332
|
line-height: 1.6;
|
|
180
333
|
padding: 0.75rem;
|
|
181
|
-
color: var(--md-text);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.backdrop::-webkit-scrollbar {
|
|
185
|
-
display: none; /* Chrome / Safari */
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.backdrop-content {
|
|
189
|
-
display: block;
|
|
190
334
|
white-space: pre-wrap;
|
|
191
335
|
word-wrap: break-word;
|
|
192
336
|
overflow-wrap: break-word;
|
|
193
|
-
|
|
337
|
+
color: var(--md-text);
|
|
194
338
|
}
|
|
195
339
|
|
|
340
|
+
/*
|
|
341
|
+
* Textarea: absolute overlay on top of the render layer. Transparent text
|
|
342
|
+
* lets highlighted content show through; caret-color keeps cursor visible.
|
|
343
|
+
* overflow: hidden — the render layer drives height, not the textarea.
|
|
344
|
+
*/
|
|
196
345
|
textarea {
|
|
197
|
-
position:
|
|
346
|
+
position: absolute;
|
|
347
|
+
inset: 0;
|
|
348
|
+
z-index: 2;
|
|
198
349
|
display: block;
|
|
199
350
|
width: 100%;
|
|
200
|
-
|
|
351
|
+
height: 100%;
|
|
201
352
|
border: none;
|
|
202
353
|
outline: none;
|
|
203
|
-
resize:
|
|
354
|
+
resize: none;
|
|
204
355
|
background: transparent;
|
|
205
356
|
color: transparent;
|
|
206
357
|
caret-color: var(--md-text);
|
|
207
358
|
box-sizing: border-box;
|
|
208
|
-
|
|
209
|
-
/* MUST match .backdrop exactly */
|
|
359
|
+
overflow: hidden;
|
|
210
360
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
211
361
|
font-size: 0.875rem;
|
|
212
362
|
line-height: 1.6;
|
|
@@ -229,11 +379,100 @@ var init_css = __esm(() => {
|
|
|
229
379
|
.preview-body {
|
|
230
380
|
padding: 0.75rem;
|
|
231
381
|
min-height: 12rem;
|
|
382
|
+
height: stretch;
|
|
383
|
+
flex: 1 1 auto;
|
|
384
|
+
display: flex;
|
|
385
|
+
flex-direction: column;
|
|
232
386
|
overflow-y: auto;
|
|
233
387
|
color: var(--md-text);
|
|
234
388
|
/* .markdown-body styles come from @duskmoon-dev/core via the element */
|
|
235
389
|
}
|
|
236
390
|
|
|
391
|
+
.preview-body[hidden] {
|
|
392
|
+
display: none;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* ── Preview skeleton (shown while render pipeline loads) ──────────── */
|
|
396
|
+
.preview-skeleton {
|
|
397
|
+
display: flex;
|
|
398
|
+
flex-direction: column;
|
|
399
|
+
gap: 0.75rem;
|
|
400
|
+
padding: 0.5rem 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
.skeleton-line {
|
|
404
|
+
height: 0.875rem;
|
|
405
|
+
background: linear-gradient(
|
|
406
|
+
90deg,
|
|
407
|
+
var(--md-bg-toolbar) 25%,
|
|
408
|
+
var(--md-bg-hover) 50%,
|
|
409
|
+
var(--md-bg-toolbar) 75%
|
|
410
|
+
);
|
|
411
|
+
background-size: 200% 100%;
|
|
412
|
+
border-radius: 4px;
|
|
413
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
@keyframes skeleton-shimmer {
|
|
417
|
+
0% {
|
|
418
|
+
background-position: 200% 0;
|
|
419
|
+
}
|
|
420
|
+
100% {
|
|
421
|
+
background-position: -200% 0;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@media (prefers-reduced-motion: reduce) {
|
|
426
|
+
.skeleton-line {
|
|
427
|
+
animation: none;
|
|
428
|
+
background: var(--md-bg-hover);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/* ── Mermaid diagram blocks ────────────────────────────────────────── */
|
|
433
|
+
.mermaid-diagram {
|
|
434
|
+
display: flex;
|
|
435
|
+
justify-content: center;
|
|
436
|
+
margin: 1rem 0;
|
|
437
|
+
overflow-x: auto;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.mermaid-error {
|
|
441
|
+
border-left: 3px solid var(--md-color-error);
|
|
442
|
+
opacity: 0.7;
|
|
443
|
+
position: relative;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.mermaid-error::before {
|
|
447
|
+
content: 'Mermaid render failed';
|
|
448
|
+
display: block;
|
|
449
|
+
font-size: 0.75rem;
|
|
450
|
+
color: var(--md-color-error);
|
|
451
|
+
font-family: inherit;
|
|
452
|
+
margin-bottom: 0.25rem;
|
|
453
|
+
padding-left: 0.5rem;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* ── Render error fallback ──────────────────────────────────────────── */
|
|
457
|
+
.render-error-fallback {
|
|
458
|
+
white-space: pre-wrap;
|
|
459
|
+
word-wrap: break-word;
|
|
460
|
+
font-size: 0.875rem;
|
|
461
|
+
opacity: 0.8;
|
|
462
|
+
border-left: 3px solid var(--md-color-error);
|
|
463
|
+
padding-left: 0.75rem;
|
|
464
|
+
color: var(--md-text-muted);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.render-error-fallback::before {
|
|
468
|
+
content: 'Preview render failed — showing raw markdown';
|
|
469
|
+
display: block;
|
|
470
|
+
font-size: 0.75rem;
|
|
471
|
+
color: var(--md-color-error);
|
|
472
|
+
font-family: inherit;
|
|
473
|
+
margin-bottom: 0.5rem;
|
|
474
|
+
}
|
|
475
|
+
|
|
237
476
|
/* ── Status bar ─────────────────────────────────────────────────────── */
|
|
238
477
|
.status-bar {
|
|
239
478
|
display: flex;
|
|
@@ -269,6 +508,12 @@ var init_css = __esm(() => {
|
|
|
269
508
|
background: var(--md-bg-hover);
|
|
270
509
|
}
|
|
271
510
|
|
|
511
|
+
.attach-btn:disabled {
|
|
512
|
+
cursor: not-allowed;
|
|
513
|
+
opacity: 0.5;
|
|
514
|
+
pointer-events: none;
|
|
515
|
+
}
|
|
516
|
+
|
|
272
517
|
.attach-btn:focus-visible {
|
|
273
518
|
outline: 2px solid var(--md-accent);
|
|
274
519
|
outline-offset: 1px;
|
|
@@ -403,14 +648,38 @@ var init_css = __esm(() => {
|
|
|
403
648
|
text-overflow: ellipsis;
|
|
404
649
|
white-space: nowrap;
|
|
405
650
|
}
|
|
651
|
+
|
|
652
|
+
/* ── Reduced motion: disable all transitions and animations ──────── */
|
|
653
|
+
@media (prefers-reduced-motion: reduce) {
|
|
654
|
+
.tab-btn,
|
|
655
|
+
.attach-btn,
|
|
656
|
+
.ac-item,
|
|
657
|
+
.upload-bar {
|
|
658
|
+
transition: none;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
406
661
|
`;
|
|
407
|
-
});
|
|
408
662
|
|
|
409
663
|
// src/highlight.ts
|
|
664
|
+
var PRISM_BASE = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0";
|
|
665
|
+
var PRISM_CORE_URL = `${PRISM_BASE}/prism.min.js`;
|
|
666
|
+
var PRISM_AUTOLOADER_URL = `${PRISM_BASE}/plugins/autoloader/prism-autoloader.min.js`;
|
|
667
|
+
var PRISM_SRI = {
|
|
668
|
+
[PRISM_CORE_URL]: "sha512-7Z9J3l1+EYfeaPKcGXu3MS/7T+w19WtKQY/n+xzmw4hZhJ9tyYmcUS+4QqAlzhicE5LAfMQSF3iFTK9bQdTxXg==",
|
|
669
|
+
[PRISM_AUTOLOADER_URL]: "sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw=="
|
|
670
|
+
};
|
|
671
|
+
var PRISM_THEME_DARK_URL = `${PRISM_BASE}/themes/prism-tomorrow.min.css`;
|
|
672
|
+
var PRISM_THEME_LIGHT_URL = `${PRISM_BASE}/themes/prism-coy.min.css`;
|
|
673
|
+
var _prismReady = null;
|
|
410
674
|
function _loadScript(src) {
|
|
411
675
|
return new Promise((resolve) => {
|
|
412
676
|
const script = document.createElement("script");
|
|
413
677
|
script.src = src;
|
|
678
|
+
const integrity = PRISM_SRI[src];
|
|
679
|
+
if (integrity) {
|
|
680
|
+
script.integrity = integrity;
|
|
681
|
+
script.crossOrigin = "anonymous";
|
|
682
|
+
}
|
|
414
683
|
script.onload = () => resolve();
|
|
415
684
|
script.onerror = () => resolve();
|
|
416
685
|
document.head.appendChild(script);
|
|
@@ -422,19 +691,22 @@ function ensurePrism() {
|
|
|
422
691
|
if (_prismReady)
|
|
423
692
|
return _prismReady;
|
|
424
693
|
_prismReady = _loadScript(PRISM_CORE_URL).then(() => {
|
|
425
|
-
if (!window.Prism)
|
|
694
|
+
if (!window.Prism) {
|
|
695
|
+
_prismReady = null;
|
|
426
696
|
return;
|
|
697
|
+
}
|
|
427
698
|
window.Prism.manual = true;
|
|
428
699
|
return _loadScript(PRISM_AUTOLOADER_URL).then(() => {
|
|
429
700
|
if (window.Prism?.plugins?.autoloader) {
|
|
430
701
|
window.Prism.plugins.autoloader.languages_path = `${PRISM_BASE}/components/`;
|
|
431
702
|
}
|
|
703
|
+
return _loadScript(`${PRISM_BASE}/components/prism-markdown.min.js`);
|
|
432
704
|
});
|
|
433
705
|
});
|
|
434
706
|
return _prismReady;
|
|
435
707
|
}
|
|
436
708
|
function escapeHtml(text) {
|
|
437
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
709
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
438
710
|
}
|
|
439
711
|
function highlightMarkdown(text) {
|
|
440
712
|
const escaped = escapeHtml(text);
|
|
@@ -456,20 +728,35 @@ function applyPrismTheme(shadowRoot, dark) {
|
|
|
456
728
|
styleEl.id = "prism-theme";
|
|
457
729
|
shadowRoot.appendChild(styleEl);
|
|
458
730
|
}
|
|
459
|
-
const
|
|
731
|
+
const previewOverrides = `
|
|
732
|
+
.preview-body pre[class*="language-"] {
|
|
733
|
+
background: transparent;
|
|
734
|
+
margin: 0;
|
|
735
|
+
padding: 0;
|
|
736
|
+
overflow: visible;
|
|
737
|
+
position: static;
|
|
738
|
+
}
|
|
739
|
+
.preview-body pre[class*="language-"] > code {
|
|
740
|
+
background: transparent;
|
|
741
|
+
border: none;
|
|
742
|
+
box-shadow: none;
|
|
743
|
+
padding: 0;
|
|
744
|
+
background-image: none;
|
|
745
|
+
display: block;
|
|
746
|
+
overflow: auto;
|
|
747
|
+
max-height: none;
|
|
748
|
+
height: auto;
|
|
749
|
+
}`;
|
|
750
|
+
const expected = `@import url("${themeUrl}");${previewOverrides}`;
|
|
460
751
|
if (styleEl.textContent !== expected) {
|
|
461
752
|
styleEl.textContent = expected;
|
|
462
753
|
}
|
|
463
754
|
}
|
|
464
|
-
var PRISM_BASE = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0", PRISM_CORE_URL, PRISM_AUTOLOADER_URL, PRISM_THEME_DARK_URL, PRISM_THEME_LIGHT_URL, _prismReady = null;
|
|
465
|
-
var init_highlight = __esm(() => {
|
|
466
|
-
PRISM_CORE_URL = `${PRISM_BASE}/prism.min.js`;
|
|
467
|
-
PRISM_AUTOLOADER_URL = `${PRISM_BASE}/plugins/autoloader/prism-autoloader.min.js`;
|
|
468
|
-
PRISM_THEME_DARK_URL = `${PRISM_BASE}/themes/prism-tomorrow.min.css`;
|
|
469
|
-
PRISM_THEME_LIGHT_URL = `${PRISM_BASE}/themes/prism-coy.min.css`;
|
|
470
|
-
});
|
|
471
755
|
|
|
472
756
|
// src/upload.ts
|
|
757
|
+
var ACCEPTED_MIME_PREFIXES = ["image/"];
|
|
758
|
+
var ACCEPTED_MIME_EXACT = ["application/pdf"];
|
|
759
|
+
var ACCEPTED_EXTENSIONS = [".zip", ".txt", ".csv", ".json", ".md"];
|
|
473
760
|
function isAcceptedType(file) {
|
|
474
761
|
const type = file.type.toLowerCase();
|
|
475
762
|
if (ACCEPTED_MIME_PREFIXES.some((p) => type.startsWith(p)))
|
|
@@ -482,12 +769,19 @@ function isAcceptedType(file) {
|
|
|
482
769
|
return false;
|
|
483
770
|
}
|
|
484
771
|
function fileToMarkdown(file, url) {
|
|
772
|
+
const isSafeUrl = /^https:\/\//i.test(url) || /^\//.test(url) || /^\.\.?\//.test(url);
|
|
773
|
+
const safeUrl = isSafeUrl ? url.replace(/\(/g, "%28").replace(/\)/g, "%29") : "#unsafe-url";
|
|
774
|
+
const safeName = file.name.replace(/[[\]]/g, "\\$&");
|
|
485
775
|
if (file.type.startsWith("image/")) {
|
|
486
|
-
return ``;
|
|
487
777
|
}
|
|
488
|
-
return `[${
|
|
778
|
+
return `[${safeName}](${safeUrl})`;
|
|
489
779
|
}
|
|
490
780
|
function uploadFile(file, uploadUrl, onProgress) {
|
|
781
|
+
const isSafeUploadUrl = /^https:\/\//i.test(uploadUrl) || /^\//.test(uploadUrl) || /^\.\.?\//.test(uploadUrl);
|
|
782
|
+
if (!isSafeUploadUrl) {
|
|
783
|
+
return Promise.reject(new Error(`[el-dm-markdown-input] upload-url "${uploadUrl}" rejected — only https: and relative URLs are allowed.`));
|
|
784
|
+
}
|
|
491
785
|
return new Promise((resolve, reject) => {
|
|
492
786
|
const xhr = new XMLHttpRequest;
|
|
493
787
|
const body = new FormData;
|
|
@@ -504,27 +798,21 @@ function uploadFile(file, uploadUrl, onProgress) {
|
|
|
504
798
|
if (data.url) {
|
|
505
799
|
resolve(data.url);
|
|
506
800
|
} else {
|
|
507
|
-
reject("Upload response missing url field");
|
|
801
|
+
reject(new Error("Upload response missing url field"));
|
|
508
802
|
}
|
|
509
803
|
} catch {
|
|
510
|
-
reject("Upload response is not valid JSON");
|
|
804
|
+
reject(new Error("Upload response is not valid JSON"));
|
|
511
805
|
}
|
|
512
806
|
} else {
|
|
513
|
-
reject(`Upload failed with status ${xhr.status}`);
|
|
807
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
514
808
|
}
|
|
515
809
|
});
|
|
516
|
-
xhr.addEventListener("error", () => reject("Network error during upload"));
|
|
517
|
-
xhr.addEventListener("abort", () => reject("Upload aborted"));
|
|
810
|
+
xhr.addEventListener("error", () => reject(new Error("Network error during upload")));
|
|
811
|
+
xhr.addEventListener("abort", () => reject(new Error("Upload aborted")));
|
|
518
812
|
xhr.open("POST", uploadUrl);
|
|
519
813
|
xhr.send(body);
|
|
520
814
|
});
|
|
521
815
|
}
|
|
522
|
-
var ACCEPTED_MIME_PREFIXES, ACCEPTED_MIME_EXACT, ACCEPTED_EXTENSIONS;
|
|
523
|
-
var init_upload = __esm(() => {
|
|
524
|
-
ACCEPTED_MIME_PREFIXES = ["image/"];
|
|
525
|
-
ACCEPTED_MIME_EXACT = ["application/pdf"];
|
|
526
|
-
ACCEPTED_EXTENSIONS = [".zip", ".txt", ".csv", ".json", ".md"];
|
|
527
|
-
});
|
|
528
816
|
|
|
529
817
|
// src/autocomplete.ts
|
|
530
818
|
function detectTrigger(value, cursorPos) {
|
|
@@ -535,13 +823,13 @@ function detectTrigger(value, cursorPos) {
|
|
|
535
823
|
const query = value.slice(i + 1, cursorPos);
|
|
536
824
|
if (!/\s/.test(query)) {
|
|
537
825
|
const before = i > 0 ? value[i - 1] : null;
|
|
538
|
-
if (before === null ||
|
|
826
|
+
if (before === null || /\s/.test(before)) {
|
|
539
827
|
return { trigger: ch, query, triggerPos: i };
|
|
540
828
|
}
|
|
541
829
|
}
|
|
542
830
|
return null;
|
|
543
831
|
}
|
|
544
|
-
if (
|
|
832
|
+
if (/\s/.test(ch)) {
|
|
545
833
|
return null;
|
|
546
834
|
}
|
|
547
835
|
i--;
|
|
@@ -552,8 +840,11 @@ function confirmSuggestion(value, triggerPos, cursorPos, trigger, replacement) {
|
|
|
552
840
|
const before = value.slice(0, triggerPos);
|
|
553
841
|
const after = value.slice(cursorPos);
|
|
554
842
|
const inserted = `${trigger}${replacement}`;
|
|
555
|
-
const
|
|
556
|
-
|
|
843
|
+
const needsSpace = after.length === 0 || after[0] !== " " && after[0] !== `
|
|
844
|
+
`;
|
|
845
|
+
const suffix = needsSpace ? " " : "";
|
|
846
|
+
const newValue = before + inserted + suffix + after;
|
|
847
|
+
const newCursorPos = triggerPos + inserted.length + suffix.length;
|
|
557
848
|
return { newValue, newCursorPos };
|
|
558
849
|
}
|
|
559
850
|
function renderDropdown(suggestions, selectedIndex) {
|
|
@@ -569,7 +860,66 @@ function renderDropdown(suggestions, selectedIndex) {
|
|
|
569
860
|
return items;
|
|
570
861
|
}
|
|
571
862
|
function escapeHtml2(text) {
|
|
572
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
863
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/pairs.ts
|
|
867
|
+
function handlePairKey(ta, key) {
|
|
868
|
+
if (key !== "`")
|
|
869
|
+
return false;
|
|
870
|
+
const start = ta.selectionStart;
|
|
871
|
+
const end = ta.selectionEnd;
|
|
872
|
+
const value = ta.value;
|
|
873
|
+
if (start !== end) {
|
|
874
|
+
const selected = value.slice(start, end);
|
|
875
|
+
ta.value = value.slice(0, start) + "`" + selected + "`" + value.slice(end);
|
|
876
|
+
ta.setSelectionRange(start + 1, end + 1);
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
if (start >= 2 && value.slice(start - 2, start) === "``") {
|
|
880
|
+
ta.value = value.slice(0, start) + "`\n\n```" + value.slice(end);
|
|
881
|
+
ta.setSelectionRange(start + 2, start + 2);
|
|
882
|
+
return true;
|
|
883
|
+
}
|
|
884
|
+
ta.value = value.slice(0, start) + "``" + value.slice(end);
|
|
885
|
+
ta.setSelectionRange(start + 1, start + 1);
|
|
886
|
+
return true;
|
|
887
|
+
}
|
|
888
|
+
function handleEnterKey(ta, e) {
|
|
889
|
+
if (e.key !== "Enter")
|
|
890
|
+
return false;
|
|
891
|
+
const pos = ta.selectionStart;
|
|
892
|
+
const value = ta.value;
|
|
893
|
+
if (ta.selectionEnd !== pos)
|
|
894
|
+
return false;
|
|
895
|
+
const lineStart = value.lastIndexOf(`
|
|
896
|
+
`, pos - 1) + 1;
|
|
897
|
+
const currentLine = value.slice(lineStart, pos);
|
|
898
|
+
const result = getLineContinuation(currentLine);
|
|
899
|
+
if (result === null)
|
|
900
|
+
return false;
|
|
901
|
+
e.preventDefault();
|
|
902
|
+
if (result.eraseCurrentLine) {
|
|
903
|
+
const newValue = value.slice(0, lineStart) + value.slice(pos);
|
|
904
|
+
ta.value = newValue;
|
|
905
|
+
ta.setSelectionRange(lineStart, lineStart);
|
|
906
|
+
} else {
|
|
907
|
+
const newValue = value.slice(0, pos) + `
|
|
908
|
+
` + result.prefix + value.slice(ta.selectionEnd);
|
|
909
|
+
const newPos = pos + 1 + result.prefix.length;
|
|
910
|
+
ta.value = newValue;
|
|
911
|
+
ta.setSelectionRange(newPos, newPos);
|
|
912
|
+
}
|
|
913
|
+
return true;
|
|
914
|
+
}
|
|
915
|
+
function getLineContinuation(line) {
|
|
916
|
+
if (line === "* ")
|
|
917
|
+
return { eraseCurrentLine: true };
|
|
918
|
+
if (/^\* ./.test(line))
|
|
919
|
+
return { prefix: "* ", eraseCurrentLine: false };
|
|
920
|
+
if (/^#{1,6} ./.test(line))
|
|
921
|
+
return { prefix: "", eraseCurrentLine: false };
|
|
922
|
+
return null;
|
|
573
923
|
}
|
|
574
924
|
|
|
575
925
|
// src/status-bar.ts
|
|
@@ -599,155 +949,156 @@ function renderStatusCount(wordCount, charCount, maxWords) {
|
|
|
599
949
|
}
|
|
600
950
|
|
|
601
951
|
// src/element.ts
|
|
602
|
-
var
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
});
|
|
606
|
-
function sanitizeUrl(url) {
|
|
607
|
-
const trimmed = url.trim();
|
|
608
|
-
if (!/^[a-z][a-z\d+\-.]*:/i.test(trimmed))
|
|
609
|
-
return trimmed;
|
|
610
|
-
if (/^https?:/i.test(trimmed))
|
|
611
|
-
return trimmed;
|
|
612
|
-
return "#";
|
|
613
|
-
}
|
|
614
|
-
function escapeHtmlStr(s) {
|
|
615
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
616
|
-
}
|
|
617
|
-
var import_el_base2, import_markdown_body, import_el_base3, coreMarkdownStyles, markdownBodySheet, ElDmMarkdownInput;
|
|
618
|
-
var init_element = __esm(() => {
|
|
619
|
-
init_css();
|
|
620
|
-
init_highlight();
|
|
621
|
-
init_upload();
|
|
622
|
-
import_el_base2 = require("@duskmoon-dev/el-base");
|
|
623
|
-
import_markdown_body = require("@duskmoon-dev/core/components/markdown-body");
|
|
624
|
-
import_el_base3 = require("@duskmoon-dev/el-base");
|
|
625
|
-
coreMarkdownStyles = import_markdown_body.css.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
626
|
-
markdownBodySheet = import_el_base3.css`
|
|
952
|
+
var import_el_base3 = require("@duskmoon-dev/el-base");
|
|
953
|
+
var coreMarkdownStyles = import_markdown_body.css.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
954
|
+
var markdownBodySheet = import_el_base3.css`
|
|
627
955
|
${coreMarkdownStyles}
|
|
628
956
|
`;
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
957
|
+
|
|
958
|
+
class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
959
|
+
static formAssociated = true;
|
|
960
|
+
static properties = {
|
|
961
|
+
name: { type: String, reflect: true, default: "" },
|
|
962
|
+
value: { type: String, default: "" },
|
|
963
|
+
placeholder: { type: String, reflect: true, default: "Write markdown…" },
|
|
964
|
+
disabled: { type: Boolean, reflect: true },
|
|
965
|
+
readonly: { type: Boolean, reflect: true },
|
|
966
|
+
required: { type: Boolean, reflect: true },
|
|
967
|
+
uploadUrl: { type: String, reflect: true, attribute: "upload-url" },
|
|
968
|
+
maxWords: { type: Number, reflect: true, attribute: "max-words" },
|
|
969
|
+
dark: { type: Boolean, reflect: true },
|
|
970
|
+
livePreview: { type: Boolean, reflect: true, attribute: "live-preview" },
|
|
971
|
+
debounce: { type: Number, reflect: true, default: 300 },
|
|
972
|
+
katexCssUrl: { type: String, reflect: true, attribute: "katex-css-url" },
|
|
973
|
+
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" }
|
|
974
|
+
};
|
|
975
|
+
#internals;
|
|
976
|
+
#initialized = false;
|
|
977
|
+
#activeTab = "write";
|
|
978
|
+
#highlightTimer = null;
|
|
979
|
+
#statusTimer = null;
|
|
980
|
+
#textarea = null;
|
|
981
|
+
#renderLayer = null;
|
|
982
|
+
#writeArea = null;
|
|
983
|
+
#previewBody = null;
|
|
984
|
+
#statusCount = null;
|
|
985
|
+
#acDropdown = null;
|
|
986
|
+
#uploadList = null;
|
|
987
|
+
#fileInput = null;
|
|
988
|
+
#acSuggestions = [];
|
|
989
|
+
#acSelectedIndex = -1;
|
|
990
|
+
#acTriggerPos = -1;
|
|
991
|
+
#acTrigger = null;
|
|
992
|
+
#acGeneration = 0;
|
|
993
|
+
#prevDark = false;
|
|
994
|
+
#renderFn = null;
|
|
995
|
+
#mermaidFn = null;
|
|
996
|
+
#livePreviewTimer = null;
|
|
997
|
+
#renderAbortController = null;
|
|
998
|
+
#lastRenderedSource = null;
|
|
999
|
+
#katexCssInjected = false;
|
|
1000
|
+
#uploadIdCounter = 0;
|
|
1001
|
+
constructor() {
|
|
1002
|
+
super();
|
|
1003
|
+
this.#internals = this.attachInternals();
|
|
1004
|
+
this.attachStyles([elementStyles, markdownBodySheet]);
|
|
1005
|
+
}
|
|
1006
|
+
connectedCallback() {
|
|
1007
|
+
super.connectedCallback();
|
|
1008
|
+
}
|
|
1009
|
+
disconnectedCallback() {
|
|
1010
|
+
this.#renderAbortController?.abort();
|
|
1011
|
+
if (this.#livePreviewTimer !== null)
|
|
1012
|
+
clearTimeout(this.#livePreviewTimer);
|
|
1013
|
+
super.disconnectedCallback();
|
|
1014
|
+
}
|
|
1015
|
+
update() {
|
|
1016
|
+
if (!this.#initialized) {
|
|
1017
|
+
super.update();
|
|
1018
|
+
this.#initialized = true;
|
|
1019
|
+
this.#cacheDOMRefs();
|
|
1020
|
+
this.#attachEventHandlers();
|
|
1021
|
+
this.#initHighlight();
|
|
1022
|
+
const initVal = this.value ?? "";
|
|
1023
|
+
if (this.#textarea) {
|
|
1024
|
+
this.#textarea.value = initVal;
|
|
671
1025
|
this.#syncFormValue();
|
|
1026
|
+
if (initVal)
|
|
1027
|
+
this.#scheduleHighlight();
|
|
672
1028
|
}
|
|
1029
|
+
this.#updateStatusBarNow();
|
|
1030
|
+
return;
|
|
673
1031
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1032
|
+
this.#patchDynamicRegions();
|
|
1033
|
+
}
|
|
1034
|
+
#patchDynamicRegions() {
|
|
1035
|
+
const ta = this.#textarea;
|
|
1036
|
+
if (!ta)
|
|
1037
|
+
return;
|
|
1038
|
+
const placeholder = this.placeholder ?? "Write markdown…";
|
|
1039
|
+
ta.placeholder = placeholder;
|
|
1040
|
+
ta.disabled = !!this.disabled;
|
|
1041
|
+
ta.readOnly = !!this.readonly;
|
|
1042
|
+
const attachBtn = this.shadowRoot.querySelector(".attach-btn");
|
|
1043
|
+
if (attachBtn) {
|
|
1044
|
+
attachBtn.disabled = ta.disabled || ta.readOnly;
|
|
677
1045
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
this.#
|
|
685
|
-
this.#updateStatusBarNow();
|
|
686
|
-
const initVal = this.value ?? "";
|
|
687
|
-
if (initVal && this.#textarea) {
|
|
688
|
-
this.#textarea.value = initVal;
|
|
689
|
-
this.#syncFormValue();
|
|
690
|
-
this.#scheduleHighlight();
|
|
691
|
-
}
|
|
692
|
-
return;
|
|
1046
|
+
const propVal = this.value ?? "";
|
|
1047
|
+
if (propVal !== ta.value) {
|
|
1048
|
+
ta.value = propVal;
|
|
1049
|
+
this.#syncFormValue();
|
|
1050
|
+
this.#scheduleHighlight();
|
|
1051
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1052
|
+
this.#renderPreview(propVal);
|
|
693
1053
|
}
|
|
694
|
-
this.#patchDynamicRegions();
|
|
695
1054
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
ta.readOnly = !!this.readonly;
|
|
704
|
-
const attachBtn = this.shadowRoot.querySelector(".attach-btn");
|
|
705
|
-
if (attachBtn) {
|
|
706
|
-
attachBtn.disabled = ta.disabled || ta.readOnly;
|
|
707
|
-
}
|
|
708
|
-
const propVal = this.value ?? "";
|
|
709
|
-
if (propVal !== ta.value) {
|
|
710
|
-
ta.value = propVal;
|
|
711
|
-
this.#syncFormValue();
|
|
712
|
-
this.#scheduleHighlight();
|
|
1055
|
+
const dark = !!this.dark;
|
|
1056
|
+
applyPrismTheme(this.shadowRoot, dark);
|
|
1057
|
+
if (dark !== this.#prevDark) {
|
|
1058
|
+
this.#prevDark = dark;
|
|
1059
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1060
|
+
this.#lastRenderedSource = null;
|
|
1061
|
+
this.#renderPreview(ta.value);
|
|
713
1062
|
}
|
|
714
|
-
const dark = !!this.dark;
|
|
715
|
-
applyPrismTheme(this.shadowRoot, dark);
|
|
716
|
-
this.#updateStatusBarNow();
|
|
717
1063
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1064
|
+
this.#updateStatusBarNow();
|
|
1065
|
+
}
|
|
1066
|
+
render() {
|
|
1067
|
+
const ph = this.placeholder ?? "Write markdown…";
|
|
1068
|
+
const disabled = !!this.disabled;
|
|
1069
|
+
const readonly = !!this.readonly;
|
|
1070
|
+
return `
|
|
723
1071
|
<div class="editor">
|
|
724
1072
|
<div class="toolbar" role="tablist" aria-label="Editor mode">
|
|
725
1073
|
<button
|
|
726
1074
|
class="tab-btn"
|
|
1075
|
+
id="tab-write"
|
|
727
1076
|
data-tab="write"
|
|
728
1077
|
role="tab"
|
|
729
1078
|
aria-selected="true"
|
|
730
1079
|
aria-controls="write-panel"
|
|
1080
|
+
tabindex="0"
|
|
731
1081
|
>Write</button>
|
|
732
1082
|
<button
|
|
733
1083
|
class="tab-btn"
|
|
1084
|
+
id="tab-preview"
|
|
734
1085
|
data-tab="preview"
|
|
735
1086
|
role="tab"
|
|
736
1087
|
aria-selected="false"
|
|
737
1088
|
aria-controls="preview-panel"
|
|
1089
|
+
tabindex="-1"
|
|
738
1090
|
>Preview</button>
|
|
739
1091
|
</div>
|
|
740
1092
|
|
|
741
|
-
<div class="write-area" id="write-panel" role="tabpanel" aria-
|
|
742
|
-
<div class="
|
|
743
|
-
<div class="backdrop-content"></div>
|
|
744
|
-
</div>
|
|
1093
|
+
<div class="write-area" id="write-panel" role="tabpanel" aria-labelledby="tab-write">
|
|
1094
|
+
<div class="render-layer" aria-hidden="true"></div>
|
|
745
1095
|
<textarea
|
|
746
1096
|
aria-label="Markdown editor"
|
|
747
1097
|
aria-haspopup="listbox"
|
|
1098
|
+
aria-expanded="false"
|
|
748
1099
|
aria-autocomplete="list"
|
|
749
1100
|
aria-controls="ac-dropdown"
|
|
750
|
-
placeholder="${ph}"
|
|
1101
|
+
placeholder="${escapeHtmlStr(ph)}"
|
|
751
1102
|
${disabled ? "disabled" : ""}
|
|
752
1103
|
${readonly ? "readonly" : ""}
|
|
753
1104
|
spellcheck="false"
|
|
@@ -761,7 +1112,7 @@ var init_element = __esm(() => {
|
|
|
761
1112
|
class="preview-body markdown-body"
|
|
762
1113
|
id="preview-panel"
|
|
763
1114
|
role="tabpanel"
|
|
764
|
-
aria-
|
|
1115
|
+
aria-labelledby="tab-preview"
|
|
765
1116
|
hidden
|
|
766
1117
|
></div>
|
|
767
1118
|
|
|
@@ -784,401 +1135,494 @@ var init_element = __esm(() => {
|
|
|
784
1135
|
</div>
|
|
785
1136
|
<ul id="ac-dropdown" class="ac-dropdown" role="listbox" aria-label="Suggestions" hidden></ul>
|
|
786
1137
|
`;
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1138
|
+
}
|
|
1139
|
+
#cacheDOMRefs() {
|
|
1140
|
+
this.#textarea = this.shadowRoot.querySelector("textarea");
|
|
1141
|
+
this.#renderLayer = this.shadowRoot.querySelector(".render-layer");
|
|
1142
|
+
this.#writeArea = this.shadowRoot.querySelector(".write-area");
|
|
1143
|
+
this.#previewBody = this.shadowRoot.querySelector(".preview-body");
|
|
1144
|
+
this.#statusCount = this.shadowRoot.querySelector(".status-bar-count");
|
|
1145
|
+
this.#acDropdown = this.shadowRoot.querySelector(".ac-dropdown");
|
|
1146
|
+
this.#uploadList = this.shadowRoot.querySelector(".upload-list");
|
|
1147
|
+
this.#fileInput = this.shadowRoot.querySelector(".file-input");
|
|
1148
|
+
}
|
|
1149
|
+
#attachEventHandlers() {
|
|
1150
|
+
const ta = this.#textarea;
|
|
1151
|
+
if (!ta)
|
|
1152
|
+
return;
|
|
1153
|
+
ta.addEventListener("input", () => {
|
|
1154
|
+
this.#syncFormValue();
|
|
1155
|
+
this.emit("change", { value: ta.value });
|
|
1156
|
+
this.#scheduleHighlight();
|
|
1157
|
+
this.#scheduleStatusUpdate();
|
|
1158
|
+
this.#handleAutocompleteInput();
|
|
1159
|
+
this.#scheduleLivePreview();
|
|
1160
|
+
});
|
|
1161
|
+
ta.addEventListener("blur", () => {
|
|
1162
|
+
setTimeout(() => {
|
|
1163
|
+
if (!this.shadowRoot?.activeElement) {
|
|
1164
|
+
this.#closeDropdown();
|
|
1165
|
+
}
|
|
1166
|
+
}, 150);
|
|
1167
|
+
});
|
|
1168
|
+
ta.addEventListener("keydown", (e) => {
|
|
1169
|
+
if (this.#acSuggestions.length > 0 && !this.#acDropdown?.hidden) {
|
|
1170
|
+
this.#handleDropdownKeydown(e);
|
|
1171
|
+
if (e.defaultPrevented)
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "P") {
|
|
1175
|
+
e.preventDefault();
|
|
1176
|
+
this.#switchTab(this.#activeTab === "write" ? "preview" : "write");
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
if (this.disabled || this.readonly) {
|
|
802
1180
|
return;
|
|
803
|
-
|
|
1181
|
+
}
|
|
1182
|
+
if (!e.ctrlKey && !e.metaKey && !e.altKey && handlePairKey(ta, e.key)) {
|
|
1183
|
+
e.preventDefault();
|
|
804
1184
|
this.#syncFormValue();
|
|
805
1185
|
this.emit("change", { value: ta.value });
|
|
806
1186
|
this.#scheduleHighlight();
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
this
|
|
813
|
-
this.#
|
|
814
|
-
|
|
815
|
-
});
|
|
816
|
-
ta.addEventListener("blur", () => {
|
|
817
|
-
setTimeout(() => {
|
|
818
|
-
if (!this.shadowRoot?.activeElement) {
|
|
819
|
-
this.#closeDropdown();
|
|
820
|
-
}
|
|
821
|
-
}, 150);
|
|
822
|
-
});
|
|
823
|
-
ta.addEventListener("keydown", (e) => {
|
|
824
|
-
if (this.#acSuggestions.length > 0 && !this.#acDropdown?.hidden) {
|
|
825
|
-
this.#handleDropdownKeydown(e);
|
|
826
|
-
}
|
|
827
|
-
if (e.ctrlKey && e.shiftKey && e.key === "P") {
|
|
828
|
-
e.preventDefault();
|
|
829
|
-
this.#switchTab(this.#activeTab === "write" ? "preview" : "write");
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (e.key === "Enter" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1190
|
+
if (handleEnterKey(ta, e)) {
|
|
1191
|
+
this.#syncFormValue();
|
|
1192
|
+
this.emit("change", { value: ta.value });
|
|
1193
|
+
this.#scheduleHighlight();
|
|
1194
|
+
this.#scheduleStatusUpdate();
|
|
830
1195
|
}
|
|
831
|
-
});
|
|
832
|
-
const writeArea = this.#writeArea;
|
|
833
|
-
if (writeArea) {
|
|
834
|
-
writeArea.addEventListener("dragover", (e) => {
|
|
835
|
-
e.preventDefault();
|
|
836
|
-
writeArea.style.opacity = "0.8";
|
|
837
|
-
});
|
|
838
|
-
writeArea.addEventListener("dragleave", () => {
|
|
839
|
-
writeArea.style.opacity = "";
|
|
840
|
-
});
|
|
841
|
-
writeArea.addEventListener("drop", (e) => {
|
|
842
|
-
e.preventDefault();
|
|
843
|
-
writeArea.style.opacity = "";
|
|
844
|
-
if (this.readonly)
|
|
845
|
-
return;
|
|
846
|
-
const files = Array.from(e.dataTransfer?.files ?? []).filter(isAcceptedType);
|
|
847
|
-
files.forEach((f) => this.#startUpload(f));
|
|
848
|
-
});
|
|
849
1196
|
}
|
|
850
|
-
|
|
1197
|
+
});
|
|
1198
|
+
const writeArea = this.#writeArea;
|
|
1199
|
+
if (writeArea) {
|
|
1200
|
+
writeArea.addEventListener("dragover", (e) => {
|
|
1201
|
+
if (this.disabled)
|
|
1202
|
+
return;
|
|
851
1203
|
if (this.readonly)
|
|
852
1204
|
return;
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
e.preventDefault();
|
|
856
|
-
imageFiles.forEach((f) => this.#startUpload(f));
|
|
857
|
-
}
|
|
1205
|
+
e.preventDefault();
|
|
1206
|
+
writeArea.style.opacity = "0.8";
|
|
858
1207
|
});
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const btn = e.target.closest(".tab-btn");
|
|
862
|
-
const tab = btn?.dataset.tab;
|
|
863
|
-
if (tab)
|
|
864
|
-
this.#switchTab(tab);
|
|
1208
|
+
writeArea.addEventListener("dragleave", () => {
|
|
1209
|
+
writeArea.style.opacity = "";
|
|
865
1210
|
});
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1211
|
+
writeArea.addEventListener("drop", (e) => {
|
|
1212
|
+
e.preventDefault();
|
|
1213
|
+
writeArea.style.opacity = "";
|
|
1214
|
+
if (this.disabled)
|
|
1215
|
+
return;
|
|
1216
|
+
if (this.readonly)
|
|
1217
|
+
return;
|
|
1218
|
+
const files = Array.from(e.dataTransfer?.files ?? []).filter(isAcceptedType);
|
|
870
1219
|
files.forEach((f) => this.#startUpload(f));
|
|
871
|
-
if (this.#fileInput)
|
|
872
|
-
this.#fileInput.value = "";
|
|
873
1220
|
});
|
|
874
|
-
this.#acDropdown?.addEventListener("click", (e) => {
|
|
875
|
-
const item = e.target.closest("[data-ac-index]");
|
|
876
|
-
if (item) {
|
|
877
|
-
const idx = parseInt(item.dataset.acIndex ?? "-1", 10);
|
|
878
|
-
if (idx >= 0) {
|
|
879
|
-
this.#acSelectedIndex = idx;
|
|
880
|
-
this.#confirmAutocomplete();
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
if (typeof ResizeObserver !== "undefined") {
|
|
885
|
-
this.#resizeObserver = new ResizeObserver(() => {
|
|
886
|
-
if (this.#backdrop && this.#textarea) {
|
|
887
|
-
this.#backdrop.style.height = `${this.#textarea.offsetHeight}px`;
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
this.#resizeObserver.observe(ta);
|
|
891
|
-
}
|
|
892
1221
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
applyPrismTheme(this.shadowRoot, dark);
|
|
896
|
-
ensurePrism().then(() => {
|
|
897
|
-
if (this.#textarea && this.#backdropContent) {
|
|
898
|
-
this.#backdropContent.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
899
|
-
}
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
#scheduleHighlight() {
|
|
903
|
-
if (this.#highlightTimer !== null)
|
|
904
|
-
clearTimeout(this.#highlightTimer);
|
|
905
|
-
this.#highlightTimer = setTimeout(() => {
|
|
906
|
-
this.#highlightTimer = null;
|
|
907
|
-
if (this.#backdropContent && this.#textarea) {
|
|
908
|
-
this.#backdropContent.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
909
|
-
}
|
|
910
|
-
if (this.#backdrop && this.#textarea) {
|
|
911
|
-
this.#backdrop.scrollTop = this.#textarea.scrollTop;
|
|
912
|
-
}
|
|
913
|
-
}, 60);
|
|
914
|
-
}
|
|
915
|
-
#switchTab(tab) {
|
|
916
|
-
if (tab === this.#activeTab)
|
|
1222
|
+
ta.addEventListener("paste", (e) => {
|
|
1223
|
+
if (this.disabled)
|
|
917
1224
|
return;
|
|
918
|
-
this
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1225
|
+
if (this.readonly)
|
|
1226
|
+
return;
|
|
1227
|
+
const imageFiles = Array.from(e.clipboardData?.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
1228
|
+
if (imageFiles.length > 0) {
|
|
1229
|
+
e.preventDefault();
|
|
1230
|
+
imageFiles.forEach((f) => this.#startUpload(f));
|
|
1231
|
+
}
|
|
1232
|
+
});
|
|
1233
|
+
const toolbar = this.shadowRoot.querySelector(".toolbar");
|
|
1234
|
+
toolbar?.addEventListener("click", (e) => {
|
|
1235
|
+
const btn = e.target.closest(".tab-btn");
|
|
1236
|
+
const tab = btn?.dataset.tab;
|
|
1237
|
+
if (tab)
|
|
1238
|
+
this.#switchTab(tab);
|
|
1239
|
+
});
|
|
1240
|
+
toolbar?.addEventListener("keydown", (e) => {
|
|
1241
|
+
const kev = e;
|
|
1242
|
+
if (kev.key === "ArrowLeft" || kev.key === "ArrowRight") {
|
|
1243
|
+
kev.preventDefault();
|
|
1244
|
+
const nextTab = this.#activeTab === "write" ? "preview" : "write";
|
|
1245
|
+
this.#switchTab(nextTab);
|
|
1246
|
+
const nextBtn = this.shadowRoot.querySelector(`.tab-btn[data-tab="${nextTab}"]`);
|
|
1247
|
+
nextBtn?.focus();
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
const attachBtn = this.shadowRoot.querySelector(".attach-btn");
|
|
1251
|
+
attachBtn?.addEventListener("click", () => this.#fileInput?.click());
|
|
1252
|
+
this.#fileInput?.addEventListener("change", () => {
|
|
1253
|
+
const files = Array.from(this.#fileInput?.files ?? []).filter(isAcceptedType);
|
|
1254
|
+
files.forEach((f) => this.#startUpload(f));
|
|
1255
|
+
if (this.#fileInput)
|
|
1256
|
+
this.#fileInput.value = "";
|
|
1257
|
+
});
|
|
1258
|
+
this.#acDropdown?.addEventListener("click", (e) => {
|
|
1259
|
+
const item = e.target.closest("[data-ac-index]");
|
|
1260
|
+
if (item) {
|
|
1261
|
+
const idx = parseInt(item.dataset.acIndex ?? "-1", 10);
|
|
1262
|
+
if (idx >= 0) {
|
|
1263
|
+
this.#acSelectedIndex = idx;
|
|
1264
|
+
this.#confirmAutocomplete();
|
|
929
1265
|
}
|
|
930
|
-
} else {
|
|
931
|
-
this.#writeArea?.removeAttribute("hidden");
|
|
932
|
-
this.#previewBody?.setAttribute("hidden", "");
|
|
933
1266
|
}
|
|
1267
|
+
});
|
|
1268
|
+
}
|
|
1269
|
+
#initHighlight() {
|
|
1270
|
+
const dark = !!this.dark;
|
|
1271
|
+
applyPrismTheme(this.shadowRoot, dark);
|
|
1272
|
+
ensurePrism().then(() => {
|
|
1273
|
+
if (this.#textarea && this.#renderLayer) {
|
|
1274
|
+
this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
#scheduleHighlight() {
|
|
1279
|
+
if (this.#highlightTimer !== null)
|
|
1280
|
+
clearTimeout(this.#highlightTimer);
|
|
1281
|
+
this.#highlightTimer = setTimeout(() => {
|
|
1282
|
+
this.#highlightTimer = null;
|
|
1283
|
+
if (this.#renderLayer && this.#textarea) {
|
|
1284
|
+
this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
1285
|
+
}
|
|
1286
|
+
}, 60);
|
|
1287
|
+
}
|
|
1288
|
+
#switchTab(tab) {
|
|
1289
|
+
if (tab === this.#activeTab)
|
|
1290
|
+
return;
|
|
1291
|
+
this.#activeTab = tab;
|
|
1292
|
+
const writeBtns = this.shadowRoot.querySelectorAll(".tab-btn");
|
|
1293
|
+
writeBtns.forEach((btn) => {
|
|
1294
|
+
const isActive = btn.dataset.tab === tab;
|
|
1295
|
+
btn.setAttribute("aria-selected", String(isActive));
|
|
1296
|
+
btn.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
1297
|
+
});
|
|
1298
|
+
if (tab === "preview") {
|
|
1299
|
+
this.#writeArea?.setAttribute("hidden", "");
|
|
1300
|
+
if (this.#previewBody) {
|
|
1301
|
+
this.#previewBody.removeAttribute("hidden");
|
|
1302
|
+
this.#renderPreview(this.#textarea?.value ?? "");
|
|
1303
|
+
}
|
|
1304
|
+
} else {
|
|
1305
|
+
this.#writeArea?.removeAttribute("hidden");
|
|
1306
|
+
this.#previewBody?.setAttribute("hidden", "");
|
|
934
1307
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
});
|
|
952
|
-
html = html.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
953
|
-
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, url) => `<img src="${sanitizeUrl(url)}" alt="${alt}">`);
|
|
954
|
-
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, text, url) => `<a href="${sanitizeUrl(url)}">${text}</a>`);
|
|
955
|
-
html = html.replace(/^###### (.+)$/gm, "<h6>$1</h6>").replace(/^##### (.+)$/gm, "<h5>$1</h5>").replace(/^#### (.+)$/gm, "<h4>$1</h4>").replace(/^### (.+)$/gm, "<h3>$1</h3>").replace(/^## (.+)$/gm, "<h2>$1</h2>").replace(/^# (.+)$/gm, "<h1>$1</h1>");
|
|
956
|
-
html = html.replace(/^[-*_]{3,}$/gm, "<hr>");
|
|
957
|
-
html = html.replace(/^> (.+)$/gm, "<blockquote>$1</blockquote>");
|
|
958
|
-
html = html.replace(/\*\*\*(.+?)\*\*\*/g, "<strong><em>$1</em></strong>").replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/\*(.+?)\*/g, "<em>$1</em>").replace(/___(.+?)___/g, "<strong><em>$1</em></strong>").replace(/__(.+?)__/g, "<strong>$1</strong>").replace(/_(.+?)_/g, "<em>$1</em>");
|
|
959
|
-
html = html.replace(/~~(.+?)~~/g, "<del>$1</del>");
|
|
960
|
-
html = html.replace(/^[ \t]*[-*+] (.+)$/gm, '<li data-list="ul">$1</li>');
|
|
961
|
-
html = html.replace(/^[ \t]*\d+\. (.+)$/gm, '<li data-list="ol">$1</li>');
|
|
962
|
-
html = html.replace(/(<li data-list="ul">[^\n]*<\/li>(?:\n<li data-list="ul">[^\n]*<\/li>)*)/g, (match) => "<ul>" + match.replace(/ data-list="ul"/g, "") + "</ul>");
|
|
963
|
-
html = html.replace(/(<li data-list="ol">[^\n]*<\/li>(?:\n<li data-list="ol">[^\n]*<\/li>)*)/g, (match) => "<ol>" + match.replace(/ data-list="ol"/g, "") + "</ol>");
|
|
964
|
-
const lines = html.split(`
|
|
965
|
-
|
|
966
|
-
`);
|
|
967
|
-
html = lines.map((block) => {
|
|
968
|
-
const t = block.trim();
|
|
969
|
-
if (!t)
|
|
970
|
-
return "";
|
|
971
|
-
if (/^<(h[1-6]|ul|ol|li|blockquote|hr|pre|img)/.test(t) || t.startsWith(CB_PH))
|
|
972
|
-
return t;
|
|
973
|
-
return `<p>${t.replace(/\n/g, "<br>")}</p>`;
|
|
974
|
-
}).filter(Boolean).join(`
|
|
975
|
-
`);
|
|
976
|
-
html = html.replace(new RegExp(`${CB_PH}(\\d+)${CB_PH}`, "g"), (_, i) => codeBlocks[parseInt(i, 10)] ?? "");
|
|
977
|
-
html = html.replace(new RegExp(`${IC_PH}(\\d+)${IC_PH}`, "g"), (_, i) => inlineCodes[parseInt(i, 10)] ?? "");
|
|
978
|
-
return html;
|
|
1308
|
+
}
|
|
1309
|
+
async#loadRenderPipeline() {
|
|
1310
|
+
if (this.#renderFn && this.#mermaidFn) {
|
|
1311
|
+
return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };
|
|
1312
|
+
}
|
|
1313
|
+
const mod = await Promise.resolve().then(() => (init_render(), exports_render));
|
|
1314
|
+
this.#renderFn = mod.renderMarkdown;
|
|
1315
|
+
this.#mermaidFn = mod.renderMermaidBlocks;
|
|
1316
|
+
return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };
|
|
1317
|
+
}
|
|
1318
|
+
async#renderPreview(source, force = false) {
|
|
1319
|
+
const preview = this.#previewBody;
|
|
1320
|
+
if (!preview)
|
|
1321
|
+
return;
|
|
1322
|
+
if (!force && this.#lastRenderedSource === source && this.#renderFn !== null) {
|
|
1323
|
+
return;
|
|
979
1324
|
}
|
|
980
|
-
|
|
981
|
-
|
|
1325
|
+
this.#renderAbortController?.abort();
|
|
1326
|
+
const controller = new AbortController;
|
|
1327
|
+
this.#renderAbortController = controller;
|
|
1328
|
+
this.emit("render-start", {});
|
|
1329
|
+
preview.setAttribute("aria-busy", "true");
|
|
1330
|
+
if (!this.#renderFn) {
|
|
1331
|
+
preview.innerHTML = `
|
|
1332
|
+
<div class="preview-skeleton" aria-label="Loading preview…">
|
|
1333
|
+
<div class="skeleton-line" style="width:90%"></div>
|
|
1334
|
+
<div class="skeleton-line" style="width:75%"></div>
|
|
1335
|
+
<div class="skeleton-line" style="width:85%"></div>
|
|
1336
|
+
<div class="skeleton-line" style="width:60%"></div>
|
|
1337
|
+
</div>`;
|
|
982
1338
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
if (!uploadUrl) {
|
|
988
|
-
this.emit("upload-error", { file, error: "no upload-url set" });
|
|
989
|
-
this.#showUploadError(file, "no upload-url set");
|
|
1339
|
+
try {
|
|
1340
|
+
const { renderMarkdown: renderMarkdown2, renderMermaidBlocks: renderMermaidBlocks2 } = await this.#loadRenderPipeline();
|
|
1341
|
+
if (controller.signal.aborted) {
|
|
1342
|
+
preview.removeAttribute("aria-busy");
|
|
990
1343
|
return;
|
|
991
1344
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
this.#showUploadError(file, errorMsg);
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
#addProgressRow(id, filename) {
|
|
1008
|
-
if (!this.#uploadList)
|
|
1345
|
+
const html = await renderMarkdown2(source);
|
|
1346
|
+
if (controller.signal.aborted) {
|
|
1347
|
+
preview.removeAttribute("aria-busy");
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
preview.innerHTML = html;
|
|
1351
|
+
preview.removeAttribute("aria-busy");
|
|
1352
|
+
this.#ensureKatexCss();
|
|
1353
|
+
const mermaidSrc = this.mermaidSrc;
|
|
1354
|
+
await renderMermaidBlocks2(preview, mermaidSrc);
|
|
1355
|
+
if (controller.signal.aborted) {
|
|
1356
|
+
preview.removeAttribute("aria-busy");
|
|
1009
1357
|
return;
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1358
|
+
}
|
|
1359
|
+
this.#lastRenderedSource = source;
|
|
1360
|
+
this.emit("render-done", { html });
|
|
1361
|
+
} catch (err) {
|
|
1362
|
+
if (controller.signal.aborted) {
|
|
1363
|
+
preview.removeAttribute("aria-busy");
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
preview.removeAttribute("aria-busy");
|
|
1367
|
+
preview.innerHTML = `<pre class="render-error-fallback">${escapeHtmlStr(source)}</pre>`;
|
|
1368
|
+
this.emit("render-error", { error: err instanceof Error ? err : new Error(String(err)) });
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
#ensureKatexCss() {
|
|
1372
|
+
if (this.#katexCssInjected)
|
|
1373
|
+
return;
|
|
1374
|
+
this.#katexCssInjected = true;
|
|
1375
|
+
const rawUrl = this.katexCssUrl ?? "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
|
|
1376
|
+
const katexUrl = /^https:\/\//i.test(rawUrl) ? rawUrl : "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
|
|
1377
|
+
if (katexUrl !== rawUrl) {
|
|
1378
|
+
console.warn(`[el-dm-markdown-input] katex-css-url "${rawUrl}" rejected — only https: URLs are allowed.`);
|
|
1379
|
+
}
|
|
1380
|
+
const link = document.createElement("link");
|
|
1381
|
+
link.id = "katex-css";
|
|
1382
|
+
link.rel = "stylesheet";
|
|
1383
|
+
link.href = katexUrl;
|
|
1384
|
+
this.shadowRoot.appendChild(link);
|
|
1385
|
+
}
|
|
1386
|
+
#scheduleLivePreview() {
|
|
1387
|
+
if (!this.livePreview)
|
|
1388
|
+
return;
|
|
1389
|
+
if (this.#activeTab !== "preview")
|
|
1390
|
+
return;
|
|
1391
|
+
if (this.#livePreviewTimer !== null)
|
|
1392
|
+
clearTimeout(this.#livePreviewTimer);
|
|
1393
|
+
const ms = this.debounce ?? 300;
|
|
1394
|
+
this.#livePreviewTimer = setTimeout(() => {
|
|
1395
|
+
this.#livePreviewTimer = null;
|
|
1396
|
+
this.#renderPreview(this.#textarea?.value ?? "");
|
|
1397
|
+
}, ms);
|
|
1398
|
+
}
|
|
1399
|
+
#syncFormValue() {
|
|
1400
|
+
this.#internals?.setFormValue(this.#textarea?.value ?? "");
|
|
1401
|
+
}
|
|
1402
|
+
#startUpload(file) {
|
|
1403
|
+
this.emit("upload-start", { file });
|
|
1404
|
+
const id = `upload-${++this.#uploadIdCounter}`;
|
|
1405
|
+
const uploadUrl = this.uploadUrl;
|
|
1406
|
+
if (!uploadUrl) {
|
|
1407
|
+
this.emit("upload-error", { file, error: "no upload-url set" });
|
|
1408
|
+
this.#showUploadError(file, "no upload-url set");
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
this.#addProgressRow(id, file.name);
|
|
1412
|
+
uploadFile(file, uploadUrl, (pct) => {
|
|
1413
|
+
this.#updateProgressRow(id, pct);
|
|
1414
|
+
}).then((url) => {
|
|
1415
|
+
this.#removeUploadRow(id);
|
|
1416
|
+
const markdown = fileToMarkdown(file, url);
|
|
1417
|
+
this.insertText(markdown);
|
|
1418
|
+
this.emit("upload-done", { file, url, markdown });
|
|
1419
|
+
}).catch((err) => {
|
|
1420
|
+
this.#removeUploadRow(id);
|
|
1421
|
+
const errorMsg = err instanceof Error ? err.message : "Upload failed";
|
|
1422
|
+
this.emit("upload-error", { file, error: errorMsg });
|
|
1423
|
+
this.#showUploadError(file, errorMsg);
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
#addProgressRow(id, filename) {
|
|
1427
|
+
if (!this.#uploadList)
|
|
1428
|
+
return;
|
|
1429
|
+
const row = document.createElement("div");
|
|
1430
|
+
row.className = "upload-row";
|
|
1431
|
+
row.id = id;
|
|
1432
|
+
row.innerHTML = `
|
|
1014
1433
|
<span class="upload-filename">${escapeHtmlStr(filename)}</span>
|
|
1015
|
-
<div class="upload-bar-track">
|
|
1434
|
+
<div class="upload-bar-track" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="Uploading ${escapeHtmlStr(filename)}">
|
|
1016
1435
|
<div class="upload-bar" style="width: 0%"></div>
|
|
1017
1436
|
</div>
|
|
1018
1437
|
`;
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1438
|
+
this.#uploadList.appendChild(row);
|
|
1439
|
+
}
|
|
1440
|
+
#updateProgressRow(id, pct) {
|
|
1441
|
+
const track = this.#uploadList?.querySelector(`#${id} .upload-bar-track`);
|
|
1442
|
+
if (track)
|
|
1443
|
+
track.setAttribute("aria-valuenow", String(pct));
|
|
1444
|
+
const bar = this.#uploadList?.querySelector(`#${id} .upload-bar`);
|
|
1445
|
+
if (bar)
|
|
1446
|
+
bar.style.width = `${pct}%`;
|
|
1447
|
+
}
|
|
1448
|
+
#removeUploadRow(id) {
|
|
1449
|
+
this.#uploadList?.querySelector(`#${id}`)?.remove();
|
|
1450
|
+
}
|
|
1451
|
+
#showUploadError(file, message) {
|
|
1452
|
+
if (!this.#uploadList)
|
|
1453
|
+
return;
|
|
1454
|
+
const row = document.createElement("div");
|
|
1455
|
+
row.className = "upload-error-row";
|
|
1456
|
+
row.setAttribute("role", "alert");
|
|
1457
|
+
row.innerHTML = `
|
|
1035
1458
|
<span class="upload-error-msg">${escapeHtmlStr(file.name)}: ${escapeHtmlStr(message)}</span>
|
|
1036
1459
|
`;
|
|
1037
|
-
|
|
1038
|
-
|
|
1460
|
+
this.#uploadList.appendChild(row);
|
|
1461
|
+
setTimeout(() => row.remove(), 4000);
|
|
1462
|
+
}
|
|
1463
|
+
#handleAutocompleteInput() {
|
|
1464
|
+
const ta = this.#textarea;
|
|
1465
|
+
if (!ta)
|
|
1466
|
+
return;
|
|
1467
|
+
const result = detectTrigger(ta.value, ta.selectionStart ?? 0);
|
|
1468
|
+
if (!result) {
|
|
1469
|
+
this.#closeDropdown();
|
|
1470
|
+
return;
|
|
1039
1471
|
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
if (
|
|
1046
|
-
this
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
this
|
|
1052
|
-
const resolve = (list) => this.setSuggestions(list);
|
|
1053
|
-
if (trigger === "@") {
|
|
1054
|
-
this.emit("mention-query", { trigger, query, resolve });
|
|
1055
|
-
} else {
|
|
1056
|
-
this.emit("reference-query", { trigger, query, resolve });
|
|
1057
|
-
}
|
|
1472
|
+
const { trigger, query, triggerPos } = result;
|
|
1473
|
+
this.#acTrigger = trigger;
|
|
1474
|
+
this.#acTriggerPos = triggerPos;
|
|
1475
|
+
const gen = ++this.#acGeneration;
|
|
1476
|
+
const resolve = (list) => {
|
|
1477
|
+
if (gen === this.#acGeneration)
|
|
1478
|
+
this.setSuggestions(list);
|
|
1479
|
+
};
|
|
1480
|
+
if (trigger === "@") {
|
|
1481
|
+
this.emit("mention-query", { trigger, query, resolve });
|
|
1482
|
+
} else {
|
|
1483
|
+
this.emit("reference-query", { trigger, query, resolve });
|
|
1058
1484
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1485
|
+
}
|
|
1486
|
+
#handleDropdownKeydown(e) {
|
|
1487
|
+
const len = this.#acSuggestions.length;
|
|
1488
|
+
if (len === 0)
|
|
1489
|
+
return;
|
|
1490
|
+
switch (e.key) {
|
|
1491
|
+
case "ArrowDown":
|
|
1492
|
+
e.preventDefault();
|
|
1493
|
+
this.#acSelectedIndex = (this.#acSelectedIndex + 1) % len;
|
|
1494
|
+
this.#updateDropdown();
|
|
1495
|
+
break;
|
|
1496
|
+
case "ArrowUp":
|
|
1497
|
+
e.preventDefault();
|
|
1498
|
+
this.#acSelectedIndex = (this.#acSelectedIndex - 1 + len) % len;
|
|
1499
|
+
this.#updateDropdown();
|
|
1500
|
+
break;
|
|
1501
|
+
case "Enter":
|
|
1502
|
+
case "Tab":
|
|
1503
|
+
if (this.#acSelectedIndex >= 0) {
|
|
1070
1504
|
e.preventDefault();
|
|
1071
|
-
this.#
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
e.preventDefault();
|
|
1078
|
-
this.#confirmAutocomplete();
|
|
1079
|
-
}
|
|
1080
|
-
break;
|
|
1081
|
-
case "Escape":
|
|
1082
|
-
this.#closeDropdown();
|
|
1083
|
-
break;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
#confirmAutocomplete() {
|
|
1087
|
-
const ta = this.#textarea;
|
|
1088
|
-
if (!ta || this.#acSelectedIndex < 0 || !this.#acTrigger)
|
|
1089
|
-
return;
|
|
1090
|
-
const suggestion = this.#acSuggestions[this.#acSelectedIndex];
|
|
1091
|
-
if (!suggestion)
|
|
1092
|
-
return;
|
|
1093
|
-
const { newValue, newCursorPos } = confirmSuggestion(ta.value, this.#acTriggerPos, ta.selectionStart ?? ta.value.length, this.#acTrigger, suggestion.id);
|
|
1094
|
-
ta.value = newValue;
|
|
1095
|
-
ta.setSelectionRange(newCursorPos, newCursorPos);
|
|
1096
|
-
this.#syncFormValue();
|
|
1097
|
-
this.emit("change", { value: ta.value });
|
|
1098
|
-
this.#scheduleHighlight();
|
|
1099
|
-
this.#scheduleStatusUpdate();
|
|
1100
|
-
this.#closeDropdown();
|
|
1101
|
-
}
|
|
1102
|
-
#closeDropdown() {
|
|
1103
|
-
this.#acSuggestions = [];
|
|
1104
|
-
this.#acSelectedIndex = -1;
|
|
1105
|
-
this.#acTrigger = null;
|
|
1106
|
-
this.#acTriggerPos = -1;
|
|
1107
|
-
if (this.#acDropdown) {
|
|
1108
|
-
this.#acDropdown.innerHTML = "";
|
|
1109
|
-
this.#acDropdown.hidden = true;
|
|
1110
|
-
}
|
|
1505
|
+
this.#confirmAutocomplete();
|
|
1506
|
+
}
|
|
1507
|
+
break;
|
|
1508
|
+
case "Escape":
|
|
1509
|
+
this.#closeDropdown();
|
|
1510
|
+
break;
|
|
1111
1511
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1512
|
+
}
|
|
1513
|
+
#confirmAutocomplete() {
|
|
1514
|
+
const ta = this.#textarea;
|
|
1515
|
+
if (!ta || this.#acSelectedIndex < 0 || !this.#acTrigger)
|
|
1516
|
+
return;
|
|
1517
|
+
const suggestion = this.#acSuggestions[this.#acSelectedIndex];
|
|
1518
|
+
if (!suggestion)
|
|
1519
|
+
return;
|
|
1520
|
+
const { newValue, newCursorPos } = confirmSuggestion(ta.value, this.#acTriggerPos, ta.selectionStart ?? ta.value.length, this.#acTrigger, suggestion.id);
|
|
1521
|
+
ta.value = newValue;
|
|
1522
|
+
ta.setSelectionRange(newCursorPos, newCursorPos);
|
|
1523
|
+
this.#syncFormValue();
|
|
1524
|
+
this.emit("change", { value: ta.value });
|
|
1525
|
+
this.#scheduleHighlight();
|
|
1526
|
+
this.#scheduleStatusUpdate();
|
|
1527
|
+
this.#closeDropdown();
|
|
1528
|
+
}
|
|
1529
|
+
#closeDropdown() {
|
|
1530
|
+
this.#acGeneration++;
|
|
1531
|
+
this.#acSuggestions = [];
|
|
1532
|
+
this.#acSelectedIndex = -1;
|
|
1533
|
+
this.#acTrigger = null;
|
|
1534
|
+
this.#acTriggerPos = -1;
|
|
1535
|
+
if (this.#acDropdown) {
|
|
1536
|
+
this.#acDropdown.innerHTML = "";
|
|
1537
|
+
this.#acDropdown.hidden = true;
|
|
1127
1538
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1539
|
+
this.#textarea?.setAttribute("aria-expanded", "false");
|
|
1540
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1541
|
+
}
|
|
1542
|
+
#updateDropdown() {
|
|
1543
|
+
if (!this.#acDropdown)
|
|
1544
|
+
return;
|
|
1545
|
+
if (this.#acSuggestions.length === 0) {
|
|
1546
|
+
this.#acDropdown.hidden = true;
|
|
1547
|
+
this.#textarea?.setAttribute("aria-expanded", "false");
|
|
1548
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1549
|
+
return;
|
|
1135
1550
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);
|
|
1551
|
+
this.#acDropdown.innerHTML = renderDropdown(this.#acSuggestions, this.#acSelectedIndex);
|
|
1552
|
+
this.#acDropdown.hidden = false;
|
|
1553
|
+
this.#textarea?.setAttribute("aria-expanded", "true");
|
|
1554
|
+
if (this.#acSelectedIndex >= 0) {
|
|
1555
|
+
this.#textarea?.setAttribute("aria-activedescendant", `ac-item-${this.#acSelectedIndex}`);
|
|
1556
|
+
} else {
|
|
1557
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1144
1558
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1559
|
+
}
|
|
1560
|
+
#scheduleStatusUpdate() {
|
|
1561
|
+
if (this.#statusTimer !== null)
|
|
1562
|
+
clearTimeout(this.#statusTimer);
|
|
1563
|
+
this.#statusTimer = setTimeout(() => {
|
|
1564
|
+
this.#statusTimer = null;
|
|
1565
|
+
this.#updateStatusBarNow();
|
|
1566
|
+
}, 100);
|
|
1567
|
+
}
|
|
1568
|
+
#updateStatusBarNow() {
|
|
1569
|
+
if (!this.#statusCount)
|
|
1570
|
+
return;
|
|
1571
|
+
const text = this.#textarea?.value ?? "";
|
|
1572
|
+
const words = countWords(text);
|
|
1573
|
+
const chars = text.length;
|
|
1574
|
+
const maxWords = this.maxWords ?? null;
|
|
1575
|
+
this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);
|
|
1576
|
+
const isRequired = !!this.required;
|
|
1577
|
+
if (maxWords && words > maxWords) {
|
|
1578
|
+
this.#internals?.setValidity({ customError: true }, `Content exceeds ${maxWords} word limit (${words} words)`, this.#textarea ?? undefined);
|
|
1579
|
+
} else if (isRequired && text.trim() === "") {
|
|
1580
|
+
this.#internals?.setValidity({ valueMissing: true }, "Please fill in this field.", this.#textarea ?? undefined);
|
|
1581
|
+
} else {
|
|
1582
|
+
this.#internals?.setValidity({});
|
|
1147
1583
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1584
|
+
}
|
|
1585
|
+
getValue() {
|
|
1586
|
+
return this.#textarea?.value ?? "";
|
|
1587
|
+
}
|
|
1588
|
+
setValue(str) {
|
|
1589
|
+
if (this.#textarea) {
|
|
1590
|
+
this.#textarea.value = str;
|
|
1591
|
+
this.#syncFormValue();
|
|
1592
|
+
this.#scheduleHighlight();
|
|
1593
|
+
this.#updateStatusBarNow();
|
|
1594
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1595
|
+
this.#renderPreview(str);
|
|
1156
1596
|
}
|
|
1597
|
+
} else {
|
|
1598
|
+
this.value = str;
|
|
1157
1599
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1600
|
+
}
|
|
1601
|
+
insertText(str) {
|
|
1602
|
+
const ta = this.#textarea;
|
|
1603
|
+
if (!ta)
|
|
1604
|
+
return;
|
|
1605
|
+
const start = ta.selectionStart ?? ta.value.length;
|
|
1606
|
+
const end = ta.selectionEnd ?? ta.value.length;
|
|
1607
|
+
ta.value = ta.value.slice(0, start) + str + ta.value.slice(end);
|
|
1608
|
+
const newPos = start + str.length;
|
|
1609
|
+
ta.setSelectionRange(newPos, newPos);
|
|
1610
|
+
ta.dispatchEvent(new Event("input", { bubbles: false }));
|
|
1611
|
+
}
|
|
1612
|
+
setSuggestions(list) {
|
|
1613
|
+
this.#acSuggestions = list;
|
|
1614
|
+
this.#acSelectedIndex = list.length > 0 ? 0 : -1;
|
|
1615
|
+
this.#updateDropdown();
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
function escapeHtmlStr(s) {
|
|
1619
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1620
|
+
}
|
|
1176
1621
|
|
|
1177
1622
|
// src/register.ts
|
|
1178
|
-
init_element();
|
|
1179
1623
|
if (!customElements.get("el-dm-markdown-input")) {
|
|
1180
1624
|
customElements.define("el-dm-markdown-input", ElDmMarkdownInput);
|
|
1181
1625
|
}
|
|
1182
1626
|
|
|
1183
|
-
//# debugId=
|
|
1627
|
+
//# debugId=CBBAC14DC8E0779464756E2164756E21
|
|
1184
1628
|
//# sourceMappingURL=register.js.map
|