electrobun 0.5.0-beta.0 → 0.6.0-beta.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.
Files changed (78) hide show
  1. package/{templates/multitab-browser/bun.lock → bun.lock} +20 -13
  2. package/dist/api/bun/proc/native.ts +84 -16
  3. package/package.json +14 -16
  4. package/BETA_RELEASE.md +0 -67
  5. package/BUILD.md +0 -90
  6. package/LICENSE +0 -21
  7. package/README.md +0 -102
  8. package/debug.js +0 -5
  9. package/templates/hello-world/README.md +0 -57
  10. package/templates/hello-world/bun.lock +0 -225
  11. package/templates/hello-world/electrobun.config.ts +0 -28
  12. package/templates/hello-world/package.json +0 -16
  13. package/templates/hello-world/src/bun/index.ts +0 -15
  14. package/templates/hello-world/src/mainview/index.css +0 -124
  15. package/templates/hello-world/src/mainview/index.html +0 -46
  16. package/templates/hello-world/src/mainview/index.ts +0 -1
  17. package/templates/interactive-playground/README.md +0 -26
  18. package/templates/interactive-playground/assets/tray-icon.png +0 -0
  19. package/templates/interactive-playground/electrobun.config.ts +0 -36
  20. package/templates/interactive-playground/package-lock.json +0 -1112
  21. package/templates/interactive-playground/package.json +0 -15
  22. package/templates/interactive-playground/src/bun/demos/files.ts +0 -70
  23. package/templates/interactive-playground/src/bun/demos/menus.ts +0 -139
  24. package/templates/interactive-playground/src/bun/demos/rpc.ts +0 -83
  25. package/templates/interactive-playground/src/bun/demos/system.ts +0 -72
  26. package/templates/interactive-playground/src/bun/demos/updates.ts +0 -105
  27. package/templates/interactive-playground/src/bun/demos/windows.ts +0 -90
  28. package/templates/interactive-playground/src/bun/index.ts +0 -124
  29. package/templates/interactive-playground/src/bun/types/rpc.ts +0 -109
  30. package/templates/interactive-playground/src/mainview/components/EventLog.ts +0 -107
  31. package/templates/interactive-playground/src/mainview/components/Sidebar.ts +0 -65
  32. package/templates/interactive-playground/src/mainview/components/Toast.ts +0 -57
  33. package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +0 -211
  34. package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +0 -102
  35. package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +0 -229
  36. package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +0 -132
  37. package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +0 -465
  38. package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +0 -207
  39. package/templates/interactive-playground/src/mainview/index.css +0 -538
  40. package/templates/interactive-playground/src/mainview/index.html +0 -103
  41. package/templates/interactive-playground/src/mainview/index.ts +0 -238
  42. package/templates/multitab-browser/README.md +0 -34
  43. package/templates/multitab-browser/electrobun.config.ts +0 -32
  44. package/templates/multitab-browser/package-lock.json +0 -20
  45. package/templates/multitab-browser/package.json +0 -12
  46. package/templates/multitab-browser/src/bun/index.ts +0 -144
  47. package/templates/multitab-browser/src/bun/tabManager.ts +0 -200
  48. package/templates/multitab-browser/src/bun/types/rpc.ts +0 -78
  49. package/templates/multitab-browser/src/mainview/index.css +0 -487
  50. package/templates/multitab-browser/src/mainview/index.html +0 -94
  51. package/templates/multitab-browser/src/mainview/index.ts +0 -634
  52. package/templates/photo-booth/README.md +0 -108
  53. package/templates/photo-booth/bun.lock +0 -239
  54. package/templates/photo-booth/electrobun.config.ts +0 -32
  55. package/templates/photo-booth/package.json +0 -17
  56. package/templates/photo-booth/src/bun/index.ts +0 -92
  57. package/templates/photo-booth/src/mainview/index.css +0 -465
  58. package/templates/photo-booth/src/mainview/index.html +0 -124
  59. package/templates/photo-booth/src/mainview/index.ts +0 -499
  60. package/test-new-window-events.ts +0 -26
  61. package/test-new-window.html +0 -75
  62. package/test-npm-install.sh +0 -34
  63. package/tests/bun.lock +0 -14
  64. package/tests/electrobun.config.ts +0 -45
  65. package/tests/package-lock.json +0 -36
  66. package/tests/package.json +0 -13
  67. package/tests/src/bun/index.ts +0 -100
  68. package/tests/src/bun/test-runner.ts +0 -508
  69. package/tests/src/mainview/index.html +0 -110
  70. package/tests/src/mainview/index.ts +0 -458
  71. package/tests/src/mainview/styles/main.css +0 -451
  72. package/tests/src/testviews/tray-test.html +0 -57
  73. package/tests/src/testviews/webview-mask.html +0 -114
  74. package/tests/src/testviews/webview-navigation.html +0 -36
  75. package/tests/src/testviews/window-create.html +0 -17
  76. package/tests/src/testviews/window-events.html +0 -29
  77. package/tests/src/testviews/window-focus.html +0 -37
  78. package/tests/src/webviewtag/index.ts +0 -11
