Files
Hankan/app/vite.config.ts
MechaCat02 68403d6102 feat(icons): 한칸 on a jade ground
The PWA manifest declared icon-192.png, icon-512.png and favicon.svg. None
of the three existed, so install-to-home-screen and the browser tab both
resolved to nothing. That was a defect in what the last pass claimed as
done.

Drawn procedurally with Pillow rather than authored as SVG: there is no
dependable SVG rasteriser here to convert from, and ImageMagick falls back
to its MSVG renderer, which cannot be trusted with text. Pillow's
FreeType + RAQM stack shapes Hangul correctly, which is the only hard
requirement. The mark uses the app's own register — the serif face already
used for 한칸 on the boot screen, in --jade on --bg.

Two glyphs read well at 180px and up; at favicon sizes 한칸 turns to mush,
so the small sizes carry 한 alone.

  icon-192 · icon-512 · apple-touch-icon (180)   한칸
  icon-512-maskable                              한칸 at 56%, inside
                                                 Android's circular crop
  favicon.ico (16/32/48) · favicon-32            한

Sizing is a binary search on the drawn ink extent, and centring uses the ink
bounds rather than the font's line box — CJK metrics leave asymmetric space
above and below, which would otherwise sit the mark visibly high.

Re-running the generator writes byte-identical files.

Android launcher icons are untouched; Android work is out of scope.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 19:16:55 +02:00

70 lines
2.2 KiB
TypeScript

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { VitePWA } from "vite-plugin-pwa";
import { fileURLToPath } from "node:url";
const at = (p: string) => fileURLToPath(new URL(p, import.meta.url));
export default defineConfig({
// The app lives in app/, but lib/ and data/ sit at the repo root so
// `node validate.mjs` keeps running against them verbatim.
resolve: {
alias: {
"@lib": at("../lib"),
"@data": at("../data"),
"@app": at("./src"),
"@shared": at("../shared"),
"@prompt": at("../prompt"),
},
},
server: {
fs: { allow: [at("..")] },
},
worker: { format: "es" },
optimizeDeps: {
// sqlite-wasm must not be pre-bundled; it loads its .wasm relative to
// its own module URL and dep-optimising rewrites that path.
exclude: ["@sqlite.org/sqlite-wasm"],
},
build: {
target: "es2022",
// The dictionary band files are large and already gzipped at build time.
assetsInlineLimit: 0,
},
plugins: [
react(),
VitePWA({
registerType: "autoUpdate",
includeAssets: ["favicon.ico", "favicon-32.png", "apple-touch-icon.png"],
manifest: {
name: "Hankan — 한국어 읽기",
short_name: "Hankan",
description: "A Korean reading tutor for manhwa. Works offline.",
lang: "en",
start_url: "/",
display: "standalone",
background_color: "#F1F4F1",
theme_color: "#0F6B5C",
icons: [
{ src: "icon-192.png", sizes: "192x192", type: "image/png", purpose: "any" },
{ src: "icon-512.png", sizes: "512x512", type: "image/png", purpose: "any" },
// Android masks icons to a circle; this one keeps 한칸 inside the
// safe zone so the crop cannot clip it.
{
src: "icon-512-maskable.png",
sizes: "512x512",
type: "image/png",
purpose: "maskable",
},
],
},
workbox: {
// Offline is the whole point: everything the app needs, including the
// dictionary band files and the wasm binary, is precached.
globPatterns: ["**/*.{js,css,html,svg,png,woff2,wasm,gz,json}"],
maximumFileSizeToCacheInBytes: 12 * 1024 * 1024,
},
}),
],
});