From 1c2f82e246b58a894919387585e96010e68026fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E7=BE=BD=E7=A7=A6?= Date: Mon, 31 Aug 2026 14:36:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Game=20Agent=20=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E7=94=BB=E5=B8=83=E5=B1=95=E7=A4=BA=E4=B8=8E=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E7=BC=A9=E6=94=BE=20(#208)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 变更内容 ### 1. 普通资源画布隐藏游戏代码 - 资源画布的大纲、分页和卡片展示不再显示“游戏代码”栏目。 - 保留内部完整的五栏目布局契约,避免已有代码资源坐标被重写。 - 补充回归测试,确认游戏代码不会出现在普通资源画布中,且代码端点不可见时不会渲染对应依赖边。 ### 2. 增加资源类型角标 - 资源卡右上角增加类型角标,用于快速识别资源类型。 - 类型从既有资源投影字段推导,覆盖图片、SVG、视频、音频、文档、任务产物、Agent 回执、项目版本等类型,并为未知类型兜底。 - 该能力仅用于前端展示,不新增后端字段或修改资源投影契约。 ### 3. 限制默认自动适配缩放 - 画布首次自动适配和显式复位时的默认最大缩放统一限制为 `1.5`,避免内容较少或只有一个资源时卡片被放得过大。 - 该限制对设计文档、美术、音频、游戏代码、项目版本全部资源栏目生效。 - 用户主动缩放仍沿用现有上限,不受默认适配上限影响。 - 用户调整后的画布缩放会按排序模式和栏目保留;切换到其他栏目再返回后,仍恢复用户调整后的 viewport,而不是重新自动放大。 ## 兼容性 - 不修改后端 API、SpacetimeDB schema 或 `/api/external/v1`。 - 资源类型角标是展示层推导逻辑,不改变资源数据结构。 - 内部布局仍保留游戏代码栏目契约,已有布局数据不需要迁移。 ## 验证 - `resourceCanvasLayoutModel.test.ts` - `project-development.suite.ts` 相关 App Surface 用例 - `npm run typecheck` - `npm run check:encoding` - Prettier 相关文件检查 - `git diff --check` 历史分支检查不替代最新 head CI;PR 更新后将以最新 CI 结果为准。 --------- Co-authored-by: 段舒康 Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/208 Co-authored-by: 董羽秦 Co-committed-by: 董羽秦 --- apps/ai-game-creator-shell/src/styles.css | 25 +++ .../src/view/project-development/index.tsx | 74 ++++---- .../resourceCanvasLayoutModel.ts | 10 +- .../resourceProjectionModel.ts | 61 +++++++ .../appSurface/project-development.suite.ts | 166 +++++++++++++++--- .../projectResourceProjectionModel.test.ts | 120 ++++++++++++- .../tests/resourceCanvasLayoutModel.test.ts | 25 ++- ...AI游戏创作】项目开发工作台PRD-2026-07-20.md | 10 +- ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 2 +- 9 files changed, 423 insertions(+), 70 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index b150722b5..c733e6704 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -5458,6 +5458,31 @@ iframe.preview-frame { visibility: visible; } +.game-resource-card-type-badge { + position: absolute; + top: 8px; + right: 8px; + z-index: 2; + display: inline-flex; + max-width: calc(100% - 16px); + align-items: center; + min-width: 0; + padding: 4px 8px; + overflow: hidden; + border: 1px solid rgb(255 255 255 / 72%); + border-radius: 999px; + background: rgb(75 48 38 / 84%); + color: #fff; + font-size: 10px; + font-weight: 900; + line-height: 1; + letter-spacing: 0.02em; + pointer-events: none; + text-overflow: ellipsis; + white-space: nowrap; + box-shadow: 0 8px 18px rgb(96 62 47 / 20%); +} + .game-resource-card-open { position: absolute; z-index: 1; diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx index 272916fa5..f539fc02f 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx @@ -82,10 +82,10 @@ import { normalizeInfiniteResourceCanvasViewport, RESOURCE_CANVAS_DRAG_THRESHOLD, RESOURCE_CANVAS_FIT_PADDING, - RESOURCE_CANVAS_INITIAL_FIT_MAX_SCALE, RESOURCE_CANVAS_SECTION_MIN_HEIGHT, RESOURCE_CANVAS_SECTION_MIN_WIDTH, RESOURCE_CANVAS_SECTION_ORDER, + RESOURCE_CANVAS_VISIBLE_SECTION_ORDER, type ResourceCanvasCardSize, resourceCanvasCardSize, resourceCanvasContentBounds, @@ -123,6 +123,7 @@ import { type ProjectResource, type ProjectResourceCategory, projectResourcesFromReadModels, + projectResourceTypeLabel, } from './resourceProjectionModel'; import { clampProjectResourceSectionZoom, @@ -393,6 +394,7 @@ export type ProjectDevelopmentViewProps = { }; const categoryOrder = RESOURCE_CANVAS_SECTION_ORDER; +const visibleCategoryOrder = RESOURCE_CANVAS_VISIBLE_SECTION_ORDER; const categoryLabels: Record = { code: '游戏代码', @@ -592,6 +594,7 @@ const ResourceCard = memo(function ResourceCard({ const [decodedIdentity, setDecodedIdentity] = useState(null); const Icon = categoryIcons[resource.category]; const kind = projectResourceCardPreviewKind(resource); + const resourceTypeLabel = projectResourceTypeLabel(resource); const isMedia = kind === 'video' || kind === 'audio'; const mediaActive = activeMediaIdentity === previewIdentity; const sourceUrl = @@ -797,6 +800,13 @@ const ResourceCard = memo(function ResourceCard({ + + {resourceTypeLabel} +