refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts - #1854

Merged
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim
Aug 1, 2026
Merged

refactor(ci): split Storybook from e2e and demote floor layout to CSS contracts#1854
Astro-Han merged 8 commits into
mainfrom
refactor/e2e-slim

Conversation

@Astro-Han

Copy link
Copy Markdown
Contributor

Summary

Electron e2e was slow for structural reasons: CI runs the suite serially, and the same job also built Storybook and smuggled ~1.5 minutes of Chromium work onto every desktop-related PR. Separately, a large block of Settings “window floor” and flat-shell chrome coverage was measuring pure CSS outcomes with full cold starts.

This PR:

  1. Splits Storybook into its own CI job (storybook), gated by a new storybook plan flag.
  2. Stops @maka/core from forcing Electron e2eTHEME_PALETTES only needs Storybook.
  3. Replaces floor/chrome e2e with CSS source contracts under apps/desktop/src/main/__tests__/:
  4. Deleteschat-chrome-style.spec.ts and the Settings floor/containment e2e cases; keeps product journeys (theme, voice, remote access, textareas, provider marks).
  5. Removes the Playwright fixtures those specs alone used (permissionSettingsWindow, usageSettingsWindow, searchSettingsWindow, chat-chrome windows). Main-process e2e-fixture scenarios stay for alignment audit.

Still e2e (on purpose): scroll-geometry, sidebar-geometry, window-titlebar, floating-layers focus, providers/mcp/bots/skills, etc.

Rough cold-start savings: ~9 fewer Electron boots (settings floor block + chrome), plus ~1.5 min off the critical e2e job path when Storybook runs in parallel.

Verification

  • node --test scripts/ci-test-plan.test.mjs — pass
  • npm run build:main -w @maka/desktop then node --test on the two new contract files — pass
  • CI: new storybook job + slimmed e2e job

@Astro-Han
Astro-Han marked this pull request as draft August 1, 2026 16:33
@Astro-Han
Astro-Han marked this pull request as ready for review August 1, 2026 16:33
… contracts
Storybook never needed Electron: move build+smoke to a parallel job and stop
core-only changes from forcing cold Electron boots. Replace settings window-
floor and flat-shell chrome e2e with source CSS contracts; drop the unused
fixtures those specs paid for.
Storybook is a design catalog, not a product ship gate. Typecheck already
covers story types and annotations; unit/e2e cover product behavior. Stop
forcing Chromium build+smoke on every desktop/ui/core PR — only stories,
.storybook, ui/stories, the smoke script, and core settings (THEME_PALETTES).
Delete Electron e2e that only re-measured CSS or structural source (topbar
overflow, QR bounding boxes, centerline/empty chat flush, model-picker mark
geometry, remote-access floor CSS, boot locale fixtures). Pin the same
invariants in unit contracts, and keep real product journeys plus titlebar
rendered-geometry and content-visibility scroll pin/warm-up in e2e.
The split storybook job only ran npm ci + build-storybook. Stories import
@maka/core (and other workspace packages) from dist/, so Rolldown failed
to resolve without the same build:workspace-deps step e2e used to pay for.
Stop CSS contracts at each rule's own brace (with mutation coverage),
restore one live permission floor smoke and locale boot reload, pin topbar
menu wiring, and drop the unused settings-search fixture path.
#1587 sets runtimeSource to gateway for cloud activation, but the
INVOCATION_SOURCES union never included it, so typecheck and unit
build of packages/cli failed after merge with main.
Sidebar expand state can survive page.reload via localStorage, so the
Expand control is gone. Only click it when visible before re-entering
Settings for the rest of the locale journey.
The title updates before the notice is rendered, so a bare assert races
and flakes in CI after the title wait already passed.
@Astro-Han
Astro-Han merged commit 79a31fb into mainAug 1, 2026
5 checks passed
@Astro-Han
Astro-Han deleted the refactor/e2e-slim branch August 1, 2026 17:20
Astro-Han added a commit that referenced this pull request Aug 1, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
Astro-Han added a commit that referenced this pull request Aug 2, 2026
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.
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.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Astro-Han