docs: design resizable activity timeline

This commit is contained in:
User
2026-07-15 14:29:50 +02:00
parent bef21f9e43
commit 7ad06cde81
@@ -0,0 +1,174 @@
# Resizable Model Activity Timeline and Dense CTE Plan Design
**Date:** 2026-07-15
## Goal
Restore the left **Model activity** panel as a readable chronological view of the original
question, model reasoning, and model response; make the panel width adjustable with the mouse;
and substantially reduce the perceived vertical padding of F6 CTE cards while retaining useful
horizontal breathing room.
This design supersedes the left-panel visibility and CTE-density sections of
`2026-07-15-central-live-log-cte-density-design.md`. The compact central live log remains in
place. Backend, SSE, workflow, persistence, and model-provider contracts do not change.
## Root causes
The session store and stream already retain all three events required by the panel:
- the submitted question is appended to `activityLog` as `prompt`;
- chain-of-thought/model reasoning arrives through `activity_delta` as `thinking`;
- the streamed model response arrives through `text_delta` as `assistant`.
The regression is at the rendering boundary. `ModelActivityPanel` currently allows only
`thinking` and `status`, so it explicitly hides the question and model response while admitting
the `F1 STATUS INFO` rows the user does not want.
The panel/conversation split is also fixed in two separate components: the panel uses `w-2/5` and
the conversation uses `w-3/5`. There is no separator, resize state, pointer handling, keyboard
interaction, clamping, responsive fallback, or persistence.
The shared `Card` primitive is not secretly adding height to F6: `CtePlanViewer` already overrides
its root padding and gaps. The remaining perceived vertical excess is mainly caused by a 20 px
content gap, repeated 8 px heading margins, relaxed line heights, and table/filter rows with 8 px
padding on both vertical edges.
## Model activity projection
The left panel uses an explicit, default-deny allowlist:
| Activity kind | Visible | Presentation |
| --- | --- | --- |
| `prompt` | Yes | **Question** label and readable body. |
| `thinking` | Yes | **Reasoning** label and Markdown body in a quieter tone. |
| `assistant` | Yes | **Response** label and Markdown body. |
| `status` | No | Excluded, including neutral, warning, and error entries. |
| `tool` | No | Low-level implementation detail. |
| `gate` | No | The corresponding form or artifact remains central. |
| `lifecycle` | No | Process/turn implementation detail. |
| unknown future kinds | No | Default-deny prevents accidental exposure. |
Visible entries keep their existing chronological order. The panel does not reconstruct or merge
separate data sources: it filters the authoritative in-memory `activityLog`. This preserves the
actual interleaving of question, reasoning, and response.
Each visible entry is rendered as a compact semantic row with a human-facing label instead of raw
kind/status/level metadata. Phase is shown only when present and visually subordinate to the
label. `prompt`, `thinking`, and `assistant` bodies support Markdown and long-value wrapping.
Rows use an 8 px vertical rhythm rather than the current 12 px. The panel follows appended visible
content only while the reader is already near the bottom; manual upward scrolling remains stable.
The central live log continues to show the assistant transcript while the model is working. Its
small bounded scrolling box is intentionally retained, even though assistant output is also
available in the full left timeline: the two surfaces serve different purposes, glanceable current
progress centrally and complete readable activity history on demand.
## Resizable split
On desktop, the fixed percentage widths are replaced by one width owned by `AppShell` and applied
to the left panel. The conversation column consumes the remaining space with `flex-1 min-w-0`.
The split contract is:
- default panel width: 384 px;
- minimum panel width: 288 px;
- maximum panel width: the smaller of 576 px and `containerWidth - 512 px`;
- minimum central width: 512 px whenever the container is wide enough to satisfy both minima;
- stored width is clamped again whenever container geometry changes.
A full-height vertical separator sits between the panel and central column. Its visible line stays
subtle, while a 12–16 px transparent hit area makes it easy to acquire. Hover, focus, and active
drag strengthen the divider. The cursor is `col-resize`, text selection is suppressed during a
drag, and pointer capture keeps the interaction stable when the pointer leaves the hit area.
The separator is keyboard operable and exposes `role="separator"`, vertical orientation, current
value, and min/max values:
- Left/Right changes width by 16 px;
- Shift+Left/Right changes width by 48 px;
- Home moves to the minimum;
- End moves to the maximum.
The last committed width is persisted globally in browser `localStorage` after pointer release or
keyboard interaction. It is not stored per session and never reaches the backend. Closing the
panel remains ephemeral; reopening it in the same or a later browser session restores the clamped
saved width.
Below the desktop breakpoint, resizing is disabled and the activity panel becomes a bounded
overlay drawer, up to 90 viewport percent and 384 px wide. The central area remains full width,
preventing the two minimum widths from creating overflow on narrow screens.
## Compact CTE plan
The change remains local to `CtePlanViewer`; the shared `Card` component, content schema, and grid
topology do not change. Horizontal padding stays at 12 px below `sm` and 16 px from `sm` upward.
Vertical density changes target the actual sources of height:
- header title/purpose gap: 12 px to 8 px;
- body section gap: 20 px to 12 px;
- dependency/key grid gap: 16 px to 12 px;
- repeated section heading margin: 8 px to 4 px;
- table and filter row padding: 8 px to 4 px per vertical edge;
- filter internal spacing: 12 px to 8 px;
- filter grid gap: 12 px to 8 px;
- filter detail gap and divider padding: reduced to 4 px;
- rationale divider padding and heading margin: reduced to 4 px;
- relaxed line heights inside dense rows: replaced by compact fixed line heights.
Header and main content outer padding remain 8 px per vertical edge so the card border does not
feel cramped. Chips, badges, borders, long-identifier wrapping, semantic sections, and the 12 px
gap between distinct CTE cards stay unchanged.
## Error and edge-case behavior
- If a provider emits no `thinking`, the question and response still render in the panel.
- If assistant text has not arrived yet, the question and available reasoning still render.
- Hidden status/tool/gate/lifecycle events do not affect the panel empty state or scroll-follow.
- Unknown activity kinds remain hidden.
- A corrupt or unavailable stored width falls back to 384 px.
- A formerly valid stored width is clamped after viewport or container resizing.
- When the container cannot satisfy both desktop minima, the responsive overlay behavior prevents
horizontal overflow.
- Long Markdown, SQL identifiers, table names, filters, and rationales wrap without horizontal
scrolling.
## Testing and verification
Frontend RED/GREEN tests must prove:
1. A mixed activity sequence renders only `prompt`, `thinking`, and `assistant`, in chronological
order, with human-facing labels; status, tool, gate, lifecycle, and unknown kinds remain hidden.
2. Prompt and assistant bodies use the readable Markdown path, thinking keeps its quieter tone,
and hidden updates cannot trigger bottom-follow.
3. The panel's empty state and existing near-bottom follow behavior are based only on visible
entries.
4. The desktop split starts at 384 px, clamps to its computed min/max, updates through pointer
capture, and gives the central column the remaining width.
5. The separator exposes the required ARIA values and supports arrows, Shift+arrows, Home, and End.
6. Valid saved widths are restored; corrupt or out-of-range values fall back or clamp safely.
7. The narrow-screen layout uses an overlay drawer and does not compress the central column.
8. CTE cards retain their semantic structure and horizontal padding while exact vertical gaps,
row padding, margins, and line heights match the compact contract.
Verification runs the focused activity-panel, AppShell/split, and CTE viewer tests first, followed
by the complete frontend Vitest suite, `npx tsc -b`, `npm run build`, the Impeccable layout detector,
and `git diff --check`.
## Deployment
Only the frontend is affected. Rebuild and force-recreate only the frontend Compose service. If
and only if the Vite entry hash changes, restart the portal web container to invalidate its cached
manifest. Do not restart the core/backend or terminate an unrelated active Pi session. Verify the
running frontend image and entry hash, container health, and that the core start timestamp is
unchanged.
## Out of scope
- Removing any event kind from `activityLog` or changing the session store fold.
- Backend, SSE, replay, persistence, workflow, or model-provider changes.
- Showing status, tool, gate, lifecycle, or unknown events in the left panel.
- Removing or expanding the compact central live log.
- Per-session or backend persistence of panel width.
- Changing CTE content, ordering, colors, schema, or the shared card design system.