dexto 1.6.0 → 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 (132) hide show
  1. package/dist/agents/coding-agent/coding-agent.yml +3 -1
  2. package/dist/cli/assets/sounds/SOURCES.md +35 -0
  3. package/dist/cli/assets/sounds/boot.wav +0 -0
  4. package/dist/cli/assets/sounds/chime.wav +0 -0
  5. package/dist/cli/assets/sounds/coin.wav +0 -0
  6. package/dist/cli/assets/sounds/confirm.wav +0 -0
  7. package/dist/cli/assets/sounds/levelup.wav +0 -0
  8. package/dist/cli/assets/sounds/ping.wav +0 -0
  9. package/dist/cli/assets/sounds/powerup.wav +0 -0
  10. package/dist/cli/assets/sounds/startup.wav +0 -0
  11. package/dist/cli/assets/sounds/success.wav +0 -0
  12. package/dist/cli/assets/sounds/treasure.wav +0 -0
  13. package/dist/cli/assets/sounds/win.wav +0 -0
  14. package/dist/cli/commands/interactive-commands/exit-handler.d.ts +12 -0
  15. package/dist/cli/commands/interactive-commands/exit-handler.d.ts.map +1 -0
  16. package/dist/cli/commands/interactive-commands/exit-handler.js +20 -0
  17. package/dist/cli/commands/interactive-commands/exit-stats.d.ts +24 -0
  18. package/dist/cli/commands/interactive-commands/exit-stats.d.ts.map +1 -0
  19. package/dist/cli/commands/interactive-commands/exit-stats.js +17 -0
  20. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  21. package/dist/cli/commands/interactive-commands/general-commands.js +53 -3
  22. package/dist/cli/commands/interactive-commands/prompt-commands.d.ts.map +1 -1
  23. package/dist/cli/commands/interactive-commands/prompt-commands.js +12 -67
  24. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts.map +1 -1
  25. package/dist/cli/commands/interactive-commands/session/session-commands.js +0 -2
  26. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts +1 -13
  27. package/dist/cli/commands/interactive-commands/system/system-commands.d.ts.map +1 -1
  28. package/dist/cli/commands/interactive-commands/system/system-commands.js +45 -54
  29. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  30. package/dist/cli/ink-cli/InkCLIRefactored.js +132 -21
  31. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  32. package/dist/cli/ink-cli/components/ApprovalPrompt.js +74 -20
  33. package/dist/cli/ink-cli/components/ElicitationForm.d.ts +5 -3
  34. package/dist/cli/ink-cli/components/ElicitationForm.d.ts.map +1 -1
  35. package/dist/cli/ink-cli/components/ElicitationForm.js +414 -180
  36. package/dist/cli/ink-cli/components/ResourceAutocomplete.d.ts.map +1 -1
  37. package/dist/cli/ink-cli/components/ResourceAutocomplete.js +20 -11
  38. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.d.ts.map +1 -1
  39. package/dist/cli/ink-cli/components/SlashCommandAutocomplete.js +47 -67
  40. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  41. package/dist/cli/ink-cli/components/StatusBar.js +10 -4
  42. package/dist/cli/ink-cli/components/base/BaseSelector.d.ts +2 -1
  43. package/dist/cli/ink-cli/components/base/BaseSelector.d.ts.map +1 -1
  44. package/dist/cli/ink-cli/components/base/BaseSelector.js +37 -27
  45. package/dist/cli/ink-cli/components/chat/Header.d.ts.map +1 -1
  46. package/dist/cli/ink-cli/components/chat/Header.js +1 -1
  47. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  48. package/dist/cli/ink-cli/components/chat/MessageItem.js +3 -1
  49. package/dist/cli/ink-cli/components/chat/ToolIcon.d.ts.map +1 -1
  50. package/dist/cli/ink-cli/components/chat/ToolIcon.js +5 -15
  51. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.d.ts.map +1 -1
  52. package/dist/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.js +1 -1
  53. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  54. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +4 -2
  55. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  56. package/dist/cli/ink-cli/components/modes/StaticCLI.js +9 -2
  57. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.d.ts +13 -0
  58. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.d.ts.map +1 -0
  59. package/dist/cli/ink-cli/components/overlays/CommandOutputOverlay.js +60 -0
  60. package/dist/cli/ink-cli/components/overlays/LogLevelSelector.js +1 -1
  61. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  62. package/dist/cli/ink-cli/components/overlays/ModelSelectorRefactored.js +213 -100
  63. package/dist/cli/ink-cli/components/overlays/PromptList.d.ts.map +1 -1
  64. package/dist/cli/ink-cli/components/overlays/PromptList.js +12 -16
  65. package/dist/cli/ink-cli/components/overlays/SoundsSelector.d.ts +21 -0
  66. package/dist/cli/ink-cli/components/overlays/SoundsSelector.d.ts.map +1 -0
  67. package/dist/cli/ink-cli/components/overlays/SoundsSelector.js +566 -0
  68. package/dist/cli/ink-cli/components/overlays/ToolBrowser.d.ts.map +1 -1
  69. package/dist/cli/ink-cli/components/overlays/ToolBrowser.js +94 -39
  70. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.d.ts.map +1 -1
  71. package/dist/cli/ink-cli/components/overlays/custom-model-wizard/LocalModelWizard.js +8 -13
  72. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts +3 -3
  73. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.d.ts.map +1 -1
  74. package/dist/cli/ink-cli/components/renderers/FilePreviewRenderer.js +6 -5
  75. package/dist/cli/ink-cli/components/renderers/FileRenderer.d.ts +3 -1
  76. package/dist/cli/ink-cli/components/renderers/FileRenderer.d.ts.map +1 -1
  77. package/dist/cli/ink-cli/components/renderers/FileRenderer.js +18 -7
  78. package/dist/cli/ink-cli/components/renderers/ShellRenderer.d.ts.map +1 -1
  79. package/dist/cli/ink-cli/components/renderers/ShellRenderer.js +7 -17
  80. package/dist/cli/ink-cli/components/renderers/index.d.ts.map +1 -1
  81. package/dist/cli/ink-cli/components/renderers/index.js +1 -1
  82. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.d.ts +7 -0
  83. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.d.ts.map +1 -0
  84. package/dist/cli/ink-cli/components/shared/FocusOverlayFrame.js +8 -0
  85. package/dist/cli/ink-cli/components/shared/HintBar.d.ts +6 -0
  86. package/dist/cli/ink-cli/components/shared/HintBar.d.ts.map +1 -0
  87. package/dist/cli/ink-cli/components/shared/HintBar.js +6 -0
  88. package/dist/cli/ink-cli/constants/spinnerFrames.d.ts +2 -0
  89. package/dist/cli/ink-cli/constants/spinnerFrames.d.ts.map +1 -0
  90. package/dist/cli/ink-cli/constants/spinnerFrames.js +1 -0
  91. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  92. package/dist/cli/ink-cli/constants/tips.js +1 -0
  93. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  94. package/dist/cli/ink-cli/containers/InputContainer.js +19 -15
  95. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  96. package/dist/cli/ink-cli/containers/OverlayContainer.js +21 -5
  97. package/dist/cli/ink-cli/hooks/useAnimationTick.d.ts +11 -0
  98. package/dist/cli/ink-cli/hooks/useAnimationTick.d.ts.map +1 -0
  99. package/dist/cli/ink-cli/hooks/useAnimationTick.js +54 -0
  100. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  101. package/dist/cli/ink-cli/hooks/useCLIState.js +1 -0
  102. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  103. package/dist/cli/ink-cli/services/processStream.js +17 -8
  104. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  105. package/dist/cli/ink-cli/state/initialState.js +1 -0
  106. package/dist/cli/ink-cli/state/types.d.ts +13 -1
  107. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  108. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  109. package/dist/cli/ink-cli/utils/commandOverlays.js +1 -0
  110. package/dist/cli/ink-cli/utils/elicitationSchema.d.ts +11 -0
  111. package/dist/cli/ink-cli/utils/elicitationSchema.d.ts.map +1 -0
  112. package/dist/cli/ink-cli/utils/elicitationSchema.js +80 -0
  113. package/dist/cli/ink-cli/utils/index.d.ts +1 -1
  114. package/dist/cli/ink-cli/utils/index.d.ts.map +1 -1
  115. package/dist/cli/ink-cli/utils/index.js +1 -1
  116. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +2 -17
  117. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  118. package/dist/cli/ink-cli/utils/messageFormatting.js +22 -128
  119. package/dist/cli/ink-cli/utils/overlayPresentation.d.ts +19 -0
  120. package/dist/cli/ink-cli/utils/overlayPresentation.d.ts.map +1 -0
  121. package/dist/cli/ink-cli/utils/overlayPresentation.js +33 -0
  122. package/dist/cli/ink-cli/utils/overlaySizing.d.ts +19 -0
  123. package/dist/cli/ink-cli/utils/overlaySizing.d.ts.map +1 -0
  124. package/dist/cli/ink-cli/utils/overlaySizing.js +11 -0
  125. package/dist/cli/ink-cli/utils/soundNotification.d.ts +19 -13
  126. package/dist/cli/ink-cli/utils/soundNotification.d.ts.map +1 -1
  127. package/dist/cli/ink-cli/utils/soundNotification.js +120 -97
  128. package/dist/utils/session-logger-factory.d.ts.map +1 -1
  129. package/dist/utils/session-logger-factory.js +17 -2
  130. package/dist/webui/assets/{index-DwtueA8l.js → index-CKhumsZA.js} +135 -135
  131. package/dist/webui/index.html +1 -1
  132. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAsFD;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GA6W9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"ResourceAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ResourceAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,MAAM,WAAW,0BAA0B;IACvC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,yBAAyB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACvD,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAsFD;;GAEG;AACH,QAAA,MAAM,yBAAyB,8GA4X9B,CAAC;AAEF;;;GAGG;AACH,QAAA,MAAM,oBAAoB,EAErB,OAAO,yBAAyB,CAAC;AAEtC,eAAe,oBAAoB,CAAC"}
