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).
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?wsfrom localStorage). - Content column:
max-w-3xl, section titletext-2xl font-semibold, one-line descriptiontext-sm text-fg-muted, hairline, then one card per subsection (.cardas 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.
4. Search
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=codexlands 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+Ffocuses search. - Suites + headers + snapshots green; before/after screenshots.