dexto 1.5.8 → 1.6.1

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 (224) hide show
  1. package/README.md +3 -3
  2. package/dist/agents/agent-template.yml +2 -2
  3. package/dist/agents/coding-agent/README.md +10 -10
  4. package/dist/agents/coding-agent/coding-agent.yml +84 -83
  5. package/dist/agents/default-agent.yml +32 -47
  6. package/dist/agents/explore-agent/explore-agent.yml +3 -6
  7. package/dist/agents/image-editor-agent/image-editor-agent.yml +1 -1
  8. package/dist/agents/nano-banana-agent/nano-banana-agent.yml +1 -1
  9. package/dist/agents/podcast-agent/podcast-agent.yml +1 -1
  10. package/dist/agents/product-name-researcher/product-name-researcher.yml +1 -1
  11. package/dist/agents/sora-video-agent/sora-video-agent.yml +4 -6
  12. package/dist/agents/triage-demo/triage-agent.yml +1 -1
  13. package/dist/analytics/events.d.ts +1 -1
  14. package/dist/analytics/events.d.ts.map +1 -1
  15. package/dist/api/mcp/tool-aggregation-handler.d.ts +2 -2
  16. package/dist/api/server-hono.d.ts +2 -2
  17. package/dist/api/server-hono.d.ts.map +1 -1
  18. package/dist/api/server-hono.js +37 -60
  19. package/dist/cli/approval/cli-approval-handler.d.ts +10 -3
  20. package/dist/cli/approval/cli-approval-handler.d.ts.map +1 -1
  21. package/dist/cli/approval/cli-approval-handler.js +1 -1
  22. package/dist/cli/assets/sounds/SOURCES.md +35 -0
  23. package/dist/cli/assets/sounds/boot.wav +0 -0
  24. package/dist/cli/assets/sounds/chime.wav +0 -0
  25. package/dist/cli/assets/sounds/coin.wav +0 -0
  26. package/dist/cli/assets/sounds/confirm.wav +0 -0
  27. package/dist/cli/assets/sounds/levelup.wav +0 -0
  28. package/dist/cli/assets/sounds/ping.wav +0 -0
  29. package/dist/cli/assets/sounds/powerup.wav +0 -0
  30. package/dist/cli/assets/sounds/startup.wav +0 -0
  31. package/dist/cli/assets/sounds/success.wav +0 -0
  32. package/dist/cli/assets/sounds/treasure.wav +0 -0
  33. package/dist/cli/assets/sounds/win.wav +0 -0
  34. package/dist/cli/commands/create-app.d.ts +1 -11
  35. package/dist/cli/commands/create-app.d.ts.map +1 -1
  36. package/dist/cli/commands/create-app.js +21 -545
  37. package/dist/cli/commands/create-image.d.ts.map +1 -1
  38. package/dist/cli/commands/create-image.js +54 -53
  39. package/dist/cli/commands/image.d.ts +52 -0
  40. package/dist/cli/commands/image.d.ts.map +1 -0
  41. package/dist/cli/commands/image.js +118 -0
  42. package/dist/cli/commands/index.d.ts +2 -1
  43. package/dist/cli/commands/index.d.ts.map +1 -1
  44. package/dist/cli/commands/index.js +3 -1
  45. package/dist/cli/commands/init-app.d.ts +4 -8
  46. package/dist/cli/commands/init-app.d.ts.map +1 -1
  47. package/dist/cli/commands/init-app.js +37 -161
  48. package/dist/cli/commands/interactive-commands/command-parser.d.ts +2 -0
  49. package/dist/cli/commands/interactive-commands/command-parser.d.ts.map +1 -1
  50. package/dist/cli/commands/interactive-commands/commands.d.ts +1 -1
  51. package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
  52. package/dist/cli/commands/interactive-commands/commands.js +2 -2
  53. package/dist/cli/commands/interactive-commands/exit-handler.d.ts +12 -0
  54. package/dist/cli/commands/interactive-commands/exit-handler.d.ts.map +1 -0
  55. package/dist/cli/commands/interactive-commands/exit-handler.js +20 -0
  56. package/dist/cli/commands/interactive-commands/exit-stats.d.ts +24 -0
  57. package/dist/cli/commands/interactive-commands/exit-stats.d.ts.map +1 -0
  58. package/dist/cli/commands/interactive-commands/exit-stats.js +17 -0
  59. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  60. package/dist/cli/commands/interactive-commands/general-commands.js +55 -5
  61. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
  62. package/dist/cli/commands/interactive-commands/prompt-commands.js +14 -74
  63. package/dist/cli/commands/interactive-commands/session/index.d.ts +2 -1
  64. package/dist/cli/commands/interactive-commands/session/index.d.ts.map +1 -1
  65. package/dist/cli/commands/interactive-commands/session/index.js +2 -1
  66. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts +2 -2
  67. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts.map +1 -1
  68. package/dist/cli/commands/interactive-commands/session/session-commands.js +2 -4
  69. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts +1 -13
  70. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
  71. package/dist/cli/commands/interactive-commands/system/system-commands.js +52 -83
  72. package/dist/cli/commands/plugin.d.ts +4 -4
  73. package/dist/cli/commands/sync-agents.d.ts +2 -12
  74. package/dist/cli/commands/sync-agents.d.ts.map +1 -1
  75. package/dist/cli/commands/sync-agents.js +2 -50
  76. package/dist/cli/ink-cli/InkCLIRefactored.d.ts +7 -1
  77. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  78. package/dist/cli/ink-cli/InkCLIRefactored.js +138 -27
  79. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts +2 -2
  80. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  81. package/dist/cli/ink-cli/components/ApprovalPrompt.js +85 -30
  82. package/dist/cli/ink-cli/components/BackgroundTasksPanel.js +1 -1
  83. package/dist/cli/ink-cli/components/ElicitationForm.d.ts +5 -3
  84. package/dist/cli/ink-cli/components/ElicitationForm.d.ts.map +1 -1
  85. package/dist/cli/ink-cli/components/ElicitationForm.js +414 -180
  86. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  87. package/dist/cli/ink-cli/components/Footer.js +1 -2
  88. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  89. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +20 -11
  90. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
  91. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +47 -67
  92. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  93. package/dist/cli/ink-cli/components/StatusBar.js +20 -10
  94. package/dist/cli/ink-cli/components/TodoPanel.js +1 -1
  95. package/dist/cli/ink-cli/components/base/BaseSelector.d.ts +2 -1
  96. package/dist/cli/ink-cli/components/base/BaseSelector.d.ts.map +1 -1
  97. package/dist/cli/ink-cli/components/base/BaseSelector.js +37 -27
  98. package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
  99. package/dist/cli/ink-cli/components/chat/Header.js +1 -1
  100. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  101. package/dist/cli/ink-cli/components/chat/MessageItem.js +3 -1
  102. package/dist/cli/ink-cli/components/chat/ToolIcon.d.ts.map +1 -1
  103. package/dist/cli/ink-cli/components/chat/ToolIcon.js +5 -15
  104. package/dist/cli/ink-cli/components/chat/styled-boxes/ConfigBox.js +1 -1
  105. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.d.ts.map +1 -1
  106. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
  107. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts +3 -1
  108. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  109. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +5 -3
  110. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts +3 -1
  111. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  112. package/dist/cli/ink-cli/components/modes/StaticCLI.js +10 -3
  113. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.d.ts +13 -0
  114. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.d.ts.map +1 -0
  115. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.js +60 -0
  116. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +1 -1
  117. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  118. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +213 -100
  119. package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
  120. package/dist/cli/ink-cli/components/overlays/PromptList.js +12 -16
  121. package/dist/cli/ink-cli/components/overlays/SoundsSelector.d.ts +21 -0
  122. package/dist/cli/ink-cli/components/overlays/SoundsSelector.d.ts.map +1 -0
  123. package/dist/cli/ink-cli/components/overlays/SoundsSelector.js +566 -0
  124. package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts +1 -1
  125. package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts.map +1 -1
  126. package/dist/cli/ink-cli/components/overlays/ToolBrowser.js +100 -45
  127. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.d.ts.map +1 -1
  128. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.js +8 -13
  129. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +3 -3
  130. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
  131. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +6 -5
  132. package/dist/cli/ink-cli/components/renderers/FileRenderer.d.ts +3 -1
  133. package/dist/cli/ink-cli/components/renderers/FileRenderer.d.ts.map +1 -1
  134. package/dist/cli/ink-cli/components/renderers/FileRenderer.js +18 -7
  135. package/dist/cli/ink-cli/components/renderers/ShellRenderer.d.ts.map +1 -1
  136. package/dist/cli/ink-cli/components/renderers/ShellRenderer.js +7 -17
  137. package/dist/cli/ink-cli/components/renderers/index.d.ts.map +1 -1
  138. package/dist/cli/ink-cli/components/renderers/index.js +1 -1
  139. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.d.ts +7 -0
  140. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.d.ts.map +1 -0
  141. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.js +8 -0
  142. package/dist/cli/ink-cli/components/shared/HintBar.d.ts +6 -0
  143. package/dist/cli/ink-cli/components/shared/HintBar.d.ts.map +1 -0
  144. package/dist/cli/ink-cli/components/shared/HintBar.js +6 -0
  145. package/dist/cli/ink-cli/constants/spinnerFrames.d.ts +2 -0
  146. package/dist/cli/ink-cli/constants/spinnerFrames.d.ts.map +1 -0
  147. package/dist/cli/ink-cli/constants/spinnerFrames.js +1 -0
  148. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  149. package/dist/cli/ink-cli/constants/tips.js +2 -1
  150. package/dist/cli/ink-cli/containers/InputContainer.d.ts +4 -0
  151. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  152. package/dist/cli/ink-cli/containers/InputContainer.js +47 -21
  153. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts +2 -0
  154. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  155. package/dist/cli/ink-cli/containers/OverlayContainer.js +101 -40
  156. package/dist/cli/ink-cli/hooks/useAgentEvents.d.ts.map +1 -1
  157. package/dist/cli/ink-cli/hooks/useAgentEvents.js +15 -16
  158. package/dist/cli/ink-cli/hooks/useAnimationTick.d.ts +11 -0
  159. package/dist/cli/ink-cli/hooks/useAnimationTick.d.ts.map +1 -0
  160. package/dist/cli/ink-cli/hooks/useAnimationTick.js +54 -0
  161. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  162. package/dist/cli/ink-cli/hooks/useCLIState.js +1 -0
  163. package/dist/cli/ink-cli/hooks/useTokenCounter.d.ts.map +1 -1
  164. package/dist/cli/ink-cli/hooks/useTokenCounter.js +7 -4
  165. package/dist/cli/ink-cli/services/CommandService.d.ts +1 -1
  166. package/dist/cli/ink-cli/services/CommandService.d.ts.map +1 -1
  167. package/dist/cli/ink-cli/services/CommandService.js +2 -2
  168. package/dist/cli/ink-cli/services/processStream.d.ts +2 -2
  169. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  170. package/dist/cli/ink-cli/services/processStream.js +27 -19
  171. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  172. package/dist/cli/ink-cli/state/initialState.js +1 -0
  173. package/dist/cli/ink-cli/state/types.d.ts +15 -3
  174. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  175. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  176. package/dist/cli/ink-cli/utils/commandOverlays.js +1 -0
  177. package/dist/cli/ink-cli/utils/elicitationSchema.d.ts +11 -0
  178. package/dist/cli/ink-cli/utils/elicitationSchema.d.ts.map +1 -0
  179. package/dist/cli/ink-cli/utils/elicitationSchema.js +80 -0
  180. package/dist/cli/ink-cli/utils/index.d.ts +1 -1
  181. package/dist/cli/ink-cli/utils/index.d.ts.map +1 -1
  182. package/dist/cli/ink-cli/utils/index.js +1 -1
  183. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +10 -19
  184. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  185. package/dist/cli/ink-cli/utils/messageFormatting.js +43 -262
  186. package/dist/cli/ink-cli/utils/overlayPresentation.d.ts +19 -0
  187. package/dist/cli/ink-cli/utils/overlayPresentation.d.ts.map +1 -0
  188. package/dist/cli/ink-cli/utils/overlayPresentation.js +33 -0
  189. package/dist/cli/ink-cli/utils/overlaySizing.d.ts +19 -0
  190. package/dist/cli/ink-cli/utils/overlaySizing.d.ts.map +1 -0
  191. package/dist/cli/ink-cli/utils/overlaySizing.js +11 -0
  192. package/dist/cli/ink-cli/utils/soundNotification.d.ts +19 -13
  193. package/dist/cli/ink-cli/utils/soundNotification.d.ts.map +1 -1
  194. package/dist/cli/ink-cli/utils/soundNotification.js +120 -97
  195. package/dist/cli/ink-cli/utils/toolUtils.d.ts.map +1 -1
  196. package/dist/cli/ink-cli/utils/toolUtils.js +2 -9
  197. package/dist/cli/utils/config-validation.d.ts +11 -11
  198. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  199. package/dist/cli/utils/config-validation.js +56 -290
  200. package/dist/cli/utils/image-store.d.ts +16 -0
  201. package/dist/cli/utils/image-store.d.ts.map +1 -0
  202. package/dist/cli/utils/image-store.js +289 -0
  203. package/dist/cli/utils/scaffolding-utils.d.ts +5 -0
  204. package/dist/cli/utils/scaffolding-utils.d.ts.map +1 -1
  205. package/dist/cli/utils/scaffolding-utils.js +46 -4
  206. package/dist/cli/utils/template-engine.d.ts +28 -16
  207. package/dist/cli/utils/template-engine.d.ts.map +1 -1
  208. package/dist/cli/utils/template-engine.js +339 -479
  209. package/dist/config/cli-overrides.d.ts +4 -3
  210. package/dist/config/cli-overrides.d.ts.map +1 -1
  211. package/dist/config/cli-overrides.js +7 -9
  212. package/dist/index-main.d.ts +2 -0
  213. package/dist/index-main.d.ts.map +1 -0
  214. package/dist/index-main.js +1554 -0
  215. package/dist/index.js +2 -1589
  216. package/dist/utils/session-logger-factory.d.ts +3 -0
  217. package/dist/utils/session-logger-factory.d.ts.map +1 -0
  218. package/dist/utils/session-logger-factory.js +34 -0
  219. package/dist/webui/assets/{index-Cz2z7NQ8.js → index-CKhumsZA.js} +231 -231
  220. package/dist/webui/index.html +1 -1
  221. package/package.json +11 -8
  222. package/dist/cli/cli-subscriber.d.ts +0 -45
  223. package/dist/cli/cli-subscriber.d.ts.map +0 -1
  224. package/dist/cli/cli-subscriber.js +0 -204