@@ -3,6 +3,8 @@ import React, { useState, useEffect, useRef, useMemo, useCallback, forwardRef, u
3
3
  import { Box, Text } from 'ink';
4
4
  import path from 'path';
5
5
  import { centerTruncatePath } from '../utils/messageFormatting.js';
6
+ import { useTerminalSize } from '../hooks/useTerminalSize.js';
7
+ import { getMaxVisibleItemsForTerminalRows } from '../utils/overlaySizing.js';
6
8
  /**
7
9
  * Get match score for resource: 0 = no match, 1 = description/URI match, 2 = name includes, 3 = name starts with
8
10
  * Prioritizes name matches over description/URI matches
@@ -73,10 +75,17 @@ function sortResources(resources, query) {
73
75
  const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isVisible, searchQuery, onSelectResource, onLoadIntoInput, onClose, agent }, ref) {
74
76
  const [resources, setResources] = useState([]);
75
77
  const [isLoading, setIsLoading] = useState(false);
78
+ const { columns: terminalWidth, rows: terminalRows } = useTerminalSize();
79
+ const maxVisibleItems = useMemo(() => {
80
+ return getMaxVisibleItemsForTerminalRows({
81
+ rows: terminalRows,
82
+ hardCap: 15,
83
+ reservedRows: 6,
84
+ });
85
+ }, [terminalRows]);
76
86
  // Combined state to guarantee single render on navigation
77
87
  const [selection, setSelection] = useState({ index: 0, offset: 0 });
78
88
  const selectedIndexRef = useRef(0);
79
- const MAX_VISIBLE_ITEMS = 5;
80
89
  // Update selection AND scroll offset in a single state update
81
90
  // This guarantees exactly one render per navigation action
82
91
  const updateSelection = useCallback((indexUpdater) => {
@@ -90,12 +99,12 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
90
99
  if (newIndex < prev.offset) {
91
100
  newOffset = newIndex;
92
101
  }
93
- else if (newIndex >= prev.offset + MAX_VISIBLE_ITEMS) {
94
- newOffset = Math.max(0, newIndex - MAX_VISIBLE_ITEMS + 1);
102
+ else if (newIndex >= prev.offset + maxVisibleItems) {
103
+ newOffset = Math.max(0, newIndex - maxVisibleItems + 1);
95
104
  }
96
105
  return { index: newIndex, offset: newOffset };
97
106
  });
98
- }, [MAX_VISIBLE_ITEMS]);
107
+ }, [maxVisibleItems]);
99
108
  // Fetch resources from agent
100
109
  useEffect(() => {
101
110
  if (!isVisible)
@@ -256,7 +265,7 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
256
265
  : Math.min(selection.index, Math.max(0, displayItems.length - 1));
257
266
  const scrollOffset = itemsChanged
258
267
  ? 0
259
- : Math.min(selection.offset, Math.max(0, displayItems.length - MAX_VISIBLE_ITEMS));
268
+ : Math.min(selection.offset, Math.max(0, displayItems.length - maxVisibleItems));
260
269
  // Sync state only when items actually changed AND state differs
261
270
  // This effect runs AFTER render, updating state for next user interaction
262
271
  useEffect(() => {
@@ -274,8 +283,8 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
274
283
  }, [itemsChanged, displayItems.length, selection.index, selection.offset]);
275
284
  // Calculate visible items based on scroll offset
276
285
  const visibleResources = useMemo(() => {
277
- return displayItems.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS);
278
- }, [displayItems, scrollOffset, MAX_VISIBLE_ITEMS]);
286
+ return displayItems.slice(scrollOffset, scrollOffset + maxVisibleItems);
287
+ }, [displayItems, scrollOffset, maxVisibleItems]);
279
288
  // Expose handleInput method via ref
280
289
  useImperativeHandle(ref, () => ({
281
290
  handleInput: (_input, key) => {
@@ -358,14 +367,14 @@ const ResourceAutocompleteInner = forwardRef(function ResourceAutocomplete({ isV
358
367
  ? `No resources match "${mentionQuery}"`
359
368
  : 'No resources available. Connect an MCP server or enable internal resources.' }) }));
360
369
  }
361
- return (_jsx(Box, { flexDirection: "column", paddingLeft: 2, children: visibleResources.map((item, visibleIndex) => {
370
+ return (_jsx(Box, { flexDirection: "column", width: terminalWidth, paddingLeft: 2, children: visibleResources.map((item, visibleIndex) => {
362
371
  const actualIndex = scrollOffset + visibleIndex;
363
372
  const isSelected = actualIndex === selectedIndex;
364
- // Use center truncation for long paths
365
- const displayPath = centerTruncatePath(item.path, 60);
373
+ // Use center truncation for long paths (and still force truncate to prevent wrapping)
374
+ const displayPath = centerTruncatePath(item.path, Math.max(20, terminalWidth - 16));
366
375
  // Check if it's an image file
367
376
  const isImage = item.resource?.mimeType?.startsWith('image/');
368
- return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: isSelected ? '❯ ' : ' ' }), _jsxs(Text, { color: isSelected ? 'cyan' : 'white', bold: isSelected, children: [isImage && '🖼️ ', displayPath, item.resource?.serverName && ` [${item.resource.serverName}]`] })] }, item.path));
377
+ return (_jsxs(Box, { children: [_jsx(Text, { color: isSelected ? 'cyan' : 'gray', children: isSelected ? '❯ ' : ' ' }), _jsxs(Text, { wrap: "truncate-end", color: isSelected ? 'cyan' : 'white', bold: isSelected, children: [isImage && '🖼️ ', displayPath, item.resource?.serverName && ` [${item.resource.serverName}]`] })] }, item.path));
369
378
  }) }));
370
379
  });
371
380
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"SlashCommandAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/SlashCommandAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAI9C,MAAM,WAAW,8BAA8B;IAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,6BAA6B;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,qBAAqB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,WAAW,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AA4GD;;GAEG;AACH,QAAA,MAAM,6BAA6B,sHAwbjC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAEhC,OAAO,6BAA6B,CAAC"}
1
+ {"version":3,"file":"SlashCommandAutocomplete.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/SlashCommandAutocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,KAQN,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAM9C,MAAM,WAAW,8BAA8B;IAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED,UAAU,6BAA6B;IACnC,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,qBAAqB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,WAAW,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;CACrB;AAmGD;;GAEG;AACH,QAAA,MAAM,6BAA6B,sHA4bjC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAEhC,OAAO,6BAA6B,CAAC"}
@@ -1,7 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useState, useEffect, useRef, useMemo, useCallback, forwardRef, useImperativeHandle, } from 'react';
3
- import { Box, Text, useStdout } from 'ink';
3
+ import { Box, Text } from 'ink';
4
4
  import { getAllCommands } from '../../commands/interactive-commands/commands.js';
5
+ import { useTerminalSize } from '../hooks/useTerminalSize.js';
6
+ import { getMaxVisibleItemsForTerminalRows } from '../utils/overlaySizing.js';
5
7
  /**
6
8
  * Get match score for prompt: 0 = no match, 1 = description/title match, 2 = name includes, 3 = name starts with
7
9
  */
@@ -74,16 +76,6 @@ function getSystemCommandMatchScore(cmd, query) {
74
76
  function matchesSystemCommandQuery(cmd, query) {
75
77
  return getSystemCommandMatchScore(cmd, query) > 0;
76
78
  }
77
- /**
78
- * Truncate text to fit within maxLength, adding ellipsis if truncated
79
- */
80
- function truncateText(text, maxLength) {
81
- if (text.length <= maxLength)
82
- return text;
83
- if (maxLength <= 3)
84
- return text.slice(0, maxLength);
85
- return text.slice(0, maxLength - 1) + '…';
86
- }
87
79
  /**
88
80
  * Inner component - wrapped with React.memo below
89
81
  */
@@ -91,12 +83,17 @@ const SlashCommandAutocompleteInner = forwardRef(function SlashCommandAutocomple
91
83
  const [prompts, setPrompts] = useState([]);
92
84
  const [systemCommands, setSystemCommands] = useState([]);
93
85
  const [isLoading, setIsLoading] = useState(false);
86
+ const { columns: terminalWidth, rows: terminalRows } = useTerminalSize();
87
+ const maxVisibleItems = useMemo(() => {
88
+ return getMaxVisibleItemsForTerminalRows({
89
+ rows: terminalRows,
90
+ hardCap: 8,
91
+ reservedRows: 8,
92
+ });
93
+ }, [terminalRows]);
94
94
  // Combined state to guarantee single render on navigation
95
95
  const [selection, setSelection] = useState({ index: 0, offset: 0 });
96
96
  const selectedIndexRef = useRef(0);
97
- const { stdout } = useStdout();
98
- const terminalWidth = stdout?.columns || 80;
99
- const MAX_VISIBLE_ITEMS = 8;
100
97
  // Update selection AND scroll offset in a single state update
101
98
  // This guarantees exactly one render per navigation action
102
99
  const updateSelection = useCallback((indexUpdater) => {
@@ -108,12 +105,12 @@ const SlashCommandAutocompleteInner = forwardRef(function SlashCommandAutocomple
108
105
  if (newIndex < prev.offset) {
109
106
  newOffset = newIndex;
110
107
  }
111
- else if (newIndex >= prev.offset + MAX_VISIBLE_ITEMS) {
112
- newOffset = Math.max(0, newIndex - MAX_VISIBLE_ITEMS + 1);
108
+ else if (newIndex >= prev.offset + maxVisibleItems) {
109
+ newOffset = Math.max(0, newIndex - maxVisibleItems + 1);
113
110
  }
114
111
  return { index: newIndex, offset: newOffset };
115
112
  });
116
- }, [MAX_VISIBLE_ITEMS]);
113
+ }, [maxVisibleItems]);
117
114
  // Fetch prompts and system commands from agent
118
115
  useEffect(() => {
119
116
  if (!isVisible)
@@ -251,7 +248,7 @@ const SlashCommandAutocompleteInner = forwardRef(function SlashCommandAutocomple
251
248
  : Math.min(selection.index, Math.max(0, combinedItems.length - 1));
252
249
  const scrollOffset = itemsChanged
253
250
  ? 0
254
- : Math.min(selection.offset, Math.max(0, combinedItems.length - MAX_VISIBLE_ITEMS));
251
+ : Math.min(selection.offset, Math.max(0, combinedItems.length - maxVisibleItems));
255
252
  // Sync state only when items actually changed AND state differs
256
253
  // This effect runs AFTER render, updating state for next user interaction
257
254
  useEffect(() => {
@@ -269,8 +266,8 @@ const SlashCommandAutocompleteInner = forwardRef(function SlashCommandAutocomple
269
266
  }, [itemsChanged, combinedItems.length, currentFirstId, selection.index, selection.offset]);
270
267
  // Calculate visible items based on scroll offset
271
268
  const visibleItems = useMemo(() => {
272
- return combinedItems.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS);
273
- }, [combinedItems, scrollOffset, MAX_VISIBLE_ITEMS]);
269
+ return combinedItems.slice(scrollOffset, scrollOffset + maxVisibleItems);
270
+ }, [combinedItems, scrollOffset, maxVisibleItems]);
274
271
  // Expose handleInput method via ref
275
272
  useImperativeHandle(ref, () => ({
276
273
  handleInput: (input, key) => {
@@ -377,53 +374,36 @@ const SlashCommandAutocompleteInner = forwardRef(function SlashCommandAutocomple
377
374
  if (combinedItems.length === 0) {
378
375
  return null;
379
376
  }
380
- const totalItems = combinedItems.length;
381
- // Show simplified header when user is typing arguments
382
- const headerText = hasArguments
383
- ? 'Press Enter to execute'
384
- : `Commands (${selectedIndex + 1}/${totalItems}) - ↑↓ navigate, Tab load, Enter execute, Esc close`;
385
- return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "purple", bold: true, children: headerText }) }), visibleItems.map((item, visibleIndex) => {
386
- const actualIndex = scrollOffset + visibleIndex;
387
- const isSelected = actualIndex === selectedIndex;
388
- if (item.kind === 'system') {
389
- const cmd = item.command;
390
- const nameText = `/${cmd.name}`;
391
- const categoryText = cmd.category ? ` (${cmd.category})` : '';
392
- const descText = cmd.description || '';
393
- // Two-line layout:
394
- // Line 1: /command-name
395
- // Line 2: Description text (category)
396
- return (_jsxs(Box, { flexDirection: "column", paddingX: 0, children: [_jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: nameText }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', descText, categoryText] })] }, `system-${cmd.name}`));
397
- }
398
- // Prompt command (MCP prompts)
399
- const prompt = item.prompt;
400
- // Use displayName for user-friendly display, fall back to full name
401
- const displayName = prompt.displayName || prompt.name;
402
- // Check if there's a collision (commandName includes source prefix)
403
- const hasCollision = prompt.commandName && prompt.commandName !== displayName;
404
- const nameText = `/${displayName}`;
405
- const argsString = prompt.arguments && prompt.arguments.length > 0
406
- ? ' ' +
407
- prompt.arguments
408
- .map((arg) => `<${arg.name}${arg.required ? '' : '?'}>`)
409
- .join(' ')
410
- : '';
411
- const description = prompt.title || prompt.description || '';
412
- // Two-line layout:
413
- // Line 1: /command-name <args>
414
- // Line 2: Description text (source)
415
- const commandText = nameText + argsString;
416
- // Show source as label, with collision indicator if needed
417
- // For plugin skills, show namespace (plugin name) instead of "config"
418
- const metadata = prompt.metadata;
419
- const displaySource = metadata?.namespace
420
- ? String(metadata.namespace)
421
- : prompt.source || 'prompt';
422
- const sourceLabel = hasCollision
423
- ? `${displaySource} - use /${prompt.commandName}`
424
- : displaySource;
425
- return (_jsxs(Box, { flexDirection: "column", paddingX: 0, children: [_jsx(Text, { color: isSelected ? 'cyan' : 'gray', bold: isSelected, children: commandText }), _jsxs(Text, { color: isSelected ? 'white' : 'gray', children: [' ', description, ` (${sourceLabel})`] })] }, `prompt-${prompt.name}`));
426
- })] }));
377
+ const nameColumnWidth = Math.max(16, Math.min(28, Math.floor(terminalWidth * 0.32)));
378
+ const descriptionColor = (isSelected) => (isSelected ? 'white' : 'gray');
379
+ const commandColor = (isSelected) => (isSelected ? 'cyan' : 'gray');
380
+ return (_jsx(Box, { flexDirection: "column", width: terminalWidth, children: visibleItems.map((item, visibleIndex) => {
381
+ const actualIndex = scrollOffset + visibleIndex;
382
+ const isSelected = actualIndex === selectedIndex;
383
+ if (item.kind === 'system') {
384
+ const cmd = item.command;
385
+ const nameText = `/${cmd.name}`;
386
+ const descText = cmd.description || '';
387
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: commandColor(isSelected), children: isSelected ? '❯' : ' ' }) }), _jsx(Box, { width: nameColumnWidth, children: _jsx(Text, { wrap: "truncate-end", color: commandColor(isSelected), bold: isSelected, children: nameText }) }), _jsx(Box, { flexGrow: 1, minWidth: 0, children: _jsx(Text, { wrap: "truncate-end", color: descriptionColor(isSelected), children: descText }) })] }, `system-${cmd.name}`));
388
+ }
389
+ // Prompt command (MCP prompts)
390
+ const prompt = item.prompt;
391
+ // Use displayName for user-friendly display, fall back to full name
392
+ const displayName = prompt.displayName || prompt.name;
393
+ // Check if there's a collision (commandName includes source prefix)
394
+ const hasCollision = prompt.commandName && prompt.commandName !== displayName;
395
+ const nameText = `/${displayName}`;
396
+ const argsString = prompt.arguments && prompt.arguments.length > 0
397
+ ? ' ' +
398
+ prompt.arguments
399
+ .map((arg) => `<${arg.name}${arg.required ? '' : '?'}>`)
400
+ .join(' ')
401
+ : '';
402
+ const description = prompt.title || prompt.description || '';
403
+ const commandText = nameText + argsString;
404
+ const collisionSuffix = hasCollision ? ` (use /${prompt.commandName})` : '';
405
+ return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: commandColor(isSelected), children: isSelected ? '' : ' ' }) }), _jsx(Box, { width: nameColumnWidth, children: _jsx(Text, { wrap: "truncate-end", color: commandColor(isSelected), bold: isSelected, children: commandText }) }), _jsx(Box, { flexGrow: 1, minWidth: 0, children: _jsxs(Text, { wrap: "truncate-end", color: descriptionColor(isSelected), children: [description, collisionSuffix] }) })] }, `prompt-${prompt.name}`));
406
+ }) }));
427
407
  });
