Skip to content

Commit

Permalink
Add application icon (#296)
Browse files Browse the repository at this point in the history
* configure lfs tracking

* add application icon and script; update readme

- add icon asset to app
- update README with tagline and to display the icon
- add script and libs to generate application icons from single asset
- update ignore files to ignore generated assets (/icons/out)
  • Loading branch information
cloverich authored Jan 12, 2025
1 parent a00ef53 commit 03f102c
Show file tree
Hide file tree
Showing 8 changed files with 1,351 additions and 5 deletions.
1,014 changes: 1,014 additions & 0 deletions .gitattributes

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ tmp/
# build output
dist/
packaged/
icons/out

# bundler (dev) outputs .bundle files
# along-side src files, used during development
Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# Chronicles
#

<div align="center">
<img src="icons/src/input_icon.png" width="200" height="200">
<h1>Chronicles</h1>
<p>
<b>Journaling for the absent minded</b>
</p>
<br>
<br>
<br>
</div>

Electron based markdown journaling application, in the spirit of [incremental note taking][incr-notes].

**Status**: Hobby project, prototyping and re-working UX to try out various concepts with little regard for usability, stability, or appearances.
**Status**: Hobby project, prototyping and re-working UX to try out various concepts with little regard for usability, stability, or appearances. Check [releases](https://github.com/cloverich/chronicles/releases) for latest updates and [roadmap](https://github.com/cloverich/chronicles/issues/160) for the plan.

## Development

Expand Down
Binary file added icons/src/input_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions package.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
const { packager } = require("@electron/packager");
const { rebuild } = require("@electron/rebuild");
const path = require("path");

// These arguments are provided by build.sh
const srcDir = process.argv[2];
Expand All @@ -35,9 +36,24 @@ console.log(
outDir,
);

const iconPath = {
darwin: path.resolve("./icons/out/app.icns"), // macOS
win32: path.resolve("./icons/out/app.ico"), // Windows
linux: path.resolve("./icons/out/app-512.png"), // Linux (recommended 512x512 PNG)
}[process.platform];

if (!iconPath) {
console.error(
"Unsupported platform -- cannot find application icon",
platform,
);
process.exit(1);
}

packager({
dir: srcDir,
out: outDir,
icon: iconPath,
// … other options
// Documentation does this in afterCopy. Why did I do this in afterPrune?
afterPrune: [
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "chronicles",
"name": "Chronicles",
"version": "1.0.0",
"main": "main.bundle.js",
"scripts": {
Expand All @@ -10,7 +10,7 @@
"lint:types:check": "tsc --noEmit --skipLibCheck",
"postinstall": "yarn run electron-rebuild",
"prestart": "tailwindcss -i ./src/index.css -o ./src/index-compiled.css",
"prebuild": "yarn run lint && tailwindcss -i ./src/index.css -o ./src/index-compiled.css",
"prebuild": "yarn run lint && tailwindcss -i ./src/index.css -o ./src/index-compiled.css && node ./scripts/icon.js",
"start": "node ./scripts/dev.mjs",
"pretest": "node ./scripts/test.mjs",
"test": "mocha 'src/**/*.test.bundle.js'"
Expand Down Expand Up @@ -54,6 +54,7 @@
"electron": "^28.2.0",
"esbuild": "^0.20.0",
"evergreen-ui": "^7.1.9",
"icon-gen": "^5.0.0",
"lodash": "^4.17.21",
"lucide-react": "^0.314.0",
"luxon": "^2.4.0",
Expand All @@ -73,6 +74,7 @@
"react-day-picker": "^8.10.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.3.0",
"sharp": "^0.33.5",
"slate": "^0.101.5",
"slate-history": "^0.100.0",
"slate-hyperscript": "^0.100.0",
Expand Down
91 changes: 91 additions & 0 deletions scripts/icons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const sharp = require("sharp");
// https://github.com/akabekobeko/npm-icon-gen
const iconGen = require("icon-gen");
const path = require("path");
const fs = require("fs");

/**
* This file is used to generate icons for the app based off of an input asset. Not much
* testing or evaluation went into this script.
*/

// Function to check if file exists
const fileExists = (filePath) => fs.existsSync(filePath);

// Paths
const inputImage = path.resolve("./icons/src/input_icon.png");
const inputPng = path.resolve("./icons/src/temp-icon.png"); // Temporary PNG file path
const roundedPng = path.resolve("./icons/src/rounded-icon.png"); // Rounded PNG file path
const outputDir = path.resolve("./icons/out");
console.log(outputDir);

// Create output directory if not exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir);
}

// Function to create an Apple-style superellipse mask as an SVG
function generateSuperellipseSVG(size, cornerRadius = size * 0.1953125) {
return `
<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="${size}" height="${size}" rx="${cornerRadius}" ry="${cornerRadius}" fill="#fff"/>
</svg>`;
}

// Save superellipse SVG and apply mask
async function applySuperellipseMask(inputPath, outputPath) {
const size = 1024;
const superellipseSVG = generateSuperellipseSVG(size); // Official radius for macOS
fs.writeFileSync("temp-superellipse.svg", superellipseSVG); // Save SVG temporarily

await sharp(inputPath)
.resize(size, size)
.composite([{ input: "temp-superellipse.svg", blend: "dest-in" }])
.toFile(outputPath);

fs.unlinkSync("temp-superellipse.svg"); // Clean up
console.log(`Superellipse icon saved to: ${outputPath}`);
}

// Main script
(async () => {
console.log("Generating icons...");
try {
// When generating and manipulating source images, I ended up with various formats,
// so until the icon process is mature, just accept all of these so I can swap it out a few
// times while I get a feel for what the best input is.
if (path.extname(inputImage).toLowerCase() === ".webp") {
await sharp(inputImage).toFormat("png").toFile(inputPng);
} else if (path.extname(inputImage).toLowerCase() === ".tiff") {
await sharp(inputImage).toFormat("png").toFile(inputPng);
} else if (path.extname(inputImage).toLowerCase() === ".png") {
fs.copyFileSync(inputImage, inputPng);
} else {
throw new Error(
"Unsupported image format. Please use PNG or Web or TIFF.",
);
}

// MacOS icons use a "superellipse" mask semi rounded, not quite
// a "squircle" but not a perfect circle either.
await applySuperellipseMask(inputPng, roundedPng);

// Generate icons from the PNG
console.log("generating inputPng", outputDir);
console.log(`Generating icons from ${outputDir}...`);
await iconGen(roundedPng, outputDir, {
report: true,
dir: outputDir,
modes: ["icns", "ico", "favicon"], // macOS, Windows, web
});
console.log(`Icons saved in "${outputDir}"`);

// Clean up temporary PNG
if (fileExists(roundedPng)) {
fs.unlinkSync(roundedPng); // Delete the temporary PNG file
console.log(`Temporary PNG ${roundedPng} removed.`);
}
} catch (err) {
console.error("Error:", err);
}
})();
Loading

0 comments on commit 03f102c

Please sign in to comment.