@@ -7,13 +7,38 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
7
7
  */
8
8
  import { useState, useEffect, forwardRef, useRef, useImperativeHandle, useMemo, useCallback, } from 'react';
9
9
  import { Box, Text } from 'ink';
10
+ import { useTerminalSize } from '../../hooks/useTerminalSize.js';
10
11
  import { listOllamaModels, DEFAULT_OLLAMA_URL, getLocalModelById, isReasoningCapableModel, } from '@dexto/core';
11
12
  import { loadCustomModels, deleteCustomModel, getAllInstalledModels, loadGlobalPreferences, isDextoAuthEnabled, } from '@dexto/agent-management';
12
13
  import { getLLMProviderDisplayName } from '../../utils/llm-provider-display.js';
14
+ import { getMaxVisibleItemsForTerminalRows } from '../../utils/overlaySizing.js';
15
+ import { HintBar } from '../shared/HintBar.js';
13
16
  function isAddCustomOption(item) {
14
17
  return 'type' in item && item.type === 'add-custom';
15
18
  }
16
- const MAX_VISIBLE_ITEMS = 10;
19
+ function asModelOption(item) {
20
+ return item;
21
+ }
22
+ function getRowPrefix({ isSelected, isDefault, isCurrent, isCustom, }) {
23
+ return `${isSelected ? '›' : ' '} ${isDefault ? '✓' : ' '} ${isCurrent ? '●' : ' '} ${isCustom ? '★' : ' '}`;
24
+ }
25
+ function computeNextSelection(currentIndex, itemsLength, viewportItems) {
26
+ const nextIndex = currentIndex;
27
+ let nextOffset = 0;
28
+ const modelsLength = Math.max(0, itemsLength - 1);
29
+ if (nextIndex > 0) {
30
+ const modelIndex = nextIndex - 1;
31
+ if (modelIndex < nextOffset) {
32
+ nextOffset = modelIndex;
33
+ }
34
+ else if (modelIndex >= nextOffset + viewportItems) {
35
+ nextOffset = Math.max(0, modelIndex - viewportItems + 1);
36
+ }
37
+ }
38
+ const maxOffset = Math.max(0, modelsLength - viewportItems);
39
+ nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
40
+ return { index: nextIndex, offset: nextOffset };
41
+ }
17
42
  // Reasoning effort options - defined at module scope to avoid recreation on each render