428
408
  /**
429
409
  * Export with React.memo to prevent unnecessary re-renders from parent
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wCAAwC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yCAAyC;IACzC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,eAAuB,EACvB,kBAA0B,EAC1B,YAAmB,EACnB,QAAgB,EAChB,cAAsB,EACtB,gBAAwB,EACxB,sBAA0B,GAC7B,EAAE,cAAc,kDA8IhB"}
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAa9C,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,wCAAwC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yCAAyC;IACzC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,eAAuB,EACvB,kBAA0B,EAC1B,YAAmB,EACnB,QAAgB,EAChB,cAAsB,EACtB,gBAAwB,EACxB,sBAA0B,GAC7B,EAAE,cAAc,kDAwIhB"}
@@ -9,10 +9,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
9
  * This 2-line layout prevents truncation on any terminal width.
10
10
  */
11
11
  import { Box, Text } from 'ink';
12
- import Spinner from 'ink-spinner';
13
12
  import { usePhraseCycler } from '../hooks/usePhraseCycler.js';
14
13
  import { useElapsedTime } from '../hooks/useElapsedTime.js';
15
14
  import { useTokenCounter } from '../hooks/useTokenCounter.js';
15
+ import { useAnimationTick } from '../hooks/useAnimationTick.js';
16
+ import { BRAILLE_SPINNER_FRAMES } from '../constants/spinnerFrames.js';
17
+ function StatusSpinner({ enabled, color }) {
18
+ const tick = useAnimationTick({ enabled, intervalMs: 80 });
19
+ const frame = BRAILLE_SPINNER_FRAMES[tick % BRAILLE_SPINNER_FRAMES.length];
20
+ return _jsx(Text, { color: color, children: frame });
21
+ }
16
22
  /**
17
23
  * Status bar that shows processing state above input area
18
24
  * Provides clear feedback on whether the agent is running or idle
@@ -67,7 +73,7 @@ export function StatusBar({ agent, isProcessing, isThinking, isCompacting, appro
67
73
  if (todoHint)
68
74
  metaParts.push(todoHint);
69
75
  const metaContent = metaParts.join(' • ');
70
- return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: "yellow", children: " \uD83D\uDCE6 Compacting context..." })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
76
+ return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(StatusSpinner, { enabled: true, color: "yellow" }), _jsx(Text, { color: "yellow", children: " \uD83D\uDCE6 Compacting context..." })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
71
77
  }
72
78
  // Show initial processing state (before streaming starts) - green/teal color
73
79
  // TODO: Rename this event/state to "reasoning" and associate it with actual reasoning tokens
@@ -87,7 +93,7 @@ export function StatusBar({ agent, isProcessing, isThinking, isCompacting, appro
87
93
  if (todoHint)
88
94
  metaParts.push(todoHint);
89
95
  const metaContent = metaParts.join(' • ');
90
- return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(Text, { color: "green", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: "green", children: [" ", phrase] })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
96
+ return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(StatusSpinner, { enabled: true, color: "green" }), _jsxs(Text, { color: "green", children: [" ", phrase] })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
91
97
  }
92
98
  // Show active streaming state - green/teal color
93
99
  // Always use 2-line layout: phrase on first line, meta on second
@@ -106,5 +112,5 @@ export function StatusBar({ agent, isProcessing, isThinking, isCompacting, appro
106
112
  if (todoHint)
107
113
  metaParts.push(todoHint);
108
114
  const metaContent = metaParts.join(' • ');
109
- return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(Text, { color: "green", children: _jsx(Spinner, { type: "dots" }) }), _jsxs(Text, { color: "green", children: [" ", phrase] }), approvalQueueCount > 0 && (_jsxs(Text, { color: "yellowBright", children: [" \u2022 ", approvalQueueCount, " queued"] }))] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
115
+ return (_jsxs(Box, { paddingX: 1, marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(StatusSpinner, { enabled: true, color: "green" }), _jsxs(Text, { color: "green", children: [" ", phrase] }), approvalQueueCount > 0 && (_jsxs(Text, { color: "yellowBright", children: [" \u2022 ", approvalQueueCount, " queued"] }))] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
110
116
  }
@@ -21,6 +21,7 @@ export interface BaseSelectorProps<T> {
21
21
  borderColor?: string;
22
22
  onTab?: (item: T) => void;
23
23
  supportsTab?: boolean;
24
+ instructionsOverride?: string;
24
25
  }
25
26
  export interface BaseSelectorHandle {
26
27
  handleInput: (input: string, key: Key) => boolean;
@@ -28,7 +29,7 @@ export interface BaseSelectorHandle {
28
29
  /**
29
30
  * Generic selector component with keyboard navigation and scrolling
30
31
  */
