@sunub/obsidian-mcp-server 0.3.4 → 0.3.9

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 (323) hide show
  1. package/build/config.d.ts +2 -27
  2. package/build/config.js +11 -31
  3. package/build/config.js.map +1 -1
  4. package/build/db.js +3 -0
  5. package/build/db.js.map +1 -1
  6. package/build/index.js +28 -6
  7. package/build/index.js.map +1 -1
  8. package/build/server.js +16 -13
  9. package/build/server.js.map +1 -1
  10. package/build/setup.d.ts +4 -0
  11. package/build/setup.js +114 -0
  12. package/build/setup.js.map +1 -0
  13. package/build/tools/create_document_with_properties/index.d.ts +5 -5
  14. package/build/tools/create_document_with_properties/index.js +2 -2
  15. package/build/tools/create_document_with_properties/index.js.map +1 -1
  16. package/build/tools/create_document_with_properties/params.d.ts +8 -8
  17. package/build/tools/create_document_with_properties/params.js +1 -1
  18. package/build/tools/create_document_with_properties/params.js.map +1 -1
  19. package/build/tools/generate_property/index.d.ts +1 -1
  20. package/build/tools/generate_property/index.js +1 -1
  21. package/build/tools/generate_property/index.js.map +1 -1
  22. package/build/tools/generate_property/params.d.ts +4 -4
  23. package/build/tools/index.d.ts +8 -8
  24. package/build/tools/index.js +5 -5
  25. package/build/tools/index.js.map +1 -1
  26. package/build/tools/organize_attachments/index.d.ts +1 -1
  27. package/build/tools/organize_attachments/index.js +2 -2
  28. package/build/tools/organize_attachments/index.js.map +1 -1
  29. package/build/tools/vault/index.d.ts +2 -2
  30. package/build/tools/vault/index.js +7 -8
  31. package/build/tools/vault/index.js.map +1 -1
  32. package/build/tools/vault/params.d.ts +4 -4
  33. package/build/tools/vault/types/collect_context.d.ts +58 -58
  34. package/build/tools/vault/types/collect_context.js +1 -1
  35. package/build/tools/vault/types/collect_context.js.map +1 -1
  36. package/build/tools/vault/types/list_all.d.ts +42 -42
  37. package/build/tools/vault/types/list_all.js +2 -2
  38. package/build/tools/vault/types/list_all.js.map +1 -1
  39. package/build/tools/vault/types/read_specific.d.ts +6 -6
  40. package/build/tools/vault/types/read_specific.js +1 -1
  41. package/build/tools/vault/types/read_specific.js.map +1 -1
  42. package/build/tools/vault/types/search.d.ts +70 -70
  43. package/build/tools/vault/utils/actions/collect_context.d.ts +1 -1
  44. package/build/tools/vault/utils/actions/collect_context.js +4 -4
  45. package/build/tools/vault/utils/actions/collect_context.js.map +1 -1
  46. package/build/tools/vault/utils/actions/list_all.d.ts +1 -1
  47. package/build/tools/vault/utils/actions/list_all.js +1 -1
  48. package/build/tools/vault/utils/actions/list_all.js.map +1 -1
  49. package/build/tools/vault/utils/actions/load_memory.d.ts +1 -1
  50. package/build/tools/vault/utils/actions/load_memory.js +3 -3
  51. package/build/tools/vault/utils/actions/load_memory.js.map +1 -1
  52. package/build/tools/vault/utils/actions/read.d.ts +1 -1
  53. package/build/tools/vault/utils/actions/read.js +1 -1
  54. package/build/tools/vault/utils/actions/read.js.map +1 -1
  55. package/build/tools/vault/utils/actions/search.d.ts +1 -1
  56. package/build/tools/vault/utils/actions/search.js +25 -15
  57. package/build/tools/vault/utils/actions/search.js.map +1 -1
  58. package/build/tools/vault/utils/shared.d.ts +1 -1
  59. package/build/tools/vault/utils.d.ts +7 -8
  60. package/build/tools/vault/utils.js +7 -8
  61. package/build/tools/vault/utils.js.map +1 -1
  62. package/build/tools/write_property/index.d.ts +5 -5
  63. package/build/tools/write_property/index.js +1 -1
  64. package/build/tools/write_property/index.js.map +1 -1
  65. package/build/tools/write_property/params.d.ts +8 -8
  66. package/build/utils/DirectoryWalker.d.ts +1 -1
  67. package/build/utils/Embedder.d.ts +11 -0
  68. package/build/utils/Embedder.js +46 -0
  69. package/build/utils/Embedder.js.map +1 -0
  70. package/build/utils/Indexer.d.ts +4 -2
  71. package/build/utils/Indexer.js +19 -2
  72. package/build/utils/Indexer.js.map +1 -1
  73. package/build/utils/LLMClient.js +16 -0
  74. package/build/utils/LLMClient.js.map +1 -1
  75. package/build/utils/LocalReranker.d.ts +17 -0
  76. package/build/utils/LocalReranker.js +71 -0
  77. package/build/utils/LocalReranker.js.map +1 -0
  78. package/build/utils/RAGIndexer.d.ts +10 -1
  79. package/build/utils/RAGIndexer.js +59 -27
  80. package/build/utils/RAGIndexer.js.map +1 -1
  81. package/build/utils/RerankerClient.d.ts +6 -6
  82. package/build/utils/RerankerClient.js +20 -1
  83. package/build/utils/RerankerClient.js.map +1 -1
  84. package/build/utils/VaultManger/VaultManager.d.ts +38 -2
  85. package/build/utils/VaultManger/VaultManager.js +268 -13
  86. package/build/utils/VaultManger/VaultManager.js.map +1 -1
  87. package/build/utils/VaultManger/index.d.ts +3 -3
  88. package/build/utils/VaultManger/index.js +2 -2
  89. package/build/utils/VaultManger/index.js.map +1 -1
  90. package/build/utils/VaultManger/types.d.ts +1 -1
  91. package/build/utils/VaultWatcher.d.ts +1 -1
  92. package/build/utils/VaultWatcher.js +12 -55
  93. package/build/utils/VaultWatcher.js.map +1 -1
  94. package/build/utils/VectorDB.js +31 -6
  95. package/build/utils/VectorDB.js.map +1 -1
  96. package/build/utils/constants.d.ts +3 -0
  97. package/build/utils/constants.js +14 -0
  98. package/build/utils/constants.js.map +1 -0
  99. package/build/utils/getVaultManager.d.ts +1 -1
  100. package/build/utils/getVaultManager.js +1 -1
  101. package/build/utils/getVaultManager.js.map +1 -1
  102. package/build/utils/processor/MatterParser.d.ts +1 -1
  103. package/build/utils/processor/MatterParser.js +1 -1
  104. package/build/utils/processor/MatterParser.js.map +1 -1
  105. package/build/utils/processor/types.d.ts +4 -0
  106. package/build/utils/processor/types.js +1 -0
  107. package/build/utils/processor/types.js.map +1 -1
  108. package/package.json +16 -49
  109. package/LICENSE +0 -15
  110. package/README.md +0 -512
  111. package/build/cli/AppContainer.d.ts +0 -1
  112. package/build/cli/AppContainer.js +0 -197
  113. package/build/cli/AppContainer.js.map +0 -1
  114. package/build/cli/app.d.ts +0 -1
  115. package/build/cli/app.js +0 -7
  116. package/build/cli/app.js.map +0 -1
  117. package/build/cli/colors.d.ts +0 -15
  118. package/build/cli/colors.js +0 -15
  119. package/build/cli/colors.js.map +0 -1
  120. package/build/cli/commands/types.d.ts +0 -19
  121. package/build/cli/commands/types.js +0 -11
  122. package/build/cli/commands/types.js.map +0 -1
  123. package/build/cli/config/SettingContext.d.ts +0 -1
  124. package/build/cli/config/SettingContext.js +0 -2
  125. package/build/cli/config/SettingContext.js.map +0 -1
  126. package/build/cli/config/mcpServersConfig.d.ts +0 -30
  127. package/build/cli/config/mcpServersConfig.js +0 -119
  128. package/build/cli/config/mcpServersConfig.js.map +0 -1
  129. package/build/cli/config/settingSchema.d.ts +0 -180
  130. package/build/cli/config/settingSchema.js +0 -128
  131. package/build/cli/config/settingSchema.js.map +0 -1
  132. package/build/cli/constants.d.ts +0 -47
  133. package/build/cli/constants.js +0 -101
  134. package/build/cli/constants.js.map +0 -1
  135. package/build/cli/context/InputContext.d.ts +0 -12
  136. package/build/cli/context/InputContext.js +0 -10
  137. package/build/cli/context/InputContext.js.map +0 -1
  138. package/build/cli/context/KeypressContext.d.ts +0 -30
  139. package/build/cli/context/KeypressContext.js +0 -200
  140. package/build/cli/context/KeypressContext.js.map +0 -1
  141. package/build/cli/context/KeypressContext.util.d.ts +0 -7
  142. package/build/cli/context/KeypressContext.util.js +0 -474
  143. package/build/cli/context/KeypressContext.util.js.map +0 -1
  144. package/build/cli/hooks/useAtCompletion.d.ts +0 -12
  145. package/build/cli/hooks/useAtCompletion.js +0 -5
  146. package/build/cli/hooks/useAtCompletion.js.map +0 -1
  147. package/build/cli/hooks/useCommandCompletion.d.ts +0 -59
  148. package/build/cli/hooks/useCommandCompletion.js +0 -344
  149. package/build/cli/hooks/useCommandCompletion.js.map +0 -1
  150. package/build/cli/hooks/useCompletion.d.ts +0 -22
  151. package/build/cli/hooks/useCompletion.js +0 -84
  152. package/build/cli/hooks/useCompletion.js.map +0 -1
  153. package/build/cli/hooks/useDispatcher.d.ts +0 -6
  154. package/build/cli/hooks/useDispatcher.js +0 -286
  155. package/build/cli/hooks/useDispatcher.js.map +0 -1
  156. package/build/cli/hooks/useFocus.d.ts +0 -2
  157. package/build/cli/hooks/useFocus.js +0 -3
  158. package/build/cli/hooks/useFocus.js.map +0 -1
  159. package/build/cli/hooks/useInputHistory.d.ts +0 -24
  160. package/build/cli/hooks/useInputHistory.js +0 -159
  161. package/build/cli/hooks/useInputHistory.js.map +0 -1
  162. package/build/cli/hooks/useKeyMatchers.d.ts +0 -17
  163. package/build/cli/hooks/useKeyMatchers.js +0 -13
  164. package/build/cli/hooks/useKeyMatchers.js.map +0 -1
  165. package/build/cli/hooks/useKeypress.d.ts +0 -19
  166. package/build/cli/hooks/useKeypress.js +0 -28
  167. package/build/cli/hooks/useKeypress.js.map +0 -1
  168. package/build/cli/hooks/useLlmStream.d.ts +0 -12
  169. package/build/cli/hooks/useLlmStream.js +0 -365
  170. package/build/cli/hooks/useLlmStream.js.map +0 -1
  171. package/build/cli/hooks/useMcpClient.d.ts +0 -10
  172. package/build/cli/hooks/useMcpClient.js +0 -100
  173. package/build/cli/hooks/useMcpClient.js.map +0 -1
  174. package/build/cli/hooks/useMcpManager.d.ts +0 -22
  175. package/build/cli/hooks/useMcpManager.js +0 -86
  176. package/build/cli/hooks/useMcpManager.js.map +0 -1
  177. package/build/cli/hooks/usePromptCompletion.d.ts +0 -19
  178. package/build/cli/hooks/usePromptCompletion.js +0 -13
  179. package/build/cli/hooks/usePromptCompletion.js.map +0 -1
  180. package/build/cli/hooks/useRagContext.d.ts +0 -6
  181. package/build/cli/hooks/useRagContext.js +0 -83
  182. package/build/cli/hooks/useRagContext.js.map +0 -1
  183. package/build/cli/hooks/useShellCompletion.d.ts +0 -18
  184. package/build/cli/hooks/useShellCompletion.js +0 -10
  185. package/build/cli/hooks/useShellCompletion.js.map +0 -1
  186. package/build/cli/hooks/useSlashCompletion.d.ts +0 -21
  187. package/build/cli/hooks/useSlashCompletion.js +0 -11
  188. package/build/cli/hooks/useSlashCompletion.js.map +0 -1
  189. package/build/cli/hooks/useTerminalSize.d.ts +0 -9
  190. package/build/cli/hooks/useTerminalSize.js +0 -26
  191. package/build/cli/hooks/useTerminalSize.js.map +0 -1
  192. package/build/cli/index.d.ts +0 -1
  193. package/build/cli/index.js +0 -39
  194. package/build/cli/index.js.map +0 -1
  195. package/build/cli/key/input.d.ts +0 -5
  196. package/build/cli/key/input.js +0 -9
  197. package/build/cli/key/input.js.map +0 -1
  198. package/build/cli/key/keyBindings.d.ts +0 -152
  199. package/build/cli/key/keyBindings.js +0 -630
  200. package/build/cli/key/keyBindings.js.map +0 -1
  201. package/build/cli/key/keyMatchers.d.ts +0 -27
  202. package/build/cli/key/keyMatchers.js +0 -33
  203. package/build/cli/key/keyMatchers.js.map +0 -1
  204. package/build/cli/key/mouse.d.ts +0 -23
  205. package/build/cli/key/mouse.js +0 -155
  206. package/build/cli/key/mouse.js.map +0 -1
  207. package/build/cli/key/text-buffer-bestcase.d.ts +0 -867
  208. package/build/cli/key/text-buffer-bestcase.js +0 -2754
  209. package/build/cli/key/text-buffer-bestcase.js.map +0 -1
  210. package/build/cli/key/text-buffer.d.ts +0 -509
  211. package/build/cli/key/text-buffer.js +0 -918
  212. package/build/cli/key/text-buffer.js.map +0 -1
  213. package/build/cli/main.d.ts +0 -1
  214. package/build/cli/main.js +0 -20
  215. package/build/cli/main.js.map +0 -1
  216. package/build/cli/reference/HistoryItemDisplay.d.ts +0 -23
  217. package/build/cli/reference/HistoryItemDisplay.js +0 -63
  218. package/build/cli/reference/HistoryItemDisplay.js.map +0 -1
  219. package/build/cli/reference/InputContext.d.ts +0 -17
  220. package/build/cli/reference/InputContext.js +0 -16
  221. package/build/cli/reference/InputContext.js.map +0 -1
  222. package/build/cli/reference/KerypressContext.d.ts +0 -37
  223. package/build/cli/reference/KerypressContext.js +0 -791
  224. package/build/cli/reference/KerypressContext.js.map +0 -1
  225. package/build/cli/reference/MainContent.d.ts +0 -6
  226. package/build/cli/reference/MainContent.js +0 -207
  227. package/build/cli/reference/MainContent.js.map +0 -1
  228. package/build/cli/reference/UIStateContext.d.ts +0 -188
  229. package/build/cli/reference/UIStateContext.js +0 -16
  230. package/build/cli/reference/UIStateContext.js.map +0 -1
  231. package/build/cli/reference/text-buffer.d.ts +0 -867
  232. package/build/cli/reference/text-buffer.js +0 -2754
  233. package/build/cli/reference/text-buffer.js.map +0 -1
  234. package/build/cli/reference/useGeminiStream.d.ts +0 -42
  235. package/build/cli/reference/useGeminiStream.js +0 -1404
  236. package/build/cli/reference/useGeminiStream.js.map +0 -1
  237. package/build/cli/services/McpClientService.d.ts +0 -32
  238. package/build/cli/services/McpClientService.js +0 -123
  239. package/build/cli/services/McpClientService.js.map +0 -1
  240. package/build/cli/services/McpManager.d.ts +0 -28
  241. package/build/cli/services/McpManager.js +0 -146
  242. package/build/cli/services/McpManager.js.map +0 -1
  243. package/build/cli/theme/builtin/default-dark.d.ts +0 -7
  244. package/build/cli/theme/builtin/default-dark.js +0 -143
  245. package/build/cli/theme/builtin/default-dark.js.map +0 -1
  246. package/build/cli/theme/builtin/no-color.d.ts +0 -7
  247. package/build/cli/theme/builtin/no-color.js +0 -125
  248. package/build/cli/theme/builtin/no-color.js.map +0 -1
  249. package/build/cli/theme/builtin/tokyonight-dark.d.ts +0 -7
  250. package/build/cli/theme/builtin/tokyonight-dark.js +0 -146
  251. package/build/cli/theme/builtin/tokyonight-dark.js.map +0 -1
  252. package/build/cli/theme/semantic-colors.d.ts +0 -2
  253. package/build/cli/theme/semantic-colors.js +0 -19
  254. package/build/cli/theme/semantic-colors.js.map +0 -1
  255. package/build/cli/theme/semantic-tokens.d.ts +0 -42
  256. package/build/cli/theme/semantic-tokens.js +0 -77
  257. package/build/cli/theme/semantic-tokens.js.map +0 -1
  258. package/build/cli/theme/theme-manager.d.ts +0 -32
  259. package/build/cli/theme/theme-manager.js +0 -150
  260. package/build/cli/theme/theme-manager.js.map +0 -1
  261. package/build/cli/theme/theme.d.ts +0 -107
  262. package/build/cli/theme/theme.js +0 -333
  263. package/build/cli/theme/theme.js.map +0 -1
  264. package/build/cli/tmp.d.ts +0 -1
  265. package/build/cli/tmp.js +0 -27
  266. package/build/cli/tmp.js.map +0 -1
  267. package/build/cli/types.d.ts +0 -72
  268. package/build/cli/types.js +0 -8
  269. package/build/cli/types.js.map +0 -1
  270. package/build/cli/ui/ConnectionStatus.d.ts +0 -13
  271. package/build/cli/ui/ConnectionStatus.js +0 -24
  272. package/build/cli/ui/ConnectionStatus.js.map +0 -1
  273. package/build/cli/ui/HelpCommands.d.ts +0 -5
  274. package/build/cli/ui/HelpCommands.js +0 -11
  275. package/build/cli/ui/HelpCommands.js.map +0 -1
  276. package/build/cli/ui/HistoryItemDisplay.d.ts +0 -9
  277. package/build/cli/ui/HistoryItemDisplay.js +0 -38
  278. package/build/cli/ui/HistoryItemDisplay.js.map +0 -1
  279. package/build/cli/ui/InputPrompt-bestcase.d.ts +0 -63
  280. package/build/cli/ui/InputPrompt-bestcase.js +0 -1301
  281. package/build/cli/ui/InputPrompt-bestcase.js.map +0 -1
  282. package/build/cli/ui/InputPrompt.d.ts +0 -12
  283. package/build/cli/ui/InputPrompt.js +0 -145
  284. package/build/cli/ui/InputPrompt.js.map +0 -1
  285. package/build/cli/ui/InputPrompt.tmp.d.ts +0 -7
  286. package/build/cli/ui/InputPrompt.tmp.js +0 -215
  287. package/build/cli/ui/InputPrompt.tmp.js.map +0 -1
  288. package/build/cli/ui/MainContent.d.ts +0 -10
  289. package/build/cli/ui/MainContent.js +0 -24
  290. package/build/cli/ui/MainContent.js.map +0 -1
  291. package/build/cli/ui/MouseContext.d.ts +0 -1
  292. package/build/cli/ui/MouseContext.js +0 -2
  293. package/build/cli/ui/MouseContext.js.map +0 -1
  294. package/build/cli/ui/SuggestionsDisplay.d.ts +0 -25
  295. package/build/cli/ui/SuggestionsDisplay.js +0 -45
  296. package/build/cli/ui/SuggestionsDisplay.js.map +0 -1
  297. package/build/cli/ui/ThinkingIndicator.d.ts +0 -2
  298. package/build/cli/ui/ThinkingIndicator.js +0 -16
  299. package/build/cli/ui/ThinkingIndicator.js.map +0 -1
  300. package/build/cli/ui/layout/DefaultAppLayout.d.ts +0 -2
  301. package/build/cli/ui/layout/DefaultAppLayout.js +0 -6
  302. package/build/cli/ui/layout/DefaultAppLayout.js.map +0 -1
  303. package/build/cli/ui/shared/ExpandableText.d.ts +0 -14
  304. package/build/cli/ui/shared/ExpandableText.js +0 -19
  305. package/build/cli/ui/shared/ExpandableText.js.map +0 -1
  306. package/build/cli/utils/cache.d.ts +0 -21
  307. package/build/cli/utils/cache.js +0 -56
  308. package/build/cli/utils/cache.js.map +0 -1
  309. package/build/cli/utils/commandUtils.d.ts +0 -11
  310. package/build/cli/utils/commandUtils.js +0 -16
  311. package/build/cli/utils/commandUtils.js.map +0 -1
  312. package/build/cli/utils/debugLogger.d.ts +0 -12
  313. package/build/cli/utils/debugLogger.js +0 -54
  314. package/build/cli/utils/debugLogger.js.map +0 -1
  315. package/build/cli/utils/historyStorage.d.ts +0 -13
  316. package/build/cli/utils/historyStorage.js +0 -64
  317. package/build/cli/utils/historyStorage.js.map +0 -1
  318. package/build/cli/utils/textUtil.d.ts +0 -68
  319. package/build/cli/utils/textUtil.js +0 -236
  320. package/build/cli/utils/textUtil.js.map +0 -1
  321. package/build/tools/vault/utils/actions/search_semantic.d.ts +0 -3
  322. package/build/tools/vault/utils/actions/search_semantic.js +0 -71
  323. package/build/tools/vault/utils/actions/search_semantic.js.map +0 -1
