From 985ab31aed1b78d83564e90c60e197ad8bb24428 Mon Sep 17 00:00:00 2001 From: ienaga Date: Sun, 1 Nov 2026 17:53:43 +0900 Subject: [PATCH] =?UTF-8?q?macOS:=20=E9=87=8D=E3=81=AD=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=AE=E3=82=A2=E3=83=97=E3=83=AA=E3=82=92=20Dock=20?= =?UTF-8?q?=E3=81=AB=E5=87=BA=E3=81=AA=E3=81=84=E5=BD=A2=EF=BC=88LSUIEleme?= =?UTF-8?q?nt=EF=BC=89=E3=81=A7=E4=BD=9C=E3=82=8A=E3=80=81=E5=8B=95?= =?UTF-8?q?=E3=81=8D=E7=B6=9A=E3=81=91=E3=82=8B=201=20=E3=81=A4=E7=9B=AE?= =?UTF-8?q?=E3=81=A0=E3=81=91=E3=81=8C=20Dock=20=E3=81=AB=E3=82=A2?= =?UTF-8?q?=E3=82=A4=E3=82=B3=E3=83=B3=E3=82=92=E5=87=BA=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/electron-config.ts | 13 ++++++++++--- templates/electron/index.js | 3 +++ tests/electron-overlay.test.ts | 5 ++++- tests/electron.test.ts | 9 +++++++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f032082..4fe51af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@next2d/builder", - "version": "0.4.3", + "version": "0.4.4", "description": "Multi-platform builder for Next2d Framework, supporting export to various platforms such as macOS, Windows, iOS, Android and Web(HTML)", "author": "Toshiyuki Ienaga ", "license": "MIT", diff --git a/src/electron-config.ts b/src/electron-config.ts index 2043c53..595692a 100644 --- a/src/electron-config.ts +++ b/src/electron-config.ts @@ -151,9 +151,16 @@ export const createElectronPackagerOptions = ( "extraResource": [resources, ...config.nativeBridge ? [path.join(path.dirname(resources), "native")] : []], "ignore": [/^\/native(?:\/|$)/, /^\/resources(?:\/|$)/, /^\/icons(?:\/|$)/, /^\/forge\.config\.[cm]?js$/, /^\/entitlements\.plist$/, /(?:^|\/)steam_appid\.txt$/], ...os !== "linux" && config.icons[os] ? { "icon": path.resolve(root, config.icons[os]) } : {}, - ...os === "macos" && config.macos.localNetworkUsageDescription ? { - "extendInfo": { "NSLocalNetworkUsageDescription": config.macos.localNetworkUsageDescription }, - "extendHelperInfo": { "NSLocalNetworkUsageDescription": config.macos.localNetworkUsageDescription } + ...os === "macos" && (config.macos.localNetworkUsageDescription || config.window.overlay?.enabled) ? { + "extendInfo": { + ...config.macos.localNetworkUsageDescription ? { "NSLocalNetworkUsageDescription": config.macos.localNetworkUsageDescription } : {}, + // An overlay starts as an agent app (no Dock icon) so that a second launch, which quits at once, + // never shows one. The first instance shows its Dock icon itself (templates/electron/index.js). + ...config.window.overlay?.enabled ? { "LSUIElement": true } : {} + }, + ...config.macos.localNetworkUsageDescription ? { + "extendHelperInfo": { "NSLocalNetworkUsageDescription": config.macos.localNetworkUsageDescription } + } : {} } : {}, ...os === "macos" && sign ? { "osxSign": { diff --git a/templates/electron/index.js b/templates/electron/index.js index 1b2f4cc..2457cea 100644 --- a/templates/electron/index.js +++ b/templates/electron/index.js @@ -144,6 +144,9 @@ const createWindow = async () => { app.whenReady().then(async () => { if (!hasLock) return; + // The overlay app is packaged as an agent app (LSUIElement) so a second launch never shows a Dock icon; + // the instance that keeps running shows it. + if (process.platform === "darwin" && overlay) app.dock?.show(); Menu.setApplicationMenu(process.platform === "darwin" ? Menu.buildFromTemplate([ { role: "appMenu" }, { role: "editMenu" }, { role: "windowMenu" } ]) : null); diff --git a/tests/electron-overlay.test.ts b/tests/electron-overlay.test.ts index 48afaca..9420289 100644 --- a/tests/electron-overlay.test.ts +++ b/tests/electron-overlay.test.ts @@ -62,7 +62,7 @@ const fakeElectron = (options = {}) => { app: { isPackaged: false, commandLine: { getSwitchValue: () => "", hasSwitch: () => false }, setName() {}, setPath() {}, requestSingleInstanceLock: () => { calls.push(["requestSingleInstanceLock"]); return options.lock !== false; }, - dock: { hide: () => calls.push(["dock.hide"]) }, + dock: { hide: () => calls.push(["dock.hide"]), show: () => calls.push(["dock.show"]) }, getPath: () => os.tmpdir(), on: (name, cb) => appListeners.set(name, cb), quit: () => calls.push(["quit"]), exit: (code) => calls.push(["exit", code]), whenReady: () => new Promise((resolve) => { ready = resolve; }) }, @@ -235,6 +235,9 @@ test("a desktop overlay runs once: a second launch quits before any window or Do const first = await startHost(t, { window: { overlay: { enabled: true, clickThrough: true } } }); assert.equal(first.windows.length, 1); assert.equal(first.calls.some(([name]) => name === "exit"), false); + // The running instance shows its Dock icon itself (the app is packaged as an agent app). + assert.equal(first.calls.some(([name]) => name === "dock.show"), process.platform === "darwin"); + assert.equal(denied.calls.some(([name]) => name === "dock.show"), false); first.calls.length = 0; first.appListeners.get("second-instance")(); assert.deepEqual(first.calls.filter(([name]) => name === "setAlwaysOnTop"), [["setAlwaysOnTop", true, "floating"]]); diff --git a/tests/electron.test.ts b/tests/electron.test.ts index 242fe85..0aa2c75 100644 --- a/tests/electron.test.ts +++ b/tests/electron.test.ts @@ -62,6 +62,15 @@ test("macOS local network purpose is opt-in and applied to the app and helpers", assert.equal(createElectronPackagerOptions(root, config, "linux", root).extendHelperInfo, undefined); fs.writeFileSync(path.join(root, "electron.config.json"), "{}"); assert.equal(createElectronPackagerOptions(root, readElectronConfig(root), "macos", root).extendInfo, undefined); + // An overlay is packaged as an agent app (no Dock icon until the running instance shows it). + fs.writeFileSync(path.join(root, "electron.config.json"), JSON.stringify({ window: { overlay: { enabled: true } } })); + const overlay = createElectronPackagerOptions(root, readElectronConfig(root), "macos", root); + assert.deepEqual(overlay.extendInfo, { LSUIElement: true }); + assert.equal(overlay.extendHelperInfo, undefined); + assert.equal(createElectronPackagerOptions(root, readElectronConfig(root), "windows", root).extendInfo, undefined); + fs.writeFileSync(path.join(root, "electron.config.json"), JSON.stringify({ window: { overlay: { enabled: true } }, macos: { localNetworkUsageDescription: description } })); + assert.deepEqual(createElectronPackagerOptions(root, readElectronConfig(root), "macos", root).extendInfo, { NSLocalNetworkUsageDescription: description, LSUIElement: true }); + fs.writeFileSync(path.join(root, "electron.config.json"), "{}"); for (const invalid of [null, 123, "", " ", "\0", "x".repeat(1001)]) { fs.writeFileSync(path.join(root, "electron.config.json"), JSON.stringify({ macos: { localNetworkUsageDescription: invalid }