@nativedesktop/cli 0.4.1 → 0.4.2

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": "@nativedesktop/cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "The nd CLI: dev-runs, builds, and packages NativeDesktop apps",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,6 +25,6 @@
25
25
  "./config": "./src/config.ts"
26
26
  },
27
27
  "dependencies": {
28
- "@nativedesktop/host": "0.4.1"
28
+ "@nativedesktop/host": "0.4.2"
29
29
  }
30
30
  }
@@ -1,8 +1,14 @@
1
- // Webview engine and scheme resolution: the decisions `nd dev`, `nd package`
2
- // and `nd doctor` all make from the same config, and the values `nd dev` hands
3
- // the host as ND_WEBVIEW_ENGINE and ND_CEF_SCHEMES.
1
+ // Webview engine, scheme and style resolution: the decisions `nd dev`,
2
+ // `nd package` and `nd doctor` all make from the same config, and the values
3
+ // `nd dev` hands the host as ND_WEBVIEW_ENGINE, ND_CEF_SCHEMES and ND_CEF_STYLE.
4
4
  import { describe, expect, test } from "bun:test";
5
- import { engineTargetFor, type NativeDesktopConfig, resolveCefSchemes, resolveWebViewEngine } from "./config.ts";
5
+ import {
6
+ engineTargetFor,
7
+ type NativeDesktopConfig,
8
+ resolveCefSchemes,
9
+ resolveCefStyle,
10
+ resolveWebViewEngine,
11
+ } from "./config.ts";
6
12
 
7
13
  const chromiumOnMac: NativeDesktopConfig = { webview: { engine: { mac: "chromium" } } };
8
14
 
@@ -52,3 +58,23 @@ describe("resolveCefSchemes", () => {
52
58
  expect(() => resolveCefSchemes(bad, {})).toThrow("webview.cef.schemes");
53
59
  });
54
60
  });
