@broberg/pwa 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,216 @@
1
+ import { deflateSync } from 'zlib';
2
+
3
+ // src/native.ts
4
+ var NATIVE_BACKGROUND_REQUIRED = "nativeIconSet needs `background` (#RRGGBB): a white symbol on a transparent canvas renders as an empty icon";
5
+ var HEX = /^#[0-9a-f]{6}$/i;
6
+ var ANDROID_DENSITIES = [
7
+ { dpi: "mdpi", foreground: 108, legacy: 48 },
8
+ { dpi: "hdpi", foreground: 162, legacy: 72 },
9
+ { dpi: "xhdpi", foreground: 216, legacy: 96 },
10
+ { dpi: "xxhdpi", foreground: 324, legacy: 144 },
11
+ { dpi: "xxxhdpi", foreground: 432, legacy: 192 }
12
+ ];
13
+ var ANDROID_SAFE_ZONE = 66 / 108;
14
+ var ICO_SIZES = [16, 32, 48, 256];
15
+ var ICNS_CHUNKS = [
16
+ ["ic11", 32],
17
+ ["ic12", 64],
18
+ ["ic07", 128],
19
+ ["ic08", 256],
20
+ ["ic13", 256],
21
+ ["ic09", 512],
22
+ ["ic14", 512],
23
+ ["ic10", 1024]
24
+ ];
25
+ function composeSvg(svg, size, opts) {
26
+ const d = size * opts.scale;
27
+ const o = (size - d) / 2;
28
+ const bg = opts.shape === "square" && opts.background ? `<rect width="${size}" height="${size}" fill="${opts.background}"/>` : opts.shape === "circle" && opts.background ? `<circle cx="${size / 2}" cy="${size / 2}" r="${size / 2}" fill="${opts.background}"/>` : "";
29
+ const href = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
30
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">${bg}<image href="${href}" x="${o}" y="${o}" width="${d}" height="${d}"/></svg>`;
31
+ }
32
+ var CRC_TABLE = (() => {
33
+ const t = new Uint32Array(256);
34
+ for (let n = 0; n < 256; n++) {
35
+ let c = n;
36
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
37
+ t[n] = c >>> 0;
38
+ }
39
+ return t;
40
+ })();
41
+ function crc32(bytes) {
42
+ let c = 4294967295;
43
+ for (const b of bytes) c = CRC_TABLE[(c ^ b) & 255] ^ c >>> 8;
44
+ return (c ^ 4294967295) >>> 0;
45
+ }
46
+ function chunk(type, data) {
47
+ const out = new Uint8Array(12 + data.length);
48
+ const v = new DataView(out.buffer);
49
+ v.setUint32(0, data.length);
50
+ for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);
51
+ out.set(data, 8);
52
+ v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
53
+ return out;
54
+ }
55
+ function encodeRgbPng(rgba, width, height) {
56
+ const raw = new Uint8Array(height * (1 + width * 3));
57
+ for (let y = 0; y < height; y++) {
58
+ const row = y * (1 + width * 3);
59
+ raw[row] = 0;
60
+ for (let x = 0; x < width; x++) {
61
+ const s = (y * width + x) * 4;
62
+ const t = row + 1 + x * 3;
63
+ raw[t] = rgba[s];
64
+ raw[t + 1] = rgba[s + 1];
65
+ raw[t + 2] = rgba[s + 2];
66
+ }
67
+ }
68
+ const ihdr = new Uint8Array(13);
69
+ const v = new DataView(ihdr.buffer);
70
+ v.setUint32(0, width);
71
+ v.setUint32(4, height);
72
+ ihdr[8] = 8;
73
+ ihdr[9] = 2;
74
+ const sig = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);
75
+ const parts = [sig, chunk("IHDR", ihdr), chunk("IDAT", new Uint8Array(deflateSync(raw))), chunk("IEND", new Uint8Array())];
76
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
77
+ let off = 0;
78
+ for (const p of parts) {
79
+ out.set(p, off);
80
+ off += p.length;
81
+ }
82
+ return out;
83
+ }
84
+ function encodeIco(pngs) {
85
+ const header = 6 + 16 * pngs.length;
86
+ const total = header + pngs.reduce((n, p) => n + p.png.length, 0);
87
+ const out = new Uint8Array(total);
88
+ const v = new DataView(out.buffer);
89
+ v.setUint16(0, 0, true);
90
+ v.setUint16(2, 1, true);
91
+ v.setUint16(4, pngs.length, true);
92
+ let off = header;
93
+ pngs.forEach((p, i) => {
94
+ const e = 6 + i * 16;
95
+ out[e] = p.size >= 256 ? 0 : p.size;
96
+ out[e + 1] = p.size >= 256 ? 0 : p.size;
97
+ v.setUint16(e + 4, 1, true);
98
+ v.setUint16(e + 6, 32, true);
99
+ v.setUint32(e + 8, p.png.length, true);
100
+ v.setUint32(e + 12, off, true);
101
+ out.set(p.png, off);
102
+ off += p.png.length;
103
+ });
104
+ return out;
105
+ }
106
+ function encodeIcns(chunks) {
107
+ const total = 8 + chunks.reduce((n, c) => n + 8 + c.png.length, 0);
108
+ const out = new Uint8Array(total);
109
+ const v = new DataView(out.buffer);
110
+ const tag = (at, s) => {
111
+ for (let i = 0; i < 4; i++) out[at + i] = s.charCodeAt(i);
112
+ };
113
+ tag(0, "icns");
114
+ v.setUint32(4, total);
115
+ let off = 8;
116
+ for (const c of chunks) {
117
+ tag(off, c.type);
118
+ v.setUint32(off + 4, 8 + c.png.length);
119
+ out.set(c.png, off + 8);
120
+ off += 8 + c.png.length;
121
+ }
122
+ return out;
123
+ }
124
+ var enc = (s) => new TextEncoder().encode(s);
125
+ var ADAPTIVE_XML = `<?xml version="1.0" encoding="utf-8"?>
126
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
127
+ <background android:drawable="@color/ic_launcher_background"/>
128
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
129
+ </adaptive-icon>
130
+ `;
131
+ var IOS_CONTENTS = JSON.stringify(
132
+ {
133
+ images: [{ filename: "AppIcon-1024.png", idiom: "universal", platform: "ios", size: "1024x1024" }],
134
+ info: { author: "@broberg/pwa", version: 1 }
135
+ },
136
+ null,
137
+ 2
138
+ );
139
+ async function nativeIconSet(svg, opts) {
140
+ if (!opts?.background || !HEX.test(opts.background)) throw new Error(NATIVE_BACKGROUND_REQUIRED);
141
+ let Resvg;
142
+ try {
143
+ ({ Resvg } = await import('@resvg/resvg-js'));
144
+ } catch {
145
+ throw new Error("nativeIconSet needs @resvg/resvg-js \u2014 install it as a dev dependency: npm i -D @resvg/resvg-js");
146
+ }
147
+ const bg = opts.background;
148
+ const want = new Set(opts.platforms ?? ["ios", "android", "desktop"]);
149
+ const render = (size, shape, scale) => new Resvg(composeSvg(svg, size, { background: bg, shape, scale }), { font: { loadSystemFonts: false } }).render();
150
+ const png = (size, shape, scale) => new Uint8Array(render(size, shape, scale).asPng());
151
+ const files = [];
152
+ if (want.has("ios")) {
153
+ const r = render(1024, "square", 0.8);
154
+ files.push(
155
+ { path: "ios/AppIcon.appiconset/AppIcon-1024.png", content: encodeRgbPng(new Uint8Array(r.pixels), r.width, r.height) },
156
+ { path: "ios/AppIcon.appiconset/Contents.json", content: enc(IOS_CONTENTS + "\n") }
157
+ );
158
+ }
159
+ if (want.has("android")) {
160
+ files.push(
161
+ { path: "android/mipmap-anydpi-v26/ic_launcher.xml", content: enc(ADAPTIVE_XML) },
162
+ { path: "android/mipmap-anydpi-v26/ic_launcher_round.xml", content: enc(ADAPTIVE_XML) },
163
+ {
164
+ path: "android/values/ic_launcher_background.xml",
165
+ content: enc(
166
+ `<?xml version="1.0" encoding="utf-8"?>
167
+ <resources>
168
+ <color name="ic_launcher_background">${bg}</color>
169
+ </resources>
170
+ `
171
+ )
172
+ }
173
+ );
174
+ for (const d of ANDROID_DENSITIES) {
175
+ files.push(
176
+ { path: `android/mipmap-${d.dpi}/ic_launcher_foreground.png`, content: png(d.foreground, "none", ANDROID_SAFE_ZONE) },
177
+ { path: `android/mipmap-${d.dpi}/ic_launcher.png`, content: png(d.legacy, "square", 0.7) },
178
+ { path: `android/mipmap-${d.dpi}/ic_launcher_round.png`, content: png(d.legacy, "circle", 0.62) }
179
+ );
180
+ }
181
+ }
182
+ if (want.has("desktop")) {
183
+ files.push(
184
+ { path: "desktop/icon.ico", content: encodeIco(ICO_SIZES.map((size) => ({ size, png: png(size, "square", 0.8) }))) },
185
+ { path: "desktop/icon.icns", content: encodeIcns(ICNS_CHUNKS.map(([type, size]) => ({ type, png: png(size, "square", 0.8) }))) }
186
+ );
187
+ }
188
+ return files;
189
+ }
190
+
191
+ // src/raster.ts
192
+ var RESVG_MISSING = "rasterizeIconSet needs @resvg/resvg-js \u2014 install it as a dev dependency: npm i -D @resvg/resvg-js";
193
+ var toPng = (p) => p.replace(/\.svg$/, ".png");
194
+ async function rasterizeIconSet(set, opts = {}) {
195
+ let Resvg;
196
+ try {
197
+ ({ Resvg } = await import('@resvg/resvg-js'));
198
+ } catch {
199
+ throw new Error(RESVG_MISSING);
200
+ }
201
+ const files = set.files.map((f) => {
202
+ const png = new Resvg(f.content, {
203
+ fitTo: { mode: "width", value: f.size },
204
+ font: { loadSystemFonts: true, fontFiles: opts.fontFiles ?? [] }
205
+ }).render().asPng();
206
+ return { path: toPng(f.path), content: new Uint8Array(png), type: "image/png", size: f.size, purpose: f.purpose };
207
+ });
208
+ const icons = set.icons.map((i) => ({ ...i, src: toPng(i.src), type: "image/png" }));
209
+ const apple = files.find((f) => f.size === 180 && f.purpose === "any");
210
+ const rootFiles = apple ? ["/apple-touch-icon.png", "/apple-touch-icon-precomposed.png"].map((path) => ({ ...apple, path })) : [];
211
+ return { files, icons, appleTouchIcon: apple?.path, rootFiles };
212
+ }
213
+
214
+ export { ANDROID_DENSITIES, ANDROID_SAFE_ZONE, ICNS_CHUNKS, ICO_SIZES, NATIVE_BACKGROUND_REQUIRED, RESVG_MISSING, encodeIcns, encodeIco, encodeRgbPng, nativeIconSet, rasterizeIconSet };
215
+ //# sourceMappingURL=chunk-TZPDZ6IO.js.map
216
+ //# sourceMappingURL=chunk-TZPDZ6IO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/native.ts","../src/raster.ts"],"names":[],"mappings":"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","file":"chunk-TZPDZ6IO.js","sourcesContent":["/**\n * @broberg/pwa/raster — F021.12: the NATIVE icon sets, from the same SVG the web\n * icons come from, so no Capacitor or Tauri app keeps its own icon script.\n *\n * ios/AppIcon.appiconset/ AppIcon-1024.png + Contents.json (Xcode 14+ single size)\n * android/mipmap-anydpi-v26/ ic_launcher.xml, ic_launcher_round.xml (adaptive)\n * android/mipmap-<dpi>/ ic_launcher_foreground.png, ic_launcher.png, ic_launcher_round.png\n * android/values/ ic_launcher_background.xml\n * desktop/ icon.ico, icon.icns\n *\n * Two platform rules decide the code, and both fail silently if ignored:\n * - App Store rejects an app icon with an alpha channel. resvg always renders\n * RGBA, so the iOS PNG is RE-ENCODED as RGB (colour type 2) — flattening onto\n * the background is not enough on its own, the channel must be gone.\n * - Android crops an adaptive icon's 108dp layer to whatever mask the launcher\n * uses; only the inner 66dp is guaranteed visible. The motif is drawn inside it.\n *\n * `background` is REQUIRED: a white symbol on a transparent canvas renders as an\n * empty icon (measured by buzzalert with `tauri icon`, 25/10).\n *\n * ICO and ICNS are written as plain byte containers holding PNGs — no dependency.\n */\nimport { deflateSync } from \"node:zlib\";\n\nexport interface NativeIconFile {\n /** Path relative to the output directory, e.g. `ios/AppIcon.appiconset/AppIcon-1024.png`. */\n path: string;\n content: Uint8Array;\n}\n\nexport interface NativeIconOptions {\n /** Hex colour behind the motif (`#RRGGBB`). Required — see the module comment. */\n background: string;\n /** Default: all three. */\n platforms?: Array<\"ios\" | \"android\" | \"desktop\">;\n}\n\nexport const NATIVE_BACKGROUND_REQUIRED =\n \"nativeIconSet needs `background` (#RRGGBB): a white symbol on a transparent canvas renders as an empty icon\";\n\nconst HEX = /^#[0-9a-f]{6}$/i;\n\n/** Android density → adaptive foreground px (108dp) and legacy launcher px (48dp). */\nexport const ANDROID_DENSITIES = [\n { dpi: \"mdpi\", foreground: 108, legacy: 48 },\n { dpi: \"hdpi\", foreground: 162, legacy: 72 },\n { dpi: \"xhdpi\", foreground: 216, legacy: 96 },\n { dpi: \"xxhdpi\", foreground: 324, legacy: 144 },\n { dpi: \"xxxhdpi\", foreground: 432, legacy: 192 },\n] as const;\n\n/** Share of the 108dp adaptive canvas that every launcher mask keeps visible. */\nexport const ANDROID_SAFE_ZONE = 66 / 108;\n\nexport const ICO_SIZES = [16, 32, 48, 256] as const;\n/** ICNS chunk type → pixel size (PNG payloads). */\nexport const ICNS_CHUNKS = [\n [\"ic11\", 32],\n [\"ic12\", 64],\n [\"ic07\", 128],\n [\"ic08\", 256],\n [\"ic13\", 256],\n [\"ic09\", 512],\n [\"ic14\", 512],\n [\"ic10\", 1024],\n] as const;\n\ntype Shape = \"square\" | \"circle\" | \"none\";\n\nfunction composeSvg(svg: string, size: number, opts: { background?: string; shape: Shape; scale: number }): string {\n const d = size * opts.scale;\n const o = (size - d) / 2;\n const bg =\n opts.shape === \"square\" && opts.background\n ? `<rect width=\"${size}\" height=\"${size}\" fill=\"${opts.background}\"/>`\n : opts.shape === \"circle\" && opts.background\n ? `<circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${size / 2}\" fill=\"${opts.background}\"/>`\n : \"\";\n const href = `data:image/svg+xml;base64,${Buffer.from(svg).toString(\"base64\")}`;\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\">` +\n `${bg}<image href=\"${href}\" x=\"${o}\" y=\"${o}\" width=\"${d}\" height=\"${d}\"/></svg>`\n );\n}\n\n// ---- PNG encoding (RGB, no alpha) -------------------------------------------\n\nconst CRC_TABLE = (() => {\n const t = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n t[n] = c >>> 0;\n }\n return t;\n})();\n\nfunction crc32(bytes: Uint8Array): number {\n let c = 0xffffffff;\n for (const b of bytes) c = CRC_TABLE[(c ^ b) & 0xff]! ^ (c >>> 8);\n return (c ^ 0xffffffff) >>> 0;\n}\n\nfunction chunk(type: string, data: Uint8Array): Uint8Array {\n const out = new Uint8Array(12 + data.length);\n const v = new DataView(out.buffer);\n v.setUint32(0, data.length);\n for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);\n out.set(data, 8);\n v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));\n return out;\n}\n\n/** Encode RGBA pixels as an RGB PNG (colour type 2) — the alpha channel is dropped, not just made opaque. */\nexport function encodeRgbPng(rgba: Uint8Array, width: number, height: number): Uint8Array {\n const raw = new Uint8Array(height * (1 + width * 3));\n for (let y = 0; y < height; y++) {\n const row = y * (1 + width * 3);\n raw[row] = 0; // filter: none\n for (let x = 0; x < width; x++) {\n const s = (y * width + x) * 4;\n const t = row + 1 + x * 3;\n raw[t] = rgba[s]!;\n raw[t + 1] = rgba[s + 1]!;\n raw[t + 2] = rgba[s + 2]!;\n }\n }\n const ihdr = new Uint8Array(13);\n const v = new DataView(ihdr.buffer);\n v.setUint32(0, width);\n v.setUint32(4, height);\n ihdr[8] = 8; // bit depth\n ihdr[9] = 2; // colour type: truecolour, NO alpha\n const sig = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);\n const parts = [sig, chunk(\"IHDR\", ihdr), chunk(\"IDAT\", new Uint8Array(deflateSync(raw))), chunk(\"IEND\", new Uint8Array())];\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let off = 0;\n for (const p of parts) {\n out.set(p, off);\n off += p.length;\n }\n return out;\n}\n\n// ---- containers --------------------------------------------------------------\n\n/** An ICO holding PNG images (Vista+ format). */\nexport function encodeIco(pngs: Array<{ size: number; png: Uint8Array }>): Uint8Array {\n const header = 6 + 16 * pngs.length;\n const total = header + pngs.reduce((n, p) => n + p.png.length, 0);\n const out = new Uint8Array(total);\n const v = new DataView(out.buffer);\n v.setUint16(0, 0, true); // reserved\n v.setUint16(2, 1, true); // type: icon\n v.setUint16(4, pngs.length, true);\n let off = header;\n pngs.forEach((p, i) => {\n const e = 6 + i * 16;\n out[e] = p.size >= 256 ? 0 : p.size; // 0 means 256\n out[e + 1] = p.size >= 256 ? 0 : p.size;\n v.setUint16(e + 4, 1, true); // colour planes\n v.setUint16(e + 6, 32, true); // bits per pixel\n v.setUint32(e + 8, p.png.length, true);\n v.setUint32(e + 12, off, true);\n out.set(p.png, off);\n off += p.png.length;\n });\n return out;\n}\n\n/** An ICNS holding PNG chunks. */\nexport function encodeIcns(chunks: Array<{ type: string; png: Uint8Array }>): Uint8Array {\n const total = 8 + chunks.reduce((n, c) => n + 8 + c.png.length, 0);\n const out = new Uint8Array(total);\n const v = new DataView(out.buffer);\n const tag = (at: number, s: string) => {\n for (let i = 0; i < 4; i++) out[at + i] = s.charCodeAt(i);\n };\n tag(0, \"icns\");\n v.setUint32(4, total);\n let off = 8;\n for (const c of chunks) {\n tag(off, c.type);\n v.setUint32(off + 4, 8 + c.png.length);\n out.set(c.png, off + 8);\n off += 8 + c.png.length;\n }\n return out;\n}\n\n// ---- the set -------------------------------------------------------------------\n\nconst enc = (s: string) => new TextEncoder().encode(s);\n\nconst ADAPTIVE_XML =\n `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n` +\n `<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\\n` +\n ` <background android:drawable=\"@color/ic_launcher_background\"/>\\n` +\n ` <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\\n` +\n `</adaptive-icon>\\n`;\n\nconst IOS_CONTENTS = JSON.stringify(\n {\n images: [{ filename: \"AppIcon-1024.png\", idiom: \"universal\", platform: \"ios\", size: \"1024x1024\" }],\n info: { author: \"@broberg/pwa\", version: 1 },\n },\n null,\n 2,\n);\n\n/**\n * Every native icon file for `svg`. Requires the optional peer @resvg/resvg-js,\n * like `rasterizeIconSet`.\n */\nexport async function nativeIconSet(svg: string, opts: NativeIconOptions): Promise<NativeIconFile[]> {\n if (!opts?.background || !HEX.test(opts.background)) throw new Error(NATIVE_BACKGROUND_REQUIRED);\n let Resvg: typeof import(\"@resvg/resvg-js\").Resvg;\n try {\n ({ Resvg } = await import(\"@resvg/resvg-js\"));\n } catch {\n throw new Error(\"nativeIconSet needs @resvg/resvg-js — install it as a dev dependency: npm i -D @resvg/resvg-js\");\n }\n const bg = opts.background;\n const want = new Set(opts.platforms ?? [\"ios\", \"android\", \"desktop\"]);\n const render = (size: number, shape: Shape, scale: number) =>\n new Resvg(composeSvg(svg, size, { background: bg, shape, scale }), { font: { loadSystemFonts: false } }).render();\n const png = (size: number, shape: Shape, scale: number) => new Uint8Array(render(size, shape, scale).asPng());\n\n const files: NativeIconFile[] = [];\n\n if (want.has(\"ios\")) {\n const r = render(1024, \"square\", 0.8);\n files.push(\n { path: \"ios/AppIcon.appiconset/AppIcon-1024.png\", content: encodeRgbPng(new Uint8Array(r.pixels), r.width, r.height) },\n { path: \"ios/AppIcon.appiconset/Contents.json\", content: enc(IOS_CONTENTS + \"\\n\") },\n );\n }\n\n if (want.has(\"android\")) {\n files.push(\n { path: \"android/mipmap-anydpi-v26/ic_launcher.xml\", content: enc(ADAPTIVE_XML) },\n { path: \"android/mipmap-anydpi-v26/ic_launcher_round.xml\", content: enc(ADAPTIVE_XML) },\n {\n path: \"android/values/ic_launcher_background.xml\",\n content: enc(\n `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n<resources>\\n <color name=\"ic_launcher_background\">${bg}</color>\\n</resources>\\n`,\n ),\n },\n );\n for (const d of ANDROID_DENSITIES) {\n files.push(\n { path: `android/mipmap-${d.dpi}/ic_launcher_foreground.png`, content: png(d.foreground, \"none\", ANDROID_SAFE_ZONE) },\n { path: `android/mipmap-${d.dpi}/ic_launcher.png`, content: png(d.legacy, \"square\", 0.7) },\n { path: `android/mipmap-${d.dpi}/ic_launcher_round.png`, content: png(d.legacy, \"circle\", 0.62) },\n );\n }\n }\n\n if (want.has(\"desktop\")) {\n files.push(\n { path: \"desktop/icon.ico\", content: encodeIco(ICO_SIZES.map((size) => ({ size, png: png(size, \"square\", 0.8) }))) },\n { path: \"desktop/icon.icns\", content: encodeIcns(ICNS_CHUNKS.map(([type, size]) => ({ type, png: png(size, \"square\", 0.8) }))) },\n );\n }\n\n return files;\n}\n","/**\n * @broberg/pwa/raster — F021.9: PNG icons, because iOS does not use an SVG\n * apple-touch-icon. «Add to Home Screen» on an iPhone with only SVG icons shows\n * a screenshot of the page instead of the logo (measured by sales, 10/10).\n *\n * Kept OUT of the core entries so `@broberg/pwa` and `/manifest` stay\n * dependency-free: the renderer, @resvg/resvg-js (prebuilt binaries, no system\n * libraries), is an optional peer loaded only when this runs.\n */\nimport type { IconSet, ManifestIcon } from \"./manifest.js\";\n\nexport interface RasterIcon {\n /** URL path, e.g. `/icons/icon-180.png`. */\n path: string;\n content: Uint8Array;\n type: \"image/png\";\n size: number;\n purpose: \"any\" | \"maskable\";\n}\n\nexport interface RasterIconSet {\n files: RasterIcon[];\n /** Manifest `icons[]` pointing at the PNGs. */\n icons: ManifestIcon[];\n /** Href for `<link rel=\"apple-touch-icon\">` — pass it to `pwaMetaTags({ appleTouchIcon })`. Undefined when 180 was not generated. */\n appleTouchIcon?: string;\n /**\n * F021.11 — the same 180 PNG at the two ROOT paths iOS asks for on its own\n * (`/apple-touch-icon.png`, `/apple-touch-icon-precomposed.png`). Write them to\n * your web root, NOT next to the other icons. Without them iOS fell back to a\n * letter («J») on a real home screen (jam, measured). Empty when 180 was not generated.\n */\n rootFiles: RasterIcon[];\n}\n\nexport interface RasterizeOptions {\n /** Extra font files for monogram text (system fonts are loaded too). */\n fontFiles?: string[];\n}\n\nexport const RESVG_MISSING =\n \"rasterizeIconSet needs @resvg/resvg-js — install it as a dev dependency: npm i -D @resvg/resvg-js\";\n\nconst toPng = (p: string) => p.replace(/\\.svg$/, \".png\");\n\n/** Rasterise every SVG from `buildIconSet` to a PNG of the same size. */\nexport async function rasterizeIconSet(set: IconSet, opts: RasterizeOptions = {}): Promise<RasterIconSet> {\n let Resvg: typeof import(\"@resvg/resvg-js\").Resvg;\n try {\n ({ Resvg } = await import(\"@resvg/resvg-js\"));\n } catch {\n throw new Error(RESVG_MISSING);\n }\n const files: RasterIcon[] = set.files.map((f) => {\n const png = new Resvg(f.content, {\n fitTo: { mode: \"width\", value: f.size },\n font: { loadSystemFonts: true, fontFiles: opts.fontFiles ?? [] },\n })\n .render()\n .asPng();\n return { path: toPng(f.path), content: new Uint8Array(png), type: \"image/png\", size: f.size, purpose: f.purpose };\n });\n const icons = set.icons.map((i) => ({ ...i, src: toPng(i.src), type: \"image/png\" }));\n const apple = files.find((f) => f.size === 180 && f.purpose === \"any\");\n const rootFiles: RasterIcon[] = apple\n ? [\"/apple-touch-icon.png\", \"/apple-touch-icon-precomposed.png\"].map((path) => ({ ...apple, path }))\n : [];\n return { files, icons, appleTouchIcon: apple?.path, rootFiles };\n}\n\n// F021.12 — native icon sets (iOS AppIcon, Android adaptive, .ico/.icns) from the same SVG.\nexport { nativeIconSet, encodeRgbPng, encodeIco, encodeIcns, NATIVE_BACKGROUND_REQUIRED, ANDROID_DENSITIES, ANDROID_SAFE_ZONE, ICO_SIZES, ICNS_CHUNKS } from \"./native.js\";\nexport type { NativeIconFile, NativeIconOptions } from \"./native.js\";\n"]}
package/dist/raster.cjs CHANGED
@@ -1,5 +1,195 @@
1
1
  'use strict';
