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).
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Phase 3 — Two Orca-derived named themes and notification sounds
|
||||
|
||||
_One PR, after phase 2. Small._
|
||||
|
||||
## Goal
|
||||
|
||||
1. Offer Orca's neutral palette as two **optional** named themes — "Orca Dark"
|
||||
and "Orca Light" — in the existing Appearance picker. Defaults do not
|
||||
change; nothing else about the theme system changes.
|
||||
2. Play a short sound with a browser notification, per category, user-chosen.
|
||||
|
||||
## 1. Themes
|
||||
|
||||
`client/src/hooks/useTheme.ts` — extend the registry only:
|
||||
|
||||
```ts
|
||||
export const THEMES = {
|
||||
dark: ["default", "darcula", "apple-dark", "orca-dark"],
|
||||
light: ["default", "apple-light", "intellij-light", "canvas", "orca-light"],
|
||||
} as const …
|
||||
```
|
||||
|
||||
`client/src/index.css` — two override blocks shaped exactly like
|
||||
`.dark[data-theme="darcula"]` (every token the other named themes set, no
|
||||
more, no fewer; comment each with the hex). Values mapped from Orca's
|
||||
`src/renderer/src/assets/main.css` (`.dark` / `:root`) onto AgentHub's roles:
|
||||
|
||||
| AgentHub token | Orca Dark | Orca Light |
|
||||
| --- | --- | --- |
|
||||
| `--surface-0` (page) | `#0a0a0a` | `#ffffff` |
|
||||
| `--surface-1` | `#111111` | `#fafafa` |
|
||||
| `--surface-2` | `#171717` (card) | `#f5f5f5` (muted) |
|
||||
| `--surface-3` (`.card`) | `#1c1c1c` | `#ffffff` |
|
||||
| `--surface-4` | `#262626` (secondary/muted) | `#ececec` |
|
||||
| `--surface-5` | `#404040` (accent/hover) | `#e5e5e5` |
|
||||
| `--border` | `#1f1f1f` (≈ white 7 % on #0a0a0a) | `#e5e5e5` |
|
||||
| `--border-light` | `#2e2e2e` (≈ white 15 %) | `#d4d4d4` |
|
||||
| `--accent` | `#e5e5e5` (Orca `primary`) — **check contrast of accent-on-surface text; if the UI uses `text-accent` on `surface-3`, use `#3b82f6` instead and note it** | `#171717` (same caveat) |
|
||||
| `--accent-hover` | `#fafafa` | `#262626` |
|
||||
| `--fg-primary` | `#fafafa` | `#0a0a0a` |
|
||||
| `--fg-secondary` | `#d4d4d4` | `#404040` |
|
||||
| `--fg-muted` | `#a1a1a1` | `#737373` |
|
||||
| `--status-success` | `#86efac` | `#15803d` |
|
||||
| `--status-warning` | `#eab308` | `#ca8a04` |
|
||||
| `--status-danger` | `#ff6568` | `#e40014` |
|
||||
| `--syn-*` | copy from `apple-dark` | copy from `apple-light` |
|
||||
|
||||
The light theme's surfaces must still *rise in lightness with their index* as
|
||||
`index.css` documents; the table above honours that except `surface-3` =
|
||||
`#ffffff` on `surface-0` = `#ffffff` — set `--surface-0` to `#f5f5f5` and
|
||||
`--surface-1` to `#fafafa` so the card lifts. State the final values in the
|
||||
PR.
|
||||
|
||||
Verify with the existing contrast rule (`fg-muted` ≥ 4.5:1 on `surface-3`,
|
||||
status labels ≥ 4.5:1 on their densest chip) — the Appearance swatch test
|
||||
(`Sidebar.theme.test.tsx` / `AppearanceSettings` tests) covers the picker;
|
||||
add the two names to whatever list those tests assert.
|
||||
|
||||
`terminalTheme.ts` already derives the xterm palette from the tokens; confirm
|
||||
it reads the new blocks live when switching.
|
||||
|
||||
i18n: `settings.json` `appearance.themes.orca-dark` / `orca-light` (en: "Orca
|
||||
Dark"/"Orca Light", vi same).
|
||||
|
||||
## 2. Notification sounds
|
||||
|
||||
### Prefs
|
||||
|
||||
`client/src/lib/notifPrefs.ts`:
|
||||
|
||||
```ts
|
||||
export type NotifSound = "none" | "chime" | "pop" | "alert";
|
||||
export interface NotifPrefs {
|
||||
…existing…
|
||||
sound: NotifSound; // global default
|
||||
soundByType?: Partial<Record<keyof Omit<NotifPrefs, "enabled" | "sound" | "soundByType">, NotifSound>>;
|
||||
}
|
||||
defaultNotifPrefs.sound = "none";
|
||||
```
|
||||
|
||||
`loadNotifPrefs` already spreads defaults over stored JSON, so existing users
|
||||
get `"none"`.
|
||||
|
||||
### Assets
|
||||
|
||||
`client/public/sounds/{chime,pop,alert}.wav`, each ≤ 20 KB, generated — not
|
||||
downloaded — by `scripts/gen-notification-sounds.js` (Node, PCM 16-bit 22.05 kHz
|
||||
mono, two or three sine partials with a short decay; commit the WAVs *and* the
|
||||
script; the script carries the file header). No licence question, no network.
|
||||
|
||||
### Playback
|
||||
|
||||
`client/src/lib/notify.ts`: `playSound(kind)` — a module-level
|
||||
`Map<NotifSound, HTMLAudioElement>` created lazily, `audio.currentTime = 0;
|
||||
audio.play().catch(() => {})` (autoplay may be refused before a user gesture;
|
||||
that is fine). `useNotifications.ts`: in `notify()`, after deciding to show
|
||||
(native, push or local fallback), resolve `soundByType[type] ?? sound` and
|
||||
play it when not `"none"`. Play whenever a notification is shown, visible tab
|
||||
or not; the per-type setting is the user's volume control. No focus
|
||||
heuristics.
|
||||
|
||||
### Settings UI
|
||||
|
||||
Phase-2 `notifications` section: a `SettingsRow` "Sound" with a `<select>`
|
||||
(None / Chime / Pop / Alert) and a ▶ preview button; under each existing
|
||||
per-type switch, a small "Use default · Chime · Pop · Alert · None" segmented
|
||||
control (`SettingsSegmented`) that writes `soundByType[type]`. i18n:
|
||||
`notifications.sound.*`.
|
||||
|
||||
## Tests
|
||||
|
||||
- `notifPrefs.test.ts`: defaults, legacy JSON without `sound` loads as
|
||||
`"none"`, `soundByType` round-trip.
|
||||
- `useNotifications` test: `playSound` called with the per-type value, not
|
||||
called for `"none"`; stub `HTMLMediaElement.prototype.play`.
|
||||
- Theme: picker lists the two new names; `data-theme` attribute set; snapshot
|
||||
of Appearance regenerated once.
|
||||
|
||||
## Docs
|
||||
|
||||
`client/README.md` (themes list, sounds), `README.md` feature list,
|
||||
`CLAUDE.md` marker ("named themes gained `orca-dark`/`orca-light`; a sound per
|
||||
notification category, assets generated by `scripts/gen-notification-sounds.js`").
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Switching to Orca Dark/Light re-colours every surface, the xterm palette and
|
||||
the diff view with no hardcoded colour leaking (walk Dashboard, Sessions,
|
||||
Workspace with the Changes panel open, Settings).
|
||||
- A turn-complete notification with sound "Pop" plays once; with "None" plays
|
||||
nothing; preview button plays immediately.
|
||||
Reference in New Issue
Block a user