electrobun 0.4.1 → 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.
- package/{templates/multitab-browser/bun.lock → bun.lock} +20 -13
- package/dist/api/bun/proc/native.ts +84 -16
- package/package.json +14 -16
- package/BETA_RELEASE.md +0 -67
- package/BUILD.md +0 -90
- package/LICENSE +0 -21
- package/README.md +0 -102
- package/debug.js +0 -5
- package/templates/hello-world/README.md +0 -57
- package/templates/hello-world/bun.lock +0 -225
- package/templates/hello-world/electrobun.config.ts +0 -28
- package/templates/hello-world/package.json +0 -16
- package/templates/hello-world/src/bun/index.ts +0 -15
- package/templates/hello-world/src/mainview/index.css +0 -124
- package/templates/hello-world/src/mainview/index.html +0 -46
- package/templates/hello-world/src/mainview/index.ts +0 -1
- package/templates/interactive-playground/README.md +0 -26
- package/templates/interactive-playground/assets/tray-icon.png +0 -0
- package/templates/interactive-playground/electrobun.config.ts +0 -36
- package/templates/interactive-playground/package-lock.json +0 -1112
- package/templates/interactive-playground/package.json +0 -15
- package/templates/interactive-playground/src/bun/demos/files.ts +0 -70
- package/templates/interactive-playground/src/bun/demos/menus.ts +0 -139
- package/templates/interactive-playground/src/bun/demos/rpc.ts +0 -83
- package/templates/interactive-playground/src/bun/demos/system.ts +0 -72
- package/templates/interactive-playground/src/bun/demos/updates.ts +0 -105
- package/templates/interactive-playground/src/bun/demos/windows.ts +0 -90
- package/templates/interactive-playground/src/bun/index.ts +0 -124
- package/templates/interactive-playground/src/bun/types/rpc.ts +0 -109
- package/templates/interactive-playground/src/mainview/components/EventLog.ts +0 -107
- package/templates/interactive-playground/src/mainview/components/Sidebar.ts +0 -65
- package/templates/interactive-playground/src/mainview/components/Toast.ts +0 -57
- package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +0 -211
- package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +0 -102
- package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +0 -229
- package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +0 -132
- package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +0 -465
- package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +0 -207
- package/templates/interactive-playground/src/mainview/index.css +0 -538
- package/templates/interactive-playground/src/mainview/index.html +0 -103
- package/templates/interactive-playground/src/mainview/index.ts +0 -238
- package/templates/multitab-browser/README.md +0 -34
- package/templates/multitab-browser/electrobun.config.ts +0 -32
- package/templates/multitab-browser/package-lock.json +0 -20
- package/templates/multitab-browser/package.json +0 -12
- package/templates/multitab-browser/src/bun/index.ts +0 -144
- package/templates/multitab-browser/src/bun/tabManager.ts +0 -200
- package/templates/multitab-browser/src/bun/types/rpc.ts +0 -78
- package/templates/multitab-browser/src/mainview/index.css +0 -487
- package/templates/multitab-browser/src/mainview/index.html +0 -94
- package/templates/multitab-browser/src/mainview/index.ts +0 -634
- package/templates/photo-booth/README.md +0 -108
- package/templates/photo-booth/bun.lock +0 -239
- package/templates/photo-booth/electrobun.config.ts +0 -32
- package/templates/photo-booth/package.json +0 -17
- package/templates/photo-booth/src/bun/index.ts +0 -92
- package/templates/photo-booth/src/mainview/index.css +0 -465
- package/templates/photo-booth/src/mainview/index.html +0 -124
- package/templates/photo-booth/src/mainview/index.ts +0 -499
- package/test-new-window-events.ts +0 -26
- package/test-new-window.html +0 -75
- package/test-npm-install.sh +0 -34
- package/tests/bun.lock +0 -14
- package/tests/electrobun.config.ts +0 -45
- package/tests/package-lock.json +0 -36
- package/tests/package.json +0 -13
- package/tests/src/bun/index.ts +0 -100
- package/tests/src/bun/test-runner.ts +0 -508
- package/tests/src/mainview/index.html +0 -110
- package/tests/src/mainview/index.ts +0 -458
- package/tests/src/mainview/styles/main.css +0 -451
- package/tests/src/testviews/tray-test.html +0 -57
- package/tests/src/testviews/webview-mask.html +0 -114
- package/tests/src/testviews/webview-navigation.html +0 -36
- package/tests/src/testviews/window-create.html +0 -17
- package/tests/src/testviews/window-events.html +0 -29
- package/tests/src/testviews/window-focus.html +0 -37
- 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
|
-
}
|