@chengchenccc/tui 0.2.0-rc.2 → 0.2.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/components/ansi-console.d.ts.map +1 -1
  2. package/dist/components/ansi-console.js +5 -1
  3. package/dist/components/loader.d.ts +9 -9
  4. package/dist/components/loader.d.ts.map +1 -1
  5. package/dist/components/loader.js +10 -9
  6. package/dist/components/output-block.d.ts.map +1 -1
  7. package/dist/components/output-block.js +10 -6
  8. package/dist/components/output-block.test.d.ts +2 -0
  9. package/dist/components/output-block.test.d.ts.map +1 -0
  10. package/dist/components/output-block.test.js +32 -0
  11. package/dist/components/statusline.d.ts.map +1 -1
  12. package/dist/components/statusline.js +3 -7
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/loader-sweep.test.js +2 -2
  17. package/dist/mermaid.d.ts.map +1 -1
  18. package/dist/mermaid.js +13 -1
  19. package/dist/stdin-buffer.d.ts +3 -0
  20. package/dist/stdin-buffer.d.ts.map +1 -1
  21. package/dist/stdin-buffer.js +13 -3
  22. package/dist/stdin-buffer.test.js +15 -0
  23. package/dist/terminal.d.ts +2 -0
  24. package/dist/terminal.d.ts.map +1 -1
  25. package/dist/terminal.js +16 -1
  26. package/dist/theme.d.ts +46 -0
  27. package/dist/theme.d.ts.map +1 -0
  28. package/dist/theme.js +52 -0
  29. package/dist/tui-scrollback.test.js +92 -0
  30. package/dist/tui.d.ts +3 -0
  31. package/dist/tui.d.ts.map +1 -1
  32. package/dist/tui.js +47 -4
  33. package/package.json +1 -1
  34. package/dist/mouse.d.ts +0 -40
  35. package/dist/mouse.d.ts.map +0 -1
  36. package/dist/mouse.js +0 -39
  37. package/dist/mouse.test.d.ts +0 -2
  38. package/dist/mouse.test.d.ts.map +0 -1
  39. package/dist/mouse.test.js +0 -36
