positron.js 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,6 +34,8 @@ Positron is built as a lightweight, secure alternative to Electron. Below is a d
34
34
  - Node.js (v16+)
35
35
  - macOS: Xcode Command Line Tools (swiftc)
36
36
  - Windows: .NET SDK (CLI tools capable of executing `dotnet publish`)
37
+ - C++ compiler (G++ is the default), GTK+ 3 and WebKit2GTK on Linux
38
+ - Docker if building for Linux on macOS or Windows
37
39
 
38
40
  ## Install
39
41
  ```bash
@@ -76,7 +78,8 @@ To create a native extension, provide a custom positron property block within yo
76
78
  "command": "toast:show",
77
79
  "platforms": {
78
80
  "darwin": "src/mac/ToastPlugin.swift",
79
- "win32": "src/win/ToastPlugin.cs"
81
+ "win32": "src/win/ToastPlugin.cs",
82
+ "linux":"src/linux/ToastPlugin.cpp"
80
83
  }
81
84
  }
82
85
  }
@@ -112,10 +115,10 @@ npx positron run
112
115
  ### Packaging
113
116
  This will rebuild the binary, then create a deployable version of the app
114
117
  ```bash
115
- npx positron package [--m | --w] [--arm64 || --x64]
118
+ npx positron package [--m | --w || --l] [--arm64 || --x64]
116
119
  ```
117
120
 
118
- > Note: Windows supports either arm64 or x64, while macOS only supports arm64 on Apple Silicon.
121
+ > Note: Windows & Linux support either arm64 or x64, while macOS only supports arm64.
119
122
 
120
123
  ## IPC Protocol Specification
121
124
  Communication relies on structured JSON communication frames routed through the IPC WebSocket server.
@@ -143,12 +146,8 @@ Communication relies on structured JSON communication frames routed through the
143
146
  }
144
147
  ```
145
148
 
146
- ## Environment Flags
147
- - POSITRON_PACKAGED: Set to "true" inside production bundles to suppress localized development shell background re-compilation triggers.
148
- - POSITRON_IPC_PORT: The port that the IPC server runs on.
149
-
150
149
  ## License
151
150
  MIT
152
151
 
153
152
  ## Documentation
