diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 9e54375..00ed035 100644 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -3,10 +3,12 @@ Adaptive icon. minSdk is 26, so this is the ONLY icon Android will ask for — no legacy raster fallback is needed. - The foreground is the shared maskable asset the web app already ships - (frontend/public/icon-maskable-512.png), which is drawn with the safe-zone - padding adaptive icons require. Reusing it means the phone, the web app and the - desktop all wear the same face rather than three near-misses. + The foreground is the inkwell mark alone on transparency, inside the 66dp safe + circle; the yellow is the background colour. It is rendered by + packaging/icons.py from the same drawing as the web and desktop icons, so all + three wear one face. Transparent rather than a full-bleed tile because the + monochrome layer below reuses it: a themed icon draws its alpha as a silhouette, + and an opaque foreground would come out as a solid square. --> diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 304847e..efbfb00 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/desktop/README.md b/desktop/README.md index 62afcb0..08544d7 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -45,13 +45,14 @@ cd desktop/src-tauri && cargo tauri dev ## Build (Linux) ```sh -cargo tauri icon "$(git rev-parse --show-toplevel)/frontend/public/icon.svg" cd desktop/src-tauri && cargo tauri build # -> .deb + .AppImage ``` -App icons are **generated** from the frontend's `icon.svg` via `cargo tauri icon` -(not committed; CI does this before `cargo tauri build`). Bundle targets: `deb`, -`appimage` (Linux-first; Windows/macOS later, no code changes expected). +App icons are **generated and committed**: `python3 packaging/icons.py` draws the +inkwell mark once and renders every web, desktop and Android icon from it, +including `app-icon.png` and `icons/*.png` here. The Windows lane derives its +`.ico` from `app-icon.png` with `cargo tauri icon` at build time. Bundle targets: +`deb`, `appimage` (Linux-first; Windows/macOS later, no code changes expected). ## Status diff --git a/desktop/src-tauri/app-icon.png b/desktop/src-tauri/app-icon.png index d62b0a8..eb5ac05 100644 Binary files a/desktop/src-tauri/app-icon.png and b/desktop/src-tauri/app-icon.png differ diff --git a/desktop/src-tauri/icons/128x128.png b/desktop/src-tauri/icons/128x128.png index bf90912..ff09050 100644 Binary files a/desktop/src-tauri/icons/128x128.png and b/desktop/src-tauri/icons/128x128.png differ diff --git a/desktop/src-tauri/icons/128x128@2x.png b/desktop/src-tauri/icons/128x128@2x.png index 370b36b..924841b 100644 Binary files a/desktop/src-tauri/icons/128x128@2x.png and b/desktop/src-tauri/icons/128x128@2x.png differ diff --git a/desktop/src-tauri/icons/32x32.png b/desktop/src-tauri/icons/32x32.png index f48f6d4..52a1058 100644 Binary files a/desktop/src-tauri/icons/32x32.png and b/desktop/src-tauri/icons/32x32.png differ diff --git a/desktop/src-tauri/icons/icon.png b/desktop/src-tauri/icons/icon.png index 7c35940..9cafc45 100644 Binary files a/desktop/src-tauri/icons/icon.png and b/desktop/src-tauri/icons/icon.png differ diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png index 6b05fca..1bd8ce1 100644 Binary files a/frontend/public/apple-touch-icon.png and b/frontend/public/apple-touch-icon.png differ diff --git a/frontend/public/favicon-32.png b/frontend/public/favicon-32.png index 8461f46..52a1058 100644 Binary files a/frontend/public/favicon-32.png and b/frontend/public/favicon-32.png differ diff --git a/frontend/public/icon-192.png b/frontend/public/icon-192.png index 0316119..204fcf0 100644 Binary files a/frontend/public/icon-192.png and b/frontend/public/icon-192.png differ diff --git a/frontend/public/icon-512.png b/frontend/public/icon-512.png index ffc0257..9cafc45 100644 Binary files a/frontend/public/icon-512.png and b/frontend/public/icon-512.png differ diff --git a/frontend/public/icon-maskable-512.png b/frontend/public/icon-maskable-512.png index 304847e..3893e96 100644 Binary files a/frontend/public/icon-maskable-512.png and b/frontend/public/icon-maskable-512.png differ diff --git a/frontend/public/icon-maskable.svg b/frontend/public/icon-maskable.svg index 68a7470..341a865 100644 --- a/frontend/public/icon-maskable.svg +++ b/frontend/public/icon-maskable.svg @@ -1,24 +1,30 @@ + rounded-square) never clip the mark, which sits inside the central 80% safe + zone. Same mark as icon.svg. --> - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - diff --git a/frontend/public/icon.svg b/frontend/public/icon.svg index 69a9092..43018f6 100644 --- a/frontend/public/icon.svg +++ b/frontend/public/icon.svg @@ -1,25 +1,29 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - diff --git a/packaging/icons.py b/packaging/icons.py new file mode 100644 index 0000000..b1b47f7 --- /dev/null +++ b/packaging/icons.py @@ -0,0 +1,128 @@ +"""Every icon Inkwell ships, from one drawing. + +The mark is a black inkpot with a quill standing in it, on the brand yellow +(Scribe note 5071). It is drawn ONCE below, in a 512 box, and each variant places it +with its own scale, because each platform crops differently: + +- icon.svg the rounded tile: web favicon/PWA "any", desktop app icon +- icon-maskable.svg full bleed, mark inside the central 80% (PWA "maskable") +- Android foreground mark alone on transparency inside the 66dp safe circle of a + 108dp adaptive canvas; the yellow is the background layer, and + the transparent mark is also what themed (monochrome) icons need + +Run from the repo root after changing the drawing; needs rsvg-convert (librsvg). +Generated output is committed, like any other generated file. + + python3 packaging/icons.py +""" +import subprocess +from pathlib import Path + +INK = "#18181b" +GOLD = "#F5C518" + +# The quill is drawn upright at the pot mouth (0,0), then rotated into place. +QUILL_AT = "translate(256 232) rotate(34)" + +QUILL_INK = f''' + + + + ''' + +POT_INK = f''' + + + ''' + +# The detail — the quill's shaft, two splits in its vane, the glint on the pot — is +# CUT OUT of the ink rather than painted over it in yellow. On the tile the two look +# the same; on Android's transparent foreground only a cut-out is right, because a +# themed icon draws the foreground's alpha as a silhouette, and painted detail +# would come out as solid, with the vane splits sticking out past its edge. +CUTS = ''' + + + + + '''.format(at=QUILL_AT) + +GLYPH = f''' + + + + {CUTS} + + + + {QUILL_INK}{POT_INK} + ''' +# Glyph bounds, measured from a render: centre the mark, then scale per variant. +CX, CY = 262, 226 + +def place(scale, cy=256): + return f'translate(256 {cy}) scale({scale}) translate({-CX} {-CY})' + +def svg(body, label=True): + aria = ' role="img" aria-label="Inkwell"' if label else '' + return f'\n{body}\n\n' + +def tile(scale=0.92): + return svg(f''' + + {GLYPH} + ''') + +def maskable(scale=0.8): + return svg(f''' + + {GLYPH} + ''') + +def foreground(scale=0.64): + # Android adaptive foreground: the mark alone on transparency, inside the 66dp + # safe circle of a 108dp canvas. The yellow lives in the background layer. + return svg(f''' {GLYPH} + ''', label=False) + + +ROOT = Path(__file__).resolve().parent.parent +WEB = ROOT / "frontend" / "public" +DESKTOP = ROOT / "desktop" / "src-tauri" +ANDROID = ROOT / "android" / "app" / "src" / "main" / "res" / "mipmap-xxxhdpi" + + +def render(svg_text: str, size: int, out: Path, background: str | None = None) -> None: + cmd = ["rsvg-convert", "-w", str(size), "-h", str(size), "-o", str(out)] + if background: + cmd += ["-b", background] + subprocess.run(cmd, input=svg_text.encode(), check=True) + + +def main() -> None: + icon, mask, fg = tile(), maskable(), foreground() + (WEB / "icon.svg").write_text(icon) + (WEB / "icon-maskable.svg").write_text(mask) + + render(icon, 32, WEB / "favicon-32.png") + render(icon, 192, WEB / "icon-192.png") + render(icon, 512, WEB / "icon-512.png") + render(mask, 512, WEB / "icon-maskable-512.png") + # iOS rounds the corners itself and shows transparency as black. + render(mask, 180, WEB / "apple-touch-icon.png") + + render(icon, 1024, DESKTOP / "app-icon.png") + render(icon, 32, DESKTOP / "icons" / "32x32.png") + render(icon, 128, DESKTOP / "icons" / "128x128.png") + render(icon, 256, DESKTOP / "icons" / "128x128@2x.png") + render(icon, 512, DESKTOP / "icons" / "icon.png") + + render(fg, 512, ANDROID / "ic_launcher_foreground.png") + + +if __name__ == "__main__": + main()