From 8c50b50749e387a3691830f5bf9f7cb90e16498c Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Thu, 24 Sep 2026 19:21:03 +0300 Subject: [PATCH] New-session page reads like a pre-session: no phase chip, outlined bar, opening-question placeholder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three owner fixes for /chat/new (refinement round 4, 2026-09-24) — the page must stop looking like a session that's already running: 1. NO PHASE CHIP: the header's dies — no session has started, so no phase exists yet. Active sessions keep their chip (chat.tsx's derives from the live timeline). 2. TOP BAR STRUCTURE MATCHS SESSIONS: the header gains the same border-b border-border as the chat header (h-15, lh-drag, 60px). The floating-bar look is gone; /chat/new now reads as the same shell with empty content. 3. COMPOSER PLACEHOLDER = THE OPENING QUESTION: active sessions keep "Ask, answer, or steer the lesson…" (now the DEFAULT of a new Composer `placeholder` prop), but the fresh page asks what the graph view's new-session input asks — "What do you want to learn?" No lesson is in flight, so the composer must not read like one is. The ConversationEmptyState intro card ("The teacher will open by probing…") stays, per owner confirmation. Live-verified on :3001 /chat/new: no phase-chip node, computed 1px header border rgb(67,67,67) at h-15/60px, placeholder reads "What do you want to learn?"; the empty-state card intact. Unit test pins the default session placeholder AND the override. 903/903. --- components/composer.tsx | 7 ++++++- components/new-session.tsx | 9 ++++++--- tests/render-composer.test.tsx | 11 +++++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/components/composer.tsx b/components/composer.tsx index 4e81e6b..6034d7c 100644 --- a/components/composer.tsx +++ b/components/composer.tsx @@ -35,6 +35,7 @@ export function Composer({ autoFocus, verdict, onEscape, + placeholder = "Ask, answer, or steer the lesson…", }: { busy: boolean; value: string; @@ -50,6 +51,10 @@ export function Composer({ /** Escape while a dictation runs stops it (STT wave, 2026-09-17). No-op * when absent — Esc otherwise means nothing in the composer. */ onEscape?: () => void; + /** The textarea's placeholder (owner refinement round, 2026-09-24): + * active sessions read "Ask, answer, or steer the lesson…"; the fresh + * new-session page asks the graph view's opening question instead. */ + placeholder?: string; }) { return (
{/* h-15 (60px) matches the chat header and the sidebar's LH wordmark band (owner, round 6). */} -
+
New session
-
@@ -125,6 +123,11 @@ export function DraftShell({ canSend={canSend} autoFocus onEscape={dictation.active ? dictation.cancel : undefined} + // The opening question, same as the graph view's new-session + // input (owner refinement round, 2026-09-24) — no lesson has + // started yet, so the composer must not read like one is in + // flight. + placeholder="What do you want to learn?" />
{}} onStop={() => {}} canSend={opts.canSend ?? true} + {...(opts.placeholder ? { placeholder: opts.placeholder } : {})} />, ); } @@ -29,10 +30,16 @@ function render(opts: { busy?: boolean; canSend?: boolean } = {}) { describe("Composer", () => { it("chat placeholder + send button", () => { const html = render(); - assert.match(html, /Ask, answer, or steer the lesson/); + assert.match(html, /Ask, answer, or steer the lesson/, "the default — active sessions"); assert.ok(html.includes('aria-label="Send"'), "send button present"); }); + it("placeholder is overridable — the new-session page asks the graph view's question", () => { + const html = render({ placeholder: "What do you want to learn?" }); + assert.match(html, /What do you want to learn\?/); + assert.ok(!html.includes("Ask, answer"), "the session placeholder must not ride along"); + }); + it("busy: send button becomes stop", () => { const html = render({ busy: true }); assert.ok(html.includes('aria-label="Stop"'), "stop replaces send while streaming");