bun-app-window 0.0.0-stage → 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 (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +400 -2
  3. package/README.zh-CN.md +399 -0
  4. package/README.zh-TW.md +399 -0
  5. package/dist/libshell.dll +0 -0
  6. package/native/build.ps1 +67 -0
  7. package/native/fetch.ps1 +66 -0
  8. package/native/shell.cc +2134 -0
  9. package/package.json +67 -5
  10. package/src/shell/assets.d.ts +16 -0
  11. package/src/shell/debug.ts +40 -0
  12. package/src/shell/embedded.ts +42 -0
  13. package/src/shell/index.ts +291 -0
  14. package/src/shell/ipc.ts +569 -0
  15. package/src/shell/native.ts +92 -0
  16. package/src/shell/port.ts +77 -0
  17. package/src/shell/resolve.ts +211 -0
  18. package/src/shell/state.ts +211 -0
  19. package/src/shell/types.ts +144 -0
  20. package/src/ui/titlebar.ts +436 -0
  21. package/vendor/webview/compatibility/CMakeLists.txt +3 -0
  22. package/vendor/webview/compatibility/mingw/CMakeLists.txt +14 -0
  23. package/vendor/webview/compatibility/mingw/include/EventToken.h +25 -0
  24. package/vendor/webview/core/CMakeLists.txt +94 -0
  25. package/vendor/webview/core/include/webview/api.h +251 -0
  26. package/vendor/webview/core/include/webview/backends.hh +40 -0
  27. package/vendor/webview/core/include/webview/c_api_impl.hh +258 -0
  28. package/vendor/webview/core/include/webview/detail/backends/cocoa_webkit.hh +626 -0
  29. package/vendor/webview/core/include/webview/detail/backends/gtk_webkitgtk.hh +355 -0
  30. package/vendor/webview/core/include/webview/detail/backends/win32_edge.hh +909 -0
  31. package/vendor/webview/core/include/webview/detail/basic_result.hh +119 -0
  32. package/vendor/webview/core/include/webview/detail/engine_base.hh +382 -0
  33. package/vendor/webview/core/include/webview/detail/exceptions.hh +42 -0
  34. package/vendor/webview/core/include/webview/detail/json.hh +337 -0
  35. package/vendor/webview/core/include/webview/detail/native_library.hh +170 -0
  36. package/vendor/webview/core/include/webview/detail/optional.hh +118 -0
  37. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSApplication.hh +107 -0
  38. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSBundle.hh +56 -0
  39. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSEvent.hh +71 -0
  40. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSInvocation.hh +66 -0
  41. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSMethodSignature.hh +52 -0
  42. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSNotification.hh +51 -0
  43. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSNumber.hh +53 -0
  44. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSObject.hh +51 -0
  45. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSOpenPanel.hh +71 -0
  46. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSPoint.hh +53 -0
  47. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSRect.hh +53 -0
  48. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSSavePanel.hh +52 -0
  49. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSSize.hh +53 -0
  50. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSString.hh +87 -0
  51. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSURL.hh +63 -0
  52. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSURLRequest.hh +52 -0
  53. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSValue.hh +56 -0
  54. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSView.hh +91 -0
  55. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/NSWindow.hh +137 -0
  56. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/cocoa.hh +59 -0
  57. package/vendor/webview/core/include/webview/detail/platform/darwin/cocoa/types.hh +49 -0
  58. package/vendor/webview/core/include/webview/detail/platform/darwin/objc/Class.hh +55 -0
  59. package/vendor/webview/core/include/webview/detail/platform/darwin/objc/autoreleasepool.hh +70 -0
  60. package/vendor/webview/core/include/webview/detail/platform/darwin/objc/invoke.hh +75 -0
  61. package/vendor/webview/core/include/webview/detail/platform/darwin/objc/memory.hh +57 -0
  62. package/vendor/webview/core/include/webview/detail/platform/darwin/objc/objc.hh +47 -0
  63. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKOpenPanelParameters.hh +57 -0
  64. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKScriptMessage.hh +51 -0
  65. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKUserContentController.hh +61 -0
  66. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKUserScript.hh +71 -0
  67. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKWebView.hh +108 -0
  68. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/WKWebViewConfiguration.hh +60 -0
  69. package/vendor/webview/core/include/webview/detail/platform/darwin/webkit/webkit.hh +46 -0
  70. package/vendor/webview/core/include/webview/detail/platform/linux/gtk/compat.hh +137 -0
  71. package/vendor/webview/core/include/webview/detail/platform/linux/webkitgtk/compat.hh +142 -0
  72. package/vendor/webview/core/include/webview/detail/platform/linux/webkitgtk/dmabuf.hh +167 -0
  73. package/vendor/webview/core/include/webview/detail/platform/windows/com_init_wrapper.hh +126 -0
  74. package/vendor/webview/core/include/webview/detail/platform/windows/dpi.hh +161 -0
  75. package/vendor/webview/core/include/webview/detail/platform/windows/dwmapi.hh +67 -0
  76. package/vendor/webview/core/include/webview/detail/platform/windows/iid.hh +78 -0
  77. package/vendor/webview/core/include/webview/detail/platform/windows/ntdll.hh +58 -0
  78. package/vendor/webview/core/include/webview/detail/platform/windows/reg_key.hh +135 -0
  79. package/vendor/webview/core/include/webview/detail/platform/windows/shcore.hh +59 -0
  80. package/vendor/webview/core/include/webview/detail/platform/windows/theme.hh +76 -0
  81. package/vendor/webview/core/include/webview/detail/platform/windows/user32.hh +90 -0
  82. package/vendor/webview/core/include/webview/detail/platform/windows/version.hh +151 -0
  83. package/vendor/webview/core/include/webview/detail/platform/windows/webview2/loader.hh +391 -0
  84. package/vendor/webview/core/include/webview/detail/user_script.hh +76 -0
  85. package/vendor/webview/core/include/webview/detail/utility/string.hh +99 -0
  86. package/vendor/webview/core/include/webview/errors.h +67 -0
  87. package/vendor/webview/core/include/webview/errors.hh +85 -0
  88. package/vendor/webview/core/include/webview/json_deprecated.hh +61 -0
  89. package/vendor/webview/core/include/webview/macros.h +123 -0
  90. package/vendor/webview/core/include/webview/types.h +81 -0
  91. package/vendor/webview/core/include/webview/types.hh +48 -0
  92. package/vendor/webview/core/include/webview/version.h +67 -0
  93. package/vendor/webview/core/include/webview/webview.h +32 -0
  94. package/vendor/webview/core/include/webview.h +15 -0
  95. package/vendor/webview/core/src/webview.cc +1 -0
  96. package/vendor/webview/core/tests/CMakeLists.txt +18 -0
  97. package/vendor/webview/core/tests/src/functional_tests.cc +316 -0
  98. package/vendor/webview/core/tests/src/unit_tests.cc +188 -0
  99. package/vendor/webview2/build/native/include/WebView2.h +23568 -0
  100. package/vendor/webview2/build/native/include/WebView2EnvironmentOptions.h +144 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dolores (TiancongLx)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,401 @@
1
- # Temporary Holding Version
1
+ # bun-app-window
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/bun-app-window?color=cb3837&logo=npm)](https://www.npmjs.com/package/bun-app-window)
4
+ [![Bun](https://img.shields.io/badge/Bun-%3E%3D1.4.3-f472b6?logo=bun&logoColor=white)](https://bun.sh)
5
+ [![Platform](https://img.shields.io/badge/Platform-Windows%20x64-0078D6?logo=windows&logoColor=white)](#requirements--design-boundaries)
6
+ [![WebView2](https://img.shields.io/badge/WebView2-System%20Runtime-008080)](#requirements--design-boundaries)
7
+ [![License](https://img.shields.io/badge/License-MIT-2ea44f)](./LICENSE)
8
+
9
+ English | [简体中文](./README.zh-CN.md) | [正體中文](./README.zh-TW.md)
10
+
11
+ A native window shell for Bun applications on Windows, hosting web UIs via the system WebView2 runtime without bundling a browser.
12
+
13
+ This library maintains a strict, minimal scope: it handles window lifecycle, DPI-aware geometry, frameless chrome customization, and the native message channel. Business logic and UI rendering live entirely outside the shell.
14
+
15
+ ```ts
16
+ import { openShell, serveOnSafePort, FLAG_FRAMELESS } from "bun-app-window";
17
+
18
+ const server = serveOnSafePort({ fetch: () => new Response("<h1>Hello</h1>") });
19
+ await openShell({ url: `http://127.0.0.1:${server.port}`, flags: FLAG_FRAMELESS });
20
+ ```
21
+
22
+ ---
23
+
24
+ ## Requirements & Design Boundaries
25
+
26
+ Before adopting this package, please review the following architectural constraints and trade-offs:
27
+
28
+ | Constraint | Implication |
29
+ | --- | --- |
30
+ | Windows x64 only | Enforced via `os` and `cpu` fields in `package.json` (the platform-neutral package name is reserved for future expansion). A prebuilt `libshell.dll` is shipped in the package; no install-time compilation is required |
31
+ | Bun runtime required | Ships raw TypeScript source files and requires Bun >= 1.4.3. While Node.js can install the package, it cannot execute it |
32
+ | Built on `bun:ffi` | `bun:ffi` is marked as experimental in Bun. While Bun recommends Node-API for production stability, this library chooses `bun:ffi` to achieve a zero-dependency footprint |
33
+
34
+ Runtime prerequisites and non-goals:
35
+
36
+ - System WebView2 Runtime: Requires the evergreen WebView2 Runtime installed on the host machine (pre-installed on Windows 11).
37
+ - Interactive desktop session: Due to Windows Session 0 isolation, WebView2 cannot run inside non-interactive background services. However, running inside elevated (High Integrity Level) administrator processes is fully supported.
38
+ - Explicit non-goals: System tray icons, native application menu bars, auto-updaters, multi-window orchestration, and cross-platform abstractions.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ bun add bun-app-window
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```ts
49
+ import { openShell, serveOnSafePort, FLAG_FRAMELESS, FLAG_CENTER } from "bun-app-window";
50
+
51
+ const server = serveOnSafePort({
52
+ fetch: () => new Response("<h1>Hello</h1>", {
53
+ headers: { "content-type": "text/html; charset=utf-8" },
54
+ }),
55
+ });
56
+
57
+ const shell = await openShell({
58
+ url: `http://127.0.0.1:${server.port}`,
59
+ title: "My App",
60
+ width: 1000,
61
+ height: 680,
62
+ flags: FLAG_FRAMELESS | FLAG_CENTER,
63
+ minWidth: 480,
64
+ minHeight: 320,
65
+ });
66
+
67
+ shell.post(JSON.stringify({ t: "ready" })); // Host -> Page one-way message
68
+ shell.setTitle("Doing work…");
69
+ ```
70
+
71
+ Note: `createShell(opts)` only resolves and loads the native DLL without opening a window. `openShell(opts)` creates the instance, starts the window immediately, and throws an error on failure.
72
+
73
+ ## Core API
74
+
75
+ ### Instance Methods & Properties
76
+
77
+ | Member | Description |
78
+ | --- | --- |
79
+ | `start()` | Opens the window and returns `{ rc, error }`. Returns immediately while the UI message loop runs on a dedicated native thread |
80
+ | `stop()` | Destroys the window and releases native resources. Safe to call multiple times (idempotent) |
81
+ | `show()` | Reveals the window. Typically paired with `FLAG_DEFER_SHOW` to display the window only after the web UI is ready |
82
+ | `isAlive()` | Checks whether the native window still exists (unlike `state()`, which cannot distinguish between unstarted and destroyed states) |
83
+ | `state()` | Returns current window state: `{ maximized, minimized, focused, dark, raw }` |
84
+ | `lastError()` | Returns the most recent failure message, or an empty string if no error occurred |
85
+ | `setTitle` / `minimize` / `toggleMaximize` / `close` | Standard window control operations; each returns a status code `rc` |
86
+ | `setMinSize(w, h)` | Sets the minimum window dimensions in logical pixels |
87
+ | `drag()` | Initiates native window dragging from a page `pointerdown` event (preserves Windows Snap Layouts) |
88
+ | `beginResize(edge)` | Initiates native border resizing from a page `pointermove` event (`edge` uses `RESIZE_EDGE.*` constants) |
89
+ | `post(json)` | Sends a fire-and-forget JSON message from the host to the web page |
90
+ | `setZoom(f)` / `getZoom()` | Gets or sets the page zoom factor (clamped automatically to `0.25`–`3.0`) |
91
+ | `reload()` / `openDevTools()` / `toggleFullscreen()` | Equivalent to `Ctrl+R`, `F12`, and `F11` respectively |
92
+ | `setBrowserUi(mask)` | Disables specific default browser behaviors using a bitwise OR of `BROWSER_UI_*` flags |
93
+ | `showContextMenu(x, y, items)` | Displays a native Win32 context menu at the specified screen coordinates (see below) |
94
+ | `pickFolder(title?)` | Opens a native folder selection dialog (synchronously blocks the calling thread) |
95
+ | `libInfo` | Metadata of the resolved native library (file path, resolution source, SHA-256 hash) |
96
+ | `symbols` | Escape hatch providing direct access to the raw `dlopen` FFI symbols |
97
+
98
+ ### Window Flags
99
+
100
+ Combine via bitwise OR (`|`) and pass to `opts.flags`:
101
+
102
+ | Flag | Description |
103
+ | --- | --- |
104
+ | `FLAG_FRAMELESS` | Creates a frameless window without the default system title bar, delegating title bar rendering to the web UI |
105
+ | `FLAG_RESIZABLE` | Allows user resizing |
106
+ | `FLAG_CENTER` | Centers the window within the active monitor work area on startup (works in both standard and frameless modes) |
107
+ | `FLAG_TOPMOST` | Keeps the window always on top |
108
+ | `FLAG_DEFER_SHOW` | Creates the window hidden initially until `show()` is explicitly called |
109
+ | `FLAG_ACCEPT_DROP` | Enables native file and folder drag-and-drop from Windows Explorer |
110
+ | `FLAG_START_MAXIMIZED` | Opens the window directly in a maximized state without an intermediate normal-sized frame flash |
111
+
112
+ ## Subpath Exports
113
+
114
+ Additional capabilities outside core window management are modularized into dedicated subpath exports.
115
+
116
+ ### `bun-app-window/ipc` — Typed RPC & Event Bus
117
+
118
+ Share a single TypeScript contract between the host and the web page for compile-time verification of method signatures and event payloads:
119
+
120
+ ```ts
121
+ // api.ts — Imported by both host and page
122
+ export type Api = { greet(name: string): string };
123
+ export type Events = { windowState: { maximized: boolean } };
124
+ ```
125
+
126
+ ```ts
127
+ // Host
128
+ import { createIpcServer } from "bun-app-window/ipc";
129
+
130
+ const ipc = createIpcServer({
131
+ service: { greet: (name: string) => `Hello ${name}` },
132
+ });
133
+
134
+ serveOnSafePort({
135
+ fetch(req) { /* Serve assets & authenticate */ },
136
+ websocket: {
137
+ open: (ws) => ipc.attach(ws),
138
+ message: (ws, raw) => ipc.handle(ws, raw),
139
+ close: (ws) => ipc.detach(ws),
140
+ },
141
+ });
142
+
143
+ ipc.emit("windowState", { maximized: false }); // Push event to connected pages
144
+ ```
145
+
146
+ ```ts
147
+ // Page
148
+ import { connect } from "bun-app-window/ipc";
149
+ import type { Api, Events } from "./api";
150
+
151
+ const api = await connect<Api, Events>();
152
+ await api.greet("Ada"); // Inferred return type: string
153
+ api.on("windowState", (s) => bar.setMaximized(s.maximized));
154
+ ```
155
+
156
+ By default, `connect()` extracts the auth token from the page URL's `?t=` query parameter and connects to `/ws`. Pass a custom `url` or `channel` option to override this behavior.
157
+
158
+ ### `bun-app-window/titlebar` — Windows 11 Title Bar
159
+
160
+ Mounts a customizable Windows 11 title bar inside frameless windows:
161
+
162
+ ```ts
163
+ // Page
164
+ import { mountTitleBar, bridgeFromWindowService } from "bun-app-window/titlebar";
165
+
166
+ const bar = mountTitleBar({
167
+ title: "My App",
168
+ icon: "/icon.svg",
169
+ bridge: bridgeFromWindowService(api.window),
170
+ });
171
+
172
+ api.on("windowState", (s) => bar.setMaximized(s.maximized));
173
+ ```
174
+
175
+ Expose the four corresponding window control methods on the host IPC service:
176
+
177
+ ```ts
178
+ // Host
179
+ createIpcServer({
180
+ service: {
181
+ window: {
182
+ minimize: () => shell.minimize(),
183
+ toggleMaximize: () => shell.toggleMaximize(),
184
+ close: () => shell.close(),
185
+ drag: () => shell.drag(),
186
+ },
187
+ },
188
+ });
189
+ ```
190
+
191
+ Implementation notes:
192
+
193
+ - Native dragging: Calling `shell.drag()` dispatches a native `WM_NCLBUTTONDOWN` (`HTCAPTION`) message, preserving Windows 11 Snap Layouts and double-click maximize behavior.
194
+ - Theming & icons: Colors and dimensions are controlled via CSS custom properties (`--ws-tb-*`). Window control buttons default to geometric glyphs for universal compatibility; setting `icons: "fluent"` switches to `Segoe Fluent Icons` (note: missing fonts on older systems will render as missing-glyph boxes).
195
+
196
+ ### `bun-app-window/state` — Window Placement Persistence
197
+
198
+ Automatically persists and restores window coordinates, dimensions, and maximized state across sessions:
199
+
200
+ ```ts
201
+ import { attachPlacementStore, loadPlacement, restorePlacement } from "bun-app-window/state";
202
+
203
+ const FILE = "C:\\Users\\me\\AppData\\Roaming\\MyApp\\window.json";
204
+ const shell = await openShell({ url, flags: FLAG_DEFER_SHOW });
205
+
206
+ const saved = await loadPlacement(FILE);
207
+ if (saved) restorePlacement(shell, saved);
208
+
209
+ shell.show();
210
+
211
+ const store = attachPlacementStore(shell, FILE); // Polls state; skips disk writes when idle
212
+ process.on("beforeExit", () => store.flush());
213
+ ```
214
+
215
+ The native placement layer handles edge cases automatically:
216
+
217
+ - Captures the normal restore rectangle (`rcNormalPosition`) when maximized rather than full-screen bounds.
218
+ - Translates coordinates across monitors with different DPI scaling factors.
219
+ - Validates and clamps off-screen coordinates when monitor topology changes.
220
+
221
+ For one-off reads and writes without polling timers, use `capturePlacement` and `savePlacement` directly.
222
+
223
+ ### `bun-app-window/debug` — Diagnostics
224
+
225
+ Exposes internal metrics and hit-testing utilities. Note: This module is excluded from semver stability guarantees and may change at any time.
226
+
227
+ ```ts
228
+ import { debugOf } from "bun-app-window/debug";
229
+
230
+ const d = debugOf(shell);
231
+ d.metrics(); // Returns pos / win / client / work / border / round geometry
232
+ d.dpi(); // Returns DPI awareness level and scaling factor
233
+ d.hittest(x, y);
234
+ ```
235
+
236
+ ## Common Recipes & Best Practices
237
+
238
+ ### 1. Disabling Browser-Specific UI
239
+
240
+ WebView2 enables standard browser interactions by default. Desktop applications typically disable all of them:
241
+
242
+ ```ts
243
+ import { BROWSER_UI_ALL } from "bun-app-window";
244
+
245
+ const shell = await openShell({ url, browserUi: BROWSER_UI_ALL });
246
+ ```
247
+
248
+ Individual flags can also be combined via bitwise OR:
249
+
250
+ | Flag | Disabled Behavior |
251
+ | --- | --- |
252
+ | `BROWSER_UI_MENU` | Default Edge right-click context menu |
253
+ | `BROWSER_UI_KEYS` | Built-in browser shortcuts (`Ctrl+P`, `Ctrl+F`, `Ctrl+S`) |
254
+ | `BROWSER_UI_STATUSBAR` | Bottom-left URL hover status bar |
255
+ | `BROWSER_UI_SWIPE` | Trackpad horizontal swipe back/forward navigation |
256
+ | `BROWSER_UI_AUTOFILL` | Form autofill popups |
257
+
258
+ Note: `F11`, `F12`, and `Ctrl±` zoom shortcuts are handled directly by the native shell and remain active regardless of these flags.
259
+
260
+ ### 2. Native Context Menu
261
+
262
+ After disabling the default browser menu, render native Win32 popup menus (`TrackPopupMenu`) via `showContextMenu`. The page cannot call it directly — expose it through the IPC service first:
263
+
264
+ ```ts
265
+ // Host: expose the menu capability on the window service
266
+ createIpcServer({
267
+ service: {
268
+ window: {
269
+ showContextMenu: (x: number, y: number, items: string) =>
270
+ shell.showContextMenu(x, y, items),
271
+ },
272
+ },
273
+ });
274
+ ```
275
+
276
+ ```ts
277
+ // Page: trigger the native menu
278
+ document.addEventListener("contextmenu", async (e) => {
279
+ e.preventDefault();
280
+ await api.window.showContextMenu(
281
+ Math.round(e.screenX),
282
+ Math.round(e.screenY),
283
+ "open|Open\nrefresh|Refresh\n-\nquit|Quit"
284
+ );
285
+ });
286
+
287
+ // Page: handle menu item selection
288
+ window.addEventListener("shell-msg", (e) => {
289
+ if (e.detail.t === "menu") console.log("Selected ID:", e.detail.id);
290
+ });
291
+ ```
292
+
293
+ Menu items are specified as a newline-delimited string: `id|Label` per line, or `-` for a separator line.
294
+
295
+ ### 3. File Drag and Drop
296
+
297
+ Pass `FLAG_ACCEPT_DROP` to receive file and folder drops from Windows Explorer:
298
+
299
+ ```ts
300
+ // Host
301
+ import { openShell, FLAG_ACCEPT_DROP } from "bun-app-window";
302
+
303
+ const shell = await openShell({ url, flags: FLAG_ACCEPT_DROP });
304
+ ```
305
+
306
+ ```js
307
+ // Page
308
+ window.addEventListener("shell-msg", (e) => {
309
+ if (e.detail.t === "drop") console.log("Dropped paths:", e.detail.paths);
310
+ });
311
+ ```
312
+
313
+ When running in an elevated administrator process (High Integrity Level) while Explorer runs at Medium Integrity Level, the shell automatically whitelists drop messages across UIPI via `ChangeWindowMessageFilterEx`.
314
+
315
+ ### 4. Recommended Desktop CSS Reset
316
+
317
+ The shell does not inject any implicit DOM nodes or stylesheets into your page. To make your web UI feel like a native desktop app, include the following baseline rules:
318
+
319
+ ```css
320
+ html, body { cursor: default; }
321
+ body { user-select: none; }
322
+ input, textarea, .log { user-select: text; cursor: auto; }
323
+ button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
324
+ ```
325
+
326
+ ```html
327
+ <input type="text" spellcheck="false" autocomplete="off" autocapitalize="off">
328
+ ```
329
+
330
+ Setting `spellcheck="false"` is particularly important—red squiggly underlines beneath file paths and numeric inputs immediately give away a web view.
331
+
332
+ ### 5. Compiling to a Standalone Executable (.exe)
333
+
334
+ When bundling with `bun build --compile`, import `bun-app-window/embedded` to embed `libshell.dll` directly inside the single-file executable:
335
+
336
+ ```ts
337
+ import { createShell } from "bun-app-window";
338
+ import { embeddedDll } from "bun-app-window/embedded"; // Embeds the DLL into the binary
339
+
340
+ const shell = await createShell({ url, embedded: embeddedDll });
341
+ ```
342
+
343
+ ```bash
344
+ bun build --compile --windows-hide-console src/index.ts --outfile dist/app.exe
345
+ ```
346
+
347
+ Tips:
348
+
349
+ - A side-effect import `import "bun-app-window/embedded";` also works automatically.
350
+ - Always pass `--windows-hide-console` for GUI builds; omitting it will spawn a black command prompt window alongside your application.
351
+
352
+ ## Internals & Reference
353
+
354
+ ### Native Library Resolution Order
355
+
356
+ At runtime, `libshell.dll` is located in the following order:
357
+
358
+ 1. Explicit path: `opts.nativeLibPath`, bypassing all fallback heuristics.
359
+ 2. Embedded payload: Extracted to `%LOCALAPPDATA%\<appName>\runtime` named by content hash (stale versions are automatically pruned).
360
+ 3. Executable directory: `libshell.dll` located next to the running `.exe`.
361
+ 4. Development paths: `.build/libshell.dll` followed by `dist/libshell.dll` relative to the package root.
362
+
363
+ ### Built-in Keyboard & Mouse Shortcuts
364
+
365
+ | Input | Behavior |
366
+ | --- | --- |
367
+ | `Ctrl +` / `Ctrl -` / `Ctrl 0` | Zoom in / Zoom out / Reset zoom to 100% |
368
+ | `Ctrl + Mouse Wheel` | Zoom in / Zoom out |
369
+ | `F11` | Toggle fullscreen mode (hides window borders and covers the taskbar) |
370
+ | `F5` / `Ctrl + R` | Reload page |
371
+ | `F12` | Open WebView2 Developer Tools |
372
+ | `Alt + Space` / Right-click title bar | Open native Windows system menu |
373
+
374
+ ## Development & Verification
375
+
376
+ Built with Bun 1.4.3 and TypeScript 7.0.2 with full strict type-checking enabled. Quality gates use standard CLI commands:
377
+
378
+ ```bash
379
+ bun check # Run TypeScript type checker
380
+ bun test # Run automated test suite (tests/, 103 test cases)
381
+ bun run demo # Launch interactive self-test demo (requires desktop session)
382
+ ```
383
+
384
+ Whenever `src/shell/` or `package.json` is modified, run the external consumer smoke test documented under the package acceptance section in `AGENTS.md`.
385
+
386
+ For window enumeration (`EnumWindows`) or automated screenshot scripts, filter by the corresponding Win32 window class name:
387
+
388
+ | Window Mode | Window Class Name |
389
+ | --- | --- |
390
+ | Standard framed (without `FLAG_FRAMELESS`) | `webview` |
391
+ | Frameless (with `FLAG_FRAMELESS`) | `webview_shell` |
392
+
393
+ ## Acknowledgments
394
+
395
+ - Platform windowing layer based on [webview/webview](https://github.com/webview/webview) `0.12.0` (MIT License, Serge Zaitsev & Steffen André Langnes). `vendor/webview/` contains an unmodified upstream copy.
396
+ - WebView2 SDK provided by [Microsoft.Web.WebView2](https://www.nuget.org/packages/Microsoft.Web.WebView2) `1.0.1150.38` (NuGet package vendored for offline builds). The WebView2 Runtime is provided by the OS.
397
+ - Co-authored-by DeepSeek
398
+
399
+ ## License
400
+
401
+ Released under the [MIT License](./LICENSE). `vendor/webview/` is also MIT-licensed with original copyright notices retained in source files.