From 35d3a92afd8e2613e58cdd135b8b69bb9c659117 Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Sat, 12 Sep 2026 16:44:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BF=AB=E9=80=9F=E7=BC=96=E8=BE=91=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E8=AF=8D=E6=94=AF=E6=8C=81=20@=20=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=EF=BC=8C@=20=E9=9D=A2=E6=9D=BF=E8=A1=A5?= =?UTF-8?q?=E6=A0=87=E7=AD=BE=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 快速编辑面板提示词换成与聊天同一个 `ResourceReferenceInput`,`@` 候选、插入动作与引用模型全部复用聊天那一套 - 共享 composer / 快速编辑面板新增 `promptInputSlot` 注入位,宿主控件替换默认 textarea,不传时 DOM 与行为逐字不变 - `ResourceReferenceInput` 新增 `showPolishAction`:快速编辑的润色仍由 ResourcePromptPolishSlot 承担,面板里不再出现第二个润色入口 - `ResourceReferenceInput` 的多行输入区把 Enter 留给换行,只有单行输入区才用 Enter 提交 - `@` 面板的搜索与筛选改用共享 `PlatformResourceFilterBar`,搜索 + 功能分类 + 标签三者在面板内叠加生效 - 新增 `resourceReferenceMatchesTagSelection` / `resourceReferenceTagLibrary`,标签口径(多标签 AND、计数与排序)落在共享标签库,两个页签各持一份 - 宿主新增 `quickEditReferences` 与 `applyResourceQuickEditDraft`,派生请求的 prompt 就是输入区回填的那份文本(`@显示名`),不引入第二种引用格式 - `useImageCanvasFloatingOptionDismiss` 新增 `isInsideExtraOverlay`:宿主 portal 到 body 的 `@` 选择器与候选菜单不再被判成点外部而收起快速编辑面板 - 资源画布快速编辑提示词输入区补样式(铺满 composer 一整行、高度上限),`@` 面板去掉手写搜索行样式 - 用例:新增标签库口径、标签筛选(多标签叠加 + 关键字 + 功能分类)、两页签独立标签、聊天与快速编辑草稿一致性、快速编辑内 `@` 出选择器且派生 payload 断言;扩展 AGC 资源工作台 V3 端到端验收用例 --- .../ResourceReferenceInput.tsx | 184 +++++++------ .../project-workspace/resourceReferences.ts | 51 ++++ .../resource-canvas/resourceCanvasChrome.css | 18 ++ apps/ai-game-creator-shell/src/styles.css | 29 +- .../src/view/project-development/index.tsx | 62 +++++ .../projectResourceLiveIntegration.test.tsx | 53 ++++ .../tests/resourceReferenceInput.test.tsx | 247 +++++++++++++++++- ...用例】AGC资源工作台V3端到端验收-2026-09-11.md | 7 +- ...ImageCanvasBasicGenerationComposerView.tsx | 53 ++-- .../ImageCanvasQuickEditPanelView.tsx | 4 + .../useImageCanvasFloatingOptionDismiss.ts | 15 +- 11 files changed, 598 insertions(+), 125 deletions(-) diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx index 0621835e6..5a9d59a71 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ResourceReferenceInput.tsx @@ -34,7 +34,6 @@ import { Loader2, Music2, RotateCcw, - Search, Sparkles, Video, } from 'lucide-react'; @@ -51,6 +50,7 @@ import { } from 'react'; import { createPortal, flushSync } from 'react-dom'; +import { PlatformResourceFilterBar } from '../../../../../packages/shared/src/components/PlatformResourceFilterBar'; import { PlatformSegmentedTabs } from '../../../../../packages/shared/src/components/PlatformSegmentedTabs'; import { type GameCreationAppAssetManifestEntry, @@ -90,7 +90,9 @@ import { resourceReferenceFromAsset, resourceReferenceMatchesCategoryFilter, resourceReferenceMatchesQuery, + resourceReferenceMatchesTagSelection, type ResourceReferenceScope, + resourceReferenceTagLibrary, } from './resourceReferences'; import { usePromptPolish } from './usePromptPolish'; @@ -114,6 +116,14 @@ type ResourceReferenceInputProps = { multiline?: boolean; rows?: number; showTriggerButton?: boolean; + /** + * 内置「AI 润色 / 恢复原文」动作是否渲染,默认 `true`。 + * + * 聊天输入区用内置那一份(不带资源编辑场景上下文);资源侧快速编辑由宿主自己的 + * `ResourcePromptPolishSlot` 承担润色(带场景上下文与长度上限),传 `false` + * 避免同一个面板里出现两个润色入口。 + */ + showPolishAction?: boolean; inputRef?: RefObject; }; @@ -121,10 +131,17 @@ type ResourcePickerScopeState = { query: string; filter: ResourceReferenceFilter; selectedResourceIds: string[]; + /** 已选标签(多选),与搜索词、功能分类三者叠加生效。 */ + selectedTags: string[]; }; function createResourcePickerScopeState(): ResourcePickerScopeState { - return { query: '', filter: 'all', selectedResourceIds: [] }; + return { + query: '', + filter: 'all', + selectedResourceIds: [], + selectedTags: [], + }; } function createResourcePickerScopeStates(): Record< @@ -291,6 +308,7 @@ function ResourceReferenceEditor({ multiline, rows, showTriggerButton = true, + showPolishAction = true, inputRef, composerRef, }: ResourceReferenceInputProps & { @@ -336,12 +354,19 @@ function ResourceReferenceEditor({ const pickerQuery = pickerScopeState.query; const pickerFilter = pickerScopeState.filter; const selectedResourceIds = pickerScopeState.selectedResourceIds; + const selectedTags = pickerScopeState.selectedTags; const scopeReferences = pickerScope === 'current-version' ? currentVersionAssetReferences : assetReferences; + // 标签库按当前页签的候选派生:两个页签各自一份,标签与计数都不跨页签串。 + const scopeTagItems = useMemo( + () => resourceReferenceTagLibrary(scopeReferences), + [scopeReferences], + ); + const updatePickerScopeState = useCallback( (patch: Partial) => { setPickerScopeStates((current) => ({ @@ -479,24 +504,23 @@ function ResourceReferenceEditor({ }); }, [editor, refreshResourceReferenceLabels]); - useEffect( - () => - editor.registerCommand( - KEY_ENTER_COMMAND, - (event) => { - if (!event || event.shiftKey || event.isComposing) { - return false; - } - event.preventDefault(); - (event.target as HTMLElement | null) - ?.closest('form') - ?.requestSubmit(); - return true; - }, - COMMAND_PRIORITY_HIGH, - ), - [editor], - ); + // Enter 提交只属于单行输入区(聊天 / 项目总控)。多行输入区(资源卡快速编辑提示词) + // 的 Enter 必须留给换行,否则用户没法在提示词里分行。 + useEffect(() => { + if (multiline) return undefined; + return editor.registerCommand( + KEY_ENTER_COMMAND, + (event) => { + if (!event || event.shiftKey || event.isComposing) { + return false; + } + event.preventDefault(); + (event.target as HTMLElement | null)?.closest('form')?.requestSubmit(); + return true; + }, + COMMAND_PRIORITY_HIGH, + ); + }, [editor, multiline]); useEffect( () => @@ -727,9 +751,12 @@ function ResourceReferenceEditor({ const pickerReferences = useMemo(() => { return scopeReferences.filter((reference) => { if (!resourceReferenceMatchesQuery(reference, pickerQuery)) return false; - return resourceReferenceMatchesCategoryFilter(reference, pickerFilter); + if (!resourceReferenceMatchesCategoryFilter(reference, pickerFilter)) { + return false; + } + return resourceReferenceMatchesTagSelection(reference, selectedTags); }); - }, [pickerFilter, pickerQuery, scopeReferences]); + }, [pickerFilter, pickerQuery, scopeReferences, selectedTags]); const visiblePickerReferences = pickerReferences.slice(0, 120); @@ -805,38 +832,46 @@ function ResourceReferenceEditor({