From 7377a81b297076350e4c8d26f6ea8db634b38683 Mon Sep 17 00:00:00 2001 From: JJ Lee Date: Fri, 7 Aug 2026 00:15:35 -0400 Subject: [PATCH] fix(app): text-card question renders visible, focused, sized textarea (#121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three compounding failures made kind:"text" question cards show no input: 1. CSS visibility: the textarea reused the choice-card's transparent/borderless custom-input style. Add a scoped [data-slot="question-text-form"] rule that gives it a border, background, radius, and min-height — the textarea is now visually distinct as the primary affordance. 2. Container measurement race: resizeInput sets height=0 then scrollHeight, but the ResizeObserver could fire during the transient. After resizeInput, sync optionsHeight explicitly so the container always accommodates the textarea. 3. Focus routing: focus() targeted customRef (the choice-card's "Type your own answer" button) which doesn't render for text-kind questions. Short-circuit the focus function when text() is true — focusCustom handles it via the ref callback. Closes #121 --- .../composer/session-question-dock.tsx | 7 +++++ .../src/components/message-part.css | 30 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/packages/app/src/pages/session/composer/session-question-dock.tsx b/packages/app/src/pages/session/composer/session-question-dock.tsx index f5cdfa8fe6..6eda5f0bc5 100644 --- a/packages/app/src/pages/session/composer/session-question-dock.tsx +++ b/packages/app/src/pages/session/composer/session-question-dock.tsx @@ -169,6 +169,9 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit } const focus = (i: number) => { + // Text-card: the textarea is the only focusable element; focusCustom + // handles it via the ref callback — skip option routing entirely. + if (text()) return const next = clamp(i) setStore("focus", next) if (store.editing) return @@ -431,6 +434,10 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit setTimeout(() => { el.focus() resizeInput(el) + // Sync the container measurement after the textarea has its final height + if (optionsRef) { + setStore("optionsHeight", (h) => Math.max(h, optionsRef!.scrollHeight)) + } }, 0) } diff --git a/packages/session-ui/src/components/message-part.css b/packages/session-ui/src/components/message-part.css index 36d6058c57..6a04faecdb 100644 --- a/packages/session-ui/src/components/message-part.css +++ b/packages/session-ui/src/components/message-part.css @@ -1141,6 +1141,26 @@ } } + /* Text-card (kind: "text") — the textarea is the ONLY affordance, so it + needs visible chrome (border + background) unlike the choice-card's + secondary custom-input which is borderless/transparent. */ + [data-slot="question-text-form"] { + padding: 4px 8px; + } + + [data-slot="question-text-form"] > [data-slot="question-custom-input"] { + border: 1px solid var(--v2-border-border-base); + border-radius: var(--radius-md); + padding: 8px 12px; + background: var(--v2-background-bg-layer-02); + min-height: 36px; + + &:focus-visible { + border-color: var(--v2-border-border-focus); + outline: none; + } + } + [data-slot="question-footer"] { display: flex; align-items: center; @@ -1589,6 +1609,16 @@ body:not([data-new-layout]) { outline: 1px solid var(--border-interactive-base); } } + + [data-slot="question-text-form"] > [data-slot="question-custom-input"] { + border-color: var(--border-weak-base); + background: var(--surface-weak); + + &:focus-visible { + border-color: var(--border-interactive-base); + outline: none; + } + } } [data-component="question-answers"] {