@liebstoeckel/thumbnails 0.3.5 → 0.3.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liebstoeckel/thumbnails",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Slide thumbnail capture for liebstoeckel decks via headless Chromium.",
5
5
  "keywords": [
6
6
  "liebstoeckel",
@@ -45,7 +45,7 @@
45
45
  "./cli": "./src/cli.ts"
46
46
  },
47
47
  "dependencies": {
48
- "@liebstoeckel/engine": "^0.3.5",
48
+ "@liebstoeckel/engine": "^0.3.7",
49
49
  "citty": "^0.2.2",
50
50
  "playwright-core": "1.60.0"
51
51
  }
package/src/capture.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { existsSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
2
5
  import { chromium, type Page } from "playwright-core";
3
6
  import {
4
7
  CAPTURE_EVENT,
@@ -13,6 +16,14 @@ import type { ThumbnailManifest } from "@liebstoeckel/engine/build/thumbnails";
13
16
 
14
17
  export type ThumbnailFormat = "webp" | "jpeg" | "png";
15
18
 
19
+ /** The exact `playwright-core` version this package resolves browsers through.
20
+ * `chromium.executablePath()` looks for one specific bundled revision, so any
21
+ * install must use the *matching* npm release. `doctor --install-chromium` pins
22
+ * `playwright@<this>`. An unpinned `playwright install` resolves to registry-latest
23
+ * and drops a newer revision into a different dir, which this version can't find
24
+ * (the install "succeeds" yet capture still reports no Chromium). */
25
+ export const playwrightCoreVersion: string = createRequire(import.meta.url)("playwright-core/package.json").version;
26
+
16
27
  /** Thumbnail capture options, the slide driver's options (ADR 0042) plus the
17
28
  * image-encoding policy specific to the thumbnail sink. Default width 640 ×
18
29
  * scale 2 = 1280×720, the native authoring canvas, so the overview is never
@@ -54,23 +65,68 @@ const DEFAULT_ARGS = [
54
65
  "--no-zygote",
55
66
  ];
56
67
 
57
- /** Resolve a Chromium binary: explicit $LIEBSTOECKEL_CHROMIUM Playwright's. */
58
- export function resolveChromium(opts: CaptureOptions = {}): string {
59
- let candidate = opts.executablePath ?? process.env.LIEBSTOECKEL_CHROMIUM;
60
- if (!candidate) {
68
+ /** Well-known Chrome/Chromium locations to probe when no binary is set explicitly,
69
+ * so a machine that already has Chrome "just works" without LIEBSTOECKEL_CHROMIUM
70
+ * Order = preference; the caller verifies each exists on disk. Pure (env-driven). */
71
+ export function systemChromiumCandidates(env: Record<string, string | undefined> = process.env): string[] {
72
+ const out: string[] = [];
73
+ // De-facto standard env vars other Chrome-driving tools honor.
74
+ if (env.PUPPETEER_EXECUTABLE_PATH) out.push(env.PUPPETEER_EXECUTABLE_PATH);
75
+ if (env.CHROME_PATH) out.push(env.CHROME_PATH);
76
+ // Puppeteer's install cache (`bunx puppeteer browsers install chrome`).
77
+ const pcache = join(homedir(), ".cache", "puppeteer", "chrome");
78
+ for (const sub of ["chrome-linux64/chrome", "chrome-win64/chrome.exe"]) {
61
79
  try {
62
- candidate = chromium.executablePath();
80
+ for (const p of new Bun.Glob(`*/${sub}`).scanSync({ cwd: pcache, absolute: true, onlyFiles: false })) out.push(p);
63
81
  } catch {
64
- candidate = undefined;
82
+ // no cache dir / glob unsupported → skip
65
83
  }
66
84
  }
85
+ // PATH binaries (Linux/BSD).
86
+ for (const bin of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "microsoft-edge"]) {
87
+ const p = Bun.which(bin);
88
+ if (p) out.push(p);
89
+ }
90
+ // macOS app bundles + common Windows install paths.
91
+ out.push(
92
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
93
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
94
+ );
95
+ for (const base of [env.PROGRAMFILES, env["PROGRAMFILES(X86)"], env.LOCALAPPDATA]) {
96
+ if (base) out.push(join(base, "Google", "Chrome", "Application", "chrome.exe"));
97
+ }
98
+ return out;
99
+ }
100
+
101
+ /** First system Chrome/Chromium that exists on disk, or undefined. */
102
+ function detectSystemChromium(): string | undefined {
103
+ for (const c of systemChromiumCandidates()) if (c && existsSync(c)) return c;
104
+ return undefined;
105
+ }
106
+
107
+ /** Resolve a Chromium binary: explicit → $LIEBSTOECKEL_CHROMIUM → a system
108
+ * Chrome/Chromium → Playwright's. The first one that exists on disk wins. */
109
+ export function resolveChromium(opts: CaptureOptions = {}): string {
110
+ const explicit = opts.executablePath ?? process.env.LIEBSTOECKEL_CHROMIUM;
111
+ if (explicit && existsSync(explicit)) return explicit;
112
+
113
+ const system = detectSystemChromium();
114
+ if (system) return system;
115
+
67
116
  // executablePath() returns a computed path even when the browser isn't
68
117
  // installed, verify the binary actually exists so hasChromium() stays honest
69
118
  // (otherwise capture is attempted where no browser exists, e.g. CI).
70
- if (candidate && existsSync(candidate)) return candidate;
119
+ let playwright: string | undefined;
120
+ try {
121
+ playwright = chromium.executablePath();
122
+ } catch {
123
+ playwright = undefined;
124
+ }
125
+ if (playwright && existsSync(playwright)) return playwright;
126
+
71
127
  throw new Error(
72
- "No Chromium found for thumbnail capture. Run `bunx playwright install chromium`, " +
73
- "or set LIEBSTOECKEL_CHROMIUM to a Chrome/Chromium binary.",
128
+ "No Chromium found for slide capture. Run `liebstoeckel doctor --install-chromium`, " +
129
+ "set LIEBSTOECKEL_CHROMIUM to a Chrome/Chromium binary, or `bunx playwright install chromium`.",
74
130
  );
75
131
  }
76
132
 
@@ -93,7 +149,7 @@ export function thumbnailsEnabled(
93
149
  ): { enabled: boolean; reason?: string } {
94
150
  if (env.LIEBSTOECKEL_NO_THUMBS) return { enabled: false, reason: "LIEBSTOECKEL_NO_THUMBS is set" };
95
151
  if (!chromium) {
96
- return { enabled: false, reason: "no Chromium (run `bunx playwright install chromium` or set LIEBSTOECKEL_CHROMIUM)" };
152
+ return { enabled: false, reason: "no Chromium (run `liebstoeckel doctor --install-chromium` or set LIEBSTOECKEL_CHROMIUM)" };
97
153
  }
98
154
  return { enabled: true };
99
155
  }
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export {
7
7
  printDeckPdf,
8
8
  resolveChromium,
9
9
  hasChromium,
10
+ systemChromiumCandidates,
11
+ playwrightCoreVersion,
10
12
  thumbnailsEnabled,
11
13
  type CaptureOptions,
12
14
  type RenderDriveOptions,