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