electrobun 0.0.19-beta.70 → 0.0.19-beta.72

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/debug.js ADDED
@@ -0,0 +1,5 @@
1
+ console.log('process.argv:', process.argv);
2
+ const indexOfElectrobun = process.argv.findIndex((arg) => arg.includes('electrobun'));
3
+ console.log('indexOfElectrobun:', indexOfElectrobun);
4
+ const commandArg = process.argv[indexOfElectrobun + 1] || 'build';
5
+ console.log('commandArg:', commandArg);
@@ -1,10 +1,26 @@
1
1
  import { join, dirname, resolve } from "path";
2
- import { homedir, platform } from "os";
2
+ import { homedir } from "os";
3
3
  import { renameSync, unlinkSync, mkdirSync, rmdirSync, statSync } from "fs";
4
4
  import tar from "tar";
5
5
  import { ZstdInit } from "@oneidentity/zstd-js/wasm";
6
-
7
- const appSupportDir = join(homedir(), "Library", "Application Support");
6
+ import { OS as currentOS, ARCH as currentArch } from '../../shared/platform';
7
+
8
+ // Cross-platform app data directory
9
+ function getAppDataDir(): string {
10
+ switch (currentOS) {
11
+ case 'macos':
12
+ return join(homedir(), "Library", "Application Support");
13
+ case 'win':
14
+ // Use APPDATA environment variable or fallback to default location
15
+ return process.env.APPDATA || join(homedir(), "AppData", "Roaming");
16
+ case 'linux':
17
+ // Use XDG_CONFIG_HOME or fallback to ~/.config
18
+ return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
19
+ default:
20
+ // Fallback to home directory with .config
21
+ return join(homedir(), ".config");
22
+ }
23
+ }
8
24
 
9
25
  // todo (yoav): share type with cli