2
2
 
3
+ var zlib = require('zlib');
4
+
5
+ // src/native.ts
6
+ var NATIVE_BACKGROUND_REQUIRED = "nativeIconSet needs `background` (#RRGGBB): a white symbol on a transparent canvas renders as an empty icon";
7
+ var HEX = /^#[0-9a-f]{6}$/i;
8
+ var ANDROID_DENSITIES = [
9
+ { dpi: "mdpi", foreground: 108, legacy: 48 },
10
+ { dpi: "hdpi", foreground: 162, legacy: 72 },
11
+ { dpi: "xhdpi", foreground: 216, legacy: 96 },
12
+ { dpi: "xxhdpi", foreground: 324, legacy: 144 },
13
+ { dpi: "xxxhdpi", foreground: 432, legacy: 192 }
14
+ ];
15
+ var ANDROID_SAFE_ZONE = 66 / 108;
16
+ var ICO_SIZES = [16, 32, 48, 256];
17
+ var ICNS_CHUNKS = [
18
+ ["ic11", 32],
19
+ ["ic12", 64],
20
+ ["ic07", 128],
21
+ ["ic08", 256],
22
+ ["ic13", 256],
23
+ ["ic09", 512],
24
+ ["ic14", 512],
25
+ ["ic10", 1024]
26
+ ];
27
+ function composeSvg(svg, size, opts) {
28
+ const d = size * opts.scale;
29
+ const o = (size - d) / 2;
30
+ const bg = opts.shape === "square" && opts.background ? `<rect width="${size}" height="${size}" fill="${opts.background}"/>` : opts.shape === "circle" && opts.background ? `<circle cx="${size / 2}" cy="${size / 2}" r="${size / 2}" fill="${opts.background}"/>` : "";
31
+ const href = `data:image/svg+xml;base64,${Buffer.from(svg).toString("base64")}`;
32
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">${bg}<image href="${href}" x="${o}" y="${o}" width="${d}" height="${d}"/></svg>`;
33
+ }
34
+ var CRC_TABLE = (() => {
35
+ const t = new Uint32Array(256);
36
+ for (let n = 0; n < 256; n++) {
37
+ let c = n;
38
+ for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
39
+ t[n] = c >>> 0;
40
+ }
41
+ return t;
42
+ })();
43
+ function crc32(bytes) {
44
+ let c = 4294967295;
45
+ for (const b of bytes) c = CRC_TABLE[(c ^ b) & 255] ^ c >>> 8;
46
+ return (c ^ 4294967295) >>> 0;
47
+ }
48
+ function chunk(type, data) {
49
+ const out = new Uint8Array(12 + data.length);
50
+ const v = new DataView(out.buffer);
51
+ v.setUint32(0, data.length);
52
+ for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);
53
+ out.set(data, 8);
54
+ v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));
55
+ return out;
56
+ }
57
+ function encodeRgbPng(rgba, width, height) {
58
+ const raw = new Uint8Array(height * (1 + width * 3));
59
+ for (let y = 0; y < height; y++) {
60
+ const row = y * (1 + width * 3);
61
+ raw[row] = 0;
62
+ for (let x = 0; x < width; x++) {
63
+ const s = (y * width + x) * 4;
64
+ const t = row + 1 + x * 3;
65
+ raw[t] = rgba[s];
66
+ raw[t + 1] = rgba[s + 1];
67
+ raw[t + 2] = rgba[s + 2];
68
+ }
69
+ }
70
+ const ihdr = new Uint8Array(13);
71
+ const v = new DataView(ihdr.buffer);
72
+ v.setUint32(0, width);
73
+ v.setUint32(4, height);
74
+ ihdr[8] = 8;
75
+ ihdr[9] = 2;
76
+ const sig = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);
77
+ const parts = [sig, chunk("IHDR", ihdr), chunk("IDAT", new Uint8Array(zlib.deflateSync(raw))), chunk("IEND", new Uint8Array())];
78
+ const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));
79
+ let off = 0;
80
+ for (const p of parts) {
81
+ out.set(p, off);
82
+ off += p.length;
83
+ }
84
+ return out;
85
+ }
86
+ function encodeIco(pngs) {
87
+ const header = 6 + 16 * pngs.length;
88
+ const total = header + pngs.reduce((n, p) => n + p.png.length, 0);
89
+ const out = new Uint8Array(total);
90
+ const v = new DataView(out.buffer);
91
+ v.setUint16(0, 0, true);
92
+ v.setUint16(2, 1, true);
93
+ v.setUint16(4, pngs.length, true);
94
+ let off = header;
95
+ pngs.forEach((p, i) => {
96
+ const e = 6 + i * 16;
97
+ out[e] = p.size >= 256 ? 0 : p.size;
98
+ out[e + 1] = p.size >= 256 ? 0 : p.size;
99
+ v.setUint16(e + 4, 1, true);
100
+ v.setUint16(e + 6, 32, true);
101
+ v.setUint32(e + 8, p.png.length, true);
102
+ v.setUint32(e + 12, off, true);
103
+ out.set(p.png, off);
104
+ off += p.png.length;
105
+ });
106
+ return out;
107
+ }
108
+ function encodeIcns(chunks) {
109
+ const total = 8 + chunks.reduce((n, c) => n + 8 + c.png.length, 0);
110
+ const out = new Uint8Array(total);
111
+ const v = new DataView(out.buffer);
112
+ const tag = (at, s) => {
113
+ for (let i = 0; i < 4; i++) out[at + i] = s.charCodeAt(i);
114
+ };
115
+ tag(0, "icns");
116
+ v.setUint32(4, total);
117
+ let off = 8;
118
+ for (const c of chunks) {
119
+ tag(off, c.type);
120
+ v.setUint32(off + 4, 8 + c.png.length);
121
+ out.set(c.png, off + 8);
122
+ off += 8 + c.png.length;
123
+ }
124
+ return out;
125
+ }
126
+ var enc = (s) => new TextEncoder().encode(s);
127
+ var ADAPTIVE_XML = `<?xml version="1.0" encoding="utf-8"?>
128
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
129
+ <background android:drawable="@color/ic_launcher_background"/>
130
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
131
+ </adaptive-icon>
132
+ `;
133
+ var IOS_CONTENTS = JSON.stringify(
134
+ {
135
+ images: [{ filename: "AppIcon-1024.png", idiom: "universal", platform: "ios", size: "1024x1024" }],
136
+ info: { author: "@broberg/pwa", version: 1 }
137
+ },
138
+ null,
139
+ 2
140
+ );
141
+ async function nativeIconSet(svg, opts) {
142
+ if (!opts?.background || !HEX.test(opts.background)) throw new Error(NATIVE_BACKGROUND_REQUIRED);
143
+ let Resvg;
144
+ try {
145
+ ({ Resvg } = await import('@resvg/resvg-js'));
146
+ } catch {
147
+ throw new Error("nativeIconSet needs @resvg/resvg-js \u2014 install it as a dev dependency: npm i -D @resvg/resvg-js");
148
+ }
149
+ const bg = opts.background;
150
+ const want = new Set(opts.platforms ?? ["ios", "android", "desktop"]);
151
+ const render = (size, shape, scale) => new Resvg(composeSvg(svg, size, { background: bg, shape, scale }), { font: { loadSystemFonts: false } }).render();
152
+ const png = (size, shape, scale) => new Uint8Array(render(size, shape, scale).asPng());
153
+ const files = [];
154
+ if (want.has("ios")) {
155
+ const r = render(1024, "square", 0.8);
156
+ files.push(
157
+ { path: "ios/AppIcon.appiconset/AppIcon-1024.png", content: encodeRgbPng(new Uint8Array(r.pixels), r.width, r.height) },
158
+ { path: "ios/AppIcon.appiconset/Contents.json", content: enc(IOS_CONTENTS + "\n") }
159
+ );
160
+ }
161
+ if (want.has("android")) {
162
+ files.push(
163
+ { path: "android/mipmap-anydpi-v26/ic_launcher.xml", content: enc(ADAPTIVE_XML) },
164
+ { path: "android/mipmap-anydpi-v26/ic_launcher_round.xml", content: enc(ADAPTIVE_XML) },
165
+ {
166
+ path: "android/values/ic_launcher_background.xml",
167
+ content: enc(
168
+ `<?xml version="1.0" encoding="utf-8"?>
169
+ <resources>
170
+ <color name="ic_launcher_background">${bg}</color>
171
+ </resources>
172
+ `
173
+ )
174
+ }
175
+ );
176
+ for (const d of ANDROID_DENSITIES) {
177
+ files.push(
178
+ { path: `android/mipmap-${d.dpi}/ic_launcher_foreground.png`, content: png(d.foreground, "none", ANDROID_SAFE_ZONE) },
179
+ { path: `android/mipmap-${d.dpi}/ic_launcher.png`, content: png(d.legacy, "square", 0.7) },
180
+ { path: `android/mipmap-${d.dpi}/ic_launcher_round.png`, content: png(d.legacy, "circle", 0.62) }
181
+ );
182
+ }
183
+ }
184
+ if (want.has("desktop")) {
185
+ files.push(
186
+ { path: "desktop/icon.ico", content: encodeIco(ICO_SIZES.map((size) => ({ size, png: png(size, "square", 0.8) }))) },
187
+ { path: "desktop/icon.icns", content: encodeIcns(ICNS_CHUNKS.map(([type, size]) => ({ type, png: png(size, "square", 0.8) }))) }
188
+ );
189
+ }
190
+ return files;
191
+ }
192
+
3
193
  // src/raster.ts
