Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ phoenix
# Electron things
src-electron/bin
src-electron/src-node
src-electron/config-effective.json
src-electron/dist
src-electron/phoenix-dist

node_modules
dist
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"releaseDistTest": "npm run _make_src-node && npm run _createDistTestReleaseConfig && tauri build --config ./src-tauri/tauri-local.conf.json",
"releaseDistTestDebug": "npm run _make_src-node && npm run _createDistTestReleaseConfig && tauri build --config ./src-tauri/tauri-local.conf.json --debug",
"_make_src-node": "node ./src-build/makeSrcNode.js ../phoenix/src-node",
"_copy_phoenix-dist": "cd ../phoenix && npm run release:prod && cd ../phoenix-desktop && shx rm -rf src-electron/phoenix-dist && shx cp -r ../phoenix/dist src-electron/phoenix-dist",
"build:electron": "npm run _make_src-node && npm run _copy_phoenix-dist && cd src-electron && npm run build:appimage",
"_ci_make_src-node": "node ./src-build/makeSrcNode.js phoenix/src-node",
"_ci-clonePhoenixForTests": "npm run _ci-env-warn && node ./src-build/clonePhoenixForTests.js",
"_ci-cloneAndBuildPhoenix": "npm run _ci-env-warn && node ./src-build/clonePhoenix.js && cd phoenix && npm ci && npm run build && cd ..",
Expand Down
8 changes: 8 additions & 0 deletions src-build/serveForPlatform.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {getPlatformDetails} from "./utils.js";
import {execa} from "execa";
import chalk from "chalk";
import {resolve} from "path";
import {copyFileSync} from "fs";

const {platform} = getPlatformDetails();

Expand Down Expand Up @@ -57,6 +58,13 @@ if (target === "tauri") {
console.log(`Running "npm install" in ${srcNodePath}`);
await execa("npm", ["install"], {cwd: srcNodePath, stdio: "inherit"});

// Copy config.json to config-effective.json (dev config for serve)
const electronDir = resolve("src-electron");
const configSrc = resolve(electronDir, "config.json");
const configDest = resolve(electronDir, "config-effective.json");
console.log('Copying config.json to config-effective.json...');
copyFileSync(configSrc, configDest);

console.log('Starting Electron...');
await execa("./src-electron/node_modules/.bin/electron", ["src-electron/main.js"], {stdio: "inherit"});
}
14 changes: 12 additions & 2 deletions src-build/setupElectron.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { copyFileSync } from 'fs';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const electronDir = join(__dirname, '..', 'src-electron');

console.log('Installing src-electron dependencies...');
execSync('npm ci', { cwd: electronDir, stdio: 'inherit' });
// Use the same npm command that was used at root level (npm install vs npm ci)
const npmCommand = process.env.npm_command === 'install' ? 'npm install' : 'npm ci';
console.log(`Installing src-electron dependencies with ${npmCommand}...`);
execSync(npmCommand, { cwd: electronDir, stdio: 'inherit' });
console.log('src-electron dependencies installed successfully!');

// Copy config.json to config-effective.json (dev config by default)
const configSrc = join(electronDir, 'config.json');
const configDest = join(electronDir, 'config-effective.json');
console.log('Copying config.json to config-effective.json...');
copyFileSync(configSrc, configDest);
console.log('Config file copied successfully!');
6 changes: 6 additions & 0 deletions src-electron/config-prod.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"identifier": "io.phcode",
"stage": "production",
"productName": "Phoenix Code",
"gaMetricsURL": "https://phcode.dev/desktop-metrics.html"
}
7 changes: 7 additions & 0 deletions src-electron/config-staging.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"identifier": "io.phcode.staging",
"stage": "stage",
"productName": "Phoenix Code Pre-release",
"trustedElectronDomains": ["phtauri://localhost/", "https://phcode.dev/", "https://staging.phcode.dev/"],
"gaMetricsURL": "https://staging.phcode.dev/desktop-metrics.html"
}
26 changes: 13 additions & 13 deletions src-electron/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
* Centralized Configuration Module
*
* This module provides a single source of truth for all configuration values.
* It reads from package.json and can apply stage-wise transforms as needed.
* It reads from config.json and can apply stage-wise transforms as needed.
*
* Usage:
* const { stage, trustedElectronDomains, productName } = require('./config');
*/

const packageJson = require('./package.json');
const configJson = require('./config-effective.json');