@@ -1 +1 @@
1
- {"version":3,"file":"ansi-console.d.ts","sourceRoot":"","sources":["../../src/components/ansi-console.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAK3C;;;;;;;;;;sCAUsC;AACtC,qBAAa,WAAY,YAAW,SAAS;IAQzC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAVlC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAqC;IACxD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAM;gBAGvB,OAAO,EAAE,MAAM,EACE,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC/B,SAAS,EAAE,MAAM,IAAI,EACrB,eAAe,GAAE,MAAM,IAAe;IAUzD,iEAAiE;IACjE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMzB;0EACsE;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAc/B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAU/B,UAAU,IAAI,IAAI;CAInB"}
1
+ {"version":3,"file":"ansi-console.d.ts","sourceRoot":"","sources":["../../src/components/ansi-console.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAS3C;;;;;;;;;;sCAUsC;AACtC,qBAAa,WAAY,YAAW,SAAS;IAQzC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAVlC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAqC;IACxD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,WAAW,CAAM;gBAGvB,OAAO,EAAE,MAAM,EACE,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC/B,SAAS,EAAE,MAAM,IAAI,EACrB,eAAe,GAAE,MAAM,IAAe;IAUzD,iEAAiE;IACjE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMzB;0EACsE;IACtE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAc/B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAU/B,UAAU,IAAI,IAAI;CAInB"}
@@ -1,5 +1,9 @@
1
1
  import xterm from "@xterm/headless";
2
+ import { sliceByColumn } from "../utils.js";
2
3
  import { lineToAnsi } from "../virtual-terminal.js";
4
+ /** sliceByColumn's strict flag: hard-cut at the column boundary rather
5
+ * than leave a wide char straddling it. */
6
+ const TRUNCATE_STRICT = true;
3
7
  const XtermTerminal = xterm.Terminal;
4
8
  /** Live ANSI console pane backed by a headless xterm VT.
5
9
  *
@@ -57,7 +61,7 @@ export class AnsiConsole {
57
61
  this.cachedWidth = width;
58
62
  this.dirty = false;
59
63
  }
60
- return this.cached.map((line) => line.slice(0, width));
64
+ return this.cached.map((line) => sliceByColumn(line, 0, width, TRUNCATE_STRICT));
61
65
  }
62
66
  handleInput(data) {
63
67
  // A lone Esc terminates the console (pi semantics); escape sequences
@@ -9,17 +9,17 @@ export interface LoaderIndicatorOptions {
9
9
  /**
10
10
  * Loader component that updates with an optional spinning animation.
11
11
  */
12
- /** The one light-sweep palette. The loader's message band and every
13
- * consumer's shimmer (oma's chrome rows, tool bodies) read from here, so the
14
- * "working" animations cannot drift into different colors: cyan is the
15
- * spinner accent, so band and spinner read as one animation. */
12
+ /** The one light-sweep palette, fed from the theme (sweepLow/Mid/High). The
13
+ * loader's message band and every consumer's shimmer (oma's chrome rows,
14
+ * tool bodies) read from here, so the "working" animations cannot drift
15
+ * into different colors: band and spinner read as one animation. */
16
16
  export declare const SHIMMER_TIER_OPEN: {
17
17
  /** Base tier: dim, the resting color of an unsettled line. */
18
- readonly low: "\u001B[2m";
19
- /** Mid band: the spinner's own cyan. */
20
- readonly mid: "\u001B[36m";
21
- /** Crest: bold cyan. */
22
- readonly high: "\u001B[1m\u001B[36m";
18
+ readonly low: string;
19
+ /** Mid band: the spinner's own accent. */
20
+ readonly mid: string;
21
+ /** Crest: bold accent. */
22
+ readonly high: `\u001B[1m${string}`;
23
23
  };
24
24
  export type ShimmerTier = keyof typeof SHIMMER_TIER_OPEN;
25
25
  export declare class Loader extends Text {
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD;;GAEG;AACH;;;iEAGiE;AACjE,eAAO,MAAM,iBAAiB;IAC5B,8DAA8D;;IAE9D,wCAAwC;;IAExC,wBAAwB;;CAEhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,IAAI;IAC9B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAK;IACzB;;mEAE+D;IAC/D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAwB;gBAGrC,EAAE,EAAE,GAAG,EACP,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,OAAO,GAAE,MAAqB,EAC9B,SAAS,CAAC,EAAE,sBAAsB;IAU3B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAOxC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAOZ,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMjC,YAAY,CAAC,SAAS,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAYtD,OAAO,CAAC,gBAAgB;IAYxB;;;qDAGiD;IACjD,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,aAAa;CAStB"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/components/loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAKD;;GAEG;AACH;;;qEAGqE;AACrE,eAAO,MAAM,iBAAiB;IAC5B,8DAA8D;;IAE9D,0CAA0C;;IAE1C,0BAA0B;;CAElB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAEzD,qBAAa,MAAO,SAAQ,IAAI;IAC9B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAAK;IACzB;;mEAE+D;IAC/D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,OAAO,CAAwB;gBAGrC,EAAE,EAAE,GAAG,EACP,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,EACvC,OAAO,GAAE,MAAqB,EAC9B,SAAS,CAAC,EAAE,sBAAsB;IAU3B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAOxC,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAOZ,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMjC,YAAY,CAAC,SAAS,CAAC,EAAE,sBAAsB,GAAG,IAAI;IAYtD,OAAO,CAAC,gBAAgB;IAYxB;;;qDAGiD;IACjD,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,aAAa;CAStB"}
@@ -1,20 +1,21 @@
1
+ import { tuiTheme } from "../theme.js";
1
2
  import { Text } from "./text.js";
2
3
  const DEFAULT_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
3
4
  const DEFAULT_INTERVAL_MS = 80;
4
5
  /**
5
6
  * Loader component that updates with an optional spinning animation.
6
7
  */
7
- /** The one light-sweep palette. The loader's message band and every
8
- * consumer's shimmer (oma's chrome rows, tool bodies) read from here, so the
9
- * "working" animations cannot drift into different colors: cyan is the
10
- * spinner accent, so band and spinner read as one animation. */
8
+ /** The one light-sweep palette, fed from the theme (sweepLow/Mid/High). The
9
+ * loader's message band and every consumer's shimmer (oma's chrome rows,
10
+ * tool bodies) read from here, so the "working" animations cannot drift
11
+ * into different colors: band and spinner read as one animation. */
11
12
  export const SHIMMER_TIER_OPEN = {
12
13
  /** Base tier: dim, the resting color of an unsettled line. */
13
- low: "\u001b[2m",
14
- /** Mid band: the spinner's own cyan. */
15
- mid: "\u001b[36m",
16
- /** Crest: bold cyan. */
17
- high: "\u001b[1m\u001b[36m",
14
+ low: tuiTheme.dim,
15
+ /** Mid band: the spinner's own accent. */
16
+ mid: tuiTheme.accent,
17
+ /** Crest: bold accent. */
18
+ high: `\u001b[1m${tuiTheme.accent}`,
18
19
  };
19
20
  export class Loader extends Text {
20
21
  frames = [...DEFAULT_FRAMES];
@@ -1 +1 @@
1
- {"version":3,"file":"output-block.d.ts","sourceRoot":"","sources":["../../src/components/output-block.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvF,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAeD,yEAAyE;AACzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,kBAAkB,CAAC,EAAE,MAAM,EAC3B,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,CAIR;AAwCD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,EAAE,CA+CvE;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAgC;IAEnD,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,MAAM,EAAE;IAStD,UAAU,IAAI,IAAI;IAKlB,OAAO,CAAC,QAAQ;CAmBjB"}
1
+ {"version":3,"file":"output-block.d.ts","sourceRoot":"","sources":["../../src/components/output-block.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvF,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+CAA+C;IAC/C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAkBD,yEAAyE;AACzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,kBAAkB,CAAC,EAAE,MAAM,EAC3B,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,CAIR;AAwCD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,EAAE,CA+CvE;AAED;;;;;;;GAOG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAgC;IAEnD,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,SAAS,MAAM,EAAE;IAStD,UAAU,IAAI,IAAI;IAKlB,OAAO,CAAC,QAAQ;CAmBjB"}
@@ -5,14 +5,18 @@
5
5
  * rich result blocks. It replaces the ad-hoc Card + Text children hacks that
6
6
  * made each tool renderer re-implement its own border/background rules.
7
7
  */
8
+ import { tuiTheme } from "../theme.js";
8
9
  import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
9
- /** Default border colors, keyed by state (omp-ish semantics). */
10
+ /** Border color per state — the SAME verdict tokens the header icon/title
11
+ * use (running gold, error crimson, success jade), so the frame echoes the
12
+ * verdict instead of a second, off-palette color system. Routed through
13
+ * tuiTheme like every other SGR decision in the TUI. */
10
14
  const STATE_BORDER = {
11
- pending: "\x1b[38;5;33m",
12
- running: "\x1b[38;5;33m",
13
- success: "\x1b[38;5;8m",
14
- error: "\x1b[38;5;31m",
15
- warning: "\x1b[38;5;11m",
15
+ pending: tuiTheme.info,
16
+ running: tuiTheme.warning,
17
+ success: tuiTheme.success,
18
+ error: tuiTheme.error,
19
+ warning: tuiTheme.warning,
16
20
  };
17
21
  function normalizePadding(value) {
18
22
  return value === undefined ? 1 : Math.max(0, Math.trunc(value));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=output-block.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-block.test.d.ts","sourceRoot":"","sources":["../../src/components/output-block.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { tuiTheme } from "../theme.js";
3
+ import { renderOutputBlock } from "./output-block.js";
4
+ /** The border is the state's verdict channel: it must carry the SAME theme
5
+ * token the header icon/title use (running gold, error crimson, success
6
+ * jade), so the frame color answers "live / failed / done" at a glance —
7
+ * and a future theme swap recolors borders with everything else. */
8
+ describe("output block state border", () => {
9
+ const top = (state) => renderOutputBlock({ state, sections: [], width: 12 })[0] ?? "";
10
+ test("border color mirrors the verdict token per state", () => {
11
+ expect(top("running")).toContain(tuiTheme.warning);
12
+ expect(top("error")).toContain(tuiTheme.error);
13
+ expect(top("success")).toContain(tuiTheme.success);
14
+ expect(top("pending")).toContain(tuiTheme.info);
15
+ expect(top("warning")).toContain(tuiTheme.warning);
16
+ });
17
+ /** The mapping this file replaced had running=blue and error=teal-blue —
18
+ * visually the same border for opposite verdicts, which is why the border
19
+ * carried no status signal at all. */
20
+ test("running and error borders are distinguishable", () => {
21
+ expect(top("running")).not.toBe(top("error"));
22
+ });
23
+ test("an explicit borderColor override still wins over the state default", () => {
24
+ const lines = renderOutputBlock({
25
+ state: "running",
26
+ borderColor: tuiTheme.dim,
27
+ sections: [],
28
+ width: 12,
29
+ });
30
+ expect(lines[0]).toContain(tuiTheme.dim);
31
+ });
32
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../src/components/statusline.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;uEACuE;AACvE,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAatE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAaT"}
1
+ {"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../src/components/statusline.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;uEACuE;AACvE,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,CAatE;AAED,kEAAkE;AAClE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,MAAM,CAaT"}
@@ -1,8 +1,4 @@
1
- /**
2
- * Minimal omp-style status bar renderer (powerline chip + semantic colors).
3
- * Pure ANSI functions, no component state. Keeping this in packages/tui lets
4
- * both the bottom status line and (optionally) the header reuse it.
5
- */
1
+ import { tuiTheme } from "../theme.js";
6
2
  /** One powerline chip: ◀bg label bg▶ using the same color for arrows/fill,
7
3
  * but white bold text inside so the label is readable on the tint. */
8
4
  export function chip(text, bgAnsi) {
@@ -17,7 +13,7 @@ export function renderStatusBar(segs) {
17
13
  if (!s.text)
18
14
  continue;
19
15
  if (i > 0)
20
- out.push(`\x1b[38;5;8m ┆ \x1b[0m`);
16
+ out.push(`${tuiTheme.dim} ┆ \x1b[0m`);
21
17
  if (s.chip && s.bg) {
22
18
  out.push(chip(s.text, s.bg));
23
19
  }
@@ -39,7 +35,7 @@ export function renderToolHeader(opts) {
39
35
  if (meta.length > 0)
40
36
  parts.push(meta.join(" · "));
41
37
  if (parts.length > 0) {
42
- const dim = opts.dimColor ?? "\u001b[2m";
38
+ const dim = opts.dimColor ?? tuiTheme.dim;
43
39
  return `${title} ${dim}${parts.join(" · ")}\u001b[0m`;
44
40
  }
45
41
  return title;
package/dist/index.d.ts CHANGED
@@ -15,12 +15,12 @@ export { TruncatedText } from "./components/truncated-text.ts";
15
15
  export type { EditorComponent } from "./editor-component.ts";
16
16
  export { getKeybindings, type Keybinding, type KeybindingConflict, type KeybindingDefinition, type KeybindingDefinitions, type Keybindings, type KeybindingsConfig, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.ts";
17
17
  export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, type KeyEventType, type KeyId, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.ts";
18
- export { parseSgrMouse, routeSgrMouseInput, type SgrMouseEvent, type SgrMouseHandler, } from "./mouse.ts";
19
18
  export { StdinBuffer, type StdinBufferEventMap, type StdinBufferOptions, } from "./stdin-buffer.ts";
20
19
  export { KITTY_KEYBOARD_SET_FLAGS, ProcessTerminal, type Terminal, } from "./terminal.ts";
21
20
  export { parseOsc11BackgroundColor, parseTerminalColorSchemeReport, type RgbColor, type TerminalColorScheme, } from "./terminal-colors.ts";
22
21
  export { deleteKittyImage, getCapabilities, isImageLine, setCellDimensions, } from "./terminal-image.ts";
23
22
  export { setTextSizingEnabled, textSizingEnabled, textSizingSupported, } from "./text-sizing.ts";
23
+ export { obsidianDarkTheme, type TuiTheme, tuiTheme } from "./theme.ts";
24
24
  export { type Component, Container, CURSOR_MARKER, type Focusable, type FramePlan, getNativeScrollbackLiveRegionStart, getRenderStablePrefixRows, type HistoryBatch, isFocusable, type NativeScrollbackCommittedRows, type NativeScrollbackLiveRegion, type NativeScrollbackReplay, type OverlayAnchor, type OverlayHandle, type OverlayMargin, type OverlayOptions, type OverlayUnfocusOptions, prepareNativeScrollbackReplay, type RenderStablePrefix, type SizeValue, setNativeScrollbackCommittedRows, type TerminalFrameProvider, TUI, type ViewportTailProvider, } from "./tui.ts";
25
25
  export { applyBackgroundToLine, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.ts";
26
26
  export { VirtualTerminal } from "./virtual-terminal.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,4BAA4B,EAC5B,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EACL,MAAM,EACN,KAAK,aAAa,EAClB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAC9C,OAAO,EACL,MAAM,EACN,KAAK,sBAAsB,EAC3B,iBAAiB,EACjB,KAAK,WAAW,GACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,KAAK,gBAAgB,EACrB,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,aAAa,GACnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,KAAK,UAAU,EACf,UAAU,EACV,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,gCAAgC,GACtC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EACL,IAAI,EACJ,eAAe,EACf,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,4BAA4B,CAAC;AACpC,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,EACL,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,GAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,GAAG,EACH,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,UAAU,EACV,QAAQ,EACR,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,WAAW,EACX,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,KAAK,QAAQ,GACd,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,yBAAyB,EACzB,8BAA8B,EAC9B,KAAK,QAAQ,EACb,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EACL,KAAK,SAAS,EACd,SAAS,EACT,aAAa,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,kCAAkC,EAClC,yBAAyB,EACzB,KAAK,YAAY,EACjB,WAAW,EACX,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,6BAA6B,EAC7B,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,gCAAgC,EAChC,KAAK,qBAAqB,EAC1B,GAAG,EACH,KAAK,oBAAoB,GAC1B,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -21,8 +21,6 @@ export { TruncatedText } from "./components/truncated-text.js";
21
21
  export { getKeybindings, KeybindingsManager, setKeybindings, TUI_KEYBINDINGS, } from "./keybindings.js";
22
22
  // Keyboard input handling
23
23
  export { decodeKittyPrintable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, Key, matchesKey, parseKey, setKittyProtocolActive, } from "./keys.js";
24
- // SGR mouse reports (wheel scroll routing)
25
- export { parseSgrMouse, routeSgrMouseInput, } from "./mouse.js";
26
24
  // Input buffering for batch splitting
27
25
  export { StdinBuffer, } from "./stdin-buffer.js";
28
26
  // Terminal interface and implementations
@@ -32,6 +30,8 @@ export { parseOsc11BackgroundColor, parseTerminalColorSchemeReport, } from "./te
32
30
  // Terminal image helpers (used internally by tui.ts)
33
31
  export { deleteKittyImage, getCapabilities, isImageLine, setCellDimensions, } from "./terminal-image.js";
34
32
  export { setTextSizingEnabled, textSizingEnabled, textSizingSupported, } from "./text-sizing.js";
33
+ // Theme tokens (default: omp obsidian dark)
34
+ export { obsidianDarkTheme, tuiTheme } from "./theme.js";
35
35
  export { Container, CURSOR_MARKER, getNativeScrollbackLiveRegionStart, getRenderStablePrefixRows, isFocusable, prepareNativeScrollbackReplay, setNativeScrollbackCommittedRows, TUI, } from "./tui.js";
36
36
  // Utilities
37
37
  export { applyBackgroundToLine, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "./utils.js";
@@ -1,9 +1,9 @@
1
1
  import { describe, expect, test, vi } from "bun:test";
2
- import { Loader } from "./components/loader.js";
2
+ import { Loader, SHIMMER_TIER_OPEN } from "./components/loader.js";
3
3
  const ESC = String.fromCharCode(27);
4
4
  /** Cell index of the bright (bold) sweep tier on the rendered line, or -1. */
5
5
  function bandCell(line) {
6
- return visibleIndex(line, `${ESC}[1m${ESC}[36m`);
6
+ return visibleIndex(line, SHIMMER_TIER_OPEN.high);
7
7
  }
8
8
  /** The message row: Loader.render pads with a blank row above and below. */
9
9
  function messageRow(loader) {
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../src/mermaid.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,kBAAkB,EAAsB,MAAM,iCAAiC,CAAC;AAE9F,YAAY,EAAE,kBAAkB,IAAI,yBAAyB,EAAE,CAAC;AA+BhE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAMf;AAED,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,GAAG,IAAI,CA4Bf;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../src/mermaid.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,KAAK,kBAAkB,EAAsB,MAAM,iCAAiC,CAAC;AAE9F,YAAY,EAAE,kBAAkB,IAAI,yBAAyB,EAAE,CAAC;AA0ChE,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,kBAAkB,GAC3B,MAAM,GAAG,IAAI,CAMf;AAED,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,qBAAqB,GAC9B,MAAM,GAAG,IAAI,CA4Bf;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
package/dist/mermaid.js CHANGED
@@ -7,7 +7,13 @@
7
7
  * forced direction when the base layout overflows the requested maxWidth.
8
8
  */
9
9
  import { renderMermaidASCII } from "./vendor/mermaid-ascii/index.js";
10
- /** Memoized renders (and failures), keyed on source + options + direction. */
10
+ /** Memoized renders (and failures), keyed on source + options + direction.
11
+ * Bounded: an assistant can emit unbounded distinct diagrams across a
12
+ * long session, and the cache holds rendered ASCII (plus failed renders)
13
+ * by strong reference — without a cap it grows until process exit.
14
+ * Insertion order gives a cheap FIFO eviction; re-hits do not reorder
15
+ * (ponytail: a true LRU matters only if diagrams repeat non-uniformly). */
16
+ const CACHE_CAP = 64;
11
17
  const cache = new Map();
12
18
  /** Widest rendered row in display columns (Bun wcwidth-style). */
13
19
  function asciiDisplayWidth(ascii) {
@@ -27,6 +33,12 @@ function renderVariant(source, baseOptions, baseKey, direction) {
27
33
  const options = direction ? { ...baseOptions, direction } : baseOptions;
28
34
  const ascii = renderMermaidAsciiSafe(source, options);
29
35
  cache.set(key, ascii);
36
+ while (cache.size > CACHE_CAP) {
37
+ const oldest = cache.keys().next().value;
38
+ if (oldest === undefined)
39
+ break;
40
+ cache.delete(oldest);
41
+ }
30
42
  return ascii;
31
43
  }
32
44
  export function renderMermaidAsciiSafe(source, options) {
@@ -40,6 +40,9 @@ export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
40
40
  private pasteBuffer;
41
41
  private pendingKittyPrintableCodepoint;
42
42
  private extendedEscFlush;
43
+ /** Survives across process() calls so a UTF-8 char split between two
44
+ * Buffer chunks decodes once, not as two replacement characters. */
45
+ private decoder;
43
46
  constructor(options?: StdinBufferOptions);
44
47
  process(data: string | Buffer): void;
45
48
  private emitDataSequence;
@@ -1 +1 @@
1
- {"version":3,"file":"stdin-buffer.d.ts","sourceRoot":"","sources":["../src/stdin-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAgPtC,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAChE,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,gBAAgB,CAAS;gBAErB,OAAO,GAAE,kBAAuB;IAKrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAwH3C,OAAO,CAAC,gBAAgB;IAWxB,KAAK,IAAI,MAAM,EAAE;IAgBjB,KAAK,IAAI,IAAI;IAWb,SAAS,IAAI,MAAM;IAInB,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"stdin-buffer.d.ts","sourceRoot":"","sources":["../src/stdin-buffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAgPtC,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,qBAAa,WAAY,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAChE,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,8BAA8B,CAAqB;IAC3D,OAAO,CAAC,gBAAgB,CAAS;IACjC;yEACqE;IACrE,OAAO,CAAC,OAAO,CAA8B;gBAEjC,OAAO,GAAE,kBAAuB;IAKrC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IA6H3C,OAAO,CAAC,gBAAgB;IAWxB,KAAK,IAAI,MAAM,EAAE;IAgBjB,KAAK,IAAI,IAAI;IAWb,SAAS,IAAI,MAAM;IAInB,OAAO,IAAI,IAAI;CAGhB"}
@@ -16,6 +16,7 @@
16
16
  * Based on code from OpenTUI (https://github.com/anomalyco/opentui)
17
17
  * MIT License - Copyright (c) 2025 opentui
18
18
  */
19
+ import { StringDecoder } from "node:string_decoder";
19
20
  import { EventEmitter } from "events";
20
21
  const ESC = "\x1b";
21
22
  const BRACKETED_PASTE_START = "\x1b[200~";
@@ -231,6 +232,9 @@ export class StdinBuffer extends EventEmitter {
231
232
  pasteBuffer = "";
232
233
  pendingKittyPrintableCodepoint;
233
234
  extendedEscFlush = false;
235
+ /** Survives across process() calls so a UTF-8 char split between two
236
+ * Buffer chunks decodes once, not as two replacement characters. */
237
+ decoder = null;
234
238
  constructor(options = {}) {
235
239
  super();
236
240
  this.timeoutMs = options.timeout ?? 10;
@@ -241,16 +245,22 @@ export class StdinBuffer extends EventEmitter {
241
245
  clearTimeout(this.timeout);
242
246
  this.timeout = null;
243
247
  }
244
- // Handle high-byte conversion (for compatibility with parseKeypress)
245
- // If buffer has single byte > 127, convert to ESC + (byte - 128)
248
+ // Handle high-byte conversion (for compatibility with parseKeypress).
249
+ // If buffer has single byte > 127, convert to ESC + (byte - 128).
250
+ // Everything else goes through a PERSISTENT StringDecoder: a UTF-8
251
+ // character split across two Buffer chunks must not decode as two
252
+ // replacement characters (emoji/CJK paste or fast typing).
246
253
  let str;
247
254
  if (Buffer.isBuffer(data)) {
248
255
  if (data.length === 1 && data[0] > 127) {
249
256
  const byte = data[0] - 128;
250
257
  str = `\x1b${String.fromCharCode(byte)}`;
258
+ this.decoder = null; // resync: the hack consumed a raw byte
251
259
  }
252
260
  else {
253
- str = data.toString();
261
+ if (!this.decoder)
262
+ this.decoder = new StringDecoder("utf8");
263
+ str = this.decoder.write(data);
254
264
  }
255
265
  }
256
266
  else {
@@ -36,3 +36,18 @@ describe("StdinBuffer bare-ESC flush extension", () => {
36
36
  expect(emitted).toEqual(["\x1b[99;1:3u"]);
37
37
  });
38
38
  });
39
+ describe("StdinBuffer cross-chunk UTF-8 (Buffer input)", () => {
40
+ test("a character split across two Buffer chunks decodes once", async () => {
41
+ const buf = new StdinBuffer({ timeout: 10 });
42
+ const emitted = [];
43
+ buf.on("data", (s) => emitted.push(s));
44
+ const bytes = Buffer.from("😀", "utf8"); // 4 code units
45
+ buf.process(bytes.subarray(0, 2));
46
+ buf.process(bytes.subarray(2));
47
+ await wait(30);
48
+ const joined = emitted.join("");
49
+ // Per-chunk toString() used to yield TWO replacement characters here.
50
+ expect(joined).toContain("😀");
51
+ expect(joined).not.toContain("\uFFFD");
52
+ });
53
+ });
@@ -51,6 +51,8 @@ export interface Terminal {
51
51
  */
52
52
  export declare class ProcessTerminal implements Terminal {
53
53
  private wasRaw;
54
+ private wasEncoding;
55
+ private wasFlowing;
54
56
  private inputHandler?;
55
57
  private resizeHandler?;
56
58
  private _kittyProtocolActive;
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
package/dist/terminal.js CHANGED
@@ -43,6 +43,8 @@ export function normalizeAppleTerminalInput(data, isAppleTerminal, isShiftPresse
43
43
  */
44
44
  export class ProcessTerminal {
45
45
  wasRaw = false;
46
+ wasEncoding = null;
47
+ wasFlowing = null;
46
48
  inputHandler;
47
49
  resizeHandler;
48
50
  _kittyProtocolActive = false;
@@ -78,8 +80,13 @@ export class ProcessTerminal {
78
80
  start(onInput, onResize) {
79
81
  this.inputHandler = onInput;
80
82
  this.resizeHandler = onResize;
81
- // Save previous state and enable raw mode
83
+ // Save previous state and enable raw mode. Encoding and flow state are
84
+ // saved too: as a LIBRARY, stop() must leave stdin the way it found it —
85
+ // an embedder's next readline/prompt would otherwise sit on a paused,
86
+ // forced-UTF8 stream (audit P2).
82
87
  this.wasRaw = process.stdin.isRaw || false;
88
+ this.wasEncoding = process.stdin.readableEncoding;
89
+ this.wasFlowing = process.stdin.readableFlowing;
83
90
  if (process.stdin.setRawMode) {
84
91
  process.stdin.setRawMode(true);
85
92
  }
@@ -362,6 +369,14 @@ export class ProcessTerminal {
362
369
  if (process.stdin.setRawMode) {
363
370
  process.stdin.setRawMode(this.wasRaw);
364
371
  }
372
+ // Restore the encoding/flow we borrowed. Flow: resume only if the
373
+ // stream was flowing before us. Encoding: a null (Buffer-mode) restore
374
+ // is not expressible in the typed surface (setEncoding lacks null);
375
+ // utf8-mode stdin is compatible with readline/prompt, so leaving it is
376
+ // the documented ceiling rather than a cast around the type.
377
+ process.stdin.setEncoding(this.wasEncoding ?? undefined);
378
+ if (this.wasFlowing)
379
+ process.stdin.resume();
365
380
  }
366
381
  write(data) {
367
382
  process.stdout.write(data);
@@ -0,0 +1,46 @@
1
+ /** TUI color tokens. Every SGR color decision in the TUI routes through
2
+ * `tuiTheme`, so a future theme feature swaps this one object instead of
3
+ * hunting escape codes across renderers.
4
+ *
5
+ * The default theme ports omp's "obsidian dark" palette (oh-my-pi
6
+ * theme/defaults/obsidian.json): warm-black neutrals, a violet accent, jade
7
+ * success, crimson error. Values are 24-bit-color SGR strings; terminals
8
+ * without truecolor degrade per their own palette. Attributes (bold,
9
+ * italic, underline, strike) are NOT themed — a theme recolors, it does not
10
+ * restyle structure.
11
+ *
12
+ * Ten color tokens, each a ROLE a renderer can name without knowing the
13
+ * palette: one brand accent, three verdict colors, one informational, two
14
+ * text tiers, three surfaces. Anything finer (a second orange, a separate
15
+ * heading tint) was tried and collapsed — tokens nobody can name from the
16
+ * sitting surface are taxonomy, not theme. */
17
+ export interface TuiTheme {
18
+ /** Brand accent: block titles, spinner + sweep band, selected markers,
19
+ * inline code, the banner. obsidian violet. */
20
+ readonly accent: string;
21
+ /** Paths, links, notice lines, informational chrome. obsidian cyan. */
22
+ readonly info: string;
23
+ /** Success marks, diff added, clean git. obsidian jade. */
24
+ readonly success: string;
25
+ /** Errors, failures, diff removed. obsidian crimson. */
26
+ readonly error: string;
27
+ /** Warnings and pressure (dirty git, hot context). obsidian gold. */
28
+ readonly warning: string;
29
+ /** Secondary text: timestamps, meta, settled rows, hints, quotes. */
30
+ readonly dim: string;
31
+ /** Faintest marks: markdown markers, hr, quote bars — structure that
32
+ * should sit below the dim tier. */
33
+ readonly faint: string;
34
+ /** Selected-row / chip / meter surface. obsidian glass. Inside an overlay
35
+ * this is ALSO the selection contrast against bgOverlay. */
36
+ readonly bgPanel: string;
37
+ /** Full-overlay surface. obsidian smoke. */
38
+ readonly bgOverlay: string;
39
+ /** "Running jobs" chip surface — jade sunk toward the panel tone. */
40
+ readonly bgChipOk: string;
41
+ }
42
+ export declare const obsidianDarkTheme: TuiTheme;
43
+ /** The active theme. Single swap point for the future theme feature
44
+ * (settings key → assign another TuiTheme here, re-render). */
45
+ export declare const tuiTheme: TuiTheme;
46
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;+CAe+C;AAE/C,MAAM,WAAW,QAAQ;IACvB;oDACgD;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;yCACqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;iEAC6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AA0BD,eAAO,MAAM,iBAAiB,EAAE,QAW/B,CAAC;AAEF;gEACgE;AAChE,eAAO,MAAM,QAAQ,EAAE,QAA4B,CAAC"}
package/dist/theme.js ADDED
@@ -0,0 +1,52 @@
1
+ /** TUI color tokens. Every SGR color decision in the TUI routes through
2
+ * `tuiTheme`, so a future theme feature swaps this one object instead of
3
+ * hunting escape codes across renderers.
4
+ *
5
+ * The default theme ports omp's "obsidian dark" palette (oh-my-pi
6
+ * theme/defaults/obsidian.json): warm-black neutrals, a violet accent, jade
7
+ * success, crimson error. Values are 24-bit-color SGR strings; terminals
8
+ * without truecolor degrade per their own palette. Attributes (bold,
9
+ * italic, underline, strike) are NOT themed — a theme recolors, it does not
10
+ * restyle structure.
11
+ *
12
+ * Ten color tokens, each a ROLE a renderer can name without knowing the
13
+ * palette: one brand accent, three verdict colors, one informational, two
14
+ * text tiers, three surfaces. Anything finer (a second orange, a separate
15
+ * heading tint) was tried and collapsed — tokens nobody can name from the
16
+ * sitting surface are taxonomy, not theme. */
17
+ /** "#rrggbb" → 24-bit foreground SGR. Module-private: themes are declared as
18
+ * hex constants and materialized once; call sites never build SGR strings. */
19
+ function fg(hex) {
20
+ return `\u001b[38;2;${parseInt(hex.slice(1, 3), 16)};${parseInt(hex.slice(3, 5), 16)};${parseInt(hex.slice(5, 7), 16)}m`;
21
+ }
22
+ /** "#rrggbb" → 24-bit background SGR. */
23
+ function bg(hex) {
24
+ return `\u001b[48;2;${parseInt(hex.slice(1, 3), 16)};${parseInt(hex.slice(3, 5), 16)};${parseInt(hex.slice(5, 7), 16)}m`;
25
+ }
26
+ /** omp obsidian dark, as hex — kept visible for diffing against upstream. */
27
+ const OBSIDIAN = {
28
+ glass: "#1a1816",
29
+ smoke: "#2b2825",
30
+ violet: "#9d7aff",
31
+ jade: "#52e8a0",
32
+ crimson: "#ff5570",
33
+ cyan: "#5cd9ff",
34
+ gold: "#ffc942",
35
+ muted: "#8a857f",
36
+ dim: "#4a4642",
37
+ };
38
+ export const obsidianDarkTheme = {
39
+ accent: fg(OBSIDIAN.violet),
40
+ info: fg(OBSIDIAN.cyan),
41
+ success: fg(OBSIDIAN.jade),
42
+ error: fg(OBSIDIAN.crimson),
43
+ warning: fg(OBSIDIAN.gold),
44
+ dim: fg(OBSIDIAN.muted),
45
+ faint: fg(OBSIDIAN.dim),
46
+ bgPanel: bg(OBSIDIAN.glass),
47
+ bgOverlay: bg(OBSIDIAN.smoke),
48
+ bgChipOk: bg("#20362b"),
49
+ };
50
+ /** The active theme. Single swap point for the future theme feature
51
+ * (settings key → assign another TuiTheme here, re-render). */
52
+ export const tuiTheme = obsidianDarkTheme;
@@ -48,6 +48,98 @@ describe("TUI provider native scrollback", () => {
48
48
  expect(scroll.filter((l) => l.startsWith("H"))).toEqual(["H0", "H1", "H2"]);
49
49
  expect(vt.getViewport().slice(-5)).toEqual(["V0", "V1", "V2", "V3", "V4"]);
50
50
  });
51
+ test("requestRender(true) repaints the screen but never purges scrollback", async () => {
52
+ const vt = new VirtualTerminal(20, 5);
53
+ const tui = new TUI(vt);
54
+ let frame = 0;
55
+ tui.setFrameProvider({
56
+ renderFrame() {
57
+ frame++;
58
+ const viewport = frame <= 2 ? ["V0", "V1", "V2", "V3", "V4"] : ["W0", "W1", "W2", "W3", "W4"];
59
+ if (frame === 1)
60
+ return { viewport, history: { id: 1, rows: ["H0", "H1"] } };
61
+ return { viewport };
62
+ },
63
+ acknowledgeHistory() { },
64
+ });
65
+ tui.start();
66
+ // frame 1 carries history, its ack recurses into frame 2; the frame
67
+ // counter is the deterministic drain signal.
68
+ while (frame < 2)
69
+ await vt.waitForRender();
70
+ expect(vt.getScrollBuffer().some((l) => l.startsWith("H0"))).toBe(true);
71
+ // The production triggers: transcript reconcile reset (/new, resume)
72
+ // and PTY overlay exit. Faking a width change here used to emit CSI 3 J
73
+ // and wipe the committed history.
74
+ tui.requestRender(true);
75
+ while (frame < 3)
76
+ await vt.waitForRender();
77
+ expect(vt.getScrollBuffer().some((l) => l.startsWith("H0"))).toBe(true);
78
+ expect(vt.getViewport().some((l) => l.startsWith("W0"))).toBe(true);
79
+ });
80
+ test("a real width change replays the committed history at the new width", async () => {
81
+ const vt = new VirtualTerminal(20, 5);
82
+ const tui = new TUI(vt);
83
+ let replayed = 0;
84
+ let frame = 0;
85
+ let frontier = 0;
86
+ let nextId = 1;
87
+ let historyRows = ["H0-0123456789abcdef", "H1-0123456789abcdef"];
88
+ tui.setFrameProvider({
89
+ beginHistoryReplay() {
90
+ replayed++;
91
+ frontier = 0;
92
+ nextId = 100;
93
+ // Re-laid-out at the new width, like a re-wrapping transcript.
94
+ historyRows = ["H0-0123", "H1-0123"];
95
+ },
96
+ renderFrame() {
97
+ frame++;
98
+ if (frontier < historyRows.length) {
99
+ const rows = historyRows.slice(frontier);
100
+ frontier += rows.length;
101
+ return { viewport: ["V*"], history: { id: nextId++, rows } };
102
+ }
103
+ return { viewport: ["V*"] };
104
+ },
105
+ acknowledgeHistory() { },
106
+ });
107
+ tui.start();
108
+ while (frame < 2)
109
+ await vt.waitForRender();
110
+ expect(vt.getScrollBuffer().some((l) => l.includes("H0-0123456789abcdef"))).toBe(true);
111
+ vt.resize(10, 5);
112
+ // Replay frame re-offers the whole prefix (frame 3) and its ack recurses
113
+ // once more (frame 4) before the stream settles.
114
+ while (frame < 4)
115
+ await vt.waitForRender();
116
+ // The purge happened (old-width rows gone) but the provider replayed
117
+ // its committed prefix, so the history survives re-wrapped.
118
+ expect(replayed).toBe(1);
119
+ const scroll = vt.getScrollBuffer().join("\n");
120
+ expect(scroll).not.toContain("H0-0123456789abcdef");
121
+ expect(scroll).toContain("H0-0123");
122
+ });
123
+ test("stop() flushes the frame provider (not only root children)", async () => {
124
+ const vt = new VirtualTerminal(20, 5);
125
+ const tui = new TUI(vt);
126
+ let flushed = 0;
127
+ tui.setFrameProvider({
128
+ renderFrame() {
129
+ return { viewport: ["V0", "V1", "V2", "V3", "V4"] };
130
+ },
131
+ acknowledgeHistory() { },
132
+ beginHistoryFlush() {
133
+ flushed++;
134
+ },
135
+ });
136
+ tui.start();
137
+ await vt.waitForRender();
138
+ tui.stop();
139
+ // The provider is not a root child; without the explicit call its
140
+ // pending history batch never drained on exit.
141
+ expect(flushed).toBe(1);
142
+ });
51
143
  });
52
144
  describe("adaptive render backpressure (omp #4145 port)", () => {
53
145
  test("a slow frame pushes the next ordinary paint out proportionally", async () => {
package/dist/tui.d.ts CHANGED
@@ -220,6 +220,9 @@ export declare class TUI extends Container {
220
220
  private providerForceRepaint;
221
221
  private fullRedrawCount;
222
222
  private stopped;
223
+ /** True once stop() ran: late async teardown (a pty overlay's deferred
224
+ * cleanup) must not write terminal modes into the user's restored shell. */
225
+ get isStopped(): boolean;
223
226
  private pendingOsc11BackgroundReplies;
224
227
  private pendingOsc11BackgroundQueries;
225
228
  private terminalColorSchemeListeners;
package/dist/tui.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"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"}
package/dist/tui.js CHANGED
@@ -155,6 +155,11 @@ export class TUI extends Container {
155
155
  providerForceRepaint = false;
156
156
  fullRedrawCount = 0;
157
157
  stopped = false;
158
+ /** True once stop() ran: late async teardown (a pty overlay's deferred
159
+ * cleanup) must not write terminal modes into the user's restored shell. */
160
+ get isStopped() {
161
+ return this.stopped;
162
+ }
158
163
  pendingOsc11BackgroundReplies = 0;
159
164
  pendingOsc11BackgroundQueries = [];
160
165
  terminalColorSchemeListeners = new Set();
@@ -518,6 +523,9 @@ export class TUI extends Container {
518
523
  for (const child of this.children) {
519
524
  child.beginHistoryFlush?.();
520
525
  }
526
+ // The frame provider is NOT a root child — flush it explicitly, or the
527
+ // provider's pending history batch never reaches the scrollback on exit.
528
+ this.frameProvider?.beginHistoryFlush?.();
521
529
  if (this.renderTimer) {
522
530
  clearTimeout(this.renderTimer);
523
531
  this.renderTimer = undefined;
@@ -537,18 +545,31 @@ export class TUI extends Container {
537
545
  }
538
546
  this.terminal.write("\r\n");
539
547
  }
548
+ else if (this.frameProvider && this.providerWindow.length > 0) {
549
+ // Provider mode never fills previousLines; the last frame placed the
550
+ // viewport at providerViewportTop. Park below it so shell output after
551
+ // exit does not land on the last frame's row.
552
+ const lastRow = this.providerViewportTop + this.providerWindow.length;
553
+ this.terminal.write(`\x1b[${lastRow + 1};1H`);
554
+ this.terminal.write("\r\n");
555
+ }
540
556
  this.terminal.showCursor();
541
557
  this.terminal.stop();
542
558
  }
543
559
  requestRender(force = false) {
544
560
  if (force) {
561
+ // Forced repaint clears the SCREEN, never the user's native
562
+ // scrollback. Faking a resize here (previousWidth=-1) used to make
563
+ // the renderer treat it as a width change and purge scrollback with
564
+ // CSI 3 J — after a transcript reset or a PTY overlay exit the
565
+ // committed history vanished. providerForceRepaint drives the clear
566
+ // in both render paths; only a REAL width change may purge.
545
567
  this.previousLines = [];
546
- this.previousWidth = -1; // -1 triggers widthChanged, forcing a full clear
547
- this.previousHeight = -1; // -1 triggers heightChanged, forcing a full clear
548
568
  this.cursorRow = 0;
549
569
  this.hardwareCursorRow = 0;
550
570
  this.maxLinesRendered = 0;
551
571
  this.previousViewportTop = 0;
572
+ this.providerForceRepaint = true;
552
573
  if (this.renderTimer) {
553
574
  clearTimeout(this.renderTimer);
554
575
  this.renderTimer = undefined;
@@ -1054,6 +1075,13 @@ export class TUI extends Container {
1054
1075
  const provider = this.frameProvider;
1055
1076
  if (!provider || width <= 0 || height <= 0)
1056
1077
  return;
1078
+ // A REAL width change rewraps everything; the purge below (CSI 3 J)
1079
+ // drops the old-width scrollback rows, so the provider must replay its
1080
+ // committed prefix at the new width first or that history is lost
1081
+ // forever (the provider's frontier still says "already delivered").
1082
+ if (this.previousWidth !== 0 && this.previousWidth !== width) {
1083
+ provider.beginHistoryReplay?.();
1084
+ }
1057
1085
  const plan = provider.renderFrame({ columns: width, rows: height });
1058
1086
  let viewport = [...plan.viewport];
1059
1087
  if (viewport.length > height)
@@ -1083,8 +1111,14 @@ export class TUI extends Container {
1083
1111
  : Math.min(this.providerViewportTop, Math.max(0, height - 1));
1084
1112
  const newTop = Math.max(0, Math.min(startTop + historyRows.length, height - rows));
1085
1113
  let buffer = "\x1b[?2026h";
1086
- if (destructiveReset)
1087
- buffer += "\x1b[H\x1b[3J\x1b[2J";
1114
+ if (destructiveReset) {
1115
+ buffer += "\x1b[H\x1b[2J";
1116
+ // Purge scrollback only when a rewrap actually misaligns the rows
1117
+ // above (width change). A plain repaint reset (didReset from the
1118
+ // reconciler) must keep the user's committed history.
1119
+ if (widthChanged)
1120
+ buffer += "\x1b[3J";
1121
+ }
1088
1122
  // Frame advance = history rows appended this frame. After those appends
1089
1123
  // the screen content that sits under new viewport slot i is the OLD
1090
1124
  // window's row (i + advance) — both when the appends scrolled the
@@ -1272,6 +1306,15 @@ export class TUI extends Container {
1272
1306
  const msg = `[${new Date().toISOString()}] fullRender: ${reason} (prev=${this.previousLines.length}, new=${newLines.length}, height=${height})\n`;
1273
1307
  fs.appendFileSync(logPath, msg);
1274
1308
  };
1309
+ // Forced repaint (requestRender(true)): full clear of the SCREEN, but
1310
+ // the user's native scrollback survives — only a real width change
1311
+ // takes the clearScrollback route below.
1312
+ if (this.providerForceRepaint) {
1313
+ logRedraw("forced repaint");
1314
+ fullRender(true);
1315
+ this.providerForceRepaint = false;
1316
+ return;
1317
+ }
1275
1318
  // First render - just output everything without clearing (assumes clean screen)
1276
1319
  if (this.previousLines.length === 0 && !widthChanged && !heightChanged) {
1277
1320
  logRedraw("first render");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chengchenccc/tui",
3
- "version": "0.2.0-rc.2",
3
+ "version": "0.2.0-rc.3",
4
4
  "description": "Terminal UI library with differential rendering (vendored from @earendil-works/pi-tui, MIT)",
5
5
  "type": "module",
6
6
  "exports": {
package/dist/mouse.d.ts DELETED
@@ -1,40 +0,0 @@
1
- /** SGR mouse report parsing (`\x1b[<button;col;rowM` / `…m`).
2
- *
3
- * Mouse tracking (modes 1000 + 1006) is enabled by the APPLICATION (oma
4
- * enables it for the whole session so the wheel can scroll the transcript —
5
- * our TUI keeps no native-scrollback copy of history). Consumers get 0-based
6
- * col/row for direct indexing into rendered lines.
7
- *
8
- * ponytail: wheel-only consumer for now; click/hover routing (pi's
9
- * SelectListMouseTarget helpers) can be ported when an overlay needs it.
10
- *
11
- * Ported from pi's packages/tui/src/mouse.ts. */
12
- /** A decoded SGR mouse report. */
13
- export interface SgrMouseEvent {
14
- /** Raw button code (bit 32 = motion, bit 64 = wheel, low bits = button). */
15
- button: number;
16
- /** 0-based column of the event. */
17
- col: number;
18
- /** 0-based row of the event. */
19
- row: number;
20
- /** True for a release report (`m` suffix). */
21
- release: boolean;
22
- /** Wheel direction: -1 up, 1 down, null when not a wheel event. */
23
- wheel: -1 | 1 | null;
24
- /** True when the pointer moved (hover or drag) rather than clicked. */
25
- motion: boolean;
26
- /** True for a left-button press (not motion, not release, not wheel). */
27
- leftClick: boolean;
28
- }
29
- /** Decode an SGR mouse report, or return null when `data` is not one.
30
- * Callers on hot keypress paths should pre-check `data.startsWith("\x1b[<")`
31
- * before paying for the regex. */
32
- export declare function parseSgrMouse(data: string): SgrMouseEvent | null;
33
- /** Handler invoked with a decoded SGR event; returning `false` reports unhandled. */
34
- export type SgrMouseHandler = (event: SgrMouseEvent) => boolean | undefined;
35
- /** Decode an SGR mouse report and forward it to `handler`. Returns `false`
36
- * when `data` is not an SGR mouse report (or fails to parse), so callers can
37
- * fall through to other input handling. Centralizes the repeated
38
- * `data.startsWith("\x1b[<")` + `parseSgrMouse()` pattern. */
39
- export declare function routeSgrMouseInput(data: string, handler: SgrMouseHandler): boolean;
40
- //# sourceMappingURL=mouse.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mouse.d.ts","sourceRoot":"","sources":["../src/mouse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;iDAUiD;AAEjD,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACrB,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;IAChB,yEAAyE;IACzE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;mCAEmC;AACnC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAWhE;AAED,qFAAqF;AACrF,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,GAAG,SAAS,CAAC;AAE5E;;;+DAG+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO,CAKlF"}
package/dist/mouse.js DELETED
@@ -1,39 +0,0 @@
1
- /** SGR mouse report parsing (`\x1b[<button;col;rowM` / `…m`).
2
- *
3
- * Mouse tracking (modes 1000 + 1006) is enabled by the APPLICATION (oma
4
- * enables it for the whole session so the wheel can scroll the transcript —
5
- * our TUI keeps no native-scrollback copy of history). Consumers get 0-based
6
- * col/row for direct indexing into rendered lines.
7
- *
8
- * ponytail: wheel-only consumer for now; click/hover routing (pi's
9
- * SelectListMouseTarget helpers) can be ported when an overlay needs it.
10
- *
11
- * Ported from pi's packages/tui/src/mouse.ts. */
12
- /** Decode an SGR mouse report, or return null when `data` is not one.
13
- * Callers on hot keypress paths should pre-check `data.startsWith("\x1b[<")`
14
- * before paying for the regex. */
15
- export function parseSgrMouse(data) {
16
- const match = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/.exec(data);
17
- if (!match)
18
- return null;
19
- const button = Number(match[1]);
20
- const col = Number(match[2]) - 1;
21
- const row = Number(match[3]) - 1;
22
- const release = match[4] === "m";
23
- const wheel = button & 64 ? (button & 1 ? 1 : -1) : null;
24
- const motion = (button & 32) !== 0 && wheel === null;
25
- const leftClick = !release && wheel === null && !motion && (button & 3) === 0;
26
- return { button, col, row, release, wheel, motion, leftClick };
27
- }
28
- /** Decode an SGR mouse report and forward it to `handler`. Returns `false`
29
- * when `data` is not an SGR mouse report (or fails to parse), so callers can
30
- * fall through to other input handling. Centralizes the repeated
31
- * `data.startsWith("\x1b[<")` + `parseSgrMouse()` pattern. */
32
- export function routeSgrMouseInput(data, handler) {
33
- if (!data.startsWith("\x1b[<"))
34
- return false;
35
- const event = parseSgrMouse(data);
36
- if (!event)
37
- return false;
38
- return handler(event) !== false;
39
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=mouse.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mouse.test.d.ts","sourceRoot":"","sources":["../src/mouse.test.ts"],"names":[],"mappings":""}
@@ -1,36 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { parseSgrMouse, routeSgrMouseInput } from "./mouse.js";
3
- describe("parseSgrMouse", () => {
4
- test("wheel up (button 64) and wheel down (button 65)", () => {
5
- const up = parseSgrMouse("\x1b[<64;10;5M");
6
- expect(up).toMatchObject({ button: 64, col: 9, row: 4, wheel: -1, release: false });
7
- const down = parseSgrMouse("\x1b[<65;1;1M");
8
- expect(down).toMatchObject({ button: 65, wheel: 1, col: 0, row: 0 });
9
- });
10
- test("left click press vs release vs motion", () => {
11
- expect(parseSgrMouse("\x1b[<0;3;3M")).toMatchObject({ leftClick: true, motion: false });
12
- expect(parseSgrMouse("\x1b[<0;3;3m")).toMatchObject({ release: true, leftClick: false });
13
- expect(parseSgrMouse("\x1b[<32;3;3M")).toMatchObject({ motion: true, leftClick: false });
14
- });
15
- test("non-mouse input returns null", () => {
16
- expect(parseSgrMouse("\x1b[A")).toBeNull();
17
- expect(parseSgrMouse("\x1b[5~")).toBeNull();
18
- expect(parseSgrMouse("x")).toBeNull();
19
- });
20
- });
21
- describe("routeSgrMouseInput", () => {
22
- test("routes wheel and reports consumed; falls through on other input", () => {
23
- const wheels = [];
24
- const consumed = routeSgrMouseInput("\x1b[<64;1;1M", (event) => {
25
- if (event.wheel)
26
- wheels.push(event.wheel);
27
- return true;
28
- });
29
- expect(consumed).toBe(true);
30
- // Not an SGR report: handler must not run.
31
- expect(routeSgrMouseInput("\x1b[5~", () => {
32
- wheels.push(99);
33
- })).toBe(false);
34
- expect(wheels).toEqual([-1]);
35
- });
36
- });