electrobun 0.0.19-beta.118 → 0.0.19-beta.119
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 +1 -1
- package/dist/api/browser/webviewtag.ts +54 -2
- package/dist/api/bun/ElectrobunConfig.ts +171 -0
- package/dist/api/bun/core/BrowserWindow.ts +4 -0
- package/dist/api/bun/core/Tray.ts +14 -0
- package/dist/api/bun/index.ts +2 -0
- package/dist/api/bun/proc/native.ts +76 -5
- package/package.json +4 -2
- package/src/cli/index.ts +43 -17
- package/templates/hello-world/electrobun.config.ts +28 -0
- package/templates/hello-world/package.json +1 -1
- package/templates/interactive-playground/README.md +26 -0
- package/templates/interactive-playground/assets/tray-icon.png +0 -0
- package/templates/interactive-playground/electrobun.config.ts +36 -0
- package/templates/interactive-playground/package-lock.json +36 -0
- package/templates/interactive-playground/package.json +15 -0
- package/templates/interactive-playground/src/bun/demos/files.ts +70 -0
- package/templates/interactive-playground/src/bun/demos/menus.ts +139 -0
- package/templates/interactive-playground/src/bun/demos/rpc.ts +83 -0
- package/templates/interactive-playground/src/bun/demos/system.ts +72 -0
- package/templates/interactive-playground/src/bun/demos/updates.ts +105 -0
- package/templates/interactive-playground/src/bun/demos/windows.ts +90 -0
- package/templates/interactive-playground/src/bun/index.ts +124 -0
- package/templates/interactive-playground/src/bun/types/rpc.ts +109 -0
- package/templates/interactive-playground/src/mainview/components/EventLog.ts +107 -0
- package/templates/interactive-playground/src/mainview/components/Sidebar.ts +65 -0
- package/templates/interactive-playground/src/mainview/components/Toast.ts +57 -0
- package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +211 -0
- package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +102 -0
- package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +229 -0
- package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +132 -0
- package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +411 -0
- package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +207 -0
- package/templates/interactive-playground/src/mainview/index.css +538 -0
- package/templates/interactive-playground/src/mainview/index.html +103 -0
- package/templates/interactive-playground/src/mainview/index.ts +238 -0
- package/templates/multitab-browser/README.md +34 -0
- package/templates/multitab-browser/bun.lock +224 -0
- package/templates/multitab-browser/electrobun.config.ts +32 -0
- package/templates/multitab-browser/package-lock.json +20 -0
- package/templates/multitab-browser/package.json +12 -0
- package/templates/multitab-browser/src/bun/index.ts +137 -0
- package/templates/multitab-browser/src/bun/tabManager.ts +200 -0
- package/templates/multitab-browser/src/bun/types/rpc.ts +78 -0
- package/templates/multitab-browser/src/mainview/index.css +487 -0
- package/templates/multitab-browser/src/mainview/index.html +94 -0
- package/templates/multitab-browser/src/mainview/index.ts +629 -0
- package/templates/photo-booth/electrobun.config.ts +28 -0
- package/templates/photo-booth/package.json +1 -1
- package/tests/bun.lock +14 -0
- package/tests/electrobun.config.ts +45 -0
- package/tests/package-lock.json +36 -0
- package/tests/package.json +13 -0
- package/tests/src/bun/index.ts +100 -0
- package/tests/src/bun/test-runner.ts +508 -0
- package/tests/src/mainview/index.html +110 -0
- package/tests/src/mainview/index.ts +458 -0
- package/tests/src/mainview/styles/main.css +451 -0
- package/tests/src/testviews/tray-test.html +57 -0
- package/tests/src/testviews/webview-mask.html +114 -0
- package/tests/src/testviews/webview-navigation.html +36 -0
- package/tests/src/testviews/window-create.html +17 -0
- package/tests/src/testviews/window-events.html +29 -0
- package/tests/src/testviews/window-focus.html +37 -0
- package/tests/src/webviewtag/index.ts +11 -0
- package/templates/hello-world/electrobun.config +0 -28
- package/templates/photo-booth/electrobun.config +0 -28
- package/templates/photo-booth/src/mainview/index_old.ts +0 -671
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Electrobun Interactive Playground
|
|
2
|
+
|
|
3
|
+
An interactive playground for exploring all Electrobun features through a clean, organized interface.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🪟 **Window Management** - Create and manage multiple windows
|
|
8
|
+
- 📡 **RPC Communication** - Test bidirectional messaging
|
|
9
|
+
- 🎛️ **Menu Systems** - Application and context menus
|
|
10
|
+
- 🔔 **System Tray** - Tray icon management
|
|
11
|
+
- 🗂️ **File Operations** - File dialogs and system integration
|
|
12
|
+
- 🌐 **WebView Features** - Advanced webview capabilities
|
|
13
|
+
- ⚙️ **System Utilities** - Platform info and notifications
|
|
14
|
+
- 🔄 **Auto-Updates** - Update system demonstration
|
|
15
|
+
|
|
16
|
+
## Getting Started
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
npm run build:dev
|
|
21
|
+
npm start
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Development
|
|
25
|
+
|
|
26
|
+
This template demonstrates best practices for building Electrobun applications with modern UI patterns and comprehensive feature coverage.
|
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
app: {
|
|
3
|
+
name: "Electrobun Interactive Playground",
|
|
4
|
+
identifier: "dev.electrobun.interactive-playground",
|
|
5
|
+
version: "0.0.1",
|
|
6
|
+
},
|
|
7
|
+
build: {
|
|
8
|
+
bun: {
|
|
9
|
+
entrypoint: "src/bun/index.ts",
|
|
10
|
+
external: [],
|
|
11
|
+
},
|
|
12
|
+
views: {
|
|
13
|
+
mainview: {
|
|
14
|
+
entrypoint: "src/mainview/index.ts",
|
|
15
|
+
external: [],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
copy: {
|
|
19
|
+
"src/mainview/index.html": "views/mainview/index.html",
|
|
20
|
+
"src/mainview/index.css": "views/mainview/index.css",
|
|
21
|
+
"assets/tray-icon.png": "views/assets/tray-icon.png",
|
|
22
|
+
},
|
|
23
|
+
mac: {
|
|
24
|
+
codesign: true,
|
|
25
|
+
notarize: false,
|
|
26
|
+
bundleCEF: true,
|
|
27
|
+
entitlements: {},
|
|
28
|
+
},
|
|
29
|
+
linux: {
|
|
30
|
+
bundleCEF: true,
|
|
31
|
+
},
|
|
32
|
+
win: {
|
|
33
|
+
bundleCEF: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "electrobun-interactive-playground",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "electrobun-interactive-playground",
|
|
9
|
+
"version": "0.0.1",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"electrobun": "file:../../"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"../..": {
|
|
15
|
+
"version": "0.0.19-beta.118",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@oneidentity/zstd-js": "^1.0.3",
|
|
19
|
+
"archiver": "^7.0.1",
|
|
20
|
+
"rpc-anywhere": "1.5.0",
|
|
21
|
+
"tar": "^6.2.1"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"electrobun": "bin/electrobun.cjs"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/archiver": "^6.0.3",
|
|
28
|
+
"@types/bun": "1.1.9"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"node_modules/electrobun": {
|
|
32
|
+
"resolved": "../..",
|
|
33
|
+
"link": true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "electrobun-interactive-playground",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Interactive playground for exploring Electrobun features",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build:dev": "electrobun build",
|
|
8
|
+
"build:canary": "electrobun build --canary",
|
|
9
|
+
"start": "electrobun dev",
|
|
10
|
+
"start:canary": "electrobun dev --canary"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"electrobun": "latest"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Utils } from "electrobun/bun";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
|
|
5
|
+
class FileManager {
|
|
6
|
+
async openFileDialog(options: {
|
|
7
|
+
multiple?: boolean;
|
|
8
|
+
fileTypes?: string[];
|
|
9
|
+
startingFolder?: string;
|
|
10
|
+
}) {
|
|
11
|
+
try {
|
|
12
|
+
const result = await Utils.openFileDialog({
|
|
13
|
+
startingFolder: options.startingFolder || join(homedir(), "Desktop"),
|
|
14
|
+
allowedFileTypes: options.fileTypes?.join(",") || "*",
|
|
15
|
+
canChooseFiles: true,
|
|
16
|
+
canChooseDirectory: false,
|
|
17
|
+
allowsMultipleSelection: options.multiple || false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Filter out empty strings
|
|
21
|
+
const filteredResult = result.filter(path => path.trim() !== "");
|
|
22
|
+
this.onFileSelected?.(filteredResult);
|
|
23
|
+
|
|
24
|
+
return filteredResult;
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error("File dialog error:", error);
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async moveToTrash(path: string) {
|
|
32
|
+
try {
|
|
33
|
+
await Utils.moveToTrash(path);
|
|
34
|
+
this.onSystemEvent?.({
|
|
35
|
+
type: 'file-trashed',
|
|
36
|
+
details: { path, success: true }
|
|
37
|
+
});
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("Move to trash error:", error);
|
|
40
|
+
this.onSystemEvent?.({
|
|
41
|
+
type: 'file-trashed',
|
|
42
|
+
details: { path, success: false, error: error.message }
|
|
43
|
+
});
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async showInFinder(path: string) {
|
|
49
|
+
try {
|
|
50
|
+
await Utils.showItemInFolder(path);
|
|
51
|
+
this.onSystemEvent?.({
|
|
52
|
+
type: 'show-in-finder',
|
|
53
|
+
details: { path, success: true }
|
|
54
|
+
});
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error("Show in finder error:", error);
|
|
57
|
+
this.onSystemEvent?.({
|
|
58
|
+
type: 'show-in-finder',
|
|
59
|
+
details: { path, success: false, error: error.message }
|
|
60
|
+
});
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Event callbacks
|
|
66
|
+
onFileSelected?: (paths: string[]) => void;
|
|
67
|
+
onSystemEvent?: (event: { type: string; details: any }) => void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const fileManager = new FileManager();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ApplicationMenu, ContextMenu, Tray } from "electrobun/bun";
|
|
2
|
+
|
|
3
|
+
class MenuManager {
|
|
4
|
+
private trays = new Map<number, Tray>();
|
|
5
|
+
private nextTrayId = 1;
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.setupApplicationMenu();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
private setupApplicationMenu() {
|
|
12
|
+
ApplicationMenu.setApplicationMenu([
|
|
13
|
+
{
|
|
14
|
+
submenu: [
|
|
15
|
+
{ label: "About", role: "about" },
|
|
16
|
+
{ type: "separator" },
|
|
17
|
+
{ label: "Quit", role: "quit", accelerator: "q" }
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: "Edit",
|
|
22
|
+
submenu: [
|
|
23
|
+
{ role: "undo" },
|
|
24
|
+
{ role: "redo" },
|
|
25
|
+
{ type: "separator" },
|
|
26
|
+
{
|
|
27
|
+
label: "Custom Demo Action",
|
|
28
|
+
action: "demo-action",
|
|
29
|
+
tooltip: "This is a demo menu item",
|
|
30
|
+
},
|
|
31
|
+
{ type: "separator" },
|
|
32
|
+
{ role: "cut" },
|
|
33
|
+
{ role: "copy" },
|
|
34
|
+
{ role: "paste" },
|
|
35
|
+
{ role: "selectAll" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: "View",
|
|
40
|
+
submenu: [
|
|
41
|
+
{ role: "reload" },
|
|
42
|
+
{ role: "forceReload" },
|
|
43
|
+
{ role: "toggleDevTools" },
|
|
44
|
+
{ type: "separator" },
|
|
45
|
+
{ role: "resetZoom" },
|
|
46
|
+
{ role: "zoomIn" },
|
|
47
|
+
{ role: "zoomOut" },
|
|
48
|
+
{ type: "separator" },
|
|
49
|
+
{ role: "togglefullscreen" }
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
label: "Window",
|
|
54
|
+
submenu: [
|
|
55
|
+
{ role: "minimize" },
|
|
56
|
+
{ role: "close" }
|
|
57
|
+
],
|
|
58
|
+
}
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async createTray(options: { title: string; image?: string }) {
|
|
63
|
+
const id = this.nextTrayId++;
|
|
64
|
+
|
|
65
|
+
const tray = new Tray({
|
|
66
|
+
title: options.title,
|
|
67
|
+
image: options.image || "views://assets/tray-icon.png",
|
|
68
|
+
template: true,
|
|
69
|
+
width: 32,
|
|
70
|
+
height: 32,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Set up tray menu
|
|
74
|
+
tray.setMenu([
|
|
75
|
+
{
|
|
76
|
+
type: "normal",
|
|
77
|
+
label: "Show Playground",
|
|
78
|
+
action: "show-playground",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "separator",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "normal",
|
|
85
|
+
label: "Demo Action",
|
|
86
|
+
action: "demo-tray-action",
|
|
87
|
+
tooltip: "This is a demo tray action",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: "normal",
|
|
91
|
+
label: "Quit",
|
|
92
|
+
action: "quit-app",
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
tray.on("tray-clicked", (e) => {
|
|
97
|
+
this.onTrayClicked?.(id, e.data.action);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.trays.set(id, tray);
|
|
101
|
+
return { id };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async removeTray(id: number) {
|
|
105
|
+
const tray = this.trays.get(id);
|
|
106
|
+
if (tray) {
|
|
107
|
+
tray.remove();
|
|
108
|
+
this.trays.delete(id);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async showContextMenu(params: { x: number; y: number }) {
|
|
113
|
+
ContextMenu.showContextMenu([
|
|
114
|
+
{ role: "undo" },
|
|
115
|
+
{ role: "redo" },
|
|
116
|
+
{ type: "separator" },
|
|
117
|
+
{
|
|
118
|
+
label: "Demo Context Action",
|
|
119
|
+
action: "demo-context-action",
|
|
120
|
+
tooltip: "This is a demo context menu item",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
label: "Disabled Action",
|
|
124
|
+
action: "disabled-action",
|
|
125
|
+
enabled: false,
|
|
126
|
+
},
|
|
127
|
+
{ type: "separator" },
|
|
128
|
+
{ role: "cut" },
|
|
129
|
+
{ role: "copy" },
|
|
130
|
+
{ role: "paste" },
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Event callbacks
|
|
135
|
+
onTrayClicked?: (id: number, action: string) => void;
|
|
136
|
+
onMenuClicked?: (action: string) => void;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const menuManager = new MenuManager();
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
class RPCTester {
|
|
2
|
+
async doMath(data: { a: number; b: number; operation: string }): Promise<number> {
|
|
3
|
+
const startTime = Date.now();
|
|
4
|
+
let result: number;
|
|
5
|
+
|
|
6
|
+
switch (data.operation) {
|
|
7
|
+
case 'add':
|
|
8
|
+
result = data.a + data.b;
|
|
9
|
+
break;
|
|
10
|
+
case 'subtract':
|
|
11
|
+
result = data.a - data.b;
|
|
12
|
+
break;
|
|
13
|
+
case 'multiply':
|
|
14
|
+
result = data.a * data.b;
|
|
15
|
+
break;
|
|
16
|
+
case 'divide':
|
|
17
|
+
if (data.b === 0) throw new Error("Division by zero");
|
|
18
|
+
result = data.a / data.b;
|
|
19
|
+
break;
|
|
20
|
+
case 'power':
|
|
21
|
+
result = Math.pow(data.a, data.b);
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
throw new Error(`Unknown operation: ${data.operation}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const duration = Date.now() - startTime;
|
|
28
|
+
|
|
29
|
+
// Don't send notification here - let the frontend handle timing and display
|
|
30
|
+
// to avoid duplicate entries
|
|
31
|
+
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async echoBigData(data: string): Promise<string> {
|
|
36
|
+
const startTime = Date.now();
|
|
37
|
+
|
|
38
|
+
// Simulate some processing time
|
|
39
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
40
|
+
|
|
41
|
+
const response = `Echo: ${data.slice(0, 100)}... (${data.length} chars)`;
|
|
42
|
+
const duration = Date.now() - startTime;
|
|
43
|
+
|
|
44
|
+
// Don't send notification here - let the frontend handle timing and display
|
|
45
|
+
// to avoid duplicate entries
|
|
46
|
+
|
|
47
|
+
return response;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async performanceTest(messageSize: number, messageCount: number): Promise<{
|
|
51
|
+
totalTime: number;
|
|
52
|
+
averageTime: number;
|
|
53
|
+
messagesPerSecond: number;
|
|
54
|
+
}> {
|
|
55
|
+
const testData = "x".repeat(messageSize);
|
|
56
|
+
const results: number[] = [];
|
|
57
|
+
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < messageCount; i++) {
|
|
61
|
+
const messageStart = Date.now();
|
|
62
|
+
await this.echoBigData(testData);
|
|
63
|
+
results.push(Date.now() - messageStart);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const totalTime = Date.now() - startTime;
|
|
67
|
+
const averageTime = results.reduce((a, b) => a + b, 0) / results.length;
|
|
68
|
+
const messagesPerSecond = (messageCount / totalTime) * 1000;
|
|
69
|
+
|
|
70
|
+
// Don't send notification here - let the frontend handle timing and display
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
totalTime,
|
|
74
|
+
averageTime,
|
|
75
|
+
messagesPerSecond
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Event callbacks
|
|
80
|
+
onRpcTestResult?: (data: { operation: string; result: any; duration: number }) => void;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const rpcTester = new RPCTester();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import Electrobun from "electrobun/bun";
|
|
2
|
+
import { platform, arch, release } from "os";
|
|
3
|
+
|
|
4
|
+
class SystemManager {
|
|
5
|
+
async getPlatformInfo() {
|
|
6
|
+
return {
|
|
7
|
+
platform: platform(),
|
|
8
|
+
arch: arch(),
|
|
9
|
+
version: release(),
|
|
10
|
+
electrobunVersion: "0.0.19-beta.118", // This should come from the actual API
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async showNotification(options: {
|
|
15
|
+
title: string;
|
|
16
|
+
body: string;
|
|
17
|
+
icon?: string;
|
|
18
|
+
}) {
|
|
19
|
+
// Note: Notification API needs to be implemented in Electrobun
|
|
20
|
+
// For now, we'll simulate it
|
|
21
|
+
console.log("Notification:", options);
|
|
22
|
+
|
|
23
|
+
this.onSystemEvent?.({
|
|
24
|
+
type: 'notification-shown',
|
|
25
|
+
details: {
|
|
26
|
+
title: options.title,
|
|
27
|
+
body: options.body,
|
|
28
|
+
timestamp: new Date().toISOString()
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// In a real implementation, this would use the native notification system
|
|
33
|
+
return Promise.resolve();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async getClipboardText(): Promise<string> {
|
|
37
|
+
// Note: Clipboard API needs to be implemented in Electrobun
|
|
38
|
+
// This is a placeholder
|
|
39
|
+
return "Clipboard API not yet implemented";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async setClipboardText(text: string): Promise<void> {
|
|
43
|
+
// Note: Clipboard API needs to be implemented in Electrobun
|
|
44
|
+
console.log("Setting clipboard text:", text);
|
|
45
|
+
|
|
46
|
+
this.onSystemEvent?.({
|
|
47
|
+
type: 'clipboard-set',
|
|
48
|
+
details: { text, timestamp: new Date().toISOString() }
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getScreenInfo() {
|
|
53
|
+
// Note: Screen API needs to be implemented in Electrobun
|
|
54
|
+
// This is a placeholder
|
|
55
|
+
return {
|
|
56
|
+
displays: [
|
|
57
|
+
{
|
|
58
|
+
id: 1,
|
|
59
|
+
width: 1920,
|
|
60
|
+
height: 1080,
|
|
61
|
+
scaleFactor: 1,
|
|
62
|
+
primary: true
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Event callbacks
|
|
69
|
+
onSystemEvent?: (event: { type: string; details: any }) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const systemManager = new SystemManager();
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Electrobun from "electrobun/bun";
|
|
2
|
+
|
|
3
|
+
class UpdateManager {
|
|
4
|
+
async checkForUpdates() {
|
|
5
|
+
try {
|
|
6
|
+
const updateInfo = await Electrobun.Updater.checkForUpdate();
|
|
7
|
+
|
|
8
|
+
this.onUpdateStatus?.({
|
|
9
|
+
status: 'checked',
|
|
10
|
+
progress: 100
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
updateAvailable: updateInfo.updateAvailable,
|
|
15
|
+
currentVersion: Electrobun.Updater.getLocalVersion?.() || "0.0.19-beta.118",
|
|
16
|
+
latestVersion: updateInfo.latestVersion,
|
|
17
|
+
};
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.error("Update check error:", error);
|
|
20
|
+
|
|
21
|
+
this.onUpdateStatus?.({
|
|
22
|
+
status: 'error',
|
|
23
|
+
progress: 0
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
updateAvailable: false,
|
|
28
|
+
currentVersion: "0.0.19-beta.118",
|
|
29
|
+
error: error.message
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async downloadUpdate() {
|
|
35
|
+
try {
|
|
36
|
+
this.onUpdateStatus?.({
|
|
37
|
+
status: 'downloading',
|
|
38
|
+
progress: 0
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Simulate download progress
|
|
42
|
+
for (let i = 0; i <= 100; i += 10) {
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
44
|
+
this.onUpdateStatus?.({
|
|
45
|
+
status: 'downloading',
|
|
46
|
+
progress: i
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await Electrobun.Updater.downloadUpdate();
|
|
51
|
+
|
|
52
|
+
this.onUpdateStatus?.({
|
|
53
|
+
status: 'downloaded',
|
|
54
|
+
progress: 100
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return { success: true };
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Update download error:", error);
|
|
60
|
+
|
|
61
|
+
this.onUpdateStatus?.({
|
|
62
|
+
status: 'error',
|
|
63
|
+
progress: 0
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return { success: false, error: error.message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async applyUpdate() {
|
|
71
|
+
try {
|
|
72
|
+
this.onUpdateStatus?.({
|
|
73
|
+
status: 'applying',
|
|
74
|
+
progress: 50
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
await Electrobun.Updater.applyUpdate();
|
|
78
|
+
|
|
79
|
+
this.onUpdateStatus?.({
|
|
80
|
+
status: 'applied',
|
|
81
|
+
progress: 100
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return { success: true };
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error("Update apply error:", error);
|
|
87
|
+
|
|
88
|
+
this.onUpdateStatus?.({
|
|
89
|
+
status: 'error',
|
|
90
|
+
progress: 0
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return { success: false, error: error.message };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getUpdateInfo() {
|
|
98
|
+
return Electrobun.Updater.updateInfo?.() || null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Event callbacks
|
|
102
|
+
onUpdateStatus?: (data: { status: string; progress?: number }) => void;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const updateManager = new UpdateManager();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { BrowserWindow } from "electrobun/bun";
|
|
2
|
+
|
|
3
|
+
class WindowManager {
|
|
4
|
+
private windows = new Map<number, BrowserWindow>();
|
|
5
|
+
private nextId = 1;
|
|
6
|
+
|
|
7
|
+
async createWindow(options: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
frameless?: boolean;
|
|
13
|
+
transparent?: boolean;
|
|
14
|
+
alwaysOnTop?: boolean;
|
|
15
|
+
}) {
|
|
16
|
+
const id = this.nextId++;
|
|
17
|
+
|
|
18
|
+
const window = new BrowserWindow({
|
|
19
|
+
title: `Demo Window ${id}`,
|
|
20
|
+
url: "views://mainview/index.html",
|
|
21
|
+
renderer: "cef",
|
|
22
|
+
frame: {
|
|
23
|
+
width: options.width,
|
|
24
|
+
height: options.height,
|
|
25
|
+
x: options.x,
|
|
26
|
+
y: options.y,
|
|
27
|
+
},
|
|
28
|
+
titleBarStyle: options.frameless ? "hiddenInset" : "default",
|
|
29
|
+
// For completely frameless, we need to set styleMask
|
|
30
|
+
styleMask: options.frameless ? {
|
|
31
|
+
Borderless: true,
|
|
32
|
+
Titled: false,
|
|
33
|
+
Closable: true,
|
|
34
|
+
Miniaturizable: true,
|
|
35
|
+
Resizable: true,
|
|
36
|
+
} : undefined,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
this.windows.set(id, window);
|
|
40
|
+
|
|
41
|
+
// Listen for window events
|
|
42
|
+
window.on("close", () => {
|
|
43
|
+
this.windows.delete(id);
|
|
44
|
+
this.onWindowClosed?.(id);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
window.on("resize", (event) => {
|
|
48
|
+
this.onWindowEvent?.({ type: 'resize', id, data: event.data });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
window.on("move", (event) => {
|
|
52
|
+
this.onWindowEvent?.({ type: 'move', id, data: event.data });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.onWindowCreated?.(id, `Demo Window ${id}`);
|
|
56
|
+
|
|
57
|
+
return { id };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async closeWindow(id: number) {
|
|
61
|
+
const window = this.windows.get(id);
|
|
62
|
+
if (window) {
|
|
63
|
+
window.close();
|
|
64
|
+
this.windows.delete(id);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async focusWindow(id: number) {
|
|
69
|
+
const window = this.windows.get(id);
|
|
70
|
+
if (window) {
|
|
71
|
+
window.focus();
|
|
72
|
+
this.onWindowFocused?.(id);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getWindowList() {
|
|
77
|
+
return Array.from(this.windows.entries()).map(([id, window]) => ({
|
|
78
|
+
id,
|
|
79
|
+
title: window.getTitle() || `Window ${id}`,
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Event callbacks
|
|
84
|
+
onWindowCreated?: (id: number, title: string) => void;
|
|
85
|
+
onWindowClosed?: (id: number) => void;
|
|
86
|
+
onWindowFocused?: (id: number) => void;
|
|
87
|
+
onWindowEvent?: (event: { type: string; id: number; data: any }) => void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const windowManager = new WindowManager();
|