@dungle-scrubs/tallow 0.9.4 → 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 (195) 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 -12
  6. package/dist/interactive-mode-patch.d.ts.map +1 -1
  7. package/dist/interactive-mode-patch.js +229 -146
  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 +4 -5
  38. package/extensions/_icons/index.ts +2 -4
  39. package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
  40. package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
  41. package/extensions/_shared/image-metadata.ts +99 -0
  42. package/extensions/_shared/inline-preview.ts +1 -1
  43. package/extensions/_shared/terminal-links.ts +22 -0
  44. package/extensions/ask-user-question-tool/index.ts +0 -3
  45. package/extensions/clear/__tests__/clear.test.ts +269 -2
  46. package/extensions/command-expansion/index.ts +1 -1
  47. package/extensions/context-files/index.ts +5 -1
  48. package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
  49. package/extensions/context-fork/extension.json +1 -1
  50. package/extensions/context-fork/index.ts +32 -0
  51. package/extensions/edit-tool-enhanced/index.ts +2 -1
  52. package/extensions/hooks/index.ts +33 -11
  53. package/extensions/loop/index.ts +14 -1
  54. package/extensions/lsp/index.ts +64 -13
  55. package/extensions/lsp/package.json +2 -2
  56. package/extensions/random-spinner/index.ts +7 -642
  57. package/extensions/read-tool-enhanced/index.ts +6 -8
  58. package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +2 -3
  59. package/extensions/render-stabilizer/index.ts +6 -6
  60. package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
  61. package/extensions/slash-command-bridge/index.ts +14 -2
  62. package/extensions/subagent-tool/model-resolver.ts +274 -7
  63. package/extensions/tasks/commands/register-tasks-extension.ts +9 -9
  64. package/extensions/teams-tool/tools/register-extension.ts +1 -3
  65. package/extensions/web-search-tool/index.ts +2 -1
  66. package/extensions/write-tool-enhanced/index.ts +2 -1
  67. package/node_modules/@mariozechner/pi-tui/README.md +56 -34
  68. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
  69. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
  70. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
  71. package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
  72. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
  73. package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
  74. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
  75. package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
  76. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
  77. package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
  78. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
  79. package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
  80. package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
  81. package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
  82. package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
  83. package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
  84. package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
  85. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
  86. package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
  87. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
  88. package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
  89. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
  90. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
  91. package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
  92. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
  93. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
  94. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
  95. package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
  96. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +1 -25
  97. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
  98. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +13 -50
  99. package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
  100. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
  101. package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
  102. package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
  103. package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
  104. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
  105. package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
  106. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
  107. package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
  108. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
  109. package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
  110. package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
  111. package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
  112. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
  113. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
  114. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
  115. package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
  116. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
  117. package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
  118. package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
  119. package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
  120. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
  121. package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
  122. package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
  123. package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
  124. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
  125. package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
  126. package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
  127. package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
  128. package/node_modules/@mariozechner/pi-tui/package.json +6 -6
  129. package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
  130. package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
  131. package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
  132. package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
  133. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
  134. package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
  135. package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
  136. package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
  137. package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
  138. package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
  139. package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
  140. package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
  141. package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
  142. package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
  143. package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +12 -51
  144. package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
  145. package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
  146. package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
  147. package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
  148. package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
  149. package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
  150. package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
  151. package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
  152. package/package.json +13 -13
  153. package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
  154. package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
  155. package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
  156. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
  157. package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
  158. package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
  159. package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
  160. package/packages/tallow-tui/node_modules/marked/README.md +5 -4
  161. package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
  162. package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
  163. package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
  164. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
  165. package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
  166. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
  167. package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
  168. package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
  169. package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
  170. package/packages/tallow-tui/node_modules/marked/package.json +26 -34
  171. package/skills/tallow-expert/SKILL.md +1 -3
  172. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
  173. package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
  174. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
  175. package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
  176. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
  177. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
  178. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
  179. package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
  180. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
  181. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
  182. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
  183. package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
  184. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
  185. package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
  186. package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
  187. package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -81
  188. package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
  189. package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
  190. package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
  191. package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
  192. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
  193. package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
  194. package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
  195. 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
  });