From be657f6e3c8843687afa801ab7e2ad1d3a0d8dd3 Mon Sep 17 00:00:00 2001
From: cynarAI <155274210+cynarAI@users.noreply.github.com>
Date: Sun, 18 Oct 2026 22:07:53 +0200
Subject: [PATCH] feat(web): remove redundant live progress card and add
adaptive project vs 1:1 agent overview sidebar
---
apps/web/src/pages/Shell.tsx | 415 ++++++++++--------
apps/web/src/pages/shell/project-overview.tsx | 278 +++++++++++-
.../web/src/pages/shell/side-panel-header.tsx | 50 +--
apps/web/src/pages/shell/transcript.tsx | 44 +-
4 files changed, 529 insertions(+), 258 deletions(-)
diff --git a/apps/web/src/pages/Shell.tsx b/apps/web/src/pages/Shell.tsx
index 627d0dc..a917c5d 100644
--- a/apps/web/src/pages/Shell.tsx
+++ b/apps/web/src/pages/Shell.tsx
@@ -286,7 +286,7 @@ export type NavPillar = "heute" | "team" | "projekte";
import { SortableGroup } from "../components/SortableGroup";
import { useRunActivity } from "../lib/run-activity";
import { ProjectLookDialog } from "./shell/project-look-dialog";
-import { ProjectOverview } from "./shell/project-overview";
+import { AgentOverview, ProjectOverview } from "./shell/project-overview";
import { ProjectRow } from "./shell/project-row";
export function ShellPage() {
@@ -3188,8 +3188,186 @@ export function ShellPage() {
+
+ {/* PROJEKTE SECTION */}
+
+
+
{t`Projects`}
+
+ {t`Threads by topic · folders`}
+
+
+
+
+
+
+ {projectSidebarGroups.length === 0 ? (
+
+
{t`No projects yet.`}
+
+
+ ) : null}
+
+ g.title).map((g) => g.spaceId)}
+ strategy={verticalListSortingStrategy}
+ >
+ {projectSidebarGroups.map((group) => {
+ const projectSpace = group.key.startsWith("project-space:")
+ ? spaces.find((space) => space.id === group.spaceId)
+ : undefined;
+ if (projectSpace) {
+ const threads = projectThreads(runActivity, {
+ spaceId: projectSpace.id,
+ });
+ const projectSelected =
+ activeNavPillar === "projekte" &&
+ bootstrapMe?.spaceId === projectSpace.id;
+ // Other chats in the project show only while it is open.
+ const projectChats = projectSelected
+ ? group.bots.filter((item) => item.chat.kind !== "main")
+ : [];
+ return (
+
+ ({
+ botId: b.id,
+ name:
+ b.name === "Chief" || b.name === "Projektkoordinator"
+ ? "Projektmanager"
+ : b.name,
+ color: b.color || FALLBACK_BOT_COLOR,
+ status: b.status,
+ }))}
+ hasMenu={
+ projectSpace.canEdit === true || projectSpace.canDelete === true
+ }
+ selected={projectSelected}
+ unread={group.bots.some((item) => item.chat.unread)}
+ needsYou={threads.needsYou.length}
+ running={threads.running.length > 0}
+ onOpen={() => {
+ setMobileSidebarOpen(false);
+ void startProject(projectSpace.id);
+ }}
+ onMenu={(menu, anchor) => {
+ spaceMenuAnchor.current = anchor;
+ setSpaceMenu(menu);
+ }}
+ />
+ {projectChats.length > 0 ? (
+
+ {projectChats.map((item) => (
+
{
+ setActiveNavPillar("projekte");
+ openSpaceChat(item.chat.spaceId, `/app/g/${item.chat.id}`);
+ }}
+ activeSpaceId={bootstrapMe?.spaceId}
+ menuAnchor={botMenuAnchor}
+ onMenu={setBotMenu}
+ />
+ ))}
+
+ ) : null}
+
+ );
+ }
+ const collapsed =
+ Boolean(group.title) && collapsedSidebarSections.has(group.key);
+ return (
+
+
+ {group.title ? (
+
void startProject(spaceId)}
+ onToggle={toggleSidebarSection}
+ sectionMenuAnchor={sectionMenuAnchor}
+ onSectionMenu={setSectionMenu}
+ spaceMenuAnchor={spaceMenuAnchor}
+ onSpaceMenu={setSpaceMenu}
+ />
+ ) : null}
+ {!collapsed && group.startSpaceId ? (
+
+
+
+ ) : null}
+ {!collapsed &&
+ group.bots.map((item) => (
+ {
+ setActiveNavPillar("projekte");
+ openSpaceChat(
+ item.chat.spaceId,
+ item.kind === "bot"
+ ? `/app/${item.chat.id}`
+ : `/app/g/${item.chat.id}`,
+ );
+ }}
+ activeSpaceId={bootstrapMe?.spaceId}
+ menuAnchor={botMenuAnchor}
+ onMenu={setBotMenu}
+ />
+ ))}
+
+
+ );
+ })}
+
+
+
-
+
{t`Talk to your Oos`}
@@ -3319,184 +3497,6 @@ export function ShellPage() {
);
})}
-
- {/* PROJEKTE SECTION */}
-
-
-
{t`Projects`}
-
- {t`Threads by topic · folders`}
-
-
-
-
-
-
- {projectSidebarGroups.length === 0 ? (
-
-
{t`No projects yet.`}
-
-
- ) : null}
-
- g.title).map((g) => g.spaceId)}
- strategy={verticalListSortingStrategy}
- >
- {projectSidebarGroups.map((group) => {
- const projectSpace = group.key.startsWith("project-space:")
- ? spaces.find((space) => space.id === group.spaceId)
- : undefined;
- if (projectSpace) {
- const threads = projectThreads(runActivity, {
- spaceId: projectSpace.id,
- });
- const projectSelected =
- activeNavPillar === "projekte" &&
- bootstrapMe?.spaceId === projectSpace.id;
- // Other chats in the project show only while it is open.
- const projectChats = projectSelected
- ? group.bots.filter((item) => item.chat.kind !== "main")
- : [];
- return (
-
- ({
- botId: b.id,
- name:
- b.name === "Chief" || b.name === "Projektkoordinator"
- ? "Projektmanager"
- : b.name,
- color: b.color || FALLBACK_BOT_COLOR,
- status: b.status,
- }))}
- hasMenu={
- projectSpace.canEdit === true || projectSpace.canDelete === true
- }
- selected={projectSelected}
- unread={group.bots.some((item) => item.chat.unread)}
- needsYou={threads.needsYou.length}
- running={threads.running.length > 0}
- onOpen={() => {
- setMobileSidebarOpen(false);
- void startProject(projectSpace.id);
- }}
- onMenu={(menu, anchor) => {
- spaceMenuAnchor.current = anchor;
- setSpaceMenu(menu);
- }}
- />
- {projectChats.length > 0 ? (
-
- {projectChats.map((item) => (
-
{
- setActiveNavPillar("projekte");
- openSpaceChat(item.chat.spaceId, `/app/g/${item.chat.id}`);
- }}
- activeSpaceId={bootstrapMe?.spaceId}
- menuAnchor={botMenuAnchor}
- onMenu={setBotMenu}
- />
- ))}
-
- ) : null}
-
- );
- }
- const collapsed =
- Boolean(group.title) && collapsedSidebarSections.has(group.key);
- return (
-
-
- {group.title ? (
-
void startProject(spaceId)}
- onToggle={toggleSidebarSection}
- sectionMenuAnchor={sectionMenuAnchor}
- onSectionMenu={setSectionMenu}
- spaceMenuAnchor={spaceMenuAnchor}
- onSpaceMenu={setSpaceMenu}
- />
- ) : null}
- {!collapsed && group.startSpaceId ? (
-
-
-
- ) : null}
- {!collapsed &&
- group.bots.map((item) => (
- {
- setActiveNavPillar("projekte");
- openSpaceChat(
- item.chat.spaceId,
- item.kind === "bot"
- ? `/app/${item.chat.id}`
- : `/app/g/${item.chat.id}`,
- );
- }}
- activeSpaceId={bootstrapMe?.spaceId}
- menuAnchor={botMenuAnchor}
- onMenu={setBotMenu}
- />
- ))}
-
-
- );
- })}
-
-
-
>
)}
@@ -4183,17 +4183,17 @@ export function ShellPage() {
setCanvasTab(tab);
if (tab === "browser") {
setPanel("browser");
- } else if (tab === "sandbox") {
- setPanel("computer");
- } else {
+ } else if (inProject && !dualSurface.isMobile && tab !== "sandbox") {
setPanel(null);
+ } else {
+ setPanel("computer");
}
}}
inProject={inProject}
/>
) : null}
- {inProject && !panel && effectiveTab === "overview" && activeSpace ? (
+ {inProject && panel !== "settings" && effectiveTab === "overview" && activeSpace ? (
setNewInquiryOpen(true)}
onEditProject={() => {
setLookTarget(activeSpace);
}}
/>
+ ) : !inProject &&
+ panel !== "settings" &&
+ effectiveTab === "overview" &&
+ (active ?? coordinatorBot) ? (
+
+
+ void setRoutineActive(routine, next)
+ }
+ onOpenRoutine={(routine) => {
+ setRoutineDraft(draftFromRoutine(routine));
+ setRoutineWebhookSecret(null);
+ setEditingRoutine(routine);
+ setRoutineError(null);
+ setPanel("routine");
+ }}
+ onAddRoutine={
+ active
+ ? () => {
+ setRoutineDraft(emptyRoutineDraft());
+ setRoutineWebhookSecret(null);
+ setEditingRoutine(null);
+ setRoutineError(null);
+ setPanel("routine");
+ }
+ : undefined
+ }
+ onStopRun={() => void stopRun()}
+ onOpenRun={(run) =>
+ openSpaceChat(
+ run.spaceId,
+ run.groupId ? `/app/g/${run.groupId}` : `/app/${run.botId}`,
+ )
+ }
+ onOpenSettings={() => setPanel("settings")}
+ />
+
) : panel === "browser" || (effectiveTab === "browser" && panel !== "settings") ? (
void;
+ onOpenBot?: (botId: string) => void;
onOpenNewInquiry?: () => void;
onEditProject?: () => void;
}) {
@@ -56,13 +61,16 @@ export function ProjectOverview({
// If not a project space and no runs, keep hidden
if (!isProjectSpace && visible.length === 0) return null;
- // Waiting threads are what the user came for, so they start open.
- const shown = open ?? (threads.needsYou.length > 0 ? "needsYou" : null);
+ // Waiting or actively running threads start open automatically so progress is visible right away.
+ const shown =
+ open ??
+ (threads.needsYou.length > 0 ? "needsYou" : threads.running.length > 0 ? "running" : null);
const current = visible.find((section) => section.id === shown);
const folderPath = space?.workspace?.path;
const gitRemote = space?.workspace?.git?.remoteUrl;
const goal = space?.goal;
+ const projectBots = space?.bots ?? [];
return (
+ ) : onEditProject ? (
+
) : null}
{folderPath || gitRemote ? (
@@ -139,6 +158,55 @@ export function ProjectOverview({
) : null}
) : null}
+
+ {projectBots.length > 0 ? (
+
+
+ Team
+
+
+ {projectBots.map((member) => {
+ const displayName =
+ member.name === "Chief" || member.name === "Projektkoordinator"
+ ? "Projektmanager"
+ : member.name;
+ const isWorking = Boolean(
+ member.status &&
+ member.status !== "idle" &&
+ member.status !== "ready" &&
+ member.status !== "done",
+ );
+ return (
+
+ );
+ })}
+
+
+ ) : null}
) : null}
@@ -168,7 +236,205 @@ export function ProjectOverview({
) : null}
+ ) : isProjectSpace ? (
+
+ No runs yet
+
) : null}
);
}
+
+/**
+ * 1:1 Agent overview in the side panel: agent identity & status, active and recent
+ * task runs for this specific agent, and its scheduled routines.
+ */
+export function AgentOverview({
+ bot,
+ running = false,
+ routines = [],
+ runningRoutineId,
+ togglingRoutineId,
+ onToggleRoutineActive,
+ onOpenRoutine,
+ onAddRoutine,
+ onStopRun,
+ onOpenRun,
+ onOpenSettings,
+}: {
+ bot: Bot;
+ running?: boolean;
+ routines?: readonly Routine[];
+ runningRoutineId?: string | null;
+ togglingRoutineId?: string | null;
+ onToggleRoutineActive?: (routine: Routine, active: boolean) => void;
+ onOpenRoutine?: (routine: Routine) => void;
+ onAddRoutine?: () => void;
+ onStopRun?: () => void;
+ onOpenRun: (run: RunActivityRow) => void;
+ onOpenSettings?: () => void;
+}) {
+ const { t } = useLingui();
+ const { active: allActiveRuns, recent: allRecentRuns } = useRunActivity();
+
+ const botActiveRuns = allActiveRuns.filter((run) => run.botId === bot.id);
+ const botRecentRuns = allRecentRuns.filter((run) => run.botId === bot.id);
+ const needsYouCount = botActiveRuns.filter((run) => isNeedsYouStatus(run.status)).length;
+ const isWorking = running || botActiveRuns.length > 0;
+
+ return (
+
+ {/* Agent Identity & Status Card */}
+
+
+
+
+
+ 0
+ ? "bg-warning"
+ : isWorking
+ ? "bg-warning animate-pulse"
+ : "bg-success",
+ )}
+ />
+
+
+
+
+ {bot.name}
+
+ {bot.title ? (
+
+ {bot.title}
+
+ ) : null}
+
+
+ {needsYouCount > 0 ? (
+
+ Needs you
+
+ ) : isWorking ? (
+
+ Working
+
+ ) : (
+
+ Ready
+
+ )}
+
+
+
+
+ {onOpenSettings ? (
+
+ ) : null}
+
+
+ {bot.description ? (
+
+ {bot.description}
+
+ ) : null}
+
+
+ {/* Agent Runs & Tasks */}
+
+
+
+ Run history
+
+
+
+ {botActiveRuns.length > 0 ? (
+
+
+ Now
+
+ {botActiveRuns.map((run) => (
+
onOpenRun(run)} />
+ ))}
+
+ ) : null}
+
+ {botRecentRuns.length > 0 ? (
+
+ {botActiveRuns.length > 0 ? (
+
+ Recent
+
+ ) : null}
+ {botRecentRuns.slice(0, 8).map((run) => (
+
onOpenRun(run)} />
+ ))}
+
+ ) : botActiveRuns.length === 0 ? (
+
+ No runs yet
+
+ ) : null}
+
+
+ {/* Scheduled Routines for this 1:1 Agent */}
+
+
+
+ Routines
+
+ {onAddRoutine ? (
+
+ ) : null}
+
+
+ {routines.length > 0 ? (
+
+ {routines.map((routine) => (
+ onToggleRoutineActive?.(routine, next)}
+ onOpen={() => onOpenRoutine?.(routine)}
+ onStop={() => onStopRun?.()}
+ />
+ ))}
+
+ ) : (
+
+
+
+ On a schedule
+
+
+ )}
+
+
+ );
+}
diff --git a/apps/web/src/pages/shell/side-panel-header.tsx b/apps/web/src/pages/shell/side-panel-header.tsx
index f2525b7..6c3cecc 100644
--- a/apps/web/src/pages/shell/side-panel-header.tsx
+++ b/apps/web/src/pages/shell/side-panel-header.tsx
@@ -42,8 +42,10 @@ export function SidePanelHeader({
{panel === "settings" ? (
Settings
- ) : inProject && (!panel || activeTab === "overview") ? (
+ ) : inProject && activeTab === "overview" ? (
Project overview
+ ) : !inProject && activeTab === "overview" ? (
+ Overview
) : (
Living Canvas
)}
@@ -112,33 +114,31 @@ export function SidePanelHeader({
{/* Surface Switcher Tabs */}
- {active && panel !== "settings" && onSelectTab ? (
+ {active && onSelectTab ? (
- {inProject ? (
-
- ) : null}
+
);
})}
- {(running || messages.some((m) => m.id.startsWith("progress:"))) && !encounterMode ? (
-
-
-
- ) : null}
{quoteDraft ? (