Merge branch 'fix-meet-native-icon' into main
14 files changed,
+197
−6
brand/README.md+17 −3brand/distribute.py+3 −3brand/generate-native.mjs+89 −0brand/native/meet/ios-1024.pngbinarybrand/native/meet/ios.svg+24 −0brand/native/meet/macos-1024.pngbinarybrand/native/meet/macos-128.pngbinarybrand/native/meet/macos-16.pngbinarybrand/native/meet/macos-256.pngbinarybrand/native/meet/macos-32.pngbinarybrand/native/meet/macos-512.pngbinarybrand/native/meet/macos-64.pngbinarybrand/native/meet/macos.svg+24 −0brand/opaque-png.go+40 −0
CI passed — run details
diff --git a/brand/README.md b/brand/README.md| index 2cc01cd..e2cd72b 100644 |
| --- a/brand/README.md |
| +++ b/brand/README.md |
| @@ -36,8 +36,8 @@ Open `preview.png` or `preview.svg` to compare the family and small sizes. |
| | Memoria | book-search | `#7C3FC8` | |
| `svg/` contains opaque square 1024-unit masters. `png/` contains 16, 32, |
| -48, 180, 192, 512 and 1024-pixel exports for each. Platform masks should |
| -provide rounded corners; the rounded tiles in the preview are illustrative. |
| +48, 180, 192, 512 and 1024-pixel web exports for each. The rounded tiles |
| +in the family preview are illustrative; these files contain square pixels. |
| The Lucide canvas occupies 70% of each tile, with the original 2-unit white |
| stroke. Use visible product names alongside launcher icons. |
| @@ -60,7 +60,21 @@ Estilo's existing notice for that same commit. Retain it when distributing |
| the icons. Upstream paths are unchanged; the generated artwork adds white |
| stroke colour, scaling and a background. |
| -Validation: preview rendered in Chromium and visually inspected at 48, 32 |
| +Web validation: preview rendered in Chromium and visually inspected at 48, 32 |
| and 16 px; all 49 PNG dimensions and full opacity checked. White/background |
| contrast ranges from 4.33:1 (Slides) to 12.12:1 (Oficina). Fine details soften |
| at 16 px; these are provisional marks, not bespoke favicon drawings. |
| + |
| +Meet’s native artwork lives in `native/meet/`. The Mac export has transparent |
| +corners, a squircle silhouette, a lit teal surface and a raised camera mark. |
| +AppKit uses those pixels directly for the Dock; the Mac bundler also makes |
| +its Finder ICNS from that same master. The iPhone export fills its canvas |
| +and has no alpha channel, so iOS can apply its own mask. The distributor |
| +copies the appropriate export to each platform instead of sharing one PNG. |
| + |
| +Regenerate native assets with `node brand/generate-native.mjs --render`. |
| +It uses the same Playwright setting as the web generator; optionally set |
| +`OFICINA_CHROMIUM` to an installed Chromium executable. Its render check |
| +rejects incorrect platform corner transparency. Rebuild and sign the Mac |
| +app after distributing assets; replacing repository PNGs does not update |
| +an installed app. |
diff --git a/brand/distribute.py b/brand/distribute.py| index 1836c2f..5e4b35b 100644 |
| --- a/brand/distribute.py |
| +++ b/brand/distribute.py |
| @@ -27,9 +27,9 @@ if app == "home": |
| files[name + ".svg"] = source / "svg" / (name + ".svg") |
| copies = {destination / name: path for name, path in files.items()} |
| if app == "meet": |
| - for name in ["native/ios/Assets.xcassets/AppIcon.appiconset/AppIcon.png", |
| - "native/apple/Sources/MeetMac/Resources/AppIcon.png"]: |
| - copies[args.checkout / name] = source / "png/meet-1024.png" |
| + # AppKit uses the pixels directly; iOS applies its own icon mask. |
| + copies[args.checkout / "native/ios/Assets.xcassets/AppIcon.appiconset/AppIcon.png"] = source / "native/meet/ios-1024.png" |
| + copies[args.checkout / "native/apple/Sources/MeetMac/Resources/AppIcon.png"] = source / "native/meet/macos-1024.png" |
| copies[args.checkout / "native/LICENSE.lucide"] = source / "LICENSE.lucide" |
| for target, original in copies.items(): |
| data = original.read_bytes() |
diff --git a/brand/generate-native.mjs b/brand/generate-native.mjs| new file mode 100644 |
| index 0000000..08c895a |
| --- /dev/null |
| +++ b/brand/generate-native.mjs |
| @@ -0,0 +1,89 @@ |
| +import { readFile, writeFile, mkdir } from 'node:fs/promises'; |
| +import { fileURLToPath } from 'node:url'; |
| +import { execFileSync } from 'node:child_process'; |
| + |
| +const root = new URL('./', import.meta.url); |
| +const output = new URL('native/meet/', root); |
| +await mkdir(output, { recursive: true }); |
| +const source = await readFile(new URL('upstream/video.svg', root), 'utf8'); |
| +const camera = source.match(/<svg[^>]*>([\s\S]*?)<\/svg>/)[1].trim(); |
| + |
| +// The legacy ICNS and runtime AppKit image need their own transparent mask. |
| +// iOS receives a full-bleed background and applies its platform mask itself. |
| +function squircle(inset) { |
| + const radius = 512 - inset; |
| + const points = Array.from({ length: 256 }, (_, i) => { |
| + const angle = i * Math.PI * 2 / 256; |
| + const axis = value => 512 + radius * Math.sign(value) * Math.abs(value) ** .45; |
| + return `${axis(Math.cos(angle)).toFixed(3)} ${axis(Math.sin(angle)).toFixed(3)}`; |
| + }); |
| + return `M${points.join(' L')} Z`; |
| +} |
| +const tile = squircle(100); |
| +const rim = squircle(103); |
| +function render(platform) { |
| + const mac = platform === 'macos'; |
| + const shape = mac ? `<path d="${tile}"/>` : '<rect width="1024" height="1024"/>'; |
| + const mark = `<g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${camera}</g>`; |
| + return `<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Meet"><title>Meet</title> |
| +<defs> |
| + <linearGradient id="teal" x1="0" y1="0" x2=".85" y2="1"><stop stop-color="#39D7D1"/><stop offset=".42" stop-color="#0C9BA9"/><stop offset="1" stop-color="#06566F"/></linearGradient> |
| + <radialGradient id="light" cx=".24" cy=".04" r=".95"><stop stop-color="#C9FFF2" stop-opacity=".54"/><stop offset=".5" stop-color="#72F2E5" stop-opacity=".07"/><stop offset="1" stop-color="#00364C" stop-opacity=".18"/></radialGradient> |
| + <linearGradient id="rim" x1="0" y1="0" x2=".2" y2="1"><stop stop-color="#E2FFFA" stop-opacity=".83"/><stop offset=".38" stop-color="#ADFFF2" stop-opacity=".22"/><stop offset=".78" stop-color="#025167" stop-opacity=".25"/><stop offset="1" stop-color="#002F46" stop-opacity=".65"/></linearGradient> |
| + <linearGradient id="porcelain" x1="0" y1="0" x2=".15" y2="1"><stop stop-color="#FFFFFF"/><stop offset=".45" stop-color="#F8FFFF"/><stop offset="1" stop-color="#B5DFE4"/></linearGradient> |
| + <linearGradient id="edge" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#C2F6F0"/><stop offset="1" stop-color="#548D9B"/></linearGradient> |
| + <filter id="tile-shadow" x="-20%" y="-20%" width="140%" height="155%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="14" stdDeviation="14" flood-color="#092B3B" flood-opacity=".28"/></filter> |
| + <filter id="mark-shadow" x="-40%" y="-50%" width="180%" height="220%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="19" stdDeviation="12" flood-color="#003846" flood-opacity=".4"/><feDropShadow dx="0" dy="3" stdDeviation="2" flood-color="#003343" flood-opacity=".22"/></filter> |
| + <clipPath id="surface">${shape}</clipPath> |
| +</defs> |
| +${mac ? `<g fill="#074E62" filter="url(#tile-shadow)">${shape}</g>` : ''} |
| +<g clip-path="url(#surface)"> |
| + <rect width="1024" height="1024" fill="url(#teal)"/> |
| + <rect width="1024" height="1024" fill="url(#light)"/> |
| + ${mac ? `<path d="${rim}" fill="none" stroke="url(#rim)" stroke-width="7"/>` : ''} |
| + <g transform="translate(0 8)" stroke="url(#edge)" filter="url(#mark-shadow)">${mark}</g> |
| + <g stroke="url(#porcelain)">${mark}</g> |
| + <g transform="translate(0 -1.5)" stroke="#FFFFFF" opacity=".26">${mark.replace('stroke-width="2"', 'stroke-width="1.84"')}</g> |
| +</g> |
| +</svg>\n`; |
| +} |
| +for (const platform of ['macos', 'ios']) { |
| + await writeFile(new URL(`${platform}.svg`, output), render(platform)); |
| +} |
| +if (process.argv.includes('--render')) { |
| + const { chromium } = await import(process.env.OFICINA_PLAYWRIGHT || 'playwright'); |
| + const browser = await chromium.launch({ headless: true, executablePath: process.env.OFICINA_CHROMIUM }); |
| + try { |
| + const page = await browser.newPage(); |
| + for (const platform of ['macos', 'ios']) { |
| + const svg = render(platform); |
| + for (const size of platform === 'macos' ? [16, 32, 64, 128, 256, 512, 1024] : [1024]) { |
| + const png = await page.evaluate(async ({ svg, size, platform }) => { |
| + const image = new Image(); |
| + image.src = 'data:image/svg+xml;base64,' + btoa(svg); |
| + await image.decode(); |
| + const canvas = document.createElement('canvas'); |
| + canvas.width = canvas.height = size; |
| + const context = canvas.getContext('2d', { alpha: platform === 'macos' }); |
| + context.drawImage(image, 0, 0, size, size); |
| + const alpha = context.getImageData(0, 0, 1, 1).data[3]; |
| + if (alpha !== (platform === 'macos' ? 0 : 255)) { |
| + throw new Error('Native icon has the wrong platform mask'); |
| + } |
| + return canvas.toDataURL('image/png').split(',')[1]; |
| + }, { svg, size, platform }); |
| + let bytes = Buffer.from(png, 'base64'); |
| + if (platform === 'ios') { |
| + bytes = execFileSync('go', ['run', fileURLToPath(new URL('opaque-png.go', root))], { |
| + input: bytes, maxBuffer: 8 << 20, |
| + }); |
| + if (bytes[25] !== 2) throw new Error('iPhone PNG must omit its alpha channel'); |
| + } |
| + await writeFile(new URL(`${platform}-${size}.png`, output), bytes); |
| + } |
| + } |
| + } finally { |
| + await browser.close(); |
| + } |
| +} |
| +console.log(`Generated native Meet artwork in ${fileURLToPath(output)}`); |
diff --git a/brand/native/meet/ios-1024.png b/brand/native/meet/ios-1024.png| new file mode 100644 |
| index 0000000..aaea742 |
| Binary files /dev/null and b/brand/native/meet/ios-1024.png differ |
diff --git a/brand/native/meet/ios.svg b/brand/native/meet/ios.svg| new file mode 100644 |
| index 0000000..1cf713d |
| --- /dev/null |
| +++ b/brand/native/meet/ios.svg |
| @@ -0,0 +1,24 @@ |
| +<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Meet"><title>Meet</title> |
| +<defs> |
| + <linearGradient id="teal" x1="0" y1="0" x2=".85" y2="1"><stop stop-color="#39D7D1"/><stop offset=".42" stop-color="#0C9BA9"/><stop offset="1" stop-color="#06566F"/></linearGradient> |
| + <radialGradient id="light" cx=".24" cy=".04" r=".95"><stop stop-color="#C9FFF2" stop-opacity=".54"/><stop offset=".5" stop-color="#72F2E5" stop-opacity=".07"/><stop offset="1" stop-color="#00364C" stop-opacity=".18"/></radialGradient> |
| + <linearGradient id="rim" x1="0" y1="0" x2=".2" y2="1"><stop stop-color="#E2FFFA" stop-opacity=".83"/><stop offset=".38" stop-color="#ADFFF2" stop-opacity=".22"/><stop offset=".78" stop-color="#025167" stop-opacity=".25"/><stop offset="1" stop-color="#002F46" stop-opacity=".65"/></linearGradient> |
| + <linearGradient id="porcelain" x1="0" y1="0" x2=".15" y2="1"><stop stop-color="#FFFFFF"/><stop offset=".45" stop-color="#F8FFFF"/><stop offset="1" stop-color="#B5DFE4"/></linearGradient> |
| + <linearGradient id="edge" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#C2F6F0"/><stop offset="1" stop-color="#548D9B"/></linearGradient> |
| + <filter id="tile-shadow" x="-20%" y="-20%" width="140%" height="155%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="14" stdDeviation="14" flood-color="#092B3B" flood-opacity=".28"/></filter> |
| + <filter id="mark-shadow" x="-40%" y="-50%" width="180%" height="220%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="19" stdDeviation="12" flood-color="#003846" flood-opacity=".4"/><feDropShadow dx="0" dy="3" stdDeviation="2" flood-color="#003343" flood-opacity=".22"/></filter> |
| + <clipPath id="surface"><rect width="1024" height="1024"/></clipPath> |
| +</defs> |
| + |
| +<g clip-path="url(#surface)"> |
| + <rect width="1024" height="1024" fill="url(#teal)"/> |
| + <rect width="1024" height="1024" fill="url(#light)"/> |
| + |
| + <g transform="translate(0 8)" stroke="url(#edge)" filter="url(#mark-shadow)"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| + <g stroke="url(#porcelain)"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| + <g transform="translate(0 -1.5)" stroke="#FFFFFF" opacity=".26"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="1.84" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| +</g> |
| +</svg> |
diff --git a/brand/native/meet/macos-1024.png b/brand/native/meet/macos-1024.png| new file mode 100644 |
| index 0000000..e734364 |
| Binary files /dev/null and b/brand/native/meet/macos-1024.png differ |
diff --git a/brand/native/meet/macos-128.png b/brand/native/meet/macos-128.png| new file mode 100644 |
| index 0000000..fac85b6 |
| Binary files /dev/null and b/brand/native/meet/macos-128.png differ |
diff --git a/brand/native/meet/macos-16.png b/brand/native/meet/macos-16.png| new file mode 100644 |
| index 0000000..9a84abf |
| Binary files /dev/null and b/brand/native/meet/macos-16.png differ |
diff --git a/brand/native/meet/macos-256.png b/brand/native/meet/macos-256.png| new file mode 100644 |
| index 0000000..203a89a |
| Binary files /dev/null and b/brand/native/meet/macos-256.png differ |
diff --git a/brand/native/meet/macos-32.png b/brand/native/meet/macos-32.png| new file mode 100644 |
| index 0000000..bf994fa |
| Binary files /dev/null and b/brand/native/meet/macos-32.png differ |
diff --git a/brand/native/meet/macos-512.png b/brand/native/meet/macos-512.png| new file mode 100644 |
| index 0000000..1d5eaa6 |
| Binary files /dev/null and b/brand/native/meet/macos-512.png differ |
diff --git a/brand/native/meet/macos-64.png b/brand/native/meet/macos-64.png| new file mode 100644 |
| index 0000000..e83075b |
| Binary files /dev/null and b/brand/native/meet/macos-64.png differ |
diff --git a/brand/native/meet/macos.svg b/brand/native/meet/macos.svg| new file mode 100644 |
| index 0000000..d315bb4 |
| --- /dev/null |
| +++ b/brand/native/meet/macos.svg |
| @@ -0,0 +1,24 @@ |
| +<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" role="img" aria-label="Meet"><title>Meet</title> |
| +<defs> |
| + <linearGradient id="teal" x1="0" y1="0" x2=".85" y2="1"><stop stop-color="#39D7D1"/><stop offset=".42" stop-color="#0C9BA9"/><stop offset="1" stop-color="#06566F"/></linearGradient> |
| + <radialGradient id="light" cx=".24" cy=".04" r=".95"><stop stop-color="#C9FFF2" stop-opacity=".54"/><stop offset=".5" stop-color="#72F2E5" stop-opacity=".07"/><stop offset="1" stop-color="#00364C" stop-opacity=".18"/></radialGradient> |
| + <linearGradient id="rim" x1="0" y1="0" x2=".2" y2="1"><stop stop-color="#E2FFFA" stop-opacity=".83"/><stop offset=".38" stop-color="#ADFFF2" stop-opacity=".22"/><stop offset=".78" stop-color="#025167" stop-opacity=".25"/><stop offset="1" stop-color="#002F46" stop-opacity=".65"/></linearGradient> |
| + <linearGradient id="porcelain" x1="0" y1="0" x2=".15" y2="1"><stop stop-color="#FFFFFF"/><stop offset=".45" stop-color="#F8FFFF"/><stop offset="1" stop-color="#B5DFE4"/></linearGradient> |
| + <linearGradient id="edge" x1="0" y1="0" x2="0" y2="1"><stop stop-color="#C2F6F0"/><stop offset="1" stop-color="#548D9B"/></linearGradient> |
| + <filter id="tile-shadow" x="-20%" y="-20%" width="140%" height="155%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="14" stdDeviation="14" flood-color="#092B3B" flood-opacity=".28"/></filter> |
| + <filter id="mark-shadow" x="-40%" y="-50%" width="180%" height="220%" color-interpolation-filters="sRGB"><feDropShadow dx="0" dy="19" stdDeviation="12" flood-color="#003846" flood-opacity=".4"/><feDropShadow dx="0" dy="3" stdDeviation="2" flood-color="#003343" flood-opacity=".22"/></filter> |
| + <clipPath id="surface"><path d="M924.000 512.000 L923.944 589.687 L923.777 618.110 L923.497 639.320 L923.106 656.872 L922.603 672.109 L921.987 685.713 L921.259 698.081 L920.419 709.471 L919.465 720.059 L918.397 729.974 L917.216 739.309 L915.920 748.139 L914.509 756.522 L912.983 764.503 L911.340 772.122 L909.580 779.408 L907.702 786.390 L905.705 793.089 L903.588 799.524 L901.350 805.712 L898.991 811.666 L896.507 817.400 L893.899 822.925 L891.165 828.249 L888.302 833.383 L885.310 838.332 L882.186 843.104 L878.928 847.706 L875.533 852.142 L872.000 856.417 L868.325 860.537 L864.505 864.505 L860.537 868.325 L856.417 872.000 L852.142 875.533 L847.706 878.928 L843.104 882.186 L838.332 885.310 L833.383 888.302 L828.249 891.165 L822.925 893.899 L817.400 896.507 L811.666 898.991 L805.712 901.350 L799.524 903.588 L793.089 905.705 L786.390 907.702 L779.408 909.580 L772.122 911.340 L764.503 912.983 L756.522 914.509 L748.139 915.920 L739.309 917.216 L729.974 918.397 L720.059 919.465 L709.471 920.419 L698.081 921.259 L685.713 921.987 L672.109 922.603 L656.872 923.106 L639.320 923.497 L618.110 923.777 L589.687 923.944 L512.000 924.000 L434.313 923.944 L405.890 923.777 L384.680 923.497 L367.128 923.106 L351.891 922.603 L338.287 921.987 L325.919 921.259 L314.529 920.419 L303.941 919.465 L294.026 918.397 L284.691 917.216 L275.861 915.920 L267.478 914.509 L259.497 912.983 L251.878 911.340 L244.592 909.580 L237.610 907.702 L230.911 905.705 L224.476 903.588 L218.288 901.350 L212.334 898.991 L206.600 896.507 L201.075 893.899 L195.751 891.165 L190.617 888.302 L185.668 885.310 L180.896 882.186 L176.294 878.928 L171.858 875.533 L167.583 872.000 L163.463 868.325 L159.495 864.505 L155.675 860.537 L152.000 856.417 L148.467 852.142 L145.072 847.706 L141.814 843.104 L138.690 838.332 L135.698 833.383 L132.835 828.249 L130.101 822.925 L127.493 817.400 L125.009 811.666 L122.650 805.712 L120.412 799.524 L118.295 793.089 L116.298 786.390 L114.420 779.408 L112.660 772.122 L111.017 764.503 L109.491 756.522 L108.080 748.139 L106.784 739.309 L105.603 729.974 L104.535 720.059 L103.581 709.471 L102.741 698.081 L102.013 685.713 L101.397 672.109 L100.894 656.872 L100.503 639.320 L100.223 618.110 L100.056 589.687 L100.000 512.000 L100.056 434.313 L100.223 405.890 L100.503 384.680 L100.894 367.128 L101.397 351.891 L102.013 338.287 L102.741 325.919 L103.581 314.529 L104.535 303.941 L105.603 294.026 L106.784 284.691 L108.080 275.861 L109.491 267.478 L111.017 259.497 L112.660 251.878 L114.420 244.592 L116.298 237.610 L118.295 230.911 L120.412 224.476 L122.650 218.288 L125.009 212.334 L127.493 206.600 L130.101 201.075 L132.835 195.751 L135.698 190.617 L138.690 185.668 L141.814 180.896 L145.072 176.294 L148.467 171.858 L152.000 167.583 L155.675 163.463 L159.495 159.495 L163.463 155.675 L167.583 152.000 L171.858 148.467 L176.294 145.072 L180.896 141.814 L185.668 138.690 L190.617 135.698 L195.751 132.835 L201.075 130.101 L206.600 127.493 L212.334 125.009 L218.288 122.650 L224.476 120.412 L230.911 118.295 L237.610 116.298 L244.592 114.420 L251.878 112.660 L259.497 111.017 L267.478 109.491 L275.861 108.080 L284.691 106.784 L294.026 105.603 L303.941 104.535 L314.529 103.581 L325.919 102.741 L338.287 102.013 L351.891 101.397 L367.128 100.894 L384.680 100.503 L405.890 100.223 L434.313 100.056 L512.000 100.000 L589.687 100.056 L618.110 100.223 L639.320 100.503 L656.872 100.894 L672.109 101.397 L685.713 102.013 L698.081 102.741 L709.471 103.581 L720.059 104.535 L729.974 105.603 L739.309 106.784 L748.139 108.080 L756.522 109.491 L764.503 111.017 L772.122 112.660 L779.408 114.420 L786.390 116.298 L793.089 118.295 L799.524 120.412 L805.712 122.650 L811.666 125.009 L817.400 127.493 L822.925 130.101 L828.249 132.835 L833.383 135.698 L838.332 138.690 L843.104 141.814 L847.706 145.072 L852.142 148.467 L856.417 152.000 L860.537 155.675 L864.505 159.495 L868.325 163.463 L872.000 167.583 L875.533 171.858 L878.928 176.294 L882.186 180.896 L885.310 185.668 L888.302 190.617 L891.165 195.751 L893.899 201.075 L896.507 206.600 L898.991 212.334 L901.350 218.288 L903.588 224.476 L905.705 230.911 L907.702 237.610 L909.580 244.592 L911.340 251.878 L912.983 259.497 L914.509 267.478 L915.920 275.861 L917.216 284.691 L918.397 294.026 L919.465 303.941 L920.419 314.529 L921.259 325.919 L921.987 338.287 L922.603 351.891 L923.106 367.128 L923.497 384.680 L923.777 405.890 L923.944 434.313 Z"/></clipPath> |
| +</defs> |
| +<g fill="#074E62" filter="url(#tile-shadow)"><path d="M924.000 512.000 L923.944 589.687 L923.777 618.110 L923.497 639.320 L923.106 656.872 L922.603 672.109 L921.987 685.713 L921.259 698.081 L920.419 709.471 L919.465 720.059 L918.397 729.974 L917.216 739.309 L915.920 748.139 L914.509 756.522 L912.983 764.503 L911.340 772.122 L909.580 779.408 L907.702 786.390 L905.705 793.089 L903.588 799.524 L901.350 805.712 L898.991 811.666 L896.507 817.400 L893.899 822.925 L891.165 828.249 L888.302 833.383 L885.310 838.332 L882.186 843.104 L878.928 847.706 L875.533 852.142 L872.000 856.417 L868.325 860.537 L864.505 864.505 L860.537 868.325 L856.417 872.000 L852.142 875.533 L847.706 878.928 L843.104 882.186 L838.332 885.310 L833.383 888.302 L828.249 891.165 L822.925 893.899 L817.400 896.507 L811.666 898.991 L805.712 901.350 L799.524 903.588 L793.089 905.705 L786.390 907.702 L779.408 909.580 L772.122 911.340 L764.503 912.983 L756.522 914.509 L748.139 915.920 L739.309 917.216 L729.974 918.397 L720.059 919.465 L709.471 920.419 L698.081 921.259 L685.713 921.987 L672.109 922.603 L656.872 923.106 L639.320 923.497 L618.110 923.777 L589.687 923.944 L512.000 924.000 L434.313 923.944 L405.890 923.777 L384.680 923.497 L367.128 923.106 L351.891 922.603 L338.287 921.987 L325.919 921.259 L314.529 920.419 L303.941 919.465 L294.026 918.397 L284.691 917.216 L275.861 915.920 L267.478 914.509 L259.497 912.983 L251.878 911.340 L244.592 909.580 L237.610 907.702 L230.911 905.705 L224.476 903.588 L218.288 901.350 L212.334 898.991 L206.600 896.507 L201.075 893.899 L195.751 891.165 L190.617 888.302 L185.668 885.310 L180.896 882.186 L176.294 878.928 L171.858 875.533 L167.583 872.000 L163.463 868.325 L159.495 864.505 L155.675 860.537 L152.000 856.417 L148.467 852.142 L145.072 847.706 L141.814 843.104 L138.690 838.332 L135.698 833.383 L132.835 828.249 L130.101 822.925 L127.493 817.400 L125.009 811.666 L122.650 805.712 L120.412 799.524 L118.295 793.089 L116.298 786.390 L114.420 779.408 L112.660 772.122 L111.017 764.503 L109.491 756.522 L108.080 748.139 L106.784 739.309 L105.603 729.974 L104.535 720.059 L103.581 709.471 L102.741 698.081 L102.013 685.713 L101.397 672.109 L100.894 656.872 L100.503 639.320 L100.223 618.110 L100.056 589.687 L100.000 512.000 L100.056 434.313 L100.223 405.890 L100.503 384.680 L100.894 367.128 L101.397 351.891 L102.013 338.287 L102.741 325.919 L103.581 314.529 L104.535 303.941 L105.603 294.026 L106.784 284.691 L108.080 275.861 L109.491 267.478 L111.017 259.497 L112.660 251.878 L114.420 244.592 L116.298 237.610 L118.295 230.911 L120.412 224.476 L122.650 218.288 L125.009 212.334 L127.493 206.600 L130.101 201.075 L132.835 195.751 L135.698 190.617 L138.690 185.668 L141.814 180.896 L145.072 176.294 L148.467 171.858 L152.000 167.583 L155.675 163.463 L159.495 159.495 L163.463 155.675 L167.583 152.000 L171.858 148.467 L176.294 145.072 L180.896 141.814 L185.668 138.690 L190.617 135.698 L195.751 132.835 L201.075 130.101 L206.600 127.493 L212.334 125.009 L218.288 122.650 L224.476 120.412 L230.911 118.295 L237.610 116.298 L244.592 114.420 L251.878 112.660 L259.497 111.017 L267.478 109.491 L275.861 108.080 L284.691 106.784 L294.026 105.603 L303.941 104.535 L314.529 103.581 L325.919 102.741 L338.287 102.013 L351.891 101.397 L367.128 100.894 L384.680 100.503 L405.890 100.223 L434.313 100.056 L512.000 100.000 L589.687 100.056 L618.110 100.223 L639.320 100.503 L656.872 100.894 L672.109 101.397 L685.713 102.013 L698.081 102.741 L709.471 103.581 L720.059 104.535 L729.974 105.603 L739.309 106.784 L748.139 108.080 L756.522 109.491 L764.503 111.017 L772.122 112.660 L779.408 114.420 L786.390 116.298 L793.089 118.295 L799.524 120.412 L805.712 122.650 L811.666 125.009 L817.400 127.493 L822.925 130.101 L828.249 132.835 L833.383 135.698 L838.332 138.690 L843.104 141.814 L847.706 145.072 L852.142 148.467 L856.417 152.000 L860.537 155.675 L864.505 159.495 L868.325 163.463 L872.000 167.583 L875.533 171.858 L878.928 176.294 L882.186 180.896 L885.310 185.668 L888.302 190.617 L891.165 195.751 L893.899 201.075 L896.507 206.600 L898.991 212.334 L901.350 218.288 L903.588 224.476 L905.705 230.911 L907.702 237.610 L909.580 244.592 L911.340 251.878 L912.983 259.497 L914.509 267.478 L915.920 275.861 L917.216 284.691 L918.397 294.026 L919.465 303.941 L920.419 314.529 L921.259 325.919 L921.987 338.287 L922.603 351.891 L923.106 367.128 L923.497 384.680 L923.777 405.890 L923.944 434.313 Z"/></g> |
| +<g clip-path="url(#surface)"> |
| + <rect width="1024" height="1024" fill="url(#teal)"/> |
| + <rect width="1024" height="1024" fill="url(#light)"/> |
| + <path d="M921.000 512.000 L920.945 589.122 L920.778 617.337 L920.501 638.393 L920.113 655.817 L919.613 670.943 L919.002 684.448 L918.279 696.726 L917.445 708.033 L916.498 718.544 L915.438 728.387 L914.265 737.654 L912.979 746.420 L911.578 754.741 L910.063 762.665 L908.432 770.227 L906.685 777.461 L904.820 784.392 L902.838 791.042 L900.737 797.430 L898.515 803.573 L896.173 809.484 L893.708 815.177 L891.118 820.661 L888.404 825.947 L885.562 831.042 L882.592 835.956 L879.490 840.693 L876.256 845.261 L872.886 849.665 L869.379 853.910 L865.730 857.999 L861.938 861.938 L857.999 865.730 L853.910 869.379 L849.665 872.886 L845.261 876.256 L840.693 879.490 L835.956 882.592 L831.042 885.562 L825.947 888.404 L820.661 891.118 L815.177 893.708 L809.484 896.173 L803.573 898.515 L797.430 900.737 L791.042 902.838 L784.392 904.820 L777.461 906.685 L770.227 908.432 L762.665 910.063 L754.741 911.578 L746.420 912.979 L737.654 914.265 L728.387 915.438 L718.544 916.498 L708.033 917.445 L696.726 918.279 L684.448 919.002 L670.943 919.613 L655.817 920.113 L638.393 920.501 L617.337 920.778 L589.122 920.945 L512.000 921.000 L434.878 920.945 L406.663 920.778 L385.607 920.501 L368.183 920.113 L353.057 919.613 L339.552 919.002 L327.274 918.279 L315.967 917.445 L305.456 916.498 L295.613 915.438 L286.346 914.265 L277.580 912.979 L269.259 911.578 L261.335 910.063 L253.773 908.432 L246.539 906.685 L239.608 904.820 L232.958 902.838 L226.570 900.737 L220.427 898.515 L214.516 896.173 L208.823 893.708 L203.339 891.118 L198.053 888.404 L192.958 885.562 L188.044 882.592 L183.307 879.490 L178.739 876.256 L174.335 872.886 L170.090 869.379 L166.001 865.730 L162.062 861.938 L158.270 857.999 L154.621 853.910 L151.114 849.665 L147.744 845.261 L144.510 840.693 L141.408 835.956 L138.438 831.042 L135.596 825.947 L132.882 820.661 L130.292 815.177 L127.827 809.484 L125.485 803.573 L123.263 797.430 L121.162 791.042 L119.180 784.392 L117.315 777.461 L115.568 770.227 L113.937 762.665 L112.422 754.741 L111.021 746.420 L109.735 737.654 L108.562 728.387 L107.502 718.544 L106.555 708.033 L105.721 696.726 L104.998 684.448 L104.387 670.943 L103.887 655.817 L103.499 638.393 L103.222 617.337 L103.055 589.122 L103.000 512.000 L103.055 434.878 L103.222 406.663 L103.499 385.607 L103.887 368.183 L104.387 353.057 L104.998 339.552 L105.721 327.274 L106.555 315.967 L107.502 305.456 L108.562 295.613 L109.735 286.346 L111.021 277.580 L112.422 269.259 L113.937 261.335 L115.568 253.773 L117.315 246.539 L119.180 239.608 L121.162 232.958 L123.263 226.570 L125.485 220.427 L127.827 214.516 L130.292 208.823 L132.882 203.339 L135.596 198.053 L138.438 192.958 L141.408 188.044 L144.510 183.307 L147.744 178.739 L151.114 174.335 L154.621 170.090 L158.270 166.001 L162.062 162.062 L166.001 158.270 L170.090 154.621 L174.335 151.114 L178.739 147.744 L183.307 144.510 L188.044 141.408 L192.958 138.438 L198.053 135.596 L203.339 132.882 L208.823 130.292 L214.516 127.827 L220.427 125.485 L226.570 123.263 L232.958 121.162 L239.608 119.180 L246.539 117.315 L253.773 115.568 L261.335 113.937 L269.259 112.422 L277.580 111.021 L286.346 109.735 L295.613 108.562 L305.456 107.502 L315.967 106.555 L327.274 105.721 L339.552 104.998 L353.057 104.387 L368.183 103.887 L385.607 103.499 L406.663 103.222 L434.878 103.055 L512.000 103.000 L589.122 103.055 L617.337 103.222 L638.393 103.499 L655.817 103.887 L670.943 104.387 L684.448 104.998 L696.726 105.721 L708.033 106.555 L718.544 107.502 L728.387 108.562 L737.654 109.735 L746.420 111.021 L754.741 112.422 L762.665 113.937 L770.227 115.568 L777.461 117.315 L784.392 119.180 L791.042 121.162 L797.430 123.263 L803.573 125.485 L809.484 127.827 L815.177 130.292 L820.661 132.882 L825.947 135.596 L831.042 138.438 L835.956 141.408 L840.693 144.510 L845.261 147.744 L849.665 151.114 L853.910 154.621 L857.999 158.270 L861.938 162.062 L865.730 166.001 L869.379 170.090 L872.886 174.335 L876.256 178.739 L879.490 183.307 L882.592 188.044 L885.562 192.958 L888.404 198.053 L891.118 203.339 L893.708 208.823 L896.173 214.516 L898.515 220.427 L900.737 226.570 L902.838 232.958 L904.820 239.608 L906.685 246.539 L908.432 253.773 L910.063 261.335 L911.578 269.259 L912.979 277.580 L914.265 286.346 L915.438 295.613 L916.498 305.456 L917.445 315.967 L918.279 327.274 L919.002 339.552 L919.613 353.057 L920.113 368.183 L920.501 385.607 L920.778 406.663 L920.945 434.878 Z" fill="none" stroke="url(#rim)" stroke-width="7"/> |
| + <g transform="translate(0 8)" stroke="url(#edge)" filter="url(#mark-shadow)"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| + <g stroke="url(#porcelain)"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| + <g transform="translate(0 -1.5)" stroke="#FFFFFF" opacity=".26"><g transform="translate(218 218) scale(24.5)" fill="none" stroke-width="1.84" stroke-linecap="round" stroke-linejoin="round"><path d="m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5" /> |
| + <rect x="2" y="6" width="14" height="12" rx="2" /></g></g> |
| +</g> |
| +</svg> |
diff --git a/brand/opaque-png.go b/brand/opaque-png.go| new file mode 100644 |
| index 0000000..0a518a7 |
| --- /dev/null |
| +++ b/brand/opaque-png.go |
| @@ -0,0 +1,40 @@ |
| +//go:build ignore |
| + |
| +// Re-encode an opaque icon as RGB PNG; Chromium may retain an unused alpha channel. |
| +package main |
| + |
| +import ( |
| + "bytes" |
| + "fmt" |
| + "image/png" |
| + "io" |
| + "os" |
| +) |
| + |
| +func main() { |
| + if err := run(); err != nil { |
| + fmt.Fprintln(os.Stderr, err) |
| + os.Exit(1) |
| + } |
| +} |
| + |
| +func run() error { |
| + raw, err := io.ReadAll(io.LimitReader(os.Stdin, 8<<20)) |
| + if err != nil || len(raw) == 8<<20 { |
| + return fmt.Errorf("icon PNG exceeds input limit") |
| + } |
| + cfg, err := png.DecodeConfig(bytes.NewReader(raw)) |
| + if err != nil || cfg.Width != 1024 || cfg.Height != 1024 { |
| + return fmt.Errorf("icon must be a 1024-pixel PNG") |
| + } |
| + img, err := png.Decode(bytes.NewReader(raw)) |
| + if err != nil { |
| + return err |
| + } |
| + opaque, ok := img.(interface{ Opaque() bool }) |
| + if !ok || !opaque.Opaque() { |
| + return fmt.Errorf("iPhone icon must be fully opaque") |
| + } |
| + // The standard encoder omits alpha when every pixel is opaque. |
| + return png.Encode(os.Stdout, img) |
| +} |