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,238 +0,0 @@
1
- import Electrobun, { Electroview } from "electrobun/view";
2
- import { type PlaygroundRPC } from "../bun/types/rpc";
3
-
4
- // Import components
5
- import { Sidebar } from "./components/Sidebar";
6
- import { EventLog } from "./components/EventLog";
7
- import { Toast } from "./components/Toast";
8
-
9
- // Import demos
10
- import { WindowDemo } from "./demos/WindowDemo";
11
- import { RPCDemo } from "./demos/RPCDemo";
12
- import { MenuDemo } from "./demos/MenuDemo";
13
- import { TrayDemo } from "./demos/TrayDemo";
14
- import { FileDemo } from "./demos/FileDemo";
15
- import { WebViewDemo } from "./demos/WebViewDemo";
16
-
17
- class InteractivePlayground {
18
- private electrobun: any;
19
- private sidebar: Sidebar;
20
- private eventLog: EventLog;
21
- private demos: Map<string, any> = new Map();
22
-
23
- constructor() {
24
- console.log("🎮 Initializing Interactive Playground...");
25
-
26
- // Set up RPC
27
- const rpc = Electroview.defineRPC<PlaygroundRPC>({
28
- maxRequestTime: 10000,
29
- handlers: {
30
- requests: {},
31
- messages: {
32
- // Window events
33
- windowCreated: (data) => {
34
- this.eventLog.addEntry('info', `Window created: ${data.title}`, data);
35
- this.demos.get('windows')?.onWindowCreated(data);
36
- Toast.success(`Window created: ${data.title}`);
37
- },
38
-
39
- windowClosed: (data) => {
40
- this.eventLog.addEntry('info', `Window closed: ${data.id}`, data);
41
- this.demos.get('windows')?.onWindowClosed(data);
42
- Toast.info(`Window ${data.id} closed`);
43
- },
44
-
45
- windowFocused: (data) => {
46
- this.eventLog.addEntry('info', `Window focused: ${data.id}`, data);
47
- this.demos.get('windows')?.onWindowFocused(data);
48
- },
49
-
50
- // Tray events
51
- trayClicked: (data) => {
52
- this.eventLog.addEntry('info', `Tray clicked: ${data.action}`, data);
53
- this.demos.get('tray')?.onTrayClicked?.(data);
54
- Toast.info(`Tray action: ${data.action}`);
55
- },
56
-
57
- // Menu events
58
- menuClicked: (data) => {
59
- this.eventLog.addEntry('info', `Menu clicked: ${data.action}`, data);
60
- this.demos.get('menus')?.onMenuClicked?.(data);
61
- Toast.info(`Menu action: ${data.action}`);
62
- },
63
-
64
- // File events
65
- fileSelected: (data) => {
66
- this.eventLog.addEntry('info', `Files selected: ${data.paths.length} files`, data);
67
- this.demos.get('files')?.onFileSelected(data);
68
- Toast.success(`Selected ${data.paths.length} file(s)`);
69
- },
70
-
71
- // RPC test results
72
- rpcTestResult: (data) => {
73
- this.eventLog.addEntry('info', `RPC test completed: ${data.operation}`, data);
74
- this.demos.get('rpc')?.onRpcTestResult(data);
75
- },
76
-
77
- // System events
78
- systemEvent: (data) => {
79
- this.eventLog.addEntry('info', `System event: ${data.type}`, data);
80
- Toast.info(`System: ${data.type}`);
81
- },
82
-
83
-
84
- // Log messages
85
- logMessage: (data) => {
86
- this.eventLog.addEntry(data.level, data.message);
87
- if (data.level === 'error') {
88
- Toast.error(data.message);
89
- } else if (data.level === 'warn') {
90
- Toast.warning(data.message);
91
- } else {
92
- Toast.info(data.message);
93
- }
94
- },
95
- },
96
- },
97
- });
98
-
99
- // Create Electroview instance
100
- this.electrobun = new Electrobun.Electroview({ rpc });
101
-
102
- // Initialize components
103
- this.sidebar = new Sidebar();
104
- this.eventLog = new EventLog();
105
-
106
- // Initialize demos
107
- this.initializeDemos();
108
-
109
- // Set up event listeners
110
- this.setupEventListeners();
111
-
112
- // Load initial demo
113
- this.loadDemo('windows');
114
-
115
- console.log("✅ Interactive Playground initialized");
116
- }
117
-
118
- private initializeDemos() {
119
- this.demos.set('windows', new WindowDemo());
120
- this.demos.set('rpc', new RPCDemo());
121
- this.demos.set('menus', new MenuDemo());
122
- this.demos.set('tray', new TrayDemo());
123
- this.demos.set('files', new FileDemo());
124
- this.demos.set('webviews', new WebViewDemo());
125
- // Add more demos as they're implemented
126
- }
127
-
128
- private setupEventListeners() {
129
- this.sidebar.onDemoChangeCallback((demo) => {
130
- this.loadDemo(demo);
131
- });
132
-
133
- // Global error handling
134
- window.addEventListener('error', (event) => {
135
- this.eventLog.addEntry('error', event.message, {
136
- filename: event.filename,
137
- lineno: event.lineno,
138
- colno: event.colno
139
- });
140
- Toast.error(`JavaScript Error: ${event.message}`);
141
- });
142
-
143
- // Unhandled promise rejections
144
- window.addEventListener('unhandledrejection', (event) => {
145
- this.eventLog.addEntry('error', `Unhandled Promise Rejection: ${event.reason}`, event.reason);
146
- Toast.error('Unhandled Promise Rejection');
147
- });
148
- }
149
-
150
- private loadDemo(demoName: string) {
151
- const content = document.getElementById('demo-content');
152
- if (!content) return;
153
-
154
- // CRITICAL: Cleanup any existing webviews before switching demos
155
- // This prevents crashes when CEF tries to clean up stale webview references
156
- this.cleanupWebviews();
157
-
158
- const demo = this.demos.get(demoName);
159
- if (!demo) {
160
- content.innerHTML = this.renderPlaceholderDemo(demoName);
161
- return;
162
- }
163
-
164
- // Render demo content
165
- content.innerHTML = demo.render();
166
-
167
- // Initialize demo with RPC
168
- if (demo.initialize) {
169
- demo.initialize(this.electrobun.rpc);
170
- }
171
-
172
- this.eventLog.addEntry('info', `Loaded demo: ${demoName}`);
173
- }
174
-
175
- private cleanupWebviews() {
176
- // Find all webview elements and properly remove them
177
- const webviews = document.querySelectorAll('electrobun-webview');
178
-
179
- webviews.forEach((webview: any) => {
180
- try {
181
- // Call the native remove method if it exists
182
- if (typeof webview.remove === 'function') {
183
- webview.remove();
184
- }
185
- } catch (error) {
186
- console.warn('Error during webview cleanup:', error);
187
- }
188
- });
189
-
190
- // Additional safety: clear any webview-related event listeners
191
- // This helps prevent memory leaks and stale references
192
- webviews.forEach((webview: any) => {
193
- try {
194
- if (typeof webview.removeAllEventListeners === 'function') {
195
- webview.removeAllEventListeners();
196
- }
197
- } catch (error) {
198
- // Silently ignore if method doesn't exist
199
- }
200
- });
201
- }
202
-
203
- private renderPlaceholderDemo(demoName: string): string {
204
- const demoInfo: Record<string, { icon: string; title: string; description: string }> = {
205
- menus: { icon: '🎛️', title: 'Menu Systems', description: 'Application and context menus' },
206
- tray: { icon: '🔔', title: 'System Tray', description: 'Tray icon management' },
207
- files: { icon: '🗂️', title: 'File Operations', description: 'File dialogs and system integration' },
208
- webviews: { icon: '🌐', title: 'WebView Features', description: 'Advanced webview capabilities' }
209
- };
210
-
211
- const info = demoInfo[demoName] || { icon: '🔧', title: 'Coming Soon', description: 'This demo is under development' };
212
-
213
- return `
214
- <div class="demo-section">
215
- <div class="demo-header">
216
- <span class="demo-icon">${info.icon}</span>
217
- <div>
218
- <h2 class="demo-title">${info.title}</h2>
219
- <p class="demo-description">${info.description}</p>
220
- </div>
221
- </div>
222
-
223
- <div class="demo-controls">
224
- <div style="text-align: center; padding: 3rem; color: #718096;">
225
- <h3>Coming Soon!</h3>
226
- <p>This demo is currently under development.</p>
227
- <p>Check back soon for interactive examples of ${info.title.toLowerCase()}.</p>
228
- </div>
229
- </div>
230
- </div>
231
- `;
232
- }
233
- }
234
-
235
- // Initialize the playground when DOM is ready
236
- document.addEventListener('DOMContentLoaded', () => {
237
- new InteractivePlayground();
238
- });
@@ -1,34 +0,0 @@
1
- # Multitab Browser
2
-
3
- A demonstration of building a multi-tab browser using Electrobun framework.
4
-
5
- ## Features
6
-
7
- - Multiple browser tabs with independent webviews
8
- - Navigation controls (back, forward, refresh, home)
9
- - URL bar with navigation
10
- - Tab management (new, close, switch)
11
- - Bookmark functionality
12
- - History tracking
13
-
14
- ## Running the Demo
15
-
16
- ```bash
17
- # Install dependencies
18
- bun install
19
-
20
- # Run in development mode
21
- bun run dev
22
-
23
- # Build for production
24
- bun run build
25
- ```
26
-
27
- ## Architecture
28
-
29
- This template demonstrates:
30
- - Using BrowserView for embedded web content
31
- - RPC communication between main process and renderer
32
- - Tab state management
33
- - Keyboard shortcuts
34
- - URL handling and navigation
@@ -1,32 +0,0 @@
1
- export default {
2
- app: {
3
- name: "multitab-browser",
4
- identifier: "multitab-browser.electrobun.dev",
5
- version: "0.0.1",
6
- },
7
- build: {
8
- bun: {
9
- entrypoint: "src/bun/index.ts",
10
- external: [],
11
- },
12
- views: {
13
- mainview: {
14
- entrypoint: "src/mainview/index.ts",
15
- external: [],
16
- },
17
- },
18
- copy: {
19
- "src/mainview/index.html": "views/mainview/index.html",
20
- "src/mainview/index.css": "views/mainview/index.css",
21
- },
22
- mac: {
23
- bundleCEF: true,
24
- },
25
- linux: {
26
- bundleCEF: true,
27
- },
28
- win: {
29
- bundleCEF: true,
30
- },
31
- },
32
- };
@@ -1,20 +0,0 @@
1
- {
2
- "name": "multitab-browser",
3
- "version": "0.0.1",
4
- "lockfileVersion": 3,
5
- "requires": true,
6
- "packages": {
7
- "": {
8
- "name": "multitab-browser",
9
- "version": "0.0.1",
10
- "dependencies": {
11
- "electrobun": "file:../"
12
- }
13
- },
14
- "..": {},
15
- "node_modules/electrobun": {
16
- "resolved": "..",
17
- "link": true
18
- }
19
- }
20
- }
@@ -1,12 +0,0 @@
1
- {
2
- "name": "multitab-browser",
3
- "version": "0.0.1",
4
- "description": "A multi-tab browser demo for Electrobun",
5
- "type": "module",
6
- "scripts": {
7
- "start": "electrobun build && electrobun dev"
8
- },
9
- "dependencies": {
10
- "electrobun": "latest"
11
- }
12
- }
@@ -1,144 +0,0 @@
1
- import Electrobun, { BrowserWindow, BrowserView } from "electrobun/bun";
2
-
3
- console.log("🌐 Multitab Browser starting...");
4
-
5
- // Simplified tab management - demo without real webviews
6
- const tabs = new Map();
7
- let nextTabId = 1;
8
- let mainRPC: any = null; // Will be set after window creation
9
-
10
- // Set up RPC using the correct API pattern from interactive-playground
11
- const rpc = BrowserView.defineRPC({
12
- maxRequestTime: 10000,
13
- handlers: {
14
- requests: {
15
- createTab: async ({ url }: { url?: string }) => {
16
- const id = `tab-${nextTabId++}`;
17
-
18
- const tab = {
19
- id,
20
- title: "New Tab",
21
- url: url || "https://electrobun.dev",
22
- canGoBack: false,
23
- canGoForward: false,
24
- isLoading: false,
25
- };
26
-
27
- tabs.set(id, tab);
28
-
29
- // Simulate getting the title after a delay
30
- setTimeout(() => {
31
- if (mainRPC) {
32
- tab.title = new URL(tab.url).hostname || "New Tab";
33
- mainRPC.send("tabUpdated", tab);
34
- }
35
- }, 500);
36
-
37
- return tab;
38
- },
39
-
40
- closeTab: async ({ id }: { id: string }) => {
41
- tabs.delete(id);
42
- return;
43
- },
44
-
45
- activateTab: async ({ tabId }: { tabId: string }) => {
46
- // Just return the tab info - the iframe switching happens in the frontend
47
- return tabs.get(tabId);
48
- },
49
-
50
- navigateTo: async ({ tabId, url }: { tabId: string; url: string }) => {
51
- const tab = tabs.get(tabId);
52
-
53
- if (tab) {
54
- // Process URL - add https if needed, or search
55
- let processedUrl = url;
56
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
57
- if (url.includes(".") && !url.includes(" ")) {
58
- processedUrl = `https://${url}`;
59
- } else {
60
- processedUrl = `https://www.google.com/search?q=${encodeURIComponent(url)}`;
61
- }
62
- }
63
-
64
- tab.url = processedUrl;
65
- tab.isLoading = false;
66
-
67
- // Simulate title update
68
- setTimeout(() => {
69
- if (mainRPC && tab) {
70
- try {
71
- tab.title = new URL(processedUrl).hostname || "New Tab";
72
- } catch {
73
- tab.title = processedUrl;
74
- }
75
- mainRPC.send("tabUpdated", tab);
76
- }
77
- }, 500);
78
- }
79
-
80
- return tab;
81
- },
82
-
83
- goBack: async ({ tabId }: { tabId: string }) => {
84
- // In a real implementation, we'd track history
85
- console.log("Go back for tab:", tabId);
86
- return tabs.get(tabId);
87
- },
88
-
89
- goForward: async ({ tabId }: { tabId: string }) => {
90
- // In a real implementation, we'd track history
91
- console.log("Go forward for tab:", tabId);
92
- return tabs.get(tabId);
93
- },
94
-
95
- reload: async ({ tabId }: { tabId: string }) => {
96
- const tab = tabs.get(tabId);
97
- if (tab) {
98
- tab.isLoading = true;
99
- if (mainRPC) {
100
- mainRPC.send("tabUpdated", tab);
101
- }
102
-
103
- setTimeout(() => {
104
- tab.isLoading = false;
105
- if (mainRPC) {
106
- mainRPC.send("tabUpdated", tab);
107
- }
108
- }, 1000);
109
- }
110
- return tab;
111
- },
112
- },
113
- messages: {
114
- "*": (messageName: string, payload: any) => {
115
- console.log(`📨 Browser message: ${messageName}`, payload);
116
- },
117
- },
118
- },
119
- });
120
-
121
- // Create main browser window with RPC
122
- const mainWindow = new BrowserWindow({
123
- title: "Multitab Browser",
124
- url: "views://mainview/index.html",
125
- frame: {
126
- width: 1400,
127
- height: 900,
128
- x: 100,
129
- y: 100,
130
- },
131
- rpc,
132
- });
133
-
134
- // Store reference to mainWindow RPC for sending messages
135
- mainRPC = mainWindow.webview.rpc;
136
-
137
- // Listen for window close event and exit the app
138
- // For this browser app, we want to exit when the main window is closed
139
- mainWindow.on("close", () => {
140
- console.log("🚪 Main window closed - exiting app");
141
- process.exit(0);
142
- });
143
-
144
- console.log("✅ Multitab Browser initialized");
@@ -1,200 +0,0 @@
1
- import { BrowserView } from "electrobun/bun";
2
- import { type Tab, type Bookmark } from "./types/rpc";
3
-
4
- export class TabManager {
5
- private tabs: Map<string, Tab> = new Map();
6
- private webviews: Map<string, BrowserView> = new Map();
7
- private bookmarks: Map<string, Bookmark> = new Map();
8
- private nextTabId = 1;
9
-
10
- public onTabUpdate?: (tab: Tab) => void;
11
- public onLoadingStateChange?: (tabId: string, isLoading: boolean) => void;
12
-
13
- constructor() {
14
- // Load bookmarks from storage if available
15
- this.loadBookmarks();
16
- }
17
-
18
- async createTab(url: string): Promise<Tab> {
19
- const id = `tab-${this.nextTabId++}`;
20
-
21
- // Create a BrowserView for this tab
22
- const webview = new BrowserView({
23
- url,
24
- frame: {
25
- x: 0,
26
- y: 100, // Leave space for tab bar and navigation
27
- width: 1400,
28
- height: 800,
29
- },
30
- });
31
-
32
- // Set up webview event handlers
33
- webview.on("page-title-updated", (event) => {
34
- const tab = this.tabs.get(id);
35
- if (tab) {
36
- tab.title = event.data.title || "New Tab";
37
- this.onTabUpdate?.(tab);
38
- }
39
- });
40
-
41
- webview.on("did-start-loading", () => {
42
- const tab = this.tabs.get(id);
43
- if (tab) {
44
- tab.isLoading = true;
45
- this.onLoadingStateChange?.(id, true);
46
- }
47
- });
48
-
49
- webview.on("did-stop-loading", () => {
50
- const tab = this.tabs.get(id);
51
- if (tab) {
52
- tab.isLoading = false;
53
- this.onLoadingStateChange?.(id, false);
54
- }
55
- });
56
-
57
- webview.on("did-navigate", (event) => {
58
- const tab = this.tabs.get(id);
59
- if (tab && event.data.url) {
60
- tab.url = event.data.url;
61
- // Update navigation state
62
- this.updateNavigationState(id);
63
- this.onTabUpdate?.(tab);
64
- }
65
- });
66
-
67
- const tab: Tab = {
68
- id,
69
- title: "New Tab",
70
- url,
71
- canGoBack: false,
72
- canGoForward: false,
73
- isLoading: true,
74
- };
75
-
76
- this.tabs.set(id, tab);
77
- this.webviews.set(id, webview);
78
-
79
- return tab;
80
- }
81
-
82
- async closeTab(id: string): Promise<void> {
83
- const webview = this.webviews.get(id);
84
- if (webview) {
85
- webview.destroy();
86
- this.webviews.delete(id);
87
- }
88
- this.tabs.delete(id);
89
- }
90
-
91
- async navigateTo(tabId: string, url: string): Promise<void> {
92
- const webview = this.webviews.get(tabId);
93
- if (webview) {
94
- // Ensure URL has protocol
95
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
96
- // Check if it looks like a domain
97
- if (url.includes(".") && !url.includes(" ")) {
98
- url = `https://${url}`;
99
- } else {
100
- // Treat as search query
101
- url = `https://www.google.com/search?q=${encodeURIComponent(url)}`;
102
- }
103
- }
104
-
105
- await webview.loadURL(url);
106
- const tab = this.tabs.get(tabId);
107
- if (tab) {
108
- tab.url = url;
109
- tab.isLoading = true;
110
- this.onTabUpdate?.(tab);
111
- }
112
- }
113
- }
114
-
115
- async goBack(tabId: string): Promise<void> {
116
- const webview = this.webviews.get(tabId);
117
- if (webview) {
118
- await webview.goBack();
119
- this.updateNavigationState(tabId);
120
- }
121
- }
122
-
123
- async goForward(tabId: string): Promise<void> {
124
- const webview = this.webviews.get(tabId);
125
- if (webview) {
126
- await webview.goForward();
127
- this.updateNavigationState(tabId);
128
- }
129
- }
130
-
131
- async reload(tabId: string): Promise<void> {
132
- const webview = this.webviews.get(tabId);
133
- if (webview) {
134
- await webview.reload();
135
- const tab = this.tabs.get(tabId);
136
- if (tab) {
137
- tab.isLoading = true;
138
- this.onTabUpdate?.(tab);
139
- }
140
- }
141
- }
142
-
143
- async getTabInfo(tabId: string): Promise<Tab | undefined> {
144
- return this.tabs.get(tabId);
145
- }
146
-
147
- getAllTabs(): Tab[] {
148
- return Array.from(this.tabs.values());
149
- }
150
-
151
- private async updateNavigationState(tabId: string): Promise<void> {
152
- const webview = this.webviews.get(tabId);
153
- const tab = this.tabs.get(tabId);
154
-
155
- if (webview && tab) {
156
- // Note: These methods might not be available in current Electrobun API
157
- // This is a placeholder for navigation state management
158
- tab.canGoBack = false; // Would need webview.canGoBack()
159
- tab.canGoForward = false; // Would need webview.canGoForward()
160
- }
161
- }
162
-
163
- // Bookmark management
164
- addBookmark(title: string, url: string): Bookmark {
165
- const id = `bookmark-${Date.now()}`;
166
- const bookmark: Bookmark = {
167
- id,
168
- title,
169
- url,
170
- createdAt: Date.now(),
171
- };
172
-
173
- this.bookmarks.set(id, bookmark);
174
- this.saveBookmarks();
175
- return bookmark;
176
- }
177
-
178
- getBookmarks(): Bookmark[] {
179
- return Array.from(this.bookmarks.values()).sort((a, b) => b.createdAt - a.createdAt);
180
- }
181
-
182
- removeBookmark(id: string): void {
183
- this.bookmarks.delete(id);
184
- this.saveBookmarks();
185
- }
186
-
187
- private loadBookmarks(): void {
188
- // In a real app, load from persistent storage
189
- // For demo, we'll start with some default bookmarks
190
- this.addBookmark("Google", "https://www.google.com");
191
- this.addBookmark("GitHub", "https://github.com");
192
- this.addBookmark("Electrobun", "https://electrobun.dev");
193
- }
194
-
195
- private saveBookmarks(): void {
196
- // In a real app, save to persistent storage
197
- // For demo, we'll just log
198
- console.log("Bookmarks saved:", this.getBookmarks());
199
- }
200
- }