tracking(workbar): replace the hand-written tab strip with TabList, and close the panel gaps that are left
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 502
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 716
Description
## Why this issue is being rewritten
The original body was written just after #2158 and described the workbar as "eight apps in a tab strip". Most of what it asked for has since landed through the panel PRs, so I re-checked every work item against `main` before rewriting it. Six of the eleven items were already done or rested on a premise that no longer held, so each of them would have opened as an empty PR.
**Updated after #4789.** PR 1 has landed and its four measured defects are closed. PR 2 shrank: the Task face is retired, so half of it no longer exists. PR 4's floor moved with the panel's. What is left is below, with the measurements that back it.
Everything marked *measured* comes from a live renderer or the built Storybook rather than from reading the CSS.
## Problem
The workbar column is composed from Astryx primitives — panels already use `Section`, `Banner`, `EmptyState`, `Toolbar`, `Collapsible`, and the resize edges are Astryx `ResizeHandle`. The tab strip was the exception: `.maka-workbar-tab*` in `styles/workbar/shell.css` plus roughly 200 lines of `workbar-surface.tsx` re-implemented a tab strip that `@astryxdesign/core/TabList` already ships, and four measured defects were all consequences of that re-implementation.
That is done — see **PR 1** below for what each defect measured before and after. The panel gaps that remain are the ones this tracker still exists for.
## Goal
One tab strip, owned by `TabList`, and no panel left drawing chrome the design system already ships.
This supersedes the direction recorded as A3 in `docs/frontend-architecture-astryx-review-2026-08-09.md` ("keep custom tab strip (dnd + `role=tab` is justified)"). Both halves of that justification are gone: `role=tab` is `TabList`'s, and drag-to-reorder was dropped rather than rebuilt — faces are opened from one menu and the strip's order is the order they were opened in.
## Plan
Three PRs left. They open flat off `main` and touch disjoint files, so none blocks another. Each carries BEFORE/AFTER Storybook captures (light and dark) from the `Product/Session Workbar` story group.
### PR 1 — Shell: the tab strip becomes `TabList` — **done (#4789)**
`workbar-surface.tsx`, `styles/workbar/shell.css`, `workbar-toggle.tsx`. `WorkbarTabStrip` and `SortableWorkbarTab` are gone, `.maka-workbar-tab*` with them, and dnd-kit left the dependency tree. Net deletion.
- [x] `.maka-workbar-tab*` gone; neither `WorkbarTabStrip` nor `SortableWorkbarTab` exists.
- [x] The strip scrolls inside itself instead of pushing the trailing controls off the edge. **Measured:** three faces at the 340px floor need 284px against 256px available, `scrolls: true`, `[+]` on screen. The "264px hole" was this defect's symptom — the strip's container had no `min-width: 0`, so it spilled rather than scrolled. `[+]` and the collapse toggle stay at the bar's trailing edge by design, the way the launcher mockups settled it.
- [x] A hovered unselected tab is distinguishable from the selected tab, in both themes, and selection carries exactly one marker. **Measured:** selected is weight 600 with no fill plus `TabList`'s rail; unselected is weight 400 and takes a background on hover. The old rule gave both `--state-hover-bg`.
- [x] Tab, `[+]` and collapse report one height. **Measured:** 28 / 28 / 28 (was 28 / 28 / 32). `WorkbarToggle` was hard-wired to `md`; it takes a `size` now and the bar passes `sm`.
- [x] Left and right padding are equal where the platform reserves nothing. **Measured:** 8px / 8px (was 8px / 24px). The right pad was `--space-6`, borrowed from the window titlebar strip, which measures from the window edge — this bar starts a content-area gap inside it.
- [x] The renderer ledger does not rise.
Two things changed shape rather than being preserved, and both were deliberate:
- **Drag-to-reorder, preview/pin and the tab context menu are gone.** `Tab` renders `endContent` inside its own ``, so a per-tab close would nest a button in a button; opening and closing share the `[+]` menu instead. Nothing in the renderer ever set `preview: true`, so that state was dead code with no producer.
- **The Task face is retired**, folded into Work Board separately. Its renderer read path went with it; `todo:read` and `querySessionTodo` stay as the boundary Work Board will read through.
### PR 2 — Work Board rows on the `List` idiom
`apps/desktop/src/renderer/work-board-panel.tsx` and its CSS. This was a two-panel PR; the Tasks half is gone with the face, so what remains is Work Board alone — and with it, the shared-status-vocabulary requirement, which had only one consumer left.
Done when:
- [ ] The panel's rows are on the `List` idiom; the hand-written `
- `/`
- ` is gone, and `.maka-work-board-archived-tag` with it.
- [ ] Status is carried by `StatusDot`, whose `label` is required — so a screen reader reads every row's status, which today it cannot: the status is a lucide icon marked `aria-hidden="true"` beside plain text.
- [ ] The empty state is clean tier 2 per `DESIGN.md` §10 — `icon` + `title` + `description`, no action. It is missing `description` today, which lands it between tiers.### PR 3 — One failure vocabulary
`session-terminal-panel.tsx`, `quote-companion-panel.tsx`. Small and self-contained.
Done when:
- [ ] Terminal and Side Chat each state a failure as a `Banner` with a labelled retry beside it, the way Review already does.
- [ ] All panels' failure states have the same shape: a `Banner`, a status, and a way back.### PR 4 — Files at the panel's floor and at 400px
`artifact-pane.tsx` plus a new artifacts fixture scenario. The fixture and the verification are one change — without seeded artifacts the panel renders empty and neither defect can be observed.
Done when:
- [ ] An artifacts fixture scenario exists and renders a populated Files panel.
- [ ] At 340px — the floor since #4789, up from 320 — the filename and size rects do not intersect, asserted rather than inspected.
- [ ] At 400px a diff scrolls inside its own container and the panel itself does not scroll horizontally.
- [ ] If both defects turn out to be already fixed, this PR is the fixture alone and the finding is recorded here.## Already done — removed from this tracker
Recorded here so nobody re-opens them.
| Original item | Current state |
|---|---|
| Column plate on `--surface-raised`, one separator | Already so: `--agents-content-area-bg` resolves to `var(--surface-raised)` (`reference-shell.css`), with no fill+border+shadow stack |
| Resize handle on tokens; drop undeclared `--maka-*` | Astryx `ResizeHandle`; `--maka-session-workbar-width` and `--maka-session-bottom-panel-height` are set inline by `workbar-host.tsx`, `--maka-plate-titlebar-clearance` is declared in `shell-layout.css`. Nothing falls back |
| Review — rows on `List`/`Item`, header as `MetadataList` | `CollapsibleGroup hasDividers density="compact" role="list"` + `Collapsible role="listitem"`, which `List` cannot replace: the row expands to a diff. Failure and empty states are `Banner` / `EmptyState` |
| Browser — audit URL field, loading, blocked | `Toolbar` + `TextInput` + `EmptyState` + `Tooltip`; loading swaps refresh for stop |
| Terminal — header and states, no bespoke chrome | There is no header to retire, and the states are `Banner` / `EmptyState` |
| Side Chat — close confirmation on `Dialog`/`Button`; quote chip overrides to `themeProps` | Confirmation is `Dialog` + `DialogHeader` + `Layout` + `CheckboxInput`. No `xstyle` or inline-style overrides remain on the quote chip |
| Tasks panel — rows on `List`, status on `StatusDot` | The face is retired (#4789). Its ledger is folded into Work Board, tracked there |## Out of scope
Panel behaviour and data flow (`apps/desktop/src/renderer/features/workbar/README.md`); AppShell composition (#4582); WorkHub (#3492). Folding SessionTodo into Work Board needs its own issue — it is a schema change (Work Board has no session scope, `agent_suggestion` requires `confirmedAt`, and the two carry different state vocabularies), not a chrome one. Splitting static tool kinds from resource-backed tabs (`terminal:*`, `side-chat:*`) is the other half of A3 and also needs its own issue.
Refs #2158, #4400, #4582, #4679, #4789
Contributor guide
Assessment
This issue has not been assessed yet.