docs: design resizable activity timeline
This commit is contained in:
@@ -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.
|
||||||
Reference in New Issue
Block a user