18
43
  const REASONING_EFFORT_OPTIONS = [
19
44
  {
@@ -36,23 +61,33 @@ const REASONING_EFFORT_OPTIONS = [
36
61
  * Model selector with search and custom model support
37
62
  */
38
63
  const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectModel, onSetDefaultModel, onClose, onAddCustomModel, onEditCustomModel, agent, }, ref) {
64
+ const { rows: terminalRows } = useTerminalSize();
65
+ const maxVisibleItems = useMemo(() => {
66
+ return getMaxVisibleItemsForTerminalRows({
67
+ rows: terminalRows,
68
+ hardCap: 8,
69
+ reservedRows: 14,
70
+ });
71
+ }, [terminalRows]);
39
72
  const [models, setModels] = useState([]);
40
73
  const [customModels, setCustomModels] = useState([]);
41
74
  const [isLoading, setIsLoading] = useState(false);
42
- const [selectedIndex, setSelectedIndex] = useState(0);
75
+ const [selection, setSelection] = useState({ index: 0, offset: 0 });
43
76
  const [searchQuery, setSearchQuery] = useState('');
44
- const [scrollOffset, setScrollOffset] = useState(0);
45
77
  const [customModelAction, setCustomModelAction] = useState(null);
46
78
  const [pendingDeleteConfirm, setPendingDeleteConfirm] = useState(false);
47
- const selectedIndexRef = useRef(selectedIndex);
79
+ const selectedIndexRef = useRef(0);
48
80
  const deleteTimeoutRef = useRef(null);
81
+ const maxVisibleItemsRef = useRef(maxVisibleItems);
49
82
  // Reasoning effort sub-step state
50
83
  const [pendingReasoningModel, setPendingReasoningModel] = useState(null);
51
84
  const [reasoningEffortIndex, setReasoningEffortIndex] = useState(0); // Default to 'Auto' (index 0)
52
85
  const [isSettingDefault, setIsSettingDefault] = useState(false); // Track if setting as default vs normal selection
53
86
  const [refreshVersion, setRefreshVersion] = useState(0);
54
87
  // Keep ref in sync
55
- selectedIndexRef.current = selectedIndex;
88
+ selectedIndexRef.current = selection.index;
89
+ maxVisibleItemsRef.current = maxVisibleItems;
90
+ const modelsViewportItems = Math.max(1, maxVisibleItems - 1);
56
91
  // Clear delete confirmation timeout on unmount
57
92
  useEffect(() => {
58
93
  return () => {
@@ -67,9 +102,10 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
67
102
  return;
68
103
  let cancelled = false;
69
104
  setIsLoading(true);
105
+ setModels([]);
106
+ setCustomModels([]);
70
107
  setSearchQuery('');
71
- setSelectedIndex(0);
72
- setScrollOffset(0);
108
+ setSelection({ index: 0, offset: 0 });
73
109
  setCustomModelAction(null);
74
110
  setPendingDeleteConfirm(false);
75
111
  setPendingReasoningModel(null);
@@ -237,7 +273,13 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
237
273
  // Set initial selection to current model (offset by 1 for "Add custom" option)
238
274
  const currentIndex = modelList.findIndex((m) => m.isCurrent);
239
275
  if (currentIndex >= 0) {
240
- setSelectedIndex(currentIndex + 1); // +1 for "Add custom" at top
276
+ const nextIndex = currentIndex + 1; // +1 for "Add custom" at top
277
+ const nextMaxVisibleItems = maxVisibleItemsRef.current;
278
+ const nextModelsViewportItems = Math.max(1, nextMaxVisibleItems - 1);
279
+ const maxOffset = Math.max(0, modelList.length - nextModelsViewportItems);
280
+ const nextOffset = Math.min(maxOffset, Math.max(0, currentIndex - nextModelsViewportItems + 1));
281
+ selectedIndexRef.current = nextIndex;
282
+ setSelection({ index: nextIndex, offset: nextOffset });
241
283
  }
242
284
  }
243
285
  }
@@ -260,31 +302,41 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
260
302
  if (!searchQuery.trim()) {
261
303
  return [addCustomOption, ...models];
262
304
  }
263
- const query = searchQuery.toLowerCase();
305
+ const query = searchQuery.toLowerCase().replace(/[\s-]+/g, '');
264
306
  const filtered = models.filter((model) => {
265
- const name = model.name.toLowerCase();
266
- const displayName = (model.displayName || '').toLowerCase();
267
- const provider = model.provider.toLowerCase();
307
+ const name = model.name.toLowerCase().replace(/[\s-]+/g, '');
308
+ const displayName = (model.displayName || '').toLowerCase().replace(/[\s-]+/g, '');
309
+ const provider = model.provider.toLowerCase().replace(/[\s-]+/g, '');
268
310
  return name.includes(query) || displayName.includes(query) || provider.includes(query);
269
311
  });
270
312
  // Always show "Add custom" when searching (user might want to add what they're searching for)
271
313
  return [addCustomOption, ...filtered];
272
314
  }, [models, searchQuery]);
273
- // Adjust selected index when filter changes
274
- useEffect(() => {
275
- if (selectedIndex >= filteredItems.length) {
276
- setSelectedIndex(Math.max(0, filteredItems.length - 1));
277
- }
278
- }, [filteredItems.length, selectedIndex]);
279
- // Calculate scroll offset
315
+ // Keep selection valid and visible when filtering or terminal height changes.
280
316
  useEffect(() => {
281
- if (selectedIndex < scrollOffset) {
282
- setScrollOffset(selectedIndex);
283
- }
284
- else if (selectedIndex >= scrollOffset + MAX_VISIBLE_ITEMS) {
285
- setScrollOffset(selectedIndex - MAX_VISIBLE_ITEMS + 1);
286
- }
287
- }, [selectedIndex, scrollOffset]);
317
+ setSelection((prev) => {
318
+ const maxIndex = Math.max(0, filteredItems.length - 1);
319
+ const nextIndex = Math.min(prev.index, maxIndex);
320
+ let nextOffset = prev.offset;
321
+ const nextModelsLength = Math.max(0, filteredItems.length - 1);
322
+ if (nextIndex > 0) {
323
+ const modelIndex = nextIndex - 1;
324
+ if (modelIndex < nextOffset) {
325
+ nextOffset = modelIndex;
326
+ }
327
+ else if (modelIndex >= nextOffset + modelsViewportItems) {
328
+ nextOffset = Math.max(0, modelIndex - modelsViewportItems + 1);
329
+ }
330
+ }
331
+ const maxOffset = Math.max(0, nextModelsLength - modelsViewportItems);
332
+ nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
333
+ if (nextIndex === prev.index && nextOffset === prev.offset) {
334
+ return prev;
335
+ }
336
+ selectedIndexRef.current = nextIndex;
337
+ return { index: nextIndex, offset: nextOffset };
338
+ });
339
+ }, [filteredItems.length, modelsViewportItems]);
288
340
  // Handle delete custom model
