mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Compute Tray Icon on the fly instead of at build
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
51a93b8cc0
commit
746342b946
@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import path from "path";
|
||||
import sharp from "sharp";
|
||||
import jsdom from "jsdom";
|
||||
import fs from "fs-extra";
|
||||
|
||||
export async function generateTrayIcon(
|
||||
{
|
||||
outputFilename = "trayIcon",
|
||||
svgIconPath = path.resolve(__dirname, "../src/renderer/components/icon/logo-lens.svg"),
|
||||
outputFolder = path.resolve(__dirname, "./tray"),
|
||||
dpiSuffix = "2x",
|
||||
pixelSize = 32,
|
||||
shouldUseDarkColors = false, // managed by electron.nativeTheme.shouldUseDarkColors
|
||||
} = {}) {
|
||||
outputFilename += `${shouldUseDarkColors ? "Dark" : ""}Template`; // e.g. output trayIconDarkTemplate@2x.png
|
||||
dpiSuffix = dpiSuffix !== "1x" ? `@${dpiSuffix}` : "";
|
||||
const pngIconDestPath = path.resolve(outputFolder, `${outputFilename}${dpiSuffix}.png`);
|
||||
|
||||
try {
|
||||
// Modify .SVG colors
|
||||
const trayIconColor = shouldUseDarkColors ? "black" : "white";
|
||||
const svgDom = await jsdom.JSDOM.fromFile(svgIconPath);
|
||||
const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0];
|
||||
|
||||
svgRoot.innerHTML += `<style>* {fill: ${trayIconColor} !important;}</style>`;
|
||||
const svgIconBuffer = Buffer.from(svgRoot.outerHTML);
|
||||
// Resize and convert to .PNG
|
||||
const pngIconBuffer: Buffer = await sharp(svgIconBuffer)
|
||||
.resize({ width: pixelSize, height: pixelSize })
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
// Save icon
|
||||
await fs.writeFile(pngIconDestPath, pngIconBuffer);
|
||||
console.info(`[DONE]: Tray icon saved at "${pngIconDestPath}"`);
|
||||
} catch (err) {
|
||||
console.error(`[ERROR]: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Run
|
||||
const iconSizes: Record<string, number> = {
|
||||
"1x": 16,
|
||||
"2x": 32,
|
||||
"3x": 48,
|
||||
};
|
||||
|
||||
Object.entries(iconSizes).forEach(([dpiSuffix, pixelSize]) => {
|
||||
generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: false });
|
||||
generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: true });
|
||||
});
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 422 B |
Binary file not shown.
|
Before Width: | Height: | Size: 925 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 460 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
@ -209,6 +209,7 @@
|
||||
"@sentry/electron": "^3.0.6",
|
||||
"@sentry/integrations": "^6.19.3",
|
||||
"@types/circular-dependency-plugin": "5.0.5",
|
||||
"@types/data-urls": "^2.0.1",
|
||||
"abort-controller": "^3.0.0",
|
||||
"auto-bind": "^4.0.0",
|
||||
"await-lock": "^2.1.0",
|
||||
@ -216,6 +217,7 @@
|
||||
"chokidar": "^3.5.3",
|
||||
"conf": "^7.1.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"data-urls": "^3.0.1",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-updater": "^4.6.5",
|
||||
"electron-window-state": "^5.0.3",
|
||||
|
||||
@ -48,6 +48,17 @@ export function getOrInsertWith<K, V>(map: Map<K, V>, key: K, builder: () => V):
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link getOrInsertWith} but the builder is async and will be awaited before inserting into the map
|
||||
*/
|
||||
export async function getOrInsertWithAsync<K, V>(map: Map<K, V>, key: K, asyncBuilder: () => Promise<V>): Promise<V> {
|
||||
if (!map.has(key)) {
|
||||
map.set(key, await asyncBuilder());
|
||||
}
|
||||
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value associated with `key` iff there was not a previous value
|
||||
* @param map The map to interact with
|
||||
|
||||
@ -305,7 +305,7 @@ async function main(di: DiContainer) {
|
||||
|
||||
onQuitCleanup.push(
|
||||
initMenu(applicationMenuItems),
|
||||
initTray(windowManager, trayMenuItems, navigateToPreferences),
|
||||
await initTray(windowManager, trayMenuItems, navigateToPreferences),
|
||||
() => ShellSession.cleanup(),
|
||||
);
|
||||
|
||||
|
||||
@ -3,19 +3,23 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import path from "path";
|
||||
import packageInfo from "../../../package.json";
|
||||
import { Menu, Tray } from "electron";
|
||||
import type { NativeImage } from "electron";
|
||||
import { Menu, nativeImage, nativeTheme, Tray } from "electron";
|
||||
import type { IComputedValue } from "mobx";
|
||||
import { autorun } from "mobx";
|
||||
import { showAbout } from "../menu/menu";
|
||||
import { checkForUpdates, isAutoUpdateEnabled } from "../app-updater";
|
||||
import type { WindowManager } from "../window-manager";
|
||||
import logger from "../logger";
|
||||
import { isDevelopment, isWindows, productName, staticFilesDirectory } from "../../common/vars";
|
||||
import { isWindows, productName } from "../../common/vars";
|
||||
import { exitApp } from "../exit-app";
|
||||
import { toJS } from "../../common/utils";
|
||||
import { getOrInsertWithAsync, toJS } from "../../common/utils";
|
||||
import type { TrayMenuRegistration } from "./tray-menu-registration";
|
||||
import parseDataURL from "data-urls";
|
||||
import sharp from "sharp";
|
||||
import LogoLens from "../../renderer/components/icon/logo-lens.svg";
|
||||
import { JSDOM } from "jsdom";
|
||||
|
||||
|
||||
const TRAY_LOG_PREFIX = "[TRAY]";
|
||||
@ -23,24 +27,64 @@ const TRAY_LOG_PREFIX = "[TRAY]";
|
||||
// note: instance of Tray should be saved somewhere, otherwise it disappears
|
||||
export let tray: Tray;
|
||||
|
||||
export function getTrayIcon(): string {
|
||||
return path.resolve(
|
||||
staticFilesDirectory,
|
||||
isDevelopment ? "../build/tray" : "icons", // copied within electron-builder extras
|
||||
"trayIconTemplate.png",
|
||||
);
|
||||
interface ComputeTrayIconArgs {
|
||||
shouldUseDarkColors: boolean;
|
||||
size: number;
|
||||
sourceSvg: string;
|
||||
}
|
||||
|
||||
export function initTray(
|
||||
const trayIcons = new Map<boolean, NativeImage>();
|
||||
|
||||
async function computeTrayIcon({ shouldUseDarkColors, size, sourceSvg }: ComputeTrayIconArgs): Promise<NativeImage> {
|
||||
return getOrInsertWithAsync(trayIcons, shouldUseDarkColors, async () => {
|
||||
const trayIconColor = shouldUseDarkColors ? "white" : "black"; // Invert to show contrast
|
||||
const parsedSvg = parseDataURL(sourceSvg);
|
||||
const svgContent = Buffer.from(parsedSvg.body.buffer).toString();
|
||||
const svgDom = new JSDOM(`<body>${svgContent}</body>`);
|
||||
const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0];
|
||||
|
||||
svgRoot.innerHTML += `<style>* {fill: ${trayIconColor} !important;}</style>`;
|
||||
|
||||
const iconBuffer = await sharp(Buffer.from(svgRoot.outerHTML))
|
||||
.resize({ width: size, height: size })
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
return nativeImage.createFromBuffer(iconBuffer);
|
||||
});
|
||||
}
|
||||
|
||||
function computeCurrentTrayIcon() {
|
||||
return computeTrayIcon({
|
||||
shouldUseDarkColors: nativeTheme.shouldUseDarkColors,
|
||||
size: 16,
|
||||
sourceSvg: LogoLens,
|
||||
});
|
||||
}
|
||||
|
||||
function watchShouldUseDarkColors(tray: Tray) {
|
||||
let prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
||||
|
||||
nativeTheme.on("updated", () => {
|
||||
if (prevShouldUseDarkColors !== nativeTheme.shouldUseDarkColors) {
|
||||
prevShouldUseDarkColors = nativeTheme.shouldUseDarkColors;
|
||||
computeCurrentTrayIcon()
|
||||
.then(img => tray.setImage(img));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function initTray(
|
||||
windowManager: WindowManager,
|
||||
trayMenuItems: IComputedValue<TrayMenuRegistration[]>,
|
||||
navigateToPreferences: () => void,
|
||||
) {
|
||||
const icon = getTrayIcon();
|
||||
const icon = await computeCurrentTrayIcon();
|
||||
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip(packageInfo.description);
|
||||
tray.setIgnoreDoubleClickEvents(true);
|
||||
watchShouldUseDarkColors(tray);
|
||||
|
||||
if (isWindows) {
|
||||
tray.on("click", () => {
|
||||
|
||||
61
yarn.lock
61
yarn.lock
@ -1470,6 +1470,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-3.1.47.tgz#36e549dd3f1322742a3a738e7c113ebe48221860"
|
||||
integrity sha512-eI6gvpcGHLk3dAuHYnRCAjX+41gMv1nz/VP55wAe5HtmAKDOoPSfr3f6vkMc08ov1S0NsjvUBxDtHHxqQY1LGA==
|
||||
|
||||
"@types/data-urls@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/data-urls/-/data-urls-2.0.1.tgz#4a5491583513e8a84044f7e7fbac2ca246c44ed9"
|
||||
integrity sha512-8/IGbezKc9SEeL9BHdZ4whS9EaYzXfQ6eEC2s34RXfnaEkTasKCWldq/gfoEzbByDD5GXyqEESogVRtdskJXEA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/whatwg-mimetype" "*"
|
||||
"@types/whatwg-url" "*"
|
||||
|
||||
"@types/debug@^4.1.6":
|
||||
version "4.1.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
|
||||
@ -2118,6 +2127,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.5.tgz#2a1413aded46e67a1fe2386800e291123ed75eb1"
|
||||
integrity sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw==
|
||||
|
||||
"@types/webidl-conversions@*":
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-6.1.1.tgz#e33bc8ea812a01f63f90481c666334844b12a09e"
|
||||
integrity sha512-XAahCdThVuCFDQLT7R7Pk/vqeObFNL3YqRyFZg+AqAP/W1/w3xHaIxuW7WszQqTbIBOPRcItYJIou3i/mppu3Q==
|
||||
|
||||
"@types/webpack-dev-server@^4.7.2":
|
||||
version "4.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-4.7.2.tgz#a12d9881aa23cdd4cecbb2d31fa784a45c4967e0"
|
||||
@ -2168,6 +2182,19 @@
|
||||
tapable "^2.2.0"
|
||||
webpack "^5"
|
||||
|
||||
"@types/whatwg-mimetype@*":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/whatwg-mimetype/-/whatwg-mimetype-2.1.1.tgz#1b7b7aecaa3695209fd2f3a808dd5729973a52fa"
|
||||
integrity sha512-ojnf89qt5AWnqsjyPqMLN8uVaxm5x23vxNQ1me6EPBOdJe1YYuIZUzg809MZUG8UU6HKhkr6ah4fi2WUvD0DFw==
|
||||
|
||||
"@types/whatwg-url@*":
|
||||
version "8.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.1.tgz#f1aac222dab7c59e011663a0cb0a3117b2ef05d4"
|
||||
integrity sha512-2YubE1sjj5ifxievI5Ge1sckb9k/Er66HyR2c+3+I6VDUUg1TLPdYYTEbQ+DjRkS4nTxMJhgWfSfMRD2sl2EYQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/webidl-conversions" "*"
|
||||
|
||||
"@types/ws@^6.0.1":
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1"
|
||||
@ -4412,6 +4439,15 @@ data-urls@^2.0.0:
|
||||
whatwg-mimetype "^2.3.0"
|
||||
whatwg-url "^8.0.0"
|
||||
|
||||
data-urls@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.1.tgz#597fc2ae30f8bc4dbcf731fcd1b1954353afc6f8"
|
||||
integrity sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw==
|
||||
dependencies:
|
||||
abab "^2.0.3"
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^10.0.0"
|
||||
|
||||
date-fns@^2.16.1:
|
||||
version "2.28.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
|
||||
@ -12967,6 +13003,13 @@ tr46@^2.1.0:
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
@ -13585,6 +13628,11 @@ webidl-conversions@^6.1.0:
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
|
||||
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||
|
||||
webidl-conversions@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
|
||||
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
|
||||
|
||||
webpack-cli@^4.9.2:
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d"
|
||||
@ -13731,6 +13779,19 @@ whatwg-mimetype@^2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||
|
||||
whatwg-mimetype@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
|
||||
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
|
||||
|
||||
whatwg-url@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
|
||||
integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user