@duskmoon-dev/el-markdown-input 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +156 -35
- package/dist/cjs/index.js.map +5 -5
- package/dist/cjs/register.js +156 -35
- package/dist/cjs/register.js.map +5 -5
- package/dist/esm/index.js +156 -35
- package/dist/esm/index.js.map +5 -5
- package/dist/esm/register.js +156 -35
- package/dist/esm/register.js.map +5 -5
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/css.d.ts.map +1 -1
- package/dist/types/element.d.ts +6 -0
- package/dist/types/element.d.ts.map +1 -1
- package/dist/types/pairs.d.ts +17 -2
- package/dist/types/pairs.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/cjs/index.js
CHANGED
|
@@ -261,6 +261,7 @@ var elementStyles = import_el_base.css`
|
|
|
261
261
|
color: var(--md-text);
|
|
262
262
|
overflow: hidden;
|
|
263
263
|
height: inherit;
|
|
264
|
+
min-height: 12rem;
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
.editor:focus-within {
|
|
@@ -312,13 +313,18 @@ var elementStyles = import_el_base.css`
|
|
|
312
313
|
|
|
313
314
|
/* ── Write area (render-layer + textarea overlay) ──────────────────── */
|
|
314
315
|
/*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
316
|
+
* CSS grid overlay model: both .render-layer and textarea occupy the same
|
|
317
|
+
* grid cell (grid-area: 1/1), making them normal-flow siblings. The
|
|
318
|
+
* render-layer drives the cell's height; the textarea stretches to match.
|
|
319
|
+
* The write-area is the scroll container — both layers scroll together
|
|
320
|
+
* with no JS sync required. This fixes overflow when the editor has a
|
|
321
|
+
* fixed height set by the consumer.
|
|
318
322
|
*/
|
|
319
323
|
.write-area {
|
|
320
324
|
position: relative;
|
|
321
|
-
|
|
325
|
+
display: grid;
|
|
326
|
+
overflow-y: auto;
|
|
327
|
+
min-height: 0;
|
|
322
328
|
flex: 1 1 auto;
|
|
323
329
|
}
|
|
324
330
|
|
|
@@ -327,15 +333,13 @@ var elementStyles = import_el_base.css`
|
|
|
327
333
|
}
|
|
328
334
|
|
|
329
335
|
/*
|
|
330
|
-
* Render layer: highlighted HTML
|
|
331
|
-
* pointer-events: none lets clicks pass through to the textarea
|
|
336
|
+
* Render layer: highlighted HTML that drives the grid cell height.
|
|
337
|
+
* pointer-events: none lets clicks pass through to the textarea on top.
|
|
332
338
|
* Font metrics MUST match the textarea exactly for pixel-aligned overlay.
|
|
333
339
|
*/
|
|
334
340
|
.render-layer {
|
|
335
|
-
|
|
336
|
-
z-index: 1;
|
|
341
|
+
grid-area: 1 / 1;
|
|
337
342
|
pointer-events: none;
|
|
338
|
-
min-height: 12rem;
|
|
339
343
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
340
344
|
font-size: 0.875rem;
|
|
341
345
|
line-height: 1.6;
|
|
@@ -347,14 +351,13 @@ var elementStyles = import_el_base.css`
|
|
|
347
351
|
}
|
|
348
352
|
|
|
349
353
|
/*
|
|
350
|
-
* Textarea:
|
|
354
|
+
* Textarea: grid overlay on top of the render layer. Transparent text
|
|
351
355
|
* lets highlighted content show through; caret-color keeps cursor visible.
|
|
352
|
-
* overflow: hidden — the
|
|
356
|
+
* overflow: hidden — the write-area is the scroll container, not textarea.
|
|
353
357
|
*/
|
|
354
358
|
textarea {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
z-index: 2;
|
|
359
|
+
grid-area: 1 / 1;
|
|
360
|
+
z-index: 1;
|
|
358
361
|
display: block;
|
|
359
362
|
width: 100%;
|
|
360
363
|
height: 100%;
|
|
@@ -379,6 +382,17 @@ var elementStyles = import_el_base.css`
|
|
|
379
382
|
color: var(--md-text-muted);
|
|
380
383
|
}
|
|
381
384
|
|
|
385
|
+
/*
|
|
386
|
+
* Selection: keep text transparent so the render layer stays visible, but
|
|
387
|
+
* apply a semi-transparent highlight so selected regions are clearly marked.
|
|
388
|
+
* Without this rule the browser's default opaque selection background covers
|
|
389
|
+
* the render layer, making selected text appear invisible.
|
|
390
|
+
*/
|
|
391
|
+
textarea::selection {
|
|
392
|
+
color: transparent;
|
|
393
|
+
background-color: var(--md-selection-bg, color-mix(in srgb, var(--md-accent) 35%, transparent));
|
|
394
|
+
}
|
|
395
|
+
|
|
382
396
|
textarea:disabled {
|
|
383
397
|
cursor: not-allowed;
|
|
384
398
|
opacity: 0.6;
|
|
@@ -387,8 +401,7 @@ var elementStyles = import_el_base.css`
|
|
|
387
401
|
/* ── Preview panel ──────────────────────────────────────────────────── */
|
|
388
402
|
.preview-body {
|
|
389
403
|
padding: 0.75rem;
|
|
390
|
-
min-height:
|
|
391
|
-
height: stretch;
|
|
404
|
+
min-height: 0; /* allow flex item to shrink and scroll */
|
|
392
405
|
flex: 1 1 auto;
|
|
393
406
|
display: flex;
|
|
394
407
|
flex-direction: column;
|
|
@@ -401,6 +414,15 @@ var elementStyles = import_el_base.css`
|
|
|
401
414
|
display: none;
|
|
402
415
|
}
|
|
403
416
|
|
|
417
|
+
/*
|
|
418
|
+
* Markdown content children must not shrink below their natural height.
|
|
419
|
+
* Without this, flex-shrink:1 (default) compresses all children to fit
|
|
420
|
+
* the 329px container instead of letting overflow-y:auto scroll them.
|
|
421
|
+
*/
|
|
422
|
+
.preview-body > * {
|
|
423
|
+
flex-shrink: 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
404
426
|
/* ── Preview skeleton (shown while render pipeline loads) ──────────── */
|
|
405
427
|
.preview-skeleton {
|
|
406
428
|
display: flex;
|
|
@@ -554,9 +576,9 @@ var elementStyles = import_el_base.css`
|
|
|
554
576
|
.ac-dropdown {
|
|
555
577
|
position: absolute;
|
|
556
578
|
z-index: 100;
|
|
557
|
-
left
|
|
558
|
-
|
|
559
|
-
|
|
579
|
+
/* top and left are set dynamically by #updateDropdown() via #getCaretCoords() */
|
|
580
|
+
top: 0;
|
|
581
|
+
left: 0;
|
|
560
582
|
min-width: 16rem;
|
|
561
583
|
max-width: 28rem;
|
|
562
584
|
max-height: 16rem;
|
|
@@ -658,6 +680,20 @@ var elementStyles = import_el_base.css`
|
|
|
658
680
|
white-space: nowrap;
|
|
659
681
|
}
|
|
660
682
|
|
|
683
|
+
/* ── Resizable editor ──────────────────────────────────────────────── */
|
|
684
|
+
/* resize attribute mirrors the CSS resize property: vertical | horizontal | both */
|
|
685
|
+
:host([resize='vertical']) .editor {
|
|
686
|
+
resize: vertical;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
:host([resize='horizontal']) .editor {
|
|
690
|
+
resize: horizontal;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
:host([resize='both']) .editor {
|
|
694
|
+
resize: both;
|
|
695
|
+
}
|
|
696
|
+
|
|
661
697
|
/* ── Reduced motion: disable all transitions and animations ──────── */
|
|
662
698
|
@media (prefers-reduced-motion: reduce) {
|
|
663
699
|
.tab-btn,
|
|
@@ -873,6 +909,14 @@ function escapeHtml2(text) {
|
|
|
873
909
|
}
|
|
874
910
|
|
|
875
911
|
// src/pairs.ts
|
|
912
|
+
function replaceRange(ta, from, to, text, cursorStart, cursorEnd) {
|
|
913
|
+
ta.setSelectionRange(from, to);
|
|
914
|
+
const execOk = typeof document.execCommand === "function" && document.execCommand("insertText", false, text);
|
|
915
|
+
if (!execOk) {
|
|
916
|
+
ta.value = ta.value.slice(0, from) + text + ta.value.slice(to);
|
|
917
|
+
}
|
|
918
|
+
ta.setSelectionRange(cursorStart, cursorEnd ?? cursorStart);
|
|
919
|
+
}
|
|
876
920
|
function handlePairKey(ta, key) {
|
|
877
921
|
if (key !== "`")
|
|
878
922
|
return false;
|
|
@@ -881,17 +925,38 @@ function handlePairKey(ta, key) {
|
|
|
881
925
|
const value = ta.value;
|
|
882
926
|
if (start !== end) {
|
|
883
927
|
const selected = value.slice(start, end);
|
|
884
|
-
ta
|
|
885
|
-
ta.setSelectionRange(start + 1, end + 1);
|
|
928
|
+
replaceRange(ta, start, end, "`" + selected + "`", start + 1, end + 1);
|
|
886
929
|
return true;
|
|
887
930
|
}
|
|
888
931
|
if (start >= 2 && value.slice(start - 2, start) === "``") {
|
|
889
|
-
|
|
890
|
-
|
|
932
|
+
let consumeEnd = end;
|
|
933
|
+
while (consumeEnd < value.length && value[consumeEnd] === "`")
|
|
934
|
+
consumeEnd++;
|
|
935
|
+
replaceRange(ta, start, consumeEnd, "`\n\n```", start + 2);
|
|
891
936
|
return true;
|
|
892
937
|
}
|
|
893
|
-
ta
|
|
894
|
-
|
|
938
|
+
replaceRange(ta, start, end, "``", start + 1);
|
|
939
|
+
return true;
|
|
940
|
+
}
|
|
941
|
+
var INDENT = " ";
|
|
942
|
+
function handleTabKey(ta, e) {
|
|
943
|
+
if (e.key !== "Tab")
|
|
944
|
+
return false;
|
|
945
|
+
e.preventDefault();
|
|
946
|
+
const { selectionStart: start, selectionEnd: end, value } = ta;
|
|
947
|
+
if (start === end && !e.shiftKey) {
|
|
948
|
+
replaceRange(ta, start, end, INDENT, start + INDENT.length);
|
|
949
|
+
return true;
|
|
950
|
+
}
|
|
951
|
+
const lineStart = value.lastIndexOf(`
|
|
952
|
+
`, start - 1) + 1;
|
|
953
|
+
const block = value.slice(lineStart, end);
|
|
954
|
+
const transformed = e.shiftKey ? block.replace(/^ {1,2}/gm, "") : block.replace(/^/gm, INDENT);
|
|
955
|
+
const delta = transformed.length - block.length;
|
|
956
|
+
const firstLineLeading = block.split(`
|
|
957
|
+
`)[0].match(/^ */)?.[0].length ?? 0;
|
|
958
|
+
const firstLineDelta = e.shiftKey ? -Math.min(2, firstLineLeading) : INDENT.length;
|
|
959
|
+
replaceRange(ta, lineStart, end, transformed, Math.max(lineStart, start + firstLineDelta), end + delta);
|
|
895
960
|
return true;
|
|
896
961
|
}
|
|
897
962
|
function handleEnterKey(ta, e) {
|
|
@@ -909,15 +974,11 @@ function handleEnterKey(ta, e) {
|
|
|
909
974
|
return false;
|
|
910
975
|
e.preventDefault();
|
|
911
976
|
if (result.eraseCurrentLine) {
|
|
912
|
-
|
|
913
|
-
ta.value = newValue;
|
|
914
|
-
ta.setSelectionRange(lineStart, lineStart);
|
|
977
|
+
replaceRange(ta, lineStart, pos, "", lineStart);
|
|
915
978
|
} else {
|
|
916
|
-
const
|
|
917
|
-
` + result.prefix
|
|
918
|
-
|
|
919
|
-
ta.value = newValue;
|
|
920
|
-
ta.setSelectionRange(newPos, newPos);
|
|
979
|
+
const insert = `
|
|
980
|
+
` + result.prefix;
|
|
981
|
+
replaceRange(ta, pos, pos, insert, pos + insert.length);
|
|
921
982
|
}
|
|
922
983
|
return true;
|
|
923
984
|
}
|
|
@@ -979,7 +1040,8 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
979
1040
|
livePreview: { type: Boolean, reflect: true, attribute: "live-preview" },
|
|
980
1041
|
debounce: { type: Number, reflect: true, default: 300 },
|
|
981
1042
|
katexCssUrl: { type: String, reflect: true, attribute: "katex-css-url" },
|
|
982
|
-
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" }
|
|
1043
|
+
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" },
|
|
1044
|
+
resize: { type: String, reflect: true, default: "none" }
|
|
983
1045
|
};
|
|
984
1046
|
#internals;
|
|
985
1047
|
#initialized = false;
|
|
@@ -1195,6 +1257,14 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
1195
1257
|
this.#scheduleHighlight();
|
|
1196
1258
|
return;
|
|
1197
1259
|
}
|
|
1260
|
+
if (e.key === "Tab" && !e.ctrlKey && !e.metaKey) {
|
|
1261
|
+
if (handleTabKey(ta, e)) {
|
|
1262
|
+
this.#syncFormValue();
|
|
1263
|
+
this.emit("change", { value: ta.value });
|
|
1264
|
+
this.#scheduleHighlight();
|
|
1265
|
+
}
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1198
1268
|
if (e.key === "Enter" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1199
1269
|
if (handleEnterKey(ta, e)) {
|
|
1200
1270
|
this.#syncFormValue();
|
|
@@ -1565,6 +1635,57 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
1565
1635
|
} else {
|
|
1566
1636
|
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1567
1637
|
}
|
|
1638
|
+
const coords = this.#getCaretCoords();
|
|
1639
|
+
if (coords) {
|
|
1640
|
+
this.#acDropdown.style.top = `${coords.top}px`;
|
|
1641
|
+
this.#acDropdown.style.left = `${coords.left}px`;
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
#getCaretCoords() {
|
|
1645
|
+
const ta = this.#textarea;
|
|
1646
|
+
if (!ta)
|
|
1647
|
+
return null;
|
|
1648
|
+
const pos = ta.selectionStart ?? 0;
|
|
1649
|
+
const cs = getComputedStyle(ta);
|
|
1650
|
+
const taRect = ta.getBoundingClientRect();
|
|
1651
|
+
const mirror = document.createElement("div");
|
|
1652
|
+
Object.assign(mirror.style, {
|
|
1653
|
+
position: "fixed",
|
|
1654
|
+
visibility: "hidden",
|
|
1655
|
+
pointerEvents: "none",
|
|
1656
|
+
top: `${taRect.top}px`,
|
|
1657
|
+
left: `${taRect.left}px`,
|
|
1658
|
+
width: `${taRect.width}px`,
|
|
1659
|
+
font: cs.font,
|
|
1660
|
+
letterSpacing: cs.letterSpacing,
|
|
1661
|
+
paddingTop: cs.paddingTop,
|
|
1662
|
+
paddingRight: cs.paddingRight,
|
|
1663
|
+
paddingBottom: cs.paddingBottom,
|
|
1664
|
+
paddingLeft: cs.paddingLeft,
|
|
1665
|
+
borderTopWidth: cs.borderTopWidth,
|
|
1666
|
+
borderRightWidth: cs.borderRightWidth,
|
|
1667
|
+
borderBottomWidth: cs.borderBottomWidth,
|
|
1668
|
+
borderLeftWidth: cs.borderLeftWidth,
|
|
1669
|
+
boxSizing: cs.boxSizing,
|
|
1670
|
+
whiteSpace: "pre-wrap",
|
|
1671
|
+
wordBreak: "break-word",
|
|
1672
|
+
overflowWrap: cs.overflowWrap,
|
|
1673
|
+
overflow: "hidden"
|
|
1674
|
+
});
|
|
1675
|
+
const before = document.createTextNode(ta.value.substring(0, pos));
|
|
1676
|
+
const marker = document.createElement("span");
|
|
1677
|
+
marker.textContent = "";
|
|
1678
|
+
mirror.appendChild(before);
|
|
1679
|
+
mirror.appendChild(marker);
|
|
1680
|
+
document.body.appendChild(mirror);
|
|
1681
|
+
const markerRect = marker.getBoundingClientRect();
|
|
1682
|
+
document.body.removeChild(mirror);
|
|
1683
|
+
const hostRect = this.getBoundingClientRect();
|
|
1684
|
+
const lineHeight = parseFloat(cs.lineHeight) || 20;
|
|
1685
|
+
return {
|
|
1686
|
+
top: markerRect.top - hostRect.top - ta.scrollTop + lineHeight,
|
|
1687
|
+
left: Math.max(0, markerRect.left - hostRect.left)
|
|
1688
|
+
};
|
|
1568
1689
|
}
|
|
1569
1690
|
#scheduleStatusUpdate() {
|
|
1570
1691
|
if (this.#statusTimer !== null)
|
|
@@ -1653,5 +1774,5 @@ var MarkdownInputHook = {
|
|
|
1653
1774
|
}
|
|
1654
1775
|
};
|
|
1655
1776
|
|
|
1656
|
-
//# debugId=
|
|
1777
|
+
//# debugId=5C9887BADB0DFDB864756E2164756E21
|
|
1657
1778
|
//# sourceMappingURL=index.js.map
|