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:
1 parent
dfebced29f
commit
fd6471988a
13 files changed
+12
-230
No files matched your search
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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": "未知智能体",
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in new issue
Block a user