(text: string, cursor: number)
| 237 | |
| 238 | // Update slash command UI state based on input |
| 239 | const updateSlashCommandState = (text: string, cursor: number) => { |
| 240 | // Only show slash commands if slash is the very first character |
| 241 | if (!text.trimStart().startsWith("/")) { |
| 242 | setShowSlashCommands(false); |
| 243 | return; |
| 244 | } |
| 245 | |
| 246 | // When using slash commands, disable file search |
| 247 | setShowFileSearch(false); |
| 248 | |
| 249 | const trimmedText = text.trimStart(); |
| 250 | const beforeCursor = trimmedText.slice( |
| 251 | 0, |
| 252 | cursor - (text.length - trimmedText.length), |
| 253 | ); |
| 254 | |
| 255 | // Only show if cursor is after the slash and before any whitespace |
| 256 | if (beforeCursor.length === 0 || !beforeCursor.startsWith("/")) { |
| 257 | setShowSlashCommands(false); |
| 258 | return; |
| 259 | } |
| 260 | |
| 261 | // Get the complete slash command from the text (not just before cursor) |
| 262 | const afterSlash = trimmedText.slice(1).split(/[\s\n]/)[0]; |
| 263 | |
| 264 | // We're in a slash command context - check if it's a complete command |
| 265 | const exactMatch = slashCommands.find((cmd) => cmd.name === afterSlash); |
| 266 | |
| 267 | // Hide selector if we have an exact match and there's any additional content |
| 268 | if (exactMatch) { |
| 269 | // Check if there's anything after the command name (space + args) |
| 270 | const commandWithSlash = "/" + exactMatch.name; |
| 271 | const textAfterCommand = trimmedText.slice(commandWithSlash.length); |
| 272 | |
| 273 | // If there's any content after the command name (space + args), hide dropdown |
| 274 | if ( |
| 275 | textAfterCommand.trim().length > 0 && |
| 276 | beforeCursor.length >= commandWithSlash.length |
| 277 | ) { |
| 278 | setShowSlashCommands(false); |
| 279 | return; |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | // Check if there are any matching commands |
| 284 | const filteredCommands = slashCommands.filter((cmd) => |
| 285 | cmd.name.toLowerCase().includes(afterSlash.toLowerCase()), |
| 286 | ); |
| 287 | |
| 288 | // If no commands match, hide the dropdown to allow normal Enter behavior |
| 289 | if (filteredCommands.length === 0) { |
| 290 | setShowSlashCommands(false); |
| 291 | return; |
| 292 | } |
| 293 | |
| 294 | // Show selector for partial matches |
| 295 | setShowSlashCommands(true); |
| 296 | setSlashCommandFilter(afterSlash); |
no outgoing calls
no test coverage detected