feat: enable attachment UI
This commit is contained in:
parent
926f3ab99a
commit
5f1cb67ffc
@ -35,7 +35,8 @@
|
|||||||
"effects": ["fullScreenUI", "mica", "tabbed", "blur", "acrylic"],
|
"effects": ["fullScreenUI", "mica", "tabbed", "blur", "acrylic"],
|
||||||
"state": "active",
|
"state": "active",
|
||||||
"radius": 8
|
"radius": 8
|
||||||
}
|
},
|
||||||
|
"dragDropEnabled": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"security": {
|
"security": {
|
||||||
|
|||||||
@ -70,6 +70,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
}>
|
}>
|
||||||
>([])
|
>([])
|
||||||
const [connectedServers, setConnectedServers] = useState<string[]>([])
|
const [connectedServers, setConnectedServers] = useState<string[]>([])
|
||||||
|
const [isDragOver, setIsDragOver] = useState(false)
|
||||||
|
|
||||||
// Check for connected MCP servers
|
// Check for connected MCP servers
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -281,6 +282,54 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDragEnter = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setIsDragOver(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragLeave = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
// Only set dragOver to false if we're leaving the drop zone entirely
|
||||||
|
// In Tauri, relatedTarget can be null, so we need to handle that case
|
||||||
|
const relatedTarget = e.relatedTarget as Node | null
|
||||||
|
if (!relatedTarget || !e.currentTarget.contains(relatedTarget)) {
|
||||||
|
setIsDragOver(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
// Ensure drag state is maintained during drag over
|
||||||
|
setIsDragOver(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
setIsDragOver(false)
|
||||||
|
|
||||||
|
// Check if dataTransfer exists (it might not in some Tauri scenarios)
|
||||||
|
if (!e.dataTransfer) {
|
||||||
|
console.warn('No dataTransfer available in drop event')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = e.dataTransfer.files
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
// Create a synthetic event to reuse existing file handling logic
|
||||||
|
const syntheticEvent = {
|
||||||
|
target: {
|
||||||
|
files: files,
|
||||||
|
},
|
||||||
|
} as React.ChangeEvent<HTMLInputElement>
|
||||||
|
|
||||||
|
handleFileChange(syntheticEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@ -305,8 +354,13 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative z-20 px-0 pb-10 border border-main-view-fg/5 rounded-lg text-main-view-fg bg-main-view',
|
'relative z-20 px-0 pb-10 border border-main-view-fg/5 rounded-lg text-main-view-fg bg-main-view',
|
||||||
isFocused && 'ring-1 ring-main-view-fg/10'
|
isFocused && 'ring-1 ring-main-view-fg/10',
|
||||||
|
isDragOver && 'ring-2 ring-accent border-accent'
|
||||||
)}
|
)}
|
||||||
|
onDragEnter={handleDragEnter}
|
||||||
|
onDragLeave={handleDragLeave}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
|
onDrop={handleDrop}
|
||||||
>
|
>
|
||||||
{uploadedFiles.length > 0 && (
|
{uploadedFiles.length > 0 && (
|
||||||
<div className="flex gap-3 items-center p-2 pb-0">
|
<div className="flex gap-3 items-center p-2 pb-0">
|
||||||
@ -372,8 +426,14 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
}}
|
}}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
// e.keyCode 229 is for IME input with Safari
|
// e.keyCode 229 is for IME input with Safari
|
||||||
const isComposing = e.nativeEvent.isComposing || e.keyCode === 229;
|
const isComposing =
|
||||||
if (e.key === 'Enter' && !e.shiftKey && prompt.trim() && !isComposing) {
|
e.nativeEvent.isComposing || e.keyCode === 229
|
||||||
|
if (
|
||||||
|
e.key === 'Enter' &&
|
||||||
|
!e.shiftKey &&
|
||||||
|
prompt.trim() &&
|
||||||
|
!isComposing
|
||||||
|
) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
// Submit the message when Enter is pressed without Shift
|
// Submit the message when Enter is pressed without Shift
|
||||||
handleSendMesage(prompt)
|
handleSendMesage(prompt)
|
||||||
@ -414,7 +474,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
)}
|
)}
|
||||||
{/* File attachment - always available */}
|
{/* File attachment - always available */}
|
||||||
<div
|
<div
|
||||||
className="h-6 hidden p-1 items-center justify-center rounded-sm hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out gap-1"
|
className="h-6 p-1 items-center justify-center rounded-sm hover:bg-main-view-fg/10 transition-all duration-200 ease-in-out gap-1"
|
||||||
onClick={handleAttachmentClick}
|
onClick={handleAttachmentClick}
|
||||||
>
|
>
|
||||||
<IconPaperclip size={18} className="text-main-view-fg/50" />
|
<IconPaperclip size={18} className="text-main-view-fg/50" />
|
||||||
@ -422,6 +482,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
|||||||
type="file"
|
type="file"
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
|
multiple
|
||||||
onChange={handleFileChange}
|
onChange={handleFileChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user