From 80916cb94529c3b5dfd25c9ab67692e2978a9c26 Mon Sep 17 00:00:00 2001 From: Ficik Date: Fri, 19 Jun 2026 00:22:36 +0200 Subject: [PATCH] Improve graphfx-ui node UX --- graphfx-ui/src/App.vue | 54 +++++++ graphfx-ui/src/components/GraphFxNode.vue | 47 ++++-- graphfx-ui/src/lib/graphfxToVueFlow.ts | 31 +++- graphfx-ui/src/lib/nodeDefinitions.ts | 137 ++++++++++++++++ graphfx-ui/src/style.css | 180 ++++++++++++++++------ 5 files changed, 381 insertions(+), 68 deletions(-) create mode 100644 graphfx-ui/src/lib/nodeDefinitions.ts 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() +
+ + + + + + +
+