@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,8 +1,14 @@
1
- import { getEditorKeybindings } from "../keybindings.js";
1
+ import { getKeybindings } from "../keybindings.js";
2
2
  import type { Component } from "../tui.js";
3
- import { truncateToWidth } from "../utils.js";
3
+ import { truncateToWidth, visibleWidth } from "../utils.js";
4
+
5
+ const DEFAULT_PRIMARY_COLUMN_WIDTH = 32;
6
+ const PRIMARY_COLUMN_GAP = 2;
7
+ const MIN_DESCRIPTION_WIDTH = 10;
4
8
 
5
9
  const normalizeToSingleLine = (text: string): string => text.replace(/[\r\n]+/g, " ").trim();
10
+ const clamp = (value: number, min: number, max: number): number =>
11
+ Math.max(min, Math.min(value, max));
6
12
 
7
13
  export interface SelectItem {
8
14
  value: string;
@@ -18,22 +24,43 @@ export interface SelectListTheme {
18
24
  noMatch: (text: string) => string;
19
25
  }
20
26
 
27
+ export interface SelectListTruncatePrimaryContext {
28
+ text: string;
29
+ maxWidth: number;
30
+ columnWidth: number;
31
+ item: SelectItem;
32
+ isSelected: boolean;
33
+ }
34
+
35
+ export interface SelectListLayoutOptions {
36
+ minPrimaryColumnWidth?: number;
37
+ maxPrimaryColumnWidth?: number;
38
+ truncatePrimary?: (context: SelectListTruncatePrimaryContext) => string;
39
+ }
40
+
21
41
  export class SelectList implements Component {
22
42
  private items: SelectItem[] = [];
23
43
  private filteredItems: SelectItem[] = [];
24
44
  private selectedIndex: number = 0;
25
45
  private maxVisible: number = 5;
26
46
  private theme: SelectListTheme;
47
+ private layout: SelectListLayoutOptions;
27
48
 
28
49
  public onSelect?: (item: SelectItem) => void;
29
50
  public onCancel?: () => void;
30
51
  public onSelectionChange?: (item: SelectItem) => void;
31
52
 
32
- constructor(items: SelectItem[], maxVisible: number, theme: SelectListTheme) {
53
+ constructor(
54
+ items: SelectItem[],
55
+ maxVisible: number,
56
+ theme: SelectListTheme,
57
+ layout: SelectListLayoutOptions = {}
58
+ ) {
33
59
  this.items = items;
34
60
  this.filteredItems = items;
35
61
  this.maxVisible = maxVisible;
36
62
  this.theme = theme;
63
+ this.layout = layout;
37
64
  }
38
65
 
39
66
  setFilter(filter: string): void {
@@ -61,6 +88,8 @@ export class SelectList implements Component {
61
88
  return lines;
62
89
  }
63
90
 
91
+ const primaryColumnWidth = this.getPrimaryColumnWidth();
92
+
64
93
  // Calculate visible range with scrolling
65
94
  const startIndex = Math.max(
66
95
  0,
@@ -80,68 +109,9 @@ export class SelectList implements Component {
80
109
  const descriptionSingleLine = item.description
81
110
  ? normalizeToSingleLine(item.description)
82
111
  : undefined;
83
-
84
- let line = "";
85
- if (isSelected) {
86
- // Use arrow indicator for selection - entire line uses selectedText color
87
- const prefixWidth = 2; // "↗ " is 2 characters visually
88
- const displayValue = item.label || item.value;
89
-
90
- if (descriptionSingleLine && width > 40) {
91
- // Calculate how much space we have for value + description
92
- const maxValueWidth = Math.min(30, width - prefixWidth - 4);
93
- const truncatedValue = truncateToWidth(displayValue, maxValueWidth, "");
94
- const spacing = " ".repeat(Math.max(1, 32 - truncatedValue.length));
95
-
96
- // Calculate remaining space for description using visible widths
97
- const descriptionStart = prefixWidth + truncatedValue.length + spacing.length;
98
- const remainingWidth = width - descriptionStart - 2; // -2 for safety
99
-
100
- if (remainingWidth > 10) {
101
- const truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, "");
102
- // Apply selectedText to entire line content
103
- line = this.theme.selectedText(`↗ ${truncatedValue}${spacing}${truncatedDesc}`);
104
- } else {
105
- // Not enough space for description
106
- const maxWidth = width - prefixWidth - 2;
107
- line = this.theme.selectedText(`↗ ${truncateToWidth(displayValue, maxWidth, "")}`);
108
- }
109
- } else {
110
- // No description or not enough width
111
- const maxWidth = width - prefixWidth - 2;
112
- line = this.theme.selectedText(`↗ ${truncateToWidth(displayValue, maxWidth, "")}`);
113
- }
114
- } else {
115
- const displayValue = item.label || item.value;
116
- const prefix = " ";
117
-
118
- if (descriptionSingleLine && width > 40) {
119
- // Calculate how much space we have for value + description
120
- const maxValueWidth = Math.min(30, width - prefix.length - 4);
121
- const truncatedValue = truncateToWidth(displayValue, maxValueWidth, "");
122
- const spacing = " ".repeat(Math.max(1, 32 - truncatedValue.length));
123
-
124
- // Calculate remaining space for description
125
- const descriptionStart = prefix.length + truncatedValue.length + spacing.length;
126
- const remainingWidth = width - descriptionStart - 2; // -2 for safety
127
-
128
- if (remainingWidth > 10) {
129
- const truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, "");
130
- const descText = this.theme.description(spacing + truncatedDesc);
131
- line = prefix + truncatedValue + descText;
132
- } else {
133
- // Not enough space for description
134
- const maxWidth = width - prefix.length - 2;
135
- line = prefix + truncateToWidth(displayValue, maxWidth, "");
136
- }
137
- } else {
138
- // No description or not enough width
139
- const maxWidth = width - prefix.length - 2;
140
- line = prefix + truncateToWidth(displayValue, maxWidth, "");
141
- }
142
- }
143
-
144
- lines.push(line);
112
+ lines.push(
113
+ this.renderItem(item, isSelected, width, descriptionSingleLine, primaryColumnWidth)
114
+ );
145
115
  }
146
116
 
147
117
  // Add scroll indicators if needed
@@ -155,34 +125,130 @@ export class SelectList implements Component {
155
125
  }
156
126
 
157
127
  handleInput(keyData: string): void {
158
- const kb = getEditorKeybindings();
128
+ const kb = getKeybindings();
159
129
  // Up arrow - wrap to bottom when at top
160
- if (kb.matches(keyData, "selectUp")) {
130
+ if (kb.matches(keyData, "tui.select.up")) {
161
131
  this.selectedIndex =
162
132
  this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;
163
133
  this.notifySelectionChange();
164
134
  }
165
135
  // Down arrow - wrap to top when at bottom
166
- else if (kb.matches(keyData, "selectDown")) {
136
+ else if (kb.matches(keyData, "tui.select.down")) {
167
137
  this.selectedIndex =
168
138
  this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;
169
139
  this.notifySelectionChange();
170
140
  }
171
141
  // Enter
172
- else if (kb.matches(keyData, "selectConfirm")) {
142
+ else if (kb.matches(keyData, "tui.select.confirm")) {
173
143
  const selectedItem = this.filteredItems[this.selectedIndex];
174
144
  if (selectedItem && this.onSelect) {
175
145
  this.onSelect(selectedItem);
176
146
  }
177
147
  }
178
148
  // Escape or Ctrl+C
179
- else if (kb.matches(keyData, "selectCancel")) {
149
+ else if (kb.matches(keyData, "tui.select.cancel")) {
180
150
  if (this.onCancel) {
181
151
  this.onCancel();
182
152
  }
183
153
  }
184
154
  }
185
155
 
156
+ private renderItem(
157
+ item: SelectItem,
158
+ isSelected: boolean,
159
+ width: number,
160
+ descriptionSingleLine: string | undefined,
161
+ primaryColumnWidth: number
162
+ ): string {
163
+ const prefix = isSelected ? "→ " : " ";
164
+ const prefixWidth = visibleWidth(prefix);
165
+
166
+ if (descriptionSingleLine && width > 40) {
167
+ const effectivePrimaryColumnWidth = Math.max(
168
+ 1,
169
+ Math.min(primaryColumnWidth, width - prefixWidth - 4)
170
+ );
171
+ const maxPrimaryWidth = Math.max(1, effectivePrimaryColumnWidth - PRIMARY_COLUMN_GAP);
172
+ const truncatedValue = this.truncatePrimary(
173
+ item,
174
+ isSelected,
175
+ maxPrimaryWidth,
176
+ effectivePrimaryColumnWidth
177
+ );
178
+ const truncatedValueWidth = visibleWidth(truncatedValue);
179
+ const spacing = " ".repeat(Math.max(1, effectivePrimaryColumnWidth - truncatedValueWidth));
180
+ const descriptionStart = prefixWidth + truncatedValueWidth + spacing.length;
181
+ const remainingWidth = width - descriptionStart - 2; // -2 for safety
182
+
183
+ if (remainingWidth > MIN_DESCRIPTION_WIDTH) {
184
+ const truncatedDesc = truncateToWidth(descriptionSingleLine, remainingWidth, "");
185
+ if (isSelected) {
186
+ return this.theme.selectedText(`${prefix}${truncatedValue}${spacing}${truncatedDesc}`);
187
+ }
188
+
189
+ const descText = this.theme.description(spacing + truncatedDesc);
190
+ return prefix + truncatedValue + descText;
191
+ }
192
+ }
193
+
194
+ const maxWidth = width - prefixWidth - 2;
195
+ const truncatedValue = this.truncatePrimary(item, isSelected, maxWidth, maxWidth);
196
+ if (isSelected) {
197
+ return this.theme.selectedText(`${prefix}${truncatedValue}`);
198
+ }
199
+
200
+ return prefix + truncatedValue;
201
+ }
202
+
203
+ private getPrimaryColumnWidth(): number {
204
+ const { min, max } = this.getPrimaryColumnBounds();
205
+ const widestPrimary = this.filteredItems.reduce((widest, item) => {
206
+ return Math.max(widest, visibleWidth(this.getDisplayValue(item)) + PRIMARY_COLUMN_GAP);
207
+ }, 0);
208
+
209
+ return clamp(widestPrimary, min, max);
210
+ }
211
+
212
+ private getPrimaryColumnBounds(): { min: number; max: number } {
213
+ const rawMin =
214
+ this.layout.minPrimaryColumnWidth ??
215
+ this.layout.maxPrimaryColumnWidth ??
216
+ DEFAULT_PRIMARY_COLUMN_WIDTH;
217
+ const rawMax =
218
+ this.layout.maxPrimaryColumnWidth ??
219
+ this.layout.minPrimaryColumnWidth ??
220
+ DEFAULT_PRIMARY_COLUMN_WIDTH;
221
+
222
+ return {
223
+ min: Math.max(1, Math.min(rawMin, rawMax)),
224
+ max: Math.max(1, Math.max(rawMin, rawMax)),
225
+ };
226
+ }
227
+
228
+ private truncatePrimary(
229
+ item: SelectItem,
230
+ isSelected: boolean,
231
+ maxWidth: number,
232
+ columnWidth: number
233
+ ): string {
234
+ const displayValue = this.getDisplayValue(item);
235
+ const truncatedValue = this.layout.truncatePrimary
236
+ ? this.layout.truncatePrimary({
237
+ text: displayValue,
238
+ maxWidth,
239
+ columnWidth,
240
+ item,
241
+ isSelected,
242
+ })
243
+ : truncateToWidth(displayValue, maxWidth, "");
244
+
245
+ return truncateToWidth(truncatedValue, maxWidth, "");
246
+ }
247
+
248
+ private getDisplayValue(item: SelectItem): string {
249
+ return item.label || item.value;
250
+ }
251
+
186
252
  private notifySelectionChange(): void {
187
253
  const selectedItem = this.filteredItems[this.selectedIndex];
188
254
  if (selectedItem && this.onSelectionChange) {
@@ -1,5 +1,5 @@
1
1
  import { fuzzyFilter } from "../fuzzy.js";
2
- import { getEditorKeybindings } from "../keybindings.js";
2
+ import { getKeybindings } from "../keybindings.js";
3
3
  import type { Component } from "../tui.js";
4
4
  import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils.js";
5
5
  import { Input } from "./input.js";
@@ -45,8 +45,6 @@ export class SettingsList implements Component {
45
45
  // Submenu state
46
46
  private submenuComponent: Component | null = null;
47
47
  private submenuItemIndex: number | null = null;
48
- private lastRenderLineCount = 0;
49
- private nextMinLineCount = 0;
50
48
 
51
49
  constructor(
52
50
  items: SettingItem[],
@@ -81,17 +79,12 @@ export class SettingsList implements Component {
81
79
  }
82
80
 
83
81
  render(width: number): string[] {
84
- const lines = this.submenuComponent
85
- ? this.submenuComponent.render(width)
86
- : this.renderMainList(width);
87
-
88
- if (this.nextMinLineCount > lines.length) {
89
- lines.push(...Array.from({ length: this.nextMinLineCount - lines.length }, () => ""));
82
+ // If submenu is active, render it instead
83
+ if (this.submenuComponent) {
84
+ return this.submenuComponent.render(width);
90
85
  }
91
86
 
92
- this.nextMinLineCount = 0;
93
- this.lastRenderLineCount = lines.length;
94
- return lines;
87
+ return this.renderMainList(width);
95
88
  }
96
89
 
97
90
  private renderMainList(width: number): string[] {
@@ -191,19 +184,19 @@ export class SettingsList implements Component {
191
184
  }
192
185
 
193
186
  // Main list input handling
194
- const kb = getEditorKeybindings();
187
+ const kb = getKeybindings();
195
188
  const displayItems = this.searchEnabled ? this.filteredItems : this.items;
196
- if (kb.matches(data, "selectUp")) {
189
+ if (kb.matches(data, "tui.select.up")) {
197
190
  if (displayItems.length === 0) return;
198
191
  this.selectedIndex =
199
192
  this.selectedIndex === 0 ? displayItems.length - 1 : this.selectedIndex - 1;
200
- } else if (kb.matches(data, "selectDown")) {
193
+ } else if (kb.matches(data, "tui.select.down")) {
201
194
  if (displayItems.length === 0) return;
202
195
  this.selectedIndex =
203
196
  this.selectedIndex === displayItems.length - 1 ? 0 : this.selectedIndex + 1;
204
- } else if (kb.matches(data, "selectConfirm") || data === " ") {
197
+ } else if (kb.matches(data, "tui.select.confirm") || data === " ") {
205
198
  this.activateItem();
206
- } else if (kb.matches(data, "selectCancel")) {
199
+ } else if (kb.matches(data, "tui.select.cancel")) {
207
200
  this.onCancel();
208
201
  } else if (this.searchEnabled && this.searchInput) {
209
202
  const sanitized = data.replace(/ /g, "");
@@ -222,9 +215,7 @@ export class SettingsList implements Component {
222
215
  if (!item) return;
223
216
 
224
217
  if (item.submenu) {
225
- // Preserve height for the first submenu frame so the TUI doesn't treat
226
- // the editor→submenu swap as a destructive shrink/expand transition.
227
- this.nextMinLineCount = this.lastRenderLineCount;
218
+ // Open submenu, passing current value so it can pre-select correctly
228
219
  this.submenuItemIndex = this.selectedIndex;
229
220
  this.submenuComponent = item.submenu(item.currentValue, (selectedValue?: string) => {
230
221
  if (selectedValue !== undefined) {
@@ -244,9 +235,6 @@ export class SettingsList implements Component {
244
235
  }
245
236
 
246
237
  private closeSubmenu(): void {
247
- // Preserve one frame of the submenu height so the transition back to the
248
- // main settings list doesn't trigger a screen-clearing shrink redraw.
249
- this.nextMinLineCount = this.lastRenderLineCount;
250
238
  this.submenuComponent = null;
251
239
  // Restore selection to the item that opened the submenu
252
240
  if (this.submenuItemIndex !== null) {
@@ -4,33 +4,25 @@
4
4
  export {
5
5
  type AutocompleteItem,
6
6
  type AutocompleteProvider,
7
+ type AutocompleteSuggestions,
7
8
  CombinedAutocompleteProvider,
8
9
  type SlashCommand,
9
10
  } from "./autocomplete.js";
10
- // Border styles
11
- export {
12
- type BorderStyle,
13
- defaultBorderStyle,
14
- FLAT,
15
- ROUNDED,
16
- SHARP,
17
- setDefaultBorderStyle,
18
- } from "./border-styles.js";
19
11
  // Components
20
- export { BorderedBox, type BorderedBoxOptions } from "./components/bordered-box.js";
21
12
  export { Box } from "./components/box.js";
22
13
  export { CancellableLoader } from "./components/cancellable-loader.js";
23
14
  export { Editor, type EditorOptions, type EditorTheme } from "./components/editor.js";
24
- export {
25
- Image,
26
- type ImageOptions,
27
- type ImageTheme,
28
- setNextImageFilePath,
29
- } from "./components/image.js";
15
+ export { Image, type ImageOptions, type ImageTheme } from "./components/image.js";
30
16
  export { Input } from "./components/input.js";
31
- export { Loader, type LoaderOptions, type MessageTransformContext } from "./components/loader.js";
17
+ export { Loader } from "./components/loader.js";
32
18
  export { type DefaultTextStyle, Markdown, type MarkdownTheme } from "./components/markdown.js";
33
- export { type SelectItem, SelectList, type SelectListTheme } from "./components/select-list.js";
19
+ export {
20
+ type SelectItem,
21
+ SelectList,
22
+ type SelectListLayoutOptions,
23
+ type SelectListTheme,
24
+ type SelectListTruncatePrimaryContext,
25
+ } from "./components/select-list.js";
34
26
  export {
35
27
  type SettingItem,
36
28
  SettingsList,
@@ -45,34 +37,28 @@ export type { EditorComponent } from "./editor-component.js";
45
37
  export { type FuzzyMatch, fuzzyFilter, fuzzyMatch } from "./fuzzy.js";
46
38
  // Keybindings
47
39
  export {
48
- DEFAULT_EDITOR_KEYBINDINGS,
49
- type EditorAction,
50
- type EditorKeybindingsConfig,
51
- EditorKeybindingsManager,
52
- getEditorKeybindings,
53
40
  getKeybindings,
54
41
  type Keybinding,
42
+ type KeybindingConflict,
55
43
  type KeybindingDefinition,
44
+ type KeybindingDefinitions,
56
45
  type Keybindings,
57
46
  type KeybindingsConfig,
58
47
  KeybindingsManager,
59
- setEditorKeybindings,
60
48
  setKeybindings,
61
49
  TUI_KEYBINDINGS,
62
50
  } from "./keybindings.js";
63
51
  // Keyboard input handling
64
52
  export {
53
+ decodeKittyPrintable,
65
54
  isKeyRelease,
66
55
  isKeyRepeat,
67
56
  isKittyProtocolActive,
68
- isMouseEvent,
69
57
  Key,
70
58
  type KeyEventType,
71
59
  type KeyId,
72
- type MouseEvent,
73
60
  matchesKey,
74
61
  parseKey,
75
- parseMouseEvent,
76
62
  setKittyProtocolActive,
77
63
  } from "./keys.js";
78
64
  // Input buffering for batch splitting
@@ -83,15 +69,12 @@ export { ProcessTerminal, type Terminal } from "./terminal.js";
83
69
  export {
84
70
  allocateImageId,
85
71
  type CellDimensions,
86
- calculateImageLayout,
87
- createImageMetadata,
72
+ calculateImageRows,
88
73
  deleteAllKittyImages,
89
74
  deleteKittyImage,
90
75
  detectCapabilities,
91
- detectImageFormat,
92
76
  encodeITerm2,
93
77
  encodeKitty,
94
- formatImageDimensions,
95
78
  getCapabilities,
96
79
  getCellDimensions,
97
80
  getGifDimensions,
@@ -99,16 +82,14 @@ export {
99
82
  getJpegDimensions,
100
83
  getPngDimensions,
101
84
  getWebpDimensions,
85
+ hyperlink,
102
86
  type ImageDimensions,
103
- type ImageFormat,
104
- type ImageLayout,
105
- type ImageMetadata,
106
87
  type ImageProtocol,
107
88
  type ImageRenderOptions,
108
89
  imageFallback,
109
- imageFormatToMime,
110
90
  renderImage,
111
91
  resetCapabilitiesCache,
92
+ setCapabilities,
112
93
  setCellDimensions,
113
94
  type TerminalCapabilities,
114
95
  } from "./terminal-image.js";
@@ -126,4 +107,4 @@ export {
126
107
  TUI,
127
108
  } from "./tui.js";
128
109
  // Utilities
129
- export { fileLink, hyperlink, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";
110
+ export { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "./utils.js";