10
26
  let localInfo: {
@@ -46,7 +62,8 @@ const Updater = {
46
62
 
47
63
  const channelBucketUrl = await Updater.channelBucketUrl();
48
64
  const cacheBuster = Math.random().toString(36).substring(7);
49
- const updateInfoUrl = join(channelBucketUrl, `update.json?${cacheBuster}`);
65
+ const platformFolder = `${localInfo.channel}-${currentOS}-${currentArch}`;
66
+ const updateInfoUrl = join(localInfo.bucketUrl, platformFolder, `update.json?${cacheBuster}`);
50
67
 
51
68
  try {
52
69
  const updateInfoResponse = await fetch(updateInfoUrl);
@@ -113,8 +130,9 @@ const Updater = {
113
130
  }
114
131
 
115
132
  // check if there's a patch file for it
133
+ const platformFolder = `${localInfo.channel}-${currentOS}-${currentArch}`;
116
134
  const patchResponse = await fetch(
117
- join(channelBucketUrl, `${currentHash}.patch`)
135
+ join(localInfo.bucketUrl, platformFolder, `${currentHash}.patch`)
118
136
  );
119
137
 
120
138
  if (!patchResponse.ok) {
@@ -210,9 +228,21 @@ const Updater = {
210
228
  // then just download it and unpack it
211
229
  if (currentHash !== latestHash) {
212
230
  const cacheBuster = Math.random().toString(36).substring(7);
231
+ const platformFolder = `${localInfo.channel}-${currentOS}-${currentArch}`;
232
+ // Platform-specific tarball naming
233
+ let tarballName: string;
234
+ if (currentOS === 'macos') {
235
+ tarballName = `${appFileName}.app.tar.zst`;
236
+ } else if (currentOS === 'win') {
237
+ tarballName = `${appFileName}.tar.zst`;
238
+ } else {
239
+ tarballName = `${appFileName}.tar.zst`;
240
+ }
241
+
213
242
  const urlToLatestTarball = join(
214
- channelBucketUrl,
215
- `${appFileName}.app.tar.zst`
243
+ localInfo.bucketUrl,
244
+ platformFolder,
245
+ tarballName
216
246
  );
217
247
  const prevVersionCompressedTarballPath = join(
218
248
  appDataFolder,
@@ -289,16 +319,24 @@ const Updater = {
289
319
  file: latestTarPath,
290
320
  cwd: extractionFolder,
291
321
  onentry: (entry) => {
292
- // find the first .app bundle in the tarball
293
- // Some apps may have nested .app bundles
294
- if (!appBundleSubpath && entry.path.endsWith(".app/")) {
295
- appBundleSubpath = entry.path;
322
+ if (currentOS === 'macos') {
323
+ // find the first .app bundle in the tarball
324
+ // Some apps may have nested .app bundles
325
+ if (!appBundleSubpath && entry.path.endsWith(".app/")) {
326
+ appBundleSubpath = entry.path;
327
+ }
328
+ } else {
329
+ // For Windows/Linux, look for the main executable
330
+ // This assumes the tarball contains the app at the root
331
+ if (!appBundleSubpath) {
332
+ appBundleSubpath = "./";
333
+ }
296
334
  }
297
335
  },
298
336
  });
299
337
 
300
338
  if (!appBundleSubpath) {
301
- console.error("Failed to find app bundle in tarball");
339
+ console.error("Failed to find app in tarball");
302
340
  return;
303
341
  }
304
342
 
@@ -306,14 +344,22 @@ const Updater = {
306
344
  const newAppBundlePath = resolve(
307
345
  join(extractionFolder, appBundleSubpath)
308
346
  );
309
- // Note: dirname(process.execPath) is the path to the running app bundle's
310
- // Contents/MacOS directory
311
- const runningAppBundlePath = resolve(
312
- dirname(process.execPath),
313
- "..",
314
- ".."
315
- );
316
- const backupAppBundlePath = join(extractionFolder, "backup.app");
347
+ // Platform-specific app path calculation
348
+ let runningAppBundlePath: string;
349
+ if (currentOS === 'macos') {
350
+ // On macOS, executable is at Contents/MacOS/binary inside .app bundle
351
+ runningAppBundlePath = resolve(
352
+ dirname(process.execPath),
353
+ "..",
354
+ ".."
355
+ );
356
+ } else {
357
+ // On Windows/Linux, the executable is the app itself
358
+ runningAppBundlePath = process.execPath;
359
+ }
360
+ // Platform-specific backup naming
361
+ const backupName = currentOS === 'macos' ? "backup.app" : "backup";
362
+ const backupAppBundlePath = join(extractionFolder, backupName);
317
363
 
318
364
  try {
319
365
  // const backupState = statSync(backupAppBundlePath);
@@ -329,7 +375,20 @@ const Updater = {
329
375
  return;
330
376
  }
331
377
 
332
- await Bun.spawn(["open", runningAppBundlePath]);
378
+ // Cross-platform app launch
379
+ switch (currentOS) {
380
+ case 'macos':
381
+ await Bun.spawn(["open", runningAppBundlePath]);
382
+ break;
383
+ case 'win':
384
+ // On Windows, the runningAppBundlePath would be the .exe file
385
+ await Bun.spawn([runningAppBundlePath]);
386
+ break;
387
+ case 'linux':
388
+ // On Linux, directly execute the binary
389
+ await Bun.spawn([runningAppBundlePath]);
390
+ break;
391
+ }
333
392
  process.exit(0);
334
393
  }
335
394
  }
@@ -337,14 +396,14 @@ const Updater = {
337
396
 
338
397
  channelBucketUrl: async () => {
339
398
  await Updater.getLocallocalInfo();
340
- // todo: tmp hardcode canary
341
- return join(localInfo.bucketUrl, localInfo.channel);
399
+ const platformFolder = `${localInfo.channel}-${currentOS}-${currentArch}`;
400
+ return join(localInfo.bucketUrl, platformFolder);
342
401
  },
343
402
 
344
403
  appDataFolder: async () => {
345
404
  await Updater.getLocallocalInfo();
346
405
  const appDataFolder = join(
347
- appSupportDir,
406
+ getAppDataDir(),
348
407
  localInfo.identifier,
349
408
  localInfo.name
350
409
  );
@@ -0,0 +1,48 @@
1
+ import { platform, arch } from 'os';
2
+
3
+ export type SupportedOS = 'macos' | 'win' | 'linux';
4
+ export type SupportedArch = 'arm64' | 'x64';
5
+
6
+ // Cache platform() result to avoid multiple system calls
7
+ const platformName = platform();
8
+ const archName = arch();
9
+
10
+ // Determine OS once
11
+ export const OS: SupportedOS = (() => {
12
+ switch (platformName) {
13
+ case "win32":
14
+ return 'win';
15
+ case "darwin":
16
+ return 'macos';
17
+ case 'linux':
18
+ return 'linux';
19
+ default:
20
+ throw new Error(`Unsupported platform: ${platformName}`);
21
+ }
22
+ })();
23
+
24
+ // Determine ARCH once, with Windows override
25
+ export const ARCH: SupportedArch = (() => {
26
+ // Always use x64 for Windows since we only build x64 Windows binaries
27
+ if (OS === 'win') {
28
+ return 'x64';
29
+ }
30
+
31
+ switch (archName) {
32
+ case "arm64":
33
+ return 'arm64';
34
+ case "x64":
35
+ return 'x64';
36
+ default:
37
+ throw new Error(`Unsupported architecture: ${archName}`);
38
+ }
39
+ })();
40
+
41
+ // Export functions for backwards compatibility if needed
42
+ export function getPlatformOS(): SupportedOS {
43
+ return OS;
44
+ }
45
+
46
+ export function getPlatformArch(): SupportedArch {
47
+ return ARCH;
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrobun",
3
- "version": "0.0.19-beta.70",
3
+ "version": "0.0.19-beta.72",
4
4
  "description": "Build ultra fast, tiny, and cross-platform desktop apps with Typescript.",
5
5
  "license": "MIT",
6
6
  "author": "Blackboard Technologies Inc.",
package/src/cli/index.ts CHANGED
@@ -2,6 +2,7 @@ import { join, dirname, basename } from "path";
2
2
  import {
3
3
  existsSync,
4
4
  readFileSync,
5
+ writeFileSync,
5
6
  cpSync,
6
7
  rmdirSync,
7
8
  mkdirSync,
@@ -12,40 +13,12 @@ import {
12
13
  import { execSync } from "child_process";
13
14
  import tar from "tar";
14
15
  import { ZstdInit } from "@oneidentity/zstd-js/wasm";
15
- import {platform, arch} from 'os';
16
+ import { OS, ARCH } from '../shared/platform';
17
+ import { getTemplate, getTemplateNames } from './templates/embedded';
16
18
  // import { loadBsdiff, loadBspatch } from 'bsdiff-wasm';
17
19
  // MacOS named pipes hang at around 4KB
18
20
  const MAX_CHUNK_SIZE = 1024 * 2;
19
21
 
20
- // TODO: dedup with built.ts
21
- const OS: 'win' | 'linux' | 'macos' = getPlatform();
22
- // Always use x64 for Windows since we only build x64 Windows binaries
23
- const ARCH: 'arm64' | 'x64' = OS === 'win' ? 'x64' : getArch();
24
-
25
- function getPlatform() {
26
- switch (platform()) {
27
- case "win32":
28
- return 'win';
29
- case "darwin":
30
- return 'macos';
31
- case 'linux':
32
- return 'linux';
33
- default:
34
- throw 'unsupported platform';
35
- }
36
- }
37
-
38
- function getArch() {
39
- switch (arch()) {
40
- case "arm64":
41
- return 'arm64';
42
- case "x64":
43
- return 'x64';
44
- default:
45
- throw 'unsupported arch'
46
- }
47
- }
48
-
49
22
 
50
23
  const binExt = OS === 'win' ? '.exe' : '';
51
24
 
@@ -711,8 +684,57 @@ const bundleFileName = targetOS === 'macos' ? `${appFileName}.app` : appFileName
711
684
  let proc = null;
712
685
 
713
686
  if (commandArg === "init") {
714
- // todo (yoav): init a repo folder structure
715
- console.log("initializing electrobun project");
687
+ const projectName = process.argv[indexOfElectrobun + 2] || "my-electrobun-app";
688
+ const templateName = process.argv.find(arg => arg.startsWith("--template="))?.split("=")[1] || "hello-world";
689
+
690
+ console.log(`🚀 Initializing Electrobun project: ${projectName}`);
691
+
692
+ // Validate template name
693
+ const availableTemplates = getTemplateNames();
694
+ if (!availableTemplates.includes(templateName)) {
695
+ console.error(`❌ Template "${templateName}" not found.`);
696
+ console.log(`Available templates: ${availableTemplates.join(", ")}`);
697
+ process.exit(1);
698
+ }
699
+
700
+ const template = getTemplate(templateName);
701
+ if (!template) {
702
+ console.error(`❌ Could not load template "${templateName}"`);
703
+ process.exit(1);
704
+ }
705
+
706
+ // Create project directory
707
+ const projectPath = join(process.cwd(), projectName);
708
+ if (existsSync(projectPath)) {
709
+ console.error(`❌ Directory "${projectName}" already exists.`);
710
+ process.exit(1);
711
+ }
712
+
713
+ mkdirSync(projectPath, { recursive: true });
714
+
715
+ // Extract template files
716
+ let fileCount = 0;
717
+ for (const [relativePath, content] of Object.entries(template.files)) {
718
+ const fullPath = join(projectPath, relativePath);
719
+ const dir = dirname(fullPath);
720
+
721
+ // Create directory if it doesn't exist
722
+ mkdirSync(dir, { recursive: true });
723
+
724
+ // Write file
725
+ writeFileSync(fullPath, content, 'utf-8');
726
+ fileCount++;
727
+ }
728
+
729
+ console.log(`✅ Created ${fileCount} files from "${templateName}" template`);
730
+ console.log(`📁 Project created at: ${projectPath}`);
731
+ console.log("");
732
+ console.log("📦 Next steps:");
733
+ console.log(` cd ${projectName}`);
734
+ console.log(" bun install");
735
+ console.log(" bunx electrobun dev");
736
+ console.log("");
737
+ console.log("🎉 Happy building with Electrobun!");
716
738
  } else if (commandArg === "build") {
717
739
  // Ensure core binaries are available for the target platform before starting build
718
740
  await ensureCoreDependencies(currentTarget.os, currentTarget.arch);
@@ -1306,7 +1328,7 @@ if (commandArg === "init") {
1306
1328
  // 6.5. code sign and notarize the dmg
1307
1329
  // 7. copy artifacts to directory [self-extractor dmg, zstd app bundle, bsdiff patch, update.json]
1308
1330
 
1309
- // Add platform suffix for all artifacts
1331
+ // Platform suffix is only used for folder names, not file names
1310
1332
  const platformSuffix = `-${targetOS}-${targetARCH}`;
1311
1333
  const tarPath = `${appBundleFolderPath}.tar`;
1312
1334
 
@@ -1331,8 +1353,7 @@ if (commandArg === "init") {
1331
1353
  // than saving 1 more MB of space/bandwidth.
1332
1354
 
1333
1355
  const compressedTarPath = `${tarPath}.zst`;
1334
- const platformCompressedTarPath = compressedTarPath.replace('.tar.zst', `${platformSuffix}.tar.zst`);
1335
- artifactsToUpload.push(platformCompressedTarPath);
1356
+ artifactsToUpload.push(compressedTarPath);
1336
1357
 
1337
1358
  // zstd compress tarball
1338
1359
  // todo (yoav): consider using c bindings for zstd for speed instead of wasm
@@ -1361,8 +1382,6 @@ if (commandArg === "init") {
1361
1382
  );
1362
1383
 
1363
1384
  await Bun.write(compressedTarPath, compressedData);
1364
- // Copy to platform-specific filename for upload
1365
- cpSync(compressedTarPath, platformCompressedTarPath);
1366
1385
  }
1367
1386
  });
1368
1387
 
@@ -1416,8 +1435,7 @@ if (commandArg === "init") {
1416
1435
  console.log("creating dmg...");
1417
1436
  // make a dmg
1418
1437
  const dmgPath = join(buildFolder, `${appFileName}.dmg`);
1419
- const platformDmgPath = join(buildFolder, `${appFileName}${platformSuffix}.dmg`);
1420
- artifactsToUpload.push(platformDmgPath);
1438
+ artifactsToUpload.push(dmgPath);
1421
1439
  // hdiutil create -volname "YourAppName" -srcfolder /path/to/YourApp.app -ov -format UDZO YourAppName.dmg
1422
1440
  // Note: use ULFO (lzfse) for better compatibility with large CEF frameworks and modern macOS
1423
1441
  execSync(
@@ -1437,9 +1455,6 @@ if (commandArg === "init") {
1437
1455
  } else {
1438
1456
  console.log("skipping notarization");
1439
1457
  }
1440
-
1441
- // Copy to platform-specific filename
1442
- cpSync(dmgPath, platformDmgPath);
1443
1458
  } else {
1444
1459
  // For Windows and Linux, add the self-extracting bundle directly
1445
1460
  const platformBundlePath = join(buildFolder, `${appFileName}${platformSuffix}${targetOS === 'win' ? '.exe' : ''}`);
@@ -1447,10 +1462,10 @@ if (commandArg === "init") {
1447
1462
  if (targetOS === 'win') {
1448
1463
  // On Windows, create a self-extracting exe
1449
1464
  // For now, just copy the bundle folder
1450
- artifactsToUpload.push(compressedTarPath.replace('.tar.zst', `${platformSuffix}.tar.zst`));
1465
+ artifactsToUpload.push(compressedTarPath);
1451
1466
  } else if (targetOS === 'linux') {
1452
1467
  // On Linux, create a tar.gz of the bundle
1453
- const linuxTarPath = join(buildFolder, `${appFileName}${platformSuffix}.tar.gz`);
1468
+ const linuxTarPath = join(buildFolder, `${appFileName}.tar.gz`);
1454
1469
  execSync(`tar -czf ${escapePathForTerminal(linuxTarPath)} -C ${escapePathForTerminal(buildFolder)} ${escapePathForTerminal(basename(appBundleFolderPath))}`);
1455
1470
  artifactsToUpload.push(linuxTarPath);
1456
1471
  }
@@ -1478,9 +1493,8 @@ if (commandArg === "init") {
1478
1493
  // bucketUrl: config.release.bucketUrl
1479
1494
  });
1480
1495
 
1481
- // Platform-specific update.json
1482
- const platformUpdateJsonName = `update${platformSuffix}.json`;
1483
- await Bun.write(join(artifactFolder, platformUpdateJsonName), updateJsonContent);
1496
+ // update.json (no platform suffix in filename, platform is in folder name)
1497
+ await Bun.write(join(artifactFolder, 'update.json'), updateJsonContent);
1484
1498
 
1485
1499
  // generate bsdiff
1486
1500
  // https://storage.googleapis.com/eggbun-static/electrobun-playground/canary/ElectrobunPlayground-canary.app.tar.zst
@@ -1495,8 +1509,8 @@ if (commandArg === "init") {
1495
1509
  } else {
1496
1510
  const urlToPrevUpdateJson = join(
1497
1511
  config.release.bucketUrl,
1498
- buildEnvironment,
1499
- `update${platformSuffix}.json`
1512
+ buildSubFolder,
1513
+ 'update.json'
1500
1514
  );
1501
1515
  const cacheBuster = Math.random().toString(36).substring(7);
1502
1516
  const updateJsonResponse = await fetch(
@@ -1507,8 +1521,8 @@ if (commandArg === "init") {
1507
1521
 
1508
1522
  const urlToLatestTarball = join(
1509
1523
  config.release.bucketUrl,
1510
- buildEnvironment,
1511
- `${appFileName}.app${platformSuffix}.tar.zst`
1524
+ buildSubFolder,
1525
+ `${appFileName}.app.tar.zst`
1512
1526
  );
1513
1527
 
1514
1528
 
@@ -1555,8 +1569,7 @@ if (commandArg === "init") {
1555
1569
  // especially for creating multiple diffs in parallel
1556
1570
  const bsdiffpath = targetPaths.BSDIFF;
1557
1571
  const patchFilePath = join(buildFolder, `${prevHash}.patch`);
1558
- const platformPatchFilePath = join(buildFolder, `${prevHash}${platformSuffix}.patch`);
1559
- artifactsToUpload.push(platformPatchFilePath);
1572
+ artifactsToUpload.push(patchFilePath);
1560
1573
  const result = Bun.spawnSync(
1561
1574
  [bsdiffpath, prevTarballPath, tarPath, patchFilePath, "--use-zstd"],
1562
1575
  { cwd: buildFolder }
@@ -1566,8 +1579,6 @@ if (commandArg === "init") {
1566
1579
  result.stdout.toString(),
1567
1580
  result.stderr.toString()
1568
1581
  );
1569
- // Copy to platform-specific filename
1570
- cpSync(patchFilePath, platformPatchFilePath);
1571
1582
  }
1572
1583
  } else {
1573
1584
  console.log("prevoius version not found at: ", urlToLatestTarball);
@@ -0,0 +1,57 @@
1
+ # Electrobun Hello World
2
+
3
+ A simple Electrobun app to get you started with the framework.
4
+
5
+ ## What You'll See
6
+
7
+ This hello world app demonstrates:
8
+ - **Native Window**: A cross-platform desktop window
9
+ - **Web-based UI**: Modern HTML, CSS, and JavaScript interface
10
+ - **Simple Architecture**: Clean separation between Bun process and UI
11
+
12
+ ## Getting Started
13
+
14
+ 1. Install dependencies:
15
+ ```bash
16
+ bun install
17
+ ```
18
+
19
+ 2. Run in development mode:
20
+ ```bash
21
+ bun run dev
22
+ ```
23
+
24
+ 3. Build for production:
25
+ ```bash
26
+ bun run build
27
+ ```
28
+
29
+ ## Project Structure
30
+
31
+ ```
32
+ src/
33
+ ├── bun/
34
+ │ └── index.ts # Main process - creates and manages windows
35
+ └── mainview/
36
+ ├── index.html # Your app's UI
37
+ ├── index.css # Styles
38
+ └── index.ts # View logic
39
+ ```
40
+
41
+ ## Next Steps
42
+
43
+ Ready to build something more complex? Check out:
44
+
45
+ - **[Documentation](https://docs.electrobun.dev)** - Learn about all Electrobun features
46
+ - **[Examples](https://github.com/blackboardsh/electrobun/tree/main/playground)** - See advanced features like RPC, menus, and system tray
47
+ - **[GitHub](https://github.com/blackboardsh/electrobun)** - Star the repo and join the community
48
+
49
+ ### Add More Features
50
+
51
+ Want to extend this app? Try adding:
52
+ - RPC communication between Bun and webview
53
+ - Native menus and system tray
54
+ - File dialogs and system integration
55
+ - Multiple windows and views
56
+
57
+ Happy building! 🚀
@@ -0,0 +1,63 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "electrobun-hello-world",
6
+ "dependencies": {
7
+ "electrobun": "latest",
8
+ },
9
+ "devDependencies": {
10
+ "@types/bun": "latest",
11
+ },
12
+ },
13
+ },
14
+ "packages": {
15
+ "@oneidentity/zstd-js": ["@oneidentity/zstd-js@1.0.3", "", { "dependencies": { "@types/emscripten": "^1.39.4" } }, "sha512-Jm6sawqxLzBrjC4sg2BeXToa33yPzUmq20CKsehKY2++D/gHb/oSwVjNgT+RH4vys+r8FynrgcNzGwhZWMLzfQ=="],
16
+
17
+ "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="],
18
+
19
+ "@types/emscripten": ["@types/emscripten@1.40.1", "", {}, "sha512-sr53lnYkQNhjHNN0oJDdUm5564biioI5DuOpycufDVK7D3y+GR3oUswe2rlwY1nPNyusHbrJ9WoTyIHl4/Bpwg=="],
20
+
21
+ "@types/filesystem": ["@types/filesystem@0.0.36", "", { "dependencies": { "@types/filewriter": "*" } }, "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA=="],
22
+
23
+ "@types/filewriter": ["@types/filewriter@0.0.33", "", {}, "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g=="],
24
+
25
+ "@types/har-format": ["@types/har-format@1.2.16", "", {}, "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A=="],
26
+
27
+ "@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
28
+
29
+ "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="],
30
+
31
+ "@types/webextension-polyfill": ["@types/webextension-polyfill@0.12.3", "", {}, "sha512-F58aDVSeN/MjUGazXo/cPsmR76EvqQhQ1v4x23hFjUX0cfAJYE+JBWwiOGW36/VJGGxoH74sVlRIF3z7SJCKyg=="],
32
+
33
+ "browser-namespace": ["browser-namespace@1.4.0", "", { "dependencies": { "@types/filesystem": "*", "@types/har-format": "*", "@types/webextension-polyfill": "*" } }, "sha512-9b4yNTNs+8HVPssSq8RSZMRunf+G4cVQ2PMtOTn+uEVFOW5C0Uo+eGXuJ5LfxS1UDph5oAdWj92thPyxVhpqXg=="],
34
+
35
+ "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="],
36
+
37
+ "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
38
+
39
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
40
+
41
+ "electrobun": ["electrobun@0.0.18", "", { "dependencies": { "@oneidentity/zstd-js": "^1.0.3", "rpc-anywhere": "1.5.0", "tar": "^6.2.1" }, "bin": { "electrobun": "dist/electrobun" } }, "sha512-RyMNGcAaHklicZlJToGfN3fVZGKHpxZv6o8S96TK9tHSY/SRze5bNPIGUnY9wr/BbWuQW5gGUGaVIWWqa5NSZQ=="],
42
+
43
+ "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="],
44
+
45
+ "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="],
46
+
47
+ "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="],
48
+
49
+ "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
50
+
51
+ "rpc-anywhere": ["rpc-anywhere@1.5.0", "", { "dependencies": { "browser-namespace": "^1.4.0" } }, "sha512-ZYrB0foAM4oE7oBnUH3BL7LwtW9d6+RkzL/rFnjj8GCaFt5c81Rbw6oVl6u9AMsGONsKeJX0mL62TpbPXSO6og=="],
52
+
53
+ "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
54
+
55
+ "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
56
+
57
+ "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
58
+
59
+ "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
60
+
61
+ "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
62
+ }
63
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "app": {
3
+ "name": "hellow-world",
4
+ "identifier": "helloworld.electrobun.dev",
5
+ "version": "0.0.1"
6
+ },
7
+ "build": {
8
+ "mac": {
9
+ "bundleCEF": false
10
+ }
11
+ },
12
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "electrobun-hello-world",
3
+ "version": "1.0.0",
4
+ "description": "A simple Electrobun app showcasing core features",
5
+ "scripts": {
6
+ "dev": "electrobun dev",
7
+ "build": "electrobun build",
8
+ "start": "bun run build && bun run dev"
9
+ },
10
+ "dependencies": {
11
+ "electrobun": "latest"
12
+ },
13
+ "devDependencies": {
14
+ "@types/bun": "latest"
15
+ }
16
+ }
@@ -0,0 +1,15 @@
1
+ import { BrowserWindow } from "electrobun/bun";
2
+
3
+ // Create the main application window
4
+ const mainWindow = new BrowserWindow({
5
+ title: "Hello Electrobun!",
6
+ url: "views://main/index.html",
7
+ frame: {
8
+ width: 800,
9
+ height: 600,
10
+ x: 200,
11
+ y: 200,
12
+ },
13
+ });
14
+
15
+ console.log("Hello Electrobun app started!");
@@ -0,0 +1,124 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ body {
6
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
7
+ margin: 0;
8
+ padding: 0;
9
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
10
+ color: #333;
11
+ min-height: 100vh;
12
+ }
13
+
14
+ .container {
15
+ max-width: 800px;
16
+ margin: 0 auto;
17
+ padding: 40px 20px;
18
+ }
19
+
20
+ h1 {
21
+ color: white;
22
+ font-size: 3rem;
23
+ text-align: center;
24
+ margin-bottom: 8px;
25
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
26
+ }
27
+
28
+ .subtitle {
29
+ color: rgba(255, 255, 255, 0.9);
30
+ font-size: 1.25rem;
31
+ text-align: center;
32
+ margin-top: 0;
33
+ margin-bottom: 40px;
34
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
35
+ }
36
+
37
+ .welcome-section {
38
+ background: white;
39
+ border-radius: 12px;
40
+ padding: 30px;
41
+ margin: 30px 0;
42
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
43
+ line-height: 1.6;
44
+ }
45
+
46
+ h2 {
47
+ color: #2563eb;
48
+ margin-top: 30px;
49
+ margin-bottom: 15px;
50
+ }
51
+
52
+ ul {
53
+ margin: 20px 0;
54
+ padding-left: 20px;
55
+ }
56
+
57
+ li {
58
+ margin: 8px 0;
59
+ }
60
+
61
+ .links {
62
+ display: flex;
63
+ gap: 15px;
64
+ margin: 25px 0;
65
+ flex-wrap: wrap;
66
+ }
67
+
68
+ .doc-link {
69
+ display: inline-block;
70
+ background: #2563eb;
71
+ color: white;
72
+ text-decoration: none;
73
+ padding: 12px 20px;
74
+ border-radius: 8px;
75
+ font-weight: 500;
76
+ transition: all 0.2s ease;
77
+ box-shadow: 0 2px 4px rgba(37, 99, 235, 0.2);
78
+ }
79
+
80
+ .doc-link:hover {
81
+ background: #1d4ed8;
82
+ transform: translateY(-1px);
83
+ box-shadow: 0 4px 8px rgba(37, 99, 235, 0.3);
84
+ }
85
+
86
+ code {
87
+ background: #f1f5f9;
88
+ color: #475569;
89
+ padding: 2px 6px;
90
+ border-radius: 4px;
91
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
92
+ font-size: 0.9em;
93
+ }
94
+
95
+ .footer {
96
+ text-align: center;
97
+ color: rgba(255, 255, 255, 0.8);
98
+ margin-top: 40px;
99
+ padding: 20px;
100
+ background: rgba(255, 255, 255, 0.1);
101
+ border-radius: 8px;
102
+ backdrop-filter: blur(10px);
103
+ }
104
+
105
+ .footer p {
106
+ margin: 8px 0;
107
+ }
108
+
109
+ /* Dark mode support */
110
+ @media (prefers-color-scheme: dark) {
111
+ .welcome-section {
112
+ background: #1f2937;
113
+ color: #f3f4f6;
114
+ }
115
+
116
+ h2 {
117
+ color: #60a5fa;
118
+ }
119
+
120
+ code {
121
+ background: #374151;
122
+ color: #d1d5db;
123
+ }
124
+ }
@@ -0,0 +1,47 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Hello Electrobun!</title>
7
+ <link rel="stylesheet" href="index.css">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <h1>Hello Electrobun! 🎉</h1>
12
+ <p class="subtitle">A fast, cross-platform desktop app framework</p>
13
+
14
+ <div class="welcome-section">
15
+ <p>Welcome to your first Electrobun app! This framework combines the power of Bun with native desktop capabilities.</p>
16
+
17
+ <h2>What is Electrobun?</h2>
18
+ <ul>
19
+ <li><strong>Fast:</strong> Built on Bun's lightning-fast JavaScript runtime</li>
20
+ <li><strong>Native:</strong> Access to system APIs like menus, trays, and file dialogs</li>
21
+ <li><strong>Cross-platform:</strong> Works on macOS, Windows, and Linux</li>
22
+ <li><strong>Web-based UI:</strong> Use familiar HTML, CSS, and JavaScript for your interface</li>
23
+ </ul>
24
+
25
+ <h2>Get Started</h2>
26
+ <p>Ready to build something amazing? Check out the documentation and examples:</p>
27
+
28
+ <div class="links">
29
+ <a href="https://docs.electrobun.dev" target="_blank" class="doc-link">
30
+ 📚 Documentation
31
+ </a>
32
+ <a href="https://github.com/blackboardsh/electrobun" target="_blank" class="doc-link">
33
+ 🐙 GitHub Repository
34
+ </a>
35
+ <a href="https://docs.electrobun.dev/examples" target="_blank" class="doc-link">
36
+ 💡 Examples
37
+ </a>
38
+ </div>
39
+ </div>
40
+
41
+ <div class="footer">
42
+ <p>Edit <code>src/bun/index.ts</code> and <code>src/mainview/</code> to customize your app</p>
43
+ <p>Press F12 to open DevTools</p>
44
+ </div>
45
+ </div>
46
+ </body>
47
+ </html>
@@ -0,0 +1,5 @@
1
+ // Simple Hello World - no RPC needed for this basic template
2
+ console.log("Hello Electrobun view loaded!");
3
+
4
+ // You can add interactive functionality here
5
+ // For RPC communication with the Bun process, check out the playground example