mirinjs 0.1.0-alpha.4 → 0.1.0-alpha.6
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.
- package/package.json +1 -1
- package/src/config.ts +30 -0
- package/src/host.ts +2 -0
- package/src/index.ts +1 -0
- package/src/native.ts +3 -3
- package/src/rpc-server.ts +2 -0
- package/src/runtime.ts +1 -1
- package/src/updater.ts +21 -9
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -42,6 +42,9 @@ export interface WindowConfig {
|
|
|
42
42
|
title?: string;
|
|
43
43
|
width?: number;
|
|
44
44
|
height?: number;
|
|
45
|
+
/** Minimum window size; the OS prevents resizing below it. */
|
|
46
|
+
minWidth?: number;
|
|
47
|
+
minHeight?: number;
|
|
45
48
|
/** Screen position (bottom-left origin, points). Centered when absent. */
|
|
46
49
|
x?: number;
|
|
47
50
|
y?: number;
|
|
@@ -78,6 +81,12 @@ export interface MirinConfig {
|
|
|
78
81
|
/** Reverse-DNS app id, e.g. "dev.peje.hello". */
|
|
79
82
|
id: string;
|
|
80
83
|
name: string;
|
|
84
|
+
/**
|
|
85
|
+
* Publisher / company name (e.g. "Netko Labs"). Shown in the Windows installer,
|
|
86
|
+
* the exe's file properties (CompanyName), and Add/Remove Programs. Defaults to
|
|
87
|
+
* the app `name`.
|
|
88
|
+
*/
|
|
89
|
+
publisher?: string;
|
|
81
90
|
/** Main-process entry, relative to the project root (runs in the Bun Worker). */
|
|
82
91
|
main: string;
|
|
83
92
|
/**
|
|
@@ -109,6 +118,14 @@ export interface MirinConfig {
|
|
|
109
118
|
* `.zip`. Requires `makensis` (NSIS) on the build machine.
|
|
110
119
|
*/
|
|
111
120
|
nsis?: boolean | NsisConfig;
|
|
121
|
+
/**
|
|
122
|
+
* Windows installer via **Inno Setup** — a modern flat wizard
|
|
123
|
+
* (`WizardStyle=modern`, the installer VS Code ships). Preferred over `nsis`
|
|
124
|
+
* when both are available; `true`/omitted builds the default installer, an
|
|
125
|
+
* object customizes it, `false` falls back to NSIS / the portable `.zip`.
|
|
126
|
+
* Requires `iscc` (Inno Setup 6+) on the build machine.
|
|
127
|
+
*/
|
|
128
|
+
inno?: boolean | InnoConfig;
|
|
112
129
|
/**
|
|
113
130
|
* External binaries bundled into the `.app` and spawned at runtime with
|
|
114
131
|
* `app.sidecar(name)`. Maps a logical name to a path (relative to the project
|
|
@@ -138,6 +155,12 @@ export interface MirinConfig {
|
|
|
138
155
|
*/
|
|
139
156
|
urlSchemes?: string[];
|
|
140
157
|
windows: Record<string, WindowConfig>;
|
|
158
|
+
/**
|
|
159
|
+
* Single-instance app (default true): a second launch focuses the running
|
|
160
|
+
* window and exits, instead of opening another window. Set false to allow
|
|
161
|
+
* multiple instances (each gets its own CEF cache dir).
|
|
162
|
+
*/
|
|
163
|
+
singleInstance?: boolean;
|
|
141
164
|
}
|
|
142
165
|
|
|
143
166
|
/** A bundled sidecar binary with optional hardened-runtime entitlements. */
|
|
@@ -222,6 +245,13 @@ export interface NsisConfig {
|
|
|
222
245
|
include?: string;
|
|
223
246
|
}
|
|
224
247
|
|
|
248
|
+
/**
|
|
249
|
+
* Inno Setup installer options — the same knobs as {@link NsisConfig}
|
|
250
|
+
* (perMachine, oneClick, shortcuts, license, publisher, runAfterFinish,
|
|
251
|
+
* installerIcon, and a raw `include` injected as `.iss` instead of NSIS script).
|
|
252
|
+
*/
|
|
253
|
+
export type InnoConfig = NsisConfig;
|
|
254
|
+
|
|
225
255
|
export interface ReleaseConfig {
|
|
226
256
|
/** Flat directory URL hosting `{channel}-{platform}-{arch}-*` update files. */
|
|
227
257
|
baseUrl: string;
|
package/src/host.ts
CHANGED
|
@@ -50,6 +50,8 @@ const coreConfig = JSON.parse(
|
|
|
50
50
|
process.env.MIRIN_DEV_URL ? { dev: true } : { resources_path: resourcesDir },
|
|
51
51
|
),
|
|
52
52
|
);
|
|
53
|
+
// Opt out of single-instance (the core default) when the app allows multiple.
|
|
54
|
+
if (manifest.singleInstance === false) coreConfig.single_instance = false;
|
|
53
55
|
|
|
54
56
|
// Load the native core on the main thread FIRST. The Worker also dlopens the
|
|
55
57
|
// same dylib in its boot; doing the main-thread dlopen before spawning the
|
package/src/index.ts
CHANGED
package/src/native.ts
CHANGED
|
@@ -24,7 +24,7 @@ const symbols = {
|
|
|
24
24
|
mirin_window_control: { args: [FFIType.u32, FFIType.ptr], returns: FFIType.void },
|
|
25
25
|
mirin_window_set_material: { args: [FFIType.u32, FFIType.ptr], returns: FFIType.void },
|
|
26
26
|
mirin_window_set_position: { args: [FFIType.u32, FFIType.f64, FFIType.f64], returns: FFIType.void },
|
|
27
|
-
mirin_window_maybe_start_drag: { args: [FFIType.u32, FFIType.i32, FFIType.i32, FFIType.i32], returns: FFIType.void },
|
|
27
|
+
mirin_window_maybe_start_drag: { args: [FFIType.u32, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.i32], returns: FFIType.void },
|
|
28
28
|
mirin_app_quit: { args: [], returns: FFIType.void },
|
|
29
29
|
mirin_app_set_dock_visible: { args: [FFIType.i32], returns: FFIType.void },
|
|
30
30
|
mirin_set_app_menu: { args: [FFIType.ptr], returns: FFIType.void },
|
|
@@ -114,8 +114,8 @@ export class Core {
|
|
|
114
114
|
this.#lib.symbols.mirin_window_set_position(id, x, y);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
windowMaybeStartDrag(id: number, x: number, y: number, detail: number): void {
|
|
118
|
-
this.#lib.symbols.mirin_window_maybe_start_drag(id, x, y, detail);
|
|
117
|
+
windowMaybeStartDrag(id: number, x: number, y: number, detail: number, ht: number): void {
|
|
118
|
+
this.#lib.symbols.mirin_window_maybe_start_drag(id, x, y, detail, ht);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
quit(): void {
|
package/src/rpc-server.ts
CHANGED
|
@@ -29,6 +29,8 @@ interface ControlFrame {
|
|
|
29
29
|
verb?: string;
|
|
30
30
|
/** Click count for pointer actions (2 = double-click → maximize). */
|
|
31
31
|
detail?: number;
|
|
32
|
+
/** Win32 resize hit-test code when the mousedown is on a window edge/corner. */
|
|
33
|
+
ht?: number;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
type IncomingFrame = RequestFrame | ControlFrame;
|
package/src/runtime.ts
CHANGED
|
@@ -107,7 +107,7 @@ export function boot(): void {
|
|
|
107
107
|
rpc.setControlHandler((frame, webview) => {
|
|
108
108
|
switch (frame.action) {
|
|
109
109
|
case "window.maybeStartDrag":
|
|
110
|
-
core.windowMaybeStartDrag(webview, frame.x ?? 0, frame.y ?? 0, frame.detail ?? 1);
|
|
110
|
+
core.windowMaybeStartDrag(webview, frame.x ?? 0, frame.y ?? 0, frame.detail ?? 1, frame.ht ?? 0);
|
|
111
111
|
break;
|
|
112
112
|
case "window.control":
|
|
113
113
|
if (frame.verb) core.windowControl(webview, frame.verb);
|
package/src/updater.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* packaged app with `release` set; in `mirin dev` the updater is idle.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { homedir } from "node:os";
|
|
16
|
+
import { homedir, tmpdir } from "node:os";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
19
19
|
import { $ } from "bun";
|
|
@@ -216,17 +216,29 @@ export class Updater {
|
|
|
216
216
|
const exe = join(runningApp, `${this.#version()!.name}.exe`);
|
|
217
217
|
const ps = [
|
|
218
218
|
`$ErrorActionPreference='SilentlyContinue'`,
|
|
219
|
+
// Wait for the host process to exit (it locks the exe).
|
|
219
220
|
`while (Get-Process -Id ${process.pid} -ErrorAction SilentlyContinue) { Start-Sleep -Milliseconds 200 }`,
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
// Give CEF's helper subprocesses a beat to exit and release their file
|
|
222
|
+
// locks (libcef.dll etc.) — they go just after the host.
|
|
223
|
+
`Start-Sleep -Milliseconds 700`,
|
|
224
|
+
// Replace the app folder, retrying while any lingering lock clears
|
|
225
|
+
// (~10s max). Unlike Unix, Windows can't delete a dir that's still a
|
|
226
|
+
// process's cwd or holds an open handle — hence the neutral cwd below.
|
|
227
|
+
`for ($i=0; $i -lt 50; $i++) { Remove-Item -Recurse -Force ${psq(runningApp)}; if (-not (Test-Path ${psq(runningApp)})) { break }; Start-Sleep -Milliseconds 200 }`,
|
|
222
228
|
`Move-Item -Force ${psq(this.#staged)} ${psq(runningApp)}`,
|
|
223
|
-
`Start-Process ${psq(exe)}`,
|
|
229
|
+
`Start-Process -FilePath ${psq(exe)} -WorkingDirectory ${psq(runningApp)}`,
|
|
224
230
|
].join("; ");
|
|
225
|
-
Bun.spawn(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
Bun.spawn(
|
|
232
|
+
["powershell", "-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", ps],
|
|
233
|
+
{
|
|
234
|
+
// Run from a neutral dir, NOT the inherited install dir — otherwise the
|
|
235
|
+
// app folder is this PowerShell's cwd and Windows refuses to delete it.
|
|
236
|
+
cwd: tmpdir(),
|
|
237
|
+
stdin: "ignore",
|
|
238
|
+
stdout: "ignore",
|
|
239
|
+
stderr: "ignore",
|
|
240
|
+
},
|
|
241
|
+
).unref();
|
|
230
242
|
} else {
|
|
231
243
|
const runningApp = join(this.#resourcesDir()!, "..", "..");
|
|
232
244
|
const script = [
|