4
194
  var RESVG_MISSING = "rasterizeIconSet needs @resvg/resvg-js \u2014 install it as a dev dependency: npm i -D @resvg/resvg-js";
5
195
  var toPng = (p) => p.replace(/\.svg$/, ".png");
@@ -23,7 +213,16 @@ async function rasterizeIconSet(set, opts = {}) {
23
213
  return { files, icons, appleTouchIcon: apple?.path, rootFiles };
24
214
  }
25
215
 
216
+ exports.ANDROID_DENSITIES = ANDROID_DENSITIES;
217
+ exports.ANDROID_SAFE_ZONE = ANDROID_SAFE_ZONE;
218
+ exports.ICNS_CHUNKS = ICNS_CHUNKS;
219
+ exports.ICO_SIZES = ICO_SIZES;
220
+ exports.NATIVE_BACKGROUND_REQUIRED = NATIVE_BACKGROUND_REQUIRED;
26
221
  exports.RESVG_MISSING = RESVG_MISSING;
222
+ exports.encodeIcns = encodeIcns;
223
+ exports.encodeIco = encodeIco;
224
+ exports.encodeRgbPng = encodeRgbPng;
225
+ exports.nativeIconSet = nativeIconSet;
27
226
  exports.rasterizeIconSet = rasterizeIconSet;
