fix: resolve mobile chat input freeze and multiline attachment button alignment (#9202)

* fix: resolve mobile chat input freeze caused by autoResize oscillation

The autoResize() function in ChatInput.vue used minHeight + 8 as the
threshold to decide whether to switch from multi-line (textarea) back
to single-line (input). On mobile viewports (max-width: 768px), the
textarea's 2-line scrollHeight is below minHeight + 8 due to smaller
line-height and padding, causing an infinite oscillation loop between
<input> and <textarea> that freezes the browser.

Fix: compute the actual single-line content height from
getComputedStyle (line-height + vertical padding) and use that as the
wrapping threshold instead of the hardcoded minHeight + 8 offset.
Also temporarily set min-height to 0 during scrollHeight measurement
to avoid the CSS min-height: 52px inflating the value.

* fix: align mobile multiline attachment button to the left

The mobile multiline grid used a 2-column layout (1fr auto) with
'left right' areas, causing the left actions area to span the full
remaining width and centering the + button via justify-content: center.

Switch to the same 3-column layout (auto 1fr auto) with 'left . right'
areas used on desktop, so the + button sits at the left edge.

* fix: use !important to override min-height during scrollHeight measurement

el.style.minHeight = '0' cannot override the CSS min-height: 52px
!important rule, leaving scrollHeight inflated and preventing the
textarea from switching back to single-line for short text.

Use setProperty('min-height', '0', 'important') to force the override,
and removeProperty to restore. Also add a fontSize * 1.2 fallback when
getComputedStyle returns 'normal' for line-height, so the threshold is
always computable.
This commit is contained in:
Fiber
2026-07-11 22:46:55 +08:00
committed by GitHub
parent 7422fb075c
commit 4c6ef8a492

View File

@@ -656,9 +656,19 @@ function autoResize() {
return;
}
el.style.height = "auto";
el.style.setProperty("min-height", "0", "important");
const measuredHeight = el.scrollHeight;
el.style.removeProperty("min-height");
const computed = getComputedStyle(el);
let lineHeight = parseFloat(computed.lineHeight);
if (!Number.isFinite(lineHeight)) {
lineHeight = parseFloat(computed.fontSize) * 1.2;
}
const paddingVertical =
parseFloat(computed.paddingTop) + parseFloat(computed.paddingBottom);
const shouldUseMultiline =
localPrompt.value.includes("\n") || measuredHeight > minHeight + 8;
localPrompt.value.includes("\n") ||
measuredHeight > lineHeight + paddingVertical + 0.5;
if (inputIsMultiline.value !== shouldUseMultiline) {
const cursor = el.selectionStart ?? localPrompt.value.length;
inputIsMultiline.value = shouldUseMultiline;
@@ -1624,10 +1634,10 @@ defineExpose({
}
.input-container.is-multiline .composer-row {
grid-template-columns: minmax(0, 1fr) auto;
grid-template-columns: auto minmax(0, 1fr) auto;
grid-template-areas:
"field field"
"left right";
"field field field"
"left . right";
min-height: auto;
row-gap: 4px;
}