feat: implement project management features with routing, including project creation, deletion, renaming, and graph persistence
This commit is contained in:
@@ -57,6 +57,12 @@ import {
|
||||
isConnectionAllowed,
|
||||
} from '@/lib/nodeRegistry'
|
||||
import type { AppNode, AppEdge } from '@/lib/nodeTypes'
|
||||
import {
|
||||
loadGraphFromStorage,
|
||||
saveGraphToStorage,
|
||||
PROJECT_FILE_EXT,
|
||||
PROJECT_VERSION,
|
||||
} from '@/app/platform/projectGraphStorage'
|
||||
|
||||
const SNAP_GRID: [number, number] = [15, 15]
|
||||
const snapToGrid = (x: number, y: number): { x: number; y: number } => ({
|
||||
@@ -110,9 +116,6 @@ function getExampleGraph(): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
}
|
||||
}
|
||||
|
||||
const PROJECT_FILE_EXT = '.zui.json'
|
||||
const PROJECT_VERSION = 1
|
||||
|
||||
export type CanvasMessage = { type: 'success' | 'error'; text: string }
|
||||
|
||||
function FlowFitViewOnLoad() {
|
||||
@@ -129,8 +132,19 @@ export type CanvasPageProps = {
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
|
||||
function getInitialGraph(projectId: string | undefined): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
if (projectId) {
|
||||
const stored = loadGraphFromStorage(projectId)
|
||||
if (stored && (stored.nodes.length > 0 || stored.edges.length > 0)) {
|
||||
return { nodes: stored.nodes as AppNode[], edges: stored.edges as AppEdge[] }
|
||||
}
|
||||
}
|
||||
return getExampleGraph()
|
||||
}
|
||||
|
||||
export function CanvasPage({ projectId }: CanvasPageProps) {
|
||||
const { theme } = useTheme()
|
||||
const initialGraph = useMemo(() => getInitialGraph(projectId), [projectId])
|
||||
const {
|
||||
nodes,
|
||||
edges,
|
||||
@@ -145,7 +159,20 @@ export function CanvasPage({ projectId: _projectId }: CanvasPageProps) {
|
||||
canUndo,
|
||||
canRedo,
|
||||
setStateImmediate,
|
||||
} = useGraphStateWithHistory(getExampleGraph().nodes, getExampleGraph().edges)
|
||||
} = useGraphStateWithHistory(initialGraph.nodes, initialGraph.edges)
|
||||
|
||||
// Persist graph to localStorage when projectId is set (debounced)
|
||||
const saveTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
React.useEffect(() => {
|
||||
if (!projectId) return
|
||||
const save = () => {
|
||||
saveGraphToStorage(projectId, { version: PROJECT_VERSION, nodes, edges })
|
||||
}
|
||||
saveTimeoutRef.current = setTimeout(save, 500)
|
||||
return () => {
|
||||
if (saveTimeoutRef.current) clearTimeout(saveTimeoutRef.current)
|
||||
}
|
||||
}, [projectId, nodes, edges])
|
||||
|
||||
const importInputRef = useRef<HTMLInputElement | null>(null)
|
||||
const [rfInstance, setRfInstance] = React.useState<unknown>(null)
|
||||
|
||||
Reference in New Issue
Block a user