(text: string, cursor: number)
| 300 | |
| 301 | // Update file search UI state based on input |
| 302 | const updateFileSearchState = (text: string, cursor: number) => { |
| 303 | // Don't show file search in remote mode |
| 304 | if (isRemoteMode) { |
| 305 | setShowFileSearch(false); |
| 306 | return; |
| 307 | } |
| 308 | |
| 309 | // Disable file search if we're in shell mode or slash command mode |
| 310 | const trimmed = text.trimStart(); |
| 311 | if (trimmed.startsWith("!") || trimmed.startsWith("/")) { |
| 312 | setShowFileSearch(false); |
| 313 | return; |
| 314 | } |
| 315 | |
| 316 | // Check if we're in a file search context |
| 317 | const beforeCursor = text.slice(0, cursor); |
| 318 | |
| 319 | // Find the first @ symbol that starts a file search context |
| 320 | // We need to find the last @ that has no space before it (or is at word boundary) |
| 321 | let firstAtIndex = -1; |
| 322 | for (let i = beforeCursor.length - 1; i >= 0; i--) { |
| 323 | if (beforeCursor[i] === "@") { |
| 324 | // Check if this @ is at the start of a word (preceded by space/newline or start of string) |
| 325 | const beforeAt = beforeCursor.slice(0, i); |
| 326 | const lastChar = beforeAt[beforeAt.length - 1]; |
| 327 | if (i === 0 || lastChar === " " || lastChar === "\n") { |
| 328 | // Check if there's any space/newline between this @ and cursor |
| 329 | const afterThis = beforeCursor.slice(i + 1); |
| 330 | if (!afterThis.includes(" ") && !afterThis.includes("\n")) { |
| 331 | firstAtIndex = i; |
| 332 | break; |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | if (firstAtIndex === -1) { |
| 339 | setShowFileSearch(false); |
| 340 | } else { |
| 341 | // Get ALL text after the @ symbol (including subsequent @ symbols) |
| 342 | const afterAt = beforeCursor.slice(firstAtIndex + 1); |
| 343 | |
| 344 | // We're in a file search context |
| 345 | setShowFileSearch(true); |
| 346 | setFileSearchFilter(afterAt); |
| 347 | setSelectedFileIndex(0); |
| 348 | setShowSlashCommands(false); |
| 349 | setShowBashMode(false); |
| 350 | } |
| 351 | }; |
| 352 | |
| 353 | // Get filtered commands for navigation - using the same sorting logic as SlashCommandUI |
| 354 | const getFilteredCommands = () => { |
no outgoing calls
no test coverage detected