Fix sidebar rail overflow on the chat page

The content offset was fine — the rail's glass panel was the problem. As a
flex-1 item in the row-flex aside, the panel's automatic minimum size
(min-width: auto) is its content's min-content width. The chat list's
search input (an <input> defaults to size=20) has a ~192px intrinsic
width, which with the New chat button stretched the panel to 345px — 89px
past the 268px rail — where it painted over the chat page: the header
title, the composer placeholder ("sk about your notes...") and the
context chip ("ts").

- min-w-0 on the panel so the rail width always wins and wide content
  truncates instead (the input and list rows already shrink/truncate).
- Chat suite: assert the panel stays inside the rail and the main content
  starts after it, expanded and collapsed (the old checks counted
  elements, never their boxes — this is what slipped through).
- README: history bullet + decision 49. SW -> freezmo-v36.

Verified on the dev instance and a production build: panel 244px inside
the 268px rail (60px in the 84px collapsed rail) at 1024-1920, content
starts after the rail, all sidebar pages unchanged, dark + light, and
the full e2e (12/12 suites, 516 checks).
This commit is contained in:
Martin Dvořák 2026-10-11 21:04:15 +00:00
commit 05e0fdcc86
4 changed files with 55 additions and 9 deletions

View file

@ -765,11 +765,12 @@ have a step explained without leaving the app.
allowlist and never accepts a model name from the client.
- **Attachments.** Photos, PDFs and documents go through the existing vision/extraction step first
(plain text), so any chat model works — text-only included.
- **History.** Conversations live in a local IndexedDB store: a list on the left on desktop, a
sheet on phones, with auto titles from your first question, rename, delete (with Undo) and a
search across titles and message text. Conversations remember their scope and model, reopen from
`?c=<id>` (so a reload continues where you left off), stay readable offline, and are included in
the backup export and the encrypted sync — still never the key.
- **History.** Conversations live in a local IndexedDB store: the list sits in the main sidebar on
desktop (it takes the deck navigation's place on `/chat`), a sheet on phones, with auto titles
from your first question, rename, delete (with Undo) and a search across titles and message
text. Conversations remember their scope and model, reopen from `?c=<id>` (so a reload continues
where you left off), stay readable offline, and are included in the backup export and the
encrypted sync — still never the key.
- **From an answer.** Every answer can become a deck (**Turn into flashcards** opens New deck with
the answer as notes, on the options step) or be appended to the scoped deck's notes (**Save to
notes**, only offered when exactly one deck is in scope).
@ -1258,11 +1259,20 @@ Things I chose while building, with the reasoning:
Assistant answers also offer **Turn into flashcards** (opens New deck with the answer as notes,
landing on the options step) and **Save to notes** (appends to the single scoped deck, trimming
at 40k characters). Motion comes from `lib/motion.ts`: message fade-up, sheet springs, a 150 ms
composer height transition — all skipped under reduced motion. Verified by the chat suite (89
composer height transition — all skipped under reduced motion. Verified by the chat suite (92
checks: everything from phase 1 plus history persistence, reload, rename/search/delete/undo,
both actions, the export payload, a two-device sync round-trip of a chat, offline readability
and the desktop panel).
49. **The sidebar rail never lets its content widen it.** The desktop rail is a fixed-width `flex`
aside and its glass panel is a `flex-1` item, so the panel's automatic minimum size
(`min-width: auto`) is its content's min-content width. When the chat list moved into the
sidebar, its search input's intrinsic width (an `<input>` defaults to `size=20`) stretched the
panel 89 px past the rail, where it painted over the page — the content offset itself was
correct, which is what made it look like the page had lost its left margin. The panel now
carries `min-w-0` so wide content truncates instead, and the chat suite asserts that the panel
stays inside the rail and the main content starts after it, expanded and collapsed.
36. **The mobile declutter pass reshapes, it doesn't fork.** Phones (≤639px) get a 3-step deck
flow and a 2-step homework flow that share the exact state and handlers with the desktop
cards (one `UploadScreen` / one `HomeworkInput`, two layouts switched by `useIsPhone()`);