Skip to content

feat(ui): split ActivityBarItem into its glyph and avatar arms (RIG-3738) - #1202

Merged
trunk-io[bot] merged 1 commit into
compass-ux/3737-avatar-initialfrom
compass-ux/3738-activitybar-union
Sep 13, 2026
Merged

trunk-io[bot] merged 1 commit into
compass-ux/3737-avatar-initialfrom
compass-ux/3738-activitybar-union

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 5 PRs:

  1. main
  2. feat(ui): add the dot-matrix Glyph primitive and its four chrome bitmaps (RIG-3736) #1194
  3. feat(ui): derive the activity-bar avatar initial through one owner (RIG-3737) #1199
  4. "feat(ui): split ActivityBarItem into its glyph and avatar arms (RIG-3738)" (this PR)
  5. feat(ui): convert the chrome pictographs to dot-matrix glyphs (RIG-3739) #1211
  6. feat(ui): finish the chrome glyph conversion (RIG-3739) #1219

ActivityBarItem.icon: string carried two unrelated things: a fixed chrome
symbol on the four static tabs, and a person's initial on the fleet tabs. The
name fit neither, and agentId/unreachable sat on the shared interface
though only fleet tabs ever have them.

The item now splits at the item, not the field: GlyphTabItem carries
name: GlyphName and renders <Glyph/>; AvatarTabItem carries letter
plus the agentId and unreachable that only it uses. A field-level union
was rejected — it keeps a field called icon whose value is sometimes a
person's initial.

The union, the constructors, the render branch and the CSS land together
because they must: the activity-bar loop reads both tab.icon and
tab.agentId off the un-narrowed item, so landing the type without the
render branch is a strict-mode error and leaves a red commit mid-stack.

Narrowing removed a non-null assertion in AgentUnreachable — its agentId
is now required by the type rather than asserted at the use site. store.ts
is unchanged: rightTabGroups holds a genuinely mixed list, so the union is
already the right type there.

The glyph box is pinned to a whole-pixel offset. An 11px box flex-centered in
the tab's 32px content box lands at 10.5px, which smears every 1px
crispEdges cell across two device pixels; whole-integer margins that fill
the axis exactly leave no free space for centering to halve. The SVG is also seated at the box's top edge: an inline-level
replaced box rides the text baseline, so without that it renders 2px below
the box the margins just placed. state-dot.css carries the same rule for
the same reason. Measured in Chromium at dpr 1 and 2: the glyph lands at
(11, 11) with no overflow; flex centering would put the box at 11.5px, and
without the seating rule the glyph sat at y=13.

bridge-colheads.png is recaptured here rather than with the rest of the
baselines, because this is the commit that repaints those pixels. Its clip is
computed from the column-head boxes and runs 1050px wide, so it overlaps the
activity bar; at 855x41 the 0.001 diff-pixel RATIO allows about 35 pixels and
the four glyphs change 99. The full-page captures contain the same changed
pixels but pass, their delta swamped by a budget proportional to mostly
unchanged area — so they are recaptured later, with the rest.

This surface had no tests. It now covers both render arms, the StateDot's
presence and absence, the constructors, and the margin arithmetic the
whole-pixel offset depends on.

Ref: RIG-3738, RIG-3603. Design: docs/designs/ui/compass-glyph-primitives/design.md (DL-367).

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Sep 13, 2026

Copy link
Copy Markdown

RIG-3738

RIG-3603

@rigel-mintaka
rigel-mintaka marked this pull request as ready for review September 13, 2026 02:04
@github-actions

github-actions Bot commented Sep 13, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-ux-3738-activitybar.compass-eng-docs.pages.dev

Deployed from compass-ux/3738-activitybar-union at e433981.

…738)

`ActivityBarItem.icon: string` carried two unrelated things: a fixed chrome
symbol on the four static tabs, and a person's initial on the fleet tabs. The
name fit neither, and `agentId`/`unreachable` sat on the shared interface
though only fleet tabs ever have them.

The item now splits at the item, not the field: `GlyphTabItem` carries
`name: GlyphName` and renders `<Glyph/>`; `AvatarTabItem` carries `letter`
plus the `agentId` and `unreachable` that only it uses. A field-level union
was rejected — it keeps a field called `icon` whose value is sometimes a
person's initial.

The union, the constructors, the render branch and the CSS land together
because they must: the activity-bar loop reads both `tab.icon` and
`tab.agentId` off the un-narrowed item, so landing the type without the
render branch is a strict-mode error and leaves a red commit mid-stack.

Narrowing removed a non-null assertion in `AgentUnreachable` — its `agentId`
is now required by the type rather than asserted at the use site. `store.ts`
is unchanged: `rightTabGroups` holds a genuinely mixed list, so the union is
already the right type there.

The glyph box is pinned to a whole-pixel offset. An 11px box flex-centered in
the tab's 32px content box lands at 10.5px, which smears every 1px
`crispEdges` cell across two device pixels; whole-integer margins that fill
the axis exactly leave no free space for centering to halve. The SVG is also seated at the box's top edge: an inline-level
replaced box rides the text baseline, so without that it renders 2px below
the box the margins just placed. `state-dot.css` carries the same rule for
the same reason. Measured in Chromium at dpr 1 and 2: the glyph lands at
(11, 11) with no overflow; flex centering would put the box at 11.5px, and
without the seating rule the glyph sat at y=13.

`bridge-colheads.png` is recaptured here rather than with the rest of the
baselines, because this is the commit that repaints those pixels. Its clip is
computed from the column-head boxes and runs 1050px wide, so it overlaps the
activity bar; at 855x41 the 0.001 diff-pixel RATIO allows about 35 pixels and
the four glyphs change 99. The full-page captures contain the same changed
pixels but pass, their delta swamped by a budget proportional to mostly
unchanged area — so they are recaptured later, with the rest.

This surface had no tests. It now covers both render arms, the StateDot's
presence and absence, the constructors, and the margin arithmetic the
whole-pixel offset depends on.

Ref: RIG-3738, RIG-3603. Design: docs/designs/ui/compass-glyph-primitives/design.md (DL-367).

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-ux/3738-activitybar-union branch from 15ba631 to e433981 Compare September 13, 2026 22:06
@trunk-io
trunk-io Bot merged commit 001b861 into main Sep 13, 2026
17 of 27 checks passed
@trunk-io
trunk-io Bot deleted the compass-ux/3738-activitybar-union branch September 13, 2026 22:44
@trunk-io

trunk-io Bot commented Sep 13, 2026

Copy link
Copy Markdown

This pull request was merged into main as part of stacked PR 1211.

Sign up for free to 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.

2 participants