31
- declare function BaseSelectorInner<T>({ items, isVisible, isLoading, selectedIndex, onSelectIndex, onSelect, onClose, formatItem, title, maxVisibleItems, loadingMessage, emptyMessage, borderColor, onTab, supportsTab, }: BaseSelectorProps<T>, ref: React.Ref<BaseSelectorHandle>): import("react/jsx-runtime").JSX.Element | null;
32
+ declare function BaseSelectorInner<T>({ items, isVisible, isLoading, selectedIndex, onSelectIndex, onSelect, onClose, formatItem, title, maxVisibleItems, loadingMessage, emptyMessage, borderColor, onTab, supportsTab, instructionsOverride, }: BaseSelectorProps<T>, ref: React.Ref<BaseSelectorHandle>): import("react/jsx-runtime").JSX.Element | null;
32
33
  export declare const BaseSelector: <T>(props: BaseSelectorProps<T> & {
33
34
  ref?: React.Ref<BaseSelectorHandle>;
34
35
  }) => ReturnType<typeof BaseSelectorInner>;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseSelector.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/base/BaseSelector.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQH,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAE/D,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,KAAK,SAAS,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IAC/B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,iBAAS,iBAAiB,CAAC,CAAC,EACxB,EACI,KAAK,EACL,SAAS,EACT,SAAiB,EACjB,aAAa,EACb,aAAa,EACb,QAAQ,EACR,OAAO,EACP,UAAU,EACV,KAAK,EACL,eAAmB,EACnB,cAA6B,EAC7B,YAA+B,EAC/B,WAAoB,EACpB,KAAK,EACL,WAAmB,GACtB,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACvB,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,kDAyJrC;AAGD,eAAO,MAAM,YAAY,EAAoC,CAAC,CAAC,EAC3D,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;CAAE,KACpE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
