Skip to content

fix(desktop): give the titlebar one control height and one name authority - #5761

Merged
jackwener merged 2 commits into
apache:mainfrom
Astro-Han:fix/4679-titlebar-identity
Sep 27, 2026
Merged

jackwener merged 2 commits into
apache:mainfrom
Astro-Han:fix/4679-titlebar-identity

Conversation

@Astro-Han

@Astro-Han Astro-Han commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Closes the Titlebar and Session rail items of #4679. On main I checked each item and changed only the ones that were still open.

Titlebar geometry. The strip is 32px (--h-titlebar 36px minus the 4px resize edge), which already sits on the 4px grid. Its controls did not share a height, though: the sidebar search and collapse buttons were Astryx md (32px, the full strip height) while the session identity buttons and the Workbar restore button were sm (28px). All titlebar controls now use sm, so they have one box size and one centre line (y 6–34 in every story I measured).

No product shadow. Titlebar controls paint no shadow at rest. The two product-drawn shadows were an inset box-shadow focus ring that replaced the Astryx outline, and the rename field's 0 1px 0 underline. Both are gone:

  • Focus ring: the inset ring existed because an outward ring on a 32px button at y=4 got clipped at the window edge. At 28px, the Astryx outline (2px wide, 3px offset) starts at y=1, so it fits without being clipped.
  • Rename field: it no longer strips the Astryx TextInput chrome (border, background, font, padding). It now renders as the standard sm field with Astryx's own focus treatment. I kept field-sizing: content so the field still hugs the name.

One title authority. Two components rendered the session name: TitlebarSessionIdentity and a branch breadcrumb in SessionContextLayer. The breadcrumb showed "parent › current name" whenever there was no Goal, and repeated both the name and the parent-return link that the titlebar identity already provides. It is removed, along with the code that only existed to feed it:

  • the BranchBanner derivation, which recomputed the same parent the titlebar already reads through activeParentSession
  • the ChatView/SessionContextLayer props and copy
  • the fromAbortedTurn token, which production never supplied

This follows the direction in docs/frontend-architecture-astryx-review-2026-08-09.md: the titlebar owns the name, and the context band owns runtime chips only. The titlebar also rendered the parent name raw, so an untitled parent read "New Chat" in every locale. It now goes through presentSessionName, like the session name.

Subagent children get their titlebar parent back (raised in review). A linked subagent child records its parent only in subagentParent; the Host never sets parentSessionId on it (session-manager.ts). Since #5532 the titlebar took its parent from a selector that read parentSessionId alone, so a subagent child opened from the stream had no return arrow. Before #5532 it came from deriveSessionRail(...).activeParentSession, the revision-aware linked tree the rail still uses to highlight the ancestor row. The selector now asks that projection first and falls back to parentSessionId for branches and side conversations. The test fixture's child had a parentSessionId it never has in production, which is what hid the gap; it now matches production, and the reads test fails on the old selector (actual: undefined).

Right-side actions. These were already Astryx IconButton + Tooltip. I did not wrap them in Astryx Toolbar: Toolbar is a Section with spacing-2 block padding, which makes it a 44px bar. That cannot sit inside the 32px drag strip without overriding Astryx internals, and its no-drag rect would reach down over the top of the content plate.

Already satisfied on main, not changed:

  • titlebar-dim-color.ts / titlebar-modal-sync.ts (DESIGN.md §8): theme.ts already samples getComputedStyle(html).backgroundColor and the open dialog's computed ::backdrop. Neither reads a token string, and titlebar-dim-color.ts is pure colour math.
  • Session rail Badge in packages/ui/src/session-history-list.tsx: none of them is decorative, so they stay. meta is the Runtime Host a remote-workspace session runs on (e.g. "Remote Mac"). executorId names an external ACP executor (e.g. antigravity) and is also exposed through the row's hover-card description. Neither is a status, so StatusDot would misstate them. The warning Badge rendered through sessionBadge (SessionTurnRequestBadge) is a real count of pending guest turn requests.

Refs #4679

Verification

Titlebar geometry, measured by attaching to the Storybook preview over CDP (1280×900, before on main, after on this branch):

Story main this branch
default-layout search/collapse 32×32 at y=4; identity 28×28 at y=6 all 28×28 at y=6
workbar-titlebar-restore same split; restore 28×28 all 28×28 at y=6
focused titlebar button (focus({focusVisible:true})) outline: none, inset box-shadow Astryx outline: 2px solid, offset 3px, ring top y=1
rename (click dispatched on the name chip) field has no border, underline box-shadow 28px Astryx TextInput, 1px border, no product shadow

Console errors were the same on both builds (two static-server 404s).

Checks:

  • @maka/ui: all 681 tests pass.
  • Desktop: session-navigation-controller, session-navigation-boundary, titlebar-modal-dim, ink-ladder-contract, quote-companion-retry, new-task-staged-content, traditional-chinese-peer-mesh-copy and session-inspector-composition pass.
  • npm --workspace @maka/desktop run typecheck (includes stories), npm run format, npm run lint, npx knip for apps/desktop and packages/ui, check:renderer-architecture --base <merge-base> (ledger token count lowered), and astryx:surface-inventory:write all pass.

