electrobun 0.5.0-beta.0 → 0.7.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 (80) hide show
  1. package/{templates/multitab-browser/bun.lock → bun.lock} +20 -13
  2. package/dist/api/bun/core/BrowserView.ts +5 -1
  3. package/dist/api/bun/events/webviewEvents.ts +8 -0
  4. package/dist/api/bun/proc/native.ts +95 -21
  5. package/package.json +14 -16
  6. package/BETA_RELEASE.md +0 -67
  7. package/BUILD.md +0 -90
  8. package/LICENSE +0 -21
  9. package/README.md +0 -102
  10. package/debug.js +0 -5
  11. package/templates/hello-world/README.md +0 -57
  12. package/templates/hello-world/bun.lock +0 -225
  13. package/templates/hello-world/electrobun.config.ts +0 -28
  14. package/templates/hello-world/package.json +0 -16
  15. package/templates/hello-world/src/bun/index.ts +0 -15
  16. package/templates/hello-world/src/mainview/index.css +0 -124
  17. package/templates/hello-world/src/mainview/index.html +0 -46
  18. package/templates/hello-world/src/mainview/index.ts +0 -1
  19. package/templates/interactive-playground/README.md +0 -26
  20. package/templates/interactive-playground/assets/tray-icon.png +0 -0
  21. package/templates/interactive-playground/electrobun.config.ts +0 -36
  22. package/templates/interactive-playground/package-lock.json +0 -1112
  23. package/templates/interactive-playground/package.json +0 -15
  24. package/templates/interactive-playground/src/bun/demos/files.ts +0 -70
  25. package/templates/interactive-playground/src/bun/demos/menus.ts +0 -139
  26. package/templates/interactive-playground/src/bun/demos/rpc.ts +0 -83
  27. package/templates/interactive-playground/src/bun/demos/system.ts +0 -72
  28. package/templates/interactive-playground/src/bun/demos/updates.ts +0 -105
  29. package/templates/interactive-playground/src/bun/demos/windows.ts +0 -90
  30. package/templates/interactive-playground/src/bun/index.ts +0 -124
  31. package/templates/interactive-playground/src/bun/types/rpc.ts +0 -109
  32. package/templates/interactive-playground/src/mainview/components/EventLog.ts +0 -107
  33. package/templates/interactive-playground/src/mainview/components/Sidebar.ts +0 -65
  34. package/templates/interactive-playground/src/mainview/components/Toast.ts +0 -57
  35. package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +0 -211
  36. package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +0 -102
  37. package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +0 -229
  38. package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +0 -132
  39. package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +0 -465
  40. package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +0 -207
  41. package/templates/interactive-playground/src/mainview/index.css +0 -538
  42. package/templates/interactive-playground/src/mainview/index.html +0 -103
  43. package/templates/interactive-playground/src/mainview/index.ts +0 -238
  44. package/templates/multitab-browser/README.md +0 -34
  45. package/templates/multitab-browser/electrobun.config.ts +0 -32
  46. package/templates/multitab-browser/package-lock.json +0 -20
  47. package/templates/multitab-browser/package.json +0 -12
  48. package/templates/multitab-browser/src/bun/index.ts +0 -144
  49. package/templates/multitab-browser/src/bun/tabManager.ts +0 -200
  50. package/templates/multitab-browser/src/bun/types/rpc.ts +0 -78
  51. package/templates/multitab-browser/src/mainview/index.css +0 -487
  52. package/templates/multitab-browser/src/mainview/index.html +0 -94
  53. package/templates/multitab-browser/src/mainview/index.ts +0 -634
  54. package/templates/photo-booth/README.md +0 -108
  55. package/templates/photo-booth/bun.lock +0 -239
  56. package/templates/photo-booth/electrobun.config.ts +0 -32
  57. package/templates/photo-booth/package.json +0 -17
  58. package/templates/photo-booth/src/bun/index.ts +0 -92
  59. package/templates/photo-booth/src/mainview/index.css +0 -465
  60. package/templates/photo-booth/src/mainview/index.html +0 -124
  61. package/templates/photo-booth/src/mainview/index.ts +0 -499
  62. package/test-new-window-events.ts +0 -26
  63. package/test-new-window.html +0 -75
  64. package/test-npm-install.sh +0 -34
  65. package/tests/bun.lock +0 -14
  66. package/tests/electrobun.config.ts +0 -45
  67. package/tests/package-lock.json +0 -36
  68. package/tests/package.json +0 -13
  69. package/tests/src/bun/index.ts +0 -100
  70. package/tests/src/bun/test-runner.ts +0 -508
  71. package/tests/src/mainview/index.html +0 -110
  72. package/tests/src/mainview/index.ts +0 -458
  73. package/tests/src/mainview/styles/main.css +0 -451
  74. package/tests/src/testviews/tray-test.html +0 -57
  75. package/tests/src/testviews/webview-mask.html +0 -114
  76. package/tests/src/testviews/webview-navigation.html +0 -36
  77. package/tests/src/testviews/window-create.html +0 -17
  78. package/tests/src/testviews/window-events.html +0 -29
  79. package/tests/src/testviews/window-focus.html +0 -37
  80. package/tests/src/webviewtag/index.ts +0 -11
