@bobfrankston/msger 0.1.413 → 0.1.415

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 CHANGED
@@ -327,7 +327,7 @@ Options:
327
327
  -debug, --debug Return debug info (HTML, size, autoSize) in result
328
328
  -render, --render [file] Render the page to a bitmap instead of displaying it.
329
329
  With <file>, writes the image (.png/.jpg/.bmp by extension);
330
- without, returns it base64 in result.render. [Windows only]
330
+ without, returns it base64 in result.render. [Windows, Linux/Pi]
331
331
  -noshow, --noshow Don't display the window. With -save, exits after saving.
332
332
  Otherwise the WebView still loads + runs scripts so a page
333
333
  can post a result via window.ipc.postMessage; only the
package/cli.js CHANGED
@@ -52,7 +52,7 @@ Options:
52
52
  appears on screen. With <file>, writes the image there (format
53
53
  from extension: .png/.jpg/.bmp) and the JSON result carries
54
54
  {render:{path,width,height,format}}. Without <file>, the image
55
- is returned base64 in {render:{data,...}}. [Windows only]
55
+ is returned base64 in {render:{data,...}}. [Windows, Linux/Pi]
56
56
  -noshow Don't display the window. With -save, also exits after saving.
57
57
  Otherwise the WebView still loads and runs scripts (so a page can
58
58
  query the DOM/CSS and post a result via window.ipc.postMessage)
Binary file
@@ -229,19 +229,51 @@ if (!isWindows && process.platform === "linux") {
229
229
  libDirs.some(dir => { try { return fs.existsSync(path.join(dir, so)); } catch { return false; } })
230
230
  );
231
231
 
