fix: auto-grow textarea using CSS field-sizing: content
Replace JS-based auto-resize with native CSS field-sizing: content. Eliminates timing issues with absolute-positioned containers.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, nextTick } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import VoiceMicButton from './VoiceMicButton.vue'
|
import VoiceMicButton from './VoiceMicButton.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -14,8 +14,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const maxH = computed(() => {
|
const maxH = computed(() => {
|
||||||
const lines = props.maxLines ?? 6
|
const lines = props.maxLines ?? 6
|
||||||
// line-height is 1.5 at 13px = ~20px per line, plus padding
|
return lines <= 1 ? '1.5em' : `${lines * 1.5}em`
|
||||||
return lines <= 1 ? '20px' : `${lines * 20}px`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
@@ -25,14 +24,6 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
|
|
||||||
const input = ref('')
|
const input = ref('')
|
||||||
const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
|
||||||
|
|
||||||
function adjustHeight() {
|
|
||||||
const el = textareaRef.value
|
|
||||||
if (!el) return
|
|
||||||
el.style.height = 'auto'
|
|
||||||
el.style.height = el.scrollHeight + 'px'
|
|
||||||
}
|
|
||||||
|
|
||||||
const notReady = computed(() => props.terminalReady === false)
|
const notReady = computed(() => props.terminalReady === false)
|
||||||
const isDisabled = computed(() => !input.value.trim() || props.processing || notReady.value)
|
const isDisabled = computed(() => !input.value.trim() || props.processing || notReady.value)
|
||||||
@@ -42,7 +33,6 @@ function handleSend() {
|
|||||||
if (!msg || props.processing || notReady.value) return
|
if (!msg || props.processing || notReady.value) return
|
||||||
emit('send', msg)
|
emit('send', msg)
|
||||||
input.value = ''
|
input.value = ''
|
||||||
nextTick(adjustHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(e: KeyboardEvent) {
|
function handleKeydown(e: KeyboardEvent) {
|
||||||
@@ -58,9 +48,6 @@ watch(() => props.voiceTranscript, (newText) => {
|
|||||||
input.value = newText
|
input.value = newText
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Auto-grow textarea when content changes
|
|
||||||
watch(input, () => nextTick(adjustHeight))
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -79,7 +66,6 @@ watch(input, () => nextTick(adjustHeight))
|
|||||||
</div>
|
</div>
|
||||||
<div class="input-container" :class="{ disabled: processing || notReady }">
|
<div class="input-container" :class="{ disabled: processing || notReady }">
|
||||||
<textarea
|
<textarea
|
||||||
ref="textareaRef"
|
|
||||||
v-model="input"
|
v-model="input"
|
||||||
class="input-field"
|
class="input-field"
|
||||||
:style="{ maxHeight: maxH }"
|
:style="{ maxHeight: maxH }"
|
||||||
@@ -182,7 +168,8 @@ watch(input, () => nextTick(adjustHeight))
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
resize: none;
|
resize: none;
|
||||||
min-height: 20px;
|
field-sizing: content;
|
||||||
|
min-height: 1lh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 0.15rem 0.25rem;
|
padding: 0.15rem 0.25rem;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
|||||||
Reference in New Issue
Block a user