289
341
  const handleDeleteCustomModel = useCallback(async (model) => {
290
342
  if (!model.isCustom)
@@ -315,6 +367,13 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
315
367
  handleInput: (input, key) => {
316
368
  if (!isVisible)
317
369
  return false;
370
+ // While loading, allow closing but ignore all other input.
371
+ if (isLoading) {
372
+ if (key.escape) {
373
+ onClose();
374
+ }
375
+ return true;
376
+ }
318
377
  // Handle reasoning effort sub-step
319
378
  if (pendingReasoningModel) {
320
379
  if (key.escape) {
@@ -391,41 +450,10 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
391
450
  setPendingDeleteConfirm(false);
392
451
  return true;
393
452
  }
394
- const actionItem = currentItem;
395
- // Check if reasoning-capable, show reasoning effort selection
396
- if (isReasoningCapableModel(actionItem.name)) {
397
- setPendingReasoningModel(actionItem);
398
- setIsSettingDefault(true);
399
- setReasoningEffortIndex(0); // Default to 'Auto'
400
- return true;
401
- }
402
- clearActionState();
403
- void (async () => {
404
- await onSetDefaultModel(actionItem.provider, actionItem.name, actionItem.displayName, actionItem.baseURL, actionItem.reasoningEffort);
405
- setRefreshVersion((prev) => prev + 1);
406
- onClose(); // Close overlay after setting default
407
- })();
408
453
  return true;
409
454
  }
410
455
  if (customModelAction === 'delete') {
411
- if (pendingDeleteConfirm) {
412
- if (deleteTimeoutRef.current) {
413
- clearTimeout(deleteTimeoutRef.current);
414
- deleteTimeoutRef.current = null;
415
- }
416
- clearActionState();
417
- void handleDeleteCustomModel(currentItem);
418
- }
419
- else {
420
- setPendingDeleteConfirm(true);
421
- if (deleteTimeoutRef.current) {
422
- clearTimeout(deleteTimeoutRef.current);
423
- }
424
- deleteTimeoutRef.current = setTimeout(() => {
425
- setPendingDeleteConfirm(false);
426
- deleteTimeoutRef.current = null;
427
- }, 3000);
428
- }
456
+ // Use Enter for delete confirmation/execution to avoid accidental deletes.
429
457
  return true;
430
458
  }
431
459
  }
@@ -469,8 +497,8 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
469
497
  // Regular character - add to search
470
498
  if (input.length === 1 && input.charCodeAt(0) >= 32) {
471
499
  setSearchQuery((prev) => prev + input);
472
- setSelectedIndex(0);
473
- setScrollOffset(0);
500
+ selectedIndexRef.current = 0;
501
+ setSelection({ index: 0, offset: 0 });
474
502
  return true;
475
503
  }
476
504
  }
@@ -487,8 +515,23 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
487
515
  clearActionState();
488
516
  }
