@nebutra/brand 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,26 @@ declare function getBrandOrigin(service: BrandService): string;
30
30
  * Public object on `nebutra-assets` via `cdn.nebutra.com`.
31
31
  * R2_PUBLIC_URL / NEXT_PUBLIC_R2_PUBLIC_URL may override the origin for dogfood.
32
32
  */
33
+ /**
34
+ * Where a product's documentation lives: a path on that product's own host.
35
+ *
36
+ * There is no `docs` entry in `brand.domains` — deliberately. A single docs
37
+ * subdomain meant a product's own host was never where its documentation
38
+ * lived: pebble's docs sat at docs.<domain>/pebble/* while
39
+ * pebble.<domain>/docs/* redirected to them. One value cannot express "each
40
+ * product serves its own", so the key is gone and the path is derived.
41
+ *
42
+ * Defaults to the marketing site, which carries the product docs. Pass a
43
+ * service to get that product's: getDocsUrl("pebble") → https://pebble.…/docs.
44
+ */
45
+ declare function getDocsUrl(service?: BrandService, path?: string): string;
46
+ /**
47
+ * The origin publicAssetUrl() resolves against — for CSP directives. A page
48
+ * that loads public assets (images, the MiSans subsets) must allow exactly
49
+ * this origin, or the browser blocks the request with no error on the page:
50
+ * font-src 'self' blocked every MiSans face on nebutra.com until 2026-09-26.
51
+ */
52
+ declare function publicAssetOrigin(base?: string): string;
33
53
  declare function publicAssetUrl(key: string, base?: string): string;
34
54
  /**
35
55
  * Bare query flag that keeps a signed-in visitor on the marketing homepage
@@ -159,4 +179,4 @@ declare function buildPwaManifest(): MetadataRoute.Manifest;
159
179
  */
160
180
  declare function buildFeedChannelMeta(): FeedChannelMeta;
161
181
 
162
- export { type BrandService, type FeedChannelMeta, MARKETING_HOME_PARAM, type OrganizationJsonLd, type SiteMetadataOptions, type SoftwareApplicationJsonLd, type WebSiteJsonLd, buildFeedChannelMeta, buildOrganizationJsonLd, buildPwaManifest, buildSoftwareApplicationJsonLd, buildWebSiteJsonLd, getBrandCookieDomain, getBrandEmail, getBrandMailFrom, getBrandOrigin, getBrandPublicUrls, getMarketingHomePath, getMarketingHomeUrl, getSiteMetadata, getSiteUrl, publicAssetUrl };
182
+ export { type BrandService, type FeedChannelMeta, MARKETING_HOME_PARAM, type OrganizationJsonLd, type SiteMetadataOptions, type SoftwareApplicationJsonLd, type WebSiteJsonLd, buildFeedChannelMeta, buildOrganizationJsonLd, buildPwaManifest, buildSoftwareApplicationJsonLd, buildWebSiteJsonLd, getBrandCookieDomain, getBrandEmail, getBrandMailFrom, getBrandOrigin, getBrandPublicUrls, getDocsUrl, getMarketingHomePath, getMarketingHomeUrl, getSiteMetadata, getSiteUrl, publicAssetOrigin, publicAssetUrl };
@@ -20,7 +20,6 @@ var brand = {
20
20
  api: "api.nebutra.com",
21
21
  auth: "auth.nebutra.com",
22
22
  sso: "sso.nebutra.com",
23
- docs: "docs.nebutra.com",
24
23
  studio: "studio.nebutra.com",
25
24
  cdn: "cdn.nebutra.com",
26
25
  router: "router.nebutra.com",
@@ -71,17 +70,17 @@ var colors = {
71
70
  },
72
71
  neutral: {
73
72
  "0": "#ffffff",
74
- "50": "#f8fafc",
75
- "100": "#f1f5f9",
76
- "200": "#e2e8f0",
77
- "300": "#cbd5e1",
78
- "400": "#94a3b8",
79
- "500": "#64748b",
80
- "600": "#475569",
81
- "700": "#334155",
82
- "800": "#1e293b",
83
- "900": "#0f172a",
84
- "950": "#020617"
73
+ "50": "#f9fafb",
74
+ "100": "#f3f4f6",
75
+ "200": "#e4e5e8",
76
+ "300": "#d2d4d8",
77
+ "400": "#9fa1a5",
78
+ "500": "#717377",
79
+ "600": "#515357",
80
+ "700": "#3f4044",
81
+ "800": "#26272a",
82
+ "900": "#18191c",
83
+ "950": "#0b0c0d"
85
84
  },
86
85
  white: "#FFFFFF",
87
86
  black: "#000000",
@@ -109,6 +108,13 @@ function getBrandOrigin(service) {
109
108
  }
110
109
  return `https://${host.replace(/^https?:\/\//, "").replace(/\/+$/, "")}`;
111
110
  }
