@bobfrankston/msger 0.1.386 → 0.1.388
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/README.md +49 -0
- package/cli.js +10 -0
- package/index.d.ts +1 -1
- package/msger-native/bin/msgernative.exe +0 -0
- package/package.json +1 -1
- package/shower.d.ts +15 -0
- package/shower.js +37 -0
package/README.md
CHANGED
|
@@ -118,6 +118,40 @@ Notes:
|
|
|
118
118
|
- The two `requestAnimationFrame` calls let layout settle before reading computed style — needed for `contrast-color`, gradients, etc.
|
|
119
119
|
- The original `-noshow -save` behavior is unchanged: it still exits after saving without ever creating a WebView.
|
|
120
120
|
|
|
121
|
+
### Render mode (`-render`) — capture the page to an image
|
|
122
|
+
|
|
123
|
+
`-render` turns msger into a headless page→bitmap renderer: no window ever appears; the page loads, settles, gets screenshotted, and msger exits. Windows (WebView2) only for now.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Write a PNG of a web page (format follows the extension: .png/.jpg/.bmp)
|
|
127
|
+
msger -url "https://example.com" -size 1024,768 -render page.png
|
|
128
|
+
|
|
129
|
+
# Render inline HTML (charts, badges, generated markup)
|
|
130
|
+
msger -html "<h1 style='color:steelblue'>Build OK</h1>" -size 400,150 -render status.png
|
|
131
|
+
|
|
132
|
+
# No file → the image comes back base64 in the JSON result
|
|
133
|
+
msger -message "hello" -render
|
|
134
|
+
# → { "button": "render", "render": { "format": "png", "data": "iVBOR...", "width": 500, "height": 375 } }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The Node API mirrors this:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Write a file; result.render = { path, width, height, format }
|
|
141
|
+
await showMessageBox({ url: "https://example.com", size: { width: 1024, height: 768 }, render: "page.png" });
|
|
142
|
+
|
|
143
|
+
// Or get the image as an object; result.render = { data (base64), width, height, format }
|
|
144
|
+
const result = await showMessageBox({ html: "<h1>Chart</h1>", render: true });
|
|
145
|
+
fs.writeFileSync("out.png", Buffer.from(result.render!.data!, "base64"));
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Notes:
|
|
149
|
+
- The capture fires after the page's `load` event + two `requestAnimationFrame`s + a settle delay (`renderDelay` option, default 100ms). If the page never fires `load`, msger captures best-effort at the timeout (default 30s in render mode).
|
|
150
|
+
- Width/height are physical pixels — a 400×300 window on a 125% DPI display captures at 500×375.
|
|
151
|
+
- `renderFormat` (`png`/`jpeg`/`bmp`) can be set explicitly in the API; the CLI derives it from the file extension.
|
|
152
|
+
- A failed capture rejects the promise (CLI: error on stderr, nonzero exit).
|
|
153
|
+
- msgview supports the same `-render` option via Electron's `capturePage()` on all its platforms.
|
|
154
|
+
|
|
121
155
|
## JSON Configuration Files
|
|
122
156
|
|
|
123
157
|
You can store message box configuration in a JSON file for reuse. JSON files support comments (JSON5 format).
|
|
@@ -290,6 +324,9 @@ Options:
|
|
|
290
324
|
-fullscreen, --fullscreen Start window in fullscreen mode (F11 to toggle, Escape to exit)
|
|
291
325
|
-raw, --raw With -html: load HTML as-is, no msger template/buttons (alias: -full)
|
|
292
326
|
-debug, --debug Return debug info (HTML, size, autoSize) in result
|
|
327
|
+
-render, --render [file] Render the page to a bitmap instead of displaying it.
|
|
328
|
+
With <file>, writes the image (.png/.jpg/.bmp by extension);
|
|
329
|
+
without, returns it base64 in result.render. [Windows only]
|
|
293
330
|
-noshow, --noshow Don't display the window. With -save, exits after saving.
|
|
294
331
|
Otherwise the WebView still loads + runs scripts so a page
|
|
295
332
|
can post a result via window.ipc.postMessage; only the
|
|
@@ -432,6 +469,10 @@ interface MessageBoxOptions {
|
|
|
432
469
|
rawHtml?: boolean; // With html: load HTML as-is, no msger template/buttons (used by mdview). CLI: -raw
|
|
433
470
|
debug?: boolean; // Return debug info (HTML, size, autoSize) in result
|
|
434
471
|
noResult?: boolean; // Suppress JSON result on stdout (CLI: -noresult, -result re-enables)
|
|
472
|
+
render?: boolean | string; // Render to a bitmap instead of displaying. String = file to write
|
|
473
|
+
// (format from extension), true = base64 object in result.render.
|
|
474
|
+
renderDelay?: number; // Render mode: ms between page load and capture (default 100)
|
|
475
|
+
renderFormat?: string; // Render mode: png (default) | jpeg | bmp
|
|
435
476
|
}
|
|
436
477
|
```
|
|
437
478
|
|
|
@@ -460,6 +501,14 @@ interface MessageBoxResult {
|
|
|
460
501
|
height: number; // Window height
|
|
461
502
|
autoSize: boolean; // Whether auto-sizing is enabled
|
|
462
503
|
};
|
|
504
|
+
render?: { // Captured screenshot (render mode)
|
|
505
|
+
format: string; // png | jpeg | bmp
|
|
506
|
+
width: number; // Physical pixels (includes DPI scale)
|
|
507
|
+
height: number;
|
|
508
|
+
data?: string; // Base64 image bytes (render: true)
|
|
509
|
+
path?: string; // Absolute path of written file (render: "file")
|
|
510
|
+
};
|
|
511
|
+
renderError?: string; // Why render produced no image (the promise rejects with this)
|
|
463
512
|
}
|
|
464
513
|
```
|
|
465
514
|
|
package/cli.js
CHANGED
|
@@ -40,6 +40,12 @@ Options:
|
|
|
40
40
|
-ontop Keep window always on top of other windows
|
|
41
41
|
-load <file> Load options from JSON file (supports comments)
|
|
42
42
|
-save <file> Save current options to JSON file
|
|
43
|
+
-render [file] Render to a bitmap instead of displaying a window. Waits for
|
|
44
|
+
the page to load, captures a screenshot, and exits — nothing
|
|
45
|
+
appears on screen. With <file>, writes the image there (format
|
|
46
|
+
from extension: .png/.jpg/.bmp) and the JSON result carries
|
|
47
|
+
{render:{path,width,height,format}}. Without <file>, the image
|
|
48
|
+
is returned base64 in {render:{data,...}}. [Windows only]
|
|
43
49
|
-noshow Don't display the window. With -save, also exits after saving.
|
|
44
50
|
Otherwise the WebView still loads and runs scripts (so a page can
|
|
45
51
|
query the DOM/CSS and post a result via window.ipc.postMessage)
|
|
@@ -70,6 +76,10 @@ Examples:
|
|
|
70
76
|
echo -e "\\x1b[31mError:\\x1b[0m Something failed" | msger
|
|
71
77
|
echo '{"message":"Test","buttons":["OK"]}' | msger
|
|
72
78
|
|
|
79
|
+
# Render a page to an image without showing a window
|
|
80
|
+
msger -url "https://example.com" -size 1024,768 -render page.png
|
|
81
|
+
msger -html "<h1>Chart</h1>" -render chart.png
|
|
82
|
+
|
|
73
83
|
# Headless evaluation — use the WebView as a CSS/JS engine, no UI
|
|
74
84
|
msger -noshow -html "<div id=x style='color:contrast-color(steelblue)'></div><script>requestAnimationFrame(()=>requestAnimationFrame(()=>window.ipc.postMessage(JSON.stringify({button:'x',value:getComputedStyle(document.getElementById('x')).color}))))</script>" -result value
|
|
75
85
|
|
package/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { showMessageBox, showMessageBoxEx, MessageBoxHandle, closeMessageBox, showService, ServiceHandle, MessageBoxOptions, MessageBoxResult, setAppName, setAppIcon } from "./shower.js";
|
|
1
|
+
export { showMessageBox, showMessageBoxEx, MessageBoxHandle, closeMessageBox, showService, ServiceHandle, MessageBoxOptions, MessageBoxResult, RenderResult, setAppName, setAppIcon } from "./shower.js";
|
|
Binary file
|
package/package.json
CHANGED
package/shower.d.ts
CHANGED
|
@@ -51,6 +51,19 @@ export interface MessageBoxOptions {
|
|
|
51
51
|
service?: boolean; /** Service mode: bidirectional IPC with parent. Stdin/stdout stay open. */
|
|
52
52
|
contentDir?: string; /** Base directory for custom protocol file serving (avoids file:// URLs) */
|
|
53
53
|
hidden?: boolean; /** Create the window invisible. The WebView still renders so JS / CSS / layout queries (e.g. `getComputedStyle`, `contrast-color()`) resolve; the window just isn't mapped on screen. Set by the CLI's `-noshow` flag. */
|
|
54
|
+
render?: boolean | string; /** Render to a bitmap instead of displaying. The window stays hidden; after the page loads (plus renderDelay) a screenshot is captured and msger exits. A string is a file path to write — format from extension (.png/.jpg/.jpeg/.bmp), result.render = {format,width,height,path}. `true` returns the image in result.render = {format,width,height,data(base64)}. Windows (WebView2) only for now. */
|
|
55
|
+
renderDelay?: number; /** Render mode: settle delay in ms between the page's load event and the capture (fonts/images/async paint). Default 100. */
|
|
56
|
+
renderFormat?: string; /** Render mode image format: png (default), jpeg, or bmp. Derived from the file extension when `render` is a path; settable directly when `render: true`. */
|
|
57
|
+
}
|
|
58
|
+
/** Screenshot returned in MessageBoxResult.render (render mode). Width/height
|
|
59
|
+
* are physical pixels — the capture includes the monitor's DPI scale, so a
|
|
60
|
+
* 400x300 logical window on a 125% display comes back 500x375. */
|
|
61
|
+
export interface RenderResult {
|
|
62
|
+
format: string; /** png | jpeg | bmp */
|
|
63
|
+
width: number; /** Physical pixel width */
|
|
64
|
+
height: number; /** Physical pixel height */
|
|
65
|
+
data?: string; /** Base64 image bytes (render: true) */
|
|
66
|
+
path?: string; /** Absolute path of the written file (render: "file") */
|
|
54
67
|
}
|
|
55
68
|
export interface MessageBoxResult {
|
|
56
69
|
button: string;
|
|
@@ -65,6 +78,8 @@ export interface MessageBoxResult {
|
|
|
65
78
|
height: number;
|
|
66
79
|
autoSize: boolean;
|
|
67
80
|
};
|
|
81
|
+
render?: RenderResult; /** Captured screenshot (render mode) */
|
|
82
|
+
renderError?: string; /** Why render mode produced no image — the result promise rejects with this */
|
|
68
83
|
}
|
|
69
84
|
/** Set the app name used for the per-user bin dir AND the per-app exe filename.
|
|
70
85
|
* Call before showService/showMessageBox — e.g. `setAppName("mailx")` makes
|
package/shower.js
CHANGED
|
@@ -362,6 +362,13 @@ function resolveBinaryPath() {
|
|
|
362
362
|
}
|
|
363
363
|
function createMessageBoxHandle(options) {
|
|
364
364
|
const binaryPath = resolveBinaryPath();
|
|
365
|
+
// Render mode: string = write the image to this file, true = return it
|
|
366
|
+
// base64 in result.render. Either way the image travels back as base64
|
|
367
|
+
// JSON on stdout, which detach mode throws away — so the combo is an error.
|
|
368
|
+
const renderTarget = typeof options.render === 'string' ? path.resolve(options.render) : undefined;
|
|
369
|
+
if (options.render && options.detach) {
|
|
370
|
+
throw new Error('render cannot be combined with detach — the image comes back on stdout, which detach discards');
|
|
371
|
+
}
|
|
365
372
|
// Validate the binary up front and synchronously. Doing this inside the
|
|
366
373
|
// Promise executor used to leave `child` undefined when the binary was
|
|
367
374
|
// missing, then `new MessageBoxHandle(child, ...)` blew up on `child.pid`
|
|
@@ -421,6 +428,23 @@ function createMessageBoxHandle(options) {
|
|
|
421
428
|
}
|
|
422
429
|
try {
|
|
423
430
|
const result = JSON.parse(stdout.trim());
|
|
431
|
+
if (options.render) {
|
|
432
|
+
if (result.renderError || !result.render?.data) {
|
|
433
|
+
reject(new Error(`render failed: ${result.renderError || 'no image in result'}`));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (renderTarget) {
|
|
437
|
+
// Write the file and hand back a path instead of the
|
|
438
|
+
// (large) base64 payload.
|
|
439
|
+
fs.writeFileSync(renderTarget, Buffer.from(result.render.data, 'base64'));
|
|
440
|
+
result.render = {
|
|
441
|
+
format: result.render.format,
|
|
442
|
+
width: result.render.width,
|
|
443
|
+
height: result.render.height,
|
|
444
|
+
path: renderTarget
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
}
|
|
424
448
|
resolve(result);
|
|
425
449
|
}
|
|
426
450
|
catch (error) {
|
|
@@ -443,6 +467,19 @@ function createMessageBoxHandle(options) {
|
|
|
443
467
|
if (optionsToSend.autoSize === undefined && !optionsToSend.size) {
|
|
444
468
|
optionsToSend.autoSize = true;
|
|
445
469
|
}
|
|
470
|
+
// Render mode: Rust wants a bare boolean plus a format string; the
|
|
471
|
+
// file path (if any) stays on this side. Force hidden so no window
|
|
472
|
+
// ever flashes on screen.
|
|
473
|
+
if (optionsToSend.render) {
|
|
474
|
+
if (renderTarget && !optionsToSend.renderFormat) {
|
|
475
|
+
const ext = path.extname(renderTarget).toLowerCase();
|
|
476
|
+
optionsToSend.renderFormat =
|
|
477
|
+
ext === '.bmp' ? 'bmp' :
|
|
478
|
+
(ext === '.jpg' || ext === '.jpeg') ? 'jpeg' : 'png';
|
|
479
|
+
}
|
|
480
|
+
optionsToSend.render = true;
|
|
481
|
+
optionsToSend.hidden = true;
|
|
482
|
+
}
|
|
446
483
|
// Convert ANSI escape sequences to HTML if message is provided
|
|
447
484
|
// ansi-to-html will escape HTML characters and convert ANSI codes to colored spans
|
|
448
485
|
// Only do this if the message contains actual ANSI codes to preserve plain text as-is
|