From 3629e0b28c28c73eec968e457a1c142a72a12fd9 Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 27 Jan 2026 16:33:45 +0530 Subject: [PATCH 1/6] feat: add electron support initial --- package-lock.json | 14 +- package.json | 2 +- src-node/package-lock.json | 8 +- src-node/package.json | 2 +- .../new-project/assets/js/code-editor.js | 2 +- src/document/DocumentCommandHandlers.js | 7 +- .../default/HealthData/SendToAnalytics.js | 6 +- src/index.html | 161 +++++++++++++----- src/loggerSetup.js | 11 +- src/phoenix/shell.js | 17 +- src/phoenix/virtual-server-loader.js | 4 +- src/view/ThemeManager.js | 2 +- test/virtual-server-loader.js | 4 +- 13 files changed, 167 insertions(+), 73 deletions(-) diff --git a/package-lock.json b/package-lock.json index 56b5cad950..be4da80386 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@floating-ui/dom": "^0.5.4", "@fortawesome/fontawesome-free": "^6.1.2", "@highlightjs/cdn-assets": "^11.5.1", - "@phcode/fs": "^4.0.1", + "@phcode/fs": "^4.0.2", "@phcode/language-support": "^1.1.0", "@pixelbrackets/gfm-stylesheet": "^1.1.0", "@prettier/plugin-php": "^0.22.2", @@ -898,9 +898,9 @@ } }, "node_modules/@phcode/fs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.1.tgz", - "integrity": "sha512-qBK7UJxEVgrM1MMGtv7vFLd6jcd50nwZi9PkD2fVKpXUjFTAgY5324agrs7xfS8QTkFqGRYnrHghirH3+roQLg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.2.tgz", + "integrity": "sha512-vjR2EtMizOj/tf30FObT1KkbBFwLRkVWFsnyUdt0sex6H/lpxRgjKp7NO8YBUKROw66eMoL+7kGyHKAlfV9zWA==", "dependencies": { "chokidar": "^3.5.3", "ignore": "^5.2.4", @@ -13612,9 +13612,9 @@ } }, "@phcode/fs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.1.tgz", - "integrity": "sha512-qBK7UJxEVgrM1MMGtv7vFLd6jcd50nwZi9PkD2fVKpXUjFTAgY5324agrs7xfS8QTkFqGRYnrHghirH3+roQLg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.2.tgz", + "integrity": "sha512-vjR2EtMizOj/tf30FObT1KkbBFwLRkVWFsnyUdt0sex6H/lpxRgjKp7NO8YBUKROw66eMoL+7kGyHKAlfV9zWA==", "requires": { "chokidar": "^3.5.3", "ignore": "^5.2.4", diff --git a/package.json b/package.json index 459eac522a..cf71502116 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@floating-ui/dom": "^0.5.4", "@fortawesome/fontawesome-free": "^6.1.2", "@highlightjs/cdn-assets": "^11.5.1", - "@phcode/fs": "^4.0.1", + "@phcode/fs": "^4.0.2", "@phcode/language-support": "^1.1.0", "@pixelbrackets/gfm-stylesheet": "^1.1.0", "@prettier/plugin-php": "^0.22.2", diff --git a/src-node/package-lock.json b/src-node/package-lock.json index 4039abc397..e74b9d573b 100644 --- a/src-node/package-lock.json +++ b/src-node/package-lock.json @@ -10,7 +10,7 @@ "license": "GNU-AGPL3.0", "dependencies": { "@expo/sudo-prompt": "^9.3.2", - "@phcode/fs": "^4.0.1", + "@phcode/fs": "^4.0.2", "cross-spawn": "^7.0.6", "lmdb": "^2.9.2", "mime-types": "^2.1.35", @@ -173,9 +173,9 @@ ] }, "node_modules/@phcode/fs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.1.tgz", - "integrity": "sha512-qBK7UJxEVgrM1MMGtv7vFLd6jcd50nwZi9PkD2fVKpXUjFTAgY5324agrs7xfS8QTkFqGRYnrHghirH3+roQLg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@phcode/fs/-/fs-4.0.2.tgz", + "integrity": "sha512-vjR2EtMizOj/tf30FObT1KkbBFwLRkVWFsnyUdt0sex6H/lpxRgjKp7NO8YBUKROw66eMoL+7kGyHKAlfV9zWA==", "dependencies": { "chokidar": "^3.5.3", "ignore": "^5.2.4", diff --git a/src-node/package.json b/src-node/package.json index 77610b2632..0220fdc5b9 100644 --- a/src-node/package.json +++ b/src-node/package.json @@ -19,7 +19,7 @@ }, "IMPORTANT!!": "Adding things here will bloat up the package size", "dependencies": { - "@phcode/fs": "^4.0.1", + "@phcode/fs": "^4.0.2", "open": "^10.1.0", "npm": "10.1.0", "ws": "^8.17.1", diff --git a/src/assets/new-project/assets/js/code-editor.js b/src/assets/new-project/assets/js/code-editor.js index 77cb07768b..5eea795ef6 100644 --- a/src/assets/new-project/assets/js/code-editor.js +++ b/src/assets/new-project/assets/js/code-editor.js @@ -178,7 +178,7 @@ function _attachSettingBtnEventListeners() { function _openURLInTauri(url) { // in tauri, the tag will not open a browser window. So we have to use phcode apis to do it. // else, the browser itself will open the url. so we dont have to do this in normal browsers. - if(window.top.__TAURI__) { + if(window.top.__IS_NATIVE_SHELL__) { window.top.Phoenix.app.openURLInDefaultBrowser(url); } } diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index 9029468d3d..695e581c1b 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -2253,15 +2253,13 @@ define(function (require, exports, module) { } } function attachTauriUnloadHandler() { - window.__TAURI__.window.appWindow.onCloseRequested((event)=>{ + Phoenix.app.onCloseWindowRequested(()=>{ _forceQuitIfNeeded(); if(closeInProgress){ - event.preventDefault(); - return; + return false; } closeInProgress = true; PreferencesManager.setViewState("windowClosingTime", new Date().getTime()); - event.preventDefault(); _handleWindowGoingAway(null, closeSuccess=>{ console.log('close success: ', closeSuccess); exitWaitPromises.push(_safeFlushDB()); @@ -2277,6 +2275,7 @@ define(function (require, exports, module) { console.log('close fail: ', closeFail); closeInProgress = false; }); + return false; }); } diff --git a/src/extensions/default/HealthData/SendToAnalytics.js b/src/extensions/default/HealthData/SendToAnalytics.js index 459f41c331..8c200ccf26 100644 --- a/src/extensions/default/HealthData/SendToAnalytics.js +++ b/src/extensions/default/HealthData/SendToAnalytics.js @@ -135,14 +135,16 @@ define(function (require, exports, module) { _sendStorageMetrics(); } + const envName = window.__TAURI__ ? "tauri-" : (window.__ELECTRON__ ? "electron-" : ""); + let bugsnagPerformanceInited = false; function _initBugsnagPerformance() { bugsnagPerformanceInited = true; BugsnagPerformance.start({ apiKey: '94ef94f4daf871ca0f2fc912c6d4764d', appVersion: AppConfig.version, - releaseStage: window.__TAURI__ ? - `tauri-${AppConfig.config.bugsnagEnv}-${Phoenix.platform}` : AppConfig.config.bugsnagEnv, + releaseStage: window.__IS_NATIVE_SHELL__ ? + `${envName}${AppConfig.config.bugsnagEnv}-${Phoenix.platform}` : AppConfig.config.bugsnagEnv, autoInstrumentRouteChanges: false, autoInstrumentNetworkRequests: false, autoInstrumentFullPageLoads: false diff --git a/src/index.html b/src/index.html index 6454d7105b..f7c20cdafe 100644 --- a/src/index.html +++ b/src/index.html @@ -78,6 +78,9 @@ "\n\nYou will now be redirected to phcode.dev."); window.location = "https://phcode.dev"; } + if(window.electronAPI) { + window.__ELECTRON__ = true; + } if(location.href.startsWith("tauri://") || location.href.startsWith('https://tauri.localhost')){ const errorMessage = `You should use custom protocol phtauri:// instead of tauri protocol ${location.href} .`; alert(errorMessage); @@ -94,20 +97,21 @@ const isSpecRunnerWindow = window.location.pathname.endsWith("/SpecRunner.html"); return isTestPhoenixWindow || isSpecRunnerWindow; } - let canAccessTauri = false; + let canAccessNativeShell = false; try { // This is to allow phoenix live previews in phoenix desktop editor so that we can // develop phoenix inside phoenix. // This will throw an error if the live preview iframe and parent are cross-origin which is case in phcode - canAccessTauri = window.top.window.__TAURI__; - canAccessTauri = true; + canAccessNativeShell = window.top.window.__TAURI__; + canAccessNativeShell = window.top.window.electronAPI; + canAccessNativeShell = true; } catch (e) { // Cross-origin, exception caught, isSameOrigin remains false } // In Mac !! the window.__TAURI__ object is present in iframes, even if it doesnt works. so we are forced // to use if(window.parent.window !== window) instead of if(!window.__TAURI__) here. - if(canAccessTauri && window.parent.window !== window && window.top.window.__TAURI__) { + if(canAccessNativeShell && window.parent.window !== window && window.top.window.__TAURI__) { // This is only intended to be used for in tests where phoenix is loaded inside iframes. // this means that we are loaded in an iframe in the specrunner. Tauri doesnt expose tauri APIs // in its iframes, so we directly use the top most windows tauri api object for tests to run properly. @@ -163,6 +167,58 @@ } // fs special handling end } + // linux electron api iframe injection for tests + if(canAccessNativeShell && window.parent.window !== window && window.top.window.__ELECTRON__) { + // This is only intended to be used for in tests where phoenix is loaded inside iframes. + // this means that we are loaded in an iframe in the specrunner. Tauri doesnt expose tauri APIs + // in its iframes, so we directly use the top most windows tauri api object for tests to run properly. + console.warn("Phoenix is loaded in iframe, attempting to use electron APIs from window.top.electronAPI"); + window.electronAPI = window.top.window.electronAPI; + } + if(window.__TAURI__ || window.__ELECTRON__) { + window.__IS_NATIVE_SHELL__ = true; + } + // Common function to process boot vars results for both Tauri and Electron + function _processBootVarsResults(results, pathSep, LOCALSTORAGE_KEY, bootStartTime) { + // results array: [appName, documentDir, appLocalDir, tempDir, homeDir] + const idx = {appName: 0, docDir: 1, appLocal: 2, temp: 3, home: 4}; + + window._tauriBootVars.appname = results[idx.appName].value; + + if(results[idx.docDir].status === "fulfilled"){ + window._tauriBootVars.documentDir = results[idx.docDir].value; + } else if(results[idx.home] && results[idx.home].status === "fulfilled"){ + // some linux distros may not have Documents folder. so we use the home folder + // https://github.com/phcode-dev/phoenix/issues/1729 + window._tauriBootVars.documentDir = results[idx.home].value; + } else if(results[idx.appLocal].status === "fulfilled"){ + console.error("Unable to determine user documents dir, defaulting to app data dir as document dir:", + results[idx.docDir].reason, "home folder error: ", results[idx.home]?.reason); + window._tauriBootVars.documentDir = results[idx.appLocal].value; + } else { + alert("Could not resolve user documents directory. \nPhoenix Code cannot start."); + } + + if(results[idx.appLocal].status === "fulfilled") { + window._tauriBootVars.appLocalDir = results[idx.appLocal].value; + } else { + alert("Could not resolve Application Data directory. \nPhoenix Code cannot start."); + } + + // For tests, documents dir is localAppDataDir/testDocuments to keep user documents garbage free for tests + // Also In github actions, the tauri get doc dir call gets stuck indefinitely + if(_isTestWindow()){ + if(!window._tauriBootVars.documentDir.endsWith(pathSep)){ + window._tauriBootVars.documentDir = window._tauriBootVars.documentDir + pathSep; + } + window._tauriBootVars.documentDir = `${window._tauriBootVars.documentDir}testDocuments${pathSep}`; + } + + window._tauriBootVars.appLocalDir = results[idx.appLocal].value; + window._tauriBootVars.tempDir = results[idx.temp].value; + window._tauriBootVars.bootstrapTime = Date.now() - bootStartTime; + localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(window._tauriBootVars)); + } if(window.__TAURI__) { function setupTauriBootVars() { // this is used by storage.js (window.PhStore) to restore our persistent storage layer in tauri. @@ -201,45 +257,63 @@ window._tauriBootVarsPromise = Promise.allSettled([appNamePromise, documentDirPromise, appLocalDirPromise, tempDirPromise, homeDirPromise]) .then((results) => { - window._tauriBootVars.appname = results[0].value; - - if(results[1].status === "fulfilled"){ - window._tauriBootVars.documentDir = results[1].value; - } else if(results[4].status === "fulfilled"){ - // some linux distros may not have Documents folder. so we use the home folder - // https://github.com/phcode-dev/phoenix/issues/1729 - window._tauriBootVars.documentDir = results[4].value; - } else if(results[2].status === "fulfilled"){ - console.error("Unable to determine user documents dir, defaulting to app data dir as document dir:", - results[1].reason, "home folder error: ", results[4].reason); - window._tauriBootVars.documentDir = results[2].value; - } else { - alert("Could not resolve user documents directory. \nPhoenix Code cannot start."); - } - - if(results[2].status === "fulfilled") { - window._tauriBootVars.appLocalDir = results[2].value; - } else { - alert("Could not resolve Application Data directory. \nPhoenix Code cannot start."); - } - - // For tests, documents dir is localAppDataDir/testDocuments to keep user documents garbage free for tests - // Also In github actions, the tauri get doc dir call gets stuck indefinitely - if(_isTestWindow()){ - if(!window._tauriBootVars.documentDir.endsWith(window.__TAURI__.path.sep)){ - window._tauriBootVars.documentDir = window._tauriBootVars.documentDir + window.__TAURI__.path.sep; - } - window._tauriBootVars.documentDir = `${window._tauriBootVars.documentDir}testDocuments${window.__TAURI__.path.sep}`; + _processBootVarsResults(results, window.__TAURI__.path.sep, TAURI_BOOT_VARS_LOCALSTORAGE_KEY, tauriBootStartTime); + }); + } + setupTauriBootVars(); + } + if(window.__ELECTRON__) { + function setupElectronBootVars() { + // this is used by storage.js (window.PhStore) to restore our persistent storage layer in electron. + window._tauriStorageRestorePromise = window.electronFSAPI.appLocalDataDir() + .then(appDataDir => { + const sep = window.electronFSAPI.path.sep; + const storagePath = `${appDataDir}${appDataDir.endsWith(sep) ? "" : sep}storageDB${sep}storageDBDump.json`; + return window.electronFSAPI.fsReadFile(storagePath); + }) + .then(data => { + if(data && data.__fsError) { + throw new Error(data.message); } - //Documents dir special case for tests + return data; + }) + .catch(err => { + console.error("First boot detected or Failed to init storage from cache." + + " If first boot, ignore this error", err); + }); + // increase this version number if you are modifying any boot vars + const TAURI_BOOT_VARS_LOCALSTORAGE_KEY = "tauriBootVarsV1"; + const tauriBootVarsStr = localStorage.getItem(TAURI_BOOT_VARS_LOCALSTORAGE_KEY); + if(tauriBootVarsStr) { + try{ + window._tauriBootVars = JSON.parse(tauriBootVarsStr); + window._tauriBootVarsPromise = Promise.resolve("Resolved from localstorage."); + } catch (e) { + console.error("Error getting boot vars from localstorage. Falling back to make electron bootstrap calls.", e); + } + } + const appNamePromise = window.electronAppAPI.getAppName(); + // for running tests, the user document dir is set to app data dir as we dont want to + // corrupt user documents dir for tests + let documentDirPromise, homeDirPromise; + if(_isTestWindow()){ + documentDirPromise = window.electronFSAPI.appLocalDataDir(); // appdata/testDocuments will be appended below + } else { + documentDirPromise = window.electronFSAPI.documentDir(); + homeDirPromise = window.electronFSAPI.homeDir(); + } - window._tauriBootVars.appLocalDir = results[2].value; - window._tauriBootVars.tempDir = results[3].value; - window._tauriBootVars.bootstrapTime = Date.now() - tauriBootStartTime; - localStorage.setItem(TAURI_BOOT_VARS_LOCALSTORAGE_KEY, JSON.stringify(window._tauriBootVars)); + const appLocalDirPromise = window.electronFSAPI.appLocalDataDir(); + const tempDirPromise = window.electronFSAPI.tempDir(); + window._tauriBootVars = {}; + const tauriBootStartTime = Date.now(); + window._tauriBootVarsPromise = Promise.allSettled([appNamePromise, documentDirPromise, + appLocalDirPromise, tempDirPromise, homeDirPromise]) + .then((results) => { + _processBootVarsResults(results, window.electronFSAPI.path.sep, TAURI_BOOT_VARS_LOCALSTORAGE_KEY, tauriBootStartTime); }); } - setupTauriBootVars(); + setupElectronBootVars(); } // environment setup for boot. do not move out of index html!! @@ -290,6 +364,7 @@ isDeskTop: !_mobileAndTabletCheck() && !_mobileCheck(), isChromeOS: /CrOS/.test(navigator.userAgent), isTauri: !!window.__TAURI__, + isElectron: !!window.__ELECTRON__, mobile: { isAndroid: (navigator.userAgent.match(/Android/i) !== null), isIos: (navigator.userAgent.match(/iPhone|iPad|iPod/i) !== null), @@ -370,7 +445,7 @@ 'https://create.phcode.dev': true } }; - window.Phoenix.isNativeApp = window.Phoenix.browser.isTauri; + window.Phoenix.isNativeApp = window.__IS_NATIVE_SHELL__; window.Phoenix.TRUSTED_ORIGINS[location.origin] = true; Phoenix.isSupportedBrowser = Phoenix.isNativeApp || (Phoenix.browser.isDeskTop && ("serviceWorker" in navigator)); @@ -667,11 +742,11 @@ _resetCacheIfNeeded(); function _addOrRemoveSplashScreenIfNeeded() { - if(!window.__TAURI__) { + if(!window.__IS_NATIVE_SHELL__) { document.getElementById('phoenix-loading-splash-screen-overlay').classList.remove('forced-hidden') } - if(window.testEnvironment || window.__TAURI__){ - // tauri means local builds and, it loads up pretty fast, so splash screen + if(window.testEnvironment || window.__IS_NATIVE_SHELL__){ + // tauri means local builds and, it loads up pretty fast, so no splash screen document.getElementById('phoenix-loading-splash-screen-overlay').remove(); window.splashScreenPresent = false; } diff --git a/src/loggerSetup.js b/src/loggerSetup.js index 243314b35c..7f39c02bbd 100644 --- a/src/loggerSetup.js +++ b/src/loggerSetup.js @@ -32,6 +32,7 @@ "https://dev.phcode.dev", // dev url "https://staging.phcode.dev" // staging url ]; + const nativeEnvName = window.__TAURI__ ? "tauri" : (window.__ELECTRON__ ? "electron" : ""); let isBugsnagLoggableURL = false; // to test bugsnag in dev builds, set this to true for(let loggableURL of loggableURLS){ if(window.location.href.startsWith(loggableURL)){ @@ -256,7 +257,7 @@ context = 'mobile'; } if(Phoenix.isNativeApp) { - context = `tauri-${context}`; + context = `${nativeEnvName}-${context}`; } if(Phoenix.browser.isMobile || Phoenix.browser.isTablet) { let device = 'unknownDevice'; @@ -298,12 +299,14 @@ Bugsnag.start({ apiKey: '94ef94f4daf871ca0f2fc912c6d4764d', context: context, - appType: Phoenix.browser && Phoenix.isNativeApp ? "tauri" : "browser", + appType: nativeEnvName || "browser", collectUserIp: false, appVersion: AppConfig.version, enabledReleaseStages: [ 'development', 'production', 'staging', - 'tauri-development', 'tauri-production', 'tauri-staging'], - releaseStage: window.__TAURI__ ? "tauri-" + AppConfig.config.bugsnagEnv : AppConfig.config.bugsnagEnv, + 'tauri-development', 'tauri-production', 'tauri-staging', + 'electron-development', 'electron-production', 'electron-staging'], + releaseStage: window.__IS_NATIVE_SHELL__ ? + `${nativeEnvName}-${AppConfig.config.bugsnagEnv}` : AppConfig.config.bugsnagEnv, // https://docs.bugsnag.com/platforms/javascript/#logging-breadcrumbs // breadcrumbs is disabled as it seems a bit intrusive in Pheonix even-though it might help with debugging. // only manual explicit privacy ready breadcrumbs are allowed diff --git a/src/phoenix/shell.js b/src/phoenix/shell.js index e4fdd6ac99..d7437edec9 100644 --- a/src/phoenix/shell.js +++ b/src/phoenix/shell.js @@ -44,7 +44,7 @@ initVFS(); const MAX_ALLOWED_TAURI_WINDOWS = 30; const CLI_ARGS_QUERY_PARAM = 'CLI_ARGS'; const CLI_CWD_QUERY_PARAM = 'CLI_CWD'; -let cliArgs, cliCWD, singleInstanceCLIHandler, quitTimeAppUpdateHandler; +let cliArgs, cliCWD, singleInstanceCLIHandler, quitTimeAppUpdateHandler, closeHandlerCb; const PHOENIX_WINDOW_PREFIX = 'phcode-'; const PHOENIX_EXTENSION_WINDOW_PREFIX = 'extn-'; @@ -137,6 +137,21 @@ Phoenix.app = { } return window.__TAURI__.invoke("toggle_devtools", {}); }, + onCloseWindowRequested: function (_closeHandlerCb) { + if(typeof _closeHandlerCb !== 'function'){ + throw new Error("onCloseWindowRequested callback must be a function!"); + } + if(closeHandlerCb){ + throw new Error("onCloseWindowRequested can only be registered once!"); + } + closeHandlerCb = _closeHandlerCb; + window.__TAURI__.window.appWindow.onCloseRequested((event)=>{ + const shouldClose = closeHandlerCb(); + if(!shouldClose){ + event.preventDefault(); + } + }); + }, closeWindow: async function (forceClose) { if(!Phoenix.isNativeApp){ throw new Error("closeWindow is not supported in browsers"); diff --git a/src/phoenix/virtual-server-loader.js b/src/phoenix/virtual-server-loader.js index 77bf165eb7..0678015994 100644 --- a/src/phoenix/virtual-server-loader.js +++ b/src/phoenix/virtual-server-loader.js @@ -56,7 +56,7 @@ function getRoute(){ return `phoenix/vfs`; } -if(!window.__TAURI__) { +if(!window.__IS_NATIVE_SHELL__) { window.fsServerUrl = _getBaseURL() + getRoute() + "/"; } @@ -86,7 +86,7 @@ async function shouldUpdate() { /** * Register Phoenix PWA and nohost web server service worker, passing `route` or other options. */ -if (!window.__TAURI__ && _isServiceWorkerLoaderPage() && 'serviceWorker' in navigator) { +if (!window.__IS_NATIVE_SHELL__ && _isServiceWorkerLoaderPage() && 'serviceWorker' in navigator) { logger.leaveTrail("Service worker loader: Loading from page..." + window.location.href); // We cannot realistically change the url of the service worker without causing major problems in service worker // load. We will have to unregister and load a new service worker and there is no way to stop the already running diff --git a/src/view/ThemeManager.js b/src/view/ThemeManager.js index 4a06887dbb..f076693159 100644 --- a/src/view/ThemeManager.js +++ b/src/view/ThemeManager.js @@ -399,7 +399,7 @@ define(function (require, exports, module) { let deferred = new $.Deferred(); const themeName = options.name || options.theme.title, - themeFolder = window.__TAURI__ ? + themeFolder = window.__IS_NATIVE_SHELL__ ? brackets.app.getApplicationSupportDirectory() + `/assets/extensions/user/${themeName}/` : brackets.app.getApplicationSupportDirectory() + `/extensions/user/${themeName}/`; diff --git a/test/virtual-server-loader.js b/test/virtual-server-loader.js index 68393559fb..edbb30b4c2 100644 --- a/test/virtual-server-loader.js +++ b/test/virtual-server-loader.js @@ -37,7 +37,7 @@ function getRoute(){ return `${basePath}/phoenix/vfs`; } -if(!window.__TAURI__) { +if(!window.__IS_NATIVE_SHELL__) { window.fsServerUrl = window.location.origin + getRoute() + "/"; } @@ -52,7 +52,7 @@ function serverInstall() { /** * Register the nohost service worker, passing `route` or other options. */ -if ('serviceWorker' in navigator && !window.__TAURI__) { +if ('serviceWorker' in navigator && !window.__IS_NATIVE_SHELL__) { console.log(window.location.href); const wb = new Workbox(`virtual-server-test.js?route=${getRoute()}`); From 992c0a9fe978299c8f30ac90b9629c1523515f45 Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 27 Jan 2026 17:20:04 +0530 Subject: [PATCH 2/6] refactor: asset url apis prepare for electron migration --- src/phoenix/init_vfs.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/phoenix/init_vfs.js b/src/phoenix/init_vfs.js index 881c74e97a..ff1e17e7e6 100644 --- a/src/phoenix/init_vfs.js +++ b/src/phoenix/init_vfs.js @@ -39,6 +39,18 @@ let extensionDIR, tempDIR, userProjectsDir; +function _getNativeAssetUrl(fullFilePath) { + if(window.__TAURI__) { + if(fullFilePath.startsWith(tauriAssetServeDir)){ + const platformPath = fs.getTauriPlatformPath(fullFilePath) + .replace(/\\/g, "/"); // windows style paths to unix style c:\x\y to c:/x/y + return decodeURIComponent(window.__TAURI__.tauri.convertFileSrc(platformPath)); + } + return null; + } + return null; +} + function _setupVFS(fsLib, pathLib){ Phoenix.VFS = { getRootDir: () => '/fs/', @@ -129,12 +141,7 @@ function _setupVFS(fsLib, pathLib){ }, getVirtualServingURLForPath: function (fullPath) { if(Phoenix.isNativeApp) { - if(fullPath.startsWith(tauriAssetServeDir)){ - const platformPath = fs.getTauriPlatformPath(fullPath) - .replace(/\\/g, "/"); // windows style paths to unix style c:\x\y to c:/x/y - return decodeURIComponent(window.__TAURI__.tauri.convertFileSrc(platformPath)); - } - return null; + return _getNativeAssetUrl(fullPath); } return window.fsServerUrl.slice(0, -1) + fullPath; }, @@ -275,9 +282,7 @@ async function setupAppSupportAndExtensionsDir() { appSupportDIR = `${appSupportDIR}/`; } tauriAssetServeDir = `${appSupportDIR}assets/`; - tauriAssetServeBaseURL = decodeURIComponent(window.__TAURI__.tauri.convertFileSrc( - fs.getTauriPlatformPath(tauriAssetServeDir))) - .replace(/\\/g, "/"); // windows style paths to unix style c:\x\y to c:/x/y + tauriAssetServeBaseURL = _getNativeAssetUrl(tauriAssetServeDir); extensionDIR = `${tauriAssetServeDir}extensions/`; // in tauri, the /fs/ folder is not a requirement for boot, so we won't fait for it. // also this creates wired indexed db lock bugs in tauri where the boot leads to a blank screen. From 44dac5c4975946d7e474ce6d9111d9a0b1741d4d Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 27 Jan 2026 19:13:50 +0530 Subject: [PATCH 3/6] fix: test suite init with new constants --- src/index.html | 15 +++-- src/phoenix/init_vfs.js | 1 + test/SpecRunner.html | 66 ++++++++++++++++++- test/spec/LiveDevelopmentMultiBrowser-test.js | 2 +- 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/index.html b/src/index.html index f7c20cdafe..6a052f7f10 100644 --- a/src/index.html +++ b/src/index.html @@ -78,7 +78,7 @@ "\n\nYou will now be redirected to phcode.dev."); window.location = "https://phcode.dev"; } - if(window.electronAPI) { + if(window.electronAppAPI) { window.__ELECTRON__ = true; } if(location.href.startsWith("tauri://") || location.href.startsWith('https://tauri.localhost')){ @@ -103,7 +103,7 @@ // develop phoenix inside phoenix. // This will throw an error if the live preview iframe and parent are cross-origin which is case in phcode canAccessNativeShell = window.top.window.__TAURI__; - canAccessNativeShell = window.top.window.electronAPI; + canAccessNativeShell = window.top.window.electronAppAPI; canAccessNativeShell = true; } catch (e) { // Cross-origin, exception caught, isSameOrigin remains false @@ -167,13 +167,14 @@ } // fs special handling end } - // linux electron api iframe injection for tests + // electron api iframe injection for tests if(canAccessNativeShell && window.parent.window !== window && window.top.window.__ELECTRON__) { // This is only intended to be used for in tests where phoenix is loaded inside iframes. - // this means that we are loaded in an iframe in the specrunner. Tauri doesnt expose tauri APIs - // in its iframes, so we directly use the top most windows tauri api object for tests to run properly. - console.warn("Phoenix is loaded in iframe, attempting to use electron APIs from window.top.electronAPI"); - window.electronAPI = window.top.window.electronAPI; + // this means that we are loaded in an iframe in the specrunner. Electron doesnt expose APIs + // in its iframes, so we directly use the top most windows electron api objects for tests to run properly. + console.warn("Phoenix is loaded in iframe, attempting to use electron APIs from window.top"); + window.electronAppAPI = window.top.window.electronAppAPI; + window.electronFSAPI = window.top.window.electronFSAPI; } if(window.__TAURI__ || window.__ELECTRON__) { window.__IS_NATIVE_SHELL__ = true; diff --git a/src/phoenix/init_vfs.js b/src/phoenix/init_vfs.js index ff1e17e7e6..315aa3da4c 100644 --- a/src/phoenix/init_vfs.js +++ b/src/phoenix/init_vfs.js @@ -48,6 +48,7 @@ function _getNativeAssetUrl(fullFilePath) { } return null; } + // todo electron return null; } diff --git a/test/SpecRunner.html b/test/SpecRunner.html index e4c2d73356..87200c3a8b 100644 --- a/test/SpecRunner.html +++ b/test/SpecRunner.html @@ -45,6 +45,12 @@ throw new Error(errorMessage); } console.warn('Make sure to run this following command before starting tests : npm run build '); + if(window.electronAppAPI) { + window.__ELECTRON__ = true; + } + if(window.__TAURI__ || window.__ELECTRON__) { + window.__IS_NATIVE_SHELL__ = true; + } if(window.__TAURI__) { window.testRunnerLogToConsole = function (...args) { const message = args.join(' '); @@ -60,6 +66,17 @@ } testRunnerLogToConsole(`Tauri test reporters attached.`); } + if(window.__ELECTRON__) { + window.testRunnerLogToConsole = function (...args) { + const message = args.join(' '); + window.electronAppAPI.consoleLog(message); + }; + window.testRunnerErrorToConsole = function (...args) { + const message = args.join(' '); + window.electronAppAPI.consoleLog(`ERROR: ${message}`); + } + testRunnerLogToConsole(`Electron test reporters attached.`); + } // in playwright tests, testRunnerLogToConsole is injected by playwright runners @@ -143,6 +160,52 @@ } setupTauriBootVars(); } + if(window.__ELECTRON__) { + function setupElectronBootVars() { + // this is used by storage.js (window.PhStore) to restore our persistent storage layer in electron. + window._tauriStorageRestorePromise = window.electronFSAPI.appLocalDataDir() + .then(appDataDir => { + const sep = window.electronFSAPI.path.sep; + const storagePath = `${appDataDir}${appDataDir.endsWith(sep) ? "" : sep}storageDB${sep}storageDBDump.json`; + return window.electronFSAPI.fsReadFile(storagePath); + }) + .then(data => { + if(data && data.__fsError) { + throw new Error(data.message); + } + return data; + }) + .catch(err => { + console.error("First boot detected or Failed to init storage from cache." + + " If first boot, ignore this error", err); + }); + const appNamePromise = window.electronAppAPI.getAppName(); + // for running tests, the user document dir is set to app data dir as we dont want to + // corrupt user documents dir for tests + const documentDirPromise = window.electronFSAPI.appLocalDataDir(); + const appLocalDirPromise = window.electronFSAPI.appLocalDataDir(); + const tempDirPromise = window.electronFSAPI.tempDir(); + window._tauriBootVars = {}; + const tauriBootStartTime = Date.now(); + window._tauriBootVarsPromise = Promise.all([appNamePromise, documentDirPromise, + appLocalDirPromise, tempDirPromise]) + .then((results) => { + window._tauriBootVars.appname = results[0]; + // For tests, documents dir is localAppDataDir/documents to keep user documents garbage free for tests + window._tauriBootVars.documentDir = results[1]; + const sep = window.electronFSAPI.path.sep; + if(!window._tauriBootVars.documentDir.endsWith(sep)){ + window._tauriBootVars.documentDir = window._tauriBootVars.documentDir + sep; + } + window._tauriBootVars.documentDir = `${window._tauriBootVars.documentDir}documents${sep}`; + //Documents dir special case for tests + window._tauriBootVars.appLocalDir = results[2]; + window._tauriBootVars.tempDir = results[3]; + window._tauriBootVars.bootstrapTime = Date.now() - tauriBootStartTime; + }); + } + setupElectronBootVars(); + } @@ -211,6 +274,7 @@ isDeskTop: !_mobileAndTabletCheck() && !_mobileCheck(), isChromeOS: /CrOS/.test(navigator.userAgent), isTauri: !!window.__TAURI__, + isElectron: !!window.__ELECTRON__, mobile: { isAndroid: (navigator.userAgent.match(/Android/i) !== null), isIos: (navigator.userAgent.match(/iPhone|iPad|iPod/i) !== null), @@ -290,7 +354,7 @@ 'https://create.phcode.dev': true } }; - window.Phoenix.isNativeApp = window.Phoenix.browser.isTauri; + window.Phoenix.isNativeApp = window.__IS_NATIVE_SHELL__; window.Phoenix.TRUSTED_ORIGINS[location.origin] = true; Phoenix.isSupportedBrowser = Phoenix.isNativeApp || (Phoenix.browser.isDeskTop && ("serviceWorker" in navigator)); diff --git a/test/spec/LiveDevelopmentMultiBrowser-test.js b/test/spec/LiveDevelopmentMultiBrowser-test.js index 30bc999eee..6b3b3181db 100644 --- a/test/spec/LiveDevelopmentMultiBrowser-test.js +++ b/test/spec/LiveDevelopmentMultiBrowser-test.js @@ -92,7 +92,7 @@ define(function (require, exports, module) { // we have to popout a new window and cant use the embedded iframe for live preview integ tests // as Firefox sandbox prevents service worker access from nexted iframes. // In tauri, we use node server, so this limitation doesn't apply in tauri, and we stick to iframes. - const useWindowInsteadOfIframe = (Phoenix.browser.desktop.isFirefox && !window.__TAURI__); + const useWindowInsteadOfIframe = Phoenix.browser.desktop.isFirefox; testWindow = await SpecRunnerUtils.createTestWindowAndRun({ forceReload: false, useWindowInsteadOfIframe }); From ae0780ff87546d47b3ba86e4fd017aba32043aef Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 27 Jan 2026 20:13:34 +0530 Subject: [PATCH 4/6] chore: adding metrics, storage assetsURL, zoom window with electron support --- src/extensionsIntegrated/appUpdater/main.js | 3 +- src/phoenix/init_vfs.js | 8 ++++- src/phoenix/shell.js | 7 ++++- src/storage.js | 33 ++++++++++++++------- src/utils/Metrics.js | 15 ++++++++-- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/extensionsIntegrated/appUpdater/main.js b/src/extensionsIntegrated/appUpdater/main.js index 4cf67dc540..cb477df8a6 100644 --- a/src/extensionsIntegrated/appUpdater/main.js +++ b/src/extensionsIntegrated/appUpdater/main.js @@ -517,10 +517,11 @@ define(function (require, exports, module) { let updateInstalledDialogShown = false, updateFailedDialogShown = false; AppInit.appReady(function () { - if(!Phoenix.isNativeApp || Phoenix.isTestWindow) { + if(!window.__TAURI__ || Phoenix.isTestWindow) { // app updates are only for desktop builds return; } + // todo electron edge for app updater if (brackets.platform === "mac") { // in mac, the `update.app.tar.gz` is downloaded, and only extracted on app quit. // we do this only in mac as the `.app` file is extracted only at app quit and deleted diff --git a/src/phoenix/init_vfs.js b/src/phoenix/init_vfs.js index 315aa3da4c..fe04e57107 100644 --- a/src/phoenix/init_vfs.js +++ b/src/phoenix/init_vfs.js @@ -48,7 +48,13 @@ function _getNativeAssetUrl(fullFilePath) { } return null; } - // todo electron + if(window.__ELECTRON__) { + if(fullFilePath.startsWith(tauriAssetServeDir)){ + const platformPath = fs.getTauriPlatformPath(fullFilePath); + return window.electronAPI.convertToAssetURL(platformPath); + } + return null; + } return null; } diff --git a/src/phoenix/shell.js b/src/phoenix/shell.js index d7437edec9..fcdbf3fc4b 100644 --- a/src/phoenix/shell.js +++ b/src/phoenix/shell.js @@ -537,7 +537,12 @@ Phoenix.app = { if(scaleFactor < .1 || scaleFactor > 2) { throw new Error("zoomWebView scale factor should be between .1 and 2"); } - return window.__TAURI__.tauri.invoke("zoom_window", {scaleFactor: scaleFactor}); + if(window.__TAURI__) { + return window.__TAURI__.tauri.invoke("zoom_window", {scaleFactor: scaleFactor}); + } + if(window.__ELECTRON__) { + return window.electronAPI.zoomWindow(scaleFactor); + } }, _openUrlInBrowserWin: function (url, browser) { // private API for internal use only. May be removed at any time. diff --git a/src/storage.js b/src/storage.js index 433cafaa41..1a66136023 100644 --- a/src/storage.js +++ b/src/storage.js @@ -206,10 +206,14 @@ import {set, entries, createStore} from './thirdparty/idb-keyval.js'; storageNodeConnector .execPeer("putItem", {key, value: valueToStore}) .catch(_reportPutItemError); - // this is an in-memory tauri store that takes care of multi window case, since we have a single - // instance, all windows share this and can reconstruct the full view from the dumb file + this map - // when the editor boots up instead of having to write the dump file frequently. - window.__TAURI__.invoke('put_item', { key, value: JSON.stringify(valueToStore) }); + // this is an in-memory tauri/electron store that takes care of multi window case, since we have a + // single instance, all windows share this and can reconstruct the full view from the dump file + this + // map when the editor boots up instead of having to write the dump file frequently. + if(window.__TAURI__) { + window.__TAURI__.invoke('put_item', { key, value: JSON.stringify(valueToStore) }); + } else if(window.__ELECTRON__) { + window.electronAPI.putItem(key, JSON.stringify(valueToStore)); + } } if(window.debugMode || isBrowser) { // in debug mode, we write to browser storage in tauri and browser builds for eazy debug of storage. @@ -291,13 +295,18 @@ import {set, entries, createStore} from './thirdparty/idb-keyval.js'; return; } if(isDesktop){ - async function mergeTauriInMemoryStorage() { - // The tauri storeage is mainly used in multi window case, where if there are 2+ windows, each window - // is till live and has not commited the dump file to disc(they only do that on exit or 30 every secs). + async function mergeNativeInMemoryStorage() { + // The native storage is mainly used in multi window case, where if there are 2+ windows, each window + // is still live and has not committed the dump file to disc (they only do that on exit or every 30 secs). // so the dump file may be stale after window._tauriStorageRestorePromise in the case. - // we merge the local memory cache maintained at tauri rust side to address this. + // we merge the local memory cache maintained at tauri/electron side to address this. try { - const map = await window.__TAURI__.invoke('get_all_items') || {}; + let map = {}; + if(window.__TAURI__) { + map = await window.__TAURI__.invoke('get_all_items') || {}; + } else if(window.__ELECTRON__) { + map = await window.electronAPI.getAllItems() || {}; + } for(const key of Object.keys(map)){ cache[key] = JSON.parse(map[key]); } @@ -317,7 +326,7 @@ import {set, entries, createStore} from './thirdparty/idb-keyval.js'; } }) .catch(console.error) - .finally(mergeTauriInMemoryStorage); + .finally(mergeNativeInMemoryStorage); return; } // Use browser default storage- IndexedDB @@ -342,8 +351,10 @@ import {set, entries, createStore} from './thirdparty/idb-keyval.js'; // do things to do that are critical to user experience here // We try to set window zoom as early as possible to prevent zoom flicker const zoomFactor = PhStore.getItem(_PHSTORE_BOOT_DESKTOP_ZOOM_SCALE_KEY) || 1; - if(Phoenix.isNativeApp){ + if(window.__TAURI__){ window.__TAURI__.tauri.invoke("zoom_window", {scaleFactor: zoomFactor}); + } else if(window.__ELECTRON__) { + window.electronAPI.zoomWindow(zoomFactor); } }); /** diff --git a/src/utils/Metrics.js b/src/utils/Metrics.js index 64220d3da3..7e6ad646bf 100644 --- a/src/utils/Metrics.js +++ b/src/utils/Metrics.js @@ -194,6 +194,17 @@ define(function (require, exports, module) { }); } + function _sendNativeGAEvent(analyticsID, customUserID, events=[]) { + if(window.__TAURI__){ + return _sendTauriGAEvent(analyticsID, customUserID, events=[]); + } + if(window.__ELECTRON__){ + // todo electron send event to metrics window with electron + return; + } + console.error("Metrics send event failed. Unknown native platform"); + } + let tauriGAEvents = new Map(); function _sendGaEvent(eventAct, category, label, count) { @@ -216,7 +227,7 @@ define(function (require, exports, module) { const TAURI_GA_EVENT_QUEUE_INTERVAL = 3000; function _sendQueuedTauriGAEvents() { - _sendTauriGAEvent(brackets.config.googleAnalyticsIDDesktop, userID, Array.from(tauriGAEvents.values())); + _sendNativeGAEvent(brackets.config.googleAnalyticsIDDesktop, userID, Array.from(tauriGAEvents.values())); tauriGAEvents.clear(); } @@ -227,7 +238,7 @@ define(function (require, exports, module) { // https urls and not the tauri custom protocol urls. So we have a hidden window that loads ga from a // http(s) page which is usually `https://phcode.dev/desktop-metrics.html` or // "http://localhost:8000/src/metrics.html" for live dev builds in tauri. - _sendTauriGAEvent(brackets.config.googleAnalyticsIDDesktop, userID); + _sendNativeGAEvent(brackets.config.googleAnalyticsIDDesktop, userID); setInterval(_sendQueuedTauriGAEvents, TAURI_GA_EVENT_QUEUE_INTERVAL); return; } From ef5bbfc11fc01d07b685119be31d5ad5de0d3523 Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 28 Jan 2026 09:34:19 +0530 Subject: [PATCH 5/6] chore: ci to node 24 --- .github/workflows/create-deploy.yml | 2 +- .github/workflows/desktop-linux-prod-test-pull.yml | 2 +- .github/workflows/desktop-linux-test-pull.yml | 2 +- .../desktop-mac-m1-prod-communityEd-test-pull.yml | 2 +- .github/workflows/desktop-mac-m1-prod-test-pull.yml | 2 +- .github/workflows/desktop-mac-m1-test-pull.yml | 2 +- .github/workflows/desktop-mac-test-pull.yml | 2 +- .github/workflows/desktop-windows-test-pull.yml | 2 +- .github/workflows/minor-version-bump.yml | 2 +- .github/workflows/patch-version-bump.yml | 2 +- .../playwright-chromium-linux-community.yml | 2 +- .github/workflows/playwright-chromium-linux.yml | 2 +- .github/workflows/playwright-chromium-macos.yml | 2 +- .../workflows/playwright-chromium-windows-prod.yml | 2 +- .github/workflows/playwright-chromium-windows.yml | 2 +- .github/workflows/playwright-firefox-linux.yml | 2 +- .github/workflows/playwright-firefox-macos.yml | 2 +- .github/workflows/playwright-firefox-windows.yml | 2 +- .github/workflows/playwright-staging-prod.yml | 12 ++++++------ .github/workflows/prod-deploy.yml | 2 +- .github/workflows/pull_request_verify.yml | 2 +- .github/workflows/staging-deploy.yml | 2 +- .github/workflows/tauri-deploy.yml | 2 +- src-node/package.json | 2 +- 24 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.github/workflows/create-deploy.yml b/.github/workflows/create-deploy.yml index 5ab1c8968f..fe4644a116 100644 --- a/.github/workflows/create-deploy.yml +++ b/.github/workflows/create-deploy.yml @@ -12,7 +12,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Npm Install run: | npm ci diff --git a/.github/workflows/desktop-linux-prod-test-pull.yml b/.github/workflows/desktop-linux-prod-test-pull.yml index b2933728f9..c05460f766 100644 --- a/.github/workflows/desktop-linux-prod-test-pull.yml +++ b/.github/workflows/desktop-linux-prod-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-linux-test-pull.yml b/.github/workflows/desktop-linux-test-pull.yml index b6f9aa0b27..5d5df0e2f3 100644 --- a/.github/workflows/desktop-linux-test-pull.yml +++ b/.github/workflows/desktop-linux-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-mac-m1-prod-communityEd-test-pull.yml b/.github/workflows/desktop-mac-m1-prod-communityEd-test-pull.yml index 5b8525ce3a..3cf1ada28a 100644 --- a/.github/workflows/desktop-mac-m1-prod-communityEd-test-pull.yml +++ b/.github/workflows/desktop-mac-m1-prod-communityEd-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-mac-m1-prod-test-pull.yml b/.github/workflows/desktop-mac-m1-prod-test-pull.yml index 54a6f7bd88..9a23285c8d 100644 --- a/.github/workflows/desktop-mac-m1-prod-test-pull.yml +++ b/.github/workflows/desktop-mac-m1-prod-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-mac-m1-test-pull.yml b/.github/workflows/desktop-mac-m1-test-pull.yml index e51a98380c..b7f52a5e4e 100644 --- a/.github/workflows/desktop-mac-m1-test-pull.yml +++ b/.github/workflows/desktop-mac-m1-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-mac-test-pull.yml b/.github/workflows/desktop-mac-test-pull.yml index 98429cfeba..f47ed8e474 100644 --- a/.github/workflows/desktop-mac-test-pull.yml +++ b/.github/workflows/desktop-mac-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/desktop-windows-test-pull.yml b/.github/workflows/desktop-windows-test-pull.yml index 85c4c99c3c..b269da8d0f 100644 --- a/.github/workflows/desktop-windows-test-pull.yml +++ b/.github/workflows/desktop-windows-test-pull.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: diff --git a/.github/workflows/minor-version-bump.yml b/.github/workflows/minor-version-bump.yml index db2f0e11bd..e68fe3beb2 100644 --- a/.github/workflows/minor-version-bump.yml +++ b/.github/workflows/minor-version-bump.yml @@ -11,7 +11,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - uses: actions/checkout@v3 with: ref: main diff --git a/.github/workflows/patch-version-bump.yml b/.github/workflows/patch-version-bump.yml index dbb600c0d4..20d1641d34 100644 --- a/.github/workflows/patch-version-bump.yml +++ b/.github/workflows/patch-version-bump.yml @@ -11,7 +11,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - uses: actions/checkout@v3 with: ref: main diff --git a/.github/workflows/playwright-chromium-linux-community.yml b/.github/workflows/playwright-chromium-linux-community.yml index 11f0589913..2b846d7f2c 100644 --- a/.github/workflows/playwright-chromium-linux-community.yml +++ b/.github/workflows/playwright-chromium-linux-community.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-chromium-linux.yml b/.github/workflows/playwright-chromium-linux.yml index 8c0e126342..01ec519bea 100644 --- a/.github/workflows/playwright-chromium-linux.yml +++ b/.github/workflows/playwright-chromium-linux.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-chromium-macos.yml b/.github/workflows/playwright-chromium-macos.yml index 6cb1af1607..e2b35f5cb3 100644 --- a/.github/workflows/playwright-chromium-macos.yml +++ b/.github/workflows/playwright-chromium-macos.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-chromium-windows-prod.yml b/.github/workflows/playwright-chromium-windows-prod.yml index 8a1087b938..723eb051c2 100644 --- a/.github/workflows/playwright-chromium-windows-prod.yml +++ b/.github/workflows/playwright-chromium-windows-prod.yml @@ -17,7 +17,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-chromium-windows.yml b/.github/workflows/playwright-chromium-windows.yml index 13cfec9396..e2bcc3d7aa 100644 --- a/.github/workflows/playwright-chromium-windows.yml +++ b/.github/workflows/playwright-chromium-windows.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-firefox-linux.yml b/.github/workflows/playwright-firefox-linux.yml index 02e8e62465..8c2e334857 100644 --- a/.github/workflows/playwright-firefox-linux.yml +++ b/.github/workflows/playwright-firefox-linux.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-firefox-macos.yml b/.github/workflows/playwright-firefox-macos.yml index 5b83ff378f..bb8940361b 100644 --- a/.github/workflows/playwright-firefox-macos.yml +++ b/.github/workflows/playwright-firefox-macos.yml @@ -19,7 +19,7 @@ # - name: setup node # uses: actions/setup-node@v3 # with: -# node-version: 20 +# node-version: 24 # - name: Install dependencies # run: npm ci # - name: Build phoenix diff --git a/.github/workflows/playwright-firefox-windows.yml b/.github/workflows/playwright-firefox-windows.yml index e225a123b4..1d8d38920f 100644 --- a/.github/workflows/playwright-firefox-windows.yml +++ b/.github/workflows/playwright-firefox-windows.yml @@ -16,7 +16,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/playwright-staging-prod.yml b/.github/workflows/playwright-staging-prod.yml index dd95030491..651ff58337 100644 --- a/.github/workflows/playwright-staging-prod.yml +++ b/.github/workflows/playwright-staging-prod.yml @@ -13,7 +13,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix @@ -101,7 +101,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix @@ -190,7 +190,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix @@ -278,7 +278,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix @@ -366,7 +366,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix @@ -455,7 +455,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Install dependencies run: npm ci - name: Build phoenix diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml index 1c371e5057..155cde1002 100644 --- a/.github/workflows/prod-deploy.yml +++ b/.github/workflows/prod-deploy.yml @@ -12,7 +12,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Npm Install run: | npm ci diff --git a/.github/workflows/pull_request_verify.yml b/.github/workflows/pull_request_verify.yml index c2068ddee1..17a5ea729a 100644 --- a/.github/workflows/pull_request_verify.yml +++ b/.github/workflows/pull_request_verify.yml @@ -12,7 +12,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Npm Install run: | npm ci diff --git a/.github/workflows/staging-deploy.yml b/.github/workflows/staging-deploy.yml index 58b72072a6..ae0f37a5b4 100644 --- a/.github/workflows/staging-deploy.yml +++ b/.github/workflows/staging-deploy.yml @@ -12,7 +12,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Npm Install run: | npm ci diff --git a/.github/workflows/tauri-deploy.yml b/.github/workflows/tauri-deploy.yml index 0db082ddfb..6b31c15197 100644 --- a/.github/workflows/tauri-deploy.yml +++ b/.github/workflows/tauri-deploy.yml @@ -17,7 +17,7 @@ jobs: - name: setup node uses: actions/setup-node@v3 with: - node-version: 20 + node-version: 24 - name: Npm Install run: | npm ci diff --git a/src-node/package.json b/src-node/package.json index 0220fdc5b9..fc25d8e4f7 100644 --- a/src-node/package.json +++ b/src-node/package.json @@ -11,7 +11,7 @@ "_watch_src-node": "cd .. && npm run _watch_src-node" }, "engines": { - "node": "20" + "node": "24" }, "repository": { "type": "git", From 0c74d07c21c4797f6af6bf625ed0269615c339fc Mon Sep 17 00:00:00 2001 From: abose Date: Wed, 28 Jan 2026 10:03:00 +0530 Subject: [PATCH 6/6] fix: test suite break with node24 upgrade in windows. tauri UNC paths not accepted by node 24 --- src/node-loader.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/node-loader.js b/src/node-loader.js index 6721b299af..a71852c7cd 100644 --- a/src/node-loader.js +++ b/src/node-loader.js @@ -637,6 +637,11 @@ function nodeLoader() { window.__TAURI__.path.resolveResource("src-node/index.js") .then(async nodeSrcPath=>{ + // Strip Windows UNC prefix (\\?\) that Tauri adds on Windows + // Node 24 doesn't handle UNC paths correctly in module resolution + if (Phoenix.platform === "win" && nodeSrcPath.startsWith('\\\\?\\')) { + nodeSrcPath = nodeSrcPath.slice(4); + } if(Phoenix.platform === "linux") { // in linux installed distributions, src-node is present in the same dir as the executable. const cliArgs = await window.__TAURI__.invoke('_get_commandline_args');