claudeup 4.19.0 → 4.23.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 (182) 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-badge.test.ts +91 -0
  18. package/src/__tests__/marketplace-refresh.test.ts +267 -0
  19. package/src/__tests__/plugin-requires.test.ts +133 -0
  20. package/src/__tests__/plugin-setup.test.ts +7 -0
  21. package/src/__tests__/profile-command.test.ts +116 -0
  22. package/src/__tests__/profile-materializer.test.ts +82 -0
  23. package/src/__tests__/profile-sync.test.ts +136 -0
  24. package/src/__tests__/registry-version-resolution.test.ts +74 -0
  25. package/src/__tests__/resolve-executable.test.ts +42 -0
  26. package/src/__tests__/resolver.test.ts +218 -0
  27. package/src/__tests__/symlink-manager.test.ts +99 -0
  28. package/src/__tests__/toolchain.test.ts +56 -0
  29. package/src/cli/claude.ts +21 -0
  30. package/src/cli/doctor.ts +132 -0
  31. package/src/cli/install.ts +360 -0
  32. package/src/cli/profile.ts +166 -0
  33. package/src/cli/router.ts +107 -0
  34. package/src/cli/update.ts +85 -0
  35. package/src/data/cli-tools.ts +7 -7
  36. package/src/data/gitignore-defaults.ts +4 -0
  37. package/src/data/marketplaces.ts +12 -0
  38. package/src/data/predefined-profiles.ts +3 -4
  39. package/src/main.tsx +11 -154
  40. package/src/prerunner/index.ts +62 -27
  41. package/src/services/claude-cli.ts +19 -22
  42. package/src/services/claude-settings.ts +99 -26
  43. package/src/services/conventions-manager.ts +865 -0
  44. package/src/services/doctor-bins.ts +49 -0
  45. package/src/services/doctor.ts +509 -0
  46. package/src/services/install-plan.ts +107 -0
  47. package/src/services/manifest.ts +166 -0
  48. package/src/services/marketplace-refresh.ts +173 -0
  49. package/src/services/plugin-manager.ts +7 -1
  50. package/src/services/plugin-requires.ts +215 -0
  51. package/src/services/plugin-version-check.ts +9 -4
  52. package/src/services/profile-materializer.ts +61 -0
  53. package/src/services/profile-sync.ts +150 -0
  54. package/src/services/resolver.ts +293 -0
  55. package/src/services/symlink-manager.ts +146 -0
  56. package/src/services/toolchain.ts +97 -0
  57. package/src/services/version-check.ts +10 -11
  58. package/src/types/index.ts +138 -31
  59. package/src/ui/App.tsx +0 -4
  60. package/src/ui/adapters/pluginsAdapter.ts +13 -0
  61. package/src/ui/screens/CliToolsScreen.tsx +11 -3
  62. package/src/ui/screens/index.ts +0 -1
  63. package/src/ui/state/reducer.ts +0 -101
  64. package/src/ui/state/types.ts +0 -35
  65. package/src/utils/command-utils.ts +23 -0
  66. package/src/utils/file-locking.ts +144 -0
  67. package/src/data/alias-flags.js +0 -205
  68. package/src/data/cli-tools.js +0 -123
  69. package/src/data/gitignore-defaults.js +0 -24
  70. package/src/data/gitignore-reasons.js +0 -97
  71. package/src/data/gitignore-templates.js +0 -21
  72. package/src/data/marketplaces.js +0 -138
  73. package/src/data/mcp-servers.js +0 -509
  74. package/src/data/predefined-profiles.js +0 -248
  75. package/src/data/settings-catalog.js +0 -625
  76. package/src/data/skill-repos.js +0 -160
  77. package/src/data/statuslines.js +0 -159
  78. package/src/data/statuslines.ts +0 -188
  79. package/src/index.js +0 -4
  80. package/src/index.ts +0 -5
  81. package/src/main.js +0 -170
  82. package/src/prerunner/index.js +0 -252
  83. package/src/services/alias-settings.js +0 -51
  84. package/src/services/alias-shell-writer.js +0 -1018
  85. package/src/services/alias-store.js +0 -129
  86. package/src/services/claude-cli.js +0 -189
  87. package/src/services/claude-runner.js +0 -28
  88. package/src/services/claude-settings.js +0 -1223
  89. package/src/services/gitignore-detector.js +0 -155
  90. package/src/services/gitignore-fixer.js +0 -231
  91. package/src/services/gitignore-prerun.js +0 -46
  92. package/src/services/gitignore-resolver.js +0 -143
  93. package/src/services/gitignore-service.js +0 -117
  94. package/src/services/local-marketplace.js +0 -339
  95. package/src/services/marketplace-fetcher.js +0 -96
  96. package/src/services/marketplace-sync.js +0 -94
  97. package/src/services/mcp-registry.js +0 -87
  98. package/src/services/plugin-manager.js +0 -473
  99. package/src/services/plugin-mcp-config.js +0 -177
  100. package/src/services/plugin-setup.js +0 -499
  101. package/src/services/plugin-version-check.js +0 -262
  102. package/src/services/profiles.js +0 -161
  103. package/src/services/settings-manager.js +0 -243
  104. package/src/services/skills-manager.js +0 -393
  105. package/src/services/skillsmp-client.js +0 -87
  106. package/src/services/update-cache.js +0 -52
  107. package/src/services/version-check.js +0 -99
  108. package/src/types/gitignore.js +0 -6
  109. package/src/types/index.js +0 -1
  110. package/src/ui/App.js +0 -355
  111. package/src/ui/adapters/pluginsAdapter.js +0 -139
  112. package/src/ui/adapters/settingsAdapter.js +0 -111
  113. package/src/ui/adapters/skillsAdapter.js +0 -154
  114. package/src/ui/components/CategoryHeader.js +0 -9
  115. package/src/ui/components/EmptyFilterState.js +0 -4
  116. package/src/ui/components/FlagDetailEditor.js +0 -0
  117. package/src/ui/components/ScopeIndicator.js +0 -30
  118. package/src/ui/components/ScrollableList.js +0 -36
  119. package/src/ui/components/SearchInput.js +0 -19
  120. package/src/ui/components/StyledText.js +0 -39
  121. package/src/ui/components/TabBar.js +0 -19
  122. package/src/ui/components/layout/FooterHints.js +0 -29
  123. package/src/ui/components/layout/Panel.js +0 -6
  124. package/src/ui/components/layout/ProgressBar.js +0 -14
  125. package/src/ui/components/layout/ScopeTabs.js +0 -6
  126. package/src/ui/components/layout/ScreenLayout.js +0 -16
  127. package/src/ui/components/layout/index.js +0 -5
  128. package/src/ui/components/modals/ConfirmModal.js +0 -14
  129. package/src/ui/components/modals/InputModal.js +0 -5
  130. package/src/ui/components/modals/LoadingModal.js +0 -17
  131. package/src/ui/components/modals/MessageModal.js +0 -16
  132. package/src/ui/components/modals/ModalContainer.js +0 -137
  133. package/src/ui/components/modals/SelectModal.js +0 -18
  134. package/src/ui/components/modals/VersionMismatchModal.js +0 -36
  135. package/src/ui/components/modals/index.js +0 -6
  136. package/src/ui/components/primitives/ActionHints.js +0 -13
  137. package/src/ui/components/primitives/DetailSection.js +0 -7
  138. package/src/ui/components/primitives/KeyValueLine.js +0 -8
  139. package/src/ui/components/primitives/ListCategoryRow.js +0 -8
  140. package/src/ui/components/primitives/MetaText.js +0 -8
  141. package/src/ui/components/primitives/ScopeDetail.js +0 -32
  142. package/src/ui/components/primitives/ScopeSquares.js +0 -11
  143. package/src/ui/components/primitives/SelectableRow.js +0 -5
  144. package/src/ui/components/primitives/index.js +0 -8
  145. package/src/ui/hooks/index.js +0 -4
  146. package/src/ui/hooks/useAsyncData.js +0 -77
  147. package/src/ui/hooks/useGitignoreModal.js +0 -74
  148. package/src/ui/hooks/useKeyboard.js +0 -13
  149. package/src/ui/hooks/useKeyboardHandler.js +0 -39
  150. package/src/ui/hooks/useMismatchModal.js +0 -77
  151. package/src/ui/registry.js +0 -1
  152. package/src/ui/renderers/cliToolRenderers.js +0 -54
  153. package/src/ui/renderers/gitignoreRenderers.js +0 -46
  154. package/src/ui/renderers/mcpRenderers.js +0 -26
  155. package/src/ui/renderers/pluginRenderers.js +0 -124
  156. package/src/ui/renderers/profileRenderers.js +0 -177
  157. package/src/ui/renderers/settingsRenderers.js +0 -73
  158. package/src/ui/renderers/skillRenderers.js +0 -138
  159. package/src/ui/screens/AliasScreen.js +0 -1111
  160. package/src/ui/screens/CliToolsScreen.js +0 -338
  161. package/src/ui/screens/EnvVarsScreen.js +0 -152
  162. package/src/ui/screens/GitignoreScreen.js +0 -328
  163. package/src/ui/screens/McpRegistryScreen.js +0 -238
  164. package/src/ui/screens/McpScreen.js +0 -176
  165. package/src/ui/screens/ModelSelectorScreen.js +0 -296
  166. package/src/ui/screens/ModelSelectorScreen.tsx +0 -444
  167. package/src/ui/screens/PluginsScreen.js +0 -769
  168. package/src/ui/screens/ProfilesScreen.js +0 -298
  169. package/src/ui/screens/SkillsScreen.js +0 -549
  170. package/src/ui/screens/StatusLineScreen.js +0 -206
  171. package/src/ui/screens/StatusLineScreen.tsx +0 -416
  172. package/src/ui/screens/index.js +0 -10
  173. package/src/ui/state/AnimationContext.js +0 -34
  174. package/src/ui/state/AppContext.js +0 -162
  175. package/src/ui/state/DimensionsContext.js +0 -71
  176. package/src/ui/state/reducer.js +0 -547
  177. package/src/ui/state/types.js +0 -1
  178. package/src/ui/theme.js +0 -47
  179. package/src/utils/clipboard.js +0 -56
  180. package/src/utils/command-utils.js +0 -19
  181. package/src/utils/fuzzy-search.js +0 -101
  182. 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;