From 25fc637816988857a1aad47e57755c4ebf0a82f7 Mon Sep 17 00:00:00 2001 From: Danilo Alonso Date: Wed, 21 Oct 2026 02:39:18 -0400 Subject: [PATCH] feat(branding): replace default logo with the ignatius mark The default is now a square symbol with cream and slate variants for dark and light mode, replacing the noorm wordmark tile. --- assets/ignatius-logo-dark.svg | 8 ++++++++ assets/ignatius-logo-light.svg | 8 ++++++++ assets/noorm-logo.svg | 6 ------ docs/design/branding.md | 8 ++++---- src/theme/branding-defaults.ts | 33 +++++++++++++++++++++------------ 5 files changed, 41 insertions(+), 22 deletions(-) create mode 100644 assets/ignatius-logo-dark.svg create mode 100644 assets/ignatius-logo-light.svg delete mode 100644 assets/noorm-logo.svg diff --git a/assets/ignatius-logo-dark.svg b/assets/ignatius-logo-dark.svg new file mode 100644 index 0000000..2f0e966 --- /dev/null +++ b/assets/ignatius-logo-dark.svg @@ -0,0 +1,8 @@ + + ignatius symbol, reversed + + + + + + diff --git a/assets/ignatius-logo-light.svg b/assets/ignatius-logo-light.svg new file mode 100644 index 0000000..a760f4a --- /dev/null +++ b/assets/ignatius-logo-light.svg @@ -0,0 +1,8 @@ + + ignatius symbol + + + + + + diff --git a/assets/noorm-logo.svg b/assets/noorm-logo.svg deleted file mode 100644 index aa844b1..0000000 --- a/assets/noorm-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/docs/design/branding.md b/docs/design/branding.md index 2c3a0ee..d385400 100644 --- a/docs/design/branding.md +++ b/docs/design/branding.md @@ -13,7 +13,7 @@ The three output surfaces — interactive viewer, static dict HTML, static graph - One config file at the models root drives branding for every surface. - Logo + title + subtitle pinned upper-left, immune to pan/zoom. - Footer at bottom-center, fixed, displaying user copyright + (by default) a "powered by Noorm" link that the user can disable. -- Sensible default: when the user supplies no branding, the page brands as Noorm Ignatius with the Noorm logo. Default case works offline. +- Sensible default: when the user supplies no branding, the page brands as Noorm Ignatius with the ignatius logo. Default case works offline. - Static outputs are self-contained: logos inlined as base64 at generation time. - Theme-aware: logo can have separate dark/light variants, with a string shorthand for the same-asset case. @@ -52,7 +52,7 @@ Evidence: the parser already implements the "optional `_*.yaml` at models root" # Logo: string shorthand OR explicit per-mode object. # Each value can be a URL or a filepath relative to the models directory. -# Falls back to the embedded Noorm logo when absent. +# Falls back to the embedded ignatius logo when absent. logo: "./assets/logo.svg" # shorthand: same asset for both modes # OR logo: @@ -73,7 +73,7 @@ poweredBy: true # default true; set false to omit the Noorm ## Defaults (when the file is absent OR a field is unset) ```yaml -logo: +logo: title: "Noorm Ignatius" subtitle: "Visualize your data model" copyright: @@ -136,7 +136,7 @@ Each value can be: - Static outputs: read from disk, base64-inlined. 3. **Unset / null**: - - Falls back to the embedded Noorm SVG. No network access for this case. + - Falls back to the embedded ignatius SVG for the same mode. No network access for this case. The Noorm SVG is committed to the repo and imported at compile time via Bun's `with { type: "file" }` attribute, the same mechanism that embeds the React bundle. It is NOT fetched at runtime. diff --git a/src/theme/branding-defaults.ts b/src/theme/branding-defaults.ts index 75903c5..462d6db 100644 --- a/src/theme/branding-defaults.ts +++ b/src/theme/branding-defaults.ts @@ -1,6 +1,7 @@ import { isAbsolute, resolve as resolvePath } from 'path'; -import noormLogoPath from '../../assets/noorm-logo.svg' with { type: 'file' }; +import logoDarkPath from '../../assets/ignatius-logo-dark.svg' with { type: 'file' }; +import logoLightPath from '../../assets/ignatius-logo-light.svg' with { type: 'file' }; export type LogoPair = { dark: string; @@ -20,14 +21,22 @@ export type Branding = { poweredBy: boolean; }; -// Read the embedded SVG at module load and build a data URI. +// Read the embedded SVGs at module load and build data URIs. // WHY: `with { type: "file" }` embeds the file into the compiled binary at // $bunfs/ — Bun.file().arrayBuffer() works both in dev and in the binary. -const noormSvgBytes = new Uint8Array(await Bun.file(noormLogoPath).arrayBuffer()); -const NOORM_DEFAULT_LOGO = `data:image/svg+xml;base64,${Buffer.from(noormSvgBytes).toString('base64')}`; +async function embedSvg(path: string): Promise { + const bytes = new Uint8Array(await Bun.file(path).arrayBuffer()); + return `data:image/svg+xml;base64,${Buffer.from(bytes).toString('base64')}`; +} + +// The two files differ only in lettering colour: cream on dark, slate on light. +const DEFAULT_LOGO: LogoPair = { + dark: await embedSvg(logoDarkPath), + light: await embedSvg(logoLightPath), +}; export const defaultBranding: Branding = { - logo: { dark: NOORM_DEFAULT_LOGO, light: NOORM_DEFAULT_LOGO }, + logo: DEFAULT_LOGO, title: 'Noorm Ignatius', subtitle: 'Visualize your data model', get copyright(): CopyrightConfig { @@ -51,8 +60,8 @@ function normalizeLogo(input: RawLogoInput): LogoPair { if (typeof input === 'string') { return { dark: input, light: input }; } - const dark = input.dark ?? input.light ?? NOORM_DEFAULT_LOGO; - const light = input.light ?? input.dark ?? NOORM_DEFAULT_LOGO; + const dark = input.dark ?? input.light ?? DEFAULT_LOGO.dark; + const light = input.light ?? input.dark ?? DEFAULT_LOGO.light; return { dark, light }; } @@ -96,7 +105,7 @@ const LOGO_MIME_BY_EXT: Record = { * an exported file is worse than the noorm mark, and parse has no findings * channel at the point branding is merged. */ -async function inlineLogo(value: string, modelRoot: string): Promise { +async function inlineLogo(value: string, modelRoot: string, fallback: string): Promise { if (value.startsWith('data:') || value.startsWith('http://') || value.startsWith('https://')) { return value; } @@ -104,11 +113,11 @@ async function inlineLogo(value: string, modelRoot: string): Promise { const dot = value.lastIndexOf('.'); const mime = dot === -1 ? undefined : LOGO_MIME_BY_EXT[value.slice(dot).toLowerCase()]; - if (mime === undefined) return NOORM_DEFAULT_LOGO; + if (mime === undefined) return fallback; const file = Bun.file(isAbsolute(value) ? value : resolvePath(modelRoot, value)); - if (!(await file.exists())) return NOORM_DEFAULT_LOGO; + if (!(await file.exists())) return fallback; const bytes = new Uint8Array(await file.arrayBuffer()); @@ -126,8 +135,8 @@ async function inlineLogo(value: string, modelRoot: string): Promise { */ export async function inlineBrandingLogos(branding: Branding, modelRoot: string): Promise { const [dark, light] = await Promise.all([ - inlineLogo(branding.logo.dark, modelRoot), - inlineLogo(branding.logo.light, modelRoot), + inlineLogo(branding.logo.dark, modelRoot, DEFAULT_LOGO.dark), + inlineLogo(branding.logo.light, modelRoot, DEFAULT_LOGO.light), ]); return { ...branding, logo: { dark, light } };