Skip to content

Sidebar and Navigation

Alan Wizemann edited this page Apr 20, 2026 · 10 revisions

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 is the source of truth for every sidebar item. Each case has a rawValue (display name) and an icon (SF Symbol name). The 23 cases are grouped into four sidebar headers:

Monitor (4)

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

Interact (3)

Section Icon
Chat text.bubble
Memory brain
Skills lightbulb

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
Projects square.grid.2x2
Tools wrench.and.screwdriver
MCP Servers puzzlepiece.extension
Gateway antenna.radiowaves.left.and.right
Cron clock.arrow.2.circlepath
Health stethoscope
Logs doc.text
Settings gearshape

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("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.


Last updated: 2026-04-20 — Scarf v2.0.1

Clone this wiki locally