@dungle-scrubs/tallow 0.9.3 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/cli.js +7 -4
  2. package/dist/cli.js.map +1 -1
  3. package/dist/config.d.ts +1 -1
  4. package/dist/config.js +1 -1
  5. package/dist/interactive-mode-patch.d.ts +24 -10
  6. package/dist/interactive-mode-patch.d.ts.map +1 -1
  7. package/dist/interactive-mode-patch.js +285 -148
  8. package/dist/interactive-mode-patch.js.map +1 -1
  9. package/dist/interactive-reset.d.ts +49 -0
  10. package/dist/interactive-reset.d.ts.map +1 -0
  11. package/dist/interactive-reset.js +40 -0
  12. package/dist/interactive-reset.js.map +1 -0
  13. package/dist/pi-tui-editor-patch.d.ts +10 -0
  14. package/dist/pi-tui-editor-patch.d.ts.map +1 -0
  15. package/dist/pi-tui-editor-patch.js +159 -0
  16. package/dist/pi-tui-editor-patch.js.map +1 -0
  17. package/dist/pi-tui-patch.d.ts +2 -0
  18. package/dist/pi-tui-patch.d.ts.map +1 -0
  19. package/dist/pi-tui-patch.js +563 -0
  20. package/dist/pi-tui-patch.js.map +1 -0
  21. package/dist/pi-tui-settings-list-patch.d.ts +11 -0
  22. package/dist/pi-tui-settings-list-patch.d.ts.map +1 -0
  23. package/dist/pi-tui-settings-list-patch.js +38 -0
  24. package/dist/pi-tui-settings-list-patch.js.map +1 -0
  25. package/dist/reset-diagnostics.d.ts +69 -0
  26. package/dist/reset-diagnostics.d.ts.map +1 -0
  27. package/dist/reset-diagnostics.js +41 -0
  28. package/dist/reset-diagnostics.js.map +1 -0
  29. package/dist/sdk.d.ts +5 -21
  30. package/dist/sdk.d.ts.map +1 -1
  31. package/dist/sdk.js +180 -149
  32. package/dist/sdk.js.map +1 -1
  33. package/dist/workspace-transition-interactive.d.ts +1 -0
  34. package/dist/workspace-transition-interactive.d.ts.map +1 -1
  35. package/dist/workspace-transition-interactive.js +7 -17
  36. package/dist/workspace-transition-interactive.js.map +1 -1
  37. package/extensions/__integration__/audit-findings.test.ts +6 -16
  38. package/extensions/__integration__/teams-runtime.test.ts +4 -1
  39. package/extensions/_icons/index.ts +2 -4
  40. package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
  41. package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
  42. package/extensions/_shared/image-metadata.ts +99 -0
  43. package/extensions/_shared/inline-preview.ts +1 -1
  44. package/extensions/_shared/pid-registry.ts +0 -1
  45. package/extensions/_shared/terminal-links.ts +22 -0
  46. package/extensions/ask-user-question-tool/index.ts +0 -3
  47. package/extensions/clear/__tests__/clear.test.ts +270 -3
  48. package/extensions/command-expansion/index.ts +1 -1
  49. package/extensions/context-files/index.ts +5 -1
  50. package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
  51. package/extensions/context-fork/extension.json +1 -1
  52. package/extensions/context-fork/index.ts +32 -0
  53. package/extensions/edit-tool-enhanced/index.ts +2 -1
  54. package/extensions/hooks/index.ts +33 -11
  55. package/extensions/loop/index.ts +14 -1
  56. package/extensions/lsp/index.ts +64 -13
  57. package/extensions/lsp/package.json +2 -2
  58. package/extensions/permissions/__tests__/permissions.test.ts +4 -4
  59. package/extensions/random-spinner/index.ts +7 -642
  60. package/extensions/read-tool-enhanced/index.ts +6 -8
  61. package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +4 -5
  62. package/extensions/render-stabilizer/index.ts +6 -6
  63. package/extensions/show-system-prompt/__tests__/show-system-prompt.test.ts +1 -1
  64. package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
  65. package/extensions/slash-command-bridge/index.ts +14 -2
  66. package/extensions/subagent-tool/index.ts +1 -1
  67. package/extensions/subagent-tool/model-resolver.ts +274 -7
  68. package/extensions/tasks/__tests__/state-ui.test.ts +3 -3
  69. package/extensions/tasks/__tests__/widget-subagents.test.ts +2 -2
  70. package/extensions/tasks/commands/register-tasks-extension.ts +10 -10
  71. package/extensions/tasks/state/index.ts +1 -1
  72. package/extensions/tasks/ui/index.ts +2 -7
  73. package/extensions/teams-tool/tools/register-extension.ts +1 -3
  74. package/extensions/web-search-tool/index.ts +2 -1
  75. package/extensions/write-tool-enhanced/__tests__/write-tool-enhanced.test.ts +21 -6
  76. package/extensions/write-tool-enhanced/index.ts +2 -1
  77. package/node_modules/@mariozechner/pi-tui/README.md +56 -34
  78. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
  79. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
  80. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
  81. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
  82. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
  83. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
  84. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
  85. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
  86. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
  87. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
  88. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
  89. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
  90. package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
  91. package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
  92. package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
  93. package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
  94. package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
  95. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
  96. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
  97. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
  98. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
  99. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
  100. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
  101. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
  102. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
  103. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
  104. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
  105. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
  106. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +0 -2
  107. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
  108. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +12 -23
  109. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
  110. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
  111. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
  112. package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
  113. package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
  114. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
  115. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
  116. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
  117. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
  118. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
  119. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
  120. package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
  121. package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
  122. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
  123. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
  124. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
  125. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
  126. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
  127. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
  128. package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
  129. package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
  130. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
  131. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
  132. package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
  133. package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
  134. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
  135. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
  136. package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
  137. package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
  138. package/node_modules/@mariozechner/pi-tui/package.json +6 -6
  139. package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
  140. package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
  141. package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
  142. package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
  143. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
  144. package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
  145. package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
  146. package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
  147. package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
  148. package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
  149. package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
  150. package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
  151. package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
  152. package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
  153. package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +11 -23
  154. package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
  155. package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
  156. package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
  157. package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
  158. package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
  159. package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
  160. package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
  161. package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
  162. package/package.json +13 -13
  163. package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
  164. package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
  165. package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
  166. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
  167. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
  168. package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
  169. package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
  170. package/packages/tallow-tui/node_modules/marked/README.md +5 -4
  171. package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
  172. package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
  173. package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
  174. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
  175. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
  176. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
  177. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
  178. package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
  179. package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
  180. package/packages/tallow-tui/node_modules/marked/package.json +26 -34
  181. package/runtime/model-metadata-overrides.ts +10 -1
  182. package/runtime/pid-schema.ts +26 -6
  183. package/skills/tallow-expert/SKILL.md +1 -3
  184. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
  185. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
  186. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
  187. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
  188. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
  189. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
  190. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
  191. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
  192. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
  193. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
  194. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
  195. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
  196. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
  197. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
  198. package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
  199. package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -49
  200. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
  201. package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
  202. package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
  203. package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
  204. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
  205. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
  206. package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
  207. package/packages/tallow-tui/node_modules/marked/marked.min.js +0 -69
