Skip to content

feat(web): hosts tab in settings (read-only roster: self + peers)#255

Open
mgabor3141 wants to merge 1 commit into
feat/settings-projects-tabfrom
feat/settings-hosts-tab
Open

feat(web): hosts tab in settings (read-only roster: self + peers)#255
mgabor3141 wants to merge 1 commit into
feat/settings-projects-tabfrom
feat/settings-hosts-tab

Conversation

@mgabor3141
Copy link
Copy Markdown
Contributor

Closes #251.

Stacked on #254 (feat/settings-projects-tab) → #253#248. Review/merge bottom-up; rebased onto main once parents land. Diff is the tab bar + Hosts roster + mock fixtures.

Summary

Third slice of the home/settings restructure (#251): add a Hosts tab to Settings — the read-only "dig" companion to the at-a-glance host status the sidebar pills already give.

Changes

  • Tab bar (Projects / Hosts) in the settings modal header, replacing the static "Add project" title. The active tab is driven by the ?settings value; ?settings=hosts deep-links straight to the roster. Tab switches use route(..., replace) so Back closes the modal rather than cycling tabs. Bare ?settings / unknown values normalize to Projects.
  • Hosts roster (read-only): the local host pinned at top as "this host" (synthesized from health: hostname, version, local session count), then the tailnet-discovered + configured peers. Each row: status dot + PeerLabel chip (self gets the "this host" tag) + name (+ "local" tag for Local peers), with status · N sessions · vX meta. A host's last_error renders inline below the row when it isn't connected.
  • Mock fixtures: MOCK_PEERS + MOCK_HEALTH added to mock mode so the Hosts tab (and the existing peer/host-suffix/version-footer surfaces) are exercisable in the ?mock=1 playground. Covers connected, a Local devcontainer, and a disconnected host with a last_error.

Testing

  • 424 web tests pass; lint/build clean.
  • Verified in mock mode: ?settings=hosts opens the roster (self + 4 peers, dots/versions/counts, bespin showing its disconnect error); switching Projects↔Hosts updates the param and preserves mock=1.

Notes

  • The roster is read-only; editable host attributes (alias/mute/etc.) are a deliberate later phase.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Try this PR

curl -sSfL https://gmux.app/install-pr.sh | sh -s -- 255

Built from 4fc0682 — feat(web): hosts tab in settings (read-only roster: self + peers)
Requires GitHub CLI with auth. Artifacts expire after 7 days.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR adds a Hosts tab to the Settings modal as the third slice of the home/settings restructure. The Projects tab is preserved unchanged; the new Hosts tab renders a read-only roster with the local host pinned at the top (synthesized from the health signal) followed by tailnet peers, each showing a status dot, session count, version, and inline last_error for disconnected hosts.

  • Tab bar: replaces the static "Add project" title with a Projects/Hosts switcher; active tab is URL-driven (?settings=hosts) and tab switches use replace so Back closes the modal rather than cycling tabs.
  • Hosts roster (HostsTab / HostRow): reads health, peers, and sessions signals directly; self-row is always connected; last_error renders only when the peer is not connected.
  • Mock fixtures: MOCK_PEERS + MOCK_HEALTH cover connected, Local devcontainer, and a disconnected-with-error peer, making all HostRow states exercisable in ?mock=1.

Confidence Score: 5/5

Read-only UI addition — no mutations, no new data fetching, no auth boundaries; safe to merge.

All changes are additive: new CSS, new JSX components projecting existing signals, and mock-only fixtures. The history-replace logic correctly handles the alreadyActive edge case.

No files require special attention; both findings are minor polish items.

Important Files Changed

Filename Overview
apps/gmux-web/src/settings.tsx Adds SettingsModal tab bar (Projects/Hosts) and a read-only HostsTab/HostRow roster; normalization of unknown tab values is correct; minor: error truncation lacks hover title, ARIA panel wiring is incomplete
apps/gmux-web/src/main.tsx Plumbs tab/onSelectTab props to SettingsModal; replace-vs-push logic is correct — alreadyActive guard prevents duplicate history entries
apps/gmux-web/src/mock-data/index.ts Adds MOCK_PEERS and MOCK_HEALTH fixtures covering all HostRow states; bespin omits version to exercise the optional render branch
apps/gmux-web/src/store.ts Wire-up of MOCK_PEERS/MOCK_HEALTH into _setRawWorld is correct; peers and health remain independent signal projections
apps/gmux-web/src/styles.css New host-list/host-row/host-meta/host-error styles; min-width:0 on .host-name is present; host-error ellipsis works correctly as a block element

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    URL["URL: ?settings=hosts / ?settings=projects"] --> SM["SettingsModal\n(tab prop + onSelectTab)"]
    SM --> |"activeTab === 'hosts'"| HT["HostsTab"]
    SM --> |"activeTab === 'projects'"| PT["Projects tab body\n(unchanged)"]
    HT --> SelfRow["HostRow (self)\nname=health.hostname / status=connected"]
    HT --> PeerRows["HostRow × N\n(peers signal)"]
    PeerRows --> Connected["status=connected: dot green"]
    PeerRows --> Disconnected["status≠connected: dot grey + last_error"]
    OT["openSettings(tab, replace)\nin main.tsx"] --> |"replace=true (tab switch)"| HIST["loc.route replace"]
    OT --> |"push (open modal)"| HISTPUSH["loc.route push"]
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/gmux-web/src/settings.tsx:361-363
The `host-error` div truncates long errors via `text-overflow: ellipsis` but there is no way to see the full message — no tooltip, no expand affordance. A `last_error` like a TLS handshake trace or a long hostname can be truncated past the useful part.

```suggestion
      {!connected && lastError && (
        <div class="host-error" title={lastError}>{lastError}</div>
      )}
```

### Issue 2 of 2
apps/gmux-web/src/settings.tsx:179-214
**ARIA tab panel wiring incomplete**

The `role="tablist"` / `role="tab"` / `aria-selected` pattern is present, but the complementary panel side is missing: the rendered panels (`modal-body` divs) have no `role="tabpanel"`, no `id`, and the tab buttons have no `aria-controls` linking to them. Screen-readers that follow the APG tabs pattern won't be able to associate the selected tab with its content region.

Reviews (3): Last reviewed commit: "feat(web): hosts tab in settings (read-o..." | Re-trigger Greptile

Comment thread apps/gmux-web/src/styles.css
@mgabor3141 mgabor3141 force-pushed the feat/settings-projects-tab branch from df03045 to 8783e9e Compare May 29, 2026 22:56
@mgabor3141 mgabor3141 force-pushed the feat/settings-hosts-tab branch from 83bf5e3 to 4fc0682 Compare May 29, 2026 22:56
@mgabor3141
Copy link
Copy Markdown
Contributor Author

@greptile review

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