diff --git a/editor/components/SceneGraph.vue b/editor/components/SceneGraph.vue index 9de26a4..8a7f10d 100644 --- a/editor/components/SceneGraph.vue +++ b/editor/components/SceneGraph.vue @@ -7,7 +7,7 @@ import '@vue-flow/core/dist/style.css' import '@vue-flow/controls/dist/style.css' import '@vue-flow/core/dist/theme-default.css' import type { Connection } from '@vue-flow/core' -import { computePositions } from '../composables/useLayout' +import { computePositions, savePosition } from '../composables/useLayout' const props = defineProps<{ sceneNodes: { id: string; label: string }[] @@ -24,7 +24,7 @@ const emit = defineEmits<{ const nodes = ref([]) const edges = ref([]) -const { onNodeClick, onConnect, onNodeContextMenu, fitView } = useVueFlow() +const { onNodeClick, onConnect, onNodeContextMenu, onNodeDragStop, fitView } = useVueFlow() const ctxMenuVisible = ref(false) const ctxMenuX = ref(0) const ctxMenuY = ref(0) @@ -130,6 +130,11 @@ function closeMenu() { ctxMenuVisible.value = false } +onNodeDragStop((ev) => { + const pos = ev.node.position + savePosition(ev.node.id, Math.round(pos.x), Math.round(pos.y)) +}) + onConnect((conn: Connection) => { if (conn.source && conn.target) emit('addEdge', conn.source, conn.target) }) diff --git a/editor/composables/useLayout.ts b/editor/composables/useLayout.ts index 72db0e0..594ba41 100644 --- a/editor/composables/useLayout.ts +++ b/editor/composables/useLayout.ts @@ -12,12 +12,39 @@ interface EdgeInfo { const NODE_W = 180 const NODE_H = 60 +const POSITIONS_KEY = 'editor_positions' + +export function loadSavedPositions(): Record { + try { + return JSON.parse(localStorage.getItem(POSITIONS_KEY) || '{}') + } catch { return {} } +} + +export function savePosition(nodeId: string, x: number, y: number) { + const saved = loadSavedPositions() + saved[nodeId] = { x, y } + localStorage.setItem(POSITIONS_KEY, JSON.stringify(saved)) +} export function computePositions( nodes: NodeInfo[], edges: EdgeInfo[], _startScene: string, ): Map { + const saved = loadSavedPositions() + const result = new Map() + + const unsaved: NodeInfo[] = [] + for (const n of nodes) { + if (saved[n.id]) { + result.set(n.id, saved[n.id]) + } else { + unsaved.push(n) + } + } + + if (unsaved.length === 0) return result + const g = new dagre.graphlib.Graph() g.setGraph({ rankdir: 'LR', @@ -28,11 +55,11 @@ export function computePositions( }) g.setDefaultEdgeLabel(() => ({})) - for (const n of nodes) { + for (const n of unsaved) { g.setNode(n.id, { width: NODE_W, height: NODE_H }) } - const nodeIds = new Set(nodes.map((n) => n.id)) + const nodeIds = new Set(unsaved.map((n) => n.id)) for (const e of edges) { if (nodeIds.has(e.source) && nodeIds.has(e.target)) { g.setEdge(e.source, e.target) @@ -41,13 +68,12 @@ export function computePositions( dagre.layout(g) - const positions = new Map() - for (const n of nodes) { + for (const n of unsaved) { const node = g.node(n.id) if (node) { - positions.set(n.id, { x: node.x - NODE_W / 2, y: node.y - NODE_H / 2 }) + result.set(n.id, { x: node.x - NODE_W / 2, y: node.y - NODE_H / 2 }) } } - return positions + return result }