@@ -1,185 +1,56 @@
1
1
  /**
2
- * Tests for tallow-tui core utility functions: visibleWidth, truncateToWidth,
3
- * wrapTextWithAnsi, hyperlink, and fileLink.
2
+ * Tests for core utility functions kept in the fork.
4
3
  */
5
4
  import { describe, expect, it } from "bun:test";
6
- import { fileLink, hyperlink, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils.js";
7
-
8
- // ── visibleWidth ─────────────────────────────────────────────────────────────
5
+ import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils.js";
9
6
 
10
7
  describe("visibleWidth", () => {
11
8
  it("counts ASCII characters", () => {
12
9
  expect(visibleWidth("hello")).toBe(5);
13
10
  });
14
11
 
15
- it("returns 0 for empty string", () => {
16
- expect(visibleWidth("")).toBe(0);
17
- });
18
-
19
12
  it("ignores SGR color codes", () => {
20
13
  expect(visibleWidth("\x1b[31mred\x1b[0m")).toBe(3);
21
14
  });
22
15
 
23
- it("ignores nested ANSI codes", () => {
24
- expect(visibleWidth("\x1b[1m\x1b[31mbold red\x1b[0m")).toBe(8);
25
- });
26
-
27
16
  it("ignores OSC 8 hyperlink sequences", () => {
28
- expect(visibleWidth("\x1b]8;;http://example.com\x07link\x1b]8;;\x07")).toBe(4);
17
+ expect(visibleWidth("\x1b]8;;http://example.com\x1b\\link\x1b]8;;\x1b\\")).toBe(4);
29
18
  });
30
19
 
31
- it("counts CJK characters as double-width", () => {
20
+ it("counts CJK and emoji as wide glyphs", () => {
32
21
  expect(visibleWidth("你好")).toBe(4);
33
- });
34
-
35
- it("handles mixed ASCII and CJK", () => {
36
- expect(visibleWidth("hi你好")).toBe(6);
37
- });
38
-
39
- it("counts emoji as double-width", () => {
40
22
  expect(visibleWidth("👋")).toBe(2);
41
23
  });
42
24
 
43
25
  it("counts tabs as 3 spaces", () => {
44
26
  expect(visibleWidth("\t")).toBe(3);
45
27
  });
46
-
47
- it("counts skin tone modifiers as single glyph", () => {
48
- expect(visibleWidth("👋🏽")).toBe(2);
49
- });
50
-
51
- it("handles string with only ANSI codes", () => {
52
- expect(visibleWidth("\x1b[31m\x1b[0m")).toBe(0);
53
- });
54
-
55
- it("handles multiple emoji in a row", () => {
56
- const w = visibleWidth("🔥🎉✅");
57
- expect(w).toBeGreaterThanOrEqual(4); // at least 2 wide emoji
58
- });
59
-
60
- // ── OSC sequence handling ────────────────────────────────────────────
61
-
62
- it("strips terminated OSC 1337 SetUserVar (BEL)", () => {
63
- expect(visibleWidth("\x1b]1337;SetUserVar=pi_status=ZG9uZQ==\x07(pass)")).toBe(6);
64
- });
65
-
66
- it("strips terminated OSC 1337 SetUserVar (ST)", () => {
67
- expect(visibleWidth("\x1b]1337;SetUserVar=pi_status=ZG9uZQ==\x1b\\(pass)")).toBe(6);
68
- });
69
-
70
- it("strips unterminated OSC without crashing (may over-strip)", () => {
71
- // Without a terminator, visible text after the OSC body is ambiguous.
72
- // The regex consumes everything until the next ESC or end-of-string.
73
- // Over-stripping is acceptable — crashing is not.
74
- const width = visibleWidth("\x1b]1337;SetUserVar=pi_status=ZG9uZQ==(pass)");
75
- expect(width).toBeLessThanOrEqual(6);
76
- });
77
-
78
- it("strips chained unterminated OSC sequences without crashing", () => {
79
- // Each unterminated OSC consumes up to the next ESC. The last one
80
- // consumes to end-of-string, eating any trailing visible text.
81
- const line =
82
- "\x1b]1337;SetUserVar=pi_status=" +
83
- "\x1b]1337;SetUserVar=pi_status=d29ya2luZw==" +
84
- "\x1b]1337;SetUserVar=pi_status=ZG9uZQ==" +
85
- "(pass)";
86
- expect(visibleWidth(line)).toBeLessThanOrEqual(6);
87
- });
88
-
89
- it("unterminated OSC stops at next ESC (preserves text after next escape)", () => {
90
- // Unterminated OSC consumes to next ESC, but text AFTER the next CSI is preserved.
91
- const line =
92
- "\x1b]1337;SetUserVar=pi_status=ZG9uZQ==" +
93
- "\x1b[39m" + // CSI resets color — next ESC stops the unterminated OSC
94
- "visible";
95
- expect(visibleWidth(line)).toBe(7);
96
- });
97
-
98
- it("handles real crash pattern: chained unterminated OSC between CSI sequences", () => {
99
- // Reproduces the actual crash: CSI color + unterminated OSC chain + CSI reset
100
- const line =
101
- "\x1b[48;2;10;10;20m" +
102
- "\x1b[38;2;152;152;152m" +
103
- "\x1b]1337;SetUserVar=pi_status=" +
104
- "\x1b]1337;SetUserVar=pi_status=d29ya2luZw==" +
105
- "\x1b]1337;SetUserVar=pi_status=ZG9uZQ==" +
106
- "\x1b]1337;SetUserVar=pi_status=d29ya2luZw==" +
107
- "\x1b]1337;SetUserVar=pi_status=ZG9uZQ==" +
108
- "(pass)" +
109
- "\x1b[49m\x1b[0m";
110
- // Must not exceed any reasonable terminal width — the original crash was 182
111
- expect(visibleWidth(line)).toBeLessThan(20);
112
- });
113
-
114
- it("strips unterminated APC sequences without crashing", () => {
115
- const width = visibleWidth("\x1b_some-apc-data(pass)");
116
- expect(width).toBeLessThanOrEqual(6);
117
- });
118
-
119
- it("strips arbitrary unterminated OSC numbers", () => {
120
- const width = visibleWidth("\x1b]999;custom=datavisible");
121
- expect(width).toBeLessThanOrEqual(7);
122
- });
123
28
  });
124
29
 
125
- // ── truncateToWidth ──────────────────────────────────────────────────────────
126
-
127
30
  describe("truncateToWidth", () => {
128
- it("returns string unchanged when within width", () => {
31
+ it("returns text unchanged when it fits", () => {
129
32
  expect(truncateToWidth("hello", 10, "…")).toBe("hello");
130
33
  });
131
34
 
132
- it("truncates and appends ellipsis", () => {
35
+ it("truncates long text to the target width", () => {
133
36
  const result = truncateToWidth("hello world", 8, "…");
134
37
  expect(visibleWidth(result)).toBeLessThanOrEqual(8);
135
38
  expect(result).toContain("…");
136
39
  });
137
40
 
138
- it("handles CJK truncation at character boundary", () => {
139
- const result = truncateToWidth("你好世界", 5, "…");
140
- expect(visibleWidth(result)).toBeLessThanOrEqual(5);
141
- });
142
-
143
- it("preserves ANSI codes in truncated output", () => {
41
+ it("preserves ANSI sequences in truncated output", () => {
144
42
  const result = truncateToWidth("\x1b[31mhello world\x1b[0m", 8, "…");
145
43
  expect(result).toContain("\x1b[31m");
146
- });
147
-
148
- it("handles exact width match", () => {
149
- const result = truncateToWidth("hello", 5, "…");
150
- expect(result).toBe("hello");
151
- });
152
-
153
- it("handles width of 1 with ellipsis", () => {
154
- const result = truncateToWidth("hello", 1, "…");
155
- expect(visibleWidth(result)).toBeLessThanOrEqual(1);
44
+ expect(visibleWidth(result)).toBeLessThanOrEqual(8);
156
45
  });
157
46
 
158
47
  it("handles OSC 8 hyperlinks", () => {
159
- const linked = "\x1b]8;;file:///test\x07long-filename.ts\x1b]8;;\x07";
48
+ const linked = "\x1b]8;;file:///test\x1blong-filename.ts\x1b]8;;\x1b\\";
160
49
  const result = truncateToWidth(linked, 10, "…");
161
50
  expect(visibleWidth(result)).toBeLessThanOrEqual(10);
162
51
  });
163
-
164
- it("preserves ANSI styling through ellipsis", () => {
165
- const red = "\x1b[31m";
166
- const reset = "\x1b[0m";
167
- const styled = `${red}${"a".repeat(20)}${reset}`;
168
- const result = truncateToWidth(styled, 10, "…");
169
- // Ellipsis should appear BEFORE reset so it inherits the line's styling
170
- expect(result).toContain("…\x1b[0m");
171
- expect(result).not.toContain("\x1b[0m…");
172
- });
173
-
174
- it("places reset after ellipsis for unstyled text too", () => {
175
- const result = truncateToWidth("a".repeat(20), 10, "…");
176
- // Even for unstyled text, the reset-after-ellipsis order is correct
177
- expect(result).toContain("…\x1b[0m");
178
- });
179
52
  });
180
53
 
181
- // ── wrapTextWithAnsi ─────────────────────────────────────────────────────────
182
-
183
54
  describe("wrapTextWithAnsi", () => {
184
55
  it("wraps plain text at word boundaries", () => {
185
56
  const lines = wrapTextWithAnsi("hello world foo", 10);
@@ -194,66 +65,10 @@ describe("wrapTextWithAnsi", () => {
194
65
  expect(lines.length).toBeGreaterThan(1);
195
66
  });
196
67
 
197
- it("handles long words that exceed width", () => {
198
- const lines = wrapTextWithAnsi("superlongword", 5);
199
- for (const line of lines) {
68
+ it("handles long words and empty strings", () => {
69
+ for (const line of wrapTextWithAnsi("superlongword", 5)) {
200
70
  expect(visibleWidth(line)).toBeLessThanOrEqual(5);
201
71
  }
202
- });
203
-
204
- it("handles empty string", () => {
205
72
  expect(wrapTextWithAnsi("", 80)).toEqual([""]);
206
73
  });
207
-
208
- it("handles CJK wrapping", () => {
209
- const lines = wrapTextWithAnsi("你好世界测试", 5);
210
- for (const line of lines) {
211
- expect(visibleWidth(line)).toBeLessThanOrEqual(5);
212
- }
213
- });
214
-
215
- it("does not wrap when text fits", () => {
216
- const lines = wrapTextWithAnsi("short", 80);
217
- expect(lines).toHaveLength(1);
218
- expect(lines[0]).toBe("short");
219
- });
220
- });
221
-
222
- // ── hyperlink ────────────────────────────────────────────────────────────────
223
-
224
- describe("hyperlink", () => {
225
- it("wraps text in OSC 8 sequences", () => {
226
- const result = hyperlink("https://example.com", "click");
227
- expect(result).toBe("\x1b]8;;https://example.com\x07click\x1b]8;;\x07");
228
- });
229
-
230
- it("has zero visible width overhead", () => {
231
- expect(visibleWidth(hyperlink("https://x.com", "text"))).toBe(4);
232
- });
233
-
234
- it("handles empty text", () => {
235
- const result = hyperlink("https://x.com", "");
236
- expect(visibleWidth(result)).toBe(0);
237
- });
238
- });
239
-
240
- // ── fileLink ─────────────────────────────────────────────────────────────────
241
-
242
- describe("fileLink", () => {
243
- it("creates file:// URL from path", () => {
244
- const result = fileLink("/path/to/file.ts");
245
- expect(result).toContain("file:///path/to/file.ts");
246
- expect(visibleWidth(result)).toBe("/path/to/file.ts".length);
247
- });
248
-
249
- it("percent-encodes spaces in path", () => {
250
- const result = fileLink("/path/with spaces/file.ts");
251
- expect(result).toContain("file:///path/with%20spaces/file.ts");
252
- expect(visibleWidth(result)).toBe("/path/with spaces/file.ts".length);
253
- });
254
-
255
- it("uses custom display text", () => {
256
- const result = fileLink("/long/path/file.ts", "file.ts");
257
- expect(visibleWidth(result)).toBe(7);
258
- });
259
74
  });