Skip to content

feat: draw every device as the system it runs, on desktop and mobile web - #3166

Merged
bobleer merged 3 commits into
GCWing:mainfrom
bobleer:lwb/device-system-marks-and-mobile-devices
Sep 21, 2026
Merged

bobleer merged 3 commits into
GCWing:mainfrom
bobleer:lwb/device-system-marks-and-mobile-devices

Conversation

@bobleer

@bobleer bobleer commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Summary

Every surface that names a device now draws the system that device reported, instead of one generic monitor, and mobile web draws the same marks as the desktop shell.

Fixes # (none - no issue filed)

Type and Areas

Type: Feature / UI/UX

Areas: web UI, mobile web, shared contracts (presentation data)

Motivation / Impact

The device overview already knew which system every device reported, and drew the same monitor for all of them: in the footer trigger, the attached row, the carousel and the connect list. Mobile web did the same in its devices page, and its compact sidebar guessed a device's shape from its own name, so lwb_server only read as a server because it is called "server".

What changes:

  • Every device name is now marked with the system that device reported: Apple for macOS, the Windows flag, Tux, the HarmonyOS mark, and the server silhouette for a headless (CLI) host. A device whose system cannot be placed keeps the monitor mark; a phone stays a phone and a chat app keeps its brand.
  • The marks and their system resolution live in src/shared/device-system, so the desktop shell and mobile web draw one device the same way while keeping their own sizes, colours and placement (desktop icon tokens, mobile 20/22px).
  • This machine's own system and kind now come from its account directory row. remote_connect_get_device_info answers with identity only, so the previous local source was always empty and the local device silently fell back to the neutral mark.
  • A mark carries its own measured optical correction, and the footer trigger places it on the text's optical centre rather than on its line box.
  • Mobile web's devices page moves "edit name" from a block under every row onto the row's own line, and its model/system line ellipsizes and wraps instead of squeezing the device name at phone widths.
  • This PR also carries a change from a parallel session: worktree lifecycle, dirty, unpublished, locked and missing render as StatusPills with the tone each state deserves.

Compatibility: device_kind is declared as optional on the mobile web directory row, so an older Relay or client that does not send it keeps the neutral mark. No persisted shape, command, or wire contract changes.

Verification

web UI:

  • pnpm --dir src/web-ui run test:run src/app/components/NavPanel src/app/components/RemoteConnectDialog src/infrastructure/config/components/WorktreeSettingsPage.test.tsx - 50 files, 351 tests pass
  • pnpm run check:web - exit 0 (Appearance contract, theme visual governance, type-check)

mobile web:

  • pnpm --dir src/mobile-web run type-check - exit 0
  • pnpm run build:mobile-web - exit 0
  • pnpm --dir src/mobile-web run test:account-login - 17 tests pass
  • pnpm --dir src/mobile-web run test:ui-components - 15 tests pass
  • pnpm --dir src/mobile-web run test:account-browser - 17 tests pass (real Chrome, simulated Relay)

repository:

  • pnpm run check:repo-hygiene - exit 0
  • pnpm run check:core-boundaries - exit 0

Mark geometry and alignment were measured in a real browser rather than eyeballed: each mark's ink box is centred in its own view box, and after the optical correction every mark's mass centre sits within 0.07px of the text's ink centre at the trigger's size (the Apple alone carries a correction, as requested).

Remote scenarios: the marks come from facts the Relay already carries per device, so they are unchanged on remote workspaces, remote control and peer device mode; remote_connect_get_device_info is ControllerLocal and stays so. Mobile web remains read-only over the same Relay directory.

