feat(canvas): 自由画布 - 增强空白起点、外观设置与节点连接体验 (#356)

This commit is contained in:
vv-get-ti authored and GitHub committed 2026-08-31 06:49:43 +08:00
1 parent cc4aa736ff
commit 151e99f8d9
22 files changed
+925 -113

No files matched your search

@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState, type Dispatch, type MutableRe
import { App } from "antd";
import { useNavigate } from "react-router";
import { canvasAppearanceBaseTheme, canvasAppearanceForTheme, normalizeCanvasAppearance, type CanvasAppearance } from "@/lib/canvas/canvas-appearance";
import type { CanvasBackgroundMode } from "@/lib/canvas-theme";
import { removeCanvasDrawing } from "@/lib/canvas/canvas-drawing-storage";
import { normalizeCanvasNodeTimestamps } from "@/lib/canvas/canvas-node-timestamps";
@@ -9,6 +10,7 @@ import { hydrateAssistantImages, hydrateCanvasImages, resetInterruptedGeneration
import { listAddedSkills, type Skill } from "@/services/api/skills";
import { createCanvasProjectWithRemoteSync, deleteCanvasProjectsWithRemoteSync, saveRemoteUserDataNow } from "@/services/user-data-sync";
import { flushCanvasStorePersistence, useCanvasStore } from "@/stores/canvas/use-canvas-store";
import { useThemeStore } from "@/stores/use-theme-store";
import type { CanvasAssistantSession, CanvasConnection, CanvasNodeData, CanvasNodeMetadata, ViewportTransform } from "@/types/canvas";
import type { CanvasHistorySnapshot } from "./use-canvas-history";
@@ -19,6 +21,7 @@ type UseCanvasProjectLifecycleOptions = {
connections: CanvasConnection[];
chatSessions: CanvasAssistantSession[];
activeChatId: string | null;
canvasAppearance: CanvasAppearance;
backgroundMode: CanvasBackgroundMode;
showImageInfo: boolean;
viewport: ViewportTransform;
@@ -30,6 +33,7 @@ type UseCanvasProjectLifecycleOptions = {
setConnections: Dispatch<SetStateAction<CanvasConnection[]>>;
setChatSessions: Dispatch<SetStateAction<CanvasAssistantSession[]>>;
setActiveChatId: Dispatch<SetStateAction<string | null>>;
setCanvasAppearance: Dispatch<SetStateAction<CanvasAppearance>>;
setBackgroundMode: Dispatch<SetStateAction<CanvasBackgroundMode>>;
setShowImageInfo: Dispatch<SetStateAction<boolean>>;
setViewport: Dispatch<SetStateAction<ViewportTransform>>;
@@ -46,6 +50,7 @@ export function useCanvasProjectLifecycle({
connections,
chatSessions,
activeChatId,
canvasAppearance,
backgroundMode,
showImageInfo,
viewport,
@@ -57,6 +62,7 @@ export function useCanvasProjectLifecycle({
setConnections,
setChatSessions,
setActiveChatId,
setCanvasAppearance,
setBackgroundMode,
setShowImageInfo,
setViewport,
@@ -87,11 +93,16 @@ export function useCanvasProjectLifecycle({
const applyRestoredProject = (restoredNodes: CanvasNodeData[], restoredSessions: CanvasAssistantSession[]) => {
if (cancelled) return;
const fallbackTheme = useThemeStore.getState().theme;
const restoredAppearance = project.appearance
? normalizeCanvasAppearance(project.appearance, fallbackTheme)
: canvasAppearanceForTheme(fallbackTheme);
const snapshot: CanvasHistorySnapshot = {
nodes: restoredNodes,
connections: project.connections,
chatSessions: restoredSessions,
activeChatId: project.activeChatId || null,
canvasAppearance: restoredAppearance,
backgroundMode: project.backgroundMode || "dots",
showImageInfo: project.showImageInfo || false,
};
@@ -102,6 +113,8 @@ export function useCanvasProjectLifecycle({
setConnections(snapshot.connections);
setChatSessions(snapshot.chatSessions);
setActiveChatId(snapshot.activeChatId);
setCanvasAppearance(snapshot.canvasAppearance);
useThemeStore.getState().setTheme(canvasAppearanceBaseTheme(snapshot.canvasAppearance, fallbackTheme));
setBackgroundMode(snapshot.backgroundMode);
setShowImageInfo(snapshot.showImageInfo);
setViewport(project.viewport);
@@ -128,7 +141,7 @@ export function useCanvasProjectLifecycle({
return () => {
cancelled = true;
};
}, [hydrated, message, navigate, openProject, projectId, resetHistory, setActiveChatId, setBackgroundMode, setChatSessions, setConnections, setNodes, setShowImageInfo, setViewport]);
}, [hydrated, message, navigate, openProject, projectId, resetHistory, setActiveChatId, setBackgroundMode, setCanvasAppearance, setChatSessions, setConnections, setNodes, setShowImageInfo, setViewport]);
useEffect(() => {
if (!projectLoaded) return;
@@ -147,8 +160,8 @@ export function useCanvasProjectLifecycle({
useEffect(() => {
if (!projectLoaded || historyPausedRef.current) return;
updateProject(projectId, { nodes, connections, chatSessions, activeChatId, backgroundMode, showImageInfo });
}, [activeChatId, backgroundMode, chatSessions, connections, historyPausedRef, nodes, projectId, projectLoaded, showImageInfo, updateProject]);
updateProject(projectId, { nodes, connections, chatSessions, activeChatId, appearance: canvasAppearance, backgroundMode, showImageInfo });
}, [activeChatId, backgroundMode, canvasAppearance, chatSessions, connections, historyPausedRef, nodes, projectId, projectLoaded, showImageInfo, updateProject]);
useEffect(() => {
if (!projectLoaded) return;
@@ -202,6 +215,7 @@ export function useCanvasProjectLifecycle({
connections: connectionsRef.current,
chatSessions,
activeChatId,
appearance: canvasAppearance,
backgroundMode,
showImageInfo,
viewport: viewportRef.current,
@@ -220,7 +234,7 @@ export function useCanvasProjectLifecycle({
message.warning(`本地画布布局已保存,云端同步失败:${detail}`);
}
return true;
}, [activeChatId, backgroundMode, chatSessions, connectionsRef, currentProject?.directorScenes, message, nodesRef, projectId, showImageInfo, updateProject, viewportRef]);
}, [activeChatId, backgroundMode, canvasAppearance, chatSessions, connectionsRef, currentProject?.directorScenes, message, nodesRef, projectId, showImageInfo, updateProject, viewportRef]);
const clearCanvasFiles = useCallback(() => {
cleanupCanvasFiles({ projectId, nodes: [], chatSessions: [] });