From c9df6edc63f171e1295828803b547dd8131951e7 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Sun, 4 Oct 2026 18:52:45 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9AAGC=20=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E6=A0=87=E9=A2=98=E6=A0=8F=E9=A1=B9=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=94=B9=E8=AE=A4=E5=BD=93=E5=89=8D=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=EF=BC=88#618=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 窗口标题栏标签主文案恒为当前项目名:WindowChromeActiveProjectRuns 新增 currentProjectName,由 WorkspaceLauncher 发布当前项目清单名,WindowChrome 透传给标题栏入口 - 正在运行的项目降级为圆点、运行中数量徽标与展开菜单:ActiveProjectRunsPanel 主文案不再取开始时间最晚的在跑项目 - 展开菜单里当前项目那一行沿用清单项目名,meta 首位标「当前」,aria-current 保留 - 运行快照读取失败不再整块替换标签:圆点转告警色,结论落在菜单内一行;既无在跑项目又无当前项目名时保留原来的纯提示 - 新增 .launcher-runs-panel-dot.is-warning 与 .launcher-runs-titlebar-menu-note 样式 - 用例:directActiveTurns 新增三条回归(别的项目在跑时标签仍是当前项目、当前项目自己跑时列表带「当前」、读取失败不顶掉当前项目名),WindowChrome 新增当前项目优先级用例,并把「只显示最后开始的项目」用例改名为「不在项目内时…」 - 文档:修正主规范 2026-09-15 段的标题栏条目、里程碑与实施计划文末修订,decision-log 追加 2026-10-04 决策 --- .../src/components/WindowChrome.tsx | 1 + .../src/components/windowChromeContext.ts | 5 + .../app-shell/ActiveProjectRunsPanel.tsx | 195 +++++++++++------- .../features/app-shell/WorkspaceLauncher.tsx | 2 + apps/ai-game-creator-shell/src/styles.css | 16 ++ .../tests/WindowChrome.test.tsx | 48 ++++- .../tests/directActiveTurns.test.tsx | 90 +++++++- ...ž合跨页面生命周期与运行中项目可见性-2026-09-15.md | 2 +- ...合跨页面生命周期与运行中项目可见性-2026-09-15.md | 13 +- .../shared-memory/decision-log.md | 10 + ...¹案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 +- 11 files changed, 302 insertions(+), 82 deletions(-) diff --git a/apps/ai-game-creator-shell/src/components/WindowChrome.tsx b/apps/ai-game-creator-shell/src/components/WindowChrome.tsx index 19d87059d..6801e0212 100644 --- a/apps/ai-game-creator-shell/src/components/WindowChrome.tsx +++ b/apps/ai-game-creator-shell/src/components/WindowChrome.tsx @@ -177,6 +177,7 @@ export function WindowChrome({ children }: WindowChromeProps) { void; }; diff --git a/apps/ai-game-creator-shell/src/features/app-shell/ActiveProjectRunsPanel.tsx b/apps/ai-game-creator-shell/src/features/app-shell/ActiveProjectRunsPanel.tsx index 1df87f2bb..66318de4d 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/ActiveProjectRunsPanel.tsx +++ b/apps/ai-game-creator-shell/src/features/app-shell/ActiveProjectRunsPanel.tsx @@ -6,15 +6,18 @@ import { projectNameFromPath } from '../agent-runtime'; import { projectPathsMatchForInvalidation } from '../project-summary/projectPath'; /** - * 窗口标题栏的"正在运行的项目"入口,也保留面板布局供独立组件测试和复用。 + * 窗口标题栏的项目入口,也保留面板布局供独立组件测试和复用。 * - * 数据来自 Rust 的活动回合注册表(同一个只读快照也用于重新进入项目时的进度重连), - * 面板只负责呈现:项目名、阶段、已运行时长,以及点击进入该项目。没有在跑回合时 - * 整块不渲染,不留空白占位。 + * 数据来自 Rust 的活动回合注册表(同一个只读快照也用于重新进入项目时的进度重连)。 + * 标题栏形态的主文案恒为**当前项目**:正在运行的项目只占圆点、数量徽标与展开菜单, + * 否则「我在哪个项目」会被后台别的项目顶掉;不在项目内(没有 `currentProjectName`)时 + * 才回落到最近启动的运行项目。没有在跑回合且没读失败时整块不渲染,不留空白占位。 */ export type ActiveProjectRunsPanelProps = { activeTurns: GameCreatorDirectActiveTurn[]; currentProjectPath?: string | null; + /** 当前工作台打开的项目名;有值即顶栏主文案,运行中的项目不再抢它。 */ + currentProjectName?: string | null; readFailed?: boolean; onOpenProject?: (projectPath: string) => void; placement?: 'panel' | 'titlebar'; @@ -58,6 +61,7 @@ function activeTurnDisplayName(turn: GameCreatorDirectActiveTurn) { export function ActiveProjectRunsPanel({ activeTurns, currentProjectPath = null, + currentProjectName = null, readFailed = false, onOpenProject, placement = 'panel', @@ -87,11 +91,26 @@ export function ActiveProjectRunsPanel({ }; }, [open, placement]); - if (activeTurns.length === 0) { - if (!readFailed) { + const now = Date.now(); + const orderedTurns = [...activeTurns].sort( + (left, right) => left.startedAt - right.startedAt, + ); + const latestTurn = orderedTurns[orderedTurns.length - 1] ?? null; + const runningCount = orderedTurns.length; + const currentName = currentProjectName?.trim() ?? ''; + /** + * 标签主文案只认当前项目:正在运行的项目不在标题栏抢主文案,只在徽标和展开菜单里表达。 + * 不在项目内(没有当前项目名)时才回落到最近启动的运行项目。 + */ + const primaryName = + currentName || (latestTurn ? activeTurnDisplayName(latestTurn) : ''); + + if (placement === 'titlebar') { + if (!latestTurn && !readFailed) { return null; } - if (placement === 'titlebar') { + // 读不到快照、又没有当前项目可显示:保持原来那条只读提示,不假装成可展开入口。 + if (!primaryName) { return ( ); } - // 三次都没读到快照:只说"没读到",不改写成业务、权限或审批结论。 - return ( - - ); - } - - const now = Date.now(); - const orderedTurns = [...activeTurns].sort( - (left, right) => left.startedAt - right.startedAt, - ); - if (placement === 'titlebar') { - const latestTurn = orderedTurns[orderedTurns.length - 1]; - if (!latestTurn) { - return null; - } - const latestName = activeTurnDisplayName(latestTurn); + const runningLabel = readFailed + ? '正在运行的项目读取失败' + : `正在运行 ${runningCount} 个项目`; + const triggerLabel = `${ + currentName + ? `当前项目:${primaryName}` + : `正在运行的项目:${primaryName}` + };${runningLabel}`; const openProject = (projectPath: string) => { setOpen(false); onOpenProject?.(projectPath); @@ -129,24 +136,26 @@ export function ActiveProjectRunsPanel({
- - ); - })} - + + {name} + + + {[ + isCurrent ? '当前' : null, + activeTurnStatusLabel(turn.status), + elapsed, + ] + .filter(Boolean) + .join(' · ')} + + + + ); + })} + + ) : null}
) : null} ); } + if (runningCount === 0) { + // 三次都没读到快照:只说"没读到",不改写成业务、权限或审批结论。 + return ( + + ); + } + return (