Fix canvas visibility and workflow import
This commit is contained in:
@@ -56,8 +56,6 @@
|
||||
:root {
|
||||
--background: oklch(0.985 0.01 85);
|
||||
--foreground: oklch(0.18 0.02 60);
|
||||
--canvas-dot-color: rgb(57 48 35 / 0.18);
|
||||
--canvas-dot-accent-color: rgb(196 126 30 / 0.26);
|
||||
--card: oklch(0.995 0.005 85);
|
||||
--card-foreground: oklch(0.18 0.02 60);
|
||||
--popover: oklch(0.995 0.005 85);
|
||||
@@ -93,8 +91,6 @@
|
||||
.dark {
|
||||
--background: oklch(0.16 0.015 260);
|
||||
--foreground: oklch(0.98 0.01 80);
|
||||
--canvas-dot-color: rgb(255 255 255 / 0.34);
|
||||
--canvas-dot-accent-color: rgb(255 255 255 / 0.2);
|
||||
--card: oklch(0.21 0.025 260);
|
||||
--card-foreground: oklch(0.98 0.01 80);
|
||||
--popover: oklch(0.25 0.025 260);
|
||||
|
||||
@@ -263,6 +263,23 @@ function collectExternalAssetKeys(value: unknown, keys = new Set<string>()) {
|
||||
return keys;
|
||||
}
|
||||
|
||||
function getImportCanvasCandidate(payload: unknown) {
|
||||
if (!payload || typeof payload !== 'object') return payload;
|
||||
const parsed = payload as {
|
||||
state?: unknown;
|
||||
project_json?: unknown;
|
||||
files?: {
|
||||
'project.data.json'?: unknown;
|
||||
projectData?: unknown;
|
||||
};
|
||||
};
|
||||
return parsed.files?.['project.data.json']
|
||||
|| parsed.files?.projectData
|
||||
|| parsed.project_json
|
||||
|| parsed.state
|
||||
|| parsed;
|
||||
}
|
||||
|
||||
function normalizeExternalRatio(value: unknown) {
|
||||
if (typeof value !== 'string' || !value) return undefined;
|
||||
const match = value.match(/^(\d+)\s*x\s*(\d+)$/i);
|
||||
@@ -410,19 +427,7 @@ function convertExternalCanvasProject(value: unknown, assetMap?: ExternalAssetMa
|
||||
|
||||
function getImportCanvasState(payload: unknown, assetMap?: ExternalAssetMap): CanvasProjectState {
|
||||
if (!payload || typeof payload !== 'object') return createEmptyCanvasState();
|
||||
const parsed = payload as {
|
||||
state?: unknown;
|
||||
project_json?: unknown;
|
||||
files?: {
|
||||
'project.data.json'?: unknown;
|
||||
projectData?: unknown;
|
||||
};
|
||||
};
|
||||
const candidate = parsed.files?.['project.data.json']
|
||||
|| parsed.files?.projectData
|
||||
|| parsed.project_json
|
||||
|| parsed.state
|
||||
|| parsed;
|
||||
const candidate = getImportCanvasCandidate(payload);
|
||||
return isExternalCanvasProject(candidate)
|
||||
? convertExternalCanvasProject(candidate, assetMap)
|
||||
: normalizeCanvasProjectState(candidate);
|
||||
@@ -1023,7 +1028,8 @@ export function InfiniteCanvasWorkspace() {
|
||||
if (!dataFile) throw new Error('没有找到 project.data.json');
|
||||
toast.info('正在读取工作流目录');
|
||||
const parsed = JSON.parse(await dataFile.text()) as { title?: unknown; projectName?: unknown; state?: unknown; nodes?: unknown };
|
||||
const requiredAssetKeys = collectExternalAssetKeys({ nodes: parsed.nodes });
|
||||
const importCandidate = getImportCanvasCandidate(parsed);
|
||||
const requiredAssetKeys = collectExternalAssetKeys(importCandidate);
|
||||
const assetFiles = allFiles.filter((file) => {
|
||||
const relativePath = file.webkitRelativePath || file.name;
|
||||
if (!relativePath.includes('/assets/') && !relativePath.startsWith('assets/')) return false;
|
||||
@@ -1041,6 +1047,10 @@ export function InfiniteCanvasWorkspace() {
|
||||
const type = file.type.startsWith('video/') ? 'video' : 'image';
|
||||
assetMap.set(key, { url, name, type });
|
||||
}));
|
||||
const importedState = getImportCanvasState(parsed, assetMap);
|
||||
if (importedState.nodes.length === 0) {
|
||||
throw new Error('没有识别到可导入的工作流节点');
|
||||
}
|
||||
applyImportedProject(parsed, assetMap);
|
||||
toast.success(`工作流目录已导入,包含 ${assetMap.size} 个素材`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
type ReactFlowInstance,
|
||||
type Viewport as FlowViewport,
|
||||
} from '@xyflow/react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { Brush, Download, Eye, FileImage, Image as ImageIcon, Layers, Link2, Loader2, Maximize2, Move, Sparkles, Type, Wand2 } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
@@ -747,9 +748,14 @@ function FlowCanvasInner({
|
||||
onPaneDoubleClick,
|
||||
}: ReactFlowCanvasProps) {
|
||||
const reactFlow = useReactFlow<CanvasFlowNode, Edge>();
|
||||
const { resolvedTheme } = useTheme();
|
||||
const draggingRef = useRef(false);
|
||||
const movingRef = useRef(false);
|
||||
const paneClickTimerRef = useRef<number | null>(null);
|
||||
const isDarkTheme = resolvedTheme === 'dark';
|
||||
const edgeStroke = isDarkTheme ? 'rgb(255 255 255 / 0.72)' : 'rgb(57 48 35 / 0.62)';
|
||||
const dotColor = isDarkTheme ? 'rgb(255 255 255 / 0.42)' : 'rgb(57 48 35 / 0.18)';
|
||||
const dotAccentColor = isDarkTheme ? 'rgb(255 255 255 / 0.24)' : 'rgb(196 126 30 / 0.26)';
|
||||
|
||||
const incomingNodes = useMemo<CanvasFlowNode[]>(() => nodes.map(node => ({
|
||||
id: node.id,
|
||||
@@ -795,13 +801,13 @@ function FlowCanvasInner({
|
||||
sourceHandle: 'output',
|
||||
targetHandle: 'input',
|
||||
type: 'smoothstep',
|
||||
markerEnd: { type: MarkerType.ArrowClosed },
|
||||
markerEnd: { type: MarkerType.ArrowClosed, color: edgeStroke },
|
||||
style: {
|
||||
stroke: 'hsl(var(--foreground) / 0.62)',
|
||||
strokeWidth: 2.25,
|
||||
stroke: edgeStroke,
|
||||
strokeWidth: 3,
|
||||
},
|
||||
interactionWidth: 18,
|
||||
})), [connections]);
|
||||
interactionWidth: 24,
|
||||
})), [connections, edgeStroke]);
|
||||
|
||||
useEffect(() => {
|
||||
setFlowNodes(current => {
|
||||
@@ -970,8 +976,8 @@ function FlowCanvasInner({
|
||||
onSelectionChange={handleSelectionChange}
|
||||
onInit={handleInit}
|
||||
>
|
||||
<Background gap={20} size={1} color="var(--canvas-dot-color)" />
|
||||
<Background gap={100} size={1.5} color="var(--canvas-dot-accent-color)" />
|
||||
<Background gap={20} size={1.15} color={dotColor} />
|
||||
<Background gap={100} size={1.8} color={dotAccentColor} />
|
||||
<Controls showInteractive={false} position="bottom-left" />
|
||||
<MiniMap
|
||||
position="bottom-right"
|
||||
|
||||
Reference in New Issue
Block a user