61
+
62
+ describe("resolveCefStyle", () => {
63
+ const chrome: NativeDesktopConfig = { webview: { cef: { style: "chrome" } } };
64
+
65
+ test("defaults to alloy", () => {
66
+ expect(resolveCefStyle({}, {})).toBe("alloy");
67
+ expect(resolveCefStyle(chrome, {})).toBe("chrome");
68
+ });
69
+
70
+ test("ND_CEF_STYLE overrides the config in both directions", () => {
71
+ expect(resolveCefStyle({}, { ND_CEF_STYLE: "chrome" })).toBe("chrome");
72
+ expect(resolveCefStyle(chrome, { ND_CEF_STYLE: "alloy" })).toBe("alloy");
73
+ });
74
+
75
+ test("an unknown style names where it came from", () => {
76
+ expect(() => resolveCefStyle({}, { ND_CEF_STYLE: "views" })).toThrow("ND_CEF_STYLE");
77
+ const bad = { webview: { cef: { style: "blink" } } } as unknown as NativeDesktopConfig;
78
+ expect(() => resolveCefStyle(bad, {})).toThrow("webview.cef.style");
79
+ });
80
+ });
package/src/config.ts CHANGED
@@ -145,6 +145,9 @@ export interface PackageConfig {
145
145
  /** `<webview>` engine: the platform's own WebKit, or Chromium via CEF. */
146
146
  export type WebViewEngine = "system" | "chromium";
147
147
 
148
+ /** CEF browser style, per `cef_runtime_style_t`. */
149
+ export type CefStyle = "alloy" | "chrome";
150
+
148
151
  export interface CefConfig {
149
152
  /** Pinned CEF release, matched against cef-builds.spotifycdn.com/index.json.
150
153
  * Default: DEFAULT_CEF_VERSION from @nativedesktop/host/cef. */
@@ -156,6 +159,11 @@ export interface CefConfig {
156
159
  * cef_initialize, which is long before app code runs, so the list has to come
157
160
  * from config rather than a runtime call. */
158
161
  schemes?: string[];
162
+ /** Browser style. Default "alloy". "chrome" gives the embedded browser
163
+ * Chromium's own browser runtime, which is the only way to run Chrome
164
+ * extensions; it costs Chromium's own handling of some UI that Alloy leaves
165
+ * to the embedder. See docs/webview.md. */
166
+ style?: CefStyle;
159
167
  }
160
168
 
161
169
  export interface WebViewConfig {
@@ -235,6 +243,29 @@ export function resolveCefSchemes(
235
243
  return [...new Set(declared)];
236
244
  }
237
245
 
246
+ const CEF_STYLES: readonly CefStyle[] = ["alloy", "chrome"];
247
+
248
+ function expectStyle(value: string, source: string): CefStyle {
249
+ if (!CEF_STYLES.includes(value as CefStyle)) {
250
+ throw new Error(`nd: ${source} must be "alloy" or "chrome" (got "${value}")`);
251
+ }
252
+ return value as CefStyle;
253
+ }
254
+
255
+ /**
256
+ * The browser style a launch declares. ND_CEF_STYLE wins outright, the same way
257
+ * ND_WEBVIEW_ENGINE and ND_CEF_SCHEMES do, and takes the name the host reads.
258
+ */
259
+ export function resolveCefStyle(
260
+ config: NativeDesktopConfig,
261
+ env: Record<string, string | undefined> = process.env,
262
+ ): CefStyle {
263
+ const override = env.ND_CEF_STYLE;
264
+ if (override) return expectStyle(override, "ND_CEF_STYLE");
265
+ const declared = config.webview?.cef?.style;
266
+ return declared ? expectStyle(declared, "webview.cef.style") : "alloy";
267
+ }
268
+
238
269
  export function defineConfig(config: NativeDesktopConfig): NativeDesktopConfig { return config; }
239
270
 
240
271
  export async function loadConfig(cwd = process.cwd()): Promise<NativeDesktopConfig> {
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  loadConfig,
14
14
  type NativeDesktopConfig,
15
15
  resolveCefSchemes,
16
+ resolveCefStyle,
16
17
  resolveWebViewEngine,
17
18
  } from "./config.ts";
18
19
  import { packageApp, type PackageOptions } from "./package/index.ts";
@@ -24,15 +25,16 @@ async function nativeEnv(config: NativeDesktopConfig): Promise<Record<string, st
24
25
  return paths.length ? { ND_PLUGINS: "1", ND_PLUGIN_PATHS: paths.join(":") } : {};
25
26
  }
26
27
 
27
- /** The host reads the engine and its launch-declared schemes off its
28
- * environment, so both config decisions have to be made here and handed down.
29
- * Windows has no engine target yet. */
28
+ /** The host reads the engine, its launch-declared schemes and the browser style
29
+ * off its environment, so those config decisions have to be made here and
30
+ * handed down. Windows has no engine target yet. */
30
31
  function engineEnv(config: NativeDesktopConfig): Record<string, string> {
31
32
  const target = engineTargetFor();
32
33
  if (!target) return {};
33
34
  const schemes = resolveCefSchemes(config);
34
35
  return {
35
36
  ND_WEBVIEW_ENGINE: resolveWebViewEngine(config, target),
37
+ ND_CEF_STYLE: resolveCefStyle(config),
36
38
  ...(schemes.length ? { ND_CEF_SCHEMES: schemes.join(",") } : {}),
37
39
  };
38
40
  }
@@ -10,6 +10,7 @@ import {
10
10
  loadConfig,
11
11
  type NativeDesktopConfig,
12
12
  resolveCefSchemes,
13
+ resolveCefStyle,
13
14
  resolveWebViewEngine,
14
15
  type WebViewEngine,
15
16
  } from "../config.ts";
@@ -78,9 +79,11 @@ export function webviewChecks(config: NativeDesktopConfig, cwd: string): Check[]
78
79
  if (!target) return [];
79
80
  let engine: WebViewEngine;
80
81
  let schemes: string[];
82
+ let style: string;
81
83
  try {
82
84
  engine = resolveWebViewEngine(config, target);
83
85
  schemes = resolveCefSchemes(config);
86
+ style = resolveCefStyle(config);
84
87
  } catch (err) {
85
88
  return [{ name: "webview", status: "error", detail: String(err) }];
86
89
  }
@@ -88,7 +91,8 @@ export function webviewChecks(config: NativeDesktopConfig, cwd: string): Check[]
88
91
  name: "webview",
89
92
  status: "ok",
90
93
  detail: `engine=${engine} (${target})${process.env.ND_WEBVIEW_ENGINE ? " from ND_WEBVIEW_ENGINE" : ""}` +
91
- (schemes.length ? ` schemes=${schemes.join(",")}` : ""),
94
+ (schemes.length ? ` schemes=${schemes.join(",")}` : "") +
95
+ (engine === "chromium" ? ` style=${style}` : ""),
92
96
  }];