1
+ {"version":3,"file":"BaseSelector.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/base/BaseSelector.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQH,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAK/D,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,KAAK,SAAS,CAAC;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,kBAAkB;IAC/B,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,iBAAS,iBAAiB,CAAC,CAAC,EACxB,EACI,KAAK,EACL,SAAS,EACT,SAAiB,EACjB,aAAa,EACb,aAAa,EACb,QAAQ,EACR,OAAO,EACP,UAAU,EACV,KAAK,EACL,eAAmB,EACnB,cAA6B,EAC7B,YAA+B,EAC/B,WAAoB,EACpB,KAAK,EACL,WAAmB,EACnB,oBAAoB,GACvB,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACvB,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,kDA8LrC;AAGD,eAAO,MAAM,YAAY,EAAoC,CAAC,CAAC,EAC3D,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;CAAE,KACpE,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Base Selector Component
4
4
  * Reusable selector with keyboard navigation for lists
@@ -6,10 +6,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  */
7
7
  import { useState, useEffect, useRef, useMemo, useCallback, forwardRef, useImperativeHandle, } from 'react';
8
8
  import { Box, Text } from 'ink';
9
+ import { useTerminalSize } from '../../hooks/useTerminalSize.js';
10
+ import { getMaxVisibleItemsForTerminalRows } from '../../utils/overlaySizing.js';
11
+ import { HintBar } from '../shared/HintBar.js';
9
12
  /**
10
13
  * Generic selector component with keyboard navigation and scrolling
11
14
  */
12
- function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex, onSelectIndex, onSelect, onClose, formatItem, title, maxVisibleItems = 8, loadingMessage = 'Loading...', emptyMessage = 'No items found', borderColor = 'cyan', onTab, supportsTab = false, }, ref) {
15
+ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex, onSelectIndex, onSelect, onClose, formatItem, title, maxVisibleItems = 8, loadingMessage = 'Loading...', emptyMessage = 'No items found', borderColor = 'cyan', onTab, supportsTab = false, instructionsOverride, }, ref) {
16
+ const { rows: terminalRows } = useTerminalSize();
17
+ const viewportItems = useMemo(() => {
18
+ return getMaxVisibleItemsForTerminalRows({
19
+ rows: terminalRows,
20
+ hardCap: maxVisibleItems,
21
+ reservedRows: 14,
22
+ });
23
+ }, [terminalRows, maxVisibleItems]);
13
24
  // Track scroll offset as state, but derive during render when needed
14
25
  const [scrollOffsetState, setScrollOffset] = useState(0);
15
26
  const selectedIndexRef = useRef(selectedIndex);
@@ -21,7 +32,7 @@ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex,
21
32
  const scrollOffset = useMemo(() => {
22
33
  const itemsChanged = items.length !== prevItemsLengthRef.current;
23
34
  // Reset scroll if items changed significantly
24
- if (itemsChanged && items.length <= maxVisibleItems) {
35
+ if (itemsChanged && items.length <= viewportItems) {
25
36
  return 0;
26
37
  }
27
38
  let offset = scrollOffsetState;
@@ -29,13 +40,13 @@ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex,
29
40
  if (selectedIndex < offset) {
30
41
  offset = selectedIndex;
31
42
  }
32
- else if (selectedIndex >= offset + maxVisibleItems) {
33
- offset = Math.max(0, selectedIndex - maxVisibleItems + 1);
43
+ else if (selectedIndex >= offset + viewportItems) {
44
+ offset = Math.max(0, selectedIndex - viewportItems + 1);
34
45
  }
35
46
  // Clamp to valid range
36
- const maxOffset = Math.max(0, items.length - maxVisibleItems);
47
+ const maxOffset = Math.max(0, items.length - viewportItems);
37
48
  return Math.min(maxOffset, Math.max(0, offset));
38
- }, [selectedIndex, items.length, maxVisibleItems, scrollOffsetState]);
49
+ }, [selectedIndex, items.length, viewportItems, scrollOffsetState]);
39
50
  // Update refs after render (not during useMemo which can run multiple times)
40
51
  useEffect(() => {
41
52
  prevItemsLengthRef.current = items.length;
@@ -54,8 +65,8 @@ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex,
54
65
  }, [onSelectIndex]);
55
66
  // Calculate visible items
56
67
  const visibleItems = useMemo(() => {
57
- return items.slice(scrollOffset, scrollOffset + maxVisibleItems);
58
- }, [items, scrollOffset, maxVisibleItems]);
68
+ return items.slice(scrollOffset, scrollOffset + viewportItems);
69
+ }, [items, scrollOffset, viewportItems]);
59
70
  // Expose handleInput method via ref
