Uh oh!
There was an error while loading. Please reload this page.
refactor(ui): render sidebar sessions as Astryx SideNav - #1860
Merged
Conversation
Replace List/TreeList session rows with SideNavItem/SideNavSection so the rail uses one navigation primitive, one padding model, and the shell-side-nav status/more-menu pattern. Drop the parallel list-row geometry CSS.
Pin session section titles to product UI size (sm rem was 9.75px under a 13px root), stop treating project sessions as subagents, reserve a fixed trailing slot for StatusDot/MoreMenu, and render Settings as a SideNavSection item like shell-side-nav.
Zero SideNav project nest indent so sessions share the time-sort left edge; park collapse chevrons before trailing MoreMenu; move footer hairline onto the sticky shell with equal padding so Settings hover no longer kisses the divider.
The leading-order experiment read worse than the stock SideNav item layout; keep project flush-nest and footer spacing.
Give time-sort two visible SideNavSection zones (Pinned / Recent) so the boundary is structural, not a single optional title above identical rows.
Use the native SideNavItem leading icon (lucide Bot) on child rows so hierarchy is scannable without CSS nest-padding overrides; parents stay iconless.
Add Storybook stories for 置顶/最近 sections, nested Bot subagent rows, and project-mode groups so the rail visual contracts match the live SideNav implementation.
Put permanent destinations in topContent so history alone scrolls, always mount archived project children so Astryx keeps collapse chrome, and zero project nest only one level so subagent trees are not punched through.
Drop the phantom top section, treat empty projects as leaves, keep one nav landmark, mount session menus permanently with endContent isolation, and stabilize row props so memo and contracts match the SideNav DOM.
Narrow stale SideNav dims to the row's own item so healthy subagents are not muted under a stale parent. Revert the Theme-wide --font-size-sm override; section titles stay product-sized via sidebar.css. Lock wiring with a parent-stale/child-healthy render contract.
Astro-Han
marked this pull request as ready for review
August 1, 2026 18:48
Uh oh!
There was an error while loading. Please reload this page.
Astro-Han added a commit
that referenced
this pull request
Aug 2, 2026
`html { font-size: 13px }` is gone, so the workarounds that existed to
undo it have nothing left to undo.
The session-list section-title pin is the load-bearing one. It was added
one commit before this branch (#1860) because Astryx declares SideNav
section titles as supporting/sm — 0.75rem, which under a 13px root
collapsed to 9.75px, smaller than caption and unreadable beside 13px nav
labels. Pinning them to the product UI tier fixed the symptom by
flattening the hierarchy: titles and labels ended up the same size, told
apart only by colour.
Measured on the sidebarLongSessions fixture after the root moved back to
the browser default:
section title 14px / 19.25px -> 12px / 20px
nav label 14px -> 14px (unchanged)
So the ladder now says what the pin was trying to say — titles a tier
below labels, and the leading lands on the same 20px grid as the rest of
the product. The `!important` went with it; product CSS sits in the last
cascade layer and never needed it.
The other two are comments that state the old base as fact: the tracking
scale's worked example and the tool-output panel's body-type note. Both
now name the tier instead of a number, so neither can go stale again the
next time the scale moves.
This was referenced Aug 2, 2026
Astro-Han added a commit
that referenced
this pull request
Aug 2, 2026
PR #1860 rendered sidebar sessions as Astryx SideNavItem rows, which broke four e2e specs: - SideNavItem renders endContent (the row-actions MoreMenu trigger) inside the row's primary <button>, so the row button's accessible name became a composite ending in the trigger's label and name queries hit strict-mode violations. The nesting is SideNavItem's own DOM, and moving the trigger out needs sidebar CSS frozen by PR #1857, so the locators are tightened (project scope .maka-project-item-end; session exact: true). - The geometry spec's 60-row pin selected rows by title^="会话 ", but the rows' title is now session metadata; the pin was deleted — its only purpose (proving the list overflows) is asserted directly at (1a) via scrollHeight - clientHeight > 50. e2e: 70 passed / 0 failed; format/lint/typecheck/dead-css and the desktop (1305) and ui (238) unit suites all pass.
Astro-Han added a commit
that referenced
this pull request
Aug 2, 2026
`html { font-size: 13px }` is gone, so the workarounds that existed to
undo it have nothing left to undo.
The session-list section-title pin is the load-bearing one. It was added
one commit before this branch (#1860) because Astryx declares SideNav
section titles as supporting/sm — 0.75rem, which under a 13px root
collapsed to 9.75px, smaller than caption and unreadable beside 13px nav
labels. Pinning them to the product UI tier fixed the symptom by
flattening the hierarchy: titles and labels ended up the same size, told
apart only by colour.
Measured on the sidebarLongSessions fixture after the root moved back to
the browser default:
section title 14px / 19.25px -> 12px / 20px
nav label 14px -> 14px (unchanged)
So the ladder now says what the pin was trying to say — titles a tier
below labels, and the leading lands on the same 20px grid as the rest of
the product. The `!important` went with it; product CSS sits in the last
cascade layer and never needed it.
The other two are comments that state the old base as fact: the tracking
scale's worked example and the tool-output panel's body-type note. Both
now name the tier instead of a number, so neither can go stale again the
next time the scale moves.Astro-Han added a commit
that referenced
this pull request
Aug 2, 2026
* refactor(ui): move typography authority to the Astryx type scale
Maka's density was expressed as `html { font-size: 13px }`, which is not a
type scale but an implicit x0.8125 multiplier on every rem in the document —
including the radius and spacing constants Astryx compiles against a 16px
root. Body copy was then pinned back to 13px on the Theme wrapper to undo the
multiplier for prose alone, leaving every other tier shrunk: supporting text
rendered at 9.75px instead of 12, h1 at 19.5 instead of 24, --radius-element
at 8px instead of 10. One intent, two contradicting expressions, and a
compensating patch between them.
Declare the intent where Astryx expects it instead. `{base: 13, ratio: 1.15}`
reproduces Maka's four hand-written tiers exactly (caption 11 / ui 13 /
heading 15 / stat 20) and supplies the matching 4px-grid line heights, so
those names become aliases rather than independent values. The root returns
to 16px and the compensating pin is gone.
The font stacks move for the same reason: Astryx's neutral default leads with
Figtree, which Maka does not bundle and whose stack carries no CJK face, so
Astryx surfaces silently fell back while the product used its own stack.
--font-sans / --font-mono now alias the theme tokens.
astryx-tokens also moves after astryx-components in the cascade order. A
theme layered before the component library can never override the neutral
defaults astryx.css declares on `:root`; that only looked harmless while
nothing resolved a token there, because inside the wrapper the theme still
won on tree distance. The 22 rem literals left in product CSS become absolute
px, matching the spacing scale's existing rule.
Measured in the live app (before -> after): body 13 -> 13px, bubble leading
21.0002 -> 20px, h1 19.5/25.9994 -> 20/28px, h2 16.25/22.75 -> 17/24px,
li and code leading 18.5718 -> 20px, supporting 9.75 -> 11px,
--radius-element 8 -> 10px. The four product tiers are unchanged.
* fix(ui): route code elements through the monospace token
Astryx's reset hard-codes a monospace stack on :where(code, kbd, samp, pre)
that never consults --font-family-code, so every code element — Markdown code
blocks in the transcript included — opted out of the theme and rendered in
ui-monospace rather than the Geist Mono the app bundles and declares.
:where() has zero specificity and reset is the lowest layer, so pointing the
same selector at the token is the whole fix. This was the last parallel
typography authority left after the type-scale consolidation.
Verified in the live app: code blocks now resolve
"Geist Mono Variable", "JetBrains Mono", ui-monospace, … at 13px.
* refactor(ui): retune the transcript scale against shipped agent UIs
The consolidated ladder was 13/1.15 — a faithful reproduction of the tiers
Maka had already converged on, chosen so the previous commits' visual delta
stayed attributable. With the authority in one place, retune the values.
Read the shipped bundles of the three products Maka is benchmarked against:
Cursor 3.14.7 (CDP against the live Agents window), Claude Code's desktop
surface (the Epitaxy layer inside Claude.app), and Codex desktop
(openai-codex-electron). All three sit at 14px body. All three keep secondary
text at 12-14px; none goes near the 11px Maka used for reasoning and tool
rows, which carry what the agent is doing and were the least legible text in
the product. Cursor puts tool rows at full body size and de-emphasises purely
with colour.
scale: {base: 14, ratio: 1.125}
1.125 is the only ratio that moves base to 14 while keeping 11 and 20 on the
ladder, so caption and stat stay put and only base (13->14) and heading
(15->16) move. It is also what Astryx's expandTypeScale header recommends for
"Dense/functional". Body leading recomputes to 20px — the same absolute
leading as before, so the type grows without the paragraph loosening.
Three transcript-scoped overrides, all taking values from the ladder:
- Reasoning and tool-call rows read at body size, not supporting size.
- Markdown headings flatten to two steps: h1 at lg, h2-h6 at body, all
weight 600, all on one baseline. Astryx's ladder is a document scale and a
turn is not a document — an agent emits `##` every few lines, and 1.4x per
level shreds one reply into unrelated slabs. Cursor, Claude Code and Codex
all flatten here relative to their own document styles; Claude Code merges
h4-h6 into `strong`. Settings and docs keep the full ladder.
- Markdown renders at density="compact", Astryx's own answer to heading
spacing tuned for pages rather than turns.
Measured in the live app: body 14/20, tool rows 12->14, h1 16/20, h2-h4
14/20 at weight 600, inline code and code blocks 14/20.
* fix(ui): keep the provider mark on its declared 1rem contract
Converting product CSS off rem swept up .modelPickerProviderMark, which is
the one place the unit is load-bearing rather than habitual:
chat-shell-layout-contract.test.ts asserts a fixed 1rem square so the mark
tracks the root instead of any single font tier and cannot be squashed by
flex. The contract landed in #1854 and the rebase surfaced the collision.
* fix(ui): attach the transcript type policy to the right things
Four defects, one mistake: transcript-only typography was attached to
selectors that were either wider than the transcript or narrower than the
DOM they were aiming at.
The reasoning rows never got the retune. `> span:not(:last-child)` reads as
"every span but the chevron"; ChatReasoning wraps its label, duration and
preview in a `<div>` (packages/ui/src/astryx-chat-reasoning.tsx), so the rule
enlarged the leading icon wrapper and left the reasoning text at supporting
size -- the single row this work exists for. Astryx's supporting atoms are
`font-size: var(--text-supporting-size)` and the matching leading, so the
rows now rebind those two tokens on the trigger: inheritance reaches every
span that opts into the role, at any depth, and the rule stops depending on
Astryx's child order.
`.astryx-heading.level-N` matched nothing. Astryx's Markdown renderer emits
bare h1-h6 with StyleX props; those classes come from the standalone Heading
component, which Markdown never mounts. They were live enough to turn the CI
`Dead CSS` gate red (7 classes over a baseline of 0) and dead enough to have
no effect.
The heading flattening reached Daily Review. Its argument is that an agent
turn is not a document -- but a review report IS one, and both render through
the shared MarkdownBody contract. Scoped to `.maka-turn`.
`--maka-chat-line` was declared on `.maka-turn, .maka-chat-message` and read
by rules matching outside them, where it resolved to nothing and took
`line-height` down with it as an invalid value. It is a calc of two global
tokens, so the scoping was invented coupling: renamed `--maka-line-body` and
declared with the tiers it derives from.
Drops the typography `!important`s while here. Astryx's StyleX lives in the
earlier astryx-components layer, so product CSS in `components` already wins
on layer order whatever the specificity; the one real inline-style opponent
was an ejected chevron's `0.75rem`, removed separately. Also updates the
comments the 14/1.125 retune left describing 13/1.15.
* refactor(ui): retire the last hand-rolled type ladder
Eleven renderer headings still sized themselves with `em` multipliers --
2.1333, 1.7333, 1.4667 and friends across hero, onboarding, settings and the
module pages. Those decimals are the tell: each was hand-derived against
whatever the body happened to be when it was written, so every base change
since has silently rescaled them. The hero comment claimed 32px; 2.1333em was
derived from a 15px body and had been rendering 27.7px under a 13px one, and
would now render 29.9px. That is three different sizes for one decision.
Each now names the ladder rung nearest what the page actually shipped, so
this is a convergence rather than a visual redesign: 28 / 22 / 20 / 18 / 16.
With these gone the renderer has no independent font-size authority left.
Two rem lengths in TSX went with them. The earlier sweep only read `.css`,
but removing `html { font-size: 13px }` is a document-level change: the tool
code block's `16rem` max height would have gone 208px -> 256px, and the
reasoning chevron's inline `0.75rem` had been disagreeing with its own
width/height attributes for as long as the root was pinned. The attributes
already carry the size, so the inline style is simply gone.
* test(ui): lock the foundations the type scale rests on
Three declarations hold the scale up, and reverting any of them was silent:
the root staying at the browser default, the generated theme layered after
the Astryx component sheet, and the product names staying aliases. Mutating
each one left all 24 existing CSS-governance tests green.
`type-scale-contract.test.ts` pins them as text, plus the ladder rungs the
four aliases point at, the monospace routing, the turn-scoped heading
flattening, the role-token rebind, and the absence of em/rem font-sizes.
Six mutations were run against it; each fails exactly one test.
`e2e/type-scale.spec.ts` measures what text cannot prove. Custom properties
resolve by tree distance while rules resolve by layer, and the two disagree
at `:root` -- which is exactly how an earlier revision of this work shipped
aliases resolving to Astryx's neutral defaults with every file reading
correctly. It probes the tokens at `:root` in px, so it sees what a portaled
Astryx component sees. Reverting the layer order turns `--font-size-heading`
into 17px, the neutral value, and the spec fails on it.
Wires `astryx:theme -- --check` into CI: makaTheme.ts is now the type-scale
authority, and nothing stopped it from drifting from the artifacts it
generates.
Corrects the cascade-layers comment while here. It claimed promoting the
theme "cannot reach product markup" because the sheet only carries `.astryx-*`
rules -- but Astryx components ARE product markup, and 67 such rules now win
where they previously lost. Card and Section padding are NOT among them:
`--astryx-*-padding` are theming hooks Astryx never declares itself, so those
applied under either order. Button, badge and status-dot colors did change.
* refactor(ui): retire the compensations the 13px root required
`html { font-size: 13px }` is gone, so the workarounds that existed to
undo it have nothing left to undo.
The session-list section-title pin is the load-bearing one. It was added
one commit before this branch (#1860) because Astryx declares SideNav
section titles as supporting/sm — 0.75rem, which under a 13px root
collapsed to 9.75px, smaller than caption and unreadable beside 13px nav
labels. Pinning them to the product UI tier fixed the symptom by
flattening the hierarchy: titles and labels ended up the same size, told
apart only by colour.
Measured on the sidebarLongSessions fixture after the root moved back to
the browser default:
section title 14px / 19.25px -> 12px / 20px
nav label 14px -> 14px (unchanged)
So the ladder now says what the pin was trying to say — titles a tier
below labels, and the leading lands on the same 20px grid as the rest of
the product. The `!important` went with it; product CSS sits in the last
cascade layer and never needed it.
The other two are comments that state the old base as fact: the tracking
scale's worked example and the tool-output panel's body-type note. Both
now name the tier instead of a number, so neither can go stale again the
next time the scale moves.
* fix(ui): answer the review with measurements, not assertions
Three independent reviews (two fresh-eye agents, one Codex) landed on the
same defects. Everything below was verified in the live app before being
acted on; two of the reviewers' own claims did not survive that and are
recorded here as rejected.
The one behaviour change: `density="compact"` was hardcoded in
MarkdownBody, whose other caller is the Daily Review panel. That
contradicted this branch's own reasoning — the heading-size flattening is
scoped to `.maka-turn` precisely because a review report is a document —
so the review kept full heading sizes but got transcript block spacing,
the combination neither half of the argument asks for. Density is now a
caller choice defaulting to `default`, with the two chat-turn call sites
opting in.
Comments that stated something false, each replaced with what was
measured:
- "the radius and spacing rem constants Astryx compiles against a 16px
root" (four copies). Astryx's spacing and radius tokens are px
literals and never moved. What is rem is its Icon size atoms, which
it documents as the px-equivalents at a 16px root — so the old pin
was rendering the whole icon set at 9.75/13/16.25/19.5 instead of
12/16/20/24. Measured on main and on this branch.
- "inside the Theme wrapper the theme still won, since custom
properties resolve by tree distance". False. Probed on main at both
<html> and the inner wrapper: --font-size-lg 13.8125px, --font-size-xl
16.25px, i.e. Astryx's neutral values. The generated theme was inert
everywhere, not merely at :root.
- "at 11px on the old one" for the disclosure rows. Measured 9.75px;
11px was a reading of an intermediate commit, not of main.
- "the same absolute leading as before". The transcript was 21px and is
now 20px — 1px tighter, not unchanged.
- "1.125 is the only ratio that keeps 11 and 20 on the ladder". 1.13
also rounds there. It is the natural choice, not the forced one.
- the cascade-layer blast radius, which said 67 class rules change.
Compared declaration by declaration: 44 are contested but resolve to
the same value, 12 are uncontested and already applied, and 11
actually move. Spot-checked live — destructive Button
rgb(165,12,37)/white to rgb(250,206,203)/rgb(165,12,37), accent
StatusDot rgb(38,38,38) to rgb(0,116,226).
DESIGN.md still named maka-tokens.css as the type authority and carried
the 15/13/11 tiers, and maka-tokens.css still claimed to be the single
source of truth for typography. Both now point at makaTheme.ts.
The eight geometry `!important`s went too. The comment four lines below
them already said product CSS wins on layer order alone; deleting them
and re-measuring confirms it — the chevrons still render 14x14 with a
10x10 svg. This file now declares none.
The tests close five holes the reviewers proved by mutation — each of
these edits was previously green:
- `@maka/ui/styles.css` reaches the renderer through a bare specifier,
which the CSS import expander skipped along with node_modules. ~30
font-size declarations were invisible to every scan; the expander now
resolves first-party workspace sheets.
- `--maka-line-body` had no guard at all. Deleting it, or replacing it
with the hand-rolled multiplier this branch exists to retire, passed
everything. Now pinned in the contract and resolved live in e2e.
- the root-pin ban only matched a bare `html`/`:root` selector, so
`html[data-os="darwin"] { font-size: 13px }` slipped through.
- the unscoped-heading ban and the `font-size: !important` ban only
read chat-message.css. Both now scan the whole renderer graph; the
second one would have caught the sidebar pin this branch removed.
- the monospace routing was asserted as text only, on the stated
grounds that it "follows arithmetically". It does not — it is a
layer-order question — and it is now probed live.
Density gets a render contract that fails if the default flips back.
Rejected after checking: that the model-picker mark's `1rem` is an
unnoticed 23% growth to be reverted. It is an icon box, and 1rem is
Astryx's `sm` icon at a 16px root — the old 13px was the anomaly. The
comment now says that instead of the circular "it tracks the root".
Also declined: a live ChatReasoning probe. No fixture renders one, and
seeding it means changing a shared scenario every spec sees; the gap is
now stated in the spec instead of left implicit.
* docs(ui): say which half of type this branch actually consolidated
The leading tiers still carried a "single source" claim from #520. Size
now has one authority and a contract that proves it; leading has three
vocabularies, and two of them disagree — --leading-normal is 1.5, i.e.
21px at the body tier and off the 4px grid, where Astryx computes 20px.
The transcript reads Astryx's and is internally consistent; product
chrome sits 1px looser.
Not fixing it here. Converging them moves 66 sites, which is a relayout
with its own before/after, and nothing about this branch blocks it.
Stating it beats leaving a stale claim four lines under the derived
token that contradicts it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replace the sidebar conversation List/TreeList path with Astryx
SideNav/SideNavItem/SideNavSectionso the session rail uses one navigation primitive (shell-side-nav status ↔ MoreMenu pattern, collapsible project groups, Settings footer item).Follow-up polish on the same path:
Product/Sidebar Session Listcovers pinned/recent, nested subagents, and project groups.Verification
origin/main.@maka/uibuild +sidebar-subtractiontests (pre-rebase).lucide-bot; parent does not.PinnedAndRecentSections,NestedSubagentSessions,ProjectGroups.Review focus