diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index 014217e10..ec6f00905 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -584,13 +584,15 @@ export function App({ onMakeGameFromApprovedGdd, }: AppProps = {}) { const { setTitle: setWindowTitle } = useWindowChrome(); - const [planningV2Active, setPlanningV2Active] = useState(planningStartMode); - const planningV2ActiveRef = useRef(planningStartMode); + const [planningV2Active, setPlanningV2Active] = useState(false); + const planningV2ActiveRef = useRef(false); + const [designAgentActive, setDesignAgentActive] = useState(planningStartMode); + const designAgentActiveRef = useRef(planningStartMode); planningV2ActiveRef.current = planningV2Active; + designAgentActiveRef.current = designAgentActive; const [designAgentView, setDesignAgentView] = useState( null, ); - const designAgentLaneRef = useRef(planningStartMode); const designAgentTurnRef = useRef<{ projectPath: string; clientTurnId: string; @@ -609,7 +611,9 @@ export function App({ projectSupervisorOnly && !supervisorChatOnly && !planningStartMode && - !planningV2Active; + !planningV2Active && + !designAgentActive; + const planningLaneActive = planningV2Active || designAgentActive; const [devMode] = useState(() => projectSupervisorOnly ? false : isDeveloperMode(), ); @@ -826,12 +830,16 @@ export function App({ useState(''); const planningV2TransientReplyTargetRef = useRef(''); const planningV2VisibleReplyRef = useRef(''); + const [designAgentTransientReply, setDesignAgentTransientReplyVisible] = + useState(''); + const designAgentTransientReplyTargetRef = useRef(''); + const designAgentVisibleReplyRef = useRef(''); const designAgentPendingViewRef = useRef<{ clientTurnId: string; projectPath: string; view: DesignView; } | null>(null); - const [planningV2Reasoning, setPlanningV2Reasoning] = useState(''); + const [designAgentReasoning, setDesignAgentReasoning] = useState(''); const designAgentReasoningTurnRef = useRef<{ projectPath: string; clientTurnId: string; @@ -864,6 +872,14 @@ export function App({ } } + function setDesignAgentTransientReplyTarget(next: string) { + designAgentTransientReplyTargetRef.current = next; + if (!next) { + designAgentVisibleReplyRef.current = ''; + setDesignAgentTransientReplyVisible(''); + } + } + function designAgentEventSubscriptionReady() { // 业务动作只读取当前订阅代次;清理与下一次 effect 建立之间不能创建 // 一个没有订阅 effect 接管的悬挂 promise。 @@ -909,12 +925,37 @@ export function App({ return () => window.clearInterval(timer); }, []); + useEffect(() => { + const timer = window.setInterval(() => { + const target = designAgentTransientReplyTargetRef.current; + setDesignAgentTransientReplyVisible((current) => { + if (!target) { + designAgentVisibleReplyRef.current = ''; + return ''; + } + const prefix = target.startsWith(current) ? current : ''; + if (prefix === target) { + designAgentVisibleReplyRef.current = target; + return target; + } + const remaining = target.length - prefix.length; + const step = remaining > 160 ? 4 : remaining > 48 ? 2 : 1; + const next = target.slice(0, prefix.length + step); + designAgentVisibleReplyRef.current = next; + return next; + }); + }, 50); + return () => window.clearInterval(timer); + }, []); + function applyPlanningV2CommandResult( result: PlanningSessionCommandResultV2, clientTurnId?: string, ) { planningV2ActiveRef.current = true; setPlanningV2Active(true); + designAgentActiveRef.current = false; + setDesignAgentActive(false); planningV2SessionRef.current = result; setPlanningV2Session(result); projectSupervisorSessionIdRef.current = result.session.sessionId; @@ -1004,13 +1045,15 @@ export function App({ setPlanningV2Session(null); setPlanGddState(null); if (planningStartMode) { - planningV2ActiveRef.current = true; - setPlanningV2Active(true); - designAgentLaneRef.current = true; + planningV2ActiveRef.current = false; + setPlanningV2Active(false); + designAgentActiveRef.current = true; + setDesignAgentActive(true); } return null; } - designAgentLaneRef.current = false; + designAgentActiveRef.current = false; + setDesignAgentActive(false); setDesignAgentView(null); applyPlanningV2CommandResult(result); return result; @@ -1039,10 +1082,9 @@ export function App({ } function applyDesignView(view: DesignView, projectPath: string) { - designAgentLaneRef.current = true; + designAgentActiveRef.current = true; + setDesignAgentActive(true); setDesignAgentView(view); - planningV2ActiveRef.current = true; - setPlanningV2Active(true); setPlanGddError(view.session.lastError); setChatAgentBusy(view.running); const conversation = designMessagesToChat(view); @@ -1056,8 +1098,8 @@ export function App({ const pendingTurnId = designAgentPendingViewRef.current?.clientTurnId; designAgentPendingViewRef.current = null; applyDesignView(view, projectPath); - setPlanningV2Reasoning(''); - setPlanningV2TransientReplyTarget(''); + setDesignAgentReasoning(''); + setDesignAgentTransientReplyTarget(''); if (designAgentTurnRef.current?.clientTurnId === pendingTurnId) { designAgentTurnRef.current = null; } @@ -1068,7 +1110,7 @@ export function App({ projectPath: string, clientTurnId: string, ) { - let target = planningV2TransientReplyTargetRef.current; + let target = designAgentTransientReplyTargetRef.current; const tracked = designAgentTurnRef.current; if (!target.trim() && !view.running) { const latestAssistantText = [...view.messages] @@ -1076,7 +1118,7 @@ export function App({ .find((message) => message.role !== 'user' && message.text.trim()) ?.text.trim(); if (latestAssistantText) { - setPlanningV2TransientReplyTarget(latestAssistantText); + setDesignAgentTransientReplyTarget(latestAssistantText); target = latestAssistantText; } } @@ -1084,7 +1126,7 @@ export function App({ !view.running && tracked?.clientTurnId === clientTurnId && target.trim() && - planningV2VisibleReplyRef.current !== target + designAgentVisibleReplyRef.current !== target ) { designAgentPendingViewRef.current = { clientTurnId, @@ -1102,8 +1144,8 @@ export function App({ if (!pending) { return; } - const target = planningV2TransientReplyTargetRef.current; - if (target && planningV2VisibleReplyRef.current !== target) { + const target = designAgentTransientReplyTargetRef.current; + if (target && designAgentVisibleReplyRef.current !== target) { return; } commitDesignAgentView(pending.view, pending.projectPath); @@ -1155,8 +1197,8 @@ export function App({ await designAgentEventSubscriptionReady(); setChatAgentBusy(true); setProjectSupervisorRuntimeError(''); - setPlanningV2TransientReplyTarget(''); - setPlanningV2Reasoning(''); + setDesignAgentTransientReplyTarget(''); + setDesignAgentReasoning(''); try { const view = await invoke('continue_design_agent_session', { projectPath: nextProjectPath, @@ -1180,7 +1222,7 @@ export function App({ } finally { if (!designAgentPendingViewRef.current) { designAgentTurnRef.current = null; - setPlanningV2TransientReplyTarget(''); + setDesignAgentTransientReplyTarget(''); } setChatAgentBusy(false); } @@ -1642,10 +1684,11 @@ export function App({ setPlanningV2TransientReplyTarget(''); designAgentPendingViewRef.current = null; designAgentReasoningTurnRef.current = null; - setPlanningV2Reasoning(''); - setPlanningV2Active(planningStartMode); - planningV2ActiveRef.current = planningStartMode; - designAgentLaneRef.current = planningStartMode; + setDesignAgentReasoning(''); + setPlanningV2Active(false); + planningV2ActiveRef.current = false; + setDesignAgentActive(planningStartMode); + designAgentActiveRef.current = planningStartMode; designAgentTurnRef.current = null; setDesignAgentView(null); } @@ -1912,7 +1955,7 @@ export function App({ if ( !canSubscribeTauriEvents() || directCodexProductRuntime || - planningV2Active + planningLaneActive ) { return; } @@ -2015,7 +2058,7 @@ export function App({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [ directCodexProductRuntime, - planningV2Active, + planningLaneActive, refreshManifest, updateProjectSupervisorResponseStream, updateProjectSupervisorRuntime, @@ -2023,7 +2066,7 @@ export function App({ useEffect(() => { const ready = createDesignAgentEventSubscriptionReady(); - if (!canSubscribeTauriEvents() || !planningV2Active) { + if (!canSubscribeTauriEvents() || !designAgentActive) { resolveDesignAgentEventSubscriptionReady(); return () => { if (designAgentEventSubscriptionReadyRef.current === ready) { @@ -2044,7 +2087,7 @@ export function App({ return; } if (payload.kind === 'text' && payload.text != null) { - setPlanningV2TransientReplyTarget(payload.text); + setDesignAgentTransientReplyTarget(payload.text); } if (payload.reasoningText != null) { const reasoningTurn = designAgentReasoningTurnRef.current; @@ -2054,11 +2097,11 @@ export function App({ reasoningTurn.clientTurnId === payload.clientTurnId ) { reasoningTurn.text = payload.reasoningText; - setPlanningV2Reasoning(payload.reasoningText); + setDesignAgentReasoning(payload.reasoningText); } } if (payload.kind === 'tool' && payload.text) { - setPlanningV2TransientReplyTarget(payload.text); + setDesignAgentTransientReplyTarget(payload.text); } if (payload.view) { applyDesignAgentViewAfterTransient( @@ -2091,7 +2134,7 @@ export function App({ // applyDesignView 读的是 refs 和当前项目路径, // 把它写进依赖会在每轮回复时重订事件。 // eslint-disable-next-line react-hooks/exhaustive-deps - }, [planningV2Active]); + }, [designAgentActive]); useEffect(() => { if (!canSubscribeTauriEvents()) { @@ -2140,7 +2183,7 @@ export function App({ if ( !invoke || directCodexProductRuntime || - planningV2Active || + planningLaneActive || !nextProjectPath || !sessionId || !trackedRunId || @@ -2236,7 +2279,7 @@ export function App({ }, [ localProject?.projectPath, directCodexProductRuntime, - planningV2Active, + planningLaneActive, projectSupervisorSessionId, projectSupervisorRuntime?.phase, projectSupervisorRuntime?.runId, @@ -2251,7 +2294,7 @@ export function App({ if ( !projectSupervisorOnly || directCodexProductRuntime || - planningV2Active || + planningLaneActive || !invoke || !nextProjectPath || !supervisorRunId @@ -2332,7 +2375,7 @@ export function App({ }, [ localProject?.projectPath, directCodexProductRuntime, - planningV2Active, + planningLaneActive, projectSupervisorOnly, projectSupervisorRuntime?.phase, projectSupervisorRuntime?.runId, @@ -2344,7 +2387,7 @@ export function App({ (!supervisorChatOnly && !( projectSupervisorOnly && - (directCodexProductRuntime || planningV2Active) + (directCodexProductRuntime || planningLaneActive) )) || !supervisorChatShouldFollowLatestRef.current ) { @@ -2364,7 +2407,7 @@ export function App({ directCodexTransientReply, directCodexTransientReplyUpdatedAt, directCodexProductRuntime, - planningV2Active, + planningLaneActive, projectSupervisorOnly, supervisorChatOnly, ]); @@ -3333,14 +3376,16 @@ export function App({ ) { return; } - planningV2ActiveRef.current = true; - setPlanningV2Active(true); + planningV2ActiveRef.current = Boolean(planningV2); + setPlanningV2Active(Boolean(planningV2)); if (planningV2) { - designAgentLaneRef.current = false; + designAgentActiveRef.current = false; + setDesignAgentActive(false); setDesignAgentView(null); applyPlanningV2CommandResult(planningV2); } else { - designAgentLaneRef.current = true; + designAgentActiveRef.current = true; + setDesignAgentActive(true); planningV2SessionRef.current = null; setPlanningV2Session(null); setPlanGddState(null); @@ -6138,7 +6183,6 @@ export function App({ setChatAgentBusy(true); setProjectSupervisorRuntimeError(''); setPlanningV2TransientReplyTarget(''); - setPlanningV2Reasoning(''); try { const result = currentSessionId ? await invoke( @@ -6204,13 +6248,17 @@ export function App({ directPolicyChecked = false, references, }: ExecuteChatAgentReplyInput) { - if (planningV2ActiveRef.current || planningStartMode) { + if ( + planningV2ActiveRef.current || + designAgentActiveRef.current || + planningStartMode + ) { const nextProjectPath = resolveChatProjectPath(localProject); if (!nextProjectPath) { setProjectSupervisorRuntimeError('请先初始化本地项目'); return; } - if (designAgentLaneRef.current) { + if (designAgentActiveRef.current) { await executeDesignAgentTurn( nextProjectPath, { type: 'message', text: prompt }, @@ -7074,7 +7122,7 @@ export function App({ answers: Record, ) { const nextProjectPath = resolveChatProjectPath(localProject); - if (planningV2ActiveRef.current || planningStartMode) { + if (planningV2ActiveRef.current) { const current = planningV2SessionRef.current; const currentQuestion = current?.session.currentQuestion; const answer = Object.values(answers) @@ -11774,7 +11822,7 @@ export function App({ (agent.runtimeStatus !== null || agent.hasRecentEvidence), ); const useDesignAgentSurface = - Boolean(designAgentView) || (planningStartMode && !planningV2Session); + Boolean(designAgentView) || (designAgentActive && !planningV2Session); if (projectSupervisorOnly && supervisorChatOnly) { const supervisorProjectPath = @@ -11807,11 +11855,13 @@ export function App({ runtimeConfigOpen={runtimeConfigOpen} runtimeError={projectSupervisorRuntimeError} transientReply={ - planningV2Active - ? planningV2TransientReply - : directCodexProductRuntime - ? directCodexTransientReply - : projectSupervisorTransientReply + designAgentActive + ? designAgentTransientReply + : planningV2Active + ? planningV2TransientReply + : directCodexProductRuntime + ? directCodexTransientReply + : projectSupervisorTransientReply } hasConversationControls={projectSupervisorHasConversationControls} hiddenConversationCount={hiddenConversationCount} @@ -11857,14 +11907,16 @@ export function App({ pendingCommand={directCodexProductRuntime ? pendingCommand : null} projectPath={localProject?.projectPath ?? projectPath} transientReply={ - planningV2Active - ? planningV2TransientReply - : directCodexProductRuntime - ? directCodexTransientReply - : projectSupervisorTransientReply + designAgentActive + ? designAgentTransientReply + : planningV2Active + ? planningV2TransientReply + : directCodexProductRuntime + ? directCodexTransientReply + : projectSupervisorTransientReply } - showDesignReasoning={planningV2Active} - designReasoning={planningV2Reasoning} + showDesignReasoning={designAgentActive} + designReasoning={designAgentReasoning} designReasoningEntries={ useDesignAgentSurface ? (designAgentView?.reasoningEntries ?? []) : [] } @@ -11880,7 +11932,7 @@ export function App({ planGddError={planGddError} onPlanGddRefresh={() => void hydratePlanGddState()} onPlanGddDecision={decidePlanGdd} - planningLane={planningV2Active} + planningLane={planningLaneActive} designView={useDesignAgentSurface ? designAgentView : null} onDesignApprove={ useDesignAgentSurface @@ -11901,8 +11953,8 @@ export function App({ text: '', }; designAgentPendingViewRef.current = null; - setPlanningV2TransientReplyTarget(''); - setPlanningV2Reasoning(''); + setDesignAgentTransientReplyTarget(''); + setDesignAgentReasoning(''); setChatAgentBusy(true); setPlanGddDecisionBusy(true); void designAgentEventSubscriptionReady() @@ -11951,7 +12003,7 @@ export function App({ } if (!designAgentPendingViewRef.current) { designAgentTurnRef.current = null; - setPlanningV2TransientReplyTarget(''); + setDesignAgentTransientReplyTarget(''); } setChatAgentBusy(false); setPlanGddDecisionBusy(false);