Chat welcome + active conversation: composer controls, layout, header
Measured welcome-screen + active-conversation feedback: Composer redesigned with two variants (no Send button — Enter to send, Shift+Enter for newline; attach + screenshot icon affordances on the left). 'welcome' = short single-row 672px-capped pill, icons inline; 'active' = taller two-row card (textarea on top, 36px icon-button row below), placeholder switches to 'Type your message…'. A cream Stop pill appears while the agent is generating, wired to a real useChat.stop() that aborts the SSE and finalizes the partial reply (new 'stopped' reducer action). Empty state un-pinned: the composer is now part of the centered welcome stack (avatar → heading → subtitle → composer → chips) instead of being pinned to the viewport bottom. Subtitle dropped to 12px. Hero avatar kept at 126px but with a proportional 40px squircle radius (a flat 17px read as square at that size). Chat header sized up to match: 58px avatar (new Avatar 'header' size), name 18px/600 neutral-200, role 14px. The neutral-500 role/subtitle tone nudged to #7d7d7d to clear AA contrast on #0a0a0a. 84 unit + 31 functional E2E green; verified both states live. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
660dbc6583
commit
7393b6538b
@@ -34,8 +34,13 @@ export function ChatWorkspace({
|
||||
sessions,
|
||||
}: ChatWorkspaceProps) {
|
||||
const router = useRouter();
|
||||
const { state, send, decide } = useChat(agent.id, sessionKey, initialMessages);
|
||||
const { state, send, decide, stop } = useChat(
|
||||
agent.id,
|
||||
sessionKey,
|
||||
initialMessages,
|
||||
);
|
||||
const [draft, setDraft] = useState("");
|
||||
const empty = state.messages.length === 0;
|
||||
const [{ sessions: showSessions }] = useQueryStates(panelParsers, {
|
||||
shallow: true,
|
||||
});
|
||||
@@ -73,22 +78,44 @@ export function ChatWorkspace({
|
||||
<section className="flex min-w-0 flex-1 flex-col">
|
||||
<ChatHeader agent={agent} onNewSession={newSession} />
|
||||
<div className="mx-auto flex w-full max-w-3xl min-w-0 flex-1 flex-col overflow-hidden px-4">
|
||||
{state.messages.length === 0 ? (
|
||||
<WelcomeState agent={agent} onPick={setDraft} />
|
||||
) : (
|
||||
<MessageList
|
||||
messages={state.messages}
|
||||
{empty ? (
|
||||
// Empty state: the composer is part of the centered welcome
|
||||
// stack (avatar → heading → subtitle → composer → chips).
|
||||
<WelcomeState
|
||||
agent={agent}
|
||||
onDecide={decide}
|
||||
onPick={setDraft}
|
||||
composer={
|
||||
<Composer
|
||||
agentName={agent.name}
|
||||
variant="welcome"
|
||||
disabled={state.streaming}
|
||||
onSend={send}
|
||||
onStop={stop}
|
||||
draft={draft}
|
||||
onDraftChange={setDraft}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
// Active conversation: transcript scrolls, the taller two-row
|
||||
// composer is pinned below it.
|
||||
<>
|
||||
<MessageList
|
||||
messages={state.messages}
|
||||
agent={agent}
|
||||
onDecide={decide}
|
||||
/>
|
||||
<Composer
|
||||
agentName={agent.name}
|
||||
variant="active"
|
||||
disabled={state.streaming}
|
||||
onSend={send}
|
||||
onStop={stop}
|
||||
draft={draft}
|
||||
onDraftChange={setDraft}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Composer
|
||||
agentName={agent.name}
|
||||
disabled={state.streaming}
|
||||
onSend={send}
|
||||
draft={draft}
|
||||
onDraftChange={setDraft}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<DevicePanel agent={agent} />
|
||||
|
||||
Reference in New Issue
Block a user