Repository navigation
Session Info: Remote Control's row draws a pointer, not a phone outline - #30273
Conversation
Leo, 2026-11-01: "the symbols we have are a little strange looking in terms of alignment". The row glyphs shared a 16px slot and a 1.5-unit stroke, but their ink did not: the phone ran 20 units tall (past the row's 19-unit crop), the Mac 13, and the folder and the puzzle piece sat below the slot's centre. Icon's new `fit` (with `row`) measures each glyph's exact ink bounds, centres them on the slot, scales the longer side to one 17-unit ink box and widens or narrows each stroke by the inverse scale, so every glyph draws at the pack's weight. Session Info's Environment rows, Remote Control and the Context and Connectors heads use it. Test: bun test src/agent-session/acpmux/icons/opticalFit.test.ts (12 pass); session-info environment/info/connectors/context suites pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a7568197607c3cdfb81
Leo, 2026-11-01: "i think we should rethink what symbol we have for remote control at least". The row drew device.phone, a bare phone outline taller and narrower than every other row icon. It now draws computeruse.click, a pointer, as Claude Code desktop marks its Remote Control row. The gallery keeps four others beside it (phone with waves, display, the replaced phone, link). Test: bun test test/session-info-environment.test.tsx (9 pass; the new pointer test fails on base, which draws device.phone). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-7a7568197607c3cdfb81
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: Shipped in:
|
There was a problem hiding this comment.
3 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="webviews/src/agent-session/acpmux/icons/opticalFit.ts">
<violation number="1" location="webviews/src/agent-session/acpmux/icons/opticalFit.ts:8">
P3: `ROW_SPAN` repeats `ROW_VIEWBOX`'s width as a literal. Derive it from the constant so a crop change cannot desynchronise the fit.</violation>
<violation number="2" location="webviews/src/agent-session/acpmux/icons/opticalFit.ts:89">
P3: Fitting each style separately means a row's icon changes size and centre when selected, for example git.branch and machine.mac. That undercuts the single shared ink box the fit is meant to give a column. Derive the fit from the Line drawing and apply it to Solid, or confirm the size change is intended.</violation>
<violation number="3" location="webviews/src/agent-session/acpmux/icons/opticalFit.ts:107">
P3: Fitting changes the viewBox scale but only compensates `w`, so fitted dashed strokes get shorter dashes relative to their stroke weight. Scale `dash` and `dashPhase` by the same factor when cloning stroke layers.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| /// spans INK_BOX units of the row crop, with every stroke widened or narrowed by the inverse scale so | ||
| /// it draws at the pack's weight. Glyphs drawn to different extents (a short Mac, a phone taller than | ||
| /// the crop) then share one optical size, centre and stroke. | ||
| export function opticalRowFit(layers: IconLayer[]): { viewBox: string; layers: IconLayer[] } { |
There was a problem hiding this comment.
P3: Fitting each style separately means a row's icon changes size and centre when selected, for example git.branch and machine.mac. That undercuts the single shared ink box the fit is meant to give a column. Derive the fit from the Line drawing and apply it to Solid, or confirm the size change is intended.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/icons/opticalFit.ts, line 89:
<comment>Fitting each style separately means a row's icon changes size and centre when selected, for example git.branch and machine.mac. That undercuts the single shared ink box the fit is meant to give a column. Derive the fit from the Line drawing and apply it to Solid, or confirm the size change is intended.</comment>
<file context>
@@ -0,0 +1,114 @@
+/// spans INK_BOX units of the row crop, with every stroke widened or narrowed by the inverse scale so
+/// it draws at the pack's weight. Glyphs drawn to different extents (a short Mac, a phone taller than
+/// the crop) then share one optical size, centre and stroke.
+export function opticalRowFit(layers: IconLayer[]): { viewBox: string; layers: IconLayer[] } {
+ const cached = fits.get(layers);
+ if (cached) return cached;
</file context>
| /// longer side, so a wide glyph (a Mac) and a tall one (a phone) read as one size in an icon column. | ||
| export const INK_BOX = 17; | ||
| /// The crop a row icon draws in (ROW_VIEWBOX's width). | ||
| const ROW_SPAN = 19; |
There was a problem hiding this comment.
P3: ROW_SPAN repeats ROW_VIEWBOX's width as a literal. Derive it from the constant so a crop change cannot desynchronise the fit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/icons/opticalFit.ts, line 8:
<comment>`ROW_SPAN` repeats `ROW_VIEWBOX`'s width as a literal. Derive it from the constant so a crop change cannot desynchronise the fit.</comment>
<file context>
@@ -0,0 +1,114 @@
+/// longer side, so a wide glyph (a Mac) and a tall one (a phone) read as one size in an icon column.
+export const INK_BOX = 17;
+/// The crop a row icon draws in (ROW_VIEWBOX's width).
+const ROW_SPAN = 19;
+const DEFAULT_STROKE = 1.5;
+
</file context>
| viewBox: [cx - span / 2, cy - span / 2, span, span].map(round).join(" "), | ||
| layers: layers.map((layer) => | ||
| layer.op === "stroke" || layer.op === "clearStroke" | ||
| ? { ...layer, w: round((layer.w ?? DEFAULT_STROKE) * scale) } |
There was a problem hiding this comment.
P3: Fitting changes the viewBox scale but only compensates w, so fitted dashed strokes get shorter dashes relative to their stroke weight. Scale dash and dashPhase by the same factor when cloning stroke layers.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At webviews/src/agent-session/acpmux/icons/opticalFit.ts, line 107:
<comment>Fitting changes the viewBox scale but only compensates `w`, so fitted dashed strokes get shorter dashes relative to their stroke weight. Scale `dash` and `dashPhase` by the same factor when cloning stroke layers.</comment>
<file context>
@@ -0,0 +1,114 @@
+ viewBox: [cx - span / 2, cy - span / 2, span, span].map(round).join(" "),
+ layers: layers.map((layer) =>
+ layer.op === "stroke" || layer.op === "clearStroke"
+ ? { ...layer, w: round((layer.w ?? DEFAULT_STROKE) * scale) }
+ : layer,
+ ),
</file context>
| ? { ...layer, w: round((layer.w ?? DEFAULT_STROKE) * scale) } | |
| ? { | |
| ...layer, | |
| w: round((layer.w ?? DEFAULT_STROKE) * scale), | |
| dash: layer.dash?.map((value) => round(value * scale)), | |
| dashPhase: layer.dashPhase === undefined ? undefined : round(layer.dashPhase * scale), | |
| } |
|
Review (subagent claude-opus-5-5 medium): SHIP at 45e2950 This PR's own commit (HEAD~1..HEAD; #30251 reviewed separately). REMOTE_CONTROL_ICON defaults through; all five candidate names exist in the bundled pack, and the custom family falls back to it. Gallery props type-check against ComponentProps. Swift supplies only the on/off state, not the icon. session-info-environment 9 pass; icons + other session-info tests 38 pass. Follow-ups:
|
|
Found 14 test failures on Blacksmith runners: Failures
...and 4 more test failures. View all on Blacksmith: run 41288126709, run 41288126665
|
…ote-control-icon # Conflicts: # webviews/src/agent-session/acpmux/session-info/EnvironmentSection.tsx
|
Review (subagent claude-opus-5-5 medium): SHIP at 55d151e Delta after SHIP at 45e2950: merge of feat-cmux-next (c5f935b) resolving one hunk in EnvironmentSection.tsx. It keeps REMOTE_CONTROL_ICON and #29826's MACHINE_KIND_LABELS, with no duplication. The machine Icon keeps Follow-ups: none |
…ote-control-icon # Conflicts: # webviews/src/agent-session/acpmux/session-info/EnvironmentSection.tsx
|
Review (subagent claude-opus-5-5 medium): SHIP at 43ce9b0 Delta after SHIP at 55d151e: a re-merge of feat-cmux-next after #30251 landed. The single conflict keeps this PR's Follow-ups: none |
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-11-01: "i think we should rethink what symbol we have for remote control at least."
Stacked on #30251 (the optical icon fit). Until that lands, this diff includes its commit. Only the last commit is this PR's.
What
Remote Control's row drew
device.phone, a bare phone outline that was taller and narrower than every other row icon. It now drawscomputeruse.click, a pointer, which is how Claude Code desktop marks its Remote Control row (Leo: "i think it's fine to follow what claude has").REMOTE_CONTROL_ICONin EnvironmentSection names it, andremoteControlIconlets the gallery show the alternatives.Gallery (SessionInfo), each with Remote Control on:
environment-remote-control/-on: shipped, the pointer.environment-remote-control-icon-phone-waves(mobile.pair): says "from your phone" more plainly, but it's still a phone first, and the waves get busy at row size.environment-remote-control-icon-display(remote.display): reads as a second machine next to the machine row.environment-remote-control-icon-phone(device.phone): the replaced one.environment-remote-control-icon-link(link): reads as a URL row next to the repository and branch links.Why the pointer: it matches the reference Leo pointed at. It is the one candidate that reads as "control" rather than "a device". It fits the shared icon box without looking like another machine row.
Spec: leo-workspace
design/session-info.mdandsession-panel.md, updated in the same pass.Changelog
Observed / Not run
Remote Control's row draws a pointer, not a phone outline: fails on base (device.phone), passes on head.session-info-environment.test.tsx: 9 pass.scripts/web-lint.sh: passed.Risk: small, a one-icon swap plus gallery states.
Agent session: agent session
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Changes Session Info's Remote Control row to draw a pointer instead of a bare phone outline, following Claude Code desktop's Remote Control row. Also introduces an optical fit for row icons in Session Info, so mixed glyphs share one ink box, centre, and stroke.
fitprop onIconwithrowscales each glyph's ink to a shared box and rescales strokes to keep pack weight.remoteControlIconprop lets the gallery and tests exercise alternatives.device.phonewas taller and narrower than every other row icon.Written for commit 43ce9b0. Summary will update on new commits.