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:
2026-02-20 12:55:10 -06:00
parent 779e32b283
commit 894d5213c7

View File

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