electrobun 0.0.19-beta.13 → 0.0.19-beta.131

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.
Files changed (100) hide show
  1. package/BUILD.md +90 -0
  2. package/README.md +1 -1
  3. package/bin/electrobun.cjs +2 -9
  4. package/debug.js +5 -0
  5. package/dist/api/browser/builtinrpcSchema.ts +19 -0
  6. package/dist/api/browser/index.ts +409 -0
  7. package/dist/api/browser/rpc/webview.ts +79 -0
  8. package/dist/api/browser/stylesAndElements.ts +3 -0
  9. package/dist/api/browser/webviewtag.ts +586 -0
  10. package/dist/api/bun/ElectrobunConfig.ts +171 -0
  11. package/dist/api/bun/core/ApplicationMenu.ts +66 -0
  12. package/dist/api/bun/core/BrowserView.ts +349 -0
  13. package/dist/api/bun/core/BrowserWindow.ts +195 -0
  14. package/dist/api/bun/core/ContextMenu.ts +67 -0
  15. package/dist/api/bun/core/Paths.ts +5 -0
  16. package/dist/api/bun/core/Socket.ts +181 -0
  17. package/dist/api/bun/core/Tray.ts +121 -0
  18. package/dist/api/bun/core/Updater.ts +681 -0
  19. package/dist/api/bun/core/Utils.ts +48 -0
  20. package/dist/api/bun/events/ApplicationEvents.ts +14 -0
  21. package/dist/api/bun/events/event.ts +29 -0
  22. package/dist/api/bun/events/eventEmitter.ts +45 -0
  23. package/dist/api/bun/events/trayEvents.ts +9 -0
  24. package/dist/api/bun/events/webviewEvents.ts +16 -0
  25. package/dist/api/bun/events/windowEvents.ts +12 -0
  26. package/dist/api/bun/index.ts +47 -0
  27. package/dist/api/bun/proc/linux.md +43 -0
  28. package/dist/api/bun/proc/native.ts +1322 -0
  29. package/dist/api/shared/platform.ts +48 -0
  30. package/dist/main.js +54 -0
  31. package/package.json +11 -6
  32. package/src/cli/index.ts +1353 -239
  33. package/templates/hello-world/README.md +57 -0
  34. package/templates/hello-world/bun.lock +225 -0
  35. package/templates/hello-world/electrobun.config.ts +28 -0
  36. package/templates/hello-world/package.json +16 -0
  37. package/templates/hello-world/src/bun/index.ts +15 -0
  38. package/templates/hello-world/src/mainview/index.css +124 -0
  39. package/templates/hello-world/src/mainview/index.html +46 -0
  40. package/templates/hello-world/src/mainview/index.ts +1 -0
  41. package/templates/interactive-playground/README.md +26 -0
  42. package/templates/interactive-playground/assets/tray-icon.png +0 -0
  43. package/templates/interactive-playground/electrobun.config.ts +36 -0
  44. package/templates/interactive-playground/package-lock.json +36 -0
  45. package/templates/interactive-playground/package.json +15 -0
  46. package/templates/interactive-playground/src/bun/demos/files.ts +70 -0
  47. package/templates/interactive-playground/src/bun/demos/menus.ts +139 -0
  48. package/templates/interactive-playground/src/bun/demos/rpc.ts +83 -0
  49. package/templates/interactive-playground/src/bun/demos/system.ts +72 -0
  50. package/templates/interactive-playground/src/bun/demos/updates.ts +105 -0
  51. package/templates/interactive-playground/src/bun/demos/windows.ts +90 -0
  52. package/templates/interactive-playground/src/bun/index.ts +124 -0
  53. package/templates/interactive-playground/src/bun/types/rpc.ts +109 -0
  54. package/templates/interactive-playground/src/mainview/components/EventLog.ts +107 -0
  55. package/templates/interactive-playground/src/mainview/components/Sidebar.ts +65 -0
  56. package/templates/interactive-playground/src/mainview/components/Toast.ts +57 -0
  57. package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +211 -0
  58. package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +102 -0
  59. package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +229 -0
  60. package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +132 -0
  61. package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +411 -0
  62. package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +207 -0
  63. package/templates/interactive-playground/src/mainview/index.css +538 -0
  64. package/templates/interactive-playground/src/mainview/index.html +103 -0
  65. package/templates/interactive-playground/src/mainview/index.ts +238 -0
  66. package/templates/multitab-browser/README.md +34 -0
  67. package/templates/multitab-browser/bun.lock +224 -0
  68. package/templates/multitab-browser/electrobun.config.ts +32 -0
  69. package/templates/multitab-browser/package-lock.json +20 -0
  70. package/templates/multitab-browser/package.json +12 -0
  71. package/templates/multitab-browser/src/bun/index.ts +144 -0
  72. package/templates/multitab-browser/src/bun/tabManager.ts +200 -0
  73. package/templates/multitab-browser/src/bun/types/rpc.ts +78 -0
  74. package/templates/multitab-browser/src/mainview/index.css +487 -0
  75. package/templates/multitab-browser/src/mainview/index.html +94 -0
  76. package/templates/multitab-browser/src/mainview/index.ts +634 -0
  77. package/templates/photo-booth/README.md +108 -0
  78. package/templates/photo-booth/bun.lock +239 -0
  79. package/templates/photo-booth/electrobun.config.ts +28 -0
  80. package/templates/photo-booth/package.json +16 -0
  81. package/templates/photo-booth/src/bun/index.ts +92 -0
  82. package/templates/photo-booth/src/mainview/index.css +465 -0
  83. package/templates/photo-booth/src/mainview/index.html +124 -0
  84. package/templates/photo-booth/src/mainview/index.ts +499 -0
  85. package/tests/bun.lock +14 -0
  86. package/tests/electrobun.config.ts +45 -0
  87. package/tests/package-lock.json +36 -0
  88. package/tests/package.json +13 -0
  89. package/tests/src/bun/index.ts +100 -0
  90. package/tests/src/bun/test-runner.ts +508 -0
  91. package/tests/src/mainview/index.html +110 -0
  92. package/tests/src/mainview/index.ts +458 -0
  93. package/tests/src/mainview/styles/main.css +451 -0
  94. package/tests/src/testviews/tray-test.html +57 -0
  95. package/tests/src/testviews/webview-mask.html +114 -0
  96. package/tests/src/testviews/webview-navigation.html +36 -0
  97. package/tests/src/testviews/window-create.html +17 -0
  98. package/tests/src/testviews/window-events.html +29 -0
  99. package/tests/src/testviews/window-focus.html +37 -0
  100. package/tests/src/webviewtag/index.ts +11 -0