489
517
  const nextIndex = (selectedIndexRef.current - 1 + itemsLength) % itemsLength;
490
- setSelectedIndex(nextIndex);
491
518
  selectedIndexRef.current = nextIndex;
519
+ setSelection((prev) => {
520
+ let nextOffset = prev.offset;
521
+ const nextModelsLength = Math.max(0, itemsLength - 1);
522
+ if (nextIndex > 0) {
523
+ const modelIndex = nextIndex - 1;
524
+ if (modelIndex < prev.offset) {
525
+ nextOffset = modelIndex;
526
+ }
527
+ else if (modelIndex >= prev.offset + modelsViewportItems) {
528
+ nextOffset = Math.max(0, modelIndex - modelsViewportItems + 1);
529
+ }
530
+ }
531
+ const maxOffset = Math.max(0, nextModelsLength - modelsViewportItems);
532
+ nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
533
+ return { index: nextIndex, offset: nextOffset };
534
+ });
492
535
  return true;
493
536
  }
494
537
  if (key.downArrow) {
@@ -497,8 +540,23 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
497
540
  clearActionState();
498
541
  }
499
542
  const nextIndex = (selectedIndexRef.current + 1) % itemsLength;
500
- setSelectedIndex(nextIndex);
501
543
  selectedIndexRef.current = nextIndex;
