From b4ab7424560d6bf0651837133da313eb69434633 Mon Sep 17 00:00:00 2001 From: David Montero Crespo Date: Thu, 14 May 2026 16:33:00 -0300 Subject: [PATCH] feat(oss): portable .vlx project export/import for self-hosters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 4 of the OSS / pro split. The OSS image has no auth and no server-side persistence — without this commit, the user's workspace was ephemeral (lost on tab refresh). `.vlx` is a single-file JSON snapshot of the entire workspace (boards, file groups, components, wires, active board id) that the user can save to disk and reload later. New: utils/vlxFile.ts - buildVlxPayload() / buildVlxBlob() — pure snapshot of the current editor + simulator stores. - triggerDownloadVlx({ name? }) — anchor-click download with a safe filename. Returns the filename actually used. - parseVlxFile(File) — async reader + validator. Checks format === "velxio-project", version <= 1, and the required arrays/objects are present. Throws VlxParseError with a human- readable message on any issue. - importVlxFile(File) — convenience wrapper that parses AND calls useSimulatorStore.loadProjectState() with the result. Format intentionally mirrors the server's POST /api/projects body so a Pro user can export-from-pro and import-into-OSS losslessly (and vice-versa once Pro adds an Export button — out of scope here). lib/proSaveAction.ts: the default (no-overlay) implementation now calls triggerDownloadVlx() instead of console.info'ing about the missing handler. The Pro overlay still wins via installSaveActionImpl() — Save in Pro keeps opening SaveProjectModal. The Save button in OSS now actually saves. components/editor/FileExplorer.tsx: new "Open .vlx" button next to New + Save. Opens a hidden file input; confirms with the user before replacing the workspace (loadProjectState is destructive); surfaces VlxParseError messages via window.alert. Verified with both builds: - OSS-only: triggerSaveAction → download .vlx; FileExplorer shows 3 buttons (New, Open, Save). - OSS + overlay: Pro's installSaveActionImpl overrides — Save opens SaveProjectModal as before. Open .vlx still works (independent button, not part of the save flow). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/components/editor/FileExplorer.tsx | 61 +++++ frontend/src/lib/proSaveAction.ts | 53 ++-- frontend/src/utils/vlxFile.ts | 231 ++++++++++++++++++ 3 files changed, 324 insertions(+), 21 deletions(-) create mode 100644 frontend/src/utils/vlxFile.ts diff --git a/frontend/src/components/editor/FileExplorer.tsx b/frontend/src/components/editor/FileExplorer.tsx index 235dc573..57e33901 100644 --- a/frontend/src/components/editor/FileExplorer.tsx +++ b/frontend/src/components/editor/FileExplorer.tsx @@ -4,6 +4,7 @@ import { useEditorStore } from '../../store/useEditorStore'; import { useSimulatorStore } from '../../store/useSimulatorStore'; import type { BoardKind } from '../../types/board'; import { BOARD_KIND_LABELS } from '../../types/board'; +import { importVlxFile, VlxParseError } from '../../utils/vlxFile'; import './FileExplorer.css'; // SVG icons — same style as EditorToolbar (stroke-based, 16x16) @@ -93,6 +94,25 @@ const IcoSave = () => ( ); +const IcoOpen = () => ( + // Folder with an "open / upload arrow" — matches Save visually (both + // are project-IO actions) but points the opposite way to signal load. + + + + + +); + const IcoChevron = ({ open }: { open: boolean }) => ( = ({ onSaveClick, onNewClick }) => { + // Hidden