@n1h/sweetview 0.0.0-stage → 0.1.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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE-0BSD +12 -0
  3. package/README.md +268 -3
  4. package/docs/README.md +26 -0
  5. package/docs/app.md +523 -0
  6. package/docs/build.md +169 -0
  7. package/docs/ipc-ws.md +202 -0
  8. package/docs/ipc.md +323 -0
  9. package/docs/native.md +1066 -0
  10. package/docs/page.md +71 -0
  11. package/docs/settings-store.md +125 -0
  12. package/docs/webview-library.md +104 -0
  13. package/docs/window-chrome.md +221 -0
  14. package/docs/worker.md +72 -0
  15. package/file-imports.d.ts +19 -0
  16. package/package.json +66 -4
  17. package/src/app.ts +350 -0
  18. package/src/build.ts +118 -0
  19. package/src/html.ts +71 -0
  20. package/src/index.ts +8 -0
  21. package/src/ipc/bridge.ts +41 -0
  22. package/src/ipc/frame.ts +16 -0
  23. package/src/ipc/index.ts +16 -0
  24. package/src/ipc/memory.ts +33 -0
  25. package/src/ipc/service.ts +75 -0
  26. package/src/ipc-ws/client.ts +111 -0
  27. package/src/ipc-ws/index.ts +5 -0
  28. package/src/ipc-ws/server.ts +133 -0
  29. package/src/make-app.ts +93 -0
  30. package/src/messages.ts +16 -0
  31. package/src/native/app-icon.ts +23 -0
  32. package/src/native/app-menu.ts +18 -0
  33. package/src/native/darwin/app-icon.ts +10 -0
  34. package/src/native/darwin/app-menu.ts +88 -0
  35. package/src/native/darwin/folder-picker.ts +29 -0
  36. package/src/native/darwin/image.ts +47 -0
  37. package/src/native/darwin/menu.ts +89 -0
  38. package/src/native/darwin/notifier.ts +95 -0
  39. package/src/native/darwin/objc.ts +246 -0
  40. package/src/native/darwin/overlay.ts +352 -0
  41. package/src/native/darwin/tray.ts +129 -0
  42. package/src/native/darwin/window.ts +64 -0
  43. package/src/native/folder-picker.ts +15 -0
  44. package/src/native/hit-regions.ts +162 -0
  45. package/src/native/icon.ts +530 -0
  46. package/src/native/index.ts +38 -0
  47. package/src/native/menu.ts +43 -0
  48. package/src/native/notifications.ts +40 -0
  49. package/src/native/overlay.ts +182 -0
  50. package/src/native/platform.ts +25 -0
  51. package/src/native/tray.ts +41 -0
  52. package/src/native/win32/api.ts +234 -0
  53. package/src/native/win32/app-icon.ts +5 -0
  54. package/src/native/win32/app-menu.ts +4 -0
  55. package/src/native/win32/folder-picker.ts +121 -0
  56. package/src/native/win32/icon.ts +85 -0
  57. package/src/native/win32/menu.ts +107 -0
  58. package/src/native/win32/message-window.ts +72 -0
  59. package/src/native/win32/notifier.ts +28 -0
  60. package/src/native/win32/overlay.ts +499 -0
  61. package/src/native/win32/tray.ts +228 -0
  62. package/src/native/win32/window.ts +77 -0
  63. package/src/native/window.ts +40 -0
  64. package/src/page.ts +48 -0
  65. package/src/ring.ts +110 -0
  66. package/src/settings-store.ts +101 -0
  67. package/src/webview-lib.ts +60 -0
  68. package/src/window-chrome.ts +118 -0
  69. package/src/worker.ts +72 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mrowr Purr
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/LICENSE-0BSD ADDED
@@ -0,0 +1,12 @@
1
+ BSD Zero Clause License (SPDX: 0BSD)
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose
4
+ with or without fee is hereby granted.
5
+
6
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
8
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
10
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
11
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
12
+ THIS SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,268 @@
1
- # Temporary Holding Version
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.
1
+ # 🥐 Sweetview
2
+
3
+ Desktop applications for **Bun**.
4
+
5
+ <img width="400" height="55" alt="DesktopPet-MacOS" src="https://github.com/user-attachments/assets/a77f1b5f-dd96-43cd-a01f-5d46cf5861cc" />
6
+
7
+ Written in TypeScript. Apps are shipped as one executable.
8
+
9
+ - [Install](#install)
10
+ - [Documentation](#documentation)
11
+ - [Quickstart](#quickstart)
12
+ - [How it works](#how-it-works)
13
+ - [Platforms](#platforms)
14
+ - [Development](#development)
15
+ - [License](#license)
16
+
17
+ ## Install
18
+
19
+ Requires Bun 1.4.2 or later. A desktop application installs the package and
20
+ its optional peer dependency, the native [webview library](docs/webview-library.md):
21
+
22
+ ```bash
23
+ bun add @n1h/sweetview @n1h/sweetview-webview-bun
24
+ ```
25
+
26
+ Only the root module `@n1h/sweetview` loads the library, when it is
27
+ imported. Every other module works without it, so a browser page or a web
28
+ server installs the package alone:
29
+
30
+ ```bash
31
+ bun add @n1h/sweetview
32
+ ```
33
+
34
+ ## Documentation
35
+
36
+ One reference page per module, indexed in the [reference index](docs/README.md).
37
+ Each documents every export: signature, parameters, return value, errors,
38
+ platform differences and thread rules.
39
+
40
+ | Module | Contents |
41
+ |---|---|
42
+ | [`@n1h/sweetview`](docs/app.md) | `createWebviewApp`: the window, the page, assets, the transport between page, UI thread and worker; `indexHtml`, `assetMime`, `Ring`, and `Webview` from the webview library |
43
+ | [`@n1h/sweetview/worker`](docs/worker.md) | `hostService` |
44
+ | [`@n1h/sweetview/page`](docs/page.md) | `isWebview`, `webviewTransport` |
45
+ | [`@n1h/sweetview/ipc`](docs/ipc.md) | the contract: `defineService`, `createBridge`, `memoryTransport`, the wire frames |
46
+ | [`@n1h/sweetview/ipc-ws`](docs/ipc-ws.md) | the contract over WebSockets: `wsHost` for `Bun.serve`, `wsTransport` and `wsUrl` for the browser; each side alone as `/ipc-ws/server` and `/ipc-ws/client` |
47
+ | [`@n1h/sweetview/native`](docs/native.md) | tray, notifications, windows and overlays, click-through hit regions, folder picker, app menu, icons; the hit-region helpers alone, without FFI, as `/native/hit-regions` |
48
+ | [`@n1h/sweetview/settings-store`](docs/settings-store.md) | a JSON settings file in the user's application data folder |
49
+ | [`@n1h/sweetview/window-chrome`](docs/window-chrome.md) | a page-drawn title bar and resize handles backed by OS moves and resizes |
50
+ | [`@n1h/sweetview/build`](docs/build.md) | `writeIconFiles` and `makeMacApp`; the page also covers the `make-app.ts` script and the `/file-imports` types |
51
+ | [the webview library](docs/webview-library.md) | the two C API additions the package relies on, and how the library is loaded |
52
+
53
+ Dependencies between the modules point one way. `ipc` imports nothing.
54
+ `ipc-ws`, `worker` and `page` import `ipc`. `native`, `settings-store` and
55
+ `build` import nothing else in the package. The root module imports `ipc`,
56
+ `native`, `window-chrome` and the webview library.
57
+
58
+ `page`, `window-chrome` and `native/hit-regions` import types only, so a
59
+ page bundle contains no Bun code.
60
+
61
+ ## Quickstart
62
+
63
+ An application has three parts, each importing its own module: the UI
64
+ thread, which owns the window; a Worker, which hosts the backend; and the
65
+ page. They share one typed contract. Six files and two commands:
66
+
67
+ 1. [Declare the contract in `protocol.ts`](#1-declare-the-contract-in-protocolts)
68
+ 2. [Write the backend in `service.ts`](#2-write-the-backend-in-servicets)
69
+ 3. [Host it in `worker.ts`](#3-host-it-in-workerts)
70
+ 4. [Write the page in `index.html` and `main.ts`](#4-write-the-page-in-indexhtml-and-maints)
71
+ 5. [Create the window in `app.ts`](#5-create-the-window-in-appts)
72
+ 6. [Run and compile](#6-run-and-compile)
73
+
74
+ ### 1. Declare the contract in `protocol.ts`
75
+
76
+ Each method maps its name to a `[params, result]` pair; each event maps its
77
+ name to a payload. Both sides import these types and nothing else from here.
78
+
79
+ ```ts
80
+ export type Methods = { greet: [{ name: string }, string] };
81
+ export type Events = { tick: { count: number } };
82
+ ```
83
+
84
+ ### 2. Write the backend in `service.ts`
85
+
86
+ `defineService` takes a setup function that receives `emit` and returns
87
+ `{ methods }`, the method handlers. The result knows nothing about windows
88
+ or sockets: the same `service` runs behind a window here, and behind a
89
+ WebSocket server when hosted with [`wsHost`](docs/ipc-ws.md#wshost).
90
+
91
+ ```ts
92
+ import { defineService } from "@n1h/sweetview/ipc";
93
+ import type { Events, Methods } from "./protocol";
94
+
95
+ export const service = defineService<Methods, Events>((emit) => {
96
+ let count = 0;
97
+ setInterval(() => emit("tick", { count: ++count }), 1000);
98
+
99
+ return {
100
+ methods: {
101
+ greet: ({ name }) => `Hello, ${name}`,
102
+ },
103
+ };
104
+ });
105
+ ```
106
+
107
+ ### 3. Host it in `worker.ts`
108
+
109
+ The Worker entry hosts the service on Bun's event loop and carries calls and
110
+ events to and from the UI thread.
111
+
112
+ ```ts
113
+ import { hostService } from "@n1h/sweetview/worker";
114
+ import { service } from "./service";
115
+
116
+ hostService(service);
117
+ ```
118
+
119
+ ### 4. Write the page in `index.html` and `main.ts`
120
+
121
+ An ordinary page. It references its script and stylesheet by relative path;
122
+ Bun bundles them when the application is run or compiled. The `<title>`
123
+ becomes the window title.
124
+
125
+ ```html
126
+ <!doctype html>
127
+ <html lang="en">
128
+ <head>
129
+ <meta charset="utf-8" />
130
+ <title>Hello</title>
131
+ </head>
132
+ <body>
133
+ <div id="root"></div>
134
+ <p id="tick"></p>
135
+ <script type="module" src="./main.ts"></script>
136
+ </body>
137
+ </html>
138
+ ```
139
+
140
+ Browser code. `webviewTransport()` connects the bridge to the window that
141
+ hosts the page; the bridge exposes the contract as `rpc` and `on`. A page
142
+ that also runs in a browser picks `wsTransport()` when `isWebview()` is
143
+ false, and that is the only place it knows the difference.
144
+
145
+ ```ts
146
+ import { createBridge } from "@n1h/sweetview/ipc";
147
+ import { webviewTransport } from "@n1h/sweetview/page";
148
+ import type { Events, Methods } from "./protocol";
149
+
150
+ const bridge = createBridge<Methods, Events>(webviewTransport());
151
+
152
+ bridge.on("tick", ({ count }) => {
153
+ document.getElementById("tick")!.textContent = `tick ${count}`;
154
+ });
155
+
156
+ document.getElementById("root")!.textContent = await bridge.rpc("greet", { name: "world" });
157
+ ```
158
+
159
+ ### 5. Create the window in `app.ts`
160
+
161
+ The UI-thread entry point. `run()` blocks in the OS loop until the window
162
+ closes or `quit()` is called.
163
+
164
+ - `page`: the HTML import. Bun bundles the page's scripts and styles, and
165
+ the host serves every output at the page's own origin.
166
+ - `worker`: the worker entry point, as a path relative to the entry script
167
+ `Bun.main`, which is this file. The same path works under `bun run` and
168
+ inside an executable from `bun build --compile`.
169
+ - `title` defaults to the page's `<title>`; `assets` adds files the page
170
+ fetches, such as models or images. All options:
171
+ [`createWebviewApp`](docs/app.md#createwebviewapp).
172
+
173
+ ```ts
174
+ import { createWebviewApp } from "@n1h/sweetview";
175
+ import index from "./index.html";
176
+ import type { Events, Methods } from "./protocol";
177
+
178
+ const app = await createWebviewApp<Methods, Events>({
179
+ page: index,
180
+ worker: "./worker.ts",
181
+ });
182
+
183
+ app.run();
184
+ ```
185
+
186
+ ### 6. Run and compile
187
+
188
+ Run from source; a window titled "Hello" opens:
189
+
190
+ ```bash
191
+ bun run app.ts
192
+ ```
193
+
194
+ Compile to one executable. The worker is a second entry point, because Bun
195
+ does not discover `new Worker` targets on its own:
196
+
197
+ ```bash
198
+ bun build --compile --target=bun-windows-x64 app.ts worker.ts --outfile hello.exe
199
+ ```
200
+
201
+ ## How it works
202
+
203
+ | Part | Runs | Does |
204
+ |---|---|---|
205
+ | UI thread | the OS event loop, blocked in `run()` | Owns the window. Serves the page's files at the page's own origin. Answers `uiMethods` such as native dialogs. Forwards every other call to the worker. |
206
+ | Worker | Bun's event loop | Hosts the `Service`: the application's methods and events. |
207
+ | Page | the webview, or a browser | Calls methods and receives events through a `Bridge`. |
208
+
209
+ The page and the worker never see each other's transport. In the window, a
210
+ call goes page to UI thread to worker and the reply comes back through a
211
+ shared-memory ring; in a browser, the same call goes over a WebSocket.
212
+
213
+ The contract is the same `Methods` and `Events` types on both sides. The
214
+ mechanism is described under [Transport](docs/app.md#transport).
215
+
216
+ ## Platforms
217
+
218
+ | | Windows x64 | macOS arm64 | Linux x64 |
219
+ |---|---|---|---|
220
+ | Window, asset serving, transparency | yes | yes | no: `createWebviewApp` throws |
221
+ | `native` (tray, notifications, overlays, folder picker, app menu) | yes | yes | throws on first call; icons and hit regions work |
222
+ | Build host that can embed the Windows exe file icon | yes | no, Bun's stock icon | no, Bun's stock icon |
223
+
224
+ - Bun accepts `--windows-icon` only on a Windows host; the demos pass it
225
+ only there.
226
+ - The macOS `.app` bundle is assembled on any OS by `makeMacApp` or
227
+ `src/make-app.ts`. It is unsigned.
228
+ - On a Mac that reports a copied bundle as damaged, these commands remove the
229
+ quarantine flag, restore the executable bit and add an ad-hoc signature:
230
+
231
+ ```bash
232
+ xattr -dr com.apple.quarantine "App.app"
233
+ chmod +x "App.app/Contents/MacOS/"*
234
+ codesign --sign - --force --deep "App.app"
235
+ ```
236
+
237
+ ## Development
238
+
239
+ | Path | Contents |
240
+ |---|---|
241
+ | `src/` | the package: `ipc/`, `ipc-ws/`, `native/`, and at the top level the window host, `worker`, `page`, `settings-store`, `window-chrome`, `build` and the `make-app.ts` command line |
242
+ | `docs/` | the reference pages, indexed by the [reference index](docs/README.md) |
243
+ | `demos/web-app/` | one backend and one React page run two ways: a desktop window with tray and notifications, or `Bun.serve` with WebSockets |
244
+ | [`demos/desktop-pet/`](demos/desktop-pet/README.md) | a transparent always-on-top pet rendered with Babylon.js, with click-through hit regions and a page-drawn settings window |
245
+ | [`webview/`](webview/README.md) | recipes that clone the webview-bun fork, with the webview fork as its submodule, and build the library from it, for working on the library itself; not needed to build the demos |
246
+
247
+ Inside this repository `@n1h/sweetview` resolves to `src/` through the
248
+ `paths` mapping in `tsconfig.json`, which Bun honours at run time and in
249
+ `bun build`.
250
+
251
+ Recipes run with [just](https://github.com/casey/just) and PowerShell 7,
252
+ from the repository root:
253
+
254
+ | Recipe | Does |
255
+ |---|---|
256
+ | `just check` | `tsc --noEmit` over `src/` and `demos/`, then `bun test` |
257
+ | `just demos web-app serve` | the web build on `http://127.0.0.1:4747` (`PORT`, `HOST`) |
258
+ | `just demos web-app app`, `just demos desktop-pet app` | the desktop application from source, with `bun run` (`WEBVIEW_DEBUG=1` opens devtools) |
259
+ | `just demos web-app compile`, `just demos desktop-pet compile` | Windows exe, macOS arm64 binary and `.app` into each demo's `dist/` |
260
+ | `just compile`, `just clean` | both demos |
261
+ | `just pack` | what `bun publish` would upload |
262
+ | `just publish` | publishes `@n1h/sweetview`; needs a registry login |
263
+
264
+ ## License
265
+
266
+ This software is available under either the [MIT License](LICENSE)
267
+ or the [BSD Zero Clause License (0BSD)](LICENSE-0BSD),
268
+ at your option.
package/docs/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @n1h/sweetview reference
2
+
3
+ One page per module. Each documents every export of that module: signature,
4
+ parameters, return value, errors, platform differences and thread rules. The
5
+ overview, install and quickstart are in the [package README](../README.md).
6
+
7
+ | Module | Contents |
8
+ |---|---|
9
+ | [`@n1h/sweetview`](app.md) | `createWebviewApp`: the window, the page, assets, the transport between page, UI thread and worker; `indexHtml`, `assetMime`, `Ring`, and `Webview` from the webview library |
10
+ | [`@n1h/sweetview/worker`](worker.md) | `hostService` |
11
+ | [`@n1h/sweetview/page`](page.md) | `isWebview`, `webviewTransport` |
12
+ | [`@n1h/sweetview/ipc`](ipc.md) | the contract: `defineService`, `createBridge`, `memoryTransport`, the wire frames |
13
+ | [`@n1h/sweetview/ipc-ws`](ipc-ws.md) | the contract over WebSockets: `wsHost` for `Bun.serve`, `wsTransport` and `wsUrl` for the browser; each side alone as `/ipc-ws/server` and `/ipc-ws/client` |
14
+ | [`@n1h/sweetview/native`](native.md) | tray, notifications, windows and overlays, click-through hit regions, folder picker, app menu, icons; the hit-region helpers alone, without FFI, as `/native/hit-regions` |
15
+ | [`@n1h/sweetview/settings-store`](settings-store.md) | a JSON settings file in the user's application data folder |
16
+ | [`@n1h/sweetview/window-chrome`](window-chrome.md) | a page-drawn title bar and resize handles backed by OS moves and resizes |
17
+ | [`@n1h/sweetview/build`](build.md) | `writeIconFiles` and `makeMacApp`; the page also covers the `make-app.ts` script and the `/file-imports` types |
18
+ | [the webview library](webview-library.md) | the two C API additions the package relies on, and how the library is loaded |
19
+
20
+ Dependencies between the modules point one way. `ipc` imports nothing.
21
+ `ipc-ws`, `worker` and `page` import `ipc`. `native`, `settings-store` and
22
+ `build` import nothing else in the package. The root module imports `ipc`,
23
+ `native`, `window-chrome` and the webview library.
24
+
25
+ `page`, `window-chrome` and `native/hit-regions` import types only, so a
26
+ page bundle contains no Bun code.