From faf881fa0c5a5575c5731c828513bda2f229080a Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 18 Aug 2026 21:21:41 +0200 Subject: [PATCH] added missing icons --- apple-touch-icon.png | Bin 0 -> 9603 bytes catcrafts-head.js | 9 ++ deploy/Caddyfile.example | 28 +++++- favicon.ico | Bin 0 -> 15086 bytes project.cpp | 12 +++ shared/interfaces/Catcrafts.Shared-Views.cppm | 7 ++ tests/ShouldStayScriptFree/main.cpp | 1 + tools/dev.sh | 3 +- tools/make-icons.sh | 80 ++++++++++++++++++ tools/publish-media.sh | 69 +++++++++++++-- 10 files changed, 201 insertions(+), 8 deletions(-) create mode 100644 apple-touch-icon.png create mode 100644 favicon.ico create mode 100755 tools/make-icons.sh diff --git a/apple-touch-icon.png b/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..72650b3fffb483598ddb4950a7e83161bd6fb8a8 GIT binary patch literal 9603 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

@68UInt{+#2SNu0KcDnPCWSOjK6s79zLz zWk6peBC$|p@yA5HP?v)4ML0GE#gj>ps?ph=9SmOm=cbpwi;@8e|3-{<4YmDn*$*ld8`Fv@z|)%8f8(a zya<@P$;5%LhDwxuDWb4FcZO4CgU1the+zTfs$yqDqt7T$-*UsqJ}w3$V-j;1 zg;G1p%pP5ZpmeF}M3JWdn)*yCNc$P#Z({RfD7*efWkU|iZqTbidY^AG!!r2o5Ja}4=DXmfSYWd8Uu0}ok7 zuJ?gkqdKB=ASfh4rX!|$S5(fg){dZ3l6j9db{~~;UENS$12~447Xp;mv?WWI2mX4V zPU%AW6ekaj$nR3vKp_n*!A(v{DVxhJ{6m2m{4HtejHT_wC|y~zN3eDNd_RN%jHHSN z0p&lqFMOls39*kQb$H*nLZQXEO<_MMNJKk*58|I5ZmeuH7g3_OzQnV4MGMPnvZ&}9r}?;8 zOl6$Bra|VZ0x55~oIJR0#^?p9LY8-M#j+IV5Ktm|?>^5k2qj~sDqJ16jg03uY#zz} z+;d)RhR!nWIzECxA>FlBiblS^B9`7Y)X6<*Jq9FM>lWtL*ZiN%8A|yiZvN7u9}#u> z#&0xBJCH>bS#9|NWL5Y^Te$gRW7UdG3^>f_qdIgdCc}BX9Xj*D@b#~1D&AM0I-LHrPtiJ25G&+)CyvZ8VM;w5b zYn`t+cP+_n17~U8^jkDEUrFJ(>~8lf_(c1_BZ>tGBvbpy*uD+W#`QxGJI#15I8jqN zR>NQQYP_JJAe-@QYj}HvBS1 zSoO53`gI~3@oJ_X?Z&hZ;1#M~jBDD>xNPrYh zRZK`;oSvq}e2dqk9$F7Q@5!(ee8yRv z^V%9t?;@ZVV*|_fn$<*)kMwilZeQQbI&RBVflPIk{X5POrEw=u2v~qN1Upp`;W8yTNqYcDN~@avhBfSKenY zIHYrGD^w70yxv(Xc`Eyl*r~acJ>LiOvzN|Nf{mgpK<2)wEe8UQrfq|*%bUi1TWN5f z>XrsD^kC1!M~7+}#^OJto`>9er5spnOOo1&fQ)+^n$oYobYEE{+xAk!a0e%_KN7G^ zdhKp>Hmz}&2fX;1g6L3zK&kp=oa+>Fno zNPSQM-_f4D8+Km(AP-ri*YWe4TeG6o3d>NbXx9uw1W}bW%Q16@F% zFY5?gM*)Al-nGA0$c%}~PFYU*1V{`=+{d=8irFrlM*Pn5E}r_90d`X2>Jmsr&eC6a z`UG!uVjKSD4N)hs(}6*txM|Gf9+;@ z3<$c0hv5ZI9ojS(2>#5KTb6QJkRsC~wr4153n}6h};8&^)Vo z&=AX&*+}K2t&hruAwbodHU-Ixw)_lQY1kzIEm&4~!}AAyFW)ycwqDTVosG@rypPmm zyXq}P)iN$Y2%xFebs6iPKWq`_n2UsYh4>sD3X)MZ`))v^{Y-QA)TF=n0oKIyU)Mj~ z*4bk#QZG)$hM zaJo4EsPm&Rpc)#sa$u>w?3-+Ty`7GhJ;)38BqX(_*femTm_mZ?*~SOewBY(yv8%|= zTHWoW$EOMOCG!fSXG-j6y*ngO-MbHv*gUxGvJwdMz)AmAc)_w&ksF)Iyv{8m`p73a z>CM9v*6SeAE#c!lZGWo>9_7O_h!1N41*$$RaImPL)SZ6id%< { return el; }); +// Keeps Safari from probing /apple-touch-icon{,-precomposed}.png at the root. +// Both files exist anyway, for the clients that probe regardless of markup. +ensure('link[rel="apple-touch-icon"]', () => { + const el = document.createElement("link"); + el.rel = "apple-touch-icon"; + el.href = "/apple-touch-icon.png"; + return el; +}); + ensure('meta[name="viewport"]', () => { const el = document.createElement("meta"); el.name = "viewport"; diff --git a/deploy/Caddyfile.example b/deploy/Caddyfile.example index 1ef3c25..7e63b71 100644 --- a/deploy/Caddyfile.example +++ b/deploy/Caddyfile.example @@ -80,8 +80,18 @@ catcrafts.net { Cross-Origin-Resource-Policy "same-origin" } # Subresources an isolated document pulls in must carry CORP themselves. + # The icons need it for the same reason the stylesheet does: "/" boots the + # wasm, so its icon fetches happen inside a require-corp document. + # + # -precomposed is listed here even though the rewrite below folds it onto + # apple-touch-icon.png: Caddy's directive order runs `header` BEFORE + # `rewrite`, so this matcher sees the URI as it arrived. (`handle` runs after, + # which is why @static does NOT need to list it.) Drop the path here and the + # probe still 200s, just bare. header /styles.css Cross-Origin-Resource-Policy "same-origin" - header /favicon.svg Cross-Origin-Resource-Policy "same-origin" + @icons path /favicon.svg /favicon.ico /apple-touch-icon.png \ + /apple-touch-icon-precomposed.png + header @icons Cross-Origin-Resource-Policy "same-origin" # ── analytics, two tiers ────────────────────────────────────────────── # @@ -120,9 +130,23 @@ catcrafts.net { # better than the backend would. `precompressed` serves the .zst / .gz # siblings the CI build produces, so the ~800 KB module is never recompressed # per request. Cache-busted by the ?v= in index.html. + # + # CAVEAT for the icons in here: nothing cache-busts them. Clients ask for + # /favicon.svg, /favicon.ico and /apple-touch-icon.png by bare path, so an + # immutable year is exactly how long a redrawn icon takes to reach a repeat + # visitor. Long-standing for favicon.svg; the rasters inherit it deliberately + # rather than diverge. Shorten all three together if that ever bites. + # + # The rewrite is Safari's legacy Home Screen probe, from before it honoured + # . What it asks for is byte-identical to + # apple-touch-icon.png, so it is folded onto that rather than committed twice + # — a second copy would also ride in the wasm bundle's VFS and be fetched at + # every boot for nothing. @static below matches the rewritten path. + rewrite /apple-touch-icon-precomposed.png /apple-touch-icon.png @static path /catcrafts*.wasm /runtime.js /dom-env.js /dom-webgpu.js \ /catcrafts-head.js /files.json /variants.json /styles.css \ - /favicon.svg /robots.txt /*.wgsl /*.jpg /posts.json /rates.json + /favicon.svg /favicon.ico /apple-touch-icon.png \ + /robots.txt /*.wgsl /*.jpg /posts.json /rates.json handle @static { header Cache-Control "public, max-age=31536000, immutable" file_server { diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8296b67bce2924ee1e2b45a14c575c1f10c63dff GIT binary patch literal 15086 zcmeI333OJ)7016Gd$gK-*-6+51VY$DSQ1&nk`R_4in#Q+B3i`_wTsq*6|4#fDq36+ z1&Y>POHr%~V$izPQfO6Nt55}(is$q|t9F~tZ@%1@`(EDrmY|;YoPOuL%)C2y`M-Jd z?#$dfiN(GVJ0O;q7qew)Z1y)}vEi{;th6+7f48Ntvb4&|=KEf;SVOy5titlxCM%Jk z+aeaTd?CLB^W#~I^5R*$>}Artb|GVRiL57Akeh9esO@a7IK7`)eQ}k$s2@JMv!PAv z&y8m-)OJS=DlxC$G0uGPq!W+x(kEh zA|v$iRUI5{#DEere^y`9xkItTb@2(k4cCYP#U?)|%e9TPJlGmn)gpiNxUS}JPfT{Y z_dPz@-2DAYv-`mbX6?KxcTtB%8}{Kn%F2lSSyQ{){%dgeyoY}_HJWE{9BoPp;_jjj zjW%T9MMmVo0;bXqxsEo;_N~<6p$?6DxAwW_-{Gt+^YP}wZlCJ3qZ(kk;+1(*_oJkyqc~;^F;@Kfdg=zV1CM zY(9C$06f+Gia!$B5Py&n2|rV6ud(*RKe-={C;E3_eopo|x$*4h^Wul>jl$Y?`XPI* z?&q*!Asnb-6drtVr@Wl3t!>wxwtr0YyS0VQW()^qYP-Bkz8gFB!#Wx+#!)Xz%Lu>4 zKtJT=WS^T#j&I`hX+6v%OKQvqznI|08sjZ^(YE2LYIEAu9;T!)o)Qo5!rFc@%0A=r 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 k a cfg.files.emplace_back(fs::path("sitemap.xml")); cfg.files.emplace_back(fs::path("feed.xml")); cfg.files.emplace_back(fs::path("favicon.svg")); + // Icon paths clients ask for without ever being told to. /favicon.ico is + // the root-path fallback a client uses when it cannot render the SVG above + // (Safari, historically) or never runs the JS that declares it (feed + // readers, the chat-app unfurlers, crawlers hitting the static shell); the + // apple-touch-icon.png is what Safari wants for its Home Screen and + // Favorites tile — its legacy -precomposed path is a Caddy rewrite onto + // this same file rather than a second entry here. Both were top entries in + // the 404 report until they shipped. Rasterised from favicon.svg by + // tools/make-icons.sh and committed — re-run it whenever that SVG is + // redrawn. + cfg.files.emplace_back(fs::path("favicon.ico")); + cfg.files.emplace_back(fs::path("apple-touch-icon.png")); // WGSL for the ray-traced WebGPU demo embedded in the blog (see // interfaces/Catcrafts-Demo.cppm). Fetched at runtime by WebGPUShader. cfg.files.emplace_back(fs::path("shaders/raygen.wgsl")); diff --git a/shared/interfaces/Catcrafts.Shared-Views.cppm b/shared/interfaces/Catcrafts.Shared-Views.cppm index b5d0a99..2af75dd 100644 --- a/shared/interfaces/Catcrafts.Shared-Views.cppm +++ b/shared/interfaces/Catcrafts.Shared-Views.cppm @@ -2260,6 +2260,13 @@ export std::string RenderDocument(const RenderedPage& page, "{}{}{}{}{}" "\n" "\n" + // Declaring this is what stops Safari probing the root for + // /apple-touch-icon-precomposed.png and /apple-touch-icon.png, which is + // its fallback when a page names no Home Screen icon. /favicon.ico + // deliberately gets NO : it exists purely so the by-path request + // resolves for clients that ignore the SVG, and declaring it would give + // SVG-capable browsers a reason to fetch the raster as well. + "\n" "\n" "{}" "\n\n" diff --git a/tests/ShouldStayScriptFree/main.cpp b/tests/ShouldStayScriptFree/main.cpp index 3639353..73108e7 100644 --- a/tests/ShouldStayScriptFree/main.cpp +++ b/tests/ShouldStayScriptFree/main.cpp @@ -115,6 +115,7 @@ int main(int argc, char** argv) { { const std::string body = srv.Body("/demos/raytracer"); for (std::string_view probe : { "rel=\"stylesheet\"", "rel=\"icon\"", + "rel=\"apple-touch-icon\"", "name=\"viewport\"" }) { const std::size_t n = CountOccurrences(body, probe); Check(n == 1, std::format("demo page has exactly one {}", probe), diff --git a/tools/dev.sh b/tools/dev.sh index 34ca882..6d7e059 100755 --- a/tools/dev.sh +++ b/tools/dev.sh @@ -113,7 +113,8 @@ cat > "$WORK/Caddyfile" <