diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 20d09c0..1525518 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -63,6 +63,18 @@ export default defineConfig({ ['link', { rel: 'icon', type: 'image/svg+xml', href: `${SITE_BASE}favicon.svg` }], ], themeConfig: { + /** + * 导航左上角的品牌名 —— 主体固定为「雪羊科技」,不要替换成别的字。 + * + * 后面那截 物联网卡与设备管理平台 + * 是品牌定位副标题(样式见 portal.css 的 .site-subtitle), + * 与品牌名一起构成左上角的两行结构:上行品牌、下行定位。 + * + * 注意区分: + * config.title → 浏览器标签页与 SEO 用的完整站名,不显示在导航 + * siteTitle → 左上角实际渲染的内容 + * 校验见 scripts/check-nav-brand.mjs(构建前自动执行)。 + */ siteTitle: '雪羊科技物联网卡与设备管理平台', logo: { src: '/snowgoat-logo.png', alt: '雪羊科技' }, nav: [ diff --git a/.vitepress/theme/styles/portal.css b/.vitepress/theme/styles/portal.css index d47f3a2..0e2733e 100644 --- a/.vitepress/theme/styles/portal.css +++ b/.vitepress/theme/styles/portal.css @@ -1075,3 +1075,50 @@ html.portal-nav-home .VPNavBarMenuLink.active:not([data-anchor-active]) { } } +/* ============================================================ + 导航左上角品牌区:两行结构 + 第一行:雪羊科技(品牌名) + 第二行:物联网卡与设备管理平台(定位副标题,更弱) + siteTitle 里的副标题是 ,由 v-html 插入。 + 注意:它的父级是 VitePress 自己的裸 (无 class), + 所以选择器不能写 .title-text .site-subtitle —— 那个类根本不存在。 + 直接用 .VPNavBarTitle .site-subtitle 即可。 + ============================================================ */ +.VPNavBarTitle .title { + line-height: 1.2; + gap: 10px; +} + +/* 品牌名与副标题的容器:两行紧凑排列,整体垂直居中 */ +.VPNavBarTitle .title > span { + display: flex; + flex-direction: column; + justify-content: center; + line-height: 1.15; + /* 品牌名与副标题作为一个整体在导航高度内垂直居中 */ + height: var(--vp-nav-height); +} + +.VPNavBarTitle .site-subtitle { + display: block; + margin-top: 3px; + font-size: 11px; + font-weight: 500; + letter-spacing: 0.03em; + line-height: 1.25; + color: var(--p-ink-3); +} + +@media (max-width: 860px) { + .VPNavBarTitle .site-subtitle { + font-size: 10.5px; + margin-top: 0; + } +} + +/* 窄屏下副标题可能挤压,超窄时隐藏,只留品牌名 */ +@media (max-width: 480px) { + .VPNavBarTitle .site-subtitle { + display: none; + } +} diff --git a/package.json b/package.json index 994db3b..13ee4dd 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dev": "vitepress dev . --host 127.0.0.1", "build": "vitepress build .", "preview": "vitepress preview . --host 127.0.0.1", - "check": "node scripts/check-registry.mjs", + "check": "node scripts/check-registry.mjs && node scripts/check-nav-brand.mjs", "prebuild": "npm run check", "predev": "npm run check" }, diff --git a/scripts/check-nav-brand.mjs b/scripts/check-nav-brand.mjs new file mode 100644 index 0000000..2de2c37 --- /dev/null +++ b/scripts/check-nav-brand.mjs @@ -0,0 +1,67 @@ +#!/usr/bin/env node +/** + * 校验导航左上角品牌名。 + * + * 约定:themeConfig.siteTitle 必须以「雪羊科技」为主体。 + * + * 允许的形态: + * 雪羊科技 + * 雪羊科技物联网卡与设备管理平台 + * + * 不允许: + * - 主体被换成别的品牌名(如「雪羊科技开源」「雪羊开源」) + * - 整个删掉 siteTitle(VitePress 会回落到 config.title 的超长站名) + * - logo 缺失或指向不存在的文件 + * + * config.title 是浏览器标签页/SEO 用的完整站名,和这里不是一回事。 + */ +import { readFileSync, existsSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..') +const configPath = join(root, '.vitepress', 'config.mts') +const src = readFileSync(configPath, 'utf-8') + +/** 品牌名主体,左上角必须以此开头 */ +const BRAND = '雪羊科技' + +// 取 siteTitle 的赋值(单引号或双引号,内容可含 HTML) +const m = src.match(/siteTitle\s*:\s*(['"])([\s\S]*?)\1\s*,/) +if (!m) { + console.error('[nav-brand] themeConfig.siteTitle 缺失。') + console.error(` 左上角会回落到 config.title(完整长名),应显式设为「${BRAND}」。`) + process.exit(1) +} + +const raw = m[2].trim() + +// 去掉可选的副标题 span,只看品牌主体 +const withoutSubtitle = raw.replace( + /]*>[\s\S]*?<\/span>/g, + '' +).trim() + +if (withoutSubtitle !== BRAND) { + console.error(`[nav-brand] siteTitle 的品牌主体是「${withoutSubtitle}」,应为「${BRAND}」。`) + console.error(' 左上角品牌名固定,副标题可保留但主体不要换。') + process.exit(1) +} + +// logo 检查:既要配置在,也要文件真的存在 +const logoNeedle = 'snowgoat-logo.png' +if (!src.includes(logoNeedle)) { + console.error(`[nav-brand] 未找到 logo 配置 ${logoNeedle}。`) + process.exit(1) +} + +const logoFile = join(root, 'pages', 'public', logoNeedle) +if (!existsSync(logoFile)) { + console.error(`[nav-brand] logo 文件不存在:pages/public/${logoNeedle}`) + process.exit(1) +} + +const hasSubtitle = /site-subtitle/.test(raw) +console.log( + `[nav-brand] OK:左上角「${BRAND}」${hasSubtitle ? ' + 定位副标题' : ''} + ${logoNeedle}` +)