60
71
  useImperativeHandle(ref, () => ({
61
72
  handleInput: (_input, key) => {
@@ -67,8 +78,8 @@ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex,
67
78
  return true;
68
79
  }
69
80
  const itemsLength = items.length;
70
- if (itemsLength === 0)
71
- return false;
81
+ if (isLoading || itemsLength === 0)
82
+ return true;
72
83
  if (key.upArrow) {
73
84
  const nextIndex = (selectedIndexRef.current - 1 + itemsLength) % itemsLength;
74
85
  handleSelectIndex(nextIndex);
@@ -95,24 +106,23 @@ function BaseSelectorInner({ items, isVisible, isLoading = false, selectedIndex,
95
106
  }
96
107
  return false;
97
108
  },
98
- }), [isVisible, items, handleSelectIndex, onClose, onSelect, onTab]);
109
+ }), [isVisible, isLoading, items, handleSelectIndex, onClose, onSelect, onTab]);
99
110
  if (!isVisible)
100
111
  return null;
101
- if (isLoading) {
102
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: loadingMessage }) }));
103
- }
104
- if (items.length === 0) {
105
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: emptyMessage }) }));
106
- }
107
- // Build instruction text based on features
108
- const instructions = supportsTab
109
- ? '↑↓ navigate, Tab load, Enter select, Esc close'
110
- : '↑↓ navigate, Enter select, Esc close';
111
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsxs(Text, { color: borderColor, bold: true, children: [title, " (", selectedIndex + 1, "/", items.length, ") - ", instructions] }) }), visibleItems.map((item, visibleIndex) => {
112
- const actualIndex = scrollOffset + visibleIndex;
113
- const isSelected = actualIndex === selectedIndex;
114
- return (_jsx(Box, { paddingX: 0, paddingY: 0, children: formatItem(item, isSelected) }, actualIndex));
115
- })] }));
112
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: borderColor, bold: true, children: title }) }), _jsx(Box, { flexDirection: "column", height: viewportItems, marginTop: 1, children: isLoading ? (_jsxs(_Fragment, { children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: loadingMessage }) }), Array.from({ length: Math.max(0, viewportItems - 1) }, (_, index) => (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `loading-spacer-${index}`)))] })) : items.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { color: "gray", children: emptyMessage }) }), Array.from({ length: Math.max(0, viewportItems - 1) }, (_, index) => (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `empty-spacer-${index}`)))] })) : (Array.from({ length: viewportItems }, (_, rowIndex) => {
113
+ const item = visibleItems[rowIndex];
114
+ if (item === undefined) {
115
+ return (_jsx(Box, { paddingX: 0, paddingY: 0, children: _jsx(Text, { children: " " }) }, `item-empty-${rowIndex}`));
116
+ }
117
+ const actualIndex = scrollOffset + rowIndex;
118
+ const isSelected = actualIndex === selectedIndex;
119
+ return (_jsx(Box, { paddingX: 0, paddingY: 0, children: formatItem(item, isSelected) }, actualIndex));
120
+ })) }), _jsx(Box, { paddingX: 0, paddingY: 0, marginTop: 1, children: instructionsOverride ? (_jsx(Text, { color: "gray", wrap: "truncate-end", children: instructionsOverride })) : (_jsx(HintBar, { hints: [
121
+ '↑↓ navigate',
122
+ supportsTab ? 'Tab load' : '',
123
+ 'Enter select',
124
+ 'Esc close',
125
+ ] })) })] }));
116
126
  }
