@duskmoon-dev/el-markdown-input 0.11.2 → 1.0.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 +3 -3
package/dist/cjs/register.js
CHANGED
|
@@ -252,6 +252,7 @@ var elementStyles = import_el_base.css`
|
|
|
252
252
|
color: var(--md-text);
|
|
253
253
|
overflow: hidden;
|
|
254
254
|
height: inherit;
|
|
255
|
+
min-height: 12rem;
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
.editor:focus-within {
|
|
@@ -303,13 +304,18 @@ var elementStyles = import_el_base.css`
|
|
|
303
304
|
|
|
304
305
|
/* ── Write area (render-layer + textarea overlay) ──────────────────── */
|
|
305
306
|
/*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
307
|
+
* CSS grid overlay model: both .render-layer and textarea occupy the same
|
|
308
|
+
* grid cell (grid-area: 1/1), making them normal-flow siblings. The
|
|
309
|
+
* render-layer drives the cell's height; the textarea stretches to match.
|
|
310
|
+
* The write-area is the scroll container — both layers scroll together
|
|
311
|
+
* with no JS sync required. This fixes overflow when the editor has a
|
|
312
|
+
* fixed height set by the consumer.
|
|
309
313
|
*/
|
|
310
314
|
.write-area {
|
|
311
315
|
position: relative;
|
|
312
|
-
|
|
316
|
+
display: grid;
|
|
317
|
+
overflow-y: auto;
|
|
318
|
+
min-height: 0;
|
|
313
319
|
flex: 1 1 auto;
|
|
314
320
|
}
|
|
315
321
|
|
|
@@ -318,15 +324,13 @@ var elementStyles = import_el_base.css`
|
|
|
318
324
|
}
|
|
319
325
|
|
|
320
326
|
/*
|
|
321
|
-
* Render layer: highlighted HTML
|
|
322
|
-
* pointer-events: none lets clicks pass through to the textarea
|
|
327
|
+
* Render layer: highlighted HTML that drives the grid cell height.
|
|
328
|
+
* pointer-events: none lets clicks pass through to the textarea on top.
|
|
323
329
|
* Font metrics MUST match the textarea exactly for pixel-aligned overlay.
|
|
324
330
|
*/
|
|
325
331
|
.render-layer {
|
|
326
|
-
|
|
327
|
-
z-index: 1;
|
|
332
|
+
grid-area: 1 / 1;
|
|
328
333
|
pointer-events: none;
|
|
329
|
-
min-height: 12rem;
|
|
330
334
|
font-family: ui-monospace, 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
331
335
|
font-size: 0.875rem;
|
|
332
336
|
line-height: 1.6;
|
|
@@ -338,14 +342,13 @@ var elementStyles = import_el_base.css`
|
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
/*
|
|
341
|
-
* Textarea:
|
|
345
|
+
* Textarea: grid overlay on top of the render layer. Transparent text
|
|
342
346
|
* lets highlighted content show through; caret-color keeps cursor visible.
|
|
343
|
-
* overflow: hidden — the
|
|
347
|
+
* overflow: hidden — the write-area is the scroll container, not textarea.
|
|
344
348
|
*/
|
|
345
349
|
textarea {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
z-index: 2;
|
|
350
|
+
grid-area: 1 / 1;
|
|
351
|
+
z-index: 1;
|
|
349
352
|
display: block;
|
|
350
353
|
width: 100%;
|
|
351
354
|
height: 100%;
|
|
@@ -370,6 +373,17 @@ var elementStyles = import_el_base.css`
|
|
|
370
373
|
color: var(--md-text-muted);
|
|
371
374
|
}
|
|
372
375
|
|
|
376
|
+
/*
|
|
377
|
+
* Selection: keep text transparent so the render layer stays visible, but
|
|
378
|
+
* apply a semi-transparent highlight so selected regions are clearly marked.
|
|
379
|
+
* Without this rule the browser's default opaque selection background covers
|
|
380
|
+
* the render layer, making selected text appear invisible.
|
|
381
|
+
*/
|
|
382
|
+
textarea::selection {
|
|
383
|
+
color: transparent;
|
|
384
|
+
background-color: var(--md-selection-bg, color-mix(in srgb, var(--md-accent) 35%, transparent));
|
|
385
|
+
}
|
|
386
|
+
|
|
373
387
|
textarea:disabled {
|
|
374
388
|
cursor: not-allowed;
|
|
375
389
|
opacity: 0.6;
|
|
@@ -378,8 +392,7 @@ var elementStyles = import_el_base.css`
|
|
|
378
392
|
/* ── Preview panel ──────────────────────────────────────────────────── */
|
|
379
393
|
.preview-body {
|
|
380
394
|
padding: 0.75rem;
|
|
381
|
-
min-height:
|
|
382
|
-
height: stretch;
|
|
395
|
+
min-height: 0; /* allow flex item to shrink and scroll */
|
|
383
396
|
flex: 1 1 auto;
|
|
384
397
|
display: flex;
|
|
385
398
|
flex-direction: column;
|
|
@@ -392,6 +405,15 @@ var elementStyles = import_el_base.css`
|
|
|
392
405
|
display: none;
|
|
393
406
|
}
|
|
394
407
|
|
|
408
|
+
/*
|
|
409
|
+
* Markdown content children must not shrink below their natural height.
|
|
410
|
+
* Without this, flex-shrink:1 (default) compresses all children to fit
|
|
411
|
+
* the 329px container instead of letting overflow-y:auto scroll them.
|
|
412
|
+
*/
|
|
413
|
+
.preview-body > * {
|
|
414
|
+
flex-shrink: 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
395
417
|
/* ── Preview skeleton (shown while render pipeline loads) ──────────── */
|
|
396
418
|
.preview-skeleton {
|
|
397
419
|
display: flex;
|
|
@@ -545,9 +567,9 @@ var elementStyles = import_el_base.css`
|
|
|
545
567
|
.ac-dropdown {
|
|
546
568
|
position: absolute;
|
|
547
569
|
z-index: 100;
|
|
548
|
-
left
|
|
549
|
-
|
|
550
|
-
|
|
570
|
+
/* top and left are set dynamically by #updateDropdown() via #getCaretCoords() */
|
|
571
|
+
top: 0;
|
|
572
|
+
left: 0;
|
|
551
573
|
min-width: 16rem;
|
|
552
574
|
max-width: 28rem;
|
|
553
575
|
max-height: 16rem;
|
|
@@ -649,6 +671,20 @@ var elementStyles = import_el_base.css`
|
|
|
649
671
|
white-space: nowrap;
|
|
650
672
|
}
|
|
651
673
|
|
|
674
|
+
/* ── Resizable editor ──────────────────────────────────────────────── */
|
|
675
|
+
/* resize attribute mirrors the CSS resize property: vertical | horizontal | both */
|
|
676
|
+
:host([resize='vertical']) .editor {
|
|
677
|
+
resize: vertical;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
:host([resize='horizontal']) .editor {
|
|
681
|
+
resize: horizontal;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
:host([resize='both']) .editor {
|
|
685
|
+
resize: both;
|
|
686
|
+
}
|
|
687
|
+
|
|
652
688
|
/* ── Reduced motion: disable all transitions and animations ──────── */
|
|
653
689
|
@media (prefers-reduced-motion: reduce) {
|
|
654
690
|
.tab-btn,
|
|
@@ -864,6 +900,14 @@ function escapeHtml2(text) {
|
|
|
864
900
|
}
|
|
865
901
|
|
|
866
902
|
// src/pairs.ts
|
|
903
|
+
function replaceRange(ta, from, to, text, cursorStart, cursorEnd) {
|
|
904
|
+
ta.setSelectionRange(from, to);
|
|
905
|
+
const execOk = typeof document.execCommand === "function" && document.execCommand("insertText", false, text);
|
|
906
|
+
if (!execOk) {
|
|
907
|
+
ta.value = ta.value.slice(0, from) + text + ta.value.slice(to);
|
|
908
|
+
}
|
|
909
|
+
ta.setSelectionRange(cursorStart, cursorEnd ?? cursorStart);
|
|
910
|
+
}
|
|
867
911
|
function handlePairKey(ta, key) {
|
|
868
912
|
if (key !== "`")
|
|
869
913
|
return false;
|
|
@@ -872,17 +916,38 @@ function handlePairKey(ta, key) {
|
|
|
872
916
|
const value = ta.value;
|
|
873
917
|
if (start !== end) {
|
|
874
918
|
const selected = value.slice(start, end);
|
|
875
|
-
ta
|
|
876
|
-
ta.setSelectionRange(start + 1, end + 1);
|
|
919
|
+
replaceRange(ta, start, end, "`" + selected + "`", start + 1, end + 1);
|
|
877
920
|
return true;
|
|
878
921
|
}
|
|
879
922
|
if (start >= 2 && value.slice(start - 2, start) === "``") {
|
|
880
|
-
|
|
881
|
-
|
|
923
|
+
let consumeEnd = end;
|
|
924
|
+
while (consumeEnd < value.length && value[consumeEnd] === "`")
|
|
925
|
+
consumeEnd++;
|
|
926
|
+
replaceRange(ta, start, consumeEnd, "`\n\n```", start + 2);
|
|
882
927
|
return true;
|
|
883
928
|
}
|
|
884
|
-
ta
|
|
885
|
-
|
|
929
|
+
replaceRange(ta, start, end, "``", start + 1);
|
|
930
|
+
return true;
|
|
931
|
+
}
|
|
932
|
+
var INDENT = " ";
|
|
933
|
+
function handleTabKey(ta, e) {
|
|
934
|
+
if (e.key !== "Tab")
|
|
935
|
+
return false;
|
|
936
|
+
e.preventDefault();
|
|
937
|
+
const { selectionStart: start, selectionEnd: end, value } = ta;
|
|
938
|
+
if (start === end && !e.shiftKey) {
|
|
939
|
+
replaceRange(ta, start, end, INDENT, start + INDENT.length);
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
const lineStart = value.lastIndexOf(`
|
|
943
|
+
`, start - 1) + 1;
|
|
944
|
+
const block = value.slice(lineStart, end);
|
|
945
|
+
const transformed = e.shiftKey ? block.replace(/^ {1,2}/gm, "") : block.replace(/^/gm, INDENT);
|
|
946
|
+
const delta = transformed.length - block.length;
|
|
947
|
+
const firstLineLeading = block.split(`
|
|
948
|
+
`)[0].match(/^ */)?.[0].length ?? 0;
|
|
949
|
+
const firstLineDelta = e.shiftKey ? -Math.min(2, firstLineLeading) : INDENT.length;
|
|
950
|
+
replaceRange(ta, lineStart, end, transformed, Math.max(lineStart, start + firstLineDelta), end + delta);
|
|
886
951
|
return true;
|
|
887
952
|
}
|
|
888
953
|
function handleEnterKey(ta, e) {
|
|
@@ -900,15 +965,11 @@ function handleEnterKey(ta, e) {
|
|
|
900
965
|
return false;
|
|
901
966
|
e.preventDefault();
|
|
902
967
|
if (result.eraseCurrentLine) {
|
|
903
|
-
|
|
904
|
-
ta.value = newValue;
|
|
905
|
-
ta.setSelectionRange(lineStart, lineStart);
|
|
968
|
+
replaceRange(ta, lineStart, pos, "", lineStart);
|
|
906
969
|
} else {
|
|
907
|
-
const
|
|
908
|
-
` + result.prefix
|
|
909
|
-
|
|
910
|
-
ta.value = newValue;
|
|
911
|
-
ta.setSelectionRange(newPos, newPos);
|
|
970
|
+
const insert = `
|
|
971
|
+
` + result.prefix;
|
|
972
|
+
replaceRange(ta, pos, pos, insert, pos + insert.length);
|
|
912
973
|
}
|
|
913
974
|
return true;
|
|
914
975
|
}
|
|
@@ -970,7 +1031,8 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
970
1031
|
livePreview: { type: Boolean, reflect: true, attribute: "live-preview" },
|
|
971
1032
|
debounce: { type: Number, reflect: true, default: 300 },
|
|
972
1033
|
katexCssUrl: { type: String, reflect: true, attribute: "katex-css-url" },
|
|
973
|
-
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" }
|
|
1034
|
+
mermaidSrc: { type: String, reflect: true, attribute: "mermaid-src" },
|
|
1035
|
+
resize: { type: String, reflect: true, default: "none" }
|
|
974
1036
|
};
|
|
975
1037
|
#internals;
|
|
976
1038
|
#initialized = false;
|
|
@@ -1186,6 +1248,14 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
1186
1248
|
this.#scheduleHighlight();
|
|
1187
1249
|
return;
|
|
1188
1250
|
}
|
|
1251
|
+
if (e.key === "Tab" && !e.ctrlKey && !e.metaKey) {
|
|
1252
|
+
if (handleTabKey(ta, e)) {
|
|
1253
|
+
this.#syncFormValue();
|
|
1254
|
+
this.emit("change", { value: ta.value });
|
|
1255
|
+
this.#scheduleHighlight();
|
|
1256
|
+
}
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1189
1259
|
if (e.key === "Enter" && !e.ctrlKey && !e.metaKey && !e.altKey) {
|
|
1190
1260
|
if (handleEnterKey(ta, e)) {
|
|
1191
1261
|
this.#syncFormValue();
|
|
@@ -1556,6 +1626,57 @@ class ElDmMarkdownInput extends import_el_base2.BaseElement {
|
|
|
1556
1626
|
} else {
|
|
1557
1627
|
this.#textarea?.removeAttribute("aria-activedescendant");
|
|
1558
1628
|
}
|
|
1629
|
+
const coords = this.#getCaretCoords();
|
|
1630
|
+
if (coords) {
|
|
1631
|
+
this.#acDropdown.style.top = `${coords.top}px`;
|
|
1632
|
+
this.#acDropdown.style.left = `${coords.left}px`;
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
#getCaretCoords() {
|
|
1636
|
+
const ta = this.#textarea;
|
|
1637
|
+
if (!ta)
|
|
1638
|
+
return null;
|
|
1639
|
+
const pos = ta.selectionStart ?? 0;
|
|
1640
|
+
const cs = getComputedStyle(ta);
|
|
1641
|
+
const taRect = ta.getBoundingClientRect();
|
|
1642
|
+
const mirror = document.createElement("div");
|
|
1643
|
+
Object.assign(mirror.style, {
|
|
1644
|
+
position: "fixed",
|
|
1645
|
+
visibility: "hidden",
|
|
1646
|
+
pointerEvents: "none",
|
|
1647
|
+
top: `${taRect.top}px`,
|
|
1648
|
+
left: `${taRect.left}px`,
|
|
1649
|
+
width: `${taRect.width}px`,
|
|
1650
|
+
font: cs.font,
|
|
1651
|
+
letterSpacing: cs.letterSpacing,
|
|
1652
|
+
paddingTop: cs.paddingTop,
|
|
1653
|
+
paddingRight: cs.paddingRight,
|
|
1654
|
+
paddingBottom: cs.paddingBottom,
|
|
1655
|
+
paddingLeft: cs.paddingLeft,
|
|
1656
|
+
borderTopWidth: cs.borderTopWidth,
|
|
1657
|
+
borderRightWidth: cs.borderRightWidth,
|
|
1658
|
+
borderBottomWidth: cs.borderBottomWidth,
|
|
1659
|
+
borderLeftWidth: cs.borderLeftWidth,
|
|
1660
|
+
boxSizing: cs.boxSizing,
|
|
1661
|
+
whiteSpace: "pre-wrap",
|
|
1662
|
+
wordBreak: "break-word",
|
|
1663
|
+
overflowWrap: cs.overflowWrap,
|
|
1664
|
+
overflow: "hidden"
|
|
1665
|
+
});
|
|
1666
|
+
const before = document.createTextNode(ta.value.substring(0, pos));
|
|
1667
|
+
const marker = document.createElement("span");
|
|
1668
|
+
marker.textContent = "";
|
|
1669
|
+
mirror.appendChild(before);
|
|
1670
|
+
mirror.appendChild(marker);
|
|
1671
|
+
document.body.appendChild(mirror);
|
|
1672
|
+
const markerRect = marker.getBoundingClientRect();
|
|
1673
|
+
document.body.removeChild(mirror);
|
|
1674
|
+
const hostRect = this.getBoundingClientRect();
|
|
1675
|
+
const lineHeight = parseFloat(cs.lineHeight) || 20;
|
|
1676
|
+
return {
|
|
1677
|
+
top: markerRect.top - hostRect.top - ta.scrollTop + lineHeight,
|
|
1678
|
+
left: Math.max(0, markerRect.left - hostRect.left)
|
|
1679
|
+
};
|
|
1559
1680
|
}
|
|
1560
1681
|
#scheduleStatusUpdate() {
|
|
1561
1682
|
if (this.#statusTimer !== null)
|
|
@@ -1624,5 +1745,5 @@ if (!customElements.get("el-dm-markdown-input")) {
|
|
|
1624
1745
|
customElements.define("el-dm-markdown-input", ElDmMarkdownInput);
|
|
1625
1746
|
}
|
|
1626
1747
|
|
|
1627
|
-
//# debugId=
|
|
1748
|
+
//# debugId=A41B9FAB725BC28A64756E2164756E21
|
|
1628
1749
|
//# sourceMappingURL=register.js.map
|