From ad8287112b664cec7c5a81384d82150c0c0c8786 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 14 Jul 2025 13:10:58 +0100 Subject: [PATCH 01/11] invoke setOverlayIcon for Windows platforms --- src/ipc.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ipc.ts b/src/ipc.ts index 3b3d3cce9c..7577132078 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -12,12 +12,18 @@ import { randomArray } from "./utils.js"; import { getDisplayMediaCallback, setDisplayMediaCallback } from "./displayMediaCallback.js"; import Store, { clearDataAndRelaunch } from "./store.js"; -ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void { +ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { if (process.platform !== "win32") { // only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron // has some Windows support too, and in some Windows environments this leads to two badges rendering atop // each other. See https://github.com/vector-im/element-web/issues/16942 app.badgeCount = count; + } else { + if (imageBuffer && imageBufferDescription !== undefined) { + global.mainWindow?.setOverlayIcon(nativeImage.createFromBuffer(Buffer.from(imageBuffer)), imageBufferDescription); + } else { + global.mainWindow?.setOverlayIcon(null, ""); + } } if (count === 0) { global.mainWindow?.flashFrame(false); From 7d488cc06dc9e0386761e69c1c70e55fc9bff492 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 14 Jul 2025 13:11:16 +0100 Subject: [PATCH 02/11] Announce support for badge overlays --- src/preload.cts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/preload.cts b/src/preload.cts index f8922ab632..21d7593983 100644 --- a/src/preload.cts +++ b/src/preload.cts @@ -55,13 +55,17 @@ contextBridge.exposeInMainWorld("electron", { sessionId: string; config: IConfigOptions; supportedSettings: Record; + /** + * Do we need to render badge overlays for new notifications? + */ + supportsBadgeOverlay: boolean; }> { const [{ protocol, sessionId }, config, supportedSettings] = await Promise.all([ ipcRenderer.invoke("getProtocol"), ipcRenderer.invoke("getConfig"), ipcRenderer.invoke("getSupportedSettings"), ]); - return { protocol, sessionId, config, supportedSettings }; + return { protocol, sessionId, config, supportedSettings, supportsBadgeOverlay: process.platform === "win32" }; }, async setSettingValue(settingName: string, value: any): Promise { From 79b9ac896ad789a27215eb1aded2438fbab3281e Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 14 Jul 2025 13:11:57 +0100 Subject: [PATCH 03/11] lint --- src/ipc.ts | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/ipc.ts b/src/ipc.ts index 7577132078..2f396db310 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -12,23 +12,29 @@ import { randomArray } from "./utils.js"; import { getDisplayMediaCallback, setDisplayMediaCallback } from "./displayMediaCallback.js"; import Store, { clearDataAndRelaunch } from "./store.js"; -ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { - if (process.platform !== "win32") { - // only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron - // has some Windows support too, and in some Windows environments this leads to two badges rendering atop - // each other. See https://github.com/vector-im/element-web/issues/16942 - app.badgeCount = count; - } else { - if (imageBuffer && imageBufferDescription !== undefined) { - global.mainWindow?.setOverlayIcon(nativeImage.createFromBuffer(Buffer.from(imageBuffer)), imageBufferDescription); +ipcMain.on( + "setBadgeCount", + function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { + if (process.platform !== "win32") { + // only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron + // has some Windows support too, and in some Windows environments this leads to two badges rendering atop + // each other. See https://github.com/vector-im/element-web/issues/16942 + app.badgeCount = count; } else { - global.mainWindow?.setOverlayIcon(null, ""); + if (imageBuffer && imageBufferDescription !== undefined) { + global.mainWindow?.setOverlayIcon( + nativeImage.createFromBuffer(Buffer.from(imageBuffer)), + imageBufferDescription, + ); + } else { + global.mainWindow?.setOverlayIcon(null, ""); + } } - } - if (count === 0) { - global.mainWindow?.flashFrame(false); - } -}); + if (count === 0) { + global.mainWindow?.flashFrame(false); + } + }, +); let focusHandlerAttached = false; ipcMain.on("loudNotification", function (): void { From 2d21835588bc7e18f30a830a8fb497065c1eea8e Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 14 Jul 2025 14:32:01 +0100 Subject: [PATCH 04/11] Document why we do this. --- src/ipc.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ipc.ts b/src/ipc.ts index 2f396db310..ea4a8b5b42 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -21,6 +21,9 @@ ipcMain.on( // each other. See https://github.com/vector-im/element-web/issues/16942 app.badgeCount = count; } else { + // similarly, we only use setOverlayIcon on Windows as it's only supported on that platform, but has good support + // from all the Windows variants we support. + // https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows if (imageBuffer && imageBufferDescription !== undefined) { global.mainWindow?.setOverlayIcon( nativeImage.createFromBuffer(Buffer.from(imageBuffer)), From 7c19e5f0a84a40153718cf326dd48de2d1c03d00 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 15 Jul 2025 13:55:04 +0100 Subject: [PATCH 05/11] Move code; drop favicon updating for win32 --- package.json | 1 - src/ipc.ts | 12 --------- src/tray.ts | 69 ++++++++++++++++++++++++++++------------------------ yarn.lock | 16 +----------- 4 files changed, 38 insertions(+), 60 deletions(-) diff --git a/package.json b/package.json index 9c3c13b65a..5eeb5396ee 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "electron-window-state": "^5.0.3", "keytar-forked": "7.10.0", "minimist": "^1.2.6", - "png-to-ico": "^2.1.1", "uuid": "^11.0.0" }, "devDependencies": { diff --git a/src/ipc.ts b/src/ipc.ts index ea4a8b5b42..40aaa63398 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -20,18 +20,6 @@ ipcMain.on( // has some Windows support too, and in some Windows environments this leads to two badges rendering atop // each other. See https://github.com/vector-im/element-web/issues/16942 app.badgeCount = count; - } else { - // similarly, we only use setOverlayIcon on Windows as it's only supported on that platform, but has good support - // from all the Windows variants we support. - // https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows - if (imageBuffer && imageBufferDescription !== undefined) { - global.mainWindow?.setOverlayIcon( - nativeImage.createFromBuffer(Buffer.from(imageBuffer)), - imageBufferDescription, - ); - } else { - global.mainWindow?.setOverlayIcon(null, ""); - } } if (count === 0) { global.mainWindow?.flashFrame(false); diff --git a/src/tray.ts b/src/tray.ts index bbf477bfe7..0ee9f002c4 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -7,10 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { app, Tray, Menu, nativeImage } from "electron"; -import pngToIco from "png-to-ico"; -import path from "node:path"; -import fs from "node:fs"; +import { app, Tray, Menu, nativeImage, ipcMain, IpcMainEvent } from "electron"; import { v5 as uuidv5 } from "uuid"; import { _t } from "./language-helper.js"; @@ -71,37 +68,45 @@ export function create(config: IConfig): void { initApplicationMenu(); trayIcon.on("click", toggleWin); - let lastFavicon: string | null = null; - global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) { - if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) { - if (lastFavicon !== null) { - global.mainWindow?.setIcon(defaultIcon); - trayIcon?.setImage(defaultIcon); - lastFavicon = null; + if (process.platform === "win32") { + // We only use setOverlayIcon on Windows as it's only supported on that platform, but has good support + // from all the Windows variants we support. + // https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows + ipcMain.on( + "setBadgeCount", + function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { + if (imageBuffer && imageBufferDescription !== undefined) { + global.mainWindow?.setOverlayIcon( + nativeImage.createFromBuffer(Buffer.from(imageBuffer)), + imageBufferDescription, + ); + } else { + global.mainWindow?.setOverlayIcon(null, ""); + } + }, + ); + } else { + // For other platforms, we instead update the application icon when the favicon changes. + let lastFavicon: string | null = null; + global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) { + if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) { + if (lastFavicon !== null) { + global.mainWindow?.setIcon(defaultIcon); + trayIcon?.setImage(defaultIcon); + lastFavicon = null; + } + return; } - return; - } - // No need to change, shortcut - if (favicons[0] === lastFavicon) return; - lastFavicon = favicons[0]; + // No need to change, shortcut + if (favicons[0] === lastFavicon) return; + lastFavicon = favicons[0]; - let newFavicon = nativeImage.createFromDataURL(favicons[0]); - - // Windows likes ico's too much. - if (process.platform === "win32") { - try { - const icoPath = path.join(app.getPath("temp"), "win32_element_icon.ico"); - fs.writeFileSync(icoPath, await pngToIco(newFavicon.toPNG())); - newFavicon = nativeImage.createFromPath(icoPath); - } catch (e) { - console.error("Failed to make win32 ico", e); - } - } - - trayIcon?.setImage(newFavicon); - global.mainWindow?.setIcon(newFavicon); - }); + let newFavicon = nativeImage.createFromDataURL(favicons[0]); + trayIcon?.setImage(newFavicon); + global.mainWindow?.setIcon(newFavicon); + }); + } global.mainWindow?.webContents.on("page-title-updated", function (ev, title) { trayIcon?.setToolTip(title); diff --git a/yarn.lock b/yarn.lock index d8bd03b458..645a42add6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2243,7 +2243,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18.19.115", "@types/node@^17.0.36", "@types/node@^22.7.7": +"@types/node@*", "@types/node@18.19.115", "@types/node@^22.7.7": version "18.19.115" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.115.tgz#cd94caf14472021b4443c99bcd7aac6bb5c4f672" integrity sha512-kNrFiTgG4a9JAn1LMQeLOv3MvXIPokzXziohMrMsvpYgLpdEt/mMiVYc4sGKtDfyxM5gIDF4VgrPRyCw4fHOYg== @@ -6237,20 +6237,6 @@ pluralizers@^0.1.7: resolved "https://registry.yarnpkg.com/pluralizers/-/pluralizers-0.1.7.tgz#8d38dd0a1b660e739b10ab2eab10b684c9d50142" integrity sha512-mw6AejUiCaMQ6uPN9ObjJDTnR5AnBSmnHHy3uVTbxrSFSxO5scfwpTs8Dxyb6T2v7GSulhvOq+pm9y+hXUvtOA== -png-to-ico@^2.1.1: - version "2.1.8" - resolved "https://registry.yarnpkg.com/png-to-ico/-/png-to-ico-2.1.8.tgz#fdc2eda6f197df1d6c33400707e36c3b802ac6dd" - integrity sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w== - dependencies: - "@types/node" "^17.0.36" - minimist "^1.2.6" - pngjs "^6.0.0" - -pngjs@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" - integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== - possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" From 92a7da38ea724ea5009255df97cb97e83e014ad8 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 15 Jul 2025 15:35:08 +0100 Subject: [PATCH 06/11] lint --- src/tray.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tray.ts b/src/tray.ts index 0ee9f002c4..72ceeaa5d5 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { app, Tray, Menu, nativeImage, ipcMain, IpcMainEvent } from "electron"; +import { app, Tray, Menu, nativeImage, ipcMain, type IpcMainEvent } from "electron"; import { v5 as uuidv5 } from "uuid"; import { _t } from "./language-helper.js"; @@ -102,7 +102,7 @@ export function create(config: IConfig): void { if (favicons[0] === lastFavicon) return; lastFavicon = favicons[0]; - let newFavicon = nativeImage.createFromDataURL(favicons[0]); + const newFavicon = nativeImage.createFromDataURL(favicons[0]); trayIcon?.setImage(newFavicon); global.mainWindow?.setIcon(newFavicon); }); From b13f3ba1bd01f15168728b25473c825b786ad302 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Mon, 14 Jul 2025 15:52:41 +0100 Subject: [PATCH 07/11] Update with a proper comment --- src/i18n/strings/en_EN.json | 8 ++++++ src/ipc.ts | 15 ----------- src/tray.ts | 52 ++++++++++++------------------------- 3 files changed, 24 insertions(+), 51 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 27690be501..a97fb5b919 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -76,5 +76,13 @@ "bring_all_to_front": "Bring All to Front", "label": "Window", "zoom": "Zoom" + }, + "icon_overlay": { + "description_notifications": { + "one": "You have %(count)s unread notification.", + "other": "You have %(count)s unread notifications." + }, + "description_error": "Error" } + } diff --git a/src/ipc.ts b/src/ipc.ts index 40aaa63398..6050553af2 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -12,21 +12,6 @@ import { randomArray } from "./utils.js"; import { getDisplayMediaCallback, setDisplayMediaCallback } from "./displayMediaCallback.js"; import Store, { clearDataAndRelaunch } from "./store.js"; -ipcMain.on( - "setBadgeCount", - function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { - if (process.platform !== "win32") { - // only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron - // has some Windows support too, and in some Windows environments this leads to two badges rendering atop - // each other. See https://github.com/vector-im/element-web/issues/16942 - app.badgeCount = count; - } - if (count === 0) { - global.mainWindow?.flashFrame(false); - } - }, -); - let focusHandlerAttached = false; ipcMain.on("loudNotification", function (): void { if (process.platform === "win32" || process.platform === "linux") { diff --git a/src/tray.ts b/src/tray.ts index 72ceeaa5d5..6df764ecd9 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -68,45 +68,25 @@ export function create(config: IConfig): void { initApplicationMenu(); trayIcon.on("click", toggleWin); - if (process.platform === "win32") { - // We only use setOverlayIcon on Windows as it's only supported on that platform, but has good support - // from all the Windows variants we support. - // https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows - ipcMain.on( - "setBadgeCount", - function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, imageBufferDescription?: string): void { - if (imageBuffer && imageBufferDescription !== undefined) { - global.mainWindow?.setOverlayIcon( - nativeImage.createFromBuffer(Buffer.from(imageBuffer)), - imageBufferDescription, - ); - } else { - global.mainWindow?.setOverlayIcon(null, ""); - } - }, - ); - } else { - // For other platforms, we instead update the application icon when the favicon changes. - let lastFavicon: string | null = null; - global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) { - if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) { - if (lastFavicon !== null) { - global.mainWindow?.setIcon(defaultIcon); - trayIcon?.setImage(defaultIcon); - lastFavicon = null; - } - return; + let lastFavicon: string | null = null; + global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) { + if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) { + if (lastFavicon !== null) { + global.mainWindow?.setIcon(defaultIcon); + trayIcon?.setImage(defaultIcon); + lastFavicon = null; } + return; + } - // No need to change, shortcut - if (favicons[0] === lastFavicon) return; - lastFavicon = favicons[0]; + // No need to change, shortcut + if (favicons[0] === lastFavicon) return; + lastFavicon = favicons[0]; - const newFavicon = nativeImage.createFromDataURL(favicons[0]); - trayIcon?.setImage(newFavicon); - global.mainWindow?.setIcon(newFavicon); - }); - } + const newFavicon = nativeImage.createFromDataURL(favicons[0]); + trayIcon?.setImage(newFavicon); + global.mainWindow?.setIcon(newFavicon); + }); global.mainWindow?.webContents.on("page-title-updated", function (ev, title) { trayIcon?.setToolTip(title); From 31be440d37a8fa31485ab340871289f68d37e4e6 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 17 Jul 2025 10:05:03 +0100 Subject: [PATCH 08/11] Split out badge --- src/badge.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ src/electron-main.ts | 1 + 2 files changed, 43 insertions(+) create mode 100644 src/badge.ts diff --git a/src/badge.ts b/src/badge.ts new file mode 100644 index 0000000000..e54088b587 --- /dev/null +++ b/src/badge.ts @@ -0,0 +1,42 @@ +import { app, ipcMain, type IpcMainEvent, nativeImage } from "electron"; + +import { _t } from "./language-helper.js"; + +// Handles calculating the correct "badge" for the window, for notifications and error states. +// Tray icon updates are handled in tray.ts + +if (process.platform === "win32") { + // We only use setOverlayIcon on Windows as it's only supported on that platform, but has good support + // from all the Windows variants we support. + // https://www.electronjs.org/docs/latest/api/browser-window#winsetoverlayiconoverlay-description-windows + ipcMain.on( + "setBadgeCount", + function (_ev: IpcMainEvent, count: number, imageBuffer?: Buffer, isError?: boolean): void { + if (count === 0) { + // Flash frame is set to true in ipc.ts "loudNotification" + global.mainWindow?.flashFrame(false); + } + if (imageBuffer) { + global.mainWindow?.setOverlayIcon( + nativeImage.createFromBuffer(Buffer.from(imageBuffer)), + isError + ? _t("icon_overlay|description_error") + : _t("icon_overlay|description_notifications", { count }), + ); + } else { + global.mainWindow?.setOverlayIcon(null, ""); + } + }, + ); +} else { + // only set badgeCount on Mac/Linux, the docs say that only those platforms support it but turns out Electron + // has some Windows support too, and in some Windows environments this leads to two badges rendering atop + // each other. See https://github.com/vector-im/element-web/issues/16942 + ipcMain.on("setBadgeCount", function (_ev: IpcMainEvent, count: number): void { + if (count === 0) { + // Flash frame is set to true in ipc.ts "loudNotification" + global.mainWindow?.flashFrame(false); + } + app.badgeCount = count; + }); +} diff --git a/src/electron-main.ts b/src/electron-main.ts index 0241b3be6f..3e02206a77 100644 --- a/src/electron-main.ts +++ b/src/electron-main.ts @@ -23,6 +23,7 @@ import minimist from "minimist"; import "./ipc.js"; import "./seshat.js"; import "./settings.js"; +import "./badge.js"; import * as tray from "./tray.js"; import Store from "./store.js"; import { buildMenuTemplate } from "./vectormenu.js"; From db78dbec09571194af3ce585b7b178adbdcf638b Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 17 Jul 2025 10:05:47 +0100 Subject: [PATCH 09/11] Restore tray icon updates for Windows. --- package.json | 1 + src/tray.ts | 26 ++++++++++++++++++++++---- yarn.lock | 16 +++++++++++++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5eeb5396ee..d5d4f5e432 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "electron-window-state": "^5.0.3", "keytar-forked": "7.10.0", "minimist": "^1.2.6", + "png-to-ico": "^2.1.8", "uuid": "^11.0.0" }, "devDependencies": { diff --git a/src/tray.ts b/src/tray.ts index 6df764ecd9..1489b55bfb 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -7,8 +7,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import { app, Tray, Menu, nativeImage, ipcMain, type IpcMainEvent } from "electron"; +import { app, Tray, Menu, nativeImage } from "electron"; import { v5 as uuidv5 } from "uuid"; +import { writeFile } from "node:fs/promises"; +import pngToIco from "png-to-ico"; +import path from "node:path"; import { _t } from "./language-helper.js"; @@ -68,6 +71,7 @@ export function create(config: IConfig): void { initApplicationMenu(); trayIcon.on("click", toggleWin); + // See also, badge.ts let lastFavicon: string | null = null; global.mainWindow?.webContents.on("page-favicon-updated", async function (ev, favicons) { if (!favicons || favicons.length <= 0 || !favicons[0].startsWith("data:")) { @@ -83,9 +87,23 @@ export function create(config: IConfig): void { if (favicons[0] === lastFavicon) return; lastFavicon = favicons[0]; - const newFavicon = nativeImage.createFromDataURL(favicons[0]); - trayIcon?.setImage(newFavicon); - global.mainWindow?.setIcon(newFavicon); + let newFavicon = nativeImage.createFromDataURL(favicons[0]); + + // Windows likes ico's too much. + if (process.platform === "win32") { + try { + const icoPath = path.join(app.getPath("temp"), "win32_element_icon.ico"); + await writeFile(icoPath, await pngToIco(newFavicon.toPNG())); + newFavicon = nativeImage.createFromPath(icoPath); + } catch (e) { + console.error("Failed to make win32 ico", e); + } + // Always update the tray icon for Windows. + trayIcon?.setImage(newFavicon); + } else { + trayIcon?.setImage(newFavicon); + global.mainWindow?.setIcon(newFavicon); + } }); global.mainWindow?.webContents.on("page-title-updated", function (ev, title) { diff --git a/yarn.lock b/yarn.lock index 645a42add6..61e14e8b9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2243,7 +2243,7 @@ "@types/node" "*" form-data "^4.0.0" -"@types/node@*", "@types/node@18.19.115", "@types/node@^22.7.7": +"@types/node@*", "@types/node@18.19.115", "@types/node@^17.0.36", "@types/node@^22.7.7": version "18.19.115" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.115.tgz#cd94caf14472021b4443c99bcd7aac6bb5c4f672" integrity sha512-kNrFiTgG4a9JAn1LMQeLOv3MvXIPokzXziohMrMsvpYgLpdEt/mMiVYc4sGKtDfyxM5gIDF4VgrPRyCw4fHOYg== @@ -6237,6 +6237,20 @@ pluralizers@^0.1.7: resolved "https://registry.yarnpkg.com/pluralizers/-/pluralizers-0.1.7.tgz#8d38dd0a1b660e739b10ab2eab10b684c9d50142" integrity sha512-mw6AejUiCaMQ6uPN9ObjJDTnR5AnBSmnHHy3uVTbxrSFSxO5scfwpTs8Dxyb6T2v7GSulhvOq+pm9y+hXUvtOA== +png-to-ico@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/png-to-ico/-/png-to-ico-2.1.8.tgz#fdc2eda6f197df1d6c33400707e36c3b802ac6dd" + integrity sha512-Nf+IIn/cZ/DIZVdGveJp86NG5uNib1ZXMiDd/8x32HCTeKSvgpyg6D/6tUBn1QO/zybzoMK0/mc3QRgAyXdv9w== + dependencies: + "@types/node" "^17.0.36" + minimist "^1.2.6" + pngjs "^6.0.0" + +pngjs@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" + integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" From 812dd20b39e6ec094293bc7edd7ff6275df3682c Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Thu, 17 Jul 2025 11:30:39 +0100 Subject: [PATCH 10/11] fix i18n --- src/i18n/strings/en_EN.json | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a97fb5b919..d4882be5e4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -35,6 +35,13 @@ "file_menu": { "label": "File" }, + "icon_overlay": { + "description_error": "Error", + "description_notifications": { + "one": "You have %(count)s unread notification.", + "other": "You have %(count)s unread notifications." + } + }, "menu": { "hide": "Hide", "hide_others": "Hide Others", @@ -76,13 +83,5 @@ "bring_all_to_front": "Bring All to Front", "label": "Window", "zoom": "Zoom" - }, - "icon_overlay": { - "description_notifications": { - "one": "You have %(count)s unread notification.", - "other": "You have %(count)s unread notifications." - }, - "description_error": "Error" } - } From 0c16caf519f5562cb48eadbce171e808350c9bcd Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 18 Jul 2025 08:22:58 +0100 Subject: [PATCH 11/11] copyrights --- src/badge.ts | 7 +++++++ src/electron-main.ts | 2 +- src/ipc.ts | 2 +- src/tray.ts | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/badge.ts b/src/badge.ts index e54088b587..c28ae7f5cb 100644 --- a/src/badge.ts +++ b/src/badge.ts @@ -1,3 +1,10 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE files in the repository root for full details. +*/ + import { app, ipcMain, type IpcMainEvent, nativeImage } from "electron"; import { _t } from "./language-helper.js"; diff --git a/src/electron-main.ts b/src/electron-main.ts index 3e02206a77..d907312298 100644 --- a/src/electron-main.ts +++ b/src/electron-main.ts @@ -1,5 +1,5 @@ /* -Copyright 2018-2024 New Vector Ltd. +Copyright 2018-2025 New Vector Ltd. Copyright 2017-2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2016 Aviral Dasgupta Copyright 2016 OpenMarket Ltd diff --git a/src/ipc.ts b/src/ipc.ts index 6050553af2..580e335663 100644 --- a/src/ipc.ts +++ b/src/ipc.ts @@ -1,5 +1,5 @@ /* -Copyright 2022-2024 New Vector Ltd. +Copyright 2022-2025 New Vector Ltd. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE files in the repository root for full details. diff --git a/src/tray.ts b/src/tray.ts index 1489b55bfb..0e906fb65d 100644 --- a/src/tray.ts +++ b/src/tray.ts @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +Copyright 2024-2025 New Vector Ltd. Copyright 2017 Karl Glatz Copyright 2017 OpenMarket Ltd