@@ -0,0 +1,229 @@
1
+ export class RPCDemo {
2
+ private testResults: Array<{ operation: string; result: any; duration: number }> = [];
3
+
4
+ render() {
5
+ return `
6
+ <div class="demo-section">
7
+ <div class="demo-header">
8
+ <span class="demo-icon">📡</span>
9
+ <div>
10
+ <h2 class="demo-title">RPC Communication</h2>
11
+ <p class="demo-description">Test bidirectional communication between the webview and bun process</p>
12
+ </div>
13
+ </div>
14
+
15
+ <div class="demo-controls">
16
+ <h3>Math Operations</h3>
17
+ <div class="control-group">
18
+ <label class="control-label">A:</label>
19
+ <input type="number" id="math-a" class="control-input" value="10">
20
+
21
+ <label class="control-label">B:</label>
22
+ <input type="number" id="math-b" class="control-input" value="5">
23
+
24
+ <label class="control-label">Operation:</label>
25
+ <select id="math-operation" class="control-input" style="width: 120px;">
26
+ <option value="add">Add (+)</option>
27
+ <option value="subtract">Subtract (-)</option>
28
+ <option value="multiply">Multiply (×)</option>
29
+ <option value="divide">Divide (÷)</option>
30
+ <option value="power">Power (^)</option>
31
+ </select>
32
+
33
+ <button class="btn btn-primary" id="do-math">Calculate</button>
34
+ </div>
35
+
36
+ <h3>Data Transfer Test</h3>
37
+ <div class="control-group">
38
+ <label class="control-label">Data Size:</label>
39
+ <select id="data-size" class="control-input" style="width: 150px;">
40
+ <option value="1024">1 KB</option>
41
+ <option value="10240">10 KB</option>
42
+ <option value="102400">100 KB</option>
43
+ <option value="1048576">1 MB</option>
44
+ <option value="2097152">2 MB</option>
45
+ </select>
46
+
47
+ <button class="btn btn-primary" id="test-big-data">Test Echo</button>
48
+ </div>
49
+
50
+ <h3>Performance Test</h3>
51
+ <div class="control-group">
52
+ <label class="control-label">Message Size:</label>
53
+ <select id="perf-size" class="control-input" style="width: 120px;">
54
+ <option value="100">100 B</option>
55
+ <option value="1024">1 KB</option>
56
+ <option value="10240">10 KB</option>
57
+ </select>
58
+
59
+ <label class="control-label">Count:</label>
60
+ <input type="number" id="perf-count" class="control-input" value="10" min="1" max="100">
61
+
62
+ <button class="btn btn-primary" id="run-performance-test">Run Test</button>
63
+ </div>
64
+ </div>
65
+
66
+ <div class="demo-results">
67
+ <div class="results-header">Test Results:</div>
68
+ <div id="rpc-results" class="rpc-results">
69
+ <div class="no-results" style="text-align: center; color: #718096; padding: 2rem;">
70
+ No tests run yet. Use the controls above to start testing RPC communication.
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ `;
76
+ }
77
+
78
+ initialize(rpc: any) {
79
+ const mathBtn = document.getElementById('do-math');
80
+ const bigDataBtn = document.getElementById('test-big-data');
81
+ const perfTestBtn = document.getElementById('run-performance-test');
82
+
83
+ mathBtn?.addEventListener('click', async () => {
84
+ const a = parseFloat((document.getElementById('math-a') as HTMLInputElement).value);
85
+ const b = parseFloat((document.getElementById('math-b') as HTMLInputElement).value);
86
+ const operation = (document.getElementById('math-operation') as HTMLSelectElement).value;
87
+
88
+ try {
89
+ const startTime = Date.now();
90
+ const result = await rpc.request.doMath({ a, b, operation });
91
+ const duration = Date.now() - startTime;
92
+
93
+ this.addTestResult({
94
+ operation: `${a} ${operation} ${b}`,
95
+ result,
96
+ duration
97
+ });
98
+ } catch (error) {
99
+ this.addTestResult({
100
+ operation: `${a} ${operation} ${b}`,
101
+ result: `Error: ${error.message}`,
102
+ duration: 0
103
+ });
104
+ }
105
+ });
106
+
107
+ bigDataBtn?.addEventListener('click', async () => {
108
+ const size = parseInt((document.getElementById('data-size') as HTMLSelectElement).value);
109
+ const testData = 'x'.repeat(size);
110
+
111
+ try {
112
+ const startTime = Date.now();
113
+ const result = await rpc.request.echoBigData(testData);
114
+ const duration = Date.now() - startTime;
115
+
116
+ this.addTestResult({
117
+ operation: `Echo ${this.formatBytes(size)}`,
118
+ result: `Received ${this.formatBytes(result.length)}`,
119
+ duration
120
+ });
121
+ } catch (error) {
122
+ this.addTestResult({
123
+ operation: `Echo ${this.formatBytes(size)}`,
124
+ result: `Error: ${error.message}`,
125
+ duration: 0
126
+ });
127
+ }
128
+ });
129
+
130
+ perfTestBtn?.addEventListener('click', async () => {
131
+ const size = parseInt((document.getElementById('perf-size') as HTMLSelectElement).value);
132
+ const count = parseInt((document.getElementById('perf-count') as HTMLInputElement).value);
133
+
134
+ perfTestBtn.textContent = 'Running...';
135
+ perfTestBtn.setAttribute('disabled', 'true');
136
+
137
+ try {
138
+ const results = await this.runPerformanceTest(rpc, size, count);
139
+ this.addTestResult({
140
+ operation: `Performance: ${count} × ${this.formatBytes(size)}`,
141
+ result: `${results.messagesPerSecond.toFixed(1)} msg/sec, avg: ${results.averageTime.toFixed(1)}ms`,
142
+ duration: results.totalTime
143
+ });
144
+ } catch (error) {
145
+ this.addTestResult({
146
+ operation: `Performance: ${count} × ${this.formatBytes(size)}`,
147
+ result: `Error: ${error.message}`,
148
+ duration: 0
149
+ });
150
+ } finally {
151
+ perfTestBtn.textContent = 'Run Test';
152
+ perfTestBtn.removeAttribute('disabled');
153
+ }
154
+ });
155
+ }
156
+
157
+ private async runPerformanceTest(rpc: any, messageSize: number, messageCount: number) {
158
+ const testData = 'x'.repeat(messageSize);
159
+ const results: number[] = [];
160
+ const startTime = Date.now();
161
+
162
+ for (let i = 0; i < messageCount; i++) {
163
+ const messageStart = Date.now();
164
+ await rpc.request.echoBigData(testData);
165
+ results.push(Date.now() - messageStart);
166
+
167
+ // Update progress
168
+ const progress = ((i + 1) / messageCount) * 100;
169
+ const perfTestBtn = document.getElementById('run-performance-test');
170
+ if (perfTestBtn) {
171
+ perfTestBtn.textContent = `Running... ${progress.toFixed(0)}%`;
172
+ }
173
+ }
174
+
175
+ const totalTime = Date.now() - startTime;
176
+ const averageTime = results.reduce((a, b) => a + b, 0) / results.length;
177
+ const messagesPerSecond = (messageCount / totalTime) * 1000;
178
+
179
+ return { totalTime, averageTime, messagesPerSecond };
180
+ }
181
+
182
+ private addTestResult(result: { operation: string; result: any; duration: number }) {
183
+ this.testResults.unshift(result);
184
+
185
+ // Keep only last 20 results
186
+ if (this.testResults.length > 20) {
187
+ this.testResults = this.testResults.slice(0, 20);
188
+ }
189
+
190
+ this.renderResults();
191
+ }
192
+
193
+ private renderResults() {
194
+ const container = document.getElementById('rpc-results');
195
+ if (!container) return;
196
+
197
+ if (this.testResults.length === 0) {
198
+ container.innerHTML = `
199
+ <div class="no-results" style="text-align: center; color: #718096; padding: 2rem;">
200
+ No tests run yet. Use the controls above to start testing RPC communication.
201
+ </div>
202
+ `;
203
+ return;
204
+ }
205
+
206
+ container.innerHTML = this.testResults.map(result => `
207
+ <div class="result-entry" style="background: #f8fafc; border-left: 3px solid #4299e1; padding: 1rem; margin-bottom: 0.5rem; border-radius: 0 0.25rem 0.25rem 0;">
208
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem;">
209
+ <strong>${result.operation}</strong>
210
+ <span style="color: #718096; font-size: 0.875rem;">${result.duration}ms</span>
211
+ </div>
212
+ <div style="color: #2d3748;">${result.result}</div>
213
+ </div>
214
+ `).join('');
215
+ }
216
+
217
+ private formatBytes(bytes: number): string {
218
+ if (bytes === 0) return '0 B';
219
+ const k = 1024;
220
+ const sizes = ['B', 'KB', 'MB', 'GB'];
221
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
222
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
223
+ }
224
+
225
+ // Handle events from the backend
226
+ onRpcTestResult(data: { operation: string; result: any; duration: number }) {
227
+ this.addTestResult(data);
228
+ }
229
+ }
@@ -0,0 +1,132 @@
1
+ export class TrayDemo {
2
+ private trays: Array<{ id: number; title: string }> = [];
3
+ private rpc: any;
4
+
5
+ render() {
6
+ return `
7
+ <div class="demo-section">
8
+ <div class="demo-header">
9
+ <span class="demo-icon">🔔</span>
10
+ <div>
11
+ <h2 class="demo-title">System Tray</h2>
12
+ <p class="demo-description">Create and manage system tray icons with menus</p>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="demo-controls">
17
+ <h3>Create System Tray</h3>
18
+ <div class="control-group">
19
+ <label class="control-label">Title:</label>
20
+ <input type="text" id="tray-title" class="control-input" value="My Tray Item" style="width: 200px;">
21
+
22
+ <button class="btn btn-primary" id="create-tray">Create Tray</button>
23
+ <button class="btn btn-danger" id="remove-all-trays">Remove All Trays</button>
24
+ </div>
25
+
26
+ <div style="margin-top: 1rem; padding: 1rem; background: #f7fafc; border-radius: 0.5rem;">
27
+ <p style="color: #4a5568; font-size: 0.875rem;">
28
+ <strong>Note:</strong> System tray icons appear in your system's menu bar or notification area.
29
+ Click on the tray icon to see its menu. On macOS, look in the top-right menu bar.
30
+ </p>
31
+ </div>
32
+ </div>
33
+
34
+ <div class="demo-results">
35
+ <div class="results-header">Active Trays (<span id="tray-count">0</span>):</div>
36
+ <div id="tray-list" class="tray-list">
37
+ <div class="no-trays" style="text-align: center; color: #718096; padding: 2rem;">
38
+ No system tray items created yet.
39
+ </div>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ `;
44
+ }
45
+
46
+ initialize(rpc: any) {
47
+ this.rpc = rpc;
48
+
49
+ const createTrayBtn = document.getElementById('create-tray');
50
+ const removeAllTraysBtn = document.getElementById('remove-all-trays');
51
+
52
+ createTrayBtn?.addEventListener('click', async () => {
53
+ const title = (document.getElementById('tray-title') as HTMLInputElement).value;
54
+
55
+ try {
56
+ const result = await rpc.request.createTray({ title });
57
+ this.trays.push({ id: result.id, title });
58
+ this.updateTrayList();
59
+ console.log('Created tray:', result);
60
+ } catch (error) {
61
+ console.error('Error creating tray:', error);
62
+ }
63
+ });
64
+
65
+ removeAllTraysBtn?.addEventListener('click', async () => {
66
+ for (const tray of this.trays) {
67
+ try {
68
+ await rpc.request.removeTray(tray.id);
69
+ console.log('Removed tray:', tray.id);
70
+ } catch (error) {
71
+ console.error(`Error removing tray ${tray.id}:`, error);
72
+ }
73
+ }
74
+ this.trays = [];
75
+ this.updateTrayList();
76
+ });
77
+ }
78
+
79
+ private updateTrayList() {
80
+ const container = document.getElementById('tray-list');
81
+ const count = document.getElementById('tray-count');
82
+
83
+ if (!container || !count) return;
84
+
85
+ count.textContent = this.trays.length.toString();
86
+
87
+ if (this.trays.length === 0) {
88
+ container.innerHTML = `
89
+ <div class="no-trays" style="text-align: center; color: #718096; padding: 2rem;">
90
+ No system tray items created yet.
91
+ </div>
92
+ `;
93
+ return;
94
+ }
95
+
96
+ container.innerHTML = this.trays.map(tray => `
97
+ <div class="tray-item" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem; padding: 1rem; margin-bottom: 0.5rem;">
98
+ <div style="display: flex; justify-content: space-between; align-items: center;">
99
+ <div>
100
+ <strong>Tray ${tray.id}</strong>
101
+ <div style="color: #718096; font-size: 0.875rem;">${tray.title}</div>
102
+ <div style="color: #4299e1; font-size: 0.75rem; margin-top: 0.25rem;">
103
+ Click tray icon in menu bar to see menu
104
+ </div>
105
+ </div>
106
+ <button class="btn btn-small btn-danger remove-tray" data-tray-id="${tray.id}">Remove</button>
107
+ </div>
108
+ </div>
109
+ `).join('');
110
+
111
+ // Add remove button listeners
112
+ const removeButtons = document.querySelectorAll('.remove-tray');
113
+ removeButtons.forEach(btn => {
114
+ btn.addEventListener('click', async (e) => {
115
+ const id = parseInt((e.target as HTMLElement).getAttribute('data-tray-id') || '0');
116
+ try {
117
+ await this.rpc.request.removeTray(id);
118
+ console.log('Removed tray:', id);
119
+ this.trays = this.trays.filter(t => t.id !== id);
120
+ this.updateTrayList();
121
+ } catch (error) {
122
+ console.error(`Error removing tray ${id}:`, error);
123
+ }
124
+ });
125
+ });
126
+ }
127
+
128
+ // Handle events from the backend
129
+ onTrayClicked(data: { id: number; action: string }) {
130
+ console.log('Tray clicked:', data);
131
+ }
132
+ }