create-opentray 0.24.0 → 0.26.0
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 +21 -13
- package/dist/{bin-BQb2Bjcu.mjs → bin-Bz16rwKs.mjs} +249 -313
- package/dist/bin-Bz16rwKs.mjs.map +1 -0
- package/dist/bin-CrMTyTux.d.mts +751 -0
- package/dist/bin-CrMTyTux.d.mts.map +1 -0
- package/dist/bin.d.mts +2 -15
- package/dist/bin.mjs +2 -2
- package/dist/{decode-ico-BHd2POHf.mjs → decode-ico-DDEIV7iZ.mjs} +2 -2
- package/dist/{decode-ico-BHd2POHf.mjs.map → decode-ico-DDEIV7iZ.mjs.map} +1 -1
- package/dist/encode-CTEluLqt.mjs +167 -0
- package/dist/encode-CTEluLqt.mjs.map +1 -0
- package/dist/icon-codec-Bs3XehqZ.mjs +20 -0
- package/dist/icon-codec-Bs3XehqZ.mjs.map +1 -0
- package/dist/index.d.mts +1 -724
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/rolldown-runtime-DJK8HYOj.mjs +34 -0
- package/dist/shell/assets/ghostty-web.js +1 -1
- package/dist/shell/assets/index.css +1 -1
- package/dist/shell/assets/index.js +2 -9
- package/dist/shell/assets/index2.js +9 -2
- package/dist/shell/assets/input.js +1 -1
- package/dist/shell/assets/main.js +8 -8
- package/dist/shell/assets/terminal-pane.js +2 -2
- package/dist/shell/assets/terminal.js +1 -1
- package/dist/shell/assets/toolbar.js +1 -0
- package/dist/shell/index.html +1 -1
- package/dist/shell/terminal.html +1 -1
- package/dist/shell/{browse.html → toolbar.html} +3 -3
- package/dist/skill/SKILL.md +14 -11
- package/dist/skill/references/cli-reference.md +1 -1
- package/dist/skill/references/how-it-works.md +15 -9
- package/dist/webui/assets/ghostty-web.js +1 -1
- package/dist/webui/assets/index.css +1 -1
- package/dist/webui/assets/index.js +2 -9
- package/dist/webui/assets/index2.js +9 -2
- package/dist/webui/assets/input.js +1 -1
- package/dist/webui/assets/main.js +8 -8
- package/dist/webui/assets/terminal-pane.js +2 -2
- package/dist/webui/assets/terminal.js +1 -1
- package/dist/webui/assets/toolbar.js +1 -0
- package/dist/webui/index.html +1 -1
- package/dist/webui/terminal.html +1 -1
- package/dist/webui/{browse.html → toolbar.html} +3 -3
- package/package.json +6 -6
- package/skill/SKILL.md +14 -11
- package/skill/references/cli-reference.md +1 -1
- package/skill/references/how-it-works.md +15 -9
- package/dist/bin-BQb2Bjcu.mjs.map +0 -1
- package/dist/bin.d.mts.map +0 -1
- package/dist/shell/assets/browse.js +0 -1
- package/dist/webui/assets/browse.js +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { r as __toESM$1, t as __commonJSMin$1 } from "./rolldown-runtime-DJK8HYOj.mjs";
|
|
1
2
|
import { _ as assetsDirectory, a as decodeImage, c as encodeImagePng, d as rasterizeSvg, f as resizeImage, g as glyphLadderFingerprint, h as embeddedGlyphFont, i as cropImage, l as insideImage, m as GLYPH_FONT_FAMILY, n as cloneImage, o as decodeImageFile, p as trimBounds, r as containImage, s as emptyImageOf, t as brightPixelRatio, u as pasteImage } from "./raster-DeMbA2zI.mjs";
|
|
3
|
+
import { i as encodeIco, n as encodeDensePng, r as encodeIcns, t as LINUX_SIZES } from "./encode-CTEluLqt.mjs";
|
|
2
4
|
import { createRequire } from "node:module";
|
|
3
5
|
import crypto, { createHash, randomBytes } from "node:crypto";
|
|
4
6
|
import fs, { access, cp, lstat, mkdir, mkdtemp, readFile, readdir, readlink, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
@@ -9,42 +11,9 @@ import { execFile, spawn, spawnSync } from "node:child_process";
|
|
|
9
11
|
import net from "node:net";
|
|
10
12
|
import { fileURLToPath } from "node:url";
|
|
11
13
|
import { getSvgPath } from "figma-squircle";
|
|
12
|
-
import { IconIcns, IconIco } from "@shockpkg/icon-encoder";
|
|
13
14
|
import { resolveDefaultDarwinAppBundlePath, sanitizeAppBundleName } from "@opentray/packaging";
|
|
14
15
|
import { promisify } from "node:util";
|
|
15
16
|
import { createServer } from "node:http";
|
|
16
|
-
//#region \0rolldown/runtime.js
|
|
17
|
-
var __create$1 = Object.create;
|
|
18
|
-
var __defProp$1 = Object.defineProperty;
|
|
19
|
-
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
|
|
20
|
-
var __getOwnPropNames$1 = Object.getOwnPropertyNames;
|
|
21
|
-
var __getProtoOf$1 = Object.getPrototypeOf;
|
|
22
|
-
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
23
|
-
var __commonJSMin$1 = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
24
|
-
var __exportAll$1 = (all, no_symbols) => {
|
|
25
|
-
let target = {};
|
|
26
|
-
for (var name in all) __defProp$1(target, name, {
|
|
27
|
-
get: all[name],
|
|
28
|
-
enumerable: true
|
|
29
|
-
});
|
|
30
|
-
if (!no_symbols) __defProp$1(target, Symbol.toStringTag, { value: "Module" });
|
|
31
|
-
return target;
|
|
32
|
-
};
|
|
33
|
-
var __copyProps$1 = (to, from, except, desc) => {
|
|
34
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
35
|
-
key = keys[i];
|
|
36
|
-
if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$1(to, key, {
|
|
37
|
-
get: ((k) => from[k]).bind(null, key),
|
|
38
|
-
enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
return to;
|
|
42
|
-
};
|
|
43
|
-
var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(__getProtoOf$1(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule ? __defProp$1(target, "default", {
|
|
44
|
-
value: mod,
|
|
45
|
-
enumerable: true
|
|
46
|
-
}) : target, mod));
|
|
47
|
-
//#endregion
|
|
48
17
|
//#region packages/core/src/app-id.ts
|
|
49
18
|
/** A token that looks like a command option, e.g. `--xx`, `-p`, or `--port=8080`. */
|
|
50
19
|
const isOptionToken$1 = (token) => token.startsWith("-") && token.length > 1;
|
|
@@ -6110,167 +6079,6 @@ parse$2.Node = Node;
|
|
|
6110
6079
|
parse$2.TextNode = TextNode;
|
|
6111
6080
|
parse$2.NodeType = NodeType;
|
|
6112
6081
|
//#endregion
|
|
6113
|
-
//#region ../icon/src/encode.ts
|
|
6114
|
-
/**
|
|
6115
|
-
* Platform encoders. PNG payloads carry explicit 72 DPI density: the WASM PNG
|
|
6116
|
-
* encoder emits no `pHYs` chunk (spike-verified), so density is injected with
|
|
6117
|
-
* a minimal chunk surgery (insert/replace after IHDR, CRC32 over type+data).
|
|
6118
|
-
* ICNS keeps the ten-tag explicit representation set; ICO and Linux PNGs keep
|
|
6119
|
-
* their ladders. All encoders consume PNG bytes so the density law applies to
|
|
6120
|
-
* every emitted pixel payload.
|
|
6121
|
-
*/
|
|
6122
|
-
var encode_exports = /* @__PURE__ */ __exportAll$1({
|
|
6123
|
-
ICNS_REPRESENTATIONS: () => ICNS_REPRESENTATIONS,
|
|
6124
|
-
ICO_SIZES: () => ICO_SIZES,
|
|
6125
|
-
LINUX_SIZES: () => LINUX_SIZES,
|
|
6126
|
-
encodeDensePng: () => encodeDensePng,
|
|
6127
|
-
encodeIcns: () => encodeIcns,
|
|
6128
|
-
encodeIco: () => encodeIco,
|
|
6129
|
-
pngWithDensity: () => pngWithDensity
|
|
6130
|
-
});
|
|
6131
|
-
/** Pixels per metre for 72 DPI (72 × 39.3701 ≈ 2834.65, the Apple value). */
|
|
6132
|
-
const PIXELS_PER_METRE_72DPI = 2835;
|
|
6133
|
-
const ICNS_REPRESENTATIONS = [
|
|
6134
|
-
{
|
|
6135
|
-
tag: "ic12",
|
|
6136
|
-
size: 64
|
|
6137
|
-
},
|
|
6138
|
-
{
|
|
6139
|
-
tag: "ic07",
|
|
6140
|
-
size: 128
|
|
6141
|
-
},
|
|
6142
|
-
{
|
|
6143
|
-
tag: "ic13",
|
|
6144
|
-
size: 256
|
|
6145
|
-
},
|
|
6146
|
-
{
|
|
6147
|
-
tag: "ic08",
|
|
6148
|
-
size: 256
|
|
6149
|
-
},
|
|
6150
|
-
{
|
|
6151
|
-
tag: "ic04",
|
|
6152
|
-
size: 16
|
|
6153
|
-
},
|
|
6154
|
-
{
|
|
6155
|
-
tag: "ic14",
|
|
6156
|
-
size: 512
|
|
6157
|
-
},
|
|
6158
|
-
{
|
|
6159
|
-
tag: "ic09",
|
|
6160
|
-
size: 512
|
|
6161
|
-
},
|
|
6162
|
-
{
|
|
6163
|
-
tag: "ic05",
|
|
6164
|
-
size: 32
|
|
6165
|
-
},
|
|
6166
|
-
{
|
|
6167
|
-
tag: "ic10",
|
|
6168
|
-
size: 1024
|
|
6169
|
-
},
|
|
6170
|
-
{
|
|
6171
|
-
tag: "ic11",
|
|
6172
|
-
size: 32
|
|
6173
|
-
}
|
|
6174
|
-
];
|
|
6175
|
-
const ICO_SIZES = [
|
|
6176
|
-
16,
|
|
6177
|
-
24,
|
|
6178
|
-
32,
|
|
6179
|
-
48,
|
|
6180
|
-
64,
|
|
6181
|
-
128,
|
|
6182
|
-
256
|
|
6183
|
-
];
|
|
6184
|
-
const LINUX_SIZES = [
|
|
6185
|
-
16,
|
|
6186
|
-
32,
|
|
6187
|
-
48,
|
|
6188
|
-
64,
|
|
6189
|
-
128,
|
|
6190
|
-
256,
|
|
6191
|
-
512
|
|
6192
|
-
];
|
|
6193
|
-
const CRC_TABLE = (() => {
|
|
6194
|
-
const table = new Array(256);
|
|
6195
|
-
for (let n = 0; n < 256; n += 1) {
|
|
6196
|
-
let c = n;
|
|
6197
|
-
for (let k = 0; k < 8; k += 1) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
6198
|
-
table[n] = c >>> 0;
|
|
6199
|
-
}
|
|
6200
|
-
return table;
|
|
6201
|
-
})();
|
|
6202
|
-
const crc32 = (bytes) => {
|
|
6203
|
-
let crc = 4294967295;
|
|
6204
|
-
for (let i = 0; i < bytes.length; i += 1) crc = CRC_TABLE[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
|
|
6205
|
-
return (crc ^ 4294967295) >>> 0;
|
|
6206
|
-
};
|
|
6207
|
-
const chunkLength = (view, offset) => view.getUint32(offset);
|
|
6208
|
-
const chunkType = (bytes, offset) => String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
|
|
6209
|
-
const PHYS_DATA = (() => {
|
|
6210
|
-
const data = /* @__PURE__ */ new Uint8Array(9);
|
|
6211
|
-
const view = new DataView(data.buffer);
|
|
6212
|
-
view.setUint32(0, PIXELS_PER_METRE_72DPI);
|
|
6213
|
-
view.setUint32(4, PIXELS_PER_METRE_72DPI);
|
|
6214
|
-
data[8] = 1;
|
|
6215
|
-
return data;
|
|
6216
|
-
})();
|
|
6217
|
-
/**
|
|
6218
|
-
* Ensure a PNG carries exactly one `pHYs` chunk at 72 DPI, inserted after
|
|
6219
|
-
* IHDR. Every pre-existing `pHYs` chunk (however many a malformed input
|
|
6220
|
-
* carried) is dropped so the emitted density cannot drift.
|
|
6221
|
-
*/
|
|
6222
|
-
const pngWithDensity = (png) => {
|
|
6223
|
-
const view = new DataView(png.buffer, png.byteOffset, png.byteLength);
|
|
6224
|
-
let offset = 8;
|
|
6225
|
-
let ihdrEnd = -1;
|
|
6226
|
-
const segments = [];
|
|
6227
|
-
const tail = [];
|
|
6228
|
-
while (offset + 12 <= png.length) {
|
|
6229
|
-
const length = chunkLength(view, offset);
|
|
6230
|
-
const type = chunkType(png, offset + 4);
|
|
6231
|
-
const chunkEnd = offset + 12 + length;
|
|
6232
|
-
if (type === "IHDR") {
|
|
6233
|
-
ihdrEnd = chunkEnd;
|
|
6234
|
-
segments.push(png.subarray(offset, chunkEnd));
|
|
6235
|
-
} else if (type !== "pHYs") (ihdrEnd === -1 ? segments : tail).push(png.subarray(offset, chunkEnd));
|
|
6236
|
-
offset = chunkEnd;
|
|
6237
|
-
if (type === "IEND") break;
|
|
6238
|
-
}
|
|
6239
|
-
if (ihdrEnd === -1) throw new Error("pngWithDensity: IHDR chunk not found");
|
|
6240
|
-
const chunk = new Uint8Array(12 + PHYS_DATA.length);
|
|
6241
|
-
const chunkView = new DataView(chunk.buffer);
|
|
6242
|
-
chunkView.setUint32(0, PHYS_DATA.length);
|
|
6243
|
-
chunk.set(new TextEncoder().encode("pHYs"), 4);
|
|
6244
|
-
chunk.set(PHYS_DATA, 8);
|
|
6245
|
-
chunkView.setUint32(8 + PHYS_DATA.length, crc32(chunk.subarray(4, 8 + PHYS_DATA.length)));
|
|
6246
|
-
return concat(png.subarray(0, 8), ...segments, chunk, ...tail);
|
|
6247
|
-
};
|
|
6248
|
-
const concat = (...parts) => {
|
|
6249
|
-
const total = parts.reduce((sum, part) => sum + part.byteLength, 0);
|
|
6250
|
-
const out = new Uint8Array(total);
|
|
6251
|
-
let offset = 0;
|
|
6252
|
-
for (const part of parts) {
|
|
6253
|
-
out.set(part, offset);
|
|
6254
|
-
offset += part.byteLength;
|
|
6255
|
-
}
|
|
6256
|
-
return out;
|
|
6257
|
-
};
|
|
6258
|
-
/** Encode RGBA pixels to a density-carrying PNG. */
|
|
6259
|
-
const encodeDensePng = async (image) => pngWithDensity(await encodeImagePng(image));
|
|
6260
|
-
/** Encode the ten-tag ICNS from per-size PNG payloads (macOS variant source). */
|
|
6261
|
-
const encodeIcns = async (pngAt) => {
|
|
6262
|
-
const icns = new IconIcns();
|
|
6263
|
-
icns.toc = true;
|
|
6264
|
-
for (const { tag, size } of ICNS_REPRESENTATIONS) await icns.addFromPng(await pngAt(size), [tag], false);
|
|
6265
|
-
return icns.encode();
|
|
6266
|
-
};
|
|
6267
|
-
/** Encode the ICO ladder from per-size PNG payloads (Windows variant source). */
|
|
6268
|
-
const encodeIco = async (pngAt) => {
|
|
6269
|
-
const ico = new IconIco();
|
|
6270
|
-
for (const size of ICO_SIZES) await ico.addFromPng(await pngAt(size), null, false);
|
|
6271
|
-
return ico.encode();
|
|
6272
|
-
};
|
|
6273
|
-
//#endregion
|
|
6274
6082
|
//#region ../icon/src/generate.ts
|
|
6275
6083
|
const ICON_SIZE = 1024;
|
|
6276
6084
|
const TILE_INSET = 64;
|
|
@@ -6709,7 +6517,7 @@ const composeAppIcon = async (options) => {
|
|
|
6709
6517
|
pasteImage(macOSVariant, macOSContent, macOSMargin, macOSMargin);
|
|
6710
6518
|
const writeVariant = async (image, suffix) => {
|
|
6711
6519
|
const path = join(compositionDir, `app-composited${suffix}.png`);
|
|
6712
|
-
const { encodeDensePng } = await
|
|
6520
|
+
const { encodeDensePng } = await import("./encode-CTEluLqt.mjs").then((n) => n.a);
|
|
6713
6521
|
await writeFile(path, await encodeDensePng(image));
|
|
6714
6522
|
return path;
|
|
6715
6523
|
};
|
|
@@ -7043,19 +6851,13 @@ const defaultFetch = {
|
|
|
7043
6851
|
if (response === void 0) return {
|
|
7044
6852
|
ok: false,
|
|
7045
6853
|
status: 0,
|
|
7046
|
-
body: ""
|
|
7047
|
-
headers: {}
|
|
6854
|
+
body: ""
|
|
7048
6855
|
};
|
|
7049
6856
|
const body = await response.text();
|
|
7050
|
-
const headers = {};
|
|
7051
|
-
response.headers.forEach((value, key) => {
|
|
7052
|
-
headers[key.toLowerCase()] = value;
|
|
7053
|
-
});
|
|
7054
6857
|
return {
|
|
7055
6858
|
ok: response.ok,
|
|
7056
6859
|
status: response.status,
|
|
7057
|
-
body
|
|
7058
|
-
headers
|
|
6860
|
+
body
|
|
7059
6861
|
};
|
|
7060
6862
|
},
|
|
7061
6863
|
async bytes(url, timeoutMs = 5e3) {
|
|
@@ -7078,22 +6880,6 @@ const defaultFetch = {
|
|
|
7078
6880
|
/** Cap on downloaded candidates per scrape. */
|
|
7079
6881
|
const MAX_ICON_DOWNLOADS = 8;
|
|
7080
6882
|
/**
|
|
7081
|
-
* Whether the page's response headers permit embedding by a third-party
|
|
7082
|
-
* origin (the toolbar wrapper): rejects on X-Frame-Options (except
|
|
7083
|
-
* ALLOWALL) and on CSP frame-ancestors without a wildcard. Absent both
|
|
7084
|
-
* policies, embedding is allowed by default — same default browsers use.
|
|
7085
|
-
*/
|
|
7086
|
-
const responseHeadersAllowEmbedding = (headers) => {
|
|
7087
|
-
const xfo = (headers["x-frame-options"] ?? "").trim().toUpperCase();
|
|
7088
|
-
if (xfo.length > 0 && !xfo.split(",").map((token) => token.trim()).includes("ALLOWALL")) return false;
|
|
7089
|
-
const csp = (headers["content-security-policy"] ?? "").toLowerCase();
|
|
7090
|
-
for (const match of csp.matchAll(/frame-ancestors\s+([^;]+)/gu)) {
|
|
7091
|
-
const sources = (match[1] ?? "").trim();
|
|
7092
|
-
if (sources.length === 0 || !sources.split(/\s+/u).includes("*")) return false;
|
|
7093
|
-
}
|
|
7094
|
-
return true;
|
|
7095
|
-
};
|
|
7096
|
-
/**
|
|
7097
6883
|
* Scrape title and ALL icon candidates from an arbitrary http(s) URL.
|
|
7098
6884
|
* Never throws: failures return `ok: false` with whatever partial identity
|
|
7099
6885
|
* was found. (`scrapeService` is the loopback-port wrapper over this.)
|
|
@@ -7151,8 +6937,7 @@ const scrapeUrl = async (url, options = {}) => {
|
|
|
7151
6937
|
title,
|
|
7152
6938
|
iconPath: originals[0]?.path,
|
|
7153
6939
|
...originals[0] === void 0 ? {} : { iconUrl: originals[0].url },
|
|
7154
|
-
icons
|
|
7155
|
-
frameEmbeddable: responseHeadersAllowEmbedding(page.headers)
|
|
6940
|
+
icons
|
|
7156
6941
|
};
|
|
7157
6942
|
};
|
|
7158
6943
|
/**
|
|
@@ -7169,8 +6954,7 @@ const deriveUrlPresets = async (url, options = {}) => {
|
|
|
7169
6954
|
const appName = title !== void 0 && title.length > 0 ? title.length > PRESET_TITLE_MAX ? `${title.slice(0, PRESET_TITLE_MAX - 1).trimEnd()}…` : title : void 0;
|
|
7170
6955
|
return {
|
|
7171
6956
|
...appName === void 0 ? {} : { appName },
|
|
7172
|
-
...scraped.iconPath === void 0 ? {} : { appIconPath: scraped.iconPath }
|
|
7173
|
-
...scraped.frameEmbeddable === void 0 ? {} : { frameEmbeddable: scraped.frameEmbeddable }
|
|
6957
|
+
...scraped.iconPath === void 0 ? {} : { appIconPath: scraped.iconPath }
|
|
7174
6958
|
};
|
|
7175
6959
|
};
|
|
7176
6960
|
/**
|
|
@@ -7280,8 +7064,8 @@ const isIcoContainer = (bytes) => bytes.length >= 8 && bytes[0] === 0 && bytes[1
|
|
|
7280
7064
|
* from the decoder's RGBA through the shared kernel encoder.
|
|
7281
7065
|
*/
|
|
7282
7066
|
const extractLargestIcoFrame = async (ico) => {
|
|
7283
|
-
const decodeIco = (await import("./decode-ico-
|
|
7284
|
-
const { toPngBuffer } = await
|
|
7067
|
+
const decodeIco = (await import("./decode-ico-DDEIV7iZ.mjs").then((m) => /* @__PURE__ */ __toESM$1(m.default, 1))).default;
|
|
7068
|
+
const { toPngBuffer } = await import("./icon-codec-Bs3XehqZ.mjs").then((n) => n.t);
|
|
7285
7069
|
let best;
|
|
7286
7070
|
for (const frame of decodeIco(ico)) {
|
|
7287
7071
|
const width = frame.width;
|
|
@@ -7391,21 +7175,6 @@ const writeIconTemp = async (bytes, dir) => {
|
|
|
7391
7175
|
return path;
|
|
7392
7176
|
};
|
|
7393
7177
|
//#endregion
|
|
7394
|
-
//#region packages/core/src/icon-codec.ts
|
|
7395
|
-
var icon_codec_exports = /* @__PURE__ */ __exportAll$1({ toPngBuffer: () => toPngBuffer });
|
|
7396
|
-
const toPngBuffer = async (rgba, width, height, channels) => {
|
|
7397
|
-
const pixels = width * height;
|
|
7398
|
-
const image = emptyImageOf(width, height);
|
|
7399
|
-
if (channels === 3) for (let i = 0; i < pixels; i += 1) {
|
|
7400
|
-
image.data[i * 4] = rgba[i * 3] ?? 0;
|
|
7401
|
-
image.data[i * 4 + 1] = rgba[i * 3 + 1] ?? 0;
|
|
7402
|
-
image.data[i * 4 + 2] = rgba[i * 3 + 2] ?? 0;
|
|
7403
|
-
image.data[i * 4 + 3] = 255;
|
|
7404
|
-
}
|
|
7405
|
-
else image.data.set(rgba.subarray(0, pixels * 4));
|
|
7406
|
-
return Buffer.from(await encodeImagePng(image));
|
|
7407
|
-
};
|
|
7408
|
-
//#endregion
|
|
7409
7178
|
//#region packages/core/src/command-run.ts
|
|
7410
7179
|
const SHELL_METACHARS = /[<>&|;$`"'%]/u;
|
|
7411
7180
|
/** True when the command line needs a shell (only Windows uses cmd /c). */
|
|
@@ -7811,6 +7580,113 @@ const runExecFileCapture = (command, args) => new Promise((resolve, reject) => {
|
|
|
7811
7580
|
});
|
|
7812
7581
|
});
|
|
7813
7582
|
//#endregion
|
|
7583
|
+
//#region packages/core/src/toolbar-carrier.ts
|
|
7584
|
+
/**
|
|
7585
|
+
* The generated-entry helper source. The embedding template supplies
|
|
7586
|
+
* `column`/`fixed`/`grow` (imported from "@opentray/ext-webview" beside
|
|
7587
|
+
* `WebviewExt`) — the carrier deliberately performs no imports of its own so
|
|
7588
|
+
* both templates stay single-import consumers of the facade.
|
|
7589
|
+
*/
|
|
7590
|
+
const toolbarCarrierSource = () => `// Toolbar navigation carrier (add-webview-orchestration plan D12/D13):
|
|
7591
|
+
// composes the native navigation toolbar over one content webview and drives
|
|
7592
|
+
// it through the create-private message channel. Channel failure is an
|
|
7593
|
+
// exposed bug (D12: no fallback path) — errors land in app.log through
|
|
7594
|
+
// options.log instead of being silently swallowed.
|
|
7595
|
+
const attachToolbarCarrier = async (shell, options) => {
|
|
7596
|
+
// Per-child bridge policy (D2): the toolbar page is trusted shell UI and
|
|
7597
|
+
// gets exactly the channel surface; the content webview gets NO policy, so
|
|
7598
|
+
// it is bridgeless regardless of what it loads.
|
|
7599
|
+
await shell.createWebview({
|
|
7600
|
+
id: "toolbar",
|
|
7601
|
+
url: options.toolbarUrl,
|
|
7602
|
+
bridge: { webviewId: true, messageChannels: true },
|
|
7603
|
+
});
|
|
7604
|
+
const content = await shell.createWebview({ id: "content", url: options.contentUrl });
|
|
7605
|
+
// Declarative layered layout (D8 sugar): toolbar pinned at a fixed 44px
|
|
7606
|
+
// height, content filling the remainder; resize is recomputed natively.
|
|
7607
|
+
await shell.setLayout(column([fixed("toolbar", 44), grow("content")]));
|
|
7608
|
+
const channel = await shell.createMessageChannel({ target: "toolbar" });
|
|
7609
|
+
const note = (error) => {
|
|
7610
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
7611
|
+
void options.log("toolbar carrier: " + message);
|
|
7612
|
+
};
|
|
7613
|
+
// Address-bar truth (D13): the content webview's urlChange events ARE the
|
|
7614
|
+
// address bar's source of truth — pushed over the channel, never scraped
|
|
7615
|
+
// from a wrapper document.
|
|
7616
|
+
const pushUrl = async (url) => {
|
|
7617
|
+
await channel.post({ kind: "url", url: String(url) });
|
|
7618
|
+
};
|
|
7619
|
+
content.onUrlChange((event) => {
|
|
7620
|
+
void pushUrl(event.url).catch(note);
|
|
7621
|
+
});
|
|
7622
|
+
// Loading affordance input (D24): the content webview's loadState pushes
|
|
7623
|
+
// (phase always; progress/errorCode best-effort) reach the toolbar page
|
|
7624
|
+
// over the same channel. The toolbar page owns the progress-bar state
|
|
7625
|
+
// machine — this carrier forwards verbatim and adds no interpretation, so
|
|
7626
|
+
// a missed "failed" frame (macOS dead-port loads finish about:blank
|
|
7627
|
+
// honestly) is converged by whatever terminal frame does arrive.
|
|
7628
|
+
const pushLoadState = async (event) => {
|
|
7629
|
+
const frame = { kind: "load-state", phase: event.phase, url: String(event.url) };
|
|
7630
|
+
if (typeof event.progress === "number") frame.progress = event.progress;
|
|
7631
|
+
if (typeof event.errorCode === "number") frame.errorCode = event.errorCode;
|
|
7632
|
+
await channel.post(frame);
|
|
7633
|
+
};
|
|
7634
|
+
content.onLoadState((event) => {
|
|
7635
|
+
void pushLoadState(event).catch(note);
|
|
7636
|
+
});
|
|
7637
|
+
if (options.titleFollows === true) {
|
|
7638
|
+
// Sync defaults project onto the CONTENT document (the real target page).
|
|
7639
|
+
// A windowOnly session has no primary webview for native titleSync, and
|
|
7640
|
+
// the v1 frozen facade exposes no host set-title command, so the entry is
|
|
7641
|
+
// the one-way projector through the native re-show title update
|
|
7642
|
+
// (apply_reused_show_updates): show({title, windowOnly}) on an existing
|
|
7643
|
+
// session updates the window title without touching content or
|
|
7644
|
+
// visibility. Icon following stays opt-in and carries no window-level
|
|
7645
|
+
// option here because a bridgeless content child is not a favicon
|
|
7646
|
+
// emitter (documented v1 limitation).
|
|
7647
|
+
const projectTitle = (title) => {
|
|
7648
|
+
if (typeof title === "string" && title.length > 0) {
|
|
7649
|
+
void shell.show({ title, windowOnly: true }).catch(note);
|
|
7650
|
+
}
|
|
7651
|
+
};
|
|
7652
|
+
content.onTitleChange((event) => {
|
|
7653
|
+
projectTitle(event.title);
|
|
7654
|
+
});
|
|
7655
|
+
}
|
|
7656
|
+
// Command surface (D12): instructions and state queries arrive as channel
|
|
7657
|
+
// payloads; back/forward drive the content webview's NATIVE history and
|
|
7658
|
+
// reload re-navigates to the current URL through the native navigate
|
|
7659
|
+
// command (never evaluate(location.reload()) on a wrapper).
|
|
7660
|
+
channel.onMessage((payload) => {
|
|
7661
|
+
const message = typeof payload === "object" && payload !== null ? payload : {};
|
|
7662
|
+
const kind = message.kind;
|
|
7663
|
+
if (kind === "navigate" && typeof message.url === "string" && message.url.length > 0) {
|
|
7664
|
+
void content.navigate(message.url).catch(note);
|
|
7665
|
+
return;
|
|
7666
|
+
}
|
|
7667
|
+
if (kind === "back") {
|
|
7668
|
+
void content.back().catch(note);
|
|
7669
|
+
return;
|
|
7670
|
+
}
|
|
7671
|
+
if (kind === "forward") {
|
|
7672
|
+
void content.forward().catch(note);
|
|
7673
|
+
return;
|
|
7674
|
+
}
|
|
7675
|
+
if (kind === "reload") {
|
|
7676
|
+
void content.getUrl().then((state) => content.navigate(state.url)).catch(note);
|
|
7677
|
+
return;
|
|
7678
|
+
}
|
|
7679
|
+
if (kind === "get-url") {
|
|
7680
|
+
void content.getUrl().then((state) => pushUrl(state.url)).catch(note);
|
|
7681
|
+
}
|
|
7682
|
+
});
|
|
7683
|
+
// Seed the address bar with the current truth (pre-subscription buffering
|
|
7684
|
+
// on both endpoints covers the race before the toolbar page registers).
|
|
7685
|
+
void content.getUrl().then((state) => pushUrl(state.url)).catch(note);
|
|
7686
|
+
return { content, channel };
|
|
7687
|
+
};
|
|
7688
|
+
`;
|
|
7689
|
+
//#endregion
|
|
7814
7690
|
//#region packages/core/src/entry-template.ts
|
|
7815
7691
|
/** The generated app entry: supervises the command, owns tray + window. */
|
|
7816
7692
|
const createEntrySource = (config) => {
|
|
@@ -7826,7 +7702,7 @@ import { dirname, resolve } from "node:path";
|
|
|
7826
7702
|
import { fileURLToPath } from "node:url";
|
|
7827
7703
|
|
|
7828
7704
|
import { createTray } from "opentray";
|
|
7829
|
-
import { WebviewExt } from "@opentray/ext-webview";
|
|
7705
|
+
import { WebviewExt, column, fixed, grow } from "@opentray/ext-webview";
|
|
7830
7706
|
|
|
7831
7707
|
const PROJECT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7832
7708
|
|
|
@@ -7847,11 +7723,20 @@ const config = ${JSON.stringify(config, null, 2)};
|
|
|
7847
7723
|
const appLogPath = resolve(PROJECT_DIR, "app.log");
|
|
7848
7724
|
await mkdir(dirname(appLogPath), { recursive: true });
|
|
7849
7725
|
const logSink = appendFile.bind(undefined, appLogPath);
|
|
7726
|
+
const logNote = (message) => { void logSink("[create-opentray] " + message + "\\n", "utf8"); };
|
|
7850
7727
|
const sleep = (ms) => new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
7851
7728
|
|
|
7852
7729
|
const shellOptions = ${JSON.stringify(config.shell ?? null)};
|
|
7853
7730
|
const showTerminal = shellOptions !== null && shellOptions.showTerminal === true;
|
|
7854
|
-
|
|
7731
|
+
// Toolbar carrier (add-webview-orchestration D13): window.toolbar is the one
|
|
7732
|
+
// canonical toolbar fact for BOTH application forms; the retired legacy shell
|
|
7733
|
+
// address-bar input is deliberately not read (stale frozen occurrences are
|
|
7734
|
+
// ignored by loose parsing).
|
|
7735
|
+
const toolbarMode = config.window.toolbar === true;
|
|
7736
|
+
const titleFollows = config.window.titleFollowsDocument !== false;
|
|
7737
|
+
const iconFollows = config.window.iconFollowsDocument === true;
|
|
7738
|
+
|
|
7739
|
+
${toolbarCarrierSource()}
|
|
7855
7740
|
|
|
7856
7741
|
// Configured env overlay (advanced command options); empty by default.
|
|
7857
7742
|
const commandEnv = ${JSON.stringify(config.command.env ?? {})};
|
|
@@ -7923,7 +7808,8 @@ const main = async () => {
|
|
|
7923
7808
|
}
|
|
7924
7809
|
|
|
7925
7810
|
// Shell host (D2): unconditional — the terminal window is every app's
|
|
7926
|
-
// abnormal-exit surface, and the
|
|
7811
|
+
// abnormal-exit surface, and the toolbar carrier's page asset is served
|
|
7812
|
+
// from the same host (its navigation surface stays channel-only, D12).
|
|
7927
7813
|
const shellApi = await import("./app-shell-server.mjs");
|
|
7928
7814
|
const shellPort = await shellApi.listenShell();
|
|
7929
7815
|
|
|
@@ -8023,11 +7909,11 @@ const main = async () => {
|
|
|
8023
7909
|
const devtools = config.developerMode === true ? { devtools: true } : {};
|
|
8024
7910
|
|
|
8025
7911
|
// Dedicated windows: one terminal window (always available; initially
|
|
8026
|
-
// visible only under showTerminal), one window per listened port —
|
|
8027
|
-
//
|
|
8028
|
-
// Each window gets its OWN extension mount (tray.extend per
|
|
8029
|
-
//
|
|
8030
|
-
//
|
|
7912
|
+
// visible only under showTerminal), one window per listened port — the
|
|
7913
|
+
// native toolbar carrier when window.toolbar is on, the direct service URL
|
|
7914
|
+
// otherwise. Each window gets its OWN extension mount (tray.extend per
|
|
7915
|
+
// window = its own window session), so dedicated windows never collide on
|
|
7916
|
+
// content.
|
|
8031
7917
|
const serviceWindows = new Map();
|
|
8032
7918
|
let terminalWindow = null;
|
|
8033
7919
|
|
|
@@ -8060,28 +7946,45 @@ const main = async () => {
|
|
|
8060
7946
|
const ensureServiceWindow = async (port) => {
|
|
8061
7947
|
if (serviceWindows.has(port)) return;
|
|
8062
7948
|
const direct = \`http://127.0.0.1:\${port}\`;
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
7949
|
+
// Toolbar service windows (D13): the SAME native carrier URL applications
|
|
7950
|
+
// use — a windowOnly session whose toolbar webview (shell-served
|
|
7951
|
+
// toolbar page) sits above a content webview loading the verified
|
|
7952
|
+
// service URL directly (no iframe wrapper, no bridge on the content
|
|
7953
|
+
// side, no embedding-policy consultation). Direct service windows keep
|
|
7954
|
+
// the D13 native sync defaults unchanged.
|
|
7955
|
+
if (toolbarMode && shellPort !== null) {
|
|
7956
|
+
const win = tray.extend(WebviewExt).createWebviewWindow({
|
|
7957
|
+
windowOnly: true,
|
|
7958
|
+
width: config.window.width,
|
|
7959
|
+
height: config.window.height,
|
|
7960
|
+
title: baseTitle,
|
|
7961
|
+
style: { appMode: true, autoHide: false, keepOnTop: false },
|
|
7962
|
+
...devtools,
|
|
7963
|
+
});
|
|
7964
|
+
await win.show().catch(() => {});
|
|
7965
|
+
await attachToolbarCarrier(win, {
|
|
7966
|
+
toolbarUrl: \`http://127.0.0.1:\${shellPort}/toolbar.html\`,
|
|
7967
|
+
contentUrl: direct,
|
|
7968
|
+
titleFollows,
|
|
7969
|
+
log: logNote,
|
|
7970
|
+
});
|
|
7971
|
+
serviceWindows.set(port, { win, detached: false });
|
|
7972
|
+
return;
|
|
7973
|
+
}
|
|
8066
7974
|
const win = tray.extend(WebviewExt).createWebviewWindow({
|
|
8067
|
-
url,
|
|
7975
|
+
url: direct,
|
|
8068
7976
|
width: config.window.width,
|
|
8069
7977
|
height: config.window.height,
|
|
8070
7978
|
title: baseTitle,
|
|
8071
7979
|
style: { appMode: true, autoHide: false, keepOnTop: false },
|
|
8072
7980
|
...devtools,
|
|
8073
|
-
...(
|
|
8074
|
-
...(
|
|
7981
|
+
...(!titleFollows ? {} : { titleSync: { documentToWindow: true } }),
|
|
7982
|
+
...(!iconFollows ? {} : { iconSync: { faviconToWindow: true } }),
|
|
8075
7983
|
});
|
|
8076
7984
|
serviceWindows.set(port, { win, detached: false });
|
|
8077
7985
|
await win.show().catch(() => {});
|
|
8078
7986
|
};
|
|
8079
7987
|
|
|
8080
|
-
// D13 sync defaults (address-bar-less service windows): the title follows
|
|
8081
|
-
// the document one-way; runtime favicon following is opt-in only.
|
|
8082
|
-
const titleFollows = config.window.titleFollowsDocument !== false;
|
|
8083
|
-
const iconFollows = config.window.iconFollowsDocument === true;
|
|
8084
|
-
|
|
8085
7988
|
if (showTerminal) {
|
|
8086
7989
|
await ensureTerminalWindow();
|
|
8087
7990
|
}
|
|
@@ -8239,7 +8142,7 @@ main().catch(async (error) => {
|
|
|
8239
8142
|
};
|
|
8240
8143
|
//#endregion
|
|
8241
8144
|
//#region packages/core/src/url-entry-template.ts
|
|
8242
|
-
/** The generated URL app entry: owns tray + one direct or
|
|
8145
|
+
/** The generated URL app entry: owns tray + one direct or toolbar-carrier window. */
|
|
8243
8146
|
const createUrlEntrySource = (config) => {
|
|
8244
8147
|
if (config.url === void 0) throw new Error("URL entry template requires a url source");
|
|
8245
8148
|
return `#!/usr/bin/env node
|
|
@@ -8251,7 +8154,7 @@ import { dirname, resolve } from "node:path";
|
|
|
8251
8154
|
import { fileURLToPath } from "node:url";
|
|
8252
8155
|
|
|
8253
8156
|
import { createTray } from "opentray";
|
|
8254
|
-
import { WebviewExt } from "@opentray/ext-webview";
|
|
8157
|
+
import { WebviewExt, column, fixed, grow } from "@opentray/ext-webview";
|
|
8255
8158
|
|
|
8256
8159
|
const PROJECT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
8257
8160
|
|
|
@@ -8275,8 +8178,9 @@ const iconFollows = config.window.iconFollowsDocument === true;
|
|
|
8275
8178
|
const appLogPath = resolve(PROJECT_DIR, "app.log");
|
|
8276
8179
|
await mkdir(dirname(appLogPath), { recursive: true });
|
|
8277
8180
|
const logSink = appendFile.bind(undefined, appLogPath);
|
|
8181
|
+
const logNote = (message) => { void logSink("[create-opentray] " + message + "\\n", "utf8"); };
|
|
8278
8182
|
|
|
8279
|
-
const main = async () => {
|
|
8183
|
+
${toolbarCarrierSource()}const main = async () => {
|
|
8280
8184
|
let appIcon;
|
|
8281
8185
|
try {
|
|
8282
8186
|
const manifest = JSON.parse(await readFile(resolve(PROJECT_DIR, "app-icon", "app-icon.json"), "utf8"));
|
|
@@ -8326,42 +8230,59 @@ const main = async () => {
|
|
|
8326
8230
|
// must not request devtools at all — default windows are not inspectable.
|
|
8327
8231
|
const devtools = config.developerMode === true ? { devtools: true } : {};
|
|
8328
8232
|
|
|
8329
|
-
// Toolbar
|
|
8330
|
-
//
|
|
8331
|
-
//
|
|
8233
|
+
// Toolbar shell host (D12): the shell server serves the toolbar page (and
|
|
8234
|
+
// nothing else for a URL app — its navigation surface is channel-only);
|
|
8235
|
+
// no PTY is registered because a URL app supervises nothing. This is a
|
|
8236
|
+
// LOCAL transport dependency only — never an embedding-policy fallback
|
|
8237
|
+
// (embedding policy is not consulted at all, D14).
|
|
8332
8238
|
let shellPort = null;
|
|
8333
8239
|
if (toolbarMode) {
|
|
8334
8240
|
const shellApi = await import("./app-shell-server.mjs");
|
|
8335
8241
|
shellPort = await shellApi.listenShell();
|
|
8336
8242
|
if (shellPort === null) {
|
|
8337
|
-
await logSink("[create-opentray] toolbar shell host failed to listen;
|
|
8243
|
+
await logSink("[create-opentray] toolbar shell host failed to listen; opening the direct window\\n", "utf8");
|
|
8338
8244
|
}
|
|
8339
8245
|
}
|
|
8340
|
-
const
|
|
8341
|
-
? \`http://127.0.0.1:\${shellPort}/browse.html?url=\${encodeURIComponent(config.url)}\`
|
|
8342
|
-
: null;
|
|
8246
|
+
const toolbarReady = toolbarMode && shellPort !== null;
|
|
8343
8247
|
|
|
8248
|
+
// Toolbar carrier (D12/D13): windowOnly session composed of the toolbar +
|
|
8249
|
+
// content webviews through attachToolbarCarrier below. Direct mode keeps
|
|
8250
|
+
// the single-webview window with the native sync defaults (D13): the title
|
|
8251
|
+
// follows the document one-way; icon following is opt-in.
|
|
8344
8252
|
const window = tray.extend(WebviewExt).createWebviewWindow({
|
|
8345
|
-
|
|
8253
|
+
...(toolbarReady ? { windowOnly: true } : { url: config.url }),
|
|
8346
8254
|
width: config.window.width,
|
|
8347
8255
|
height: config.window.height,
|
|
8348
8256
|
title: config.appName,
|
|
8349
8257
|
style: { appMode: true, autoHide: false, keepOnTop: false },
|
|
8350
8258
|
...devtools,
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
8355
|
-
...(titleFollows ? { titleSync: { documentToWindow: true } } : {}),
|
|
8356
|
-
...(iconFollows ? { iconSync: { faviconToWindow: true } } : {}),
|
|
8357
|
-
}
|
|
8358
|
-
: {}),
|
|
8259
|
+
...(toolbarReady ? {} : {
|
|
8260
|
+
...(titleFollows ? { titleSync: { documentToWindow: true } } : {}),
|
|
8261
|
+
...(iconFollows ? { iconSync: { faviconToWindow: true } } : {}),
|
|
8262
|
+
}),
|
|
8359
8263
|
});
|
|
8360
8264
|
await window.show().catch(() => {});
|
|
8361
8265
|
|
|
8362
|
-
|
|
8266
|
+
let content = null;
|
|
8267
|
+
if (toolbarReady) {
|
|
8268
|
+
({ content } = await attachToolbarCarrier(window, {
|
|
8269
|
+
toolbarUrl: \`http://127.0.0.1:\${shellPort}/toolbar.html\`,
|
|
8270
|
+
contentUrl: config.url,
|
|
8271
|
+
titleFollows,
|
|
8272
|
+
log: logNote,
|
|
8273
|
+
}));
|
|
8274
|
+
}
|
|
8275
|
+
|
|
8276
|
+
// Tray reload (D11): toolbar mode reloads the CONTENT webview natively
|
|
8277
|
+
// (navigate to its current URL — no wrapper evaluate); direct mode reloads
|
|
8278
|
+
// the page in place through the evaluate channel. The app never restarts.
|
|
8363
8279
|
const reload = async () => {
|
|
8364
8280
|
try {
|
|
8281
|
+
if (content !== null) {
|
|
8282
|
+
const { url } = await content.getUrl();
|
|
8283
|
+
await content.navigate(url);
|
|
8284
|
+
return;
|
|
8285
|
+
}
|
|
8365
8286
|
await window.evaluate("location.reload()");
|
|
8366
8287
|
} catch { /* window gone */ }
|
|
8367
8288
|
};
|
|
@@ -8594,14 +8515,14 @@ const writeScaffold = async (options) => {
|
|
|
8594
8515
|
};
|
|
8595
8516
|
};
|
|
8596
8517
|
/**
|
|
8597
|
-
* Locate the adapter-staged shell UI (terminal.html/
|
|
8518
|
+
* Locate the adapter-staged shell UI (terminal.html/toolbar.html + assets).
|
|
8598
8519
|
* Layouts, in order:
|
|
8599
8520
|
* - published create-opentray: `dist/shell` beside the package root (resolved
|
|
8600
8521
|
* through the package self-reference; core is bundled into that dist);
|
|
8601
8522
|
* - source checkout: `<create root>/dist/shell` (built) or the create-webui
|
|
8602
8523
|
* vite output one workspace up.
|
|
8603
8524
|
* Undefined means "no staged assets found" — the entry still runs; only the
|
|
8604
|
-
* optional terminal/
|
|
8525
|
+
* optional terminal/toolbar pages would 404.
|
|
8605
8526
|
*/
|
|
8606
8527
|
const resolveBundledShellAssetsDir = async () => {
|
|
8607
8528
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -13874,7 +13795,8 @@ const readWizardProjectConfig = async (projectDir) => {
|
|
|
13874
13795
|
const url = typeof raw.url === "string" ? raw.url : void 0;
|
|
13875
13796
|
const window = isRecord(raw.window) ? {
|
|
13876
13797
|
width: Number.isFinite(raw.window.width) ? Number(raw.window.width) : 1200,
|
|
13877
|
-
height: Number.isFinite(raw.window.height) ? Number(raw.window.height) : 800
|
|
13798
|
+
height: Number.isFinite(raw.window.height) ? Number(raw.window.height) : 800,
|
|
13799
|
+
...raw.window.toolbar === true ? { toolbar: true } : {}
|
|
13878
13800
|
} : {
|
|
13879
13801
|
width: 1200,
|
|
13880
13802
|
height: 800
|
|
@@ -14793,7 +14715,6 @@ const createWizardSession = (options) => {
|
|
|
14793
14715
|
let currentTokens = [];
|
|
14794
14716
|
let currentCommand = "";
|
|
14795
14717
|
let urlSource;
|
|
14796
|
-
let urlFrameEmbeddable;
|
|
14797
14718
|
let scrapedTitle;
|
|
14798
14719
|
let interactive = true;
|
|
14799
14720
|
let uiLocale = "zh-CN";
|
|
@@ -14820,7 +14741,7 @@ const createWizardSession = (options) => {
|
|
|
14820
14741
|
pm: false,
|
|
14821
14742
|
force: false,
|
|
14822
14743
|
showStartupTerminal: false,
|
|
14823
|
-
|
|
14744
|
+
toolbar: false,
|
|
14824
14745
|
imageSmoothingEnabled: false,
|
|
14825
14746
|
developerMode: false
|
|
14826
14747
|
};
|
|
@@ -14833,7 +14754,7 @@ const createWizardSession = (options) => {
|
|
|
14833
14754
|
trayIconPath: "",
|
|
14834
14755
|
force: options.force === true,
|
|
14835
14756
|
showStartupTerminal: false,
|
|
14836
|
-
|
|
14757
|
+
toolbar: false,
|
|
14837
14758
|
imageSmoothingEnabled: true,
|
|
14838
14759
|
developerMode: false,
|
|
14839
14760
|
pm: options.packageManager ?? detectPackageManager([], process.env.npm_config_user_agent)
|
|
@@ -15179,7 +15100,6 @@ const createWizardSession = (options) => {
|
|
|
15179
15100
|
const trimmed = url.trim();
|
|
15180
15101
|
if (trimmed.length === 0) {
|
|
15181
15102
|
urlSource = void 0;
|
|
15182
|
-
urlFrameEmbeddable = void 0;
|
|
15183
15103
|
scrapedTitle = void 0;
|
|
15184
15104
|
iconCandidates = [];
|
|
15185
15105
|
iconPort = void 0;
|
|
@@ -15217,7 +15137,6 @@ const createWizardSession = (options) => {
|
|
|
15217
15137
|
currentIconPath = scraped.ok ? scraped.icons[0]?.path : void 0;
|
|
15218
15138
|
currentIconUrl = scraped.ok ? scraped.icons[0]?.url : void 0;
|
|
15219
15139
|
scrapedTitle = scraped.ok ? scraped.title : void 0;
|
|
15220
|
-
urlFrameEmbeddable = scraped.ok ? scraped.frameEmbeddable : void 0;
|
|
15221
15140
|
stopTimers();
|
|
15222
15141
|
setState("discovered");
|
|
15223
15142
|
emit({
|
|
@@ -15292,7 +15211,7 @@ const createWizardSession = (options) => {
|
|
|
15292
15211
|
...touched.force ? { force: form.force } : { force: options.force === true },
|
|
15293
15212
|
...touched.trayIconPath ? { trayIconPath: form.trayIconPath } : { trayIconPath: "" },
|
|
15294
15213
|
...touched.showStartupTerminal ? { showStartupTerminal: form.showStartupTerminal } : { showStartupTerminal: false },
|
|
15295
|
-
...touched.
|
|
15214
|
+
...touched.toolbar ? { toolbar: form.toolbar } : { toolbar: false },
|
|
15296
15215
|
...touched.imageSmoothingEnabled ? { imageSmoothingEnabled: form.imageSmoothingEnabled } : { imageSmoothingEnabled: true },
|
|
15297
15216
|
...touched.developerMode ? { developerMode: form.developerMode } : { developerMode: false },
|
|
15298
15217
|
pm: options.packageManager ?? detectPackageManager([], process.env.npm_config_user_agent)
|
|
@@ -15640,7 +15559,7 @@ const createWizardSession = (options) => {
|
|
|
15640
15559
|
window: {
|
|
15641
15560
|
width: 1200,
|
|
15642
15561
|
height: 800,
|
|
15643
|
-
...frozen.
|
|
15562
|
+
...frozen.toolbar === true ? { toolbar: true } : {},
|
|
15644
15563
|
titleFollowsDocument: true,
|
|
15645
15564
|
iconFollowsDocument: false
|
|
15646
15565
|
}
|
|
@@ -15654,6 +15573,7 @@ const createWizardSession = (options) => {
|
|
|
15654
15573
|
window: {
|
|
15655
15574
|
width: 1200,
|
|
15656
15575
|
height: 800,
|
|
15576
|
+
...frozen.toolbar === true ? { toolbar: true } : {},
|
|
15657
15577
|
titleFollowsDocument: true,
|
|
15658
15578
|
iconFollowsDocument: false
|
|
15659
15579
|
}
|
|
@@ -15727,12 +15647,6 @@ const createWizardSession = (options) => {
|
|
|
15727
15647
|
run = void 0;
|
|
15728
15648
|
}
|
|
15729
15649
|
if (urlSource !== void 0) {
|
|
15730
|
-
const toolbarWanted = frozen.showAddressBar === true;
|
|
15731
|
-
const toolbar = toolbarWanted && urlFrameEmbeddable !== false;
|
|
15732
|
-
if (toolbarWanted && !toolbar) emit({
|
|
15733
|
-
type: "materialize-log",
|
|
15734
|
-
message: `warning: ${urlSource} forbids iframe embedding (X-Frame-Options / CSP frame-ancestors); the address bar falls back to the direct window`
|
|
15735
|
-
});
|
|
15736
15650
|
try {
|
|
15737
15651
|
result = await materialize({
|
|
15738
15652
|
config: {
|
|
@@ -15744,7 +15658,7 @@ const createWizardSession = (options) => {
|
|
|
15744
15658
|
window: {
|
|
15745
15659
|
width: 1200,
|
|
15746
15660
|
height: 800,
|
|
15747
|
-
...toolbar ? { toolbar: true } : {},
|
|
15661
|
+
...frozen.toolbar === true ? { toolbar: true } : {},
|
|
15748
15662
|
titleFollowsDocument: true,
|
|
15749
15663
|
iconFollowsDocument: false
|
|
15750
15664
|
},
|
|
@@ -15818,6 +15732,7 @@ const createWizardSession = (options) => {
|
|
|
15818
15732
|
window: {
|
|
15819
15733
|
width: 1200,
|
|
15820
15734
|
height: 800,
|
|
15735
|
+
...frozen.toolbar === true ? { toolbar: true } : {},
|
|
15821
15736
|
titleFollowsDocument: true,
|
|
15822
15737
|
iconFollowsDocument: false
|
|
15823
15738
|
},
|
|
@@ -15830,10 +15745,7 @@ const createWizardSession = (options) => {
|
|
|
15830
15745
|
...frozen.iconScale === void 0 ? {} : { iconScale: frozen.iconScale },
|
|
15831
15746
|
...currentTrayIconPath === void 0 ? {} : { trayIconSourcePath: currentTrayIconPath },
|
|
15832
15747
|
...trayIconIsSolid ? { trayIconIsSolid: true } : {},
|
|
15833
|
-
shell: {
|
|
15834
|
-
showTerminal: frozen.showStartupTerminal,
|
|
15835
|
-
showAddressBar: frozen.showAddressBar
|
|
15836
|
-
},
|
|
15748
|
+
shell: { showTerminal: frozen.showStartupTerminal },
|
|
15837
15749
|
...frozen.imageSmoothingEnabled === false ? { imageSmoothingEnabled: false } : {},
|
|
15838
15750
|
...frozen.developerMode === true ? { developerMode: true } : {},
|
|
15839
15751
|
packageManager: frozen.pm,
|
|
@@ -16037,6 +15949,7 @@ const handleWorkbenchApi = async (request) => {
|
|
|
16037
15949
|
window: {
|
|
16038
15950
|
width: entry.config.window.width,
|
|
16039
15951
|
height: entry.config.window.height,
|
|
15952
|
+
...entry.config.window.toolbar === true ? { toolbar: true } : {},
|
|
16040
15953
|
titleFollowsDocument: true,
|
|
16041
15954
|
iconFollowsDocument: false
|
|
16042
15955
|
},
|
|
@@ -16204,6 +16117,7 @@ const handleWorkbenchApi = async (request) => {
|
|
|
16204
16117
|
window: {
|
|
16205
16118
|
width: entry.config.window.width,
|
|
16206
16119
|
height: entry.config.window.height,
|
|
16120
|
+
...entry.config.window.toolbar === true ? { toolbar: true } : {},
|
|
16207
16121
|
titleFollowsDocument: true,
|
|
16208
16122
|
iconFollowsDocument: false
|
|
16209
16123
|
},
|
|
@@ -16913,7 +16827,7 @@ const handleApi = async (pathname, request, response, session, preReadBody) => {
|
|
|
16913
16827
|
if (typeof body.iconScale === "number" && body.iconScale >= .5 && body.iconScale <= .95) patch.iconScale = body.iconScale;
|
|
16914
16828
|
for (const key of [
|
|
16915
16829
|
"showStartupTerminal",
|
|
16916
|
-
"
|
|
16830
|
+
"toolbar",
|
|
16917
16831
|
"imageSmoothingEnabled",
|
|
16918
16832
|
"developerMode",
|
|
16919
16833
|
"force"
|
|
@@ -17516,17 +17430,11 @@ const runCreate = async (args, context) => {
|
|
|
17516
17430
|
}
|
|
17517
17431
|
let enrichment;
|
|
17518
17432
|
let presetIconPath;
|
|
17519
|
-
|
|
17520
|
-
if (args.url !== void 0 && args.scrape !== false && (args.appName === void 0 || args.appIcon === void 0 || toolbarRequested)) {
|
|
17433
|
+
if (args.url !== void 0 && args.scrape !== false && (args.appName === void 0 || args.appIcon === void 0)) {
|
|
17521
17434
|
const presets = await deriveUrlPresets(args.url);
|
|
17522
17435
|
enrichment = presets;
|
|
17523
17436
|
presetIconPath = presets.appIconPath;
|
|
17524
17437
|
if (presets.appName !== void 0 || presets.appIconPath !== void 0) emitProgress(`scraped defaults from ${args.url}: ${[presets.appName !== void 0 ? "title" : void 0, presets.appIconPath !== void 0 ? "favicon" : void 0].filter(Boolean).join(" + ")} (defaults; explicit flags win)`, context.streams, args.json === true);
|
|
17525
|
-
if (toolbarRequested && presets.frameEmbeddable === false) {
|
|
17526
|
-
toolbarRequested = false;
|
|
17527
|
-
delete flags.toolbar;
|
|
17528
|
-
emitProgress(`warning: ${args.url} forbids iframe embedding (X-Frame-Options / CSP frame-ancestors); falling back to the direct window`, context.streams, args.json === true);
|
|
17529
|
-
}
|
|
17530
17438
|
}
|
|
17531
17439
|
const compiled = await compileDesiredConfig(flags, args.config, cwd, enrichment);
|
|
17532
17440
|
if (!compiled.ok) {
|
|
@@ -17651,7 +17559,7 @@ const createCommand = (context) => ({
|
|
|
17651
17559
|
describe: "window size <width>x<height> (default 1200x800)"
|
|
17652
17560
|
}).option("toolbar", {
|
|
17653
17561
|
type: "boolean",
|
|
17654
|
-
describe: "
|
|
17562
|
+
describe: "compose the native navigation toolbar over the target/service page (URL and command applications; back/forward/reload/address bar)"
|
|
17655
17563
|
}).option("title-follow", {
|
|
17656
17564
|
type: "boolean",
|
|
17657
17565
|
describe: "window title follows document.title (default true; negate with --no-title-follow)"
|
|
@@ -18361,6 +18269,33 @@ const openBrowser = async (url) => {
|
|
|
18361
18269
|
windowsHide: true
|
|
18362
18270
|
}).unref();
|
|
18363
18271
|
};
|
|
18272
|
+
/** Draft form values carry fields across versions — forward only the
|
|
18273
|
+
* known-safe, type-checked subset. Retired fields (the legacy
|
|
18274
|
+
* showAddressBar, replaced by `toolbar` in add-webview-orchestration D13)
|
|
18275
|
+
* are ignored here: a stale draft never resurrects a removed input.
|
|
18276
|
+
* Exported for tests alongside parseWizardCli. */
|
|
18277
|
+
const normalizeDraftForm = (raw) => {
|
|
18278
|
+
if (typeof raw !== "object" || raw === null) return;
|
|
18279
|
+
const source = raw;
|
|
18280
|
+
const patch = {};
|
|
18281
|
+
for (const key of [
|
|
18282
|
+
"appId",
|
|
18283
|
+
"appName",
|
|
18284
|
+
"iconPath",
|
|
18285
|
+
"trayIconPath"
|
|
18286
|
+
]) if (typeof source[key] === "string") patch[key] = source[key];
|
|
18287
|
+
if (source.pm === "npm" || source.pm === "pnpm" || source.pm === "bun") patch.pm = source.pm;
|
|
18288
|
+
if (source.iconBackground === "black" || source.iconBackground === "white" || source.iconBackground === "transparent") patch.iconBackground = source.iconBackground;
|
|
18289
|
+
if (typeof source.iconScale === "number" && source.iconScale >= .5 && source.iconScale <= .95) patch.iconScale = source.iconScale;
|
|
18290
|
+
for (const key of [
|
|
18291
|
+
"showStartupTerminal",
|
|
18292
|
+
"toolbar",
|
|
18293
|
+
"imageSmoothingEnabled",
|
|
18294
|
+
"developerMode",
|
|
18295
|
+
"force"
|
|
18296
|
+
]) if (typeof source[key] === "boolean") patch[key] = source[key];
|
|
18297
|
+
return Object.keys(patch).length > 0 ? patch : void 0;
|
|
18298
|
+
};
|
|
18364
18299
|
const runWebAdapter = async (options) => {
|
|
18365
18300
|
ensureLoopbackNoProxy();
|
|
18366
18301
|
const invocationDir = process.env.INIT_CWD || process.cwd();
|
|
@@ -18388,9 +18323,10 @@ const runWebAdapter = async (options) => {
|
|
|
18388
18323
|
const raw = await readFile(join(tmpdir(), `create-opentray-draft-${port ?? 0}.json`), "utf8");
|
|
18389
18324
|
const parsed = JSON.parse(raw);
|
|
18390
18325
|
const commandOptions = normalizeDraftCommandOptions(parsed.commandOptions);
|
|
18391
|
-
|
|
18326
|
+
const form = parsed.form === void 0 ? void 0 : normalizeDraftForm(parsed.form);
|
|
18327
|
+
if (form === void 0 && parsed.command === void 0 && commandOptions === void 0) return;
|
|
18392
18328
|
return {
|
|
18393
|
-
form
|
|
18329
|
+
form,
|
|
18394
18330
|
command: parsed.command,
|
|
18395
18331
|
commandOptions
|
|
18396
18332
|
};
|
|
@@ -18467,6 +18403,6 @@ if (isMainModule()) main(process.argv.slice(2)).then((code) => {
|
|
|
18467
18403
|
process.exitCode = code;
|
|
18468
18404
|
});
|
|
18469
18405
|
//#endregion
|
|
18470
|
-
export {
|
|
18406
|
+
export { resolveLaunchVector as A, parseLsofPorts as C, verifyHttpService as D, serviceUrl as E, isValidAppId as F, toProjectDirectoryName as I, tokenizeCommandLine as M, deriveDefaultAppId as N, waitForTcpPort as O, deriveDefaultAppName as P, createPortDiscovery as S, parsePowerShellPorts as T, extractTitle as _, createWizardServer as a, resolveFaviconUrl as b, createWizardSession as c, detectPackageManager as d, expectedDarwinBundlePath as f, extractFaviconCandidates as g, writeScaffold as h, parseWizardCli as i, resolveOnPath as j, parseShebangInterpreter as k, openMaterializedApp as l, materialize as m, normalizeDraftForm as n, isAuthorized as o, isDirectoryOccupied$1 as p, openBrowser as r, isLoopbackHost as s, main as t, pinningHint as u, faviconCandidateSize as v, parseNetstatPorts as w, scrapeService as x, rankFaviconCandidates as y };
|
|
18471
18407
|
|
|
18472
|
-
//# sourceMappingURL=bin-
|
|
18408
|
+
//# sourceMappingURL=bin-Bz16rwKs.mjs.map
|