Skip to content

Commit 2bd09e2

Browse files
committed
feat: add electron support initial
1 parent c3a6969 commit 2bd09e2

13 files changed

Lines changed: 167 additions & 73 deletions

File tree

package-lock.json

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
"@floating-ui/dom": "^0.5.4",
9595
"@fortawesome/fontawesome-free": "^6.1.2",
9696
"@highlightjs/cdn-assets": "^11.5.1",
97-
"@phcode/fs": "^4.0.1",
97+
"@phcode/fs": "^4.0.2",
9898
"@phcode/language-support": "^1.1.0",
9999
"@pixelbrackets/gfm-stylesheet": "^1.1.0",
100100
"@prettier/plugin-php": "^0.22.2",

src-node/package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
},
2020
"IMPORTANT!!": "Adding things here will bloat up the package size",
2121
"dependencies": {
22-
"@phcode/fs": "^4.0.1",
22+
"@phcode/fs": "^4.0.2",
2323
"open": "^10.1.0",
2424
"npm": "10.1.0",
2525
"ws": "^8.17.1",

src/assets/new-project/assets/js/code-editor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function _attachSettingBtnEventListeners() {
178178
function _openURLInTauri(url) {
179179
// in tauri, the <a> tag will not open a browser window. So we have to use phcode apis to do it.
180180
// else, the browser itself will open the url. so we dont have to do this in normal browsers.
181-
if(window.top.__TAURI__) {
181+
if(window.top.__IS_NATIVE_SHELL__) {
182182
window.top.Phoenix.app.openURLInDefaultBrowser(url);
183183
}
184184
}

src/document/DocumentCommandHandlers.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,15 +2253,13 @@ define(function (require, exports, module) {
22532253
}
22542254
}
22552255
function attachTauriUnloadHandler() {
2256-
window.__TAURI__.window.appWindow.onCloseRequested((event)=>{
2256+
Phoenix.app.onCloseWindowRequested(()=>{
22572257
_forceQuitIfNeeded();
22582258
if(closeInProgress){
2259-
event.preventDefault();
2260-
return;
2259+
return false;
22612260
}
22622261
closeInProgress = true;
22632262
PreferencesManager.setViewState("windowClosingTime", new Date().getTime());
2264-
event.preventDefault();
22652263
_handleWindowGoingAway(null, closeSuccess=>{
22662264
console.log('close success: ', closeSuccess);
22672265
exitWaitPromises.push(_safeFlushDB());
@@ -2277,6 +2275,7 @@ define(function (require, exports, module) {
22772275
console.log('close fail: ', closeFail);
22782276
closeInProgress = false;
22792277
});
2278+
return false;
22802279
});
22812280
}
22822281

src/extensions/default/HealthData/SendToAnalytics.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,16 @@ define(function (require, exports, module) {
135135
_sendStorageMetrics();
136136
}
137137

138+
const envName = window.__TAURI__ ? "tauri-" : (window.__ELECTRON__ ? "electron-" : "");
139+
138140
let bugsnagPerformanceInited = false;
139141
function _initBugsnagPerformance() {
140142
bugsnagPerformanceInited = true;
141143
BugsnagPerformance.start({
142144
apiKey: '94ef94f4daf871ca0f2fc912c6d4764d',
143145
appVersion: AppConfig.version,
144-
releaseStage: window.__TAURI__ ?
145-
`tauri-${AppConfig.config.bugsnagEnv}-${Phoenix.platform}` : AppConfig.config.bugsnagEnv,
146+
releaseStage: window.__IS_NATIVE_SHELL__ ?
147+
`${envName}${AppConfig.config.bugsnagEnv}-${Phoenix.platform}` : AppConfig.config.bugsnagEnv,
146148
autoInstrumentRouteChanges: false,
147149
autoInstrumentNetworkRequests: false,
148150
autoInstrumentFullPageLoads: false

src/index.html

Lines changed: 118 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@
7878
"\n\nYou will now be redirected to phcode.dev.");
7979
window.location = "https://phcode.dev";
8080
}
81+
if(window.electronAPI) {
82+
window.__ELECTRON__ = true;
83+
}
8184
if(location.href.startsWith("tauri://") || location.href.startsWith('https://tauri.localhost')){
8285
const errorMessage = `You should use custom protocol phtauri:// instead of tauri protocol ${location.href} .`;
8386
alert(errorMessage);
@@ -94,20 +97,21 @@
9497
const isSpecRunnerWindow = window.location.pathname.endsWith("/SpecRunner.html");
9598
return isTestPhoenixWindow || isSpecRunnerWindow;
9699
}
97-
let canAccessTauri = false;
100+
let canAccessNativeShell = false;
98101
try {
99102
// This is to allow phoenix live previews in phoenix desktop editor so that we can
100103
// develop phoenix inside phoenix.
101104
// This will throw an error if the live preview iframe and parent are cross-origin which is case in phcode
102-
canAccessTauri = window.top.window.__TAURI__;
103-
canAccessTauri = true;
105+
canAccessNativeShell = window.top.window.__TAURI__;
106+
canAccessNativeShell = window.top.window.electronAPI;
107+
canAccessNativeShell = true;
104108
} catch (e) {
105109
// Cross-origin, exception caught, isSameOrigin remains false
106110
}
107111

108112
// In Mac !! the window.__TAURI__ object is present in iframes, even if it doesnt works. so we are forced
109113
// to use if(window.parent.window !== window) instead of if(!window.__TAURI__) here.
110-
if(canAccessTauri && window.parent.window !== window && window.top.window.__TAURI__) {
114+
if(canAccessNativeShell && window.parent.window !== window && window.top.window.__TAURI__) {
111115
// This is only intended to be used for in tests where phoenix is loaded inside iframes.
112116
// this means that we are loaded in an iframe in the specrunner. Tauri doesnt expose tauri APIs
113117
// in its iframes, so we directly use the top most windows tauri api object for tests to run properly.
@@ -163,6 +167,58 @@
163167
}
164168
// fs special handling end
165169
}
170+
// linux electron api iframe injection for tests
171+
if(canAccessNativeShell && window.parent.window !== window && window.top.window.__ELECTRON__) {
172+
// This is only intended to be used for in tests where phoenix is loaded inside iframes.
173+
// this means that we are loaded in an iframe in the specrunner. Tauri doesnt expose tauri APIs
174+
// in its iframes, so we directly use the top most windows tauri api object for tests to run properly.
175+
console.warn("Phoenix is loaded in iframe, attempting to use electron APIs from window.top.electronAPI");
176+
window.electronAPI = window.top.window.electronAPI;
177+
}
178+
if(window.__TAURI__ || window.__ELECTRON__) {
179+
window.__IS_NATIVE_SHELL__ = true;
180+
}
181+
// Common function to process boot vars results for both Tauri and Electron
182+
function _processBootVarsResults(results, pathSep, LOCALSTORAGE_KEY, bootStartTime) {
183+
// results array: [appName, documentDir, appLocalDir, tempDir, homeDir]
184+
const idx = {appName: 0, docDir: 1, appLocal: 2, temp: 3, home: 4};
185+
186+
window._tauriBootVars.appname = results[idx.appName].value;
187+
188+
if(results[idx.docDir].status === "fulfilled"){
189+
window._tauriBootVars.documentDir = results[idx.docDir].value;
190+
} else if(results[idx.home] && results[idx.home].status === "fulfilled"){
191+
// some linux distros may not have Documents folder. so we use the home folder
192+
// https://github.com/phcode-dev/phoenix/issues/1729
193+
window._tauriBootVars.documentDir = results[idx.home].value;
194+
} else if(results[idx.appLocal].status === "fulfilled"){
195+
console.error("Unable to determine user documents dir, defaulting to app data dir as document dir:",
196+
results[idx.docDir].reason, "home folder error: ", results[idx.home]?.reason);
197+
window._tauriBootVars.documentDir = results[idx.appLocal].value;
198+
} else {
199+
alert("Could not resolve user documents directory. \nPhoenix Code cannot start.");
200+
}
201+
202+
if(results[idx.appLocal].status === "fulfilled") {
203+
window._tauriBootVars.appLocalDir = results[idx.appLocal].value;
204+
} else {
205+
alert("Could not resolve Application Data directory. \nPhoenix Code cannot start.");
206+
}
207+
208+
// For tests, documents dir is localAppDataDir/testDocuments to keep user documents garbage free for tests
209+
// Also In github actions, the tauri get doc dir call gets stuck indefinitely
210+
if(_isTestWindow()){
211+
if(!window._tauriBootVars.documentDir.endsWith(pathSep)){
212+
window._tauriBootVars.documentDir = window._tauriBootVars.documentDir + pathSep;
213+
}
214+
window._tauriBootVars.documentDir = `${window._tauriBootVars.documentDir}testDocuments${pathSep}`;
215+
}
216+
217+
window._tauriBootVars.appLocalDir = results[idx.appLocal].value;
218+
window._tauriBootVars.tempDir = results[idx.temp].value;
219+
window._tauriBootVars.bootstrapTime = Date.now() - bootStartTime;
220+
localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(window._tauriBootVars));
221+
}
166222
if(window.__TAURI__) {
167223
function setupTauriBootVars() {
168224
// this is used by storage.js (window.PhStore) to restore our persistent storage layer in tauri.
@@ -201,45 +257,63 @@
201257
window._tauriBootVarsPromise = Promise.allSettled([appNamePromise, documentDirPromise,
202258
appLocalDirPromise, tempDirPromise, homeDirPromise])
203259
.then((results) => {
204-
window._tauriBootVars.appname = results[0].value;
205-
206-
if(results[1].status === "fulfilled"){
207-
window._tauriBootVars.documentDir = results[1].value;
208-
} else if(results[4].status === "fulfilled"){
209-
// some linux distros may not have Documents folder. so we use the home folder
210-
// https://github.com/phcode-dev/phoenix/issues/1729
211-
window._tauriBootVars.documentDir = results[4].value;
212-
} else if(results[2].status === "fulfilled"){
213-
console.error("Unable to determine user documents dir, defaulting to app data dir as document dir:",
214-
results[1].reason, "home folder error: ", results[4].reason);
215-
window._tauriBootVars.documentDir = results[2].value;
216-
} else {
217-
alert("Could not resolve user documents directory. \nPhoenix Code cannot start.");
218-
}
219-
220-
if(results[2].status === "fulfilled") {
221-
window._tauriBootVars.appLocalDir = results[2].value;
222-
} else {
223-
alert("Could not resolve Application Data directory. \nPhoenix Code cannot start.");
224-
}
225-
226-
// For tests, documents dir is localAppDataDir/testDocuments to keep user documents garbage free for tests
227-
// Also In github actions, the tauri get doc dir call gets stuck indefinitely
228-
if(_isTestWindow()){
229-
if(!window._tauriBootVars.documentDir.endsWith(window.__TAURI__.path.sep)){
230-
window._tauriBootVars.documentDir = window._tauriBootVars.documentDir + window.__TAURI__.path.sep;
231-
}
232-
window._tauriBootVars.documentDir = `${window._tauriBootVars.documentDir}testDocuments${window.__TAURI__.path.sep}`;
260+
_processBootVarsResults(results, window.__TAURI__.path.sep, TAURI_BOOT_VARS_LOCALSTORAGE_KEY, tauriBootStartTime);
261+
});
262+
}
263+
setupTauriBootVars();
264+
}
265+
if(window.__ELECTRON__) {
266+
function setupElectronBootVars() {
267+
// this is used by storage.js (window.PhStore) to restore our persistent storage layer in electron.
268+
window._tauriStorageRestorePromise = window.electronFSAPI.appLocalDataDir()
269+
.then(appDataDir => {
270+
const sep = window.electronFSAPI.path.sep;
271+
const storagePath = `${appDataDir}${appDataDir.endsWith(sep) ? "" : sep}storageDB${sep}storageDBDump.json`;
272+
return window.electronFSAPI.fsReadFile(storagePath);
273+
})
274+
.then(data => {
275+
if(data && data.__fsError) {
276+
throw new Error(data.message);
233277
}
234-
//Documents dir special case for tests
278+
return data;
279+
})
280+
.catch(err => {
281+
console.error("First boot detected or Failed to init storage from cache." +
282+
" If first boot, ignore this error", err);
283+
});
284+
// increase this version number if you are modifying any boot vars
285+
const TAURI_BOOT_VARS_LOCALSTORAGE_KEY = "tauriBootVarsV1";
286+
const tauriBootVarsStr = localStorage.getItem(TAURI_BOOT_VARS_LOCALSTORAGE_KEY);
287+
if(tauriBootVarsStr) {
288+
try{
289+
window._tauriBootVars = JSON.parse(tauriBootVarsStr);
290+
window._tauriBootVarsPromise = Promise.resolve("Resolved from localstorage.");
291+
} catch (e) {
292+
console.error("Error getting boot vars from localstorage. Falling back to make electron bootstrap calls.", e);
293+
}
294+
}
295+
const appNamePromise = window.electronAppAPI.getAppName();
296+
// for running tests, the user document dir is set to app data dir as we dont want to
297+
// corrupt user documents dir for tests
298+
let documentDirPromise, homeDirPromise;
299+
if(_isTestWindow()){
300+
documentDirPromise = window.electronFSAPI.appLocalDataDir(); // appdata/testDocuments will be appended below
301+
} else {
302+
documentDirPromise = window.electronFSAPI.documentDir();
303+
homeDirPromise = window.electronFSAPI.homeDir();
304+
}
235305

236-
window._tauriBootVars.appLocalDir = results[2].value;
237-
window._tauriBootVars.tempDir = results[3].value;
238-
window._tauriBootVars.bootstrapTime = Date.now() - tauriBootStartTime;
239-
localStorage.setItem(TAURI_BOOT_VARS_LOCALSTORAGE_KEY, JSON.stringify(window._tauriBootVars));
306+
const appLocalDirPromise = window.electronFSAPI.appLocalDataDir();
307+
const tempDirPromise = window.electronFSAPI.tempDir();
308+
window._tauriBootVars = {};
309+
const tauriBootStartTime = Date.now();
310+
window._tauriBootVarsPromise = Promise.allSettled([appNamePromise, documentDirPromise,
311+
appLocalDirPromise, tempDirPromise, homeDirPromise])
312+
.then((results) => {
313+
_processBootVarsResults(results, window.electronFSAPI.path.sep, TAURI_BOOT_VARS_LOCALSTORAGE_KEY, tauriBootStartTime);
240314
});
241315
}
242-
setupTauriBootVars();
316+
setupElectronBootVars();
243317
}
244318

245319
// environment setup for boot. do not move out of index html!!
@@ -290,6 +364,7 @@
290364
isDeskTop: !_mobileAndTabletCheck() && !_mobileCheck(),
291365
isChromeOS: /CrOS/.test(navigator.userAgent),
292366
isTauri: !!window.__TAURI__,
367+
isElectron: !!window.__ELECTRON__,
293368
mobile: {
294369
isAndroid: (navigator.userAgent.match(/Android/i) !== null),
295370
isIos: (navigator.userAgent.match(/iPhone|iPad|iPod/i) !== null),
@@ -370,7 +445,7 @@
370445
'https://create.phcode.dev': true
371446
}
372447
};
373-
window.Phoenix.isNativeApp = window.Phoenix.browser.isTauri;
448+
window.Phoenix.isNativeApp = window.__IS_NATIVE_SHELL__;
374449
window.Phoenix.TRUSTED_ORIGINS[location.origin] = true;
375450
Phoenix.isSupportedBrowser = Phoenix.isNativeApp ||
376451
(Phoenix.browser.isDeskTop && ("serviceWorker" in navigator));
@@ -667,11 +742,11 @@
667742
_resetCacheIfNeeded();
668743

669744
function _addOrRemoveSplashScreenIfNeeded() {
670-
if(!window.__TAURI__) {
745+
if(!window.__IS_NATIVE_SHELL__) {
671746
document.getElementById('phoenix-loading-splash-screen-overlay').classList.remove('forced-hidden')
672747
}
673-
if(window.testEnvironment || window.__TAURI__){
674-
// tauri means local builds and, it loads up pretty fast, so splash screen
748+
if(window.testEnvironment || window.__IS_NATIVE_SHELL__){
749+
// tauri means local builds and, it loads up pretty fast, so no splash screen
675750
document.getElementById('phoenix-loading-splash-screen-overlay').remove();
676751
window.splashScreenPresent = false;
677752
}

0 commit comments

Comments
 (0)