f37f1f3b59
ci-pangolin / Lint — shellcheck (pull_request) Successful in 10s
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 29s
ci-pangolin / OpenAPI Sync Check (pull_request) Successful in 32s
ci-pangolin / Cleartext Scan — Android 禁明文 (pull_request) Successful in 21s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 24s
ci-pangolin / Flutter — analyze + test (pull_request) Failing after 21s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Successful in 10s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (pull_request) Successful in 8s
ci-pangolin / Go — build + test (pull_request) Successful in 10s
Deploy Site / deploy-site (push) Successful in 2m33s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Failing after 4m33s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (pull_request) Successful in 19s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 13m16s
#1 官网语言下拉漂移:根因=官网 CSP 无 unsafe-inline,Header.jsx 下拉全用内联 style= 被浏览器拦→菜单飘视口右边。修:搬进 website.css 的 .langwrap/.langsel /.langmenu/.langmenu a(全走 var(--token),right:0 锚按钮下方,min-width 160 加宽),Header.jsx 删净内联 style。 #2 主页登录态右上角「用户中心」按钮:Header.jsx 读同源 localStorage 'pg_uc_refresh',有则 .linklogin 位显「用户中心」(→ /user/)、无则「Log in」。 i18n nav.center 6 语言。 #3 用户中心「返回主页」按钮:UserCenter.tsx 顶栏加 <a href="/">(home 图标), i18n backHome 6 语言。 顺带堵同源闸漏洞:check-l1-sync ③ 原只匹配带引号图标键('refresh-cw'),漏了 裸标识符键(home),导致新加的 home 未被校验就通过。修解析器匹配裸键;并按 ds-flow 把 home 登记进 design/prototype/icons.js。同源闸复跑绿(裸键现全受检)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
99 lines
3.7 KiB
Plaintext
99 lines
3.7 KiB
Plaintext
---
|
||
/**
|
||
* Site.astro — 官网整页布局(构建期单显一种语言)。
|
||
* 两条路由复用:/ → lang="zh",/en/ → lang="en"(铁律 6 单显不并排)。
|
||
*
|
||
* 样式来源顺序(全部自托管,无第三方 CDN):
|
||
* @fontsource/* → 自托管字体 @font-face(替代原型的 Google Fonts CDN)
|
||
* tokens.gen.css → 由 design/colors_and_type.css 生成(唯一令牌真相源)
|
||
* website.css → 逐字迁自 ui_kits/website
|
||
* site-extra.css → 仅承载「原型内联 style= → class」的等价工具类
|
||
*/
|
||
import '@fontsource/sora/500.css';
|
||
import '@fontsource/sora/600.css';
|
||
import '@fontsource/sora/700.css';
|
||
import '@fontsource/manrope/400.css';
|
||
import '@fontsource/manrope/500.css';
|
||
import '@fontsource/manrope/600.css';
|
||
import '@fontsource/manrope/700.css';
|
||
import '@fontsource/noto-sans-sc/400.css';
|
||
import '@fontsource/noto-sans-sc/500.css';
|
||
import '@fontsource/noto-sans-sc/700.css';
|
||
import '@fontsource/jetbrains-mono/400.css';
|
||
import '@fontsource/jetbrains-mono/500.css';
|
||
|
||
import '../styles/tokens.gen.css';
|
||
import '../styles/website.css';
|
||
import '../styles/site-extra.css';
|
||
|
||
import { createT, type Lang } from '../i18n/strings';
|
||
import Header from '../components/Header.jsx';
|
||
import AnnouncementBar from '../components/AnnouncementBar.jsx';
|
||
import Hero from '../components/Hero.astro';
|
||
import Features from '../components/Features.astro';
|
||
import WhySignup from '../components/WhySignup.astro';
|
||
import Pricing from '../components/Pricing.astro';
|
||
import Download from '../components/Download.astro';
|
||
import Docs from '../components/Docs.astro';
|
||
// Blog 区暂隐藏(占位内容未就绪);有真实文章后恢复 import + <Blog/> + 导航/页脚链接。
|
||
import CtaBand from '../components/CtaBand.astro';
|
||
import Footer from '../components/Footer.astro';
|
||
|
||
interface Props { lang: Lang }
|
||
const { lang } = Astro.props;
|
||
const t = createT(lang);
|
||
|
||
// 语言 → <html lang> / og:locale / 路由前缀(默认 en = /,其余 /<lang>/)。
|
||
const HTML_LANG: Record<Lang, string> = { zh: 'zh-CN', en: 'en', ja: 'ja', ko: 'ko', ru: 'ru', es: 'es' };
|
||
const OG_LOCALE: Record<Lang, string> = { zh: 'zh_CN', en: 'en_US', ja: 'ja_JP', ko: 'ko_KR', ru: 'ru_RU', es: 'es_ES' };
|
||
const ALL_LANGS: Lang[] = ['en', 'zh', 'ja', 'ko', 'ru', 'es'];
|
||
const langPath = (l: Lang) => (l === 'en' ? '/' : `/${l}/`);
|
||
|
||
const headerT = {
|
||
product: t('nav.product'),
|
||
pricing: t('nav.pricing'),
|
||
download: t('nav.download'),
|
||
docs: t('nav.docs'),
|
||
blog: t('nav.blog'),
|
||
login: t('nav.login'),
|
||
center: t('nav.center'),
|
||
get: t('nav.get'),
|
||
suBtn: t('su.btn'),
|
||
};
|
||
---
|
||
<!doctype html>
|
||
<html lang={HTML_LANG[lang]}>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>{t('meta.title')}</title>
|
||
<meta name="description" content={t('meta.desc')} />
|
||
<meta name="robots" content="index,follow" />
|
||
<link rel="canonical" href={new URL(langPath(lang), Astro.site).href} />
|
||
{ALL_LANGS.map((l) => (
|
||
<link rel="alternate" hreflang={HTML_LANG[l]} href={new URL(langPath(l), Astro.site).href} />
|
||
))}
|
||
<link rel="alternate" hreflang="x-default" href={new URL('/', Astro.site).href} />
|
||
<meta name="theme-color" content="#B96A3D" />
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:title" content={t('meta.title')} />
|
||
<meta property="og:description" content={t('meta.desc')} />
|
||
<meta property="og:locale" content={OG_LOCALE[lang]} />
|
||
</head>
|
||
<body>
|
||
<AnnouncementBar client:idle text={t('ann.txt')} cta={t('ann.cta')} />
|
||
<Header client:load lang={lang} t={headerT} />
|
||
|
||
<a id="top"></a>
|
||
|
||
<Hero t={t} />
|
||
<Features t={t} />
|
||
<WhySignup t={t} />
|
||
<Pricing t={t} lang={lang} />
|
||
<Download t={t} />
|
||
<Docs t={t} />
|
||
<CtaBand t={t} />
|
||
<Footer t={t} />
|
||
</body>
|
||
</html>
|