544
+ setSelection((prev) => {
545
+ let nextOffset = prev.offset;
546
+ const nextModelsLength = Math.max(0, itemsLength - 1);
547
+ if (nextIndex > 0) {
548
+ const modelIndex = nextIndex - 1;
549
+ if (modelIndex < prev.offset) {
550
+ nextOffset = modelIndex;
551
+ }
552
+ else if (modelIndex >= prev.offset + modelsViewportItems) {
553
+ nextOffset = Math.max(0, modelIndex - modelsViewportItems + 1);
554
+ }
555
+ }
556
+ const maxOffset = Math.max(0, nextModelsLength - modelsViewportItems);
557
+ nextOffset = Math.min(maxOffset, Math.max(0, nextOffset));
558
+ return { index: nextIndex, offset: nextOffset };
559
+ });
502
560
  return true;
503
561
  }
504
562
  if (key.return && itemsLength > 0) {
@@ -568,7 +626,10 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
568
626
  },
569
627
  }), [
570
628
  isVisible,
629
+ isLoading,
571
630
  filteredItems,
631
+ maxVisibleItems,
632
+ modelsViewportItems,
572
633
  onClose,
573
634
  onSelectModel,
574
635
  onSetDefaultModel,
@@ -584,47 +645,99 @@ const ModelSelector = forwardRef(function ModelSelector({ isVisible, onSelectMod
584
645
  ]);
585
646
  if (!isVisible)
586
647
  return null;
587
- if (isLoading) {
588
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "Loading models..." }) }));
589
- }
590
648
  // Reasoning effort sub-step UI
591
649
  if (pendingReasoningModel) {
592
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "cyan", bold: true, children: ["Configure Reasoning Effort", isSettingDefault && _jsx(Text, { color: "gray", children: " (Setting as Default)" })] }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "gray", children: ["for ", pendingReasoningModel.displayName || pendingReasoningModel.name] }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc back" }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), REASONING_EFFORT_OPTIONS.map((option, index) => {
593
- const isSelected = index === reasoningEffortIndex;
594
- return (_jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: [isSelected ? '› ' : ' ', option.label] }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', "- ", option.description] })] }, option.value));
595
- })] }));
650
+ const reasoningVisibleItems = Math.min(maxVisibleItems, REASONING_EFFORT_OPTIONS.length);
651
+ const reasoningOffset = Math.min(Math.max(0, reasoningEffortIndex - reasoningVisibleItems + 1), Math.max(0, REASONING_EFFORT_OPTIONS.length - reasoningVisibleItems));
652
+ const visibleReasoningOptions = REASONING_EFFORT_OPTIONS.slice(reasoningOffset, reasoningOffset + reasoningVisibleItems);
653
+ const selectedReasoningOption = REASONING_EFFORT_OPTIONS[reasoningEffortIndex];
654
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "cyan", bold: true, children: ["Reasoning Effort", isSettingDefault ? _jsx(Text, { color: "gray", children: " (default)" }) : null] }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", wrap: "truncate-end", children: pendingReasoningModel.displayName || pendingReasoningModel.name }) }), _jsx(Box, { flexDirection: "column", height: maxVisibleItems, marginTop: 1, children: Array.from({ length: maxVisibleItems }, (_, rowIndex) => {
655
+ const option = visibleReasoningOptions[rowIndex];
656
+ if (!option) {
657
+ return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `reasoning-empty-${rowIndex}`));
658
+ }
659
+ const actualIndex = reasoningOffset + rowIndex;
660
+ const isSelected = actualIndex === reasoningEffortIndex;
661
+ return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, wrap: "truncate-end", children: [isSelected ? '›' : ' ', " ", option.label] }) }, option.value));
662
+ }) }), _jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: _jsx(Text, { color: "gray", wrap: "truncate-end", children: selectedReasoningOption?.description ?? '' }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(HintBar, { hints: ['↑↓ navigate', 'Enter select', 'Esc back'] }) })] }));
596
663
  }
597
- const visibleItems = filteredItems.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS);
598
- const hasCustomModels = customModels.length > 0;
599
- const hasActionableItems = filteredItems.some((item) => !isAddCustomOption(item));
664
+ const selectedIndex = selection.index;
665
+ const scrollOffset = selection.offset;
666
+ const modelsOnly = filteredItems.slice(1);
667
+ const visibleModels = modelsOnly.slice(scrollOffset, scrollOffset + modelsViewportItems);
600
668
  const selectedItem = filteredItems[selectedIndex];
