@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.
- package/README.md +6 -3
- package/dist/components/Logo.js +0 -1
- package/dist/components/Logo.js.map +1 -1
- package/dist/index.d.ts +82 -89
- package/dist/index.js +271 -263
- package/dist/index.js.map +1 -1
- package/dist/metadata-helpers.d.ts +21 -1
- package/dist/metadata-helpers.js +21 -13
- package/dist/metadata-helpers.js.map +1 -1
- package/dist/metadata.d.ts +18 -24
- package/dist/metadata.js +16 -23
- package/dist/metadata.js.map +1 -1
- package/dist/og/index.d.ts +3 -3
- package/dist/og/index.js +75 -18
- package/dist/og/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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 };
|
package/dist/metadata-helpers.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": "#
|
|
75
|
-
"100": "#
|
|
76
|
-
"200": "#
|
|
77
|
-
"300": "#
|
|
78
|
-
"400": "#
|
|
79
|
-
"500": "#
|
|
80
|
-
"600": "#
|
|
81
|
-
"700": "#
|
|
82
|
-
"800": "#
|
|
83
|
-
"900": "#
|
|
84
|
-
"950": "#
|
|
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:
|
|
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":[]}
|
package/dist/metadata.d.ts
CHANGED
|
@@ -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": "#
|
|
93
|
-
readonly "100": "#
|
|
94
|
-
readonly "200": "#
|
|
95
|
-
readonly "300": "#
|
|
96
|
-
readonly "400": "#
|
|
97
|
-
readonly "500": "#
|
|
98
|
-
readonly "600": "#
|
|
99
|
-
readonly "700": "#
|
|
100
|
-
readonly "800": "#
|
|
101
|
-
readonly "900": "#
|
|
102
|
-
readonly "950": "#
|
|
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
|
|
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,
|
|
127
|
-
readonly cn: "\"
|
|
128
|
-
readonly sans: "\"Geist\", \"
|
|
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: "\"
|
|
131
|
-
readonly heading: "\"
|
|
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": "#
|
|
75
|
-
"100": "#
|
|
76
|
-
"200": "#
|
|
77
|
-
"300": "#
|
|
78
|
-
"400": "#
|
|
79
|
-
"500": "#
|
|
80
|
-
"600": "#
|
|
81
|
-
"700": "#
|
|
82
|
-
"800": "#
|
|
83
|
-
"900": "#
|
|
84
|
-
"950": "#
|
|
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,
|
|
106
|
-
cn: '"
|
|
107
|
-
sans: '"Geist", "
|
|
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: '"
|
|
110
|
-
heading: '"
|
|
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,
|
package/dist/metadata.js.map
CHANGED
|
@@ -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":[]}
|
package/dist/og/index.d.ts
CHANGED
|
@@ -40,9 +40,9 @@ interface OgTemplateProps {
|
|
|
40
40
|
palette: OgThemePalette;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
* Dark-theme palette.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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: "
|
|
5
|
-
// colors.neutral["950"]
|
|
68
|
+
bg: colors.neutral["950"],
|
|
6
69
|
grid: "rgba(255,255,255,0.04)",
|
|
7
|
-
glowA: "
|
|
8
|
-
|
|
9
|
-
|
|
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: "
|
|
14
|
-
// colors.accent["500"]
|
|
74
|
+
accent: colors.accent["500"]
|
|
15
75
|
};
|
|
16
76
|
var OG_PALETTE_LIGHT = {
|
|
17
|
-
bg:
|
|
77
|
+
bg: colors.white,
|
|
18
78
|
grid: "rgba(0,0,0,0.05)",
|
|
19
|
-
glowA: "
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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(
|