refactor(app): remove Console feature entirely

Per user request: the "显示控制台 / Show console" button in the top-right
header and the bottom drawer it opened are no longer needed — the Runs
section already renders the same log tail in-page via RunDetail, so the
drawer was a duplicate surface. Deleted the entire feature instead of
leaving a hidden toggle that attracts bugs.

Removed files:
- src/renderer/views/ConsoleView.tsx (126 LOC)

Removed renderer integration points:
- App.tsx: header "Show/Hide console" button, ConsoleView render,
  setConsoleOpen(true) call after startRun, TerminalWindow icon import
- views/sections/ExecutionSections.tsx: onOpenConsole callback,
  setConsoleOpen subscription
- components/runs/RunCenter.tsx: onOpenConsole prop (removed from
  forwarding chain)
- components/runs/RunDetail.tsx: onOpenConsole prop + the Lightning
  "Open console" ActionButton. The cancel-run button remains as the
  sole Panel action now that the console entry point is gone.
- hooks/useUIController.ts: consoleOpen / setConsoleOpen subscription
- hooks/useDesktopEvents.ts: "toggle-console" shortcut dispatcher
- state/slices/navigation.ts: consoleOpen field, setConsoleOpen,
  toggleConsole actions, and the two auto-open-on-navigate triggers
  that used to flip the drawer when jumping to a run

