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