* fix(branding): render tenant logos unoptimized so self-hosted sidebars work (#2203) On the official Docker image a byrå logo uploaded under Settings > Brand worked as favicon but rendered broken in the sidebar. BrandHomeLink (and BrandWordmark) sent the Supabase Storage URL through the Next.js image optimizer, whose remote-host allowlist is derived from NEXT_PUBLIC_SUPABASE_URL at BUILD time. The generic image bakes a sentinel there and docker-entrypoint.sh substitutes the real URL only at container start, so /_next/image answered 400 '"url" parameter is not allowed'. Both tenant-logo <Image> elements now pass `unoptimized`: the browser fetches the public object directly, which is exactly what the favicon already did, and CSP img-src already permits https:. The remotePatterns block in next.config.ts stays for builds that know the URL, with its comment updated to say what it still covers. Closes #2203 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VnConrmMCxJRQ5kfiPPWyy * chore: carry the DECISIONS.md line for this PR in #2247 instead (append-only log conflicts on every merge) --------- Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
'use client'
|
|
|
|
import Image from 'next/image'
|
|
import { cn } from '@/lib/utils'
|
|
import { useBranding } from '@/lib/branding/brand-context'
|
|
|
|
interface BrandWordmarkProps {
|
|
/**
|
|
* Visual size. `'hero'` is for landing/auth/onboarding hero slots (~the
|
|
* same vertical weight as the old 240px logo image). `'inline'` matches
|
|
* the old 30px image used in top-left nav contexts.
|
|
*/
|
|
size?: 'hero' | 'inline'
|
|
/**
|
|
* Force lowercase rendering. Defaults to true to match the existing
|
|
* font-display + `.toLowerCase()` pattern used elsewhere in the app.
|
|
*/
|
|
lowercase?: boolean
|
|
className?: string
|
|
}
|
|
|
|
/**
|
|
* Wordmark used in place of the legacy logo image on auth / onboarding /
|
|
* sandbox / invite surfaces. On a branded host with an uploaded logo it
|
|
* renders the logo image ALONE (founder call 2026-08-05: byrå logos usually
|
|
* carry their own name, so logo + text read as a duplicate); otherwise it
|
|
* renders exactly the text-only wordmark: the active brand's `appName` in
|
|
* Hedvig Letters Serif at weight 700 (the display font is single-weight on
|
|
* Google Fonts so 700 ends up synthetically bolded, but that matches the
|
|
* requested aesthetic).
|
|
*/
|
|
export function BrandWordmark({
|
|
size = 'hero',
|
|
lowercase = true,
|
|
className,
|
|
}: BrandWordmarkProps) {
|
|
const branding = useBranding()
|
|
const name = lowercase ? branding.appName.toLowerCase() : branding.appName
|
|
|
|
if (branding.logoUrl) {
|
|
// The logo carries the brand alone; the app name moves into alt text so
|
|
// the image keeps an accessible name.
|
|
return (
|
|
<span className={cn('inline-flex items-center', className)}>
|
|
<Image
|
|
src={branding.logoUrl}
|
|
alt={name}
|
|
width={size === 'hero' ? 214 : 88}
|
|
height={size === 'hero' ? 64 : 22}
|
|
className={cn('w-auto', size === 'hero' ? 'h-16' : 'h-[22px]')}
|
|
priority={size === 'hero'}
|
|
// Bypass the image optimizer: its remote-host allowlist is fixed at
|
|
// build time, which the runtime-configured Docker image cannot
|
|
// satisfy (issue #2203, same reasoning as BrandHomeLink).
|
|
unoptimized
|
|
/>
|
|
</span>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<span
|
|
className={cn(
|
|
'font-display tracking-tight inline-block',
|
|
size === 'hero' ? 'text-5xl md:text-6xl' : 'text-base',
|
|
className,
|
|
)}
|
|
style={{ fontWeight: 700 }}
|
|
>
|
|
{name}
|
|
</span>
|
|
)
|
|
}
|