From cf042b25e773e9ec7164ab647bb21d4b59d317d4 Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Tue, 13 Oct 2026 10:51:22 +0300 Subject: [PATCH] =?UTF-8?q?icons:=20per-platform=20icon=20set=20=E2=80=94?= =?UTF-8?q?=20real=20Liquid=20Glass=20on=20macOS,=20flat=20design=20on=20W?= =?UTF-8?q?indows/Linux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hand-packed icns was pixel-clean (verified by unpacking and scanning every size), but macOS 26 composites legacy .icns icons into the Liquid Glass system at render time; stacked on Icon Composer's baked PNG-export lighting, that clips the rim highlights into the near-corner specks and abrupt highlight ends (owner report 2026-10-13). The fix is the real layered icon: mac.icon now points at the Icon Composer document (build/icon.icon, copied from ~/Documents/Affinity/lh-logo) and electron-builder compiles it via actool into Assets.car — macOS 26+ renders the true Liquid Glass design (dark/tinted variants included). actool's backwards-compat icns caps at 256px, so scripts/after-pack.cjs swaps in the existing full-res build/icon.icns before signing: pre-Tahoe macOS keeps today's quality, Tahoe gets the layered icon. Windows/Linux get the owner's flat design (rounded squircle with baked transparency): scripts/make-icons.mjs renders build/icon-flat.svg via resvg into build/icon.ico (16→256 PNG entries) + build/icon.png (512), and electron-builder.yml pins win.icon/linux.icon. desktop:dist injects DEVELOPER_DIR (actool needs Xcode; the machine selects CommandLineTools by default, CI's setup-xcode takes precedence via the :- default). The release workflow's mac job selects Xcode 26+ explicitly. Verified locally: mac --dir build (Assets.car + checksummed full-res fallback + CFBundleIconName/IconFile) and the win-unpacked exe carries our ico bytes; NSIS/AppImage assembly is CI's (cross-build tooling can't run on arm64 macOS). Runbook updated. 1610/1610. --- .github/workflows/release.yml | 9 +++++ build/icon-flat.svg | 20 +++++++++++ build/icon.ico | Bin 0 -> 18529 bytes build/icon.icon/Assets/lh_0 2.svg | 9 +++++ build/icon.icon/Assets/lh_1 2.svg | 9 +++++ build/icon.icon/Assets/lh_2 2.svg | 9 +++++ build/icon.icon/icon.json | 37 ++++++++++++++++++++ build/icon.png | Bin 0 -> 18074 bytes docs/desktop-release.md | 2 +- electron-builder.yml | 17 +++++++++ package.json | 2 +- scripts/after-pack.cjs | 27 +++++++++++++++ scripts/make-icons.mjs | 55 ++++++++++++++++++++++++++++++ 13 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 build/icon-flat.svg create mode 100644 build/icon.ico create mode 100644 build/icon.icon/Assets/lh_0 2.svg create mode 100644 build/icon.icon/Assets/lh_1 2.svg create mode 100644 build/icon.icon/Assets/lh_2 2.svg create mode 100644 build/icon.icon/icon.json create mode 100644 build/icon.png create mode 100644 scripts/after-pack.cjs create mode 100644 scripts/make-icons.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c31f35..a26dbb5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -113,6 +113,15 @@ jobs: with: node-version: "26" cache: npm + # The mac icon is the Icon Composer document (build/icon.icon); + # compiling it needs actool ≥ 26 (Xcode 26+). macos-15 images carry + # several Xcodes with an older default — select the latest stable + # explicitly. No-op for the windows/linux matrix legs. + - name: Select Xcode 26+ for actool (mac icon compile) + if: runner.os == 'macOS' + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable - run: npm ci - name: Native renderer tests on this OS run: npx electron --import tsx --test tests/pdf-native-render.test.ts tests/pdf-ops.test.ts tests/svg-native-render.test.ts tests/browser-service.test.ts diff --git a/build/icon-flat.svg b/build/icon-flat.svg new file mode 100644 index 0000000..19fa818 --- /dev/null +++ b/build/icon-flat.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/build/icon.ico b/build/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7b0f23011997564f0ef93fab17cb87579dfe037d GIT binary patch literal 18529 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 U>U>wKE&u=k07*qoM6N<$f@|kO)&Kwi literal 0 HcmV?d00001 diff --git a/build/icon.icon/Assets/lh_0 2.svg b/build/icon.icon/Assets/lh_0 2.svg new file mode 100644 index 0000000..6ca3e24 --- /dev/null +++ b/build/icon.icon/Assets/lh_0 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/build/icon.icon/Assets/lh_1 2.svg b/build/icon.icon/Assets/lh_1 2.svg new file mode 100644 index 0000000..0f795aa --- /dev/null +++ b/build/icon.icon/Assets/lh_1 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/build/icon.icon/Assets/lh_2 2.svg b/build/icon.icon/Assets/lh_2 2.svg new file mode 100644 index 0000000..bbcb692 --- /dev/null +++ b/build/icon.icon/Assets/lh_2 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/build/icon.icon/icon.json b/build/icon.icon/icon.json new file mode 100644 index 0000000..dcdac9d --- /dev/null +++ b/build/icon.icon/icon.json @@ -0,0 +1,37 @@ +{ + "fill" : { + "automatic-gradient" : "srgb:0.07843,0.07843,0.07843,1.00000" + }, + "groups" : [ + { + "layers" : [ + { + "image-name" : "lh_2 2.svg", + "name" : "lh_2 2" + }, + { + "image-name" : "lh_1 2.svg", + "name" : "lh_1 2" + }, + { + "image-name" : "lh_0 2.svg", + "name" : "lh_0 2" + } + ], + "shadow" : { + "kind" : "neutral", + "opacity" : 0.5 + }, + "translucency" : { + "enabled" : true, + "value" : 0.3 + } + } + ], + "supported-platforms" : { + "circles" : [ + "watchOS" + ], + "squares" : "shared" + } +} \ No newline at end of file diff --git a/build/icon.png b/build/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..315d202239f908a92332358c5d8167b983e5c3b1 GIT binary patch literal 18074 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 literal 0 HcmV?d00001 diff --git a/docs/desktop-release.md b/docs/desktop-release.md index 3ac8301..e6dd66f 100644 --- a/docs/desktop-release.md +++ b/docs/desktop-release.md @@ -12,7 +12,7 @@ Windows signing is deliberately disabled for this first release. SmartScreen may The custom app bar stays on all three platforms. macOS keeps its left-side traffic lights; Windows/Linux use Electron's native right-side Window Controls Overlay, with CSS space reserved from the overlay's actual geometry. Windows/Linux hide the native menu until Alt is pressed. macOS keeps its system menu. -The existing `build/icon.icns` is the source for all platforms: electron-builder converts it to ICO/Linux PNGs. Linux's desktop identity is `learnharness.desktop`. Public package-maintainer metadata uses the owner's GitHub no-reply address; support goes through [GitHub Issues](https://github.com/amosblomqvist/learnharness/issues). +Icons are per-platform (2026-10-13): macOS compiles the Icon Composer document `build/icon.icon` via actool into `Assets.car` (macOS 26 Liquid Glass) plus a fallback icns that `scripts/after-pack.cjs` replaces with the hand-packed full-res `build/icon.icns` (actool's caps at 256px); Windows embeds `build/icon.ico` and Linux uses `build/icon.png`, both generated from the flat design (`build/icon-flat.svg`) by `node scripts/make-icons.mjs`. Building the mac icon needs Xcode 26+ (`actool`) — `desktop:dist` injects `DEVELOPER_DIR` locally and the release workflow selects Xcode via setup-xcode. Linux's desktop identity is `learnharness.desktop`. Public package-maintainer metadata uses the owner's GitHub no-reply address; support goes through [GitHub Issues](https://github.com/amosblomqvist/learnharness/issues). ## Build pipeline diff --git a/electron-builder.yml b/electron-builder.yml index ba15b18..5c91e18 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -13,6 +13,19 @@ appId: com.learnharness.LearnHarness productName: LearnHarness directories: output: dist +# Icons (owner ruling, 2026-10-13) — per-platform sources in build/: +# mac build/icon.icon — the Icon Composer document (layered Liquid +# Glass); actool (Xcode 26+) compiles Assets.car (CFBundleIconName, +# macOS 26+ renders the true layered icon) + an actool fallback icns +# capped at 256px, which scripts/after-pack.cjs swaps for the +# hand-packed full-res build/icon.icns (pre-Tahoe keeps 16→1024). +# Requires actool ≥ 26: desktop:dist injects DEVELOPER_DIR locally; +# the release workflow's mac job runs setup-xcode. +# win build/icon.ico — generated (16→256) from the flat design by +# scripts/make-icons.mjs. +# linux build/icon.png — 512×512, same script. Rounded squircle with +# baked transparency on both (owner ruling) — Windows/Linux don't +# mask, the shape ships in the artwork. files: # Only the shell goes into the asar. ORDER IS LOAD-BEARING (payload # wave, 2026-09-24): electron-builder's dependency collector packs the @@ -82,6 +95,7 @@ extraResources: filter: - "standalone/**/*" mac: + icon: build/icon.icon # zip rides next to dmg (flipped 2026-10-08): Squirrel.Mac — Electron's # macOS auto-updater — installs from ZIP and cannot read DMGs. DMG is # for humans; ZIP + blockmap are the updater's payload. @@ -114,7 +128,9 @@ mac: notarize: true dmg: title: "${productName} ${version}" +afterPack: scripts/after-pack.cjs win: + icon: build/icon.ico target: - target: nsis arch: @@ -128,6 +144,7 @@ nsis: oneClick: true perMachine: false linux: + icon: build/icon.png target: - target: AppImage arch: diff --git a/package.json b/package.json index dcbb9fe..c4c1e69 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "typecheck": "tsc --noEmit", "desktop:dev": "concurrently -k \"next dev\" \"LH_DEV_URL=http://localhost:3000 electron .\"", "dev:fresh": "bash scripts/dev-fresh.sh", - "desktop:dist": "rm -rf dist .next/standalone && next build && node scripts/build-server.mjs && node scripts/smoke-server.mjs && APPLE_KEYCHAIN_PROFILE=LH_NOTARY APPLE_TEAM_ID=JS6XN22HY6 electron-builder --mac --arm64" + "desktop:dist": "rm -rf dist .next/standalone && next build && node scripts/build-server.mjs && node scripts/smoke-server.mjs && DEVELOPER_DIR=${DEVELOPER_DIR:-/Applications/Xcode.app/Contents/Developer} APPLE_KEYCHAIN_PROFILE=LH_NOTARY APPLE_TEAM_ID=JS6XN22HY6 electron-builder --mac --arm64" }, "dependencies": { "@earendil-works/pi-agent-core": "1.0.2", diff --git a/scripts/after-pack.cjs b/scripts/after-pack.cjs new file mode 100644 index 0000000..69597b9 --- /dev/null +++ b/scripts/after-pack.cjs @@ -0,0 +1,27 @@ +// electron-builder afterPack hook — mac only. +// +// WHY (owner ruling 2026-10-13): mac.icon is the Icon Composer document +// (build/icon.icon); actool compiles it into Assets.car (the macOS 26 +// Liquid Glass render) PLUS a backwards-compat icns — but actool's +// fallback icns caps at 256px (known actool behavior), which looks soft +// at Finder/Launchpad sizes on pre-Tahoe macOS. This hook swaps in the +// hand-packed full-res build/icon.icns (all 10 entries, 16→1024, packed +// via iconutil from the design's PNG exports) AFTER packaging and BEFORE +// signing, so: +// +// macOS 26+ → Assets.car (CFBundleIconName) — true layered glass icon; +// older mac → icon.icns (CFBundleIconFile) — today's full-res flatten. +// +// Windows/Linux builds return immediately (their icons are static files: +// build/icon.ico / build/icon.png). +const fs = require("node:fs"); +const path = require("node:path"); + +module.exports = async function afterPack(context) { + if (context.electronPlatformName !== "darwin") return; + const appBundle = path.join(context.appOutDir, `${context.packager.appInfo.productFilename}.app`); + const target = path.join(appBundle, "Contents", "Resources", "icon.icns"); + const source = path.join(context.packager.info.projectDir, "build", "icon.icns"); + fs.copyFileSync(source, target); + console.log(`[after-pack] pre-Tahoe fallback icns (full-res) → ${target}`); +}; diff --git a/scripts/make-icons.mjs b/scripts/make-icons.mjs new file mode 100644 index 0000000..066dbb6 --- /dev/null +++ b/scripts/make-icons.mjs @@ -0,0 +1,55 @@ +// Regenerate the Windows/Linux icon set from the flat design source +// (build/icon-flat.svg — the owner's Affinity export, rounded squircle +// with baked transparency, owner ruling 2026-10-13). +// +// build/icon.ico — Windows (.ico with 16/24/32/48/64/128/256 PNG entries) +// build/icon.png — Linux (512×512; AppImage/deb/desktop entry) +// +// macOS does NOT come from here: build/icon.icon (the Icon Composer +// document) is the mac source of truth, compiled by actool at package +// time into Assets.car (macOS 26 Liquid Glass) + a fallback icns; the +// hand-packed build/icon.icns replaces that fallback's 256px cap via +// scripts/after-pack.cjs (pre-Tahoe keeps full-res). +// +// Renders each size DIRECTLY from the SVG (resvg) — sharper squircle +// edges than downsampling a raster. Committed outputs; rerun only when +// the design changes: `node scripts/make-icons.mjs`. +import { Resvg } from "@resvg/resvg-js"; +import { readFileSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const svg = readFileSync(path.join(root, "build", "icon-flat.svg"), "utf8"); + +const render = (size) => new Resvg(svg, { fitTo: { mode: "width", value: size } }).render(size, size).asPng(); + +// ---- build/icon.ico: ICONDIR + PNG entries ------------------------------ +const ICO_SIZES = [16, 24, 32, 48, 64, 128, 256]; +const pngs = ICO_SIZES.map(render); +// Header (6) + one 16-byte directory entry per image, then the PNGs. +const header = Buffer.alloc(6 + ICO_SIZES.length * 16); +header.writeUInt16LE(0, 0); // reserved +header.writeUInt16LE(1, 2); // type: icon +header.writeUInt16LE(ICO_SIZES.length, 4); +let offset = header.length; +ICO_SIZES.forEach((size, i) => { + const e = 6 + i * 16; + header.writeUInt8(size === 256 ? 0 : size, e); // 0 encodes 256 + header.writeUInt8(size === 256 ? 0 : size, e + 1); + header.writeUInt8(0, e + 2); // colors + header.writeUInt8(0, e + 3); // reserved + header.writeUInt16LE(1, e + 4); // planes + header.writeUInt16LE(32, e + 6); // bpp + header.writeUInt32LE(pngs[i].length, e + 8); + header.writeUInt32LE(offset, e + 12); + offset += pngs[i].length; +}); +const ico = Buffer.concat([header, ...pngs]); +writeFileSync(path.join(root, "build", "icon.ico"), ico); +console.log(`build/icon.ico ${ICO_SIZES.join("/")} — ${(ico.length / 1024).toFixed(1)} KB`); + +// ---- build/icon.png: the Linux icon ------------------------------------- +const png512 = render(512); +writeFileSync(path.join(root, "build", "icon.png"), png512); +console.log(`build/icon.png 512×512 — ${(png512.length / 1024).toFixed(1)} KB`);