Reviewer Notes

  • src/shared/device-system/deviceSystemMarks.ts is the shared mark table plus the os-to-system resolver; src/web-ui/.../deviceSystemMarks.ts keeps only this shell's artwork-to-mark map and re-exports the shared table.
  • The Linux mark is the Font Awesome Linux brand icon (CC BY 4.0, already recorded in THIRD_PARTY_NOTICES.md).
  • opticalShift per mark is a measured value, documented where it is defined; marks whose mass is already centred carry 0 and render no inline style at all.
  • RemoteConnectDialog.contract.test.ts asserted the footer foreground token on the message-app block. One row of peers now shares one colour, so the token moved to the row that owns it: the contract moved with it and now covers every device kind instead of the chat brand alone. The chat mark still carries its own unframed contract.
  • Screenshots from the real mobile web build (simulated Relay, puppeteer) are attached below.

Checklist

  • This PR is focused and does not include secrets, temporary prompts, generated scratch files, or unrelated artifacts.
  • Relevant verification is recorded above, or skipped checks are explained.
  • User-facing strings, docs, and locales are updated where applicable. (No new strings: existing keys are reused.)

@bobleer
bobleer force-pushed the lwb/device-system-marks-and-mobile-devices branch from 51ad577 to 755a784 Compare September 21, 2026 02:39
bobleer and others added 3 commits September 21, 2026 10:55
The device overview knew which system each device reported and drew one
generic monitor for all of them: in the footer trigger, in the attached
group, in the carousel and in the connect list.

Each of those places now draws the system that device reported - the Apple
mark for macOS, the Windows flag, Tux, the HarmonyOS mark, and the server
silhouette for a headless host - and keeps the monitor only for a device
whose system cannot be placed.

- The marks and their system resolution move to src/shared, so the desktop
  shell and mobile web draw one device the same way. Each surface keeps its
  own sizes, colours and placement.
- This machine's own system and kind now come from its account directory row.
  The device info command answers with identity only, so the local source was
  always empty and the local device fell back to the neutral mark.
- A mark states its own optical correction, measured from the mass of its own
  drawing: the Apple's leaf is thin above a heavy body, so its mass sits about
  9% below the centre of its box and the mark read as low beside a line of text.
- The footer trigger's mark is placed on that line's optical centre instead of
  its line box, which is split by the font's ascent and descent rather than by
  the glyphs drawn in it.

A phone stays a phone and a chat app keeps its brand, because neither is a
system this client can draw.

Co-authored-by: bitfun-ai <318544290+bitfun-ai@users.noreply.github.com>
The devices page drew one monitor for every device, and the compact sidebar
guessed a device's shape from its own name, so a Linux desktop and a headless
host each read as whatever they happened to be called.

Both now draw the mark the desktop shell draws, from the same shared table,
with this surface's own sizes and colours: the devices page keeps its 20px
leading slot and the sidebar its 22px.

- RelayDeviceInfo now declares device_kind, which the Relay already sends. It
  stays optional, so an older Relay or client keeps the neutral mark.
- A headless host draws the server silhouette whatever it runs; a device that
  reports no system this client can place keeps the monitor, and a phone keeps
  the phone.
- The devices page moves "edit name" from a block under every row to an icon
  button on the row's own line. A row is a button and cannot hold another one,
  so the action sits beside it rather than inside it.
- The model and system line ellipsizes, and moves to its own line when it does
  not fit beside the name, instead of squeezing the name at phone widths.

Co-authored-by: bitfun-ai <318544290+bitfun-ai@users.noreply.github.com>
Lifecycle, dirty, unpublished commits, locked and missing read as state, not
as another run of muted text beside the branch name: each becomes a StatusPill
with the tone its state deserves, and the session count gets its own class.

The project header aligns the group count on the workspace name's baseline, and
an unused actions rule is dropped with it.

Co-authored-by: bitfun-ai <318544290+bitfun-ai@users.noreply.github.com>
@bobleer
bobleer force-pushed the lwb/device-system-marks-and-mobile-devices branch from 755a784 to 280729a Compare September 21, 2026 02:55
@bobleer
bobleer merged commit c7024d2 into GCWing:main Sep 21, 2026
13 checks passed
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.

1 participant