@greppleai/grip-tui 0.1.4 → 0.1.6
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/autocomplete.d.ts +3 -1
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +16 -8
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/alt-screen-flash.d.ts.map +1 -1
- package/dist/components/alt-screen-flash.js +2 -3
- package/dist/components/alt-screen-flash.js.map +1 -1
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +1 -6
- package/dist/components/box.js.map +1 -1
- package/dist/components/cancellable-loader.d.ts.map +1 -1
- package/dist/components/cancellable-loader.js +4 -3
- package/dist/components/cancellable-loader.js.map +1 -1
- package/dist/components/editor.d.ts +1 -0
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +55 -54
- package/dist/components/editor.js.map +1 -1
- package/dist/components/h-stack.d.ts.map +1 -1
- package/dist/components/h-stack.js +1 -1
- package/dist/components/h-stack.js.map +1 -1
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +0 -8
- package/dist/components/image.js.map +1 -1
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +14 -14
- package/dist/components/input.js.map +1 -1
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js +8 -10
- package/dist/components/loader.js.map +1 -1
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +0 -11
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/scroll-view.d.ts.map +1 -1
- package/dist/components/scroll-view.js +5 -15
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +4 -9
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +4 -12
- package/dist/components/settings-list.js.map +1 -1
- package/dist/components/spacer.d.ts.map +1 -1
- package/dist/components/spacer.js +0 -1
- package/dist/components/spacer.js.map +1 -1
- package/dist/components/stack.d.ts.map +1 -1
- package/dist/components/stack.js +1 -3
- package/dist/components/stack.js.map +1 -1
- package/dist/components/text.d.ts.map +1 -1
- package/dist/components/text.js +0 -8
- package/dist/components/text.js.map +1 -1
- package/dist/components/truncated-text.d.ts.map +1 -1
- package/dist/components/truncated-text.js +0 -3
- package/dist/components/truncated-text.js.map +1 -1
- package/dist/components/v-stack.d.ts.map +1 -1
- package/dist/components/v-stack.js +1 -1
- package/dist/components/v-stack.js.map +1 -1
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +2 -4
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js.map +1 -1
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js +3 -1
- package/dist/kill-ring.js.map +1 -1
- package/dist/latex.d.ts.map +1 -1
- package/dist/latex.js +3 -7
- package/dist/latex.js.map +1 -1
- package/dist/layout-node.d.ts.map +1 -1
- package/dist/layout-node.js.map +1 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js.map +1 -1
- package/dist/native-modifiers.d.ts.map +1 -1
- package/dist/native-modifiers.js.map +1 -1
- package/dist/stdin-buffer.d.ts.map +1 -1
- package/dist/stdin-buffer.js +4 -6
- package/dist/stdin-buffer.js.map +1 -1
- package/dist/terminal-control.d.ts.map +1 -1
- package/dist/terminal-control.js.map +1 -1
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +25 -30
- package/dist/terminal.js.map +1 -1
- package/dist/tui-alt-screen.d.ts.map +1 -1
- package/dist/tui-alt-screen.js +15 -32
- package/dist/tui-alt-screen.js.map +1 -1
- package/dist/tui-main-screen.d.ts.map +1 -1
- package/dist/tui-main-screen.js +12 -9
- package/dist/tui-main-screen.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +17 -20
- package/dist/tui.js.map +1 -1
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js +3 -1
- package/dist/undo-stack.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +14 -12
- package/dist/utils.js.map +1 -1
- package/dist/word-navigation.d.ts.map +1 -1
- package/dist/word-navigation.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h-stack.d.ts","sourceRoot":"","sources":["../../src/components/h-stack.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAuB,MAAM,YAAY,CAAC;AAEhH,qBAAa,MAAO,SAAQ,KAAK;IAChC,SAAS,CAAC,QAAQ,CAAC,UAAU,
|
|
1
|
+
{"version":3,"file":"h-stack.d.ts","sourceRoot":"","sources":["../../src/components/h-stack.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAuB,MAAM,YAAY,CAAC;AAEhH,qBAAa,MAAO,SAAQ,KAAK;IAChC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAG,QAAQ,CAAU;IAElD,YAAY,QAAQ,GAAE,UAAU,EAAO,EAAE,OAAO,GAAE,YAAiB,EAElE;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+BvC;CACD"}
|
|
@@ -2,9 +2,9 @@ import { compositeTuiLine } from "../tui.js";
|
|
|
2
2
|
import { visibleWidth } from "../utils.js";
|
|
3
3
|
import { allocateStackSizes, Stack, visibleStackEntries } from "./stack.js";
|
|
4
4
|
export class HStack extends Stack {
|
|
5
|
-
layoutType = "hstack";
|
|
6
5
|
constructor(children = [], options = {}) {
|
|
7
6
|
super(children, options);
|
|
7
|
+
this.layoutType = "hstack";
|
|
8
8
|
}
|
|
9
9
|
render(width) {
|
|
10
10
|
const safeWidth = Math.max(1, width);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h-stack.js","sourceRoot":"","sources":["../../src/components/h-stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAsC,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEhH,MAAM,OAAO,MAAO,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"h-stack.js","sourceRoot":"","sources":["../../src/components/h-stack.ts"],"names":[],"mappings":"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","sourcesContent":["import { compositeTuiLine } from \"../tui.ts\";\nimport { visibleWidth } from \"../utils.ts\";\nimport { allocateStackSizes, Stack, type StackChild, type StackOptions, visibleStackEntries } from \"./stack.ts\";\n\nexport class HStack extends Stack {\n\tprotected readonly layoutType = \"hstack\" as const;\n\n\tconstructor(children: StackChild[] = [], options: StackOptions = {}) {\n\t\tsuper(children, options);\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst viewport = { width: safeWidth, height: Number.MAX_SAFE_INTEGER };\n\t\tconst entries = visibleStackEntries(this.entries, viewport);\n\t\tif (entries.length === 0) return [];\n\n\t\tconst intrinsicWidths = entries.map((entry) => {\n\t\t\tconst lines = entry.component.render(safeWidth);\n\t\t\treturn lines.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\t\t});\n\t\tconst widths = allocateStackSizes(entries, intrinsicWidths, safeWidth, this.gap);\n\t\tconst rendered = entries.map((entry, index) =>\n\t\t\twidths[index] === 0 ? [] : entry.component.render(widths[index]!),\n\t\t);\n\t\tconst height = rendered.reduce((max, lines) => Math.max(max, lines.length), 0);\n\t\tconst result = Array.from({ length: height }, () => \"\");\n\t\tlet x = 0;\n\t\tfor (let index = 0; index < rendered.length; index++) {\n\t\t\tconst lines = rendered[index]!;\n\t\t\tconst childWidth = widths[index]!;\n\t\t\tlet offset = 0;\n\t\t\tif (this.align === \"center\") offset = Math.floor((height - lines.length) / 2);\n\t\t\telse if (this.align === \"end\") offset = height - lines.length;\n\t\t\tfor (let row = 0; row < lines.length; row++) {\n\t\t\t\tconst target = row + offset;\n\t\t\t\tif (target < 0 || target >= result.length) continue;\n\t\t\t\tresult[target] = compositeTuiLine(result[target]!, lines[row]!, x, childWidth, safeWidth);\n\t\t\t}\n\t\t\tx += childWidth + this.gap;\n\t\t}\n\t\treturn result;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/components/image.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,KAAK,eAAe,EAKpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,UAAU;IAC1B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,KAAM,YAAW,SAAS;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,WAAW,CAAC,CAAW;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,YACC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,YAAiB,EAC1B,UAAU,CAAC,EAAE,eAAe,EAQ5B;IAED,0DAA0D;IAC1D,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,UAAU,IAAI,IAAI,CAGjB;IAED,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,wBAAwB;IAkBhC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9B;CACD"
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/components/image.ts"],"names":[],"mappings":"AAAA,OAAO,EAKN,KAAK,eAAe,EAKpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,UAAU;IAC1B,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,YAAY;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,KAAM,YAAW,SAAS;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAS;IAEzB,OAAO,CAAC,WAAW,CAAC,CAAW;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,YACC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,YAAiB,EAC1B,UAAU,CAAC,EAAE,eAAe,EAQ5B;IAED,0DAA0D;IAC1D,UAAU,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,UAAU,IAAI,IAAI,CAGjB;IAED,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,wBAAwB;IAkBhC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAa9B;CACD"}
|
package/dist/components/image.js
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { allocateImageId, getCapabilities, getCellDimensions, getImageDimensions, imageFallback, renderImage, } from "../terminal-image.js";
|
|
2
2
|
export class Image {
|
|
3
|
-
base64Data;
|
|
4
|
-
mimeType;
|
|
5
|
-
dimensions;
|
|
6
|
-
theme;
|
|
7
|
-
options;
|
|
8
|
-
imageId;
|
|
9
|
-
cachedLines;
|
|
10
|
-
cachedWidth;
|
|
11
3
|
constructor(base64Data, mimeType, theme, options = {}, dimensions) {
|
|
12
4
|
this.base64Data = base64Data;
|
|
13
5
|
this.mimeType = mimeType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/components/image.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAIlB,aAAa,EACb,WAAW,GACX,MAAM,sBAAsB,CAAC;AAe9B,MAAM,OAAO,KAAK;
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/components/image.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tallocateImageId,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetImageDimensions,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderResult,\n\timageFallback,\n\trenderImage,\n} from \"../terminal-image.ts\";\nimport type { Component } from \"../tui.ts\";\n\nexport interface ImageTheme {\n\tfallbackColor: (str: string) => string;\n}\n\nexport interface ImageOptions {\n\tmaxWidthCells?: number;\n\tmaxHeightCells?: number;\n\tfilename?: string;\n\t/** Kitty image ID. If provided, reuses this ID (for animations/updates). */\n\timageId?: number;\n}\n\nexport class Image implements Component {\n\tprivate base64Data: string;\n\tprivate mimeType: string;\n\tprivate dimensions: ImageDimensions;\n\tprivate theme: ImageTheme;\n\tprivate options: ImageOptions;\n\tprivate imageId?: number;\n\n\tprivate cachedLines?: string[];\n\tprivate cachedWidth?: number;\n\n\tconstructor(\n\t\tbase64Data: string,\n\t\tmimeType: string,\n\t\ttheme: ImageTheme,\n\t\toptions: ImageOptions = {},\n\t\tdimensions?: ImageDimensions,\n\t) {\n\t\tthis.base64Data = base64Data;\n\t\tthis.mimeType = mimeType;\n\t\tthis.theme = theme;\n\t\tthis.options = options;\n\t\tthis.dimensions = dimensions || getImageDimensions(base64Data, mimeType) || { widthPx: 800, heightPx: 600 };\n\t\tthis.imageId = options.imageId;\n\t}\n\n\t/** Get the Kitty image ID used by this image (if any). */\n\tgetImageId(): number | undefined {\n\t\treturn this.imageId;\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedLines = undefined;\n\t\tthis.cachedWidth = undefined;\n\t}\n\n\tprivate renderFallbackLines(): string[] {\n\t\tconst fallback = imageFallback(this.mimeType, this.dimensions, this.options.filename);\n\t\treturn [this.theme.fallbackColor(fallback)];\n\t}\n\n\tprivate renderKittyImageLines(result: ImageRenderResult): string[] {\n\t\tconst lines = [result.sequence];\n\t\tfor (let index = 0; index < result.rows - 1; index++) lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderITermImageLines(result: ImageRenderResult): string[] {\n\t\tconst lines: string[] = [];\n\t\tfor (let index = 0; index < result.rows - 1; index++) lines.push(\"\");\n\t\tconst rowOffset = result.rows - 1;\n\t\tconst moveUp = rowOffset > 0 ? `\\x1b[${rowOffset}A` : \"\";\n\t\tlines.push(moveUp + result.sequence);\n\t\treturn lines;\n\t}\n\n\tprivate renderTerminalImageLines(\n\t\tprotocol: ImageProtocol,\n\t\tmaxWidthCells: number,\n\t\tmaxHeightCells: number,\n\t): string[] | undefined {\n\t\tif (!protocol) return undefined;\n\t\tif (protocol === \"kitty\" && this.imageId === undefined) this.imageId = allocateImageId();\n\t\tconst result = renderImage(this.base64Data, this.dimensions, {\n\t\t\tmaxWidthCells,\n\t\t\tmaxHeightCells,\n\t\t\timageId: this.imageId,\n\t\t\tmoveCursor: false,\n\t\t});\n\t\tif (!result) return undefined;\n\t\tif (result.imageId) this.imageId = result.imageId;\n\t\treturn protocol === \"kitty\" ? this.renderKittyImageLines(result) : this.renderITermImageLines(result);\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.cachedLines && this.cachedWidth === width) return this.cachedLines;\n\n\t\tconst maxWidth = Math.max(1, Math.min(width - 2, this.options.maxWidthCells ?? 60));\n\t\tconst cellDimensions = getCellDimensions();\n\t\tconst defaultMaxHeight = Math.max(1, Math.ceil((maxWidth * cellDimensions.widthPx) / cellDimensions.heightPx));\n\t\tconst maxHeight = this.options.maxHeightCells ?? defaultMaxHeight;\n\t\tconst lines =\n\t\t\tthis.renderTerminalImageLines(getCapabilities().images, maxWidth, maxHeight) ?? this.renderFallbackLines();\n\n\t\tthis.cachedLines = lines;\n\t\tthis.cachedWidth = width;\n\t\treturn lines;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAiB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAmB1E;;GAEG;AACH,qBAAa,KAAM,YAAW,SAAS,EAAE,SAAS;IACjD,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAa;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAE7B,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAS;IAGzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAkB;IAGnC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,UAAU,CAA4B;IAG9C,OAAO,CAAC,SAAS,CAA+B;IAEhD,QAAQ,IAAI,MAAM,CAEjB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,yBAAyB;IAejC,OAAO,CAAC,oBAAoB;IAa5B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU9B;IAED,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,WAAW;IAYnB,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,gBAAgB;IAmBxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B9B;CACD","sourcesContent":["import { getKeybindings, type KeybindingsManager } from \"../keybindings.ts\";\nimport { decodeKittyPrintable } from \"../keys.ts\";\nimport { KillRing } from \"../kill-ring.ts\";\nimport { type Component, CURSOR_MARKER, type Focusable } from \"../tui.ts\";\nimport { UndoStack } from \"../undo-stack.ts\";\nimport { getGraphemeSegmenter, isWhitespaceChar, sliceByColumn, visibleWidth } from \"../utils.ts\";\nimport { findWordBackward, findWordForward } from \"../word-navigation.ts\";\nimport type { PasteInputResult } from \"./editor.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface InputState {\n\tvalue: string;\n\tcursor: number;\n}\n\ninterface InputVisibleWindow {\n\ttext: string;\n\tcursor: number;\n}\n\ntype InputAction = \"kill\" | \"yank\" | \"type-word\";\n/**\n * Input component - single-line text input with horizontal scrolling\n */\nexport class Input implements Component, Focusable {\n\tprivate value: string = \"\";\n\tprivate cursor: number = 0; // Cursor position in the value\n\tpublic onSubmit?: (value: string) => void;\n\tpublic onEscape?: () => void;\n\n\t/** Focusable interface - set by TUI when focus changes */\n\tfocused: boolean = false;\n\n\t// Bracketed paste mode buffering\n\tprivate pasteBuffer: string = \"\";\n\tprivate isInPaste: boolean = false;\n\n\t// Kill ring for Emacs-style kill/yank operations\n\tprivate killRing = new KillRing();\n\tprivate lastAction: InputAction | null = null;\n\n\t// Undo support\n\tprivate undoStack = new UndoStack<InputState>();\n\n\tgetValue(): string {\n\t\treturn this.value;\n\t}\n\n\tsetValue(value: string): void {\n\t\tthis.value = value;\n\t\tthis.cursor = Math.min(this.cursor, value.length);\n\t}\n\n\tprivate handleBracketedPasteInput(data: string): PasteInputResult {\n\t\tlet remainingData = data;\n\t\tif (remainingData.includes(\"\\x1b[200~\")) {\n\t\t\tthis.isInPaste = true;\n\t\t\tthis.pasteBuffer = \"\";\n\t\t\tremainingData = remainingData.replace(\"\\x1b[200~\", \"\");\n\t\t}\n\t\tif (!this.isInPaste) return { data: remainingData, consumed: false };\n\t\tthis.pasteBuffer += remainingData;\n\t\tconst endIndex = this.pasteBuffer.indexOf(\"\\x1b[201~\");\n\t\tif (endIndex === -1) return { data: remainingData, consumed: true };\n\t\tconst pasteContent = this.pasteBuffer.substring(0, endIndex);\n\t\tthis.handlePaste(pasteContent);\n\t\tthis.isInPaste = false;\n\t\tconst trailingInput = this.pasteBuffer.substring(endIndex + 6);\n\t\tthis.pasteBuffer = \"\";\n\t\tif (trailingInput) this.handleInput(trailingInput);\n\t\treturn { data: remainingData, consumed: true };\n\t}\n\n\tprivate handleControlInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onEscape?.();\n\t\t\treturn true;\n\t\t}\n\t\tif (keybindings.matches(data, \"tui.editor.undo\")) {\n\t\t\tthis.undo();\n\t\t\treturn true;\n\t\t}\n\t\tif (!keybindings.matches(data, \"tui.input.submit\") && data !== \"\\n\") return false;\n\t\tthis.onSubmit?.(this.value);\n\t\treturn true;\n\t}\n\n\tprivate handleDeletionInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.deleteCharBackward\")) this.handleBackspace();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteCharForward\")) this.handleForwardDelete();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordBackward\")) this.deleteWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordForward\")) this.deleteWordForward();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineStart\")) this.deleteToLineStart();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineEnd\")) this.deleteToLineEnd();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handleKillRingInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.yank\")) this.yank();\n\t\telse if (keybindings.matches(data, \"tui.editor.yankPop\")) this.yankPop();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate moveCursorLeft(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor === 0) return;\n\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\tthis.cursor -= lastGrapheme ? lastGrapheme.segment.length : 1;\n\t}\n\n\tprivate moveCursorRight(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor >= this.value.length) return;\n\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\tconst firstGrapheme = graphemes[0];\n\t\tthis.cursor += firstGrapheme ? firstGrapheme.segment.length : 1;\n\t}\n\n\tprivate handleCursorMovementInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.cursorLeft\")) this.moveCursorLeft();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorRight\")) this.moveCursorRight();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorLineStart\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = 0;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorLineEnd\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = this.value.length;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorWordLeft\")) this.moveWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorWordRight\")) this.moveWordForwards();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handlePrintableInput(data: string): void {\n\t\tconst kittyPrintable = decodeKittyPrintable(data);\n\t\tif (kittyPrintable !== undefined) {\n\t\t\tthis.insertCharacter(kittyPrintable);\n\t\t\treturn;\n\t\t}\n\t\tconst hasControlChars = [...data].some((character) => {\n\t\t\tconst code = character.charCodeAt(0);\n\t\t\treturn code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f);\n\t\t});\n\t\tif (!hasControlChars) this.insertCharacter(data);\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst paste = this.handleBracketedPasteInput(data);\n\t\tif (paste.consumed) return;\n\t\tdata = paste.data;\n\t\tconst keybindings = getKeybindings();\n\t\tif (this.handleControlInput(data, keybindings)) return;\n\t\tif (this.handleDeletionInput(data, keybindings)) return;\n\t\tif (this.handleKillRingInput(data, keybindings)) return;\n\t\tif (this.handleCursorMovementInput(data, keybindings)) return;\n\t\tthis.handlePrintableInput(data);\n\t}\n\n\tprivate insertCharacter(char: string): void {\n\t\t// Undo coalescing: consecutive word chars coalesce into one undo unit\n\t\tif (isWhitespaceChar(char) || this.lastAction !== \"type-word\") {\n\t\t\tthis.pushUndo();\n\t\t}\n\t\tthis.lastAction = \"type-word\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + char + this.value.slice(this.cursor);\n\t\tthis.cursor += char.length;\n\t}\n\n\tprivate handleBackspace(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor > 0) {\n\t\t\tthis.pushUndo();\n\t\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\t\tconst graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor - graphemeLength) + this.value.slice(this.cursor);\n\t\t\tthis.cursor -= graphemeLength;\n\t\t}\n\t}\n\n\tprivate handleForwardDelete(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor < this.value.length) {\n\t\t\tthis.pushUndo();\n\t\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\t\tconst firstGrapheme = graphemes[0];\n\t\t\tconst graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + graphemeLength);\n\t\t}\n\t}\n\n\tprivate deleteToLineStart(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(0, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(this.cursor);\n\t\tthis.cursor = 0;\n\t}\n\n\tprivate deleteToLineEnd(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(0, this.cursor);\n\t}\n\n\tprivate deleteWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\n\t\t// Save lastAction before cursor movement (moveWordBackwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordBackwards();\n\t\tconst deleteFrom = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(deleteFrom, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, deleteFrom) + this.value.slice(this.cursor);\n\t\tthis.cursor = deleteFrom;\n\t}\n\n\tprivate deleteWordForward(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\n\t\t// Save lastAction before cursor movement (moveWordForwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordForwards();\n\t\tconst deleteTo = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(this.cursor, deleteTo);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(deleteTo);\n\t}\n\n\tprivate yank(): void {\n\t\tconst text = this.killRing.peek();\n\t\tif (!text) return;\n\n\t\tthis.pushUndo();\n\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate yankPop(): void {\n\t\tif (this.lastAction !== \"yank\" || this.killRing.length <= 1) return;\n\n\t\tthis.pushUndo();\n\n\t\t// Delete the previously yanked text (still at end of ring before rotation)\n\t\tconst prevText = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor - prevText.length) + this.value.slice(this.cursor);\n\t\tthis.cursor -= prevText.length;\n\n\t\t// Rotate and insert new entry\n\t\tthis.killRing.rotate();\n\t\tconst text = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate pushUndo(): void {\n\t\tthis.undoStack.push({ value: this.value, cursor: this.cursor });\n\t}\n\n\tprivate undo(): void {\n\t\tconst snapshot = this.undoStack.pop();\n\t\tif (!snapshot) return;\n\t\tthis.value = snapshot.value;\n\t\tthis.cursor = snapshot.cursor;\n\t\tthis.lastAction = null;\n\t}\n\n\tprivate moveWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordBackward(this.value, this.cursor);\n\t}\n\n\tprivate moveWordForwards(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordForward(this.value, this.cursor);\n\t}\n\n\tprivate handlePaste(pastedText: string): void {\n\t\tthis.lastAction = null;\n\t\tthis.pushUndo();\n\n\t\t// Clean the pasted text - remove newlines and carriage returns\n\t\tconst cleanText = pastedText.replace(/\\r\\n/g, \"\").replace(/\\r/g, \"\").replace(/\\n/g, \"\").replace(/\\t/g, \" \");\n\n\t\t// Insert at cursor position\n\t\tthis.value = this.value.slice(0, this.cursor) + cleanText + this.value.slice(this.cursor);\n\t\tthis.cursor += cleanText.length;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\tprivate getVisibleWindow(availableWidth: number): InputVisibleWindow {\n\t\tconst totalWidth = visibleWidth(this.value);\n\t\tif (totalWidth < availableWidth) return { text: this.value, cursor: this.cursor };\n\n\t\tconst scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;\n\t\tif (scrollWidth <= 0) return { text: \"\", cursor: 0 };\n\n\t\tconst cursorColumn = visibleWidth(this.value.slice(0, this.cursor));\n\t\tconst halfWidth = Math.floor(scrollWidth / 2);\n\t\tlet startColumn: number;\n\t\tif (cursorColumn < halfWidth) startColumn = 0;\n\t\telse if (cursorColumn > totalWidth - halfWidth) startColumn = Math.max(0, totalWidth - scrollWidth);\n\t\telse startColumn = Math.max(0, cursorColumn - halfWidth);\n\n\t\tconst text = sliceByColumn(this.value, startColumn, scrollWidth, true);\n\t\tconst beforeCursor = sliceByColumn(this.value, startColumn, Math.max(0, cursorColumn - startColumn), true);\n\t\treturn { text, cursor: beforeCursor.length };\n\t}\n\n\trender(width: number): string[] {\n\t\tconst prompt = \"> \";\n\t\tconst availableWidth = width - prompt.length;\n\t\tif (availableWidth <= 0) return [prompt];\n\n\t\tconst visibleWindow = this.getVisibleWindow(availableWidth);\n\t\tconst visibleText = visibleWindow.text;\n\t\tconst cursorDisplay = visibleWindow.cursor;\n\n\t\t// Build line with fake cursor\n\t\t// Insert cursor character at cursor position\n\t\tconst graphemes = [...segmenter.segment(visibleText.slice(cursorDisplay))];\n\t\tconst cursorGrapheme = graphemes[0];\n\n\t\tconst beforeCursor = visibleText.slice(0, cursorDisplay);\n\t\tconst atCursor = cursorGrapheme?.segment ?? \" \"; // Character at cursor, or space if at end\n\t\tconst afterCursor = visibleText.slice(cursorDisplay + atCursor.length);\n\n\t\t// Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n\t\tconst marker = this.focused ? CURSOR_MARKER : \"\";\n\n\t\t// Use inverse video to show cursor\n\t\tconst cursorChar = `\\x1b[7m${atCursor}\\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal\n\t\tconst textWithCursor = beforeCursor + marker + cursorChar + afterCursor;\n\n\t\t// Calculate visual width\n\t\tconst visualLength = visibleWidth(textWithCursor);\n\t\tconst padding = \" \".repeat(Math.max(0, availableWidth - visualLength));\n\t\tconst line = prompt + textWithCursor + padding;\n\n\t\treturn [line];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAiB,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAmB1E;;GAEG;AACH,qBAAa,KAAM,YAAW,SAAS,EAAE,SAAS;IACjD,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAa;IACpB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAE7B,0DAA0D;IAC1D,OAAO,EAAE,OAAO,CAAS;IAGzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,SAAS,CAAkB;IAGnC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,UAAU,CAA4B;IAG9C,OAAO,CAAC,SAAS,CAA+B;IAEhD,QAAQ,IAAI,MAAM,CAEjB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,OAAO,CAAC,yBAAyB;IAoBjC,OAAO,CAAC,kBAAkB;IAc1B,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,yBAAyB;IAejC,OAAO,CAAC,oBAAoB;IAa5B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU9B;IAED,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,iBAAiB;IAUzB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,mBAAmB;IAqB3B,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,WAAW;IAYnB,UAAU,IAAI,IAAI,CAEjB;IAED,OAAO,CAAC,gBAAgB;IAmBxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+B9B;CACD"}
|
package/dist/components/input.js
CHANGED
|
@@ -10,20 +10,20 @@ const segmenter = getGraphemeSegmenter();
|
|
|
10
10
|
* Input component - single-line text input with horizontal scrolling
|
|
11
11
|
*/
|
|
12
12
|
export class Input {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
constructor() {
|
|
14
|
+
this.value = "";
|
|
15
|
+
this.cursor = 0; // Cursor position in the value
|
|
16
|
+
/** Focusable interface - set by TUI when focus changes */
|
|
17
|
+
this.focused = false;
|
|
18
|
+
// Bracketed paste mode buffering
|
|
19
|
+
this.pasteBuffer = "";
|
|
20
|
+
this.isInPaste = false;
|
|
21
|
+
// Kill ring for Emacs-style kill/yank operations
|
|
22
|
+
this.killRing = new KillRing();
|
|
23
|
+
this.lastAction = null;
|
|
24
|
+
// Undo support
|
|
25
|
+
this.undoStack = new UndoStack();
|
|
26
|
+
}
|
|
27
27
|
getValue() {
|
|
28
28
|
return this.value;
|
|
29
29
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings, type KeybindingsManager } from \"../keybindings.ts\";\nimport { decodeKittyPrintable } from \"../keys.ts\";\nimport { KillRing } from \"../kill-ring.ts\";\nimport { type Component, CURSOR_MARKER, type Focusable } from \"../tui.ts\";\nimport { UndoStack } from \"../undo-stack.ts\";\nimport { getGraphemeSegmenter, isWhitespaceChar, sliceByColumn, visibleWidth } from \"../utils.ts\";\nimport { findWordBackward, findWordForward } from \"../word-navigation.ts\";\nimport type { PasteInputResult } from \"./editor.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface InputState {\n\tvalue: string;\n\tcursor: number;\n}\n\ninterface InputVisibleWindow {\n\ttext: string;\n\tcursor: number;\n}\n\ntype InputAction = \"kill\" | \"yank\" | \"type-word\";\n/**\n * Input component - single-line text input with horizontal scrolling\n */\nexport class Input implements Component, Focusable {\n\tprivate value: string = \"\";\n\tprivate cursor: number = 0; // Cursor position in the value\n\tpublic onSubmit?: (value: string) => void;\n\tpublic onEscape?: () => void;\n\n\t/** Focusable interface - set by TUI when focus changes */\n\tfocused: boolean = false;\n\n\t// Bracketed paste mode buffering\n\tprivate pasteBuffer: string = \"\";\n\tprivate isInPaste: boolean = false;\n\n\t// Kill ring for Emacs-style kill/yank operations\n\tprivate killRing = new KillRing();\n\tprivate lastAction: InputAction | null = null;\n\n\t// Undo support\n\tprivate undoStack = new UndoStack<InputState>();\n\n\tgetValue(): string {\n\t\treturn this.value;\n\t}\n\n\tsetValue(value: string): void {\n\t\tthis.value = value;\n\t\tthis.cursor = Math.min(this.cursor, value.length);\n\t}\n\n\tprivate handleBracketedPasteInput(data: string): PasteInputResult {\n\t\tlet remainingData = data;\n\t\tif (remainingData.includes(\"\\x1b[200~\")) {\n\t\t\tthis.isInPaste = true;\n\t\t\tthis.pasteBuffer = \"\";\n\t\t\tremainingData = remainingData.replace(\"\\x1b[200~\", \"\");\n\t\t}\n\t\tif (!this.isInPaste) return { data: remainingData, consumed: false };\n\t\tthis.pasteBuffer += remainingData;\n\t\tconst endIndex = this.pasteBuffer.indexOf(\"\\x1b[201~\");\n\t\tif (endIndex === -1) return { data: remainingData, consumed: true };\n\t\tconst pasteContent = this.pasteBuffer.substring(0, endIndex);\n\t\tthis.handlePaste(pasteContent);\n\t\tthis.isInPaste = false;\n\t\tconst trailingInput = this.pasteBuffer.substring(endIndex + 6);\n\t\tthis.pasteBuffer = \"\";\n\t\tif (trailingInput) this.handleInput(trailingInput);\n\t\treturn { data: remainingData, consumed: true };\n\t}\n\n\tprivate handleControlInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onEscape?.();\n\t\t\treturn true;\n\t\t}\n\t\tif (keybindings.matches(data, \"tui.editor.undo\")) {\n\t\t\tthis.undo();\n\t\t\treturn true;\n\t\t}\n\t\tif (!keybindings.matches(data, \"tui.input.submit\") && data !== \"\\n\") return false;\n\t\tthis.onSubmit?.(this.value);\n\t\treturn true;\n\t}\n\n\tprivate handleDeletionInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.deleteCharBackward\")) this.handleBackspace();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteCharForward\")) this.handleForwardDelete();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordBackward\")) this.deleteWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordForward\")) this.deleteWordForward();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineStart\")) this.deleteToLineStart();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineEnd\")) this.deleteToLineEnd();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handleKillRingInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.yank\")) this.yank();\n\t\telse if (keybindings.matches(data, \"tui.editor.yankPop\")) this.yankPop();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate moveCursorLeft(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor === 0) return;\n\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\tthis.cursor -= lastGrapheme ? lastGrapheme.segment.length : 1;\n\t}\n\n\tprivate moveCursorRight(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor >= this.value.length) return;\n\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\tconst firstGrapheme = graphemes[0];\n\t\tthis.cursor += firstGrapheme ? firstGrapheme.segment.length : 1;\n\t}\n\n\tprivate handleCursorMovementInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.cursorLeft\")) this.moveCursorLeft();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorRight\")) this.moveCursorRight();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorLineStart\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = 0;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorLineEnd\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = this.value.length;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorWordLeft\")) this.moveWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorWordRight\")) this.moveWordForwards();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handlePrintableInput(data: string): void {\n\t\tconst kittyPrintable = decodeKittyPrintable(data);\n\t\tif (kittyPrintable !== undefined) {\n\t\t\tthis.insertCharacter(kittyPrintable);\n\t\t\treturn;\n\t\t}\n\t\tconst hasControlChars = [...data].some((character) => {\n\t\t\tconst code = character.charCodeAt(0);\n\t\t\treturn code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f);\n\t\t});\n\t\tif (!hasControlChars) this.insertCharacter(data);\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst paste = this.handleBracketedPasteInput(data);\n\t\tif (paste.consumed) return;\n\t\tdata = paste.data;\n\t\tconst keybindings = getKeybindings();\n\t\tif (this.handleControlInput(data, keybindings)) return;\n\t\tif (this.handleDeletionInput(data, keybindings)) return;\n\t\tif (this.handleKillRingInput(data, keybindings)) return;\n\t\tif (this.handleCursorMovementInput(data, keybindings)) return;\n\t\tthis.handlePrintableInput(data);\n\t}\n\n\tprivate insertCharacter(char: string): void {\n\t\t// Undo coalescing: consecutive word chars coalesce into one undo unit\n\t\tif (isWhitespaceChar(char) || this.lastAction !== \"type-word\") {\n\t\t\tthis.pushUndo();\n\t\t}\n\t\tthis.lastAction = \"type-word\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + char + this.value.slice(this.cursor);\n\t\tthis.cursor += char.length;\n\t}\n\n\tprivate handleBackspace(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor > 0) {\n\t\t\tthis.pushUndo();\n\t\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\t\tconst graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor - graphemeLength) + this.value.slice(this.cursor);\n\t\t\tthis.cursor -= graphemeLength;\n\t\t}\n\t}\n\n\tprivate handleForwardDelete(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor < this.value.length) {\n\t\t\tthis.pushUndo();\n\t\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\t\tconst firstGrapheme = graphemes[0];\n\t\t\tconst graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + graphemeLength);\n\t\t}\n\t}\n\n\tprivate deleteToLineStart(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(0, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(this.cursor);\n\t\tthis.cursor = 0;\n\t}\n\n\tprivate deleteToLineEnd(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(0, this.cursor);\n\t}\n\n\tprivate deleteWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\n\t\t// Save lastAction before cursor movement (moveWordBackwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordBackwards();\n\t\tconst deleteFrom = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(deleteFrom, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, deleteFrom) + this.value.slice(this.cursor);\n\t\tthis.cursor = deleteFrom;\n\t}\n\n\tprivate deleteWordForward(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\n\t\t// Save lastAction before cursor movement (moveWordForwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordForwards();\n\t\tconst deleteTo = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(this.cursor, deleteTo);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(deleteTo);\n\t}\n\n\tprivate yank(): void {\n\t\tconst text = this.killRing.peek();\n\t\tif (!text) return;\n\n\t\tthis.pushUndo();\n\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate yankPop(): void {\n\t\tif (this.lastAction !== \"yank\" || this.killRing.length <= 1) return;\n\n\t\tthis.pushUndo();\n\n\t\t// Delete the previously yanked text (still at end of ring before rotation)\n\t\tconst prevText = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor - prevText.length) + this.value.slice(this.cursor);\n\t\tthis.cursor -= prevText.length;\n\n\t\t// Rotate and insert new entry\n\t\tthis.killRing.rotate();\n\t\tconst text = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate pushUndo(): void {\n\t\tthis.undoStack.push({ value: this.value, cursor: this.cursor });\n\t}\n\n\tprivate undo(): void {\n\t\tconst snapshot = this.undoStack.pop();\n\t\tif (!snapshot) return;\n\t\tthis.value = snapshot.value;\n\t\tthis.cursor = snapshot.cursor;\n\t\tthis.lastAction = null;\n\t}\n\n\tprivate moveWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordBackward(this.value, this.cursor);\n\t}\n\n\tprivate moveWordForwards(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordForward(this.value, this.cursor);\n\t}\n\n\tprivate handlePaste(pastedText: string): void {\n\t\tthis.lastAction = null;\n\t\tthis.pushUndo();\n\n\t\t// Clean the pasted text - remove newlines and carriage returns\n\t\tconst cleanText = pastedText.replace(/\\r\\n/g, \"\").replace(/\\r/g, \"\").replace(/\\n/g, \"\").replace(/\\t/g, \" \");\n\n\t\t// Insert at cursor position\n\t\tthis.value = this.value.slice(0, this.cursor) + cleanText + this.value.slice(this.cursor);\n\t\tthis.cursor += cleanText.length;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\tprivate getVisibleWindow(availableWidth: number): InputVisibleWindow {\n\t\tconst totalWidth = visibleWidth(this.value);\n\t\tif (totalWidth < availableWidth) return { text: this.value, cursor: this.cursor };\n\n\t\tconst scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;\n\t\tif (scrollWidth <= 0) return { text: \"\", cursor: 0 };\n\n\t\tconst cursorColumn = visibleWidth(this.value.slice(0, this.cursor));\n\t\tconst halfWidth = Math.floor(scrollWidth / 2);\n\t\tlet startColumn: number;\n\t\tif (cursorColumn < halfWidth) startColumn = 0;\n\t\telse if (cursorColumn > totalWidth - halfWidth) startColumn = Math.max(0, totalWidth - scrollWidth);\n\t\telse startColumn = Math.max(0, cursorColumn - halfWidth);\n\n\t\tconst text = sliceByColumn(this.value, startColumn, scrollWidth, true);\n\t\tconst beforeCursor = sliceByColumn(this.value, startColumn, Math.max(0, cursorColumn - startColumn), true);\n\t\treturn { text, cursor: beforeCursor.length };\n\t}\n\n\trender(width: number): string[] {\n\t\tconst prompt = \"> \";\n\t\tconst availableWidth = width - prompt.length;\n\t\tif (availableWidth <= 0) return [prompt];\n\n\t\tconst visibleWindow = this.getVisibleWindow(availableWidth);\n\t\tconst visibleText = visibleWindow.text;\n\t\tconst cursorDisplay = visibleWindow.cursor;\n\n\t\t// Build line with fake cursor\n\t\t// Insert cursor character at cursor position\n\t\tconst graphemes = [...segmenter.segment(visibleText.slice(cursorDisplay))];\n\t\tconst cursorGrapheme = graphemes[0];\n\n\t\tconst beforeCursor = visibleText.slice(0, cursorDisplay);\n\t\tconst atCursor = cursorGrapheme?.segment ?? \" \"; // Character at cursor, or space if at end\n\t\tconst afterCursor = visibleText.slice(cursorDisplay + atCursor.length);\n\n\t\t// Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n\t\tconst marker = this.focused ? CURSOR_MARKER : \"\";\n\n\t\t// Use inverse video to show cursor\n\t\tconst cursorChar = `\\x1b[7m${atCursor}\\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal\n\t\tconst textWithCursor = beforeCursor + marker + cursorChar + afterCursor;\n\n\t\t// Calculate visual width\n\t\tconst visualLength = visibleWidth(textWithCursor);\n\t\tconst padding = \" \".repeat(Math.max(0, availableWidth - visualLength));\n\t\tconst line = prompt + textWithCursor + padding;\n\n\t\treturn [line];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/components/input.ts"],"names":[],"mappings":"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","sourcesContent":["import { getKeybindings, type KeybindingsManager } from \"../keybindings.ts\";\nimport { decodeKittyPrintable } from \"../keys.ts\";\nimport { KillRing } from \"../kill-ring.ts\";\nimport { type Component, CURSOR_MARKER, type Focusable } from \"../tui.ts\";\nimport { UndoStack } from \"../undo-stack.ts\";\nimport { getGraphemeSegmenter, isWhitespaceChar, sliceByColumn, visibleWidth } from \"../utils.ts\";\nimport { findWordBackward, findWordForward } from \"../word-navigation.ts\";\nimport type { PasteInputResult } from \"./editor.ts\";\n\nconst segmenter = getGraphemeSegmenter();\n\ninterface InputState {\n\tvalue: string;\n\tcursor: number;\n}\n\ninterface InputVisibleWindow {\n\ttext: string;\n\tcursor: number;\n}\n\ntype InputAction = \"kill\" | \"yank\" | \"type-word\";\n/**\n * Input component - single-line text input with horizontal scrolling\n */\nexport class Input implements Component, Focusable {\n\tprivate value: string = \"\";\n\tprivate cursor: number = 0; // Cursor position in the value\n\tpublic onSubmit?: (value: string) => void;\n\tpublic onEscape?: () => void;\n\n\t/** Focusable interface - set by TUI when focus changes */\n\tfocused: boolean = false;\n\n\t// Bracketed paste mode buffering\n\tprivate pasteBuffer: string = \"\";\n\tprivate isInPaste: boolean = false;\n\n\t// Kill ring for Emacs-style kill/yank operations\n\tprivate killRing = new KillRing();\n\tprivate lastAction: InputAction | null = null;\n\n\t// Undo support\n\tprivate undoStack = new UndoStack<InputState>();\n\n\tgetValue(): string {\n\t\treturn this.value;\n\t}\n\n\tsetValue(value: string): void {\n\t\tthis.value = value;\n\t\tthis.cursor = Math.min(this.cursor, value.length);\n\t}\n\n\tprivate handleBracketedPasteInput(data: string): PasteInputResult {\n\t\tlet remainingData = data;\n\t\tif (remainingData.includes(\"\\x1b[200~\")) {\n\t\t\tthis.isInPaste = true;\n\t\t\tthis.pasteBuffer = \"\";\n\t\t\tremainingData = remainingData.replace(\"\\x1b[200~\", \"\");\n\t\t}\n\t\tif (!this.isInPaste) return { data: remainingData, consumed: false };\n\t\tthis.pasteBuffer += remainingData;\n\t\tconst endIndex = this.pasteBuffer.indexOf(\"\\x1b[201~\");\n\t\tif (endIndex === -1) return { data: remainingData, consumed: true };\n\t\tconst pasteContent = this.pasteBuffer.substring(0, endIndex);\n\t\tthis.handlePaste(pasteContent);\n\t\tthis.isInPaste = false;\n\t\tconst trailingInput = this.pasteBuffer.substring(endIndex + 6);\n\t\tthis.pasteBuffer = \"\";\n\t\tif (trailingInput) this.handleInput(trailingInput);\n\t\treturn { data: remainingData, consumed: true };\n\t}\n\n\tprivate handleControlInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onEscape?.();\n\t\t\treturn true;\n\t\t}\n\t\tif (keybindings.matches(data, \"tui.editor.undo\")) {\n\t\t\tthis.undo();\n\t\t\treturn true;\n\t\t}\n\t\tif (!keybindings.matches(data, \"tui.input.submit\") && data !== \"\\n\") return false;\n\t\tthis.onSubmit?.(this.value);\n\t\treturn true;\n\t}\n\n\tprivate handleDeletionInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.deleteCharBackward\")) this.handleBackspace();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteCharForward\")) this.handleForwardDelete();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordBackward\")) this.deleteWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteWordForward\")) this.deleteWordForward();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineStart\")) this.deleteToLineStart();\n\t\telse if (keybindings.matches(data, \"tui.editor.deleteToLineEnd\")) this.deleteToLineEnd();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handleKillRingInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.yank\")) this.yank();\n\t\telse if (keybindings.matches(data, \"tui.editor.yankPop\")) this.yankPop();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate moveCursorLeft(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor === 0) return;\n\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\tthis.cursor -= lastGrapheme ? lastGrapheme.segment.length : 1;\n\t}\n\n\tprivate moveCursorRight(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor >= this.value.length) return;\n\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\tconst firstGrapheme = graphemes[0];\n\t\tthis.cursor += firstGrapheme ? firstGrapheme.segment.length : 1;\n\t}\n\n\tprivate handleCursorMovementInput(data: string, keybindings: KeybindingsManager): boolean {\n\t\tif (keybindings.matches(data, \"tui.editor.cursorLeft\")) this.moveCursorLeft();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorRight\")) this.moveCursorRight();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorLineStart\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = 0;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorLineEnd\")) {\n\t\t\tthis.lastAction = null;\n\t\t\tthis.cursor = this.value.length;\n\t\t} else if (keybindings.matches(data, \"tui.editor.cursorWordLeft\")) this.moveWordBackwards();\n\t\telse if (keybindings.matches(data, \"tui.editor.cursorWordRight\")) this.moveWordForwards();\n\t\telse return false;\n\t\treturn true;\n\t}\n\n\tprivate handlePrintableInput(data: string): void {\n\t\tconst kittyPrintable = decodeKittyPrintable(data);\n\t\tif (kittyPrintable !== undefined) {\n\t\t\tthis.insertCharacter(kittyPrintable);\n\t\t\treturn;\n\t\t}\n\t\tconst hasControlChars = [...data].some((character) => {\n\t\t\tconst code = character.charCodeAt(0);\n\t\t\treturn code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f);\n\t\t});\n\t\tif (!hasControlChars) this.insertCharacter(data);\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst paste = this.handleBracketedPasteInput(data);\n\t\tif (paste.consumed) return;\n\t\tdata = paste.data;\n\t\tconst keybindings = getKeybindings();\n\t\tif (this.handleControlInput(data, keybindings)) return;\n\t\tif (this.handleDeletionInput(data, keybindings)) return;\n\t\tif (this.handleKillRingInput(data, keybindings)) return;\n\t\tif (this.handleCursorMovementInput(data, keybindings)) return;\n\t\tthis.handlePrintableInput(data);\n\t}\n\n\tprivate insertCharacter(char: string): void {\n\t\t// Undo coalescing: consecutive word chars coalesce into one undo unit\n\t\tif (isWhitespaceChar(char) || this.lastAction !== \"type-word\") {\n\t\t\tthis.pushUndo();\n\t\t}\n\t\tthis.lastAction = \"type-word\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + char + this.value.slice(this.cursor);\n\t\tthis.cursor += char.length;\n\t}\n\n\tprivate handleBackspace(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor > 0) {\n\t\t\tthis.pushUndo();\n\t\t\tconst beforeCursor = this.value.slice(0, this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(beforeCursor)];\n\t\t\tconst lastGrapheme = graphemes[graphemes.length - 1];\n\t\t\tconst graphemeLength = lastGrapheme ? lastGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor - graphemeLength) + this.value.slice(this.cursor);\n\t\t\tthis.cursor -= graphemeLength;\n\t\t}\n\t}\n\n\tprivate handleForwardDelete(): void {\n\t\tthis.lastAction = null;\n\t\tif (this.cursor < this.value.length) {\n\t\t\tthis.pushUndo();\n\t\t\tconst afterCursor = this.value.slice(this.cursor);\n\t\t\tconst graphemes = [...segmenter.segment(afterCursor)];\n\t\t\tconst firstGrapheme = graphemes[0];\n\t\t\tconst graphemeLength = firstGrapheme ? firstGrapheme.segment.length : 1;\n\t\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(this.cursor + graphemeLength);\n\t\t}\n\t}\n\n\tprivate deleteToLineStart(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(0, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(this.cursor);\n\t\tthis.cursor = 0;\n\t}\n\n\tprivate deleteToLineEnd(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.pushUndo();\n\t\tconst deletedText = this.value.slice(this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: this.lastAction === \"kill\" });\n\t\tthis.lastAction = \"kill\";\n\t\tthis.value = this.value.slice(0, this.cursor);\n\t}\n\n\tprivate deleteWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\n\t\t// Save lastAction before cursor movement (moveWordBackwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordBackwards();\n\t\tconst deleteFrom = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(deleteFrom, this.cursor);\n\t\tthis.killRing.push(deletedText, { prepend: true, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, deleteFrom) + this.value.slice(this.cursor);\n\t\tthis.cursor = deleteFrom;\n\t}\n\n\tprivate deleteWordForward(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\n\t\t// Save lastAction before cursor movement (moveWordForwards resets it)\n\t\tconst wasKill = this.lastAction === \"kill\";\n\n\t\tthis.pushUndo();\n\n\t\tconst oldCursor = this.cursor;\n\t\tthis.moveWordForwards();\n\t\tconst deleteTo = this.cursor;\n\t\tthis.cursor = oldCursor;\n\n\t\tconst deletedText = this.value.slice(this.cursor, deleteTo);\n\t\tthis.killRing.push(deletedText, { prepend: false, accumulate: wasKill });\n\t\tthis.lastAction = \"kill\";\n\n\t\tthis.value = this.value.slice(0, this.cursor) + this.value.slice(deleteTo);\n\t}\n\n\tprivate yank(): void {\n\t\tconst text = this.killRing.peek();\n\t\tif (!text) return;\n\n\t\tthis.pushUndo();\n\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate yankPop(): void {\n\t\tif (this.lastAction !== \"yank\" || this.killRing.length <= 1) return;\n\n\t\tthis.pushUndo();\n\n\t\t// Delete the previously yanked text (still at end of ring before rotation)\n\t\tconst prevText = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor - prevText.length) + this.value.slice(this.cursor);\n\t\tthis.cursor -= prevText.length;\n\n\t\t// Rotate and insert new entry\n\t\tthis.killRing.rotate();\n\t\tconst text = this.killRing.peek() || \"\";\n\t\tthis.value = this.value.slice(0, this.cursor) + text + this.value.slice(this.cursor);\n\t\tthis.cursor += text.length;\n\t\tthis.lastAction = \"yank\";\n\t}\n\n\tprivate pushUndo(): void {\n\t\tthis.undoStack.push({ value: this.value, cursor: this.cursor });\n\t}\n\n\tprivate undo(): void {\n\t\tconst snapshot = this.undoStack.pop();\n\t\tif (!snapshot) return;\n\t\tthis.value = snapshot.value;\n\t\tthis.cursor = snapshot.cursor;\n\t\tthis.lastAction = null;\n\t}\n\n\tprivate moveWordBackwards(): void {\n\t\tif (this.cursor === 0) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordBackward(this.value, this.cursor);\n\t}\n\n\tprivate moveWordForwards(): void {\n\t\tif (this.cursor >= this.value.length) return;\n\t\tthis.lastAction = null;\n\t\tthis.cursor = findWordForward(this.value, this.cursor);\n\t}\n\n\tprivate handlePaste(pastedText: string): void {\n\t\tthis.lastAction = null;\n\t\tthis.pushUndo();\n\n\t\t// Clean the pasted text - remove newlines and carriage returns\n\t\tconst cleanText = pastedText.replace(/\\r\\n/g, \"\").replace(/\\r/g, \"\").replace(/\\n/g, \"\").replace(/\\t/g, \" \");\n\n\t\t// Insert at cursor position\n\t\tthis.value = this.value.slice(0, this.cursor) + cleanText + this.value.slice(this.cursor);\n\t\tthis.cursor += cleanText.length;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\tprivate getVisibleWindow(availableWidth: number): InputVisibleWindow {\n\t\tconst totalWidth = visibleWidth(this.value);\n\t\tif (totalWidth < availableWidth) return { text: this.value, cursor: this.cursor };\n\n\t\tconst scrollWidth = this.cursor === this.value.length ? availableWidth - 1 : availableWidth;\n\t\tif (scrollWidth <= 0) return { text: \"\", cursor: 0 };\n\n\t\tconst cursorColumn = visibleWidth(this.value.slice(0, this.cursor));\n\t\tconst halfWidth = Math.floor(scrollWidth / 2);\n\t\tlet startColumn: number;\n\t\tif (cursorColumn < halfWidth) startColumn = 0;\n\t\telse if (cursorColumn > totalWidth - halfWidth) startColumn = Math.max(0, totalWidth - scrollWidth);\n\t\telse startColumn = Math.max(0, cursorColumn - halfWidth);\n\n\t\tconst text = sliceByColumn(this.value, startColumn, scrollWidth, true);\n\t\tconst beforeCursor = sliceByColumn(this.value, startColumn, Math.max(0, cursorColumn - startColumn), true);\n\t\treturn { text, cursor: beforeCursor.length };\n\t}\n\n\trender(width: number): string[] {\n\t\tconst prompt = \"> \";\n\t\tconst availableWidth = width - prompt.length;\n\t\tif (availableWidth <= 0) return [prompt];\n\n\t\tconst visibleWindow = this.getVisibleWindow(availableWidth);\n\t\tconst visibleText = visibleWindow.text;\n\t\tconst cursorDisplay = visibleWindow.cursor;\n\n\t\t// Build line with fake cursor\n\t\t// Insert cursor character at cursor position\n\t\tconst graphemes = [...segmenter.segment(visibleText.slice(cursorDisplay))];\n\t\tconst cursorGrapheme = graphemes[0];\n\n\t\tconst beforeCursor = visibleText.slice(0, cursorDisplay);\n\t\tconst atCursor = cursorGrapheme?.segment ?? \" \"; // Character at cursor, or space if at end\n\t\tconst afterCursor = visibleText.slice(cursorDisplay + atCursor.length);\n\n\t\t// Hardware cursor marker (zero-width, emitted before fake cursor for IME positioning)\n\t\tconst marker = this.focused ? CURSOR_MARKER : \"\";\n\n\t\t// Use inverse video to show cursor\n\t\tconst cursorChar = `\\x1b[7m${atCursor}\\x1b[27m`; // ESC[7m = reverse video, ESC[27m = normal\n\t\tconst textWithCursor = beforeCursor + marker + cursorChar + afterCursor;\n\n\t\t// Calculate visual width\n\t\tconst visualLength = visibleWidth(textWithCursor);\n\t\tconst padding = \" \".repeat(Math.max(0, availableWidth - visualLength));\n\t\tconst line = prompt + textWithCursor + padding;\n\n\t\treturn [line];\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;GAEG;AACH,qBAAa,MAAO,SAAQ,IAAI;IAC/B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAwB;IAEvC,YACC,EAAE,EAAE,GAAG,EACP,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,OAAO,GAAE,MAAqB,EAC9B,SAAS,CAAC,EAAE,sBAAsB,EAQlC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9B;IAED,KAAK,IAAI,IAAI,CAKZ;IAED,IAAI,IAAI,IAAI,CAMX;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhC;IAED,YAAY,CAAC,SAAS,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAOrD;IAED,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;CASrB"
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACtC,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAKD;;GAEG;AACH,qBAAa,MAAO,SAAQ,IAAI;IAC/B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAwB;IAEvC,YACC,EAAE,EAAE,GAAG,EACP,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,OAAO,GAAE,MAAqB,EAC9B,SAAS,CAAC,EAAE,sBAAsB,EAQlC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9B;IAED,KAAK,IAAI,IAAI,CAKZ;IAED,IAAI,IAAI,IAAI,CAMX;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAGhC;IAED,YAAY,CAAC,SAAS,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAOrD;IAED,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,aAAa;CASrB"}
|
|
@@ -5,18 +5,16 @@ const DEFAULT_INTERVAL_MS = 80;
|
|
|
5
5
|
* Loader component that updates with an optional spinning animation.
|
|
6
6
|
*/
|
|
7
7
|
export class Loader extends Text {
|
|
8
|
-
frames = [...DEFAULT_FRAMES];
|
|
9
|
-
intervalMs = DEFAULT_INTERVAL_MS;
|
|
10
|
-
currentFrame = 0;
|
|
11
|
-
intervalId = null;
|
|
12
|
-
started = false;
|
|
13
|
-
ui = null;
|
|
14
|
-
renderIndicatorVerbatim = false;
|
|
15
|
-
spinnerColorFn;
|
|
16
|
-
messageColorFn;
|
|
17
|
-
message = "Loading...";
|
|
18
8
|
constructor(ui, spinnerColorFn, messageColorFn, message = "Loading...", indicator) {
|
|
19
9
|
super("", 1, 0);
|
|
10
|
+
this.frames = [...DEFAULT_FRAMES];
|
|
11
|
+
this.intervalMs = DEFAULT_INTERVAL_MS;
|
|
12
|
+
this.currentFrame = 0;
|
|
13
|
+
this.intervalId = null;
|
|
14
|
+
this.started = false;
|
|
15
|
+
this.ui = null;
|
|
16
|
+
this.renderIndicatorVerbatim = false;
|
|
17
|
+
this.message = "Loading...";
|
|
20
18
|
this.ui = ui;
|
|
21
19
|
this.spinnerColorFn = spinnerColorFn;
|
|
22
20
|
this.messageColorFn = messageColorFn;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AASjC,MAAM,cAAc,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TUI } from \"../tui.ts\";\nimport { Text } from \"./text.ts\";\n\nexport interface LoaderIndicatorOptions {\n\t/** Animation frames. Use an empty array to hide the indicator. */\n\tframes?: string[];\n\t/** Frame interval in milliseconds for animated indicators. */\n\tintervalMs?: number;\n}\n\nconst DEFAULT_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst DEFAULT_INTERVAL_MS = 80;\n\n/**\n * Loader component that updates with an optional spinning animation.\n */\nexport class Loader extends Text {\n\tprivate frames = [...DEFAULT_FRAMES];\n\tprivate intervalMs = DEFAULT_INTERVAL_MS;\n\tprivate currentFrame = 0;\n\tprivate intervalId: NodeJS.Timeout | null = null;\n\tprivate started = false;\n\tprivate ui: TUI | null = null;\n\tprivate renderIndicatorVerbatim = false;\n\tprivate spinnerColorFn: (str: string) => string;\n\tprivate messageColorFn: (str: string) => string;\n\tprivate message: string = \"Loading...\";\n\n\tconstructor(\n\t\tui: TUI,\n\t\tspinnerColorFn: (str: string) => string,\n\t\tmessageColorFn: (str: string) => string,\n\t\tmessage: string = \"Loading...\",\n\t\tindicator?: LoaderIndicatorOptions,\n\t) {\n\t\tsuper(\"\", 1, 0);\n\t\tthis.ui = ui;\n\t\tthis.spinnerColorFn = spinnerColorFn;\n\t\tthis.messageColorFn = messageColorFn;\n\t\tthis.message = message;\n\t\tthis.setIndicator(indicator);\n\t}\n\n\trender(width: number): string[] {\n\t\tif (!this.started) this.start();\n\t\treturn [\"\", ...super.render(width)];\n\t}\n\n\tstart(): void {\n\t\tif (this.started) return;\n\t\tthis.started = true;\n\t\tthis.updateDisplay();\n\t\tthis.restartAnimation();\n\t}\n\n\tstop(): void {\n\t\tthis.started = false;\n\t\tif (this.intervalId) {\n\t\t\tclearInterval(this.intervalId);\n\t\t\tthis.intervalId = null;\n\t\t}\n\t}\n\n\tsetMessage(message: string): void {\n\t\tthis.message = message;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetIndicator(indicator?: LoaderIndicatorOptions): void {\n\t\tthis.renderIndicatorVerbatim = indicator !== undefined;\n\t\tthis.frames = indicator?.frames !== undefined ? [...indicator.frames] : [...DEFAULT_FRAMES];\n\t\tthis.intervalMs = indicator?.intervalMs && indicator.intervalMs > 0 ? indicator.intervalMs : DEFAULT_INTERVAL_MS;\n\t\tthis.currentFrame = 0;\n\t\tthis.updateDisplay();\n\t\tif (this.started) this.restartAnimation();\n\t}\n\n\tprivate restartAnimation(): void {\n\t\tif (this.intervalId) clearInterval(this.intervalId);\n\t\tthis.intervalId = null;\n\t\tif (this.frames.length <= 1) {\n\t\t\treturn;\n\t\t}\n\t\tthis.intervalId = setInterval(() => {\n\t\t\tthis.currentFrame = (this.currentFrame + 1) % this.frames.length;\n\t\t\tthis.updateDisplay();\n\t\t}, this.intervalMs);\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tconst frame = this.frames[this.currentFrame] ?? \"\";\n\t\tconst renderedFrame = this.renderIndicatorVerbatim ? frame : this.spinnerColorFn(frame);\n\t\tconst indicator = frame.length > 0 ? `${renderedFrame} ` : \"\";\n\t\tthis.setText(`${indicator}${this.messageColorFn(this.message)}`);\n\t\tif (this.ui) {\n\t\t\tthis.ui.requestRender();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/components/markdown.ts"],"names":[],"mappings":"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","sourcesContent":["import { Marked, type Token, Tokenizer, type TokenizerExtension, type Tokens } from \"marked\";\nimport { renderLatex } from \"../latex.ts\";\nimport { getCapabilities, hyperlink, isImageLine } from \"../terminal-image.ts\";\nimport type { Component } from \"../tui.ts\";\nimport { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from \"../utils.ts\";\n\nconst STRICT_STRIKETHROUGH_REGEX = /^(~~)(?=[^\\s~])((?:\\\\.|[^\\\\])*?(?:\\\\.|[^\\s~\\\\]))\\1(?=[^~]|$)/;\nconst HTML_COMMENT_REGEX = /<!--[\\s\\S]*?-->/g;\n\nfunction stripHtmlComments(html: string): string {\n\treturn html.replace(HTML_COMMENT_REGEX, \"\");\n}\n\nclass StrictStrikethroughTokenizer extends Tokenizer {\n\toverride del(src: string): Tokens.Del | undefined {\n\t\tconst match = STRICT_STRIKETHROUGH_REGEX.exec(src);\n\t\tif (!match) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst text = match[2];\n\t\treturn {\n\t\t\ttype: \"del\",\n\t\t\traw: match[0],\n\t\t\ttext,\n\t\t\ttokens: this.lexer.inlineTokens(text),\n\t\t};\n\t}\n}\n\ntype LatexTokenType = \"latex\" | \"latexBlock\";\n\ninterface LatexToken extends Tokens.Generic {\n\ttype: LatexTokenType;\n\ttext: string;\n\tpending?: boolean;\n}\n\nfunction isEscaped(source: string, index: number): boolean {\n\tlet backslashes = 0;\n\tfor (let position = index - 1; position >= 0 && source[position] === \"\\\\\"; position--) {\n\t\tbackslashes++;\n\t}\n\treturn backslashes % 2 === 1;\n}\n\nfunction findClosingDelimiter(source: string, closing: string, start: number): number {\n\tlet index = source.indexOf(closing, start);\n\twhile (index >= 0 && isEscaped(source, index)) {\n\t\tindex = source.indexOf(closing, index + closing.length);\n\t}\n\treturn index;\n}\n\nfunction looksLikePendingDollarMath(source: string): boolean {\n\treturn /\\\\[A-Za-z]+|[_^=+*/<>()[\\]|±≤≥≠≈∈→⇒∞∫∑√-]/.test(source);\n}\n\nfunction looksLikeShellVariableChain(content: string, following: string): boolean {\n\treturn /^[A-Z_][A-Z0-9_]*(?:[/:.-][A-Za-z0-9_./:-]*)*$/.test(content) && /^[A-Za-z_][A-Za-z0-9_]*/.test(following);\n}\n\nfunction tokenizeInlineLatex(source: string): LatexToken | undefined {\n\tlet opening = \"\";\n\tlet closing = \"\";\n\tif (source.startsWith(\"$$\")) {\n\t\topening = \"$$\";\n\t\tclosing = \"$$\";\n\t} else if (source.startsWith(\"\\\\(\")) {\n\t\topening = \"\\\\(\";\n\t\tclosing = \"\\\\)\";\n\t} else if (source.startsWith(\"\\\\[\")) {\n\t\topening = \"\\\\[\";\n\t\tclosing = \"\\\\]\";\n\t} else if (source.startsWith(\"$\") && !/^\\$\\s/.test(source)) {\n\t\topening = \"$\";\n\t\tclosing = \"$\";\n\t} else {\n\t\treturn undefined;\n\t}\n\n\tconst closingIndex = findClosingDelimiter(source, closing, opening.length);\n\tif (\n\t\tclosingIndex >= 0 &&\n\t\topening === \"$\" &&\n\t\t(/\\s$/.test(source.slice(opening.length, closingIndex)) ||\n\t\t\t/^\\d/.test(source.slice(closingIndex + 1)) ||\n\t\t\tlooksLikeShellVariableChain(\n\t\t\t\tsource.slice(opening.length, closingIndex),\n\t\t\t\tsource.slice(closingIndex + closing.length),\n\t\t\t) ||\n\t\t\tsource.slice(opening.length, closingIndex).includes(\"`\"))\n\t) {\n\t\treturn undefined;\n\t}\n\n\tif (closingIndex < 0) {\n\t\tconst pendingSource = source.slice(opening.length);\n\t\tif (opening.startsWith(\"\\\\\") || looksLikePendingDollarMath(pendingSource)) {\n\t\t\treturn { type: \"latex\", raw: source, text: pendingSource, pending: true };\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tconst text = source.slice(opening.length, closingIndex);\n\tif (!text || text.includes(\"\\n\")) {\n\t\treturn undefined;\n\t}\n\n\tconst raw = source.slice(0, closingIndex + closing.length);\n\treturn { type: \"latex\", raw, text };\n}\n\nfunction tokenizeBlockLatex(source: string): LatexToken | undefined {\n\tconst dollarMatch = /^ {0,3}\\$\\$[ \\t]*(?:\\n)?([\\s\\S]*?)\\$\\$[ \\t]*(?:\\n|$)/.exec(source);\n\tif (dollarMatch?.[1]) {\n\t\treturn { type: \"latexBlock\", raw: dollarMatch[0], text: dollarMatch[1].trim() };\n\t}\n\n\tconst bracketMatch = /^ {0,3}\\\\\\[[ \\t]*(?:\\n)?([\\s\\S]*?)\\\\\\][ \\t]*(?:\\n|$)/.exec(source);\n\tif (bracketMatch?.[1]) {\n\t\treturn { type: \"latexBlock\", raw: bracketMatch[0], text: bracketMatch[1].trim() };\n\t}\n\n\tconst pendingBracket = /^ {0,3}\\\\\\[[ \\t]*(?:\\n)?([\\s\\S]*)$/.exec(source);\n\tif (pendingBracket) {\n\t\treturn { type: \"latexBlock\", raw: pendingBracket[0], text: pendingBracket[1], pending: true };\n\t}\n\tconst pendingDollar = /^ {0,3}\\$\\$[ \\t]*(?:\\n)?([\\s\\S]*)$/.exec(source);\n\tif (pendingDollar?.[1] && looksLikePendingDollarMath(pendingDollar[1])) {\n\t\treturn { type: \"latexBlock\", raw: pendingDollar[0], text: pendingDollar[1], pending: true };\n\t}\n\treturn undefined;\n}\n\nconst LATEX_MARKDOWN_EXTENSIONS: readonly TokenizerExtension[] = [\n\t{\n\t\tname: \"latexBlock\",\n\t\tlevel: \"block\",\n\t\tstart(source) {\n\t\t\tconst match = /(?:^|\\n) {0,3}(?:\\$\\$|\\\\\\[)/.exec(source);\n\t\t\treturn match ? match.index + (match[0].startsWith(\"\\n\") ? 1 : 0) : undefined;\n\t\t},\n\t\ttokenizer: tokenizeBlockLatex,\n\t},\n\t{\n\t\tname: \"latex\",\n\t\tlevel: \"inline\",\n\t\tstart(source) {\n\t\t\tconst indices = [source.indexOf(\"$\"), source.indexOf(\"\\\\(\"), source.indexOf(\"\\\\[\")].filter(\n\t\t\t\t(index) => index >= 0,\n\t\t\t);\n\t\t\treturn indices.length > 0 ? Math.min(...indices) : undefined;\n\t\t},\n\t\ttokenizer: tokenizeInlineLatex,\n\t},\n];\n\nconst markdownParser = new Marked();\nmarkdownParser.setOptions({\n\ttokenizer: new StrictStrikethroughTokenizer(),\n});\nmarkdownParser.use({ extensions: [...LATEX_MARKDOWN_EXTENSIONS] });\n\n/**\n * Default text styling for markdown content.\n * Applied to all text unless overridden by markdown formatting.\n */\nexport interface DefaultTextStyle {\n\t/** Foreground color function */\n\tcolor?: (text: string) => string;\n\t/** Background color function */\n\tbgColor?: (text: string) => string;\n\t/** Bold text */\n\tbold?: boolean;\n\t/** Italic text */\n\titalic?: boolean;\n\t/** Strikethrough text */\n\tstrikethrough?: boolean;\n\t/** Underline text */\n\tunderline?: boolean;\n}\n\n/**\n * Theme functions for markdown elements.\n * Each function takes text and returns styled text with ANSI codes.\n */\nexport interface MarkdownTheme {\n\theading: (text: string) => string;\n\tlink: (text: string) => string;\n\tlinkUrl: (text: string) => string;\n\tcode: (text: string) => string;\n\tcodeBlock: (text: string) => string;\n\tcodeBlockBorder: (text: string) => string;\n\tquote: (text: string) => string;\n\tquoteBorder: (text: string) => string;\n\thr: (text: string) => string;\n\tlistBullet: (text: string) => string;\n\tbold: (text: string) => string;\n\titalic: (text: string) => string;\n\tstrikethrough: (text: string) => string;\n\tunderline: (text: string) => string;\n\thighlightCode?: (code: string, lang?: string) => string[];\n\t/** Prefix applied to each rendered code block line (default: \" \") */\n\tcodeBlockIndent?: string;\n}\n\nexport interface MarkdownOptions {\n\t/** Preserve source ordered-list markers instead of normalizing them from the list start. */\n\tpreserveOrderedListMarkers?: boolean;\n\t/** Preserve source backslash escapes instead of normalizing escaped punctuation. */\n\tpreserveBackslashEscapes?: boolean;\n\t/** Transform source Markdown before parsing, with the exact width available for content. */\n\ttransform?: (markdown: string, availableWidth: number) => string;\n\t/** Render supported LaTeX math expressions as Unicode text (default: true). */\n\trenderLatex?: boolean;\n}\n\ninterface InlineStyleContext {\n\tapplyText: (text: string) => string;\n\tstylePrefix: string;\n}\n\ninterface TableColumnMeasurements {\n\tnaturalWidths: number[];\n\tminimumWidths: number[];\n}\n\nexport class Markdown implements Component {\n\tprivate text: string;\n\tprivate paddingX: number; // Left/right padding\n\tprivate paddingY: number; // Top/bottom padding\n\tprivate defaultTextStyle?: DefaultTextStyle;\n\tprivate theme: MarkdownTheme;\n\tprivate options: MarkdownOptions;\n\tprivate defaultStylePrefix?: string;\n\n\t// Cache for rendered output\n\tprivate cachedText?: string;\n\tprivate cachedWidth?: number;\n\tprivate cachedLines?: string[];\n\n\tconstructor(\n\t\ttext: string,\n\t\tpaddingX: number,\n\t\tpaddingY: number,\n\t\ttheme: MarkdownTheme,\n\t\tdefaultTextStyle?: DefaultTextStyle,\n\t\toptions?: MarkdownOptions,\n\t) {\n\t\tthis.text = text;\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.theme = theme;\n\t\tthis.defaultTextStyle = defaultTextStyle;\n\t\tthis.options = options ? { ...options } : {};\n\t}\n\n\tsetText(text: string): void {\n\t\tthis.text = text;\n\t\tthis.invalidate();\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cachedText = undefined;\n\t\tthis.cachedWidth = undefined;\n\t\tthis.cachedLines = undefined;\n\t}\n\n\tprivate renderMarkdownTokens(text: string, contentWidth: number): string[] {\n\t\tconst tokens = markdownParser.lexer(text);\n\t\tconst renderedLines: string[] = [];\n\t\tfor (let index = 0; index < tokens.length; index++) {\n\t\t\tconst tokenLines = this.renderToken(tokens[index], contentWidth, tokens[index + 1]?.type);\n\t\t\tfor (const tokenLine of tokenLines) renderedLines.push(tokenLine);\n\t\t}\n\t\treturn renderedLines;\n\t}\n\n\tprivate wrapRenderedLines(renderedLines: string[], contentWidth: number): string[] {\n\t\tconst wrappedLines: string[] = [];\n\t\tfor (const line of renderedLines) {\n\t\t\tif (isImageLine(line)) {\n\t\t\t\twrappedLines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const wrappedLine of wrapTextWithAnsi(line, contentWidth)) wrappedLines.push(wrappedLine);\n\t\t}\n\t\treturn wrappedLines;\n\t}\n\n\tprivate padRenderedLines(lines: string[], width: number, paddingX: number): string[] {\n\t\tconst margin = \" \".repeat(paddingX);\n\t\tconst bgFn = this.defaultTextStyle?.bgColor;\n\t\tconst contentLines: string[] = [];\n\t\tfor (const line of lines) {\n\t\t\tif (isImageLine(line)) {\n\t\t\t\tcontentLines.push(line);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst lineWithMargins = margin + line + margin;\n\t\t\tif (bgFn) {\n\t\t\t\tcontentLines.push(applyBackgroundToLine(lineWithMargins, width, bgFn));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst paddingNeeded = Math.max(0, width - visibleWidth(lineWithMargins));\n\t\t\tcontentLines.push(lineWithMargins + \" \".repeat(paddingNeeded));\n\t\t}\n\t\treturn contentLines;\n\t}\n\n\tprivate createVerticalPadding(width: number): string[] {\n\t\tconst bgFn = this.defaultTextStyle?.bgColor;\n\t\tconst emptyLine = \" \".repeat(width);\n\t\tconst emptyLines: string[] = [];\n\t\tfor (let index = 0; index < this.paddingY; index++) {\n\t\t\temptyLines.push(bgFn ? applyBackgroundToLine(emptyLine, width, bgFn) : emptyLine);\n\t\t}\n\t\treturn emptyLines;\n\t}\n\n\tprivate cacheRenderedLines(width: number, lines: string[]): void {\n\t\tthis.cachedText = this.text;\n\t\tthis.cachedWidth = width;\n\t\tthis.cachedLines = lines;\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.cachedLines && this.cachedText === this.text && this.cachedWidth === width) {\n\t\t\treturn this.cachedLines;\n\t\t}\n\n\t\tconst effectivePaddingX = Math.min(this.paddingX, Math.floor(Math.max(0, width - 1) / 2));\n\t\tconst contentWidth = Math.max(1, width - effectivePaddingX * 2);\n\t\tconst text = this.options.transform?.(this.text, contentWidth) ?? this.text;\n\t\tif (!text || text.trim() === \"\") {\n\t\t\tconst result: string[] = [];\n\t\t\tthis.cacheRenderedLines(width, result);\n\t\t\treturn result;\n\t\t}\n\n\t\tconst normalizedText = text.replace(/\\t/g, \" \");\n\t\tconst renderedLines = this.renderMarkdownTokens(normalizedText, contentWidth);\n\t\tconst wrappedLines = this.wrapRenderedLines(renderedLines, contentWidth);\n\t\tconst contentLines = this.padRenderedLines(wrappedLines, width, effectivePaddingX);\n\t\tconst verticalPadding = this.createVerticalPadding(width);\n\t\tconst result = verticalPadding.concat(contentLines, verticalPadding);\n\t\tthis.cacheRenderedLines(width, result);\n\t\treturn result.length > 0 ? result : [\"\"];\n\t}\n\n\t/**\n\t * Apply default text style to a string.\n\t * This is the base styling applied to all text content.\n\t * NOTE: Background color is NOT applied here - it's applied at the padding stage\n\t * to ensure it extends to the full line width.\n\t */\n\tprivate applyDefaultStyle(text: string): string {\n\t\tif (!this.defaultTextStyle) {\n\t\t\treturn text;\n\t\t}\n\n\t\tlet styled = text;\n\n\t\t// Apply foreground color (NOT background - that's applied at padding stage)\n\t\tif (this.defaultTextStyle.color) {\n\t\t\tstyled = this.defaultTextStyle.color(styled);\n\t\t}\n\n\t\t// Apply text decorations using this.theme\n\t\tif (this.defaultTextStyle.bold) {\n\t\t\tstyled = this.theme.bold(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.italic) {\n\t\t\tstyled = this.theme.italic(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.strikethrough) {\n\t\t\tstyled = this.theme.strikethrough(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.underline) {\n\t\t\tstyled = this.theme.underline(styled);\n\t\t}\n\n\t\treturn styled;\n\t}\n\n\tprivate getDefaultStylePrefix(): string {\n\t\tif (!this.defaultTextStyle) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tif (this.defaultStylePrefix !== undefined) {\n\t\t\treturn this.defaultStylePrefix;\n\t\t}\n\n\t\tconst sentinel = \"\\u0000\";\n\t\tlet styled = sentinel;\n\n\t\tif (this.defaultTextStyle.color) {\n\t\t\tstyled = this.defaultTextStyle.color(styled);\n\t\t}\n\n\t\tif (this.defaultTextStyle.bold) {\n\t\t\tstyled = this.theme.bold(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.italic) {\n\t\t\tstyled = this.theme.italic(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.strikethrough) {\n\t\t\tstyled = this.theme.strikethrough(styled);\n\t\t}\n\t\tif (this.defaultTextStyle.underline) {\n\t\t\tstyled = this.theme.underline(styled);\n\t\t}\n\n\t\tconst sentinelIndex = styled.indexOf(sentinel);\n\t\tthis.defaultStylePrefix = sentinelIndex >= 0 ? styled.slice(0, sentinelIndex) : \"\";\n\t\treturn this.defaultStylePrefix;\n\t}\n\n\tprivate getStylePrefix(styleFn: (text: string) => string): string {\n\t\tconst sentinel = \"\\u0000\";\n\t\tconst styled = styleFn(sentinel);\n\t\tconst sentinelIndex = styled.indexOf(sentinel);\n\t\treturn sentinelIndex >= 0 ? styled.slice(0, sentinelIndex) : \"\";\n\t}\n\n\tprivate getDefaultInlineStyleContext(): InlineStyleContext {\n\t\treturn {\n\t\t\tapplyText: (text: string) => this.applyDefaultStyle(text),\n\t\t\tstylePrefix: this.getDefaultStylePrefix(),\n\t\t};\n\t}\n\n\tprivate shouldAppendBlockSpacing(nextTokenType?: string): boolean {\n\t\treturn Boolean(nextTokenType && nextTokenType !== \"space\");\n\t}\n\n\tprivate renderHeadingToken(token: Tokens.Heading, nextTokenType?: string): string[] {\n\t\tconst headingPrefix = `${\"#\".repeat(token.depth)} `;\n\t\tconst headingStyle =\n\t\t\ttoken.depth === 1\n\t\t\t\t? (text: string) => this.theme.heading(this.theme.bold(this.theme.underline(text)))\n\t\t\t\t: (text: string) => this.theme.heading(this.theme.bold(text));\n\t\tconst styleContext: InlineStyleContext = {\n\t\t\tapplyText: headingStyle,\n\t\t\tstylePrefix: this.getStylePrefix(headingStyle),\n\t\t};\n\t\tconst headingText = this.renderInlineTokens(token.tokens || [], styleContext);\n\t\tconst styledHeading = token.depth >= 3 ? headingStyle(headingPrefix) + headingText : headingText;\n\t\treturn this.shouldAppendBlockSpacing(nextTokenType) ? [styledHeading, \"\"] : [styledHeading];\n\t}\n\n\tprivate renderParagraphToken(\n\t\ttoken: Tokens.Paragraph,\n\t\tnextTokenType?: string,\n\t\tstyleContext?: InlineStyleContext,\n\t): string[] {\n\t\tconst lines = [this.renderInlineTokens(token.tokens || [], styleContext)];\n\t\tif (nextTokenType && nextTokenType !== \"list\" && nextTokenType !== \"space\") lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderLatexBlockToken(token: LatexToken, width: number, nextTokenType?: string): string[] {\n\t\tconst candidate =\n\t\t\t!token.pending && this.options.renderLatex !== false ? renderLatex(token.text, { display: true }) : undefined;\n\t\tconst rendered = candidate?.split(\"\\n\").every((line) => visibleWidth(line) <= width)\n\t\t\t? candidate\n\t\t\t: token.raw.trim();\n\t\tconst lines = rendered.split(\"\\n\").map((line) => this.applyDefaultStyle(line));\n\t\tif (this.shouldAppendBlockSpacing(nextTokenType)) lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderCodeToken(token: Tokens.Code, nextTokenType?: string): string[] {\n\t\tconst indent = this.theme.codeBlockIndent ?? \" \";\n\t\tconst lines = [this.theme.codeBlockBorder(`\\`\\`\\`${token.lang || \"\"}`)];\n\t\tif (this.theme.highlightCode) {\n\t\t\tfor (const highlightedLine of this.theme.highlightCode(token.text, token.lang)) {\n\t\t\t\tlines.push(`${indent}${highlightedLine}`);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const codeLine of token.text.split(\"\\n\")) {\n\t\t\t\tlines.push(`${indent}${this.theme.codeBlock(codeLine)}`);\n\t\t\t}\n\t\t}\n\t\tlines.push(this.theme.codeBlockBorder(\"```\"));\n\t\tif (this.shouldAppendBlockSpacing(nextTokenType)) lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderBlockquoteToken(token: Tokens.Blockquote, width: number, nextTokenType?: string): string[] {\n\t\tconst quoteStyle = (text: string) => this.theme.quote(this.theme.italic(text));\n\t\tconst quoteStylePrefix = this.getStylePrefix(quoteStyle);\n\t\tconst quoteContentWidth = Math.max(1, width - 2);\n\t\tconst quoteStyleContext: InlineStyleContext = {\n\t\t\tapplyText: (text: string) => text,\n\t\t\tstylePrefix: quoteStylePrefix,\n\t\t};\n\t\tconst renderedQuoteLines: string[] = [];\n\t\tconst quoteTokens = token.tokens || [];\n\t\tfor (let index = 0; index < quoteTokens.length; index++) {\n\t\t\trenderedQuoteLines.push(\n\t\t\t\t...this.renderToken(quoteTokens[index], quoteContentWidth, quoteTokens[index + 1]?.type, quoteStyleContext),\n\t\t\t);\n\t\t}\n\t\twhile (renderedQuoteLines.length > 0 && renderedQuoteLines[renderedQuoteLines.length - 1] === \"\") {\n\t\t\trenderedQuoteLines.pop();\n\t\t}\n\n\t\tconst lines: string[] = [];\n\t\tfor (const quoteLine of renderedQuoteLines) {\n\t\t\tconst lineWithReappliedStyle = quoteStylePrefix\n\t\t\t\t? quoteLine.replace(/\\x1b\\[0m/g, `\\x1b[0m${quoteStylePrefix}`)\n\t\t\t\t: quoteLine;\n\t\t\tfor (const wrappedLine of wrapTextWithAnsi(quoteStyle(lineWithReappliedStyle), quoteContentWidth)) {\n\t\t\t\tlines.push(this.theme.quoteBorder(\"│ \") + wrappedLine);\n\t\t\t}\n\t\t}\n\t\tif (this.shouldAppendBlockSpacing(nextTokenType)) lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderHorizontalRule(width: number, nextTokenType?: string): string[] {\n\t\tconst lines = [this.theme.hr(\"─\".repeat(Math.min(width, 80)))];\n\t\tif (this.shouldAppendBlockSpacing(nextTokenType)) lines.push(\"\");\n\t\treturn lines;\n\t}\n\n\tprivate renderHtmlBlock(token: Token): string[] {\n\t\tif (!(\"raw\" in token) || typeof token.raw !== \"string\") return [];\n\t\tconst visibleHtml = stripHtmlComments(token.raw).trim();\n\t\treturn visibleHtml ? [this.applyDefaultStyle(visibleHtml)] : [];\n\t}\n\n\tprivate renderFallbackToken(token: Token): string[] {\n\t\treturn \"text\" in token && typeof token.text === \"string\" ? [token.text] : [];\n\t}\n\n\tprivate renderToken(\n\t\ttoken: Token,\n\t\twidth: number,\n\t\tnextTokenType?: string,\n\t\tstyleContext?: InlineStyleContext,\n\t): string[] {\n\t\tswitch (token.type) {\n\t\t\tcase \"heading\":\n\t\t\t\treturn this.renderHeadingToken(token as Tokens.Heading, nextTokenType);\n\t\t\tcase \"paragraph\":\n\t\t\t\treturn this.renderParagraphToken(token as Tokens.Paragraph, nextTokenType, styleContext);\n\t\t\tcase \"text\":\n\t\t\t\treturn [this.renderInlineTokens([token], styleContext)];\n\t\t\tcase \"latexBlock\":\n\t\t\t\treturn this.renderLatexBlockToken(token as LatexToken, width, nextTokenType);\n\t\t\tcase \"code\":\n\t\t\t\treturn this.renderCodeToken(token as Tokens.Code, nextTokenType);\n\t\t\tcase \"list\":\n\t\t\t\treturn this.renderList(token as Tokens.List, 0, width, styleContext);\n\t\t\tcase \"table\":\n\t\t\t\treturn this.renderTable(token as Tokens.Table, width, nextTokenType, styleContext);\n\t\t\tcase \"blockquote\":\n\t\t\t\treturn this.renderBlockquoteToken(token as Tokens.Blockquote, width, nextTokenType);\n\t\t\tcase \"hr\":\n\t\t\t\treturn this.renderHorizontalRule(width, nextTokenType);\n\t\t\tcase \"html\":\n\t\t\t\treturn this.renderHtmlBlock(token);\n\t\t\tcase \"space\":\n\t\t\t\treturn [\"\"];\n\t\t\tdefault:\n\t\t\t\treturn this.renderFallbackToken(token);\n\t\t}\n\t}\n\n\tprivate applyInlineText(text: string, styleContext: InlineStyleContext): string {\n\t\treturn text\n\t\t\t.split(\"\\n\")\n\t\t\t.map((segment) => styleContext.applyText(segment))\n\t\t\t.join(\"\\n\");\n\t}\n\n\tprivate renderInlineLinkToken(token: Tokens.Link, styleContext: InlineStyleContext): string {\n\t\tconst linkText = this.renderInlineTokens(token.tokens || [], styleContext);\n\t\tconst styledLink = this.theme.link(this.theme.underline(linkText));\n\t\tif (getCapabilities().hyperlinks) {\n\t\t\t// OSC 8: render as a clickable hyperlink. The URL is not printed inline,\n\t\t\t// so we always show only the link text regardless of whether it matches href.\n\t\t\treturn hyperlink(styledLink, token.href) + styleContext.stylePrefix;\n\t\t}\n\n\t\t// Fallback: print URL in parentheses when text differs from href.\n\t\t// Compare raw token.text (not styled) against href for the equality check.\n\t\t// For mailto: links strip the prefix (autolinked emails use text=\"foo@bar.com\"\n\t\t// but href=\"mailto:foo@bar.com\").\n\t\tconst hrefForComparison = token.href.startsWith(\"mailto:\") ? token.href.slice(7) : token.href;\n\t\tif (token.text === token.href || token.text === hrefForComparison) {\n\t\t\treturn styledLink + styleContext.stylePrefix;\n\t\t}\n\t\treturn styledLink + this.theme.linkUrl(` (${token.href})`) + styleContext.stylePrefix;\n\t}\n\n\tprivate renderInlineLatexToken(token: LatexToken, styleContext: InlineStyleContext): string {\n\t\tconst rendered =\n\t\t\t!token.pending && this.options.renderLatex !== false ? (renderLatex(token.text) ?? token.raw) : token.raw;\n\t\treturn this.applyInlineText(rendered, styleContext);\n\t}\n\n\tprivate renderInlineTextToken(token: Tokens.Text, styleContext: InlineStyleContext): string {\n\t\t// Text tokens in list items can have nested tokens for inline formatting.\n\t\treturn token.tokens?.length\n\t\t\t? this.renderInlineTokens(token.tokens, styleContext)\n\t\t\t: this.applyInlineText(token.text, styleContext);\n\t}\n\n\tprivate renderInlineHtmlToken(token: Token, styleContext: InlineStyleContext): string {\n\t\tif (!(\"raw\" in token) || typeof token.raw !== \"string\") return \"\";\n\t\tconst visibleHtml = stripHtmlComments(token.raw);\n\t\treturn visibleHtml ? this.applyInlineText(visibleHtml, styleContext) : \"\";\n\t}\n\n\tprivate renderInlineFallbackToken(token: Token, styleContext: InlineStyleContext): string {\n\t\tif (!(\"text\" in token) || typeof token.text !== \"string\") return \"\";\n\t\treturn this.applyInlineText(token.text, styleContext);\n\t}\n\n\tprivate renderInlineToken(token: Token, styleContext: InlineStyleContext): string {\n\t\tconst { stylePrefix } = styleContext;\n\t\tswitch (token.type) {\n\t\t\tcase \"latex\":\n\t\t\t\treturn this.renderInlineLatexToken(token as LatexToken, styleContext);\n\t\t\tcase \"escape\":\n\t\t\t\treturn this.applyInlineText(this.options.preserveBackslashEscapes ? token.raw : token.text, styleContext);\n\t\t\tcase \"text\":\n\t\t\t\treturn this.renderInlineTextToken(token as Tokens.Text, styleContext);\n\t\t\tcase \"paragraph\":\n\t\t\t\treturn this.renderInlineTokens(token.tokens || [], styleContext);\n\t\t\tcase \"strong\":\n\t\t\t\treturn this.theme.bold(this.renderInlineTokens(token.tokens || [], styleContext)) + stylePrefix;\n\t\t\tcase \"em\":\n\t\t\t\treturn this.theme.italic(this.renderInlineTokens(token.tokens || [], styleContext)) + stylePrefix;\n\t\t\tcase \"codespan\":\n\t\t\t\treturn this.theme.code(token.text) + stylePrefix;\n\t\t\tcase \"link\":\n\t\t\t\treturn this.renderInlineLinkToken(token as Tokens.Link, styleContext);\n\t\t\tcase \"br\":\n\t\t\t\treturn \"\\n\";\n\t\t\tcase \"del\":\n\t\t\t\treturn this.theme.strikethrough(this.renderInlineTokens(token.tokens || [], styleContext)) + stylePrefix;\n\t\t\tcase \"html\":\n\t\t\t\treturn this.renderInlineHtmlToken(token, styleContext);\n\t\t\tdefault:\n\t\t\t\treturn this.renderInlineFallbackToken(token, styleContext);\n\t\t}\n\t}\n\n\tprivate renderInlineTokens(tokens: Token[], styleContext?: InlineStyleContext): string {\n\t\tconst resolvedStyleContext = styleContext ?? this.getDefaultInlineStyleContext();\n\t\tlet result = \"\";\n\t\tfor (const token of tokens) {\n\t\t\tresult += this.renderInlineToken(token, resolvedStyleContext);\n\t\t}\n\n\t\twhile (resolvedStyleContext.stylePrefix && result.endsWith(resolvedStyleContext.stylePrefix)) {\n\t\t\tresult = result.slice(0, -resolvedStyleContext.stylePrefix.length);\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate getOrderedListMarker(item: Tokens.ListItem): string | undefined {\n\t\tconst match = /^(?: {0,3})(\\d{1,9}[.)])[ \\t]+/.exec(item.raw);\n\t\treturn match ? `${match[1]} ` : undefined;\n\t}\n\n\tprivate getListItemMarker(token: Tokens.List, item: Tokens.ListItem, index: number, startNumber: number): string {\n\t\tif (!token.ordered) return \"- \";\n\t\tif (this.options.preserveOrderedListMarkers) {\n\t\t\treturn this.getOrderedListMarker(item) ?? `${startNumber + index}. `;\n\t\t}\n\t\treturn `${startNumber + index}. `;\n\t}\n\n\tprivate renderListItem(\n\t\ttoken: Tokens.List,\n\t\titem: Tokens.ListItem,\n\t\tindex: number,\n\t\tstartNumber: number,\n\t\tdepth: number,\n\t\twidth: number,\n\t\tstyleContext?: InlineStyleContext,\n\t): string[] {\n\t\tconst indent = \" \".repeat(depth);\n\t\tconst bullet = this.getListItemMarker(token, item, index, startNumber);\n\t\tconst taskMarker = item.task ? `[${item.checked ? \"x\" : \" \"}] ` : \"\";\n\t\tconst marker = bullet + taskMarker;\n\t\tconst firstPrefix = indent + this.theme.listBullet(marker);\n\t\tconst continuationPrefix = indent + \" \".repeat(visibleWidth(marker));\n\t\tconst itemWidth = Math.max(1, width - visibleWidth(firstPrefix));\n\t\tconst lines: string[] = [];\n\t\tlet renderedAnyLine = false;\n\n\t\tfor (const itemToken of item.tokens) {\n\t\t\tif (itemToken.type === \"list\") {\n\t\t\t\tlines.push(...this.renderList(itemToken as Tokens.List, depth + 1, width, styleContext));\n\t\t\t\trenderedAnyLine = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst itemLines = this.renderToken(itemToken, itemWidth, undefined, styleContext);\n\t\t\tconst wrappedItemLines = itemLines.flatMap((line) => wrapTextWithAnsi(line, itemWidth));\n\t\t\tfor (const wrappedLine of wrappedItemLines) {\n\t\t\t\tconst linePrefix = renderedAnyLine ? continuationPrefix : firstPrefix;\n\t\t\t\tlines.push(linePrefix + wrappedLine);\n\t\t\t\trenderedAnyLine = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!renderedAnyLine) lines.push(firstPrefix);\n\t\treturn lines;\n\t}\n\n\t/**\n\t * Render a list with proper nesting support\n\t */\n\tprivate renderList(token: Tokens.List, depth: number, width: number, styleContext?: InlineStyleContext): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst startNumber = typeof token.start === \"number\" ? token.start : 1;\n\t\tfor (let index = 0; index < token.items.length; index++) {\n\t\t\tlines.push(...this.renderListItem(token, token.items[index], index, startNumber, depth, width, styleContext));\n\t\t}\n\t\treturn lines;\n\t}\n\n\t/**\n\t * Get the visible width of the longest word in a string.\n\t */\n\tprivate getLongestWordWidth(text: string, maxWidth?: number): number {\n\t\tconst words = text.split(/\\s+/).filter((word) => word.length > 0);\n\t\tlet longest = 0;\n\t\tfor (const word of words) {\n\t\t\tlongest = Math.max(longest, visibleWidth(word));\n\t\t}\n\t\tif (maxWidth === undefined) {\n\t\t\treturn longest;\n\t\t}\n\t\treturn Math.min(longest, maxWidth);\n\t}\n\n\t/**\n\t * Wrap a table cell to fit into a column.\n\t *\n\t * Delegates to wrapTextWithAnsi() so ANSI codes + long tokens are handled\n\t * consistently with the rest of the renderer.\n\t */\n\tprivate wrapCellText(text: string, maxWidth: number): string[] {\n\t\treturn wrapTextWithAnsi(text, Math.max(1, maxWidth));\n\t}\n\n\t/**\n\t * Render a table with width-aware cell wrapping.\n\t * Cells that don't fit are wrapped to multiple lines.\n\t */\n\tprivate measureTableColumns(\n\t\ttoken: Tokens.Table,\n\t\tstyleContext: InlineStyleContext | undefined,\n\t): TableColumnMeasurements {\n\t\tconst naturalWidths: number[] = [];\n\t\tconst minimumWidths: number[] = [];\n\t\tconst maxUnbrokenWordWidth = 30;\n\t\tfor (let column = 0; column < token.header.length; column++) {\n\t\t\tconst headerText = this.renderInlineTokens(token.header[column].tokens || [], styleContext);\n\t\t\tnaturalWidths[column] = visibleWidth(headerText);\n\t\t\tminimumWidths[column] = Math.max(1, this.getLongestWordWidth(headerText, maxUnbrokenWordWidth));\n\t\t}\n\t\tfor (const row of token.rows) {\n\t\t\tfor (let column = 0; column < row.length; column++) {\n\t\t\t\tconst cellText = this.renderInlineTokens(row[column].tokens || [], styleContext);\n\t\t\t\tnaturalWidths[column] = Math.max(naturalWidths[column] || 0, visibleWidth(cellText));\n\t\t\t\tminimumWidths[column] = Math.max(\n\t\t\t\t\tminimumWidths[column] || 1,\n\t\t\t\t\tthis.getLongestWordWidth(cellText, maxUnbrokenWordWidth),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\treturn { naturalWidths, minimumWidths };\n\t}\n\n\tprivate fitMinimumTableColumnWidths(minimumWidths: number[], availableForCells: number): number[] {\n\t\tif (minimumWidths.reduce((total, width) => total + width, 0) <= availableForCells) return minimumWidths;\n\t\tconst fittedWidths = new Array(minimumWidths.length).fill(1);\n\t\tconst remaining = availableForCells - minimumWidths.length;\n\t\tif (remaining <= 0) return fittedWidths;\n\t\tconst totalWeight = minimumWidths.reduce((total, width) => total + Math.max(0, width - 1), 0);\n\t\tconst growth = minimumWidths.map((width) => {\n\t\t\tconst weight = Math.max(0, width - 1);\n\t\t\treturn totalWeight > 0 ? Math.floor((weight / totalWeight) * remaining) : 0;\n\t\t});\n\t\tfor (let column = 0; column < minimumWidths.length; column++) fittedWidths[column] += growth[column] ?? 0;\n\t\tconst allocated = growth.reduce((total, width) => total + width, 0);\n\t\tlet leftover = remaining - allocated;\n\t\tfor (let column = 0; leftover > 0 && column < minimumWidths.length; column++) {\n\t\t\tfittedWidths[column]++;\n\t\t\tleftover--;\n\t\t}\n\t\treturn fittedWidths;\n\t}\n\n\tprivate distributeRemainingTableWidth(\n\t\tcolumnWidths: number[],\n\t\tnaturalWidths: number[],\n\t\tremainingWidth: number,\n\t): void {\n\t\tlet remaining = remainingWidth;\n\t\twhile (remaining > 0) {\n\t\t\tlet grew = false;\n\t\t\tfor (let column = 0; column < columnWidths.length && remaining > 0; column++) {\n\t\t\t\tif (columnWidths[column] >= naturalWidths[column]) continue;\n\t\t\t\tcolumnWidths[column]++;\n\t\t\t\tremaining--;\n\t\t\t\tgrew = true;\n\t\t\t}\n\t\t\tif (!grew) break;\n\t\t}\n\t}\n\n\tprivate calculateTableColumnWidths(\n\t\tnaturalWidths: number[],\n\t\tminimumWidths: number[],\n\t\tavailableForCells: number,\n\t\tborderOverhead: number,\n\t): number[] {\n\t\tconst fittedMinimumWidths = this.fitMinimumTableColumnWidths(minimumWidths, availableForCells);\n\t\tconst totalNaturalWidth = naturalWidths.reduce((total, width) => total + width, 0) + borderOverhead;\n\t\tif (totalNaturalWidth <= availableForCells + borderOverhead) {\n\t\t\treturn naturalWidths.map((width, column) => Math.max(width, fittedMinimumWidths[column]));\n\t\t}\n\t\tconst minimumCellsWidth = fittedMinimumWidths.reduce((total, width) => total + width, 0);\n\t\tconst totalGrowPotential = naturalWidths.reduce(\n\t\t\t(total, width, column) => total + Math.max(0, width - fittedMinimumWidths[column]),\n\t\t\t0,\n\t\t);\n\t\tconst extraWidth = Math.max(0, availableForCells - minimumCellsWidth);\n\t\tconst columnWidths = fittedMinimumWidths.map((minimumWidth, column) => {\n\t\t\tconst growthPotential = Math.max(0, naturalWidths[column] - minimumWidth);\n\t\t\tconst growth = totalGrowPotential > 0 ? Math.floor((growthPotential / totalGrowPotential) * extraWidth) : 0;\n\t\t\treturn minimumWidth + growth;\n\t\t});\n\t\tconst allocated = columnWidths.reduce((total, width) => total + width, 0);\n\t\tthis.distributeRemainingTableWidth(columnWidths, naturalWidths, availableForCells - allocated);\n\t\treturn columnWidths;\n\t}\n\n\tprivate renderTableBorder(columnWidths: number[], left: string, junction: string, right: string): string {\n\t\treturn `${left}─${columnWidths.map((width) => \"─\".repeat(width)).join(`─${junction}─`)}─${right}`;\n\t}\n\n\tprivate renderTableCellLines(\n\t\tcells: Tokens.TableCell[],\n\t\tcolumnWidths: number[],\n\t\tstyleContext: InlineStyleContext | undefined,\n\t): string[][] {\n\t\treturn cells.map((cell, column) => {\n\t\t\tconst text = this.renderInlineTokens(cell.tokens || [], styleContext);\n\t\t\treturn this.wrapCellText(text, columnWidths[column]);\n\t\t});\n\t}\n\n\t// grip-ignore noExcessiveCollectionIterations: Every rendered table line must format each column once, so this rectangular traversal is linear in the emitted cell slots. @class:229-925\n\tprivate appendTableRow(lines: string[], cellLines: string[][], columnWidths: number[], bold: boolean): void {\n\t\tconst lineCount = Math.max(...cellLines.map((cell) => cell.length));\n\t\tfor (let lineIndex = 0; lineIndex < lineCount; lineIndex++) {\n\t\t\tconst rowParts = cellLines.map((columnLines, column) => {\n\t\t\t\tconst text = columnLines[lineIndex] || \"\";\n\t\t\t\tconst padded = text + \" \".repeat(Math.max(0, columnWidths[column] - visibleWidth(text)));\n\t\t\t\treturn bold ? this.theme.bold(padded) : padded;\n\t\t\t});\n\t\t\tlines.push(`│ ${rowParts.join(\" │ \")} │`);\n\t\t}\n\t}\n\n\tprivate renderTable(\n\t\ttoken: Tokens.Table,\n\t\tavailableWidth: number,\n\t\tnextTokenType?: string,\n\t\tstyleContext?: InlineStyleContext,\n\t): string[] {\n\t\tconst columnCount = token.header.length;\n\t\tif (columnCount === 0) return [];\n\t\tconst borderOverhead = 3 * columnCount + 1;\n\t\tconst availableForCells = availableWidth - borderOverhead;\n\t\tif (availableForCells < columnCount) {\n\t\t\tconst fallbackLines = token.raw ? wrapTextWithAnsi(token.raw, availableWidth) : [];\n\t\t\tif (nextTokenType && nextTokenType !== \"space\") fallbackLines.push(\"\");\n\t\t\treturn fallbackLines;\n\t\t}\n\n\t\tconst measurements = this.measureTableColumns(token, styleContext);\n\t\tconst columnWidths = this.calculateTableColumnWidths(\n\t\t\tmeasurements.naturalWidths,\n\t\t\tmeasurements.minimumWidths,\n\t\t\tavailableForCells,\n\t\t\tborderOverhead,\n\t\t);\n\t\tconst lines = [this.renderTableBorder(columnWidths, \"┌\", \"┬\", \"┐\")];\n\t\tthis.appendTableRow(\n\t\t\tlines,\n\t\t\tthis.renderTableCellLines(token.header, columnWidths, styleContext),\n\t\t\tcolumnWidths,\n\t\t\ttrue,\n\t\t);\n\t\tconst separator = this.renderTableBorder(columnWidths, \"├\", \"┼\", \"┤\");\n\t\tlines.push(separator);\n\t\tfor (let rowIndex = 0; rowIndex < token.rows.length; rowIndex++) {\n\t\t\tthis.appendTableRow(\n\t\t\t\tlines,\n\t\t\t\tthis.renderTableCellLines(token.rows[rowIndex], columnWidths, styleContext),\n\t\t\t\tcolumnWidths,\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\tif (rowIndex < token.rows.length - 1) lines.push(separator);\n\t\t}\n\t\tlines.push(this.renderTableBorder(columnWidths, \"└\", \"┴\", \"┘\"));\n\t\tif (nextTokenType && nextTokenType !== \"space\") lines.push(\"\");\n\t\treturn lines;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/components/markdown.ts"],"names":[],"mappings":"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"}
|
|
@@ -132,17 +132,6 @@ markdownParser.setOptions({
|
|
|
132
132
|
});
|
|
133
133
|
markdownParser.use({ extensions: [...LATEX_MARKDOWN_EXTENSIONS] });
|
|
134
134
|
export class Markdown {
|
|
135
|
-
text;
|
|
136
|
-
paddingX; // Left/right padding
|
|
137
|
-
paddingY; // Top/bottom padding
|
|
138
|
-
defaultTextStyle;
|
|
139
|
-
theme;
|
|
140
|
-
options;
|
|
141
|
-
defaultStylePrefix;
|
|
142
|
-
// Cache for rendered output
|
|
143
|
-
cachedText;
|
|
144
|
-
cachedWidth;
|
|
145
|
-
cachedLines;
|
|
146
135
|
constructor(text, paddingX, paddingY, theme, defaultTextStyle, options) {
|
|
147
136
|
this.text = text;
|
|
148
137
|
this.paddingX = paddingX;
|