/** * The join between the demo registry and the vertical lineup. * * This is the ONLY place the marketing pages touch the registry. Home, Gallery * and Vertical all read demos through here, so when the registry's export name * or path moves, it moves in one line instead of in four pages. */ import { demos } from '@/lib/demo-kit/registry'; import type { DemoMeta, Vertical } from '@/lib/demo-kit/types'; import { VERTICALS, verticalByKey } from '@/content/verticals'; import type { VerticalEntry } from '@/content/verticals'; /** * Route shapes, written down once. The router owns the actual `` * elements; these are what every link on the marketing side builds, so if the * two ever disagree, they disagree here and not in twenty JSX attributes. */ export const routes = { home: '/', gallery: '/gallery', honesty: '/honesty', demo: (slug: string) => `/demos/${slug}`, vertical: (slug: string) => `/verticals/${slug}`, /** Gallery pre-filtered to one vertical. Deep-linkable on purpose. */ galleryFiltered: (key: Vertical) => `/gallery?vertical=${key}`, } as const; /** * The repository link belongs to the chrome, which already owns it. Re-exported * rather than repeated so the marketing pages and the footer cannot end up * pointing at two different repositories. */ export { REPO_URL } from '@/components/site/links'; /** Already sorted by `order` then slug by the registry. Never sort it again. */ export const allDemos: readonly DemoMeta[] = demos; export const liveDemos: readonly DemoMeta[] = allDemos.filter((d) => d.status === 'live'); /** * The demo the site leads with. `reference` is the hello-world vertical, and * the first live one in it is the front door; if there is none yet, any live * demo will do, and only then do we fall back to whatever the registry has. * Written as a function of the registry so adding a demo never edits Home. */ export const featuredDemo: DemoMeta | undefined = liveDemos.find((d) => d.vertical === 'reference') ?? liveDemos[0] ?? allDemos[0]; /** Every vertical key that at least one demo is filed under. Filter source. */ export const verticalKeysInUse: readonly Vertical[] = Array.from( new Set(allDemos.map((d) => d.vertical)), ); export function demosForVertical(key: Vertical | null): readonly DemoMeta[] { if (!key) return []; return allDemos.filter((d) => d.vertical === key); } export function demoBySlug(slug: string | undefined): DemoMeta | undefined { if (!slug) return undefined; return allDemos.find((d) => d.slug === slug); } /** * A demo's vertical, for a label on a card. `reference` deliberately resolves * to nothing — the word game is not an industry, and labelling it as one would * be the first small lie on a site whose whole argument is that it doesn't * tell them. */ export function verticalForDemo(demo: DemoMeta): VerticalEntry | undefined { return verticalByKey(demo.vertical); } /** The lineup, in the order we would build it. */ export const lineup: readonly VerticalEntry[] = [...VERTICALS].sort((a, b) => a.rank - b.rank);