@@ -1,634 +0,0 @@
1
- import Electrobun, { Electroview } from "electrobun/view";
2
-
3
- console.log("🌐 Initializing Multitab Browser UI...");
4
-
5
- // Create RPC client
6
- const rpc = Electroview.defineRPC({
7
- maxRequestTime: 10000,
8
- handlers: {
9
- requests: {},
10
- messages: {
11
- tabUpdated: (tab: any) => {
12
- console.log("Tab updated:", tab);
13
- if ((window as any).multitabBrowser) {
14
- (window as any).multitabBrowser.handleTabUpdate(tab);
15
- }
16
- },
17
- tabClosed: ({ id }: { id: string }) => {
18
- console.log("Tab closed:", id);
19
- if ((window as any).multitabBrowser) {
20
- (window as any).multitabBrowser.handleTabClosed(id);
21
- }
22
- },
23
- }
24
- }
25
- });
26
-
27
- // Initialize Electrobun with RPC
28
- const electrobun = new Electrobun.Electroview({ rpc });
29
-
30
- class MultitabBrowser {
31
- private tabs: Map<string, any> = new Map();
32
- private webviews: Map<string, HTMLElement> = new Map();
33
- private activeTabId: string | null = null;
34
- private bookmarks: Map<string, any> = new Map();
35
-
36
- constructor() {
37
- // Store reference globally for RPC message handlers
38
- (window as any).multitabBrowser = this;
39
-
40
- this.initializeUI();
41
- this.loadBookmarks();
42
- }
43
-
44
- private initializeUI(): void {
45
- // New tab button
46
- document.getElementById("new-tab-btn")?.addEventListener("click", () => {
47
- this.createNewTab();
48
- });
49
-
50
- // URL bar navigation
51
- const urlBar = document.getElementById("url-bar") as HTMLInputElement;
52
- urlBar?.addEventListener("keypress", async (e) => {
53
- if (e.key === "Enter") {
54
- const url = urlBar.value.trim();
55
- if (url) {
56
- try {
57
- // Process URL
58
- let processedUrl = url;
59
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
60
- if (url.includes(".") && !url.includes(" ")) {
61
- processedUrl = `https://${url}`;
62
- } else {
63
- processedUrl = `https://www.google.com/search?q=${encodeURIComponent(url)}`;
64
- }
65
- }
66
-
67
- // If no active tab, create a new one with this URL
68
- if (!this.activeTabId) {
69
- await this.createNewTab(processedUrl);
70
- return;
71
- }
72
-
73
- // Navigate the existing webview
74
- const webview = this.webviews.get(this.activeTabId) as any;
75
- if (webview) {
76
- webview.src = processedUrl;
77
- }
78
-
79
- // Update tab info
80
- const tab = this.tabs.get(this.activeTabId);
81
- if (tab) {
82
- tab.url = processedUrl;
83
- this.handleTabUpdate(tab);
84
- }
85
-
86
- await rpc.request.navigateTo({ tabId: this.activeTabId, url: processedUrl });
87
- } catch (error) {
88
- console.error("Failed to navigate:", error);
89
- }
90
- }
91
- }
92
- });
93
-
94
- // Navigation buttons
95
- document.getElementById("back-btn")?.addEventListener("click", async () => {
96
- if (this.activeTabId) {
97
- const webview = this.webviews.get(this.activeTabId) as any;
98
- if (webview && webview.goBack) {
99
- webview.goBack();
100
- }
101
- }
102
- });
103
-
104
- document.getElementById("forward-btn")?.addEventListener("click", async () => {
105
- if (this.activeTabId) {
106
- const webview = this.webviews.get(this.activeTabId) as any;
107
- if (webview && webview.goForward) {
108
- webview.goForward();
109
- }
110
- }
111
- });
112
-
113
- document.getElementById("reload-btn")?.addEventListener("click", async () => {
114
- if (this.activeTabId) {
115
- const webview = this.webviews.get(this.activeTabId) as any;
116
- if (webview && webview.reload) {
117
- webview.reload();
118
- }
119
- }
120
- });
121
-
122
- document.getElementById("home-btn")?.addEventListener("click", async () => {
123
- const homeUrl = "https://electrobun.dev";
124
-
125
- if (this.activeTabId) {
126
- // Navigate existing tab to home
127
- const webview = this.webviews.get(this.activeTabId) as any;
128
- if (webview) {
129
- webview.src = homeUrl;
130
-
131
- // Update tab info
132
- const tab = this.tabs.get(this.activeTabId);
133
- if (tab) {
134
- tab.url = homeUrl;
135
- this.handleTabUpdate(tab);
136
- }
137
- }
138
- } else {
139
- // Create new tab with home URL
140
- await this.createNewTab(homeUrl);
141
- }
142
- });
143
-
144
- // Bookmark button
145
- document.getElementById("bookmark-btn")?.addEventListener("click", () => {
146
- this.toggleBookmark();
147
- });
148
-
149
- // Bookmarks menu button
150
- const bookmarksMenuBtn = document.getElementById("bookmarks-menu-btn");
151
- console.log("Found bookmarks menu button:", bookmarksMenuBtn);
152
- bookmarksMenuBtn?.addEventListener("click", (e) => {
153
- console.log("Bookmarks menu button clicked");
154
- e.stopPropagation();
155
- this.toggleBookmarksMenu();
156
- });
157
-
158
- // Reset bookmarks button (delegate to handle dynamically added buttons)
159
- document.addEventListener("click", (e) => {
160
- if ((e.target as HTMLElement)?.id === "reset-bookmarks-btn") {
161
- e.preventDefault();
162
- e.stopPropagation();
163
- console.log("Reset button clicked");
164
- this.resetBookmarks();
165
- }
166
- });
167
-
168
-
169
- // Close bookmarks dropdown when clicking outside
170
- document.addEventListener("click", (e) => {
171
- const dropdown = document.getElementById("bookmarks-dropdown");
172
- const menuBtn = document.getElementById("bookmarks-menu-btn");
173
- const resetBtn = document.getElementById("reset-bookmarks-btn");
174
- if (dropdown && !dropdown.contains(e.target as Node) && e.target !== menuBtn && e.target !== resetBtn) {
175
- dropdown.classList.add("hidden");
176
- }
177
- });
178
-
179
- // Keyboard shortcuts - support both Cmd (Mac) and Ctrl (Windows/Linux)
180
- document.addEventListener("keydown", (e) => {
181
- const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
182
- const modifierPressed = isMac ? e.metaKey : e.ctrlKey;
183
-
184
- // Also support the opposite modifier for cross-platform compatibility
185
- const altModifierPressed = isMac ? e.ctrlKey : e.metaKey;
186
-
187
- if ((modifierPressed || altModifierPressed) && e.key.toLowerCase() === "t") {
188
- e.preventDefault();
189
- this.createNewTab();
190
- }
191
-
192
- if ((modifierPressed || altModifierPressed) && e.key.toLowerCase() === "w") {
193
- e.preventDefault();
194
- if (this.activeTabId) {
195
- this.closeTab(this.activeTabId);
196
- }
197
- }
198
-
199
- if ((modifierPressed || altModifierPressed) && e.key.toLowerCase() === "l") {
200
- e.preventDefault();
201
- const urlBar = document.getElementById("url-bar") as HTMLInputElement;
202
- urlBar?.focus();
203
- urlBar?.select();
204
- }
205
- });
206
-
207
- // Show welcome screen initially
208
- this.showWelcomeScreen();
209
- }
210
-
211
- private async createNewTab(url?: string): Promise<void> {
212
- try {
213
- const tab = await rpc.request.createTab({ url });
214
- this.tabs.set(tab.id, tab);
215
-
216
- // Create electrobun-webview element for this tab
217
- const webview = document.createElement('electrobun-webview');
218
- webview.setAttribute('src', tab.url);
219
- webview.setAttribute('id', `webview-${tab.id}`);
220
- webview.setAttribute('masks', '#bookmarks-dropdown');
221
- webview.setAttribute('renderer', 'cef');
222
- webview.classList.add('tab-webview');
223
-
224
- // Add webview to container
225
- const container = document.getElementById('webview-container');
226
- if (container) {
227
- container.appendChild(webview);
228
- }
229
-
230
- this.webviews.set(tab.id, webview);
231
-
232
- // Set up webview event listeners
233
- webview.addEventListener('page-title-updated', (e: any) => {
234
- const updatedTab = this.tabs.get(tab.id);
235
- if (updatedTab) {
236
- updatedTab.title = e.detail?.title || 'New Tab';
237
- this.handleTabUpdate(updatedTab);
238
- }
239
- });
240
-
241
- webview.addEventListener('did-navigate', (e: any) => {
242
- const updatedTab = this.tabs.get(tab.id);
243
- if (updatedTab && e.detail?.url) {
244
- updatedTab.url = e.detail.url;
245
- this.handleTabUpdate(updatedTab);
246
- }
247
- });
248
-
249
- this.renderTab(tab);
250
- this.switchToTab(tab.id);
251
- } catch (error) {
252
- console.error("Failed to create tab:", error);
253
- }
254
- }
255
-
256
- private renderTab(tab: any): void {
257
- const tabsContainer = document.getElementById("tabs-container");
258
- if (!tabsContainer) return;
259
-
260
- const tabElement = document.createElement("div");
261
- tabElement.className = "tab";
262
- tabElement.id = `tab-${tab.id}`;
263
- tabElement.innerHTML = `
264
- <span class="tab-title">${this.truncateTitle(tab.title)}</span>
265
- <button class="tab-close" data-tab-id="${tab.id}">×</button>
266
- `;
267
-
268
- tabElement.addEventListener("click", (e) => {
269
- if (!(e.target as HTMLElement).classList.contains("tab-close")) {
270
- this.switchToTab(tab.id);
271
- }
272
- });
273
-
274
- tabElement.querySelector(".tab-close")?.addEventListener("click", (e) => {
275
- e.stopPropagation();
276
- this.closeTab(tab.id);
277
- });
278
-
279
- tabsContainer.appendChild(tabElement);
280
- }
281
-
282
- private async switchToTab(tabId: string): Promise<void> {
283
- try {
284
- // Update UI immediately
285
- document.querySelectorAll(".tab").forEach(tab => {
286
- tab.classList.remove("active");
287
- });
288
- document.getElementById(`tab-${tabId}`)?.classList.add("active");
289
-
290
- // Hide all webviews
291
- this.webviews.forEach((webview) => {
292
- // webview.classList.remove('active');
293
- webview.toggleHidden(true)
294
- webview.togglePassthrough(true)
295
- });
296
-
297
- // Show the selected webview
298
- const selectedWebview = this.webviews.get(tabId);
299
- if (selectedWebview) {
300
- selectedWebview.classList.add('active');
301
- selectedWebview.toggleHidden(false)
302
- selectedWebview.togglePassthrough(false)
303
- }
304
-
305
- this.activeTabId = tabId;
306
- const tab = this.tabs.get(tabId);
307
-
308
- if (tab) {
309
- const urlBar = document.getElementById("url-bar") as HTMLInputElement;
310
- if (urlBar) {
311
- urlBar.value = tab.url;
312
- }
313
-
314
- this.updateBookmarkButton();
315
- this.hideWelcomeScreen();
316
- }
317
-
318
- // Notify backend about tab switch (optional)
319
- await rpc.request.activateTab({ tabId });
320
- } catch (error) {
321
- console.error("Failed to switch tab:", error);
322
- }
323
- }
324
-
325
- private async closeTab(tabId: string): Promise<void> {
326
- try {
327
- console.log(`Closing tab ${tabId}, active tab: ${this.activeTabId}, total tabs before: ${this.tabs.size}`);
328
-
329
- await rpc.request.closeTab({ id: tabId });
330
- this.tabs.delete(tabId);
331
-
332
- // Remove the webview element
333
- const webview = this.webviews.get(tabId);
334
- if (webview) {
335
- webview.remove();
336
- this.webviews.delete(tabId);
337
- }
338
-
339
- document.getElementById(`tab-${tabId}`)?.remove();
340
-
341
- const remainingTabs = Array.from(this.tabs.keys());
342
- console.log(`Remaining tabs after close: ${remainingTabs.length}`, remainingTabs);
343
-
344
- // Check if this was the active tab
345
- if (this.activeTabId === tabId) {
346
- console.log("Closed the active tab");
347
- this.activeTabId = null;
348
-
349
- if (remainingTabs.length > 0) {
350
- console.log("Switching to remaining tab:", remainingTabs[remainingTabs.length - 1]);
351
- this.switchToTab(remainingTabs[remainingTabs.length - 1]);
352
- } else {
353
- console.log("No tabs left - showing welcome screen");
354
- this.showWelcomeScreen();
355
- }
356
- } else {
357
- console.log("Closed a non-active tab");
358
- if (remainingTabs.length === 0) {
359
- console.log("No tabs left after closing non-active tab - showing welcome screen");
360
- this.activeTabId = null;
361
- this.showWelcomeScreen();
362
- }
363
- }
364
- } catch (error) {
365
- console.error("Failed to close tab:", error);
366
- }
367
- }
368
-
369
- public handleTabUpdate(tab: any): void {
370
- this.tabs.set(tab.id, tab);
371
-
372
- const tabElement = document.getElementById(`tab-${tab.id}`);
373
- if (tabElement) {
374
- const titleElement = tabElement.querySelector(".tab-title");
375
- if (titleElement) {
376
- titleElement.textContent = this.truncateTitle(tab.title);
377
- }
378
- }
379
-
380
- if (this.activeTabId === tab.id) {
381
- const urlBar = document.getElementById("url-bar") as HTMLInputElement;
382
- if (urlBar && document.activeElement !== urlBar) {
383
- urlBar.value = tab.url;
384
- }
385
- this.updateBookmarkButton();
386
- }
387
- }
388
-
389
- public handleTabClosed(id: string): void {
390
- this.tabs.delete(id);
391
- document.getElementById(`tab-${id}`)?.remove();
392
- }
393
-
394
- private showWelcomeScreen(): void {
395
- const welcome = document.getElementById("welcome-screen");
396
- const webview = document.getElementById("webview-container");
397
- if (welcome) welcome.style.display = "flex";
398
- if (webview) webview.style.display = "none";
399
-
400
- const urlBar = document.getElementById("url-bar") as HTMLInputElement;
401
- if (urlBar) urlBar.value = "";
402
- }
403
-
404
- private hideWelcomeScreen(): void {
405
- const welcome = document.getElementById("welcome-screen");
406
- const webview = document.getElementById("webview-container");
407
- if (welcome) welcome.style.display = "none";
408
- if (webview) webview.style.display = "block";
409
- }
410
-
411
- private truncateTitle(title: string, maxLength: number = 20): string {
412
- if (title.length <= maxLength) return title;
413
- return title.substring(0, maxLength - 3) + "...";
414
- }
415
-
416
- private async loadBookmarks(): Promise<void> {
417
- try {
418
- // Load bookmarks from localStorage or backend
419
- const stored = localStorage.getItem('bookmarks');
420
- if (stored) {
421
- const bookmarksArray = JSON.parse(stored);
422
- bookmarksArray.forEach((bookmark: any) => {
423
- this.bookmarks.set(bookmark.url, bookmark);
424
- });
425
- } else {
426
- // Add default bookmarks
427
- this.addBookmark("Electrobun", "https://electrobun.dev");
428
- this.addBookmark("Electrobun GitHub", "https://github.com/blackboardsh/electrobun");
429
- this.addBookmark("Yoav on Bluesky", "https://bsky.app/profile/yoav.codes");
430
- this.addBookmark("Blackboard", "https://www.blackboard.sh");
431
- }
432
- this.renderBookmarks();
433
- this.renderQuickLinks();
434
- } catch (error) {
435
- console.error("Failed to load bookmarks:", error);
436
- }
437
- }
438
-
439
- private saveBookmarks(): void {
440
- const bookmarksArray = Array.from(this.bookmarks.values());
441
- localStorage.setItem('bookmarks', JSON.stringify(bookmarksArray));
442
- }
443
-
444
- private resetBookmarks(): void {
445
- console.log("resetBookmarks called - resetting without confirmation");
446
-
447
- // Clear existing bookmarks
448
- this.bookmarks.clear();
449
- localStorage.removeItem('bookmarks');
450
-
451
- // Add default bookmarks with unique IDs
452
- let counter = 0;
453
- const addDefaultBookmark = (title: string, url: string) => {
454
- const bookmark = {
455
- id: `bookmark-default-${counter++}`,
456
- title,
457
- url,
458
- createdAt: Date.now() + counter
459
- };
460
- this.bookmarks.set(url, bookmark);
461
- console.log("Added bookmark:", bookmark);
462
- };
463
-
464
- addDefaultBookmark("Electrobun", "https://electrobun.dev");
465
- addDefaultBookmark("Electrobun GitHub", "https://github.com/blackboardsh/electrobun");
466
- addDefaultBookmark("Yoav on Bluesky", "https://bsky.app/profile/yoav.codes");
467
- addDefaultBookmark("Blackboard", "https://www.blackboard.sh");
468
-
469
- // Save and re-render
470
- this.saveBookmarks();
471
- this.renderBookmarks();
472
- this.renderQuickLinks();
473
- this.updateBookmarkButton();
474
-
475
- console.log("Bookmarks reset completed, total bookmarks:", this.bookmarks.size);
476
- }
477
-
478
- private addBookmark(title: string, url: string): void {
479
- const bookmark = {
480
- id: `bookmark-${Date.now()}`,
481
- title,
482
- url,
483
- createdAt: Date.now()
484
- };
485
- this.bookmarks.set(url, bookmark);
486
- this.saveBookmarks();
487
- }
488
-
489
- private removeBookmark(url: string): void {
490
- this.bookmarks.delete(url);
491
- this.saveBookmarks();
492
- }
493
-
494
- private toggleBookmark(): void {
495
- if (!this.activeTabId) return;
496
-
497
- const tab = this.tabs.get(this.activeTabId);
498
- if (!tab) return;
499
-
500
- const bookmarkBtn = document.getElementById("bookmark-btn");
501
- if (!bookmarkBtn) return;
502
-
503
- if (this.bookmarks.has(tab.url)) {
504
- // Remove bookmark
505
- this.removeBookmark(tab.url);
506
- bookmarkBtn.classList.remove("bookmarked");
507
- } else {
508
- // Add bookmark
509
- this.addBookmark(tab.title || "Untitled", tab.url);
510
- bookmarkBtn.classList.add("bookmarked");
511
- }
512
-
513
- this.renderBookmarks();
514
- this.renderQuickLinks();
515
- }
516
-
517
- private updateBookmarkButton(): void {
518
- const bookmarkBtn = document.getElementById("bookmark-btn");
519
- if (!bookmarkBtn || !this.activeTabId) return;
520
-
521
- const tab = this.tabs.get(this.activeTabId);
522
- if (tab && this.bookmarks.has(tab.url)) {
523
- bookmarkBtn.classList.add("bookmarked");
524
- } else {
525
- bookmarkBtn.classList.remove("bookmarked");
526
- }
527
- }
528
-
529
- private toggleBookmarksMenu(): void {
530
- const dropdown = document.getElementById("bookmarks-dropdown");
531
- if (dropdown) {
532
- console.log("Toggling bookmarks menu, current hidden:", dropdown.classList.contains("hidden"));
533
- dropdown.classList.toggle("hidden");
534
- } else {
535
- console.error("Bookmarks dropdown not found");
536
- }
537
- }
538
-
539
- private renderBookmarks(): void {
540
- const bookmarksList = document.getElementById("bookmarks-list");
541
- if (!bookmarksList) return;
542
-
543
- bookmarksList.innerHTML = "";
544
-
545
- if (this.bookmarks.size === 0) {
546
- bookmarksList.innerHTML = '<div class="no-bookmarks">No bookmarks yet</div>';
547
- return;
548
- }
549
-
550
- this.bookmarks.forEach(bookmark => {
551
- const item = document.createElement("div");
552
- item.className = "bookmark-item";
553
- item.innerHTML = `
554
- <div class="bookmark-info">
555
- <div class="bookmark-title">${bookmark.title}</div>
556
- <div class="bookmark-url">${this.truncateUrl(bookmark.url)}</div>
557
- </div>
558
- <button class="bookmark-delete" data-url="${bookmark.url}">×</button>
559
- `;
560
-
561
- item.querySelector(".bookmark-info")?.addEventListener("click", async () => {
562
- if (this.activeTabId) {
563
- // Navigate current tab
564
- const webview = this.webviews.get(this.activeTabId) as any;
565
- if (webview) {
566
- webview.src = bookmark.url;
567
- const tab = this.tabs.get(this.activeTabId);
568
- if (tab) {
569
- tab.url = bookmark.url;
570
- this.handleTabUpdate(tab);
571
- }
572
- }
573
- } else {
574
- // Create new tab with bookmark
575
- await this.createNewTab(bookmark.url);
576
- }
577
- // Hide dropdown
578
- const dropdown = document.getElementById("bookmarks-dropdown");
579
- if (dropdown) {
580
- dropdown.classList.add("hidden");
581
- }
582
- });
583
-
584
- item.querySelector(".bookmark-delete")?.addEventListener("click", (e) => {
585
- e.stopPropagation();
586
- const url = (e.currentTarget as HTMLElement).dataset.url;
587
- if (url) {
588
- this.removeBookmark(url);
589
- this.renderBookmarks();
590
- this.renderQuickLinks();
591
- this.updateBookmarkButton();
592
- }
593
- });
594
-
595
- bookmarksList.appendChild(item);
596
- });
597
- }
598
-
599
- private renderQuickLinks(): void {
600
- const container = document.getElementById("quick-links-container");
601
- if (!container) return;
602
-
603
- container.innerHTML = "";
604
-
605
- // Show first 6 bookmarks as quick links
606
- const bookmarksArray = Array.from(this.bookmarks.values());
607
- bookmarksArray.slice(0, 6).forEach(bookmark => {
608
- const link = document.createElement("button");
609
- link.className = "quick-link";
610
- link.innerHTML = `
611
- <div class="quick-link-favicon">🌐</div>
612
- <div class="quick-link-title">${this.truncateTitle(bookmark.title, 15)}</div>
613
- `;
614
- link.addEventListener("click", () => {
615
- this.createNewTab(bookmark.url);
616
- });
617
- container.appendChild(link);
618
- });
619
- }
620
-
621
- private truncateUrl(url: string, maxLength: number = 40): string {
622
- if (url.length <= maxLength) return url;
623
- return url.substring(0, maxLength - 3) + "...";
624
- }
625
- }
626
-
627
- // Initialize when DOM is ready
628
- if (document.readyState === "loading") {
629
- document.addEventListener("DOMContentLoaded", () => {
630
- new MultitabBrowser();
631
- });
632
- } else {
633
- new MultitabBrowser();
634
- }