111
+ function getDocsUrl(service = "landing", path = "") {
112
+ const suffix = path.replace(/^\/+/, "");
113
+ return `${getBrandOrigin(service)}/docs${suffix ? `/${suffix}` : ""}`;
114
+ }
115
+ function publicAssetOrigin(base) {
116
+ return new URL(publicAssetUrl("x", base)).origin;
117
+ }
112
118
  function publicAssetUrl(key, base) {
113
119
  const origin = (base || process.env.NEXT_PUBLIC_R2_PUBLIC_URL || process.env.R2_PUBLIC_URL || getBrandOrigin("cdn")).replace(/\/+$/, "");
114
120
  const path = key.replace(/^\/+/, "");
@@ -149,7 +155,7 @@ function getBrandPublicUrls() {
149
155
  apiUrl: getBrandOrigin("api"),
150
156
  authUrl: getBrandOrigin("auth"),
151
157
  ssoUrl: getBrandOrigin("sso"),
152
- docsUrl: getBrandOrigin("docs"),
158
+ docsUrl: getDocsUrl(),
153
159
  routerUrl: getBrandOrigin("router"),
154
160
  forgeUrl: getBrandOrigin("forge"),
155
161
  designUrl: getBrandOrigin("design"),
@@ -320,10 +326,12 @@ export {
320
326
  getBrandMailFrom,
321
327
  getBrandOrigin,
322
328
  getBrandPublicUrls,
329
+ getDocsUrl,
323
330
  getMarketingHomePath,
324
331
  getMarketingHomeUrl,
325
332
  getSiteMetadata,
326
333
  getSiteUrl,
334
+ publicAssetOrigin,
327
335
  publicAssetUrl
328
336
  };
329
337
  //# sourceMappingURL=metadata-helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/metadata.ts","../src/metadata-helpers.ts"],"sourcesContent":["/**\n * Nebutra Brand Metadata\n * Central source of truth for brand identity\n *\n * AUTO-GENERATED by `pnpm brand:apply` from `scripts/brand-types.ts`\n * (DEFAULT_BRAND) or a sibling `brand.config.ts` if present. Do NOT\n * edit by hand — your edits will be wiped on the next brand:apply.\n *\n * To rebrand, edit DEFAULT_BRAND in scripts/brand-types.ts and run\n * `pnpm brand:apply`. To extend the shape, add a field to BrandConfig,\n * set its value in DEFAULT_BRAND, then read it from this writer.\n *\n * Pipeline guard: tests/architecture/brand-metadata-drift.test.ts\n * asserts `metadata.ts` is byte-identical to what brand:apply emits.\n */\n\nexport const brand = {\n name: \"Nebutra\",\n nameCn: \"云毓智能\",\n nameFull: \"无锡云毓智能科技有限公司\",\n nameFullEn: \"Wuxi Nebutra Intelligence Technology Co., Ltd.\",\n\n tagline: \"Ship AI products, not boilerplate.\",\n taglineCn: \"AI原生·快速出海·即刻交付\",\n description:\n \"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure — pre-configured.\",\n descriptionCn:\n \"面向AI创业者的一体化SaaS基础设施模板,覆盖认证、计费、多租户、AI服务与设计系统,开箱即产品\",\n\n story: {\n concept:\n \"Logo以首字母N的基础造型概念为主要设计框架,通过几何正负空间构建隐形'N',形成近似六边形的稳定结构\",\n colorMeaning: \"蓝绿渐变体现未来感与科技锋芒,'云'代表云端平台,'毓'寓意孕育与转化\",\n values: [\"AI Native\", \"Ship Fast\", \"Open by Default\", \"Global-Ready\", \"Enterprise-Grade\"],\n missionStatement:\n \"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build.\",\n },\n\n domains: {\n landing: \"nebutra.com\",\n app: \"app.nebutra.com\",\n api: \"api.nebutra.com\",\n auth: \"auth.nebutra.com\",\n sso: \"sso.nebutra.com\",\n docs: \"docs.nebutra.com\",\n studio: \"studio.nebutra.com\",\n cdn: \"cdn.nebutra.com\",\n router: \"router.nebutra.com\",\n forge: \"forge.nebutra.com\",\n design: \"design.nebutra.com\",\n status: \"status.nebutra.com\",\n open: \"open.nebutra.com\",\n admin: \"admin.nebutra.com\",\n analytics: \"analytics.nebutra.com\",\n pebble: \"pebble.nebutra.com\",\n carina: \"carina.nebutra.com\",\n kuanlan: \"kuanlan.nebutra.com\",\n origin: \"origin.nebutra.com\",\n },\n\n social: {\n twitter: \"https://twitter.com/nebutra\",\n github: \"https://github.com/nebutra\",\n discord: \"https://discord.gg/nebutra\",\n linkedin: \"https://linkedin.com/company/nebutra\",\n },\n} as const;\n\n/**\n * Brand Colors — full VI palette + brand gradients + semantic colors.\n * Aligned with @nebutra/design-tokens DTCG SSOT (tokens/core.json).\n */\nexport const colors = {\n primary: {\n \"50\": \"#f0f4ff\",\n \"100\": \"#dbe4ff\",\n \"200\": \"#bac8ff\",\n \"300\": \"#91a7ff\",\n \"400\": \"#5c7cfa\",\n \"500\": \"#0033FE\",\n \"600\": \"#002ad4\",\n \"700\": \"#0021ab\",\n \"800\": \"#001882\",\n \"900\": \"#000f59\",\n \"950\": \"#000830\",\n },\n accent: {\n \"50\": \"#e6fff8\",\n \"100\": \"#b3ffec\",\n \"200\": \"#80ffe0\",\n \"300\": \"#4dfcd4\",\n \"400\": \"#1af7c8\",\n \"500\": \"#0BF1C3\",\n \"600\": \"#09c9a3\",\n \"700\": \"#07a183\",\n \"800\": \"#057963\",\n \"900\": \"#035143\",\n \"950\": \"#012923\",\n },\n neutral: {\n \"0\": \"#ffffff\",\n \"50\": \"#f8fafc\",\n \"100\": \"#f1f5f9\",\n \"200\": \"#e2e8f0\",\n \"300\": \"#cbd5e1\",\n \"400\": \"#94a3b8\",\n \"500\": \"#64748b\",\n \"600\": \"#475569\",\n \"700\": \"#334155\",\n \"800\": \"#1e293b\",\n \"900\": \"#0f172a\",\n \"950\": \"#020617\",\n },\n white: \"#FFFFFF\",\n black: \"#000000\",\n gradient: {\n primary: \"linear-gradient(135deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryReverse: \"linear-gradient(135deg, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n primaryVertical: \"linear-gradient(180deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryRadial: \"radial-gradient(circle, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n },\n p3Overrides: {\n primary500: \"color(display-p3 0.03 0.19 0.99)\",\n accent500: \"color(display-p3 0.07 0.94 0.79)\",\n },\n success: \"#22c55e\",\n warning: \"#f59e0b\",\n error: \"#ef4444\",\n info: \"#0033FE\",\n} as const;\n\n/**\n * Typography — \"Precision Stack\" (Geist + Noto Sans SC + Geist Mono).\n */\nexport const typography = {\n fontFamily: {\n en: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif',\n cn: '\"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif',\n sans: '\"Geist\", \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace',\n display: '\"Geist\", \"Noto Sans SC\", sans-serif',\n heading: '\"Geist\", \"Noto Sans SC\", sans-serif',\n brandPrint: '\"Noto Sans SC\", \"PingFang SC\", sans-serif',\n },\n cssVars: {\n sans: \"var(--font-geist-sans)\",\n mono: \"var(--font-geist-mono)\",\n cnSans: \"var(--font-noto-sc)\",\n },\n fontWeight: {\n thin: 100,\n extraLight: 200,\n light: 300,\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extraBold: 800,\n black: 900,\n },\n letterSpacing: {\n display: \"-0.04em\",\n heading: \"-0.03em\",\n subheading: \"-0.02em\",\n body: \"0em\",\n caption: \"0.01em\",\n mono: \"0em\",\n },\n lineHeight: {\n none: 1,\n tight: 1.2,\n snug: 1.4,\n normal: 1.6,\n relaxed: 1.75,\n loose: 2,\n },\n} as const;\n\n/**\n * Logo asset paths (relative to package), dual-edition structure:\n * - classic (v1.0): preferred for UX / product surfaces\n * - compliant (v2.0): required for commercial / legal contexts\n */\nexport const logoAssets = {\n classic: {\n default: \"assets/logo/logo-color.svg\",\n color: \"assets/logo/logo-color.svg\",\n inverse: \"assets/logo/logo-inverse.svg\",\n mono: \"assets/logo/logo-mono.svg\",\n en: \"assets/logo/logo-en.svg\",\n zh: \"assets/logo/logo-zh.svg\",\n zhEn: \"assets/logo/logo-zh-en.svg\",\n horizontalEn: \"assets/logo/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo/logo-vertical-en.svg\",\n verticalZh: \"assets/logo/logo-vertical-zh.svg\",\n },\n compliant: {\n default: \"assets/logo-compliant/logo-color.svg\",\n color: \"assets/logo-compliant/logo-color.svg\",\n inverse: \"assets/logo-compliant/logo-inverse.svg\",\n mono: \"assets/logo-compliant/logo-mono.svg\",\n en: \"assets/logo-compliant/logo-en.svg\",\n zh: \"assets/logo-compliant/logo-zh.svg\",\n zhEn: \"assets/logo-compliant/logo-zh-en.svg\",\n horizontalEn: \"assets/logo-compliant/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo-compliant/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo-compliant/logo-vertical-en.svg\",\n verticalZh: \"assets/logo-compliant/logo-vertical-zh.svg\",\n horizontalEnMono: \"assets/logo-compliant/logo-horizontal-en-mono.svg\",\n horizontalZhMono: \"assets/logo-compliant/logo-horizontal-zh-mono.svg\",\n verticalEnMono: \"assets/logo-compliant/logo-vertical-en-mono.svg\",\n verticalZhMono: \"assets/logo-compliant/logo-vertical-zh-mono.svg\",\n },\n} as const;\n\n/**\n * Font asset paths (relative to package).\n */\nexport const fontAssets = {\n poppins: {\n thin: \"assets/fonts/poppins/Poppins-Thin.otf\",\n thinItalic: \"assets/fonts/poppins/Poppins-ThinItalic.otf\",\n extraLight: \"assets/fonts/poppins/Poppins-ExtraLight.otf\",\n extraLightItalic: \"assets/fonts/poppins/Poppins-ExtraLightItalic.otf\",\n light: \"assets/fonts/poppins/Poppins-Light.otf\",\n lightItalic: \"assets/fonts/poppins/Poppins-LightItalic.otf\",\n regular: \"assets/fonts/poppins/Poppins-Regular.otf\",\n italic: \"assets/fonts/poppins/Poppins-Italic.otf\",\n medium: \"assets/fonts/poppins/Poppins-Medium.otf\",\n mediumItalic: \"assets/fonts/poppins/Poppins-MediumItalic.otf\",\n semiBold: \"assets/fonts/poppins/Poppins-SemiBold.otf\",\n semiBoldItalic: \"assets/fonts/poppins/Poppins-SemiBoldItalic.otf\",\n bold: \"assets/fonts/poppins/Poppins-Bold.otf\",\n boldItalic: \"assets/fonts/poppins/Poppins-BoldItalic.otf\",\n extraBold: \"assets/fonts/poppins/Poppins-ExtraBold.otf\",\n extraBoldItalic: \"assets/fonts/poppins/Poppins-ExtraBoldItalic.otf\",\n black: \"assets/fonts/poppins/Poppins-Black.otf\",\n blackItalic: \"assets/fonts/poppins/Poppins-BlackItalic.otf\",\n },\n} as const;\n\n/**\n * Favicon assets.\n */\nexport const faviconAssets = {\n ico: \"assets/favicon/favicon.ico\",\n svg: \"assets/favicon/favicon.svg\",\n apple: \"assets/favicon/apple-touch-icon.png\",\n android192: \"assets/favicon/android-chrome-192x192.png\",\n android512: \"assets/favicon/android-chrome-512x512.png\",\n} as const;\n\n/**\n * OG Image dimensions.\n */\nexport const ogImageDimensions = {\n default: {\n width: 1200,\n height: 630,\n },\n twitter: {\n width: 1200,\n height: 600,\n },\n square: {\n width: 1200,\n height: 1200,\n },\n} as const;\n\nexport type BrandColors = typeof colors;\nexport type BrandTypography = typeof typography;\nexport type LogoAssets = typeof logoAssets;\n","/**\n * @nebutra/brand — metadata-helpers\n *\n * Pure functions deriving Next.js Metadata, JSON-LD objects, PWA manifest,\n * and feed channel headers from the single `brand` + `colors` SSOT.\n *\n * Zero hardcoded brand-name literals — every string is derived from `brand`\n * or `colors`. The arch drift guard `tests/architecture/brand-metadata-drift.test.ts`\n * asserts this invariant at CI time.\n *\n * NOTE: `next` is a TYPE-ONLY import here. The package declares `next` as an\n * optional peer dep + devDep (catalog:). skipLibCheck:true is set in tsconfig.json.\n * Adding `import \"server-only\"` is intentionally omitted: this file is consumed\n * by both server and client render paths in tests; the CALLER must enforce\n * server-only boundaries if needed.\n *\n * NOTE: getSiteUrl(service) in this module is DISTINCT from the zero-param\n * getSiteUrl() in apps/landing/src/lib/seo/site-routes.ts — different\n * modules, no collision. The local one remains the landing URL util.\n */\n\nimport type { Metadata, MetadataRoute } from \"next\";\nimport { brand, colors } from \"./metadata\";\n\n/** Services that have a canonical domain in brand.domains */\nexport type BrandService = keyof typeof brand.domains;\n\n/** Absolute origin from brand.domains only (no env hijack). */\nexport function getBrandOrigin(service: BrandService): string {\n const host = brand.domains[service];\n if (!host?.trim()) {\n throw new Error(`brand.domains.${String(service)} is empty — run brand:apply`);\n }\n return `https://${host.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\")}`;\n}\n\n/**\n * Public object on `nebutra-assets` via `cdn.nebutra.com`.\n * R2_PUBLIC_URL / NEXT_PUBLIC_R2_PUBLIC_URL may override the origin for dogfood.\n */\nexport function publicAssetUrl(key: string, base?: string): string {\n const origin = (\n base ||\n process.env.NEXT_PUBLIC_R2_PUBLIC_URL ||\n process.env.R2_PUBLIC_URL ||\n getBrandOrigin(\"cdn\")\n ).replace(/\\/+$/, \"\");\n const path = key.replace(/^\\/+/, \"\");\n if (!path || path.includes(\"..\") || path.includes(\"\\\\\")) {\n throw new Error(\"invalid_public_asset_key\");\n }\n return `${origin}/${path}`;\n}\n\n/**\n * Bare query flag that keeps a signed-in visitor on the marketing homepage\n * (`?home`, no value). Apex `/` without it is a product launcher.\n */\nexport const MARKETING_HOME_PARAM = \"home\";\n\n/**\n * Marketing homepage path. Always includes the `?home` skip — that is the\n * only signal landing honours. `getBrandOrigin(\"landing\")` is the launcher.\n *\n * `defaultLocale` is the landing path locale that lives on `/` (today `en`).\n * Pass it from `@nebutra/i18n` so brand does not import the i18n package.\n */\nexport function getMarketingHomePath(options?: {\n locale?: string;\n defaultLocale?: string;\n}): string {\n const defaultLocale = options?.defaultLocale?.trim() || \"en\";\n const locale = options?.locale?.trim();\n const path = locale && locale !== defaultLocale ? `/${locale}` : \"/\";\n return path === \"/\" ? `/?${MARKETING_HOME_PARAM}` : `${path}?${MARKETING_HOME_PARAM}`;\n}\n\n/** Absolute marketing homepage. Same contract as `getMarketingHomePath`. */\nexport function getMarketingHomeUrl(options?: { locale?: string; defaultLocale?: string }): string {\n const origin = getBrandOrigin(\"landing\");\n return new URL(getMarketingHomePath(options), `${origin}/`).toString();\n}\n\n/** Cookie parent domain from landing apex (e.g. `.example.com`). */\nexport function getBrandCookieDomain(): string {\n const apex = brand.domains.landing.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\");\n if (!apex) return \"\";\n return apex.startsWith(\".\") ? apex : `.${apex}`;\n}\n\n/**\n * Canonical base URL. NEXT_PUBLIC_SITE_URL only overrides landing.\n */\nexport function getSiteUrl(service: BrandService = \"landing\"): string {\n if (service === \"landing\") {\n const envUrl = process.env[\"NEXT_PUBLIC_SITE_URL\"];\n if (envUrl && envUrl.trim() !== \"\") {\n return envUrl.trim().replace(/\\/+$/, \"\");\n }\n }\n return getBrandOrigin(service);\n}\n\n/** Production multi-app public URL map (dogfood brand.domains). */\nexport function getBrandPublicUrls() {\n return {\n siteUrl: getBrandOrigin(\"landing\"),\n appUrl: getBrandOrigin(\"app\"),\n apiUrl: getBrandOrigin(\"api\"),\n authUrl: getBrandOrigin(\"auth\"),\n ssoUrl: getBrandOrigin(\"sso\"),\n docsUrl: getBrandOrigin(\"docs\"),\n routerUrl: getBrandOrigin(\"router\"),\n forgeUrl: getBrandOrigin(\"forge\"),\n designUrl: getBrandOrigin(\"design\"),\n statusUrl: getBrandOrigin(\"status\"),\n openUrl: getBrandOrigin(\"open\"),\n studioUrl: getBrandOrigin(\"studio\"),\n cdnUrl: getBrandOrigin(\"cdn\"),\n analyticsUrl: getBrandOrigin(\"analytics\"),\n pebbleUrl: getBrandOrigin(\"pebble\"),\n carinaUrl: getBrandOrigin(\"carina\"),\n cookieDomain: getBrandCookieDomain(),\n } as const;\n}\n\n/**\n * Product mailbox derived from landing apex — rebrand-safe.\n * e.g. getBrandEmail(\"contact\") → contact@example.com\n */\nexport function getBrandEmail(localPart: string): string {\n const apex = brand.domains.landing\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/+$/, \"\")\n .replace(/^www\\./, \"\");\n if (!apex) {\n throw new Error(\"brand.domains.landing is empty — run brand:apply\");\n }\n const local = localPart.trim().replace(/@.*$/, \"\");\n if (!local) {\n throw new Error(\"getBrandEmail requires a non-empty local part\");\n }\n return `${local}@${apex}`;\n}\n\n/** Default transactional From header: `Brand <noreply@apex>`. */\nexport function getBrandMailFrom(): string {\n return `${brand.name} <${getBrandEmail(\"noreply\")}>`;\n}\n\n// ─── Metadata ────────────────────────────────────────────────────────────────\n\nexport interface SiteMetadataOptions {\n readonly service: BrandService;\n readonly title?: string;\n readonly description?: string;\n}\n\n/**\n * Returns a partial Next.js Metadata object suitable for spreading into a\n * layout.tsx export const metadata. Callers may override any field.\n */\nexport function getSiteMetadata(opts: SiteMetadataOptions): Metadata {\n const siteUrl = getSiteUrl(opts.service);\n const title = opts.title ?? brand.name;\n const description = opts.description ?? brand.tagline;\n\n return {\n applicationName: brand.name,\n title: {\n default: title,\n template: `%s — ${brand.name}`,\n },\n description,\n metadataBase: new URL(siteUrl),\n authors: [{ name: brand.name, url: siteUrl }],\n creator: brand.name,\n publisher: brand.name,\n openGraph: {\n type: \"website\",\n siteName: brand.name,\n title,\n description,\n url: siteUrl,\n },\n twitter: {\n card: \"summary_large_image\",\n site: brand.social.twitter.replace(\"https://twitter.com/\", \"@\"),\n title,\n description,\n },\n };\n}\n\n// ─── JSON-LD types ─────────────────────────────────────────────────────────────\n\nexport interface OrganizationJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"Organization\";\n \"@id\": string;\n name: string;\n alternateName: string;\n url: string;\n sameAs: string[];\n [key: string]: unknown;\n}\n\nexport interface WebSiteJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"WebSite\";\n \"@id\": string;\n name: string;\n url: string;\n description: string;\n publisher: { \"@id\": string };\n inLanguage: string[];\n [key: string]: unknown;\n}\n\nexport interface SoftwareApplicationJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"SoftwareApplication\";\n name: string;\n applicationCategory: string;\n url: string;\n author: { \"@id\": string };\n [key: string]: unknown;\n}\n\nexport interface FeedChannelMeta {\n title: string;\n link: string;\n description: string;\n feedUrl: string;\n language: string;\n}\n\n// ─── JSON-LD builders ────────────────────────────────────────────────────────\n\n/**\n * Base Organization JSON-LD — company-specific fields\n * (foundingDate, address, contactPoint) are intentionally omitted here.\n * Merge them at the call site to avoid hard-coding values here.\n */\nexport function buildOrganizationJsonLd(): OrganizationJsonLd {\n const siteUrl = getSiteUrl(\"landing\");\n const id = `${siteUrl}/#organization`;\n\n const sameAs = [\n brand.social.github,\n brand.social.twitter.replace(\"https://twitter.com/\", \"https://x.com/\"),\n brand.social.linkedin,\n brand.social.discord,\n ].filter((url) => url.trim() !== \"\");\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"@id\": id,\n name: brand.name,\n alternateName: brand.nameCn,\n url: siteUrl,\n sameAs,\n };\n}\n\nexport function buildWebSiteJsonLd(): WebSiteJsonLd {\n const siteUrl = getSiteUrl(\"landing\");\n const org = buildOrganizationJsonLd();\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"WebSite\",\n \"@id\": `${siteUrl}/#website`,\n name: brand.name,\n url: siteUrl,\n description: brand.tagline,\n publisher: { \"@id\": org[\"@id\"] },\n inLanguage: [\"en\", \"zh\"],\n };\n}\n\nexport function buildSoftwareApplicationJsonLd(): SoftwareApplicationJsonLd {\n const org = buildOrganizationJsonLd();\n const githubPath = brand.social.github.replace(\"https://github.com/\", \"\");\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"SoftwareApplication\",\n name: `${brand.name} Sailor`,\n applicationCategory: \"DeveloperApplication\",\n url: `https://github.com/${githubPath}`,\n author: { \"@id\": org[\"@id\"] },\n };\n}\n\n// ─── PWA Manifest ────────────────────────────────────────────────────────────\n\n/**\n * Returns a base PWA manifest derived from brand + colors.\n * background_color and theme_color use the palette's canonical hex values\n * (NOT CSS variables — service workers do not resolve var()).\n *\n * CONTRAST CAVEAT: theme_color = colors.primary[\"500\"] assumes a dark\n * background context. If a rebrand sets a light primary, update this value.\n */\nexport function buildPwaManifest(): MetadataRoute.Manifest {\n return {\n name: `${brand.name} — ${brand.tagline}`,\n short_name: brand.name,\n description: brand.description,\n start_url: \"/\",\n display: \"standalone\",\n background_color: colors.neutral[\"950\"],\n theme_color: colors.primary[\"500\"],\n orientation: \"portrait-primary\",\n scope: \"/\",\n lang: \"en\",\n categories: [\"business\", \"productivity\", \"utilities\"],\n icons: [\n {\n src: \"/favicon.ico\",\n sizes: \"any\",\n type: \"image/x-icon\",\n },\n {\n src: \"/android-chrome-192x192.png\",\n sizes: \"192x192\",\n type: \"image/png\",\n purpose: \"maskable\",\n },\n {\n src: \"/android-chrome-512x512.png\",\n sizes: \"512x512\",\n type: \"image/png\",\n purpose: \"maskable\",\n },\n {\n src: \"/android-chrome-192x192.png\",\n sizes: \"192x192\",\n type: \"image/png\",\n purpose: \"any\",\n },\n {\n src: \"/android-chrome-512x512.png\",\n sizes: \"512x512\",\n type: \"image/png\",\n purpose: \"any\",\n },\n ],\n };\n}\n\n// ─── Feed ────────────────────────────────────────────────────────────────────\n\n/**\n * Returns the shared channel-level fields for RSS and Atom feeds.\n * Item-level links are built at the call site using the version string.\n */\nexport function buildFeedChannelMeta(): FeedChannelMeta {\n const siteUrl = getSiteUrl(\"landing\");\n\n return {\n title: `${brand.name} Changelog`,\n link: `${siteUrl}/changelog`,\n description: brand.tagline,\n feedUrl: `${siteUrl}/api/changelog`,\n language: \"en-us\",\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/metadata.ts","../src/metadata-helpers.ts"],"sourcesContent":["/**\n * Nebutra Brand Metadata\n * Central source of truth for brand identity\n *\n * AUTO-GENERATED by `pnpm brand:apply` from `scripts/brand-types.ts`\n * (DEFAULT_BRAND) or a sibling `brand.config.ts` if present. Do NOT\n * edit by hand — your edits will be wiped on the next brand:apply.\n *\n * To rebrand, edit DEFAULT_BRAND in scripts/brand-types.ts and run\n * `pnpm brand:apply`. To extend the shape, add a field to BrandConfig,\n * set its value in DEFAULT_BRAND, then read it from this writer.\n *\n * Pipeline guard: tests/architecture/brand-metadata-drift.test.ts\n * asserts `metadata.ts` is byte-identical to what brand:apply emits.\n */\n\nexport const brand = {\n name: \"Nebutra\",\n nameCn: \"云毓智能\",\n nameFull: \"无锡云毓智能科技有限公司\",\n nameFullEn: \"Wuxi Nebutra Intelligence Technology Co., Ltd.\",\n\n tagline: \"Ship AI products, not boilerplate.\",\n taglineCn: \"AI原生·快速出海·即刻交付\",\n description:\n \"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure — pre-configured.\",\n descriptionCn:\n \"面向AI创业者的一体化SaaS基础设施模板,覆盖认证、计费、多租户、AI服务与设计系统,开箱即产品\",\n\n story: {\n concept:\n \"Logo以首字母N的基础造型概念为主要设计框架,通过几何正负空间构建隐形'N',形成近似六边形的稳定结构\",\n colorMeaning: \"蓝绿渐变体现未来感与科技锋芒,'云'代表云端平台,'毓'寓意孕育与转化\",\n values: [\"AI Native\", \"Ship Fast\", \"Open by Default\", \"Global-Ready\", \"Enterprise-Grade\"],\n missionStatement:\n \"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build.\",\n },\n\n domains: {\n landing: \"nebutra.com\",\n app: \"app.nebutra.com\",\n api: \"api.nebutra.com\",\n auth: \"auth.nebutra.com\",\n sso: \"sso.nebutra.com\",\n studio: \"studio.nebutra.com\",\n cdn: \"cdn.nebutra.com\",\n router: \"router.nebutra.com\",\n forge: \"forge.nebutra.com\",\n design: \"design.nebutra.com\",\n status: \"status.nebutra.com\",\n open: \"open.nebutra.com\",\n admin: \"admin.nebutra.com\",\n analytics: \"analytics.nebutra.com\",\n pebble: \"pebble.nebutra.com\",\n carina: \"carina.nebutra.com\",\n kuanlan: \"kuanlan.nebutra.com\",\n origin: \"origin.nebutra.com\",\n },\n\n social: {\n twitter: \"https://twitter.com/nebutra\",\n github: \"https://github.com/nebutra\",\n discord: \"https://discord.gg/nebutra\",\n linkedin: \"https://linkedin.com/company/nebutra\",\n },\n} as const;\n\n/**\n * Brand Colors — full VI palette + brand gradients + semantic colors.\n * Aligned with @nebutra/design-tokens DTCG SSOT (tokens/core.json).\n */\nexport const colors = {\n primary: {\n \"50\": \"#f0f4ff\",\n \"100\": \"#dbe4ff\",\n \"200\": \"#bac8ff\",\n \"300\": \"#91a7ff\",\n \"400\": \"#5c7cfa\",\n \"500\": \"#0033FE\",\n \"600\": \"#002ad4\",\n \"700\": \"#0021ab\",\n \"800\": \"#001882\",\n \"900\": \"#000f59\",\n \"950\": \"#000830\",\n },\n accent: {\n \"50\": \"#e6fff8\",\n \"100\": \"#b3ffec\",\n \"200\": \"#80ffe0\",\n \"300\": \"#4dfcd4\",\n \"400\": \"#1af7c8\",\n \"500\": \"#0BF1C3\",\n \"600\": \"#09c9a3\",\n \"700\": \"#07a183\",\n \"800\": \"#057963\",\n \"900\": \"#035143\",\n \"950\": \"#012923\",\n },\n neutral: {\n \"0\": \"#ffffff\",\n \"50\": \"#f9fafb\",\n \"100\": \"#f3f4f6\",\n \"200\": \"#e4e5e8\",\n \"300\": \"#d2d4d8\",\n \"400\": \"#9fa1a5\",\n \"500\": \"#717377\",\n \"600\": \"#515357\",\n \"700\": \"#3f4044\",\n \"800\": \"#26272a\",\n \"900\": \"#18191c\",\n \"950\": \"#0b0c0d\",\n },\n white: \"#FFFFFF\",\n black: \"#000000\",\n gradient: {\n primary: \"linear-gradient(135deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryReverse: \"linear-gradient(135deg, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n primaryVertical: \"linear-gradient(180deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryRadial: \"radial-gradient(circle, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n },\n p3Overrides: {\n primary500: \"color(display-p3 0.03 0.19 0.99)\",\n accent500: \"color(display-p3 0.07 0.94 0.79)\",\n },\n success: \"#22c55e\",\n warning: \"#f59e0b\",\n error: \"#ef4444\",\n info: \"#0033FE\",\n} as const;\n\n/**\n * Typography. fontFamily is read from design-tokens core.json:fontFamily by\n * brand:apply — change faces there, not here.\n */\nexport const typography = {\n fontFamily: {\n en: '\"Geist\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n cn: '\"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif',\n sans: '\"Geist\", \"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace',\n display: '\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif',\n heading: '\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif',\n },\n fontWeight: {\n thin: 100,\n extraLight: 200,\n light: 300,\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extraBold: 800,\n black: 900,\n },\n letterSpacing: {\n display: \"-0.04em\",\n heading: \"-0.03em\",\n subheading: \"-0.02em\",\n body: \"0em\",\n caption: \"0.01em\",\n mono: \"0em\",\n },\n lineHeight: {\n none: 1,\n tight: 1.2,\n snug: 1.4,\n normal: 1.6,\n relaxed: 1.75,\n loose: 2,\n },\n} as const;\n\n/**\n * Logo asset paths (relative to package), dual-edition structure:\n * - classic (v1.0): preferred for UX / product surfaces\n * - compliant (v2.0): required for commercial / legal contexts\n */\nexport const logoAssets = {\n classic: {\n default: \"assets/logo/logo-color.svg\",\n color: \"assets/logo/logo-color.svg\",\n inverse: \"assets/logo/logo-inverse.svg\",\n mono: \"assets/logo/logo-mono.svg\",\n en: \"assets/logo/logo-en.svg\",\n zh: \"assets/logo/logo-zh.svg\",\n zhEn: \"assets/logo/logo-zh-en.svg\",\n horizontalEn: \"assets/logo/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo/logo-vertical-en.svg\",\n verticalZh: \"assets/logo/logo-vertical-zh.svg\",\n },\n compliant: {\n default: \"assets/logo-compliant/logo-color.svg\",\n color: \"assets/logo-compliant/logo-color.svg\",\n inverse: \"assets/logo-compliant/logo-inverse.svg\",\n mono: \"assets/logo-compliant/logo-mono.svg\",\n en: \"assets/logo-compliant/logo-en.svg\",\n zh: \"assets/logo-compliant/logo-zh.svg\",\n zhEn: \"assets/logo-compliant/logo-zh-en.svg\",\n horizontalEn: \"assets/logo-compliant/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo-compliant/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo-compliant/logo-vertical-en.svg\",\n verticalZh: \"assets/logo-compliant/logo-vertical-zh.svg\",\n horizontalEnMono: \"assets/logo-compliant/logo-horizontal-en-mono.svg\",\n horizontalZhMono: \"assets/logo-compliant/logo-horizontal-zh-mono.svg\",\n verticalEnMono: \"assets/logo-compliant/logo-vertical-en-mono.svg\",\n verticalZhMono: \"assets/logo-compliant/logo-vertical-zh-mono.svg\",\n },\n} as const;\n\n/**\n * Font asset paths (relative to package).\n */\nexport const fontAssets = {\n poppins: {\n thin: \"assets/fonts/poppins/Poppins-Thin.otf\",\n thinItalic: \"assets/fonts/poppins/Poppins-ThinItalic.otf\",\n extraLight: \"assets/fonts/poppins/Poppins-ExtraLight.otf\",\n extraLightItalic: \"assets/fonts/poppins/Poppins-ExtraLightItalic.otf\",\n light: \"assets/fonts/poppins/Poppins-Light.otf\",\n lightItalic: \"assets/fonts/poppins/Poppins-LightItalic.otf\",\n regular: \"assets/fonts/poppins/Poppins-Regular.otf\",\n italic: \"assets/fonts/poppins/Poppins-Italic.otf\",\n medium: \"assets/fonts/poppins/Poppins-Medium.otf\",\n mediumItalic: \"assets/fonts/poppins/Poppins-MediumItalic.otf\",\n semiBold: \"assets/fonts/poppins/Poppins-SemiBold.otf\",\n semiBoldItalic: \"assets/fonts/poppins/Poppins-SemiBoldItalic.otf\",\n bold: \"assets/fonts/poppins/Poppins-Bold.otf\",\n boldItalic: \"assets/fonts/poppins/Poppins-BoldItalic.otf\",\n extraBold: \"assets/fonts/poppins/Poppins-ExtraBold.otf\",\n extraBoldItalic: \"assets/fonts/poppins/Poppins-ExtraBoldItalic.otf\",\n black: \"assets/fonts/poppins/Poppins-Black.otf\",\n blackItalic: \"assets/fonts/poppins/Poppins-BlackItalic.otf\",\n },\n} as const;\n\n/**\n * Favicon assets.\n */\nexport const faviconAssets = {\n ico: \"assets/favicon/favicon.ico\",\n svg: \"assets/favicon/favicon.svg\",\n apple: \"assets/favicon/apple-touch-icon.png\",\n android192: \"assets/favicon/android-chrome-192x192.png\",\n android512: \"assets/favicon/android-chrome-512x512.png\",\n} as const;\n\n/**\n * OG Image dimensions.\n */\nexport const ogImageDimensions = {\n default: {\n width: 1200,\n height: 630,\n },\n twitter: {\n width: 1200,\n height: 600,\n },\n square: {\n width: 1200,\n height: 1200,\n },\n} as const;\n\nexport type BrandColors = typeof colors;\nexport type BrandTypography = typeof typography;\nexport type LogoAssets = typeof logoAssets;\n","/**\n * @nebutra/brand — metadata-helpers\n *\n * Pure functions deriving Next.js Metadata, JSON-LD objects, PWA manifest,\n * and feed channel headers from the single `brand` + `colors` SSOT.\n *\n * Zero hardcoded brand-name literals — every string is derived from `brand`\n * or `colors`. The arch drift guard `tests/architecture/brand-metadata-drift.test.ts`\n * asserts this invariant at CI time.\n *\n * NOTE: `next` is a TYPE-ONLY import here. The package declares `next` as an\n * optional peer dep + devDep (catalog:). skipLibCheck:true is set in tsconfig.json.\n * Adding `import \"server-only\"` is intentionally omitted: this file is consumed\n * by both server and client render paths in tests; the CALLER must enforce\n * server-only boundaries if needed.\n *\n * NOTE: getSiteUrl(service) in this module is DISTINCT from the zero-param\n * getSiteUrl() in apps/landing/src/lib/seo/site-routes.ts — different\n * modules, no collision. The local one remains the landing URL util.\n */\n\nimport type { Metadata, MetadataRoute } from \"next\";\nimport { brand, colors } from \"./metadata\";\n\n/** Services that have a canonical domain in brand.domains */\nexport type BrandService = keyof typeof brand.domains;\n\n/** Absolute origin from brand.domains only (no env hijack). */\nexport function getBrandOrigin(service: BrandService): string {\n const host = brand.domains[service];\n if (!host?.trim()) {\n throw new Error(`brand.domains.${String(service)} is empty — run brand:apply`);\n }\n return `https://${host.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\")}`;\n}\n\n/**\n * Public object on `nebutra-assets` via `cdn.nebutra.com`.\n * R2_PUBLIC_URL / NEXT_PUBLIC_R2_PUBLIC_URL may override the origin for dogfood.\n */\n/**\n * Where a product's documentation lives: a path on that product's own host.\n *\n * There is no `docs` entry in `brand.domains` — deliberately. A single docs\n * subdomain meant a product's own host was never where its documentation\n * lived: pebble's docs sat at docs.<domain>/pebble/* while\n * pebble.<domain>/docs/* redirected to them. One value cannot express \"each\n * product serves its own\", so the key is gone and the path is derived.\n *\n * Defaults to the marketing site, which carries the product docs. Pass a\n * service to get that product's: getDocsUrl(\"pebble\") → https://pebble.…/docs.\n */\nexport function getDocsUrl(service: BrandService = \"landing\", path = \"\"): string {\n const suffix = path.replace(/^\\/+/, \"\");\n return `${getBrandOrigin(service)}/docs${suffix ? `/${suffix}` : \"\"}`;\n}\n\n/**\n * The origin publicAssetUrl() resolves against — for CSP directives. A page\n * that loads public assets (images, the MiSans subsets) must allow exactly\n * this origin, or the browser blocks the request with no error on the page:\n * font-src 'self' blocked every MiSans face on nebutra.com until 2026-09-26.\n */\nexport function publicAssetOrigin(base?: string): string {\n return new URL(publicAssetUrl(\"x\", base)).origin;\n}\n\nexport function publicAssetUrl(key: string, base?: string): string {\n const origin = (\n base ||\n process.env.NEXT_PUBLIC_R2_PUBLIC_URL ||\n process.env.R2_PUBLIC_URL ||\n getBrandOrigin(\"cdn\")\n ).replace(/\\/+$/, \"\");\n const path = key.replace(/^\\/+/, \"\");\n if (!path || path.includes(\"..\") || path.includes(\"\\\\\")) {\n throw new Error(\"invalid_public_asset_key\");\n }\n return `${origin}/${path}`;\n}\n\n/**\n * Bare query flag that keeps a signed-in visitor on the marketing homepage\n * (`?home`, no value). Apex `/` without it is a product launcher.\n */\nexport const MARKETING_HOME_PARAM = \"home\";\n\n/**\n * Marketing homepage path. Always includes the `?home` skip — that is the\n * only signal landing honours. `getBrandOrigin(\"landing\")` is the launcher.\n *\n * `defaultLocale` is the landing path locale that lives on `/` (today `en`).\n * Pass it from `@nebutra/i18n` so brand does not import the i18n package.\n */\nexport function getMarketingHomePath(options?: {\n locale?: string;\n defaultLocale?: string;\n}): string {\n const defaultLocale = options?.defaultLocale?.trim() || \"en\";\n const locale = options?.locale?.trim();\n const path = locale && locale !== defaultLocale ? `/${locale}` : \"/\";\n return path === \"/\" ? `/?${MARKETING_HOME_PARAM}` : `${path}?${MARKETING_HOME_PARAM}`;\n}\n\n/** Absolute marketing homepage. Same contract as `getMarketingHomePath`. */\nexport function getMarketingHomeUrl(options?: { locale?: string; defaultLocale?: string }): string {\n const origin = getBrandOrigin(\"landing\");\n return new URL(getMarketingHomePath(options), `${origin}/`).toString();\n}\n\n/** Cookie parent domain from landing apex (e.g. `.example.com`). */\nexport function getBrandCookieDomain(): string {\n const apex = brand.domains.landing.replace(/^https?:\\/\\//, \"\").replace(/\\/+$/, \"\");\n if (!apex) return \"\";\n return apex.startsWith(\".\") ? apex : `.${apex}`;\n}\n\n/**\n * Canonical base URL. NEXT_PUBLIC_SITE_URL only overrides landing.\n */\nexport function getSiteUrl(service: BrandService = \"landing\"): string {\n if (service === \"landing\") {\n const envUrl = process.env[\"NEXT_PUBLIC_SITE_URL\"];\n if (envUrl && envUrl.trim() !== \"\") {\n return envUrl.trim().replace(/\\/+$/, \"\");\n }\n }\n return getBrandOrigin(service);\n}\n\n/** Production multi-app public URL map (dogfood brand.domains). */\nexport function getBrandPublicUrls() {\n return {\n siteUrl: getBrandOrigin(\"landing\"),\n appUrl: getBrandOrigin(\"app\"),\n apiUrl: getBrandOrigin(\"api\"),\n authUrl: getBrandOrigin(\"auth\"),\n ssoUrl: getBrandOrigin(\"sso\"),\n docsUrl: getDocsUrl(),\n routerUrl: getBrandOrigin(\"router\"),\n forgeUrl: getBrandOrigin(\"forge\"),\n designUrl: getBrandOrigin(\"design\"),\n statusUrl: getBrandOrigin(\"status\"),\n openUrl: getBrandOrigin(\"open\"),\n studioUrl: getBrandOrigin(\"studio\"),\n cdnUrl: getBrandOrigin(\"cdn\"),\n analyticsUrl: getBrandOrigin(\"analytics\"),\n pebbleUrl: getBrandOrigin(\"pebble\"),\n carinaUrl: getBrandOrigin(\"carina\"),\n cookieDomain: getBrandCookieDomain(),\n } as const;\n}\n\n/**\n * Product mailbox derived from landing apex — rebrand-safe.\n * e.g. getBrandEmail(\"contact\") → contact@example.com\n */\nexport function getBrandEmail(localPart: string): string {\n const apex = brand.domains.landing\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/+$/, \"\")\n .replace(/^www\\./, \"\");\n if (!apex) {\n throw new Error(\"brand.domains.landing is empty — run brand:apply\");\n }\n const local = localPart.trim().replace(/@.*$/, \"\");\n if (!local) {\n throw new Error(\"getBrandEmail requires a non-empty local part\");\n }\n return `${local}@${apex}`;\n}\n\n/** Default transactional From header: `Brand <noreply@apex>`. */\nexport function getBrandMailFrom(): string {\n return `${brand.name} <${getBrandEmail(\"noreply\")}>`;\n}\n\n// ─── Metadata ────────────────────────────────────────────────────────────────\n\nexport interface SiteMetadataOptions {\n readonly service: BrandService;\n readonly title?: string;\n readonly description?: string;\n}\n\n/**\n * Returns a partial Next.js Metadata object suitable for spreading into a\n * layout.tsx export const metadata. Callers may override any field.\n */\nexport function getSiteMetadata(opts: SiteMetadataOptions): Metadata {\n const siteUrl = getSiteUrl(opts.service);\n const title = opts.title ?? brand.name;\n const description = opts.description ?? brand.tagline;\n\n return {\n applicationName: brand.name,\n title: {\n default: title,\n template: `%s — ${brand.name}`,\n },\n description,\n metadataBase: new URL(siteUrl),\n authors: [{ name: brand.name, url: siteUrl }],\n creator: brand.name,\n publisher: brand.name,\n openGraph: {\n type: \"website\",\n siteName: brand.name,\n title,\n description,\n url: siteUrl,\n },\n twitter: {\n card: \"summary_large_image\",\n site: brand.social.twitter.replace(\"https://twitter.com/\", \"@\"),\n title,\n description,\n },\n };\n}\n\n// ─── JSON-LD types ─────────────────────────────────────────────────────────────\n\nexport interface OrganizationJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"Organization\";\n \"@id\": string;\n name: string;\n alternateName: string;\n url: string;\n sameAs: string[];\n [key: string]: unknown;\n}\n\nexport interface WebSiteJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"WebSite\";\n \"@id\": string;\n name: string;\n url: string;\n description: string;\n publisher: { \"@id\": string };\n inLanguage: string[];\n [key: string]: unknown;\n}\n\nexport interface SoftwareApplicationJsonLd {\n \"@context\": \"https://schema.org\";\n \"@type\": \"SoftwareApplication\";\n name: string;\n applicationCategory: string;\n url: string;\n author: { \"@id\": string };\n [key: string]: unknown;\n}\n\nexport interface FeedChannelMeta {\n title: string;\n link: string;\n description: string;\n feedUrl: string;\n language: string;\n}\n\n// ─── JSON-LD builders ────────────────────────────────────────────────────────\n\n/**\n * Base Organization JSON-LD — company-specific fields\n * (foundingDate, address, contactPoint) are intentionally omitted here.\n * Merge them at the call site to avoid hard-coding values here.\n */\nexport function buildOrganizationJsonLd(): OrganizationJsonLd {\n const siteUrl = getSiteUrl(\"landing\");\n const id = `${siteUrl}/#organization`;\n\n const sameAs = [\n brand.social.github,\n brand.social.twitter.replace(\"https://twitter.com/\", \"https://x.com/\"),\n brand.social.linkedin,\n brand.social.discord,\n ].filter((url) => url.trim() !== \"\");\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"Organization\",\n \"@id\": id,\n name: brand.name,\n alternateName: brand.nameCn,\n url: siteUrl,\n sameAs,\n };\n}\n\nexport function buildWebSiteJsonLd(): WebSiteJsonLd {\n const siteUrl = getSiteUrl(\"landing\");\n const org = buildOrganizationJsonLd();\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"WebSite\",\n \"@id\": `${siteUrl}/#website`,\n name: brand.name,\n url: siteUrl,\n description: brand.tagline,\n publisher: { \"@id\": org[\"@id\"] },\n inLanguage: [\"en\", \"zh\"],\n };\n}\n\nexport function buildSoftwareApplicationJsonLd(): SoftwareApplicationJsonLd {\n const org = buildOrganizationJsonLd();\n const githubPath = brand.social.github.replace(\"https://github.com/\", \"\");\n\n return {\n \"@context\": \"https://schema.org\",\n \"@type\": \"SoftwareApplication\",\n name: `${brand.name} Sailor`,\n applicationCategory: \"DeveloperApplication\",\n url: `https://github.com/${githubPath}`,\n author: { \"@id\": org[\"@id\"] },\n };\n}\n\n// ─── PWA Manifest ────────────────────────────────────────────────────────────\n\n/**\n * Returns a base PWA manifest derived from brand + colors.\n * background_color and theme_color use the palette's canonical hex values\n * (NOT CSS variables — service workers do not resolve var()).\n *\n * CONTRAST CAVEAT: theme_color = colors.primary[\"500\"] assumes a dark\n * background context. If a rebrand sets a light primary, update this value.\n */\nexport function buildPwaManifest(): MetadataRoute.Manifest {\n return {\n name: `${brand.name} — ${brand.tagline}`,\n short_name: brand.name,\n description: brand.description,\n start_url: \"/\",\n display: \"standalone\",\n background_color: colors.neutral[\"950\"],\n theme_color: colors.primary[\"500\"],\n orientation: \"portrait-primary\",\n scope: \"/\",\n lang: \"en\",\n categories: [\"business\", \"productivity\", \"utilities\"],\n icons: [\n {\n src: \"/favicon.ico\",\n sizes: \"any\",\n type: \"image/x-icon\",\n },\n {\n src: \"/android-chrome-192x192.png\",\n sizes: \"192x192\",\n type: \"image/png\",\n purpose: \"maskable\",\n },\n {\n src: \"/android-chrome-512x512.png\",\n sizes: \"512x512\",\n type: \"image/png\",\n purpose: \"maskable\",\n },\n {\n src: \"/android-chrome-192x192.png\",\n sizes: \"192x192\",\n type: \"image/png\",\n purpose: \"any\",\n },\n {\n src: \"/android-chrome-512x512.png\",\n sizes: \"512x512\",\n type: \"image/png\",\n purpose: \"any\",\n },\n ],\n };\n}\n\n// ─── Feed ────────────────────────────────────────────────────────────────────\n\n/**\n * Returns the shared channel-level fields for RSS and Atom feeds.\n * Item-level links are built at the call site using the version string.\n */\nexport function buildFeedChannelMeta(): FeedChannelMeta {\n const siteUrl = getSiteUrl(\"landing\");\n\n return {\n title: `${brand.name} Changelog`,\n link: `${siteUrl}/changelog`,\n description: brand.tagline,\n feedUrl: `${siteUrl}/api/changelog`,\n language: \"en-us\",\n };\n}\n"],"mappings":"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","names":[]}
@@ -34,7 +34,6 @@ declare const brand: {
34
34
  readonly api: "api.nebutra.com";
35
35
  readonly auth: "auth.nebutra.com";
36
36
  readonly sso: "sso.nebutra.com";
37
- readonly docs: "docs.nebutra.com";
38
37
  readonly studio: "studio.nebutra.com";
39
38
  readonly cdn: "cdn.nebutra.com";
40
39
  readonly router: "router.nebutra.com";
@@ -89,17 +88,17 @@ declare const colors: {
89
88
  };
90
89
  readonly neutral: {
91
90
  readonly "0": "#ffffff";
92
- readonly "50": "#f8fafc";
93
- readonly "100": "#f1f5f9";
94
- readonly "200": "#e2e8f0";
95
- readonly "300": "#cbd5e1";
96
- readonly "400": "#94a3b8";
97
- readonly "500": "#64748b";
98
- readonly "600": "#475569";
99
- readonly "700": "#334155";
100
- readonly "800": "#1e293b";
101
- readonly "900": "#0f172a";
102
- readonly "950": "#020617";
91
+ readonly "50": "#f9fafb";
92
+ readonly "100": "#f3f4f6";
93
+ readonly "200": "#e4e5e8";
94
+ readonly "300": "#d2d4d8";
95
+ readonly "400": "#9fa1a5";
96
+ readonly "500": "#717377";
97
+ readonly "600": "#515357";
98
+ readonly "700": "#3f4044";
99
+ readonly "800": "#26272a";
100
+ readonly "900": "#18191c";
101
+ readonly "950": "#0b0c0d";
103
102
  };
104
103
  readonly white: "#FFFFFF";
105
104
  readonly black: "#000000";
@@ -119,22 +118,17 @@ declare const colors: {
119
118
  readonly info: "#0033FE";
120
119
  };
121
120
  /**
122
- * Typography — "Precision Stack" (Geist + Noto Sans SC + Geist Mono).
121
+ * Typography. fontFamily is read from design-tokens core.json:fontFamily by
122
+ * brand:apply — change faces there, not here.
123
123
  */
124
124
  declare const typography: {
125
125
  readonly fontFamily: {
126
- readonly en: "\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif";
127
- readonly cn: "\"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif";
128
- readonly sans: "\"Geist\", \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
126
+ readonly en: "\"Geist\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
127
+ readonly cn: "\"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif";
128
+ readonly sans: "\"Geist\", \"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif";
129
129
  readonly mono: "\"Geist Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace";
130
- readonly display: "\"Geist\", \"Noto Sans SC\", sans-serif";
131
- readonly heading: "\"Geist\", \"Noto Sans SC\", sans-serif";
132
- readonly brandPrint: "\"Noto Sans SC\", \"PingFang SC\", sans-serif";
133
- };
134
- readonly cssVars: {
135
- readonly sans: "var(--font-geist-sans)";
136
- readonly mono: "var(--font-geist-mono)";
137
- readonly cnSans: "var(--font-noto-sc)";
130
+ readonly display: "\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif";
131
+ readonly heading: "\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif";
138
132
  };
139
133
  readonly fontWeight: {
140
134
  readonly thin: 100;
package/dist/metadata.js CHANGED
@@ -20,7 +20,6 @@ var brand = {
20
20
  api: "api.nebutra.com",
21
21
  auth: "auth.nebutra.com",
22
22
  sso: "sso.nebutra.com",
23
- docs: "docs.nebutra.com",
24
23
  studio: "studio.nebutra.com",
25
24
  cdn: "cdn.nebutra.com",
26
25
  router: "router.nebutra.com",
@@ -71,17 +70,17 @@ var colors = {
71
70
  },
72
71
  neutral: {
73
72
  "0": "#ffffff",
74
- "50": "#f8fafc",
75
- "100": "#f1f5f9",
76
- "200": "#e2e8f0",
77
- "300": "#cbd5e1",
78
- "400": "#94a3b8",
79
- "500": "#64748b",
80
- "600": "#475569",
81
- "700": "#334155",
82
- "800": "#1e293b",
83
- "900": "#0f172a",
84
- "950": "#020617"
73
+ "50": "#f9fafb",
74
+ "100": "#f3f4f6",
75
+ "200": "#e4e5e8",
76
+ "300": "#d2d4d8",
77
+ "400": "#9fa1a5",
78
+ "500": "#717377",
79
+ "600": "#515357",
80
+ "700": "#3f4044",
81
+ "800": "#26272a",
82
+ "900": "#18191c",
83
+ "950": "#0b0c0d"
85
84
  },
86
85
  white: "#FFFFFF",
87
86
  black: "#000000",
@@ -102,18 +101,12 @@ var colors = {
102
101
  };
103
102
  var typography = {
104
103
  fontFamily: {
105
- en: '"Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',
106
- cn: '"Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif',
107
- sans: '"Geist", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
104
+ en: '"Geist", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
105
+ cn: '"MiSans", "PingFang SC", "Microsoft YaHei", sans-serif',
106
+ sans: '"Geist", "MiSans", "PingFang SC", "Microsoft YaHei", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',
108
107
  mono: '"Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
109
- display: '"Geist", "Noto Sans SC", sans-serif',
110
- heading: '"Geist", "Noto Sans SC", sans-serif',
111
- brandPrint: '"Noto Sans SC", "PingFang SC", sans-serif'
112
- },
113
- cssVars: {
114
- sans: "var(--font-geist-sans)",
115
- mono: "var(--font-geist-mono)",
116
- cnSans: "var(--font-noto-sc)"
108
+ display: '"DM Sans", "MiSans", "PingFang SC", sans-serif',
109
+ heading: '"DM Sans", "MiSans", "PingFang SC", sans-serif'
117
110
  },
118
111
  fontWeight: {
119
112
  thin: 100,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/metadata.ts"],"sourcesContent":["/**\n * Nebutra Brand Metadata\n * Central source of truth for brand identity\n *\n * AUTO-GENERATED by `pnpm brand:apply` from `scripts/brand-types.ts`\n * (DEFAULT_BRAND) or a sibling `brand.config.ts` if present. Do NOT\n * edit by hand — your edits will be wiped on the next brand:apply.\n *\n * To rebrand, edit DEFAULT_BRAND in scripts/brand-types.ts and run\n * `pnpm brand:apply`. To extend the shape, add a field to BrandConfig,\n * set its value in DEFAULT_BRAND, then read it from this writer.\n *\n * Pipeline guard: tests/architecture/brand-metadata-drift.test.ts\n * asserts `metadata.ts` is byte-identical to what brand:apply emits.\n */\n\nexport const brand = {\n name: \"Nebutra\",\n nameCn: \"云毓智能\",\n nameFull: \"无锡云毓智能科技有限公司\",\n nameFullEn: \"Wuxi Nebutra Intelligence Technology Co., Ltd.\",\n\n tagline: \"Ship AI products, not boilerplate.\",\n taglineCn: \"AI原生·快速出海·即刻交付\",\n description:\n \"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure — pre-configured.\",\n descriptionCn:\n \"面向AI创业者的一体化SaaS基础设施模板,覆盖认证、计费、多租户、AI服务与设计系统,开箱即产品\",\n\n story: {\n concept:\n \"Logo以首字母N的基础造型概念为主要设计框架,通过几何正负空间构建隐形'N',形成近似六边形的稳定结构\",\n colorMeaning: \"蓝绿渐变体现未来感与科技锋芒,'云'代表云端平台,'毓'寓意孕育与转化\",\n values: [\"AI Native\", \"Ship Fast\", \"Open by Default\", \"Global-Ready\", \"Enterprise-Grade\"],\n missionStatement:\n \"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build.\",\n },\n\n domains: {\n landing: \"nebutra.com\",\n app: \"app.nebutra.com\",\n api: \"api.nebutra.com\",\n auth: \"auth.nebutra.com\",\n sso: \"sso.nebutra.com\",\n docs: \"docs.nebutra.com\",\n studio: \"studio.nebutra.com\",\n cdn: \"cdn.nebutra.com\",\n router: \"router.nebutra.com\",\n forge: \"forge.nebutra.com\",\n design: \"design.nebutra.com\",\n status: \"status.nebutra.com\",\n open: \"open.nebutra.com\",\n admin: \"admin.nebutra.com\",\n analytics: \"analytics.nebutra.com\",\n pebble: \"pebble.nebutra.com\",\n carina: \"carina.nebutra.com\",\n kuanlan: \"kuanlan.nebutra.com\",\n origin: \"origin.nebutra.com\",\n },\n\n social: {\n twitter: \"https://twitter.com/nebutra\",\n github: \"https://github.com/nebutra\",\n discord: \"https://discord.gg/nebutra\",\n linkedin: \"https://linkedin.com/company/nebutra\",\n },\n} as const;\n\n/**\n * Brand Colors — full VI palette + brand gradients + semantic colors.\n * Aligned with @nebutra/design-tokens DTCG SSOT (tokens/core.json).\n */\nexport const colors = {\n primary: {\n \"50\": \"#f0f4ff\",\n \"100\": \"#dbe4ff\",\n \"200\": \"#bac8ff\",\n \"300\": \"#91a7ff\",\n \"400\": \"#5c7cfa\",\n \"500\": \"#0033FE\",\n \"600\": \"#002ad4\",\n \"700\": \"#0021ab\",\n \"800\": \"#001882\",\n \"900\": \"#000f59\",\n \"950\": \"#000830\",\n },\n accent: {\n \"50\": \"#e6fff8\",\n \"100\": \"#b3ffec\",\n \"200\": \"#80ffe0\",\n \"300\": \"#4dfcd4\",\n \"400\": \"#1af7c8\",\n \"500\": \"#0BF1C3\",\n \"600\": \"#09c9a3\",\n \"700\": \"#07a183\",\n \"800\": \"#057963\",\n \"900\": \"#035143\",\n \"950\": \"#012923\",\n },\n neutral: {\n \"0\": \"#ffffff\",\n \"50\": \"#f8fafc\",\n \"100\": \"#f1f5f9\",\n \"200\": \"#e2e8f0\",\n \"300\": \"#cbd5e1\",\n \"400\": \"#94a3b8\",\n \"500\": \"#64748b\",\n \"600\": \"#475569\",\n \"700\": \"#334155\",\n \"800\": \"#1e293b\",\n \"900\": \"#0f172a\",\n \"950\": \"#020617\",\n },\n white: \"#FFFFFF\",\n black: \"#000000\",\n gradient: {\n primary: \"linear-gradient(135deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryReverse: \"linear-gradient(135deg, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n primaryVertical: \"linear-gradient(180deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryRadial: \"radial-gradient(circle, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n },\n p3Overrides: {\n primary500: \"color(display-p3 0.03 0.19 0.99)\",\n accent500: \"color(display-p3 0.07 0.94 0.79)\",\n },\n success: \"#22c55e\",\n warning: \"#f59e0b\",\n error: \"#ef4444\",\n info: \"#0033FE\",\n} as const;\n\n/**\n * Typography — \"Precision Stack\" (Geist + Noto Sans SC + Geist Mono).\n */\nexport const typography = {\n fontFamily: {\n en: '\"Geist\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", system-ui, sans-serif',\n cn: '\"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif',\n sans: '\"Geist\", \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace',\n display: '\"Geist\", \"Noto Sans SC\", sans-serif',\n heading: '\"Geist\", \"Noto Sans SC\", sans-serif',\n brandPrint: '\"Noto Sans SC\", \"PingFang SC\", sans-serif',\n },\n cssVars: {\n sans: \"var(--font-geist-sans)\",\n mono: \"var(--font-geist-mono)\",\n cnSans: \"var(--font-noto-sc)\",\n },\n fontWeight: {\n thin: 100,\n extraLight: 200,\n light: 300,\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extraBold: 800,\n black: 900,\n },\n letterSpacing: {\n display: \"-0.04em\",\n heading: \"-0.03em\",\n subheading: \"-0.02em\",\n body: \"0em\",\n caption: \"0.01em\",\n mono: \"0em\",\n },\n lineHeight: {\n none: 1,\n tight: 1.2,\n snug: 1.4,\n normal: 1.6,\n relaxed: 1.75,\n loose: 2,\n },\n} as const;\n\n/**\n * Logo asset paths (relative to package), dual-edition structure:\n * - classic (v1.0): preferred for UX / product surfaces\n * - compliant (v2.0): required for commercial / legal contexts\n */\nexport const logoAssets = {\n classic: {\n default: \"assets/logo/logo-color.svg\",\n color: \"assets/logo/logo-color.svg\",\n inverse: \"assets/logo/logo-inverse.svg\",\n mono: \"assets/logo/logo-mono.svg\",\n en: \"assets/logo/logo-en.svg\",\n zh: \"assets/logo/logo-zh.svg\",\n zhEn: \"assets/logo/logo-zh-en.svg\",\n horizontalEn: \"assets/logo/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo/logo-vertical-en.svg\",\n verticalZh: \"assets/logo/logo-vertical-zh.svg\",\n },\n compliant: {\n default: \"assets/logo-compliant/logo-color.svg\",\n color: \"assets/logo-compliant/logo-color.svg\",\n inverse: \"assets/logo-compliant/logo-inverse.svg\",\n mono: \"assets/logo-compliant/logo-mono.svg\",\n en: \"assets/logo-compliant/logo-en.svg\",\n zh: \"assets/logo-compliant/logo-zh.svg\",\n zhEn: \"assets/logo-compliant/logo-zh-en.svg\",\n horizontalEn: \"assets/logo-compliant/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo-compliant/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo-compliant/logo-vertical-en.svg\",\n verticalZh: \"assets/logo-compliant/logo-vertical-zh.svg\",\n horizontalEnMono: \"assets/logo-compliant/logo-horizontal-en-mono.svg\",\n horizontalZhMono: \"assets/logo-compliant/logo-horizontal-zh-mono.svg\",\n verticalEnMono: \"assets/logo-compliant/logo-vertical-en-mono.svg\",\n verticalZhMono: \"assets/logo-compliant/logo-vertical-zh-mono.svg\",\n },\n} as const;\n\n/**\n * Font asset paths (relative to package).\n */\nexport const fontAssets = {\n poppins: {\n thin: \"assets/fonts/poppins/Poppins-Thin.otf\",\n thinItalic: \"assets/fonts/poppins/Poppins-ThinItalic.otf\",\n extraLight: \"assets/fonts/poppins/Poppins-ExtraLight.otf\",\n extraLightItalic: \"assets/fonts/poppins/Poppins-ExtraLightItalic.otf\",\n light: \"assets/fonts/poppins/Poppins-Light.otf\",\n lightItalic: \"assets/fonts/poppins/Poppins-LightItalic.otf\",\n regular: \"assets/fonts/poppins/Poppins-Regular.otf\",\n italic: \"assets/fonts/poppins/Poppins-Italic.otf\",\n medium: \"assets/fonts/poppins/Poppins-Medium.otf\",\n mediumItalic: \"assets/fonts/poppins/Poppins-MediumItalic.otf\",\n semiBold: \"assets/fonts/poppins/Poppins-SemiBold.otf\",\n semiBoldItalic: \"assets/fonts/poppins/Poppins-SemiBoldItalic.otf\",\n bold: \"assets/fonts/poppins/Poppins-Bold.otf\",\n boldItalic: \"assets/fonts/poppins/Poppins-BoldItalic.otf\",\n extraBold: \"assets/fonts/poppins/Poppins-ExtraBold.otf\",\n extraBoldItalic: \"assets/fonts/poppins/Poppins-ExtraBoldItalic.otf\",\n black: \"assets/fonts/poppins/Poppins-Black.otf\",\n blackItalic: \"assets/fonts/poppins/Poppins-BlackItalic.otf\",\n },\n} as const;\n\n/**\n * Favicon assets.\n */\nexport const faviconAssets = {\n ico: \"assets/favicon/favicon.ico\",\n svg: \"assets/favicon/favicon.svg\",\n apple: \"assets/favicon/apple-touch-icon.png\",\n android192: \"assets/favicon/android-chrome-192x192.png\",\n android512: \"assets/favicon/android-chrome-512x512.png\",\n} as const;\n\n/**\n * OG Image dimensions.\n */\nexport const ogImageDimensions = {\n default: {\n width: 1200,\n height: 630,\n },\n twitter: {\n width: 1200,\n height: 600,\n },\n square: {\n width: 1200,\n height: 1200,\n },\n} as const;\n\nexport type BrandColors = typeof colors;\nexport type BrandTypography = typeof typography;\nexport type LogoAssets = typeof logoAssets;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/metadata.ts"],"sourcesContent":["/**\n * Nebutra Brand Metadata\n * Central source of truth for brand identity\n *\n * AUTO-GENERATED by `pnpm brand:apply` from `scripts/brand-types.ts`\n * (DEFAULT_BRAND) or a sibling `brand.config.ts` if present. Do NOT\n * edit by hand — your edits will be wiped on the next brand:apply.\n *\n * To rebrand, edit DEFAULT_BRAND in scripts/brand-types.ts and run\n * `pnpm brand:apply`. To extend the shape, add a field to BrandConfig,\n * set its value in DEFAULT_BRAND, then read it from this writer.\n *\n * Pipeline guard: tests/architecture/brand-metadata-drift.test.ts\n * asserts `metadata.ts` is byte-identical to what brand:apply emits.\n */\n\nexport const brand = {\n name: \"Nebutra\",\n nameCn: \"云毓智能\",\n nameFull: \"无锡云毓智能科技有限公司\",\n nameFullEn: \"Wuxi Nebutra Intelligence Technology Co., Ltd.\",\n\n tagline: \"Ship AI products, not boilerplate.\",\n taglineCn: \"AI原生·快速出海·即刻交付\",\n description:\n \"Production-ready Next.js monorepo template for AI SaaS products. Auth, billing, multi-tenancy, AI services, design system, and enterprise infrastructure — pre-configured.\",\n descriptionCn:\n \"面向AI创业者的一体化SaaS基础设施模板,覆盖认证、计费、多租户、AI服务与设计系统,开箱即产品\",\n\n story: {\n concept:\n \"Logo以首字母N的基础造型概念为主要设计框架,通过几何正负空间构建隐形'N',形成近似六边形的稳定结构\",\n colorMeaning: \"蓝绿渐变体现未来感与科技锋芒,'云'代表云端平台,'毓'寓意孕育与转化\",\n values: [\"AI Native\", \"Ship Fast\", \"Open by Default\", \"Global-Ready\", \"Enterprise-Grade\"],\n missionStatement:\n \"Help AI founders and SaaS teams go from idea to production 10x faster by providing the infrastructure layer they shouldn't have to build.\",\n },\n\n domains: {\n landing: \"nebutra.com\",\n app: \"app.nebutra.com\",\n api: \"api.nebutra.com\",\n auth: \"auth.nebutra.com\",\n sso: \"sso.nebutra.com\",\n studio: \"studio.nebutra.com\",\n cdn: \"cdn.nebutra.com\",\n router: \"router.nebutra.com\",\n forge: \"forge.nebutra.com\",\n design: \"design.nebutra.com\",\n status: \"status.nebutra.com\",\n open: \"open.nebutra.com\",\n admin: \"admin.nebutra.com\",\n analytics: \"analytics.nebutra.com\",\n pebble: \"pebble.nebutra.com\",\n carina: \"carina.nebutra.com\",\n kuanlan: \"kuanlan.nebutra.com\",\n origin: \"origin.nebutra.com\",\n },\n\n social: {\n twitter: \"https://twitter.com/nebutra\",\n github: \"https://github.com/nebutra\",\n discord: \"https://discord.gg/nebutra\",\n linkedin: \"https://linkedin.com/company/nebutra\",\n },\n} as const;\n\n/**\n * Brand Colors — full VI palette + brand gradients + semantic colors.\n * Aligned with @nebutra/design-tokens DTCG SSOT (tokens/core.json).\n */\nexport const colors = {\n primary: {\n \"50\": \"#f0f4ff\",\n \"100\": \"#dbe4ff\",\n \"200\": \"#bac8ff\",\n \"300\": \"#91a7ff\",\n \"400\": \"#5c7cfa\",\n \"500\": \"#0033FE\",\n \"600\": \"#002ad4\",\n \"700\": \"#0021ab\",\n \"800\": \"#001882\",\n \"900\": \"#000f59\",\n \"950\": \"#000830\",\n },\n accent: {\n \"50\": \"#e6fff8\",\n \"100\": \"#b3ffec\",\n \"200\": \"#80ffe0\",\n \"300\": \"#4dfcd4\",\n \"400\": \"#1af7c8\",\n \"500\": \"#0BF1C3\",\n \"600\": \"#09c9a3\",\n \"700\": \"#07a183\",\n \"800\": \"#057963\",\n \"900\": \"#035143\",\n \"950\": \"#012923\",\n },\n neutral: {\n \"0\": \"#ffffff\",\n \"50\": \"#f9fafb\",\n \"100\": \"#f3f4f6\",\n \"200\": \"#e4e5e8\",\n \"300\": \"#d2d4d8\",\n \"400\": \"#9fa1a5\",\n \"500\": \"#717377\",\n \"600\": \"#515357\",\n \"700\": \"#3f4044\",\n \"800\": \"#26272a\",\n \"900\": \"#18191c\",\n \"950\": \"#0b0c0d\",\n },\n white: \"#FFFFFF\",\n black: \"#000000\",\n gradient: {\n primary: \"linear-gradient(135deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryReverse: \"linear-gradient(135deg, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n primaryVertical: \"linear-gradient(180deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)\",\n primaryRadial: \"radial-gradient(circle, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)\",\n },\n p3Overrides: {\n primary500: \"color(display-p3 0.03 0.19 0.99)\",\n accent500: \"color(display-p3 0.07 0.94 0.79)\",\n },\n success: \"#22c55e\",\n warning: \"#f59e0b\",\n error: \"#ef4444\",\n info: \"#0033FE\",\n} as const;\n\n/**\n * Typography. fontFamily is read from design-tokens core.json:fontFamily by\n * brand:apply — change faces there, not here.\n */\nexport const typography = {\n fontFamily: {\n en: '\"Geist\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n cn: '\"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", sans-serif',\n sans: '\"Geist\", \"MiSans\", \"PingFang SC\", \"Microsoft YaHei\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif',\n mono: '\"Geist Mono\", ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace',\n display: '\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif',\n heading: '\"DM Sans\", \"MiSans\", \"PingFang SC\", sans-serif',\n },\n fontWeight: {\n thin: 100,\n extraLight: 200,\n light: 300,\n normal: 400,\n medium: 500,\n semibold: 600,\n bold: 700,\n extraBold: 800,\n black: 900,\n },\n letterSpacing: {\n display: \"-0.04em\",\n heading: \"-0.03em\",\n subheading: \"-0.02em\",\n body: \"0em\",\n caption: \"0.01em\",\n mono: \"0em\",\n },\n lineHeight: {\n none: 1,\n tight: 1.2,\n snug: 1.4,\n normal: 1.6,\n relaxed: 1.75,\n loose: 2,\n },\n} as const;\n\n/**\n * Logo asset paths (relative to package), dual-edition structure:\n * - classic (v1.0): preferred for UX / product surfaces\n * - compliant (v2.0): required for commercial / legal contexts\n */\nexport const logoAssets = {\n classic: {\n default: \"assets/logo/logo-color.svg\",\n color: \"assets/logo/logo-color.svg\",\n inverse: \"assets/logo/logo-inverse.svg\",\n mono: \"assets/logo/logo-mono.svg\",\n en: \"assets/logo/logo-en.svg\",\n zh: \"assets/logo/logo-zh.svg\",\n zhEn: \"assets/logo/logo-zh-en.svg\",\n horizontalEn: \"assets/logo/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo/logo-vertical-en.svg\",\n verticalZh: \"assets/logo/logo-vertical-zh.svg\",\n },\n compliant: {\n default: \"assets/logo-compliant/logo-color.svg\",\n color: \"assets/logo-compliant/logo-color.svg\",\n inverse: \"assets/logo-compliant/logo-inverse.svg\",\n mono: \"assets/logo-compliant/logo-mono.svg\",\n en: \"assets/logo-compliant/logo-en.svg\",\n zh: \"assets/logo-compliant/logo-zh.svg\",\n zhEn: \"assets/logo-compliant/logo-zh-en.svg\",\n horizontalEn: \"assets/logo-compliant/logo-horizontal-en.svg\",\n horizontalZh: \"assets/logo-compliant/logo-horizontal-zh.svg\",\n verticalEn: \"assets/logo-compliant/logo-vertical-en.svg\",\n verticalZh: \"assets/logo-compliant/logo-vertical-zh.svg\",\n horizontalEnMono: \"assets/logo-compliant/logo-horizontal-en-mono.svg\",\n horizontalZhMono: \"assets/logo-compliant/logo-horizontal-zh-mono.svg\",\n verticalEnMono: \"assets/logo-compliant/logo-vertical-en-mono.svg\",\n verticalZhMono: \"assets/logo-compliant/logo-vertical-zh-mono.svg\",\n },\n} as const;\n\n/**\n * Font asset paths (relative to package).\n */\nexport const fontAssets = {\n poppins: {\n thin: \"assets/fonts/poppins/Poppins-Thin.otf\",\n thinItalic: \"assets/fonts/poppins/Poppins-ThinItalic.otf\",\n extraLight: \"assets/fonts/poppins/Poppins-ExtraLight.otf\",\n extraLightItalic: \"assets/fonts/poppins/Poppins-ExtraLightItalic.otf\",\n light: \"assets/fonts/poppins/Poppins-Light.otf\",\n lightItalic: \"assets/fonts/poppins/Poppins-LightItalic.otf\",\n regular: \"assets/fonts/poppins/Poppins-Regular.otf\",\n italic: \"assets/fonts/poppins/Poppins-Italic.otf\",\n medium: \"assets/fonts/poppins/Poppins-Medium.otf\",\n mediumItalic: \"assets/fonts/poppins/Poppins-MediumItalic.otf\",\n semiBold: \"assets/fonts/poppins/Poppins-SemiBold.otf\",\n semiBoldItalic: \"assets/fonts/poppins/Poppins-SemiBoldItalic.otf\",\n bold: \"assets/fonts/poppins/Poppins-Bold.otf\",\n boldItalic: \"assets/fonts/poppins/Poppins-BoldItalic.otf\",\n extraBold: \"assets/fonts/poppins/Poppins-ExtraBold.otf\",\n extraBoldItalic: \"assets/fonts/poppins/Poppins-ExtraBoldItalic.otf\",\n black: \"assets/fonts/poppins/Poppins-Black.otf\",\n blackItalic: \"assets/fonts/poppins/Poppins-BlackItalic.otf\",\n },\n} as const;\n\n/**\n * Favicon assets.\n */\nexport const faviconAssets = {\n ico: \"assets/favicon/favicon.ico\",\n svg: \"assets/favicon/favicon.svg\",\n apple: \"assets/favicon/apple-touch-icon.png\",\n android192: \"assets/favicon/android-chrome-192x192.png\",\n android512: \"assets/favicon/android-chrome-512x512.png\",\n} as const;\n\n/**\n * OG Image dimensions.\n */\nexport const ogImageDimensions = {\n default: {\n width: 1200,\n height: 630,\n },\n twitter: {\n width: 1200,\n height: 600,\n },\n square: {\n width: 1200,\n height: 1200,\n },\n} as const;\n\nexport type BrandColors = typeof colors;\nexport type BrandTypography = typeof typography;\nexport type LogoAssets = typeof logoAssets;\n"],"mappings":"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","names":[]}
@@ -40,9 +40,9 @@ interface OgTemplateProps {
40
40
  palette: OgThemePalette;
41
41
  }
42
42
  /**
43
- * Dark-theme palette.
44
- * Colors are sourced from @nebutra/brand/metadata colors object — callers
45
- * may import from there to keep them in sync with brand:apply changes.
43
+ * Dark-theme palette, read from the brand colours (brand:apply's output).
44
+ * The comments here used to name the source while the values had drifted
45
+ * from it — bg was pre-House Slate while claiming to be neutral-950.
46
46
  */
47
47
  declare const OG_PALETTE_DARK: OgThemePalette;
48
48
  /**
package/dist/og/index.js CHANGED
@@ -1,29 +1,86 @@
1
+ // src/metadata.ts
2
+ var colors = {
3
+ primary: {
4
+ "50": "#f0f4ff",
5
+ "100": "#dbe4ff",
6
+ "200": "#bac8ff",
7
+ "300": "#91a7ff",
8
+ "400": "#5c7cfa",
9
+ "500": "#0033FE",
10
+ "600": "#002ad4",
11
+ "700": "#0021ab",
12
+ "800": "#001882",
13
+ "900": "#000f59",
14
+ "950": "#000830"
15
+ },
16
+ accent: {
17
+ "50": "#e6fff8",
18
+ "100": "#b3ffec",
19
+ "200": "#80ffe0",
20
+ "300": "#4dfcd4",
21
+ "400": "#1af7c8",
22
+ "500": "#0BF1C3",
23
+ "600": "#09c9a3",
24
+ "700": "#07a183",
25
+ "800": "#057963",
26
+ "900": "#035143",
27
+ "950": "#012923"
28
+ },
29
+ neutral: {
30
+ "0": "#ffffff",
31
+ "50": "#f9fafb",
32
+ "100": "#f3f4f6",
33
+ "200": "#e4e5e8",
34
+ "300": "#d2d4d8",
35
+ "400": "#9fa1a5",
36
+ "500": "#717377",
37
+ "600": "#515357",
38
+ "700": "#3f4044",
39
+ "800": "#26272a",
40
+ "900": "#18191c",
41
+ "950": "#0b0c0d"
42
+ },
43
+ white: "#FFFFFF",
44
+ black: "#000000",
45
+ gradient: {
46
+ primary: "linear-gradient(135deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)",
47
+ primaryReverse: "linear-gradient(135deg, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)",
48
+ primaryVertical: "linear-gradient(180deg, #0033FE 0%, #00A2E9 50%, #0BF1C3 100%)",
49
+ primaryRadial: "radial-gradient(circle, #0BF1C3 0%, #00A2E9 50%, #0033FE 100%)"
50
+ },
51
+ p3Overrides: {
52
+ primary500: "color(display-p3 0.03 0.19 0.99)",
53
+ accent500: "color(display-p3 0.07 0.94 0.79)"
54
+ },
55
+ success: "#22c55e",
56
+ warning: "#f59e0b",
57
+ error: "#ef4444",
58
+ info: "#0033FE"
59
+ };
60
+
1
61
  // src/og/og-template.tsx
2
62
  import { jsx, jsxs } from "react/jsx-runtime";
63
+ function withAlpha(hex, alpha) {
64
+ const n = Number.parseInt(hex.replace("#", ""), 16);
65
+ return `rgba(${n >> 16 & 255},${n >> 8 & 255},${n & 255},${alpha})`;
66
+ }
3
67
  var OG_PALETTE_DARK = {
4
- bg: "#020617",
5
- // colors.neutral["950"]
68
+ bg: colors.neutral["950"],
6
69
  grid: "rgba(255,255,255,0.04)",
7
- glowA: "rgba(0,51,254,0.28)",
8
- // colors.primary["500"] at 28% opacity
9
- glowB: "rgba(11,241,195,0.22)",
10
- // colors.accent["500"] at 22% opacity
11
- title: "#ffffff",
70
+ glowA: withAlpha(colors.primary["500"], 0.28),
71
+ glowB: withAlpha(colors.accent["500"], 0.22),
72
+ title: colors.white,
12
73
  subtitle: "rgba(255,255,255,0.72)",
13
- accent: "#0bf1c3"
14
- // colors.accent["500"]
74
+ accent: colors.accent["500"]
15
75
  };
16
76
  var OG_PALETTE_LIGHT = {
17
- bg: "#ffffff",
77
+ bg: colors.white,
18
78
  grid: "rgba(0,0,0,0.05)",
19
- glowA: "rgba(0,51,254,0.22)",
20
- // colors.primary["500"] at 22% opacity
21
- glowB: "rgba(11,241,195,0.20)",
22
- // colors.accent["500"] at 20% opacity
23
- title: "#0a0a0a",
24
- subtitle: "rgba(10,10,10,0.66)",
25
- accent: "#0033fe"
26
- // colors.primary["500"]
79
+ glowA: withAlpha(colors.primary["500"], 0.22),
80
+ glowB: withAlpha(colors.accent["500"], 0.2),
81
+ title: colors.neutral["950"],
82
+ subtitle: withAlpha(colors.neutral["950"], 0.66),
83
+ accent: colors.primary["500"]
27
84
  };
28
85
  function OgTemplate({ title, subtitle, brandName, palette }) {
29
86
  return /* @__PURE__ */ jsxs(