diff --git a/frontend/src/desktop/menu.ts b/frontend/src/desktop/menu.ts index 97355647..f677163a 100644 --- a/frontend/src/desktop/menu.ts +++ b/frontend/src/desktop/menu.ts @@ -224,6 +224,22 @@ function newProject(): void { // Clear the compile output panel so old build logs don't carry over. compileLogs.clear(); + + // Leave whatever project URL we were on: staying there would reload the + // OLD project over this fresh workspace on refresh. replaceState (not + // navigateTo's pushState) so the back button can't pop to the stale + // project URL either; the popstate dispatch lets React Router render + // the plain editor route. + const cur = window.location.pathname; + const localeMatch = cur.match(/^\/([a-z]{2}(?:-[a-z]{2})?)\b/); + const prefix = localeMatch && LOCALES.includes(localeMatch[1] as Locale) + ? `/${localeMatch[1]}` + : ''; + const editorPath = `${prefix}/editor`; + if (cur !== editorPath) { + window.history.replaceState(null, '', editorPath); + window.dispatchEvent(new PopStateEvent('popstate')); + } } async function checkForUpdates(): Promise { diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index c614d4e4..0e2d00b4 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -6,6 +6,7 @@ import React, { useRef, useState, useCallback, useEffect, lazy, Suspense } from import { useTranslation } from 'react-i18next'; import { startSimulation } from '../simulation/spice/start'; import { useSEO } from '../utils/useSEO'; +import { getLocaleFromPath, localizedPath } from '../i18n/path'; import { restoreStashedWorkspace } from '../utils/workspaceDraft'; import { CodeEditor } from '../components/editor/CodeEditor'; import { EditorToolbar } from '../components/editor/EditorToolbar'; @@ -219,6 +220,16 @@ export const EditorPage: React.FC = () => { .getState() .addBoard('arduino-uno', DEFAULT_BOARD_POSITION.x, DEFAULT_BOARD_POSITION.y); useSimulatorStore.getState().setActiveBoardId(newId); + // The workspace no longer belongs to whatever project URL we were on — + // leaving it would silently reload the OLD project over this fresh + // workspace on refresh (and via the back button). replaceState, not + // pushState: a back-entry pointing at the stale project URL would + // remount the project route and cause exactly that reload. + const locale = getLocaleFromPath(window.location.pathname); + const editorPath = localizedPath('/editor', locale); + if (window.location.pathname !== editorPath) { + window.history.replaceState(null, '', editorPath); + } }, [t]); // Track mobile breakpoint diff --git a/frontend/src/utils/vlxFile.ts b/frontend/src/utils/vlxFile.ts index f050749d..c6cf5f92 100644 --- a/frontend/src/utils/vlxFile.ts +++ b/frontend/src/utils/vlxFile.ts @@ -34,6 +34,7 @@ import type { Component } from '../types/component'; import type { Wire } from '../types/wire'; import { useEditorStore, chipFileGroupId } from '../store/useEditorStore'; import { useSimulatorStore } from '../store/useSimulatorStore'; +import { useProjectStore } from '../store/useProjectStore'; const VLX_FORMAT = 'velxio-project'; const VLX_VERSION = 1; @@ -228,6 +229,12 @@ export async function parseVlxFile(file: File): Promise { */ export async function importVlxFile(file: File): Promise { const payload = await parseVlxFile(file); + // CRITICAL — sever the current project identity BEFORE mutating any store + // (same guard as loadExample.ts). With a saved project open, the auto-save + // hook would otherwise see the imported content as dirty edits on the OLD + // projectId and silently PUT the .vlx contents over the user's saved + // project (and push the clobber to GitHub when the project is linked). + useProjectStore.getState().clearCurrentProject(); useSimulatorStore.getState().loadProjectState({ boards: payload.boards as unknown as BoardInstance[], fileGroups: payload.fileGroups,