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:
@@ -62,7 +62,7 @@ import { keepIfSame } from "../lib/keepIfSame";
|
||||
import { useLanes } from "../hooks/useLanes";
|
||||
import { LaneConsolePane } from "../components/run/LaneConsolePane";
|
||||
import { ConsoleTabs } from "../components/run/ConsoleTabs";
|
||||
import { type LayoutUpdate, PaneGrid, usePaneLayout } from "../components/run/PaneGrid";
|
||||
import { type LayoutUpdate, PaneGrid, SplitMenus, usePaneLayout } from "../components/run/PaneGrid";
|
||||
import {
|
||||
type Edge,
|
||||
type PaneLayout,
|
||||
@@ -298,7 +298,7 @@ function ConsoleArea({
|
||||
onRunsChanged();
|
||||
if (!r.run_id || laneRef.current !== laneId) return;
|
||||
pending.current.set(slot, activeRuns);
|
||||
change((l) => moveInLayout(l, slot, target, edge));
|
||||
change((l) => moveInLayout(l.root ? l : view, slot, target, edge));
|
||||
} catch (err) {
|
||||
setSplitError(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
@@ -327,8 +327,25 @@ function ConsoleArea({
|
||||
}}
|
||||
onClosed={(runId) => ended(slotOf(runId))}
|
||||
onHome={onNewSession}
|
||||
// Per-workspace actions (mcp sync, sync-base check, readme).
|
||||
actions={<LaneToolsMenu lane={lane} />}
|
||||
// Per-workspace actions (mcp sync, sync-base check, readme), and
|
||||
// while one pane is shown its split menus (a split pane has its own).
|
||||
actions={
|
||||
<div className="flex items-center gap-1">
|
||||
{shown.length === 1 && shown[0] !== undefined && (
|
||||
<SplitMenus
|
||||
slot={shown[0]}
|
||||
shown={shown}
|
||||
runs={tabs}
|
||||
label={label}
|
||||
// A layout never saved is the one-pane `view`: split that.
|
||||
onChange={(fn) => change((l) => fn(l.root ? l : view))}
|
||||
canStart={free !== null}
|
||||
onNewSplit={(target, edge) => void newSplit(target, edge)}
|
||||
/>
|
||||
)}
|
||||
<LaneToolsMenu lane={lane} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{splitError && (
|
||||
|
||||
Reference in New Issue
Block a user