bunite-core 0.0.1

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.
@@ -0,0 +1,322 @@
1
+ import type { Pointer } from "bun:ffi";
2
+ import { BuniteEvent } from "../events/event";
3
+ import { buniteEventEmitter } from "../events/eventEmitter";
4
+ import { ensureNativeRuntime, getNativeLibrary, toCString } from "../proc/native";
5
+ import { BrowserView, type BrowserViewOptions } from "./BrowserView";
6
+ import type { RPCWithTransport } from "../../shared/rpc";
7
+ import { getNextWindowId } from "./windowIds";
8
+ import { resolveDefaultViewsRoot } from "../../shared/paths";
9
+
10
+ export type WindowOptionsType<T = undefined> = {
11
+ title: string;
12
+ frame: {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ maximized?: boolean;
18
+ minimized?: boolean;
19
+ };
20
+ url: string | null;
21
+ html: string | null;
22
+ preload: string | null;
23
+ viewsRoot: string | null;
24
+ rpc?: T;
25
+ titleBarStyle: "hidden" | "hiddenInset" | "default";
26
+ transparent: boolean;
27
+ hidden?: boolean;
28
+ navigationRules: string[] | null;
29
+ sandbox: boolean;
30
+ };
31
+
32
+ const defaultOptions: WindowOptionsType = {
33
+ title: "bunite",
34
+ frame: {
35
+ x: 80,
36
+ y: 80,
37
+ width: 1280,
38
+ height: 900
39
+ },
40
+ url: null,
41
+ html: null,
42
+ preload: null,
43
+ viewsRoot: null,
44
+ titleBarStyle: "default",
45
+ transparent: false,
46
+ hidden: false,
47
+ navigationRules: null,
48
+ sandbox: false
49
+ };
50
+
51
+ const BrowserWindowMap: Record<number, BrowserWindow<any>> = {};
52
+
53
+ export class BrowserWindow<T extends RPCWithTransport = RPCWithTransport> {
54
+ id = getNextWindowId();
55
+ ptr: Pointer | null = null;
56
+ title: string;
57
+ frame: WindowOptionsType["frame"];
58
+ url: string | null;
59
+ html: string | null;
60
+ preload: string | null;
61
+ viewsRoot: string | null;
62
+ titleBarStyle: WindowOptionsType["titleBarStyle"];
63
+ transparent: boolean;
64
+ hidden: boolean;
65
+ navigationRules: string[] | null;
66
+ sandbox: boolean;
67
+ webviewId: number;
68
+ private closed = false;
69
+ private restoreMaximizedAfterMinimize = false;
70
+ private readonly handleNativeMove = (event: unknown) => {
71
+ const data = (event as {
72
+ data?: { x?: number; y?: number; maximized?: boolean; minimized?: boolean };
73
+ }).data;
74
+ if (!data) {
75
+ return;
76
+ }
77
+
78
+ this.frame = {
79
+ ...this.frame,
80
+ x: data.x ?? this.frame.x,
81
+ y: data.y ?? this.frame.y,
82
+ maximized: data.maximized ?? this.frame.maximized,
83
+ minimized: data.minimized ?? this.frame.minimized
84
+ };
85
+ };
86
+ private readonly handleNativeResize = (event: unknown) => {
87
+ const data = (event as {
88
+ data?: { x?: number; y?: number; width?: number; height?: number; maximized?: boolean; minimized?: boolean };
89
+ }).data;
90
+ if (!data) {
91
+ return;
92
+ }
93
+
94
+ this.frame = {
95
+ ...this.frame,
96
+ x: data.x ?? this.frame.x,
97
+ y: data.y ?? this.frame.y,
98
+ width: data.width ?? this.frame.width,
99
+ height: data.height ?? this.frame.height,
100
+ maximized: data.maximized ?? this.frame.maximized,
101
+ minimized: data.minimized ?? this.frame.minimized
102
+ };
103
+ };
104
+ private readonly handleNativeClose = () => {
105
+ if (this.closed) {
106
+ return;
107
+ }
108
+ this.closed = true;
109
+ this.ptr = null;
110
+ BrowserView.getById(this.webviewId)?.detachFromNative();
111
+ delete BrowserWindowMap[this.id];
112
+ buniteEventEmitter.off(`move-${this.id}`, this.handleNativeMove);
113
+ buniteEventEmitter.off(`resize-${this.id}`, this.handleNativeResize);
114
+ buniteEventEmitter.off(`close-${this.id}`, this.handleNativeClose);
115
+ };
116
+
117
+ constructor(options: Partial<WindowOptionsType<T>> = {}) {
118
+ ensureNativeRuntime();
119
+
120
+ this.title = options.title ?? defaultOptions.title;
121
+ this.frame = { ...defaultOptions.frame, ...options.frame };
122
+ this.url = options.url ?? defaultOptions.url;
123
+ this.html = options.html ?? defaultOptions.html;
124
+ this.preload = options.preload ?? defaultOptions.preload;
125
+ this.viewsRoot = options.viewsRoot ?? defaultOptions.viewsRoot ?? resolveDefaultViewsRoot();
126
+ this.titleBarStyle = options.titleBarStyle ?? defaultOptions.titleBarStyle;
127
+ this.transparent = options.transparent ?? defaultOptions.transparent;
128
+ this.hidden = options.hidden ?? defaultOptions.hidden!;
129
+ this.navigationRules = options.navigationRules ?? defaultOptions.navigationRules;
130
+ this.sandbox = options.sandbox ?? defaultOptions.sandbox;
131
+
132
+ const native = getNativeLibrary();
133
+ this.ptr =
134
+ native?.symbols.bunite_window_create(
135
+ this.id,
136
+ this.frame.x,
137
+ this.frame.y,
138
+ this.frame.width,
139
+ this.frame.height,
140
+ toCString(this.title),
141
+ toCString(this.titleBarStyle),
142
+ this.transparent,
143
+ this.hidden,
144
+ Boolean(this.frame.minimized),
145
+ Boolean(this.frame.maximized)
146
+ ) ?? null;
147
+
148
+ BrowserWindowMap[this.id] = this;
149
+ buniteEventEmitter.on(`move-${this.id}`, this.handleNativeMove);
150
+ buniteEventEmitter.on(`resize-${this.id}`, this.handleNativeResize);
151
+ buniteEventEmitter.on(`close-${this.id}`, this.handleNativeClose);
152
+
153
+ const webview = new BrowserView({
154
+ url: this.url,
155
+ html: this.html,
156
+ preload: this.preload,
157
+ viewsRoot: this.viewsRoot,
158
+ windowPtr: this.ptr,
159
+ frame: {
160
+ x: 0,
161
+ y: 0,
162
+ width: this.frame.width,
163
+ height: this.frame.height
164
+ },
165
+ rpc: options.rpc as BrowserViewOptions<T>["rpc"],
166
+ windowId: this.id,
167
+ navigationRules: this.navigationRules,
168
+ sandbox: this.sandbox
169
+ });
170
+
171
+ this.webviewId = webview.id;
172
+ }
173
+
174
+ static getById(id: number) {
175
+ return BrowserWindowMap[id];
176
+ }
177
+
178
+ get webview() {
179
+ return BrowserView.getById(this.webviewId) as BrowserView<T>;
180
+ }
181
+
182
+ show() {
183
+ this.hidden = false;
184
+ if (this.ptr) {
185
+ getNativeLibrary()?.symbols.bunite_window_show(this.ptr);
186
+ }
187
+ }
188
+
189
+ close() {
190
+ if (this.closed) {
191
+ return;
192
+ }
193
+ this.closed = true;
194
+ const hadNativePtr = Boolean(this.ptr);
195
+ if (this.ptr) {
196
+ getNativeLibrary()?.symbols.bunite_window_close(this.ptr);
197
+ this.ptr = null;
198
+ } else {
199
+ BrowserView.getById(this.webviewId)?.remove();
200
+ }
201
+ delete BrowserWindowMap[this.id];
202
+ buniteEventEmitter.off(`move-${this.id}`, this.handleNativeMove);
203
+ buniteEventEmitter.off(`resize-${this.id}`, this.handleNativeResize);
204
+ buniteEventEmitter.off(`close-${this.id}`, this.handleNativeClose);
205
+ if (!hadNativePtr) {
206
+ buniteEventEmitter.emitEvent(buniteEventEmitter.events.window.close({ id: this.id }), this.id);
207
+ }
208
+ }
209
+
210
+ maximize() {
211
+ if (!this.ptr) {
212
+ this.frame.maximized = true;
213
+ this.frame.minimized = false;
214
+ return;
215
+ }
216
+
217
+ const native = getNativeLibrary();
218
+ if (!native) {
219
+ return;
220
+ }
221
+
222
+ native.symbols.bunite_window_maximize(this.ptr);
223
+ this.frame.minimized = native.symbols.bunite_window_is_minimized(this.ptr);
224
+ this.frame.maximized = native.symbols.bunite_window_is_maximized(this.ptr);
225
+ }
226
+
227
+ unmaximize() {
228
+ if (!this.ptr) {
229
+ this.frame.maximized = false;
230
+ return;
231
+ }
232
+
233
+ const native = getNativeLibrary();
234
+ if (!native) {
235
+ return;
236
+ }
237
+
238
+ native.symbols.bunite_window_unmaximize(this.ptr);
239
+ this.frame.minimized = native.symbols.bunite_window_is_minimized(this.ptr);
240
+ this.frame.maximized = native.symbols.bunite_window_is_maximized(this.ptr);
241
+ }
242
+
243
+ isMaximized() {
244
+ if (!this.ptr) {
245
+ return Boolean(this.frame.maximized);
246
+ }
247
+
248
+ const maximized = getNativeLibrary()?.symbols.bunite_window_is_maximized(this.ptr) ?? false;
249
+ this.frame.maximized = maximized;
250
+ return maximized;
251
+ }
252
+
253
+ minimize() {
254
+ if (!this.ptr) {
255
+ this.restoreMaximizedAfterMinimize = Boolean(this.frame.maximized);
256
+ this.frame.minimized = true;
257
+ this.frame.maximized = false;
258
+ return;
259
+ }
260
+
261
+ const native = getNativeLibrary();
262
+ if (!native) {
263
+ return;
264
+ }
265
+
266
+ native.symbols.bunite_window_minimize(this.ptr);
267
+ this.frame.minimized = native.symbols.bunite_window_is_minimized(this.ptr);
268
+ this.frame.maximized = native.symbols.bunite_window_is_maximized(this.ptr);
269
+ }
270
+
271
+ unminimize() {
272
+ if (!this.ptr) {
273
+ this.frame.minimized = false;
274
+ this.frame.maximized = this.restoreMaximizedAfterMinimize;
275
+ this.restoreMaximizedAfterMinimize = false;
276
+ return;
277
+ }
278
+
279
+ const native = getNativeLibrary();
280
+ if (!native) {
281
+ return;
282
+ }
283
+
284
+ native.symbols.bunite_window_unminimize(this.ptr);
285
+ this.frame.minimized = native.symbols.bunite_window_is_minimized(this.ptr);
286
+ this.frame.maximized = native.symbols.bunite_window_is_maximized(this.ptr);
287
+ }
288
+
289
+ isMinimized() {
290
+ if (!this.ptr) {
291
+ return Boolean(this.frame.minimized);
292
+ }
293
+
294
+ const minimized = getNativeLibrary()?.symbols.bunite_window_is_minimized(this.ptr) ?? false;
295
+ this.frame.minimized = minimized;
296
+ return minimized;
297
+ }
298
+
299
+ setTitle(title: string) {
300
+ this.title = title;
301
+ if (this.ptr) {
302
+ getNativeLibrary()?.symbols.bunite_window_set_title(this.ptr, toCString(title));
303
+ }
304
+ }
305
+
306
+ setFrame(x: number, y: number, width: number, height: number) {
307
+ this.frame = { ...this.frame, x, y, width, height };
308
+ if (this.ptr) {
309
+ getNativeLibrary()?.symbols.bunite_window_set_frame(this.ptr, x, y, width, height);
310
+ }
311
+ }
312
+
313
+ getFrame() {
314
+ return this.frame;
315
+ }
316
+
317
+ on(name: "close" | "focus" | "blur" | "move" | "resize", handler: (event: unknown) => void) {
318
+ const specificName = `${name}-${this.id}`;
319
+ buniteEventEmitter.on(specificName, handler);
320
+ return () => buniteEventEmitter.off(specificName, handler);
321
+ }
322
+ }
@@ -0,0 +1,186 @@
1
+ import type { Server, ServerWebSocket } from "bun";
2
+ import type { BrowserView } from "./BrowserView";
3
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
4
+ import type { RPCPacket, RPCRequestPacket } from "../../shared/rpc";
5
+ import type { GlobalIPCHandler } from "./App";
6
+ import {
7
+ asUint8Array,
8
+ createEncryptedRPCFrame,
9
+ decodeRPCPacket,
10
+ encodeRPCPacket,
11
+ parseEncryptedRPCFrame
12
+ } from "../../shared/rpcWire";
13
+ import { RPC_AUTH_TAG_LENGTH } from "../../shared/rpcWireConstants";
14
+
15
+ type ViewRegistry = {
16
+ getById(id: number): BrowserView | undefined;
17
+ };
18
+
19
+ type WebSocketData = {
20
+ webviewId: number;
21
+ };
22
+
23
+ let rpcServer: Server<WebSocketData> | null = null;
24
+ let rpcPort = 0;
25
+
26
+ const socketMap: Record<number, ServerWebSocket<WebSocketData> | null> = {};
27
+ let registry: ViewRegistry | null = null;
28
+ let globalIPCResolver: ((channel: string) => GlobalIPCHandler | undefined) | null = null;
29
+
30
+ export function attachGlobalIPCResolver(resolver: (channel: string) => GlobalIPCHandler | undefined) {
31
+ globalIPCResolver = resolver;
32
+ }
33
+
34
+ function encrypt(secretKey: Uint8Array, payload: Uint8Array) {
35
+ const iv = new Uint8Array(randomBytes(12));
36
+ const cipher = createCipheriv("aes-256-gcm", secretKey, iv);
37
+ const encrypted = Buffer.concat([cipher.update(payload), cipher.final(), cipher.getAuthTag()]);
38
+ return createEncryptedRPCFrame(iv, new Uint8Array(encrypted));
39
+ }
40
+
41
+ function decrypt(secretKey: Uint8Array, frame: Uint8Array) {
42
+ const { iv, encryptedPayload } = parseEncryptedRPCFrame(frame);
43
+ const ciphertext = encryptedPayload.subarray(0, encryptedPayload.byteLength - RPC_AUTH_TAG_LENGTH);
44
+ const tag = encryptedPayload.subarray(encryptedPayload.byteLength - RPC_AUTH_TAG_LENGTH);
45
+ const decipher = createDecipheriv("aes-256-gcm", secretKey, iv);
46
+ decipher.setAuthTag(tag);
47
+ return new Uint8Array(Buffer.concat([decipher.update(ciphertext), decipher.final()]));
48
+ }
49
+
50
+ function normalizeIncomingBinaryMessage(
51
+ message: string | ArrayBuffer | Uint8Array | Buffer
52
+ ): Uint8Array | null {
53
+ if (typeof message === "string") {
54
+ return null;
55
+ }
56
+ return asUint8Array(message);
57
+ }
58
+
59
+ export function attachBrowserViewRegistry(nextRegistry: ViewRegistry) {
60
+ registry = nextRegistry;
61
+ }
62
+
63
+ export function ensureRPCServer() {
64
+ if (rpcServer) {
65
+ return { rpcServer, rpcPort };
66
+ }
67
+
68
+ let port = 45000;
69
+ while (port <= 65535) {
70
+ try {
71
+ rpcServer = Bun.serve<WebSocketData>({
72
+ hostname: "127.0.0.1",
73
+ port,
74
+ fetch(req, server) {
75
+ const url = new URL(req.url);
76
+ if (url.pathname !== "/socket") {
77
+ return new Response("Not found", { status: 404 });
78
+ }
79
+
80
+ const webviewId = Number(url.searchParams.get("webviewId"));
81
+ if (!Number.isFinite(webviewId)) {
82
+ return new Response("Missing webviewId", { status: 400 });
83
+ }
84
+ if (!registry?.getById(webviewId)) {
85
+ return new Response("Unknown webviewId", { status: 403 });
86
+ }
87
+
88
+ const upgraded = server.upgrade(req, {
89
+ data: { webviewId }
90
+ });
91
+ return upgraded ? undefined : new Response("Upgrade failed", { status: 500 });
92
+ },
93
+ websocket: {
94
+ open(ws) {
95
+ socketMap[ws.data.webviewId] = ws;
96
+ },
97
+ close(ws) {
98
+ socketMap[ws.data.webviewId] = null;
99
+ },
100
+ message(ws, message) {
101
+ const view = registry?.getById(ws.data.webviewId);
102
+ const binaryMessage = normalizeIncomingBinaryMessage(message);
103
+ if (!view || !binaryMessage) {
104
+ return;
105
+ }
106
+ try {
107
+ const decryptedMessage = decrypt(view.secretKey, binaryMessage);
108
+ const packet = decodeRPCPacket(decryptedMessage);
109
+
110
+ if (packet.type === "request" && (packet as RPCRequestPacket).scope === "global") {
111
+ void handleGlobalIPC(packet as RPCRequestPacket, ws.data.webviewId);
112
+ return;
113
+ }
114
+
115
+ view.handleIncomingRPC(packet);
116
+ } catch (error) {
117
+ console.error("[bunite] Failed to parse RPC payload", error);
118
+ }
119
+ }
120
+ }
121
+ });
122
+ rpcPort = port;
123
+ break;
124
+ } catch (error: any) {
125
+ if (error?.code === "EADDRINUSE") {
126
+ port += 1;
127
+ continue;
128
+ }
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ if (!rpcServer) {
134
+ throw new Error("Could not start bunite RPC server.");
135
+ }
136
+
137
+ return { rpcServer, rpcPort };
138
+ }
139
+
140
+ export function getRPCPort(): number {
141
+ return rpcPort;
142
+ }
143
+
144
+ async function handleGlobalIPC(packet: RPCRequestPacket, viewId: number) {
145
+ const handler = globalIPCResolver?.(packet.method);
146
+ if (!handler) {
147
+ sendMessageToView(viewId, {
148
+ type: "response",
149
+ id: packet.id,
150
+ success: false,
151
+ error: `No handler registered for: ${packet.method}`,
152
+ scope: "global"
153
+ });
154
+ return;
155
+ }
156
+ try {
157
+ const result = await handler(packet.params, { viewId });
158
+ sendMessageToView(viewId, {
159
+ type: "response",
160
+ id: packet.id,
161
+ success: true,
162
+ payload: result,
163
+ scope: "global"
164
+ });
165
+ } catch (error) {
166
+ sendMessageToView(viewId, {
167
+ type: "response",
168
+ id: packet.id,
169
+ success: false,
170
+ error: error instanceof Error ? error.message : String(error),
171
+ scope: "global"
172
+ });
173
+ }
174
+ }
175
+
176
+ export function sendMessageToView(viewId: number, message: RPCPacket): boolean {
177
+ const socket = socketMap[viewId];
178
+ const view = registry?.getById(viewId);
179
+ if (!socket || socket.readyState !== WebSocket.OPEN || !view) {
180
+ return false;
181
+ }
182
+
183
+ const encrypted = encrypt(view.secretKey, encodeRPCPacket(message));
184
+ socket.send(encrypted);
185
+ return true;
186
+ }
@@ -0,0 +1,72 @@
1
+ import { buniteEventEmitter } from "../events/eventEmitter";
2
+ import {
3
+ cancelBrowserMessageBoxRequest,
4
+ ensureNativeRuntime,
5
+ getNativeLibrary,
6
+ requestBrowserMessageBox,
7
+ showNativeMessageBox
8
+ } from "../proc/native";
9
+
10
+ export type MessageBoxOptions = {
11
+ type?: "none" | "info" | "warning" | "error" | "question";
12
+ title?: string;
13
+ message?: string;
14
+ detail?: string;
15
+ buttons?: string[];
16
+ defaultId?: number;
17
+ cancelId?: number;
18
+ };
19
+
20
+ export type MessageBoxResponse = {
21
+ response: number;
22
+ };
23
+
24
+ export async function showMessageBox(
25
+ options: MessageBoxOptions = {}
26
+ ): Promise<MessageBoxResponse> {
27
+ ensureNativeRuntime();
28
+
29
+ if (!getNativeLibrary()) {
30
+ console.warn(
31
+ "[bunite] Utils.showMessageBox() requires the native runtime. Returning a stub response."
32
+ );
33
+ return {
34
+ response: options.cancelId ?? options.defaultId ?? 0
35
+ };
36
+ }
37
+
38
+ const requestId = requestBrowserMessageBox(options);
39
+ if (requestId > 0) {
40
+ const response = await new Promise<number>((resolve) => {
41
+ const fallbackResponse = options.cancelId ?? options.defaultId ?? 0;
42
+ const handleResponse = (event: unknown) => {
43
+ const data = (event as { data?: { requestId?: number; response?: number } }).data;
44
+ if (!data || data.requestId !== requestId) {
45
+ return;
46
+ }
47
+
48
+ clearTimeout(timeoutId);
49
+ buniteEventEmitter.off("message-box-response", handleResponse);
50
+ resolve(
51
+ typeof data.response === "number" && data.response >= 0
52
+ ? data.response
53
+ : fallbackResponse
54
+ );
55
+ };
56
+
57
+ const timeoutId = setTimeout(() => {
58
+ buniteEventEmitter.off("message-box-response", handleResponse);
59
+ cancelBrowserMessageBoxRequest(requestId);
60
+ resolve(fallbackResponse);
61
+ }, 15_000);
62
+
63
+ buniteEventEmitter.on("message-box-response", handleResponse);
64
+ });
65
+
66
+ return { response };
67
+ }
68
+
69
+ return {
70
+ response: showNativeMessageBox(options)
71
+ };
72
+ }
@@ -0,0 +1,7 @@
1
+ let nextWindowId = 1;
2
+
3
+ export function getNextWindowId() {
4
+ const id = nextWindowId;
5
+ nextWindowId += 1;
6
+ return id;
7
+ }
@@ -0,0 +1,7 @@
1
+ import { BuniteEvent } from "./event";
2
+
3
+ export default {
4
+ ready: (data: Record<string, unknown>) => new BuniteEvent("ready", data),
5
+ beforeQuit: (data: Record<string, unknown>) =>
6
+ new BuniteEvent<Record<string, unknown>, { allow?: boolean }>("before-quit", data)
7
+ };
@@ -0,0 +1,20 @@
1
+ export class BuniteEvent<Data = unknown, Response = unknown> {
2
+ name: string;
3
+ data: Data;
4
+ private _response: Response | undefined;
5
+ responseWasSet = false;
6
+
7
+ constructor(name: string, data: Data) {
8
+ this.name = name;
9
+ this.data = data;
10
+ }
11
+
12
+ get response(): Response | undefined {
13
+ return this._response;
14
+ }
15
+
16
+ set response(value: Response) {
17
+ this._response = value;
18
+ this.responseWasSet = true;
19
+ }
20
+ }
@@ -0,0 +1,28 @@
1
+ import EventEmitter from "node:events";
2
+ import { BuniteEvent } from "./event";
3
+ import appEvents from "./appEvents";
4
+ import windowEvents from "./windowEvents";
5
+ import webviewEvents from "./webviewEvents";
6
+
7
+ class BuniteEventEmitter extends EventEmitter {
8
+ emitEvent(event: BuniteEvent, specifier?: string | number) {
9
+ if (specifier !== undefined) {
10
+ this.emit(`${event.name}-${specifier}`, event);
11
+ }
12
+ this.emit(event.name, event);
13
+ }
14
+
15
+ events = {
16
+ app: {
17
+ ...appEvents
18
+ },
19
+ window: {
20
+ ...windowEvents
21
+ },
22
+ webview: {
23
+ ...webviewEvents
24
+ }
25
+ };
26
+ }
27
+
28
+ export const buniteEventEmitter = new BuniteEventEmitter();
@@ -0,0 +1,13 @@
1
+ import { BuniteEvent } from "./event";
2
+
3
+ export default {
4
+ willNavigate: (data: { detail: string }) => new BuniteEvent("will-navigate", data),
5
+ didNavigate: (data: { detail: string }) => new BuniteEvent("did-navigate", data),
6
+ domReady: (data: { detail: string }) => new BuniteEvent("dom-ready", data),
7
+ newWindowOpen: (data: { detail: string | { url: string } }) =>
8
+ new BuniteEvent("new-window-open", data),
9
+ permissionRequested: (data: { requestId: number; kind: number; url?: string }) =>
10
+ new BuniteEvent("permission-requested", data),
11
+ messageBoxResponse: (data: { requestId: number; response: number }) =>
12
+ new BuniteEvent("message-box-response", data)
13
+ };
@@ -0,0 +1,19 @@
1
+ import { BuniteEvent } from "./event";
2
+
3
+ export default {
4
+ close: (data: { id: number }) => new BuniteEvent("close", data),
5
+ focus: (data: { id: number }) => new BuniteEvent("focus", data),
6
+ blur: (data: { id: number }) => new BuniteEvent("blur", data),
7
+ move: (data: { id: number; x: number; y: number; maximized: boolean; minimized: boolean }) =>
8
+ new BuniteEvent("move", data),
9
+ resize: (data: {
10
+ id: number;
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ maximized: boolean;
16
+ minimized: boolean;
17
+ }) =>
18
+ new BuniteEvent("resize", data)
19
+ };