fix(web): improve chat input UX — IME composing, send button, file upload

- Prevent Enter from sending message during IME composing (keyCode 229)
- Remove separator line between textarea and toolbar
- Change send/stop buttons to compact circular icon-only style
- Fix send icon color to white for dark mode visibility
- Add missing hidden file input element so the attach button works
This commit is contained in:
Acbox
2026-03-29 18:09:28 +08:00
parent bb56ed3048
commit 122382d7d4
@@ -118,6 +118,13 @@
</div> </div>
</div> </div>
<input
ref="fileInput"
type="file"
multiple
class="hidden"
@change="handleFileInputChange"
>
<section> <section>
<InputGroup class="bg-transparent overflow-hidden"> <InputGroup class="bg-transparent overflow-hidden">
<InputGroupTextarea <InputGroupTextarea
@@ -129,7 +136,6 @@
@keydown.enter.exact="handleKeydown" @keydown.enter.exact="handleKeydown"
@paste="handlePaste" @paste="handlePaste"
/> />
<Separator />
<InputGroupAddon <InputGroupAddon
align="block-end" align="block-end"
class="bg-[#FAFAFA] dark:bg-background items-center py-1.5" class="bg-[#FAFAFA] dark:bg-background items-center py-1.5"
@@ -161,23 +167,22 @@
<Button <Button
v-if="!streaming" v-if="!streaming"
type="button" type="button"
size="sm" size="icon"
:disabled="(!inputText.trim() && !pendingFiles.length) || !currentBotId || activeChatReadOnly" :disabled="(!inputText.trim() && !pendingFiles.length) || !currentBotId || activeChatReadOnly"
aria-label="Send message" aria-label="Send message"
class="ml-auto bg-[#8B56E3]" class="ml-auto size-7 rounded-full bg-[#8B56E3] text-white"
@click="handleSend" @click="handleSend"
> >
<Send <Send
class="size-2" class="size-3"
/> />
{{ $t('chat.send') }}
</Button> </Button>
<Button <Button
v-else v-else
type="button" type="button"
size="sm" size="icon"
variant="destructive" variant="destructive"
class="ml-auto" class="ml-auto size-7 rounded-full"
aria-label="Stop generating response" aria-label="Stop generating response"
@click="chatStore.abort()" @click="chatStore.abort()"
> >
@@ -220,7 +225,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, nextTick, onMounted, provide, useTemplateRef, watchEffect} from 'vue' import { ref, computed, nextTick, onMounted, provide, useTemplateRef, watchEffect} from 'vue'
import { LoaderCircle, Image as ImageIcon, File as FileIcon, X, Paperclip, FolderOpen, Send } from 'lucide-vue-next' import { LoaderCircle, Image as ImageIcon, File as FileIcon, X, Paperclip, FolderOpen, Send } from 'lucide-vue-next'
import { ScrollArea, Button, InputGroup, InputGroupAddon, InputGroupTextarea, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription,Separator } from '@memohai/ui' import { ScrollArea, Button, InputGroup, InputGroupAddon, InputGroupTextarea, Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '@memohai/ui'
import { useChatStore } from '@/store/chat-list' import { useChatStore } from '@/store/chat-list'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import MessageItem from './message-item.vue' import MessageItem from './message-item.vue'
@@ -340,12 +345,22 @@ watchEffect(() => {
}) })
function handleKeydown(e: KeyboardEvent) { function handleKeydown(e: KeyboardEvent) {
if (e.isComposing) return if (e.isComposing || e.keyCode === 229) return
e.preventDefault() e.preventDefault()
handleSend() handleSend()
} }
function handleFileInputChange(e: Event) {
const input = e.target as HTMLInputElement
if (input.files) {
for (const file of Array.from(input.files)) {
pendingFiles.value.push(file)
}
}
input.value = ''
}
function handlePaste(e: ClipboardEvent) { function handlePaste(e: ClipboardEvent) {
const items = e.clipboardData?.items const items = e.clipboardData?.items
if (!items) return if (!items) return