From 5f63f7c7445d22e4f0e967bbe8e00ed0654fbd26 Mon Sep 17 00:00:00 2001 From: Andrew Beal Date: Sun, 22 Mar 2026 20:07:26 +0000 Subject: [PATCH] feat: add stacked layout for chat input when content wraps Add dynamic layout switching that moves input buttons below the text area when content height exceeds initial height. Includes height tracking, state management via checkStacked(), and CSS grid updates for stacked mode on desktop only. --- Components/ChatInput.svelte | 45 ++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/Components/ChatInput.svelte b/Components/ChatInput.svelte index ea29581..9a62a21 100644 --- a/Components/ChatInput.svelte +++ b/Components/ChatInput.svelte @@ -49,6 +49,7 @@ let userInstructionAreaActive: boolean = false; let userInstructionActive: boolean = true; + let stacked: boolean = false; let userRequest: string = ""; @@ -57,6 +58,7 @@ let countdownIntervalId: ReturnType | null = null; let countdownSecondsRemaining: number = 0; + let inputInitialHeight: number = 0; const diffOpenedRef: EventRef = eventService.on(Event.DiffOpened, () => { inputMode = InputMode.Diff; focusInput(); }); const diffClosedRef: EventRef = eventService.on(Event.DiffClosed, () => { inputMode = InputMode.Normal; focusInput(); }); @@ -64,6 +66,7 @@ onMount(async () => { userInstructionActive = (await aiPrompt.userInstruction()).trim() !== ""; + inputInitialHeight = textareaElement.innerHeight; }); onDestroy(() => { @@ -73,6 +76,18 @@ stopCountdown(); }); + function checkStacked() { + if (textareaElement.textContent.trim() === "") { + stacked = false; + return; + } + + if (textareaElement.innerHeight > inputInitialHeight) { + stacked = true; + return; + } + } + export function focusInput(onMobile: boolean = false) { // don't focus on mobile, it's annoying if (onMobile || !Platform.isMobile) { @@ -247,6 +262,7 @@ textareaElement.textContent = ""; userRequest = ""; + checkStacked(); if (Platform.isMobile) { textareaElement.blur(); @@ -399,6 +415,8 @@ if (userRequest.trim() === "") { textareaElement.textContent = ""; } + + checkStacked(); } } @@ -471,7 +489,7 @@ } -
+
0 ? "var(--size-4-2)" : 0}>
@@ -739,6 +757,31 @@ background-color: var(--alt-interactive-accent-hover); } + /* Stacked layout: input above, buttons below (desktop only, when content wraps) */ + #input-container.stacked { + grid-template-rows: auto auto auto auto var(--size-4-3) 1fr var(--size-4-2) auto var(--size-4-3); + } + + #input-container.stacked #input-field { + grid-column: 2 / 11; + } + + #input-container.stacked #user-instruction-button { + grid-row: 8; + } + + #input-container.stacked #edit-mode-button { + grid-row: 8; + } + + #input-container.stacked #planning-mode-button { + grid-row: 8; + } + + #input-container.stacked #submit-button { + grid-row: 8; + } + /* Narrow/mobile layout: input above, buttons below */ :global(.is-mobile) #input-container { grid-template-rows: auto auto auto auto var(--size-4-3) 1fr var(--size-4-2) auto var(--size-4-3);