@kolisachint/hoocode-tui 0.5.38 → 0.5.40

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.
@@ -1,4 +1,32 @@
1
1
  import type { Component } from "../tui.js";
2
+ /**
3
+ * The paper treatment a box asks its owner for on every frame: the ink of its
4
+ * shadow, the gutter it holds back from the right margin, and whether its right
5
+ * edge is cut by hand. Each part is optional, and a sheet with none of them is
6
+ * the plain full-width band.
7
+ */
8
+ export interface PaperSheet {
9
+ /** Paints the shadow's own glyphs. Omitted, the sheet casts no shadow. */
10
+ shadow?: (text: string) => string;
11
+ /**
12
+ * Columns of page held back at the right margin.
13
+ *
14
+ * A block that runs the full width of the terminal has no right edge to
15
+ * show: it is a band of colour between two screen edges, not a sheet on a
16
+ * page. Reserving a few columns gives it one, which is what makes both the
17
+ * shadow's right-hand column and a cut edge possible at all.
18
+ */
19
+ inset?: number;
20
+ /**
21
+ * Cut the right edge by hand rather than ruling it.
22
+ *
23
+ * The jitter is one column at most and it only ever eats padding, never
24
+ * content, so a cut edge cannot cost a character. It is derived from the row
25
+ * index and the block's own content, which keeps it identical between frames
26
+ * — an edge that reshuffled on every render would read as noise, not paper.
27
+ */
28
+ cutEdge?: boolean;
29
+ }
2
30
  /**
3
31
  * Box component - a container that applies padding and background to all children
4
32
  */
