diff --git a/build/build_tray_icon.ts b/build/build_tray_icon.ts new file mode 100644 index 0000000000..2324f43295 --- /dev/null +++ b/build/build_tray_icon.ts @@ -0,0 +1,51 @@ +// Generate tray icons from SVG to PNG + different sizes and colors (B&W) +// Command: `yarn build:tray-icons` +import path from "path" +import sharp from "sharp"; +import jsdom from "jsdom" +import fs from "fs-extra" + +export async function generateTrayIcon( + { + outputFilename = "tray_icon", // e.g. output tray_icon_dark@2x.png + svgIconPath = path.resolve(__dirname, "../src/renderer/components/icon/logo-lens.svg"), + outputFolder = path.resolve(__dirname, "./icons"), + dpiSuffix = "2x", + pixelSize = 32, + shouldUseDarkColors = false, // managed by electron.nativeTheme.shouldUseDarkColors + } = {}) { + outputFilename += shouldUseDarkColors ? "_dark" : "" + dpiSuffix = dpiSuffix !== "1x" ? `@${dpiSuffix}` : "" + const pngIconDestPath = path.resolve(outputFolder, `${outputFilename}${dpiSuffix}.png`) + try { + // Modify .SVG colors + const trayIconColor = shouldUseDarkColors ? "white" : "black"; + const svgDom = await jsdom.JSDOM.fromFile(svgIconPath); + const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0]; + svgRoot.innerHTML += `` + 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 = { + "1x": 16, + "2x": 32, + "3x": 48, +}; +Object.entries(iconSizes).forEach(([dpiSuffix, pixelSize]) => { + generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: false }); + generateTrayIcon({ dpiSuffix, pixelSize, shouldUseDarkColors: true }); +}); diff --git a/build/icons/tray_icon.png b/build/icons/tray_icon.png new file mode 100644 index 0000000000..73c7346d33 Binary files /dev/null and b/build/icons/tray_icon.png differ diff --git a/build/icons/tray_icon@2x.png b/build/icons/tray_icon@2x.png new file mode 100644 index 0000000000..71206802ac Binary files /dev/null and b/build/icons/tray_icon@2x.png differ diff --git a/build/icons/tray_icon@3x.png b/build/icons/tray_icon@3x.png new file mode 100644 index 0000000000..a293ba7d32 Binary files /dev/null and b/build/icons/tray_icon@3x.png differ diff --git a/build/icons/tray_icon_dark.png b/build/icons/tray_icon_dark.png new file mode 100644 index 0000000000..568d13e00b Binary files /dev/null and b/build/icons/tray_icon_dark.png differ diff --git a/build/icons/tray_icon_dark@2x.png b/build/icons/tray_icon_dark@2x.png new file mode 100644 index 0000000000..3f28605dbf Binary files /dev/null and b/build/icons/tray_icon_dark@2x.png differ diff --git a/build/icons/tray_icon_dark@3x.png b/build/icons/tray_icon_dark@3x.png new file mode 100644 index 0000000000..5e682a5d82 Binary files /dev/null and b/build/icons/tray_icon_dark@3x.png differ diff --git a/package.json b/package.json index e84e3e69b3..eea0c95166 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "download-bins": "concurrently yarn:download:*", "download:kubectl": "yarn run ts-node build/download_kubectl.ts", "download:helm": "yarn run ts-node build/download_helm.ts", + "build:tray-icons": "yarn run ts-node build/build_tray_icon.ts", "lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/", "rebuild-pty": "yarn run electron-rebuild -f -w node-pty" }, @@ -89,8 +90,8 @@ "filter": "!**/main.js" }, { - "from": "src/renderer/components/icon/logo-lens.svg", - "to": "static/logo.svg" + "from": "build/icons/", + "to": "build/icons/" }, "LICENSE" ], @@ -177,7 +178,6 @@ "@types/node": "^12.12.45", "@types/proper-lockfile": "^4.1.1", "@types/react-beautiful-dnd": "^13.0.0", - "@types/sharp": "^0.26.0", "@types/tar": "^4.0.3", "array-move": "^3.0.0", "chalk": "^4.1.0", @@ -207,14 +207,13 @@ "openid-client": "^3.15.2", "path-to-regexp": "^6.1.0", "proper-lockfile": "^4.1.1", - "react-beautiful-dnd": "^13.0.0", "react": "^16.13.1", + "react-beautiful-dnd": "^13.0.0", "react-router": "^5.2.0", "request": "^2.88.2", "request-promise-native": "^1.0.8", "semver": "^7.3.2", "serializr": "^2.0.3", - "sharp": "^0.26.1", "shell-env": "^3.0.0", "spdy": "^4.0.2", "tar": "^6.0.2", @@ -260,6 +259,7 @@ "@types/request": "^2.48.5", "@types/request-promise-native": "^1.0.17", "@types/semver": "^7.2.0", + "@types/sharp": "^0.26.0", "@types/shelljs": "^0.8.8", "@types/spdy": "^3.4.4", "@types/tcp-port-used": "^1.0.0", @@ -317,6 +317,7 @@ "react-select": "^3.1.0", "react-window": "^1.8.5", "sass-loader": "^8.0.2", + "sharp": "^0.26.1", "spectron": "11.0.0", "style-loader": "^1.2.1", "terser-webpack-plugin": "^3.0.3", diff --git a/src/main/tray.ts b/src/main/tray.ts index 5a43aec50a..72bbf51874 100644 --- a/src/main/tray.ts +++ b/src/main/tray.ts @@ -1,9 +1,6 @@ import path from "path" -import sharp from "sharp"; -import jsdom from "jsdom" import packageInfo from "../../package.json" -import { app, dialog, Menu, NativeImage, nativeImage, nativeTheme, Tray } from "electron" -import { isDevelopment, isMac } from "../common/vars"; +import { app, dialog, Menu, NativeImage, nativeTheme, Tray } from "electron" import { autorun } from "mobx"; import { showAbout } from "./menu"; import { AppUpdater } from "./app-updater"; @@ -16,26 +13,22 @@ import logger from "./logger"; // note: instance of Tray should be saved somewhere, otherwise it disappears export let tray: Tray; -export let trayIcon: NativeImage; -export const trayIconPath = isDevelopment - ? path.resolve(__static, "../src/renderer/components/icon/logo-lens.svg") - : path.resolve(__static, "logo.svg") // electron-builder's extraResources +// refresh icon when MacOS dark/light theme has changed +nativeTheme.on("updated", () => tray?.setImage(getTrayIcon())); -// update icon when MacOS dark/light theme has changed -nativeTheme.on("updated", async () => { - if (tray) { - trayIcon = await createTrayIconFromSvg(); - tray.setImage(trayIcon); - } -}); - -export async function initTray(windowManager: WindowManager) { - trayIcon = await createTrayIconFromSvg(); // generate icon once on tray activation +export function getTrayIcon(isDark = nativeTheme.shouldUseDarkColors): string { + return path.resolve(__static, "../build/icons", `tray_icon${isDark ? "_dark" : ""}.png`) +} +export function initTray(windowManager: WindowManager) { const dispose = autorun(() => { - const menu = createTrayMenu(windowManager); - buildTray(trayIcon, menu); + try { + const menu = createTrayMenu(windowManager); + buildTray(getTrayIcon(), menu); + } catch (err) { + logger.error(`[TRAY]: building failed: ${err}`); + } }) return () => { dispose(); @@ -44,24 +37,7 @@ export async function initTray(windowManager: WindowManager) { } } -export async function createTrayIconFromSvg(filePath = trayIconPath): Promise { - // modify icon's svg - const svgDom = await jsdom.JSDOM.fromFile(filePath); - const svgRoot = svgDom.window.document.body.getElementsByTagName("svg")[0]; - const trayIconColor = nativeTheme.shouldUseDarkColors ? "white" : "black"; - svgRoot.innerHTML += `` - const svgIconBuffer = Buffer.from(svgRoot.outerHTML); - - // convert to .png or .ico and resize - const pngIcon = await sharp(svgIconBuffer).png().toBuffer(); - const iconSize = isMac ? 16 : 32; // todo: verify on windows/linux - return nativeImage.createFromBuffer(pngIcon).resize({ - width: iconSize, - height: iconSize - }); -} - -export async function buildTray(icon: NativeImage, menu: Menu) { +export function buildTray(icon: string | NativeImage, menu: Menu) { logger.info("[TRAY]: build start"); if (!tray) { diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index e768058fb3..fcee752b05 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -81,10 +81,10 @@ export class WindowManager { this.disposers.menuAutoUpdater = initMenu(this); } - protected async initTray() { - this.disposers.trayAutoBind = reaction(() => userStore.preferences.trayEnabled, async isEnabled => { + protected initTray() { + this.disposers.trayAutoBind = reaction(() => userStore.preferences.trayEnabled, isEnabled => { if (isEnabled) { - this.disposers.trayAutoUpdater = await initTray(this); + this.disposers.trayAutoUpdater = initTray(this); } else if (this.disposers.trayAutoUpdater) { this.disposers.trayAutoUpdater(); } diff --git a/src/renderer/components/icon/logo-kontena.svg b/src/renderer/components/icon/logo-kontena.svg deleted file mode 100644 index 6b05f1ba17..0000000000 --- a/src/renderer/components/icon/logo-kontena.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file