From 9fd5b999728fc16ab879460364ba5815b5ae406e Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Wed, 9 Sep 2026 20:04:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=20asset-canvas=20=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=B2=BE=E4=BF=AE=E8=8D=89=E7=A8=BF=E5=85=A5=E5=8F=A3?= =?UTF-8?q?=E5=B9=B6=E9=87=8D=E6=8E=A5=E8=B5=84=E6=BA=90=E6=B4=BE=E7=94=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 apps/ai-game-creator-shell/src/features/asset-canvas 全部四个文件(AssetCanvasSurface.tsx、assetCanvasNaming.ts、assetCanvasSurface.css、tauriImageCanvasHostAdapter.ts) - index.tsx 删除 assetCanvasRoute 状态与 ref、beginAssetCanvas/openAssetCanvas/normalizeRasterSourceAndOpen、cancelAssetCanvas/handleAssetCanvasSaveAttempt/handleAssetCommitted 与 game-creator-local-asset-committed 事件监听、中央主视窗渲染分支与 resources.asset-canvas 视图状态 - index.tsx 把 assetCanvasNotice 改名为通用 resourceWorkbenchNotice,保留非草稿流程文案与资源定位按钮 - resourceEditModel.ts 取消 image-canvas 路由,PNG/JPEG/WebP 统一走 derive + image-reference(复用 resource_editor.rs 的 /editor/images/edits) - styles.css 删除 resources.asset-canvas 两条画布选择器,保留 resources.ui-editor 规则 - check-config.mjs 把 normalize_local_project_raster_resource 加入 native-only 白名单(临时项:C3 快速编辑重建后会重新获得前端调用方,届时删除该 allowlist 条目) - index.tsx 移除 useMemo 依赖数组里模块级常量 visibleCategoryOrder(基线遗留的 react-hooks/exhaustive-deps warning,会阻断 pre-commit,属通过门禁所需的最小修正) --- .../scripts/check-config.mjs | 1 + .../asset-canvas/AssetCanvasSurface.tsx | 4518 ----------------- .../asset-canvas/assetCanvasNaming.ts | 82 - .../asset-canvas/assetCanvasSurface.css | 931 ---- .../tauriImageCanvasHostAdapter.ts | 932 ---- apps/ai-game-creator-shell/src/styles.css | 7 - .../src/view/project-development/index.tsx | 521 +- .../project-development/resourceEditModel.ts | 42 +- 8 files changed, 55 insertions(+), 6979 deletions(-) delete mode 100644 apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx delete mode 100644 apps/ai-game-creator-shell/src/features/asset-canvas/assetCanvasNaming.ts delete mode 100644 apps/ai-game-creator-shell/src/features/asset-canvas/assetCanvasSurface.css delete mode 100644 apps/ai-game-creator-shell/src/features/asset-canvas/tauriImageCanvasHostAdapter.ts diff --git a/apps/ai-game-creator-shell/scripts/check-config.mjs b/apps/ai-game-creator-shell/scripts/check-config.mjs index 0149d953a..93977e9c3 100644 --- a/apps/ai-game-creator-shell/scripts/check-config.mjs +++ b/apps/ai-game-creator-shell/scripts/check-config.mjs @@ -111,6 +111,7 @@ const allowedUncalledTauriCommands = [ 'chat_with_game_creator_agent', 'check_ui_editor_font_glyph_coverage', 'create_ui_design_resource', + 'normalize_local_project_raster_resource', 'open_game_creator_launcher_window', 'open_game_creator_workspace_window', 'read_direct_project_conversation', diff --git a/apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx b/apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx deleted file mode 100644 index e77e7d7a7..000000000 --- a/apps/ai-game-creator-shell/src/features/asset-canvas/AssetCanvasSurface.tsx +++ /dev/null @@ -1,4518 +0,0 @@ -/* eslint-disable react-refresh/only-export-components -- The surface exports its host callback contracts and deterministic fixture helpers for integration tests. */ - -import './assetCanvasSurface.css'; - -import { - type CanvasHistoryAction, - type CanvasHistorySnapshot, - type CanvasLayer, - type CanvasViewport, - createMinimapModel, - fitViewportToLayers, - type ImageCanvasDraft, - type ImageCanvasDraftCanvas, - type ImageCanvasGenerationProgress, - type ImageCanvasGenerationProgressPhase, - type ImageCanvasGenerationRecord, - type ImageCanvasGenerationServiceIdentityConfirmation, - type ImageCanvasHostScope, - type ImageCanvasMediaRef, - moveViewportFromMinimapPointer, - moveViewportFromPan, - removeCanvasLayers, - resizeCanvasLayerBounds, - resolveLayerPointerSelection, - resolveQuickEditFocusViewport, - resolveViewportFromWheel, - scaleViewportFromScreenPoint, - transformCanvasLayers, -} from '@genarrative/image-canvas-core'; -import { - CanvasChromeButton, - CanvasPortal, - CanvasToolbar, - CanvasToolbarDivider, - CanvasToolbarGroup, - CanvasViewport as SharedCanvasViewport, - CanvasWorld, - LayerRenderer, - Minimap, - useCanvasHistory, - ZoomControls, -} from '@genarrative/image-canvas-react'; -import { - ArrowLeft, - Check, - CircleCheck, - CircleX, - Clock3, - ImagePlus, - ListChecks, - LoaderCircle, - LocateFixed, - Maximize2, - Minus, - Plus, - Redo2, - RotateCcw, - Save, - Sparkles, - Trash2, - Undo2, - Upload, - X, -} from 'lucide-react'; -import { - type ChangeEvent, - type CSSProperties, - type PointerEvent as ReactPointerEvent, - useCallback, - useEffect, - useLayoutEffect, - useMemo, - useRef, - useState, -} from 'react'; - -import type { GameCreationAppManifest } from '../../../../../packages/shared/src/contracts/gameCreationApp'; -import { useWalletStore } from '../../stores/useWalletStore'; -import { isValidAssetCanvasName } from './assetCanvasNaming'; -import type { - LocalAssetCommittedEvent, - TauriImageCanvasHostAdapter, -} from './tauriImageCanvasHostAdapter'; - -export type AssetCanvasLifecycleState = - | { kind: 'canvas.creating' } - | { kind: 'canvas.editing'; dirty: boolean } - | { - kind: 'canvas.saving'; - stage: 'draft' | 'staging' | 'committing' | 'projecting'; - } - | { kind: 'canvas.recovering' } - | { - kind: 'canvas.generating'; - phase: ImageCanvasGenerationProgressPhase; - } - | { - kind: 'canvas.failed'; - operation: - | 'generation' - | 'draft-save' - | 'asset-commit' - | 'recovery' - | 'cancellation'; - code: string; - message: string; - reconciliationRequired: boolean; - }; - -export type AssetCanvasSaveAttempt = { - saveAttemptId: string; - sessionId: string; - projectId: string; - draftId: string; - commitId: string; -}; - -export type AssetCanvasExitResult = { - draftId: string; - disposition: 'kept' | 'discarded'; -}; - -export type AssetCanvasCommitNotification = { - source: 'command' | 'event'; - projectPath: string; - projectId: string; - draftId: string; - commitId: string; - assetId: string; - manifest: GameCreationAppManifest; - projectRevision: number; - committedProjectRevision: number; - eventId?: string; -}; - -export const ASSET_CANVAS_KIND_OPTIONS = [ - { value: 'game-art', label: '普通游戏美术' }, - { value: 'icon-spec', label: '统一视觉规范' }, - { value: 'ui-prototype', label: '游戏界面原型' }, - { value: 'art-spritesheet', label: '核心美术图集' }, -] as const; - -type RuntimeCanvasLayer = CanvasLayer & { mediaRef: ImageCanvasMediaRef }; - -type PendingGenerationIdentity = { - saveAttemptId: string; - intentId: string; - generationId: string; - idempotencyKey: string; - commitId: string; - commitIdempotencyKey: string; -}; - -const ASSET_CANVAS_WORLD_SIZE = 12_000; - -const GENERATION_ASPECT_RATIOS = ['1:1', '2:3', '3:2', '9:16', '16:9'] as const; - -type GenerationAspectRatio = (typeof GENERATION_ASPECT_RATIOS)[number]; - -function numericAspectRatio(aspectRatio: GenerationAspectRatio) { - const [width = 1, height = 1] = aspectRatio.split(':').map(Number); - return width / height; -} - -export function generationAspectRatioForOriginalImage( - width: number, - height: number, -): GenerationAspectRatio { - if ( - !Number.isFinite(width) || - !Number.isFinite(height) || - width <= 0 || - height <= 0 - ) { - return '1:1'; - } - const originalRatio = width / height; - return GENERATION_ASPECT_RATIOS.reduce( - (closest, candidate) => - Math.abs(Math.log(originalRatio / numericAspectRatio(candidate))) < - Math.abs(Math.log(originalRatio / numericAspectRatio(closest))) - ? candidate - : closest, - '1:1', - ); -} - -export function resolveQuickEditPanelPosition(input: { - anchorX: number; - belowTop: number; - aboveTop: number; - canvasSize: { width: number; height: number }; - panelSize: { width: number; height: number }; - edgePadding?: number; -}) { - const edgePadding = input.edgePadding ?? 12; - const panelWidth = Math.max(0, input.panelSize.width); - const panelHeight = Math.max(0, input.panelSize.height); - const minCenterX = edgePadding + panelWidth / 2; - const maxCenterX = Math.max( - minCenterX, - input.canvasSize.width - edgePadding - panelWidth / 2, - ); - const maxTop = Math.max( - edgePadding, - input.canvasSize.height - edgePadding - panelHeight, - ); - const top = - input.belowTop + panelHeight <= input.canvasSize.height - edgePadding - ? input.belowTop - : input.aboveTop; - return { - left: Math.min(Math.max(input.anchorX, minCenterX), maxCenterX), - top: Math.min(Math.max(top, edgePadding), maxTop), - }; -} - -type RuntimeGenerationTask = { - generationId: string; - sourceLayerId: string | null; - placeholder: { x: number; y: number; width: number; height: number } | null; - phase: ImageCanvasGenerationProgressPhase; - progress: number | null; - errorCode: string | null; - createdAt: number; - updatedAt: number; -}; - -type GenerationTaskVisualStatus = 'pending' | 'running' | 'failed' | 'done'; - -function generationTaskVisualStatus( - phase: ImageCanvasGenerationProgressPhase, -): GenerationTaskVisualStatus { - if (phase === 'failed') return 'failed'; - if ( - phase === 'candidate-ready' || - phase === 'asset-durable-committed' || - phase === 'manifest-projected' || - phase === 'layout-ready' || - phase === 'selected' - ) { - return 'done'; - } - if (phase === 'confirmation-required' || phase === 'generation-accepted') { - return 'pending'; - } - return 'running'; -} - -function generationTaskStatusLabel(status: GenerationTaskVisualStatus) { - if (status === 'pending') return '排队中'; - if (status === 'running') return '生成中'; - if (status === 'failed') return '失败'; - return '已完成'; -} - -function generationTaskStatusIcon(status: GenerationTaskVisualStatus) { - if (status === 'pending') return