From aa0e0ce5fa0ef538a4fff8c0d21f8a576e5b90cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=94=E4=BB=A4=E5=BC=98?= Date: Mon, 31 Aug 2026 14:37:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=B9=E5=87=86=20GDD=20=E5=9B=9E=E5=A1=AB?= =?UTF-8?q?=E5=81=9A=E6=B8=B8=E6=88=8F=E5=85=A5=E5=8F=A3=20(#210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增批准态 GDD 的做成游戏按钮与 Launcher 回调链 读取 game/fast_gdd.md 并回首页预填做游戏参考附件 补充不触发创建项目和 Runtime 的 appSurface 回归 同步 Fast GDD 技术方案与共享决策记录 Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/210 Co-authored-by: 孔令弘 Co-committed-by: 孔令弘 --- apps/ai-game-creator-shell/src/App.tsx | 18 ++ .../features/app-shell/WorkspaceLauncher.tsx | 2 + .../src/features/app-shell/model.ts | 1 + .../app-shell/useHomeProjectCreation.ts | 68 ++++++ .../project-workspace/GddApprovalCard.tsx | 34 +++ .../ProjectSupervisorView.tsx | 3 + .../ProjectWorkspaceChatPane.tsx | 3 + .../tests/appSurface/home.suite.ts | 101 ++++++++ .../tests/appSurface/plan-gdd.suite.ts | 22 ++ .../shared-memory/decision-log.md | 8 + ...方案】立项策划Agent(Fast GDD)-2026-08-10.md | 15 +- ...irectProject未消费用户上传权威文档-2026-08-30.md | 216 ++++++++++++++++++ 12 files changed, 484 insertions(+), 7 deletions(-) create mode 100644 docs/technical/【技术说明】DirectProject未消费用户上传权威文档-2026-08-30.md diff --git a/apps/ai-game-creator-shell/src/App.tsx b/apps/ai-game-creator-shell/src/App.tsx index e265d1e82..903debb7d 100644 --- a/apps/ai-game-creator-shell/src/App.tsx +++ b/apps/ai-game-creator-shell/src/App.tsx @@ -419,6 +419,7 @@ type AppProps = { summaries: ProjectAgentRuntimeSummary[], ) => void; onAgentResultsChange?: (results: ProjectAgentResultSummary[]) => void; + onMakeGameFromApprovedGdd?: (projectPath: string) => Promise; }; export function App({ @@ -437,6 +438,7 @@ export function App({ onPreviewChange, onAgentRuntimeSummariesChange, onAgentResultsChange, + onMakeGameFromApprovedGdd, }: AppProps = {}) { const { setTitle: setWindowTitle } = useWindowChrome(); // 做方案入口独立成链:立项策划需要委派、澄清 pending 与 GDD 审批,这些只存在于 @@ -10915,6 +10917,14 @@ export function App({ planGddError={planGddError} onPlanGddRefresh={() => void hydratePlanGddState()} onPlanGddDecision={decidePlanGdd} + onMakeGameFromApprovedGdd={ + onMakeGameFromApprovedGdd + ? () => + onMakeGameFromApprovedGdd( + localProject?.projectPath ?? projectPath, + ) + : undefined + } runtime={projectSupervisorRuntime} error={projectSupervisorRuntimeError} runtimeByAgentId={agentRuntimeById} @@ -11013,6 +11023,14 @@ export function App({ planGddError={planGddError} onPlanGddRefresh={() => void hydratePlanGddState()} onPlanGddDecision={decidePlanGdd} + onMakeGameFromApprovedGdd={ + onMakeGameFromApprovedGdd + ? () => + onMakeGameFromApprovedGdd( + localProject?.projectPath ?? projectPath, + ) + : undefined + } queueAgentRunControlFromPanel={queueAgentRunControlFromPanel} queueOrExecuteProjectIndex={queueOrExecuteProjectIndex} queuePendingCommand={queuePendingCommand} diff --git a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx index 6d63481a4..501eca0b1 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx +++ b/apps/ai-game-creator-shell/src/features/app-shell/WorkspaceLauncher.tsx @@ -77,6 +77,7 @@ export function WorkspaceLauncherShell({ activeProjectAgentResults, setAgentResults: setActiveProjectAgentResults, resetLauncherHomeDraft, + startGameFromApprovedGdd, createHomeDraftAutomatically, openProject, } = homeProject; @@ -351,6 +352,7 @@ export function WorkspaceLauncherShell({ setActiveProjectAgentRuntimeSummaries } onAgentResultsChange={setActiveProjectAgentResults} + onMakeGameFromApprovedGdd={startGameFromApprovedGdd} /> } /> diff --git a/apps/ai-game-creator-shell/src/features/app-shell/model.ts b/apps/ai-game-creator-shell/src/features/app-shell/model.ts index f87101410..eb8aa9f07 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/model.ts +++ b/apps/ai-game-creator-shell/src/features/app-shell/model.ts @@ -50,6 +50,7 @@ export type ProjectSupervisorComponentProps = { summaries: ProjectAgentRuntimeSummary[], ) => void; onAgentResultsChange?: (results: ProjectAgentResultSummary[]) => void; + onMakeGameFromApprovedGdd?: (projectPath: string) => Promise; }; export type WorkspaceLauncherShellProps = WorkspaceLauncherProps & { diff --git a/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts b/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts index b5f9e13cd..366ddbf6f 100644 --- a/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts +++ b/apps/ai-game-creator-shell/src/features/app-shell/useHomeProjectCreation.ts @@ -19,6 +19,7 @@ import type { LauncherProjectContext, LocalGameProjectRevisionStatus, LocalProjectDirectoryStatus, + LocalProjectFileResult, PendingNonEmptyProject, ProjectStartMode, TauriInvoke, @@ -48,6 +49,27 @@ type UseHomeProjectCreationOptions = { rememberRecentWorkspace: (projectPath: string) => void; }; +const APPROVED_GDD_BUILD_PROMPT = [ + '请按照附件中的已批准 GDD 开始建造这款游戏。', + '', + '这份 GDD 已覆盖游戏定位与一句话概念、类型与美术方向、游戏支柱、核心循环、目标用户、平台与输入事实、MVP 系统、暂不纳入范围、创作者提示和原型验证项。', + '', + '请先阅读并理解附件中的 fast_gdd.md,以它作为本次建造的主要依据,优先实现其中 MVP 范围内的可运行游戏原型。', +].join('\n'); + +function createTextAttachmentFile(content: string) { + const file = new File([content], 'fast_gdd.md', { + type: 'text/markdown', + lastModified: Date.now(), + }); + if (typeof file.arrayBuffer !== 'function') { + Object.defineProperty(file, 'arrayBuffer', { + value: async () => new TextEncoder().encode(content).buffer, + }); + } + return file; +} + export function useHomeProjectCreation({ setStatus, setLauncherView, @@ -77,6 +99,7 @@ export function useHomeProjectCreation({ const resetLauncherHomeDraft = useLauncherHomeDraftStore( (state) => state.reset, ); + const approvedGddStartInFlightRef = useRef(false); function validateProjectPath(nextProjectPath: string) { const trimmedProjectPath = nextProjectPath.trim(); @@ -91,6 +114,50 @@ export function useHomeProjectCreation({ return trimmedProjectPath; } + async function startGameFromApprovedGdd(nextProjectPath: string) { + if (approvedGddStartInFlightRef.current) { + return; + } + approvedGddStartInFlightRef.current = true; + const invoke = resolveTauriInvoke(); + try { + if (!invoke) { + throw new Error('需要在陶泥儿客户端内运行'); + } + const projectPath = nextProjectPath.trim(); + if (!projectPath) { + throw new Error('当前项目路径无效'); + } + + setStatus('正在读取已批准 GDD'); + const result = await invoke( + 'read_local_project_file', + { + projectPath, + relativePath: 'game/fast_gdd.md', + commandId: 'file.read', + }, + ); + const file = createTextAttachmentFile(result.content); + + await createHomeDraftAutomatically( + { + creationType: 'game', + prompt: APPROVED_GDD_BUILD_PROMPT, + attachments: [ + { + id: `approved-gdd-${Date.now().toString(36)}`, + file, + }, + ], + }, + 'direct-build', + ); + } finally { + approvedGddStartInFlightRef.current = false; + } + } + function enterProjectDevelopment(context: LauncherProjectContext) { setCurrentProjectContext(context); setActiveProjectPreview(context.manifest.preview ?? null); @@ -600,6 +667,7 @@ export function useHomeProjectCreation({ projectBusy: projectAction !== null, pendingNonEmptyProject, resetLauncherHomeDraft, + startGameFromApprovedGdd, createHomeDraft, createHomeDraftAutomatically, openProject, diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx index 628f6f517..5a470dd39 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/GddApprovalCard.tsx @@ -42,6 +42,7 @@ type GddApprovalCardProps = { action: PlanGddDecisionAction, comment: string | null, ) => Promise; + onMakeGame?: () => Promise; }; const stateLabels: Record = { @@ -82,6 +83,7 @@ export function PlanGddSurface({ error, onRefresh, onDecision, + onMakeGame, }: GddApprovalCardProps & { active?: boolean; projectPath: string }) { const showProgress = stageProgressVisible(state, active); const showCard = approvalCardVisible(state); @@ -98,6 +100,7 @@ export function PlanGddSurface({ state={state} active={active} projectPath={projectPath} + onMakeGame={onMakeGame} /> ) : null} {showCard ? ( @@ -118,14 +121,18 @@ export function PlanGddStageProgress({ state, active = false, projectPath = '', + onMakeGame, }: { state: PlanGddStateViewV1 | null; active?: boolean; projectPath?: string; + onMakeGame?: () => Promise; }) { const [detailsOpen, setDetailsOpen] = useState(false); const [openError, setOpenError] = useState(''); const [opening, setOpening] = useState(false); + const [makingGame, setMakingGame] = useState(false); + const [makeGameError, setMakeGameError] = useState(''); if (!state || !stageProgressVisible(state, active)) { return null; } @@ -198,6 +205,25 @@ export function PlanGddStageProgress({ > {opening ? '正在打开' : '打开文件'} + {onMakeGame ? ( + + ) : null} {openError ? ( ) : null} + {makeGameError ? ( + + {makeGameError} + + ) : null} ) : null} {detailsOpen && deliveredGdd ? ( diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx index 1e22942f0..dba7534dc 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ProjectSupervisorView.tsx @@ -68,6 +68,7 @@ type ProjectSupervisorViewProps = RuntimePanelProps & { action: PlanGddDecisionAction, comment: string | null, ) => Promise; + onMakeGameFromApprovedGdd?: () => Promise; }; export function ProjectSupervisorView({ @@ -99,6 +100,7 @@ export function ProjectSupervisorView({ planGddError, onPlanGddRefresh, onPlanGddDecision, + onMakeGameFromApprovedGdd, ...runtimePanelProps }: ProjectSupervisorViewProps) { const submitLabel = needsUserInput @@ -121,6 +123,7 @@ export function ProjectSupervisorView({ error={planGddError} onRefresh={onPlanGddRefresh} onDecision={onPlanGddDecision} + onMakeGame={onMakeGameFromApprovedGdd} />
Promise; + onMakeGameFromApprovedGdd?: () => Promise; queueAgentRunControlFromPanel: (action: 'kill' | 'retry' | 'resume') => void; queueOrExecuteProjectIndex: () => Promise; queuePendingCommand: (command: PendingCommand) => void; @@ -278,6 +279,7 @@ export function ProjectWorkspaceChatPane({ planGddError, onPlanGddRefresh, onPlanGddDecision, + onMakeGameFromApprovedGdd, queueAgentRunControlFromPanel, queueOrExecuteProjectIndex, queuePendingCommand, @@ -376,6 +378,7 @@ export function ProjectWorkspaceChatPane({ error={planGddError} onRefresh={onPlanGddRefresh} onDecision={onPlanGddDecision} + onMakeGame={onMakeGameFromApprovedGdd} />