154
- Read docs [here](https://positronjs.gitbook.io/v1)
153
+ [Read here](https://positronjs.gitbook.io/v1)
package/builder.js CHANGED
@@ -11,11 +11,13 @@ function performNativeBuild() {
11
11
 
12
12
  let buildingForWindows = process.argv.includes("--windows") || process.argv.includes("--w");
13
13
  let buildingForMac = process.argv.includes("--mac") || process.argv.includes("--m");
14
+ let buildingForLinux = process.argv.includes("--linux") || process.argv.includes("--l");
14
15
 
15
16
 
16
17
  const appRoot = process.cwd(); // The developer's project folder
17
18
  const nativeExtensionsMac = [];
18
19
  const nativeExtensionsWindows = [];
20
+ const nativeExtensionsLinux = [];
19
21
 
20
22
  // 1. Discover Extensions from the developer's package.json
21
23
  const rootPackagePath = path.join(appRoot, "package.json");
@@ -50,6 +52,7 @@ function performNativeBuild() {
50
52
  else {
51
53
  if(!depPackage.positron.platforms.darwin) missing.push("platforms.darwin");
52
54
  if(!depPackage.positron.platforms.win32) missing.push("platforms.win32");
55
+ if(!depPackage.positron.platforms.linux) missing.push("platforms.linux");
53
56
  }
54
57
 
55
58
  if(missing.includes("className") || missing.includes("command") || missing.includes("platforms")) {
@@ -75,15 +78,26 @@ function performNativeBuild() {
75
78
  } else {
76
79
  warn(`[Builder] Dependency "${dep}" is missing a Windows platform source file. Skipping Windows native extension build for this dependency.`);
77
80
  }
81
+ if(!missing.includes("platforms.linux")) {
82
+ nativeExtensionsLinux.push({
83
+ className: depPackage.positron.className,
84
+ command: depPackage.positron.command,
85
+ sourceFile: path.join(depDir, depPackage.positron.platforms.linux)
86
+ });
87
+ } else {
88
+ warn(`[Builder] Dependency "${dep}" is missing a Linux platform source file. Skipping Linux native extension build for this dependency.`);
89
+ }
78
90
  }
79
91
  }
80
92
  }
81
93
 
82
- if(buildingForWindows == false && buildingForMac == false) {
94
+ if(buildingForWindows == false && buildingForMac == false && buildingForLinux == false) {
83
95
  if (process.platform === "win32") {
84
96
  buildingForWindows = true;
85
97
  } else if (process.platform === "darwin") {
86
98
  buildingForMac = true;
99
+ } else if (process.platform === "linux") {
100
+ buildingForLinux = true;
87
101
  } else {
88
102
  error("[Builder] Unsupported platform for native build.");
89
103
  return false;
@@ -91,7 +105,17 @@ function performNativeBuild() {
91
105
  }
92
106
 
93
107
  if (buildingForMac) {
94
- info(`[Builder] Stitching ${nativeExtensionsMac.length} native extensions...`);
108
+
109
+ const coreMacDir = path.join(__dirname, "core", "mac");
110
+
111
+ nativeExtensionsMac.push({
112
+ command:"createTray",
113
+ className:"TrayExtension",
114
+ sourceFile:path.join(coreMacDir, "tray.swift")
115
+ });
116
+
117
+ // -1 to account for the built-in tray extension
118
+ info(`[Builder] Stitching ${nativeExtensionsMac.length-1} native extensions...`);
95
119
 
96
120
  let registryContent = `// Auto-generated by Positron. Do not edit.\n`;
97
121
  registryContent += `func getExtensionRegistry() -> [String: (Int, [String]) -> Void] {\n`;
@@ -112,7 +136,6 @@ function performNativeBuild() {
112
136
 
113
137
  registryContent += `}\n`;
114
138
 
115
- const coreMacDir = path.join(__dirname, "core", "mac");
116
139
  fs.writeFileSync(path.join(coreMacDir, "Registry.swift"), registryContent);
117
140
 
118
141
  info("[Builder] Compiling native binary...");
@@ -189,6 +212,7 @@ function performNativeBuild() {
189
212
  const coreWinDir = path.join(__dirname, "core", "win");
190
213
  const extensionsDir = path.join(coreWinDir, "extensions");
191
214
 
215
+
192
216
  // 1. Clean and prepare a staging folder for all native extensions
193
217
  if (fs.existsSync(extensionsDir)) fs.rmSync(extensionsDir, { recursive: true, force: true });
194
218
  fs.mkdirSync(extensionsDir, { recursive: true });
@@ -235,6 +259,19 @@ function performNativeBuild() {
235
259
  dotnetArgs.push(`/p:ApplicationIcon=${iconPath}`);
236
260
  }
237
261
 
262
+ // Add package.json metadata to Windows executable
263
+ const appName = rootPackage.productName || rootPackage.name || "PositronApp";
264
+ const version = rootPackage.version || "1.0.0";
265
+ const author = rootPackage.author || "Positron";
266
+ const bundleId = rootPackage.bundleIdentifier || `com.${author.toLowerCase().replace(/[^a-z0-9]/g, '')}.${appName.toLowerCase().replace(/[^a-z0-9]/g, '')}`;
267
+ const winCategory = rootPackage.winCategory || rootPackage.macCategory || "An application built with Positron.js";
268
+
269
+ dotnetArgs.push(`/p:Version=${version}`);
270
+ dotnetArgs.push(`/p:Authors=${author}`);
271
+ dotnetArgs.push(`/p:Company=${author}`);
272
+ dotnetArgs.push(`/p:Product=${bundleId}`);
273
+ dotnetArgs.push(`/p:Description=${winCategory}`);
274
+
238
275
  cp.execFileSync("dotnet", dotnetArgs, { stdio: "inherit" });
239
276
  success("[Builder] Windows native compilation successful.");
240
277
  return true;
@@ -244,6 +281,126 @@ function performNativeBuild() {
244
281
  }
245
282
  }
246
283
 
284
+ if (buildingForLinux) {
285
+ info(`[Builder] Stitching ${nativeExtensionsLinux.length} native Linux extensions...`);
286
+
287
+ const coreLinuxDir = path.join(__dirname, "core", "linux");
288
+
289
+ let registryContent = `// Auto-generated by Positron. Do not edit.\n`;
290
+ registryContent += `#include <string>\n#include <unordered_map>\n#include <vector>\n\n`;
291
+ registryContent += `using namespace std;\n\n`;
292
+
293
+ nativeExtensionsLinux.forEach((ext, index) => {
294
+ registryContent += `void handle_${ext.className}(int windowId, vector<string> args);\n`;
295
+ });
296
+
297
+ registryContent += `\nunordered_map<string, void(*)(int, vector<string>)> getExtensionRegistry() {\n`;
298
+ registryContent += ` return {\n`;
299
+
300
+ nativeExtensionsLinux.forEach((ext, index) => {
301
+ const comma = index === nativeExtensionsLinux.length - 1 ? "" : ",";
302
+ registryContent += ` {"${ext.command}", handle_${ext.className}}${comma}\n`;
303
+ });
304
+
305
+ registryContent += ` };\n}\n`;
306
+
307
+ fs.writeFileSync(path.join(coreLinuxDir, "Registry.cpp"), registryContent);
308
+
309
+ if (process.platform !== "linux") {
310
+ info("[Builder] Cross-compiling for Linux using Docker...");
311
+ try {
312
+ const dockerVer = cp.spawnSync("docker", ["--version"], { stdio: "ignore" });
313
+ if (dockerVer.error || dockerVer.status !== 0) throw new Error("Docker is not available");
314
+ } catch (e) {
315
+ error("[Builder] Fatal: Docker is required to cross-compile for Linux on macOS/Windows.");
316
+ process.exit(1);
317
+ }
318
+
319
+ const dockerArch = arch === "arm64" ? "linux/arm64" : "linux/amd64";
320
+ const dockerTag = `positron-linux-builder-${arch}`;
321
+ const dockerfileContent = [
322
+ "FROM ubuntu:latest",
323
+ "ENV DEBIAN_FRONTEND=noninteractive",
324
+ "RUN apt-get update && apt-get install -y "+
325
+ "g++ pkg-config "+
326
+ "libgtk-3-dev libwebkit2gtk-4.1-dev libjson-glib-dev "+
327
+ "libsoup2.4-dev libnotify-dev",
328
+ "WORKDIR /app"
329
+ ].join("\n");
330
+
331
+ fs.writeFileSync(path.join(coreLinuxDir, "Dockerfile.linux"), dockerfileContent);
332
+ info(`[Builder] Building Docker image for ${dockerArch}...`);
333
+
334
+ try {
335
+ const buildRes = cp.spawnSync("docker", ["build", "--platform", dockerArch, "-t", dockerTag, "-f", path.join(coreLinuxDir, "Dockerfile.linux"), coreLinuxDir], { stdio: "inherit" });
336
+ if (buildRes.error) throw buildRes.error;
337
+ if (buildRes.status !== 0) throw new Error("Docker build failed");
338
+
339
+ const outBinaryDir = path.join(appRoot, "bin");
340
+
341
+ if(!fs.existsSync(outBinaryDir)) fs.mkdirSync(outBinaryDir, { recursive: true });
342
+
343
+ const extensionSources = nativeExtensionsLinux.map(e => e.sourceFile);
344
+ const mappedExtensions = extensionSources.map(s => {
345
+ if (s.startsWith(appRoot)) {
346
+ return "/app/" + path.relative(appRoot, s).replace(/\\/g, '/');
347
+ } else if (s.startsWith(__dirname)) {
348
+ return "/framework/" + path.relative(__dirname, s).replace(/\\/g, '/');
349
+ }
350
+ return s;
351
+ });
352
+
353
+ const compiler = process.argv.find(arg => arg.startsWith("--compiler="))?.split("=")[1] || "g++";
354
+
355
+ const gccArgs = [
356
+ compiler, "-O3",
357
+ "/framework/core/linux/main.cpp",
358
+ "/framework/core/linux/Registry.cpp",
359
+ ...mappedExtensions,
360
+ "-o", "/app/bin/positron-runtime",
361
+ "$(pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.1 json-glib-1.0 libnotify)"
362
+ ].join(" ");
363
+
364
+ info("[Builder] Compiling inside Docker container...");
365
+ const runRes = cp.spawnSync("docker", ["run", "--rm", "--platform", dockerArch, "-v", `\${appRoot}:/app`, "-v", `\${__dirname}:/framework`, dockerTag, "bash", "-c", gccArgs], { stdio: "inherit" });
366
+ if (runRes.error) throw runRes.error;
367
+ if (runRes.status !== 0) throw new Error("Docker run failed");
368
+
369
+ success("[Builder] Linux native cross-compilation successful.");
370
+ return true;
371
+ } catch (err) {
372
+ error("[Builder] Linux cross-compilation via Docker failed:", err.message);
373
+ return false;
374
+ }
375
+ } else {
376
+ info("[Builder] Compiling Linux native binary natively via g++...");
377
+ try {
378
+ const pkgRes = cp.spawnSync("pkg-config", ["--cflags", "--libs", "gtk+-3.0", "webkit2gtk-4.1", "json-glib-1.0", "libnotify"], { encoding: 'utf8' });
379
+ if (pkgRes.error) throw pkgRes.error;
380
+ if (pkgRes.status !== 0) throw new Error("pkg-config failed");
381
+ const pkgConfigOutput = pkgRes.stdout.trim().split(/\\s+/);
382
+
383
+ const extensionSources = nativeExtensionsLinux.map(e => e.sourceFile);
384
+ const gccArgs = [
385
+ "-O3",
386
+ path.join(coreLinuxDir, "main.cpp"),
387
+ path.join(coreLinuxDir, "Registry.cpp"),
388
+ ...extensionSources,
389
+ "-o", path.join(outBinaryDir, "positron-runtime"),
390
+ ...pkgConfigOutput
391
+ ];
392
+
393
+ cp.execFileSync("g++", gccArgs, { stdio: "inherit" });
394
+ success("[Builder] Linux native compilation successful.");
395
+ return true;
396
+ } catch (err) {
397
+ error("[Builder] Linux compilation failed:", err.message);
398
+ warn("Ensure you have installed: libgtk-3-dev libwebkit2gtk-4.1-dev libjson-glib-dev libsoup2.4-dev");
399
+ return false;
400
+ }
401
+ }
402
+ }
403
+
247
404
  return false; // Unsupported platform
248
405
 
249
406
  }