feat(frontend): rework chat composer (sticky input, inline new session, status footer)

- Center ThothII logo + tagline; break "Human In The Loop" onto a second line
- Rename "New question" → "New session"; the button now clears the screen and
  focuses the sticky bottom composer instead of opening a dialog
- Composer is always rendered and pinned at the bottom; typing the first
  message there creates the session (no modal)
- Add inline stop control (square + inner circle) that interrupts and saves
  the session via /close
- Always-present send affordance (CornerDownLeft ↵) on the right of the box
- Status footer under the box: mic (inert), model name, thinking level, and a
  context-usage gauge (placeholder)
- Brand-tinted spinner beside the logo while the harness/backend holds the
  ball; hidden once a widget needs human input

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 20:36:08 +02:00
co-authored by Claude Opus 4.8
parent bcaf6708de
commit d67409ff16
5 changed files with 192 additions and 39 deletions
+4 -7
View File
@@ -30,13 +30,10 @@ test("F1: create session -> widget via SSE -> respond -> POST /response", async
);
render(<App />);
// Open the "Nuova domanda" dialog
await userEvent.click(screen.getByRole("button", { name: /new/i }));
// Fill in the question and submit
const textarea = await screen.findByLabelText(/question/i);
await userEvent.type(textarea, "Quante vendite?");
await userEvent.click(screen.getByRole("button", { name: /^create$/i }));
// Type the first question directly in the sticky composer and send it
const composer = screen.getByRole("textbox", { name: /new question/i });
await userEvent.type(composer, "Quante vendite?");
await userEvent.click(screen.getByRole("button", { name: /send/i }));
// Wait for the session to be created and SSE to connect
await waitFor(() => expect(FakeEventSource.instances).toHaveLength(1));