117
127
  // Export with proper generic type support
118
128
  export const BaseSelector = forwardRef(BaseSelectorInner);
@@ -1 +1 @@
1
- {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/Header.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,WAAW,2CAoF1F"}
1
+ {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/Header.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGxD,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,WAAW,2CA6E1F"}
@@ -12,5 +12,5 @@ export function Header({ modelName, sessionId, hasActiveSession, startupInfo })
12
12
  ██║ ██║█████╗ ╚███╔╝ ██║ ██║ ██║
13
13
  ██║ ██║██╔══╝ ██╔██╗ ██║ ██║ ██║
14
14
  ██████╔╝███████╗██╔╝ ██╗ ██║ ╚██████╔╝
15
- ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝` }) }), _jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsx(Text, { color: "gray", children: "Model: " }), _jsx(Text, { color: "white", children: modelName }), hasActiveSession && sessionId && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: " \u2022 Session: " }), _jsx(Text, { color: "white", children: sessionId.slice(0, 8) })] }))] }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "gray", children: "Servers: " }), _jsx(Text, { color: "white", children: startupInfo.connectedServers.count }), _jsx(Text, { color: "gray", children: " \u2022 Tools: " }), _jsx(Text, { color: "white", children: startupInfo.toolCount })] }), startupInfo.failedConnections.length > 0 && (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { color: "yellowBright", children: ["\u26A0\uFE0F Failed: ", startupInfo.failedConnections.join(', ')] }) })), startupInfo.logFile && process.env.DEXTO_DEV_MODE === 'true' && (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { color: "gray", children: ["Logs: ", startupInfo.logFile] }) })), startupInfo.updateInfo && (_jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: ["\u2B06\uFE0F Update available: ", startupInfo.updateInfo.current, " \u2192", ' ', startupInfo.updateInfo.latest] }), _jsx(Text, { color: "gray", children: " \u2022 Run: " }), _jsx(Text, { color: "cyan", children: startupInfo.updateInfo.updateCommand })] })), startupInfo.needsAgentSync && (_jsxs(Box, { marginTop: startupInfo.updateInfo ? 0 : 1, flexDirection: "row", children: [_jsx(Text, { color: "yellow", children: "\uD83D\uDD04 Agent configs have updates available. Run: " }), _jsx(Text, { color: "cyan", children: "dexto sync-agents" })] })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: " " }) })] }));
15
+ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝` }) }), _jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsx(Text, { color: "gray", children: "Model: " }), _jsx(Text, { color: "white", children: modelName }), hasActiveSession && sessionId && (_jsxs(_Fragment, { children: [_jsx(Text, { color: "gray", children: " \u2022 Session: " }), _jsx(Text, { color: "white", children: sessionId.slice(0, 8) })] }))] }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "gray", children: "Servers: " }), _jsx(Text, { color: "white", children: startupInfo.connectedServers.count }), _jsx(Text, { color: "gray", children: " \u2022 Tools: " }), _jsx(Text, { color: "white", children: startupInfo.toolCount })] }), startupInfo.failedConnections.length > 0 && (_jsx(Box, { flexDirection: "row", children: _jsxs(Text, { color: "yellowBright", children: ["\u26A0\uFE0F Failed: ", startupInfo.failedConnections.join(', ')] }) })), startupInfo.updateInfo && (_jsxs(Box, { marginTop: 1, flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: ["\u2B06\uFE0F Update available: ", startupInfo.updateInfo.current, " \u2192", ' ', startupInfo.updateInfo.latest] }), _jsx(Text, { color: "gray", children: " \u2022 Run: " }), _jsx(Text, { color: "cyan", children: startupInfo.updateInfo.updateCommand })] })), startupInfo.needsAgentSync && (_jsxs(Box, { marginTop: startupInfo.updateInfo ? 0 : 1, flexDirection: "row", children: [_jsx(Text, { color: "yellow", children: "\uD83D\uDD04 Agent configs have updates available. Run: " }), _jsx(Text, { color: "cyan", children: "dexto sync-agents" })] })), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { children: " " }) })] }));
16
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAoD9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,mEACc,gBAAgB,6CAyOrD,CAAC"}
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAoD9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,mEACc,gBAAgB,6CA4OrD,CAAC"}
@@ -147,7 +147,9 @@ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
147
147
  trim: false,
