diff --git a/README.md b/README.md index fff41ed..0e8f1ea 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,8 @@ npm run build # TypeScript + Vite production build npm run desktop # Run Electron against the built dist/ npm run dist:dir # Build an unpacked Electron app in release/ npm run dist:mac # Build macOS .dmg and .zip packages in release/ +npm run dist:win # Build Windows installer + zip for the current Windows architecture +npm run dist:win:arm64 # Build Windows ARM64 installer + zip in release/ ``` ## Production-Like Local Run @@ -129,10 +131,12 @@ Current packaging commands: - `npm run dist:dir` creates an unpacked app bundle in `release/` - `npm run dist:mac` creates macOS `.dmg` and `.zip` artifacts in `release/` +- `npm run dist:win` creates Windows `.exe` and `.zip` artifacts for the current Windows architecture +- `npm run dist:win:arm64` creates Windows ARM64 `.exe` and `.zip` artifacts in `release/` The current mac build is unsigned and not notarized, which is fine for local use and testing but not enough for friction-free public distribution through Gatekeeper. -Windows and Linux targets are also configured in `package.json`, but they have not been validated in this repository yet. +Windows ARM64 packaging has been validated in this repository with Electron Builder on Windows 11 ARM running under Parallels. Linux targets are still configured in `package.json`, but they have not been validated here yet. ## Legacy Tauri Script diff --git a/package.json b/package.json index 8daa2d4..5cc99ae 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,14 @@ "dev:renderer": "vite", "dev:electron": "wait-on tcp:1420 && cross-env ELECTRON_RENDERER_URL=http://localhost:1420 electron .", "dev:desktop": "concurrently -k \"npm:dev:renderer\" \"npm:dev:electron\"", + "gen:win-icon": "node scripts/generate-win-icon.mjs", "build": "tsc && vite build", "desktop": "electron .", "dist:dir": "npm run build && electron-builder --dir", "dist:mac": "npm run build && electron-builder --mac dmg zip", "dist:linux": "npm run build && electron-builder --linux AppImage deb", + "dist:win": "npm run gen:win-icon && npm run build && electron-builder --win nsis zip", + "dist:win:arm64": "npm run gen:win-icon && npm run build && electron-builder --win nsis zip --arm64", "preview": "vite preview", "tauri": "tauri" }, diff --git a/scripts/generate-win-icon.mjs b/scripts/generate-win-icon.mjs new file mode 100644 index 0000000..1435494 --- /dev/null +++ b/scripts/generate-win-icon.mjs @@ -0,0 +1,46 @@ +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const projectRoot = path.resolve(__dirname, ".."); + +const sourcePngPath = path.join(projectRoot, "src-tauri", "icons", "128x128@2x.png"); +const targetIcoPath = path.join(projectRoot, "src-tauri", "icons", "icon.ico"); + +const pngBytes = fs.readFileSync(sourcePngPath); + +if (pngBytes.length < 24 || pngBytes.toString("ascii", 1, 4) !== "PNG") { + throw new Error(`Expected a PNG file at ${sourcePngPath}`); +} + +const width = pngBytes.readUInt32BE(16); +const height = pngBytes.readUInt32BE(20); + +if (width !== 256 || height !== 256) { + throw new Error(`Expected a 256x256 PNG, received ${width}x${height}`); +} + +const headerSize = 6; +const directoryEntrySize = 16; +const imageOffset = headerSize + directoryEntrySize; +const icoBytes = Buffer.alloc(imageOffset + pngBytes.length); + +icoBytes.writeUInt16LE(0, 0); +icoBytes.writeUInt16LE(1, 2); +icoBytes.writeUInt16LE(1, 4); + +icoBytes.writeUInt8(0, 6); +icoBytes.writeUInt8(0, 7); +icoBytes.writeUInt8(0, 8); +icoBytes.writeUInt8(0, 9); +icoBytes.writeUInt16LE(1, 10); +icoBytes.writeUInt16LE(32, 12); +icoBytes.writeUInt32LE(pngBytes.length, 14); +icoBytes.writeUInt32LE(imageOffset, 18); + +pngBytes.copy(icoBytes, imageOffset); + +fs.writeFileSync(targetIcoPath, icoBytes); +console.log(`Generated ${path.relative(projectRoot, targetIcoPath)} from ${path.relative(projectRoot, sourcePngPath)}`); diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index c5394a1..2d5e7f3 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