electrobun 0.0.19-beta.97 → 0.1.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 (77) hide show
  1. package/README.md +1 -1
  2. package/dist/api/browser/webviewtag.ts +54 -2
  3. package/dist/api/bun/ElectrobunConfig.ts +171 -0
  4. package/dist/api/bun/core/BrowserWindow.ts +4 -0
  5. package/dist/api/bun/core/Tray.ts +14 -0
  6. package/dist/api/bun/core/Updater.ts +4 -3
  7. package/dist/api/bun/index.ts +2 -0
  8. package/dist/api/bun/proc/native.ts +107 -5
  9. package/dist/main.js +5 -4
  10. package/package.json +4 -2
  11. package/src/cli/index.ts +565 -148
  12. package/templates/hello-world/bun.lock +164 -2
  13. package/templates/hello-world/electrobun.config.ts +28 -0
  14. package/templates/hello-world/src/bun/index.ts +2 -2
  15. package/templates/hello-world/src/mainview/index.html +5 -6
  16. package/templates/hello-world/src/mainview/index.ts +1 -5
  17. package/templates/interactive-playground/README.md +26 -0
  18. package/templates/interactive-playground/assets/tray-icon.png +0 -0
  19. package/templates/interactive-playground/electrobun.config.ts +36 -0
  20. package/templates/interactive-playground/package-lock.json +36 -0
  21. package/templates/interactive-playground/package.json +15 -0
  22. package/templates/interactive-playground/src/bun/demos/files.ts +70 -0
  23. package/templates/interactive-playground/src/bun/demos/menus.ts +139 -0
  24. package/templates/interactive-playground/src/bun/demos/rpc.ts +83 -0
  25. package/templates/interactive-playground/src/bun/demos/system.ts +72 -0
  26. package/templates/interactive-playground/src/bun/demos/updates.ts +105 -0
  27. package/templates/interactive-playground/src/bun/demos/windows.ts +90 -0
  28. package/templates/interactive-playground/src/bun/index.ts +124 -0
  29. package/templates/interactive-playground/src/bun/types/rpc.ts +109 -0
  30. package/templates/interactive-playground/src/mainview/components/EventLog.ts +107 -0
  31. package/templates/interactive-playground/src/mainview/components/Sidebar.ts +65 -0
  32. package/templates/interactive-playground/src/mainview/components/Toast.ts +57 -0
  33. package/templates/interactive-playground/src/mainview/demos/FileDemo.ts +211 -0
  34. package/templates/interactive-playground/src/mainview/demos/MenuDemo.ts +102 -0
  35. package/templates/interactive-playground/src/mainview/demos/RPCDemo.ts +229 -0
  36. package/templates/interactive-playground/src/mainview/demos/TrayDemo.ts +132 -0
  37. package/templates/interactive-playground/src/mainview/demos/WebViewDemo.ts +411 -0
  38. package/templates/interactive-playground/src/mainview/demos/WindowDemo.ts +207 -0
  39. package/templates/interactive-playground/src/mainview/index.css +538 -0
  40. package/templates/interactive-playground/src/mainview/index.html +103 -0
  41. package/templates/interactive-playground/src/mainview/index.ts +238 -0
  42. package/templates/multitab-browser/README.md +34 -0
  43. package/templates/multitab-browser/bun.lock +224 -0
  44. package/templates/multitab-browser/electrobun.config.ts +32 -0
  45. package/templates/multitab-browser/package-lock.json +20 -0
  46. package/templates/multitab-browser/package.json +12 -0
  47. package/templates/multitab-browser/src/bun/index.ts +144 -0
  48. package/templates/multitab-browser/src/bun/tabManager.ts +200 -0
  49. package/templates/multitab-browser/src/bun/types/rpc.ts +78 -0
  50. package/templates/multitab-browser/src/mainview/index.css +487 -0
  51. package/templates/multitab-browser/src/mainview/index.html +94 -0
  52. package/templates/multitab-browser/src/mainview/index.ts +634 -0
  53. package/templates/photo-booth/README.md +108 -0
  54. package/templates/photo-booth/bun.lock +239 -0
  55. package/templates/photo-booth/electrobun.config.ts +28 -0
  56. package/templates/photo-booth/package.json +16 -0
  57. package/templates/photo-booth/src/bun/index.ts +92 -0
  58. package/templates/photo-booth/src/mainview/index.css +465 -0
  59. package/templates/photo-booth/src/mainview/index.html +124 -0
  60. package/templates/photo-booth/src/mainview/index.ts +499 -0
  61. package/tests/bun.lock +14 -0
  62. package/tests/electrobun.config.ts +45 -0
  63. package/tests/package-lock.json +36 -0
  64. package/tests/package.json +13 -0
  65. package/tests/src/bun/index.ts +100 -0
  66. package/tests/src/bun/test-runner.ts +508 -0
  67. package/tests/src/mainview/index.html +110 -0
  68. package/tests/src/mainview/index.ts +458 -0
  69. package/tests/src/mainview/styles/main.css +451 -0
  70. package/tests/src/testviews/tray-test.html +57 -0
  71. package/tests/src/testviews/webview-mask.html +114 -0
  72. package/tests/src/testviews/webview-navigation.html +36 -0
  73. package/tests/src/testviews/window-create.html +17 -0
  74. package/tests/src/testviews/window-events.html +29 -0
  75. package/tests/src/testviews/window-focus.html +37 -0
  76. package/tests/src/webviewtag/index.ts +11 -0
  77. package/templates/hello-world/electrobun.config +0 -18