Removed main-process integration:
- main/desktop-shell-menu.ts: "Toggle Console" menu item + CmdOrCtrl+`
  accelerator
- main/desktop-shell-window.ts: onShortcut union type narrowed
- shared/types/events.ts: ShortcutEvent action union narrowed

Removed i18n keys (zh + en):
- app.hideConsole / app.showConsole
- console.title / console.noRuns / console.close
- common.openLog / common.noRunSelected / common.waitingForLog
- runs.openConsole

Log capture itself (logBufferRef in useCompanyController + run-log
events) is unchanged — the Runs section still displays it. Nothing
was moved to a "disabled but present" state; the feature is out of
the tree entirely. Typecheck and lint pass.
This commit is contained in:
ZacharyZhang-NY committed 2026-04-17 20:32:34 -04:00
1 parent dfebced29f
commit fd6471988a
13 files changed
+12 -230

No files matched your search

+1 -6
View File
@@ -18,7 +18,7 @@ export interface DesktopShellMenuDependencies {
isDev: boolean; isDev: boolean;
onCheckForUpdates: () => Promise<void>; onCheckForUpdates: () => Promise<void>;
onBackupProfile: () => Promise<void>; onBackupProfile: () => Promise<void>;
onShortcut: (action: "toggle-command-palette" | "toggle-console" | "refresh") => void; onShortcut: (action: "toggle-command-palette" | "refresh") => void;
getMainWindow: () => BrowserWindow | null; getMainWindow: () => BrowserWindow | null;
ensureWindow: () => Promise<BrowserWindow | null>; ensureWindow: () => Promise<BrowserWindow | null>;
requestQuitWithConfirmation: () => Promise<void>; requestQuitWithConfirmation: () => Promise<void>;
@@ -117,11 +117,6 @@ export function createDesktopShellMenu(deps: DesktopShellMenuDependencies) {
accelerator: "CmdOrCtrl+K", accelerator: "CmdOrCtrl+K",
click: () => onShortcut("toggle-command-palette"), click: () => onShortcut("toggle-command-palette"),
}, },
{
label: "Toggle Console",
accelerator: "CmdOrCtrl+`",
click: () => onShortcut("toggle-console"),
},
{ {
label: "Refresh Data", label: "Refresh Data",
accelerator: "CmdOrCtrl+R", accelerator: "CmdOrCtrl+R",
+1 -1
View File
@@ -27,7 +27,7 @@ export interface DesktopShellWindowDependencies {
onRequestQuit: () => void; onRequestQuit: () => void;
onCheckForUpdates: () => Promise<void>; onCheckForUpdates: () => Promise<void>;
onBackupProfile: () => Promise<void>; onBackupProfile: () => Promise<void>;
onShortcut: (action: "toggle-command-palette" | "toggle-console" | "refresh") => void; onShortcut: (action: "toggle-command-palette" | "refresh") => void;
notify: (options: NotificationOptions) => void; notify: (options: NotificationOptions) => void;
getActiveRunCount: () => number; getActiveRunCount: () => number;
} }
+1 -28
View File
@@ -1,5 +1,5 @@
import { useCallback, useMemo, type ReactNode } from "react"; import { useCallback, useMemo, type ReactNode } from "react";
import { CircleNotch, TerminalWindow } from "./components/ui/icons"; import { CircleNotch } from "./components/ui/icons";
import { LocaleContext, TranslationContext, type TranslationFn } from "./i18n"; import { LocaleContext, TranslationContext, type TranslationFn } from "./i18n";
import type { AppLocale } from "@shared/locale"; import type { AppLocale } from "@shared/locale";
import { CompanyOnboarding } from "./components/company-onboarding/OnboardingWizard"; import { CompanyOnboarding } from "./components/company-onboarding/OnboardingWizard";
@@ -7,7 +7,6 @@ import TitleBar from "./components/TitleBar";
import ToastContainer from "./components/Toast"; import ToastContainer from "./components/Toast";
import { SidebarView } from "./views/SidebarView"; import { SidebarView } from "./views/SidebarView";
import { SectionContentView } from "./views/SectionContentView"; import { SectionContentView } from "./views/SectionContentView";
import { ConsoleView } from "./views/ConsoleView";
import { CommandPaletteView } from "./views/CommandPaletteView"; import { CommandPaletteView } from "./views/CommandPaletteView";
import { useAppStore } from "./state/store"; import { useAppStore } from "./state/store";
import { useUIController } from "./hooks/useUIController"; import { useUIController } from "./hooks/useUIController";
@@ -49,7 +48,6 @@ export function App() {
const error = useAppStore((s) => s.error); const error = useAppStore((s) => s.error);
const setError = useAppStore((s) => s.setError); const setError = useAppStore((s) => s.setError);
const setSelectedRunId = useAppStore((s) => s.setSelectedRunId); const setSelectedRunId = useAppStore((s) => s.setSelectedRunId);
const setConsoleOpen = useAppStore((s) => s.setConsoleOpen);
const navigateTo = useAppStore((s) => s.navigateTo); const navigateTo = useAppStore((s) => s.navigateTo);
const showCustomTitleBar = window.agentCompany.platform !== "darwin"; const showCustomTitleBar = window.agentCompany.platform !== "darwin";
@@ -60,7 +58,6 @@ export function App() {
const result = await window.agentCompany.startTaskRun({ companyId: company.currentCompany.id, taskId }); const result = await window.agentCompany.startTaskRun({ companyId: company.currentCompany.id, taskId });
if (!result.ok) { setError(result.error.message); return; } if (!result.ok) { setError(result.error.message); return; }
setSelectedRunId(result.data); setSelectedRunId(result.data);
setConsoleOpen(true);
await company.refresh(); await company.refresh();
// eslint-disable-next-line react-hooks/exhaustive-deps -- Zustand setters and company.refresh are stable // eslint-disable-next-line react-hooks/exhaustive-deps -- Zustand setters and company.refresh are stable
}, [company.currentCompany, company.refresh]); }, [company.currentCompany, company.refresh]);
@@ -189,18 +186,6 @@ export function App() {
<h1 className="text-[28px] font-bold tracking-[-0.02em] text-foreground"> <h1 className="text-[28px] font-bold tracking-[-0.02em] text-foreground">
{ui.navItems.find((item) => item.id === ui.section)?.label ?? ui.t(`nav.${ui.section}`)} {ui.navItems.find((item) => item.id === ui.section)?.label ?? ui.t(`nav.${ui.section}`)}
</h1> </h1>
<div className="flex shrink-0 items-center gap-3 lg:gap-5">
<button
type="button"
aria-label={ui.consoleOpen ? ui.t("app.hideConsole") : ui.t("app.showConsole")}
title={ui.consoleOpen ? ui.t("app.hideConsole") : ui.t("app.showConsole")}
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 inline-flex items-center gap-2 py-1 text-[12px] font-semibold text-muted-foreground transition hover:text-foreground"
onClick={() => ui.setConsoleOpen(!ui.consoleOpen)}
>
<TerminalWindow size={16} />
<span className="hidden lg:inline">{ui.consoleOpen ? ui.t("app.hideConsole") : ui.t("app.showConsole")}</span>
</button>
</div>
</div> </div>
</header> </header>
@@ -209,18 +194,6 @@ export function App() {
error={error} error={error}
pageTransition={ui.pageTransition} pageTransition={ui.pageTransition}
/> />
<ConsoleView
consoleOpen={ui.consoleOpen}
setConsoleOpen={ui.setConsoleOpen}
selectedRun={company.selectedRun}
setSelectedRunId={setSelectedRunId}
runs={company.runs}
logBufferRef={company.logBufferRef}
openPathWithFeedback={company.openPathWithFeedback}
panelTransition={ui.panelTransition}
t={ui.t}
/>
</main> </main>
<CommandPaletteView <CommandPaletteView
@@ -15,7 +15,6 @@ interface RunCenterProps {
onSelectRun: (runId: string) => void; onSelectRun: (runId: string) => void;
onCancelRun: (runId: string) => Promise<void> | void; onCancelRun: (runId: string) => Promise<void> | void;
onOpenLogPath: (path: string) => Promise<void> | void; onOpenLogPath: (path: string) => Promise<void> | void;
onOpenConsole: (runId: string) => void;
} }
export function RunCenter({ export function RunCenter({
@@ -27,7 +26,6 @@ export function RunCenter({
onSelectRun, onSelectRun,
onCancelRun, onCancelRun,
onOpenLogPath, onOpenLogPath,
onOpenConsole,
}: RunCenterProps) { }: RunCenterProps) {
const t = useT(); const t = useT();
const [statusFilter, setStatusFilter] = useState<"all" | RunRecord["status"]>("all"); const [statusFilter, setStatusFilter] = useState<"all" | RunRecord["status"]>("all");
@@ -109,7 +107,6 @@ export function RunCenter({
agentMap={agentMap} agentMap={agentMap}
workspaceMap={workspaceMap} workspaceMap={workspaceMap}
logState={logState} logState={logState}
onOpenConsole={onOpenConsole}
onCancelRun={onCancelRun} onCancelRun={onCancelRun}
onOpenLogPath={onOpenLogPath} onOpenLogPath={onOpenLogPath}
onReloadLog={reloadLogs} onReloadLog={reloadLogs}
+8 -18
View File
@@ -1,7 +1,6 @@
import { import {
ArrowClockwise, ArrowClockwise,
FolderOpen, FolderOpen,
Lightning,
ListChecks, ListChecks,
WarningCircle, WarningCircle,
} from "../ui/icons"; } from "../ui/icons";
@@ -18,7 +17,6 @@ interface RunDetailProps {
agentMap: Map<string, AgentRecord>; agentMap: Map<string, AgentRecord>;
workspaceMap: Map<string, WorkspaceRecord>; workspaceMap: Map<string, WorkspaceRecord>;
logState: RunLogState; logState: RunLogState;
onOpenConsole: (runId: string) => void;
onCancelRun: (runId: string) => Promise<void> | void; onCancelRun: (runId: string) => Promise<void> | void;
onOpenLogPath: (path: string) => Promise<void> | void; onOpenLogPath: (path: string) => Promise<void> | void;
onReloadLog: () => void; onReloadLog: () => void;
@@ -40,7 +38,6 @@ export function RunDetail({
taskMap, taskMap,
agentMap, agentMap,
logState, logState,
onOpenConsole,
onCancelRun, onCancelRun,
onOpenLogPath, onOpenLogPath,
onReloadLog, onReloadLog,
@@ -54,23 +51,16 @@ export function RunDetail({
<div className="space-y-6"> <div className="space-y-6">
<Panel <Panel
title={task?.title ?? selectedRun.summary ?? t("runs.runDetail")} title={task?.title ?? selectedRun.summary ?? t("runs.runDetail")}
action={( action={
<div className="flex items-center gap-2"> isActiveStatus(selectedRun.status) ? (
<ActionButton <ActionButton
label={t("runs.openConsole")} label={t("runs.cancelRun")}
icon={Lightning} tone="danger"
onClick={() => onOpenConsole(selectedRun.id)} icon={WarningCircle}
onClick={() => void onCancelRun(selectedRun.id)}
/> />
{isActiveStatus(selectedRun.status) ? ( ) : undefined
<ActionButton }
label={t("runs.cancelRun")}
tone="danger"
icon={WarningCircle}
onClick={() => void onCancelRun(selectedRun.id)}
/>
) : null}
</div>
)}
> >
<div className="divide-y divide-[color:var(--border)]"> <div className="divide-y divide-[color:var(--border)]">
<DetailRow label="Status"><StatusPill status={selectedRun.status} /></DetailRow> <DetailRow label="Status"><StatusPill status={selectedRun.status} /></DetailRow>
-1
View File
@@ -141,7 +141,6 @@ export function useDesktopEvents(
} }
if (event.type === "shortcut") { if (event.type === "shortcut") {
if (event.action === "toggle-command-palette") store.getState().toggleCommandPalette(); if (event.action === "toggle-command-palette") store.getState().toggleCommandPalette();
if (event.action === "toggle-console") store.getState().toggleConsole();
if (event.action === "refresh") void refreshRef.current(); if (event.action === "refresh") void refreshRef.current();
return; return;
} }
-7
View File
@@ -79,7 +79,6 @@ export function useUIController() {
const { const {
section, section,
consoleOpen,
sidebarCollapsed, sidebarCollapsed,
companySwitcherOpen, companySwitcherOpen,
confirmDeleteCompanyId, confirmDeleteCompanyId,
@@ -92,7 +91,6 @@ export function useUIController() {
} = useAppStore( } = useAppStore(
useShallow((s) => ({ useShallow((s) => ({
section: s.section, section: s.section,
consoleOpen: s.consoleOpen,
sidebarCollapsed: s.sidebarCollapsed, sidebarCollapsed: s.sidebarCollapsed,
companySwitcherOpen: s.companySwitcherOpen, companySwitcherOpen: s.companySwitcherOpen,
confirmDeleteCompanyId: s.confirmDeleteCompanyId, confirmDeleteCompanyId: s.confirmDeleteCompanyId,
@@ -106,7 +104,6 @@ export function useUIController() {
); );
const { const {
setSection, setSection,
setConsoleOpen,
setSidebarCollapsed, setSidebarCollapsed,
setCompanySwitcherOpen, setCompanySwitcherOpen,
setConfirmDeleteCompanyId, setConfirmDeleteCompanyId,
@@ -189,8 +186,6 @@ export function useUIController() {
navItems, navItems,
section, section,
setSection, setSection,
consoleOpen,
setConsoleOpen,
sidebarCollapsed, sidebarCollapsed,
setSidebarCollapsed, setSidebarCollapsed,
companySwitcherOpen, companySwitcherOpen,
@@ -225,8 +220,6 @@ export function useUIController() {
navItems, navItems,
section, section,
setSection, setSection,
consoleOpen,
setConsoleOpen,
sidebarCollapsed, sidebarCollapsed,
setSidebarCollapsed, setSidebarCollapsed,
companySwitcherOpen, companySwitcherOpen,
-9
View File
@@ -41,8 +41,6 @@ export const en: Record<string, string> = {
"app.commandPalette": "Command palette", "app.commandPalette": "Command palette",
"app.refresh": "Refresh", "app.refresh": "Refresh",
"app.console": "Console", "app.console": "Console",
"app.hideConsole": "Hide console",
"app.showConsole": "Show console",
"app.collapse": "Collapse", "app.collapse": "Collapse",
"app.expand": "Expand", "app.expand": "Expand",
"app.noResults": "No results for", "app.noResults": "No results for",
@@ -296,9 +294,6 @@ export const en: Record<string, string> = {
"common.started": "Started", "common.started": "Started",
"common.finished": "Finished", "common.finished": "Finished",
"common.cost": "Cost", "common.cost": "Cost",
"common.openLog": "Open log",
"common.noRunSelected": "No run selected",
"common.waitingForLog": "Waiting for log output...",
"common.sections": "Sections", "common.sections": "Sections",
"common.agents": "Agents", "common.agents": "Agents",
"common.tasks": "Tasks", "common.tasks": "Tasks",
@@ -307,9 +302,6 @@ export const en: Record<string, string> = {
"common.projects": "Projects", "common.projects": "Projects",
// ── Console panel ── // ── Console panel ──
"console.title": "Console",
"console.noRuns": "No runs",
"console.close": "Close console",
// ── Entity labels (command palette sections) ── // ── Entity labels (command palette sections) ──
"entity.sections": "Sections", "entity.sections": "Sections",
@@ -795,7 +787,6 @@ export const en: Record<string, string> = {
"runs.noRunsMatch": "No runs match this filter", "runs.noRunsMatch": "No runs match this filter",
"runs.noRunsMatchDetail": "Try another filter.", "runs.noRunsMatchDetail": "Try another filter.",
"runs.runDetail": "Run detail", "runs.runDetail": "Run detail",
"runs.openConsole": "Open console",
"runs.cancelRun": "Cancel run", "runs.cancelRun": "Cancel run",
"runs.noSummary": "No summary yet", "runs.noSummary": "No summary yet",
"runs.unknownAgent": "Unknown agent", "runs.unknownAgent": "Unknown agent",
-9
View File
@@ -41,8 +41,6 @@ export const zh: Record<string, string> = {
"app.commandPalette": "命令面板", "app.commandPalette": "命令面板",
"app.refresh": "刷新", "app.refresh": "刷新",
"app.console": "控制台", "app.console": "控制台",
"app.hideConsole": "隐藏控制台",
"app.showConsole": "显示控制台",
"app.collapse": "收起", "app.collapse": "收起",
"app.expand": "展开", "app.expand": "展开",
"app.noResults": "未找到匹配结果", "app.noResults": "未找到匹配结果",
@@ -296,9 +294,6 @@ export const zh: Record<string, string> = {
"common.started": "开始时间", "common.started": "开始时间",
"common.finished": "结束时间", "common.finished": "结束时间",
"common.cost": "费用", "common.cost": "费用",
"common.openLog": "打开日志",
"common.noRunSelected": "未选择运行",
"common.waitingForLog": "等待日志输出...",
"common.sections": "版块", "common.sections": "版块",
"common.agents": "智能体", "common.agents": "智能体",
"common.tasks": "任务", "common.tasks": "任务",
@@ -307,9 +302,6 @@ export const zh: Record<string, string> = {
"common.projects": "项目", "common.projects": "项目",
// ── 控制台面板 ── // ── 控制台面板 ──
"console.title": "控制台",
"console.noRuns": "暂无运行",
"console.close": "关闭控制台",
// ── 实体标签(命令面板分类) ── // ── 实体标签(命令面板分类) ──
"entity.sections": "版块", "entity.sections": "版块",
@@ -795,7 +787,6 @@ export const zh: Record<string, string> = {
"runs.noRunsMatch": "没有匹配此筛选条件的运行", "runs.noRunsMatch": "没有匹配此筛选条件的运行",
"runs.noRunsMatchDetail": "换个筛选试试。", "runs.noRunsMatchDetail": "换个筛选试试。",
"runs.runDetail": "运行详情", "runs.runDetail": "运行详情",
"runs.openConsole": "打开控制台",
"runs.cancelRun": "取消运行", "runs.cancelRun": "取消运行",
"runs.noSummary": "暂无摘要", "runs.noSummary": "暂无摘要",
"runs.unknownAgent": "未知智能体", "runs.unknownAgent": "未知智能体",
-14
View File
@@ -7,7 +7,6 @@ export interface NavigationSlice {
commandPaletteOpen: boolean; commandPaletteOpen: boolean;
commandPaletteSearch: string; commandPaletteSearch: string;
selectedPaletteIndex: number; selectedPaletteIndex: number;
consoleOpen: boolean;
sidebarCollapsed: boolean; sidebarCollapsed: boolean;
companySwitcherOpen: boolean; companySwitcherOpen: boolean;
setSection: (section: SectionId) => void; setSection: (section: SectionId) => void;
@@ -15,8 +14,6 @@ export interface NavigationSlice {
toggleCommandPalette: () => void; toggleCommandPalette: () => void;
setCommandPaletteSearch: (search: string) => void; setCommandPaletteSearch: (search: string) => void;
setSelectedPaletteIndex: (index: number) => void; setSelectedPaletteIndex: (index: number) => void;
setConsoleOpen: (open: boolean) => void;
toggleConsole: () => void;
setSidebarCollapsed: (collapsed: boolean) => void; setSidebarCollapsed: (collapsed: boolean) => void;
toggleSidebar: () => void; toggleSidebar: () => void;
setCompanySwitcherOpen: (open: boolean) => void; setCompanySwitcherOpen: (open: boolean) => void;
@@ -34,7 +31,6 @@ export const createNavigationSlice: StateCreator<
commandPaletteOpen: false, commandPaletteOpen: false,
commandPaletteSearch: "", commandPaletteSearch: "",
selectedPaletteIndex: 0, selectedPaletteIndex: 0,
consoleOpen: false,
sidebarCollapsed: false, sidebarCollapsed: false,
companySwitcherOpen: false, companySwitcherOpen: false,
setSection: (section) => setSection: (section) =>
@@ -66,14 +62,6 @@ export const createNavigationSlice: StateCreator<
set((state) => { set((state) => {
state.selectedPaletteIndex = index; state.selectedPaletteIndex = index;
}), }),
setConsoleOpen: (open) =>
set((state) => {
state.consoleOpen = open;
}),
toggleConsole: () =>
set((state) => {
state.consoleOpen = !state.consoleOpen;
}),
setSidebarCollapsed: (collapsed) => setSidebarCollapsed: (collapsed) =>
set((state) => { set((state) => {
state.sidebarCollapsed = collapsed; state.sidebarCollapsed = collapsed;
@@ -98,7 +86,6 @@ export const createNavigationSlice: StateCreator<
else if (section === "approvals") state.selectedApprovalId = entityId; else if (section === "approvals") state.selectedApprovalId = entityId;
else if (section === "runs") { else if (section === "runs") {
state.selectedRunId = entityId; state.selectedRunId = entityId;
state.consoleOpen = true;
} else if (section === "goals") state.selectedGoalId = entityId; } else if (section === "goals") state.selectedGoalId = entityId;
else if (section === "projects") state.selectedProjectId = entityId; else if (section === "projects") state.selectedProjectId = entityId;
else if (section === "connectors") state.selectedConnectorId = entityId; else if (section === "connectors") state.selectedConnectorId = entityId;
@@ -116,7 +103,6 @@ export const createNavigationSlice: StateCreator<
if (matchingRun) { if (matchingRun) {
state.selectedRunId = matchingRun.id; state.selectedRunId = matchingRun.id;
state.selectedTaskId = matchingRun.taskId; state.selectedTaskId = matchingRun.taskId;
state.consoleOpen = true;
return; return;
} }
state.selectedTaskId = entityId; state.selectedTaskId = entityId;
-128
View File
@@ -1,128 +0,0 @@
import { useMemo } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { FolderOpen, TerminalWindow, X } from "../components/ui/icons";
import type { RunRecord } from "@shared/types";
import { ActionButton, Select, StatusPill } from "../components/ui";
import { formatMoney, formatTime } from "../lib/formatters";
import { useAppStore } from "../state/store";
// Run summaries that indicate the run is no longer active; these entries
// should not appear in the console run selector.
const SHUTDOWN_SUMMARY = "Application shut down.";
const WORKER_CRASH_SUMMARY = "Worker process crashed.";
interface ConsoleViewProps {
consoleOpen: boolean;
setConsoleOpen: (value: boolean) => void;
selectedRun: RunRecord | null;
setSelectedRunId: (id: string | null) => void;
runs: RunRecord[];
logBufferRef: React.RefObject<Record<string, string>>;
openPathWithFeedback: (path: string) => Promise<void>;
panelTransition: { duration: number; ease?: readonly [number, number, number, number] } | { duration: number };
t: (key: string) => string;
}
export function ConsoleView({
consoleOpen,
setConsoleOpen,
selectedRun,
setSelectedRunId,
runs,
logBufferRef,
openPathWithFeedback,
panelTransition,
t,
}: ConsoleViewProps) {
const logRevision = useAppStore((s) => s.logRevision);
const logBuffer = useMemo(
() => (selectedRun ? logBufferRef.current[selectedRun.id] ?? "" : ""),
// logRevision is intentionally part of deps: it bumps on every run-log
// push into the ref, forcing this memo (and this component) to re-evaluate
// the ref contents on re-render.
// eslint-disable-next-line react-hooks/exhaustive-deps
[selectedRun, logRevision, logBufferRef],
);
const runSelectOptions = useMemo(() => {
const activeRuns = runs.filter(
(run) => run.summary !== SHUTDOWN_SUMMARY && run.summary !== WORKER_CRASH_SUMMARY,
);
return activeRuns.length > 0
? activeRuns.map((run) => ({ value: run.id, label: run.summary || `Run ${run.id.slice(0, 8)}` }))
: [{ value: "", label: t("console.noRuns") }];
}, [runs, t]);
return (
<AnimatePresence>
{consoleOpen ? (
<motion.section
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={panelTransition}
className="shrink-0 overflow-hidden border-t border-border bg-card"
>
<div className="flex items-center justify-between px-5 py-2.5">
<div className="flex items-center gap-3">
<TerminalWindow size={15} className="text-muted-foreground" />
<span className="text-[13px] font-semibold text-foreground">{t("console.title")}</span>
</div>
<div className="flex items-center gap-3">
<Select
compact
value={selectedRun?.id ?? ""}
onChange={setSelectedRunId}
options={runSelectOptions}
/>
<button
type="button"
className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-lg p-1 text-muted-foreground transition hover:text-foreground"
onClick={() => setConsoleOpen(false)}
aria-label={t("console.close")}
>
<X size={14} />
</button>
</div>
</div>
{selectedRun ? (
<div className="grid border-t border-border xl:grid-cols-[240px_minmax(0,1fr)]">
<div className="border-b border-border px-5 py-3 xl:border-b-0 xl:border-r">
<div className="space-y-2.5 text-[12px] text-muted-foreground">
<div className="flex items-center justify-between">
<span>{t("common.status")}</span>
<StatusPill status={selectedRun.status} />
</div>
<div className="flex items-center justify-between">
<span>{t("common.started")}</span>
<span className="font-mono text-[11px] text-foreground">{formatTime(selectedRun.startedAt)}</span>
</div>
<div className="flex items-center justify-between">
<span>{t("common.finished")}</span>
<span className="font-mono text-[11px] text-foreground">{formatTime(selectedRun.finishedAt)}</span>
</div>
<div className="flex items-center justify-between">
<span>{t("common.cost")}</span>
<span className="font-mono text-[11px] text-foreground">{formatMoney(selectedRun.costUsd)}</span>
</div>
{selectedRun.logPath ? (
<div className="pt-1">
<ActionButton label={t("common.openLog")} onClick={() => { const p = selectedRun.logPath; if (p) void openPathWithFeedback(p); }} icon={FolderOpen} />
</div>
) : null}
</div>
</div>
<div className="h-[200px] overflow-auto px-5 py-3">
<pre className="font-mono whitespace-pre-wrap break-all text-[12px] leading-relaxed text-foreground/90">
{logBuffer || t("common.waitingForLog")}
</pre>
</div>
</div>
) : (
<div className="flex h-[200px] items-center justify-center border-t border-border">
<div className="text-center text-[12px] text-muted-foreground">{t("common.noRunSelected")}</div>
</div>
)}
</motion.section>
) : null}
</AnimatePresence>
);
}
@@ -90,7 +90,6 @@ export function ExecutionSections({ section }: ExecutionSectionsProps) {
const taskViewMode = useAppStore((s) => s.taskViewMode); const taskViewMode = useAppStore((s) => s.taskViewMode);
const setTaskViewMode = useAppStore((s) => s.setTaskViewMode); const setTaskViewMode = useAppStore((s) => s.setTaskViewMode);
const setSection = useAppStore((s) => s.setSection); const setSection = useAppStore((s) => s.setSection);
const setConsoleOpen = useAppStore((s) => s.setConsoleOpen);
const setSelectedCommunicationEntityId = useAppStore((s) => s.setSelectedCommunicationEntityId); const setSelectedCommunicationEntityId = useAppStore((s) => s.setSelectedCommunicationEntityId);
const [costsTab, setCostsTab] = useState<"costs" | "policies" | "incidents">("costs"); const [costsTab, setCostsTab] = useState<"costs" | "policies" | "incidents">("costs");
@@ -113,10 +112,6 @@ export function ExecutionSections({ section }: ExecutionSectionsProps) {
await openPathWithFeedback(path); await openPathWithFeedback(path);
}} }}
onCancelRun={cancelRun} onCancelRun={cancelRun}
onOpenConsole={(runId: string) => {
setSelectedRunId(runId);
setConsoleOpen(true);
}}
/> />
) : null} ) : null}
+1 -1
View File
@@ -15,7 +15,7 @@ export interface DomainChangedEvent {
export interface ShortcutEvent { export interface ShortcutEvent {
type: "shortcut"; type: "shortcut";
action: "toggle-command-palette" | "toggle-console" | "refresh"; action: "toggle-command-palette" | "refresh";
} }
export interface RunStatusEvent { export interface RunStatusEvent {