feat(ui): finish the workspace-sessions plan — tool filter, session search, explicit Resume
- Sidebar: a tool filter (shown once workspaces of two tools exist, kept in agenthub.sidebar.projects → tool; reordering under it keeps the hidden workspaces' places), and search that also finds a workspace by one of its session names, showing only the matching sessions. - Workspace home: the repository it belongs to (root, main branch, worktree count) under the title, and each recent's cost. - Transcript page: an explicit Resume for a Claude session started in a workspace's own folder (SessionResumeButton). It warns that a resume re-reads the whole transcript, re-reads the live panes, then starts in the first free pane and opens it; a live session offers Open pane instead.
This commit is contained in:
@@ -22,8 +22,8 @@ import {
|
||||
type PermissionMode,
|
||||
type RunHandle,
|
||||
} from "../../lib/api";
|
||||
import type { Lane, Session } from "../../lib/types";
|
||||
import { timeAgo } from "../../lib/format";
|
||||
import type { Lane, Project, Session } from "../../lib/types";
|
||||
import { fmtCost, timeAgo } from "../../lib/format";
|
||||
import { useAiTools } from "../../hooks/useAiTools";
|
||||
import { useProjectTreePrefs } from "../../lib/projectTreePrefs";
|
||||
import { ModelPicker, PromptEditor } from "../run/RunSetup";
|
||||
@@ -137,6 +137,22 @@ function HomeBody({
|
||||
};
|
||||
}, [lane.id]);
|
||||
|
||||
// The repository this workspace belongs to (Settings → Projects used to
|
||||
// show this): read once per visit, from the server's cached project list.
|
||||
const [project, setProject] = useState<Project | null>(null);
|
||||
useEffect(() => {
|
||||
let live = true;
|
||||
api.projects
|
||||
.list()
|
||||
.then(({ projects }) => {
|
||||
if (live) setProject(projects.find((p) => p.workspaces.includes(lane.id)) ?? null);
|
||||
})
|
||||
.catch(() => undefined);
|
||||
return () => {
|
||||
live = false;
|
||||
};
|
||||
}, [lane.id]);
|
||||
|
||||
// Suggestions are read when the folder form opens, not on every visit.
|
||||
useEffect(() => {
|
||||
if (!folderOpen) return;
|
||||
@@ -268,6 +284,20 @@ function HomeBody({
|
||||
<MoreHorizontal className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
{project?.root && (
|
||||
<p
|
||||
data-testid="workspace-home-facts"
|
||||
className="mt-1 flex flex-wrap gap-x-3 font-mono text-[11px] text-fg-muted"
|
||||
>
|
||||
<span title={t("home.repoRoot")}>{project.root}</span>
|
||||
{project.worktrees.find((w) => w.main)?.branch && (
|
||||
<span title={t("home.mainBranch")}>
|
||||
{project.worktrees.find((w) => w.main)?.branch}
|
||||
</span>
|
||||
)}
|
||||
<span>{t("home.worktreeCount", { count: project.worktrees.length })}</span>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="card mt-5 p-3">
|
||||
<PromptEditor
|
||||
@@ -340,6 +370,11 @@ function HomeBody({
|
||||
<span className="min-w-0 flex-1 truncate text-sm text-fg-primary">
|
||||
{s.name || s.id.slice(0, 8)}
|
||||
</span>
|
||||
{!!s.cost && s.cost > 0 && (
|
||||
<span className="shrink-0 text-xs tabular-nums text-fg-muted">
|
||||
{fmtCost(s.cost)}
|
||||
</span>
|
||||
)}
|
||||
<span className="shrink-0 text-xs text-fg-muted">
|
||||
{timeAgo(s.last_activity ?? s.started_at)}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user