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:
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @file RunSetup.tsx
|
||||
* @description The run-form pieces the rest of the app reuses — the pane's
|
||||
* @description The run-form pieces the rest of the app reuses (an attached
|
||||
* image's right-click offers View / Remove) — the pane's
|
||||
* own run form is gone, a new session starts from the workspace home's prompt
|
||||
* box (`WorkspaceHome.tsx`):
|
||||
* - `PromptEditor` — the prompt textarea with `/` (commands and skills,
|
||||
@@ -41,6 +42,7 @@ import {
|
||||
import type { CwdSuggestion, CcMdItem } from "../../lib/api";
|
||||
import type { LaneTool } from "../../lib/types";
|
||||
import { Select } from "../Select";
|
||||
import { ContextMenu, MenuItem, menuPoint, type MenuPoint } from "../ContextMenu";
|
||||
|
||||
export interface SlashCommand {
|
||||
name: string;
|
||||
@@ -262,6 +264,7 @@ export function PromptEditor({
|
||||
// added to the prompt only when it is sent (promptWithImages).
|
||||
const [upload, setUpload] = useState<"uploading" | "failed" | null>(null);
|
||||
const [viewing, setViewing] = useState<PromptImage | null>(null);
|
||||
const [imageMenu, setImageMenu] = useState<(MenuPoint & { img: PromptImage }) | null>(null);
|
||||
const attachImages = (list: FileList | null | undefined) => {
|
||||
const picked = Array.from(list ?? []).filter((f) => f.type.startsWith("image/"));
|
||||
if (picked.length === 0) return false;
|
||||
@@ -342,6 +345,7 @@ export function PromptEditor({
|
||||
aria-label={img.name}
|
||||
title={img.name}
|
||||
onClick={() => setViewing(img)}
|
||||
onContextMenu={(e) => setImageMenu({ ...menuPoint(e), img })}
|
||||
className="block h-14 w-14 overflow-hidden rounded-lg border border-border bg-surface-3 hover:border-accent/50"
|
||||
>
|
||||
<img src={img.url} alt="" className="h-full w-full object-cover" />
|
||||
@@ -357,6 +361,27 @@ export function PromptEditor({
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
{imageMenu && (
|
||||
<ContextMenu x={imageMenu.x} y={imageMenu.y} onClose={() => setImageMenu(null)}>
|
||||
<MenuItem
|
||||
onSelect={() => {
|
||||
setImageMenu(null);
|
||||
setViewing(imageMenu.img);
|
||||
}}
|
||||
>
|
||||
{t("drop.view")}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
danger
|
||||
onSelect={() => {
|
||||
setImageMenu(null);
|
||||
removeImage(imageMenu.img);
|
||||
}}
|
||||
>
|
||||
{t("drop.remove")}
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<textarea
|
||||
|
||||
Reference in New Issue
Block a user