FN-5706: hide chat input overflow until max-height

Prevent the chat composer from showing a right-edge scrollbar line before it reaches the autosize cap.

- set chat textarea default overflow to hidden in ChatView CSS
- add resolveChatInputOverflowY to switch overflow to auto only past the 640px cap
- apply overflow mode during composer autosize updates
- extend chat input autosize tests and docs to cover overflow behavior

Files changed:
 docs/dashboard-guide.md                                          | 2 +-
 packages/dashboard/app/components/ChatView.css                   | 1 +
 packages/dashboard/app/components/ChatView.tsx                   | 5 +++++
 packages/dashboard/app/components/__tests__/ChatView.chat-input-autosize.test.tsx   | 9 ++++++++-
 4 files changed, 15 insertions(+), 2 deletions(-)

Fusion-Task-Id: FN-5706

Fusion-Task-Lineage: 4619aa30-c02f-453b-bdf2-f60f360e9372
This commit is contained in:
gsxdsm
2026-05-29 20:47:13 -07:00
parent beab117771
commit ab1f6a7ffa
4 changed files with 15 additions and 2 deletions

View File

@@ -1078,7 +1078,7 @@ const baseOnly = await loadAllAppCssBaseOnly(); // strips @media/@supports
### File browser editor & autosize textarea
- `FileEditor.tsx` is CodeMirror 6-only (no `<textarea>` fallback). Language resolution: `packages/dashboard/app/utils/codemirror-language.ts`.
- For chat-style composer fields use `packages/dashboard/app/hooks/useAutosizeTextarea.ts`. Pattern: `height = "auto"` then clamp `scrollHeight` to min/max in `useLayoutEffect`. Pair with `resize: none` and `overflow-y: auto`.
- For chat-style composer fields use `packages/dashboard/app/hooks/useAutosizeTextarea.ts`. Pattern: `height = "auto"` then clamp `scrollHeight` to min/max in `useLayoutEffect`. Pair with `resize: none`; keep `overflow-y: hidden` while under the max-height cap and switch to `overflow-y: auto` only after content exceeds the cap.
### File-path links

View File

@@ -1422,6 +1422,7 @@
-webkit-appearance: none;
appearance: none;
resize: none;
overflow-y: hidden;
border: 1px solid var(--border);
border-radius: 12px;
padding: 10px 14px;

View File

@@ -59,6 +59,10 @@ export interface ChatViewProps {
const CHAT_INPUT_MAX_HEIGHT_PX = 640;
let chatViewWasPreviouslyInactive = false;
export function resolveChatInputOverflowY(scrollHeight: number): "auto" | "hidden" {
return scrollHeight > CHAT_INPUT_MAX_HEIGHT_PX ? "auto" : "hidden";
}
export function clampChatInputHeight(scrollHeight: number): number {
// Floor matches QuickChat (clampQuickChatInputHeight) and the CSS min-height,
// so a 0-scrollHeight measurement (e.g. before layout) still yields a
@@ -1770,6 +1774,7 @@ export function ChatView({ projectId, addToast, experimentalFeatures }: ChatView
composer.style.height = "auto";
composer.style.height = `${clampChatInputHeight(composer.scrollHeight)}px`;
composer.style.overflowY = resolveChatInputOverflowY(composer.scrollHeight);
}, []);
const handleComposerRef = useCallback((textarea: HTMLTextAreaElement | null) => {

View File

@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { clampChatInputHeight } from "../ChatView";
import { clampChatInputHeight, resolveChatInputOverflowY } from "../ChatView";
const chatViewCss = readFileSync(resolve(__dirname, "../ChatView.css"), "utf8");
@@ -12,6 +12,7 @@ describe("ChatView chat input autosize", () => {
expect(textareaRule).not.toBeNull();
expect(textareaRule?.[0]).toContain("max-height: 640px");
expect(textareaRule?.[0]).toContain("flex: 0 0 auto");
expect(textareaRule?.[0]).toContain("overflow-y: hidden");
});
it("clamps oversized textarea growth to the new max height", () => {
@@ -23,4 +24,10 @@ describe("ChatView chat input autosize", () => {
it("preserves smaller textarea heights below the cap", () => {
expect(clampChatInputHeight(80)).toBe(80);
});
it("keeps overflow hidden until content exceeds the max height cap", () => {
expect(resolveChatInputOverflowY(80)).toBe("hidden");
expect(resolveChatInputOverflowY(640)).toBe("hidden");
expect(resolveChatInputOverflowY(641)).toBe("auto");
});
});