601
- const isSelectedCustom = selectedItem && !isAddCustomOption(selectedItem) && selectedItem.isCustom;
602
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "cyan", bold: true, children: ["Select Model (", selectedIndex + 1, "/", filteredItems.length, ")"] }) }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\u2191\u2193 navigate, Enter select, Esc close" }), hasActionableItems && _jsx(Text, { color: "gray", children: ", \u2192\u2190 for actions" })] }), _jsxs(Box, { paddingX: 0, paddingY: 0, children: [_jsx(Text, { color: "gray", children: "\uD83D\uDD0D " }), _jsx(Text, { color: searchQuery ? 'white' : 'gray', children: searchQuery || 'Type to search...' }), _jsx(Text, { color: "cyan", children: "\u258C" })] }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: '─'.repeat(50) }) }), visibleItems.map((item, visibleIndex) => {
603
- const actualIndex = scrollOffset + visibleIndex;
604
- const isSelected = actualIndex === selectedIndex;
605
- if (isAddCustomOption(item)) {
606
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: isSelected ? 'green' : 'gray', bold: isSelected, children: "\u2795 Add custom model..." }) }, "add-custom"));
607
- }
608
- // Show action buttons for selected custom models
609
- const showActions = isSelected && !isAddCustomOption(item);
610
- // Keep the UI label simple: show the actual provider being selected.
611
- // Gateway routing details are intentionally hidden from the main picker.
612
- const providerDisplay = getLLMProviderDisplayName(item.provider);
613
- return (_jsxs(Box, { paddingX: 0, paddingY: 0, children: [item.isCustom && _jsx(Text, { color: isSelected ? 'orange' : 'gray', children: "\u2605 " }), _jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: item.displayName || item.name }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [" (", providerDisplay, ")"] }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', "\u2022 ", item.maxInputTokens.toLocaleString(), " tokens"] }), item.isDefault && (_jsx(Text, { color: isSelected ? 'white' : 'gray', children: " [DEFAULT]" })), item.isCurrent && !showActions && (_jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: [' ', "\u2190 Current"] })), showActions && (_jsxs(_Fragment, { children: [item.isCustom && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { color: customModelAction === 'edit' ? 'green' : 'gray', bold: customModelAction === 'edit', inverse: customModelAction === 'edit', children: [' ', "Edit", ' '] })] })), _jsx(Text, { children: " " }), _jsxs(Text, { color: customModelAction === 'default' ? 'cyan' : 'gray', bold: customModelAction === 'default', inverse: customModelAction === 'default', children: [' ', "Set as Default", ' '] }), item.isCustom && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { color: customModelAction === 'delete' ? 'red' : 'gray', bold: customModelAction === 'delete', inverse: customModelAction === 'delete', children: [' ', "Delete", ' '] })] }))] }))] }, `${item.provider}-${item.name}-${item.isCustom ? 'custom' : 'registry'}`));
614
- }), filteredItems.length > MAX_VISIBLE_ITEMS && (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: "gray", wrap: "truncate", children: [scrollOffset > 0 ? '↑ more above' : '', scrollOffset > 0 && scrollOffset + MAX_VISIBLE_ITEMS < filteredItems.length
615
- ? ' '
616
- : '', scrollOffset + MAX_VISIBLE_ITEMS < filteredItems.length
617
- ? '↓ more below'
618
- : ''] }) })), customModelAction === 'delete' && pendingDeleteConfirm && (_jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: _jsx(Text, { color: "yellowBright", children: "\u26A0\uFE0F Press \u2192 or Enter again to confirm delete" }) })), customModelAction && !pendingDeleteConfirm && (_jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: _jsxs(Text, { color: "gray", children: ["\u2190", ' ', customModelAction === 'edit'
619
- ? 'deselect'
620
- : isSelectedCustom
621
- ? 'edit'
622
- : 'deselect', ' ', "| \u2192", ' ', customModelAction === 'edit'
623
- ? 'default'
624
- : customModelAction === 'default'
625
- ? isSelectedCustom
626
- ? 'delete'
627
- : 'confirm'
628
- : 'confirm', ' ', "| Enter ", customModelAction] }) }))] }));
669
+ const hasActionableItems = selectedItem && !isAddCustomOption(selectedItem);
670
+ let detailLine = '';
671
+ if (isLoading) {
672
+ detailLine = 'Loading models…';
673
+ }
674
+ else if (customModelAction === 'delete' && pendingDeleteConfirm) {
675
+ detailLine = 'Confirm delete: press Enter again';
676
+ }
677
+ else if (customModelAction) {
678
+ const label = customModelAction === 'edit'
679
+ ? 'Edit'
680
+ : customModelAction === 'default'
681
+ ? 'Set as default'
682
+ : 'Delete';
683
+ detailLine = `Action: ${label}`;
684
+ }
685
+ else if (searchQuery.trim() && filteredItems.length <= 1) {
686
+ detailLine = 'No models match your search';
687
+ }
688
+ else if (!selectedItem) {
689
+ detailLine = '';
690
+ }
691
+ else if (isAddCustomOption(selectedItem)) {
692
+ detailLine = 'Enter to add a custom model';
693
+ }
694
+ else {
695
+ const provider = getLLMProviderDisplayName(selectedItem.provider);
696
+ const name = selectedItem.displayName || selectedItem.name;
697
+ const flags = [];
698
+ if (selectedItem.isDefault)
699
+ flags.push('default');
700
+ if (selectedItem.isCurrent)
701
+ flags.push('current');
702
+ detailLine =
703
+ flags.length > 0
704
+ ? `${name} (${provider}) • ${flags.join(', ')}`
705
+ : `${name} (${provider})`;
706
+ }
707
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "cyan", bold: true, children: "Models" }) }), _jsxs(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: [_jsx(Text, { color: "gray", children: "Search: " }), _jsx(Text, { color: searchQuery ? 'white' : 'gray', wrap: "truncate-end", children: searchQuery || 'Type to filter models…' })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: selectedIndex === 0 ? 'green' : 'gray', bold: selectedIndex === 0, wrap: "truncate-end", children: [getRowPrefix({
708
+ isSelected: selectedIndex === 0,
709
+ isDefault: false,
710
+ isCurrent: false,
711
+ isCustom: false,
712
+ }), ' ', "Add custom model\u2026"] }) }), _jsx(Box, { flexDirection: "column", height: modelsViewportItems, children: isLoading || modelsOnly.length === 0
713
+ ? Array.from({ length: modelsViewportItems }, (_, index) => (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `model-empty-${index}`)))
714
+ : Array.from({ length: modelsViewportItems }, (_, rowIndex) => {
715
+ const item = visibleModels[rowIndex];
716
+ if (!item) {
717
+ return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `model-empty-${rowIndex}`));
718
+ }
719
+ const actualIndex = 1 + scrollOffset + rowIndex;
720
+ const isSelected = actualIndex === selectedIndex;
721
+ const providerDisplay = getLLMProviderDisplayName(item.provider);
722
+ const name = item.displayName || item.name;
723
+ const prefix = getRowPrefix({
724
+ isSelected,
725
+ isDefault: item.isDefault,
726
+ isCurrent: item.isCurrent,
727
+ isCustom: item.isCustom,
728
+ });
729
+ return (_jsxs(Box, { flexDirection: "row", paddingX: 0, paddingY: 0, children: [_jsx(Box, { flexGrow: 1, children: _jsxs(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, wrap: "truncate-end", children: [prefix, " ", name, " (", providerDisplay, ")"] }) }), isSelected && (_jsxs(Box, { flexDirection: "row", marginLeft: 1, children: [item.isCustom && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: customModelAction === 'edit'
730
+ ? 'green'
731
+ : 'gray', bold: customModelAction === 'edit', inverse: customModelAction === 'edit', children: [' ', "Edit", ' '] }), _jsx(Text, { children: " " })] })), _jsxs(Text, { color: customModelAction === 'default'
732
+ ? 'cyan'
733
+ : 'gray', bold: customModelAction === 'default', inverse: customModelAction === 'default', children: [' ', "Set as Default", ' '] }), item.isCustom && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsxs(Text, { color: customModelAction === 'delete'
734
+ ? 'red'
735
+ : 'gray', bold: customModelAction === 'delete', inverse: customModelAction === 'delete', children: [' ', "Delete", ' '] })] }))] }))] }, `${item.provider}-${item.name}-${item.isCustom ? 'custom' : 'registry'}`));
736
+ }) })] }), _jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: _jsx(Text, { color: "gray", wrap: "truncate-end", children: detailLine }) }), _jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(HintBar, { hints: [
737
+ '↑↓ navigate',
738
+ 'Enter select',
739
+ 'Esc close',
740
+ hasActionableItems ? '←→ actions' : '',
741
+ ] }) })] }));
629
742
  });
