Slash commands (#5718)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -18,7 +18,7 @@ import { useModelAndProvider } from './ModelAndProviderContext';
|
||||
import { useWhisper } from '../hooks/useWhisper';
|
||||
import { WaveformVisualizer } from './WaveformVisualizer';
|
||||
import { toastError } from '../toasts';
|
||||
import MentionPopover, { FileItemWithMatch } from './MentionPopover';
|
||||
import MentionPopover, { DisplayItemWithMatch } from './MentionPopover';
|
||||
import { useDictationSettings } from '../hooks/useDictationSettings';
|
||||
import { COST_TRACKING_ENABLED, VOICE_DICTATION_ELEVENLABS_ENABLED } from '../updates';
|
||||
import { CostTracker } from './bottom_menu/CostTracker';
|
||||
@@ -215,15 +215,17 @@ export default function ChatInput({
|
||||
query: string;
|
||||
mentionStart: number;
|
||||
selectedIndex: number;
|
||||
isSlashCommand: boolean;
|
||||
}>({
|
||||
isOpen: false,
|
||||
position: { x: 0, y: 0 },
|
||||
query: '',
|
||||
mentionStart: -1,
|
||||
selectedIndex: 0,
|
||||
isSlashCommand: false,
|
||||
});
|
||||
const mentionPopoverRef = useRef<{
|
||||
getDisplayFiles: () => FileItemWithMatch[];
|
||||
getDisplayFiles: () => DisplayItemWithMatch[];
|
||||
selectFile: (index: number) => void;
|
||||
}>(null);
|
||||
|
||||
@@ -563,13 +565,17 @@ export default function ChatInput({
|
||||
setDisplayValue(val);
|
||||
updateValue(val);
|
||||
setHasUserTyped(true);
|
||||
checkForMention(val, cursorPosition, evt.target);
|
||||
checkForMentionOrSlash(val, cursorPosition, evt.target);
|
||||
};
|
||||
|
||||
const checkForMention = (text: string, cursorPosition: number, textArea: HTMLTextAreaElement) => {
|
||||
// Find the last @ before the cursor
|
||||
const checkForMentionOrSlash = (
|
||||
text: string,
|
||||
cursorPosition: number,
|
||||
textArea: HTMLTextAreaElement
|
||||
) => {
|
||||
const isSlashCommand = text.startsWith('/');
|
||||
const beforeCursor = text.slice(0, cursorPosition);
|
||||
const lastAtIndex = beforeCursor.lastIndexOf('@');
|
||||
const lastAtIndex = isSlashCommand ? 0 : beforeCursor.lastIndexOf('@');
|
||||
|
||||
if (lastAtIndex === -1) {
|
||||
// No @ found, close mention popover
|
||||
@@ -597,6 +603,7 @@ export default function ChatInput({
|
||||
query: afterAt,
|
||||
mentionStart: lastAtIndex,
|
||||
selectedIndex: 0, // Reset selection when query changes
|
||||
isSlashCommand,
|
||||
// filteredFiles will be populated by the MentionPopover component
|
||||
}));
|
||||
};
|
||||
@@ -1024,13 +1031,13 @@ export default function ChatInput({
|
||||
}
|
||||
};
|
||||
|
||||
const handleMentionFileSelect = (filePath: string) => {
|
||||
const handleMentionItemSelect = (itemText: string) => {
|
||||
// Replace the @ mention with the file path
|
||||
const beforeMention = displayValue.slice(0, mentionPopover.mentionStart);
|
||||
const afterMention = displayValue.slice(
|
||||
mentionPopover.mentionStart + 1 + mentionPopover.query.length
|
||||
);
|
||||
const newValue = `${beforeMention}${filePath}${afterMention}`;
|
||||
const newValue = `${beforeMention}${itemText}${afterMention}`;
|
||||
|
||||
setDisplayValue(newValue);
|
||||
setValue(newValue);
|
||||
@@ -1040,7 +1047,7 @@ export default function ChatInput({
|
||||
// Set cursor position after the inserted file path
|
||||
setTimeout(() => {
|
||||
if (textAreaRef.current) {
|
||||
const newCursorPosition = beforeMention.length + filePath.length;
|
||||
const newCursorPosition = beforeMention.length + itemText.length;
|
||||
textAreaRef.current.setSelectionRange(newCursorPosition, newCursorPosition);
|
||||
}
|
||||
}, 0);
|
||||
@@ -1453,7 +1460,6 @@ export default function ChatInput({
|
||||
{/* Directory path */}
|
||||
<DirSwitcher className="mr-0" />
|
||||
<div className="w-px h-4 bg-border-default mx-2" />
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
@@ -1468,9 +1474,7 @@ export default function ChatInput({
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Attach file or directory</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<div className="w-px h-4 bg-border-default mx-2" />
|
||||
|
||||
{/* Model selector, mode selector, alerts, summarize button */}
|
||||
<div className="flex flex-row items-center">
|
||||
{/* Cost Tracker */}
|
||||
@@ -1538,7 +1542,6 @@ export default function ChatInput({
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{sessionId && diagnosticsOpen && (
|
||||
<DiagnosticsModal
|
||||
isOpen={diagnosticsOpen}
|
||||
@@ -1546,12 +1549,12 @@ export default function ChatInput({
|
||||
sessionId={sessionId}
|
||||
/>
|
||||
)}
|
||||
|
||||
<MentionPopover
|
||||
ref={mentionPopoverRef}
|
||||
isOpen={mentionPopover.isOpen}
|
||||
isSlashCommand={mentionPopover.isSlashCommand}
|
||||
onClose={() => setMentionPopover((prev) => ({ ...prev, isOpen: false }))}
|
||||
onSelect={handleMentionFileSelect}
|
||||
onSelect={handleMentionItemSelect}
|
||||
position={mentionPopover.position}
|
||||
query={mentionPopover.query}
|
||||
selectedIndex={mentionPopover.selectedIndex}
|
||||
|
||||
Reference in New Issue
Block a user