diff --git a/graphfx-ui/src/App.vue b/graphfx-ui/src/App.vue index e181794..3567126 100644 --- a/graphfx-ui/src/App.vue +++ b/graphfx-ui/src/App.vue @@ -19,6 +19,8 @@ const edges = ref([]) const warnings = ref([]) const layoutDirection = ref<'LR' | 'TB'>('LR') const preferSavedPositions = ref(false) +const selectedSaveSlot = ref(localStorage.getItem('GRAPHFX_UI_SELECTED_SAVE_SLOT') || 'My first graph') +const fileInput = ref(null) const { fitView } = useVueFlow() const stats = computed(() => ({ @@ -51,6 +53,49 @@ function loadSample() { refreshFlow() } +function saveSlotKey(name = selectedSaveSlot.value) { + return `GRAPHFX_UI_SAVE_${name}` +} + +function saveGraph() { + refreshFlow() + if (parseError.value) return + localStorage.setItem('GRAPHFX_UI_SELECTED_SAVE_SLOT', selectedSaveSlot.value) + localStorage.setItem(saveSlotKey(), JSON.stringify(graph.value, null, 2)) +} + +function loadGraph() { + const saved = localStorage.getItem(saveSlotKey()) + if (!saved) { + parseError.value = `No saved graph named "${selectedSaveSlot.value}".` + return + } + localStorage.setItem('GRAPHFX_UI_SELECTED_SAVE_SLOT', selectedSaveSlot.value) + editorText.value = saved + refreshFlow() +} + +function exportGraph() { + refreshFlow() + if (parseError.value) return + const blob = new Blob([JSON.stringify(graph.value, null, 2)], { type: 'application/json' }) + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = `${selectedSaveSlot.value || 'graphfx-graph'}.json` + link.click() + URL.revokeObjectURL(url) +} + +async function importGraph(event: Event) { + const input = event.target as HTMLInputElement + const file = input.files?.[0] + if (!file) return + editorText.value = await file.text() + input.value = '' + refreshFlow() +} + function relayout() { const model = graphFxToVueFlow(graph.value) nodes.value = layoutGraph(model.nodes, model.edges, { @@ -86,6 +131,15 @@ refreshFlow() +
+ + + + + + +
+