diff --git a/docs-site/bun.lock b/docs-site/bun.lock index 5d74d78..1bdf060 100644 --- a/docs-site/bun.lock +++ b/docs-site/bun.lock @@ -9,6 +9,7 @@ "@scalar/api-reference-react": "^0.9.47", "@tanstack/react-router": "^1.121.0", "@tanstack/react-start": "^1.121.0", + "@unom/app-ui": "^0.1.0", "@unom/style": "^0.4.4", "@unom/ui": "^0.8.16", "fumadocs-core": "^16.10.5", @@ -231,6 +232,8 @@ "@headlessui/vue": ["@headlessui/vue@1.7.23", "", { "dependencies": { "@tanstack/vue-virtual": "^3.0.0-beta.60" }, "peerDependencies": { "vue": "^3.2.0" } }, "sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg=="], + "@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@13.13.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-B5HhQMIpcSH4z8IZ8HFhD59CboHceKYMpPC9kAwGyKntvPdyJJv26DLu4Z1wAjcCLyrJhf11tMhiQGom9Rxb9g=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], @@ -909,6 +912,8 @@ "@unhead/vue": ["@unhead/vue@2.1.15", "", { "dependencies": { "hookable": "^6.0.1", "unhead": "2.1.15" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-SSByXfEjhzPn8gXdEdgpYqpLMPSkLUH2HVE0GxZfOtNsJ0GgOHQs0g9T67ZZ1z0kTELLKdtOtYrzrbv9+ffF7g=="], + "@unom/app-ui": ["@unom/app-ui@0.1.0", "https://git.unom.io/api/packages/unom/npm/%40unom%2Fapp-ui/-/0.1.0/app-ui-0.1.0.tgz", { "dependencies": { "@icons-pack/react-simple-icons": "^13.13.0" }, "peerDependencies": { "@unom/style": "^0.4.4", "react": "^19.0.0" } }, "sha512-znHZOIRWyJDj4va2X/E4GwvxWZsVeWEYpvu7iHTBIa0UXjkX9aoiujJcMyfPpc2Vof53iafl9hIszgSgjQwzhg=="], + "@unom/style": ["@unom/style@0.4.4", "https://git.unom.io/api/packages/unom/npm/%40unom%2Fstyle/-/0.4.4/style-0.4.4.tgz", { "peerDependencies": { "motion": "^12" } }, "sha512-M45nihK+LGyxwy2mmHYRKggaocTt+EKNVFNaMpTvTaIUpozi7bmKIkbM2/enMYS0/UYTaZrBSZs/a0nPXqkAKw=="], "@unom/ui": ["@unom/ui@0.8.16", "https://git.unom.io/api/packages/unom/npm/%40unom%2Fui/-/0.8.16/ui-0.8.16.tgz", { "dependencies": { "@tanstack/react-router": "^1.170.11", "@tsdown/css": "^0.22.1", "clsx": "^2.1.1", "howler": "^2.2.4", "sonner": "^2.0.7", "tailwind-merge": "^3.6.0" }, "peerDependencies": { "@payloadcms/richtext-lexical": "^3.85.0", "@tanstack/react-virtual": "^3.14.2", "@unom/style": "^0.4.4", "class-variance-authority": "^0.7.1", "embla-carousel-react": "^8.6.0", "lucide-react": "^1.17.0", "motion": "^12.40.0", "radix-ui": "^1.4.3", "react": "^19.2.7", "react-dom": "^19.2.7", "typescript": "^6.0.3", "zod": "^4.4.3" } }, "sha512-ZH7VOyaRDT81VY8nm1hmx8a4CeObykP8egZbnV4Nju6kE8rQ28wdpBo0X+Zsdu8WvTEmHZGwPR53NHWJULyciw=="], diff --git a/docs-site/package.json b/docs-site/package.json index bcac882..a335269 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -14,6 +14,7 @@ "@scalar/api-reference-react": "^0.9.47", "@tanstack/react-router": "^1.121.0", "@tanstack/react-start": "^1.121.0", + "@unom/app-ui": "^0.1.0", "@unom/style": "^0.4.4", "@unom/ui": "^0.8.16", "fumadocs-core": "^16.10.5", diff --git a/docs-site/src/components/Footer.tsx b/docs-site/src/components/Footer.tsx index ddd9c75..2d5c21f 100644 --- a/docs-site/src/components/Footer.tsx +++ b/docs-site/src/components/Footer.tsx @@ -1,51 +1,27 @@ import { getRouteApi } from '@tanstack/react-router' -import type { NavigationLink, NavigationSection } from '@/lib/cms' +import { FooterView } from '@unom/app-ui/footer' const rootApi = getRouteApi('__root__') -// The docs share the marketing site's footer (same CMS global). Root-relative -// links target the website, so resolve them against its origin — the docs don't -// host /legal/* etc. themselves. Mirrors the website Footer, themed for docs. +// Footer markup is shared with the marketing site via @unom/app-ui so the two +// stay in sync. It themes itself through @unom/style tokens, which the docs map +// onto their Fumadocs surfaces. Root-relative links target the website (the +// docs don't host /legal/* etc.), so rebase them onto its origin. const SITE_URL = 'https://punktfunk.unom.io' -const resolve = (to?: string | null) => - to ? (to.startsWith('/') ? `${SITE_URL}${to}` : to) : '#' +const resolveHref = (to: string) => + to.startsWith('/') ? `${SITE_URL}${to}` : to export default function Footer() { const { footer } = rootApi.useLoaderData() - const sections: NavigationSection[] = footer?.sections ?? [] - const tagline = footer?.tagline?.trim() - - if (!sections.length && !tagline) return null return ( - + ) } diff --git a/docs-site/src/lib/cms.ts b/docs-site/src/lib/cms.ts index ead922e..c37ebcd 100644 --- a/docs-site/src/lib/cms.ts +++ b/docs-site/src/lib/cms.ts @@ -1,33 +1,21 @@ // The docs reuse the punktfunk footer from the shared unom CMS (cms.unom.io). -// The CMS is multi-tenant: footer is a per-tenant collection, so scope the read -// to this project's tenant. Read-only GET, so a plain typed fetch rather than -// pulling in the Payload SDK + generated types. +// The footer shape comes from @unom/app-ui/footer so the docs and the marketing +// site share one type. The CMS is multi-tenant: footer is a per-tenant +// collection, so scope the read to this project's tenant. Read-only GET, so a +// plain typed fetch rather than pulling in the Payload SDK + generated types. +import type { FooterData } from '@unom/app-ui/footer' + const CMS_URL = 'https://cms.unom.io' // This project's tenant in the shared CMS. const TENANT = 'punktfunk' -export interface NavigationLink { - id?: string | null - label?: string | null - to?: string | null -} +export type { FooterData as Footer } from '@unom/app-ui/footer' -export interface NavigationSection { - id?: string | null - title?: string | null - entries?: NavigationLink[] | null -} - -export interface Footer { - tagline?: string | null - sections?: NavigationSection[] | null -} - -export async function findFooter(): Promise