@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
@@ -23,10 +23,7 @@ Line two
23
23
  Line three "
24
24
  `;
25
25
 
26
- exports[`Markdown renders heading 1`] = `
27
- "Hello World
28
- "
29
- `;
26
+ exports[`Markdown renders heading 1`] = `"Hello World "`;
30
27
 
31
28
  exports[`Markdown renders paragraph text 1`] = `
32
29
  "Some paragraph text that should be
@@ -37,8 +34,7 @@ exports[`Markdown renders code block 1`] = `
37
34
  "\`\`\`ts
38
35
  const x = 1;
39
36
  const y = 2;
40
- \`\`\`
41
- "
37
+ \`\`\` "
42
38
  `;
43
39
 
44
40
  exports[`Markdown renders unordered list 1`] = `
@@ -53,10 +49,7 @@ exports[`Markdown renders ordered list 1`] = `
53
49
  3. Third "
54
50
  `;
55
51
 
56
- exports[`Markdown renders blockquote 1`] = `
57
- "│ This is a quote
58
- "
59
- `;
52
+ exports[`Markdown renders blockquote 1`] = `"│ This is a quote "`;
60
53
 
61
54
  exports[`Markdown renders at narrow width (20) 1`] = `
62
55
  "Hello World
@@ -78,36 +71,6 @@ Below "
78
71
 
79
72
  exports[`Markdown handles empty content 1`] = `""`;
80
73
 
81
- exports[`BorderedBox renders content with default border 1`] = `
82
- "┌────────────────────────────┐
83
- │ Hello │
84
- │ World │
85
- └────────────────────────────┘"
86
- `;
87
-
88
- exports[`BorderedBox renders with title 1`] = `
89
- "┌─ My Box ───────────────────┐
90
- │ Content line │
91
- └────────────────────────────┘"
92
- `;
93
-
94
- exports[`BorderedBox renders with custom padding 1`] = `
95
- "┌────────────────────────────┐
96
- │ Padded │
97
- └────────────────────────────┘"
98
- `;
99
-
100
- exports[`BorderedBox handles empty content 1`] = `
101
- "┌────────────────────────────┐
102
- └────────────────────────────┘"
103
- `;
104
-
105
- exports[`BorderedBox handles very narrow width 1`] = `
106
- "┌───┐
107
- │ . │
108
- └───┘"
109
- `;
110
-
111
74
  exports[`TruncatedText renders short text without truncation 1`] = `"Short "`;
112
75
 
113
76
  exports[`TruncatedText truncates long text 1`] = `"This text is much..."`;
@@ -1,113 +1,103 @@
1
1
  import { describe, expect, it } from "bun:test";
2
2
  import { Image, type ImageTheme } from "../components/image.js";
3
- import { withCapabilityEnv } from "../test-utils/capability-env.js";
3
+ import { resetCapabilitiesCache } from "../terminal-image.js";
4
4
 
5
5
  const IDENTITY_THEME: ImageTheme = {
6
6
  fallbackColor: (text) => text,
7
7
  };
8
8
 