Storybook play functions on the built catalog in light and dark: default-layout, update-downloaded-collapsed, titlebar-project-feedback-narrow, titlebar-parent-return, titlebar-identity-truncated, session-context-layer, titlebar-with-wide-workbar, workbar-titlebar-restore, narrow-workbar-clears-titlebar-reserve (at 720px, its intended viewport) and subagent-sessions--child-session all pass. My local runner sometimes missed storyFinished on both builds; each story was re-run until it reported a result. The full smoke and E2E suites are left to CI.

New checks:

  • The default-layout play now asserts that all titlebar controls have one height. On main it would see {32, 28} and fail.
  • The titlebar identity unit test now asserts that an untitled parent reads "New task" in the parent-return label. Old code gives "New Chat".
  • The ComposedShell story now passes the titlebar parent the way production does. Before, lineage stories showed no parent at all.

Not covered:

  • No existing story renders a branch without a Goal, so there is no before/after image of the breadcrumb removal. The removal is enforced at compile time, because ChatView no longer accepts the prop.
  • Existing on main and unchanged here: renaming a very long title grows the field past the window's left edge, because the Astryx field wrappers keep min-width: auto.

Titlebar control height, light
Titlebar control height, dark
Rename field, light
Rename field, dark
Lineage in the titlebar, light
Lineage in the titlebar, dark

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Devin (Claude) audited the #4679 items against main, made the changes and tests, ran the CDP measurements and screenshots, and drafted this description.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@github-actions github-actions Bot added the effort/M Under 500 readable lines label Sep 27, 2026
@Astro-Han
Astro-Han force-pushed the fix/4679-titlebar-identity branch from ba82ee0 to 27691b8 Compare September 27, 2026 12:25
@Astro-Han
Astro-Han marked this pull request as ready for review September 27, 2026 12:51

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[kabi-opus-dev] Review of removal completeness and the architecture gate. Automated review (Claude). The parent-session entry point and focus rings are Haoqing Sol6_2's part; I make no claim on them. Same owner as the coordinating seat, so this is not independent corroboration.

Bound to 27691b884c6390768a6772f24bd6559e821a9187; head re-checked before publishing. Merge-base fdaa85806.

Conclusion: no P0–P3. The removal is complete, and the app-shell.tsx ledger change is real.

① Removal completeness

Repo-wide git grep at head, with base counts as the positive control:

symbol base head
branchBanner / onBranchBannerClick 12 / 4 0 / 0
BranchBanner / BranchBannerSessionInput / SessionContextBranch 24 / 3 / 2 1† / 0 / 0
deriveBranchBanner / branch-banner 8 / 4 1† / 2†
fromAbortedTurn / onBranchNavigate 9 / 4 0 / 0
copy branchBeforeInterrupt / sessionLineageAriaLabel (3 locales) 6 / 5 0 / 0
CSS maka-session-context__lineage / __breadcrumb* / interrupt-origin 2 / 7 / 1 0 / 0 / 0

† All remaining hits are in docs/archive/desktop-smoke-plan-legacy.md, a historical record that is correct to keep.

  • fromAbortedTurn removal loses no reachable behaviour. At base, the only production caller, use-session-navigation-reads.ts:47, called deriveBranchBanner with two arguments, and so did the story. The flag was never set, so the "从中断前分支" token could not render in the app.
  • Knip (npx knip --workspace apps/desktop and --workspace packages/ui, the same invocations CI uses): both exit 0 with no findings. Positive control: an unused export planted in session-context-layer.tsx was reported (exit 1), then reverted.
  • Types: @maka/ui typecheck exit 0. For desktop I ran tsconfig.main, .renderer and .storybook individually, and all three exit 0 with 0 errors. The npm typecheck script chains them with &&, and my environment has 7 pre-existing errors in the untouched src/preload/runtime-host-session-catalog.ts, so running the chain would have hidden the other three. So no caller still passes the removed ChatView/SessionContextLayer props (branchBanner, onBranchBannerClick, branch, onBranchNavigate, sessionName), and the stories compile.
  • openSessionInChat stays live: it still has other uses in app-shell.tsx, for example the titlebar parent onOpen at :1228.
  • Biome lint on the 12 changed TS/TSX files is clean.

② Architecture gate, run the way CI runs it

  • check-renderer-architecture.mjs --base fdaa85806… --strict-base: passed (fixtures 112/112). Also against current main 2a568ef97 (3 commits ahead of the merge-base): passed.
  • The ledger number is real, not just accepted by the gate. I regenerated the ledger at head with --write, and it is byte-identical to the committed renderer-architecture.json. I then restored the file, and the tree is clean. The −12 on app-shell.tsx (nonTriviaTokens 12385 → 12373) matches the three removed lines exactly: branchBanner, (2 tokens), branchBanner={branchBanner} (5 tokens) and onBranchBannerClick={openSessionInChat} (5 tokens).
  • git merge-tree --write-tree HEAD origin/main exits 0.

Tests run