@@ -1,1301 +0,0 @@
1
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
- // @ts-nocheck
3
- /**
4
- * @license
5
- * Copyright 2025 Google LLC
6
- * SPDX-License-Identifier: Apache-2.0
7
- */
8
- import * as path from "node:path";
9
- import { ApprovalMode, coreEvents, debugLogger, } from "@google/gemini-cli-core";
10
- import chalk from "chalk";
11
- import clipboardy from "clipboardy";
12
- import { Box, Text, useStdout } from "ink";
13
- import { Fragment, useCallback, useEffect, useMemo, useRef, useState, } from "react";
14
- import stringWidth from "string-width";
15
- import { parseSlashCommand } from "../../utils/commands.js";
16
- import { AppEvent, appEvents, TransientMessageType, } from "../../utils/events.js";
17
- import { useInputState } from "../contexts/InputContext.js";
18
- import { useMouse } from "../contexts/MouseContext.js";
19
- import { useSettings } from "../contexts/SettingsContext.js";
20
- import { useShellFocusState } from "../contexts/ShellFocusContext.js";
21
- import { useUIActions } from "../contexts/UIActionsContext.js";
22
- import { useUIState } from "../contexts/UIStateContext.js";
23
- import { escapeAtSymbols } from "../hooks/atCommandProcessor.js";
24
- import { useAlternateBuffer } from "../hooks/useAlternateBuffer.js";
25
- import { CompletionMode, useCommandCompletion, } from "../hooks/useCommandCompletion.js";
26
- import { useInputHistory } from "../hooks/useInputHistory.js";
27
- import { useKeyMatchers } from "../hooks/useKeyMatchers.js";
28
- import { useKeypress } from "../hooks/useKeypress.js";
29
- import { useKittyKeyboardProtocol } from "../hooks/useKittyKeyboardProtocol.js";
30
- import { useMouseClick } from "../hooks/useMouseClick.js";
31
- import { useRepeatedKeyPress } from "../hooks/useRepeatedKeyPress.js";
32
- import { useReverseSearchCompletion } from "../hooks/useReverseSearchCompletion.js";
33
- import { useShellHistory } from "../hooks/useShellHistory.js";
34
- import { formatCommand } from "../key/keybindingUtils.js";
35
- import { Command } from "../key/keyMatchers.js";
36
- import { theme } from "../semantic-colors.js";
37
- import { SCREEN_READER_USER_PREFIX } from "../textConstants.js";
38
- import { getSafeLowColorBackground } from "../themes/color-utils.js";
39
- import { StreamingState } from "../types.js";
40
- import { cleanupOldClipboardImages, clipboardHasImage, saveClipboardImage, } from "../utils/clipboardUtils.js";
41
- import { isAutoExecutableCommand, isSlashCommand, } from "../utils/commandUtils.js";
42
- import { parseInputForHighlighting, parseSegmentsFromTokens, } from "../utils/highlight.js";
43
- import { useIsHelpDismissKey } from "../utils/shortcutsHelp.js";
44
- import { isLowColorDepth } from "../utils/terminalUtils.js";
45
- import { cpIndexToOffset, cpLen, cpSlice, toCodePoints, } from "../utils/textUtils.js";
46
- import { MAX_WIDTH, SuggestionsDisplay } from "./SuggestionsDisplay.js";
47
- import { HalfLinePaddedBox } from "./shared/HalfLinePaddedBox.js";
48
- import { ScrollableList, } from "./shared/ScrollableList.js";
49
- import { expandPastePlaceholders, getTransformUnderCursor, LARGE_PASTE_CHAR_THRESHOLD, LARGE_PASTE_LINE_THRESHOLD, logicalPosToOffset, } from "./shared/text-buffer.js";
50
- /**
51
- * Returns if the terminal can be trusted to handle paste events atomically
52
- * rather than potentially sending multiple paste events separated by line
53
- * breaks which could trigger unintended command execution.
54
- */
55
- export function isTerminalPasteTrusted(kittyProtocolSupported) {
56
- // Ideally we could trust all VSCode family terminals as well but it appears
57
- // we cannot as Cursor users on windows reported being impacted by this
58
- // issue (https://github.com/google-gemini/gemini-cli/issues/3763).
59
- return kittyProtocolSupported;
60
- }
61
- // The input content, input container, and input suggestions list may have different widths
62
- export const calculatePromptWidths = (mainContentWidth) => {
63
- const FRAME_PADDING_AND_BORDER = 4; // Border (2) + padding (2)
64
- const PROMPT_PREFIX_WIDTH = 2; // '> ' or '! '
65
- const FRAME_OVERHEAD = FRAME_PADDING_AND_BORDER + PROMPT_PREFIX_WIDTH;
66
- const suggestionsWidth = Math.max(20, mainContentWidth);
67
- return {
68
- inputWidth: Math.max(mainContentWidth - FRAME_OVERHEAD, 1),
69
- containerWidth: mainContentWidth,
70
- suggestionsWidth,
71
- frameOverhead: FRAME_OVERHEAD,
72
- };
73
- };
74
- /**
75
- * Returns true if the given text exceeds the thresholds for being considered a "large paste".
76
- */
77
- export function isLargePaste(text) {
78
- const pasteLineCount = text.split("\n").length;
79
- return (pasteLineCount > LARGE_PASTE_LINE_THRESHOLD ||
80
- text.length > LARGE_PASTE_CHAR_THRESHOLD);
81
- }
82
- const DOUBLE_TAB_CLEAN_UI_TOGGLE_WINDOW_MS = 350;
83
- /**
84
- * Attempt to toggle expansion of a paste placeholder in the buffer.
85
- * Returns true if a toggle action was performed or hint was shown, false otherwise.
86
- */
87
- export function tryTogglePasteExpansion(buffer) {
88
- if (!buffer.pastedContent || Object.keys(buffer.pastedContent).length === 0) {
89
- return false;
90
- }
91
- const [row, col] = buffer.cursor;
92
- // 1. Check if cursor is on or immediately after a collapsed placeholder
93
- const transform = getTransformUnderCursor(row, col, buffer.transformationsByLine, { includeEdge: true });
94
- if (transform?.type === "paste" && transform.id) {
95
- buffer.togglePasteExpansion(transform.id, row, col);
96
- return true;
97
- }
98
- // 2. Check if cursor is inside an expanded paste region — collapse it
99
- const expandedId = buffer.getExpandedPasteAtLine(row);
100
- if (expandedId) {
101
- buffer.togglePasteExpansion(expandedId, row, col);
102
- return true;
103
- }
104
- // 3. Placeholders exist but cursor isn't on one — show hint
105
- appEvents.emit(AppEvent.TransientMessage, {
106
- message: "Move cursor within placeholder to expand",
107
- type: TransientMessageType.Hint,
108
- });
109
- return true;
110
- }
111
- export const InputPrompt = ({ onSubmit, onClearScreen, config, slashCommands, commandContext, placeholder = " Type your message or @path/to/file", focus = true, setShellModeActive, approvalMode, onEscapePromptChange, onSuggestionsVisibilityChange, vimHandleInput, isEmbeddedShellFocused, setQueueErrorMessage, streamingState, popAllMessages, onQueueMessage, suggestionsPosition = "below", setBannerVisible, }) => {
112
- const inputState = useInputState();
113
- const { buffer, userMessages, shellModeActive, copyModeEnabled, inputWidth, suggestionsWidth, } = inputState;
114
- const isHelpDismissKey = useIsHelpDismissKey();
115
- const keyMatchers = useKeyMatchers();
116
- const { stdout } = useStdout();
117
- const { merged: settings } = useSettings();
118
- const kittyProtocol = useKittyKeyboardProtocol();
119
- const isShellFocused = useShellFocusState();
120
- const { setEmbeddedShellFocused, setShortcutsHelpVisible, toggleCleanUiDetailsVisible, } = useUIActions();
121
- const { terminalWidth, activePtyId, history, backgroundTasks, backgroundTaskHeight, shortcutsHelpVisible, } = useUIState();
122
- const [suppressCompletion, setSuppressCompletion] = useState(false);
123
- const { handlePress: registerPlainTabPress, resetCount: resetPlainTabPress } = useRepeatedKeyPress({
124
- windowMs: DOUBLE_TAB_CLEAN_UI_TOGGLE_WINDOW_MS,
125
- });
126
- const [showEscapePrompt, setShowEscapePrompt] = useState(false);
127
- const { handlePress: handleEscPress, resetCount: resetEscapeState } = useRepeatedKeyPress({
128
- windowMs: 500,
129
- onRepeat: (count) => {
130
- if (count === 1) {
131
- setShowEscapePrompt(true);
132
- }
133
- else if (count === 2) {
134
- resetEscapeState();
135
- if (buffer.text.length > 0) {
136
- buffer.setText("");
137
- resetCompletionState();
138
- }
139
- else if (history.length > 0) {
140
- onSubmit("/rewind");
141
- }
142
- else {
143
- coreEvents.emitFeedback("info", "Nothing to rewind to");
144
- }
145
- }
146
- },
147
- onReset: () => setShowEscapePrompt(false),
148
- });
149
- const [recentUnsafePasteTime, setRecentUnsafePasteTime] = useState(null);
150
- const pasteTimeoutRef = useRef(null);
151
- const innerBoxRef = useRef(null);
152
- const hasUserNavigatedSuggestions = useRef(false);
153
- const listRef = useRef(null);
154
- const [reverseSearchActive, setReverseSearchActive] = useState(false);
155
- const [commandSearchActive, setCommandSearchActive] = useState(false);
156
- const [textBeforeReverseSearch, setTextBeforeReverseSearch] = useState("");
157
- const [cursorPosition, setCursorPosition] = useState([
158
- 0, 0,
159
- ]);
160
- const [expandedSuggestionIndex, setExpandedSuggestionIndex] = useState(-1);
161
- const shellHistory = useShellHistory(config.getProjectRoot(), config.storage);
162
- const shellHistoryData = shellHistory.history;
163
- const completion = useCommandCompletion({
164
- buffer,
165
- cwd: config.getTargetDir(),
166
- slashCommands,
167
- commandContext,
168
- reverseSearchActive,
169
- shellModeActive,
170
- config,
171
- active: !suppressCompletion,
172
- });
173
- const reverseSearchCompletion = useReverseSearchCompletion(buffer, shellHistoryData, reverseSearchActive);
174
- const reversedUserMessages = useMemo(() => [...userMessages].reverse(), [userMessages]);
175
- const commandSearchCompletion = useReverseSearchCompletion(buffer, reversedUserMessages, commandSearchActive);
176
- const resetCompletionState = completion.resetCompletionState;
177
- const resetReverseSearchCompletionState = reverseSearchCompletion.resetCompletionState;
178
- const resetCommandSearchCompletionState = commandSearchCompletion.resetCompletionState;
179
- const getActiveCompletion = useCallback(() => {
180
- if (commandSearchActive)
181
- return commandSearchCompletion;
182
- if (reverseSearchActive)
183
- return reverseSearchCompletion;
184
- return completion;
185
- }, [
186
- commandSearchActive,
187
- commandSearchCompletion,
188
- reverseSearchActive,
189
- reverseSearchCompletion,
190
- completion,
191
- ]);
192
- const activeCompletion = getActiveCompletion();
193
- const shouldShowSuggestions = activeCompletion.showSuggestions;
194
- const { forceShowShellSuggestions, setForceShowShellSuggestions, isShellSuggestionsVisible, } = completion;
195
- const showCursor = focus && isShellFocused && !isEmbeddedShellFocused && !copyModeEnabled;
196
- useEffect(() => {
197
- appEvents.emit(AppEvent.ScrollToBottom);
198
- }, []);
199
- // Notify parent component about escape prompt state changes
200
- useEffect(() => {
201
- if (onEscapePromptChange) {
202
- onEscapePromptChange(showEscapePrompt);
203
- }
204
- }, [showEscapePrompt, onEscapePromptChange]);
205
- // Clear paste timeout on unmount
206
- useEffect(() => () => {
207
- if (pasteTimeoutRef.current) {
208
- clearTimeout(pasteTimeoutRef.current);
209
- }
210
- }, []);
211
- const handleSubmitAndClear = useCallback((submittedValue) => {
212
- let processedValue = submittedValue;
213
- if (buffer.pastedContent) {
214
- processedValue = expandPastePlaceholders(processedValue, buffer.pastedContent);
215
- }
216
- if (shellModeActive) {
217
- shellHistory.addCommandToHistory(processedValue);
218
- }
219
- // Clear the buffer *before* calling onSubmit to prevent potential re-submission
220
- // if onSubmit triggers a re-render while the buffer still holds the old value.
221
- buffer.setText("");
222
- onSubmit(processedValue);
223
- resetCompletionState();
224
- resetReverseSearchCompletionState();
225
- }, [
226
- buffer,
227
- onSubmit,
228
- resetCompletionState,
229
- shellModeActive,
230
- shellHistory,
231
- resetReverseSearchCompletionState,
232
- ]);
233
- const customSetTextAndResetCompletionSignal = useCallback((newText, cursorPosition) => {
234
- buffer.setText(newText, cursorPosition);
235
- setSuppressCompletion(true);
236
- }, [buffer]);
237
- const inputHistory = useInputHistory({
238
- userMessages,
239
- onSubmit: handleSubmitAndClear,
240
- isActive: (!(completion.showSuggestions && isShellSuggestionsVisible) ||
241
- completion.suggestions.length === 1) &&
242
- !shellModeActive,
243
- currentQuery: buffer.text,
244
- currentCursorOffset: buffer.getOffset(),
245
- onChange: customSetTextAndResetCompletionSignal,
246
- });
247
- const handleSubmit = useCallback((submittedValue) => {
248
- const trimmedMessage = submittedValue.trim();
249
- const isSlash = isSlashCommand(trimmedMessage);
250
- const isShell = shellModeActive;
251
- if ((isSlash || isShell) &&
252
- streamingState === StreamingState.Responding) {
253
- if (isSlash) {
254
- const { commandToExecute } = parseSlashCommand(trimmedMessage, slashCommands);
255
- if (commandToExecute?.isSafeConcurrent) {
256
- handleSubmitAndClear(trimmedMessage);
257
- return;
258
- }
259
- }
260
- setQueueErrorMessage(`${isShell ? "Shell" : "Slash"} commands cannot be queued`);
261
- return;
262
- }
263
- inputHistory.handleSubmit(trimmedMessage);
264
- }, [
265
- inputHistory,
266
- shellModeActive,
267
- streamingState,
268
- setQueueErrorMessage,
269
- slashCommands,
270
- handleSubmitAndClear,
271
- ]);
272
- // Effect to reset completion if history navigation just occurred and set the text
273
- useEffect(() => {
274
- if (suppressCompletion) {
275
- resetCompletionState();
276
- resetReverseSearchCompletionState();
277
- resetCommandSearchCompletionState();
278
- setExpandedSuggestionIndex(-1);
279
- }
280
- }, [
281
- suppressCompletion,
282
- resetCompletionState,
283
- resetReverseSearchCompletionState,
284
- resetCommandSearchCompletionState,
285
- ]);
286
- // Helper function to handle loading queued messages into input
287
- // Returns true if we should continue with input history navigation
288
- const tryLoadQueuedMessages = useCallback(() => {
289
- if (buffer.text.trim() === "" && popAllMessages) {
290
- const allMessages = popAllMessages();
291
- if (allMessages) {
292
- buffer.setText(allMessages);
293
- return true;
294
- }
295
- else {
296
- // No queued messages, proceed with input history
297
- inputHistory.navigateUp();
298
- }
299
- return true; // We handled the up arrow key
300
- }
301
- return false;
302
- }, [buffer, popAllMessages, inputHistory]);
303
- // Handle clipboard image pasting with Ctrl+V
304
- const handleClipboardPaste = useCallback(async () => {
305
- if (shortcutsHelpVisible) {
306
- setShortcutsHelpVisible(false);
307
- }
308
- try {
309
- if (await clipboardHasImage()) {
310
- const imagePath = await saveClipboardImage(config.getTargetDir());
311
- if (imagePath) {
312
- // Clean up old images
313
- cleanupOldClipboardImages(config.getTargetDir()).catch(() => {
314
- // Ignore cleanup errors
315
- });
316
- // Get relative path from current directory
317
- const relativePath = path.relative(config.getTargetDir(), imagePath);
318
- // Insert @path reference at cursor position
319
- const insertText = `@${relativePath}`;
320
- const currentText = buffer.text;
321
- const offset = buffer.getOffset();
322
- // Add spaces around the path if needed
323
- let textToInsert = insertText;
324
- const charBefore = offset > 0 ? currentText[offset - 1] : "";
325
- const charAfter = offset < currentText.length ? currentText[offset] : "";
326
- if (charBefore && charBefore !== " " && charBefore !== "\n") {
327
- textToInsert = ` ${textToInsert}`;
328
- }
329
- if (!charAfter || (charAfter !== " " && charAfter !== "\n")) {
330
- textToInsert = `${textToInsert} `;
331
- }
332
- // Insert at cursor position
333
- buffer.replaceRangeByOffset(offset, offset, textToInsert);
334
- }
335
- }
336
- if (settings.experimental?.useOSC52Paste) {
337
- stdout.write("\x1b]52;c;?\x07");
338
- }
339
- else {
340
- const textToInsert = await clipboardy.read();
341
- const escapedText = settings.ui?.escapePastedAtSymbols
342
- ? escapeAtSymbols(textToInsert)
343
- : textToInsert;
344
- buffer.insert(escapedText, { paste: true });
345
- if (isLargePaste(textToInsert)) {
346
- appEvents.emit(AppEvent.TransientMessage, {
347
- message: `Press ${formatCommand(Command.EXPAND_PASTE)} to expand pasted text`,
348
- type: TransientMessageType.Hint,
349
- });
350
- }
351
- }
352
- }
353
- catch (error) {
354
- debugLogger.error("Error handling paste:", error);
355
- }
356
- }, [
357
- buffer,
358
- config,
359
- stdout,
360
- settings,
361
- shortcutsHelpVisible,
362
- setShortcutsHelpVisible,
363
- ]);
364
- useMouseClick(innerBoxRef, (_event, relX, relY) => {
365
- setSuppressCompletion(true);
366
- if (isEmbeddedShellFocused) {
367
- setEmbeddedShellFocused(false);
368
- }
369
- const currentScrollTop = Math.round(listRef.current?.getScrollState().scrollTop ?? buffer.visualScrollRow);
370
- const visualRow = currentScrollTop + relY;
371
- buffer.moveToVisualPosition(visualRow, relX);
372
- }, { isActive: focus });
373
- const isAlternateBuffer = useAlternateBuffer();
374
- // Double-click to expand/collapse paste placeholders
375
- useMouseClick(innerBoxRef, (_event, relX, relY) => {
376
- if (!isAlternateBuffer)
377
- return;
378
- const currentScrollTop = Math.round(listRef.current?.getScrollState().scrollTop ?? buffer.visualScrollRow);
379
- const visualLine = buffer.allVisualLines[currentScrollTop + relY];
380
- if (!visualLine)
381
- return;
382
- // Even if we click past the end of the line, we might want to collapse an expanded paste
383
- const isPastEndOfLine = relX >= stringWidth(visualLine);
384
- const logicalPos = isPastEndOfLine
385
- ? null
386
- : buffer.getLogicalPositionFromVisual(currentScrollTop + relY, relX);
387
- // Check for paste placeholder (collapsed state)
388
- if (logicalPos) {
389
- const transform = getTransformUnderCursor(logicalPos.row, logicalPos.col, buffer.transformationsByLine, { includeEdge: true });
390
- if (transform?.type === "paste" && transform.id) {
391
- buffer.togglePasteExpansion(transform.id, logicalPos.row, logicalPos.col);
392
- return;
393
- }
394
- }
395
- // If we didn't click a placeholder to expand, check if we are inside or after
396
- // an expanded paste region and collapse it.
397
- const visualRow = currentScrollTop + relY;
398
- const mapEntry = buffer.visualToLogicalMap[visualRow];
399
- const row = mapEntry ? mapEntry[0] : visualRow;
400
- const expandedId = buffer.getExpandedPasteAtLine(row);
401
- if (expandedId) {
402
- buffer.togglePasteExpansion(expandedId, row, logicalPos?.col ?? relX);
403
- }
404
- }, { isActive: focus, name: "double-click" });
405
- useMouse((event) => {
406
- if (event.name === "right-release") {
407
- setSuppressCompletion(false);
408
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
409
- handleClipboardPaste();
410
- }
411
- }, { isActive: focus });
412
- const handleInput = useCallback((key) => {
413
- // Determine if this keypress is a history navigation command
414
- const isHistoryUp = !shellModeActive &&
415
- (keyMatchers[Command.HISTORY_UP](key) ||
416
- (keyMatchers[Command.NAVIGATION_UP](key) &&
417
- (buffer.allVisualLines.length === 1 ||
418
- (buffer.visualCursor[0] === 0 && buffer.visualScrollRow === 0))));
419
- const isHistoryDown = !shellModeActive &&
420
- (keyMatchers[Command.HISTORY_DOWN](key) ||
421
- (keyMatchers[Command.NAVIGATION_DOWN](key) &&
422
- (buffer.allVisualLines.length === 1 ||
423
- buffer.visualCursor[0] === buffer.allVisualLines.length - 1)));
424
- const isHistoryNav = isHistoryUp || isHistoryDown;
425
- const isCursorMovement = keyMatchers[Command.MOVE_LEFT](key) ||
426
- keyMatchers[Command.MOVE_RIGHT](key) ||
427
- keyMatchers[Command.MOVE_UP](key) ||
428
- keyMatchers[Command.MOVE_DOWN](key) ||
429
- keyMatchers[Command.MOVE_WORD_LEFT](key) ||
430
- keyMatchers[Command.MOVE_WORD_RIGHT](key) ||
431
- keyMatchers[Command.HOME](key) ||
432
- keyMatchers[Command.END](key);
433
- const isSuggestionsNav = shouldShowSuggestions &&
434
- (keyMatchers[Command.COMPLETION_UP](key) ||
435
- keyMatchers[Command.COMPLETION_DOWN](key) ||
436
- keyMatchers[Command.EXPAND_SUGGESTION](key) ||
437
- keyMatchers[Command.COLLAPSE_SUGGESTION](key) ||
438
- keyMatchers[Command.ACCEPT_SUGGESTION](key));
439
- // Reset completion suppression if the user performs any action other than
440
- // history navigation or cursor movement.
441
- // We explicitly skip this if we are currently navigating suggestions.
442
- if (!isSuggestionsNav) {
443
- setSuppressCompletion(isHistoryNav || isCursorMovement || keyMatchers[Command.ESCAPE](key));
444
- hasUserNavigatedSuggestions.current = false;
445
- if (key.name !== "tab") {
446
- setForceShowShellSuggestions(false);
447
- }
448
- }
449
- // TODO(jacobr): this special case is likely not needed anymore.
450
- // We should probably stop supporting paste if the InputPrompt is not
451
- // focused.
452
- /// We want to handle paste even when not focused to support drag and drop.
453
- if (!focus && key.name !== "paste") {
454
- return false;
455
- }
456
- // Handle escape to close shortcuts panel first, before letting it bubble
457
- // up for cancellation. This ensures pressing Escape once closes the panel,
458
- // and pressing again cancels the operation.
459
- if (shortcutsHelpVisible && key.name === "escape") {
460
- setShortcutsHelpVisible(false);
461
- return true;
462
- }
463
- const isGenerating = streamingState === StreamingState.Responding ||
464
- streamingState === StreamingState.WaitingForConfirmation;
465
- const isQueueMessageKey = keyMatchers[Command.QUEUE_MESSAGE](key);
466
- const isPlainTab = key.name === "tab" && !key.shift && !key.alt && !key.ctrl && !key.cmd;
467
- const hasTabCompletionInteraction = (completion.showSuggestions && isShellSuggestionsVisible) ||
468
- Boolean(completion.promptCompletion.text) ||
469
- reverseSearchActive ||
470
- commandSearchActive;
471
- if (isGenerating &&
472
- isQueueMessageKey &&
473
- !hasTabCompletionInteraction &&
474
- buffer.text.trim().length > 0) {
475
- const trimmedMessage = buffer.text.trim();
476
- const isSlash = isSlashCommand(trimmedMessage);
477
- if (isSlash || shellModeActive) {
478
- setQueueErrorMessage(`${shellModeActive ? "Shell" : "Slash"} commands cannot be queued`);
479
- }
480
- else if (onQueueMessage) {
481
- onQueueMessage(buffer.text);
482
- buffer.setText("");
483
- resetCompletionState();
484
- resetReverseSearchCompletionState();
485
- }
486
- resetPlainTabPress();
487
- return true;
488
- }
489
- if (isPlainTab && shellModeActive) {
490
- resetPlainTabPress();
491
- if (!shouldShowSuggestions) {
492
- setSuppressCompletion(false);
493
- if (completion.promptCompletion.text) {
494
- completion.promptCompletion.accept();
495
- return true;
496
- }
497
- else if (completion.suggestions.length > 0 &&
498
- !forceShowShellSuggestions) {
499
- setForceShowShellSuggestions(true);
500
- return true;
501
- }
502
- }
503
- }
504
- else if (isPlainTab) {
505
- if (!hasTabCompletionInteraction) {
506
- if (registerPlainTabPress() === 2) {
507
- toggleCleanUiDetailsVisible();
508
- resetPlainTabPress();
509
- return true;
510
- }
511
- }
512
- else {
513
- resetPlainTabPress();
514
- }
515
- }
516
- else {
517
- resetPlainTabPress();
518
- }
519
- if (key.name === "paste") {
520
- if (shortcutsHelpVisible) {
521
- setShortcutsHelpVisible(false);
522
- }
523
- // Record paste time to prevent accidental auto-submission
524
- if (!isTerminalPasteTrusted(kittyProtocol.enabled)) {
525
- setRecentUnsafePasteTime(Date.now());
526
- // Clear any existing paste timeout
527
- if (pasteTimeoutRef.current) {
528
- clearTimeout(pasteTimeoutRef.current);
529
- }
530
- // Clear the paste protection after a very short delay to prevent
531
- // false positives.
532
- // Due to how we use a reducer for text buffer state updates, it is
533
- // reasonable to expect that key events that are really part of the
534
- // same paste will be processed in the same event loop tick. 40ms
535
- // is chosen arbitrarily as it is faster than a typical human
536
- // could go from pressing paste to pressing enter. The fastest typists
537
- // can type at 200 words per minute which roughly translates to 50ms
538
- // per letter.
539
- pasteTimeoutRef.current = setTimeout(() => {
540
- setRecentUnsafePasteTime(null);
541
- pasteTimeoutRef.current = null;
542
- }, 40);
543
- }
544
- if (settings.ui?.escapePastedAtSymbols) {
545
- buffer.handleInput({
546
- ...key,
547
- sequence: escapeAtSymbols(key.sequence || ""),
548
- });
549
- }
550
- else {
551
- buffer.handleInput(key);
552
- }
553
- if (key.sequence && isLargePaste(key.sequence)) {
554
- appEvents.emit(AppEvent.TransientMessage, {
555
- message: `Press ${formatCommand(Command.EXPAND_PASTE)} to expand pasted text`,
556
- type: TransientMessageType.Hint,
557
- });
558
- }
559
- return true;
560
- }
561
- if (shortcutsHelpVisible && isHelpDismissKey(key)) {
562
- setShortcutsHelpVisible(false);
563
- }
564
- if (shortcutsHelpVisible) {
565
- if (key.sequence === "?" && key.insertable) {
566
- setShortcutsHelpVisible(false);
567
- buffer.handleInput(key);
568
- return true;
569
- }
570
- // Escape is handled earlier to ensure it closes the panel before
571
- // potentially cancelling an operation
572
- if (key.name === "backspace" || key.sequence === "\b") {
573
- setShortcutsHelpVisible(false);
574
- return true;
575
- }
576
- if (key.insertable) {
577
- setShortcutsHelpVisible(false);
578
- }
579
- }
580
- if (key.sequence === "?" &&
581
- key.insertable &&
582
- !shortcutsHelpVisible &&
583
- buffer.text.length === 0) {
584
- setShortcutsHelpVisible(true);
585
- return true;
586
- }
587
- if (vimHandleInput?.(key)) {
588
- return true;
589
- }
590
- // Reset ESC count and hide prompt on any non-ESC key
591
- if (key.name !== "escape") {
592
- resetEscapeState();
593
- }
594
- // Ctrl+O to expand/collapse paste placeholders
595
- if (keyMatchers[Command.EXPAND_PASTE](key)) {
596
- const handled = tryTogglePasteExpansion(buffer);
597
- if (handled)
598
- return true;
599
- }
600
- if (key.sequence === "!" &&
601
- buffer.text === "" &&
602
- !(completion.showSuggestions && isShellSuggestionsVisible)) {
603
- setShellModeActive(!shellModeActive);
604
- buffer.setText(""); // Clear the '!' from input
605
- return true;
606
- }
607
- if (keyMatchers[Command.ESCAPE](key)) {
608
- const cancelSearch = (setActive, resetCompletion) => {
609
- setActive(false);
610
- resetCompletion();
611
- buffer.setText(textBeforeReverseSearch);
612
- const offset = logicalPosToOffset(buffer.lines, cursorPosition[0], cursorPosition[1]);
613
- buffer.moveToOffset(offset);
614
- setExpandedSuggestionIndex(-1);
615
- };
616
- if (reverseSearchActive) {
617
- cancelSearch(setReverseSearchActive, reverseSearchCompletion.resetCompletionState);
618
- return true;
619
- }
620
- if (commandSearchActive) {
621
- cancelSearch(setCommandSearchActive, commandSearchCompletion.resetCompletionState);
622
- return true;
623
- }
624
- if (completion.showSuggestions && isShellSuggestionsVisible) {
625
- completion.resetCompletionState();
626
- setExpandedSuggestionIndex(-1);
627
- resetEscapeState();
628
- return true;
629
- }
630
- if (shellModeActive) {
631
- setShellModeActive(false);
632
- resetEscapeState();
633
- return true;
634
- }
635
- // If we're generating and no local overlay consumed Escape, let it
636
- // propagate to the global cancellation handler.
637
- if (isGenerating) {
638
- return false;
639
- }
640
- handleEscPress();
641
- return true;
642
- }
643
- if (keyMatchers[Command.CLEAR_SCREEN](key)) {
644
- setBannerVisible(false);
645
- onClearScreen();
646
- return true;
647
- }
648
- if (shellModeActive && keyMatchers[Command.REVERSE_SEARCH](key)) {
649
- setReverseSearchActive(true);
650
- setTextBeforeReverseSearch(buffer.text);
651
- setCursorPosition(buffer.cursor);
652
- return true;
653
- }
654
- if (reverseSearchActive || commandSearchActive) {
655
- const isCommandSearch = commandSearchActive;
656
- const sc = isCommandSearch
657
- ? commandSearchCompletion
658
- : reverseSearchCompletion;
659
- const { activeSuggestionIndex, navigateUp, navigateDown, showSuggestions, suggestions, } = sc;
660
- const setActive = isCommandSearch
661
- ? setCommandSearchActive
662
- : setReverseSearchActive;
663
- const resetState = sc.resetCompletionState;
664
- if (showSuggestions) {
665
- if (keyMatchers[Command.NAVIGATION_UP](key)) {
666
- navigateUp();
667
- return true;
668
- }
669
- if (keyMatchers[Command.NAVIGATION_DOWN](key)) {
670
- navigateDown();
671
- return true;
672
- }
673
- if (keyMatchers[Command.COLLAPSE_SUGGESTION](key)) {
674
- if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
675
- setExpandedSuggestionIndex(-1);
676
- return true;
677
- }
678
- }
679
- if (keyMatchers[Command.EXPAND_SUGGESTION](key)) {
680
- if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
681
- setExpandedSuggestionIndex(activeSuggestionIndex);
682
- return true;
683
- }
684
- }
685
- if (keyMatchers[Command.ACCEPT_SUGGESTION_REVERSE_SEARCH](key)) {
686
- sc.handleAutocomplete(activeSuggestionIndex);
687
- resetState();
688
- setActive(false);
689
- return true;
690
- }
691
- }
692
- if (keyMatchers[Command.SUBMIT_REVERSE_SEARCH](key)) {
693
- const textToSubmit = showSuggestions && activeSuggestionIndex > -1
694
- ? suggestions[activeSuggestionIndex].value
695
- : buffer.text;
696
- handleSubmit(textToSubmit);
697
- resetState();
698
- setActive(false);
699
- return true;
700
- }
701
- // Prevent up/down from falling through to regular history navigation
702
- if (keyMatchers[Command.NAVIGATION_UP](key) ||
703
- keyMatchers[Command.NAVIGATION_DOWN](key)) {
704
- return true;
705
- }
706
- }
707
- // If the command is a perfect match, pressing enter should execute it.
708
- // We prioritize execution unless the user is explicitly selecting a different suggestion.
709
- if (completion.isPerfectMatch &&
710
- keyMatchers[Command.SUBMIT](key) &&
711
- recentUnsafePasteTime === null &&
712
- (!(completion.showSuggestions && isShellSuggestionsVisible) ||
713
- (completion.activeSuggestionIndex <= 0 &&
714
- !hasUserNavigatedSuggestions.current))) {
715
- handleSubmit(buffer.text);
716
- return true;
717
- }
718
- // Newline insertion
719
- if (keyMatchers[Command.NEWLINE](key)) {
720
- buffer.newline();
721
- return true;
722
- }
723
- if (completion.showSuggestions && isShellSuggestionsVisible) {
724
- if (completion.suggestions.length > 1) {
725
- if (keyMatchers[Command.COMPLETION_UP](key)) {
726
- completion.navigateUp();
727
- hasUserNavigatedSuggestions.current = true;
728
- setExpandedSuggestionIndex(-1); // Reset expansion when navigating
729
- return true;
730
- }
731
- if (keyMatchers[Command.COMPLETION_DOWN](key)) {
732
- completion.navigateDown();
733
- hasUserNavigatedSuggestions.current = true;
734
- setExpandedSuggestionIndex(-1); // Reset expansion when navigating
735
- return true;
736
- }
737
- }
738
- if (keyMatchers[Command.ACCEPT_SUGGESTION](key)) {
739
- if (completion.suggestions.length > 0) {
740
- const targetIndex = completion.activeSuggestionIndex === -1
741
- ? 0 // Default to the first if none is active
742
- : completion.activeSuggestionIndex;
743
- if (targetIndex < completion.suggestions.length) {
744
- const suggestion = completion.suggestions[targetIndex];
745
- const isEnterKey = key.name === "enter" && !key.ctrl;
746
- if (isEnterKey && shellModeActive) {
747
- if (hasUserNavigatedSuggestions.current) {
748
- completion.handleAutocomplete(completion.activeSuggestionIndex);
749
- setExpandedSuggestionIndex(-1);
750
- hasUserNavigatedSuggestions.current = false;
751
- return true;
752
- }
753
- completion.resetCompletionState();
754
- setExpandedSuggestionIndex(-1);
755
- hasUserNavigatedSuggestions.current = false;
756
- if (buffer.text.trim()) {
757
- handleSubmit(buffer.text);
758
- }
759
- return true;
760
- }
761
- if (isEnterKey && buffer.text.startsWith("/")) {
762
- if (suggestion.submitValue) {
763
- setExpandedSuggestionIndex(-1);
764
- handleSubmit(suggestion.submitValue.trim());
765
- return true;
766
- }
767
- const { isArgumentCompletion, leafCommand } = completion.slashCompletionRange;
768
- if (isArgumentCompletion &&
769
- isAutoExecutableCommand(leafCommand)) {
770
- // isArgumentCompletion guarantees leafCommand exists
771
- const completedText = completion.getCompletedText(suggestion);
772
- if (completedText) {
773
- setExpandedSuggestionIndex(-1);
774
- handleSubmit(completedText.trim());
775
- return true;
776
- }
777
- }
778
- else if (!isArgumentCompletion) {
779
- // Existing logic for command name completion
780
- const command = completion.getCommandFromSuggestion(suggestion);
781
- // Only auto-execute if the command has no completion function
782
- // (i.e., it doesn't require an argument to be selected)
783
- if (command &&
784
- isAutoExecutableCommand(command) &&
785
- !command.completion) {
786
- const completedText = completion.getCompletedText(suggestion);
787
- if (completedText) {
788
- setExpandedSuggestionIndex(-1);
789
- handleSubmit(completedText.trim());
790
- return true;
791
- }
792
- }
793
- }
794
- }
795
- // Default behavior: auto-complete to prompt box
796
- completion.handleAutocomplete(targetIndex);
797
- setExpandedSuggestionIndex(-1); // Reset expansion after selection
798
- }
799
- }
800
- return true;
801
- }
802
- }
803
- // Handle Tab key for ghost text acceptance
804
- if (key.name === "tab" &&
805
- !key.shift &&
806
- !(completion.showSuggestions && isShellSuggestionsVisible) &&
807
- completion.promptCompletion.text) {
808
- completion.promptCompletion.accept();
809
- return true;
810
- }
811
- if (!shellModeActive) {
812
- if (keyMatchers[Command.REVERSE_SEARCH](key)) {
813
- setCommandSearchActive(true);
814
- setTextBeforeReverseSearch(buffer.text);
815
- setCursorPosition(buffer.cursor);
816
- return true;
817
- }
818
- if (isHistoryUp) {
819
- if (keyMatchers[Command.NAVIGATION_UP](key) &&
820
- buffer.visualCursor[1] > 0) {
821
- buffer.move("home");
822
- return true;
823
- }
824
- // Check for queued messages first when input is empty
825
- // If no queued messages, inputHistory.navigateUp() is called inside tryLoadQueuedMessages
826
- if (tryLoadQueuedMessages()) {
827
- return true;
828
- }
829
- // Only navigate history if popAllMessages doesn't exist
830
- inputHistory.navigateUp();
831
- return true;
832
- }
833
- if (isHistoryDown) {
834
- if (keyMatchers[Command.NAVIGATION_DOWN](key) &&
835
- buffer.visualCursor[1] <
836
- cpLen(buffer.allVisualLines[buffer.visualCursor[0]] || "")) {
837
- buffer.move("end");
838
- return true;
839
- }
840
- inputHistory.navigateDown();
841
- return true;
842
- }
843
- }
844
- else {
845
- // Shell History Navigation
846
- if (keyMatchers[Command.NAVIGATION_UP](key)) {
847
- if ((buffer.allVisualLines.length === 1 ||
848
- (buffer.visualCursor[0] === 0 && buffer.visualScrollRow === 0)) &&
849
- buffer.visualCursor[1] > 0) {
850
- buffer.move("home");
851
- return true;
852
- }
853
- const prevCommand = shellHistory.getPreviousCommand();
854
- if (prevCommand !== null)
855
- buffer.setText(prevCommand);
856
- return true;
857
- }
858
- if (keyMatchers[Command.NAVIGATION_DOWN](key)) {
859
- if ((buffer.allVisualLines.length === 1 ||
860
- buffer.visualCursor[0] === buffer.allVisualLines.length - 1) &&
861
- buffer.visualCursor[1] <
862
- cpLen(buffer.allVisualLines[buffer.visualCursor[0]] || "")) {
863
- buffer.move("end");
864
- return true;
865
- }
866
- const nextCommand = shellHistory.getNextCommand();
867
- if (nextCommand !== null)
868
- buffer.setText(nextCommand);
869
- return true;
870
- }
871
- }
872
- if (keyMatchers[Command.SUBMIT](key)) {
873
- if (buffer.text.trim()) {
874
- // Check if a paste operation occurred recently to prevent accidental auto-submission
875
- if (recentUnsafePasteTime !== null) {
876
- // Paste occurred recently in a terminal where we don't trust pastes
877
- // to be reported correctly so assume this paste was really a
878
- // newline that was part of the paste.
879
- // This has the added benefit that in the worst case at least users
880
- // get some feedback that their keypress was handled rather than
881
- // wondering why it was completely ignored.
882
- buffer.newline();
883
- return true;
884
- }
885
- const [row, col] = buffer.cursor;
886
- const line = buffer.lines[row];
887
- const charBefore = col > 0 ? cpSlice(line, col - 1, col) : "";
888
- if (charBefore === "\\") {
889
- buffer.backspace();
890
- buffer.newline();
891
- }
892
- else {
893
- handleSubmit(buffer.text);
894
- }
895
- }
896
- return true;
897
- }
898
- // Ctrl+A (Home) / Ctrl+E (End)
899
- if (keyMatchers[Command.HOME](key)) {
900
- buffer.move("home");
901
- return true;
902
- }
903
- if (keyMatchers[Command.END](key)) {
904
- buffer.move("end");
905
- return true;
906
- }
907
- // Kill line commands
908
- if (keyMatchers[Command.KILL_LINE_RIGHT](key)) {
909
- buffer.killLineRight();
910
- return true;
911
- }
912
- if (keyMatchers[Command.KILL_LINE_LEFT](key)) {
913
- buffer.killLineLeft();
914
- return true;
915
- }
916
- if (keyMatchers[Command.DELETE_WORD_BACKWARD](key)) {
917
- buffer.deleteWordLeft();
918
- return true;
919
- }
920
- // External editor
921
- if (keyMatchers[Command.OPEN_EXTERNAL_EDITOR](key)) {
922
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
923
- buffer.openInExternalEditor();
924
- return true;
925
- }
926
- if (keyMatchers[Command.DEPRECATED_OPEN_EXTERNAL_EDITOR](key)) {
927
- const cmdKey = formatCommand(Command.OPEN_EXTERNAL_EDITOR);
928
- appEvents.emit(AppEvent.TransientMessage, {
929
- message: `Use ${cmdKey} to open the external editor.`,
930
- type: TransientMessageType.Hint,
931
- });
932
- return true;
933
- }
934
- // Ctrl+V for clipboard paste
935
- if (keyMatchers[Command.PASTE_CLIPBOARD](key)) {
936
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
937
- handleClipboardPaste();
938
- return true;
939
- }
940
- if (keyMatchers[Command.TOGGLE_BACKGROUND_SHELL](key)) {
941
- return false;
942
- }
943
- if (keyMatchers[Command.FOCUS_SHELL_INPUT](key)) {
944
- if (activePtyId ||
945
- (backgroundTasks.size > 0 && backgroundTaskHeight > 0)) {
946
- setEmbeddedShellFocused(true);
947
- return true;
948
- }
949
- return false;
950
- }
951
- // Fall back to the text buffer's default input handling for all other keys
952
- const handled = buffer.handleInput(key);
953
- if (handled) {
954
- if (keyMatchers[Command.CLEAR_INPUT](key)) {
955
- resetCompletionState();
956
- }
957
- // Clear ghost text when user types regular characters (not navigation/control keys)
958
- if (completion.promptCompletion.text &&
959
- key.sequence &&
960
- key.sequence.length === 1 &&
961
- !key.alt &&
962
- !key.ctrl &&
963
- !key.cmd) {
964
- completion.promptCompletion.clear();
965
- setExpandedSuggestionIndex(-1);
966
- }
967
- }
968
- return handled;
969
- }, [
970
- focus,
971
- buffer,
972
- completion,
973
- setForceShowShellSuggestions,
974
- shellModeActive,
975
- setShellModeActive,
976
- onClearScreen,
977
- inputHistory,
978
- handleSubmit,
979
- shellHistory,
980
- reverseSearchCompletion,
981
- handleClipboardPaste,
982
- resetCompletionState,
983
- resetEscapeState,
984
- vimHandleInput,
985
- reverseSearchActive,
986
- textBeforeReverseSearch,
987
- cursorPosition,
988
- recentUnsafePasteTime,
989
- commandSearchActive,
990
- commandSearchCompletion,
991
- kittyProtocol.enabled,
992
- shortcutsHelpVisible,
993
- setShortcutsHelpVisible,
994
- tryLoadQueuedMessages,
995
- onQueueMessage,
996
- setQueueErrorMessage,
997
- resetReverseSearchCompletionState,
998
- setBannerVisible,
999
- activePtyId,
1000
- setEmbeddedShellFocused,
1001
- backgroundTasks.size,
1002
- backgroundTaskHeight,
1003
- streamingState,
1004
- handleEscPress,
1005
- registerPlainTabPress,
1006
- resetPlainTabPress,
1007
- toggleCleanUiDetailsVisible,
1008
- shouldShowSuggestions,
1009
- isShellSuggestionsVisible,
1010
- forceShowShellSuggestions,
1011
- keyMatchers,
1012
- isHelpDismissKey,
1013
- settings,
1014
- ]);
1015
- useKeypress(handleInput, {
1016
- isActive: !isEmbeddedShellFocused && !copyModeEnabled,
1017
- priority: true,
1018
- });
1019
- const [cursorVisualRowAbsolute, cursorVisualColAbsolute] = buffer.visualCursor;
1020
- const getGhostTextLines = useCallback(() => {
1021
- if (!completion.promptCompletion.text ||
1022
- !buffer.text ||
1023
- !completion.promptCompletion.text.startsWith(buffer.text)) {
1024
- return { inlineGhost: "", additionalLines: [] };
1025
- }
1026
- const ghostSuffix = completion.promptCompletion.text.slice(buffer.text.length);
1027
- if (!ghostSuffix) {
1028
- return { inlineGhost: "", additionalLines: [] };
1029
- }
1030
- const currentLogicalLine = buffer.lines[buffer.cursor[0]] || "";
1031
- const cursorCol = buffer.cursor[1];
1032
- const textBeforeCursor = cpSlice(currentLogicalLine, 0, cursorCol);
1033
- const usedWidth = stringWidth(textBeforeCursor);
1034
- const remainingWidth = Math.max(0, inputWidth - usedWidth);
1035
- const ghostTextLinesRaw = ghostSuffix.split("\n");
1036
- const firstLineRaw = ghostTextLinesRaw.shift() || "";
1037
- let inlineGhost = "";
1038
- let remainingFirstLine = "";
1039
- if (stringWidth(firstLineRaw) <= remainingWidth) {
1040
- inlineGhost = firstLineRaw;
1041
- }
1042
- else {
1043
- const words = firstLineRaw.split(" ");
1044
- let currentLine = "";
1045
- let wordIdx = 0;
1046
- for (const word of words) {
1047
- const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
1048
- if (stringWidth(prospectiveLine) > remainingWidth) {
1049
- break;
1050
- }
1051
- currentLine = prospectiveLine;
1052
- wordIdx++;
1053
- }
1054
- inlineGhost = currentLine;
1055
- if (words.length > wordIdx) {
1056
- remainingFirstLine = words.slice(wordIdx).join(" ");
1057
- }
1058
- }
1059
- const linesToWrap = [];
1060
- if (remainingFirstLine) {
1061
- linesToWrap.push(remainingFirstLine);
1062
- }
1063
- linesToWrap.push(...ghostTextLinesRaw);
1064
- const remainingGhostText = linesToWrap.join("\n");
1065
- const additionalLines = [];
1066
- if (remainingGhostText) {
1067
- const textLines = remainingGhostText.split("\n");
1068
- for (const textLine of textLines) {
1069
- const words = textLine.split(" ");
1070
- let currentLine = "";
1071
- for (const word of words) {
1072
- const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
1073
- const prospectiveWidth = stringWidth(prospectiveLine);
1074
- if (prospectiveWidth > inputWidth) {
1075
- if (currentLine) {
1076
- additionalLines.push(currentLine);
1077
- }
1078
- let wordToProcess = word;
1079
- while (stringWidth(wordToProcess) > inputWidth) {
1080
- let part = "";
1081
- const wordCP = toCodePoints(wordToProcess);
1082
- let partWidth = 0;
1083
- let splitIndex = 0;
1084
- for (let i = 0; i < wordCP.length; i++) {
1085
- const char = wordCP[i];
1086
- const charWidth = stringWidth(char);
1087
- if (partWidth + charWidth > inputWidth) {
1088
- break;
1089
- }
1090
- part += char;
1091
- partWidth += charWidth;
1092
- splitIndex = i + 1;
1093
- }
1094
- additionalLines.push(part);
1095
- wordToProcess = cpSlice(wordToProcess, splitIndex);
1096
- }
1097
- currentLine = wordToProcess;
1098
- }
1099
- else {
1100
- currentLine = prospectiveLine;
1101
- }
1102
- }
1103
- if (currentLine) {
1104
- additionalLines.push(currentLine);
1105
- }
1106
- }
1107
- }
1108
- return { inlineGhost, additionalLines };
1109
- }, [
1110
- completion.promptCompletion.text,
1111
- buffer.text,
1112
- buffer.lines,
1113
- buffer.cursor,
1114
- inputWidth,
1115
- ]);
1116
- const { inlineGhost, additionalLines } = getGhostTextLines();
1117
- const scrollableData = useMemo(() => {
1118
- const items = buffer.allVisualLines.map((lineText, index) => ({
1119
- type: "visualLine",
1120
- lineText,
1121
- absoluteVisualIdx: index,
1122
- }));
1123
- additionalLines.forEach((ghostLine, index) => {
1124
- items.push({
1125
- type: "ghostLine",
1126
- ghostLine,
1127
- index,
1128
- });
1129
- });
1130
- return items;
1131
- }, [buffer.allVisualLines, additionalLines]);
1132
- const renderItem = useCallback(({ item }) => {
1133
- if (item.type === "ghostLine") {
1134
- const padding = Math.max(0, inputWidth - stringWidth(item.ghostLine));
1135
- return (_jsx(Box, { height: 1, children: _jsxs(Text, { color: theme.text.secondary, children: [item.ghostLine, " ".repeat(padding)] }) }));
1136
- }
1137
- const { lineText, absoluteVisualIdx } = item;
1138
- // console.log('renderItem called with:', lineText);
1139
- const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
1140
- if (!mapEntry)
1141
- return _jsx(Text, { children: " " });
1142
- const isOnCursorLine = focus && absoluteVisualIdx === cursorVisualRowAbsolute;
1143
- const renderedLine = [];
1144
- const [logicalLineIdx] = mapEntry;
1145
- const logicalLine = buffer.lines[logicalLineIdx] || "";
1146
- const transformations = buffer.transformationsByLine[logicalLineIdx] ?? [];
1147
- const tokens = parseInputForHighlighting(logicalLine, logicalLineIdx, transformations, ...(focus && buffer.cursor[0] === logicalLineIdx
1148
- ? [buffer.cursor[1]]
1149
- : []));
1150
- const visualStartCol = buffer.visualToTransformedMap[absoluteVisualIdx] ?? 0;
1151
- const visualEndCol = visualStartCol + cpLen(lineText);
1152
- const segments = parseSegmentsFromTokens(tokens, visualStartCol, visualEndCol);
1153
- let charCount = 0;
1154
- segments.forEach((seg, segIdx) => {
1155
- const segLen = cpLen(seg.text);
1156
- let display = seg.text;
1157
- if (isOnCursorLine) {
1158
- const relCol = cursorVisualColAbsolute;
1159
- const segStart = charCount;
1160
- const segEnd = segStart + segLen;
1161
- if (relCol >= segStart && relCol < segEnd) {
1162
- const charToHighlight = cpSlice(display, relCol - segStart, relCol - segStart + 1);
1163
- const highlighted = showCursor
1164
- ? chalk.inverse(charToHighlight)
1165
- : charToHighlight;
1166
- display =
1167
- cpSlice(display, 0, relCol - segStart) +
1168
- highlighted +
1169
- cpSlice(display, relCol - segStart + 1);
1170
- }
1171
- charCount = segEnd;
1172
- }
1173
- else {
1174
- charCount += segLen;
1175
- }
1176
- const color = seg.type === "command" || seg.type === "file" || seg.type === "paste"
1177
- ? theme.text.accent
1178
- : theme.text.primary;
1179
- renderedLine.push(_jsx(Text, { color: color, children: display }, `token-${segIdx}`));
1180
- });
1181
- const currentLineGhost = isOnCursorLine ? inlineGhost : "";
1182
- if (isOnCursorLine &&
1183
- cursorVisualColAbsolute === cpLen(lineText) &&
1184
- !currentLineGhost) {
1185
- renderedLine.push(_jsx(Text, { children: showCursor ? chalk.inverse(" ") : " " }, `cursor-end-${cursorVisualColAbsolute}`));
1186
- }
1187
- const showCursorBeforeGhost = focus &&
1188
- isOnCursorLine &&
1189
- cursorVisualColAbsolute === cpLen(lineText) &&
1190
- currentLineGhost;
1191
- return (_jsx(Box, { height: 1, children: _jsxs(Text, { terminalCursorFocus: showCursor && isOnCursorLine, terminalCursorPosition: cpIndexToOffset(lineText, cursorVisualColAbsolute), children: [renderedLine, showCursorBeforeGhost && (showCursor ? chalk.inverse(" ") : " "), currentLineGhost && (_jsx(Text, { color: theme.text.secondary, children: currentLineGhost }))] }) }));
1192
- }, [
1193
- buffer.visualToLogicalMap,
1194
- buffer.lines,
1195
- buffer.transformationsByLine,
1196
- buffer.cursor,
1197
- buffer.visualToTransformedMap,
1198
- focus,
1199
- cursorVisualRowAbsolute,
1200
- cursorVisualColAbsolute,
1201
- showCursor,
1202
- inlineGhost,
1203
- inputWidth,
1204
- ]);
1205
- const useBackgroundColor = config.getUseBackgroundColor();
1206
- const isLowColor = isLowColorDepth();
1207
- const terminalBg = theme.background.primary || "black";
1208
- // We should fallback to lines if the background color is disabled OR if it is
1209
- // enabled but we are in a low color depth terminal where we don't have a safe
1210
- // background color to use.
1211
- const useLineFallback = useMemo(() => {
1212
- if (!useBackgroundColor) {
1213
- return true;
1214
- }
1215
- if (isLowColor) {
1216
- return !getSafeLowColorBackground(terminalBg);
1217
- }
1218
- return false;
1219
- }, [useBackgroundColor, isLowColor, terminalBg]);
1220
- const prevCursorRef = useRef(buffer.visualCursor);
1221
- const prevTextRef = useRef(buffer.text);
1222
- // Effect to ensure cursor remains visible after interactions
1223
- useEffect(() => {
1224
- const cursorChanged = prevCursorRef.current !== buffer.visualCursor;
1225
- const textChanged = prevTextRef.current !== buffer.text;
1226
- prevCursorRef.current = buffer.visualCursor;
1227
- prevTextRef.current = buffer.text;
1228
- if (!cursorChanged && !textChanged)
1229
- return;
1230
- if (!listRef.current || !focus)
1231
- return;
1232
- const { scrollTop, innerHeight } = listRef.current.getScrollState();
1233
- if (innerHeight === 0)
1234
- return;
1235
- const cursorVisualRow = buffer.visualCursor[0];
1236
- const actualScrollTop = Math.round(scrollTop);
1237
- // If cursor is out of the currently visible viewport...
1238
- if (cursorVisualRow < actualScrollTop ||
1239
- cursorVisualRow >= actualScrollTop + innerHeight) {
1240
- // Calculate minimal scroll to make it visible
1241
- let newScrollTop = actualScrollTop;
1242
- if (cursorVisualRow < actualScrollTop) {
1243
- newScrollTop = cursorVisualRow;
1244
- }
1245
- else if (cursorVisualRow >= actualScrollTop + innerHeight) {
1246
- newScrollTop = cursorVisualRow - innerHeight + 1;
1247
- }
1248
- listRef.current.scrollToIndex({ index: newScrollTop });
1249
- }
1250
- }, [buffer.visualCursor, buffer.text, focus]);
1251
- const listBackgroundColor = useLineFallback || !useBackgroundColor ? undefined : theme.background.input;
1252
- useEffect(() => {
1253
- if (onSuggestionsVisibilityChange) {
1254
- onSuggestionsVisibilityChange(shouldShowSuggestions);
1255
- }
1256
- }, [shouldShowSuggestions, onSuggestionsVisibilityChange]);
1257
- const showAutoAcceptStyling = !shellModeActive && approvalMode === ApprovalMode.AUTO_EDIT;
1258
- const showYoloStyling = !shellModeActive && approvalMode === ApprovalMode.YOLO;
1259
- const showPlanStyling = !shellModeActive && approvalMode === ApprovalMode.PLAN;
1260
- let statusColor;
1261
- let statusText = "";
1262
- if (shellModeActive) {
1263
- statusColor = theme.ui.symbol;
1264
- statusText = "Shell mode";
1265
- }
1266
- else if (showYoloStyling) {
1267
- statusColor = theme.status.error;
1268
- statusText = "YOLO mode";
1269
- }
1270
- else if (showPlanStyling) {
1271
- statusColor = theme.status.success;
1272
- statusText = "Plan mode";
1273
- }
1274
- else if (showAutoAcceptStyling) {
1275
- statusColor = theme.status.warning;
1276
- statusText = "Accepting edits";
1277
- }
1278
- const suggestionsNode = shouldShowSuggestions ? (_jsx(Box, { paddingRight: 2, children: _jsx(SuggestionsDisplay, { suggestions: activeCompletion.suggestions, activeIndex: activeCompletion.activeSuggestionIndex, isLoading: activeCompletion.isLoadingSuggestions, width: suggestionsWidth, scrollOffset: activeCompletion.visibleStartIndex, userInput: buffer.text, mode: completion.completionMode === CompletionMode.AT ||
1279
- completion.completionMode === CompletionMode.SHELL
1280
- ? "reverse"
1281
- : buffer.text.startsWith("/") &&
1282
- !reverseSearchActive &&
1283
- !commandSearchActive
1284
- ? "slash"
1285
- : "reverse", expandedIndex: expandedSuggestionIndex }) })) : null;
1286
- const borderColor = isShellFocused && !isEmbeddedShellFocused
1287
- ? (statusColor ?? theme.ui.focus)
1288
- : theme.border.default;
1289
- return (_jsxs(_Fragment, { children: [suggestionsPosition === "above" && suggestionsNode, useLineFallback ? (_jsx(Box, { borderStyle: "round", borderTop: true, borderBottom: false, borderLeft: false, borderRight: false, borderColor: borderColor, width: terminalWidth, flexDirection: "row", alignItems: "flex-start", height: 0 })) : null, _jsx(HalfLinePaddedBox, { backgroundBaseColor: theme.background.input, backgroundOpacity: 1, useBackgroundColor: useBackgroundColor, children: _jsxs(Box, { flexGrow: 1, flexDirection: "row", paddingX: 1, borderColor: borderColor, borderStyle: useLineFallback ? "round" : undefined, borderTop: false, borderBottom: false, borderLeft: !useBackgroundColor, borderRight: !useBackgroundColor, children: [_jsxs(Text, { color: statusColor ?? theme.text.accent, "aria-label": statusText || undefined, children: [shellModeActive ? (reverseSearchActive ? (_jsxs(Text, { color: theme.text.link, "aria-label": SCREEN_READER_USER_PREFIX, children: ["(r:)", " "] })) : ("!")) : commandSearchActive ? (_jsx(Text, { color: theme.text.accent, children: "(r:) " })) : showYoloStyling ? ("*") : (">"), " "] }), _jsx(Box, { flexGrow: 1, flexDirection: "column", ref: innerBoxRef, children: buffer.text.length === 0 && placeholder ? (showCursor ? (_jsxs(Text, { terminalCursorFocus: showCursor, terminalCursorPosition: 0, children: [chalk.inverse(placeholder.slice(0, 1)), _jsx(Text, { color: theme.text.secondary, children: placeholder.slice(1) })] })) : (_jsx(Text, { color: theme.text.secondary, children: placeholder }))) : (_jsx(Box, { flexDirection: "column", height: Math.min(buffer.viewportHeight, scrollableData.length), width: "100%", children: isAlternateBuffer ? (_jsx(ScrollableList, { ref: listRef, hasFocus: focus, data: scrollableData, renderItem: renderItem, estimatedItemHeight: () => 1, fixedItemHeight: true, keyExtractor: (item) => item.type === "visualLine"
1290
- ? `line-${item.absoluteVisualIdx}`
1291
- : `ghost-${item.index}`, width: inputWidth, backgroundColor: listBackgroundColor, containerHeight: Math.min(buffer.viewportHeight, scrollableData.length) })) : (scrollableData
1292
- .slice(buffer.visualScrollRow, buffer.visualScrollRow + buffer.viewportHeight)
1293
- .map((item, index) => {
1294
- const actualIndex = buffer.visualScrollRow + index;
1295
- const key = item.type === "visualLine"
1296
- ? `line-${item.absoluteVisualIdx}`
1297
- : `ghost-${item.index}`;
1298
- return (_jsx(Fragment, { children: renderItem({ item, index: actualIndex }) }, key));
1299
- })) })) })] }) }), useLineFallback ? (_jsx(Box, { borderStyle: "round", borderTop: false, borderBottom: true, borderLeft: false, borderRight: false, borderColor: borderColor, width: terminalWidth, flexDirection: "row", alignItems: "flex-start", height: 0 })) : null, suggestionsPosition === "below" && suggestionsNode] }));
1300
- };
1301
- //# sourceMappingURL=InputPrompt-bestcase.js.map