refactor(canvas): 预演台 - 重构工作台与场景交互链路

This commit is contained in:
ddcat666
2026-10-06 09:06:43 +08:00
parent 1dfc8ce7ac
commit 112916587c
128 changed files with 42372 additions and 5011 deletions

View File

@@ -10,7 +10,7 @@ import { getActiveUserScope } from "@/lib/user-scope";
import { DEFAULT_CANVAS_COLOR_THEME, type CanvasBackgroundMode } from "@/lib/canvas-theme";
import type { CanvasStarterMode } from "@/lib/canvas/canvas-starter";
import type { CanvasAssistantSession, CanvasConnection, CanvasNodeData, ViewportTransform } from "@/types/canvas";
import type { DirectorScene } from "@/types/director";
import type { PrevisScene } from "@/types/previs";
import type { TimelineProject } from "@/types/timeline";
export type CanvasProject = {
@@ -30,7 +30,7 @@ export type CanvasProject = {
backgroundMode: CanvasBackgroundMode;
showImageInfo: boolean;
viewport: ViewportTransform;
directorScenes: DirectorScene[];
previsScenes: PrevisScene[];
timeline?: TimelineProject;
};
@@ -43,7 +43,7 @@ type CanvasStore = {
renameProject: (id: string, title: string) => void;
deleteProjects: (ids: string[]) => void;
replaceProjects: (projects: CanvasProject[]) => void;
updateProject: (id: string, patch: Partial<Pick<CanvasProject, "projectId" | "nodes" | "connections" | "chatSessions" | "activeChatId" | "starterMode" | "appearance" | "backgroundMode" | "showImageInfo" | "viewport" | "directorScenes" | "timeline">>) => void;
updateProject: (id: string, patch: Partial<Pick<CanvasProject, "projectId" | "nodes" | "connections" | "chatSessions" | "activeChatId" | "starterMode" | "appearance" | "backgroundMode" | "showImageInfo" | "viewport" | "previsScenes" | "timeline">>) => void;
};
const initialViewport: ViewportTransform = { x: 0, y: 0, k: 1 };
@@ -465,7 +465,7 @@ export const useCanvasStore = create<CanvasStore>()(
backgroundMode: appearanceDefault?.backgroundMode || DEFAULT_CANVAS_BACKGROUND_MODE,
showImageInfo: false,
viewport: initialViewport,
directorScenes: [],
previsScenes: [],
};
set((state) => ({ projects: [project, ...state.projects] }));
return id;
@@ -488,7 +488,7 @@ export const useCanvasStore = create<CanvasStore>()(
backgroundMode: source.backgroundMode || DEFAULT_CANVAS_BACKGROUND_MODE,
showImageInfo: source.showImageInfo || false,
viewport: source.viewport || initialViewport,
directorScenes: source.directorScenes || [],
previsScenes: source.previsScenes || [],
};
set((state) => ({ projects: [project, ...state.projects] }));
return project.id;

View File

@@ -1,18 +1,18 @@
import { create } from "zustand";
import { DIRECTOR_DEFAULT_MODE, directorModeCapabilities, resolveDirectorModeTransition, type DirectorMode } from "@/lib/canvas/director/director-modes";
import { DIRECTOR_DEFAULT_VIEW_MODE, type DirectorViewMode } from "@/lib/canvas/director/director-view-modes";
import type { DirectorRenderMode } from "@/types/director";
import { PREVIS_DEFAULT_MODE, previsModeCapabilities, resolvePrevisModeTransition, type PrevisMode } from "@/lib/canvas/previs/previs-modes";
import { PREVIS_DEFAULT_VIEW_MODE, type PrevisViewMode } from "@/lib/canvas/previs/previs-view-modes";
import type { PrevisRenderMode } from "@/types/previs";
type DirectorWorkbenchStore = {
type PrevisWorkbenchStore = {
selectedObjectId: string | null;
selectedBone: string | null;
selectedLightId: string | null;
mode: DirectorMode;
mode: PrevisMode;
/** 取景模式(3D/CAM/五个正交轴向)。纯视口状态,不影响一级模式的能力矩阵。 */
viewMode: DirectorViewMode;
viewMode: PrevisViewMode;
transformMode: "translate" | "rotate" | "scale";
renderMode: DirectorRenderMode;
renderMode: PrevisRenderMode;
playhead: number;
playing: boolean;
autoKey: boolean;
@@ -22,12 +22,12 @@ type DirectorWorkbenchStore = {
setSelectedBone: (bone: string | null) => void;
setSelectedLightId: (id: string | null) => void;
/** 切换一级模式。离开动画模式时一并停播、关 Auto Key、夹回合法渲染视图。 */
setMode: (mode: DirectorMode) => void;
/** 切换取景模式。UI-only:不写入 DirectorScene,不产生 undo/history。 */
setViewMode: (mode: DirectorViewMode) => void;
setTransformMode: (mode: DirectorWorkbenchStore["transformMode"]) => void;
setMode: (mode: PrevisMode) => void;
/** 切换取景模式。UI-only:不写入 PrevisScene,不产生 undo/history。 */
setViewMode: (mode: PrevisViewMode) => void;
setTransformMode: (mode: PrevisWorkbenchStore["transformMode"]) => void;
/** 切换渲染视图。当前模式不允许的视图一律忽略,不做静默降级。 */
setRenderMode: (mode: DirectorRenderMode) => void;
setRenderMode: (mode: PrevisRenderMode) => void;
setPlayhead: (time: number) => void;
setPlaying: (playing: boolean) => void;
setAutoKey: (autoKey: boolean) => void;
@@ -37,8 +37,8 @@ type DirectorWorkbenchStore = {
};
const initialState = {
mode: DIRECTOR_DEFAULT_MODE,
viewMode: DIRECTOR_DEFAULT_VIEW_MODE,
mode: PREVIS_DEFAULT_MODE,
viewMode: PREVIS_DEFAULT_VIEW_MODE,
selectedObjectId: null,
selectedBone: null,
selectedLightId: null,
@@ -51,17 +51,17 @@ const initialState = {
sequencerVisible: true,
};
export const useDirectorWorkbenchStore = create<DirectorWorkbenchStore>((set) => ({
export const usePrevisWorkbenchStore = create<PrevisWorkbenchStore>((set) => ({
...initialState,
setSelectedObjectId: (selectedObjectId) => set({ selectedObjectId, selectedBone: null, selectedLightId: null }),
setSelectedBone: (selectedBone) => set({ selectedBone }),
setSelectedLightId: (selectedLightId) => set({ selectedLightId, selectedObjectId: null, selectedBone: null }),
setMode: (mode) => set((state) => resolveDirectorModeTransition({ mode, playing: state.playing, autoKey: state.autoKey, renderMode: state.renderMode })),
setMode: (mode) => set((state) => resolvePrevisModeTransition({ mode, playing: state.playing, autoKey: state.autoKey, renderMode: state.renderMode })),
setViewMode: (viewMode) => set({ viewMode }),
setTransformMode: (transformMode) => set({ transformMode }),
// 夹在 store 层而不是只在 UI 层过滤:dock 与顶栏是两条路径,
// 只挡其中一条迟早会漏(本轮就漏过一次:dock 的「骨骼视图」在摆场模式仍可点)。
setRenderMode: (renderMode) => set((state) => (directorModeCapabilities(state.mode).renderModes.includes(renderMode) ? { renderMode } : {})),
setRenderMode: (renderMode) => set((state) => (previsModeCapabilities(state.mode).renderModes.includes(renderMode) ? { renderMode } : {})),
setPlayhead: (playhead) => set({ playhead }),
setPlaying: (playing) => set({ playing }),
setAutoKey: (autoKey) => set({ autoKey }),