feat(ui): a Claude-Desktop-style home for every workspace
/workspace?lane=<id> opens on the workspace's home (WorkspaceHome): a prompt box whose Start opens a new session in the first free pane slot, Recents (a live session attaches its pane, any other opens its transcript, nothing resumes), and Instructions / Memory / Context / Folder. Instructions live in lanes.instructions, extra folders and uploaded files in the new lane_sources table (copies under <data dir>/workspace-context/<lane id>/); spawnRun reads them from the lane's rows and hands Claude --add-dir + an appended system prompt file, Codex --add-dir + developer_instructions. Memory is read-only. Every dashboard pane carries AGENTHUB_RUN_ID; hooks forward it and the run records which session the pane holds (a Claude pane follows /clear and /resume but not a nested claude -p), so the sidebar and notifications open the right pane (&run=). Settings loses its Projects group; Worktrees now decides which worktrees Add folder suggests.
This commit is contained in:
@@ -18,6 +18,19 @@ import { timeAgo } from "../../lib/format";
|
||||
|
||||
export const SESSION_LIMIT = 20;
|
||||
|
||||
/**
|
||||
* The live pane holding a session: the run the hooks bound it to
|
||||
* (`dashboard_runs.session_id`), else — a pane started before the hooks
|
||||
* reported it — the lane's own pane when the lane's bound session is this one.
|
||||
*/
|
||||
export function liveRunFor(lane: Lane, runs: RunHandle[], sessionId: string): RunHandle | null {
|
||||
const live = runs.filter((r) => r.laneId === lane.id && r.status === "running");
|
||||
return (
|
||||
live.find((r) => r.sessionId === sessionId) ??
|
||||
(lane.session_id === sessionId ? (live.find((r) => r.id === lane.run_id) ?? null) : null)
|
||||
);
|
||||
}
|
||||
|
||||
export function WorkspaceSessionList({
|
||||
lane,
|
||||
liveRuns,
|
||||
@@ -53,8 +66,7 @@ export function WorkspaceSessionList({
|
||||
const shown = (sessions ?? []).filter(
|
||||
(s) => !q || (s.name ?? "").toLowerCase().includes(q) || s.id.startsWith(q)
|
||||
);
|
||||
const runFor = (s: Session) =>
|
||||
liveRuns.find((r) => r.sessionId === s.id && r.status === "running") ?? null;
|
||||
const runFor = (s: Session) => liveRunFor(lane, liveRuns, s.id);
|
||||
|
||||
if (sessions !== null && shown.length === 0) {
|
||||
return <p className="py-1 pl-8 text-[11px] text-fg-muted">{t("tree.noSessions")}</p>;
|
||||
|
||||
Reference in New Issue
Block a user