28
227
  //# sourceMappingURL=raster.cjs.map
29
228
  //# sourceMappingURL=raster.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/raster.ts"],"names":[],"mappings":";;;AAwCO,IAAM,aAAA,GACX;AAEF,IAAM,QAAQ,CAAC,CAAA,KAAc,CAAA,CAAE,OAAA,CAAQ,UAAU,MAAM,CAAA;AAGvD,eAAsB,gBAAA,CAAiB,GAAA,EAAc,IAAA,GAAyB,EAAC,EAA2B;AACxG,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI;AACF,IAAA,CAAC,EAAE,KAAA,EAAM,GAAI,MAAM,OAAO,iBAAiB,CAAA;AAAA,EAC7C,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,MAAM,aAAa,CAAA;AAAA,EAC/B;AACA,EAAA,MAAM,KAAA,GAAsB,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM;AAC/C,IAAA,MAAM,GAAA,GAAM,IAAI,KAAA,CAAM,CAAA,CAAE,OAAA,EAAS;AAAA,MAC/B,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,EAAE,IAAA,EAAK;AAAA,MACtC,IAAA,EAAM,EAAE,eAAA,EAAiB,IAAA,EAAM,WAAW,IAAA,CAAK,SAAA,IAAa,EAAC;AAAE,KAChE,CAAA,CACE,MAAA,EAAO,CACP,KAAA,EAAM;AACT,IAAA,OAAO,EAAE,IAAA,EAAM,KAAA,CAAM,EAAE,IAAI,CAAA,EAAG,SAAS,IAAI,UAAA,CAAW,GAAG,CAAA,EAAG,MAAM,WAAA,EAAa,IAAA,EAAM,EAAE,IAAA,EAAM,OAAA,EAAS,EAAE,OAAA,EAAQ;AAAA,EAClH,CAAC,CAAA;AACD,EAAA,MAAM,QAAQ,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,OAAO,EAAE,GAAG,CAAA,EAAG,GAAA,EAAK,MAAM,CAAA,CAAE,GAAG,CAAA,EAAG,IAAA,EAAM,aAAY,CAAE,CAAA;AACnF,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,GAAA,IAAO,CAAA,CAAE,OAAA,KAAY,KAAK,CAAA;AACrE,EAAA,MAAM,SAAA,GAA0B,KAAA,GAC5B,CAAC,uBAAA,EAAyB,mCAAmC,CAAA,CAAE,GAAA,CAAI,CAAC,IAAA,MAAU,EAAE,GAAG,KAAA,EAAO,IAAA,EAAK,CAAE,IACjG,EAAC;AACL,EAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,cAAA,EAAgB,KAAA,EAAO,MAAM,SAAA,EAAU;AAChE","file":"raster.cjs","sourcesContent":["/**\n * @broberg/pwa/raster — F021.9: PNG icons, because iOS does not use an SVG\n * apple-touch-icon. «Add to Home Screen» on an iPhone with only SVG icons shows\n * a screenshot of the page instead of the logo (measured by sales, 10/10).\n *\n * Kept OUT of the core entries so `@broberg/pwa` and `/manifest` stay\n * dependency-free: the renderer, @resvg/resvg-js (prebuilt binaries, no system\n * libraries), is an optional peer loaded only when this runs.\n */\nimport type { IconSet, ManifestIcon } from \"./manifest.js\";\n\nexport interface RasterIcon {\n /** URL path, e.g. `/icons/icon-180.png`. */\n path: string;\n content: Uint8Array;\n type: \"image/png\";\n size: number;\n purpose: \"any\" | \"maskable\";\n}\n\nexport interface RasterIconSet {\n files: RasterIcon[];\n /** Manifest `icons[]` pointing at the PNGs. */\n icons: ManifestIcon[];\n /** Href for `<link rel=\"apple-touch-icon\">` — pass it to `pwaMetaTags({ appleTouchIcon })`. Undefined when 180 was not generated. */\n appleTouchIcon?: string;\n /**\n * F021.11 — the same 180 PNG at the two ROOT paths iOS asks for on its own\n * (`/apple-touch-icon.png`, `/apple-touch-icon-precomposed.png`). Write them to\n * your web root, NOT next to the other icons. Without them iOS fell back to a\n * letter («J») on a real home screen (jam, measured). Empty when 180 was not generated.\n */\n rootFiles: RasterIcon[];\n}\n\nexport interface RasterizeOptions {\n /** Extra font files for monogram text (system fonts are loaded too). */\n fontFiles?: string[];\n}\n\nexport const RESVG_MISSING =\n \"rasterizeIconSet needs @resvg/resvg-js — install it as a dev dependency: npm i -D @resvg/resvg-js\";\n\nconst toPng = (p: string) => p.replace(/\\.svg$/, \".png\");\n\n/** Rasterise every SVG from `buildIconSet` to a PNG of the same size. */\nexport async function rasterizeIconSet(set: IconSet, opts: RasterizeOptions = {}): Promise<RasterIconSet> {\n let Resvg: typeof import(\"@resvg/resvg-js\").Resvg;\n try {\n ({ Resvg } = await import(\"@resvg/resvg-js\"));\n } catch {\n throw new Error(RESVG_MISSING);\n }\n const files: RasterIcon[] = set.files.map((f) => {\n const png = new Resvg(f.content, {\n fitTo: { mode: \"width\", value: f.size },\n font: { loadSystemFonts: true, fontFiles: opts.fontFiles ?? [] },\n })\n .render()\n .asPng();\n return { path: toPng(f.path), content: new Uint8Array(png), type: \"image/png\", size: f.size, purpose: f.purpose };\n });\n const icons = set.icons.map((i) => ({ ...i, src: toPng(i.src), type: \"image/png\" }));\n const apple = files.find((f) => f.size === 180 && f.purpose === \"any\");\n const rootFiles: RasterIcon[] = apple\n ? [\"/apple-touch-icon.png\", \"/apple-touch-icon-precomposed.png\"].map((path) => ({ ...apple, path }))\n : [];\n return { files, icons, appleTouchIcon: apple?.path, rootFiles };\n}\n"]}