@@ -7,9 +35,7 @@ export declare class Box implements Component {
7
35
  private paddingX;
8
36
  private paddingY;
9
37
  private bgFn?;
10
- private shadowFn?;
11
- private inset;
12
- private cutEdge;
38
+ private paperFn?;
13
39
  private cache?;
14
40
  constructor(paddingX?: number, paddingY?: number, bgFn?: (text: string) => string);
15
41
  /**
@@ -25,39 +51,27 @@ export declare class Box implements Component {
25
51
  clear(): void;
26
52
  setBgFn(bgFn?: (text: string) => string): void;
27
53
  /**
28
- * Draw an offset band under the box, so the block reads as a sheet laid on
29
- * the page rather than a color printed into it.
54
+ * Give the box the paper treatment, resolved on every frame.
30
55
  *
31
- * A terminal has no sub-pixel offsets, so the shadow is one extra row of
32
- * `▀` an upper half-block, which paints solid color across the top half of
33
- * its cells and so hugs the box's bottom edge indented one column to give
34
- * the offset. An inset box gets a matching column of `▌` down its right edge
35
- * and the bottom run reaches under it, so the two meet at the corner; with
36
- * no inset the band already owns the last cell, so the bottom edge is all
37
- * there is room for.
38
- *
39
- * Passing no function draws no shadow, which is the behavior of every theme
40
- * that does not opt in.
41
- */
42
- setShadowFn(shadowFn?: (text: string) => string): void;
43
- /**
44
- * Hold the band back from the right margin, leaving a gutter of page.
56
+ * A shadow, a gutter and a cut edge arrive together because they are one
57
+ * decision, and it is a decision the *theme* makes so the box asks for it
58
+ * at render time rather than being told once. A box built under one theme
59
+ * outlives it: the user switches theme with the block already on screen, and
60
+ * a shadow function captured at construction would still be painting the old
61
+ * theme's ink (or reaching for a colour the new theme never defined).
45
62
  *
46
- * A block that runs the full width of the terminal has no right edge to
47
- * show: it is a band of colour between two screen edges, not a sheet on a
48
- * page. Reserving a few columns gives it one, which is what makes both the
49
- * shadow's right-hand column and a cut edge possible at all.
50
- */
51
- setInset(inset: number): void;
52
- /**
53
- * Cut the right edge by hand rather than ruling it.
63
+ * The shadow itself is one extra row of `▀` an upper half-block, which
64
+ * paints solid colour across the top half of its cells and so hugs the box's
65
+ * bottom edge indented one column to give the offset a terminal cannot
66
+ * give in sub-pixels. An inset box gets a matching column of `▌` down its
67
+ * right edge and the bottom run reaches under it, so the two close the
68
+ * corner; with no inset the band already owns the last cell, so the bottom
69
+ * edge is all there is room for.
54
70
  *
55
- * The jitter is one column at most and it only ever eats padding, never
56
- * content, so a cut edge cannot cost a character. It is derived from the row
57
- * index and the block's own content, which keeps it identical between frames
58
- * — an edge that reshuffled on every render would read as noise, not paper.
71
+ * Passing no provider, or one that returns nothing, draws the full-width
72
+ * band the box has always drawn.
59
73
  */
60
- setCutEdge(cutEdge: boolean): void;
74
+ setPaper(paperFn?: () => PaperSheet | undefined): void;
61
75
  private invalidateCache;
62
76
  private matchCache;
63
77
  invalidate(): void;
@@ -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;AAgC3C;;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,QAAQ,CAAC,CAA2B;IAC5C,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAS;IAGxB,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;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAGrD;IAED;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI5B;IAED;;;;;;;OAOG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAIjC;IAED,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,UAAU;IAmBlB,UAAU,IAAI,IAAI,CAKjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqF9B;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\tcutEdge: boolean;\n\tlines: string[];\n};\n\n/**\n * Roughly one row in this many takes the cut.\n *\n * A coin flip per row is not a cut edge, it is a sawtooth: the eye reads\n * alternating columns as a pattern, which is the opposite of hand-made. Scissors\n * leave a mostly straight line with the occasional nick, so the deviation has to\n * be rare enough to read as an accident.\n */\nconst CUT_EVERY = 5;\n\n/** djb2. Small, stable, and good enough to decide one column of a paper edge. */\nfunction hashString(value: string): number {\n\tlet hash = 5381;\n\tfor (let i = 0; i < value.length; i++) {\n\t\thash = ((hash << 5) + hash + value.charCodeAt(i)) >>> 0;\n\t}\n\treturn hash;\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 shadowFn?: (text: string) => string;\n\tprivate inset = 0;\n\tprivate cutEdge = false;\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 * Draw an offset band under the box, so the block reads as a sheet laid on\n\t * the page rather than a color printed into it.\n\t *\n\t * A terminal has no sub-pixel offsets, so the shadow is one extra row of\n\t * `▀` — an upper half-block, which paints solid color across the top half of\n\t * its cells and so hugs the box's bottom edge — indented one column to give\n\t * the offset. An inset box gets a matching column of `▌` down its right edge\n\t * and the bottom run reaches under it, so the two meet at the corner; with\n\t * no inset the band already owns the last cell, so the bottom edge is all\n\t * there is room for.\n\t *\n\t * Passing no function draws no shadow, which is the behavior of every theme\n\t * that does not opt in.\n\t */\n\tsetShadowFn(shadowFn?: (text: string) => string): void {\n\t\tthis.shadowFn = shadowFn;\n\t\tthis.invalidateCache();\n\t}\n\n\t/**\n\t * Hold the band back from the right margin, leaving a gutter of page.\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 cut edge possible at all.\n\t */\n\tsetInset(inset: number): void {\n\t\tif (this.inset === inset) return;\n\t\tthis.inset = Math.max(0, inset);\n\t\tthis.invalidateCache();\n\t}\n\n\t/**\n\t * Cut the right edge by hand rather than ruling it.\n\t *\n\t * The jitter is one column at most and it only ever eats padding, never\n\t * content, so a cut edge cannot cost a character. It is derived from the row\n\t * index and the block's own content, which keeps it identical between frames\n\t * — an edge that reshuffled on every render would read as noise, not paper.\n\t */\n\tsetCutEdge(cutEdge: boolean): void {\n\t\tif (this.cutEdge === cutEdge) return;\n\t\tthis.cutEdge = cutEdge;\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): 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 === this.inset &&\n\t\t\tcache.cutEdge === this.cutEdge &&\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 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 - this.inset);\n\t\tconst contentWidth = Math.max(1, bandWidth - this.paddingX * 2);\n\t\tconst leftPad = \" \".repeat(this.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 = this.shadowFn ? this.shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample)) {\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// A cut edge needs a seed that is stable for this block but different\n\t\t// from its neighbours, or every sheet on screen is cut identically.\n\t\tconst seed = this.cutEdge ? hashString(childLines.join(\"\\n\")) : 0;\n\t\tconst shadowFn = this.shadowFn;\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && this.inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Never let the cut reach into the content: the jitter comes out of\n\t\t\t// the padding the row was going to draw anyway.\n\t\t\tconst slack = Math.max(0, bandWidth - visibleWidth(line));\n\t\t\tconst cut = this.cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;\n\t\t\tconst rowWidth = bandWidth - cut;\n\t\t\tconst band = this.applyBg(line, rowWidth);\n\t\t\t// The shadow holds one column whatever the cut does to the edge in\n\t\t\t// front of it. Following the cut was the first attempt and it broke\n\t\t\t// the shadow: `▌` paints half a cell, so a one-column step leaves no\n\t\t\t// overlap at all between one row's mark and the next, and what the\n\t\t\t// eye gets is a dashed staircase rather than an edge. A cut row\n\t\t\t// shows its nick as a column of page between sheet and shadow —\n\t\t\t// which is what a nick is — and the shadow stays a single line.\n\t\t\t// The first row has none: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? \" \".repeat(cut) + 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\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▀\".repeat(hasColumn ? bandWidth : 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: this.inset,\n\t\t\tcutEdge: this.cutEdge,\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;AAuB3C;;;;;GAKG;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;IACf;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAWD;;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;IAqBlB,UAAU,IAAI,IAAI,CAKjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4F9B;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\tcutEdge: boolean;\n\tlines: string[];\n};\n\n/**\n * Roughly one row in this many takes the cut.\n *\n * A coin flip per row is not a cut edge, it is a sawtooth: the eye reads\n * alternating columns as a pattern, which is the opposite of hand-made. Scissors\n * leave a mostly straight line with the occasional nick, so the deviation has to\n * be rare enough to read as an accident.\n */\nconst CUT_EVERY = 5;\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow, the gutter it holds back from the right margin, and whether its right\n * edge is cut by hand. Each part is optional, and a sheet with none of them is\n * 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 cut edge possible at all.\n\t */\n\tinset?: number;\n\t/**\n\t * Cut the right edge by hand rather than ruling it.\n\t *\n\t * The jitter is one column at most and it only ever eats padding, never\n\t * content, so a cut edge cannot cost a character. It is derived from the row\n\t * index and the block's own content, which keeps it identical between frames\n\t * — an edge that reshuffled on every render would read as noise, not paper.\n\t */\n\tcutEdge?: boolean;\n}\n\n/** djb2. Small, stable, and good enough to decide one column of a paper edge. */\nfunction hashString(value: string): number {\n\tlet hash = 5381;\n\tfor (let i = 0; i < value.length; i++) {\n\t\thash = ((hash << 5) + hash + value.charCodeAt(i)) >>> 0;\n\t}\n\treturn hash;\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, a gutter and a cut edge 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\tcutEdge: boolean,\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.cutEdge === cutEdge &&\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\tconst shadowFn = paper?.shadow;\n\t\tconst inset = Math.max(0, paper?.inset ?? 0);\n\t\tconst cutEdge = paper?.cutEdge === true;\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\tconst contentWidth = Math.max(1, bandWidth - this.paddingX * 2);\n\t\tconst leftPad = \" \".repeat(this.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, cutEdge)) {\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// A cut edge needs a seed that is stable for this block but different\n\t\t// from its neighbours, or every sheet on screen is cut identically.\n\t\tconst seed = cutEdge ? hashString(childLines.join(\"\\n\")) : 0;\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// Never let the cut reach into the content: the jitter comes out of\n\t\t\t// the padding the row was going to draw anyway.\n\t\t\tconst slack = Math.max(0, bandWidth - visibleWidth(line));\n\t\t\tconst cut = cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;\n\t\t\tconst rowWidth = bandWidth - cut;\n\t\t\tconst band = this.applyBg(line, rowWidth);\n\t\t\t// The shadow holds one column whatever the cut does to the edge in\n\t\t\t// front of it. Following the cut was the first attempt and it broke\n\t\t\t// the shadow: `▌` paints half a cell, so a one-column step leaves no\n\t\t\t// overlap at all between one row's mark and the next, and what the\n\t\t\t// eye gets is a dashed staircase rather than an edge. A cut row\n\t\t\t// shows its nick as a column of page between sheet and shadow —\n\t\t\t// which is what a nick is — and the shadow stays a single line.\n\t\t\t// The first row has none: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? \" \".repeat(cut) + 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\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▀\".repeat(hasColumn ? bandWidth : 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\tcutEdge,\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"]}
@@ -24,9 +24,7 @@ export class Box {
24
24
  paddingX;
25
25
  paddingY;
26
26
  bgFn;
27
- shadowFn;
28
- inset = 0;
29
- cutEdge = false;
27
+ paperFn;
30
28
  // Cache for rendered output
31
29
  cache;
32
30
  constructor(paddingX = 1, paddingY = 1, bgFn) {
@@ -67,63 +65,41 @@ export class Box {
67
65
  // Don't invalidate here - we'll detect bgFn changes by sampling output
68
66
  }
69
67
  /**
70
- * Draw an offset band under the box, so the block reads as a sheet laid on
71
- * the page rather than a color printed into it.
68
+ * Give the box the paper treatment, resolved on every frame.
72
69
  *
73
- * A terminal has no sub-pixel offsets, so the shadow is one extra row of
74
- * `▀` an upper half-block, which paints solid color across the top half of
75
- * its cells and so hugs the box's bottom edge indented one column to give
76
- * the offset. An inset box gets a matching column of `▌` down its right edge
77
- * and the bottom run reaches under it, so the two meet at the corner; with
78
- * no inset the band already owns the last cell, so the bottom edge is all
79
- * there is room for.
70
+ * A shadow, a gutter and a cut edge arrive together because they are one
71
+ * decision, and it is a decision the *theme* makes so the box asks for it
72
+ * at render time rather than being told once. A box built under one theme
73
+ * outlives it: the user switches theme with the block already on screen, and
74
+ * a shadow function captured at construction would still be painting the old
75
+ * theme's ink (or reaching for a colour the new theme never defined).
80
76
  *
81
- * Passing no function draws no shadow, which is the behavior of every theme
82
- * that does not opt in.
83
- */
84
- setShadowFn(shadowFn) {
85
- this.shadowFn = shadowFn;
86
- this.invalidateCache();
87
- }
88
- /**
89
- * Hold the band back from the right margin, leaving a gutter of page.
77
+ * The shadow itself is one extra row of `▀` an upper half-block, which
78
+ * paints solid colour across the top half of its cells and so hugs the box's
79
+ * bottom edge — indented one column to give the offset a terminal cannot
80
+ * give in sub-pixels. An inset box gets a matching column of `▌` down its
81
+ * right edge and the bottom run reaches under it, so the two close the
82
+ * corner; with no inset the band already owns the last cell, so the bottom
83
+ * edge is all there is room for.
90
84
  *
91
- * A block that runs the full width of the terminal has no right edge to
92
- * show: it is a band of colour between two screen edges, not a sheet on a
93
- * page. Reserving a few columns gives it one, which is what makes both the
94
- * shadow's right-hand column and a cut edge possible at all.
85
+ * Passing no provider, or one that returns nothing, draws the full-width
86
+ * band the box has always drawn.
95
87
  */
96
- setInset(inset) {
97
- if (this.inset === inset)
98
- return;
99
- this.inset = Math.max(0, inset);
100
- this.invalidateCache();
101
- }
102
- /**
103
- * Cut the right edge by hand rather than ruling it.
104
- *
105
- * The jitter is one column at most and it only ever eats padding, never
106
- * content, so a cut edge cannot cost a character. It is derived from the row
107
- * index and the block's own content, which keeps it identical between frames
108
- * — an edge that reshuffled on every render would read as noise, not paper.
109
- */
110
- setCutEdge(cutEdge) {
111
- if (this.cutEdge === cutEdge)
112
- return;
113
- this.cutEdge = cutEdge;
88
+ setPaper(paperFn) {
89
+ this.paperFn = paperFn;
114
90
  this.invalidateCache();
115
91
  }
116
92
  invalidateCache() {
117
93
  this.cache = undefined;
118
94
  }
119
- matchCache(width, childLines, bgSample, shadowSample) {
95
+ matchCache(width, childLines, bgSample, shadowSample, inset, cutEdge) {
120
96
  const cache = this.cache;
121
97
  return (!!cache &&
122
98
  cache.width === width &&
123
99
  cache.bgSample === bgSample &&
124
100
  cache.shadowSample === shadowSample &&
125
- cache.inset === this.inset &&
126
- cache.cutEdge === this.cutEdge &&
101
+ cache.inset === inset &&
102
+ cache.cutEdge === cutEdge &&
127
103
  cache.childLines.length === childLines.length &&
128
104
  cache.childLines.every((line, i) => line === childLines[i]));
129
105
  }
@@ -137,9 +113,16 @@ export class Box {
137
113
  if (this.children.length === 0) {
138
114
  return [];
139
115
  }
116
+ // The paper treatment is asked for per frame, so a box already on screen
117
+ // follows a theme switch instead of holding the treatment it was built
118
+ // with.
119
+ const paper = this.paperFn?.();
120
+ const shadowFn = paper?.shadow;
121
+ const inset = Math.max(0, paper?.inset ?? 0);
122
+ const cutEdge = paper?.cutEdge === true;
140
123
  // The band stops short of the right margin when inset, leaving a gutter of
141
124
  // page for the sheet's own edge and the shadow that follows it.
142
- const bandWidth = Math.max(1, width - this.inset);
125
+ const bandWidth = Math.max(1, width - inset);
143
126
  const contentWidth = Math.max(1, bandWidth - this.paddingX * 2);
144
127
  const leftPad = " ".repeat(this.paddingX);
145
128
  // Render all children
@@ -155,9 +138,9 @@ export class Box {
155
138
  }
156
139
  // Check if bgFn output changed by sampling
157
140
  const bgSample = this.bgFn ? this.bgFn("test") : undefined;
158
- const shadowSample = this.shadowFn ? this.shadowFn("test") : undefined;
141
+ const shadowSample = shadowFn ? shadowFn("test") : undefined;
159
142
  // Check cache validity
160
- if (this.matchCache(width, childLines, bgSample, shadowSample)) {
143
+ if (this.matchCache(width, childLines, bgSample, shadowSample, inset, cutEdge)) {
161
144
  return this.cache.lines;
162
145
  }
163
146
  // Apply background and padding
@@ -169,16 +152,15 @@ export class Box {
169
152
  rows.push("");
170
153
  // A cut edge needs a seed that is stable for this block but different
171
154
  // from its neighbours, or every sheet on screen is cut identically.
172
- const seed = this.cutEdge ? hashString(childLines.join("\n")) : 0;
173
- const shadowFn = this.shadowFn;
155
+ const seed = cutEdge ? hashString(childLines.join("\n")) : 0;
174
156
  // The right-hand column only exists when a gutter was reserved for it.
175
- const hasColumn = shadowFn !== undefined && this.inset > 0;
157
+ const hasColumn = shadowFn !== undefined && inset > 0;
176
158
  const result = [];
177
159
  rows.forEach((line, index) => {
178
160
  // Never let the cut reach into the content: the jitter comes out of
179
161
  // the padding the row was going to draw anyway.
180
162
  const slack = Math.max(0, bandWidth - visibleWidth(line));
181
- const cut = this.cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;
163
+ const cut = cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;
182
164
  const rowWidth = bandWidth - cut;
183
165
  const band = this.applyBg(line, rowWidth);
184
166
  // The shadow holds one column whatever the cut does to the edge in
@@ -205,8 +187,8 @@ export class Box {
205
187
  width,
206
188
  bgSample,
207
189
  shadowSample,
208
- inset: this.inset,
209
- cutEdge: this.cutEdge,
190
+ inset,
191
+ cutEdge,
210
192
  lines: result,
211
193
  };
212
194
  return result;
@@ -1 +1 @@
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\tcutEdge: boolean;\n\tlines: string[];\n};\n\n/**\n * Roughly one row in this many takes the cut.\n *\n * A coin flip per row is not a cut edge, it is a sawtooth: the eye reads\n * alternating columns as a pattern, which is the opposite of hand-made. Scissors\n * leave a mostly straight line with the occasional nick, so the deviation has to\n * be rare enough to read as an accident.\n */\nconst CUT_EVERY = 5;\n\n/** djb2. Small, stable, and good enough to decide one column of a paper edge. */\nfunction hashString(value: string): number {\n\tlet hash = 5381;\n\tfor (let i = 0; i < value.length; i++) {\n\t\thash = ((hash << 5) + hash + value.charCodeAt(i)) >>> 0;\n\t}\n\treturn hash;\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 shadowFn?: (text: string) => string;\n\tprivate inset = 0;\n\tprivate cutEdge = false;\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 * Draw an offset band under the box, so the block reads as a sheet laid on\n\t * the page rather than a color printed into it.\n\t *\n\t * A terminal has no sub-pixel offsets, so the shadow is one extra row of\n\t * `▀` — an upper half-block, which paints solid color across the top half of\n\t * its cells and so hugs the box's bottom edge — indented one column to give\n\t * the offset. An inset box gets a matching column of `▌` down its right edge\n\t * and the bottom run reaches under it, so the two meet at the corner; with\n\t * no inset the band already owns the last cell, so the bottom edge is all\n\t * there is room for.\n\t *\n\t * Passing no function draws no shadow, which is the behavior of every theme\n\t * that does not opt in.\n\t */\n\tsetShadowFn(shadowFn?: (text: string) => string): void {\n\t\tthis.shadowFn = shadowFn;\n\t\tthis.invalidateCache();\n\t}\n\n\t/**\n\t * Hold the band back from the right margin, leaving a gutter of page.\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 cut edge possible at all.\n\t */\n\tsetInset(inset: number): void {\n\t\tif (this.inset === inset) return;\n\t\tthis.inset = Math.max(0, inset);\n\t\tthis.invalidateCache();\n\t}\n\n\t/**\n\t * Cut the right edge by hand rather than ruling it.\n\t *\n\t * The jitter is one column at most and it only ever eats padding, never\n\t * content, so a cut edge cannot cost a character. It is derived from the row\n\t * index and the block's own content, which keeps it identical between frames\n\t * — an edge that reshuffled on every render would read as noise, not paper.\n\t */\n\tsetCutEdge(cutEdge: boolean): void {\n\t\tif (this.cutEdge === cutEdge) return;\n\t\tthis.cutEdge = cutEdge;\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): 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 === this.inset &&\n\t\t\tcache.cutEdge === this.cutEdge &&\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 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 - this.inset);\n\t\tconst contentWidth = Math.max(1, bandWidth - this.paddingX * 2);\n\t\tconst leftPad = \" \".repeat(this.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 = this.shadowFn ? this.shadowFn(\"test\") : undefined;\n\n\t\t// Check cache validity\n\t\tif (this.matchCache(width, childLines, bgSample, shadowSample)) {\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// A cut edge needs a seed that is stable for this block but different\n\t\t// from its neighbours, or every sheet on screen is cut identically.\n\t\tconst seed = this.cutEdge ? hashString(childLines.join(\"\\n\")) : 0;\n\t\tconst shadowFn = this.shadowFn;\n\t\t// The right-hand column only exists when a gutter was reserved for it.\n\t\tconst hasColumn = shadowFn !== undefined && this.inset > 0;\n\t\tconst result: string[] = [];\n\t\trows.forEach((line, index) => {\n\t\t\t// Never let the cut reach into the content: the jitter comes out of\n\t\t\t// the padding the row was going to draw anyway.\n\t\t\tconst slack = Math.max(0, bandWidth - visibleWidth(line));\n\t\t\tconst cut = this.cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;\n\t\t\tconst rowWidth = bandWidth - cut;\n\t\t\tconst band = this.applyBg(line, rowWidth);\n\t\t\t// The shadow holds one column whatever the cut does to the edge in\n\t\t\t// front of it. Following the cut was the first attempt and it broke\n\t\t\t// the shadow: `▌` paints half a cell, so a one-column step leaves no\n\t\t\t// overlap at all between one row's mark and the next, and what the\n\t\t\t// eye gets is a dashed staircase rather than an edge. A cut row\n\t\t\t// shows its nick as a column of page between sheet and shadow —\n\t\t\t// which is what a nick is — and the shadow stays a single line.\n\t\t\t// The first row has none: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? \" \".repeat(cut) + 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\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▀\".repeat(hasColumn ? bandWidth : 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: this.inset,\n\t\t\tcutEdge: this.cutEdge,\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\tcutEdge: boolean;\n\tlines: string[];\n};\n\n/**\n * Roughly one row in this many takes the cut.\n *\n * A coin flip per row is not a cut edge, it is a sawtooth: the eye reads\n * alternating columns as a pattern, which is the opposite of hand-made. Scissors\n * leave a mostly straight line with the occasional nick, so the deviation has to\n * be rare enough to read as an accident.\n */\nconst CUT_EVERY = 5;\n\n/**\n * The paper treatment a box asks its owner for on every frame: the ink of its\n * shadow, the gutter it holds back from the right margin, and whether its right\n * edge is cut by hand. Each part is optional, and a sheet with none of them is\n * 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 cut edge possible at all.\n\t */\n\tinset?: number;\n\t/**\n\t * Cut the right edge by hand rather than ruling it.\n\t *\n\t * The jitter is one column at most and it only ever eats padding, never\n\t * content, so a cut edge cannot cost a character. It is derived from the row\n\t * index and the block's own content, which keeps it identical between frames\n\t * — an edge that reshuffled on every render would read as noise, not paper.\n\t */\n\tcutEdge?: boolean;\n}\n\n/** djb2. Small, stable, and good enough to decide one column of a paper edge. */\nfunction hashString(value: string): number {\n\tlet hash = 5381;\n\tfor (let i = 0; i < value.length; i++) {\n\t\thash = ((hash << 5) + hash + value.charCodeAt(i)) >>> 0;\n\t}\n\treturn hash;\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, a gutter and a cut edge 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\tcutEdge: boolean,\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.cutEdge === cutEdge &&\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\tconst shadowFn = paper?.shadow;\n\t\tconst inset = Math.max(0, paper?.inset ?? 0);\n\t\tconst cutEdge = paper?.cutEdge === true;\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\tconst contentWidth = Math.max(1, bandWidth - this.paddingX * 2);\n\t\tconst leftPad = \" \".repeat(this.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, cutEdge)) {\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// A cut edge needs a seed that is stable for this block but different\n\t\t// from its neighbours, or every sheet on screen is cut identically.\n\t\tconst seed = cutEdge ? hashString(childLines.join(\"\\n\")) : 0;\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// Never let the cut reach into the content: the jitter comes out of\n\t\t\t// the padding the row was going to draw anyway.\n\t\t\tconst slack = Math.max(0, bandWidth - visibleWidth(line));\n\t\t\tconst cut = cutEdge && slack > 0 && hashString(`${seed}:${index}`) % CUT_EVERY === 0 ? 1 : 0;\n\t\t\tconst rowWidth = bandWidth - cut;\n\t\t\tconst band = this.applyBg(line, rowWidth);\n\t\t\t// The shadow holds one column whatever the cut does to the edge in\n\t\t\t// front of it. Following the cut was the first attempt and it broke\n\t\t\t// the shadow: `▌` paints half a cell, so a one-column step leaves no\n\t\t\t// overlap at all between one row's mark and the next, and what the\n\t\t\t// eye gets is a dashed staircase rather than an edge. A cut row\n\t\t\t// shows its nick as a column of page between sheet and shadow —\n\t\t\t// which is what a nick is — and the shadow stays a single line.\n\t\t\t// The first row has none: the offset is down *and* right.\n\t\t\tconst column = hasColumn && index > 0 ? \" \".repeat(cut) + 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\tif (shadowFn && bandWidth > 1) {\n\t\t\tresult.push(` ${shadowFn(\"▀\".repeat(hasColumn ? bandWidth : 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\tcutEdge,\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
@@ -1,5 +1,5 @@
1
1
  export { type AutocompleteItem, type AutocompleteProvider, type AutocompleteSuggestions, CombinedAutocompleteProvider, type SlashCommand, } from "./autocomplete.js";
2
- export { Box } from "./components/box.js";
2
+ export { Box, type PaperSheet } from "./components/box.js";
3
3
  export { CancellableLoader } from "./components/cancellable-loader.js";
4
4
  export { DEFAULT_EDITOR_BORDER_CHARS, Editor, type EditorBorderChars, type EditorBorderStyle, type EditorOptions, type EditorTheme, } from "./components/editor.js";
5
5
  export { Image, type ImageOptions, type ImageTheme } from "./components/image.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,YAAY,GACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EACN,2BAA2B,EAC3B,MAAM,EACN,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,WAAW,GAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EACN,KAAK,UAAU,EACf,UAAU,EACV,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gCAAgC,GACrC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,KAAK,WAAW,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AACvG,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAE/D,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EACN,cAAc,EACd,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EACH,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,UAAU,EACV,QAAQ,EACR,sBAAsB,GACtB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEnG,OAAO,EAAE,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EACN,eAAe,EACf,KAAK,cAAc,EACnB,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,EACT,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,aAAa,EACb,WAAW,EACX,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,KAAK,oBAAoB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,aAAa,EACb,KAAK,SAAS,EACd,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,GAAG,GACH,MAAM,UAAU,CAAC;AAElB,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 } 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} from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { 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 { 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\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 SizeValue,\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} from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { 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 { 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\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 SizeValue,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
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,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EACN,2BAA2B,EAC3B,MAAM,GAKN,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,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,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,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,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,EAMX,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 } 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} from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { 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 { 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\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 SizeValue,\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,GAKN,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAsC,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,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,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,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,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,EAMX,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} from \"./components/editor.js\";\nexport { Image, type ImageOptions, type ImageTheme } from \"./components/image.js\";\nexport { 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 { 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\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 SizeValue,\n\tTUI,\n} from \"./tui.js\";\n// Utilities\nexport { applyBackgroundToLine, truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"./utils.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolisachint/hoocode-tui",
3
- "version": "0.5.38",
3
+ "version": "0.5.40",
4
4
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",