-
-
Notifications
You must be signed in to change notification settings - Fork 41
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.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 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:
| Section | Icon |
|---|---|
| Dashboard | gauge.with.dots.needle.33percent |
| Insights | chart.bar |
| Sessions | bubble.left.and.bubble.right |
| Activity | bolt.horizontal |
| Section | Icon |
|---|---|
| Chat | text.bubble |
| Memory | brain |
| Skills | lightbulb |
| 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 |
| 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.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.
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).
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
Wiki edited via the local .wiki-worktree/ clone. See Wiki Maintenance for the workflow. Last sync: 2026-04-20.
Getting Started
ScarfGo (iOS)
User Guide
- Dashboard
- Insights & Activity
- Chat
- Slash Commands
- Memory & Skills
- Projects & Profiles
- Project Templates
- Template Catalog
- Template Ideas
- Platforms / Personalities / Quick Commands
- Servers & Remote
- MCP, Plugins, Webhooks, Tools
- Gateway / Cron / Health / Logs
Architecture
- Overview
- Core Services
- Design System
- Data Model
- Transport Layer
- ScarfCore Package
- Sidebar & Navigation
- ACP Subprocess
Developer Guide
Reference
Troubleshooting
Contributing
Release History
Legal & Support
Unsorted