// Core package.json values
const name = packageJson.name;
const identifier = packageJson.identifier;
const stage = packageJson.stage;
const version = packageJson.version;
const productName = packageJson.productName;
const description = packageJson.description;
// Core config values
const identifier = configJson.identifier;
const phoenixLoadURL = configJson.phoenixLoadURL;
const gaMetricsURL = configJson.gaMetricsURL;
const stage = configJson.stage;
const version = configJson.version;
const productName = configJson.productName;

// Security configuration
const trustedElectronDomains = packageJson.trustedElectronDomains || [];
const trustedElectronDomains = configJson.trustedElectronDomains || [];

/**
* Initialize configuration (call once at app startup if needed).
Expand All @@ -35,13 +35,13 @@ function initConfig() {
}

module.exports = {
// Package info
name,
// App info
identifier,
phoenixLoadURL,
gaMetricsURL,
stage,
version,
productName,
description,

// Security
trustedElectronDomains,
Expand Down
9 changes: 9 additions & 0 deletions src-electron/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"identifier": "io.phcode.dev",
"stage": "dev",
"version": "5.0.5",
"productName": "Phoenix Code Experimental Build",
"phoenixLoadURL": "phtauri://localhost/src/",
"trustedElectronDomains": ["phtauri://localhost/", "https://phcode.dev/"],
"gaMetricsURL": "phtauri://localhost/src/desktop-metrics.html"
}
32 changes: 32 additions & 0 deletions src-electron/electron-builder.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
appId: io.phcode.dev
productName: Phoenix Code
copyright: Copyright © 2024 phcode.dev

asar: true

linux:
target:
- AppImage
category: Development
icon: ../src-tauri/icons

files:
- "**/*"
- "!dist/**"
- "!src-node/**"
- "!bin/**"
- "!phoenix-dist/**"

asarUnpack:
- "node_modules/keytar/**"

extraResources:
- from: "src-node"
to: "src-node"
- from: "bin"
to: "bin"
- from: "phoenix-dist"
to: "phoenix-dist"

extraMetadata:
main: main.js
21 changes: 16 additions & 5 deletions src-electron/main-window-ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ function getNextLabel(prefix) {
// Track close handlers per window
const windowCloseHandlers = new Map();

// Callback when all registered windows are closed
let onAllWindowsClosedCallback = null;

function registerWindow(win, label) {
const webContents = win.webContents;
const webContentsId = webContents.id;
Expand All @@ -52,6 +55,10 @@ function registerWindow(win, label) {
cleanupWindowTrust(webContentsId, label);
// Clean up security trust
cleanupTrust(webContentsId);
// Notify if all registered windows are closed
if (windowRegistry.size === 0 && onAllWindowsClosedCallback) {
onAllWindowsClosedCallback();
}
});
}

Expand Down Expand Up @@ -237,8 +244,8 @@ function registerWindowIpcHandlers() {
// Try reading as file URLs
const text = clipboard.read('public.file-url');
if (text) {
// Convert file:// URLs to paths
const paths = text.split('\n')
// Convert file:// URLs to paths (handle both \n and \r\n line endings)
const paths = text.split(/\r?\n/)
.filter(url => url.startsWith('file://'))
.map(url => decodeURIComponent(url.replace('file://', '')));
if (paths.length > 0) {
Expand All @@ -247,11 +254,11 @@ function registerWindowIpcHandlers() {
}
}

// Linux: text/uri-list format
// Linux: text/uri-list format (uses \r\n line endings per RFC 2483)
if (process.platform === 'linux' && formats.includes('text/uri-list')) {
const text = clipboard.read('text/uri-list');
if (text) {
const paths = text.split('\n')
const paths = text.split(/\r?\n/)
.filter(url => url.startsWith('file://'))
.map(url => decodeURIComponent(url.replace('file://', '')));
if (paths.length > 0) {
Expand Down Expand Up @@ -313,4 +320,8 @@ function getWindowLabel(webContentsId) {
return webContentsToLabel.get(webContentsId) || 'unknown';
}

module.exports = { registerWindowIpcHandlers, registerWindow, setupCloseHandler, windowRegistry, getWindowLabel };
function setOnAllWindowsClosed(callback) {
onAllWindowsClosedCallback = callback;
}

module.exports = { registerWindowIpcHandlers, registerWindow, getWindowLabel, setOnAllWindowsClosed };
Loading
Loading