Files
nntrivi2001 a6bd33bcff feat: workspaces own their sessions; desktop parity; sidebar and settings cleanup
Workspace sessions (docs/plans/desktop-parity, phases 1-5 and 7):
- sessions.lane_id: a workspace lists only the sessions its panes held,
  stamped by bindRunSession (first writer wins, nested `claude -p`
  excluded); GET /api/sessions?lane=&archived=
- session row state dot, double-click rename, archive (sessions.archived_at,
  hides only); the activity bar's duplicate Sessions tab removed
- Fork beside Resume (--fork-session, Claude only), Ctrl/Cmd+click a live
  session opens it in a second pane, a diff line's comment is pasted into
  the workspace's last-focused pane (lib/panePaste.ts)

dashboard_runs stability:
- a pane's previous run is archived as <pane>@<started_at> instead of being
  overwritten by INSERT OR REPLACE
- a run is flagged abandoned only once its tmux session is gone, at boot and
  on the run-heal beat (panes outlive a server restart)

UI:
- sidebar: no footer, language or theme block; small collapse button in the
  brand corner; version + update check in the connection details modal, a
  status-bar dot while a release is out; language in Settings -> General
- one Switch component for every switch, the themed Checkbox for every
  checkbox (no native checkboxes)

Also: AI Tools catalog trimmed to 10 built-ins (custom-file prompt modes
tested through fixtures), and the workspace plans under docs/plans/ (the
multi-user server plan lives in the AgentHub-Server repo).
2026-10-02 13:19:50 +07:00

8.7 KiB

Phase 2 — Settings as a full-page grouped navigation

One PR, after phase 1.

Goal

Replace the horizontal PageTabs bar on /settings with a left navigation grouped under small uppercase headings, one section per route, a search box that filters sections, and consistent row primitives — the structure in reference/orca-settings1.png / orca-settings-agents.png / orca-settings-scrolled.png, in AgentHub's own tokens.

Non-goals

  • No new settings. Every control that exists today is kept, moved, and reachable from search. (One exception: the worktree-visibility toggles from phase 1 get their first proper home here.)
  • No theme change. No new dependency.
  • No change to any server setting route.

1. Routes

App.tsx: settings → settings/:section?. SettingsHub.tsx reads :section; unknown or missing ⇒ redirect to settings/general. The old ?tool=<id> query is honoured for one release: ?tool=claude-code ⇒ /settings/agents/claude-code, ?tool=ai-router ⇒ /settings/ai-router, and #ai-tools ⇒ /settings/agents with the catalog modal open. Every in-app link that builds a settings URL (AddLaneModal's AI Tools link, the Claude tab's cross-links, Sidebar nav) is updated to the path form.

Section ids are stable strings (they are URLs and i18n keys):