232
- if (!haveWebkit()) {
232
+ // Emoji font: NOT needed for msger to start, but without one every emoji in
233
+ // a hosted page draws as a tofu box — Linux browsers get emoji from system
234
+ // fonts and nothing bundles them (Bob 2026-08-01: the wallclock app's 📋 and
235
+ // 🧃 were empty boxes on a Pi with zero emoji fonts). Treated as a
236
+ // recommended dependency: installed alongside, never fatal.
237
+ // Keep in sync with hasEmojiFont() in shower.ts.
238
+ const haveEmojiFont = () => {
239
+ // fontconfig is authoritative when present. Probe U+1F4CB CLIPBOARD, not
240
+ // the obvious U+1F600 GRINNING FACE: DejaVu (on every Debian box) covers
241
+ // 1F600 without being an emoji font, so that probe says "present" on a
242
+ // machine that draws every real emoji as tofu. 1F4CB is in every emoji
243
+ // font (Unicode 6.0) and in none of the generic text fonts.
244
+ try {
245
+ const fc = spawnSync("fc-list", [":charset=1f4cb", "family"], { encoding: "utf8" });
246
+ if (fc.status === 0) return fc.stdout.trim().length > 0;
247
+ } catch { /* no fontconfig — fall through to a filename scan */ }
248
+ const fontDirs = [
249
+ "/usr/share/fonts/truetype/noto", "/usr/share/fonts/truetype",
250
+ "/usr/share/fonts/opentype", "/usr/share/fonts",
251
+ path.join(process.env.HOME || "/root", ".local/share/fonts"),
252
+ ];
253
+ return fontDirs.some(dir => {
254
+ try { return fs.readdirSync(dir).some(f => /emoji/i.test(f)); } catch { return false; }
255
+ });
256
+ };
257
+
258
+ const wanted = [];
259
+ if (!haveWebkit()) wanted.push({ pkg: "libwebkit2gtk-4.1-0", what: "WebKitGTK runtime", required: true });
260
+ if (!haveEmojiFont()) wanted.push({ pkg: "fonts-noto-color-emoji", what: "emoji font", required: false });
261
+
262
+ if (wanted.length) {
233
263
  const isRoot = typeof process.getuid === "function" && process.getuid() === 0;
234
264
  const hasAptGet = fs.existsSync("/usr/bin/apt-get");
235
265
  // `sudo -n true` succeeds only when sudo needs no password.
236
266
  const sudoNoPassword = !isRoot && hasAptGet &&
237
267
  spawnSync("sudo", ["-n", "true"], { stdio: "ignore" }).status === 0;
238
268
  const canInstall = hasAptGet && (isRoot || sudoNoPassword) && process.env.MSGER_SKIP_DEPS !== "1";
269
+ const names = wanted.map(w => w.pkg);
270
+ const missingRequired = wanted.some(w => w.required);
239
271
 
240
272
  if (canInstall) {
241
273
  const prefix = isRoot ? [] : ["sudo", "-n"];
242
- const args = [...prefix, "apt-get", "install", "-y", "libwebkit2gtk-4.1-0"];
274
+ const args = [...prefix, "apt-get", "install", "-y", ...names];
243
275
  console.log("");
244
- console.log(" msger: WebKitGTK runtime missing — installing it now:");
276
+ console.log(` msger: missing ${wanted.map(w => w.what).join(" + ")} — installing now:`);
245
277
  console.log(` ${args.join(" ")}`);
246
278
  const run = (a) => spawnSync(a[0], a.slice(1), {
247
279
  stdio: "inherit",
@@ -256,27 +288,30 @@ if (!isWindows && process.platform === "linux") {
256
288
  res = run(args);
257
289
  }
258
290
  if (res.status === 0 && haveWebkit()) {
259
- console.log(" msger: WebKitGTK installed — msger is ready to run.");
291
+ console.log(" msger: dependencies installed — msger is ready to run.");
260
292
  console.log("");
261
293
  } else {
262
294
  console.log("");
263
- console.log(" msger: WARNING — automatic install of WebKitGTK failed.");
264
- console.log(" Run this by hand, then msger will work:");
295
+ console.log(" msger: WARNING — automatic dependency install failed.");
296
+ console.log(" Run this by hand:");
265
297
  console.log("");
266
- console.log(" sudo apt install libwebkit2gtk-4.1-0");
298
+ console.log(` sudo apt install ${names.join(" ")}`);
267
299
  console.log("");
268
300
  }
269
301
  } else {
270
302
  console.log("");
271
- console.log(" msger: WARNING — the WebKitGTK runtime was not found.");
272
- console.log(" msger renders with the system webview and cannot");
273
- console.log(" start without it. Install it with:");
303
+ console.log(` msger: ${missingRequired ? "WARNING — " : "note: "}missing ${wanted.map(w => w.what).join(" + ")}.`);
304
+ if (missingRequired) {
305
+ console.log(" msger renders with the system webview and cannot");
306
+ console.log(" start without it.");
307
+ } else {
308
+ console.log(" msger runs, but emoji in hosted pages will show");
309
+ console.log(" as empty boxes until a font provides them.");
310
+ }
274
311
  console.log("");
275
- console.log(" sudo apt install libwebkit2gtk-4.1-0");
312
+ console.log(` sudo apt install ${names.join(" ")}`);
276
313
  console.log(" or: msger --install-deps");
277
314
  console.log("");
278
- console.log(" (runtime only; -dev is needed just to build msger)");
279
- console.log("");
280
315
  }
281
316
  }
282
317
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/msger",
3
- "version": "0.1.413",
3
+ "version": "0.1.415",
4
4
  "description": "Fast, lightweight, cross-platform message box - Rust-powered alternative to msgview",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/shower.d.ts CHANGED
@@ -95,10 +95,24 @@ export declare function setAppName(name: string): void;
95
95
  * provision so Rust's window icon search picks it up. Per-call `icon` in
96
96
  * `MessageBoxOptions` still takes precedence. */
97
97
  export declare function setAppIcon(iconPath: string): void;
98
- /** The right package for this distro. Only the runtime library — building
99
- * msger additionally needs the -dev package, which is not msger's business to
100
- * install on a machine that only runs it. */
101
- export declare function webkitInstallCommand(): string;
98
+ /** The right packages for this distro. Only runtime pieces — building msger
99
+ * additionally needs the -dev package, which is not msger's business to
100
+ * install on a machine that only runs it.
101
+ *
102
+ * Two dependencies, different weight:
103
+ * - WebKitGTK is *required*: msger renders with the system webview and its
104
+ * binary won't even load without it.
105
+ * - An emoji font is *recommended*: msger runs fine, but every emoji in a
106
+ * hosted page draws as a tofu box, since Linux browsers take emoji from
107
+ * system fonts and nothing bundles them. */
108
+ export declare function webkitInstallCommand(missing?: {
109
+ webkit: boolean;
110
+ emoji: boolean;
111
+ }): string;
112
+ /** Does any installed font provide emoji? fontconfig is authoritative when it
113
+ * is present; otherwise fall back to looking for a font file with "emoji" in
114
+ * the name. Keep in sync with the copy in msger-native/builder/postinstall.js. */
115
+ export declare function hasEmojiFont(): boolean;
102
116
  /** `msger --install-deps` — install the WebKitGTK runtime msger links against.
103
117
  *
104
118
  * Deliberately NOT done from postinstall: an npm script that shells out to
package/shower.js CHANGED
@@ -86,20 +86,70 @@ function missingWebkitHint(stderr) {
86
86
  ' msger --install-deps\n' +
87
87
  '(runtime only — the -dev package is needed just for building msger.)';
88
88
  }
89
- /** The right package for this distro. Only the runtime library — building
90
- * msger additionally needs the -dev package, which is not msger's business to
91
- * install on a machine that only runs it. */
92
- export function webkitInstallCommand() {
89
+ /** The right packages for this distro. Only runtime pieces — building msger
90
+ * additionally needs the -dev package, which is not msger's business to
91
+ * install on a machine that only runs it.
92
+ *
93
+ * Two dependencies, different weight:
94
+ * - WebKitGTK is *required*: msger renders with the system webview and its
95
+ * binary won't even load without it.
96
+ * - An emoji font is *recommended*: msger runs fine, but every emoji in a
97
+ * hosted page draws as a tofu box, since Linux browsers take emoji from
98
+ * system fonts and nothing bundles them. */
99
+ export function webkitInstallCommand(missing) {
100
+ const need = missing ?? { webkit: !hasWebkitRuntime(), emoji: !hasEmojiFont() };
93
101
  const has = (cmd) => fs.existsSync(`/usr/bin/${cmd}`) || fs.existsSync(`/bin/${cmd}`);
102
+ const pick = (mgr) => {
103
+ const names = {
104
+ apt: { webkit: 'libwebkit2gtk-4.1-0', emoji: 'fonts-noto-color-emoji' },
105
+ dnf: { webkit: 'webkit2gtk4.1', emoji: 'google-noto-emoji-color-fonts' },
106
+ pacman: { webkit: 'webkit2gtk-4.1', emoji: 'noto-fonts-emoji' },
107
+ zypper: { webkit: 'libwebkit2gtk-4_1-0', emoji: 'noto-coloremoji-fonts' },
108
+ };
109
+ const verb = { apt: 'apt install', dnf: 'dnf install', pacman: 'pacman -S', zypper: 'zypper install' }[mgr];
110
+ const pkgs = [need.webkit && names[mgr].webkit, need.emoji && names[mgr].emoji].filter(Boolean);
111
+ return `sudo ${verb} ${pkgs.join(' ')}`;
112
+ };
94
113
  if (has('apt'))
95
- return 'sudo apt install libwebkit2gtk-4.1-0';
114
+ return pick('apt');
96
115
  if (has('dnf'))
97
- return 'sudo dnf install webkit2gtk4.1';
116
+ return pick('dnf');
98
117
  if (has('pacman'))
99
- return 'sudo pacman -S webkit2gtk-4.1';
118
+ return pick('pacman');
100
119
  if (has('zypper'))
101
- return 'sudo zypper install libwebkit2gtk-4_1-0';
102
- return 'install the WebKitGTK 4.1 runtime for your distribution';
120
+ return pick('zypper');
121
+ return 'install the WebKitGTK 4.1 runtime (and an emoji font) for your distribution';
122
+ }
123
+ /** Does any installed font provide emoji? fontconfig is authoritative when it
124
+ * is present; otherwise fall back to looking for a font file with "emoji" in
125
+ * the name. Keep in sync with the copy in msger-native/builder/postinstall.js. */
126
+ export function hasEmojiFont() {
127
+ if (platform() !== 'linux')
128
+ return true;
129
+ try {
130
+ // U+1F4CB CLIPBOARD, not the obvious U+1F600 GRINNING FACE: DejaVu (on
131
+ // every Debian box) covers 1F600 without being an emoji font at all, so
132
+ // probing it reports "present" on a machine that draws every real emoji
133
+ // as tofu. 1F4CB is old enough (Unicode 6.0) that every emoji font has
134
+ // it and absent from the generic text fonts — a clean discriminator.
135
+ const fc = spawnSync('fc-list', [':charset=1f4cb', 'family'], { encoding: 'utf8' });
136
+ if (fc.status === 0)
137
+ return (fc.stdout ?? '').trim().length > 0;
138
+ }
139
+ catch { /* no fontconfig — fall through */ }
140
+ const fontDirs = [
141
+ '/usr/share/fonts/truetype/noto', '/usr/share/fonts/truetype',
142
+ '/usr/share/fonts/opentype', '/usr/share/fonts',
143
+ path.join(process.env.HOME || '/root', '.local/share/fonts'),
144
+ ];
145
+ return fontDirs.some(dir => {
146
+ try {
147
+ return fs.readdirSync(dir).some(f => /emoji/i.test(f));
148
+ }
149
+ catch {
150
+ return false;
151
+ }
152
+ });
103
153
  }
104
154
  /** `msger --install-deps` — install the WebKitGTK runtime msger links against.
105
155
  *
@@ -115,11 +165,14 @@ export function installLinuxDeps() {
115
165
  console.error('--install-deps is for Linux; nothing to install on ' + platform());
116
166
  return 1;
117
167
  }
118
- if (hasWebkitRuntime()) {
119
- console.log('WebKitGTK runtime is already present — nothing to do.');
168
+ const missing = { webkit: !hasWebkitRuntime(), emoji: !hasEmojiFont() };
169
+ if (!missing.webkit && !missing.emoji) {
170
+ console.log('WebKitGTK runtime and an emoji font are both present — nothing to do.');
120
171
  return 0;
121
172
  }
122
- const command = webkitInstallCommand();
173
+ console.log(`Missing: ${[missing.webkit && 'WebKitGTK runtime', missing.emoji && 'emoji font']
174
+ .filter(Boolean).join(', ')}`);
175
+ const command = webkitInstallCommand(missing);
123
176
  if (!command.startsWith('sudo ')) {
124
177
  console.error(`msger could not identify this distribution's package manager.\nPlease ${command}.`);
125
178
  return 1;
@@ -134,11 +187,16 @@ export function installLinuxDeps() {
134
187
  }
135
188
  if (res.status !== 0)
136
189
  return res.status ?? 1;
137
- if (!hasWebkitRuntime()) {
190
+ if (missing.webkit && !hasWebkitRuntime()) {
138
191
  console.error('Install reported success but the WebKitGTK runtime is still not visible.');
139
192
  return 1;
140
193
  }
141
- console.log('WebKitGTK runtime installed msger is ready to run.');
194
+ // The emoji font is best-effort: say so if it didn't take, but don't fail —
195
+ // msger runs fine without it, pages just lose their emoji.
196
+ if (missing.emoji && !hasEmojiFont()) {
197
+ console.warn('Note: an emoji font still is not visible; emoji in pages may show as boxes.');
198
+ }
199
+ console.log('Dependencies installed — msger is ready to run.');
142
200
  return 0;
143
201
  }
144
202
  /** Is the WebKitGTK runtime msger links against present? Mirrors the check in