claudeup 4.19.0 → 4.22.0

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 (180) hide show
  1. package/bin/claudeup.js +43 -19
  2. package/package.json +14 -19
  3. package/scripts/build-binaries.ts +77 -0
  4. package/scripts/check-optional-deps.ts +49 -0
  5. package/scripts/test-isolated.ts +43 -0
  6. package/src/__tests__/cli-router.test.ts +65 -0
  7. package/src/__tests__/conventions-integration.test.ts +745 -0
  8. package/src/__tests__/conventions-manager.test.ts +1172 -0
  9. package/src/__tests__/doctor-bins.test.ts +35 -0
  10. package/src/__tests__/doctor.test.ts +450 -0
  11. package/src/__tests__/dual-write-prevention.test.ts +14 -4
  12. package/src/__tests__/file-locking.test.ts +208 -0
  13. package/src/__tests__/gitignore-prerun.test.ts +1 -0
  14. package/src/__tests__/install-command.test.ts +81 -0
  15. package/src/__tests__/install-plan.test.ts +98 -0
  16. package/src/__tests__/manifest.test.ts +136 -0
  17. package/src/__tests__/marketplace-refresh.test.ts +267 -0
  18. package/src/__tests__/plugin-requires.test.ts +133 -0
  19. package/src/__tests__/plugin-setup.test.ts +7 -0
  20. package/src/__tests__/profile-command.test.ts +116 -0
  21. package/src/__tests__/profile-materializer.test.ts +82 -0
  22. package/src/__tests__/profile-sync.test.ts +136 -0
  23. package/src/__tests__/registry-version-resolution.test.ts +74 -0
  24. package/src/__tests__/resolve-executable.test.ts +42 -0
  25. package/src/__tests__/resolver.test.ts +218 -0
  26. package/src/__tests__/symlink-manager.test.ts +99 -0
  27. package/src/__tests__/toolchain.test.ts +56 -0
  28. package/src/cli/claude.ts +21 -0
  29. package/src/cli/doctor.ts +132 -0
  30. package/src/cli/install.ts +360 -0
  31. package/src/cli/profile.ts +166 -0
  32. package/src/cli/router.ts +107 -0
  33. package/src/cli/update.ts +85 -0
  34. package/src/data/cli-tools.ts +7 -7
  35. package/src/data/gitignore-defaults.ts +4 -0
  36. package/src/data/marketplaces.ts +12 -0
  37. package/src/data/predefined-profiles.ts +3 -4
  38. package/src/main.tsx +11 -154
  39. package/src/prerunner/index.ts +62 -27
  40. package/src/services/claude-cli.ts +19 -22
  41. package/src/services/claude-settings.ts +99 -26
  42. package/src/services/conventions-manager.ts +865 -0
  43. package/src/services/doctor-bins.ts +49 -0
  44. package/src/services/doctor.ts +509 -0
  45. package/src/services/install-plan.ts +107 -0
  46. package/src/services/manifest.ts +166 -0
  47. package/src/services/marketplace-refresh.ts +173 -0
  48. package/src/services/plugin-manager.ts +7 -1
  49. package/src/services/plugin-requires.ts +215 -0
  50. package/src/services/plugin-version-check.ts +9 -4
  51. package/src/services/profile-materializer.ts +61 -0
  52. package/src/services/profile-sync.ts +150 -0
  53. package/src/services/resolver.ts +293 -0
  54. package/src/services/symlink-manager.ts +146 -0
  55. package/src/services/toolchain.ts +97 -0
  56. package/src/services/version-check.ts +10 -11
  57. package/src/types/index.ts +138 -31
  58. package/src/ui/App.tsx +0 -4
  59. package/src/ui/screens/CliToolsScreen.tsx +11 -3
  60. package/src/ui/screens/index.ts +0 -1
  61. package/src/ui/state/reducer.ts +0 -101
  62. package/src/ui/state/types.ts +0 -35
  63. package/src/utils/command-utils.ts +23 -0
  64. package/src/utils/file-locking.ts +144 -0
  65. package/src/data/alias-flags.js +0 -205
  66. package/src/data/cli-tools.js +0 -123
  67. package/src/data/gitignore-defaults.js +0 -24
  68. package/src/data/gitignore-reasons.js +0 -97
  69. package/src/data/gitignore-templates.js +0 -21
  70. package/src/data/marketplaces.js +0 -138
  71. package/src/data/mcp-servers.js +0 -509
  72. package/src/data/predefined-profiles.js +0 -248
  73. package/src/data/settings-catalog.js +0 -625
  74. package/src/data/skill-repos.js +0 -160
  75. package/src/data/statuslines.js +0 -159
  76. package/src/data/statuslines.ts +0 -188
  77. package/src/index.js +0 -4
  78. package/src/index.ts +0 -5
  79. package/src/main.js +0 -170
  80. package/src/prerunner/index.js +0 -252
  81. package/src/services/alias-settings.js +0 -51
  82. package/src/services/alias-shell-writer.js +0 -1018
  83. package/src/services/alias-store.js +0 -129
  84. package/src/services/claude-cli.js +0 -189
  85. package/src/services/claude-runner.js +0 -28
  86. package/src/services/claude-settings.js +0 -1223
  87. package/src/services/gitignore-detector.js +0 -155
  88. package/src/services/gitignore-fixer.js +0 -231
  89. package/src/services/gitignore-prerun.js +0 -46
  90. package/src/services/gitignore-resolver.js +0 -143
  91. package/src/services/gitignore-service.js +0 -117
  92. package/src/services/local-marketplace.js +0 -339
  93. package/src/services/marketplace-fetcher.js +0 -96
  94. package/src/services/marketplace-sync.js +0 -94
  95. package/src/services/mcp-registry.js +0 -87
  96. package/src/services/plugin-manager.js +0 -473
  97. package/src/services/plugin-mcp-config.js +0 -177
  98. package/src/services/plugin-setup.js +0 -499
  99. package/src/services/plugin-version-check.js +0 -262
  100. package/src/services/profiles.js +0 -161
  101. package/src/services/settings-manager.js +0 -243
  102. package/src/services/skills-manager.js +0 -393
  103. package/src/services/skillsmp-client.js +0 -87
  104. package/src/services/update-cache.js +0 -52
  105. package/src/services/version-check.js +0 -99
  106. package/src/types/gitignore.js +0 -6
  107. package/src/types/index.js +0 -1
  108. package/src/ui/App.js +0 -355
  109. package/src/ui/adapters/pluginsAdapter.js +0 -139
  110. package/src/ui/adapters/settingsAdapter.js +0 -111
  111. package/src/ui/adapters/skillsAdapter.js +0 -154
  112. package/src/ui/components/CategoryHeader.js +0 -9
  113. package/src/ui/components/EmptyFilterState.js +0 -4
  114. package/src/ui/components/FlagDetailEditor.js +0 -0
  115. package/src/ui/components/ScopeIndicator.js +0 -30
  116. package/src/ui/components/ScrollableList.js +0 -36
  117. package/src/ui/components/SearchInput.js +0 -19
  118. package/src/ui/components/StyledText.js +0 -39
  119. package/src/ui/components/TabBar.js +0 -19
  120. package/src/ui/components/layout/FooterHints.js +0 -29
  121. package/src/ui/components/layout/Panel.js +0 -6
  122. package/src/ui/components/layout/ProgressBar.js +0 -14
  123. package/src/ui/components/layout/ScopeTabs.js +0 -6
  124. package/src/ui/components/layout/ScreenLayout.js +0 -16
  125. package/src/ui/components/layout/index.js +0 -5
  126. package/src/ui/components/modals/ConfirmModal.js +0 -14
  127. package/src/ui/components/modals/InputModal.js +0 -5
  128. package/src/ui/components/modals/LoadingModal.js +0 -17
  129. package/src/ui/components/modals/MessageModal.js +0 -16
  130. package/src/ui/components/modals/ModalContainer.js +0 -137
  131. package/src/ui/components/modals/SelectModal.js +0 -18
  132. package/src/ui/components/modals/VersionMismatchModal.js +0 -36
  133. package/src/ui/components/modals/index.js +0 -6
  134. package/src/ui/components/primitives/ActionHints.js +0 -13
  135. package/src/ui/components/primitives/DetailSection.js +0 -7
  136. package/src/ui/components/primitives/KeyValueLine.js +0 -8
  137. package/src/ui/components/primitives/ListCategoryRow.js +0 -8
  138. package/src/ui/components/primitives/MetaText.js +0 -8
  139. package/src/ui/components/primitives/ScopeDetail.js +0 -32
  140. package/src/ui/components/primitives/ScopeSquares.js +0 -11
  141. package/src/ui/components/primitives/SelectableRow.js +0 -5
  142. package/src/ui/components/primitives/index.js +0 -8
  143. package/src/ui/hooks/index.js +0 -4
  144. package/src/ui/hooks/useAsyncData.js +0 -77
  145. package/src/ui/hooks/useGitignoreModal.js +0 -74
  146. package/src/ui/hooks/useKeyboard.js +0 -13
  147. package/src/ui/hooks/useKeyboardHandler.js +0 -39
  148. package/src/ui/hooks/useMismatchModal.js +0 -77
  149. package/src/ui/registry.js +0 -1
  150. package/src/ui/renderers/cliToolRenderers.js +0 -54
  151. package/src/ui/renderers/gitignoreRenderers.js +0 -46
  152. package/src/ui/renderers/mcpRenderers.js +0 -26
  153. package/src/ui/renderers/pluginRenderers.js +0 -124
  154. package/src/ui/renderers/profileRenderers.js +0 -177
  155. package/src/ui/renderers/settingsRenderers.js +0 -73
  156. package/src/ui/renderers/skillRenderers.js +0 -138
  157. package/src/ui/screens/AliasScreen.js +0 -1111
  158. package/src/ui/screens/CliToolsScreen.js +0 -338
  159. package/src/ui/screens/EnvVarsScreen.js +0 -152
  160. package/src/ui/screens/GitignoreScreen.js +0 -328
  161. package/src/ui/screens/McpRegistryScreen.js +0 -238
  162. package/src/ui/screens/McpScreen.js +0 -176
  163. package/src/ui/screens/ModelSelectorScreen.js +0 -296
  164. package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
  165. package/src/ui/screens/PluginsScreen.js +0 -769
  166. package/src/ui/screens/ProfilesScreen.js +0 -298
  167. package/src/ui/screens/SkillsScreen.js +0 -549
  168. package/src/ui/screens/StatusLineScreen.js +0 -206
  169. package/src/ui/screens/StatusLineScreen.tsx +0 -416
  170. package/src/ui/screens/index.js +0 -10
  171. package/src/ui/state/AnimationContext.js +0 -34
  172. package/src/ui/state/AppContext.js +0 -162
  173. package/src/ui/state/DimensionsContext.js +0 -71
  174. package/src/ui/state/reducer.js +0 -547
  175. package/src/ui/state/types.js +0 -1
  176. package/src/ui/theme.js +0 -47
  177. package/src/utils/clipboard.js +0 -56
  178. package/src/utils/command-utils.js +0 -19
  179. package/src/utils/fuzzy-search.js +0 -101
  180. package/src/utils/string-utils.js +0 -62
