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,666 @@
1
+ import { CString, dlopen, FFIType, JSCallback, ptr, type Pointer } from "bun:ffi";
2
+ import { existsSync } from "node:fs";
3
+ import { delimiter, join } from "node:path";
4
+ import { buniteEventEmitter } from "../events/eventEmitter";
5
+ import { resolveNativeArtifacts, type ResolvedNativeArtifacts } from "../../shared/paths";
6
+
7
+ export type NativeBootstrapOptions = {
8
+ allowStub?: boolean;
9
+ hideConsole?: boolean;
10
+ popupBlocking?: boolean;
11
+ chromiumFlags?: Record<string, string | boolean>;
12
+ };
13
+
14
+ export type NativeRuntimeState = {
15
+ initialized: boolean;
16
+ usingStub: boolean;
17
+ nativeLoaded: boolean;
18
+ artifacts: ResolvedNativeArtifacts;
19
+ };
20
+
21
+ type CStringPointer = Pointer;
22
+
23
+ type NativeSymbols = {
24
+ bunite_init: (
25
+ processHelperPath: CStringPointer,
26
+ cefDir: CStringPointer,
27
+ hideConsole: boolean,
28
+ popupBlocking: boolean,
29
+ chromiumFlagsJson: CStringPointer
30
+ ) => boolean;
31
+ bunite_run_loop: () => void;
32
+ bunite_quit: () => void;
33
+ bunite_free_cstring: (value: Pointer) => void;
34
+ bunite_window_create: (
35
+ windowId: number,
36
+ x: number,
37
+ y: number,
38
+ width: number,
39
+ height: number,
40
+ title: CStringPointer,
41
+ titleBarStyle: CStringPointer,
42
+ transparent: boolean,
43
+ hidden: boolean,
44
+ minimized: boolean,
45
+ maximized: boolean
46
+ ) => Pointer;
47
+ bunite_window_show: (windowPtr: Pointer) => void;
48
+ bunite_window_close: (windowPtr: Pointer) => void;
49
+ bunite_window_set_title: (windowPtr: Pointer, title: CStringPointer) => void;
50
+ bunite_window_minimize: (windowPtr: Pointer) => void;
51
+ bunite_window_unminimize: (windowPtr: Pointer) => void;
52
+ bunite_window_is_minimized: (windowPtr: Pointer) => boolean;
53
+ bunite_window_maximize: (windowPtr: Pointer) => void;
54
+ bunite_window_unmaximize: (windowPtr: Pointer) => void;
55
+ bunite_window_is_maximized: (windowPtr: Pointer) => boolean;
56
+ bunite_window_set_frame: (
57
+ windowPtr: Pointer,
58
+ x: number,
59
+ y: number,
60
+ width: number,
61
+ height: number
62
+ ) => void;
63
+ bunite_view_create: (
64
+ viewId: number,
65
+ windowPtr: Pointer | null,
66
+ url: CStringPointer,
67
+ html: CStringPointer,
68
+ preload: CStringPointer,
69
+ viewsRoot: CStringPointer,
70
+ navigationRulesJson: CStringPointer,
71
+ x: number,
72
+ y: number,
73
+ width: number,
74
+ height: number,
75
+ autoResize: boolean,
76
+ sandbox: boolean
77
+ ) => Pointer;
78
+ bunite_register_view_route: (path: CStringPointer) => void;
79
+ bunite_unregister_view_route: (path: CStringPointer) => void;
80
+ bunite_complete_route_request: (requestId: number, html: CStringPointer) => void;
81
+ bunite_view_set_visible: (viewPtr: Pointer, visible: boolean) => void;
82
+ bunite_view_set_bounds: (viewPtr: Pointer, x: number, y: number, width: number, height: number) => void;
83
+ bunite_view_set_anchor: (viewPtr: Pointer, mode: number, inset: number) => void;
84
+ bunite_view_go_back: (viewPtr: Pointer) => void;
85
+ bunite_view_reload: (viewPtr: Pointer) => void;
86
+ bunite_view_load_url: (viewPtr: Pointer, url: CStringPointer) => void;
87
+ bunite_view_load_html: (viewPtr: Pointer, html: CStringPointer) => void;
88
+ bunite_view_remove: (viewPtr: Pointer) => void;
89
+ bunite_view_open_devtools: (viewPtr: Pointer) => void;
90
+ bunite_view_close_devtools: (viewPtr: Pointer) => void;
91
+ bunite_view_toggle_devtools: (viewPtr: Pointer) => void;
92
+ bunite_complete_permission_request: (requestId: number, state: number) => void;
93
+ bunite_show_message_box: (
94
+ type: CStringPointer,
95
+ title: CStringPointer,
96
+ message: CStringPointer,
97
+ detail: CStringPointer,
98
+ buttons: CStringPointer,
99
+ defaultId: number,
100
+ cancelId: number
101
+ ) => number;
102
+ bunite_show_browser_message_box: (
103
+ type: CStringPointer,
104
+ title: CStringPointer,
105
+ message: CStringPointer,
106
+ detail: CStringPointer,
107
+ buttons: CStringPointer,
108
+ defaultId: number,
109
+ cancelId: number
110
+ ) => number;
111
+ bunite_cancel_browser_message_box: (requestId: number) => void;
112
+ bunite_set_webview_event_handler: (handler: JSCallback) => void;
113
+ bunite_set_window_event_handler: (handler: JSCallback) => void;
114
+ };
115
+
116
+ type LoadedNativeLibrary = {
117
+ symbols: NativeSymbols;
118
+ };
119
+
120
+ const messageBoxButtonSeparator = "\x1f";
121
+ const unsetCancelId = -1;
122
+
123
+ const nativeSymbolDefinitions = {
124
+ bunite_init: {
125
+ args: [FFIType.cstring, FFIType.cstring, FFIType.bool, FFIType.bool, FFIType.cstring],
126
+ returns: FFIType.bool
127
+ },
128
+ bunite_run_loop: {
129
+ args: [],
130
+ returns: FFIType.void
131
+ },
132
+ bunite_quit: {
133
+ args: [],
134
+ returns: FFIType.void
135
+ },
136
+ bunite_free_cstring: {
137
+ args: [FFIType.ptr],
138
+ returns: FFIType.void
139
+ },
140
+ bunite_window_create: {
141
+ args: [
142
+ FFIType.u32,
143
+ FFIType.f64,
144
+ FFIType.f64,
145
+ FFIType.f64,
146
+ FFIType.f64,
147
+ FFIType.cstring,
148
+ FFIType.cstring,
149
+ FFIType.bool,
150
+ FFIType.bool,
151
+ FFIType.bool,
152
+ FFIType.bool
153
+ ],
154
+ returns: FFIType.ptr
155
+ },
156
+ bunite_window_show: {
157
+ args: [FFIType.ptr],
158
+ returns: FFIType.void
159
+ },
160
+ bunite_window_close: {
161
+ args: [FFIType.ptr],
162
+ returns: FFIType.void
163
+ },
164
+ bunite_window_set_title: {
165
+ args: [FFIType.ptr, FFIType.cstring],
166
+ returns: FFIType.void
167
+ },
168
+ bunite_window_minimize: {
169
+ args: [FFIType.ptr],
170
+ returns: FFIType.void
171
+ },
172
+ bunite_window_unminimize: {
173
+ args: [FFIType.ptr],
174
+ returns: FFIType.void
175
+ },
176
+ bunite_window_is_minimized: {
177
+ args: [FFIType.ptr],
178
+ returns: FFIType.bool
179
+ },
180
+ bunite_window_maximize: {
181
+ args: [FFIType.ptr],
182
+ returns: FFIType.void
183
+ },
184
+ bunite_window_unmaximize: {
185
+ args: [FFIType.ptr],
186
+ returns: FFIType.void
187
+ },
188
+ bunite_window_is_maximized: {
189
+ args: [FFIType.ptr],
190
+ returns: FFIType.bool
191
+ },
192
+ bunite_window_set_frame: {
193
+ args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
194
+ returns: FFIType.void
195
+ },
196
+ bunite_view_create: {
197
+ args: [
198
+ FFIType.u32,
199
+ FFIType.ptr,
200
+ FFIType.cstring,
201
+ FFIType.cstring,
202
+ FFIType.cstring,
203
+ FFIType.cstring,
204
+ FFIType.cstring,
205
+ FFIType.f64,
206
+ FFIType.f64,
207
+ FFIType.f64,
208
+ FFIType.f64,
209
+ FFIType.bool,
210
+ FFIType.bool
211
+ ],
212
+ returns: FFIType.ptr
213
+ },
214
+ bunite_register_view_route: {
215
+ args: [FFIType.cstring],
216
+ returns: FFIType.void
217
+ },
218
+ bunite_unregister_view_route: {
219
+ args: [FFIType.cstring],
220
+ returns: FFIType.void
221
+ },
222
+ bunite_complete_route_request: {
223
+ args: [FFIType.u32, FFIType.cstring],
224
+ returns: FFIType.void
225
+ },
226
+ bunite_view_set_visible: {
227
+ args: [FFIType.ptr, FFIType.bool],
228
+ returns: FFIType.void
229
+ },
230
+ bunite_view_set_bounds: {
231
+ args: [FFIType.ptr, FFIType.f64, FFIType.f64, FFIType.f64, FFIType.f64],
232
+ returns: FFIType.void
233
+ },
234
+ bunite_view_set_anchor: {
235
+ args: [FFIType.ptr, FFIType.i32, FFIType.f64],
236
+ returns: FFIType.void
237
+ },
238
+ bunite_view_go_back: {
239
+ args: [FFIType.ptr],
240
+ returns: FFIType.void
241
+ },
242
+ bunite_view_reload: {
243
+ args: [FFIType.ptr],
244
+ returns: FFIType.void
245
+ },
246
+ bunite_view_load_url: {
247
+ args: [FFIType.ptr, FFIType.cstring],
248
+ returns: FFIType.void
249
+ },
250
+ bunite_view_load_html: {
251
+ args: [FFIType.ptr, FFIType.cstring],
252
+ returns: FFIType.void
253
+ },
254
+ bunite_view_remove: {
255
+ args: [FFIType.ptr],
256
+ returns: FFIType.void
257
+ },
258
+ bunite_view_open_devtools: {
259
+ args: [FFIType.ptr],
260
+ returns: FFIType.void
261
+ },
262
+ bunite_view_close_devtools: {
263
+ args: [FFIType.ptr],
264
+ returns: FFIType.void
265
+ },
266
+ bunite_view_toggle_devtools: {
267
+ args: [FFIType.ptr],
268
+ returns: FFIType.void
269
+ },
270
+ bunite_complete_permission_request: {
271
+ args: [FFIType.u32, FFIType.u32],
272
+ returns: FFIType.void
273
+ },
274
+ bunite_show_message_box: {
275
+ args: [
276
+ FFIType.cstring,
277
+ FFIType.cstring,
278
+ FFIType.cstring,
279
+ FFIType.cstring,
280
+ FFIType.cstring,
281
+ FFIType.i32,
282
+ FFIType.i32
283
+ ],
284
+ returns: FFIType.i32
285
+ },
286
+ bunite_show_browser_message_box: {
287
+ args: [
288
+ FFIType.cstring,
289
+ FFIType.cstring,
290
+ FFIType.cstring,
291
+ FFIType.cstring,
292
+ FFIType.cstring,
293
+ FFIType.i32,
294
+ FFIType.i32
295
+ ],
296
+ returns: FFIType.u32
297
+ },
298
+ bunite_cancel_browser_message_box: {
299
+ args: [FFIType.u32],
300
+ returns: FFIType.void
301
+ },
302
+ bunite_set_webview_event_handler: {
303
+ args: [FFIType.function],
304
+ returns: FFIType.void
305
+ },
306
+ bunite_set_window_event_handler: {
307
+ args: [FFIType.function],
308
+ returns: FFIType.void
309
+ }
310
+ } as const;
311
+
312
+ let state: NativeRuntimeState | null = null;
313
+ let nativeLibrary: LoadedNativeLibrary | null = null;
314
+ const retainedCStringBuffers: Buffer[] = [];
315
+ let webviewEventCallback: JSCallback | null = null;
316
+ let windowEventCallback: JSCallback | null = null;
317
+ let routeRequestHandler: ((requestId: number, path: string) => void) | null = null;
318
+
319
+ export function setRouteRequestHandler(handler: (requestId: number, path: string) => void) {
320
+ routeRequestHandler = handler;
321
+ }
322
+
323
+ export function toCString(value: string): CStringPointer {
324
+ const normalized = value.endsWith("\0") ? value : `${value}\0`;
325
+ const buffer = Buffer.from(normalized, "utf8");
326
+
327
+ // Keep recent CString buffers alive long enough for native code to copy them.
328
+ // This is not a long-term ownership model for retained native pointers, but it
329
+ // avoids immediate GC hazards across the current FFI call boundary.
330
+ retainedCStringBuffers.push(buffer);
331
+ if (retainedCStringBuffers.length > 1024) {
332
+ retainedCStringBuffers.shift();
333
+ }
334
+
335
+ return ptr(buffer);
336
+ }
337
+
338
+ function applyEnvironment(artifacts: ResolvedNativeArtifacts) {
339
+ const cefBinaryDir = artifacts.cefDir && existsSync(join(artifacts.cefDir, "Release", "libcef.dll"))
340
+ ? join(artifacts.cefDir, "Release")
341
+ : artifacts.cefDir;
342
+ const cefResourceDir = artifacts.cefDir && existsSync(join(artifacts.cefDir, "Resources", "resources.pak"))
343
+ ? join(artifacts.cefDir, "Resources")
344
+ : artifacts.cefDir;
345
+
346
+ if (cefResourceDir && !process.env.ICU_DATA) {
347
+ process.env.ICU_DATA = cefResourceDir;
348
+ }
349
+ if (cefBinaryDir) {
350
+ const pathEntries = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
351
+ if (!pathEntries.includes(cefBinaryDir)) {
352
+ process.env.PATH = [cefBinaryDir, ...pathEntries].join(delimiter);
353
+ }
354
+ }
355
+ }
356
+
357
+ function tryLoadNativeLibrary(artifacts: ResolvedNativeArtifacts) {
358
+ if (!artifacts.nativeLibPath || !existsSync(artifacts.nativeLibPath)) {
359
+ return null;
360
+ }
361
+
362
+ try {
363
+ const library = dlopen(artifacts.nativeLibPath, nativeSymbolDefinitions as any);
364
+ return {
365
+ symbols: library.symbols as unknown as NativeSymbols
366
+ } satisfies LoadedNativeLibrary;
367
+ } catch (error) {
368
+ console.warn("[bunite] Failed to load native library via FFI.", error);
369
+ return null;
370
+ }
371
+ }
372
+
373
+ function maybeParsePayload(payload: string) {
374
+ const trimmed = payload.trim();
375
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
376
+ return payload;
377
+ }
378
+
379
+ try {
380
+ return JSON.parse(trimmed);
381
+ } catch {
382
+ return payload;
383
+ }
384
+ }
385
+
386
+ function registerNativeCallbacks(library: LoadedNativeLibrary) {
387
+ if (!webviewEventCallback) {
388
+ webviewEventCallback = new JSCallback(
389
+ (viewId, eventNamePtr, payloadPtr) => {
390
+ const eventName = new CString(eventNamePtr).toString();
391
+ const payload = new CString(payloadPtr).toString();
392
+ nativeLibrary?.symbols.bunite_free_cstring(eventNamePtr as Pointer);
393
+ nativeLibrary?.symbols.bunite_free_cstring(payloadPtr as Pointer);
394
+
395
+ switch (eventName) {
396
+ case "will-navigate":
397
+ buniteEventEmitter.emitEvent(
398
+ buniteEventEmitter.events.webview.willNavigate({ detail: payload }),
399
+ viewId
400
+ );
401
+ break;
402
+ case "did-navigate":
403
+ buniteEventEmitter.emitEvent(
404
+ buniteEventEmitter.events.webview.didNavigate({ detail: payload }),
405
+ viewId
406
+ );
407
+ break;
408
+ case "dom-ready":
409
+ buniteEventEmitter.emitEvent(
410
+ buniteEventEmitter.events.webview.domReady({ detail: payload }),
411
+ viewId
412
+ );
413
+ break;
414
+ case "new-window-open":
415
+ buniteEventEmitter.emitEvent(
416
+ buniteEventEmitter.events.webview.newWindowOpen({
417
+ detail: maybeParsePayload(payload) as string | { url: string }
418
+ }),
419
+ viewId
420
+ );
421
+ break;
422
+ case "permission-requested":
423
+ buniteEventEmitter.emitEvent(
424
+ buniteEventEmitter.events.webview.permissionRequested(
425
+ maybeParsePayload(payload) as { requestId: number; kind: number; url?: string }
426
+ ),
427
+ viewId
428
+ );
429
+ break;
430
+ case "message-box-response":
431
+ buniteEventEmitter.emitEvent(
432
+ buniteEventEmitter.events.webview.messageBoxResponse(
433
+ maybeParsePayload(payload) as { requestId: number; response: number }
434
+ ),
435
+ viewId
436
+ );
437
+ break;
438
+ case "route-request": {
439
+ const parsed = maybeParsePayload(payload) as { requestId: number; path: string };
440
+ routeRequestHandler?.(parsed.requestId, parsed.path);
441
+ break;
442
+ }
443
+ }
444
+ },
445
+ {
446
+ args: [FFIType.u32, FFIType.cstring, FFIType.cstring],
447
+ returns: FFIType.void,
448
+ threadsafe: true
449
+ }
450
+ );
451
+ }
452
+
453
+ if (!windowEventCallback) {
454
+ windowEventCallback = new JSCallback(
455
+ (windowId, eventNamePtr, payloadPtr) => {
456
+ const eventName = new CString(eventNamePtr).toString();
457
+ const payload = new CString(payloadPtr).toString();
458
+ nativeLibrary?.symbols.bunite_free_cstring(eventNamePtr as Pointer);
459
+ nativeLibrary?.symbols.bunite_free_cstring(payloadPtr as Pointer);
460
+ const parsedPayload = maybeParsePayload(payload);
461
+
462
+ switch (eventName) {
463
+ case "close":
464
+ buniteEventEmitter.emitEvent(
465
+ buniteEventEmitter.events.window.close({ id: windowId }),
466
+ windowId
467
+ );
468
+ break;
469
+ case "focus":
470
+ buniteEventEmitter.emitEvent(
471
+ buniteEventEmitter.events.window.focus({ id: windowId }),
472
+ windowId
473
+ );
474
+ break;
475
+ case "blur":
476
+ buniteEventEmitter.emitEvent(
477
+ buniteEventEmitter.events.window.blur({ id: windowId }),
478
+ windowId
479
+ );
480
+ break;
481
+ case "move":
482
+ if (parsedPayload && typeof parsedPayload === "object") {
483
+ const { x = 0, y = 0, maximized = false, minimized = false } = parsedPayload as {
484
+ x?: number;
485
+ y?: number;
486
+ maximized?: boolean;
487
+ minimized?: boolean;
488
+ };
489
+ buniteEventEmitter.emitEvent(
490
+ buniteEventEmitter.events.window.move({ id: windowId, x, y, maximized, minimized }),
491
+ windowId
492
+ );
493
+ }
494
+ break;
495
+ case "resize":
496
+ if (parsedPayload && typeof parsedPayload === "object") {
497
+ const { x = 0, y = 0, width = 0, height = 0, maximized = false, minimized = false } = parsedPayload as {
498
+ x?: number;
499
+ y?: number;
500
+ width?: number;
501
+ height?: number;
502
+ maximized?: boolean;
503
+ minimized?: boolean;
504
+ };
505
+ buniteEventEmitter.emitEvent(
506
+ buniteEventEmitter.events.window.resize({
507
+ id: windowId,
508
+ x,
509
+ y,
510
+ width,
511
+ height,
512
+ maximized,
513
+ minimized
514
+ }),
515
+ windowId
516
+ );
517
+ }
518
+ break;
519
+ }
520
+ },
521
+ {
522
+ args: [FFIType.u32, FFIType.cstring, FFIType.cstring],
523
+ returns: FFIType.void,
524
+ threadsafe: true
525
+ }
526
+ );
527
+ }
528
+
529
+ library.symbols.bunite_set_webview_event_handler(webviewEventCallback);
530
+ library.symbols.bunite_set_window_event_handler(windowEventCallback);
531
+ }
532
+
533
+ export async function initNativeRuntime(
534
+ options: NativeBootstrapOptions = {}
535
+ ): Promise<NativeRuntimeState> {
536
+ if (state) {
537
+ return state;
538
+ }
539
+
540
+ const allowStub = options.allowStub ?? true;
541
+ const artifacts = resolveNativeArtifacts();
542
+ const hasNativeArtifacts = Boolean(
543
+ artifacts.nativeLibPath &&
544
+ artifacts.processHelperPath &&
545
+ existsSync(artifacts.nativeLibPath) &&
546
+ existsSync(artifacts.processHelperPath)
547
+ );
548
+
549
+ applyEnvironment(artifacts);
550
+
551
+ if (!hasNativeArtifacts && !allowStub) {
552
+ throw new Error(
553
+ "bunite native runtime packages are missing. Install platform packages or allow stub mode."
554
+ );
555
+ }
556
+
557
+ nativeLibrary = hasNativeArtifacts ? tryLoadNativeLibrary(artifacts) : null;
558
+
559
+ if (nativeLibrary) {
560
+ registerNativeCallbacks(nativeLibrary);
561
+ const chromiumFlagsJson = options.chromiumFlags
562
+ ? JSON.stringify(options.chromiumFlags)
563
+ : "";
564
+ const initOk = nativeLibrary.symbols.bunite_init(
565
+ toCString(artifacts.processHelperPath ?? ""),
566
+ toCString(artifacts.cefDir ?? ""),
567
+ options.hideConsole ?? false,
568
+ options.popupBlocking ?? false,
569
+ toCString(chromiumFlagsJson)
570
+ );
571
+
572
+ if (!initOk) {
573
+ nativeLibrary = null;
574
+ if (!allowStub) {
575
+ throw new Error("bunite native runtime failed to initialize.");
576
+ }
577
+ }
578
+ }
579
+
580
+ if (!nativeLibrary) {
581
+ console.warn(
582
+ "[bunite] Native runtime packages were not found or could not be loaded. Initializing in stub mode."
583
+ );
584
+ }
585
+
586
+ state = {
587
+ initialized: true,
588
+ usingStub: !nativeLibrary,
589
+ nativeLoaded: Boolean(nativeLibrary),
590
+ artifacts
591
+ };
592
+ return state;
593
+ }
594
+
595
+ export function getNativeRuntimeState(): NativeRuntimeState | null {
596
+ return state;
597
+ }
598
+
599
+ export function ensureNativeRuntime(): NativeRuntimeState {
600
+ if (!state) {
601
+ throw new Error("bunite app has not been initialized. Call await app.init() first.");
602
+ }
603
+ return state;
604
+ }
605
+
606
+ export function getNativeLibrary(): LoadedNativeLibrary | null {
607
+ return nativeLibrary;
608
+ }
609
+
610
+ export function completePermissionRequest(requestId: number, stateValue: number): void {
611
+ nativeLibrary?.symbols.bunite_complete_permission_request(requestId, stateValue);
612
+ }
613
+
614
+ export function showNativeMessageBox(params: {
615
+ type?: string;
616
+ title?: string;
617
+ message?: string;
618
+ detail?: string;
619
+ buttons?: string[];
620
+ defaultId?: number;
621
+ cancelId?: number;
622
+ }): number {
623
+ const native = getNativeLibrary();
624
+ if (!native) {
625
+ return params.cancelId ?? params.defaultId ?? 0;
626
+ }
627
+
628
+ return native.symbols.bunite_show_message_box(
629
+ toCString(params.type ?? "info"),
630
+ toCString(params.title ?? ""),
631
+ toCString(params.message ?? ""),
632
+ toCString(params.detail ?? ""),
633
+ toCString((params.buttons ?? ["OK"]).join(messageBoxButtonSeparator)),
634
+ params.defaultId ?? 0,
635
+ params.cancelId ?? unsetCancelId
636
+ );
637
+ }
638
+
639
+ export function requestBrowserMessageBox(params: {
640
+ type?: string;
641
+ title?: string;
642
+ message?: string;
643
+ detail?: string;
644
+ buttons?: string[];
645
+ defaultId?: number;
646
+ cancelId?: number;
647
+ }): number {
648
+ const native = getNativeLibrary();
649
+ if (!native) {
650
+ return 0;
651
+ }
652
+
653
+ return native.symbols.bunite_show_browser_message_box(
654
+ toCString(params.type ?? "info"),
655
+ toCString(params.title ?? ""),
656
+ toCString(params.message ?? ""),
657
+ toCString(params.detail ?? ""),
658
+ toCString((params.buttons ?? ["OK"]).join(messageBoxButtonSeparator)),
659
+ params.defaultId ?? 0,
660
+ params.cancelId ?? unsetCancelId
661
+ );
662
+ }
663
+
664
+ export function cancelBrowserMessageBoxRequest(requestId: number): void {
665
+ getNativeLibrary()?.symbols.bunite_cancel_browser_message_box(requestId);
666
+ }
@@ -0,0 +1,6 @@
1
+ #pragma once
2
+
3
+ #include <cstdint>
4
+
5
+ typedef void (*BuniteWebviewEventHandler)(uint32_t view_id, const char* event_name, const char* payload);
6
+ typedef void (*BuniteWindowEventHandler)(uint32_t window_id, const char* event_name, const char* payload);