@bobfrankston/msgapidefs 0.1.39 → 0.1.40
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 +4 -0
- package/msgapidefs.ts +29 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,10 @@ nothing about capability.
|
|
|
157
157
|
| Headless mode (`-noshow` without `-save`) | ✅ | ✅ | ❌ | msger creates the WebView invisible (`with_visible(false)`) but still loads HTML and runs scripts — an embedded `<script>` can query the engine and `window.ipc.postMessage` a result back. Combined with `-result <fieldname>` for clean stdout, this lets msger act as a headless CSS/JS resolver (`contrast-color()`, `getComputedStyle()`, `CSS.supports()`). msgview matches with `BrowserWindow({show:false})` + `backgroundThrottling:false`. |
|
|
158
158
|
| Render to bitmap (`-render [file]`, `render` option) | ✅ | ✅ | ❌ | Headless page→image capture: the window is never shown; the page loads, settles (`renderDelay`, default 100ms after `load` + two rAFs), is screenshotted, and the host exits. `-render <file>` writes the image (format from extension: `.png`/`.jpg`/`.bmp`) and the JSON result carries `render: {path, width, height, format}`; bare `-render` (or API `render: true`) returns it as `render: {data (base64), width, height, format}`. Library API: `showMessageBox({render: "shot.png" \| true, renderDelay?, renderFormat?})`; a failed capture rejects (msger) or sets `renderError` in the result. Both hosts fall back to capturing whatever rendered if the page hasn't loaded within the timeout (default 30s in render mode). msger: WebView2 DevTools `Page.captureScreenshot` on Windows, `webkit_web_view_get_snapshot()` → cairo PNG on Linux/Pi (2026-08-01); msgview: `webContents.capturePage()`. Both msger paths produce the same base64 PNG, so format transcoding and file writing are shared. **Linux caveat:** WebKitGTK cannot snapshot a window that was never mapped, so on Linux the render window IS mapped — made invisible by going fully transparent and skipping the taskbar, since Wayland has no way to park a window off-screen. Width/height are physical pixels (include DPI scale) and cover the WebView viewport, not the window frame. |
|
|
159
159
|
|
|
160
|
+
| Desktop overlay (`-overlay`, `overlay` option) | ❌ | ✅ | ❌ | msger-only (2026-08-09). Frameless + transparent window drawn over the desktop, click-through, always-on-top, kept out of the taskbar and never focused — for transient notices such as per-monitor identification. Needs all four of `with_decorations(false)`, `with_transparent(true)` on the tao window, `with_transparent(true)` on the wry WebView, and a page that declares its own transparent background (the built-in template does; `-url`/`-raw` pages must). Click-through is `set_ignore_cursor_events` (Windows/Mac/X11/Wayland; best-effort, warns and stays clickable if refused). No taskbar slot on Windows/Linux only — macOS has no per-window equivalent, Dock presence being a process-level activation policy. Pair with `timeout`: an overlay has no close button and swallows no clicks, so nothing in it can be operated. Each overlay defaults to its own WebView2 profile keyed by screen, because concurrent WebView2 creations against one user-data dir fail with `0x80070057` — which is what launching one overlay per display does. **Verified on Windows; Linux/Pi and Mac are built but not yet exercised.** Linux/X11 additionally needs a running compositor or transparency renders black. |
|
|
161
|
+
| Monitor enumeration (`-list`, `listMonitors` option) | ❌ | ✅ | ❌ | msger-only (2026-08-09). Prints `{"monitors":[{index,name,x,y,width,height,scale,primary}]}` and exits without creating a window. Indices match `-screen`, ordered bottom-row-first then left-to-right (the ordering `-screen` has always used on Windows, preserved so existing indices keep pointing at the same display). Exists so a caller can discover how many displays there are, and what to label them, before spawning one overlay per display. |
|
|
162
|
+
| Screen selection (`-screen <n>`, `screen` option) | ❌ | ✅ | ❌ | Offsets `-pos` into the named monitor **and** picks which display `-fullscreen` covers. Was Windows-only for `-pos`; the gate was wrong rather than protective, so Mac/X11 now honor it too (2026-08-09). `fullscreen` + `screen` is the only portable way to target a display: Wayland does not let a client position its own window at absolute coordinates, but it does honor fullscreen on a chosen output. |
|
|
163
|
+
|
|
160
164
|
> **msger** now injects `window.msgapi` via `msger-api.js` with window control, UDP, and HTTP. File system and shell are not yet implemented in msger or msgview.
|
|
161
165
|
>
|
|
162
166
|
> **Markdown rendering** (last two rows): both msger and msgview detect `.md`/`.markdown` URLs pointing at local files and render them via the shared `renderMarkdown()` helper in `msgcommon/markdown` — no extensions, no per-machine setup, GFM with light/dark CSS that follows `prefers-color-scheme`. Library callers can `import { renderMarkdown, looksLikeMarkdown } from '@bobfrankston/msgcommon/markdown'` directly. **Extension loading** is msgview-only by design: see the row's note.
|
package/msgapidefs.ts
CHANGED
|
@@ -90,6 +90,29 @@ export interface MsgVersionInfo { // Structure of versions.json
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// msgapi JavaScript API — available via window.msgapi inside msgview/msger/msga
|
|
93
|
+
/** Why an update prompted, and whether the post-update relaunch can reach the foreground.
|
|
94
|
+
* Android permits a silent self-update only when the updating app is the installer of record, so
|
|
95
|
+
* a browser or adb sideload makes the NEXT update prompt (selfInstalled false). Separately, a
|
|
96
|
+
* background process may not start an activity without canDrawOverlays, which is why a silent
|
|
97
|
+
* update can leave the user on the home screen. */
|
|
98
|
+
export interface MsgInstallInfo {
|
|
99
|
+
package: string;
|
|
100
|
+
installer: string; // installing package name, or "none"
|
|
101
|
+
selfInstalled: boolean; // true => next update can be silent
|
|
102
|
+
canDrawOverlays: boolean; // true => post-update relaunch is allowed to foreground
|
|
103
|
+
canInstallPackages: boolean;// "Install unknown apps" for this app
|
|
104
|
+
sdkInt: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Handle returned by udp.listen(). send/broadcast leave from this listener's own socket, so the
|
|
108
|
+
* device's reply arrives back at this listener rather than at whichever one registered first.
|
|
109
|
+
* send/broadcast are optional: msga builds before 1.5.45 return a handle with only close(). */
|
|
110
|
+
export interface UdpListenHandle {
|
|
111
|
+
close(): void;
|
|
112
|
+
send?(host: string, port: number, data: string | Uint8Array): Promise<void>;
|
|
113
|
+
broadcast?(port: number, data: string | Uint8Array): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
export interface MsgAPI {
|
|
94
117
|
|
|
95
118
|
version?: string; // Host app version (e.g. "1.3.0") — semver string, compare numerically not lexicographically
|
|
@@ -109,6 +132,8 @@ export interface MsgAPI {
|
|
|
109
132
|
|
|
110
133
|
diag?: {
|
|
111
134
|
getExitReasons(max?: number): Promise<MsgExitInfo[]>; // Previous-run exit records, newest first (default max 5). msga Android 11+ only; [] elsewhere.
|
|
135
|
+
getInstallInfo?(): Promise<MsgInstallInfo>; // Installer of record + permission state (msga 1.5.48+)
|
|
136
|
+
requestOverlay?(): Promise<'already' | 'opened' | 'n/a'>; // Open the Display-over-other-apps settings page
|
|
112
137
|
};
|
|
113
138
|
|
|
114
139
|
// ── Window Control ──────────────────────────────────────────
|
|
@@ -142,7 +167,10 @@ export interface MsgAPI {
|
|
|
142
167
|
|
|
143
168
|
udp?: {
|
|
144
169
|
send(host: string, port: number, data: string | Uint8Array): Promise<void>; // Send UDP packet to host:port
|
|
145
|
-
|
|
170
|
+
// Listen on port (0=ephemeral). The returned handle's send/broadcast go out THIS listener's
|
|
171
|
+
// socket, so replies come back to this listener — prefer them over the bare send/broadcast
|
|
172
|
+
// above, which cannot tell which listener the caller meant and use the first one registered.
|
|
173
|
+
listen(port: number, callback: (data: Uint8Array, sender: { host: string; port: number }) => void): Promise<UdpListenHandle>;
|
|
146
174
|
sendReceive(host: string, port: number, data: string | Uint8Array, timeoutMs?: number): Promise<Uint8Array>; // Send and wait for response
|
|
147
175
|
broadcast(port: number, data: string | Uint8Array): Promise<void>; // Broadcast to local network
|
|
148
176
|
};
|