@oh-my-pi/pi-tui 18.0.0 → 18.0.3
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/CHANGELOG.md +37 -0
- package/dist/types/autocomplete.d.ts +7 -5
- package/dist/types/components/editor.d.ts +2 -4
- package/dist/types/components/image.d.ts +2 -5
- package/dist/types/components/markdown.d.ts +2 -25
- package/dist/types/components/text.d.ts +0 -1
- package/dist/types/terminal-capabilities.d.ts +10 -3
- package/dist/types/terminal.d.ts +10 -0
- package/dist/types/tui.d.ts +47 -245
- package/package.json +3 -3
- package/src/autocomplete.ts +66 -10
- package/src/components/editor.ts +137 -134
- package/src/components/image.ts +4 -6
- package/src/components/loader.ts +1 -9
- package/src/components/markdown.ts +48 -407
- package/src/components/text.ts +8 -18
- package/src/fuzzy.ts +63 -6
- package/src/stdin-buffer.ts +130 -20
- package/src/terminal-capabilities.ts +13 -2
- package/src/terminal.ts +14 -0
- package/src/tui.ts +567 -3599
|
@@ -11,12 +11,7 @@ import { latexToBlock } from "../latex-block";
|
|
|
11
11
|
import { inlineMathSpanEnd, isBareMathEnvironment, latexToUnicode } from "../latex-to-unicode";
|
|
12
12
|
import type { SymbolTheme } from "../symbols";
|
|
13
13
|
import { TERMINAL } from "../terminal-capabilities";
|
|
14
|
-
import type {
|
|
15
|
-
Component,
|
|
16
|
-
NativeScrollbackCommittedRows,
|
|
17
|
-
NativeScrollbackReplay,
|
|
18
|
-
NativeScrollbackWidthEpoch,
|
|
19
|
-
} from "../tui";
|
|
14
|
+
import type { Component } from "../tui";
|
|
20
15
|
import {
|
|
21
16
|
applyBackgroundToLine,
|
|
22
17
|
Ellipsis,
|
|
@@ -924,16 +919,11 @@ function renderedLine(text: string, literalCode?: boolean): RenderedLine {
|
|
|
924
919
|
return literalCode ? { text, literalCode: true } : { text };
|
|
925
920
|
}
|
|
926
921
|
|
|
927
|
-
|
|
928
|
-
lines: readonly string[];
|
|
929
|
-
tables: readonly RenderedTableLayout[];
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
const renderCache = new LRUCache<string, RenderCacheEntry>({
|
|
922
|
+
const renderCache = new LRUCache<string, readonly string[]>({
|
|
933
923
|
max: RENDER_CACHE_MAX,
|
|
934
924
|
maxSize: RENDER_CACHE_MAX_SIZE,
|
|
935
925
|
maxEntrySize: RENDER_CACHE_MAX_ENTRY_SIZE,
|
|
936
|
-
sizeCalculation:
|
|
926
|
+
sizeCalculation: renderedLinesCacheSize,
|
|
937
927
|
});
|
|
938
928
|
|
|
939
929
|
function renderedLinesCacheSize(lines: readonly string[]): number {
|
|
@@ -942,12 +932,6 @@ function renderedLinesCacheSize(lines: readonly string[]): number {
|
|
|
942
932
|
return Math.max(1, size);
|
|
943
933
|
}
|
|
944
934
|
|
|
945
|
-
function renderCacheEntrySize(entry: RenderCacheEntry): number {
|
|
946
|
-
let size = renderedLinesCacheSize(entry.lines);
|
|
947
|
-
for (const table of entry.tables) size += table.key.length + table.columnWidths.length + 4;
|
|
948
|
-
return size;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
935
|
// A reference-link definition (`[label]: dest`) resolves across the whole
|
|
952
936
|
// document, so a split lex cannot reproduce it — disable the streaming fast path
|
|
953
937
|
// when one is present (rare in streamed output). The label may contain
|
|
@@ -1470,7 +1454,6 @@ interface StreamPrefixLineCache extends RenderSignature {
|
|
|
1470
1454
|
text: string;
|
|
1471
1455
|
tokenCount: number;
|
|
1472
1456
|
lines: readonly string[];
|
|
1473
|
-
tables: readonly TableRenderSpec[];
|
|
1474
1457
|
}
|
|
1475
1458
|
interface StreamingHighlightCache extends RenderSignature {
|
|
1476
1459
|
lang: string | undefined;
|
|
@@ -1489,27 +1472,7 @@ function splitPushedHighlightLines(pushed: string): string[] {
|
|
|
1489
1472
|
return lines;
|
|
1490
1473
|
}
|
|
1491
1474
|
|
|
1492
|
-
|
|
1493
|
-
availableWidth: number;
|
|
1494
|
-
columnWidths: readonly number[];
|
|
1495
|
-
}
|
|
1496
|
-
|
|
1497
|
-
interface TableRenderSpec extends TableLayoutLock {
|
|
1498
|
-
key: string;
|
|
1499
|
-
lineCount: number;
|
|
1500
|
-
startRow: number;
|
|
1501
|
-
endRow: number;
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
interface RenderedTableLayout extends TableLayoutLock {
|
|
1505
|
-
key: string;
|
|
1506
|
-
startRow: number;
|
|
1507
|
-
endRow: number;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
export class Markdown
|
|
1511
|
-
implements Component, NativeScrollbackCommittedRows, NativeScrollbackReplay, NativeScrollbackWidthEpoch
|
|
1512
|
-
{
|
|
1475
|
+
export class Markdown implements Component {
|
|
1513
1476
|
#text: string;
|
|
1514
1477
|
#paddingX: number; // Left/right padding
|
|
1515
1478
|
#paddingY: number; // Top/bottom padding
|
|
@@ -1537,61 +1500,19 @@ export class Markdown
|
|
|
1537
1500
|
#streamPrefixText?: string;
|
|
1538
1501
|
#streamPrefixTokens?: Token[];
|
|
1539
1502
|
#streamPrefixLineCache?: StreamPrefixLineCache;
|
|
1540
|
-
// Rows of the most recent render() that are settled — top padding plus the
|
|
1541
|
-
// rendered frozen token prefix — exposed via getLastRenderSettledRows()
|
|
1542
|
-
// for native-scrollback commit gating.
|
|
1543
|
-
#lastRenderSettledRows = 0;
|
|
1544
|
-
// Frozen-prefix text backing the last non-zero settled exposure. Settled
|
|
1545
|
-
// rows are declared final downstream, so a render whose frozen text no
|
|
1546
|
-
// longer extends this prefix (a rewind / wholesale rewrite) resets the
|
|
1547
|
-
// exposure to 0 and re-earns it — the exposure is hard-monotone within a
|
|
1548
|
-
// text lineage.
|
|
1549
|
-
#settledExposedText?: string;
|
|
1550
|
-
// Semantic source state that produced the most recent render. Unlike #text,
|
|
1551
|
-
// it does not advance when streaming updates arrive before the next paint.
|
|
1552
|
-
#lastRenderedText?: string;
|
|
1553
|
-
#lastRenderedTransientRenderCache = false;
|
|
1554
|
-
#lastRenderedHasMutableTrailingRow = false;
|
|
1555
|
-
#widthEpochBoundaries = new WeakMap<
|
|
1556
|
-
object,
|
|
1557
|
-
{ text: string; transientRenderCache: boolean; hasMutableTrailingRow: boolean }
|
|
1558
|
-
>();
|
|
1559
|
-
|
|
1560
1503
|
// True while #renderStreamingContentLines renders the frozen token range:
|
|
1561
1504
|
// frozen code blocks highlight even in transient mode so their bytes match
|
|
1562
1505
|
// the finalized render (they render once into the prefix line cache, so
|
|
1563
1506
|
// the FFI cost is amortized). In the volatile tail, an open fence
|
|
1564
1507
|
// incrementally highlights its completed lines through a stateful
|
|
1565
1508
|
// highlight stream (falling back to per-line highlighting for diff-family
|
|
1566
|
-
// fences when the theme lacks one) so
|
|
1567
|
-
//
|
|
1568
|
-
|
|
1569
|
-
#renderingFrozenPrefix = false;
|
|
1509
|
+
// fences when the theme lacks one) so completed rows are styled immediately;
|
|
1510
|
+
// only the trailing partial line stays unhighlighted.
|
|
1511
|
+
#renderingStablePrefix = false;
|
|
1570
1512
|
#streamingHighlightCache?: StreamingHighlightCache;
|
|
1571
1513
|
#activeRenderSignature?: RenderSignature;
|
|
1572
|
-
// Streaming tables may grow naturally while wholly repaintable. Once any
|
|
1573
|
-
// physical row of a table enters native scrollback, its current column widths
|
|
1574
|
-
// are locked for the rest of this append-only text lineage: future wider cells
|
|
1575
|
-
// wrap inside those columns instead of reflowing immutable history above.
|
|
1576
|
-
#tableLayoutWidth?: number;
|
|
1577
|
-
#lockedTableLayouts = new Map<string, TableLayoutLock>();
|
|
1578
|
-
#lastRenderedTableLayouts: RenderedTableLayout[] = [];
|
|
1579
|
-
#activeTableRenderSpecs?: TableRenderSpec[];
|
|
1580
|
-
|
|
1581
1514
|
#ignoreTight = false;
|
|
1582
|
-
// Width-independent mutation counter for the width-epoch leading-stability
|
|
1583
|
-
// checks (getNativeScrollbackWidthEpochRevision): a Markdown that precedes an
|
|
1584
|
-
// epoch source (startup changelog before the transcript, a thinking block
|
|
1585
|
-
// before the streaming answer) would otherwise be validated by comparing
|
|
1586
|
-
// width-dependent row counts, which conflates reflow with mutation and fails
|
|
1587
|
-
// resolution on every width change. Bumped by every content-shape mutation.
|
|
1588
|
-
#widthEpochRevision = 0;
|
|
1589
|
-
|
|
1590
1515
|
setIgnoreTight(ignore: boolean): this {
|
|
1591
|
-
if (this.#ignoreTight !== ignore) {
|
|
1592
|
-
this.#clearTableLayouts();
|
|
1593
|
-
this.#widthEpochRevision++;
|
|
1594
|
-
}
|
|
1595
1516
|
this.#ignoreTight = ignore;
|
|
1596
1517
|
this.invalidate();
|
|
1597
1518
|
return this;
|
|
@@ -1621,7 +1542,6 @@ export class Markdown
|
|
|
1621
1542
|
// full lex + wrap runs per re-emit — one of the top CPU hotspots during
|
|
1622
1543
|
// streaming (issue #4353). Mirrors `Text.setText`'s guard.
|
|
1623
1544
|
if (text === this.#text) return false;
|
|
1624
|
-
if (!text.startsWith(this.#text)) this.#clearTableLayouts();
|
|
1625
1545
|
this.#text = text;
|
|
1626
1546
|
if (!text.trim()) {
|
|
1627
1547
|
// Blank replacement: render() early-returns before #lexTokens can see
|
|
@@ -1630,17 +1550,11 @@ export class Markdown
|
|
|
1630
1550
|
this.#streamPrefixText = undefined;
|
|
1631
1551
|
this.#streamPrefixTokens = undefined;
|
|
1632
1552
|
this.#streamPrefixLineCache = undefined;
|
|
1633
|
-
this.#settledExposedText = undefined;
|
|
1634
1553
|
}
|
|
1635
|
-
this.#widthEpochRevision++;
|
|
1636
1554
|
this.invalidate();
|
|
1637
1555
|
return true;
|
|
1638
1556
|
}
|
|
1639
1557
|
|
|
1640
|
-
getNativeScrollbackWidthEpochRevision(): number {
|
|
1641
|
-
return this.#widthEpochRevision;
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
1558
|
invalidate(): void {
|
|
1645
1559
|
this.#cachedText = undefined;
|
|
1646
1560
|
this.#cachedWidth = undefined;
|
|
@@ -1654,108 +1568,9 @@ export class Markdown
|
|
|
1654
1568
|
const next = value === true;
|
|
1655
1569
|
if (this.#transientRenderCache === next) return;
|
|
1656
1570
|
this.#transientRenderCache = next;
|
|
1657
|
-
this.#widthEpochRevision++;
|
|
1658
1571
|
this.invalidate();
|
|
1659
1572
|
}
|
|
1660
1573
|
|
|
1661
|
-
/**
|
|
1662
|
-
* Rows at the top of the most recent render() (top padding + rendered
|
|
1663
|
-
* frozen-token prefix) whose bytes are settled: byte-stable at this
|
|
1664
|
-
* width/theme for as long as the text keeps growing append-only. Hosts
|
|
1665
|
-
* feed this to transcript commit gating (see the coding agent's
|
|
1666
|
-
* `FinalizableBlock.getTranscriptBlockSettledRows`). 0 outside streaming
|
|
1667
|
-
* (`transientRenderCache`) mode, after a text rewind (re-earned on the new
|
|
1668
|
-
* lineage), and on cache-served non-streaming renders.
|
|
1669
|
-
*/
|
|
1670
|
-
getLastRenderSettledRows(): number {
|
|
1671
|
-
return this.#lastRenderSettledRows;
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
captureNativeScrollbackWidthEpoch(): unknown {
|
|
1675
|
-
if (this.#lastRenderedText === undefined) return undefined;
|
|
1676
|
-
const marker = {};
|
|
1677
|
-
this.#widthEpochBoundaries.set(marker, {
|
|
1678
|
-
text: this.#lastRenderedText,
|
|
1679
|
-
transientRenderCache: this.#lastRenderedTransientRenderCache,
|
|
1680
|
-
hasMutableTrailingRow: this.#lastRenderedHasMutableTrailingRow,
|
|
1681
|
-
});
|
|
1682
|
-
return marker;
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
resolveNativeScrollbackWidthEpoch(boundary: unknown): number | undefined {
|
|
1686
|
-
if (typeof boundary !== "object" || boundary === null || this.#cachedWidth === undefined) return undefined;
|
|
1687
|
-
const captured = this.#widthEpochBoundaries.get(boundary);
|
|
1688
|
-
if (captured === undefined) return undefined;
|
|
1689
|
-
const snapshot = new Markdown(
|
|
1690
|
-
captured.text,
|
|
1691
|
-
this.#paddingX,
|
|
1692
|
-
this.#paddingY,
|
|
1693
|
-
this.#theme,
|
|
1694
|
-
this.#defaultTextStyle,
|
|
1695
|
-
this.#codeBlockIndent,
|
|
1696
|
-
);
|
|
1697
|
-
snapshot.#ignoreTight = this.#ignoreTight;
|
|
1698
|
-
snapshot.#transientRenderCache = captured.transientRenderCache;
|
|
1699
|
-
return Math.max(
|
|
1700
|
-
0,
|
|
1701
|
-
snapshot.render(this.#cachedWidth).length - this.#paddingY - (captured.hasMutableTrailingRow ? 1 : 0),
|
|
1702
|
-
);
|
|
1703
|
-
}
|
|
1704
|
-
|
|
1705
|
-
getNativeScrollbackWidthEpochRows(): number | undefined {
|
|
1706
|
-
return this.#cachedLines === undefined ? undefined : this.#widthEpochRows(this.#cachedLines.length);
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
isNativeScrollbackWidthEpochAppendOnly(boundary: unknown): boolean {
|
|
1710
|
-
if (typeof boundary !== "object" || boundary === null) return true;
|
|
1711
|
-
return this.#widthEpochBoundaries.get(boundary)?.hasMutableTrailingRow !== true;
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
#widthEpochRows(renderedRows: number): number {
|
|
1715
|
-
return Math.max(0, renderedRows - this.#paddingY - (this.#transientRenderCache ? 1 : 0));
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
#recordLastRenderedState(hasContentRows: boolean): void {
|
|
1719
|
-
this.#lastRenderedText = this.#text;
|
|
1720
|
-
this.#lastRenderedTransientRenderCache = this.#transientRenderCache;
|
|
1721
|
-
this.#lastRenderedHasMutableTrailingRow = this.#transientRenderCache && hasContentRows;
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
/**
|
|
1725
|
-
* Freeze every table whose first physical row is already part of the native
|
|
1726
|
-
* scrollback prefix. The recorded widths came from the exact frame that was
|
|
1727
|
-
* just emitted, so the next streamed delta cannot retroactively widen it.
|
|
1728
|
-
*/
|
|
1729
|
-
setNativeScrollbackCommittedRows(rows: number): void {
|
|
1730
|
-
const committed = Number.isFinite(rows) ? Math.max(0, Math.trunc(rows)) : 0;
|
|
1731
|
-
let changed = false;
|
|
1732
|
-
for (const table of this.#lastRenderedTableLayouts) {
|
|
1733
|
-
if (table.startRow >= committed || this.#lockedTableLayouts.has(table.key)) continue;
|
|
1734
|
-
this.#lockedTableLayouts.set(table.key, {
|
|
1735
|
-
availableWidth: table.availableWidth,
|
|
1736
|
-
columnWidths: table.columnWidths.slice(),
|
|
1737
|
-
});
|
|
1738
|
-
changed = true;
|
|
1739
|
-
}
|
|
1740
|
-
if (changed) this.invalidate();
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
/** A destructive replay removes the immutable tape this layout was guarding. */
|
|
1744
|
-
prepareNativeScrollbackReplay(): void {
|
|
1745
|
-
this.#clearTableLayouts();
|
|
1746
|
-
this.#tableLayoutWidth = undefined;
|
|
1747
|
-
this.invalidate();
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
#clearTableLayouts(): void {
|
|
1751
|
-
this.#lockedTableLayouts.clear();
|
|
1752
|
-
this.#lastRenderedTableLayouts = [];
|
|
1753
|
-
this.#activeTableRenderSpecs = undefined;
|
|
1754
|
-
// Same-width replay/non-append rewrites could otherwise reuse physical
|
|
1755
|
-
// prefix lines rendered with the retired locked widths.
|
|
1756
|
-
this.#streamPrefixLineCache = undefined;
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
1574
|
// Lex `text` into block tokens, reusing the frozen stable prefix when the text
|
|
1760
1575
|
// only grew (the streaming path). Falls back to a full lex whenever the prefix
|
|
1761
1576
|
// is no longer a prefix (non-append edit), the text carries reference-link
|
|
@@ -1763,17 +1578,19 @@ export class Markdown
|
|
|
1763
1578
|
// raw-span offsets). Every fallback is correctness-preserving — only speed
|
|
1764
1579
|
// differs; the render loop sees the identical token list either way.
|
|
1765
1580
|
#lexTokens(text: string): Token[] {
|
|
1766
|
-
|
|
1581
|
+
// When a frozen prefix exists, it was already verified ref-def-free when
|
|
1582
|
+
// frozen (#freezeStablePrefix only runs when canStream was true). The prefix
|
|
1583
|
+
// ends at a "\n\n" block boundary (stableBlockBoundary), so the tail starts
|
|
1584
|
+
// at a fresh line — scanning only the tail for ref defs is sufficient and
|
|
1585
|
+
// avoids re-scanning the growing prefix every frame (O(n²) → O(n) overall).
|
|
1767
1586
|
const prefix = this.#streamPrefixText;
|
|
1768
1587
|
const prefixTokens = this.#streamPrefixTokens;
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
) {
|
|
1776
|
-
const tailTokens = lexDocument(text.slice(prefix.length));
|
|
1588
|
+
const hasPrefix =
|
|
1589
|
+
prefix !== undefined && prefixTokens !== undefined && text.length > prefix.length && text.startsWith(prefix);
|
|
1590
|
+
const refDefText = hasPrefix ? text.slice(prefix.length) : text;
|
|
1591
|
+
const canStream = !HAS_REF_DEF.test(refDefText) && !refDefText.includes("\r");
|
|
1592
|
+
if (canStream && hasPrefix) {
|
|
1593
|
+
const tailTokens = lexDocument(refDefText);
|
|
1777
1594
|
const tokens = [...prefixTokens, ...tailTokens];
|
|
1778
1595
|
this.#freezeStablePrefix(text, tokens, { preserveExisting: true });
|
|
1779
1596
|
return tokens;
|
|
@@ -1810,24 +1627,14 @@ export class Markdown
|
|
|
1810
1627
|
}
|
|
1811
1628
|
|
|
1812
1629
|
render(width: number): readonly string[] {
|
|
1813
|
-
if (this.#tableLayoutWidth !== undefined && this.#tableLayoutWidth !== width) {
|
|
1814
|
-
this.#clearTableLayouts();
|
|
1815
|
-
this.invalidate();
|
|
1816
|
-
}
|
|
1817
|
-
this.#tableLayoutWidth = width;
|
|
1818
1630
|
// L1: per-instance cache — fastest path for repeated renders of the same
|
|
1819
1631
|
// instance at the same width (e.g. resize debounce, repeated redraws).
|
|
1820
1632
|
// Returning the cached reference is load-bearing: parents memoize their
|
|
1821
1633
|
// concatenation on reference equality.
|
|
1822
1634
|
if (this.#cachedLines && this.#cachedText === this.#text && this.#cachedWidth === width) {
|
|
1823
|
-
this.#recordLastRenderedState(this.#cachedLines.length > 0);
|
|
1824
1635
|
return this.#cachedLines;
|
|
1825
1636
|
}
|
|
1826
1637
|
|
|
1827
|
-
// Recomputed below by the streaming path; every other path (cache-served,
|
|
1828
|
-
// empty text, non-streaming full render) exposes no settled rows.
|
|
1829
|
-
this.#lastRenderSettledRows = 0;
|
|
1830
|
-
|
|
1831
1638
|
// Calculate available width for content (subtract horizontal padding)
|
|
1832
1639
|
const paddingX = this.#ignoreTight ? this.#paddingX : getPaddingX(this.#paddingX);
|
|
1833
1640
|
const contentWidth = Math.max(1, width - paddingX * 2);
|
|
@@ -1837,7 +1644,6 @@ export class Markdown
|
|
|
1837
1644
|
this.#cachedText = this.#text;
|
|
1838
1645
|
this.#cachedWidth = width;
|
|
1839
1646
|
this.#cachedLines = EMPTY_RENDER_LINES;
|
|
1840
|
-
this.#recordLastRenderedState(false);
|
|
1841
1647
|
return EMPTY_RENDER_LINES;
|
|
1842
1648
|
}
|
|
1843
1649
|
|
|
@@ -1860,41 +1666,29 @@ export class Markdown
|
|
|
1860
1666
|
// theme.heading is used as the representative theme probe — it's required
|
|
1861
1667
|
// by MarkdownTheme and is one of the most styling-sensitive entries.
|
|
1862
1668
|
let cacheKey: string | undefined;
|
|
1863
|
-
if (!this.transientRenderCache
|
|
1669
|
+
if (!this.transientRenderCache) {
|
|
1864
1670
|
cacheKey = this.#renderCacheKey(normalizedText, signature);
|
|
1865
1671
|
const cached = renderCache.get(cacheKey);
|
|
1866
1672
|
if (cached !== undefined) {
|
|
1867
|
-
// Restore both the rendered rows and the geometry metadata that produced
|
|
1868
|
-
// them. A later scrollback publication must never lock widths from an
|
|
1869
|
-
// older transient frame against rows served from this cache entry.
|
|
1870
|
-
this.#lastRenderedTableLayouts = cached.tables.map(table => ({
|
|
1871
|
-
...table,
|
|
1872
|
-
columnWidths: table.columnWidths.slice(),
|
|
1873
|
-
}));
|
|
1874
1673
|
// Populate L1 so subsequent calls from this instance are O(1) map lookup.
|
|
1875
1674
|
this.#cachedText = this.#text;
|
|
1876
1675
|
this.#cachedWidth = width;
|
|
1877
|
-
this.#cachedLines = cached
|
|
1878
|
-
|
|
1879
|
-
return cached.lines;
|
|
1676
|
+
this.#cachedLines = cached;
|
|
1677
|
+
return cached;
|
|
1880
1678
|
}
|
|
1881
1679
|
}
|
|
1882
1680
|
|
|
1883
1681
|
// Parse markdown to HTML-like tokens
|
|
1884
1682
|
const tokens = this.#lexTokens(normalizedText);
|
|
1885
1683
|
let contentLines: string[];
|
|
1886
|
-
const tableRenderSpecs: TableRenderSpec[] = [];
|
|
1887
|
-
this.#activeTableRenderSpecs = tableRenderSpecs;
|
|
1888
1684
|
this.#activeRenderSignature = signature;
|
|
1889
1685
|
try {
|
|
1890
1686
|
contentLines = this.transientRenderCache
|
|
1891
1687
|
? this.#renderStreamingContentLines(tokens, normalizedText, signature, contentWidth)
|
|
1892
|
-
: this.#renderContentLines(tokens, 0, tokens.length, contentWidth, signature
|
|
1688
|
+
: this.#renderContentLines(tokens, 0, tokens.length, contentWidth, signature);
|
|
1893
1689
|
} finally {
|
|
1894
1690
|
this.#activeRenderSignature = undefined;
|
|
1895
|
-
this.#activeTableRenderSpecs = undefined;
|
|
1896
1691
|
}
|
|
1897
|
-
this.#lastRenderedTableLayouts = this.#resolveRenderedTableLayouts(tableRenderSpecs, signature.paddingY);
|
|
1898
1692
|
const emptyLines = this.#renderEmptyPaddingLines(signature);
|
|
1899
1693
|
|
|
1900
1694
|
// Combine top padding, content, and bottom padding
|
|
@@ -1911,16 +1705,8 @@ export class Markdown
|
|
|
1911
1705
|
// Update L2 module-level LRU so future instances with the same key skip
|
|
1912
1706
|
// the marked.lexer + highlightCode (Rust FFI) work entirely.
|
|
1913
1707
|
if (cacheKey !== undefined) {
|
|
1914
|
-
renderCache.set(cacheKey,
|
|
1915
|
-
lines: result,
|
|
1916
|
-
tables: this.#lastRenderedTableLayouts.map(table => ({
|
|
1917
|
-
...table,
|
|
1918
|
-
columnWidths: table.columnWidths.slice(),
|
|
1919
|
-
})),
|
|
1920
|
-
});
|
|
1708
|
+
renderCache.set(cacheKey, result);
|
|
1921
1709
|
}
|
|
1922
|
-
this.#recordLastRenderedState(contentLines.length > 0);
|
|
1923
|
-
|
|
1924
1710
|
return result;
|
|
1925
1711
|
}
|
|
1926
1712
|
|
|
@@ -1952,78 +1738,43 @@ export class Markdown
|
|
|
1952
1738
|
signature: RenderSignature,
|
|
1953
1739
|
contentWidth: number,
|
|
1954
1740
|
): string[] {
|
|
1955
|
-
const
|
|
1956
|
-
const
|
|
1957
|
-
if (
|
|
1958
|
-
return this.#renderContentLines(tokens, 0, tokens.length, contentWidth, signature
|
|
1741
|
+
const stableText = this.#streamPrefixText;
|
|
1742
|
+
const stableTokenCount = this.#streamPrefixTokens?.length ?? 0;
|
|
1743
|
+
if (stableText === undefined || stableTokenCount === 0 || !normalizedText.startsWith(stableText)) {
|
|
1744
|
+
return this.#renderContentLines(tokens, 0, tokens.length, contentWidth, signature);
|
|
1959
1745
|
}
|
|
1960
1746
|
|
|
1961
1747
|
const contentLines: string[] = [];
|
|
1962
|
-
const reusablePrefix = this.#matchingStreamPrefixLineCache(normalizedText,
|
|
1748
|
+
const reusablePrefix = this.#matchingStreamPrefixLineCache(normalizedText, stableText, signature);
|
|
1963
1749
|
let renderedUntil = 0;
|
|
1964
|
-
|
|
1965
|
-
if (reusablePrefix && reusablePrefix.tokenCount <= frozenTokenCount) {
|
|
1750
|
+
if (reusablePrefix && reusablePrefix.tokenCount <= stableTokenCount) {
|
|
1966
1751
|
contentLines.push(...reusablePrefix.lines);
|
|
1967
|
-
this.#activeTableRenderSpecs?.push(...reusablePrefix.tables);
|
|
1968
1752
|
renderedUntil = reusablePrefix.tokenCount;
|
|
1969
|
-
renderedSourceOffset = reusablePrefix.text.length;
|
|
1970
1753
|
}
|
|
1971
1754
|
|
|
1972
|
-
if (renderedUntil <
|
|
1973
|
-
//
|
|
1755
|
+
if (renderedUntil < stableTokenCount) {
|
|
1756
|
+
// Stable tokens render with full fidelity (syntax highlighting on)
|
|
1974
1757
|
// so these cached rows byte-match the finalized render.
|
|
1975
|
-
this.#
|
|
1758
|
+
this.#renderingStablePrefix = true;
|
|
1976
1759
|
try {
|
|
1977
1760
|
contentLines.push(
|
|
1978
|
-
...this.#renderContentLines(
|
|
1979
|
-
tokens,
|
|
1980
|
-
renderedUntil,
|
|
1981
|
-
frozenTokenCount,
|
|
1982
|
-
contentWidth,
|
|
1983
|
-
signature,
|
|
1984
|
-
contentLines.length,
|
|
1985
|
-
renderedSourceOffset,
|
|
1986
|
-
),
|
|
1761
|
+
...this.#renderContentLines(tokens, renderedUntil, stableTokenCount, contentWidth, signature),
|
|
1987
1762
|
);
|
|
1988
1763
|
} finally {
|
|
1989
|
-
this.#
|
|
1764
|
+
this.#renderingStablePrefix = false;
|
|
1990
1765
|
}
|
|
1991
|
-
renderedUntil =
|
|
1766
|
+
renderedUntil = stableTokenCount;
|
|
1992
1767
|
}
|
|
1993
1768
|
|
|
1994
1769
|
this.#streamPrefixLineCache = {
|
|
1995
1770
|
...signature,
|
|
1996
|
-
text:
|
|
1997
|
-
tokenCount:
|
|
1771
|
+
text: stableText,
|
|
1772
|
+
tokenCount: stableTokenCount,
|
|
1998
1773
|
lines: contentLines.slice(),
|
|
1999
|
-
tables: this.#activeTableRenderSpecs?.slice() ?? [],
|
|
2000
1774
|
};
|
|
2001
1775
|
|
|
2002
|
-
// Settled exposure (hard-monotone): these rows are declared final to
|
|
2003
|
-
// the host, so expose them only while the frozen text still extends
|
|
2004
|
-
// the previously exposed prefix; a rewind resets to 0 and re-earns on
|
|
2005
|
-
// the rewritten lineage.
|
|
2006
|
-
if (contentLines.length > 0) {
|
|
2007
|
-
if (this.#settledExposedText === undefined || frozenText.startsWith(this.#settledExposedText)) {
|
|
2008
|
-
this.#settledExposedText = frozenText;
|
|
2009
|
-
this.#lastRenderSettledRows = signature.paddingY + contentLines.length;
|
|
2010
|
-
} else {
|
|
2011
|
-
this.#settledExposedText = undefined;
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
1776
|
if (renderedUntil < tokens.length) {
|
|
2016
|
-
contentLines.push(
|
|
2017
|
-
...this.#renderContentLines(
|
|
2018
|
-
tokens,
|
|
2019
|
-
renderedUntil,
|
|
2020
|
-
tokens.length,
|
|
2021
|
-
contentWidth,
|
|
2022
|
-
signature,
|
|
2023
|
-
contentLines.length,
|
|
2024
|
-
frozenText.length,
|
|
2025
|
-
),
|
|
2026
|
-
);
|
|
1777
|
+
contentLines.push(...this.#renderContentLines(tokens, renderedUntil, tokens.length, contentWidth, signature));
|
|
2027
1778
|
}
|
|
2028
1779
|
|
|
2029
1780
|
return contentLines;
|
|
@@ -2031,12 +1782,12 @@ export class Markdown
|
|
|
2031
1782
|
|
|
2032
1783
|
#matchingStreamPrefixLineCache(
|
|
2033
1784
|
normalizedText: string,
|
|
2034
|
-
|
|
1785
|
+
stableText: string,
|
|
2035
1786
|
signature: RenderSignature,
|
|
2036
1787
|
): StreamPrefixLineCache | undefined {
|
|
2037
1788
|
const cache = this.#streamPrefixLineCache;
|
|
2038
1789
|
if (!cache) return undefined;
|
|
2039
|
-
if (!normalizedText.startsWith(cache.text) || !
|
|
1790
|
+
if (!normalizedText.startsWith(cache.text) || !stableText.startsWith(cache.text)) return undefined;
|
|
2040
1791
|
if (cache.width !== signature.width) return undefined;
|
|
2041
1792
|
if (cache.paddingX !== signature.paddingX) return undefined;
|
|
2042
1793
|
if (cache.paddingY !== signature.paddingY) return undefined;
|
|
@@ -2057,25 +1808,12 @@ export class Markdown
|
|
|
2057
1808
|
end: number,
|
|
2058
1809
|
contentWidth: number,
|
|
2059
1810
|
signature: RenderSignature,
|
|
2060
|
-
rowOffset: number,
|
|
2061
|
-
startingSourceOffset: number,
|
|
2062
1811
|
): string[] {
|
|
2063
1812
|
const wrappedLines: RenderedLine[] = [];
|
|
2064
|
-
let sourceOffset = startingSourceOffset;
|
|
2065
1813
|
for (let i = start; i < end; i++) {
|
|
2066
1814
|
const token = tokens[i];
|
|
2067
1815
|
const nextToken = tokens[i + 1];
|
|
2068
|
-
const
|
|
2069
|
-
const tokenWrappedRowStart = wrappedLines.length;
|
|
2070
|
-
const tokenRowStart = rowOffset + tokenWrappedRowStart;
|
|
2071
|
-
const renderedTokenLines = this.#renderToken(
|
|
2072
|
-
token,
|
|
2073
|
-
contentWidth,
|
|
2074
|
-
nextToken?.type,
|
|
2075
|
-
undefined,
|
|
2076
|
-
`offset:${sourceOffset}`,
|
|
2077
|
-
);
|
|
2078
|
-
const tokenLineOffsets = [0];
|
|
1816
|
+
const renderedTokenLines = this.#renderToken(token, contentWidth, nextToken?.type);
|
|
2079
1817
|
for (const renderedRow of renderedTokenLines) {
|
|
2080
1818
|
// Lists wrap while their structural prefixes are still available, so
|
|
2081
1819
|
// continuation rows retain the correct hanging indent. Re-wrapping the
|
|
@@ -2092,30 +1830,7 @@ export class Markdown
|
|
|
2092
1830
|
}
|
|
2093
1831
|
}
|
|
2094
1832
|
}
|
|
2095
|
-
tokenLineOffsets.push(wrappedLines.length - tokenWrappedRowStart);
|
|
2096
1833
|
}
|
|
2097
|
-
const tableSpecs = this.#activeTableRenderSpecs;
|
|
2098
|
-
if (tableSpecs !== undefined) {
|
|
2099
|
-
for (let specIndex = tableSpecStart; specIndex < tableSpecs.length; specIndex++) {
|
|
2100
|
-
const spec = tableSpecs[specIndex]!;
|
|
2101
|
-
let relativeStart: number;
|
|
2102
|
-
let relativeEnd: number;
|
|
2103
|
-
if (token.type === "table") {
|
|
2104
|
-
// Exclude the optional inter-block blank from a top-level table's span.
|
|
2105
|
-
relativeStart = 0;
|
|
2106
|
-
relativeEnd = Math.min(renderedTokenLines.length, spec.lineCount);
|
|
2107
|
-
} else {
|
|
2108
|
-
// Container renderers express nested table spans relative to their
|
|
2109
|
-
// returned lines. Preserve that exact span through this final wrap.
|
|
2110
|
-
if (spec.startRow < 0 || spec.endRow <= spec.startRow) continue;
|
|
2111
|
-
relativeStart = Math.min(renderedTokenLines.length, spec.startRow);
|
|
2112
|
-
relativeEnd = Math.min(renderedTokenLines.length, spec.endRow);
|
|
2113
|
-
}
|
|
2114
|
-
spec.startRow = tokenRowStart + tokenLineOffsets[relativeStart]!;
|
|
2115
|
-
spec.endRow = tokenRowStart + tokenLineOffsets[relativeEnd]!;
|
|
2116
|
-
}
|
|
2117
|
-
}
|
|
2118
|
-
sourceOffset += token.raw.length;
|
|
2119
1834
|
}
|
|
2120
1835
|
|
|
2121
1836
|
const leftMargin = padding(signature.paddingX);
|
|
@@ -2164,21 +1879,6 @@ export class Markdown
|
|
|
2164
1879
|
return contentLines;
|
|
2165
1880
|
}
|
|
2166
1881
|
|
|
2167
|
-
#resolveRenderedTableLayouts(specs: readonly TableRenderSpec[], topPadding: number): RenderedTableLayout[] {
|
|
2168
|
-
const layouts: RenderedTableLayout[] = [];
|
|
2169
|
-
for (const spec of specs) {
|
|
2170
|
-
if (spec.startRow < 0 || spec.endRow <= spec.startRow) continue;
|
|
2171
|
-
layouts.push({
|
|
2172
|
-
key: spec.key,
|
|
2173
|
-
availableWidth: spec.availableWidth,
|
|
2174
|
-
columnWidths: spec.columnWidths.slice(),
|
|
2175
|
-
startRow: topPadding + spec.startRow,
|
|
2176
|
-
endRow: topPadding + spec.endRow,
|
|
2177
|
-
});
|
|
2178
|
-
}
|
|
2179
|
-
return layouts;
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
1882
|
#renderCodeBodyLines(token: Token, codeIndent: string): RenderedLine[] {
|
|
2183
1883
|
const literalCode = this.#codeBlockIndent === 0;
|
|
2184
1884
|
const bodyLines: RenderedLine[] = [];
|
|
@@ -2188,11 +1888,11 @@ export class Markdown
|
|
|
2188
1888
|
bodyLines.push(renderedLine(literalCode ? line : codeIndent + line, literalCode));
|
|
2189
1889
|
};
|
|
2190
1890
|
|
|
2191
|
-
const streaming = this.transientRenderCache && !this.#
|
|
1891
|
+
const streaming = this.transientRenderCache && !this.#renderingStablePrefix;
|
|
2192
1892
|
if (this.#theme.highlightCode && (!streaming || this.#codeTokenHasClosingFence(token))) {
|
|
2193
1893
|
// Finalized content — or a fence that closed mid-stream, which
|
|
2194
1894
|
// highlights through the same whole-block call the finalized render
|
|
2195
|
-
// uses so
|
|
1895
|
+
// uses so cached stable rows byte-match it.
|
|
2196
1896
|
const highlightedLines = this.#theme.highlightCode(tokenText, lang);
|
|
2197
1897
|
for (const hlLine of highlightedLines) {
|
|
2198
1898
|
addBodyLine(hlLine);
|
|
@@ -2202,8 +1902,8 @@ export class Markdown
|
|
|
2202
1902
|
|
|
2203
1903
|
if (streaming && this.#theme.highlightCode) {
|
|
2204
1904
|
// Open fence: highlight completed lines incrementally so semantic
|
|
2205
|
-
// colors reach
|
|
2206
|
-
//
|
|
1905
|
+
// colors reach completed rows immediately; only the trailing partial
|
|
1906
|
+
// line stays unhighlighted.
|
|
2207
1907
|
const lineEnd = tokenText.lastIndexOf("\n");
|
|
2208
1908
|
const completedLines = lineEnd >= 0 ? this.#highlightStreamingLines(tokenText.slice(0, lineEnd), lang) : null;
|
|
2209
1909
|
if (completedLines) {
|
|
@@ -2426,7 +2126,6 @@ export class Markdown
|
|
|
2426
2126
|
width: number,
|
|
2427
2127
|
nextTokenType?: string,
|
|
2428
2128
|
styleContext?: InlineStyleContext,
|
|
2429
|
-
tokenKey = "root",
|
|
2430
2129
|
): RenderedLine[] {
|
|
2431
2130
|
const lines: RenderedLine[] = [];
|
|
2432
2131
|
|
|
@@ -2536,7 +2235,7 @@ export class Markdown
|
|
|
2536
2235
|
}
|
|
2537
2236
|
|
|
2538
2237
|
case "table": {
|
|
2539
|
-
const tableLines = this.#renderTable(token as TableToken, width, nextTokenType, styleContext
|
|
2238
|
+
const tableLines = this.#renderTable(token as TableToken, width, nextTokenType, styleContext);
|
|
2540
2239
|
for (const tableLine of tableLines) lines.push(renderedLine(tableLine));
|
|
2541
2240
|
break;
|
|
2542
2241
|
}
|
|
@@ -2549,58 +2248,23 @@ export class Markdown
|
|
|
2549
2248
|
const quoteContentWidth = Math.max(1, width - 2);
|
|
2550
2249
|
const quoteTokens = token.tokens || [];
|
|
2551
2250
|
const renderedQuoteLines: RenderedLine[] = [];
|
|
2552
|
-
const blockquoteSpecStart = this.#activeTableRenderSpecs?.length ?? 0;
|
|
2553
|
-
|
|
2554
2251
|
for (let i = 0; i < quoteTokens.length; i++) {
|
|
2555
2252
|
const quoteToken = quoteTokens[i];
|
|
2556
2253
|
const nextQuoteToken = quoteTokens[i + 1];
|
|
2557
|
-
const quoteTokenRowStart = renderedQuoteLines.length;
|
|
2558
|
-
const quoteSpecStart = this.#activeTableRenderSpecs?.length ?? 0;
|
|
2559
2254
|
const quoteTokenLines = this.#renderToken(
|
|
2560
2255
|
quoteToken,
|
|
2561
2256
|
quoteContentWidth,
|
|
2562
2257
|
nextQuoteToken?.type,
|
|
2563
2258
|
quoteInlineStyleContext,
|
|
2564
|
-
`${tokenKey}/quote:${i}`,
|
|
2565
2259
|
);
|
|
2566
2260
|
renderedQuoteLines.push(...quoteTokenLines);
|
|
2567
|
-
|
|
2568
|
-
const tableSpecs = this.#activeTableRenderSpecs;
|
|
2569
|
-
if (tableSpecs !== undefined) {
|
|
2570
|
-
for (let specIndex = quoteSpecStart; specIndex < tableSpecs.length; specIndex++) {
|
|
2571
|
-
const spec = tableSpecs[specIndex]!;
|
|
2572
|
-
if (spec.startRow < 0) {
|
|
2573
|
-
// Direct child tables initially have no row coordinates. Their
|
|
2574
|
-
// structural line count excludes any inter-block blank.
|
|
2575
|
-
spec.startRow = quoteTokenRowStart;
|
|
2576
|
-
spec.endRow = quoteTokenRowStart + Math.min(quoteTokenLines.length, spec.lineCount);
|
|
2577
|
-
} else {
|
|
2578
|
-
// A nested blockquote already mapped the table into its own
|
|
2579
|
-
// returned rows; translate those rows into this quote's input.
|
|
2580
|
-
spec.startRow += quoteTokenRowStart;
|
|
2581
|
-
spec.endRow += quoteTokenRowStart;
|
|
2582
|
-
}
|
|
2583
|
-
}
|
|
2584
|
-
}
|
|
2585
2261
|
}
|
|
2586
2262
|
|
|
2587
2263
|
while (renderedQuoteLines.length > 0 && renderedQuoteLines[renderedQuoteLines.length - 1]!.text === "") {
|
|
2588
2264
|
renderedQuoteLines.pop();
|
|
2589
2265
|
}
|
|
2590
2266
|
|
|
2591
|
-
const
|
|
2592
|
-
const borderedQuoteLines = this.#applyQuoteBorder(renderedQuoteLines, width, quoteRowOffsets);
|
|
2593
|
-
const tableSpecs = this.#activeTableRenderSpecs;
|
|
2594
|
-
if (tableSpecs !== undefined) {
|
|
2595
|
-
for (let specIndex = blockquoteSpecStart; specIndex < tableSpecs.length; specIndex++) {
|
|
2596
|
-
const spec = tableSpecs[specIndex]!;
|
|
2597
|
-
if (spec.startRow < 0 || spec.endRow <= spec.startRow) continue;
|
|
2598
|
-
const relativeStart = Math.min(renderedQuoteLines.length, spec.startRow);
|
|
2599
|
-
const relativeEnd = Math.min(renderedQuoteLines.length, spec.endRow);
|
|
2600
|
-
spec.startRow = quoteRowOffsets[relativeStart]!;
|
|
2601
|
-
spec.endRow = quoteRowOffsets[relativeEnd]!;
|
|
2602
|
-
}
|
|
2603
|
-
}
|
|
2267
|
+
const borderedQuoteLines = this.#applyQuoteBorder(renderedQuoteLines, width);
|
|
2604
2268
|
lines.push(...borderedQuoteLines);
|
|
2605
2269
|
if (nextTokenType && nextTokenType !== "space") {
|
|
2606
2270
|
lines.push(renderedLine("")); // Add spacing after blockquotes (unless space token follows)
|
|
@@ -2648,7 +2312,7 @@ export class Markdown
|
|
|
2648
2312
|
* Wrap already-rendered lines in the blockquote border and quote styling.
|
|
2649
2313
|
* `width` is the full content width; the border reserves two cells.
|
|
2650
2314
|
*/
|
|
2651
|
-
#applyQuoteBorder(renderedLines: RenderedLine[], width: number
|
|
2315
|
+
#applyQuoteBorder(renderedLines: RenderedLine[], width: number): RenderedLine[] {
|
|
2652
2316
|
const quoteStyle = (text: string) => this.#theme.quote(this.#theme.italic(text));
|
|
2653
2317
|
const quoteStylePrefix = this.#getStylePrefix(quoteStyle);
|
|
2654
2318
|
const applyQuoteStyle = (line: string): string => {
|
|
@@ -2660,7 +2324,6 @@ export class Markdown
|
|
|
2660
2324
|
};
|
|
2661
2325
|
const quoteContentWidth = Math.max(1, width - 2);
|
|
2662
2326
|
const lines: RenderedLine[] = [];
|
|
2663
|
-
sourceRowOffsets?.push(0);
|
|
2664
2327
|
for (const quoteLine of renderedLines) {
|
|
2665
2328
|
if (quoteLine.literalCode) {
|
|
2666
2329
|
const wrappedLiteralRows = wrapTextWithAnsi(quoteLine.text, quoteContentWidth);
|
|
@@ -2677,7 +2340,6 @@ export class Markdown
|
|
|
2677
2340
|
lines.push(renderedLine(this.#theme.quoteBorder(`${this.#theme.symbols.quoteBorder} `) + wrappedLine));
|
|
2678
2341
|
}
|
|
2679
2342
|
}
|
|
2680
|
-
sourceRowOffsets?.push(lines.length);
|
|
2681
2343
|
}
|
|
2682
2344
|
return lines;
|
|
2683
2345
|
}
|
|
@@ -3051,7 +2713,6 @@ export class Markdown
|
|
|
3051
2713
|
availableWidth: number,
|
|
3052
2714
|
nextTokenType?: string,
|
|
3053
2715
|
styleContext?: InlineStyleContext,
|
|
3054
|
-
tableKey = "table",
|
|
3055
2716
|
): string[] {
|
|
3056
2717
|
const lines: string[] = [];
|
|
3057
2718
|
const numCols = token.header.length;
|
|
@@ -3166,17 +2827,6 @@ export class Markdown
|
|
|
3166
2827
|
}
|
|
3167
2828
|
}
|
|
3168
2829
|
|
|
3169
|
-
const lockedLayout = this.#lockedTableLayouts.get(tableKey);
|
|
3170
|
-
if (
|
|
3171
|
-
lockedLayout !== undefined &&
|
|
3172
|
-
lockedLayout.availableWidth === availableWidth &&
|
|
3173
|
-
lockedLayout.columnWidths.length === numCols &&
|
|
3174
|
-
lockedLayout.columnWidths.every(width => Number.isFinite(width) && width >= 1) &&
|
|
3175
|
-
lockedLayout.columnWidths.reduce((total, width) => total + width, borderOverhead) <= availableWidth
|
|
3176
|
-
) {
|
|
3177
|
-
columnWidths = lockedLayout.columnWidths.slice();
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
2830
|
const t = this.#theme.symbols.table;
|
|
3181
2831
|
const h = t.horizontal;
|
|
3182
2832
|
const v = t.vertical;
|
|
@@ -3232,15 +2882,6 @@ export class Markdown
|
|
|
3232
2882
|
const bottomBorderCells = columnWidths.map(w => h.repeat(w));
|
|
3233
2883
|
const bottomBorder = `${t.bottomLeft}${h}${bottomBorderCells.join(`${h}${t.teeUp}${h}`)}${h}${t.bottomRight}`;
|
|
3234
2884
|
lines.push(bottomBorder);
|
|
3235
|
-
this.#activeTableRenderSpecs?.push({
|
|
3236
|
-
key: tableKey,
|
|
3237
|
-
availableWidth,
|
|
3238
|
-
columnWidths: columnWidths.slice(),
|
|
3239
|
-
lineCount: lines.length,
|
|
3240
|
-
startRow: -1,
|
|
3241
|
-
endRow: -1,
|
|
3242
|
-
});
|
|
3243
|
-
|
|
3244
2885
|
if (nextTokenType && nextTokenType !== "space") {
|
|
3245
2886
|
lines.push(""); // Add spacing after table
|
|
3246
2887
|
}
|