5
Sidebar and Navigation
Alan Wizemann edited this page 2026-05-01 15:52:56 +02:00

Sidebar and Navigation

Navigation state lives in a single @Observable coordinator. The sidebar is a List bound to it; feature views observe it. There is no router, no NavigationStack stack tracking, no global state library — just one source of truth for "where am I?".

AppCoordinator

AppCoordinator.swift holds three pieces of state:

  • selectedSection: SidebarSection — defaults to .dashboard.
  • selectedSessionId: String? — optional deep link into the Sessions browser.
  • selectedProjectName: String? — optional deep link into a project dashboard.

It is injected at the root of each window via .environment(coordinator) in ContextBoundRoot so any view can read it with @Environment(AppCoordinator.self) private var coordinator.

Each Scarf window has its own AppCoordinator — selection in one window doesn't bleed into another. The coordinator is paired with one ServerContext for the lifetime of the window.

SidebarSection

SidebarSection (AppCoordinator.swift) is the source of truth for every sidebar item. Each case has a rawValue (display name) and an icon (SF Symbol name). 24 cases grouped into 5 sidebar headers (the order is hardcoded in SidebarView.swift). Two new items in v2.6: Curator (under Interact) and Kanban (under Manage), both capability-gated on Hermes v0.12 and hidden on older hosts.

Monitor (4)

Section Icon
Dashboard gauge.with.dots.needle.33percent
Insights chart.bar
Sessions bubble.left.and.bubble.right
Activity bolt.horizontal

Projects (1)

Section Icon
Projects square.grid.2x2

Projects has its own header — not a Manage sub-item — because per-project work (Dashboard / Sessions / Site / Slash Commands tabs, template install) is a top-level workflow surface in v2.5.

Interact (4)

Section Icon
Chat text.bubble
Memory brain
Curator wand.and.stars
Skills lightbulb

Curator (v2.6, Hermes v0.12+ only) wraps hermes curator — status panel, run / pause / resume, three leaderboards (least-recently-active / most-active / least-active), inline pin toggle, restore-archived sheet, last-run REPORT.md inline. Capability-gated on HermesCapabilities.hasCurator so the row disappears entirely on pre-v0.12 hosts.

Configure (7)

Section Icon
Platforms dot.radiowaves.left.and.right
Personalities theatermasks
Quick Commands command.square
Credential Pools key.horizontal
Plugins app.badge.checkmark
Webhooks arrow.up.right.square
Profiles person.2.crop.square.stack

Manage (8)

Section Icon
Tools wrench.and.screwdriver
MCP Servers puzzlepiece.extension
Messaging Gateway antenna.radiowaves.left.and.right
Kanban rectangle.stack
Cron clock.arrow.2.circlepath
Health stethoscope
Logs doc.text
Settings gearshape

Kanban (v2.6, Hermes v0.12+ only) is a read-only view over hermes kanban list --json — paginated table filtered by status, status badges, meta chips (id / assignee / workspace / skills), 5s polling while foregrounded. Capability-gated on HermesCapabilities.hasKanban; the create / claim / dispatch UI is deferred until upstream stabilizes the multi-profile collab layer (which was reverted in v0.12).

The Gateway item's displayName is "Messaging Gateway" — disambiguates from the v2.3 Tool Gateway (Nous Portal subscription routing) which is a Health-tab surface, not its own sidebar item. The enum case is still .gateway and the persisted state file path (~/.hermes/gateway_state.json) is unchanged.

SidebarView

SidebarView.swift is a List with hardcoded Section headers. Selection is two-way bound to coordinator.selectedSection:

List(selection: $coordinator.selectedSection) {
    Section("Monitor")   {  }
    Section("Projects")  {  }
    Section("Interact")  {  }
    Section("Configure") {  }
    Section("Manage")    {  }
}
.listStyle(.sidebar)

There is no search bar, no collapsible-section state to persist — every section is always expanded.

Routing

ContentView's detailView is a single switch coordinator.selectedSection over every SidebarSection case, returning the right view for the current selection. New features add one case here (see Adding a Feature Module).

Multi-window

Each window is bound to one ServerContext and one AppCoordinator. The window menu (and ⌘1…⌘9 keyboard shortcuts) opens additional windows for other servers — see Keyboard Shortcuts. Closing a window destroys its coordinator; reopening reads the section back from defaults.

ScarfGo (iOS) navigation

ScarfGo uses a different model — a 5-tab TabView rather than a sidebar. The tabs (Dashboard | Projects | Chat | Skills | System) are wrapped in their own NavigationStacks so push navigation (Cron editor, Memory detail, Project detail, Settings) stays scoped to the tab. Cross-tab signalling (Dashboard row → Chat tab resume, Project Detail → in-project chat handoff, notification deep-link → Chat) flows through ScarfGoCoordinator.

.tabViewStyle(.sidebarAdaptable) is wired so the system can switch to a sidebar layout on larger devices — but as of v2.5 the iOS target ships iPhone-only (TARGETED_DEVICE_FAMILY = 1, SUPPORTS_MACCATALYST = NO, SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO). iPad polish is on the ScarfGo Roadmap but not in scope for v2.5.

The Mac sidebar's "System" / advanced sections collapse into the iOS System tab (server identity, Memory link, Cron link, Settings link, Disconnect / Forget). See Platform Differences for the full Mac↔iOS feature matrix.


Last updated: 2026-04-25 — Scarf v2.5.0 (audit pass: 5 sidebar groups not 4, 22 cases not 23, Projects own group, Messaging Gateway display name, iPhone-only iOS target)