1
+ {"version":3,"sources":["../src/native.ts","../src/raster.ts"],"names":["deflateSync"],"mappings":"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","file":"raster.cjs","sourcesContent":["/**\n * @broberg/pwa/raster — F021.12: the NATIVE icon sets, from the same SVG the web\n * icons come from, so no Capacitor or Tauri app keeps its own icon script.\n *\n * ios/AppIcon.appiconset/ AppIcon-1024.png + Contents.json (Xcode 14+ single size)\n * android/mipmap-anydpi-v26/ ic_launcher.xml, ic_launcher_round.xml (adaptive)\n * android/mipmap-<dpi>/ ic_launcher_foreground.png, ic_launcher.png, ic_launcher_round.png\n * android/values/ ic_launcher_background.xml\n * desktop/ icon.ico, icon.icns\n *\n * Two platform rules decide the code, and both fail silently if ignored:\n * - App Store rejects an app icon with an alpha channel. resvg always renders\n * RGBA, so the iOS PNG is RE-ENCODED as RGB (colour type 2) — flattening onto\n * the background is not enough on its own, the channel must be gone.\n * - Android crops an adaptive icon's 108dp layer to whatever mask the launcher\n * uses; only the inner 66dp is guaranteed visible. The motif is drawn inside it.\n *\n * `background` is REQUIRED: a white symbol on a transparent canvas renders as an\n * empty icon (measured by buzzalert with `tauri icon`, 25/10).\n *\n * ICO and ICNS are written as plain byte containers holding PNGs — no dependency.\n */\nimport { deflateSync } from \"node:zlib\";\n\nexport interface NativeIconFile {\n /** Path relative to the output directory, e.g. `ios/AppIcon.appiconset/AppIcon-1024.png`. */\n path: string;\n content: Uint8Array;\n}\n\nexport interface NativeIconOptions {\n /** Hex colour behind the motif (`#RRGGBB`). Required — see the module comment. */\n background: string;\n /** Default: all three. */\n platforms?: Array<\"ios\" | \"android\" | \"desktop\">;\n}\n\nexport const NATIVE_BACKGROUND_REQUIRED =\n \"nativeIconSet needs `background` (#RRGGBB): a white symbol on a transparent canvas renders as an empty icon\";\n\nconst HEX = /^#[0-9a-f]{6}$/i;\n\n/** Android density → adaptive foreground px (108dp) and legacy launcher px (48dp). */\nexport const ANDROID_DENSITIES = [\n { dpi: \"mdpi\", foreground: 108, legacy: 48 },\n { dpi: \"hdpi\", foreground: 162, legacy: 72 },\n { dpi: \"xhdpi\", foreground: 216, legacy: 96 },\n { dpi: \"xxhdpi\", foreground: 324, legacy: 144 },\n { dpi: \"xxxhdpi\", foreground: 432, legacy: 192 },\n] as const;\n\n/** Share of the 108dp adaptive canvas that every launcher mask keeps visible. */\nexport const ANDROID_SAFE_ZONE = 66 / 108;\n\nexport const ICO_SIZES = [16, 32, 48, 256] as const;\n/** ICNS chunk type → pixel size (PNG payloads). */\nexport const ICNS_CHUNKS = [\n [\"ic11\", 32],\n [\"ic12\", 64],\n [\"ic07\", 128],\n [\"ic08\", 256],\n [\"ic13\", 256],\n [\"ic09\", 512],\n [\"ic14\", 512],\n [\"ic10\", 1024],\n] as const;\n\ntype Shape = \"square\" | \"circle\" | \"none\";\n\nfunction composeSvg(svg: string, size: number, opts: { background?: string; shape: Shape; scale: number }): string {\n const d = size * opts.scale;\n const o = (size - d) / 2;\n const bg =\n opts.shape === \"square\" && opts.background\n ? `<rect width=\"${size}\" height=\"${size}\" fill=\"${opts.background}\"/>`\n : opts.shape === \"circle\" && opts.background\n ? `<circle cx=\"${size / 2}\" cy=\"${size / 2}\" r=\"${size / 2}\" fill=\"${opts.background}\"/>`\n : \"\";\n const href = `data:image/svg+xml;base64,${Buffer.from(svg).toString(\"base64\")}`;\n return (\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\" viewBox=\"0 0 ${size} ${size}\">` +\n `${bg}<image href=\"${href}\" x=\"${o}\" y=\"${o}\" width=\"${d}\" height=\"${d}\"/></svg>`\n );\n}\n\n// ---- PNG encoding (RGB, no alpha) -------------------------------------------\n\nconst CRC_TABLE = (() => {\n const t = new Uint32Array(256);\n for (let n = 0; n < 256; n++) {\n let c = n;\n for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;\n t[n] = c >>> 0;\n }\n return t;\n})();\n\nfunction crc32(bytes: Uint8Array): number {\n let c = 0xffffffff;\n for (const b of bytes) c = CRC_TABLE[(c ^ b) & 0xff]! ^ (c >>> 8);\n return (c ^ 0xffffffff) >>> 0;\n}\n\nfunction chunk(type: string, data: Uint8Array): Uint8Array {\n const out = new Uint8Array(12 + data.length);\n const v = new DataView(out.buffer);\n v.setUint32(0, data.length);\n for (let i = 0; i < 4; i++) out[4 + i] = type.charCodeAt(i);\n out.set(data, 8);\n v.setUint32(8 + data.length, crc32(out.subarray(4, 8 + data.length)));\n return out;\n}\n\n/** Encode RGBA pixels as an RGB PNG (colour type 2) — the alpha channel is dropped, not just made opaque. */\nexport function encodeRgbPng(rgba: Uint8Array, width: number, height: number): Uint8Array {\n const raw = new Uint8Array(height * (1 + width * 3));\n for (let y = 0; y < height; y++) {\n const row = y * (1 + width * 3);\n raw[row] = 0; // filter: none\n for (let x = 0; x < width; x++) {\n const s = (y * width + x) * 4;\n const t = row + 1 + x * 3;\n raw[t] = rgba[s]!;\n raw[t + 1] = rgba[s + 1]!;\n raw[t + 2] = rgba[s + 2]!;\n }\n }\n const ihdr = new Uint8Array(13);\n const v = new DataView(ihdr.buffer);\n v.setUint32(0, width);\n v.setUint32(4, height);\n ihdr[8] = 8; // bit depth\n ihdr[9] = 2; // colour type: truecolour, NO alpha\n const sig = Uint8Array.from([137, 80, 78, 71, 13, 10, 26, 10]);\n const parts = [sig, chunk(\"IHDR\", ihdr), chunk(\"IDAT\", new Uint8Array(deflateSync(raw))), chunk(\"IEND\", new Uint8Array())];\n const out = new Uint8Array(parts.reduce((n, p) => n + p.length, 0));\n let off = 0;\n for (const p of parts) {\n out.set(p, off);\n off += p.length;\n }\n return out;\n}\n\n// ---- containers --------------------------------------------------------------\n\n/** An ICO holding PNG images (Vista+ format). */\nexport function encodeIco(pngs: Array<{ size: number; png: Uint8Array }>): Uint8Array {\n const header = 6 + 16 * pngs.length;\n const total = header + pngs.reduce((n, p) => n + p.png.length, 0);\n const out = new Uint8Array(total);\n const v = new DataView(out.buffer);\n v.setUint16(0, 0, true); // reserved\n v.setUint16(2, 1, true); // type: icon\n v.setUint16(4, pngs.length, true);\n let off = header;\n pngs.forEach((p, i) => {\n const e = 6 + i * 16;\n out[e] = p.size >= 256 ? 0 : p.size; // 0 means 256\n out[e + 1] = p.size >= 256 ? 0 : p.size;\n v.setUint16(e + 4, 1, true); // colour planes\n v.setUint16(e + 6, 32, true); // bits per pixel\n v.setUint32(e + 8, p.png.length, true);\n v.setUint32(e + 12, off, true);\n out.set(p.png, off);\n off += p.png.length;\n });\n return out;\n}\n\n/** An ICNS holding PNG chunks. */\nexport function encodeIcns(chunks: Array<{ type: string; png: Uint8Array }>): Uint8Array {\n const total = 8 + chunks.reduce((n, c) => n + 8 + c.png.length, 0);\n const out = new Uint8Array(total);\n const v = new DataView(out.buffer);\n const tag = (at: number, s: string) => {\n for (let i = 0; i < 4; i++) out[at + i] = s.charCodeAt(i);\n };\n tag(0, \"icns\");\n v.setUint32(4, total);\n let off = 8;\n for (const c of chunks) {\n tag(off, c.type);\n v.setUint32(off + 4, 8 + c.png.length);\n out.set(c.png, off + 8);\n off += 8 + c.png.length;\n }\n return out;\n}\n\n// ---- the set -------------------------------------------------------------------\n\nconst enc = (s: string) => new TextEncoder().encode(s);\n\nconst ADAPTIVE_XML =\n `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n` +\n `<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\\n` +\n ` <background android:drawable=\"@color/ic_launcher_background\"/>\\n` +\n ` <foreground android:drawable=\"@mipmap/ic_launcher_foreground\"/>\\n` +\n `</adaptive-icon>\\n`;\n\nconst IOS_CONTENTS = JSON.stringify(\n {\n images: [{ filename: \"AppIcon-1024.png\", idiom: \"universal\", platform: \"ios\", size: \"1024x1024\" }],\n info: { author: \"@broberg/pwa\", version: 1 },\n },\n null,\n 2,\n);\n\n/**\n * Every native icon file for `svg`. Requires the optional peer @resvg/resvg-js,\n * like `rasterizeIconSet`.\n */\nexport async function nativeIconSet(svg: string, opts: NativeIconOptions): Promise<NativeIconFile[]> {\n if (!opts?.background || !HEX.test(opts.background)) throw new Error(NATIVE_BACKGROUND_REQUIRED);\n let Resvg: typeof import(\"@resvg/resvg-js\").Resvg;\n try {\n ({ Resvg } = await import(\"@resvg/resvg-js\"));\n } catch {\n throw new Error(\"nativeIconSet needs @resvg/resvg-js — install it as a dev dependency: npm i -D @resvg/resvg-js\");\n }\n const bg = opts.background;\n const want = new Set(opts.platforms ?? [\"ios\", \"android\", \"desktop\"]);\n const render = (size: number, shape: Shape, scale: number) =>\n new Resvg(composeSvg(svg, size, { background: bg, shape, scale }), { font: { loadSystemFonts: false } }).render();\n const png = (size: number, shape: Shape, scale: number) => new Uint8Array(render(size, shape, scale).asPng());\n\n const files: NativeIconFile[] = [];\n\n if (want.has(\"ios\")) {\n const r = render(1024, \"square\", 0.8);\n files.push(\n { path: \"ios/AppIcon.appiconset/AppIcon-1024.png\", content: encodeRgbPng(new Uint8Array(r.pixels), r.width, r.height) },\n { path: \"ios/AppIcon.appiconset/Contents.json\", content: enc(IOS_CONTENTS + \"\\n\") },\n );\n }\n\n if (want.has(\"android\")) {\n files.push(\n { path: \"android/mipmap-anydpi-v26/ic_launcher.xml\", content: enc(ADAPTIVE_XML) },\n { path: \"android/mipmap-anydpi-v26/ic_launcher_round.xml\", content: enc(ADAPTIVE_XML) },\n {\n path: \"android/values/ic_launcher_background.xml\",\n content: enc(\n `<?xml version=\"1.0\" encoding=\"utf-8\"?>\\n<resources>\\n <color name=\"ic_launcher_background\">${bg}</color>\\n</resources>\\n`,\n ),\n },\n );\n for (const d of ANDROID_DENSITIES) {\n files.push(\n { path: `android/mipmap-${d.dpi}/ic_launcher_foreground.png`, content: png(d.foreground, \"none\", ANDROID_SAFE_ZONE) },\n { path: `android/mipmap-${d.dpi}/ic_launcher.png`, content: png(d.legacy, \"square\", 0.7) },\n { path: `android/mipmap-${d.dpi}/ic_launcher_round.png`, content: png(d.legacy, \"circle\", 0.62) },\n );\n }\n }\n\n if (want.has(\"desktop\")) {\n files.push(\n { path: \"desktop/icon.ico\", content: encodeIco(ICO_SIZES.map((size) => ({ size, png: png(size, \"square\", 0.8) }))) },\n { path: \"desktop/icon.icns\", content: encodeIcns(ICNS_CHUNKS.map(([type, size]) => ({ type, png: png(size, \"square\", 0.8) }))) },\n );\n }\n\n return files;\n}\n","/**\n * @broberg/pwa/raster — F021.9: PNG icons, because iOS does not use an SVG\n * apple-touch-icon. «Add to Home Screen» on an iPhone with only SVG icons shows\n * a screenshot of the page instead of the logo (measured by sales, 10/10).\n *\n * Kept OUT of the core entries so `@broberg/pwa` and `/manifest` stay\n * dependency-free: the renderer, @resvg/resvg-js (prebuilt binaries, no system\n * libraries), is an optional peer loaded only when this runs.\n */\nimport type { IconSet, ManifestIcon } from \"./manifest.js\";\n\nexport interface RasterIcon {\n /** URL path, e.g. `/icons/icon-180.png`. */\n path: string;\n content: Uint8Array;\n type: \"image/png\";\n size: number;\n purpose: \"any\" | \"maskable\";\n}\n\nexport interface RasterIconSet {\n files: RasterIcon[];\n /** Manifest `icons[]` pointing at the PNGs. */\n icons: ManifestIcon[];\n /** Href for `<link rel=\"apple-touch-icon\">` — pass it to `pwaMetaTags({ appleTouchIcon })`. Undefined when 180 was not generated. */\n appleTouchIcon?: string;\n /**\n * F021.11 — the same 180 PNG at the two ROOT paths iOS asks for on its own\n * (`/apple-touch-icon.png`, `/apple-touch-icon-precomposed.png`). Write them to\n * your web root, NOT next to the other icons. Without them iOS fell back to a\n * letter («J») on a real home screen (jam, measured). Empty when 180 was not generated.\n */\n rootFiles: RasterIcon[];\n}\n\nexport interface RasterizeOptions {\n /** Extra font files for monogram text (system fonts are loaded too). */\n fontFiles?: string[];\n}\n\nexport const RESVG_MISSING =\n \"rasterizeIconSet needs @resvg/resvg-js — install it as a dev dependency: npm i -D @resvg/resvg-js\";\n\nconst toPng = (p: string) => p.replace(/\\.svg$/, \".png\");\n\n/** Rasterise every SVG from `buildIconSet` to a PNG of the same size. */\nexport async function rasterizeIconSet(set: IconSet, opts: RasterizeOptions = {}): Promise<RasterIconSet> {\n let Resvg: typeof import(\"@resvg/resvg-js\").Resvg;\n try {\n ({ Resvg } = await import(\"@resvg/resvg-js\"));\n } catch {\n throw new Error(RESVG_MISSING);\n }\n const files: RasterIcon[] = set.files.map((f) => {\n const png = new Resvg(f.content, {\n fitTo: { mode: \"width\", value: f.size },\n font: { loadSystemFonts: true, fontFiles: opts.fontFiles ?? [] },\n })\n .render()\n .asPng();\n return { path: toPng(f.path), content: new Uint8Array(png), type: \"image/png\", size: f.size, purpose: f.purpose };\n });\n const icons = set.icons.map((i) => ({ ...i, src: toPng(i.src), type: \"image/png\" }));\n const apple = files.find((f) => f.size === 180 && f.purpose === \"any\");\n const rootFiles: RasterIcon[] = apple\n ? [\"/apple-touch-icon.png\", \"/apple-touch-icon-precomposed.png\"].map((path) => ({ ...apple, path }))\n : [];\n return { files, icons, appleTouchIcon: apple?.path, rootFiles };\n}\n\n// F021.12 — native icon sets (iOS AppIcon, Android adaptive, .ico/.icns) from the same SVG.\nexport { nativeIconSet, encodeRgbPng, encodeIco, encodeIcns, NATIVE_BACKGROUND_REQUIRED, ANDROID_DENSITIES, ANDROID_SAFE_ZONE, ICO_SIZES, ICNS_CHUNKS } from \"./native.js\";\nexport type { NativeIconFile, NativeIconOptions } from \"./native.js\";\n"]}
package/dist/raster.d.cts CHANGED
@@ -20,6 +20,63 @@ interface IconSet {
20
20
  icons: ManifestIcon[];
21
21
  }
