fix: green the work-board build — decouple logo assets from brands.ts

- brands.ts was importing .svg logos at top level; vite.config.ts imports
  brands.ts (for build-time title/CSP), and the config bundler parses SVG
  markup as JSX → 'Unexpected JSX expression'. Split the .svg imports into a
  runtime-only src/brand-logos.ts (BRAND_LOGOS keyed by BrandKey); brands.ts
  is now pure, bundler-safe config data. ui.tsx BrandMark/BrandLogo read
  BRAND_LOGOS[BRAND_KEY].
- fixture.ts: add lux+hanzo empty fixtures (no live bounty yet) to satisfy
  Record<BrandKey,...> after brands.ts grew to 4 brands.

Full build green (tsc + vite). Lands the in-flight ⌘K palette, 6-category
symmetrical grid, real brand logos, and proper accents (Zoo #6c5efb indigo,
Pars #1C3879 Persian blue, Lux #7000FF, Hanzo #ea580c — placeholder green gone).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
This commit is contained in:
zeekay
2026-07-17 21:54:22 -07:00
co-authored by Hanzo Dev
parent cf881fc763
commit 477a05f1ab
4 changed files with 38 additions and 29 deletions
+27
View File
@@ -0,0 +1,27 @@
import type { BrandKey } from './brands';
// Runtime-only brand logo assets. Kept SEPARATE from brands.ts on purpose:
// vite.config.ts imports brands.ts (for the build-time <title> + CSP RPC host),
// and the Vite config bundler can't process `.svg` imports — it parses the SVG
// `<svg>` markup as JSX and fails. So brands.ts stays pure config data (bundler-
// safe) and the .svg imports live here, imported only by the components that
// render a logo. Vite inlines these small SVGs as `data:` URIs (under the 4 KB
// limit), which the strict production CSP permits via `img-src 'self' data:`.
// Each mark is the brand's own canonical asset — never cross-brand.
import zooWordmark from './assets/brands/zoo/wordmark.svg';
import parsWordmark from './assets/brands/pars/wordmark.svg';
import parsMark from './assets/brands/pars/mark.svg';
import luxWordmark from './assets/brands/lux/wordmark.svg';
import luxMark from './assets/brands/lux/mark.svg';
import hanzoWordmark from './assets/brands/hanzo/wordmark.svg';
import hanzoMark from './assets/brands/hanzo/mark.svg';
export type BrandLogos = { logo: string; logoMark: string };
// logo = full wordmark; logoMark = square/icon mark. Zoo ships one trigram for both.
export const BRAND_LOGOS: Record<BrandKey, BrandLogos> = {
zoo: { logo: zooWordmark, logoMark: zooWordmark },
pars: { logo: parsWordmark, logoMark: parsMark },
lux: { logo: luxWordmark, logoMark: luxMark },
hanzo: { logo: hanzoWordmark, logoMark: hanzoMark },
};
+3 -26
View File
@@ -1,18 +1,7 @@
import type { Address } from 'viem';
// Brand logos — embedded at build time. Vite inlines these small SVGs as `data:`
// URIs (well under the 4 KB asset-inline limit), which the strict production CSP
// permits via `img-src 'self' data:`; a missing file fails the build. Each mark is
// the brand's own canonical asset (from its @<org>/brand package or app public/),
// never cross-brand: a Zoo build embeds only Zoo. `logo` is the full wordmark,
// `logoMark` the square/icon mark. Zoo ships one trigram that serves as both.
import zooWordmark from './assets/brands/zoo/wordmark.svg';
import parsWordmark from './assets/brands/pars/wordmark.svg';
import parsMark from './assets/brands/pars/mark.svg';
import luxWordmark from './assets/brands/lux/wordmark.svg';
import luxMark from './assets/brands/lux/mark.svg';
import hanzoWordmark from './assets/brands/hanzo/wordmark.svg';
import hanzoMark from './assets/brands/hanzo/mark.svg';
// NOTE: brand logo .svg imports live in ./brand-logos.ts, NOT here. vite.config.ts
// imports this module for the build-time title/CSP, and the config bundler can't
// parse .svg imports (it reads the SVG markup as JSX). Keep brands.ts asset-free.
// The one place a white-label lives. A brand is selected at BUILD time by the
// VITE_BRAND build-arg (see Dockerfile / docker.yml); default is 'zoo'. Every
@@ -60,10 +49,6 @@ export type Brand = {
accent: string;
// Optional secondary/highlight hex (e.g. Pars gold). Omit for single-accent brands.
accentSecondary?: string;
// Brand logos — embedded SVGs (imported → build-inlined `data:` URI string).
// `logo` is the full wordmark; `logoMark` the square/icon mark. Never cross-brand.
logo: string;
logoMark: string;
usdPerNative: number; // native → USD for the "Total paid: $X" header stat
social: { discord?: string; twitter?: string; website?: string };
tags: string[]; // About-panel chips
@@ -94,8 +79,6 @@ export const BRANDS = {
workspace: 'Zoo',
tagline: 'Open AI research network — DeAI & DeSci',
accent: '#6c5efb',
logo: zooWordmark,
logoMark: zooWordmark,
usdPerNative: 1,
social: { discord: 'https://discord.gg/zoo', twitter: 'https://twitter.com/zoo_labs', website: 'https://zoo.ngo' },
tags: ['DeAI', 'DeSci', 'Research', 'Open Source', 'AI'],
@@ -125,8 +108,6 @@ export const BRANDS = {
tagline: 'The community L1',
accent: '#1C3879',
accentSecondary: '#D4AF37',
logo: parsWordmark,
logoMark: parsMark,
usdPerNative: 1,
social: { website: 'https://pars.network' },
tags: ['Community', 'L1', 'Governance'],
@@ -156,8 +137,6 @@ export const BRANDS = {
workspace: 'Lux',
tagline: 'Quantum-safe multi-consensus L1',
accent: '#7000FF',
logo: luxWordmark,
logoMark: luxMark,
usdPerNative: 1,
social: { discord: 'https://discord.gg/lux', twitter: 'https://x.com/luxfi', website: 'https://lux.network' },
tags: ['Consensus', 'Post-Quantum', 'L1', 'DeFi', 'Open Source'],
@@ -187,8 +166,6 @@ export const BRANDS = {
workspace: 'Hanzo',
tagline: 'Frontier AI & foundational models',
accent: '#ea580c',
logo: hanzoWordmark,
logoMark: hanzoMark,
usdPerNative: 1,
social: { discord: 'https://discord.gg/hanzo', website: 'https://hanzo.ai' },
tags: ['AI', 'LLM', 'MCP', 'Agents', 'Open Source'],
+4
View File
@@ -51,6 +51,10 @@ const FIXTURES: Record<BrandKey, BountyView[]> = {
reputation: { completed: 1n, earned: 200000000000000n },
},
],
// lux + hanzo: work-market staged (not yet deployed) → no live bounty #0 to mirror.
// Empty until their contracts land; the UI shows an empty board offline, not another chain's data.
lux: [],
hanzo: [],
};
export const FIXTURE: BountyView[] = FIXTURES[BRAND_KEY];
+4 -3
View File
@@ -4,7 +4,8 @@ import { skillMeta } from './skills';
import { formatReward } from './reward';
import type { NftMeta } from './reward';
import type { Reward } from './types';
import { NATIVE_SYMBOL, ORG } from './config';
import { NATIVE_SYMBOL, ORG, BRAND_KEY } from './config';
import { BRAND_LOGOS } from './brand-logos';
// ---- Brand accent ----
export const accent: CSSProperties = { backgroundColor: 'var(--brand)' };
@@ -17,7 +18,7 @@ export const accentText: CSSProperties = { color: 'var(--brand)' };
// the logo fields defensively (optional) — ae530aa6 supplies them; until then the
// letter tile / name stand in, and the image appears the moment the field lands.
export function BrandMark({ size = 28, className = '' }: { size?: number; className?: string }) {
const mark = (ORG as { logoMark?: string }).logoMark;
const mark = BRAND_LOGOS[BRAND_KEY].logoMark;
const radius = Math.round(size * 0.28);
if (mark) {
return (
@@ -43,7 +44,7 @@ export function BrandMark({ size = 28, className = '' }: { size?: number; classN
}
export function BrandLogo({ className = '' }: { className?: string }) {
const logo = (ORG as { logo?: string }).logo;
const logo = BRAND_LOGOS[BRAND_KEY].logo;
return (
<span className={`flex min-w-0 items-center gap-2.5 ${className}`}>
{logo ? (