feat: multiple scenes
This commit is contained in:
@@ -202,11 +202,13 @@ function FullscreenNodeContent({ node }: { node: AppNode }) {
|
||||
export type CanvasPageProps = {
|
||||
/** Optional recollection id for per-recollection graph loading */
|
||||
recollectionId?: string
|
||||
/** Optional scene id for per-scene graph loading */
|
||||
sceneId?: string
|
||||
/** Optional node id to focus when the canvas loads (e.g. from Katalogos artifacts). */
|
||||
focusNodeId?: string
|
||||
}
|
||||
|
||||
export function CanvasPage({ recollectionId, focusNodeId }: CanvasPageProps) {
|
||||
export function CanvasPage({ recollectionId, sceneId, focusNodeId }: CanvasPageProps) {
|
||||
const { theme } = useTheme()
|
||||
const { showMinimap } = usePlatform()
|
||||
const {
|
||||
@@ -225,7 +227,7 @@ export function CanvasPage({ recollectionId, focusNodeId }: CanvasPageProps) {
|
||||
setStateImmediate,
|
||||
save,
|
||||
saveStatus,
|
||||
} = useCanvasGraph(recollectionId)
|
||||
} = useCanvasGraph(recollectionId, sceneId)
|
||||
|
||||
const [rfInstance, setRfInstance] = React.useState<unknown>(null)
|
||||
const [renamingNodeId, setRenamingNodeId] = React.useState<string | null>(null)
|
||||
@@ -281,10 +283,10 @@ export function CanvasPage({ recollectionId, focusNodeId }: CanvasPageProps) {
|
||||
// Reset previous run state, save, then run
|
||||
resetRun()
|
||||
save()
|
||||
createAndStreamRun(recollectionId, { nodes, edges }, connectToRun).catch((err) => {
|
||||
createAndStreamRun(recollectionId, { nodes, edges }, connectToRun, sceneId).catch((err) => {
|
||||
toast.error(`Run failed: ${err.message}`)
|
||||
})
|
||||
}, [recollectionId, nodes, edges, connectToRun, save, runStatus, resetRun])
|
||||
}, [recollectionId, sceneId, nodes, edges, connectToRun, save, runStatus, resetRun])
|
||||
|
||||
const nodesRef = useRef(nodes)
|
||||
nodesRef.current = nodes
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import type { AppNode, AppEdge } from '@/lib/graph/nodeTypes'
|
||||
import { DEFAULT_NODE_STYLE } from '@/lib/graph/flowUtils'
|
||||
import { loadGraphFromStorage } from '@/app/recollections/state/recollectionGraphStorage'
|
||||
import { getSceneGraph } from '@/app/recollections/state/recollectionStore'
|
||||
|
||||
/** Ensure each edge has data.targetType from the target node (for labels without depending on nodes in edgesForFlow). */
|
||||
export function backfillEdgeTargetTypes(
|
||||
@@ -70,9 +71,12 @@ export function getExampleGraph(): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
return { nodes, edges }
|
||||
}
|
||||
|
||||
export function getInitialGraph(recollectionId: string | undefined): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
export function getInitialGraph(recollectionId: string | undefined, sceneId?: string): { nodes: AppNode[]; edges: AppEdge[] } {
|
||||
if (recollectionId) {
|
||||
const stored = loadGraphFromStorage(recollectionId)
|
||||
// Scene-aware: load from scene key; only fall back to legacy when no sceneId
|
||||
const stored = sceneId
|
||||
? getSceneGraph(recollectionId, sceneId)
|
||||
: loadGraphFromStorage(recollectionId)
|
||||
if (stored && (stored.nodes.length > 0 || stored.edges.length > 0)) {
|
||||
const nodes = stored.nodes as AppNode[]
|
||||
const edges = backfillEdgeTargetTypes(nodes, stored.edges as AppEdge[])
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useGraphStateWithHistory } from '@/hooks/useGraphStateWithHistory'
|
||||
import { getInitialGraph } from '@/app/canvas/canvasGraphUtils'
|
||||
import { saveGraphToStorage, RECOLLECTION_VERSION } from '@/app/recollections/state/recollectionGraphStorage'
|
||||
import { setSceneGraph } from '@/app/recollections/state/recollectionStore'
|
||||
import type { AppNode, AppEdge } from '@/lib/graph/nodeTypes'
|
||||
|
||||
export type SaveStatus = 'saved' | 'unsaved' | 'saving'
|
||||
@@ -18,8 +19,8 @@ export type UseCanvasGraphResult = ReturnType<typeof useGraphStateWithHistory> &
|
||||
saveStatus: SaveStatus
|
||||
}
|
||||
|
||||
export function useCanvasGraph(recollectionId: string | undefined): UseCanvasGraphResult {
|
||||
const initialGraph = useMemo(() => getInitialGraph(recollectionId), [recollectionId])
|
||||
export function useCanvasGraph(recollectionId: string | undefined, sceneId?: string): UseCanvasGraphResult {
|
||||
const initialGraph = useMemo(() => getInitialGraph(recollectionId, sceneId), [recollectionId, sceneId])
|
||||
const result = useGraphStateWithHistory(initialGraph.nodes, initialGraph.edges)
|
||||
const { nodes, edges } = result
|
||||
|
||||
@@ -47,17 +48,23 @@ export function useCanvasGraph(recollectionId: string | undefined): UseCanvasGra
|
||||
edges: edgesRef.current,
|
||||
})
|
||||
setIsSaving(true)
|
||||
saveGraphToStorage(recollectionId, {
|
||||
const graphState = {
|
||||
version: RECOLLECTION_VERSION,
|
||||
nodes: nodesRef.current,
|
||||
edges: edgesRef.current,
|
||||
})
|
||||
}
|
||||
// Save to scene-specific key if sceneId is provided, otherwise legacy key
|
||||
if (sceneId) {
|
||||
setSceneGraph(recollectionId, sceneId, graphState)
|
||||
} else {
|
||||
saveGraphToStorage(recollectionId, graphState)
|
||||
}
|
||||
const SAVING_DISPLAY_MS = 360
|
||||
setTimeout(() => {
|
||||
setLastSavedSerialized(snapshot)
|
||||
setIsSaving(false)
|
||||
}, SAVING_DISPLAY_MS)
|
||||
}, [recollectionId])
|
||||
}, [recollectionId, sceneId])
|
||||
|
||||
// Auto-save after 5 seconds of inactivity when there are unsaved changes.
|
||||
// Prevents data loss if the user closes the tab without pressing Ctrl+S.
|
||||
|
||||
Reference in New Issue
Block a user