velxio/frontend/src/lib/proSaveAction.ts

61 lines
2.4 KiB
TypeScript
Raw Normal View History

refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
/**
* Pro save-action registry.
*
* EditorPage used to import useAuthStore + SaveProjectModal + LoginPromptModal
* directly, branching on `user` to decide which modal to open when the
* user pressed Save. After Phase 3 of the OSS split, all of those live
* in the pro overlay. The OSS editor exposes a stable `triggerSaveAction()`
* that:
*
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
* - In OSS without an overlay downloads a portable `.vlx` snapshot
* of the current workspace. The user picks where it goes (browser
* save dialog) and can re-load it later via the "Open .vlx" button.
* This is Phase 4 of the OSS split gives self-hosters durable
* project persistence without requiring a DB or auth.
* - With the pro overlay loaded installSaveActionImpl() overrides
* the default. The overlay's impl inspects the auth store and opens
* SaveProjectModal (logged in) or LoginPromptModal (anonymous).
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
*
* Impl receives no arguments and returns nothing. State lives in the
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
* caller's React tree (modal open/close, project data) this registry
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
* is just the doorbell.
*/
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
import { triggerDownloadVlx } from '../utils/vlxFile';
import { useProjectStore } from '../store/useProjectStore';
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
let _impl: (() => void) | null = null;
export function installSaveActionImpl(impl: (() => void) | null): void {
_impl = impl;
}
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
function defaultSaveAction(): void {
// OSS fallback: dump the workspace to a `.vlx` file the user downloads.
// Use the loaded project's name if there is one (slug/identity tracking
// doesn't require auth — useProjectStore stays in OSS).
const proj = useProjectStore.getState().currentProject;
const name = proj?.slug ?? proj?.id ?? undefined;
const filename = triggerDownloadVlx({ name });
// eslint-disable-next-line no-console
console.info(`[oss] downloaded workspace as ${filename}`);
}
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
export function triggerSaveAction(): void {
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
const impl = _impl ?? defaultSaveAction;
try {
impl();
} catch (err) {
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
// eslint-disable-next-line no-console
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
console.warn('[oss] save-action impl threw:', err);
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
}
}
feat(oss): portable .vlx project export/import for self-hosters 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) <noreply@anthropic.com>
2026-05-15 02:33:00 +07:00
/** Whether a custom (overlay) implementation has been installed. The OSS
* default always works, so callers don't usually need this. Kept as a
* hook for UIs that want to label the button differently in OSS vs Pro
* (e.g. "Download .vlx" vs "Save project"). */
refactor(oss-split): remove auth/admin/profile frontend from OSS Phase 3 of the OSS / pro split — frontend side. Phase 2 already moved the auth/DB stack out of the OSS backend; this commit does the same for the React app. After this, the OSS image is editor + simulator + landing + docs only. What moved to the private overlay (pro/frontend/src/pro/): pages/{Login,Register,ForgotPassword,ResetPassword}Page.tsx pages/{Admin,UserProfile,Project,ProjectById}Page.tsx components/admin/{AdminBoardsTab,AdminDashboardTab,UserActivityModal}.tsx components/layout/{SaveProjectModal,LoginPromptModal}.tsx services/{authService,adminService}.ts store/useAuthStore.ts hooks/autoSaveImpl.ts New seams added so OSS components stay decoupled: * lib/proRoutes.ts — registerProRoutes()/useProRoutes() via useSyncExternalStore. mountPro() injects the moved pages at runtime; App.tsx subscribes to the registry, so registration after the initial render re-renders without a Not-Found flash. * lib/proSession.ts — registerSessionCheck()/triggerSessionCheck(). App.tsx fires this on mount instead of useAuthStore.checkSession(); pure OSS no-ops. * lib/proSaveAction.ts — installSaveActionImpl()/triggerSaveAction(). EditorPage's Save button dispatches through this; the overlay decides whether to show SaveProjectModal or LoginPromptModal based on auth state. In OSS without an overlay it's a no-op today; in Phase 4 of the split it becomes the .vlx Export entry point. OSS-side rewrites: * App.tsx drops the 8 page imports + 8 route entries; uses triggerSessionCheck() instead of useAuthStore directly. * AppHeader.tsx drops the user/login/register block entirely. The header-auth slot (introduced in Phase 1) now stays empty in OSS and gets filled by the overlay's portal mount. * EditorPage.tsx drops useAuthStore + SaveProjectModal + LoginPromptModal imports. The Save handler is now triggerSaveAction(). * LandingPage.tsx drops the dead UserMenu component (defined but never rendered) + its useAuthStore imports. * main.tsx drops the side-effect import of hooks/autoSaveImpl — the impl lives in pro now and self-registers via mountPro(). Build config: * vite.config.ts adds @velxio alias → src/. Lets the overlay import upstream modules (lib/proRoutes etc.) by stable name regardless of whether it's symlinked (local dev) or COPYed (Docker). * preserveSymlinks now gated on VITE_PRO_BUILD only (not on serve mode). Needed so Rollup keeps the overlay logically inside src/pro/ during local junction-based builds. Build verification: * OSS-only: 20-ish routes, no /login, /admin, /:username — 285 SEO pages prerendered. Bundle drops ~80-120 KB. * OSS + overlay: full 38 routes (30 upstream + 8 from registerProRoutes), HeaderAuth dropdown injected via slot, save action wired to the overlay's modal flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:31:12 +07:00
export function hasSaveActionImpl(): boolean {
return _impl !== null;
}