Rework icon with higher quality and new color
Change icon color to match the primary color of the theme (i.e., `#3a65ab`). The new color looks good on both dark and light surfaces which solves #155. Introduce SVG logo instead of PNG for better quality and scalability. Improve icon creation. Introduce an automated script to create different logo formats in different sizes enabling easier update of logo from single place.
This commit is contained in:
12
img/README.md
Normal file
12
img/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# img
|
||||
|
||||
This folder contains image files and other resources related to images.
|
||||
|
||||
## logo.svg
|
||||
|
||||
[logo.svg](./logo.svg) is the master logo from which all other icons or images are created from.
|
||||
It should be the only file that will be changed manually.
|
||||
|
||||
[`logo-update.mjs`](./logo-update.mjs) script in this folder updates all the logo files.
|
||||
It should be executed everytime the logo is changed.
|
||||
It automates recreation of logo files in different formats.
|
||||
127
img/logo-update.mjs
Normal file
127
img/logo-update.mjs
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
import { resolve, join } from 'path';
|
||||
import { rm, mkdtemp, stat } from 'fs/promises';
|
||||
import { spawn } from 'child_process';
|
||||
import { URL, fileURLToPath } from 'url';
|
||||
|
||||
class Paths {
|
||||
constructor(selfDirectory) {
|
||||
const projectRoot = resolve(selfDirectory, '../');
|
||||
this.sourceImage = join(projectRoot, 'img/logo.svg');
|
||||
this.publicDirectory = join(projectRoot, 'public');
|
||||
this.electronBuildDirectory = join(projectRoot, 'build');
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Source image: ${this.sourceImage}\n`
|
||||
+ `Public directory: ${this.publicDirectory}\n`
|
||||
+ `Electron build directory: ${this.electronBuildDirectory}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const paths = new Paths(getCurrentScriptDirectory());
|
||||
console.log(`Paths:\n\t${paths.toString().replaceAll('\n', '\n\t')}`);
|
||||
await updateDesktopLauncherAndTrayIcon(paths.sourceImage, paths.publicDirectory);
|
||||
await updateWebFavicon(paths.sourceImage, paths.publicDirectory);
|
||||
await updateDesktopIcons(paths.sourceImage, paths.electronBuildDirectory);
|
||||
console.log('🎉 (Re)created icons successfully.');
|
||||
}
|
||||
|
||||
async function updateDesktopLauncherAndTrayIcon(sourceImage, publicFolder) {
|
||||
await ensureFileExists(sourceImage);
|
||||
await ensureFolderExists(publicFolder);
|
||||
const electronTrayIconFile = join(publicFolder, 'icon.png');
|
||||
console.log(`Updating desktop launcher and tray icon at ${electronTrayIconFile}.`);
|
||||
await runCommand(
|
||||
'npx',
|
||||
'svgexport',
|
||||
sourceImage,
|
||||
electronTrayIconFile,
|
||||
);
|
||||
}
|
||||
|
||||
async function updateWebFavicon(sourceImage, faviconFolder) {
|
||||
console.log('Updating favicon');
|
||||
await ensureFileExists(sourceImage);
|
||||
await ensureFolderExists(faviconFolder);
|
||||
await runCommand(
|
||||
'npx',
|
||||
'icon-gen',
|
||||
`--input ${sourceImage}`,
|
||||
`--output ${faviconFolder}`,
|
||||
'--ico',
|
||||
'--ico-name \'favicon\'',
|
||||
'--report',
|
||||
);
|
||||
}
|
||||
|
||||
async function updateDesktopIcons(sourceImage, electronIconsDir) {
|
||||
await ensureFileExists(sourceImage);
|
||||
await ensureFolderExists(electronIconsDir);
|
||||
const temporaryDir = await mkdtemp('icon-');
|
||||
const temporaryPngFile = join(temporaryDir, 'icon.png');
|
||||
console.log(`Converting from SVG (${sourceImage}) to PNG: ${temporaryPngFile}`); // required by icon-builder
|
||||
await runCommand(
|
||||
'npx',
|
||||
'svgexport',
|
||||
sourceImage,
|
||||
temporaryPngFile,
|
||||
'1024:1024',
|
||||
);
|
||||
console.log(`Creating electron icons to ${electronIconsDir}.`);
|
||||
await runCommand(
|
||||
'npx',
|
||||
'electron-icon-builder',
|
||||
`--input="${temporaryPngFile}"`,
|
||||
`--output="${electronIconsDir}"`,
|
||||
'--flatten',
|
||||
);
|
||||
console.log('Cleaning up temporary directory.');
|
||||
await rm(temporaryDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
async function ensureFileExists(filePath) {
|
||||
const path = await stat(filePath);
|
||||
if (!path.isFile()) {
|
||||
throw new Error(`Not a file: ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureFolderExists(folderPath) {
|
||||
const path = await stat(folderPath);
|
||||
if (!path.isDirectory()) {
|
||||
throw new Error(`Not a directory: ${folderPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function runCommand(...args) {
|
||||
const command = args.join(' ');
|
||||
console.log(`Running command: ${command}`);
|
||||
await new Promise((resolve, reject) => {
|
||||
const process = spawn(command, { shell: true });
|
||||
process.stdout.on('data', (stdout) => {
|
||||
console.log(stdout.toString());
|
||||
});
|
||||
process.stderr.on('data', (stderr) => {
|
||||
console.error(stderr.toString());
|
||||
});
|
||||
process.on('error', (err) => {
|
||||
reject(err);
|
||||
});
|
||||
process.on('close', (exitCode) => {
|
||||
if (exitCode !== 0) {
|
||||
reject(new Error(`Process exited with non-zero exit code: ${exitCode}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
process.stdin.end();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getCurrentScriptDirectory() {
|
||||
return fileURLToPath(new URL('.', import.meta.url));
|
||||
}
|
||||
|
||||
await main();
|
||||
56
img/logo.svg
Normal file
56
img/logo.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="90.2998mm" height="90.2998mm"
|
||||
viewBox="0 0 256 256">
|
||||
<path id="logo"
|
||||
fill="#3a65ab" stroke="#3a65ab" stroke-width="1"
|
||||
d="M 128.00,173.00
|
||||
C 128.00,173.00 102.00,175.00 102.00,175.00
|
||||
85.39,174.97 64.02,170.31 49.00,163.22
|
||||
38.46,158.24 28.39,152.17 20.01,143.96
|
||||
14.88,138.93 10.72,133.32 7.31,127.00
|
||||
3.36,119.66 1.10,112.37 1.00,104.00
|
||||
1.00,104.00 1.00,98.00 1.00,98.00
|
||||
1.29,73.92 24.76,53.44 44.00,42.43
|
||||
84.66,19.15 129.75,23.12 169.00,47.00
|
||||
188.74,59.02 207.93,76.23 208.00,101.00
|
||||
208.00,101.00 188.00,101.00 188.00,101.00
|
||||
186.46,86.48 168.72,71.84 157.00,64.61
|
||||
140.76,54.59 121.33,46.78 102.00,47.00
|
||||
88.42,47.16 72.20,52.52 60.00,58.32
|
||||
45.30,65.30 19.83,84.81 21.10,103.00
|
||||
21.39,107.16 22.92,110.33 24.76,114.00
|
||||
32.70,129.78 48.16,140.02 64.00,146.72
|
||||
75.16,151.44 92.90,155.26 105.00,154.99
|
||||
113.45,154.79 121.81,152.84 130.00,151.00
|
||||
130.00,151.00 128.00,173.00 128.00,173.00 Z
|
||||
M 136.00,79.00
|
||||
C 142.71,81.35 144.84,93.60 144.99,100.00
|
||||
145.51,122.74 130.31,140.73 107.00,141.00
|
||||
83.63,141.26 67.43,126.52 66.09,103.00
|
||||
64.82,80.73 85.85,58.90 104.00,64.00
|
||||
100.18,69.73 95.45,74.53 96.20,82.00
|
||||
97.29,92.87 110.06,102.98 121.00,99.03
|
||||
129.92,95.81 134.61,87.96 136.00,79.00 Z
|
||||
M 186.00,113.46
|
||||
C 206.11,110.69 225.57,114.92 239.91,130.01
|
||||
252.85,143.63 255.21,157.09 255.00,175.00
|
||||
254.76,195.49 241.26,214.25 223.00,222.88
|
||||
213.06,227.58 204.72,228.12 194.00,228.00
|
||||
150.34,227.49 126.71,178.85 146.32,142.00
|
||||
154.93,125.82 168.55,117.23 186.00,113.46 Z
|
||||
M 233.00,181.00
|
||||
C 242.24,158.78 221.84,133.54 199.00,133.01
|
||||
188.40,132.77 182.75,135.31 174.00,141.00
|
||||
178.60,146.85 195.92,157.24 203.00,161.86
|
||||
209.82,166.32 226.61,178.55 233.00,181.00 Z
|
||||
M 221.00,200.00
|
||||
C 216.39,194.15 206.42,188.61 200.00,184.33
|
||||
192.31,179.21 168.77,162.59 162.00,160.00
|
||||
159.67,165.03 159.94,166.57 160.00,172.00
|
||||
160.23,190.99 177.11,207.55 196.00,207.99
|
||||
206.60,208.23 212.25,205.69 221.00,200.00 Z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user