@@ -0,0 +1,144 @@
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");
@@ -0,0 +1,200 @@
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
+ }
@@ -0,0 +1,78 @@
1
+ import type { RPCSchema } from "electrobun/bun";
2
+
3
+ export type Tab = {
4
+ id: string;
5
+ title: string;
6
+ url: string;
7
+ canGoBack: boolean;
8
+ canGoForward: boolean;
9
+ isLoading: boolean;
10
+ favicon?: string;
11
+ };
12
+
13
+ export type Bookmark = {
14
+ id: string;
15
+ title: string;
16
+ url: string;
17
+ createdAt: number;
18
+ };
19
+
20
+ export type BrowserRPC = {
21
+ bun: RPCSchema<{
22
+ requests: {
23
+ createTab: {
24
+ params: { url?: string };
25
+ response: Tab;
26
+ };
27
+ closeTab: {
28
+ params: { id: string };
29
+ response: void;
30
+ };
31
+ navigateTo: {
32
+ params: { tabId: string; url: string };
33
+ response: void;
34
+ };
35
+ goBack: {
36
+ params: { tabId: string };
37
+ response: void;
38
+ };
39
+ goForward: {
40
+ params: { tabId: string };
41
+ response: void;
42
+ };
43
+ reload: {
44
+ params: { tabId: string };
45
+ response: void;
46
+ };
47
+ getTabInfo: {
48
+ params: { tabId: string };
49
+ response: Tab;
50
+ };
51
+ getAllTabs: {
52
+ params: {};
53
+ response: Tab[];
54
+ };
55
+ addBookmark: {
56
+ params: { title: string; url: string };
57
+ response: Bookmark;
58
+ };
59
+ getBookmarks: {
60
+ params: {};
61
+ response: Bookmark[];
62
+ };
63
+ removeBookmark: {
64
+ params: { id: string };
65
+ response: void;
66
+ };
67
+ };
68
+ messages: {
69
+ tabUpdated: Tab;
70
+ tabClosed: { id: string };
71
+ loadingStateChanged: { tabId: string; isLoading: boolean };
72
+ };
73
+ }>;
74
+ webview: RPCSchema<{
75
+ requests: {};
76
+ messages: {};
77
+ }>;
78
+ };