@@ -1,465 +0,0 @@
1
- export class WebViewDemo {
2
- private rpc: any;
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">WebView Browser</h2>
11
- <p class="demo-description">Embedded browser with navigation, transparency, masking, and advanced features</p>
12
- </div>
13
- </div>
14
-
15
- <div class="demo-controls" style="padding: 1rem;">
16
- <!-- Browser-like navigation bar -->
17
- <div style="display: flex; gap: 0.5rem; align-items: center; padding: 0.5rem; background: #f7fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem; margin-bottom: 1rem;">
18
- <button class="btn btn-sm" id="webview-back" style="padding: 0.25rem 0.5rem;">←</button>
19
- <button class="btn btn-sm" id="webview-forward" style="padding: 0.25rem 0.5rem;">→</button>
20
- <button class="btn btn-sm" id="webview-reload" style="padding: 0.25rem 0.5rem;">🔄</button>
21
- <input type="text" id="url-bar" class="control-input" style="flex: 1; padding: 0.25rem 0.5rem; font-size: 0.875rem;" placeholder="Enter URL or click quick nav buttons" />
22
- <button class="btn btn-primary btn-sm" id="url-go" style="padding: 0.25rem 0.75rem;">Go</button>
23
- </div>
24
-
25
- <!-- Quick navigation buttons -->
26
- <div style="display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap;">
27
- <button class="btn btn-sm" id="nav-eggbun">eggbun.sh</button>
28
- <button class="btn btn-sm" id="nav-electrobun">electrobun.dev</button>
29
- <button class="btn btn-sm" id="nav-github">GitHub</button>
30
- <button class="btn btn-sm" id="nav-wikipedia">Wikipedia Random</button>
31
- </div>
32
-
33
- <!-- WebView control buttons -->
34
- <div style="display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap;">
35
- <button class="btn btn-secondary btn-sm" id="toggle-transparent">Toggle Transparent</button>
36
- <button class="btn btn-secondary btn-sm" id="toggle-passthrough">Toggle Passthrough</button>
37
- <button class="btn btn-secondary btn-sm" id="toggle-hidden">Toggle Hidden</button>
38
- <button class="btn btn-secondary btn-sm" id="add-mask">Add Mask</button>
39
- <button class="btn btn-secondary btn-sm" id="remove-mask">Remove Mask</button>
40
- </div>
41
-
42
- <!-- Draggable element for testing -->
43
- <div style="display: flex; gap: 0.5rem; margin-bottom: 1rem; flex-wrap: wrap;">
44
- <div id="draggable-test" draggable="true" style="padding: 10px; width: 120px; height: 80px; border: 3px dashed #4a5568; cursor: move; background: white; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 0.875rem; text-align: center;">
45
- <div>Drag me over the webview</div>
46
- <div id="drag-coords" style="font-size: 0.75rem; color: #4a5568; margin-top: 0.25rem;">x: 0, y: 0</div>
47
- </div>
48
- </div>
49
-
50
- </div>
51
-
52
- <div class="demo-results" style="position: relative;">
53
- <!-- Main webview container with mask overlay elements -->
54
- <div style="position: relative; width: 100%; height: 500px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem;">
55
-
56
- <!-- Overlay squares for mask testing -->
57
- <div class="element-to-mask" style="position: absolute; z-index: 10; top: -30px; right: 50px; width: 120px; height: 110px; background: black; color: white; padding: 10px; display: flex; align-items: center; justify-content: center; font-weight: bold; border: 10px solid firebrick">
58
- Mask Layer 1
59
- </div>
60
-
61
- <div class="element-to-mask" style="position: absolute; z-index: 10; top: -20px; right: 160px; width: 120px; height: 130px; background: green; color: white; padding: 10px; display: flex; align-items: center; justify-content: center; font-weight: bold; border: 5px solid cadetblue;">
62
- Mask Layer 2
63
- </div>
64
-
65
- <!-- Main webview -->
66
- <electrobun-webview
67
- id="main-webview"
68
- style="width: 100%; height: 100%;"
69
- src="https://electrobun.dev"
70
- preload=""
71
- renderer="cef">
72
- </electrobun-webview>
73
- </div>
74
-
75
-
76
-
77
- <!-- Compact event log -->
78
- <div style="margin-top: 1rem;">
79
- <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem; padding: 0.5rem;">
80
- <div style="font-weight: 500; padding: 0.25rem;">WebView Events Log</div>
81
- <div id="webview-events" style="max-height: 150px; overflow-y: auto; margin-top: 0.5rem;">
82
- <div class="no-events" style="text-align: center; color: #718096; font-size: 0.875rem;">
83
- Events will appear here
84
- </div>
85
- </div>
86
- </div>
87
- </div>
88
-
89
- <!-- Additional webviews for testing partitions -->
90
- <div style="margin-top: 1rem;">
91
- <div style="font-weight: 500; padding: 0.5rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem;">Session Partition Testing (Wikipedia)</div>
92
-
93
- <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-top: 0.5rem;">
94
- <div style="border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
95
- <div style="background: #f7fafc; padding: 0.25rem 0.5rem; font-size: 0.75rem;">
96
- Shared (Default)
97
- </div>
98
- <electrobun-webview
99
- id="partition-default"
100
- style="width: 100%; height: 200px;"
101
- src="https://en.wikipedia.org/wiki/Main_Page"
102
- renderer="cef"
103
- partition="">
104
- </electrobun-webview>
105
- </div>
106
-
107
- <div style="border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
108
- <div style="background: #f7fafc; padding: 0.25rem 0.5rem; font-size: 0.75rem;">
109
- Shared (Also Default)
110
- </div>
111
- <electrobun-webview
112
- id="partition-default2"
113
- style="width: 100%; height: 200px;"
114
- src="https://en.wikipedia.org/wiki/Main_Page"
115
- renderer="cef"
116
- partition="">
117
- </electrobun-webview>
118
- </div>
119
-
120
- <div style="border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
121
- <div style="background: #eff6ff; padding: 0.25rem 0.5rem; font-size: 0.75rem;">
122
- Isolated (persist:user1)
123
- </div>
124
- <electrobun-webview
125
- id="partition-user1"
126
- style="width: 100%; height: 200px;"
127
- src="https://en.wikipedia.org/wiki/Main_Page"
128
- renderer="cef"
129
- partition="persist:user1">
130
- </electrobun-webview>
131
- </div>
132
-
133
- <div style="border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
134
- <div style="background: #f0fdf4; padding: 0.25rem 0.5rem; font-size: 0.75rem;">
135
- Isolated (persist:user2)
136
- </div>
137
- <electrobun-webview
138
- id="partition-user2"
139
- style="width: 100%; height: 200px;"
140
- src="https://en.wikipedia.org/wiki/Main_Page"
141
- renderer="cef"
142
- partition="persist:user2">
143
- </electrobun-webview>
144
- </div>
145
- </div>
146
-
147
- <div style="margin-top: 0.5rem; padding: 0.5rem; background: #ebf8ff; border: 1px solid #90cdf4; border-radius: 0.25rem; font-size: 0.75rem; color: #2b6cb0;">
148
- <strong>How to test partitions:</strong> Click on any Wikipedia link in one webview. The two "Shared" webviews will both show visited links in purple, while the "Isolated" webviews maintain separate browsing history. Try logging in or changing settings - shared partitions share everything, isolated ones are independent.
149
- </div>
150
- </div>
151
-
152
- <!-- HTML content test -->
153
- <div style="margin-top: 1rem;">
154
- <div style="font-weight: 500; padding: 0.5rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem;">Inline HTML WebView</div>
155
- <div style="margin-top: 0.5rem; border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
156
- <electrobun-webview
157
- style="width: 100%; height: 150px;"
158
- preload="window.onload = () => {document.body.innerHTML += '<br>Hello from preload script!';}"
159
- html="<html><body style='padding: 20px; font-family: system-ui;'><h2>WebView with inline HTML</h2><p>This webview is rendered from HTML string instead of URL.</p></body></html>">
160
- </electrobun-webview>
161
- </div>
162
- </div>
163
-
164
- <!-- CMD+Click Test WebView -->
165
- <div style="margin-top: 1rem;">
166
- <div style="font-weight: 500; padding: 0.5rem; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 0.5rem;">CMD+Click New Window Test</div>
167
- <div style="margin-top: 0.5rem; border: 1px solid #e2e8f0; border-radius: 0.25rem; overflow: hidden;">
168
- <electrobun-webview
169
- id="cmd-click-test"
170
- style="width: 100%; height: 200px;"
171
- html="<html><head><style>body{font-family:system-ui;padding:20px;background:#f9f9f9;}a{display:block;margin:10px 0;padding:10px;background:white;border:1px solid #ddd;text-decoration:none;border-radius:5px;transition:background 0.2s;}a:hover{background:#e6f3ff;}</style></head><body><h3>Test CMD+Click to Open New Windows</h3><p>Hold <strong>CMD</strong> (macOS) and click these links to test new window events:</p><a href='https://electrobun.dev' target='_self'>Regular Link (same window)</a><a href='https://github.com/blackboardsh/electrobun' target='_blank'>Target _blank Link</a><a href='https://bun.sh'>CMD+Click Me!</a><a href='javascript:window.open(\"https://anthropic.com\", \"_blank\")'>JavaScript window.open()</a><p style='color:#666;font-size:14px;'>Watch the events log above to see how different link types are handled.</p></body></html>">
172
- </electrobun-webview>
173
- </div>
174
- </div>
175
- </div>
176
- </div>
177
- `;
178
- }
179
-
180
- initialize(rpc: any) {
181
- this.rpc = rpc;
182
- this.setupEventListeners();
183
- this.setupWebViewEvents();
184
- }
185
-
186
- private setupEventListeners() {
187
- const webview = document.getElementById('main-webview') as any;
188
- const urlBar = document.getElementById('url-bar') as HTMLInputElement;
189
-
190
- // Safety check: If webview doesn't exist, don't set up listeners
191
- if (!webview) {
192
- console.warn('WebView element not found during event listener setup');
193
- return;
194
- }
195
-
196
- // Navigation controls
197
- document.getElementById('webview-back')?.addEventListener('click', () => {
198
- if (webview && typeof webview.goBack === 'function') {
199
- webview.goBack();
200
- this.addWebViewEvent('← Back');
201
- }
202
- });
203
-
204
- document.getElementById('webview-forward')?.addEventListener('click', () => {
205
- if (webview && typeof webview.goForward === 'function') {
206
- webview.goForward();
207
- this.addWebViewEvent('→ Forward');
208
- }
209
- });
210
-
211
- document.getElementById('webview-reload')?.addEventListener('click', () => {
212
- if (webview && typeof webview.reload === 'function') {
213
- webview.reload();
214
- this.addWebViewEvent('🔄 Reload');
215
- }
216
- });
217
-
218
- // URL bar navigation
219
- const navigateToUrl = () => {
220
- const url = urlBar?.value?.trim();
221
- if (url && webview && typeof webview.setAttribute === 'function') {
222
- // Add protocol if missing
223
- const finalUrl = url.startsWith('http://') || url.startsWith('https://')
224
- ? url
225
- : `https://${url}`;
226
- webview.setAttribute('src', finalUrl);
227
- this.addWebViewEvent(`Navigate: ${finalUrl}`);
228
- }
229
- };
230
-
231
- document.getElementById('url-go')?.addEventListener('click', navigateToUrl);
232
- urlBar?.addEventListener('keypress', (e) => {
233
- if (e.key === 'Enter') {
234
- navigateToUrl();
235
- }
236
- });
237
-
238
- // Quick navigation
239
- document.getElementById('nav-eggbun')?.addEventListener('click', () => {
240
- if (webview && typeof webview.setAttribute === 'function' && urlBar) {
241
- webview.setAttribute('src', 'https://eggbun.sh');
242
- urlBar.value = 'https://eggbun.sh';
243
- this.addWebViewEvent('Nav: eggbun.sh');
244
- }
245
- });
246
-
247
- document.getElementById('nav-electrobun')?.addEventListener('click', () => {
248
- if (webview && typeof webview.setAttribute === 'function' && urlBar) {
249
- webview.setAttribute('src', 'https://electrobun.dev');
250
- urlBar.value = 'https://electrobun.dev';
251
- this.addWebViewEvent('Nav: electrobun.dev');
252
- }
253
- });
254
-
255
- document.getElementById('nav-github')?.addEventListener('click', () => {
256
- if (webview && typeof webview.setAttribute === 'function' && urlBar) {
257
- webview.setAttribute('src', 'https://github.com/blackboardsh/electrobun');
258
- urlBar.value = 'https://github.com/blackboardsh/electrobun';
259
- this.addWebViewEvent('Nav: GitHub');
260
- }
261
- });
262
-
263
- document.getElementById('nav-wikipedia')?.addEventListener('click', () => {
264
- if (webview && typeof webview.setAttribute === 'function' && urlBar) {
265
- webview.setAttribute('src', 'https://en.wikipedia.org/wiki/Special:Random');
266
- urlBar.value = 'https://en.wikipedia.org/wiki/Special:Random';
267
- this.addWebViewEvent('Nav: Wikipedia Random');
268
- }
269
- });
270
-
271
- // WebView effects
272
- let isTransparent = false;
273
- let isPassthrough = false;
274
- let isHidden = false;
275
-
276
- document.getElementById('toggle-transparent')?.addEventListener('click', () => {
277
- if (webview && typeof webview.toggleTransparent === 'function') {
278
- webview.toggleTransparent();
279
- isTransparent = !isTransparent;
280
- this.addWebViewEvent(`Transparent: ${isTransparent ? 'ON' : 'OFF'}`);
281
- }
282
- });
283
-
284
- document.getElementById('toggle-passthrough')?.addEventListener('click', () => {
285
- if (webview && typeof webview.togglePassthrough === 'function') {
286
- webview.togglePassthrough();
287
- isPassthrough = !isPassthrough;
288
- this.addWebViewEvent(`Passthrough: ${isPassthrough ? 'ON' : 'OFF'}`);
289
- }
290
- });
291
-
292
- document.getElementById('toggle-hidden')?.addEventListener('click', () => {
293
- if (webview && typeof webview.toggleHidden === 'function') {
294
- webview.toggleHidden();
295
- isHidden = !isHidden;
296
- this.addWebViewEvent(`Hidden: ${isHidden ? 'ON' : 'OFF'}`);
297
- }
298
- });
299
-
300
- // Element masking
301
- let maskActive = false;
302
- document.getElementById('add-mask')?.addEventListener('click', () => {
303
- if (webview && typeof webview.addMaskSelector === 'function') {
304
- webview.addMaskSelector('.element-to-mask');
305
- maskActive = true;
306
- this.addWebViewEvent('Mask added: .element-to-mask');
307
- }
308
- });
309
-
310
- document.getElementById('remove-mask')?.addEventListener('click', () => {
311
- if (webview && typeof webview.removeMaskSelector === 'function') {
312
- webview.removeMaskSelector('.element-to-mask');
313
- maskActive = false;
314
- this.addWebViewEvent('Mask removed: .element-to-mask');
315
- }
316
- });
317
-
318
- // Draggable element position tracking
319
- const draggable = document.getElementById('draggable-test');
320
- const dragCoords = document.getElementById('drag-coords');
321
-
322
- if (draggable && dragCoords) {
323
- draggable.addEventListener('dragstart', (e: DragEvent) => {
324
- // Store initial offset
325
- const rect = draggable.getBoundingClientRect();
326
- (e.dataTransfer as any).effectAllowed = 'move';
327
- });
328
-
329
- // Update coordinates during drag
330
- document.addEventListener('dragover', (e: DragEvent) => {
331
- e.preventDefault();
332
- if (dragCoords) {
333
- dragCoords.textContent = `x: ${e.clientX}, y: ${e.clientY}`;
334
- }
335
- });
336
-
337
- draggable.addEventListener('dragend', (e: DragEvent) => {
338
- if (dragCoords) {
339
- dragCoords.textContent = `x: ${e.clientX}, y: ${e.clientY}`;
340
- }
341
- });
342
- }
343
-
344
- // Add mask selectors to all webviews (with safety checks)
345
- document.querySelectorAll('electrobun-webview').forEach((w: any) => {
346
- if (w && typeof w.addMaskSelector === 'function') {
347
- w.addMaskSelector("header");
348
- }
349
- });
350
-
351
- }
352
-
353
- private setupWebViewEvents() {
354
- const webview = document.getElementById('main-webview') as any;
355
- const cmdClickTestWebview = document.getElementById('cmd-click-test') as any;
356
- const urlBar = document.getElementById('url-bar') as HTMLInputElement;
357
-
358
- // Setup events for main webview
359
- if (webview && typeof webview.on === 'function') {
360
- // Use the on() method as shown in the old playground
361
- webview.on('did-navigate', (e: any) => {
362
- const url = e.detail?.url || 'unknown';
363
- if (urlBar) {
364
- urlBar.value = url;
365
- }
366
- this.addWebViewEvent(`did-navigate: ${url}`);
367
- });
368
-
369
- webview.on('did-navigate-in-page', (e: any) => {
370
- this.addWebViewEvent(`in-page-nav: ${e.detail?.url || 'unknown'}`);
371
- });
372
-
373
- webview.on('did-commit-navigation', (e: any) => {
374
- this.addWebViewEvent(`commit-nav: ${e.detail?.url || 'unknown'}`);
375
- });
376
-
377
- webview.on('dom-ready', () => {
378
- this.addWebViewEvent(`DOM ready`);
379
- });
380
-
381
- webview.on('new-window-open', (e: any) => {
382
- const detail = e.detail;
383
-
384
- // Handle both legacy string format and new JSON format
385
- if (typeof detail === 'string') {
386
- this.addWebViewEvent(`new-window: ${detail}`);
387
- } else if (detail && typeof detail === 'object') {
388
- const { url, isCmdClick, modifierFlags, userGesture } = detail;
389
- const eventDesc = isCmdClick ? 'cmd+click' : 'popup';
390
- this.addWebViewEvent(`new-window (${eventDesc}): ${url || 'unknown'}`);
391
-
392
- // Log additional details for debugging
393
- console.log('New window event details:', { url, isCmdClick, modifierFlags, userGesture });
394
- } else {
395
- this.addWebViewEvent(`new-window: ${JSON.stringify(detail)}`);
396
- }
397
- });
398
-
399
- // Also try addEventListener for compatibility
400
- if (typeof webview.addEventListener === 'function') {
401
- webview.addEventListener('did-navigate', (e: any) => {
402
- const url = e.detail?.url || 'unknown';
403
- if (urlBar) {
404
- urlBar.value = url;
405
- }
406
- this.addWebViewEvent(`did-navigate: ${url}`);
407
- });
408
- }
409
- }
410
-
411
- // Setup events for cmd+click test webview
412
- if (cmdClickTestWebview && typeof cmdClickTestWebview.on === 'function') {
413
- cmdClickTestWebview.on('new-window-open', (e: any) => {
414
- const detail = e.detail;
415
-
416
- // Handle both legacy string format and new JSON format
417
- if (typeof detail === 'string') {
418
- this.addWebViewEvent(`[TEST] new-window: ${detail}`);
419
- } else if (detail && typeof detail === 'object') {
420
- const { url, isCmdClick, modifierFlags, userGesture } = detail;
421
- const eventDesc = isCmdClick ? 'cmd+click' : 'popup';
422
- this.addWebViewEvent(`[TEST] new-window (${eventDesc}): ${url || 'unknown'}`);
423
-
424
- // Log additional details for debugging
425
- console.log('CMD+Click test webview event:', { url, isCmdClick, modifierFlags, userGesture });
426
- } else {
427
- this.addWebViewEvent(`[TEST] new-window: ${JSON.stringify(detail)}`);
428
- }
429
- });
430
-
431
- cmdClickTestWebview.on('did-navigate', (e: any) => {
432
- const url = e.detail?.url || 'unknown';
433
- this.addWebViewEvent(`[TEST] did-navigate: ${url}`);
434
- });
435
- }
436
- }
437
-
438
- private addWebViewEvent(event: string) {
439
- const container = document.getElementById('webview-events');
440
- if (!container) return;
441
-
442
- const time = new Date().toLocaleTimeString();
443
- const eventHtml = `
444
- <div style="border-left: 2px solid #0ea5e9; padding: 0.25rem 0.5rem; margin-bottom: 0.25rem; font-size: 0.75rem;">
445
- <span style="color: #64748b;">${time}</span>
446
- <span style="color: #0369a1; margin-left: 0.5rem;">${event}</span>
447
- </div>
448
- `;
449
-
450
- // Remove "no events" message if it exists
451
- const noEvents = container.querySelector('.no-events');
452
- if (noEvents) {
453
- container.innerHTML = '';
454
- }
455
-
456
- // Add new event at the top
457
- container.insertAdjacentHTML('afterbegin', eventHtml);
458
-
459
- // Keep only last 10 events
460
- const events = container.children;
461
- while (events.length > 10) {
462
- container.removeChild(events[events.length - 1]);
463
- }
464
- }
465
- }
@@ -1,207 +0,0 @@
1
- export class WindowDemo {
2
- private windows: 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">Window Management</h2>
12
- <p class="demo-description">Create and manage multiple application windows with different properties</p>
13
- </div>
14
- </div>
15
-
16
- <div class="demo-controls">
17
- <div class="control-group">
18
- <label class="control-label">Width:</label>
19
- <input type="number" id="window-width" class="control-input" value="800" min="200" max="2000">
20
-
21
- <label class="control-label">Height:</label>
22
- <input type="number" id="window-height" class="control-input" value="600" min="200" max="1200">
23
-
24
- <label class="control-label">X:</label>
25
- <input type="number" id="window-x" class="control-input" value="100" min="0" max="2000">
26
-
27
- <label class="control-label">Y:</label>
28
- <input type="number" id="window-y" class="control-input" value="100" min="0" max="1200">
29
- </div>
30
-
31
- <div class="control-group">
32
- <label class="control-checkbox">
33
- <input type="checkbox" id="window-frameless"> Frameless
34
- </label>
35
- <label class="control-checkbox" title="Transparency is for webviews, not windows">
36
- <input type="checkbox" id="window-transparent" disabled> Transparent (WebView only)
37
- </label>
38
- <label class="control-checkbox" title="Not yet implemented">
39
- <input type="checkbox" id="window-always-on-top" disabled> Always on Top (Coming soon)
40
- </label>
41
- </div>
42
-
43
- <div class="control-group">
44
- <button class="btn btn-primary" id="create-window">Create Window</button>
45
- <button class="btn btn-secondary" id="get-window-list">Refresh List</button>
46
- <button class="btn btn-danger" id="close-all-windows">Close All</button>
47
- </div>
48
- </div>
49
-
50
- <div class="demo-results">
51
- <div class="results-header">Active Windows (<span id="window-count">0</span>):</div>
52
- <div class="window-grid" id="window-grid">
53
- <div class="no-windows" style="grid-column: 1 / -1; text-align: center; color: #718096; padding: 2rem;">
54
- No windows created yet. Use the controls above to create your first window.
55
- </div>
56
- </div>
57
- </div>
58
- </div>
59
- `;
60
- }
61
-
62
- initialize(rpc: any) {
63
- this.rpc = rpc; // Store RPC reference
64
-
65
- // Get DOM elements
66
- const createBtn = document.getElementById('create-window');
67
- const refreshBtn = document.getElementById('get-window-list');
68
- const closeAllBtn = document.getElementById('close-all-windows');
69
-
70
- // Event listeners
71
- createBtn?.addEventListener('click', async () => {
72
- const width = parseInt((document.getElementById('window-width') as HTMLInputElement).value);
73
- const height = parseInt((document.getElementById('window-height') as HTMLInputElement).value);
74
- const x = parseInt((document.getElementById('window-x') as HTMLInputElement).value);
75
- const y = parseInt((document.getElementById('window-y') as HTMLInputElement).value);
76
- const frameless = (document.getElementById('window-frameless') as HTMLInputElement).checked;
77
- // Transparent and alwaysOnTop are disabled for now
78
- // const transparent = (document.getElementById('window-transparent') as HTMLInputElement).checked;
79
- // const alwaysOnTop = (document.getElementById('window-always-on-top') as HTMLInputElement).checked;
80
-
81
- try {
82
- const result = await rpc.request.createWindow({
83
- width, height, x, y, frameless
84
- });
85
- console.log('Window created:', result);
86
- } catch (error) {
87
- console.error('Error creating window:', error);
88
- }
89
- });
90
-
91
- refreshBtn?.addEventListener('click', async () => {
92
- try {
93
- const windows = await rpc.request.getWindowList();
94
- this.updateWindowList(windows);
95
- } catch (error) {
96
- console.error('Error getting window list:', error);
97
- }
98
- });
99
-
100
- closeAllBtn?.addEventListener('click', async () => {
101
- for (const window of this.windows) {
102
- try {
103
- await rpc.request.closeWindow(window.id);
104
- } catch (error) {
105
- console.error(`Error closing window ${window.id}:`, error);
106
- }
107
- }
108
- });
109
-
110
- // Load initial window list
111
- this.loadWindowList(rpc);
112
- }
113
-
114
- private async loadWindowList(rpc: any) {
115
- try {
116
- const windows = await rpc.request.getWindowList();
117
- this.updateWindowList(windows);
118
- } catch (error) {
119
- console.error('Error loading window list:', error);
120
- }
121
- }
122
-
123
- updateWindowList(windows: Array<{ id: number; title: string }>) {
124
- this.windows = windows;
125
- const grid = document.getElementById('window-grid');
126
- const count = document.getElementById('window-count');
127
-
128
- if (!grid || !count) return;
129
-
130
- count.textContent = windows.length.toString();
131
-
132
- if (windows.length === 0) {
133
- grid.innerHTML = `
134
- <div class="no-windows" style="grid-column: 1 / -1; text-align: center; color: #718096; padding: 2rem;">
135
- No windows created yet. Use the controls above to create your first window.
136
- </div>
137
- `;
138
- return;
139
- }
140
-
141
- grid.innerHTML = windows.map(window => `
142
- <div class="window-card" data-window-id="${window.id}">
143
- <div class="window-preview">Window Preview</div>
144
- <div class="window-title">${window.title}</div>
145
- <div class="window-actions">
146
- <button class="btn btn-small btn-secondary focus-window" data-window-id="${window.id}">Focus</button>
147
- <button class="btn btn-small btn-danger close-window" data-window-id="${window.id}">×</button>
148
- </div>
149
- </div>
150
- `).join('');
151
-
152
- // Add event listeners for window actions
153
- this.attachWindowActionListeners();
154
- }
155
-
156
- private attachWindowActionListeners() {
157
- const focusButtons = document.querySelectorAll('.focus-window');
158
- const closeButtons = document.querySelectorAll('.close-window');
159
-
160
- focusButtons.forEach(btn => {
161
- btn.addEventListener('click', async (e) => {
162
- const id = parseInt((e.target as HTMLElement).getAttribute('data-window-id') || '0');
163
- try {
164
- await this.rpc.request.focusWindow(id);
165
- console.log(`Focused window ${id}`);
166
- } catch (error) {
167
- console.error(`Error focusing window ${id}:`, error);
168
- }
169
- });
170
- });
171
-
172
- closeButtons.forEach(btn => {
173
- btn.addEventListener('click', async (e) => {
174
- const id = parseInt((e.target as HTMLElement).getAttribute('data-window-id') || '0');
175
- try {
176
- await this.rpc.request.closeWindow(id);
177
- console.log(`Closed window ${id}`);
178
- // Remove from local list
179
- this.windows = this.windows.filter(w => w.id !== id);
180
- this.updateWindowList(this.windows);
181
- } catch (error) {
182
- console.error(`Error closing window ${id}:`, error);
183
- }
184
- });
185
- });
186
- }
187
-
188
- // Handle events from the backend
189
- onWindowCreated(data: { id: number; title: string }) {
190
- this.windows.push(data);
191
- this.updateWindowList(this.windows);
192
- }
193
-
194
- onWindowClosed(data: { id: number }) {
195
- this.windows = this.windows.filter(w => w.id !== data.id);
196
- this.updateWindowList(this.windows);
197
- }
198
-
199
- onWindowFocused(data: { id: number }) {
200
- // Visual feedback for focused window
201
- const windowCard = document.querySelector(`[data-window-id="${data.id}"]`);
202
- if (windowCard) {
203
- windowCard.classList.add('focused');
204
- setTimeout(() => windowCard.classList.remove('focused'), 1000);
205
- }
206
- }
207
- }