Compare commits

...

4 Commits

Author SHA1 Message Date
0xtoshii 9025e33fed nit 2025-04-21 15:20:18 -07:00
0xtoshii 07032992a6 changeset 2025-04-21 15:08:13 -07:00
0xtoshii 05c33fd6a9 menu 2025-04-21 15:07:11 -07:00
0xtoshii de921564f6 scroll 2025-04-21 11:50:53 -07:00
3 changed files with 69 additions and 7 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
menu fix for slash commands
@@ -17,9 +17,11 @@ import {
} from "@/utils/context-mentions"
import {
SlashCommand,
slashCommandDeleteRegex,
shouldShowSlashCommandsMenu,
getMatchingSlashCommands,
insertSlashCommand,
removeSlashCommand,
validateSlashCommand,
} from "@/utils/slash-commands"
import { useMetaKeyDetection, useShortcut } from "@/utils/hooks"
@@ -253,6 +255,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
const [selectedMenuIndex, setSelectedMenuIndex] = useState(-1)
const [selectedType, setSelectedType] = useState<ContextMenuOptionType | null>(null)
const [justDeletedSpaceAfterMention, setJustDeletedSpaceAfterMention] = useState(false)
const [justDeletedSpaceAfterSlashCommand, setJustDeletedSpaceAfterSlashCommand] = useState(false)
const [intendedCursorPosition, setIntendedCursorPosition] = useState<number | null>(null)
const contextMenuContainerRef = useRef<HTMLDivElement>(null)
const [showModelSelector, setShowModelSelector] = useState(false)
@@ -525,13 +528,14 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
charBeforeCursor === " " || charBeforeCursor === "\n" || charBeforeCursor === "\r\n"
const charAfterIsWhitespace =
charAfterCursor === " " || charAfterCursor === "\n" || charAfterCursor === "\r\n"
// checks if char before cursor is whitespace after a mention
// Check if we're right after a space that follows a mention or slash command
if (
charBeforeIsWhitespace &&
inputValue.slice(0, cursorPosition - 1).match(new RegExp(mentionRegex.source + "$")) // "$" is added to ensure the match occurs at the end of the string
inputValue.slice(0, cursorPosition - 1).match(new RegExp(mentionRegex.source + "$"))
) {
// File mention handling
const newCursorPosition = cursorPosition - 1
// if mention is followed by another word, then instead of deleting the space separating them we just move the cursor to the end of the mention
if (!charAfterIsWhitespace) {
event.preventDefault()
textAreaRef.current?.setSelectionRange(newCursorPosition, newCursorPosition)
@@ -539,17 +543,44 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
setCursorPosition(newCursorPosition)
setJustDeletedSpaceAfterMention(true)
} else if (justDeletedSpaceAfterMention) {
setJustDeletedSpaceAfterSlashCommand(false)
} else if (charBeforeIsWhitespace && inputValue.slice(0, cursorPosition - 1).match(slashCommandDeleteRegex)) {
// New slash command handling
const newCursorPosition = cursorPosition - 1
if (!charAfterIsWhitespace) {
event.preventDefault()
textAreaRef.current?.setSelectionRange(newCursorPosition, newCursorPosition)
setCursorPosition(newCursorPosition)
}
setCursorPosition(newCursorPosition)
setJustDeletedSpaceAfterSlashCommand(true)
setJustDeletedSpaceAfterMention(false)
}
// Handle the second backspace press for mentions or slash commands
else if (justDeletedSpaceAfterMention) {
const { newText, newPosition } = removeMention(inputValue, cursorPosition)
if (newText !== inputValue) {
event.preventDefault()
setInputValue(newText)
setIntendedCursorPosition(newPosition) // Store the new cursor position in state
setIntendedCursorPosition(newPosition)
}
setJustDeletedSpaceAfterMention(false)
setShowContextMenu(false)
} else {
} else if (justDeletedSpaceAfterSlashCommand) {
// New slash command deletion
const { newText, newPosition } = removeSlashCommand(inputValue, cursorPosition)
if (newText !== inputValue) {
event.preventDefault()
setInputValue(newText)
setIntendedCursorPosition(newPosition)
}
setJustDeletedSpaceAfterSlashCommand(false)
setShowSlashCommandsMenu(false)
}
// Default case - reset flags if none of the above apply
else {
setJustDeletedSpaceAfterMention(false)
setJustDeletedSpaceAfterSlashCommand(false)
}
}
},
@@ -566,6 +597,10 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
justDeletedSpaceAfterMention,
queryItems,
fileSearchResults,
showSlashCommandsMenu,
selectedSlashCommandsIndex,
slashCommandsQuery,
handleSlashCommandsSelect,
],
)
@@ -792,7 +827,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
if (isValidCommand) {
const fullCommand = processedText.substring(slashIndex, endIndex) // includes slash
const highlighted = `<mark class="slash-command-match-textarea-highlight">${fullCommand}</mark>`
const highlighted = `<mark class="mention-context-textarea-highlight">${fullCommand}</mark>`
processedText = processedText.substring(0, slashIndex) + highlighted + processedText.substring(endIndex)
}
}
+22
View File
@@ -13,6 +13,28 @@ export const SUPPORTED_SLASH_COMMANDS: SlashCommand[] = [
// Regex for detecting slash commands in text
export const slashCommandRegex = /\/([a-zA-Z0-9_-]+)(\s|$)/
export const slashCommandRegexGlobal = new RegExp(slashCommandRegex.source, "g")
export const slashCommandDeleteRegex = /^\s*\/([a-zA-Z0-9_-]+)$/
/**
* Removes a slash command at the cursor position
*/
export function removeSlashCommand(text: string, position: number): { newText: string; newPosition: number } {
const beforeCursor = text.slice(0, position)
const afterCursor = text.slice(position)
// Check if we're at the end of a slash command
const matchEnd = beforeCursor.match(slashCommandDeleteRegex)
if (matchEnd) {
// If we're at the end of a slash command, remove it
const newText = text.slice(0, position - matchEnd[0].length) + afterCursor.replace(" ", "") // removes the first space after the command
const newPosition = position - matchEnd[0].length
return { newText, newPosition }
}
// If we're not at the end of a slash command, just return the original text and position
return { newText: text, newPosition: position }
}
/**
* Determines whether the slash command menu should be displayed based on text input