@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/index.js
CHANGED
|
@@ -37,11 +37,181 @@ 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/index.ts
|
|
200
|
+
var exports_src = {};
|
|
201
|
+
__export(exports_src, {
|
|
202
|
+
register: () => register,
|
|
203
|
+
MarkdownInputHook: () => MarkdownInputHook,
|
|
204
|
+
ElDmMarkdownInput: () => ElDmMarkdownInput
|
|
205
|
+
});
|
|
206
|
+
module.exports = __toCommonJS(exports_src);
|
|
207
|
+
|
|
208
|
+
// src/element.ts
|
|
209
|
+
var import_el_base2 = require("@duskmoon-dev/el-base");
|
|
210
|
+
var import_markdown_body = require("@duskmoon-dev/core/components/markdown-body");
|
|
211
|
+
|
|
40
212
|
// 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`
|
|
213
|
+
var import_el_base = require("@duskmoon-dev/el-base");
|
|
214
|
+
var elementStyles = import_el_base.css`
|
|
45
215
|
/* ── Custom property defaults with design-system fallbacks ─────────── */
|
|
46
216
|
:host {
|
|
47
217
|
--md-border: var(--color-outline, #d0d7de);
|
|
@@ -90,6 +260,7 @@ var init_css = __esm(() => {
|
|
|
90
260
|
background: var(--md-bg);
|
|
91
261
|
color: var(--md-text);
|
|
92
262
|
overflow: hidden;
|
|
263
|
+
height: inherit;
|
|
93
264
|
}
|
|
94
265
|
|
|
95
266
|
.editor:focus-within {
|
|
@@ -139,74 +310,62 @@ var init_css = __esm(() => {
|
|
|
139
310
|
border-radius: 3px;
|
|
140
311
|
}
|
|
141
312
|
|
|
142
|
-
/* ── Write area (
|
|
313
|
+
/* ── Write area (render-layer + textarea overlay) ──────────────────── */
|
|
314
|
+
/*
|
|
315
|
+
* CodeMirror-style render model: .render-layer sits in normal flow and
|
|
316
|
+
* drives the container height; the textarea is absolutely positioned on
|
|
317
|
+
* top. No scroll sync required — both layers always share the same size.
|
|
318
|
+
*/
|
|
143
319
|
.write-area {
|
|
144
320
|
position: relative;
|
|
145
321
|
min-height: 12rem;
|
|
146
|
-
flex: 1;
|
|
322
|
+
flex: 1 1 auto;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.write-area[hidden] {
|
|
326
|
+
display: none;
|
|
147
327
|
}
|
|
148
328
|
|
|
149
329
|
/*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
330
|
+
* Render layer: highlighted HTML in normal flow. Drives container height.
|
|
331
|
+
* pointer-events: none lets clicks pass through to the textarea underneath.
|
|
332
|
+
* Font metrics MUST match the textarea exactly for pixel-aligned overlay.
|
|
152
333
|
*/
|
|
153
|
-
.
|
|
154
|
-
position:
|
|
155
|
-
|
|
334
|
+
.render-layer {
|
|
335
|
+
position: relative;
|
|
336
|
+
z-index: 1;
|
|
156
337
|
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
|
-
|
|
338
|
+
min-height: 12rem;
|
|
177
339
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
178
340
|
font-size: 0.875rem;
|
|
179
341
|
line-height: 1.6;
|
|
180
342
|
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
343
|
white-space: pre-wrap;
|
|
191
344
|
word-wrap: break-word;
|
|
192
345
|
overflow-wrap: break-word;
|
|
193
|
-
|
|
346
|
+
color: var(--md-text);
|
|
194
347
|
}
|
|
195
348
|
|
|
349
|
+
/*
|
|
350
|
+
* Textarea: absolute overlay on top of the render layer. Transparent text
|
|
351
|
+
* lets highlighted content show through; caret-color keeps cursor visible.
|
|
352
|
+
* overflow: hidden — the render layer drives height, not the textarea.
|
|
353
|
+
*/
|
|
196
354
|
textarea {
|
|
197
|
-
position:
|
|
355
|
+
position: absolute;
|
|
356
|
+
inset: 0;
|
|
357
|
+
z-index: 2;
|
|
198
358
|
display: block;
|
|
199
359
|
width: 100%;
|
|
200
|
-
|
|
360
|
+
height: 100%;
|
|
201
361
|
border: none;
|
|
202
362
|
outline: none;
|
|
203
|
-
resize:
|
|
363
|
+
resize: none;
|
|
204
364
|
background: transparent;
|
|
205
365
|
color: transparent;
|
|
206
366
|
caret-color: var(--md-text);
|
|
207
367
|
box-sizing: border-box;
|
|
208
|
-
|
|
209
|
-
/* MUST match .backdrop exactly */
|
|
368
|
+
overflow: hidden;
|
|
210
369
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
211
370
|
font-size: 0.875rem;
|
|
212
371
|
line-height: 1.6;
|
|
@@ -229,11 +388,100 @@ var init_css = __esm(() => {
|
|
|
229
388
|
.preview-body {
|
|
230
389
|
padding: 0.75rem;
|
|
231
390
|
min-height: 12rem;
|
|
391
|
+
height: stretch;
|
|
392
|
+
flex: 1 1 auto;
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-direction: column;
|
|
232
395
|
overflow-y: auto;
|
|
233
396
|
color: var(--md-text);
|
|
234
397
|
/* .markdown-body styles come from @duskmoon-dev/core via the element */
|
|
235
398
|
}
|
|
236
399
|
|
|
400
|
+
.preview-body[hidden] {
|
|
401
|
+
display: none;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ── Preview skeleton (shown while render pipeline loads) ──────────── */
|
|
405
|
+
.preview-skeleton {
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
gap: 0.75rem;
|
|
409
|
+
padding: 0.5rem 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.skeleton-line {
|
|
413
|
+
height: 0.875rem;
|
|
414
|
+
background: linear-gradient(
|
|
415
|
+
90deg,
|
|
416
|
+
var(--md-bg-toolbar) 25%,
|
|
417
|
+
var(--md-bg-hover) 50%,
|
|
418
|
+
var(--md-bg-toolbar) 75%
|
|
419
|
+
);
|
|
420
|
+
background-size: 200% 100%;
|
|
421
|
+
border-radius: 4px;
|
|
422
|
+
animation: skeleton-shimmer 1.5s ease-in-out infinite;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@keyframes skeleton-shimmer {
|
|
426
|
+
0% {
|
|
427
|
+
background-position: 200% 0;
|
|
428
|
+
}
|
|
429
|
+
100% {
|
|
430
|
+
background-position: -200% 0;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
@media (prefers-reduced-motion: reduce) {
|
|
435
|
+
.skeleton-line {
|
|
436
|
+
animation: none;
|
|
437
|
+
background: var(--md-bg-hover);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* ── Mermaid diagram blocks ────────────────────────────────────────── */
|
|
442
|
+
.mermaid-diagram {
|
|
443
|
+
display: flex;
|
|
444
|
+
justify-content: center;
|
|
445
|
+
margin: 1rem 0;
|
|
446
|
+
overflow-x: auto;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.mermaid-error {
|
|
450
|
+
border-left: 3px solid var(--md-color-error);
|
|
451
|
+
opacity: 0.7;
|
|
452
|
+
position: relative;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.mermaid-error::before {
|
|
456
|
+
content: 'Mermaid render failed';
|
|
457
|
+
display: block;
|
|
458
|
+
font-size: 0.75rem;
|
|
459
|
+
color: var(--md-color-error);
|
|
460
|
+
font-family: inherit;
|
|
461
|
+
margin-bottom: 0.25rem;
|
|
462
|
+
padding-left: 0.5rem;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* ── Render error fallback ──────────────────────────────────────────── */
|
|
466
|
+
.render-error-fallback {
|
|
467
|
+
white-space: pre-wrap;
|
|
468
|
+
word-wrap: break-word;
|
|
469
|
+
font-size: 0.875rem;
|
|
470
|
+
opacity: 0.8;
|
|
471
|
+
border-left: 3px solid var(--md-color-error);
|
|
472
|
+
padding-left: 0.75rem;
|
|
473
|
+
color: var(--md-text-muted);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.render-error-fallback::before {
|
|
477
|
+
content: 'Preview render failed — showing raw markdown';
|
|
478
|
+
display: block;
|
|
479
|
+
font-size: 0.75rem;
|
|
480
|
+
color: var(--md-color-error);
|
|
481
|
+
font-family: inherit;
|
|
482
|
+
margin-bottom: 0.5rem;
|
|
483
|
+
}
|
|
484
|
+
|
|
237
485
|
/* ── Status bar ─────────────────────────────────────────────────────── */
|
|
238
486
|
.status-bar {
|
|
239
487
|
display: flex;
|
|
@@ -269,6 +517,12 @@ var init_css = __esm(() => {
|
|
|
269
517
|
background: var(--md-bg-hover);
|
|
270
518
|
}
|
|
271
519
|
|
|
520
|
+
.attach-btn:disabled {
|
|
521
|
+
cursor: not-allowed;
|
|
522
|
+
opacity: 0.5;
|
|
523
|
+
pointer-events: none;
|
|
524
|
+
}
|
|
525
|
+
|
|
272
526
|
.attach-btn:focus-visible {
|
|
273
527
|
outline: 2px solid var(--md-accent);
|
|
274
528
|
outline-offset: 1px;
|
|
@@ -403,14 +657,38 @@ var init_css = __esm(() => {
|
|
|
403
657
|
text-overflow: ellipsis;
|
|
404
658
|
white-space: nowrap;
|
|
405
659
|
}
|
|
660
|
+
|
|
661
|
+
/* ── Reduced motion: disable all transitions and animations ──────── */
|
|
662
|
+
@media (prefers-reduced-motion: reduce) {
|
|
663
|
+
.tab-btn,
|
|
664
|
+
.attach-btn,
|
|
665
|
+
.ac-item,
|
|
666
|
+
.upload-bar {
|
|
667
|
+
transition: none;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
406
670
|
`;
|
|
407
|
-
});
|
|
408
671
|
|
|
409
672
|
// src/highlight.ts
|
|
673
|
+
var PRISM_BASE = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0";
|
|
674
|
+
var PRISM_CORE_URL = `${PRISM_BASE}/prism.min.js`;
|
|
675
|
+
var PRISM_AUTOLOADER_URL = `${PRISM_BASE}/plugins/autoloader/prism-autoloader.min.js`;
|
|
676
|
+
var PRISM_SRI = {
|
|
677
|
+
[PRISM_CORE_URL]: "sha512-7Z9J3l1+EYfeaPKcGXu3MS/7T+w19WtKQY/n+xzmw4hZhJ9tyYmcUS+4QqAlzhicE5LAfMQSF3iFTK9bQdTxXg==",
|
|
678
|
+
[PRISM_AUTOLOADER_URL]: "sha512-SkmBfuA2hqjzEVpmnMt/LINrjop3GKWqsuLSSB3e7iBmYK7JuWw4ldmmxwD9mdm2IRTTi0OxSAfEGvgEi0i2Kw=="
|
|
679
|
+
};
|
|
680
|
+
var PRISM_THEME_DARK_URL = `${PRISM_BASE}/themes/prism-tomorrow.min.css`;
|
|
681
|
+
var PRISM_THEME_LIGHT_URL = `${PRISM_BASE}/themes/prism-coy.min.css`;
|
|
682
|
+
var _prismReady = null;
|
|
410
683
|
function _loadScript(src) {
|
|
411
684
|
return new Promise((resolve) => {
|
|
412
685
|
const script = document.createElement("script");
|
|
413
686
|
script.src = src;
|
|
687
|
+
const integrity = PRISM_SRI[src];
|
|
688
|
+
if (integrity) {
|
|
689
|
+
script.integrity = integrity;
|
|
690
|
+
script.crossOrigin = "anonymous";
|
|
691
|
+
}
|
|
414
692
|
script.onload = () => resolve();
|
|
415
693
|
script.onerror = () => resolve();
|
|
416
694
|
document.head.appendChild(script);
|
|
@@ -422,19 +700,22 @@ function ensurePrism() {
|
|
|
422
700
|
if (_prismReady)
|
|
423
701
|
return _prismReady;
|
|
424
702
|
_prismReady = _loadScript(PRISM_CORE_URL).then(() => {
|
|
425
|
-
if (!window.Prism)
|
|
703
|
+
if (!window.Prism) {
|
|
704
|
+
_prismReady = null;
|
|
426
705
|
return;
|
|
706
|
+
}
|
|
427
707
|
window.Prism.manual = true;
|
|
428
708
|
return _loadScript(PRISM_AUTOLOADER_URL).then(() => {
|
|
429
709
|
if (window.Prism?.plugins?.autoloader) {
|
|
430
710
|
window.Prism.plugins.autoloader.languages_path = `${PRISM_BASE}/components/`;
|
|
431
711
|
}
|
|
712
|
+
return _loadScript(`${PRISM_BASE}/components/prism-markdown.min.js`);
|
|
432
713
|
});
|
|
433
714
|
});
|
|
434
715
|
return _prismReady;
|
|
435
716
|
}
|
|
436
717
|
function escapeHtml(text) {
|
|
437
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
718
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
438
719
|
}
|
|
439
720
|
function highlightMarkdown(text) {
|
|
440
721
|
const escaped = escapeHtml(text);
|
|
@@ -456,20 +737,35 @@ function applyPrismTheme(shadowRoot, dark) {
|
|
|
456
737
|
styleEl.id = "prism-theme";
|
|
457
738
|
shadowRoot.appendChild(styleEl);
|
|
458
739
|
}
|
|
459
|
-
const
|
|
740
|
+
const previewOverrides = `
|
|
741
|
+
.preview-body pre[class*="language-"] {
|
|
742
|
+
background: transparent;
|
|
743
|
+
margin: 0;
|
|
744
|
+
padding: 0;
|
|
745
|
+
overflow: visible;
|
|
746
|
+
position: static;
|
|
747
|
+
}
|
|
748
|
+
.preview-body pre[class*="language-"] > code {
|
|
749
|
+
background: transparent;
|
|
750
|
+
border: none;
|
|
751
|
+
box-shadow: none;
|
|
752
|
+
padding: 0;
|
|
753
|
+
background-image: none;
|
|
754
|
+
display: block;
|
|
755
|
+
overflow: auto;
|
|
756
|
+
max-height: none;
|
|
757
|
+
height: auto;
|
|
758
|
+
}`;
|
|
759
|
+
const expected = `@import url("${themeUrl}");${previewOverrides}`;
|
|
460
760
|
if (styleEl.textContent !== expected) {
|
|
461
761
|
styleEl.textContent = expected;
|
|
462
762
|
}
|
|
463
763
|
}
|
|
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
764
|
|
|
472
765
|
// src/upload.ts
|
|
766
|
+
var ACCEPTED_MIME_PREFIXES = ["image/"];
|
|
767
|
+
var ACCEPTED_MIME_EXACT = ["application/pdf"];
|
|
768
|
+
var ACCEPTED_EXTENSIONS = [".zip", ".txt", ".csv", ".json", ".md"];
|
|
473
769
|
function isAcceptedType(file) {
|
|
474
770
|
const type = file.type.toLowerCase();
|
|
475
771
|
if (ACCEPTED_MIME_PREFIXES.some((p) => type.startsWith(p)))
|
|
@@ -482,12 +778,19 @@ function isAcceptedType(file) {
|
|
|
482
778
|
return false;
|
|
483
779
|
}
|
|
484
780
|
function fileToMarkdown(file, url) {
|
|
781
|
+
const isSafeUrl = /^https:\/\//i.test(url) || /^\//.test(url) || /^\.\.?\//.test(url);
|
|
782
|
+
const safeUrl = isSafeUrl ? url.replace(/\(/g, "%28").replace(/\)/g, "%29") : "#unsafe-url";
|
|
783
|
+
const safeName = file.name.replace(/[[\]]/g, "\\$&");
|
|
485
784
|
if (file.type.startsWith("image/")) {
|
|
486
|
-
return ``;
|
|
487
786
|
}
|
|
488
|
-
return `[${
|
|
787
|
+
return `[${safeName}](${safeUrl})`;
|
|
489
788
|
}
|
|
490
789
|
function uploadFile(file, uploadUrl, onProgress) {
|
|
790
|
+
const isSafeUploadUrl = /^https:\/\//i.test(uploadUrl) || /^\//.test(uploadUrl) || /^\.\.?\//.test(uploadUrl);
|
|
791
|
+
if (!isSafeUploadUrl) {
|
|
792
|
+
return Promise.reject(new Error(`[el-dm-markdown-input] upload-url "${uploadUrl}" rejected — only https: and relative URLs are allowed.`));
|
|
793
|
+
}
|
|
491
794
|
return new Promise((resolve, reject) => {
|
|
492
795
|
const xhr = new XMLHttpRequest;
|
|
493
796
|
const body = new FormData;
|
|
@@ -504,27 +807,21 @@ function uploadFile(file, uploadUrl, onProgress) {
|
|
|
504
807
|
if (data.url) {
|
|
505
808
|
resolve(data.url);
|
|
506
809
|
} else {
|
|
507
|
-
reject("Upload response missing url field");
|
|
810
|
+
reject(new Error("Upload response missing url field"));
|
|
508
811
|
}
|
|
509
812
|
} catch {
|
|
510
|
-
reject("Upload response is not valid JSON");
|
|
813
|
+
reject(new Error("Upload response is not valid JSON"));
|
|
511
814
|
}
|
|
512
815
|
} else {
|
|
513
|
-
reject(`Upload failed with status ${xhr.status}`);
|
|
816
|
+
reject(new Error(`Upload failed with status ${xhr.status}`));
|
|
514
817
|
}
|
|
515
818
|
});
|
|
516
|
-
xhr.addEventListener("error", () => reject("Network error during upload"));
|
|
517
|
-
xhr.addEventListener("abort", () => reject("Upload aborted"));
|
|
819
|
+
xhr.addEventListener("error", () => reject(new Error("Network error during upload")));
|
|
820
|
+
xhr.addEventListener("abort", () => reject(new Error("Upload aborted")));
|
|
518
821
|
xhr.open("POST", uploadUrl);
|
|
519
822
|
xhr.send(body);
|
|
520
823
|
});
|
|
521
824
|
}
|
|
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
825
|
|
|
529
826
|
// src/autocomplete.ts
|
|
530
827
|
function detectTrigger(value, cursorPos) {
|
|
@@ -535,13 +832,13 @@ function detectTrigger(value, cursorPos) {
|
|
|
535
832
|
const query = value.slice(i + 1, cursorPos);
|
|
536
833
|
if (!/\s/.test(query)) {
|
|
537
834
|
const before = i > 0 ? value[i - 1] : null;
|
|
538
|
-
if (before === null ||
|
|
835
|
+
if (before === null || /\s/.test(before)) {
|
|
539
836
|
return { trigger: ch, query, triggerPos: i };
|
|
540
837
|
}
|
|
541
838
|
}
|
|
542
839
|
return null;
|
|
543
840
|
}
|
|
544
|
-
if (
|
|
841
|
+
if (/\s/.test(ch)) {
|
|
545
842
|
return null;
|
|
546
843
|
}
|
|
547
844
|
i--;
|
|
@@ -552,8 +849,11 @@ function confirmSuggestion(value, triggerPos, cursorPos, trigger, replacement) {
|
|
|
552
849
|
const before = value.slice(0, triggerPos);
|
|
553
850
|
const after = value.slice(cursorPos);
|
|
554
851
|
const inserted = `${trigger}${replacement}`;
|
|
555
|
-
const
|
|
556
|
-
|
|
852
|
+
const needsSpace = after.length === 0 || after[0] !== " " && after[0] !== `
|
|
853
|
+
`;
|
|
854
|
+
const suffix = needsSpace ? " " : "";
|
|
855
|
+
const newValue = before + inserted + suffix + after;
|
|
856
|
+
const newCursorPos = triggerPos + inserted.length + suffix.length;
|
|
557
857
|
return { newValue, newCursorPos };
|
|
558
858
|
}
|
|
559
859
|
function renderDropdown(suggestions, selectedIndex) {
|
|
@@ -569,7 +869,66 @@ function renderDropdown(suggestions, selectedIndex) {
|
|
|
569
869
|
return items;
|
|
570
870
|
}
|
|
571
871
|
function escapeHtml2(text) {
|
|
572
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
872
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// src/pairs.ts
|
|
876
|
+
function handlePairKey(ta, key) {
|
|
877
|
+
if (key !== "`")
|
|
878
|
+
return false;
|
|
879
|
+
const start = ta.selectionStart;
|
|
880
|
+
const end = ta.selectionEnd;
|
|
881
|
+
const value = ta.value;
|
|
882
|
+
if (start !== end) {
|
|
883
|
+
const selected = value.slice(start, end);
|
|
884
|
+
ta.value = value.slice(0, start) + "`" + selected + "`" + value.slice(end);
|
|
885
|
+
ta.setSelectionRange(start + 1, end + 1);
|
|
886
|
+
return true;
|
|
887
|
+
}
|
|
888
|
+
if (start >= 2 && value.slice(start - 2, start) === "``") {
|
|
889
|
+
ta.value = value.slice(0, start) + "`\n\n```" + value.slice(end);
|
|
890
|
+
ta.setSelectionRange(start + 2, start + 2);
|
|
891
|
+
return true;
|
|
892
|
+
}
|
|
893
|
+
ta.value = value.slice(0, start) + "``" + value.slice(end);
|
|
894
|
+
ta.setSelectionRange(start + 1, start + 1);
|
|
895
|
+
return true;
|
|
896
|
+
}
|
|
897
|
+
function handleEnterKey(ta, e) {
|
|
898
|
+
if (e.key !== "Enter")
|
|
899
|
+
return false;
|
|
900
|
+
const pos = ta.selectionStart;
|
|
901
|
+
const value = ta.value;
|
|
902
|
+
if (ta.selectionEnd !== pos)
|
|
903
|
+
return false;
|
|
904
|
+
const lineStart = value.lastIndexOf(`
|
|
905
|
+
`, pos - 1) + 1;
|
|
906
|
+
const currentLine = value.slice(lineStart, pos);
|
|
907
|
+
const result = getLineContinuation(currentLine);
|
|
908
|
+
if (result === null)
|
|
909
|
+
return false;
|
|
910
|
+
e.preventDefault();
|
|
911
|
+
if (result.eraseCurrentLine) {
|
|
912
|
+
const newValue = value.slice(0, lineStart) + value.slice(pos);
|
|
913
|
+
ta.value = newValue;
|
|
914
|
+
ta.setSelectionRange(lineStart, lineStart);
|
|
915
|
+
} else {
|
|
916
|
+
const newValue = value.slice(0, pos) + `
|
|
917
|
+
` + result.prefix + value.slice(ta.selectionEnd);
|
|
918
|
+
const newPos = pos + 1 + result.prefix.length;
|
|
919
|
+
ta.value = newValue;
|
|
920
|
+
ta.setSelectionRange(newPos, newPos);
|
|
921
|
+
}
|
|
922
|
+
return true;
|
|
923
|
+
}
|
|
924
|
+
function getLineContinuation(line) {
|
|
925
|
+
if (line === "* ")
|
|
926
|
+
return { eraseCurrentLine: true };
|
|
927
|
+
if (/^\* ./.test(line))
|
|
928
|
+
return { prefix: "* ", eraseCurrentLine: false };
|
|
929
|
+
if (/^#{1,6} ./.test(line))
|
|
930
|
+
return { prefix: "", eraseCurrentLine: false };
|
|
931
|
+
return null;
|
|
573
932
|
}
|
|
574
933
|
|
|
575
934
|
// src/status-bar.ts
|
|
@@ -599,155 +958,156 @@ function renderStatusCount(wordCount, charCount, maxWords) {
|
|
|
599
958
|
}
|
|
600
959
|
|
|
601
960
|
// 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`
|
|
961
|
+
var import_el_base3 = require("@duskmoon-dev/el-base");
|
|
962
|
+
var coreMarkdownStyles = import_markdown_body.css.replace(/@layer\s+components\s*\{/, "").replace(/\}\s*$/, "");
|
|
963
|
+
var markdownBodySheet = import_el_base3.css`
|
|
627
964
|
${coreMarkdownStyles}
|
|
628
965
|
`;
|
|
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
|
-
|
|
966
|
+
|
|
967
|
+
class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
968
|
+
static formAssociated = true;
|
|
969
|
+
static properties = {
|
|
970
|
+
name: { type: String, reflect: true, default: "" },
|
|
971
|
+
value: { type: String, default: "" },
|
|
972
|
+
placeholder: { type: String, reflect: true, default: "Write markdown…" },
|
|
973
|
+
disabled: { type: Boolean, reflect: true },
|
|
974
|
+
readonly: { type: Boolean, reflect: true },
|
|
975
|
+
required: { type: Boolean, reflect: true },
|
|
976
|
+
uploadUrl: { type: String, reflect: true, attribute: "upload-url" },
|
|
977
|
+
maxWords: { type: Number, reflect: true, attribute: "max-words" },
|
|
978
|
+
dark: { type: Boolean, reflect: true },
|
|
979
|
+
livePreview: { type: Boolean, reflect: true, attribute: "live-preview" },
|
|
980
|
+
debounce: { type: Number, reflect: true, default: 300 },
|
|
981
|
+
katexCssUrl: { type: String, reflect: true, attribute: "katex-css-url" },
|
|
982
|
+
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" }
|
|
983
|
+
};
|
|
984
|
+
#internals;
|
|
985
|
+
#initialized = false;
|
|
986
|
+
#activeTab = "write";
|
|
987
|
+
#highlightTimer = null;
|
|
988
|
+
#statusTimer = null;
|
|
989
|
+
#textarea = null;
|
|
990
|
+
#renderLayer = null;
|
|
991
|
+
#writeArea = null;
|
|
992
|
+
#previewBody = null;
|
|
993
|
+
#statusCount = null;
|
|
994
|
+
#acDropdown = null;
|
|
995
|
+
#uploadList = null;
|
|
996
|
+
#fileInput = null;
|
|
997
|
+
#acSuggestions = [];
|
|
998
|
+
#acSelectedIndex = -1;
|
|
999
|
+
#acTriggerPos = -1;
|
|
1000
|
+
#acTrigger = null;
|
|
1001
|
+
#acGeneration = 0;
|
|
1002
|
+
#prevDark = false;
|
|
1003
|
+
#renderFn = null;
|
|
1004
|
+
#mermaidFn = null;
|
|
1005
|
+
#livePreviewTimer = null;
|
|
1006
|
+
#renderAbortController = null;
|
|
1007
|
+
#lastRenderedSource = null;
|
|
1008
|
+
#katexCssInjected = false;
|
|
1009
|
+
#uploadIdCounter = 0;
|
|
1010
|
+
constructor() {
|
|
1011
|
+
super();
|
|
1012
|
+
this.#internals = this.attachInternals();
|
|
1013
|
+
this.attachStyles([elementStyles, markdownBodySheet]);
|
|
1014
|
+
}
|
|
1015
|
+
connectedCallback() {
|
|
1016
|
+
super.connectedCallback();
|
|
1017
|
+
}
|
|
1018
|
+
disconnectedCallback() {
|
|
1019
|
+
this.#renderAbortController?.abort();
|
|
1020
|
+
if (this.#livePreviewTimer !== null)
|
|
1021
|
+
clearTimeout(this.#livePreviewTimer);
|
|
1022
|
+
super.disconnectedCallback();
|
|
1023
|
+
}
|
|
1024
|
+
update() {
|
|
1025
|
+
if (!this.#initialized) {
|
|
1026
|
+
super.update();
|
|
1027
|
+
this.#initialized = true;
|
|
1028
|
+
this.#cacheDOMRefs();
|
|
1029
|
+
this.#attachEventHandlers();
|
|
1030
|
+
this.#initHighlight();
|
|
1031
|
+
const initVal = this.value ?? "";
|
|
1032
|
+
if (this.#textarea) {
|
|
1033
|
+
this.#textarea.value = initVal;
|
|
671
1034
|
this.#syncFormValue();
|
|
1035
|
+
if (initVal)
|
|
1036
|
+
this.#scheduleHighlight();
|
|
672
1037
|
}
|
|
1038
|
+
this.#updateStatusBarNow();
|
|
1039
|
+
return;
|
|
673
1040
|
}
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
1041
|
+
this.#patchDynamicRegions();
|
|
1042
|
+
}
|
|
1043
|
+
#patchDynamicRegions() {
|
|
1044
|
+
const ta = this.#textarea;
|
|
1045
|
+
if (!ta)
|
|
1046
|
+
return;
|
|
1047
|
+
const placeholder = this.placeholder ?? "Write markdown…";
|
|
1048
|
+
ta.placeholder = placeholder;
|
|
1049
|
+
ta.disabled = !!this.disabled;
|
|
1050
|
+
ta.readOnly = !!this.readonly;
|
|
1051
|
+
const attachBtn = this.shadowRoot.querySelector(".attach-btn");
|
|
1052
|
+
if (attachBtn) {
|
|
1053
|
+
attachBtn.disabled = ta.disabled || ta.readOnly;
|
|
677
1054
|
}
|
|
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;
|
|
1055
|
+
const propVal = this.value ?? "";
|
|
1056
|
+
if (propVal !== ta.value) {
|
|
1057
|
+
ta.value = propVal;
|
|
1058
|
+
this.#syncFormValue();
|
|
1059
|
+
this.#scheduleHighlight();
|
|
1060
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1061
|
+
this.#renderPreview(propVal);
|
|
693
1062
|
}
|
|
694
|
-
this.#patchDynamicRegions();
|
|
695
1063
|
}
|
|
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;
|
|
1064
|
+
const dark = !!this.dark;
|
|
1065
|
+
applyPrismTheme(this.shadowRoot, dark);
|
|
1066
|
+
if (dark !== this.#prevDark) {
|
|
1067
|
+
this.#prevDark = dark;
|
|
1068
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1069
|
+
this.#lastRenderedSource = null;
|
|
1070
|
+
this.#renderPreview(ta.value);
|
|
707
1071
|
}
|
|
708
|
-
const propVal = this.value ?? "";
|
|
709
|
-
if (propVal !== ta.value) {
|
|
710
|
-
ta.value = propVal;
|
|
711
|
-
this.#syncFormValue();
|
|
712
|
-
this.#scheduleHighlight();
|
|
713
|
-
}
|
|
714
|
-
const dark = !!this.dark;
|
|
715
|
-
applyPrismTheme(this.shadowRoot, dark);
|
|
716
|
-
this.#updateStatusBarNow();
|
|
717
1072
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1073
|
+
this.#updateStatusBarNow();
|
|
1074
|
+
}
|
|
1075
|
+
render() {
|
|
1076
|
+
const ph = this.placeholder ?? "Write markdown…";
|
|
1077
|
+
const disabled = !!this.disabled;
|
|
1078
|
+
const readonly = !!this.readonly;
|
|
1079
|
+
return `
|
|
723
1080
|
<div class="editor">
|
|
724
1081
|
<div class="toolbar" role="tablist" aria-label="Editor mode">
|
|
725
1082
|
<button
|
|
726
1083
|
class="tab-btn"
|
|
1084
|
+
id="tab-write"
|
|
727
1085
|
data-tab="write"
|
|
728
1086
|
role="tab"
|
|
729
1087
|
aria-selected="true"
|
|
730
1088
|
aria-controls="write-panel"
|
|
1089
|
+
tabindex="0"
|
|
731
1090
|
>Write</button>
|
|
732
1091
|
<button
|
|
733
1092
|
class="tab-btn"
|
|
1093
|
+
id="tab-preview"
|
|
734
1094
|
data-tab="preview"
|
|
735
1095
|
role="tab"
|
|
736
1096
|
aria-selected="false"
|
|
737
1097
|
aria-controls="preview-panel"
|
|
1098
|
+
tabindex="-1"
|
|
738
1099
|
>Preview</button>
|
|
739
1100
|
</div>
|
|
740
1101
|
|
|
741
|
-
<div class="write-area" id="write-panel" role="tabpanel" aria-
|
|
742
|
-
<div class="
|
|
743
|
-
<div class="backdrop-content"></div>
|
|
744
|
-
</div>
|
|
1102
|
+
<div class="write-area" id="write-panel" role="tabpanel" aria-labelledby="tab-write">
|
|
1103
|
+
<div class="render-layer" aria-hidden="true"></div>
|
|
745
1104
|
<textarea
|
|
746
1105
|
aria-label="Markdown editor"
|
|
747
1106
|
aria-haspopup="listbox"
|
|
1107
|
+
aria-expanded="false"
|
|
748
1108
|
aria-autocomplete="list"
|
|
749
1109
|
aria-controls="ac-dropdown"
|
|
750
|
-
placeholder="${ph}"
|
|
1110
|
+
placeholder="${escapeHtmlStr(ph)}"
|
|
751
1111
|
${disabled ? "disabled" : ""}
|
|
752
1112
|
${readonly ? "readonly" : ""}
|
|
753
1113
|
spellcheck="false"
|
|
@@ -761,7 +1121,7 @@ var init_element = __esm(() => {
|
|
|
761
1121
|
class="preview-body markdown-body"
|
|
762
1122
|
id="preview-panel"
|
|
763
1123
|
role="tabpanel"
|
|
764
|
-
aria-
|
|
1124
|
+
aria-labelledby="tab-preview"
|
|
765
1125
|
hidden
|
|
766
1126
|
></div>
|
|
767
1127
|
|
|
@@ -784,410 +1144,493 @@ var init_element = __esm(() => {
|
|
|
784
1144
|
</div>
|
|
785
1145
|
<ul id="ac-dropdown" class="ac-dropdown" role="listbox" aria-label="Suggestions" hidden></ul>
|
|
786
1146
|
`;
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1147
|
+
}
|
|
1148
|
+
#cacheDOMRefs() {
|
|
1149
|
+
this.#textarea = this.shadowRoot.querySelector("textarea");
|
|
1150
|
+
this.#renderLayer = this.shadowRoot.querySelector(".render-layer");
|
|
1151
|
+
this.#writeArea = this.shadowRoot.querySelector(".write-area");
|
|
1152
|
+
this.#previewBody = this.shadowRoot.querySelector(".preview-body");
|
|
1153
|
+
this.#statusCount = this.shadowRoot.querySelector(".status-bar-count");
|
|
1154
|
+
this.#acDropdown = this.shadowRoot.querySelector(".ac-dropdown");
|
|
1155
|
+
this.#uploadList = this.shadowRoot.querySelector(".upload-list");
|
|
1156
|
+
this.#fileInput = this.shadowRoot.querySelector(".file-input");
|
|
1157
|
+
}
|
|
1158
|
+
#attachEventHandlers() {
|
|
1159
|
+
const ta = this.#textarea;
|
|
1160
|
+
if (!ta)
|
|
1161
|
+
return;
|
|
1162
|
+
ta.addEventListener("input", () => {
|
|
1163
|
+
this.#syncFormValue();
|
|
1164
|
+
this.emit("change", { value: ta.value });
|
|
1165
|
+
this.#scheduleHighlight();
|
|
1166
|
+
this.#scheduleStatusUpdate();
|
|
1167
|
+
this.#handleAutocompleteInput();
|
|
1168
|
+
this.#scheduleLivePreview();
|
|
1169
|
+
});
|
|
1170
|
+
ta.addEventListener("blur", () => {
|
|
1171
|
+
setTimeout(() => {
|
|
1172
|
+
if (!this.shadowRoot?.activeElement) {
|
|
1173
|
+
this.#closeDropdown();
|
|
1174
|
+
}
|
|
1175
|
+
}, 150);
|
|
1176
|
+
});
|
|
1177
|
+
ta.addEventListener("keydown", (e) => {
|
|
1178
|
+
if (this.#acSuggestions.length > 0 && !this.#acDropdown?.hidden) {
|
|
1179
|
+
this.#handleDropdownKeydown(e);
|
|
1180
|
+
if (e.defaultPrevented)
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === "P") {
|
|
1184
|
+
e.preventDefault();
|
|
1185
|
+
this.#switchTab(this.#activeTab === "write" ? "preview" : "write");
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
if (this.disabled || this.readonly) {
|
|
802
1189
|
return;
|
|
803
|
-
|
|
1190
|
+
}
|
|
1191
|
+
if (!e.ctrlKey && !e.metaKey && !e.altKey && handlePairKey(ta, e.key)) {
|
|
1192
|
+
e.preventDefault();
|
|
804
1193
|
this.#syncFormValue();
|
|
805
1194
|
this.emit("change", { value: ta.value });
|
|
806
1195
|
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");
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
if (e.key === "Enter" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1199
|
+
if (handleEnterKey(ta, e)) {
|
|
1200
|
+
this.#syncFormValue();
|
|
1201
|
+
this.emit("change", { value: ta.value });
|
|
1202
|
+
this.#scheduleHighlight();
|
|
1203
|
+
this.#scheduleStatusUpdate();
|
|
830
1204
|
}
|
|
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
1205
|
}
|
|
850
|
-
|
|
1206
|
+
});
|
|
1207
|
+
const writeArea = this.#writeArea;
|
|
1208
|
+
if (writeArea) {
|
|
1209
|
+
writeArea.addEventListener("dragover", (e) => {
|
|
1210
|
+
if (this.disabled)
|
|
1211
|
+
return;
|
|
851
1212
|
if (this.readonly)
|
|
852
1213
|
return;
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
e.preventDefault();
|
|
856
|
-
imageFiles.forEach((f) => this.#startUpload(f));
|
|
857
|
-
}
|
|
1214
|
+
e.preventDefault();
|
|
1215
|
+
writeArea.style.opacity = "0.8";
|
|
858
1216
|
});
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
const btn = e.target.closest(".tab-btn");
|
|
862
|
-
const tab = btn?.dataset.tab;
|
|
863
|
-
if (tab)
|
|
864
|
-
this.#switchTab(tab);
|
|
1217
|
+
writeArea.addEventListener("dragleave", () => {
|
|
1218
|
+
writeArea.style.opacity = "";
|
|
865
1219
|
});
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1220
|
+
writeArea.addEventListener("drop", (e) => {
|
|
1221
|
+
e.preventDefault();
|
|
1222
|
+
writeArea.style.opacity = "";
|
|
1223
|
+
if (this.disabled)
|
|
1224
|
+
return;
|
|
1225
|
+
if (this.readonly)
|
|
1226
|
+
return;
|
|
1227
|
+
const files = Array.from(e.dataTransfer?.files ?? []).filter(isAcceptedType);
|
|
870
1228
|
files.forEach((f) => this.#startUpload(f));
|
|
871
|
-
if (this.#fileInput)
|
|
872
|
-
this.#fileInput.value = "";
|
|
873
|
-
});
|
|
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
1229
|
});
|
|
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
1230
|
}
|
|
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)
|
|
1231
|
+
ta.addEventListener("paste", (e) => {
|
|
1232
|
+
if (this.disabled)
|
|
917
1233
|
return;
|
|
918
|
-
this
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1234
|
+
if (this.readonly)
|
|
1235
|
+
return;
|
|
1236
|
+
const imageFiles = Array.from(e.clipboardData?.files ?? []).filter((f) => f.type.startsWith("image/"));
|
|
1237
|
+
if (imageFiles.length > 0) {
|
|
1238
|
+
e.preventDefault();
|
|
1239
|
+
imageFiles.forEach((f) => this.#startUpload(f));
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
const toolbar = this.shadowRoot.querySelector(".toolbar");
|
|
1243
|
+
toolbar?.addEventListener("click", (e) => {
|
|
1244
|
+
const btn = e.target.closest(".tab-btn");
|
|
1245
|
+
const tab = btn?.dataset.tab;
|
|
1246
|
+
if (tab)
|
|
1247
|
+
this.#switchTab(tab);
|
|
1248
|
+
});
|
|
1249
|
+
toolbar?.addEventListener("keydown", (e) => {
|
|
1250
|
+
const kev = e;
|
|
1251
|
+
if (kev.key === "ArrowLeft" || kev.key === "ArrowRight") {
|
|
1252
|
+
kev.preventDefault();
|
|
1253
|
+
const nextTab = this.#activeTab === "write" ? "preview" : "write";
|
|
1254
|
+
this.#switchTab(nextTab);
|
|
1255
|
+
const nextBtn = this.shadowRoot.querySelector(`.tab-btn[data-tab="${nextTab}"]`);
|
|
1256
|
+
nextBtn?.focus();
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
const attachBtn = this.shadowRoot.querySelector(".attach-btn");
|
|
1260
|
+
attachBtn?.addEventListener("click", () => this.#fileInput?.click());
|
|
1261
|
+
this.#fileInput?.addEventListener("change", () => {
|
|
1262
|
+
const files = Array.from(this.#fileInput?.files ?? []).filter(isAcceptedType);
|
|
1263
|
+
files.forEach((f) => this.#startUpload(f));
|
|
1264
|
+
if (this.#fileInput)
|
|
1265
|
+
this.#fileInput.value = "";
|
|
1266
|
+
});
|
|
1267
|
+
this.#acDropdown?.addEventListener("click", (e) => {
|
|
1268
|
+
const item = e.target.closest("[data-ac-index]");
|
|
1269
|
+
if (item) {
|
|
1270
|
+
const idx = parseInt(item.dataset.acIndex ?? "-1", 10);
|
|
1271
|
+
if (idx >= 0) {
|
|
1272
|
+
this.#acSelectedIndex = idx;
|
|
1273
|
+
this.#confirmAutocomplete();
|
|
929
1274
|
}
|
|
930
|
-
} else {
|
|
931
|
-
this.#writeArea?.removeAttribute("hidden");
|
|
932
|
-
this.#previewBody?.setAttribute("hidden", "");
|
|
933
1275
|
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
#initHighlight() {
|
|
1279
|
+
const dark = !!this.dark;
|
|
1280
|
+
applyPrismTheme(this.shadowRoot, dark);
|
|
1281
|
+
ensurePrism().then(() => {
|
|
1282
|
+
if (this.#textarea && this.#renderLayer) {
|
|
1283
|
+
this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
#scheduleHighlight() {
|
|
1288
|
+
if (this.#highlightTimer !== null)
|
|
1289
|
+
clearTimeout(this.#highlightTimer);
|
|
1290
|
+
this.#highlightTimer = setTimeout(() => {
|
|
1291
|
+
this.#highlightTimer = null;
|
|
1292
|
+
if (this.#renderLayer && this.#textarea) {
|
|
1293
|
+
this.#renderLayer.innerHTML = highlightMarkdown(this.#textarea.value);
|
|
1294
|
+
}
|
|
1295
|
+
}, 60);
|
|
1296
|
+
}
|
|
1297
|
+
#switchTab(tab) {
|
|
1298
|
+
if (tab === this.#activeTab)
|
|
1299
|
+
return;
|
|
1300
|
+
this.#activeTab = tab;
|
|
1301
|
+
const writeBtns = this.shadowRoot.querySelectorAll(".tab-btn");
|
|
1302
|
+
writeBtns.forEach((btn) => {
|
|
1303
|
+
const isActive = btn.dataset.tab === tab;
|
|
1304
|
+
btn.setAttribute("aria-selected", String(isActive));
|
|
1305
|
+
btn.setAttribute("tabindex", isActive ? "0" : "-1");
|
|
1306
|
+
});
|
|
1307
|
+
if (tab === "preview") {
|
|
1308
|
+
this.#writeArea?.setAttribute("hidden", "");
|
|
1309
|
+
if (this.#previewBody) {
|
|
1310
|
+
this.#previewBody.removeAttribute("hidden");
|
|
1311
|
+
this.#renderPreview(this.#textarea?.value ?? "");
|
|
1312
|
+
}
|
|
1313
|
+
} else {
|
|
1314
|
+
this.#writeArea?.removeAttribute("hidden");
|
|
1315
|
+
this.#previewBody?.setAttribute("hidden", "");
|
|
934
1316
|
}
|
|
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;
|
|
1317
|
+
}
|
|
1318
|
+
async#loadRenderPipeline() {
|
|
1319
|
+
if (this.#renderFn && this.#mermaidFn) {
|
|
1320
|
+
return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };
|
|
1321
|
+
}
|
|
1322
|
+
const mod = await Promise.resolve().then(() => (init_render(), exports_render));
|
|
1323
|
+
this.#renderFn = mod.renderMarkdown;
|
|
1324
|
+
this.#mermaidFn = mod.renderMermaidBlocks;
|
|
1325
|
+
return { renderMarkdown: this.#renderFn, renderMermaidBlocks: this.#mermaidFn };
|
|
1326
|
+
}
|
|
1327
|
+
async#renderPreview(source, force = false) {
|
|
1328
|
+
const preview = this.#previewBody;
|
|
1329
|
+
if (!preview)
|
|
1330
|
+
return;
|
|
1331
|
+
if (!force && this.#lastRenderedSource === source && this.#renderFn !== null) {
|
|
1332
|
+
return;
|
|
979
1333
|
}
|
|
980
|
-
|
|
981
|
-
|
|
1334
|
+
this.#renderAbortController?.abort();
|
|
1335
|
+
const controller = new AbortController;
|
|
1336
|
+
this.#renderAbortController = controller;
|
|
1337
|
+
this.emit("render-start", {});
|
|
1338
|
+
preview.setAttribute("aria-busy", "true");
|
|
1339
|
+
if (!this.#renderFn) {
|
|
1340
|
+
preview.innerHTML = `
|
|
1341
|
+
<div class="preview-skeleton" aria-label="Loading preview…">
|
|
1342
|
+
<div class="skeleton-line" style="width:90%"></div>
|
|
1343
|
+
<div class="skeleton-line" style="width:75%"></div>
|
|
1344
|
+
<div class="skeleton-line" style="width:85%"></div>
|
|
1345
|
+
<div class="skeleton-line" style="width:60%"></div>
|
|
1346
|
+
</div>`;
|
|
982
1347
|
}
|
|
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");
|
|
1348
|
+
try {
|
|
1349
|
+
const { renderMarkdown: renderMarkdown2, renderMermaidBlocks: renderMermaidBlocks2 } = await this.#loadRenderPipeline();
|
|
1350
|
+
if (controller.signal.aborted) {
|
|
1351
|
+
preview.removeAttribute("aria-busy");
|
|
990
1352
|
return;
|
|
991
1353
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}).then((url) => {
|
|
996
|
-
this.#removeUploadRow(id);
|
|
997
|
-
const markdown = fileToMarkdown(file, url);
|
|
998
|
-
this.insertText(markdown);
|
|
999
|
-
this.emit("upload-done", { file, url, markdown });
|
|
1000
|
-
}).catch((err) => {
|
|
1001
|
-
this.#removeUploadRow(id);
|
|
1002
|
-
const errorMsg = typeof err === "string" ? err : "Upload failed";
|
|
1003
|
-
this.emit("upload-error", { file, error: errorMsg });
|
|
1004
|
-
this.#showUploadError(file, errorMsg);
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
#addProgressRow(id, filename) {
|
|
1008
|
-
if (!this.#uploadList)
|
|
1354
|
+
const html = await renderMarkdown2(source);
|
|
1355
|
+
if (controller.signal.aborted) {
|
|
1356
|
+
preview.removeAttribute("aria-busy");
|
|
1009
1357
|
return;
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1358
|
+
}
|
|
1359
|
+
preview.innerHTML = html;
|
|
1360
|
+
preview.removeAttribute("aria-busy");
|
|
1361
|
+
this.#ensureKatexCss();
|
|
1362
|
+
const mermaidSrc = this.mermaidSrc;
|
|
1363
|
+
await renderMermaidBlocks2(preview, mermaidSrc);
|
|
1364
|
+
if (controller.signal.aborted) {
|
|
1365
|
+
preview.removeAttribute("aria-busy");
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
this.#lastRenderedSource = source;
|
|
1369
|
+
this.emit("render-done", { html });
|
|
1370
|
+
} catch (err) {
|
|
1371
|
+
if (controller.signal.aborted) {
|
|
1372
|
+
preview.removeAttribute("aria-busy");
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
preview.removeAttribute("aria-busy");
|
|
1376
|
+
preview.innerHTML = `<pre class="render-error-fallback">${escapeHtmlStr(source)}</pre>`;
|
|
1377
|
+
this.emit("render-error", { error: err instanceof Error ? err : new Error(String(err)) });
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
#ensureKatexCss() {
|
|
1381
|
+
if (this.#katexCssInjected)
|
|
1382
|
+
return;
|
|
1383
|
+
this.#katexCssInjected = true;
|
|
1384
|
+
const rawUrl = this.katexCssUrl ?? "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
|
|
1385
|
+
const katexUrl = /^https:\/\//i.test(rawUrl) ? rawUrl : "https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css";
|
|
1386
|
+
if (katexUrl !== rawUrl) {
|
|
1387
|
+
console.warn(`[el-dm-markdown-input] katex-css-url "${rawUrl}" rejected — only https: URLs are allowed.`);
|
|
1388
|
+
}
|
|
1389
|
+
const link = document.createElement("link");
|
|
1390
|
+
link.id = "katex-css";
|
|
1391
|
+
link.rel = "stylesheet";
|
|
1392
|
+
link.href = katexUrl;
|
|
1393
|
+
this.shadowRoot.appendChild(link);
|
|
1394
|
+
}
|
|
1395
|
+
#scheduleLivePreview() {
|
|
1396
|
+
if (!this.livePreview)
|
|
1397
|
+
return;
|
|
1398
|
+
if (this.#activeTab !== "preview")
|
|
1399
|
+
return;
|
|
1400
|
+
if (this.#livePreviewTimer !== null)
|
|
1401
|
+
clearTimeout(this.#livePreviewTimer);
|
|
1402
|
+
const ms = this.debounce ?? 300;
|
|
1403
|
+
this.#livePreviewTimer = setTimeout(() => {
|
|
1404
|
+
this.#livePreviewTimer = null;
|
|
1405
|
+
this.#renderPreview(this.#textarea?.value ?? "");
|
|
1406
|
+
}, ms);
|
|
1407
|
+
}
|
|
1408
|
+
#syncFormValue() {
|
|
1409
|
+
this.#internals?.setFormValue(this.#textarea?.value ?? "");
|
|
1410
|
+
}
|
|
1411
|
+
#startUpload(file) {
|
|
1412
|
+
this.emit("upload-start", { file });
|
|
1413
|
+
const id = `upload-${++this.#uploadIdCounter}`;
|
|
1414
|
+
const uploadUrl = this.uploadUrl;
|
|
1415
|
+
if (!uploadUrl) {
|
|
1416
|
+
this.emit("upload-error", { file, error: "no upload-url set" });
|
|
1417
|
+
this.#showUploadError(file, "no upload-url set");
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
this.#addProgressRow(id, file.name);
|
|
1421
|
+
uploadFile(file, uploadUrl, (pct) => {
|
|
1422
|
+
this.#updateProgressRow(id, pct);
|
|
1423
|
+
}).then((url) => {
|
|
1424
|
+
this.#removeUploadRow(id);
|
|
1425
|
+
const markdown = fileToMarkdown(file, url);
|
|
1426
|
+
this.insertText(markdown);
|
|
1427
|
+
this.emit("upload-done", { file, url, markdown });
|
|
1428
|
+
}).catch((err) => {
|
|
1429
|
+
this.#removeUploadRow(id);
|
|
1430
|
+
const errorMsg = err instanceof Error ? err.message : "Upload failed";
|
|
1431
|
+
this.emit("upload-error", { file, error: errorMsg });
|
|
1432
|
+
this.#showUploadError(file, errorMsg);
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
#addProgressRow(id, filename) {
|
|
1436
|
+
if (!this.#uploadList)
|
|
1437
|
+
return;
|
|
1438
|
+
const row = document.createElement("div");
|
|
1439
|
+
row.className = "upload-row";
|
|
1440
|
+
row.id = id;
|
|
1441
|
+
row.innerHTML = `
|
|
1014
1442
|
<span class="upload-filename">${escapeHtmlStr(filename)}</span>
|
|
1015
|
-
<div class="upload-bar-track">
|
|
1443
|
+
<div class="upload-bar-track" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="Uploading ${escapeHtmlStr(filename)}">
|
|
1016
1444
|
<div class="upload-bar" style="width: 0%"></div>
|
|
1017
1445
|
</div>
|
|
1018
1446
|
`;
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1447
|
+
this.#uploadList.appendChild(row);
|
|
1448
|
+
}
|
|
1449
|
+
#updateProgressRow(id, pct) {
|
|
1450
|
+
const track = this.#uploadList?.querySelector(`#${id} .upload-bar-track`);
|
|
1451
|
+
if (track)
|
|
1452
|
+
track.setAttribute("aria-valuenow", String(pct));
|
|
1453
|
+
const bar = this.#uploadList?.querySelector(`#${id} .upload-bar`);
|
|
1454
|
+
if (bar)
|
|
1455
|
+
bar.style.width = `${pct}%`;
|
|
1456
|
+
}
|
|
1457
|
+
#removeUploadRow(id) {
|
|
1458
|
+
this.#uploadList?.querySelector(`#${id}`)?.remove();
|
|
1459
|
+
}
|
|
1460
|
+
#showUploadError(file, message) {
|
|
1461
|
+
if (!this.#uploadList)
|
|
1462
|
+
return;
|
|
1463
|
+
const row = document.createElement("div");
|
|
1464
|
+
row.className = "upload-error-row";
|
|
1465
|
+
row.setAttribute("role", "alert");
|
|
1466
|
+
row.innerHTML = `
|
|
1035
1467
|
<span class="upload-error-msg">${escapeHtmlStr(file.name)}: ${escapeHtmlStr(message)}</span>
|
|
1036
1468
|
`;
|
|
1037
|
-
|
|
1038
|
-
|
|
1469
|
+
this.#uploadList.appendChild(row);
|
|
1470
|
+
setTimeout(() => row.remove(), 4000);
|
|
1471
|
+
}
|
|
1472
|
+
#handleAutocompleteInput() {
|
|
1473
|
+
const ta = this.#textarea;
|
|
1474
|
+
if (!ta)
|
|
1475
|
+
return;
|
|
1476
|
+
const result = detectTrigger(ta.value, ta.selectionStart ?? 0);
|
|
1477
|
+
if (!result) {
|
|
1478
|
+
this.#closeDropdown();
|
|
1479
|
+
return;
|
|
1039
1480
|
}
|
|
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
|
-
}
|
|
1481
|
+
const { trigger, query, triggerPos } = result;
|
|
1482
|
+
this.#acTrigger = trigger;
|
|
1483
|
+
this.#acTriggerPos = triggerPos;
|
|
1484
|
+
const gen = ++this.#acGeneration;
|
|
1485
|
+
const resolve = (list) => {
|
|
1486
|
+
if (gen === this.#acGeneration)
|
|
1487
|
+
this.setSuggestions(list);
|
|
1488
|
+
};
|
|
1489
|
+
if (trigger === "@") {
|
|
1490
|
+
this.emit("mention-query", { trigger, query, resolve });
|
|
1491
|
+
} else {
|
|
1492
|
+
this.emit("reference-query", { trigger, query, resolve });
|
|
1058
1493
|
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1494
|
+
}
|
|
1495
|
+
#handleDropdownKeydown(e) {
|
|
1496
|
+
const len = this.#acSuggestions.length;
|
|
1497
|
+
if (len === 0)
|
|
1498
|
+
return;
|
|
1499
|
+
switch (e.key) {
|
|
1500
|
+
case "ArrowDown":
|
|
1501
|
+
e.preventDefault();
|
|
1502
|
+
this.#acSelectedIndex = (this.#acSelectedIndex + 1) % len;
|
|
1503
|
+
this.#updateDropdown();
|
|
1504
|
+
break;
|
|
1505
|
+
case "ArrowUp":
|
|
1506
|
+
e.preventDefault();
|
|
1507
|
+
this.#acSelectedIndex = (this.#acSelectedIndex - 1 + len) % len;
|
|
1508
|
+
this.#updateDropdown();
|
|
1509
|
+
break;
|
|
1510
|
+
case "Enter":
|
|
1511
|
+
case "Tab":
|
|
1512
|
+
if (this.#acSelectedIndex >= 0) {
|
|
1070
1513
|
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
|
-
}
|
|
1514
|
+
this.#confirmAutocomplete();
|
|
1515
|
+
}
|
|
1516
|
+
break;
|
|
1517
|
+
case "Escape":
|
|
1518
|
+
this.#closeDropdown();
|
|
1519
|
+
break;
|
|
1111
1520
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1521
|
+
}
|
|
1522
|
+
#confirmAutocomplete() {
|
|
1523
|
+
const ta = this.#textarea;
|
|
1524
|
+
if (!ta || this.#acSelectedIndex < 0 || !this.#acTrigger)
|
|
1525
|
+
return;
|
|
1526
|
+
const suggestion = this.#acSuggestions[this.#acSelectedIndex];
|
|
1527
|
+
if (!suggestion)
|
|
1528
|
+
return;
|
|
1529
|
+
const { newValue, newCursorPos } = confirmSuggestion(ta.value, this.#acTriggerPos, ta.selectionStart ?? ta.value.length, this.#acTrigger, suggestion.id);
|
|
1530
|
+
ta.value = newValue;
|
|
1531
|
+
ta.setSelectionRange(newCursorPos, newCursorPos);
|
|
1532
|
+
this.#syncFormValue();
|
|
1533
|
+
this.emit("change", { value: ta.value });
|
|
1534
|
+
this.#scheduleHighlight();
|
|
1535
|
+
this.#scheduleStatusUpdate();
|
|
1536
|
+
this.#closeDropdown();
|
|
1537
|
+
}
|
|
1538
|
+
#closeDropdown() {
|
|
1539
|
+
this.#acGeneration++;
|
|
1540
|
+
this.#acSuggestions = [];
|
|
1541
|
+
this.#acSelectedIndex = -1;
|
|
1542
|
+
this.#acTrigger = null;
|
|
1543
|
+
this.#acTriggerPos = -1;
|
|
1544
|
+
if (this.#acDropdown) {
|
|
1545
|
+
this.#acDropdown.innerHTML = "";
|
|
1546
|
+
this.#acDropdown.hidden = true;
|
|
1127
1547
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1548
|
+
this.#textarea?.setAttribute("aria-expanded", "false");
|
|
1549
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1550
|
+
}
|
|
1551
|
+
#updateDropdown() {
|
|
1552
|
+
if (!this.#acDropdown)
|
|
1553
|
+
return;
|
|
1554
|
+
if (this.#acSuggestions.length === 0) {
|
|
1555
|
+
this.#acDropdown.hidden = true;
|
|
1556
|
+
this.#textarea?.setAttribute("aria-expanded", "false");
|
|
1557
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1558
|
+
return;
|
|
1135
1559
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);
|
|
1560
|
+
this.#acDropdown.innerHTML = renderDropdown(this.#acSuggestions, this.#acSelectedIndex);
|
|
1561
|
+
this.#acDropdown.hidden = false;
|
|
1562
|
+
this.#textarea?.setAttribute("aria-expanded", "true");
|
|
1563
|
+
if (this.#acSelectedIndex >= 0) {
|
|
1564
|
+
this.#textarea?.setAttribute("aria-activedescendant", `ac-item-${this.#acSelectedIndex}`);
|
|
1565
|
+
} else {
|
|
1566
|
+
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1144
1567
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1568
|
+
}
|
|
1569
|
+
#scheduleStatusUpdate() {
|
|
1570
|
+
if (this.#statusTimer !== null)
|
|
1571
|
+
clearTimeout(this.#statusTimer);
|
|
1572
|
+
this.#statusTimer = setTimeout(() => {
|
|
1573
|
+
this.#statusTimer = null;
|
|
1574
|
+
this.#updateStatusBarNow();
|
|
1575
|
+
}, 100);
|
|
1576
|
+
}
|
|
1577
|
+
#updateStatusBarNow() {
|
|
1578
|
+
if (!this.#statusCount)
|
|
1579
|
+
return;
|
|
1580
|
+
const text = this.#textarea?.value ?? "";
|
|
1581
|
+
const words = countWords(text);
|
|
1582
|
+
const chars = text.length;
|
|
1583
|
+
const maxWords = this.maxWords ?? null;
|
|
1584
|
+
this.#statusCount.innerHTML = renderStatusCount(words, chars, maxWords);
|
|
1585
|
+
const isRequired = !!this.required;
|
|
1586
|
+
if (maxWords && words > maxWords) {
|
|
1587
|
+
this.#internals?.setValidity({ customError: true }, `Content exceeds ${maxWords} word limit (${words} words)`, this.#textarea ?? undefined);
|
|
1588
|
+
} else if (isRequired && text.trim() === "") {
|
|
1589
|
+
this.#internals?.setValidity({ valueMissing: true }, "Please fill in this field.", this.#textarea ?? undefined);
|
|
1590
|
+
} else {
|
|
1591
|
+
this.#internals?.setValidity({});
|
|
1147
1592
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1593
|
+
}
|
|
1594
|
+
getValue() {
|
|
1595
|
+
return this.#textarea?.value ?? "";
|
|
1596
|
+
}
|
|
1597
|
+
setValue(str) {
|
|
1598
|
+
if (this.#textarea) {
|
|
1599
|
+
this.#textarea.value = str;
|
|
1600
|
+
this.#syncFormValue();
|
|
1601
|
+
this.#scheduleHighlight();
|
|
1602
|
+
this.#updateStatusBarNow();
|
|
1603
|
+
if (this.#activeTab === "preview" && this.#previewBody) {
|
|
1604
|
+
this.#renderPreview(str);
|
|
1156
1605
|
}
|
|
1606
|
+
} else {
|
|
1607
|
+
this.value = str;
|
|
1157
1608
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1609
|
+
}
|
|
1610
|
+
insertText(str) {
|
|
1611
|
+
const ta = this.#textarea;
|
|
1612
|
+
if (!ta)
|
|
1613
|
+
return;
|
|
1614
|
+
const start = ta.selectionStart ?? ta.value.length;
|
|
1615
|
+
const end = ta.selectionEnd ?? ta.value.length;
|
|
1616
|
+
ta.value = ta.value.slice(0, start) + str + ta.value.slice(end);
|
|
1617
|
+
const newPos = start + str.length;
|
|
1618
|
+
ta.setSelectionRange(newPos, newPos);
|
|
1619
|
+
ta.dispatchEvent(new Event("input", { bubbles: false }));
|
|
1620
|
+
}
|
|
1621
|
+
setSuggestions(list) {
|
|
1622
|
+
this.#acSuggestions = list;
|
|
1623
|
+
this.#acSelectedIndex = list.length > 0 ? 0 : -1;
|
|
1624
|
+
this.#updateDropdown();
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
function escapeHtmlStr(s) {
|
|
1628
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1629
|
+
}
|
|
1177
1630
|
// src/index.ts
|
|
1178
|
-
init_element();
|
|
1179
|
-
var exports_src = {};
|
|
1180
|
-
__export(exports_src, {
|
|
1181
|
-
register: () => register,
|
|
1182
|
-
MarkdownInputHook: () => MarkdownInputHook,
|
|
1183
|
-
ElDmMarkdownInput: () => ElDmMarkdownInput
|
|
1184
|
-
});
|
|
1185
|
-
module.exports = __toCommonJS(exports_src);
|
|
1186
1631
|
function register() {
|
|
1187
1632
|
if (!customElements.get("el-dm-markdown-input")) {
|
|
1188
|
-
|
|
1189
|
-
customElements.define("el-dm-markdown-input", ElDmMarkdownInput2);
|
|
1190
|
-
});
|
|
1633
|
+
customElements.define("el-dm-markdown-input", ElDmMarkdownInput);
|
|
1191
1634
|
}
|
|
1192
1635
|
}
|
|
1193
1636
|
var MarkdownInputHook = {
|
|
@@ -1210,5 +1653,5 @@ var MarkdownInputHook = {
|
|
|
1210
1653
|
}
|
|
1211
1654
|
};
|
|
1212
1655
|
|
|
1213
|
-
//# debugId=
|
|
1656
|
+
//# debugId=6EA563DA00732E0C64756E2164756E21
|
|
1214
1657
|
//# sourceMappingURL=index.js.map
|