630
743
  export default ModelSelector;
@@ -1 +1 @@
1
- {"version":3,"file":"PromptList.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/PromptList.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1D,MAAM,MAAM,gBAAgB,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAEhC,UAAU,eAAe;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAuCD;;GAEG;AACH,QAAA,MAAM,UAAU,0FAqKd,CAAC;AAEH,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"PromptList.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/PromptList.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG1D,MAAM,MAAM,gBAAgB,GACtB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,CAAC;AAEhC,UAAU,eAAe;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC7C,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;IAClD,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAuCD;;GAEG;AACH,QAAA,MAAM,UAAU,0FAgKd,CAAC;AAEH,eAAe,UAAU,CAAC"}
@@ -66,7 +66,7 @@ const PromptList = forwardRef(function PromptList({ isVisible, onAction, onLoadI
66
66
  setIsLoading(false);
67
67
  })
68
68
  .catch((err) => {
69
- console.error(`PromptList: Failed to load prompts: ${err}`);
69
+ agent.logger.error(`PromptList: Failed to load prompts: ${err instanceof Error ? err.message : String(err)}`);
70
70
  setPrompts([]);
71
71
  setIsLoading(false);
72
72
  });
@@ -88,21 +88,17 @@ const PromptList = forwardRef(function PromptList({ isVisible, onAction, onLoadI
88
88
  }, [isVisible, refreshKey, loadPrompts]);
89
89
  // Build list items: prompts + Add/Delete actions
90
90
  const items = useMemo(() => {
91
- const list = prompts.map((prompt) => ({
92
- id: prompt.name,
93
- type: 'prompt',
94
- prompt,
95
- }));
96
- // Add action items at the end
97
- list.push({
98
- id: '__add__',
99
- type: 'add',
100
- });
101
- list.push({
102
- id: '__delete__',
103
- type: 'delete',
104
- });
105
- return list;
91
+ return [
92
+ // Action items at the top
93
+ { id: '__add__', type: 'add' },
94
+ { id: '__delete__', type: 'delete' },
95
+ // Prompts list
96
+ ...prompts.map((prompt) => ({
97
+ id: prompt.name,
98
+ type: 'prompt',
99
+ prompt,
100
+ })),
101
+ ];
106
102
  }, [prompts]);
107
103
  // Format item for display
108
104
  const formatItem = (item, isSelected) => {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SoundsSelector Component
3
+ * Interactive overlay for configuring sound notifications and selecting built-in sounds.
4
+ *
5
+ * Built-in sounds are copied into the Dexto sounds directory (typically ~/.dexto/sounds/builtins/)
6
+ * and selected via preferences.yml using paths relative to that directory (e.g., builtins/coin.wav).
7
+ *
8
+ * This reuses the existing sound resolution logic in soundNotification.ts.
9
+ */
10
+ import React from 'react';
11
+ import type { Key } from '../../hooks/useInputOrchestrator.js';
12
+ interface SoundsSelectorProps {
13
+ isVisible: boolean;
14
+ onClose: () => void;
15
+ }
16
+ export interface SoundsSelectorHandle {
17
+ handleInput: (input: string, key: Key) => boolean;
18
+ }
19
+ declare const SoundsSelector: React.ForwardRefExoticComponent<SoundsSelectorProps & React.RefAttributes<SoundsSelectorHandle>>;
20
+ export default SoundsSelector;
21
+ //# sourceMappingURL=SoundsSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoundsSelector.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/overlays/SoundsSelector.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAQN,MAAM,OAAO,CAAC;AAKf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAkB/D,UAAU,mBAAmB;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAuMD,QAAA,MAAM,cAAc,kGAsiBnB,CAAC;AAEF,eAAe,cAAc,CAAC"}