@kolisachint/hoocode-tui 0.5.72 → 0.5.74
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/components/box.d.ts +12 -5
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +28 -20
- package/dist/components/box.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/tui.d.ts +84 -36
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +244 -52
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts +13 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +40 -0
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/components/box.d.ts
CHANGED
|
@@ -50,14 +50,21 @@ export declare class Box implements Component {
|
|
|
50
50
|
* a shadow function captured at construction would still be painting the old
|
|
51
51
|
* theme's ink (or reaching for a colour the new theme never defined).
|
|
52
52
|
*
|
|
53
|
-
* The shadow itself is one extra row of
|
|
54
|
-
* paints
|
|
53
|
+
* The shadow itself is one extra row of `▔` — an upper *one-eighth* block,
|
|
54
|
+
* which paints a hairline across the top of its cells and so hugs the box's
|
|
55
55
|
* bottom edge — indented one column to give the offset a terminal cannot
|
|
56
|
-
* give in sub-pixels. An inset box gets a matching column of
|
|
57
|
-
* right edge and the
|
|
58
|
-
*
|
|
56
|
+
* give in sub-pixels. An inset box gets a matching column of `▏` down its
|
|
57
|
+
* right edge, and the run stops where that column starts, so the two meet as
|
|
58
|
+
* an L; with no inset the band already owns the last cell, so the bottom
|
|
59
59
|
* edge is all there is room for.
|
|
60
60
|
*
|
|
61
|
+
* Eighths, not halves. `▀` and `▌` are half a cell of solid ink, which at a
|
|
62
|
+
* terminal's resolution is not a shadow but a second band of colour wrapped
|
|
63
|
+
* around two sides of every message — heavy enough to pull the eye off the
|
|
64
|
+
* text it is supposed to sit behind. A shadow's whole job is to be noticed
|
|
65
|
+
* without being looked at, and an eighth is as close to that as a cell grid
|
|
66
|
+
* gets.
|
|
67
|
+
*
|
|
61
68
|
* Passing no provider, or one that returns nothing, draws the full-width
|
|
62
69
|
* band the box has always drawn.
|
|
63
70
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../../src/components/box.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAY3C;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,GAAI,YAAW,SAAS;IACpC,QAAQ,EAAE,SAAS,EAAE,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAC,CAA2B;IACxC,OAAO,CAAC,OAAO,CAAC,CAA+B;IAG/C,OAAO,CAAC,KAAK,CAAC,CAAc;IAE5B,YAAY,QAAQ,SAAI,EAAE,QAAQ,SAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAItE;IAED;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIlC;IAED,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAGnC;IAED,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAMtC;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAG7C;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,UAAU,GAAG,SAAS,GAAG,IAAI,CAGrD;IAED,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,UAAU;IAmBlB,UAAU,IAAI,IAAI,CAKjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA8G9B;IAED,OAAO,CAAC,OAAO;CAUf","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { applyBackgroundToLine, visibleWidth } from \"../utils.js\";\n\ntype RenderCache = {\n\tchildLines: string[];\n\twidth: number;\n\tbgSample: string | undefined;\n\tshadowSample: string | undefined;\n\tinset: number;\n\tlines: string[];\n};\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow and the gutter it holds back from the right margin. Both are optional,\n * and a sheet with neither is the plain full-width band.\n */\nexport interface PaperSheet {\n\t/** Paints the shadow's own glyphs. Omitted, the sheet casts no shadow. */\n\tshadow?: (text: string) => string;\n\t/**\n\t * Columns of page held back at the right margin.\n\t *\n\t * A block that runs the full width of the terminal has no right edge to\n\t * show: it is a band of colour between two screen edges, not a sheet on a\n\t * page. Reserving a few columns gives it one, which is what makes both the\n\t * shadow's right-hand column and a visible right edge possible at all.\n\t */\n\tinset?: number;\n}\n\n/**\n * Box component - a container that applies padding and background to all children\n */\nexport class Box implements Component {\n\tchildren: Component[] = [];\n\tprivate paddingX: number;\n\tprivate paddingY: number;\n\tprivate bgFn?: (text: string) => string;\n\tprivate paperFn?: () => PaperSheet | undefined;\n\n\t// Cache for rendered output\n\tprivate cache?: RenderCache;\n\n\tconstructor(paddingX = 1, paddingY = 1, bgFn?: (text: string) => string) {\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.bgFn = bgFn;\n\t}\n\n\t/**\n\t * Change the horizontal padding after construction.\n\t *\n\t * For a box whose padding exists to carry a background band: with no band to\n\t * draw, the padding is just an indent that pushes the content out of line\n\t * with everything around it.\n\t */\n\tsetPaddingX(paddingX: number): void {\n\t\tif (this.paddingX === paddingX) return;\n\t\tthis.paddingX = paddingX;\n\t\tthis.invalidateCache();\n\t}\n\n\taddChild(component: Component): void {\n\t\tthis.children.push(component);\n\t\tthis.invalidateCache();\n\t}\n\n\tremoveChild(component: Component): void {\n\t\tconst index = this.children.indexOf(component);\n\t\tif (index !== -1) {\n\t\t\tthis.children.splice(index, 1);\n\t\t\tthis.invalidateCache();\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.children = [];\n\t\tthis.invalidateCache();\n\t}\n\n\tsetBgFn(bgFn?: (text: string) => string): void {\n\t\tthis.bgFn = bgFn;\n\t\t// Don't invalidate here - we'll detect bgFn changes by sampling output\n\t}\n\n\t/**\n\t * Give the box the paper treatment, resolved on every frame.\n\t *\n\t * A shadow and the gutter it needs arrive together because they are one\n\t * decision, and it is a decision the *theme* makes — so the box asks for it\n\t * at render time rather than being told once. A box built under one theme\n\t * outlives it: the user switches theme with the block already on screen, and\n\t * a shadow function captured at construction would still be painting the old\n\t * theme's ink (or reaching for a colour the new theme never defined).\n\t *\n\t * The shadow itself is one extra row of `▀` — an upper half-block, which\n\t * paints solid colour across the top half of its cells and so hugs the box's\n\t * bottom edge — indented one column to give the offset a terminal cannot\n\t * give in sub-pixels. An inset box gets a matching column of `▌` down its\n\t * right edge and the bottom run reaches under it, so the two close the\n\t * corner; with no inset the band already owns the last cell, so the bottom\n\t * edge is all there is room for.\n\t *\n\t * Passing no provider, or one that returns nothing, draws the full-width\n\t * band the box has always drawn.\n\t */\n\tsetPaper(paperFn?: () => PaperSheet | undefined): void {\n\t\tthis.paperFn = paperFn;\n\t\tthis.invalidateCache();\n\t}\n\n\tprivate invalidateCache(): void {\n\t\tthis.cache = undefined;\n\t}\n\n\tprivate matchCache(\n\t\twidth: number,\n\t\tchildLines: string[],\n\t\tbgSample: string | undefined,\n\t\tshadowSample: string | undefined,\n\t\tinset: number,\n\t): boolean {\n\t\tconst cache = this.cache;\n\t\treturn (\n\t\t\t!!cache &&\n\t\t\tcache.width === width &&\n\t\t\tcache.bgSample === bgSample &&\n\t\t\tcache.shadowSample === shadowSample &&\n\t\t\tcache.inset === inset &&\n\t\t\tcache.childLines.length === childLines.length &&\n\t\t\tcache.childLines.every((line, i) => line === childLines[i])\n\t\t);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.invalidateCache();\n\t\tfor (const child of this.children) {\n\t\t\tchild.invalidate?.();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.children.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// The paper treatment is asked for per frame, so a box already on screen\n\t\t// follows a theme switch instead of holding the treatment it was built\n\t\t// with.\n\t\tconst paper = this.paperFn?.();\n\t\t// A sheet needs a band wide enough to carry its own bottom run. Narrower\n\t\t// than that the gutter has nowhere to go: the treatment degenerated into\n\t\t// a one-column band with a shadow beside it and no run under it. Below\n\t\t// the threshold the box falls back to the plain full-width band a theme\n\t\t// without paper draws, which is the honest answer at that size.\n\t\tconst wide = width - Math.max(0, paper?.inset ?? 0) > 1;\n\t\tconst shadowFn = wide ? paper?.shadow : undefined;\n\t\tconst inset = wide ? Math.max(0, paper?.inset ?? 0) : 0;\n\n\t\t// The band stops short of the right margin when inset, leaving a gutter of\n\t\t// page for the sheet's own edge and the shadow that follows it.\n\t\tconst bandWidth = Math.max(1, width - inset);\n\t\t// Padding only exists to carry the band, so it gives way rather than\n\t\t// pushing content off the end of it. Clamping `contentWidth` alone was\n\t\t// not enough: at a width where the gutter and two columns of padding\n\t\t// leave no room, the row came out wider than the band it was supposed\n\t\t// to fill, the shadow's column went with it, and the line wrapped past\n\t\t// the right margin. Both halves are derived from the band instead.\n\t\tconst paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((bandWidth - 1) / 2)));\n\t\tconst contentWidth = Math.max(1, bandWidth - paddingX * 2);\n\t\tconst leftPad = \" \".repeat(paddingX);\n\n\t\t// Render all children\n\t\tconst childLines: string[] = [];\n\t\tfor (const child of this.children) {\n\t\t\tconst lines = child.render(contentWidth);\n\t\t\tfor (const line of lines) {\n\t\t\t\tchildLines.push(leftPad + line);\n\t\t\t}\n\t\t}\n\n\t\tif (childLines.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Check if bgFn output changed by sampling\n\t\tconst bgSample = this.bgFn ? this.bgFn(\"test\") : undefined;\n\t\tconst shadowSample = shadowFn ? shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample, inset)) {\n\t\t\treturn this.cache!.lines;\n\t\t}\n\n\t\t// Apply background and padding\n\t\tconst rows: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\t\trows.push(...childLines);\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Every row of the sheet ends in the same column.\n\t\t\t//\n\t\t\t// The edge used to be nicked one column in on roughly every fifth\n\t\t\t// row, to read as cut by hand rather than ruled. At a terminal's\n\t\t\t// resolution it could not: a nick is a whole cell, which is a step\n\t\t\t// far too coarse to read as the wobble of a pair of scissors, and it\n\t\t\t// landed as damage instead. On the top row — the one row with no\n\t\t\t// shadow behind it, because the offset is down as well as right — it\n\t\t\t// showed as a bite taken out of the sheet's corner. Everywhere else\n\t\t\t// it was backfilled with a block of shadow ink, which put a tooth of\n\t\t\t// shadow *inside* the sheet's own outline. Both are the same mistake\n\t\t\t// seen from two sides: the fill was leaving holes and the shadow was\n\t\t\t// covering for them. A ruled edge has neither.\n\t\t\tconst band = this.applyBg(line, bandWidth);\n\t\t\t// `▌` paints the left half of its cell, so the column reads as a thin\n\t\t\t// line hugging the sheet rather than a second band beside it. The\n\t\t\t// first row has no column at all: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? shadowFn(\"▌\") : \"\";\n\t\t\tresult.push(band + column);\n\t\t});\n\n\t\t// The shadow's bottom run, offset one column right of the band. It ends\n\t\t// under the right-hand column so the two close the corner; with no\n\t\t// gutter there is no such column, and the run stops one cell short of\n\t\t// the margin instead of wrapping past it.\n\t\t//\n\t\t// That last cell is `▘`, not `▀`. The run is a full-width glyph and the\n\t\t// column above it is a half-width one, so a run that ended on `▀`\n\t\t// overshot the column by half a cell and left a tip poking out past the\n\t\t// corner — a stray line coming out of the shadow. `▘` is the same top\n\t\t// half narrowed to the column's own width, so the two edges close flush.\n\t\tif (shadowFn && bandWidth > 1) {\n\t\t\tconst run = \"▀\".repeat(bandWidth - 1);\n\t\t\tresult.push(` ${shadowFn(hasColumn ? `${run}▘` : run)}`);\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache = {\n\t\t\tchildLines,\n\t\t\twidth,\n\t\t\tbgSample,\n\t\t\tshadowSample,\n\t\t\tinset,\n\t\t\tlines: result,\n\t\t};\n\n\t\treturn result;\n\t}\n\n\tprivate applyBg(line: string, width: number): string {\n\t\tconst visLen = visibleWidth(line);\n\t\tconst padNeeded = Math.max(0, width - visLen);\n\t\tconst padded = line + \" \".repeat(padNeeded);\n\n\t\tif (this.bgFn) {\n\t\t\treturn applyBackgroundToLine(padded, width, this.bgFn);\n\t\t}\n\t\treturn padded;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../../src/components/box.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAY3C;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,GAAI,YAAW,SAAS;IACpC,QAAQ,EAAE,SAAS,EAAE,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAC,CAA2B;IACxC,OAAO,CAAC,OAAO,CAAC,CAA+B;IAG/C,OAAO,CAAC,KAAK,CAAC,CAAc;IAE5B,YAAY,QAAQ,SAAI,EAAE,QAAQ,SAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAItE;IAED;;;;;;OAMG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAIlC;IAED,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAGnC;IAED,WAAW,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAMtC;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAG7C;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,UAAU,GAAG,SAAS,GAAG,IAAI,CAGrD;IAED,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,UAAU;IAmBlB,UAAU,IAAI,IAAI,CAKjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA+G9B;IAED,OAAO,CAAC,OAAO;CAUf","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { applyBackgroundToLine, visibleWidth } from \"../utils.js\";\n\ntype RenderCache = {\n\tchildLines: string[];\n\twidth: number;\n\tbgSample: string | undefined;\n\tshadowSample: string | undefined;\n\tinset: number;\n\tlines: string[];\n};\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow and the gutter it holds back from the right margin. Both are optional,\n * and a sheet with neither is the plain full-width band.\n */\nexport interface PaperSheet {\n\t/** Paints the shadow's own glyphs. Omitted, the sheet casts no shadow. */\n\tshadow?: (text: string) => string;\n\t/**\n\t * Columns of page held back at the right margin.\n\t *\n\t * A block that runs the full width of the terminal has no right edge to\n\t * show: it is a band of colour between two screen edges, not a sheet on a\n\t * page. Reserving a few columns gives it one, which is what makes both the\n\t * shadow's right-hand column and a visible right edge possible at all.\n\t */\n\tinset?: number;\n}\n\n/**\n * Box component - a container that applies padding and background to all children\n */\nexport class Box implements Component {\n\tchildren: Component[] = [];\n\tprivate paddingX: number;\n\tprivate paddingY: number;\n\tprivate bgFn?: (text: string) => string;\n\tprivate paperFn?: () => PaperSheet | undefined;\n\n\t// Cache for rendered output\n\tprivate cache?: RenderCache;\n\n\tconstructor(paddingX = 1, paddingY = 1, bgFn?: (text: string) => string) {\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.bgFn = bgFn;\n\t}\n\n\t/**\n\t * Change the horizontal padding after construction.\n\t *\n\t * For a box whose padding exists to carry a background band: with no band to\n\t * draw, the padding is just an indent that pushes the content out of line\n\t * with everything around it.\n\t */\n\tsetPaddingX(paddingX: number): void {\n\t\tif (this.paddingX === paddingX) return;\n\t\tthis.paddingX = paddingX;\n\t\tthis.invalidateCache();\n\t}\n\n\taddChild(component: Component): void {\n\t\tthis.children.push(component);\n\t\tthis.invalidateCache();\n\t}\n\n\tremoveChild(component: Component): void {\n\t\tconst index = this.children.indexOf(component);\n\t\tif (index !== -1) {\n\t\t\tthis.children.splice(index, 1);\n\t\t\tthis.invalidateCache();\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.children = [];\n\t\tthis.invalidateCache();\n\t}\n\n\tsetBgFn(bgFn?: (text: string) => string): void {\n\t\tthis.bgFn = bgFn;\n\t\t// Don't invalidate here - we'll detect bgFn changes by sampling output\n\t}\n\n\t/**\n\t * Give the box the paper treatment, resolved on every frame.\n\t *\n\t * A shadow and the gutter it needs arrive together because they are one\n\t * decision, and it is a decision the *theme* makes — so the box asks for it\n\t * at render time rather than being told once. A box built under one theme\n\t * outlives it: the user switches theme with the block already on screen, and\n\t * a shadow function captured at construction would still be painting the old\n\t * theme's ink (or reaching for a colour the new theme never defined).\n\t *\n\t * The shadow itself is one extra row of `▔` — an upper *one-eighth* block,\n\t * which paints a hairline across the top of its cells and so hugs the box's\n\t * bottom edge — indented one column to give the offset a terminal cannot\n\t * give in sub-pixels. An inset box gets a matching column of `▏` down its\n\t * right edge, and the run stops where that column starts, so the two meet as\n\t * an L; with no inset the band already owns the last cell, so the bottom\n\t * edge is all there is room for.\n\t *\n\t * Eighths, not halves. `▀` and `▌` are half a cell of solid ink, which at a\n\t * terminal's resolution is not a shadow but a second band of colour wrapped\n\t * around two sides of every message — heavy enough to pull the eye off the\n\t * text it is supposed to sit behind. A shadow's whole job is to be noticed\n\t * without being looked at, and an eighth is as close to that as a cell grid\n\t * gets.\n\t *\n\t * Passing no provider, or one that returns nothing, draws the full-width\n\t * band the box has always drawn.\n\t */\n\tsetPaper(paperFn?: () => PaperSheet | undefined): void {\n\t\tthis.paperFn = paperFn;\n\t\tthis.invalidateCache();\n\t}\n\n\tprivate invalidateCache(): void {\n\t\tthis.cache = undefined;\n\t}\n\n\tprivate matchCache(\n\t\twidth: number,\n\t\tchildLines: string[],\n\t\tbgSample: string | undefined,\n\t\tshadowSample: string | undefined,\n\t\tinset: number,\n\t): boolean {\n\t\tconst cache = this.cache;\n\t\treturn (\n\t\t\t!!cache &&\n\t\t\tcache.width === width &&\n\t\t\tcache.bgSample === bgSample &&\n\t\t\tcache.shadowSample === shadowSample &&\n\t\t\tcache.inset === inset &&\n\t\t\tcache.childLines.length === childLines.length &&\n\t\t\tcache.childLines.every((line, i) => line === childLines[i])\n\t\t);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.invalidateCache();\n\t\tfor (const child of this.children) {\n\t\t\tchild.invalidate?.();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.children.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// The paper treatment is asked for per frame, so a box already on screen\n\t\t// follows a theme switch instead of holding the treatment it was built\n\t\t// with.\n\t\tconst paper = this.paperFn?.();\n\t\t// A sheet needs a band wide enough to carry its own bottom run. Narrower\n\t\t// than that the gutter has nowhere to go: the treatment degenerated into\n\t\t// a one-column band with a shadow beside it and no run under it. Below\n\t\t// the threshold the box falls back to the plain full-width band a theme\n\t\t// without paper draws, which is the honest answer at that size.\n\t\tconst wide = width - Math.max(0, paper?.inset ?? 0) > 1;\n\t\tconst shadowFn = wide ? paper?.shadow : undefined;\n\t\tconst inset = wide ? Math.max(0, paper?.inset ?? 0) : 0;\n\n\t\t// The band stops short of the right margin when inset, leaving a gutter of\n\t\t// page for the sheet's own edge and the shadow that follows it.\n\t\tconst bandWidth = Math.max(1, width - inset);\n\t\t// Padding only exists to carry the band, so it gives way rather than\n\t\t// pushing content off the end of it. Clamping `contentWidth` alone was\n\t\t// not enough: at a width where the gutter and two columns of padding\n\t\t// leave no room, the row came out wider than the band it was supposed\n\t\t// to fill, the shadow's column went with it, and the line wrapped past\n\t\t// the right margin. Both halves are derived from the band instead.\n\t\tconst paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((bandWidth - 1) / 2)));\n\t\tconst contentWidth = Math.max(1, bandWidth - paddingX * 2);\n\t\tconst leftPad = \" \".repeat(paddingX);\n\n\t\t// Render all children\n\t\tconst childLines: string[] = [];\n\t\tfor (const child of this.children) {\n\t\t\tconst lines = child.render(contentWidth);\n\t\t\tfor (const line of lines) {\n\t\t\t\tchildLines.push(leftPad + line);\n\t\t\t}\n\t\t}\n\n\t\tif (childLines.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Check if bgFn output changed by sampling\n\t\tconst bgSample = this.bgFn ? this.bgFn(\"test\") : undefined;\n\t\tconst shadowSample = shadowFn ? shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample, inset)) {\n\t\t\treturn this.cache!.lines;\n\t\t}\n\n\t\t// Apply background and padding\n\t\tconst rows: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\t\trows.push(...childLines);\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Every row of the sheet ends in the same column.\n\t\t\t//\n\t\t\t// The edge used to be nicked one column in on roughly every fifth\n\t\t\t// row, to read as cut by hand rather than ruled. At a terminal's\n\t\t\t// resolution it could not: a nick is a whole cell, which is a step\n\t\t\t// far too coarse to read as the wobble of a pair of scissors, and it\n\t\t\t// landed as damage instead. On the top row — the one row with no\n\t\t\t// shadow behind it, because the offset is down as well as right — it\n\t\t\t// showed as a bite taken out of the sheet's corner. Everywhere else\n\t\t\t// it was backfilled with a block of shadow ink, which put a tooth of\n\t\t\t// shadow *inside* the sheet's own outline. Both are the same mistake\n\t\t\t// seen from two sides: the fill was leaving holes and the shadow was\n\t\t\t// covering for them. A ruled edge has neither.\n\t\t\tconst band = this.applyBg(line, bandWidth);\n\t\t\t// `▏` paints the leftmost eighth of its cell, so the column reads as a\n\t\t\t// hairline hugging the sheet rather than a band beside it. The first row\n\t\t\t// has no column at all: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? shadowFn(\"▏\") : \"\";\n\t\t\tresult.push(band + column);\n\t\t});\n\n\t\t// The shadow's bottom run, offset one column right of the band, and\n\t\t// nothing after it — the gutter cell the column occupies stays empty on\n\t\t// this row.\n\t\t//\n\t\t// The corner closes on its own. `▔` paints its cell edge to edge, so a\n\t\t// run of `bandWidth - 1` starting in column 1 ends at exactly the cell\n\t\t// boundary the column's `▏` paints its hairline on: the horizontal leg's\n\t\t// right end abuts the vertical leg's left edge. Carrying `▏` down into\n\t\t// this row to \"close\" it instead overshot the other way — `▏` fills the\n\t\t// full height of its cell while `▔` fills the top eighth of one, so the\n\t\t// glyph hung a whole row below the shadow's bottom edge and the L came\n\t\t// out as a cross with a tick poking under it.\n\t\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▔\".repeat(bandWidth - 1))}`);\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache = {\n\t\t\tchildLines,\n\t\t\twidth,\n\t\t\tbgSample,\n\t\t\tshadowSample,\n\t\t\tinset,\n\t\t\tlines: result,\n\t\t};\n\n\t\treturn result;\n\t}\n\n\tprivate applyBg(line: string, width: number): string {\n\t\tconst visLen = visibleWidth(line);\n\t\tconst padNeeded = Math.max(0, width - visLen);\n\t\tconst padded = line + \" \".repeat(padNeeded);\n\n\t\tif (this.bgFn) {\n\t\t\treturn applyBackgroundToLine(padded, width, this.bgFn);\n\t\t}\n\t\treturn padded;\n\t}\n}\n"]}
|
package/dist/components/box.js
CHANGED
|
@@ -57,14 +57,21 @@ export class Box {
|
|
|
57
57
|
* a shadow function captured at construction would still be painting the old
|
|
58
58
|
* theme's ink (or reaching for a colour the new theme never defined).
|
|
59
59
|
*
|
|
60
|
-
* The shadow itself is one extra row of
|
|
61
|
-
* paints
|
|
60
|
+
* The shadow itself is one extra row of `▔` — an upper *one-eighth* block,
|
|
61
|
+
* which paints a hairline across the top of its cells and so hugs the box's
|
|
62
62
|
* bottom edge — indented one column to give the offset a terminal cannot
|
|
63
|
-
* give in sub-pixels. An inset box gets a matching column of
|
|
64
|
-
* right edge and the
|
|
65
|
-
*
|
|
63
|
+
* give in sub-pixels. An inset box gets a matching column of `▏` down its
|
|
64
|
+
* right edge, and the run stops where that column starts, so the two meet as
|
|
65
|
+
* an L; with no inset the band already owns the last cell, so the bottom
|
|
66
66
|
* edge is all there is room for.
|
|
67
67
|
*
|
|
68
|
+
* Eighths, not halves. `▀` and `▌` are half a cell of solid ink, which at a
|
|
69
|
+
* terminal's resolution is not a shadow but a second band of colour wrapped
|
|
70
|
+
* around two sides of every message — heavy enough to pull the eye off the
|
|
71
|
+
* text it is supposed to sit behind. A shadow's whole job is to be noticed
|
|
72
|
+
* without being looked at, and an eighth is as close to that as a cell grid
|
|
73
|
+
* gets.
|
|
74
|
+
*
|
|
68
75
|
* Passing no provider, or one that returns nothing, draws the full-width
|
|
69
76
|
* band the box has always drawn.
|
|
70
77
|
*/
|
|
@@ -162,25 +169,26 @@ export class Box {
|
|
|
162
169
|
// seen from two sides: the fill was leaving holes and the shadow was
|
|
163
170
|
// covering for them. A ruled edge has neither.
|
|
164
171
|
const band = this.applyBg(line, bandWidth);
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
const column = hasColumn && index > 0 ? shadowFn("
|
|
172
|
+
// `▏` paints the leftmost eighth of its cell, so the column reads as a
|
|
173
|
+
// hairline hugging the sheet rather than a band beside it. The first row
|
|
174
|
+
// has no column at all: the offset is down *and* right.
|
|
175
|
+
const column = hasColumn && index > 0 ? shadowFn("▏") : "";
|
|
169
176
|
result.push(band + column);
|
|
170
177
|
});
|
|
171
|
-
// The shadow's bottom run, offset one column right of the band
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
// the margin instead of wrapping past it.
|
|
178
|
+
// The shadow's bottom run, offset one column right of the band, and
|
|
179
|
+
// nothing after it — the gutter cell the column occupies stays empty on
|
|
180
|
+
// this row.
|
|
175
181
|
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
182
|
+
// The corner closes on its own. `▔` paints its cell edge to edge, so a
|
|
183
|
+
// run of `bandWidth - 1` starting in column 1 ends at exactly the cell
|
|
184
|
+
// boundary the column's `▏` paints its hairline on: the horizontal leg's
|
|
185
|
+
// right end abuts the vertical leg's left edge. Carrying `▏` down into
|
|
186
|
+
// this row to "close" it instead overshot the other way — `▏` fills the
|
|
187
|
+
// full height of its cell while `▔` fills the top eighth of one, so the
|
|
188
|
+
// glyph hung a whole row below the shadow's bottom edge and the L came
|
|
189
|
+
// out as a cross with a tick poking under it.
|
|
181
190
|
if (shadowFn && bandWidth > 1) {
|
|
182
|
-
|
|
183
|
-
result.push(` ${shadowFn(hasColumn ? `${run}▘` : run)}`);
|
|
191
|
+
result.push(` ${shadowFn("▔".repeat(bandWidth - 1))}`);
|
|
184
192
|
}
|
|
185
193
|
// Update cache
|
|
186
194
|
this.cache = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.js","sourceRoot":"","sources":["../../src/components/box.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AA8BlE;;GAEG;AACH,MAAM,OAAO,GAAG;IACf,QAAQ,GAAgB,EAAE,CAAC;IACnB,QAAQ,CAAS;IACjB,QAAQ,CAAS;IACjB,IAAI,CAA4B;IAChC,OAAO,CAAgC;IAE/C,4BAA4B;IACpB,KAAK,CAAe;IAE5B,YAAY,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,EAAE,IAA+B,EAAE;QACxE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;IAED;;;;;;OAMG;IACH,WAAW,CAAC,QAAgB,EAAQ;QACnC,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ;YAAE,OAAO;QACvC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,eAAe,EAAE,CAAC;IAAA,CACvB;IAED,QAAQ,CAAC,SAAoB,EAAQ;QACpC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,eAAe,EAAE,CAAC;IAAA,CACvB;IAED,WAAW,CAAC,SAAoB,EAAQ;QACvC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,eAAe,EAAE,CAAC;QACxB,CAAC;IAAA,CACD;IAED,KAAK,GAAS;QACb,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,eAAe,EAAE,CAAC;IAAA,CACvB;IAED,OAAO,CAAC,IAA+B,EAAQ;QAC9C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,uEAAuE;IADtD,CAEjB;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,OAAsC,EAAQ;QACtD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,eAAe,EAAE,CAAC;IAAA,CACvB;IAEO,eAAe,GAAS;QAC/B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IAAA,CACvB;IAEO,UAAU,CACjB,KAAa,EACb,UAAoB,EACpB,QAA4B,EAC5B,YAAgC,EAChC,KAAa,EACH;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,OAAO,CACN,CAAC,CAAC,KAAK;YACP,KAAK,CAAC,KAAK,KAAK,KAAK;YACrB,KAAK,CAAC,QAAQ,KAAK,QAAQ;YAC3B,KAAK,CAAC,YAAY,KAAK,YAAY;YACnC,KAAK,CAAC,KAAK,KAAK,KAAK;YACrB,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM;YAC7C,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,CAAC,CAC3D,CAAC;IAAA,CACF;IAED,UAAU,GAAS;QAClB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;QACtB,CAAC;IAAA,CACD;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,OAAO,EAAE,CAAC;QACX,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,QAAQ;QACR,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/B,yEAAyE;QACzE,yEAAyE;QACzE,uEAAuE;QACvE,wEAAwE;QACxE,gEAAgE;QAChE,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAExD,2EAA2E;QAC3E,gEAAgE;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;QAC7C,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,sEAAsE;QACtE,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAErC,sBAAsB;QACtB,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAC1B,UAAU,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;YACjC,CAAC;QACF,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,EAAE,CAAC;QACX,CAAC;QAED,2CAA2C;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE7D,uBAAuB;QACvB,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;YACvE,OAAO,IAAI,CAAC,KAAM,CAAC,KAAK,CAAC;QAC1B,CAAC;QAED,+BAA+B;QAC/B,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEtD,uEAAuE;QACvE,MAAM,SAAS,GAAG,QAAQ,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC;QACtD,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC7B,kDAAkD;YAClD,EAAE;YACF,kEAAkE;YAClE,iEAAiE;YACjE,mEAAmE;YACnE,qEAAqE;YACrE,mEAAiE;YACjE,uEAAqE;YACrE,oEAAoE;YACpE,qEAAqE;YACrE,qEAAqE;YACrE,qEAAqE;YACrE,+CAA+C;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YAC3C,wEAAsE;YACtE,kEAAkE;YAClE,kEAAkE;YAClE,MAAM,MAAM,GAAG,SAAS,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;QAAA,CAC3B,CAAC,CAAC;QAEH,wEAAwE;QACxE,mEAAmE;QACnE,sEAAsE;QACtE,0CAA0C;QAC1C,EAAE;QACF,4EAAwE;QACxE,oEAAkE;QAClE,wEAAwE;QACxE,0EAAsE;QACtE,yEAAyE;QACzE,IAAI,QAAQ,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAG,KAAG,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,KAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QAED,eAAe;QACf,IAAI,CAAC,KAAK,GAAG;YACZ,UAAU;YACV,KAAK;YACL,QAAQ;YACR,YAAY;YACZ,KAAK;YACL,KAAK,EAAE,MAAM;SACb,CAAC;QAEF,OAAO,MAAM,CAAC;IAAA,CACd;IAEO,OAAO,CAAC,IAAY,EAAE,KAAa,EAAU;QACpD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAE5C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,MAAM,CAAC;IAAA,CACd;CACD","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { applyBackgroundToLine, visibleWidth } from \"../utils.js\";\n\ntype RenderCache = {\n\tchildLines: string[];\n\twidth: number;\n\tbgSample: string | undefined;\n\tshadowSample: string | undefined;\n\tinset: number;\n\tlines: string[];\n};\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow and the gutter it holds back from the right margin. Both are optional,\n * and a sheet with neither is the plain full-width band.\n */\nexport interface PaperSheet {\n\t/** Paints the shadow's own glyphs. Omitted, the sheet casts no shadow. */\n\tshadow?: (text: string) => string;\n\t/**\n\t * Columns of page held back at the right margin.\n\t *\n\t * A block that runs the full width of the terminal has no right edge to\n\t * show: it is a band of colour between two screen edges, not a sheet on a\n\t * page. Reserving a few columns gives it one, which is what makes both the\n\t * shadow's right-hand column and a visible right edge possible at all.\n\t */\n\tinset?: number;\n}\n\n/**\n * Box component - a container that applies padding and background to all children\n */\nexport class Box implements Component {\n\tchildren: Component[] = [];\n\tprivate paddingX: number;\n\tprivate paddingY: number;\n\tprivate bgFn?: (text: string) => string;\n\tprivate paperFn?: () => PaperSheet | undefined;\n\n\t// Cache for rendered output\n\tprivate cache?: RenderCache;\n\n\tconstructor(paddingX = 1, paddingY = 1, bgFn?: (text: string) => string) {\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.bgFn = bgFn;\n\t}\n\n\t/**\n\t * Change the horizontal padding after construction.\n\t *\n\t * For a box whose padding exists to carry a background band: with no band to\n\t * draw, the padding is just an indent that pushes the content out of line\n\t * with everything around it.\n\t */\n\tsetPaddingX(paddingX: number): void {\n\t\tif (this.paddingX === paddingX) return;\n\t\tthis.paddingX = paddingX;\n\t\tthis.invalidateCache();\n\t}\n\n\taddChild(component: Component): void {\n\t\tthis.children.push(component);\n\t\tthis.invalidateCache();\n\t}\n\n\tremoveChild(component: Component): void {\n\t\tconst index = this.children.indexOf(component);\n\t\tif (index !== -1) {\n\t\t\tthis.children.splice(index, 1);\n\t\t\tthis.invalidateCache();\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.children = [];\n\t\tthis.invalidateCache();\n\t}\n\n\tsetBgFn(bgFn?: (text: string) => string): void {\n\t\tthis.bgFn = bgFn;\n\t\t// Don't invalidate here - we'll detect bgFn changes by sampling output\n\t}\n\n\t/**\n\t * Give the box the paper treatment, resolved on every frame.\n\t *\n\t * A shadow and the gutter it needs arrive together because they are one\n\t * decision, and it is a decision the *theme* makes — so the box asks for it\n\t * at render time rather than being told once. A box built under one theme\n\t * outlives it: the user switches theme with the block already on screen, and\n\t * a shadow function captured at construction would still be painting the old\n\t * theme's ink (or reaching for a colour the new theme never defined).\n\t *\n\t * The shadow itself is one extra row of `▀` — an upper half-block, which\n\t * paints solid colour across the top half of its cells and so hugs the box's\n\t * bottom edge — indented one column to give the offset a terminal cannot\n\t * give in sub-pixels. An inset box gets a matching column of `▌` down its\n\t * right edge and the bottom run reaches under it, so the two close the\n\t * corner; with no inset the band already owns the last cell, so the bottom\n\t * edge is all there is room for.\n\t *\n\t * Passing no provider, or one that returns nothing, draws the full-width\n\t * band the box has always drawn.\n\t */\n\tsetPaper(paperFn?: () => PaperSheet | undefined): void {\n\t\tthis.paperFn = paperFn;\n\t\tthis.invalidateCache();\n\t}\n\n\tprivate invalidateCache(): void {\n\t\tthis.cache = undefined;\n\t}\n\n\tprivate matchCache(\n\t\twidth: number,\n\t\tchildLines: string[],\n\t\tbgSample: string | undefined,\n\t\tshadowSample: string | undefined,\n\t\tinset: number,\n\t): boolean {\n\t\tconst cache = this.cache;\n\t\treturn (\n\t\t\t!!cache &&\n\t\t\tcache.width === width &&\n\t\t\tcache.bgSample === bgSample &&\n\t\t\tcache.shadowSample === shadowSample &&\n\t\t\tcache.inset === inset &&\n\t\t\tcache.childLines.length === childLines.length &&\n\t\t\tcache.childLines.every((line, i) => line === childLines[i])\n\t\t);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.invalidateCache();\n\t\tfor (const child of this.children) {\n\t\t\tchild.invalidate?.();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.children.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// The paper treatment is asked for per frame, so a box already on screen\n\t\t// follows a theme switch instead of holding the treatment it was built\n\t\t// with.\n\t\tconst paper = this.paperFn?.();\n\t\t// A sheet needs a band wide enough to carry its own bottom run. Narrower\n\t\t// than that the gutter has nowhere to go: the treatment degenerated into\n\t\t// a one-column band with a shadow beside it and no run under it. Below\n\t\t// the threshold the box falls back to the plain full-width band a theme\n\t\t// without paper draws, which is the honest answer at that size.\n\t\tconst wide = width - Math.max(0, paper?.inset ?? 0) > 1;\n\t\tconst shadowFn = wide ? paper?.shadow : undefined;\n\t\tconst inset = wide ? Math.max(0, paper?.inset ?? 0) : 0;\n\n\t\t// The band stops short of the right margin when inset, leaving a gutter of\n\t\t// page for the sheet's own edge and the shadow that follows it.\n\t\tconst bandWidth = Math.max(1, width - inset);\n\t\t// Padding only exists to carry the band, so it gives way rather than\n\t\t// pushing content off the end of it. Clamping `contentWidth` alone was\n\t\t// not enough: at a width where the gutter and two columns of padding\n\t\t// leave no room, the row came out wider than the band it was supposed\n\t\t// to fill, the shadow's column went with it, and the line wrapped past\n\t\t// the right margin. Both halves are derived from the band instead.\n\t\tconst paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((bandWidth - 1) / 2)));\n\t\tconst contentWidth = Math.max(1, bandWidth - paddingX * 2);\n\t\tconst leftPad = \" \".repeat(paddingX);\n\n\t\t// Render all children\n\t\tconst childLines: string[] = [];\n\t\tfor (const child of this.children) {\n\t\t\tconst lines = child.render(contentWidth);\n\t\t\tfor (const line of lines) {\n\t\t\t\tchildLines.push(leftPad + line);\n\t\t\t}\n\t\t}\n\n\t\tif (childLines.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Check if bgFn output changed by sampling\n\t\tconst bgSample = this.bgFn ? this.bgFn(\"test\") : undefined;\n\t\tconst shadowSample = shadowFn ? shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample, inset)) {\n\t\t\treturn this.cache!.lines;\n\t\t}\n\n\t\t// Apply background and padding\n\t\tconst rows: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\t\trows.push(...childLines);\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Every row of the sheet ends in the same column.\n\t\t\t//\n\t\t\t// The edge used to be nicked one column in on roughly every fifth\n\t\t\t// row, to read as cut by hand rather than ruled. At a terminal's\n\t\t\t// resolution it could not: a nick is a whole cell, which is a step\n\t\t\t// far too coarse to read as the wobble of a pair of scissors, and it\n\t\t\t// landed as damage instead. On the top row — the one row with no\n\t\t\t// shadow behind it, because the offset is down as well as right — it\n\t\t\t// showed as a bite taken out of the sheet's corner. Everywhere else\n\t\t\t// it was backfilled with a block of shadow ink, which put a tooth of\n\t\t\t// shadow *inside* the sheet's own outline. Both are the same mistake\n\t\t\t// seen from two sides: the fill was leaving holes and the shadow was\n\t\t\t// covering for them. A ruled edge has neither.\n\t\t\tconst band = this.applyBg(line, bandWidth);\n\t\t\t// `▌` paints the left half of its cell, so the column reads as a thin\n\t\t\t// line hugging the sheet rather than a second band beside it. The\n\t\t\t// first row has no column at all: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? shadowFn(\"▌\") : \"\";\n\t\t\tresult.push(band + column);\n\t\t});\n\n\t\t// The shadow's bottom run, offset one column right of the band. It ends\n\t\t// under the right-hand column so the two close the corner; with no\n\t\t// gutter there is no such column, and the run stops one cell short of\n\t\t// the margin instead of wrapping past it.\n\t\t//\n\t\t// That last cell is `▘`, not `▀`. The run is a full-width glyph and the\n\t\t// column above it is a half-width one, so a run that ended on `▀`\n\t\t// overshot the column by half a cell and left a tip poking out past the\n\t\t// corner — a stray line coming out of the shadow. `▘` is the same top\n\t\t// half narrowed to the column's own width, so the two edges close flush.\n\t\tif (shadowFn && bandWidth > 1) {\n\t\t\tconst run = \"▀\".repeat(bandWidth - 1);\n\t\t\tresult.push(` ${shadowFn(hasColumn ? `${run}▘` : run)}`);\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache = {\n\t\t\tchildLines,\n\t\t\twidth,\n\t\t\tbgSample,\n\t\t\tshadowSample,\n\t\t\tinset,\n\t\t\tlines: result,\n\t\t};\n\n\t\treturn result;\n\t}\n\n\tprivate applyBg(line: string, width: number): string {\n\t\tconst visLen = visibleWidth(line);\n\t\tconst padNeeded = Math.max(0, width - visLen);\n\t\tconst padded = line + \" \".repeat(padNeeded);\n\n\t\tif (this.bgFn) {\n\t\t\treturn applyBackgroundToLine(padded, width, this.bgFn);\n\t\t}\n\t\treturn padded;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"box.js","sourceRoot":"","sources":["../../src/components/box.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"../tui.js\";\nimport { applyBackgroundToLine, visibleWidth } from \"../utils.js\";\n\ntype RenderCache = {\n\tchildLines: string[];\n\twidth: number;\n\tbgSample: string | undefined;\n\tshadowSample: string | undefined;\n\tinset: number;\n\tlines: string[];\n};\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow and the gutter it holds back from the right margin. Both are optional,\n * and a sheet with neither is the plain full-width band.\n */\nexport interface PaperSheet {\n\t/** Paints the shadow's own glyphs. Omitted, the sheet casts no shadow. */\n\tshadow?: (text: string) => string;\n\t/**\n\t * Columns of page held back at the right margin.\n\t *\n\t * A block that runs the full width of the terminal has no right edge to\n\t * show: it is a band of colour between two screen edges, not a sheet on a\n\t * page. Reserving a few columns gives it one, which is what makes both the\n\t * shadow's right-hand column and a visible right edge possible at all.\n\t */\n\tinset?: number;\n}\n\n/**\n * Box component - a container that applies padding and background to all children\n */\nexport class Box implements Component {\n\tchildren: Component[] = [];\n\tprivate paddingX: number;\n\tprivate paddingY: number;\n\tprivate bgFn?: (text: string) => string;\n\tprivate paperFn?: () => PaperSheet | undefined;\n\n\t// Cache for rendered output\n\tprivate cache?: RenderCache;\n\n\tconstructor(paddingX = 1, paddingY = 1, bgFn?: (text: string) => string) {\n\t\tthis.paddingX = paddingX;\n\t\tthis.paddingY = paddingY;\n\t\tthis.bgFn = bgFn;\n\t}\n\n\t/**\n\t * Change the horizontal padding after construction.\n\t *\n\t * For a box whose padding exists to carry a background band: with no band to\n\t * draw, the padding is just an indent that pushes the content out of line\n\t * with everything around it.\n\t */\n\tsetPaddingX(paddingX: number): void {\n\t\tif (this.paddingX === paddingX) return;\n\t\tthis.paddingX = paddingX;\n\t\tthis.invalidateCache();\n\t}\n\n\taddChild(component: Component): void {\n\t\tthis.children.push(component);\n\t\tthis.invalidateCache();\n\t}\n\n\tremoveChild(component: Component): void {\n\t\tconst index = this.children.indexOf(component);\n\t\tif (index !== -1) {\n\t\t\tthis.children.splice(index, 1);\n\t\t\tthis.invalidateCache();\n\t\t}\n\t}\n\n\tclear(): void {\n\t\tthis.children = [];\n\t\tthis.invalidateCache();\n\t}\n\n\tsetBgFn(bgFn?: (text: string) => string): void {\n\t\tthis.bgFn = bgFn;\n\t\t// Don't invalidate here - we'll detect bgFn changes by sampling output\n\t}\n\n\t/**\n\t * Give the box the paper treatment, resolved on every frame.\n\t *\n\t * A shadow and the gutter it needs arrive together because they are one\n\t * decision, and it is a decision the *theme* makes — so the box asks for it\n\t * at render time rather than being told once. A box built under one theme\n\t * outlives it: the user switches theme with the block already on screen, and\n\t * a shadow function captured at construction would still be painting the old\n\t * theme's ink (or reaching for a colour the new theme never defined).\n\t *\n\t * The shadow itself is one extra row of `▔` — an upper *one-eighth* block,\n\t * which paints a hairline across the top of its cells and so hugs the box's\n\t * bottom edge — indented one column to give the offset a terminal cannot\n\t * give in sub-pixels. An inset box gets a matching column of `▏` down its\n\t * right edge, and the run stops where that column starts, so the two meet as\n\t * an L; with no inset the band already owns the last cell, so the bottom\n\t * edge is all there is room for.\n\t *\n\t * Eighths, not halves. `▀` and `▌` are half a cell of solid ink, which at a\n\t * terminal's resolution is not a shadow but a second band of colour wrapped\n\t * around two sides of every message — heavy enough to pull the eye off the\n\t * text it is supposed to sit behind. A shadow's whole job is to be noticed\n\t * without being looked at, and an eighth is as close to that as a cell grid\n\t * gets.\n\t *\n\t * Passing no provider, or one that returns nothing, draws the full-width\n\t * band the box has always drawn.\n\t */\n\tsetPaper(paperFn?: () => PaperSheet | undefined): void {\n\t\tthis.paperFn = paperFn;\n\t\tthis.invalidateCache();\n\t}\n\n\tprivate invalidateCache(): void {\n\t\tthis.cache = undefined;\n\t}\n\n\tprivate matchCache(\n\t\twidth: number,\n\t\tchildLines: string[],\n\t\tbgSample: string | undefined,\n\t\tshadowSample: string | undefined,\n\t\tinset: number,\n\t): boolean {\n\t\tconst cache = this.cache;\n\t\treturn (\n\t\t\t!!cache &&\n\t\t\tcache.width === width &&\n\t\t\tcache.bgSample === bgSample &&\n\t\t\tcache.shadowSample === shadowSample &&\n\t\t\tcache.inset === inset &&\n\t\t\tcache.childLines.length === childLines.length &&\n\t\t\tcache.childLines.every((line, i) => line === childLines[i])\n\t\t);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.invalidateCache();\n\t\tfor (const child of this.children) {\n\t\t\tchild.invalidate?.();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.children.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// The paper treatment is asked for per frame, so a box already on screen\n\t\t// follows a theme switch instead of holding the treatment it was built\n\t\t// with.\n\t\tconst paper = this.paperFn?.();\n\t\t// A sheet needs a band wide enough to carry its own bottom run. Narrower\n\t\t// than that the gutter has nowhere to go: the treatment degenerated into\n\t\t// a one-column band with a shadow beside it and no run under it. Below\n\t\t// the threshold the box falls back to the plain full-width band a theme\n\t\t// without paper draws, which is the honest answer at that size.\n\t\tconst wide = width - Math.max(0, paper?.inset ?? 0) > 1;\n\t\tconst shadowFn = wide ? paper?.shadow : undefined;\n\t\tconst inset = wide ? Math.max(0, paper?.inset ?? 0) : 0;\n\n\t\t// The band stops short of the right margin when inset, leaving a gutter of\n\t\t// page for the sheet's own edge and the shadow that follows it.\n\t\tconst bandWidth = Math.max(1, width - inset);\n\t\t// Padding only exists to carry the band, so it gives way rather than\n\t\t// pushing content off the end of it. Clamping `contentWidth` alone was\n\t\t// not enough: at a width where the gutter and two columns of padding\n\t\t// leave no room, the row came out wider than the band it was supposed\n\t\t// to fill, the shadow's column went with it, and the line wrapped past\n\t\t// the right margin. Both halves are derived from the band instead.\n\t\tconst paddingX = Math.min(this.paddingX, Math.max(0, Math.floor((bandWidth - 1) / 2)));\n\t\tconst contentWidth = Math.max(1, bandWidth - paddingX * 2);\n\t\tconst leftPad = \" \".repeat(paddingX);\n\n\t\t// Render all children\n\t\tconst childLines: string[] = [];\n\t\tfor (const child of this.children) {\n\t\t\tconst lines = child.render(contentWidth);\n\t\t\tfor (const line of lines) {\n\t\t\t\tchildLines.push(leftPad + line);\n\t\t\t}\n\t\t}\n\n\t\tif (childLines.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\t// Check if bgFn output changed by sampling\n\t\tconst bgSample = this.bgFn ? this.bgFn(\"test\") : undefined;\n\t\tconst shadowSample = shadowFn ? shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample, inset)) {\n\t\t\treturn this.cache!.lines;\n\t\t}\n\n\t\t// Apply background and padding\n\t\tconst rows: string[] = [];\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\t\trows.push(...childLines);\n\t\tfor (let i = 0; i < this.paddingY; i++) rows.push(\"\");\n\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Every row of the sheet ends in the same column.\n\t\t\t//\n\t\t\t// The edge used to be nicked one column in on roughly every fifth\n\t\t\t// row, to read as cut by hand rather than ruled. At a terminal's\n\t\t\t// resolution it could not: a nick is a whole cell, which is a step\n\t\t\t// far too coarse to read as the wobble of a pair of scissors, and it\n\t\t\t// landed as damage instead. On the top row — the one row with no\n\t\t\t// shadow behind it, because the offset is down as well as right — it\n\t\t\t// showed as a bite taken out of the sheet's corner. Everywhere else\n\t\t\t// it was backfilled with a block of shadow ink, which put a tooth of\n\t\t\t// shadow *inside* the sheet's own outline. Both are the same mistake\n\t\t\t// seen from two sides: the fill was leaving holes and the shadow was\n\t\t\t// covering for them. A ruled edge has neither.\n\t\t\tconst band = this.applyBg(line, bandWidth);\n\t\t\t// `▏` paints the leftmost eighth of its cell, so the column reads as a\n\t\t\t// hairline hugging the sheet rather than a band beside it. The first row\n\t\t\t// has no column at all: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? shadowFn(\"▏\") : \"\";\n\t\t\tresult.push(band + column);\n\t\t});\n\n\t\t// The shadow's bottom run, offset one column right of the band, and\n\t\t// nothing after it — the gutter cell the column occupies stays empty on\n\t\t// this row.\n\t\t//\n\t\t// The corner closes on its own. `▔` paints its cell edge to edge, so a\n\t\t// run of `bandWidth - 1` starting in column 1 ends at exactly the cell\n\t\t// boundary the column's `▏` paints its hairline on: the horizontal leg's\n\t\t// right end abuts the vertical leg's left edge. Carrying `▏` down into\n\t\t// this row to \"close\" it instead overshot the other way — `▏` fills the\n\t\t// full height of its cell while `▔` fills the top eighth of one, so the\n\t\t// glyph hung a whole row below the shadow's bottom edge and the L came\n\t\t// out as a cross with a tick poking under it.\n\t\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▔\".repeat(bandWidth - 1))}`);\n\t\t}\n\n\t\t// Update cache\n\t\tthis.cache = {\n\t\t\tchildLines,\n\t\t\twidth,\n\t\t\tbgSample,\n\t\t\tshadowSample,\n\t\t\tinset,\n\t\t\tlines: result,\n\t\t};\n\n\t\treturn result;\n\t}\n\n\tprivate applyBg(line: string, width: number): string {\n\t\tconst visLen = visibleWidth(line);\n\t\tconst padNeeded = Math.max(0, width - visLen);\n\t\tconst padded = line + \" \".repeat(padNeeded);\n\n\t\tif (this.bgFn) {\n\t\t\treturn applyBackgroundToLine(padded, width, this.bgFn);\n\t\t}\n\t\treturn padded;\n\t}\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -21,5 +21,5 @@ export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from "
|
|
|
21
21
|
export { ProcessTerminal, type Terminal } from "./terminal.js";
|
|
22
22
|
export { allocateImageId, type CellDimensions, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, type ImageDimensions, type ImageProtocol, type ImageRenderOptions, imageFallback, isImageLine, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, type TerminalCapabilities, } from "./terminal-image.js";
|
|
23
23
|
export { type Component, Container, CURSOR_MARKER, type Focusable, isFocusable, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type ScrollSearchStatus, type ScrollStatus, type ScrollStatusFormatter, type SizeValue, Slot, TUI, } from "./tui.js";
|
|
24
|
-
export { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
24
|
+
export { applyBackgroundToLine, hyperlinkAt, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box, type PaperSheet } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport {\n\tDEFAULT_EDITOR_BORDER_CHARS,\n\tEditor,\n\ttype EditorBorderChars,\n\ttype EditorBorderStyle,\n\ttype EditorOptions,\n\ttype EditorTheme,\n\ttype EditorTopBorderLabel,\n} from \"./components/editor.js\";\nexport {\n\tDEFAULT_FRAME_BORDER_CHARS,\n\tFrame,\n\ttype FrameBorderChars,\n\ttype FrameBorderStyle,\n\ttype FrameEdgeOptions,\n\ttype FrameLabel,\n\ttype FrameOptions,\n\trenderFrameEdge,\n} from \"./components/frame.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { DEFAULT_INPUT_PROMPT, Input } from \"./components/input.js\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { FlexSpacer, Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\n// Mouse reporting\nexport {\n\tisMouseSequence,\n\tMOUSE_DISABLE,\n\tMOUSE_ENABLE,\n\ttype MouseEvent,\n\ttype MouseEventKind,\n\tmouseSequenceLength,\n\tparseMouseEvent,\n} from \"./mouse.js\";\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\tisImageLine,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype ScrollSearchStatus,\n\ttype ScrollStatus,\n\ttype ScrollStatusFormatter,\n\ttype SizeValue,\n\tSlot,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box, type PaperSheet } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport {\n\tDEFAULT_EDITOR_BORDER_CHARS,\n\tEditor,\n\ttype EditorBorderChars,\n\ttype EditorBorderStyle,\n\ttype EditorOptions,\n\ttype EditorTheme,\n\ttype EditorTopBorderLabel,\n} from \"./components/editor.js\";\nexport {\n\tDEFAULT_FRAME_BORDER_CHARS,\n\tFrame,\n\ttype FrameBorderChars,\n\ttype FrameBorderStyle,\n\ttype FrameEdgeOptions,\n\ttype FrameLabel,\n\ttype FrameOptions,\n\trenderFrameEdge,\n} from \"./components/frame.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { DEFAULT_INPUT_PROMPT, Input } from \"./components/input.js\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { FlexSpacer, Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\n// Mouse reporting\nexport {\n\tisMouseSequence,\n\tMOUSE_DISABLE,\n\tMOUSE_ENABLE,\n\ttype MouseEvent,\n\ttype MouseEventKind,\n\tmouseSequenceLength,\n\tparseMouseEvent,\n} from \"./mouse.js\";\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\tisImageLine,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype ScrollSearchStatus,\n\ttype ScrollStatus,\n\ttype ScrollStatusFormatter,\n\ttype SizeValue,\n\tSlot,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, hyperlinkAt, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -31,5 +31,5 @@ export { ProcessTerminal } from "./terminal.js";
|
|
|
31
31
|
export { allocateImageId, calculateImageRows, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, getCapabilities, getCellDimensions, getGifDimensions, getImageDimensions, getJpegDimensions, getPngDimensions, getWebpDimensions, hyperlink, imageFallback, isImageLine, renderImage, resetCapabilitiesCache, setCapabilities, setCellDimensions, } from "./terminal-image.js";
|
|
32
32
|
export { Container, CURSOR_MARKER, isFocusable, Slot, TUI, } from "./tui.js";
|
|
33
33
|
// Utilities
|
|
34
|
-
export { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
34
|
+
export { applyBackgroundToLine, hyperlinkAt, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
|
|
35
35
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EACN,2BAA2B,EAC3B,MAAM,GAMN,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,0BAA0B,EAC1B,KAAK,EAML,eAAe,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,sCAAsC;AACtC,kBAAkB;AAClB,OAAO,EACN,eAAe,EACf,aAAa,EACb,YAAY,EAGZ,mBAAmB,EACnB,eAAe,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEN,SAAS,EACT,aAAa,EAEb,WAAW,EASX,IAAI,EACJ,GAAG,GACH,MAAM,UAAU,CAAC;AAClB,YAAY;AACZ,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box, type PaperSheet } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport {\n\tDEFAULT_EDITOR_BORDER_CHARS,\n\tEditor,\n\ttype EditorBorderChars,\n\ttype EditorBorderStyle,\n\ttype EditorOptions,\n\ttype EditorTheme,\n\ttype EditorTopBorderLabel,\n} from \"./components/editor.js\";\nexport {\n\tDEFAULT_FRAME_BORDER_CHARS,\n\tFrame,\n\ttype FrameBorderChars,\n\ttype FrameBorderStyle,\n\ttype FrameEdgeOptions,\n\ttype FrameLabel,\n\ttype FrameOptions,\n\trenderFrameEdge,\n} from \"./components/frame.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { DEFAULT_INPUT_PROMPT, Input } from \"./components/input.js\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { FlexSpacer, Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\n// Mouse reporting\nexport {\n\tisMouseSequence,\n\tMOUSE_DISABLE,\n\tMOUSE_ENABLE,\n\ttype MouseEvent,\n\ttype MouseEventKind,\n\tmouseSequenceLength,\n\tparseMouseEvent,\n} from \"./mouse.js\";\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\tisImageLine,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype ScrollSearchStatus,\n\ttype ScrollStatus,\n\ttype ScrollStatusFormatter,\n\ttype SizeValue,\n\tSlot,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAElC,uBAAuB;AACvB,OAAO,EAIN,4BAA4B,GAE5B,MAAM,mBAAmB,CAAC;AAC3B,aAAa;AACb,OAAO,EAAE,GAAG,EAAmB,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EACN,2BAA2B,EAC3B,MAAM,GAMN,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,0BAA0B,EAC1B,KAAK,EAML,eAAe,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EAAE,MAAM,EAA+B,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,QAAQ,EAAsB,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAEN,UAAU,GAIV,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAoB,YAAY,EAA0B,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,iBAAiB;AACjB,OAAO,EAAmB,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,cAAc;AACd,OAAO,EACN,cAAc,EAOd,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,0BAA0B;AAC1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EAGH,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AACnB,sCAAsC;AACtC,kBAAkB;AAClB,OAAO,EACN,eAAe,EACf,aAAa,EACb,YAAY,EAGZ,mBAAmB,EACnB,eAAe,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAqD,MAAM,mBAAmB,CAAC;AACnG,yCAAyC;AACzC,OAAO,EAAE,eAAe,EAAiB,MAAM,eAAe,CAAC;AAC/D,yBAAyB;AACzB,OAAO,EACN,eAAe,EAEf,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,WAAW,EACX,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,SAAS,EAIT,aAAa,EACb,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAEjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEN,SAAS,EACT,aAAa,EAEb,WAAW,EASX,IAAI,EACJ,GAAG,GACH,MAAM,UAAU,CAAC;AAClB,YAAY;AACZ,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["// Core TUI interfaces and classes\n\n// Autocomplete support\nexport {\n\ttype AutocompleteItem,\n\ttype AutocompleteProvider,\n\ttype AutocompleteSuggestions,\n\tCombinedAutocompleteProvider,\n\ttype SlashCommand,\n} from \"./autocomplete.js\";\n// Components\nexport { Box, type PaperSheet } from \"./components/box.js\";\nexport { CancellableLoader } from \"./components/cancellable-loader.js\";\nexport {\n\tDEFAULT_EDITOR_BORDER_CHARS,\n\tEditor,\n\ttype EditorBorderChars,\n\ttype EditorBorderStyle,\n\ttype EditorOptions,\n\ttype EditorTheme,\n\ttype EditorTopBorderLabel,\n} from \"./components/editor.js\";\nexport {\n\tDEFAULT_FRAME_BORDER_CHARS,\n\tFrame,\n\ttype FrameBorderChars,\n\ttype FrameBorderStyle,\n\ttype FrameEdgeOptions,\n\ttype FrameLabel,\n\ttype FrameOptions,\n\trenderFrameEdge,\n} from \"./components/frame.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { DEFAULT_INPUT_PROMPT, Input } from \"./components/input.js\";\nexport { Loader, type LoaderIndicatorOptions } from \"./components/loader.js\";\nexport { type DefaultTextStyle, Markdown, type MarkdownTheme } from \"./components/markdown.js\";\nexport {\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\ttype SelectListTheme,\n\ttype SelectListTruncatePrimaryContext,\n} from \"./components/select-list.js\";\nexport { type SettingItem, SettingsList, type SettingsListTheme } from \"./components/settings-list.js\";\nexport { FlexSpacer, Spacer } from \"./components/spacer.js\";\nexport { Text } from \"./components/text.js\";\nexport { TruncatedText } from \"./components/truncated-text.js\";\n// Editor component interface (for custom editors)\nexport type { EditorComponent } from \"./editor-component.js\";\n// Fuzzy matching\nexport { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from \"./fuzzy.js\";\n// Keybindings\nexport {\n\tgetKeybindings,\n\ttype Keybinding,\n\ttype KeybindingConflict,\n\ttype KeybindingDefinition,\n\ttype KeybindingDefinitions,\n\ttype Keybindings,\n\ttype KeybindingsConfig,\n\tKeybindingsManager,\n\tsetKeybindings,\n\tTUI_KEYBINDINGS,\n} from \"./keybindings.js\";\n// Keyboard input handling\nexport {\n\tdecodeKittyPrintable,\n\tisKeyRelease,\n\tisKeyRepeat,\n\tisKittyProtocolActive,\n\tKey,\n\ttype KeyEventType,\n\ttype KeyId,\n\tmatchesKey,\n\tparseKey,\n\tsetKittyProtocolActive,\n} from \"./keys.js\";\n// Input buffering for batch splitting\n// Mouse reporting\nexport {\n\tisMouseSequence,\n\tMOUSE_DISABLE,\n\tMOUSE_ENABLE,\n\ttype MouseEvent,\n\ttype MouseEventKind,\n\tmouseSequenceLength,\n\tparseMouseEvent,\n} from \"./mouse.js\";\nexport { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions } from \"./stdin-buffer.js\";\n// Terminal interface and implementations\nexport { ProcessTerminal, type Terminal } from \"./terminal.js\";\n// Terminal image support\nexport {\n\tallocateImageId,\n\ttype CellDimensions,\n\tcalculateImageRows,\n\tdeleteAllKittyImages,\n\tdeleteKittyImage,\n\tdetectCapabilities,\n\tencodeITerm2,\n\tencodeKitty,\n\tgetCapabilities,\n\tgetCellDimensions,\n\tgetGifDimensions,\n\tgetImageDimensions,\n\tgetJpegDimensions,\n\tgetPngDimensions,\n\tgetWebpDimensions,\n\thyperlink,\n\ttype ImageDimensions,\n\ttype ImageProtocol,\n\ttype ImageRenderOptions,\n\timageFallback,\n\tisImageLine,\n\trenderImage,\n\tresetCapabilitiesCache,\n\tsetCapabilities,\n\tsetCellDimensions,\n\ttype TerminalCapabilities,\n} from \"./terminal-image.js\";\nexport {\n\ttype Component,\n\tContainer,\n\tCURSOR_MARKER,\n\ttype Focusable,\n\tisFocusable,\n\ttype OverlayAnchor,\n\ttype OverlayHandle,\n\ttype OverlayMargin,\n\ttype OverlayOptions,\n\ttype ScrollSearchStatus,\n\ttype ScrollStatus,\n\ttype ScrollStatusFormatter,\n\ttype SizeValue,\n\tSlot,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, hyperlinkAt, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
|
package/dist/tui.d.ts
CHANGED
|
@@ -233,6 +233,20 @@ export declare class TUI extends Container {
|
|
|
233
233
|
* per-frame full-buffer scan (collectKittyImageIds) is skipped entirely —
|
|
234
234
|
* the common case for a pure-text session. */
|
|
235
235
|
private sawImageLine;
|
|
236
|
+
/**
|
|
237
|
+
* Live kitty image id -> the id the pinned window transmits its own copy
|
|
238
|
+
* under, and the copies currently placed on the alternate screen.
|
|
239
|
+
*
|
|
240
|
+
* The pinned window repaints whole, so a placement from the previous scroll
|
|
241
|
+
* position has to be deleted or it hangs over the new one. Deleting a kitty
|
|
242
|
+
* image deletes *every* placement of it, though, including the one on the
|
|
243
|
+
* normal screen — which the differential frame on the way back out has no
|
|
244
|
+
* reason to repaint, so the picture would simply be gone. The window
|
|
245
|
+
* therefore transmits a second copy under an id of its own and only ever
|
|
246
|
+
* deletes that one.
|
|
247
|
+
*/
|
|
248
|
+
private scrollImageIds;
|
|
249
|
+
private scrollPlacedImages;
|
|
236
250
|
private static readonly EMPTY_KITTY_IDS;
|
|
237
251
|
private previousWidth;
|
|
238
252
|
private previousHeight;
|
|
@@ -281,6 +295,18 @@ export declare class TUI extends Container {
|
|
|
281
295
|
* any embedder outside the app rely on.
|
|
282
296
|
*/
|
|
283
297
|
private flexSpacer?;
|
|
298
|
+
/** Where the left button went down, for telling a click from a drag. */
|
|
299
|
+
private pressedCell?;
|
|
300
|
+
/** What the pinned window painted last, so a click on it can be placed. */
|
|
301
|
+
private scrollViewLines?;
|
|
302
|
+
/**
|
|
303
|
+
* Open the URL behind a clicked hyperlink. Unset, clicks do nothing.
|
|
304
|
+
*
|
|
305
|
+
* The TUI resolves *which* link was clicked and deliberately stops there:
|
|
306
|
+
* opening a URL is spawning a process, which is the embedder's policy to
|
|
307
|
+
* make, not a rendering library's.
|
|
308
|
+
*/
|
|
309
|
+
onHyperlink?: (url: string) => void;
|
|
284
310
|
/**
|
|
285
311
|
* The pinned viewport.
|
|
286
312
|
*
|
|
@@ -348,9 +374,10 @@ export declare class TUI extends Container {
|
|
|
348
374
|
/**
|
|
349
375
|
* Nominate the child that absorbs the leftover rows (see `flexSpacer`).
|
|
350
376
|
*
|
|
351
|
-
* It must already be a child of the root, and it
|
|
352
|
-
*
|
|
353
|
-
* bottom
|
|
377
|
+
* It must already be a child of the root, and it belongs at the *top* of the
|
|
378
|
+
* tree: everything after it is pushed to the foot of the screen, so the app
|
|
379
|
+
* reads bottom-up the way a terminal does — the newest row next to the
|
|
380
|
+
* prompt, and whatever room is left over above the first thing drawn.
|
|
354
381
|
*/
|
|
355
382
|
setFlexSpacer(spacer: FlexSpacer | undefined): void;
|
|
356
383
|
/**
|
|
@@ -361,29 +388,19 @@ export declare class TUI extends Container {
|
|
|
361
388
|
* frame that answers it is always the second one. Both passes are cheap
|
|
362
389
|
* after the first: every other child returns its memoized array untouched.
|
|
363
390
|
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* mid-screen with a band of blank rows under it, and it stays stranded
|
|
378
|
-
* until enough output arrives to push it back down.
|
|
379
|
-
*
|
|
380
|
-
* So the fill takes what the shrinking content gave up, which keeps the
|
|
381
|
-
* buffer the length it already was and the last row where it already is.
|
|
382
|
-
* The blank band ends up *above* the chrome instead of below it, where it
|
|
383
|
-
* reads as room rather than as a layout that came apart, and the next
|
|
384
|
-
* output to arrive lands in it rather than scrolling the screen. Capped at
|
|
385
|
-
* a screenful: a fill longer than the screen is rows nobody can see, and it
|
|
386
|
-
* is given up altogether on the frames that repaint the whole screen anyway.
|
|
391
|
+
* The whole rule is one line, and it is the same one in both directions:
|
|
392
|
+
* the buffer is never shorter than the screen. A session that fits gets the
|
|
393
|
+
* difference as blank rows, which — with the spacer at the top of the tree —
|
|
394
|
+
* land *above* the first thing drawn, so the transcript always ends against
|
|
395
|
+
* the prompt. A session too long to fit gets nothing, because the terminal's
|
|
396
|
+
* own scroll is already holding the last row on the last row.
|
|
397
|
+
*
|
|
398
|
+
* It deliberately does not bank rows a shrinking frame gave up. That kept
|
|
399
|
+
* the prompt on the floor, but it paid for it with a band of blank rows
|
|
400
|
+
* between the conversation and the prompt — a screenful of it when a view
|
|
401
|
+
* dial folded the transcript — which is the thing the fill exists to avoid.
|
|
402
|
+
* A buffer that has to move back over the screen is repainted instead; see
|
|
403
|
+
* the window-repaint branch in `doRender`.
|
|
387
404
|
*/
|
|
388
405
|
private fitFlexSpacer;
|
|
389
406
|
/**
|
|
@@ -535,14 +552,33 @@ export declare class TUI extends Container {
|
|
|
535
552
|
*/
|
|
536
553
|
private consumeMouseReports;
|
|
537
554
|
/**
|
|
538
|
-
* What the mouse does.
|
|
555
|
+
* What the mouse does: the wheel scrolls, and a click opens a link.
|
|
539
556
|
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
557
|
+
* The click is here because capturing the mouse took it away. A terminal
|
|
558
|
+
* resolves a click on an OSC 8 hyperlink itself right up until an app turns
|
|
559
|
+
* reporting on, at which point the report comes to the app and the link stops
|
|
560
|
+
* working — so the app owes the user an answer. It is the same answer the
|
|
561
|
+
* terminal would have given: the URL under the pointer, handed to whoever set
|
|
562
|
+
* `onHyperlink`. Anything else is still swallowed, because a click that fell
|
|
563
|
+
* through to the focused component would arrive as raw report text typed into
|
|
564
|
+
* whatever field has focus.
|
|
565
|
+
*
|
|
566
|
+
* Press and release both have to land on the same cell. A drag that happens
|
|
567
|
+
* to end on a link is someone selecting text, not someone asking for a
|
|
568
|
+
* browser.
|
|
544
569
|
*/
|
|
545
570
|
private handleMouseEvent;
|
|
571
|
+
/**
|
|
572
|
+
* The link on the screen cell a mouse report names, if there is one.
|
|
573
|
+
*
|
|
574
|
+
* Screen rows are turned into line-buffer rows through the window the last
|
|
575
|
+
* frame painted — `scrollOffset` while pinned, `previousViewportTop` live —
|
|
576
|
+
* so this answers from exactly what the terminal is showing. A live buffer
|
|
577
|
+
* shorter than the screen is declined rather than guessed at: nothing pins
|
|
578
|
+
* where such a frame starts on screen, and a wrong row is a click on the
|
|
579
|
+
* wrong link.
|
|
580
|
+
*/
|
|
581
|
+
private hyperlinkAtScreenCell;
|
|
546
582
|
/** Run a requested render now, bypassing the coalescing delay. Used for
|
|
547
583
|
* input-driven frames where echo latency matters more than batching. */
|
|
548
584
|
private expediteRender;
|
|
@@ -605,16 +641,28 @@ export declare class TUI extends Container {
|
|
|
605
641
|
* left it, because `scrollToLive` throws it away rather than resuming from it.
|
|
606
642
|
*/
|
|
607
643
|
private renderScrollView;
|
|
644
|
+
/** Take the pinned window's own copies of the images off the screen. */
|
|
645
|
+
private clearScrollImages;
|
|
646
|
+
/**
|
|
647
|
+
* On the way off the alternate screen: free the copies and the ids with
|
|
648
|
+
* them, while there is still a screen to write to. The live ids are never
|
|
649
|
+
* touched, so the normal screen comes back with its pictures intact.
|
|
650
|
+
*/
|
|
651
|
+
private releaseScrollImages;
|
|
608
652
|
/**
|
|
609
653
|
* One transcript row, ready for the pinned window.
|
|
610
654
|
*
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
655
|
+
* `row` is where in the window the line lands, which decides whether an image
|
|
656
|
+
* on it can be drawn at all: a picture reaches `imageRowOffset` rows *above*
|
|
657
|
+
* its line, and a terminal will not draw above the first row — it clamps,
|
|
658
|
+
* putting the picture over rows that are not its own and leaving it there.
|
|
659
|
+
* A block hanging off the top of the window is named instead of drawn, so
|
|
660
|
+
* scrolling one into view shows a placeholder until all of it is on screen.
|
|
616
661
|
*/
|
|
617
662
|
private emitScrollLine;
|
|
663
|
+
private static readonly IMAGE_PLACEHOLDER;
|
|
664
|
+
/** An image line in the pinned window: drawn if all of it fits, named if not. */
|
|
665
|
+
private emitScrollImage;
|
|
618
666
|
private doRender;
|
|
619
667
|
/**
|
|
620
668
|
* Build the escape sequence that parks the hardware cursor for this frame.
|