feat: right-click menus everywhere, workspace/session cost, long-term DB safety
Right-click menus: one portal shell (components/ContextMenu.tsx) for every item with actions - sidebar/Recents/archived session rows (SessionMenu), console tabs, pane headers, the Changes panel's files/tree/commits, the workspace home, sessions outside a workspace (SessionRowMenu: Sessions table, Dashboard, Activity, Workflow runs), prompt images, every Settings list and every Claude Code/Codex config list (FileItemMenu). Buttons stay; a delete keeps its button's confirm; the shell stops portal clicks from reaching the card that opened it. Status bar: Workspace $ and Session $ on the Workspace page, from ?lane= on GET /api/pricing/cost and the focused/replayed session's cost. Database, no data dropped: synchronous=NORMAL, journal_size_limit 64 MB, hourly PASSIVE checkpoint + PRAGMA optimize, idx_events_session_created (replacing the redundant idx_events_session), watchdog reads sessions.transcript_path, transcript parsed before the hook's write transaction, GET /api/sessions/:id?events=0 for the web page (reloads only on its own session, debounced), streamed export, transactional clear-data and chunked cleanup, subagent import reads tool_use_ids once, runtime logs rotated past 5 MB.
This commit is contained in:
+11
-7
@@ -784,16 +784,20 @@ graph TB
|
||||
|
||||
### SQLite Pragmas
|
||||
|
||||
What `server/db.js` actually sets on every open:
|
||||
|
||||
```javascript
|
||||
// db.js - Performance tuning
|
||||
db.pragma('journal_mode = WAL'); // Write-Ahead Logging
|
||||
db.pragma('synchronous = NORMAL'); // Faster writes (safe with WAL)
|
||||
db.pragma('cache_size = -64000'); // 64MB cache
|
||||
db.pragma('temp_store = MEMORY'); // Temp tables in memory
|
||||
db.pragma('mmap_size = 30000000000'); // Memory-mapped I/O (30GB)
|
||||
db.pragma('page_size = 4096'); // Optimal page size
|
||||
db.pragma("journal_mode = WAL"); // readers never block the writer
|
||||
db.pragma("foreign_keys = ON");
|
||||
db.pragma("busy_timeout = 5000"); // two dashboards may share the file
|
||||
db.pragma("synchronous = NORMAL"); // safe with WAL: a power cut loses only the last commits, never the file
|
||||
db.pragma("journal_size_limit = 67108864"); // a checkpoint truncates the WAL back to 64 MB
|
||||
```
|
||||
|
||||
**Upkeep.** `maintainDatabase()` (exported by `server/db.js`) runs hourly from `server/index.js` (`DASHBOARD_DB_MAINTAIN_MS`, `0` = off): a `wal_checkpoint(PASSIVE)` — it never waits on a reader, such as the other dashboard — after which the next write shrinks the WAL to `journal_size_limit`, and `PRAGMA optimize`. Nothing runs `VACUUM`: it rewrites the whole file under an exclusive lock, so space freed by a cleanup is reused for new rows instead of returned to the disk.
|
||||
|
||||
**Growth.** `events.data` keeps each hook's full payload (a `PostToolUse` carries the tool's whole output), and no data is pruned automatically — `events` is ~95% of a long-lived database (on the order of 300–500 MB a month of heavy use). The only deletions are the user's: Settings → clear data / cleanup, a workspace's purge, `scripts/clear-data.js`. Clear data runs in ONE transaction (foreign keys off around it, so workflows are deleted explicitly), cleanup deletes in 500-id chunks inside one transaction. `GET /api/settings/export` streams `events` in keyset pages of 500 instead of loading the table.
|
||||
|
||||
### Prepared Statements
|
||||
|
||||
```javascript
|
||||
|
||||
Reference in New Issue
Block a user