Merge branch 'main' into fix/registration-race-condition

This commit is contained in:
viper151
2025-07-13 11:37:21 +02:00
committed by GitHub

View File

@@ -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">