@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.
- package/dist/cli.js +7 -4
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/interactive-mode-patch.d.ts +24 -12
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +229 -146
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/interactive-reset.d.ts +49 -0
- package/dist/interactive-reset.d.ts.map +1 -0
- package/dist/interactive-reset.js +40 -0
- package/dist/interactive-reset.js.map +1 -0
- package/dist/pi-tui-editor-patch.d.ts +10 -0
- package/dist/pi-tui-editor-patch.d.ts.map +1 -0
- package/dist/pi-tui-editor-patch.js +159 -0
- package/dist/pi-tui-editor-patch.js.map +1 -0
- package/dist/pi-tui-patch.d.ts +2 -0
- package/dist/pi-tui-patch.d.ts.map +1 -0
- package/dist/pi-tui-patch.js +563 -0
- package/dist/pi-tui-patch.js.map +1 -0
- package/dist/pi-tui-settings-list-patch.d.ts +11 -0
- package/dist/pi-tui-settings-list-patch.d.ts.map +1 -0
- package/dist/pi-tui-settings-list-patch.js +38 -0
- package/dist/pi-tui-settings-list-patch.js.map +1 -0
- package/dist/reset-diagnostics.d.ts +69 -0
- package/dist/reset-diagnostics.d.ts.map +1 -0
- package/dist/reset-diagnostics.js +41 -0
- package/dist/reset-diagnostics.js.map +1 -0
- package/dist/sdk.d.ts +5 -21
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +180 -149
- package/dist/sdk.js.map +1 -1
- package/dist/workspace-transition-interactive.d.ts +1 -0
- package/dist/workspace-transition-interactive.d.ts.map +1 -1
- package/dist/workspace-transition-interactive.js +7 -17
- package/dist/workspace-transition-interactive.js.map +1 -1
- package/extensions/__integration__/audit-findings.test.ts +4 -5
- package/extensions/_icons/index.ts +2 -4
- package/extensions/_shared/__tests__/image-metadata.test.ts +33 -0
- package/extensions/_shared/__tests__/terminal-links.test.ts +18 -0
- package/extensions/_shared/image-metadata.ts +99 -0
- package/extensions/_shared/inline-preview.ts +1 -1
- package/extensions/_shared/terminal-links.ts +22 -0
- package/extensions/ask-user-question-tool/index.ts +0 -3
- package/extensions/clear/__tests__/clear.test.ts +269 -2
- package/extensions/command-expansion/index.ts +1 -1
- package/extensions/context-files/index.ts +5 -1
- package/extensions/context-fork/__tests__/context-fork.test.ts +94 -1
- package/extensions/context-fork/extension.json +1 -1
- package/extensions/context-fork/index.ts +32 -0
- package/extensions/edit-tool-enhanced/index.ts +2 -1
- package/extensions/hooks/index.ts +33 -11
- package/extensions/loop/index.ts +14 -1
- package/extensions/lsp/index.ts +64 -13
- package/extensions/lsp/package.json +2 -2
- package/extensions/random-spinner/index.ts +7 -642
- package/extensions/read-tool-enhanced/index.ts +6 -8
- package/extensions/render-stabilizer/__tests__/render-stabilizer.test.ts +2 -3
- package/extensions/render-stabilizer/index.ts +6 -6
- package/extensions/slash-command-bridge/__tests__/slash-command-bridge.test.ts +26 -0
- package/extensions/slash-command-bridge/index.ts +14 -2
- package/extensions/subagent-tool/model-resolver.ts +274 -7
- package/extensions/tasks/commands/register-tasks-extension.ts +9 -9
- package/extensions/teams-tool/tools/register-extension.ts +1 -3
- package/extensions/web-search-tool/index.ts +2 -1
- package/extensions/write-tool-enhanced/index.ts +2 -1
- package/node_modules/@mariozechner/pi-tui/README.md +56 -34
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts +18 -13
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js +182 -113
- package/node_modules/@mariozechner/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js +3 -3
- package/node_modules/@mariozechner/pi-tui/dist/components/cancellable-loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts +45 -36
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js +489 -325
- package/node_modules/@mariozechner/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts +1 -99
- package/node_modules/@mariozechner/pi-tui/dist/components/image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js +17 -192
- package/node_modules/@mariozechner/pi-tui/dist/components/image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js +57 -60
- package/node_modules/@mariozechner/pi-tui/dist/components/input.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts +2 -69
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js +5 -102
- package/node_modules/@mariozechner/pi-tui/dist/components/loader.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js +111 -53
- package/node_modules/@mariozechner/pi-tui/dist/components/markdown.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts +19 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js +78 -67
- package/node_modules/@mariozechner/pi-tui/dist/components/select-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts +1 -25
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js +13 -50
- package/node_modules/@mariozechner/pi-tui/dist/components/settings-list.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts +8 -10
- package/node_modules/@mariozechner/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/index.js +6 -9
- package/node_modules/@mariozechner/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts +108 -238
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js +108 -365
- package/node_modules/@mariozechner/pi-tui/dist/keybindings.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts +33 -48
- package/node_modules/@mariozechner/pi-tui/dist/keys.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/keys.js +239 -155
- package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts +14 -94
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js +44 -186
- package/node_modules/@mariozechner/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts +13 -58
- package/node_modules/@mariozechner/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js +78 -111
- package/node_modules/@mariozechner/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts +24 -110
- package/node_modules/@mariozechner/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/tui.js +188 -435
- package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts +0 -18
- package/node_modules/@mariozechner/pi-tui/dist/utils.d.ts.map +1 -1
- package/node_modules/@mariozechner/pi-tui/dist/utils.js +251 -119
- package/node_modules/@mariozechner/pi-tui/dist/utils.js.map +1 -1
- package/node_modules/@mariozechner/pi-tui/package.json +6 -6
- package/node_modules/@mariozechner/pi-tui/src/__tests__/__snapshots__/render.test.ts.snap +3 -40
- package/node_modules/@mariozechner/pi-tui/src/__tests__/image-component.test.ts +71 -81
- package/node_modules/@mariozechner/pi-tui/src/__tests__/render.test.ts +0 -33
- package/node_modules/@mariozechner/pi-tui/src/__tests__/terminal-image.test.ts +93 -334
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-render-scheduling.test.ts +1 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/utils.test.ts +11 -196
- package/node_modules/@mariozechner/pi-tui/src/autocomplete.ts +228 -142
- package/node_modules/@mariozechner/pi-tui/src/components/cancellable-loader.ts +3 -3
- package/node_modules/@mariozechner/pi-tui/src/components/editor.ts +624 -390
- package/node_modules/@mariozechner/pi-tui/src/components/image.ts +17 -227
- package/node_modules/@mariozechner/pi-tui/src/components/input.ts +71 -63
- package/node_modules/@mariozechner/pi-tui/src/components/loader.ts +5 -137
- package/node_modules/@mariozechner/pi-tui/src/components/markdown.ts +143 -52
- package/node_modules/@mariozechner/pi-tui/src/components/select-list.ts +136 -70
- package/node_modules/@mariozechner/pi-tui/src/components/settings-list.ts +12 -51
- package/node_modules/@mariozechner/pi-tui/src/index.ts +17 -36
- package/node_modules/@mariozechner/pi-tui/src/keybindings.ts +148 -421
- package/node_modules/@mariozechner/pi-tui/src/keys.ts +253 -181
- package/node_modules/@mariozechner/pi-tui/src/terminal-image.ts +51 -252
- package/node_modules/@mariozechner/pi-tui/src/terminal.ts +78 -133
- package/node_modules/@mariozechner/pi-tui/src/tui.ts +202 -478
- package/node_modules/@mariozechner/pi-tui/src/utils.ts +289 -125
- package/node_modules/@mariozechner/pi-tui/tsconfig.build.json +1 -0
- package/package.json +13 -13
- package/packages/tallow-tui/node_modules/@types/mime-types/README.md +8 -2
- package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +6 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/package.json +9 -3
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup-data.js +18 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +116 -384
- package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +5 -4
- package/packages/tallow-tui/node_modules/get-east-asian-width/utilities.js +24 -0
- package/packages/tallow-tui/node_modules/marked/README.md +5 -4
- package/packages/tallow-tui/node_modules/marked/bin/main.js +10 -8
- package/packages/tallow-tui/node_modules/marked/bin/marked.js +2 -1
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +156 -125
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +67 -2179
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +67 -2201
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +3 -3
- package/packages/tallow-tui/node_modules/marked/man/marked.1 +4 -2
- package/packages/tallow-tui/node_modules/marked/man/marked.1.md +2 -1
- package/packages/tallow-tui/node_modules/marked/package.json +26 -34
- package/skills/tallow-expert/SKILL.md +1 -3
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts +0 -32
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js +0 -46
- package/node_modules/@mariozechner/pi-tui/dist/border-styles.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts +0 -52
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js +0 -89
- package/node_modules/@mariozechner/pi-tui/dist/components/bordered-box.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts +0 -14
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.d.ts.map +0 -1
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js +0 -55
- package/node_modules/@mariozechner/pi-tui/dist/test-utils/capability-env.js.map +0 -1
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-change-listener.test.ts +0 -121
- package/node_modules/@mariozechner/pi-tui/src/__tests__/editor-ghost-text.test.ts +0 -112
- package/node_modules/@mariozechner/pi-tui/src/__tests__/mouse-events.test.ts +0 -134
- package/node_modules/@mariozechner/pi-tui/src/__tests__/settings-list.test.ts +0 -81
- package/node_modules/@mariozechner/pi-tui/src/__tests__/tui-diff-regression.test.ts +0 -555
- package/node_modules/@mariozechner/pi-tui/src/border-styles.ts +0 -60
- package/node_modules/@mariozechner/pi-tui/src/components/bordered-box.ts +0 -113
- package/node_modules/@mariozechner/pi-tui/src/test-utils/capability-env.ts +0 -56
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +0 -2211
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +0 -7
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +0 -728
- package/packages/tallow-tui/node_modules/marked/marked.min.js +0 -69
|
@@ -1,185 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for
|
|
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 {
|
|
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\
|
|
17
|
+
expect(visibleWidth("\x1b]8;;http://example.com\x1b\\link\x1b]8;;\x1b\\")).toBe(4);
|
|
29
18
|
});
|
|
30
19
|
|
|
31
|
-
it("counts CJK
|
|
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
|
|
31
|
+
it("returns text unchanged when it fits", () => {
|
|
129
32
|
expect(truncateToWidth("hello", 10, "…")).toBe("hello");
|
|
130
33
|
});
|
|
131
34
|
|
|
132
|
-
it("truncates
|
|
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("
|
|
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\
|
|
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
|
|
198
|
-
const
|
|
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
|
});
|