From cde5be5958a607e6aca5db4ac912a190e56f054b Mon Sep 17 00:00:00 2001 From: han <475166676@qq.com> Date: Fri, 13 Mar 2026 10:31:05 +0800 Subject: [PATCH] fix(chat): prevent textarea content under bottom toolbar --- src/components/chat/message-input.tsx | 75 ++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/src/components/chat/message-input.tsx b/src/components/chat/message-input.tsx index 01540b9..8d2edc8 100644 --- a/src/components/chat/message-input.tsx +++ b/src/components/chat/message-input.tsx @@ -1,6 +1,13 @@ "use client" -import { useCallback, useEffect, useMemo, useRef, useState } from "react" +import { + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} from "react" import { TauriEvent } from "@tauri-apps/api/event" import { getCurrentWebview } from "@tauri-apps/api/webview" import { open } from "@tauri-apps/plugin-dialog" @@ -1079,6 +1086,9 @@ export function MessageInput({ const hasImageAttachments = imageAttachments.length > 0 const hasResourceAttachments = resourceAttachments.length > 0 + const bottomBarRef = useRef(null) + const actionAreaRef = useRef(null) + const actionButtonRef = useRef(null) const topPaddingClass = hasImageAttachments && hasResourceAttachments ? "pt-[6.25rem]" @@ -1087,9 +1097,45 @@ export function MessageInput({ : hasResourceAttachments ? "pt-10" : "pt-3" - const bottomPaddingClass = "pb-10" + const [bottomPaddingPx, setBottomPaddingPx] = useState(40) const showDragActive = isDragActive && !disabled + useLayoutEffect(() => { + const bottomOffsetPx = 8 // Tailwind `bottom-2` + const bufferPx = 6 + + const bottomBar = bottomBarRef.current + const actionArea = actionAreaRef.current + const actionButton = actionButtonRef.current + if (!bottomBar && !actionArea && !actionButton) return + + const measure = () => { + const bottomBarHeight = bottomBar?.getBoundingClientRect().height ?? 0 + const actionAreaHeight = actionArea?.getBoundingClientRect().height ?? 0 + const actionButtonHeight = + actionButton?.getBoundingClientRect().height ?? 0 + const next = Math.ceil( + Math.max(bottomBarHeight, actionAreaHeight, actionButtonHeight) + + bottomOffsetPx + + bufferPx + ) + setBottomPaddingPx((prev) => (Math.abs(prev - next) < 1 ? prev : next)) + } + + measure() + + const observer = new ResizeObserver(() => { + measure() + }) + if (bottomBar) observer.observe(bottomBar) + if (actionArea) observer.observe(actionArea) + if (actionButton) observer.observe(actionButton) + + return () => { + observer.disconnect() + } + }, [hasAnySelector, isEditingQueueItem, isPrompting]) + const selectorItems = ( <> {showConfigLoading && ( @@ -1141,11 +1187,11 @@ export function MessageInput({ onPaste={handlePaste} onFocus={onFocus} placeholder={resolvedPlaceholder} + style={{ paddingBottom: bottomPaddingPx }} className={cn( "text-sm pr-12 resize-none bg-transparent", showDragActive && "ring-1 ring-primary/40", topPaddingClass, - bottomPaddingClass, className )} autoFocus={autoFocus} @@ -1211,7 +1257,15 @@ export function MessageInput({ {t("dropFilesToAttach")} )} -
+