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,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
- }