148
148
  });
149
149
  const toolLines = wrappedToolText.split('\n');
150
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), subHeaderLine && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: subHeaderLine }) })), subAgentProgress && isRunning && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "gray", children: ["\u2514\u2500 ", subAgentProgress.toolsCalled, " tool", subAgentProgress.toolsCalled !== 1 ? 's' : '', " called | Current:", ' ', subAgentProgress.currentTool, subAgentProgress.tokenUsage &&
150
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), subHeaderLine && (_jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: subHeaderLine }) })), subAgentProgress && isRunning && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "gray", children: ["\u2514\u2500 ", subAgentProgress.toolsCalled, " tool", subAgentProgress.toolsCalled !== 1 ? 's' : '', " called | Current:", ' ', subAgentProgress.currentTool, subAgentProgress.runtimeAgentId
151
+ ? ` | Agent: ${subAgentProgress.agentId} (${subAgentProgress.runtimeAgentId})`
152
+ : ` | Agent: ${subAgentProgress.agentId}`, subAgentProgress.tokenUsage &&
151
153
  subAgentProgress.tokenUsage.total > 0
152
154
  ? ` | ${subAgentProgress.tokenUsage.total.toLocaleString()} tokens`
153
155
  : ''] }) })), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: ["\u23BF ", formatToolResultPreview(message.toolResult)] }) })))] }));
@@ -1 +1 @@
1
- {"version":3,"file":"ToolIcon.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/ToolIcon.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,UAAU,aAAa;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAKD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,aAAa,2CAqD1D"}
1
+ {"version":3,"file":"ToolIcon.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/ToolIcon.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAIvD,UAAU,aAAa;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,aAAa,2CAyC1D"}
@@ -3,10 +3,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  * ToolIcon Component
4
4
  * Animated icon for tool calls with status-based visual feedback
5
5
  */
6
- import { useState, useEffect } from 'react';
7
6
  import { Text } from 'ink';
8
- // Spinner frames for running animation
9
- const SPINNER_FRAMES = ['⠋', '⠙', '', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
7
+ import { useAnimationTick } from '../../hooks/useAnimationTick.js';
8
+ import { BRAILLE_SPINNER_FRAMES } from '../../constants/spinnerFrames.js';
10
9
  /**
11
10
  * Animated tool icon that changes based on execution status
12
11
  * - Running: Animated spinner (green/teal)
@@ -14,17 +13,8 @@ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧',
14
13
  * - Finished (error): Red dot
15
14
  */
16
15
  export function ToolIcon({ status, isError }) {
17
- const [frame, setFrame] = useState(0);
18
- // Animate spinner only when actually running (not during approval)
19
- useEffect(() => {
20
- if (status !== 'running') {
21
- return;
22
- }
23
- const interval = setInterval(() => {
24
- setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
25
- }, 80); // 80ms per frame for smooth animation
26
- return () => clearInterval(interval);
27
- }, [status]);
16
+ const tick = useAnimationTick({ enabled: status === 'running', intervalMs: 80 });
17
+ const frame = tick % BRAILLE_SPINNER_FRAMES.length;
28
18
  // Pending: static gray dot (tool call received, checking approval)
29
19
  if (status === 'pending') {
30
20
  return _jsx(Text, { color: "gray", children: "\u25CF " });
@@ -42,5 +32,5 @@ export function ToolIcon({ status, isError }) {
42
32
  return (_jsxs(Text, { color: "green", bold: true, children: ["\u25CF", ' '] }));
43
33
  }
44
34
  // Running state with spinner
45
- return (_jsxs(Text, { color: "green", bold: true, children: [SPINNER_FRAMES[frame], ' '] }));
35
+ return (_jsxs(Text, { color: "green", bold: true, children: [BRAILLE_SPINNER_FRAMES[frame], ' '] }));
46
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LogConfigBox.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,UAAU,iBAAiB;IACvB,IAAI,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA8BvD"}
1
+ {"version":3,"file":"LogConfigBox.d.ts","sourceRoot":"","sources":["../../../../../../src/cli/ink-cli/components/chat/styled-boxes/LogConfigBox.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAGnE,UAAU,iBAAiB;IACvB,IAAI,EAAE,mBAAmB,CAAC;CAC7B;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE,iBAAiB,2CA4BvD"}
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
3
  import { StyledBox, StyledRow, StyledListItem } from './StyledBox.js';
4
4
  export function LogConfigBox({ data }) {
5
- return (_jsxs(StyledBox, { title: "Logging Configuration", children: [_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StyledRow, { label: "Current level", value: data.currentLevel, valueColor: "green" }), data.logFile && process.env.DEXTO_DEV_MODE === 'true' && (_jsx(StyledRow, { label: "Log file", value: data.logFile }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Available levels (least to most verbose):" }), data.availableLevels.map((level) => {
5
+ return (_jsxs(StyledBox, { title: "Logging Configuration", children: [_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(StyledRow, { label: "Current level", value: data.currentLevel, valueColor: "green" }), data.logFile ? _jsx(StyledRow, { label: "Log file", value: data.logFile }) : null] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "gray", children: "Available levels (least to most verbose):" }), data.availableLevels.map((level) => {
6
6
  const isCurrent = level === data.currentLevel;
7
7
  return (_jsx(StyledListItem, { icon: isCurrent ? '>' : ' ', text: level, isActive: isCurrent }, level));
8
8
  })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "Use /log <level> to change level" }) })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA8BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CA4VzB"}
1
+ {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CAgWzB"}