9
- /**
10
- * Extract Kitty `c=<columns>` param from rendered image lines.
11
- *
12
- * @param lines - Rendered TUI lines from Image.render
13
- * @returns Parsed Kitty column count, or null if sequence not present
14
- */
15
- function getKittyColumns(lines: readonly string[]): number | null {
16
- for (const line of lines) {
17
- const start = line.indexOf("\x1b_G");
18
- if (start < 0) {
19
- continue;
9
+ type CapabilityEnvOverrides = Readonly<Record<string, string | undefined>>;
10
+
11
+ function withCapabilityEnv<T>(overrides: CapabilityEnvOverrides, run: () => T): T {
12
+ const keys = [
13
+ "COLORTERM",
14
+ "GHOSTTY_RESOURCES_DIR",
15
+ "ITERM_SESSION_ID",
16
+ "KITTY_WINDOW_ID",
17
+ "TERM",
18
+ "TERM_PROGRAM",
19
+ "TMUX",
20
+ "WEZTERM_PANE",
21
+ ] as const;
22
+ const previous: Partial<Record<(typeof keys)[number], string | undefined>> = {};
23
+ for (const key of keys) {
24
+ previous[key] = process.env[key];
25
+ if (Object.hasOwn(overrides, key)) {
26
+ const value = overrides[key];
27
+ if (value === undefined) {
28
+ delete process.env[key];
29
+ } else {
30
+ process.env[key] = value;
31
+ }
32
+ } else {
33
+ delete process.env[key];
20
34
  }
21
- const match = line.slice(start).match(/c=(\d+)/);
22
- if (match) {
23
- return Number(match[1]);
35
+ }
36
+ resetCapabilitiesCache();
37
+ try {
38
+ return run();
39
+ } finally {
40
+ for (const key of keys) {
41
+ const value = previous[key];
42
+ if (value === undefined) {
43
+ delete process.env[key];
44
+ } else {
45
+ process.env[key] = value;
46
+ }
24
47
  }
48
+ resetCapabilitiesCache();
25
49
  }
26
- return null;
27
50
  }
28
51
 
29
- describe("Image component narrow-pane rendering", () => {
30
- it("applies one safe width contract for border and no-border rendering", () => {
31
- withCapabilityEnv({ TERM_PROGRAM: "kitty" }, () => {
32
- const dimensions = { widthPx: 4000, heightPx: 2500 };
33
- const cases = [
34
- { border: false, expectedColumns: 1, width: 1 },
35
- { border: false, expectedColumns: 6, width: 8 },
36
- { border: false, expectedColumns: 18, width: 20 },
37
- { border: true, expectedColumns: 1, width: 1 },
38
- { border: true, expectedColumns: 2, width: 8 },
39
- { border: true, expectedColumns: 14, width: 20 },
40
- ] as const;
41
-
42
- for (const testCase of cases) {
43
- const image = new Image(
44
- "AA==",
45
- "image/png",
46
- IDENTITY_THEME,
47
- { border: testCase.border, maxHeightCells: 200 },
48
- dimensions
49
- );
50
- const lines = image.render(testCase.width);
51
- expect(lines.length).toBeGreaterThan(0);
52
- expect(getKittyColumns(lines)).toBe(testCase.expectedColumns);
53
- }
54
- });
55
- });
52
+ function containsKittySequence(lines: readonly string[]): boolean {
53
+ return lines.some((line) => line.includes("\x1b_G"));
54
+ }
56
55
 
57
- it("renders portrait images at narrow widths with valid positive geometry", () => {
58
- withCapabilityEnv({ TERM_PROGRAM: "kitty" }, () => {
56
+ describe("Image component", () => {
57
+ it("renders kitty sequences when image support is available", () => {
58
+ withCapabilityEnv({ TERM_PROGRAM: "kitty", TMUX: undefined }, () => {
59
59
  const image = new Image(
60
60
  "AA==",
61
61
  "image/png",
62
62
  IDENTITY_THEME,
63
- { border: false, maxHeightCells: 200 },
63
+ { maxHeightCells: 200 },
64
64
  { widthPx: 1179, heightPx: 2556 }
65
65
  );
66
66
 
67
67
  for (const width of [8, 12, 20, 30]) {
68
68
  const lines = image.render(width);
69
- expect(lines.some((line) => line.includes("\x1b_G"))).toBe(true);
70
- const columns = getKittyColumns(lines);
71
- expect(columns).not.toBeNull();
72
- expect(columns).toBeGreaterThanOrEqual(1);
69
+ expect(lines.length).toBeGreaterThan(0);
70
+ expect(containsKittySequence(lines)).toBe(true);
73
71
  }
74
72
  });
75
73
  });
76
74
 
77
- it("renders landscape images in narrow panes for both border modes", () => {
78
- withCapabilityEnv({ TERM_PROGRAM: "kitty" }, () => {
79
- const dimensions = { widthPx: 2400, heightPx: 1200 };
80
- for (const border of [false, true]) {
81
- const image = new Image(
82
- "AA==",
83
- "image/png",
84
- IDENTITY_THEME,
85
- { border, maxHeightCells: 200 },
86
- dimensions
87
- );
88
- const lines = image.render(12);
89
- expect(lines.length).toBeGreaterThan(0);
90
- expect(getKittyColumns(lines)).toBeGreaterThanOrEqual(1);
91
- }
75
+ it("returns stable output for repeated renders at the same width", () => {
76
+ withCapabilityEnv({ TERM_PROGRAM: "kitty", TMUX: undefined }, () => {
77
+ const image = new Image(
78
+ "AA==",
79
+ "image/png",
80
+ IDENTITY_THEME,
81
+ { maxHeightCells: 200 },
82
+ { widthPx: 2400, heightPx: 1200 }
83
+ );
84
+ const first = image.render(12);
85
+ const second = image.render(12);
86
+ expect(second).toEqual(first);
92
87
  });
93
88
  });
94
89
 
95
- it("degrades safely for ultra-narrow pane widths", () => {
96
- withCapabilityEnv({ TERM_PROGRAM: "kitty" }, () => {
97
- for (const border of [false, true]) {
98
- const image = new Image(
99
- "AA==",
100
- "image/png",
101
- IDENTITY_THEME,
102
- { border, maxHeightCells: 200 },
103
- { widthPx: 1200, heightPx: 1800 }
104
- );
105
-
106
- expect(() => image.render(0)).not.toThrow();
107
- expect(() => image.render(1)).not.toThrow();
108
- expect(getKittyColumns(image.render(0))).toBe(1);
109
- expect(getKittyColumns(image.render(1))).toBe(1);
110
- }
90
+ it("falls back to text when image protocols are unavailable", () => {
91
+ withCapabilityEnv({ TERM: "tmux-256color", TERM_PROGRAM: "unknown", TMUX: "1" }, () => {
92
+ const image = new Image(
93
+ "AA==",
94
+ "image/png",
95
+ IDENTITY_THEME,
96
+ { filename: "example.png" },
97
+ { widthPx: 1200, heightPx: 1800 }
98
+ );
99
+ const lines = image.render(12);
100
+ expect(lines).toEqual(["[Image: example.png [image/png] 1200x1800]"]);
111
101
  });
112
102
  });
113
103
  });
@@ -6,7 +6,6 @@
6
6
  */
7
7
  import { describe, expect, it } from "bun:test";
8
8
  import { renderSnapshot } from "../../../../test-utils/virtual-terminal.js";
9
- import { BorderedBox } from "../components/bordered-box.js";
10
9
  import { Markdown, type MarkdownTheme } from "../components/markdown.js";
11
10
  import { Text } from "../components/text.js";
12
11
  import { TruncatedText } from "../components/truncated-text.js";
@@ -120,38 +119,6 @@ describe("Markdown", () => {
120
119
  });
121
120
  });
122
121
 
123
- // ════════════════════════════════════════════════════════════════
124
- // BorderedBox
125
- // ════════════════════════════════════════════════════════════════
126
-
127
- describe("BorderedBox", () => {
128
- it("renders content with default border", () => {
129
- const box = new BorderedBox(["Hello", "World"]);
130
- expect(renderSnapshot(box, 30)).toMatchSnapshot();
131
- });
132
-
133
- it("renders with title", () => {
134
- const box = new BorderedBox(["Content line"], { title: "My Box" });
135
- expect(renderSnapshot(box, 30)).toMatchSnapshot();
136
- });
137
-
138
- it("renders with custom padding", () => {
139
- const box = new BorderedBox(["Padded"], { paddingX: 3 });
140
- expect(renderSnapshot(box, 30)).toMatchSnapshot();
141
- });
142
-
143
- it("handles empty content", () => {
144
- const box = new BorderedBox([]);
145
- expect(renderSnapshot(box, 30)).toMatchSnapshot();
146
- });
147
-
148
- it("handles very narrow width", () => {
149
- const box = new BorderedBox(["Some content"]);
150
- // At width 5, inner width = 5 - 2 - 2 = 1
151
- expect(renderSnapshot(box, 5)).toMatchSnapshot();
152
- });
153
- });
154
-
155
122
  // ════════════════════════════════════════════════════════════════
156
123
  // TruncatedText
157
124
  // ════════════════════════════════════════════════════════════════