From aa58daf4c51ceef82c151e9baccc6cfc2f56b6a1 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Fri, 22 May 2026 10:42:56 -0700 Subject: [PATCH 1/3] Add docs manifests for configuring web app versions of our sites --- apps/docs/app/layout.tsx | 13 +++++++ apps/docs/app/manifest.ts | 74 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 apps/docs/app/manifest.ts diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 1127f35ed..716e25ab2 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -66,6 +66,19 @@ export const viewport: Viewport = { maximumScale: 1, viewportFit: 'cover', }), + // diffshub body uses --diffshub-sidebar-bg (#f7f7f7 / #101010) rather than + // the plain neutral background shared by diffs and trees, so it gets its + // own theme-color pair for the browser chrome address bar. + themeColor: + process.env.NEXT_PUBLIC_SITE === 'diffshub' + ? [ + { media: '(prefers-color-scheme: light)', color: '#f7f7f7' }, + { media: '(prefers-color-scheme: dark)', color: '#101010' }, + ] + : [ + { media: '(prefers-color-scheme: light)', color: '#ffffff' }, + { media: '(prefers-color-scheme: dark)', color: '#252525' }, + ], }; // When running in a worktree, prefix the title with a stable emoji + slug so diff --git a/apps/docs/app/manifest.ts b/apps/docs/app/manifest.ts new file mode 100644 index 000000000..2d74852f5 --- /dev/null +++ b/apps/docs/app/manifest.ts @@ -0,0 +1,74 @@ +import type { MetadataRoute } from 'next'; + +import { type ProductId, PRODUCTS } from '@/lib/product-config'; + +const SITE = (process.env.NEXT_PUBLIC_SITE ?? 'diffs') as ProductId; + +// SVG icon path is per-site; diffs uses the root favicon since it predates +// the *-brand directory convention. +const ICON_SVG_BY_SITE: Record = { + diffs: '/favicon.svg', + trees: '/trees-brand/icon.svg', + diffshub: '/diffshub-brand/icon.svg', +}; + +// diffshub behaves like a standalone app (viewport-fit cover, no browser +// chrome desired), while the diffs/trees sites are primarily documentation +// and benefit from keeping browser navigation controls visible. +const DISPLAY_BY_SITE: Record = { + diffs: 'minimal-ui', + trees: 'minimal-ui', + diffshub: 'standalone', +}; + +// Match the body background per site. diffshub uses --diffshub-sidebar-bg +// (#f7f7f7 light / #101010 dark) rather than the plain neutral palette used +// by diffs and trees. The manifest only accepts one theme_color; we use the +// light value since that pairs with the white background_color. +const THEME_COLOR_BY_SITE: Record = { + diffs: '#ffffff', + trees: '#ffffff', + diffshub: '#f7f7f7', +}; + +export default function manifest(): MetadataRoute.Manifest { + const product = PRODUCTS[SITE]; + + return { + name: `${product.name}, from Pierre`, + short_name: product.name, + description: product.description, + id: '/', + start_url: '/', + display: DISPLAY_BY_SITE[SITE], + orientation: 'any', + lang: 'en', + dir: 'ltr', + // Match the light-mode background. The dark-mode browser chrome tint is + // handled separately via themeColor in the viewport export, which supports + // media-query-based light/dark values that the manifest spec doesn't allow. + background_color: '#ffffff', + theme_color: THEME_COLOR_BY_SITE[SITE], + categories: ['developer', 'productivity'], + icons: [ + { + src: ICON_SVG_BY_SITE[SITE], + type: 'image/svg+xml', + sizes: 'any', + purpose: 'any', + }, + { + src: `/${SITE}-brand/apple-icon.png`, + type: 'image/png', + sizes: '180x180', + purpose: 'any', + }, + { + src: `/${SITE}-brand/apple-icon.png`, + type: 'image/png', + sizes: '180x180', + purpose: 'maskable', + }, + ], + }; +} From abfa88f78fc2b31f64f19423797214f898e88e6e Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Fri, 22 May 2026 17:26:39 -0700 Subject: [PATCH 2/3] same size --- apps/docs/app/manifest.ts | 11 ++++++----- apps/docs/public/diffshub-brand/apple-icon.png | Bin 832 -> 2076 bytes 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/docs/app/manifest.ts b/apps/docs/app/manifest.ts index 2d74852f5..d6cfa6759 100644 --- a/apps/docs/app/manifest.ts +++ b/apps/docs/app/manifest.ts @@ -12,9 +12,10 @@ const ICON_SVG_BY_SITE: Record = { diffshub: '/diffshub-brand/icon.svg', }; -// diffshub behaves like a standalone app (viewport-fit cover, no browser -// chrome desired), while the diffs/trees sites are primarily documentation -// and benefit from keeping browser navigation controls visible. +// Chrome needs ≥192px for icon, ≥512px for the splash-screen +const APPLE_ICON_SIZE = '640x640'; + +// diffshub behaves like a standalone app (viewport-fit cover, no browser chrome desired), while the diffs/trees sites are primarily documentation and benefit from keeping browser navigation controls visible. const DISPLAY_BY_SITE: Record = { diffs: 'minimal-ui', trees: 'minimal-ui', @@ -60,13 +61,13 @@ export default function manifest(): MetadataRoute.Manifest { { src: `/${SITE}-brand/apple-icon.png`, type: 'image/png', - sizes: '180x180', + sizes: APPLE_ICON_SIZE, purpose: 'any', }, { src: `/${SITE}-brand/apple-icon.png`, type: 'image/png', - sizes: '180x180', + sizes: APPLE_ICON_SIZE, purpose: 'maskable', }, ], diff --git a/apps/docs/public/diffshub-brand/apple-icon.png b/apps/docs/public/diffshub-brand/apple-icon.png index d0753be1fb890021d1363cd5635e98a04225d1a4..425b620f6ac3efb27678eda92e9d1677757d8e2b 100644 GIT binary patch literal 2076 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 TRab5{2g!T7`njxgN@xNA=$M5= From 79f58245fbe40fdf90120e411f401b9332b550f3 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Tue, 26 May 2026 11:30:13 -0700 Subject: [PATCH 3/3] Make SVGs color mode adaptive, streamline some assets, bulletproof some more icons, simplify manifest --- apps/docs/app/layout.tsx | 8 +++---- apps/docs/app/manifest.ts | 26 +++++++---------------- apps/docs/public/diffs-brand/icon.ico | Bin 0 -> 15406 bytes apps/docs/public/diffs-brand/icon.svg | 1 + apps/docs/public/diffshub-brand/icon.svg | 11 +++++----- apps/docs/public/favicon.png | Bin 294 -> 0 bytes apps/docs/public/favicon.svg | 1 - apps/docs/public/trees-brand/icon.svg | 5 +++-- 8 files changed, 22 insertions(+), 30 deletions(-) create mode 100644 apps/docs/public/diffs-brand/icon.ico create mode 100644 apps/docs/public/diffs-brand/icon.svg delete mode 100644 apps/docs/public/favicon.png delete mode 100644 apps/docs/public/favicon.svg diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index 716e25ab2..4c8b4d061 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -136,22 +136,22 @@ const description = SITE_PRODUCT.description; const SITE_ICONS_BY_SITE: Record = { diffs: { icon: [ - { url: '/favicon.svg', type: 'image/svg+xml' }, - { url: '/favicon.png', type: 'image/png' }, + { url: '/diffs-brand/icon.svg', type: 'image/svg+xml' }, + { url: '/diffs-brand/icon.ico', sizes: '32x32' }, ], apple: '/diffs-brand/apple-icon.png', }, trees: { icon: [ { url: '/trees-brand/icon.svg', type: 'image/svg+xml' }, - { url: '/trees-brand/icon.ico', sizes: 'any' }, + { url: '/trees-brand/icon.ico', sizes: '32x32' }, ], apple: '/trees-brand/apple-icon.png', }, diffshub: { icon: [ { url: '/diffshub-brand/icon.svg', type: 'image/svg+xml' }, - { url: '/diffshub-brand/icon.ico', sizes: 'any' }, + { url: '/diffshub-brand/icon.ico', sizes: '32x32' }, ], apple: '/diffshub-brand/apple-icon.png', }, diff --git a/apps/docs/app/manifest.ts b/apps/docs/app/manifest.ts index d6cfa6759..0a0bc7c5f 100644 --- a/apps/docs/app/manifest.ts +++ b/apps/docs/app/manifest.ts @@ -4,28 +4,21 @@ import { type ProductId, PRODUCTS } from '@/lib/product-config'; const SITE = (process.env.NEXT_PUBLIC_SITE ?? 'diffs') as ProductId; -// SVG icon path is per-site; diffs uses the root favicon since it predates -// the *-brand directory convention. -const ICON_SVG_BY_SITE: Record = { - diffs: '/favicon.svg', - trees: '/trees-brand/icon.svg', - diffshub: '/diffshub-brand/icon.svg', -}; - -// Chrome needs ≥192px for icon, ≥512px for the splash-screen +// All apple-icon.png assets are 640×640, satisfying Chrome's ≥192px install +// prompt and ≥512px splash-screen requirements. const APPLE_ICON_SIZE = '640x640'; -// diffshub behaves like a standalone app (viewport-fit cover, no browser chrome desired), while the diffs/trees sites are primarily documentation and benefit from keeping browser navigation controls visible. +// diffshub is a full standalone app (viewport-fit cover); diffs and trees are +// documentation sites that benefit from keeping browser navigation visible. const DISPLAY_BY_SITE: Record = { diffs: 'minimal-ui', trees: 'minimal-ui', diffshub: 'standalone', }; -// Match the body background per site. diffshub uses --diffshub-sidebar-bg -// (#f7f7f7 light / #101010 dark) rather than the plain neutral palette used -// by diffs and trees. The manifest only accepts one theme_color; we use the -// light value since that pairs with the white background_color. +// diffshub body uses --diffshub-sidebar-bg (#f7f7f7) rather than plain white. +// The manifest only accepts a single theme_color, so we use the light value; +// dark-mode tinting is handled via themeColor in the viewport export. const THEME_COLOR_BY_SITE: Record = { diffs: '#ffffff', trees: '#ffffff', @@ -45,15 +38,12 @@ export default function manifest(): MetadataRoute.Manifest { orientation: 'any', lang: 'en', dir: 'ltr', - // Match the light-mode background. The dark-mode browser chrome tint is - // handled separately via themeColor in the viewport export, which supports - // media-query-based light/dark values that the manifest spec doesn't allow. background_color: '#ffffff', theme_color: THEME_COLOR_BY_SITE[SITE], categories: ['developer', 'productivity'], icons: [ { - src: ICON_SVG_BY_SITE[SITE], + src: `/${SITE}-brand/icon.svg`, type: 'image/svg+xml', sizes: 'any', purpose: 'any', diff --git a/apps/docs/public/diffs-brand/icon.ico b/apps/docs/public/diffs-brand/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..89be077dfa19f43523f0c692cab57315ca0b1a97 GIT binary patch literal 15406 zcmeI2%}XRl7{*&SejEf17%!qsK(Y&Zm;^SPn{yTvbIm1v2pIITe?gp!UIk%U$RTe2 z0>j=k#6b^w5>I;(VIY@W59GcS_pc{;cU4XTTR8ySnQAc&h5H?y4R=Z@_!v zO-y*YR=giS@w~4+&#P2E+&}x&^L|lXwc2X`!t+i)^So!$k%=rm)a#NagKGRz_(?bt z{uFKm(*kAcXxkLN5}pVMSM2^4-V4?y?TRio#Kjl)Y5H+(*uZ(y!4`jTg~L(Ues*@& zY;0_pmoH!5!}|KVp)IW2vSnQE>Q`1)%I&!0axZ{NN(2L}h{`uaKmW$IqMcoAoNj{`bM_V|wQ zuz3}&H*h{XI}4D)-{xeZ{psnc85tS*sJ+F5xhDtSM|cJLOG`@z#I?2RHgzQjUGtaR zXAQt1uMfDQi%ob9;tM`ULNu@JIMaq5oQd1E@SLd#HG$`ogUrb08)02|E&L%|2!9E; zLUjD8qwSq=DEut^Am9UEc1%=EmCx1sO1KjK6QXg7-s1ybzY6$Hl3_J`EBqrAjgeO$ z-`@$uiVF4r3VD6G>Jfu}hJ|tIWWS?aiDl=4ykKskI-R~J#}IOp{OEMNsDEPGHC9HR zNBw8r6O*;c8co$59v(K8N+maDW@ZdFS$@^dvxVnGqAq@RcXv&*+02crtE<2!2Zz^+ z{d-#c>FH^6d3hP#He}wzfpJLltu~&i@6vSW!}j*Jxw*N?j2Twg+S)QhLql2d?ejcK zuT$+w?~=@Ti;Ih9XJ;n><+L$OX^VT0mVcojGIfGUBuTcm`zYebi12E0kS)$cAShd*8zLCB;8fY~BG66`yw>@4&X= zlN&BRWMlW|h4&v%pFTAwCno{s=H^`D+x>_0{%dS(%#4qZyTr!E?!QzF&Ks_NTzDszXv#)$D@NBW?ZxtUUIs$wr(I;BZ7Y4kF{+1@V zT@~uWp3rEasVguUuzJkrzOX0M1z)HLoe*D6iFGYx@oTr87{u}gK8d>uwsnE=YL}C% zJ$->g(SB5A^N_rw{e??S$Roa#qV~OV54SX_sj*kMCh;IQ;)EP)3Q2aoQV++t^-%8v z?sg2}8W#G~jc22c-|m$;2#c?qc71l+9?n*HUvxWuyYYUrK+e?$B)9Qqx6UoR_%_+IbAK{oJjI@-F%EYl9|{m#G4@d$iXt5wrz zH12`AewpW5t#-FAZGpN+{@vLp^vn3eGY)a@H|pYaA7WihOiTp7x!c@s_sVbhj{66v zH5E6$?e>9pdbiehC-x7Fi6j4JQ9I_EIWJ4_MNSswA$_*knjUqyhZ3?Bo+vGFv7{D72N%^j(Ou^O{c*k|4FT^00FLXjGa_S3p hVNW2|g}_(=lL4#OkRI3*>cXl}6&~er1iCf?{{vsRwzU8N literal 0 HcmV?d00001 diff --git a/apps/docs/public/diffs-brand/icon.svg b/apps/docs/public/diffs-brand/icon.svg new file mode 100644 index 000000000..73e6c9900 --- /dev/null +++ b/apps/docs/public/diffs-brand/icon.svg @@ -0,0 +1 @@ + diff --git a/apps/docs/public/diffshub-brand/icon.svg b/apps/docs/public/diffshub-brand/icon.svg index 2caea4dc8..cebd2961b 100644 --- a/apps/docs/public/diffshub-brand/icon.svg +++ b/apps/docs/public/diffshub-brand/icon.svg @@ -1,7 +1,8 @@ - - - - - + + + + + + diff --git a/apps/docs/public/favicon.png b/apps/docs/public/favicon.png deleted file mode 100644 index 57acd940402b9cbd1ea87871ccbfe5d622a22283..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmV+>0oneEP)kdg0002(NklXgpGo%STaAlEK=rs;(?L9EG#!XfkZ|S z^h#Z44g#J58#@JkBSOM) \ No newline at end of file diff --git a/apps/docs/public/trees-brand/icon.svg b/apps/docs/public/trees-brand/icon.svg index 5c91e09af..7eb124526 100644 --- a/apps/docs/public/trees-brand/icon.svg +++ b/apps/docs/public/trees-brand/icon.svg @@ -1,4 +1,5 @@ - - + + +