[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
197
electron/bridges/appIconManager.cjs
Normal file
197
electron/bridges/appIconManager.cjs
Normal file
@@ -0,0 +1,197 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("node:path");
|
||||
const fs = require("node:fs");
|
||||
|
||||
const VALID_VARIANTS = new Set([
|
||||
"original",
|
||||
"bright",
|
||||
"dark",
|
||||
"colorful",
|
||||
"high-contrast",
|
||||
"white-navy",
|
||||
"white-sky",
|
||||
"white-rose",
|
||||
"white-emerald",
|
||||
"white-amber",
|
||||
"white-violet",
|
||||
"rainbow",
|
||||
]);
|
||||
|
||||
const DEFAULT_VARIANT = "original";
|
||||
|
||||
let currentVariant = DEFAULT_VARIANT;
|
||||
let currentIconPath = null;
|
||||
let preferPublicSources = false;
|
||||
let useMacIconSources = false;
|
||||
|
||||
function isValidAppIconVariant(variant) {
|
||||
return typeof variant === "string" && VALID_VARIANTS.has(variant);
|
||||
}
|
||||
|
||||
function normalizeAppIconVariant(variant) {
|
||||
return isValidAppIconVariant(variant) ? variant : DEFAULT_VARIANT;
|
||||
}
|
||||
|
||||
function isPackagedApp(app) {
|
||||
try {
|
||||
return app?.isPackaged === true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function pickExistingPath(candidates) {
|
||||
return candidates.find((candidate) => fs.existsSync(candidate));
|
||||
}
|
||||
|
||||
function buildSourceCandidates(appPath, relativeParts) {
|
||||
const publicCandidate = path.join(appPath, "public", ...relativeParts);
|
||||
const distCandidate = path.join(appPath, "dist", ...relativeParts);
|
||||
return preferPublicSources
|
||||
? [publicCandidate, distCandidate]
|
||||
: [distCandidate, publicCandidate];
|
||||
}
|
||||
|
||||
function resolveOriginalIconPath(appPath) {
|
||||
const primaryParts = useMacIconSources
|
||||
? ["icons", "variants", "macos", "original.png"]
|
||||
: ["icons", "variants", "original.png"];
|
||||
const candidates = useMacIconSources
|
||||
? buildSourceCandidates(appPath, primaryParts)
|
||||
: [
|
||||
...buildSourceCandidates(appPath, primaryParts),
|
||||
...buildSourceCandidates(appPath, ["icon-win.png"]),
|
||||
...buildSourceCandidates(appPath, ["icon.png"]),
|
||||
];
|
||||
const existing = pickExistingPath(candidates);
|
||||
if (existing) return existing;
|
||||
// Dev fallback: elf SVG.
|
||||
const svgCandidate = path.join(appPath, "public", "senmesh-elf-logo.svg");
|
||||
if (fs.existsSync(svgCandidate)) return svgCandidate;
|
||||
return candidates[0];
|
||||
}
|
||||
|
||||
function buildVariantSourceCandidates(appPath, fileName) {
|
||||
const relativeParts = useMacIconSources
|
||||
? ["icons", "variants", "macos", fileName]
|
||||
: ["icons", "variants", fileName];
|
||||
return buildSourceCandidates(appPath, relativeParts);
|
||||
}
|
||||
|
||||
function resolveVariantIconPath(variant, appPath) {
|
||||
const normalized = normalizeAppIconVariant(variant);
|
||||
if (normalized === "original") {
|
||||
return resolveOriginalIconPath(appPath);
|
||||
}
|
||||
|
||||
const fileName = `${normalized}.png`;
|
||||
const candidates = buildVariantSourceCandidates(appPath, fileName);
|
||||
const resolved = pickExistingPath(candidates);
|
||||
if (resolved) return resolved;
|
||||
return resolveOriginalIconPath(appPath);
|
||||
}
|
||||
|
||||
function resolveStrictVariantIconPath(variant, appPath) {
|
||||
const normalized = normalizeAppIconVariant(variant);
|
||||
if (normalized === "original") {
|
||||
return resolveOriginalIconPath(appPath);
|
||||
}
|
||||
|
||||
const fileName = `${normalized}.png`;
|
||||
const candidates = buildVariantSourceCandidates(appPath, fileName);
|
||||
return pickExistingPath(candidates) || null;
|
||||
}
|
||||
|
||||
function initializeAppIconManager(appPath, options = {}) {
|
||||
preferPublicSources = options.preferPublic === true;
|
||||
useMacIconSources = options.isMac === true;
|
||||
currentVariant = DEFAULT_VARIANT;
|
||||
currentIconPath = resolveVariantIconPath(currentVariant, appPath);
|
||||
return currentIconPath;
|
||||
}
|
||||
|
||||
function getAppIconPath(appPath) {
|
||||
if (!currentIconPath) {
|
||||
return initializeAppIconManager(appPath);
|
||||
}
|
||||
return currentIconPath;
|
||||
}
|
||||
|
||||
function getAppIconVariant() {
|
||||
return currentVariant;
|
||||
}
|
||||
|
||||
function createNativeImage(nativeImage, iconPath) {
|
||||
if (!nativeImage || !iconPath || !fs.existsSync(iconPath)) return null;
|
||||
try {
|
||||
// Read from disk so regenerated assets at the same path refresh immediately.
|
||||
return nativeImage.createFromBuffer(fs.readFileSync(iconPath));
|
||||
} catch {
|
||||
try {
|
||||
return nativeImage.createFromPath(iconPath);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyIconToWindow(win, iconPath, nativeImage) {
|
||||
if (!win || win.isDestroyed?.() || !iconPath || !win.setIcon) return;
|
||||
try {
|
||||
const image = createNativeImage(nativeImage, iconPath);
|
||||
if (image) {
|
||||
win.setIcon(image);
|
||||
return;
|
||||
}
|
||||
win.setIcon(iconPath);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function applyAppIconVariant(variant, context) {
|
||||
const { app, BrowserWindow, nativeImage, appPath, isMac } = context;
|
||||
preferPublicSources = !isPackagedApp(app);
|
||||
useMacIconSources = isMac === true;
|
||||
const normalized = normalizeAppIconVariant(variant);
|
||||
const iconPath = resolveStrictVariantIconPath(normalized, appPath);
|
||||
if (!iconPath || !fs.existsSync(iconPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentVariant = normalized;
|
||||
currentIconPath = iconPath;
|
||||
|
||||
const windows = BrowserWindow?.getAllWindows?.() || [];
|
||||
for (const win of windows) {
|
||||
applyIconToWindow(win, iconPath, nativeImage);
|
||||
}
|
||||
|
||||
if (isMac && app?.dock?.setIcon && nativeImage) {
|
||||
try {
|
||||
const dockImage = createNativeImage(nativeImage, iconPath);
|
||||
if (dockImage) {
|
||||
app.dock.setIcon(dockImage);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
DEFAULT_VARIANT,
|
||||
VALID_VARIANTS,
|
||||
isValidAppIconVariant,
|
||||
normalizeAppIconVariant,
|
||||
initializeAppIconManager,
|
||||
getAppIconPath,
|
||||
getAppIconVariant,
|
||||
resolveVariantIconPath,
|
||||
resolveStrictVariantIconPath,
|
||||
applyAppIconVariant,
|
||||
applyIconToWindow,
|
||||
};
|
||||
Reference in New Issue
Block a user