Group (i18n settingsNav.group.*) Section id Content = today's component
General general Settings.tsx sections: lanesSection (renamed "Workspaces": housekeeping) + data (import, remote sources, clear data) + about
appearance AppearanceSettings
notifications Settings.tsx notifications + alertsHub (AlertsNotifications, WebhookSettings)
Agents agents AiToolsSettings inline (the modal's content as a page; AiToolsModal stays for the Add-workspace link)
agents/claude-code CcConfig (its own sub-tabs stay inside the page)
agents/codex CodexConfig
agents/<catalog id> AiToolConfig toolId — one entry per installed, enabled tool without a launcher (same rule as today's catalogTabs)
Routing ai-router AiRouterConfig
pricing PricingSettings with a vendor filter for all vendors (today it is embedded per tool; keep those embeds too — this page is the all-vendors view)
Git git-credentials GitCredentialsConfig
worktrees new WorktreeVisibilitySettings: "Show untracked worktrees" switch; per-source rows .claude/worktrees/* (Show/Hide), AgentHub-managed (LANES_ROOT) (Show/Hide); writes the phase-1 localStorage key
Projects projects/<encoded root> new ProjectSettings: read-only project card (root, main branch, worktree count), its workspaces (link to each), the per-project override of the worktree sources above (hideSources keyed by root), and "Forget all workspaces of this project" (loops POST …/remove, confirm modal listing them)

A tool section is hidden while that tool is disabled (same aiTools.byId(...).enabled check as today).

2. Layout

┌ sidebar (app) ┬──────────────────────────────────────────────────────────┐
│               │ ← Back to Workspace                                       │
│               │ [🔍 Search settings            Ctrl+F]                     │
│               │                                                           │
│               │ GENERAL          │  General                                │
│               │  ⚙ General       │  Workspace housekeeping, data, about.   │
│               │  ◐ Appearance    │  ───────────────────────────────────── │
│               │  🔔 Notifications│  ┌ card ─────────────────────────────┐  │
│               │ AGENTS           │  │ Housekeeping                      │  │
│               │  ▣ AI tools      │  │ Run housekeeping     [Dry run][Run]│  │
│               │  ✳ Claude Code   │  ├───────────────────────────────────┤  │
│               │  ◎ Codex         │  │ Data                              │  │
│               │  ▲ Antigravity   │  │ Import history …                  │  │
│               │ ROUTING …        │  └───────────────────────────────────┘  │
│               │ PROJECTS         │                                         │
│               │  ◈ agenthub      │                                         │
└───────────────┴──────────────────┴─────────────────────────────────────────┘
  • The settings nav is a second column inside the page (w-56, sticky, own scroll), not the app sidebar. The app sidebar stays as it is.
  • "← Back to Workspace" is a plain link to /workspace (keeps ?ws from localStorage).
  • Content column: max-w-3xl, section title text-2xl font-semibold, one-line description text-sm text-fg-muted, hairline, then one card per subsection (.card as today). Do not nest cards.
  • Mobile (< md): the nav becomes a <select> above the content.

3. Primitives (client/src/components/settings/SettingsRow.tsx)

<SettingsSection title description>            // h3 + muted description inside a card
<SettingsRow label description control alignTop?>  // 2 columns: label+desc | control (shrink-0)
<SettingsSwitchRow label description checked onChange disabled? />
<SettingsSegmented options value onChange />  // role="radiogroup", existing button styles

Every control already in Settings.tsx, AppearanceSettings.tsx, ClaudeAgentHubSettings.tsx, CodexHooksSettings.tsx, AntigravityHooksSettings.tsx, AiToolConfig.tsx is re-wrapped in these rows. CcConfig.tsx (172 KB) and CodexConfig.tsx keep their internal layout in this phase — only their outer frame changes. Rows: py-3 with a description, py-2 without; divider border-b border-border/60 between rows, none after the last.

client/src/lib/settingsSearch.ts:

interface SettingsEntry { section: string; title: string; description?: string; keywords?: string[] }
rankSettings(query, entries): { section: string; score: number }[]

Scoring (lowercase; exact > prefix > substring): section title 900/850/800, entry title 700/650/600, description 500/450/400, keywords 300/250/200. Each section registers its entries in a static SETTINGS_INDEX array (one per SettingsRow label at minimum; i18n-resolved at render). While the query is non-empty the nav shows only matching sections (groups with no match hidden), the first match is focused, Enter opens it; Esc clears. Ctrl+F / Cmd+F focuses the box when the settings page has focus (do not steal it from a terminal — /settings has none, so a plain keydown listener on the page root is enough).

5. i18n

settings.json: settingsNav.group.{general,agents,routing,git,projects}, settingsNav.section.* (title + description per id), search.placeholder, search.noMatch, back, worktrees.*, project.*. Remove tools.* keys that no longer render (keep tools.label if AiToolsModal still uses it).

6. Tests

  • SettingsHub.test.tsx: route → section, legacy ?tool= redirect, hidden tool section when disabled, mobile select.
  • settingsSearch.test.ts: ranking order, empty query, no match.
  • WorktreeVisibilitySettings.test.tsx: writes the phase-1 key, per-project override.
  • Existing panel tests unchanged except their mount path.
  • Snapshots: Settings screens regenerate once.

7. Docs

docs/LANES.md (worktree visibility, per-project settings), README.md (settings tour), client/README.md (route list, primitives), CLAUDE.md marker: "Settings is /settings/:section with a grouped nav; a tool's own settings live under agents/<id>; the whole-page search index is SETTINGS_INDEX".

Acceptance

  • Every control reachable before is reachable after (walk Settings.tsx's section list and each tool tab, tick them off in the PR body).
  • /settings?tool=codex lands on /settings/agents/codex.
  • Typing "pricing" in search shows Routing → Pricing and the Claude/Codex pages (both embed the table); Enter opens the first.
  • Keyboard: Tab order follows the visual order; Ctrl+F focuses search.
  • Suites + headers + snapshots green; before/after screenshots.