@@ -1,444 +0,0 @@
1
- import React, { useMemo, useEffect } from "react";
2
- import { useApp } from "../state/AppContext.js";
3
- import { useDimensions } from "../state/DimensionsContext.js";
4
-
5
- import { fuzzyFilter, highlightMatches } from "../../utils/fuzzy-search.js";
6
- import { useKeyboard } from "../hooks/useKeyboard.js";
7
- import { ScrollableList } from "../components/ScrollableList.js";
8
- import { FooterHints } from "../components/layout/index.js";
9
-
10
- interface ModelItem {
11
- id: string;
12
- type: "header" | "model";
13
- label: string;
14
- provider?: string;
15
- configured?: boolean;
16
- modelId?: string;
17
- description?: string;
18
- }
19
-
20
- const RECENT_MODELS: ModelItem[] = [
21
- {
22
- id: "recent-1",
23
- type: "model",
24
- label: "MoonshotAI: Kimi K2 0905 (exacto)",
25
- provider: "OpenRouter",
26
- modelId: "moonshot/kimi-k2-0905",
27
- configured: true,
28
- },
29
- ];
30
-
31
- const ANTHROPIC_MODELS: ModelItem[] = [
32
- {
33
- id: "claude-opus-4",
34
- type: "model",
35
- label: "Claude Opus 4",
36
- provider: "Anthropic",
37
- modelId: "anthropic/claude-opus-4",
38
- configured: true,
39
- },
40
- {
41
- id: "claude-sonnet-4",
42
- type: "model",
43
- label: "Claude Sonnet 4",
44
- provider: "Anthropic",
45
- modelId: "anthropic/claude-sonnet-4",
46
- configured: true,
47
- },
48
- {
49
- id: "claude-opus-4.1",
50
- type: "model",
51
- label: "Claude Opus 4.1",
52
- provider: "Anthropic",
53
- modelId: "anthropic/claude-opus-4.1",
54
- configured: true,
55
- },
56
- {
57
- id: "claude-opus-4.5",
58
- type: "model",
59
- label: "Claude Opus 4.5",
60
- provider: "Anthropic",
61
- modelId: "anthropic/claude-opus-4.5",
62
- configured: true,
63
- },
64
- {
65
- id: "claude-3.5-haiku",
66
- type: "model",
67
- label: "Claude 3.5 Haiku",
68
- provider: "Anthropic",
69
- modelId: "anthropic/claude-3.5-haiku",
70
- configured: true,
71
- },
72
- {
73
- id: "claude-4.5-haiku",
74
- type: "model",
75
- label: "Claude 4.5 Haiku",
76
- provider: "Anthropic",
77
- modelId: "anthropic/claude-4.5-haiku",
78
- configured: true,
79
- },
80
- {
81
- id: "claude-3.7-sonnet",
82
- type: "model",
83
- label: "Claude 3.7 Sonnet",
84
- provider: "Anthropic",
85
- modelId: "anthropic/claude-3.7-sonnet",
86
- configured: true,
87
- },
88
- {
89
- id: "claude-sonnet-4.5",
90
- type: "model",
91
- label: "Claude Sonnet 4.5",
92
- provider: "Anthropic",
93
- modelId: "anthropic/claude-sonnet-4.5",
94
- configured: true,
95
- },
96
- {
97
- id: "claude-3.5-sonnet-new",
98
- type: "model",
99
- label: "Claude 3.5 Sonnet (New)",
100
- provider: "Anthropic",
101
- modelId: "anthropic/claude-3-5-sonnet-new",
102
- configured: true,
103
- },
104
- {
105
- id: "claude-3.5-sonnet-old",
106
- type: "model",
107
- label: "Claude 3.5 Sonnet (Old)",
108
- provider: "Anthropic",
109
- modelId: "anthropic/claude-3-5-sonnet-old",
110
- configured: true,
111
- },
112
- ];
113
-
114
- const OPENAI_MODELS: ModelItem[] = [
115
- {
116
- id: "gpt-5-codex",
117
- type: "model",
118
- label: "GPT-5 Codex",
119
- provider: "OpenAI",
120
- modelId: "openai/gpt-5-codex",
121
- configured: true,
122
- },
123
- {
124
- id: "gpt-5.1-codex",
125
- type: "model",
126
- label: "GPT-5.1 Codex",
127
- provider: "OpenAI",
128
- modelId: "openai/gpt-5.1-codex",
129
- configured: true,
130
- },
131
- {
132
- id: "gpt-5.1-codex-max",
133
- type: "model",
134
- label: "GPT-5.1 Codex Max",
135
- provider: "OpenAI",
136
- modelId: "openai/gpt-5.1-codex-max",
137
- configured: true,
138
- },
139
- {
140
- id: "gpt-5.1-codex-mini",
141
- type: "model",
142
- label: "GPT-5.1 Codex Mini",
143
- provider: "OpenAI",
144
- modelId: "openai/gpt-5.1-codex-mini",
145
- configured: true,
146
- },
147
- ];
148
-
149
- const XAI_MODELS: ModelItem[] = [
150
- {
151
- id: "grok-code-fast",
152
- type: "model",
153
- label: "Grok Code Fast",
154
- provider: "xAI",
155
- modelId: "xai/grok-code-fast",
156
- configured: false,
157
- }, // Configured check missing in reference image? No, line is very faint. Assuming not configured or just separator missing.
158
- ];
159
-
160
- export function ModelSelectorScreen() {
161
- const { state, dispatch } = useApp();
162
- const { modelSelector } = state;
163
- const dimensions = useDimensions();
164
-
165
- // Combine items into display list
166
- const allItems = useMemo<ModelItem[]>(() => {
167
- return [
168
- {
169
- id: "header-recent",
170
- type: "header",
171
- label: "Recently used",
172
- provider: "OpenRouter",
173
- }, // Provider column used for right text
174
- ...RECENT_MODELS,
175
- {
176
- id: "header-anthropic",
177
- type: "header",
178
- label: "Anthropic",
179
- configured: true,
180
- },
181
- ...ANTHROPIC_MODELS,
182
- {
183
- id: "header-openai",
184
- type: "header",
185
- label: "OpenAI",
186
- configured: true,
187
- },
188
- ...OPENAI_MODELS,
189
- { id: "header-xai", type: "header", label: "xAI" },
190
- ...XAI_MODELS,
191
- ];
192
- }, []);
193
-
194
- // Filter items
195
- const filteredItems = useMemo(() => {
196
- if (!modelSelector.searchQuery) return allItems;
197
-
198
- // Filter only models
199
- const models = allItems.filter((i) => i.type === "model");
200
- const fuzzyResults = fuzzyFilter(
201
- models,
202
- modelSelector.searchQuery,
203
- (item) => item.label,
204
- );
205
-
206
- // If searching, just show matched models without headers to keep it clean, or keep hierarchy?
207
- // "Switch Model" TUI usually flattens on search.
208
- return fuzzyResults.map(
209
- (r) =>
210
- ({ ...r.item, _matches: r.matches }) as ModelItem & {
211
- _matches?: number[];
212
- },
213
- );
214
- }, [allItems, modelSelector.searchQuery]);
215
-
216
- // Ensure selection is valid (not a header) on mount or search change
217
- useEffect(() => {
218
- // If current selection is a header or out of bounds, find first model
219
- const current = filteredItems[modelSelector.selectedIndex];
220
- if (!current || current.type === "header") {
221
- const firstModelIndex = filteredItems.findIndex(
222
- (i) => i.type === "model",
223
- );
224
- if (
225
- firstModelIndex !== -1 &&
226
- firstModelIndex !== modelSelector.selectedIndex
227
- ) {
228
- dispatch({ type: "MODEL_SELECTOR_SELECT", index: firstModelIndex });
229
- }
230
- }
231
- }, [filteredItems, modelSelector.selectedIndex, dispatch]);
232
-
233
- // Handle keyboard
234
- useKeyboard((event) => {
235
- // Search input handling
236
- if (event.name === "backspace" || event.name === "delete") {
237
- dispatch({
238
- type: "MODEL_SELECTOR_SET_SEARCH",
239
- query: modelSelector.searchQuery.slice(0, -1),
240
- });
241
- return;
242
- }
243
-
244
- // Toggle Task Size
245
- if (event.name === "tab") {
246
- const newSize = modelSelector.taskSize === "large" ? "small" : "large";
247
- dispatch({ type: "MODEL_SELECTOR_SET_TASK_SIZE", size: newSize });
248
- return;
249
- }
250
-
251
- // Navigation
252
- if (event.name === "up" || (event.ctrl && event.name === "p")) {
253
- // Standard navigation
254
- let newIndex = modelSelector.selectedIndex - 1;
255
- // Skip headers while moving up
256
- while (newIndex >= 0 && filteredItems[newIndex]?.type === "header") {
257
- newIndex--;
258
- }
259
- if (newIndex >= 0) {
260
- dispatch({ type: "MODEL_SELECTOR_SELECT", index: newIndex });
261
- }
262
- return;
263
- }
264
-
265
- if (event.name === "down" || (event.ctrl && event.name === "n")) {
266
- let newIndex = modelSelector.selectedIndex + 1;
267
- // Skip headers while moving down
268
- while (
269
- newIndex < filteredItems.length &&
270
- filteredItems[newIndex]?.type === "header"
271
- ) {
272
- newIndex++;
273
- }
274
- if (newIndex < filteredItems.length) {
275
- dispatch({ type: "MODEL_SELECTOR_SELECT", index: newIndex });
276
- }
277
- return;
278
- }
279
-
280
- if (event.name === "enter" || event.name === "return") {
281
- // Just log selection for now or exit
282
- // dispatch({ type: 'NAVIGATE', route: { screen: 'plugins' } });
283
- return;
284
- }
285
-
286
- // Regular text input (single character keys)
287
- if (event.name.length === 1 && !event.ctrl && !event.shift) {
288
- dispatch({
289
- type: "MODEL_SELECTOR_SET_SEARCH",
290
- query: modelSelector.searchQuery + event.name,
291
- });
292
- return;
293
- }
294
- });
295
-
296
- // Render list item
297
- const renderItem = (item: ModelItem, _index: number, isSelected: boolean) => {
298
- if (item.type === "header") {
299
- // Header style: "Name ----------------------------- Status"
300
- const status = item.configured ? "✔ Configured" : "";
301
-
302
- return (
303
- <box margin={0}>
304
- <text fg="gray">{item.label} </text>
305
- <text fg="#888888">⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯</text>
306
- {status && <text fg="green"> {status}</text>}
307
- {item.provider && <text fg="#5c9aff"> {item.provider}</text>}
308
- </box>
309
- );
310
- }
311
-
312
- // Model item
313
- // Matches highlighting
314
- const matches = (item as ModelItem & { _matches?: number[] })._matches;
315
- const labelSegment = matches ? highlightMatches(item.label, matches) : null;
316
-
317
- const Label = () => (
318
- <text fg={isSelected ? "white" : "white"}>
319
- {labelSegment
320
- ? labelSegment.map((seg, i) => (
321
- <text
322
- key={i}
323
- fg={
324
- seg.highlighted
325
- ? isSelected
326
- ? "white"
327
- : "cyan"
328
- : isSelected
329
- ? "white"
330
- : "white"
331
- }
332
- >
333
- {seg.highlighted ? (
334
- <u>
335
- <strong>{seg.text}</strong>
336
- </u>
337
- ) : (
338
- seg.text
339
- )}
340
- </text>
341
- ))
342
- : item.label}
343
- </text>
344
- );
345
-
346
- if (isSelected) {
347
- return (
348
- <box overflow="hidden">
349
- <text bg="magenta" fg="white">
350
- {" "}
351
- </text>
352
- <text bg="magenta" fg="white">
353
- <Label />
354
- </text>
355
- <box flexGrow={1}>
356
- <text bg="magenta"> </text>
357
- </box>
358
- {item.provider && (
359
- <text bg="magenta" fg="white">
360
- {item.provider}{" "}
361
- </text>
362
- )}
363
- </box>
364
- );
365
- } else {
366
- return (
367
- <box overflow="hidden">
368
- <Label />
369
- <box flexGrow={1} />
370
- {item.provider && <text fg="gray">{item.provider}</text>}
371
- </box>
372
- );
373
- }
374
- };
375
-
376
- const footerHints = [
377
- { keys: ["↑", "↓"], label: "choose" },
378
- { keys: ["tab"], label: "toggle type" },
379
- { keys: ["enter"], label: "choose" },
380
- { keys: ["esc"], label: "exit" },
381
- ];
382
- // Available height calculation
383
- // Header: 1 line (title) + 1 line (search) + 1 line (separator) = 3 lines?
384
- // Title line: Switch Model /// (o) Large
385
- // Cursor line: > c
386
- // Separator: handled by list? or explicit?
387
-
388
- const listHeight = Math.max(5, dimensions.contentHeight - 5);
389
-
390
- return (
391
- <box flexDirection="column" height={dimensions.contentHeight}>
392
- {/* Top Bar */}
393
- <box
394
- flexDirection="row"
395
- border
396
- borderStyle="single"
397
- borderColor="#7e57c2"
398
- paddingLeft={1}
399
- paddingRight={1}
400
- >
401
- <box flexDirection="column" flexGrow={1}>
402
- <box flexDirection="row" justifyContent="space-between">
403
- <box>
404
- <text fg="#7e57c2">Switch Model </text>
405
- <text fg={modelSelector.taskSize === "large" ? "white" : "gray"}>
406
- {modelSelector.taskSize === "large" ? "◎" : "○"} Large Task
407
- {" "}
408
- </text>
409
- <text fg={modelSelector.taskSize === "small" ? "white" : "gray"}>
410
- {modelSelector.taskSize === "small" ? "◎" : "○"} Small Task
411
- </text>
412
- </box>
413
- </box>
414
- <box flexDirection="row" marginTop={1}>
415
- <text fg="green">{"> "}</text>
416
- <text>{modelSelector.searchQuery}</text>
417
- <text bg="gray" fg="black">
418
- {" "}
419
- </text>
420
- </box>
421
- </box>
422
- </box>
423
-
424
- {/* Initial selection ensurement */}
425
- {/* (Handled in reducer or manually here if needed to avoid index -1) */}
426
-
427
- <box flexGrow={1} paddingLeft={1} paddingRight={1}>
428
- <ScrollableList
429
- items={filteredItems}
430
- selectedIndex={modelSelector.selectedIndex}
431
- renderItem={renderItem}
432
- maxHeight={listHeight}
433
- getKey={(item) => item.id}
434
- />
435
- </box>
436
-
437
- <box height={1}>
438
- <FooterHints hints={footerHints} />
439
- </box>
440
- </box>
441
- );
442
- }
443
-
444
- export default ModelSelectorScreen;