mirror of
https://github.com/siteboon/claudecodeui.git
synced 2025-12-09 08:29:37 +00:00
Merge branch 'main' into fix/registration-race-condition
This commit is contained in:
@@ -1985,19 +1985,34 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
||||
const spaceIndex = textAfterAtQuery.indexOf(' ');
|
||||
const textAfterQuery = spaceIndex !== -1 ? textAfterAtQuery.slice(spaceIndex) : '';
|
||||
|
||||
const newInput = textBeforeAt + '@' + file.path + textAfterQuery;
|
||||
const newInput = textBeforeAt + '@' + file.path + ' ' + textAfterQuery;
|
||||
const newCursorPos = textBeforeAt.length + 1 + file.path.length + 1;
|
||||
|
||||
// Immediately ensure focus is maintained
|
||||
if (textareaRef.current && !textareaRef.current.matches(':focus')) {
|
||||
textareaRef.current.focus();
|
||||
}
|
||||
|
||||
// Update input and cursor position
|
||||
setInput(newInput);
|
||||
setCursorPosition(newCursorPos);
|
||||
|
||||
// Hide dropdown
|
||||
setShowFileDropdown(false);
|
||||
setAtSymbolPosition(-1);
|
||||
|
||||
// Focus back to textarea and set cursor position
|
||||
// Set cursor position synchronously
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.focus();
|
||||
const newCursorPos = textBeforeAt.length + 1 + file.path.length;
|
||||
setTimeout(() => {
|
||||
textareaRef.current.setSelectionRange(newCursorPos, newCursorPos);
|
||||
setCursorPosition(newCursorPos);
|
||||
}, 0);
|
||||
// Use requestAnimationFrame for smoother updates
|
||||
requestAnimationFrame(() => {
|
||||
if (textareaRef.current) {
|
||||
textareaRef.current.setSelectionRange(newCursorPos, newCursorPos);
|
||||
// Ensure focus is maintained
|
||||
if (!textareaRef.current.matches(':focus')) {
|
||||
textareaRef.current.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2238,6 +2253,37 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* File dropdown - positioned outside dropzone to avoid conflicts */}
|
||||
{showFileDropdown && filteredFiles.length > 0 && (
|
||||
<div className="absolute bottom-full left-0 right-0 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg max-h-48 overflow-y-auto z-50 backdrop-blur-sm">
|
||||
{filteredFiles.map((file, index) => (
|
||||
<div
|
||||
key={file.path}
|
||||
className={`px-4 py-3 cursor-pointer border-b border-gray-100 dark:border-gray-700 last:border-b-0 touch-manipulation ${
|
||||
index === selectedFileIndex
|
||||
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300'
|
||||
: 'hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
onMouseDown={(e) => {
|
||||
// Prevent textarea from losing focus on mobile
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
selectFile(file);
|
||||
}}
|
||||
>
|
||||
<div className="font-medium text-sm">{file.name}</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 font-mono">
|
||||
{file.path}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div {...getRootProps()} className={`relative bg-white dark:bg-gray-800 rounded-2xl shadow-lg border border-gray-200 dark:border-gray-600 focus-within:ring-2 focus-within:ring-blue-500 dark:focus-within:ring-blue-500 focus-within:border-blue-500 transition-all duration-200 ${isTextareaExpanded ? 'chat-input-expanded' : ''}`}>
|
||||
<input {...getInputProps()} />
|
||||
<textarea
|
||||
@@ -2355,28 +2401,6 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{/* File dropdown */}
|
||||
{showFileDropdown && filteredFiles.length > 0 && (
|
||||
<div className="absolute bottom-full left-0 right-0 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-lg shadow-lg max-h-48 overflow-y-auto z-50">
|
||||
{filteredFiles.map((file, index) => (
|
||||
<div
|
||||
key={file.path}
|
||||
className={`px-4 py-2 cursor-pointer border-b border-gray-100 dark:border-gray-700 last:border-b-0 ${
|
||||
index === selectedFileIndex
|
||||
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300'
|
||||
: 'hover:bg-gray-50 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-300'
|
||||
}`}
|
||||
onClick={() => selectFile(file)}
|
||||
>
|
||||
<div className="font-medium text-sm">{file.name}</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 font-mono">
|
||||
{file.path}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Hint text */}
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 text-center mt-2 hidden sm:block">
|
||||
|
||||
Reference in New Issue
Block a user