feat(chat): add custom drag-resize handle on input top border (#725)

* feat(chat): add custom drag-resize handle on input top border

* fix(chat): skip auto-resize when user has manually set height via drag handle
This commit is contained in:
memeflyfly
2026-05-14 21:02:44 +08:00
committed by GitHub
parent d551b2d6db
commit 7420f7aad5
2 changed files with 107 additions and 10 deletions
@@ -20,6 +20,37 @@ const isDragging = ref(false)
const dragCounter = ref(0)
const isComposing = ref(false)
// 自定义高度拖拽
const textareaHeight = ref<number | null>(null) // null = auto
function startResize(e: MouseEvent) {
e.preventDefault()
const el = textareaRef.value
if (!el) return
// 如果当前是 auto,用实际 clientHeight 作为起始值
const startHeight = el.clientHeight
const startY = e.clientY
function onMouseMove(e: MouseEvent) {
const deltaY = e.clientY - startY
// 往上拖 (deltaY < 0) → 高度增加
const newHeight = startHeight - deltaY
textareaHeight.value = Math.max(20, Math.min(400, Math.round(newHeight)))
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove)
document.removeEventListener('mouseup', onMouseUp)
document.body.style.cursor = ''
document.body.style.userSelect = ''
}
document.body.style.cursor = 'row-resize'
document.body.style.userSelect = 'none'
document.addEventListener('mousemove', onMouseMove)
document.addEventListener('mouseup', onMouseUp)
}
// 自动播放语音开关
const autoPlaySpeech = ref(false)
@@ -229,6 +260,8 @@ function handleKeydown(e: KeyboardEvent) {
}
function handleInput(e: Event) {
// 用户手动拖拽自定义高度时,不覆盖
if (textareaHeight.value !== null) return
const el = e.target as HTMLTextAreaElement
el.style.height = 'auto'
el.style.height = Math.min(el.scrollHeight, 100) + 'px'
@@ -349,10 +382,12 @@ function isImage(type: string): boolean {
class="file-input-hidden"
@change="handleFileChange"
/>
<div class="resize-handle" @mousedown="startResize"></div>
<textarea
ref="textareaRef"
v-model="inputText"
class="input-textarea"
:style="textareaHeight ? { height: textareaHeight + 'px' } : {}"
:placeholder="t('chat.inputPlaceholder')"
rows="1"
@keydown="handleKeydown"
@@ -594,6 +629,7 @@ function isImage(type: string): boolean {
border: 1px solid $border-color;
border-radius: $radius-md;
padding: 10px 12px;
position: relative;
transition: border-color $transition-fast, background-color $transition-fast;
&:focus-within {
@@ -605,6 +641,21 @@ function isImage(type: string): boolean {
}
}
.resize-handle {
position: absolute;
top: -4px;
left: 0;
right: 0;
height: 8px;
cursor: row-resize;
z-index: 2;
&:hover {
background: rgba($accent-primary, 0.15);
border-radius: 4px;
}
}
.input-textarea {
flex: 1;
background: none;
@@ -615,7 +666,7 @@ function isImage(type: string): boolean {
font-size: 14px;
line-height: 1.5;
resize: none;
max-height: 100px;
max-height: 400px;
min-height: 20px;
overflow-y: auto;