session-context-layer-goal + titlebar-session-identity (6/6), session-navigation-controller (5/5).

Not verified

I did not check the titlebar size change, the removed titlebar focus-ring and rename-input CSS, the parent-entry behaviour, or any rendering or visual effect. Those are Sol6_2's part. I did not run the app, E2E, Storybook smoke or the full suite.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the 17-file change at 27691b884c6390768a6772f24bd6559e821a9187. I found one P2 usability regression, described inline. Ordinary branch navigation still reaches its parent through parentSessionId and the titlebar arrow, but the parent title is no longer visible when the rail is collapsed.

I checked the renderer parent selector and titlebar wiring, the context-layer deletion, titlebar geometry and keyboard focus in Chromium Storybook at 1280, 720, and 420 px, and the untitled-parent copy for en, zh-CN, and zh-TW. The 28 px titlebar buttons start at y=6; Astryx's 2 px outline with a 3 px offset starts at y=1 and was not clipped in those browser checks. The localized parent labels resolve to New task, 新建任务, and 建立任務. npm ci, build:test, 11 focused tests, Desktop story typecheck, Storybook build, git diff --check, and a merge-tree against current main passed; the current-head hosted test check is green.

A linked subagent child uses subagentParent.parentSessionId, whereas the production titlebar selector reads only parentSessionId (use-session-navigation-reads.ts:47-56), so it still has no titlebar parent arrow. The removed breadcrumb used that same ordinary field, so I did not attribute this pre-existing gap to this PR; the subagent story injects a parent directly and does not verify the production selector. I did not test packaged Electron, native window controls, or screen readers.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

Comment thread packages/ui/src/session-context-layer.tsx
…rity

The titlebar mixed two control heights: the sidebar search and collapse
buttons were Astryx md (32px, flush with the 32px strip) while the session
identity and the Workbar restore were sm (28px). All titlebar controls now
use sm, so they share one centre line and one box.

At 28px the default Astryx focus outline (2px + 3px offset) clears the top
of the window, so the product's inset box-shadow focus ring for titlebar
buttons is gone. The rename field also stops stripping the Astryx
TextInput and drawing its own underline shadow; only the content-sized
width stays.

SessionContextLayer still repeated the session name (and the parent) in a
branch breadcrumb, a second presentation of what TitlebarSessionIdentity
already shows with its parent-return button. The breadcrumb, its
BranchBanner derivation and the fromAbortedTurn token that production
never supplied are removed; the parent name in the titlebar now goes
through presentSessionName like the session name does.

Generated-by: Devin
…e titlebar

A linked subagent child records its parent only in subagentParent; the Host
never sets parentSessionId on it. Since apache#5532 the titlebar read its parent
from a selector that looked at parentSessionId alone, so a subagent child
opened from the stream had no return arrow. Before that, the titlebar took
the parent from the rail's revision-aware linked tree, which is still the
projection the rail uses to highlight the ancestor row.

The selector now asks that projection first and falls back to the ordinary
parentSessionId, which branches and side conversations still use for their
return arrow. The test fixture's child drops the parentSessionId it never
has in production, which is what hid the gap.

Generated-by: Devin
@Astro-Han
Astro-Han force-pushed the fix/4679-titlebar-identity branch from 27691b8 to 7b88874 Compare September 27, 2026 13:52
@Astro-Han

Copy link
Copy Markdown
Contributor Author

The linked-subagent parent gap from the review summary is fixed in 7b88874. Since #5532 the titlebar parent came from a selector that read only parentSessionId, which the Host never sets on a linked subagent child. It now takes the parent from the rail's revision-aware linked tree first, as it did before #5532, and falls back to parentSessionId for branches and side conversations. The test fixture's child no longer carries the parentSessionId it never has in production, so the reads test fails on the old selector.

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving at 7b8887475ad6cad52f25858e29f61c1e3f34450a. No open P0–P2.

  • The earlier P2 (parent name only in the back-arrow tooltip once the context-band breadcrumb is gone) is accepted as design by the author and maintainer, per the #4985 titlebar decision recorded above; not re-litigated here.
  • 7b8887475: the titlebar parent now comes from the rail's revision-aware linked tree first, then parentSessionId. Probed on the current build: a linked subagent child returns to the family representative (root-v2), branch / side conversation still return to the physical parent, and a parentless revision gets no arrow. Related tests 18/18; the fixture no longer carries the production-impossible parentSessionId, so the reads test fails on the old selector.
  • Earlier round: removal of the breadcrumb/BranchBanner/fromAbortedTurn surface is clean repo-wide; --strict-base architecture gate passes and the ledger regenerates byte-identical; focus ring unclipped at 1280/720/420; untitled parent label correct in en / zh-CN / zh-TW.
  • CI test green; merges cleanly with current main.

Not run: packaged Electron, native window, screen reader.

Reviews: #5761 (review) , #5761 (review)

@jackwener
jackwener merged commit df5bdfd into apache:main Sep 27, 2026
1 check passed
@Astro-Han
Astro-Han deleted the fix/4679-titlebar-identity branch September 27, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/M Under 500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants