@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,121 +0,0 @@
1
- /**
2
- * Tests for Editor.addChangeListener — ensures change listeners fire
3
- * alongside onChange without interfering with each other.
4
- */
5
- import { describe, expect, test } from "bun:test";
6
- import { Editor, type EditorTheme } from "../components/editor.js";
7
- import type { TUI } from "../tui.js";
8
-
9
- /** Minimal TUI mock providing only what Editor needs. */
10
- function createMockTUI(): TUI {
11
- return {
12
- requestRender: () => {},
13
- terminal: { rows: 40, cols: 80 },
14
- } as unknown as TUI;
15
- }
16
-
17
- const theme: EditorTheme = {
18
- borderColor: (s: string) => s,
19
- selectList: {
20
- selectedBg: (s: string) => s,
21
- selectedFg: (s: string) => s,
22
- normalBg: (s: string) => s,
23
- normalFg: (s: string) => s,
24
- matchHighlight: (s: string) => s,
25
- descriptionFg: (s: string) => s,
26
- },
27
- };
28
-
29
- /** Simulate typing a string character by character. */
30
- function typeText(editor: Editor, text: string): void {
31
- for (const char of text) {
32
- editor.handleInput(char);
33
- }
34
- }
35
-
36
- describe("Editor.addChangeListener", () => {
37
- test("listener fires on character input", () => {
38
- const editor = new Editor(createMockTUI(), theme);
39
- const received: string[] = [];
40
- editor.addChangeListener((text) => received.push(text));
41
-
42
- typeText(editor, "hi");
43
- expect(received).toEqual(["h", "hi"]);
44
- });
45
-
46
- test("listener fires alongside onChange", () => {
47
- const editor = new Editor(createMockTUI(), theme);
48
- const onChangeCalls: string[] = [];
49
- const listenerCalls: string[] = [];
50
-
51
- editor.onChange = (text) => onChangeCalls.push(text);
52
- editor.addChangeListener((text) => listenerCalls.push(text));
53
-
54
- typeText(editor, "ab");
55
- expect(onChangeCalls).toEqual(["a", "ab"]);
56
- expect(listenerCalls).toEqual(["a", "ab"]);
57
- });
58
-
59
- test("overwriting onChange does not affect listener", () => {
60
- const editor = new Editor(createMockTUI(), theme);
61
- const listenerCalls: string[] = [];
62
-
63
- editor.addChangeListener((text) => listenerCalls.push(text));
64
-
65
- // Simulate framework overwriting onChange (second write overwrites first)
66
- editor.onChange = () => {};
67
- editor.onChange = () => {}; // intentional: tests that listener survives overwrites
68
-
69
- typeText(editor, "x");
70
- expect(listenerCalls).toEqual(["x"]);
71
- });
72
-
73
- test("multiple listeners all fire", () => {
74
- const editor = new Editor(createMockTUI(), theme);
75
- const calls1: string[] = [];
76
- const calls2: string[] = [];
77
- const calls3: string[] = [];
78
-
79
- editor.addChangeListener((text) => calls1.push(text));
80
- editor.addChangeListener((text) => calls2.push(text));
81
- editor.addChangeListener((text) => calls3.push(text));
82
-
83
- typeText(editor, "z");
84
- expect(calls1).toEqual(["z"]);
85
- expect(calls2).toEqual(["z"]);
86
- expect(calls3).toEqual(["z"]);
87
- });
88
-
89
- test("listener fires on backspace", () => {
90
- const editor = new Editor(createMockTUI(), theme);
91
- const received: string[] = [];
92
- editor.addChangeListener((text) => received.push(text));
93
-
94
- typeText(editor, "ab");
95
- editor.handleInput("\x7f"); // backspace
96
- expect(received).toEqual(["a", "ab", "a"]);
97
- });
98
-
99
- test("listener fires on setText", () => {
100
- const editor = new Editor(createMockTUI(), theme);
101
- const received: string[] = [];
102
- editor.addChangeListener((text) => received.push(text));
103
-
104
- editor.setText("hello world");
105
- expect(received).toEqual(["hello world"]);
106
- });
107
-
108
- test("listener fires on submit (text cleared)", () => {
109
- const editor = new Editor(createMockTUI(), theme);
110
- const received: string[] = [];
111
- editor.addChangeListener((text) => received.push(text));
112
- editor.onSubmit = () => {}; // prevent default submit behavior
113
-
114
- typeText(editor, "test");
115
- received.length = 0; // clear typing events
116
-
117
- editor.handleInput("\r"); // Enter/submit
118
- // After submit, editor text is cleared — listener gets ""
119
- expect(received).toContain("");
120
- });
121
- });
@@ -1,112 +0,0 @@
1
- /**
2
- * Tests for Editor ghost text (inline suggestion) functionality.
3
- *
4
- * Uses a minimal TUI mock since Editor requires a TUI instance.
5
- */
6
- import { describe, expect, test } from "bun:test";
7
- import { Editor, type EditorTheme } from "../components/editor.js";
8
- import type { TUI } from "../tui.js";
9
-
10
- /** Minimal TUI mock providing only what Editor needs. */
11
- function createMockTUI(): TUI {
12
- return {
13
- requestRender: () => {},
14
- terminal: { rows: 40, cols: 80 },
15
- } as unknown as TUI;
16
- }
17
-
18
- const theme: EditorTheme = {
19
- borderColor: (s: string) => s,
20
- selectList: {
21
- selectedBg: (s: string) => s,
22
- selectedFg: (s: string) => s,
23
- normalBg: (s: string) => s,
24
- normalFg: (s: string) => s,
25
- matchHighlight: (s: string) => s,
26
- descriptionFg: (s: string) => s,
27
- },
28
- };
29
-
30
- describe("Editor ghost text", () => {
31
- test("setGhostText stores and getGhostText retrieves", () => {
32
- const editor = new Editor(createMockTUI(), theme);
33
- expect(editor.getGhostText()).toBeNull();
34
- editor.setGhostText("hello world");
35
- expect(editor.getGhostText()).toBe("hello world");
36
- });
37
-
38
- test("setGhostText(null) clears ghost text", () => {
39
- const editor = new Editor(createMockTUI(), theme);
40
- editor.setGhostText("suggestion");
41
- editor.setGhostText(null);
42
- expect(editor.getGhostText()).toBeNull();
43
- });
44
-
45
- test("ghost text renders as dim ANSI after cursor", () => {
46
- const editor = new Editor(createMockTUI(), theme);
47
- editor.setGhostText("complete me");
48
- const lines = editor.render(80);
49
- // Ghost text should appear as muted gray (256-color 242) in the output
50
- const joined = lines.join("\n");
51
- expect(joined).toContain("\x1b[38;5;242m");
52
- expect(joined).toContain("complete me");
53
- });
54
-
55
- test("ghost text cleared when character is typed", () => {
56
- const editor = new Editor(createMockTUI(), theme);
57
- editor.setGhostText("suggestion");
58
- editor.handleInput("a");
59
- expect(editor.getGhostText()).toBeNull();
60
- });
61
-
62
- test("ghost text cleared on backspace", () => {
63
- const editor = new Editor(createMockTUI(), theme);
64
- editor.setText("hello");
65
- editor.setGhostText("world");
66
- // Backspace (DEL character)
67
- editor.handleInput("\x7f");
68
- expect(editor.getGhostText()).toBeNull();
69
- });
70
-
71
- test("Tab accepts ghost text into buffer", () => {
72
- const editor = new Editor(createMockTUI(), theme);
73
- editor.setGhostText("complete me");
74
- // Tab
75
- editor.handleInput("\t");
76
- expect(editor.getGhostText()).toBeNull();
77
- expect(editor.getText()).toBe("complete me");
78
- });
79
-
80
- test("Enter on empty input accepts ghost text and submits", () => {
81
- const editor = new Editor(createMockTUI(), theme);
82
- let submitted = "";
83
- editor.onSubmit = (text: string) => {
84
- submitted = text;
85
- };
86
- editor.setGhostText("suggested prompt");
87
- // Enter (carriage return)
88
- editor.handleInput("\r");
89
- expect(submitted).toBe("suggested prompt");
90
- expect(editor.getGhostText()).toBeNull();
91
- });
92
-
93
- test("Enter on non-empty input submits typed text, not ghost text", () => {
94
- const editor = new Editor(createMockTUI(), theme);
95
- let submitted = "";
96
- editor.onSubmit = (text: string) => {
97
- submitted = text;
98
- };
99
- editor.setText("my input");
100
- editor.setGhostText("ghost suggestion");
101
- editor.handleInput("\r");
102
- expect(submitted).toBe("my input");
103
- });
104
-
105
- test("Escape dismisses ghost text", () => {
106
- const editor = new Editor(createMockTUI(), theme);
107
- editor.setGhostText("suggestion");
108
- // Escape
109
- editor.handleInput("\x1b");
110
- expect(editor.getGhostText()).toBeNull();
111
- });
112
- });
@@ -1,134 +0,0 @@
1
- /**
2
- * Tests for SGR mouse event parsing: parseMouseEvent and isMouseEvent.
3
- */
4
- import { describe, expect, it } from "bun:test";
5
- import { isMouseEvent, parseMouseEvent } from "../keys.js";
6
-
7
- // ── parseMouseEvent ──────────────────────────────────────────────────────────
8
-
9
- describe("parseMouseEvent", () => {
10
- it("parses scroll-up event", () => {
11
- // SGR: \x1b[<64;10;5M — code 64 = scroll up, col 10, row 5, press
12
- expect(parseMouseEvent("\x1b[<64;10;5M")).toEqual({
13
- type: "scroll-up",
14
- button: 0,
15
- x: 10,
16
- y: 5,
17
- });
18
- });
19
-
20
- it("parses scroll-down event", () => {
21
- // SGR: \x1b[<65;20;15M — code 65 = scroll down, col 20, row 15, press
22
- expect(parseMouseEvent("\x1b[<65;20;15M")).toEqual({
23
- type: "scroll-down",
24
- button: 0,
25
- x: 20,
26
- y: 15,
27
- });
28
- });
29
-
30
- it("parses left button press", () => {
31
- // SGR: \x1b[<0;5;3M — code 0 = left button, col 5, row 3, M = press
32
- expect(parseMouseEvent("\x1b[<0;5;3M")).toEqual({
33
- type: "press",
34
- button: 0,
35
- x: 5,
36
- y: 3,
37
- });
38
- });
39
-
40
- it("parses left button release", () => {
41
- // SGR: \x1b[<0;5;3m — code 0, lowercase m = release
42
- expect(parseMouseEvent("\x1b[<0;5;3m")).toEqual({
43
- type: "release",
44
- button: 0,
45
- x: 5,
46
- y: 3,
47
- });
48
- });
49
-
50
- it("parses middle button press", () => {
51
- // SGR: \x1b[<1;12;8M — code 1 = middle button
52
- expect(parseMouseEvent("\x1b[<1;12;8M")).toEqual({
53
- type: "press",
54
- button: 1,
55
- x: 12,
56
- y: 8,
57
- });
58
- });
59
-
60
- it("parses right button press", () => {
61
- // SGR: \x1b[<2;30;20M — code 2 = right button
62
- expect(parseMouseEvent("\x1b[<2;30;20M")).toEqual({
63
- type: "press",
64
- button: 2,
65
- x: 30,
66
- y: 20,
67
- });
68
- });
69
-
70
- it("parses drag event (bit 5 set)", () => {
71
- // SGR: \x1b[<32;15;10M — code 32 = motion + left button
72
- expect(parseMouseEvent("\x1b[<32;15;10M")).toEqual({
73
- type: "drag",
74
- button: 0,
75
- x: 15,
76
- y: 10,
77
- });
78
- });
79
-
80
- it("parses drag with right button", () => {
81
- // SGR: \x1b[<34;15;10M — code 34 = 32 (motion) + 2 (right)
82
- expect(parseMouseEvent("\x1b[<34;15;10M")).toEqual({
83
- type: "drag",
84
- button: 2,
85
- x: 15,
86
- y: 10,
87
- });
88
- });
89
-
90
- it("handles large coordinates (beyond 223-column limit)", () => {
91
- // SGR format supports arbitrary coordinates — this is why we use mode 1006
92
- expect(parseMouseEvent("\x1b[<0;300;150M")).toEqual({
93
- type: "press",
94
- button: 0,
95
- x: 300,
96
- y: 150,
97
- });
98
- });
99
-
100
- it("returns null for non-mouse input", () => {
101
- expect(parseMouseEvent("a")).toBeNull();
102
- expect(parseMouseEvent("\x1b[A")).toBeNull(); // arrow up
103
- expect(parseMouseEvent("\x1b[1;2H")).toBeNull(); // cursor position
104
- expect(parseMouseEvent("")).toBeNull();
105
- });
106
-
107
- it("returns null for malformed SGR sequences", () => {
108
- expect(parseMouseEvent("\x1b[<0;5M")).toBeNull(); // missing y
109
- expect(parseMouseEvent("\x1b[<0;5;3")).toBeNull(); // missing M/m terminator
110
- expect(parseMouseEvent("\x1b[<;5;3M")).toBeNull(); // missing code
111
- });
112
- });
113
-
114
- // ── isMouseEvent ─────────────────────────────────────────────────────────────
115
-
116
- describe("isMouseEvent", () => {
117
- it("returns true for SGR mouse sequences", () => {
118
- expect(isMouseEvent("\x1b[<0;5;3M")).toBe(true);
119
- expect(isMouseEvent("\x1b[<64;10;5M")).toBe(true);
120
- expect(isMouseEvent("\x1b[<0;300;150m")).toBe(true);
121
- });
122
-
123
- it("returns false for non-mouse input", () => {
124
- expect(isMouseEvent("a")).toBe(false);
125
- expect(isMouseEvent("\x1b[A")).toBe(false);
126
- expect(isMouseEvent("\x1b[1")).toBe(false);
127
- expect(isMouseEvent("")).toBe(false);
128
- });
129
-
130
- it("returns false for strings shorter than minimum mouse sequence", () => {
131
- // Minimum: \x1b[<N;N;NM = 9 chars
132
- expect(isMouseEvent("\x1b[<0;5;")).toBe(false);
133
- });
134
- });
@@ -1,49 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { SettingsList, type SettingsListTheme } from "../components/settings-list.js";
3
-
4
- const theme: SettingsListTheme = {
5
- cursor: "> ",
6
- description: (text) => text,
7
- hint: (text) => text,
8
- label: (text) => text,
9
- value: (text) => text,
10
- };
11
-
12
- describe("SettingsList submenu transitions", () => {
13
- it("preserves submenu height for one frame when closing back to the main list", () => {
14
- let changedValue: string | undefined;
15
- const list = new SettingsList(
16
- [
17
- {
18
- id: "thinking",
19
- label: "Thinking level",
20
- currentValue: "medium",
21
- description: "Reasoning depth",
22
- submenu: (_currentValue, done) => ({
23
- handleInput: () => done("high"),
24
- invalidate: () => {},
25
- render: () => ["submenu", "", "one", "two", "three", "four", "five", "six"],
26
- }),
27
- },
28
- ],
29
- 10,
30
- theme,
31
- (_id, newValue) => {
32
- changedValue = newValue;
33
- },
34
- () => {}
35
- );
36
-
37
- const initialLines = list.render(80);
38
- list.handleInput(" ");
39
- const submenuLines = list.render(80);
40
- list.handleInput("x");
41
- const firstFrameAfterClose = list.render(80);
42
- const secondFrameAfterClose = list.render(80);
43
-
44
- expect(changedValue).toBe("high");
45
- expect(submenuLines.length).toBeGreaterThan(initialLines.length);
46
- expect(firstFrameAfterClose.length).toBe(submenuLines.length);
47
- expect(secondFrameAfterClose.length).toBe(initialLines.length);
48
- });
49
- });