22
22
 
23
+ interface NativeIconFile {
24
+ /** Path relative to the output directory, e.g. `ios/AppIcon.appiconset/AppIcon-1024.png`. */
25
+ path: string;
26
+ content: Uint8Array;
27
+ }
28
+ interface NativeIconOptions {
29
+ /** Hex colour behind the motif (`#RRGGBB`). Required — see the module comment. */
30
+ background: string;
31
+ /** Default: all three. */
32
+ platforms?: Array<"ios" | "android" | "desktop">;
33
+ }
34
+ declare const NATIVE_BACKGROUND_REQUIRED = "nativeIconSet needs `background` (#RRGGBB): a white symbol on a transparent canvas renders as an empty icon";
35
+ /** Android density → adaptive foreground px (108dp) and legacy launcher px (48dp). */
36
+ declare const ANDROID_DENSITIES: readonly [{
37
+ readonly dpi: "mdpi";
38
+ readonly foreground: 108;
39
+ readonly legacy: 48;
40
+ }, {
41
+ readonly dpi: "hdpi";
42
+ readonly foreground: 162;
43
+ readonly legacy: 72;
44
+ }, {
45
+ readonly dpi: "xhdpi";
46
+ readonly foreground: 216;
47
+ readonly legacy: 96;
48
+ }, {
49
+ readonly dpi: "xxhdpi";
50
+ readonly foreground: 324;
51
+ readonly legacy: 144;
52
+ }, {
53
+ readonly dpi: "xxxhdpi";
54
+ readonly foreground: 432;
55
+ readonly legacy: 192;
56
+ }];
57
+ /** Share of the 108dp adaptive canvas that every launcher mask keeps visible. */
58
+ declare const ANDROID_SAFE_ZONE: number;
59
+ declare const ICO_SIZES: readonly [16, 32, 48, 256];
60
+ /** ICNS chunk type → pixel size (PNG payloads). */
61
+ declare const ICNS_CHUNKS: readonly [readonly ["ic11", 32], readonly ["ic12", 64], readonly ["ic07", 128], readonly ["ic08", 256], readonly ["ic13", 256], readonly ["ic09", 512], readonly ["ic14", 512], readonly ["ic10", 1024]];
62
+ /** Encode RGBA pixels as an RGB PNG (colour type 2) — the alpha channel is dropped, not just made opaque. */
63
+ declare function encodeRgbPng(rgba: Uint8Array, width: number, height: number): Uint8Array;
64
+ /** An ICO holding PNG images (Vista+ format). */
65
+ declare function encodeIco(pngs: Array<{
66
+ size: number;
67
+ png: Uint8Array;
68
+ }>): Uint8Array;
69
+ /** An ICNS holding PNG chunks. */
70
+ declare function encodeIcns(chunks: Array<{
71
+ type: string;
72
+ png: Uint8Array;
73
+ }>): Uint8Array;
74
+ /**
75
+ * Every native icon file for `svg`. Requires the optional peer @resvg/resvg-js,
76
+ * like `rasterizeIconSet`.
77
+ */
78
+ declare function nativeIconSet(svg: string, opts: NativeIconOptions): Promise<NativeIconFile[]>;
79
+
23
80
  /**
24
81
  * @broberg/pwa/raster — F021.9: PNG icons, because iOS does not use an SVG
25
82
  * apple-touch-icon. «Add to Home Screen» on an iPhone with only SVG icons shows
@@ -60,4 +117,4 @@ declare const RESVG_MISSING = "rasterizeIconSet needs @resvg/resvg-js \u2014 ins
60
117
  /** Rasterise every SVG from `buildIconSet` to a PNG of the same size. */
61
118
  declare function rasterizeIconSet(set: IconSet, opts?: RasterizeOptions): Promise<RasterIconSet>;
62
119
 
63
- export { RESVG_MISSING, type RasterIcon, type RasterIconSet, type RasterizeOptions, rasterizeIconSet };
120
+ export { ANDROID_DENSITIES, ANDROID_SAFE_ZONE, ICNS_CHUNKS, ICO_SIZES, NATIVE_BACKGROUND_REQUIRED, type NativeIconFile, type NativeIconOptions, RESVG_MISSING, type RasterIcon, type RasterIconSet, type RasterizeOptions, encodeIcns, encodeIco, encodeRgbPng, nativeIconSet, rasterizeIconSet };