93
97
 
94
98
  const version = cefVersionFor(config.webview?.cef);
@@ -208,7 +208,7 @@ describe("buildDesktopEntry / appRunTemplate", () => {
208
208
  expect(script).not.toContain("ND_WEBVIEW_ENGINE");
209
209
  });
210
210
 
211
- test("AppRun exports the engine and its schemes, behind an override", () => {
211
+ test("AppRun exports the engine, its schemes and the style, behind an override", () => {
212
212
  const script = appRunTemplate({
213
213
  entry: "src/main.tsx",
214
214
  cwd: ".",
@@ -216,8 +216,10 @@ describe("buildDesktopEntry / appRunTemplate", () => {
216
216
  pluginPaths: [],
217
217
  engine: "chromium",
218
218
  schemes: ["nbext", "nbint"],
219
+ style: "chrome",
219
220
  });
220
221
  expect(script).toContain('export ND_WEBVIEW_ENGINE="${ND_WEBVIEW_ENGINE:-chromium}"');
221
222
  expect(script).toContain('export ND_CEF_SCHEMES="${ND_CEF_SCHEMES:-nbext,nbint}"');
223
+ expect(script).toContain('export ND_CEF_STYLE="${ND_CEF_STYLE:-chrome}"');
222
224
  });
223
225
  });
@@ -7,7 +7,7 @@ import { chmodSync, cpSync, mkdirSync, writeFileSync } from "node:fs";
7
7
  import { join, resolve } from "node:path";
8
8
  import { resolveHostBinary } from "@nativedesktop/host";
9
9
  import { cefPlatformKey } from "@nativedesktop/host/cef";
10
- import { type NativeDesktopConfig, resolveCefSchemes, resolveWebViewEngine } from "../config.ts";
10
+ import { type NativeDesktopConfig, resolveCefSchemes, resolveCefStyle, resolveWebViewEngine } from "../config.ts";
11
11
  import { applyCefLinuxPlan, cefVersionFor, ensureCefDist, planCefLinux } from "./cef.ts";
12
12
  import { installLinuxIcon } from "./icons.ts";
13
13
  import { buildAppRun, buildDesktopEntry, buildMimeInfoXml, type ResolvedIdentity } from "./identity.ts";
@@ -32,6 +32,7 @@ export async function packageLinuxApp(
32
32
 
33
33
  const engine = resolveWebViewEngine(config, "linux");
34
34
  const schemes = resolveCefSchemes(config);
35
+ const style = resolveCefStyle(config);
35
36
  const payload = await assemblePayload({
36
37
  appDir,
37
38
  config,
@@ -39,6 +40,7 @@ export async function packageLinuxApp(
39
40
  appRoot: join(appdir, "app"),
40
41
  engine,
41
42
  schemes,
43
+ style,
42
44
  entry: options.entry,
43
45
  compile: options.compile,
44
46
  });
@@ -51,6 +53,7 @@ export async function packageLinuxApp(
51
53
  pluginPaths: payload.pluginPaths,
52
54
  engine,
53
55
  schemes,
56
+ style,
54
57
  }));
55
58
  chmodSync(join(appdir, "AppRun"), 0o755);
56
59
  writeFileSync(join(appdir, `${identity.slug}.desktop`), buildDesktopEntry(identity, linux));
@@ -7,7 +7,7 @@ import { chmodSync, cpSync, existsSync, mkdirSync, readFileSync, writeFileSync }
7
7
  import { join, resolve } from "node:path";
8
8
  import { resolveHostBinary } from "@nativedesktop/host";
9
9
  import { cefPlatformKey } from "@nativedesktop/host/cef";
10
- import { type NativeDesktopConfig, resolveCefSchemes, resolveWebViewEngine } from "../config.ts";
10
+ import { type NativeDesktopConfig, resolveCefSchemes, resolveCefStyle, resolveWebViewEngine } from "../config.ts";
11
11
  import {
12
12
  applyCefMacPlan,
13
13
  type CefMacPlan,
@@ -80,6 +80,7 @@ export async function packageMacApp(
80
80
  appRoot: join(c, "Resources", "app"),
81
81
  engine,
82
82
  schemes: resolveCefSchemes(config),
83
+ style: resolveCefStyle(config),
83
84
  entry: options.entry,
84
85
  compile: options.compile,
85
86
  });
@@ -4,7 +4,7 @@
4
4
  // workspace-relative path and relative imports keep resolving packaged.
5
5
  import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { basename, join, relative, resolve, sep } from "node:path";
7
- import { buildNativePlugins, type NativeDesktopConfig, type PackageConfig, type WebViewEngine } from "../config.ts";
7
+ import { buildNativePlugins, type CefStyle, type NativeDesktopConfig, type PackageConfig, type WebViewEngine } from "../config.ts";
8
8
  import type { ResolvedIdentity } from "./identity.ts";
9
9
  import { assertResolvableEntries, flattenRuntimeModules } from "./modules.ts";
10
10
 
@@ -46,6 +46,8 @@ export interface PayloadOptions {
46
46
  engine: WebViewEngine;
47
47
  /** Launch-declared CEF schemes, recorded for the same reason. */
48
48
  schemes: string[];
49
+ /** CEF browser style, recorded for the same reason. */
50
+ style: CefStyle;
49
51
  /** CLI --entry override (app-relative source entry). */
50
52
  entry?: string;
51
53
  /** false forces raw source (--no-compile). */
@@ -170,6 +172,7 @@ export async function assemblePayload(o: PayloadOptions): Promise<PayloadResult>
170
172
  pluginPaths,
171
173
  engine: o.engine,
172
174
  schemes: o.schemes,
175
+ style: o.style,
173
176
  }, null, 2)}\n`);
174
177
 
175
178
  return { entry: appEntry, cwd, pluginPaths };
@@ -99,6 +99,8 @@ export interface AppRunSpec {
99
99
  engine?: string;
100
100
  /** Launch-declared CEF schemes. */
101
101
  schemes?: string[];
102
+ /** CEF browser style the AppDir was staged for. */
103
+ style?: string;
102
104
  }
103
105
 
104
106
  export function appRunTemplate(s: AppRunSpec): string {
@@ -108,11 +110,12 @@ export function appRunTemplate(s: AppRunSpec): string {
108
110
  `export ND_SCRIPT="$HERE/app/${s.entry}"\n` +
109
111
  'export PATH="$HERE/usr/bin:$PATH"\n';
110
112
  if (s.appId) script += `export ND_APP_ID="${s.appId}"\n`;
111
- // Every CEF process reads both of these, and both have to be settled before
113
+ // Every CEF process reads these, and each has to be settled before
112
114
  // cef_initialize. The :- form keeps an explicit override ahead of the bundle,
113
115
  // matching how the mac bootstrap defers to an already-set variable.
114
116
  if (s.engine) script += `export ND_WEBVIEW_ENGINE="\${ND_WEBVIEW_ENGINE:-${s.engine}}"\n`;
115
117
  if (s.schemes?.length) script += `export ND_CEF_SCHEMES="\${ND_CEF_SCHEMES:-${s.schemes.join(",")}}"\n`;
118
+ if (s.style) script += `export ND_CEF_STYLE="\${ND_CEF_STYLE:-${s.style}}"\n`;
116
119
  if (s.pluginPaths.length) {
117
120
  script += 'export ND_PLUGINS="1"\n';
118
121
  script += `export ND_PLUGIN_PATHS="${s.pluginPaths.map((p) => `$HERE/app/${p}`).join(":")}"\n`;