Show known-provider logos beside foreign publication hostnames
Foreign `site.standard.publication` records (written by Leaflet, pckt,
Offprint, …) now display the originating service's logo next to their
hostname — on the dashboard "From other apps" list and the public
profile "Elsewhere" section, which also gains the hostname it didn't
show before.
The hostname alone can't identify the service, because the paid tiers
serve from a custom domain. So detection is two-step: an app-specific
`$type` discriminator embedded in the record first (pckt writes
`theme.$type === "blog.pckt.theme"`, which survives a custom domain),
then a dot-boundary hostname-suffix fallback (`*.leaflet.pub`,
`*.pckt.blog`, `*.offprint.app`). Leaflet records carry no such
discriminator and Offprint is unsampled, so those two on a custom
domain stay logo-less, exactly as before — see Decision 0017.
Detection and the monochrome glyph data live in one framework-agnostic
module (`lib/publish/providers.ts`), since Astro can't server-render a
React component and the read path takes no client island: the React
dashboard and the Astro profile each render the shared data through a
tiny `ProviderLogo` of their own. Leaflet's saved asset was a raster
PNG, so its glyph is a substitute vector feather (Lucide, ISC).
Show known-provider logos beside foreign publication hostnames
Foreign `site.standard.publication` records (written by Leaflet, pckt,
Offprint, …) now display the originating service's logo next to their
hostname — on the dashboard "From other apps" list and the public
profile "Elsewhere" section, which also gains the hostname it didn't
show before.
The hostname alone can't identify the service, because the paid tiers
serve from a custom domain. So detection is two-step: an app-specific
`$type` discriminator embedded in the record first (pckt writes
`theme.$type === "blog.pckt.theme"`, which survives a custom domain),
then a dot-boundary hostname-suffix fallback (`*.leaflet.pub`,
`*.pckt.blog`, `*.offprint.app`). Leaflet records carry no such
discriminator and Offprint is unsampled, so those two on a custom
domain stay logo-less, exactly as before — see Decision 0017.
Detection and the monochrome glyph data live in one framework-agnostic
module (`lib/publish/providers.ts`), since Astro can't server-render a
React component and the read path takes no client island: the React
dashboard and the Astro profile each render the shared data through a
tiny `ProviderLogo` of their own. Leaflet's saved asset was a raster
PNG, so its glyph is a substitute vector feather (Lucide, ISC).
Show known-provider logos beside foreign publication hostnames
Foreign `site.standard.publication` records (written by Leaflet, pckt,
Offprint, …) now display the originating service's logo next to their
hostname — on the dashboard "From other apps" list and the public
profile "Elsewhere" section, which also gains the hostname it didn't
show before.
The hostname alone can't identify the service, because the paid tiers
serve from a custom domain. So detection is two-step: an app-specific
`$type` discriminator embedded in the record first (pckt writes
`theme.$type === "blog.pckt.theme"`, which survives a custom domain),
then a dot-boundary hostname-suffix fallback (`*.leaflet.pub`,
`*.pckt.blog`, `*.offprint.app`). Leaflet records carry no such
discriminator and Offprint is unsampled, so those two on a custom
domain stay logo-less, exactly as before — see Decision 0017.
Detection and the monochrome glyph data live in one framework-agnostic
module (`lib/publish/providers.ts`), since Astro can't server-render a
React component and the read path takes no client island: the React
dashboard and the Astro profile each render the shared data through a
tiny `ProviderLogo` of their own. Leaflet's saved asset was a raster
PNG, so its glyph is a substitute vector feather (Lucide, ISC).
Fix publication theme save: render on public pages, unstick Save button
Two bugs surfaced when editing a publication's theme from the dashboard
Settings tab (the putRecord 401/200 in the network tab is normal DPoP
nonce negotiation, not a failure — the write succeeds).
Theme never rendered on public pages: themeStyleBlock injected a bare
:root override, but global.css defines the light and dark design tokens
on :root and @media (prefers-color-scheme: dark) :root — equal
specificity (0,1,0). Astro links global.css into the head after the
injected <style>, so source order let the defaults win in both colour
schemes and the theme silently vanished. Target :root:root (0,2,0) so
the override outranks both defaults regardless of load order.
Save button stuck on 'Saving…': onSubmit only cleared the saving flag on
error, relying on unmount otherwise. The create flow unmounts the form,
but the Settings tab keeps the same instance mounted across a save, so
the flag stayed true forever despite a 200. Reset it on the success path.
Both fixes covered by regression tests.