diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..8491f54 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/components/contributors.tsx b/src/components/contributors.tsx index aeb674e..606fdaf 100644 --- a/src/components/contributors.tsx +++ b/src/components/contributors.tsx @@ -29,6 +29,9 @@ export function Contributors() {

感谢这些为 Mix Space 开源社区做出贡献的开发者

+

+ 共 {humans.length + bots.length} 位贡献者 +

{/* Human contributors */} diff --git a/src/components/docs.tsx b/src/components/docs.tsx index bd4a486..7d5dec0 100644 --- a/src/components/docs.tsx +++ b/src/components/docs.tsx @@ -15,7 +15,7 @@ import { ViewOptionsPopover, } from 'fumadocs-ui/layouts/docs/page'; import { - Rocket, + Compass, Server, Settings, PaintRoller, @@ -27,7 +27,7 @@ import { baseOptions } from '@/lib/layout.shared'; import { RootProvider } from '@/components/root-provider'; const iconMap: Record = { - '新手入门': , + '新手入门': , '部署': , '配置': , '前端主题': , diff --git a/src/components/features.tsx b/src/components/features.tsx index 87d27f6..8429153 100644 --- a/src/components/features.tsx +++ b/src/components/features.tsx @@ -5,8 +5,8 @@ import { Bot, Cpu, FileEdit, + Palette, Settings, - Sparkles, } from 'lucide-react'; const p = 'text-sm leading-relaxed text-neutral-600 dark:text-neutral-400'; @@ -57,8 +57,8 @@ export function Features() {

后端 API 和前端界面分开部署,可以各自升级、各自扩容。

- - + +

提供 Yohaku、Shiro 等官方主题,界面简洁现代,支持深色模式。

diff --git a/src/components/footer.tsx b/src/components/footer.tsx index 2188501..27abe36 100644 --- a/src/components/footer.tsx +++ b/src/components/footer.tsx @@ -27,7 +27,19 @@ export function Footer() { > GitHub - © 2021-{new Date().getFullYear()} Mix Space Team + + 隐私政策 + + + 服务条款 + + © 2021-{new Date().getFullYear()} Mix Space Team diff --git a/src/components/preview-images.tsx b/src/components/preview-images.tsx index d41ea09..401f2ca 100644 --- a/src/components/preview-images.tsx +++ b/src/components/preview-images.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { Image } from '@/components/image'; import type { ResponsiveImage } from '@/lib/images'; import { cn } from '@/lib/cn'; @@ -9,18 +9,32 @@ interface PreviewItem extends ResponsiveImage { export function PreviewImages({ previews }: { previews: PreviewItem[] }) { const [active, setActive] = useState(0); + const [paused, setPaused] = useState(false); + const [reducedMotion, setReducedMotion] = useState(false); const btnRefs = useRef<(HTMLButtonElement | null)[]>([]); + const tablistRef = useRef(null); const [indicator, setIndicator] = useState({ left: 0, width: 0 }); - // Auto-rotate every 4s + // Auto-rotate every 4s;reduced-motion 用户不自动播放,悬停时暂停 useEffect(() => { + if (paused || reducedMotion) return; const timer = setInterval(() => { setActive((prev) => (prev + 1) % previews.length); }, 4000); return () => clearInterval(timer); - }, []); + }, [paused, reducedMotion, previews.length]); + // prefers-reduced-motion:响应式监听系统设置变化 useEffect(() => { + const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); + const onChange = () => setReducedMotion(mq.matches); + onChange(); + mq.addEventListener('change', onChange); + return () => mq.removeEventListener('change', onChange); + }, []); + + // 指示条位置以 tab 相对容器的偏移计算,切换或尺寸变化时重算 + const updateIndicator = useCallback(() => { const btn = btnRefs.current[active]; if (!btn) return; const parent = btn.parentElement; @@ -33,8 +47,25 @@ export function PreviewImages({ previews }: { previews: PreviewItem[] }) { }); }, [active]); + useEffect(() => { + updateIndicator(); + }, [updateIndicator]); + + // 容器尺寸变化(窗口 resize、字体加载等)时重算指示条位置 + useEffect(() => { + const el = tablistRef.current; + if (!el) return; + const ro = new ResizeObserver(updateIndicator); + ro.observe(el); + return () => ro.disconnect(); + }, [updateIndicator]); + return ( -
+
setPaused(true)} + onMouseLeave={() => setPaused(false)} + > {/* 环境辉光:品牌色弥散阴影;内收一圈,避免模糊溢出到卡片外被误认为图片漏底 */}
(
-
+
{ btnRefs.current[i] = el; }} + role="tab" + id={`preview-tab-${i}`} + aria-selected={active === i} + aria-controls={`preview-panel-${i}`} onClick={() => setActive(i)} className={cn( 'relative z-10 h-8 px-3 sm:px-5 text-[13px] sm:text-sm font-medium rounded-full transition-colors whitespace-nowrap', diff --git a/src/components/theme-card.tsx b/src/components/theme-card.tsx index 404bac0..bd4f3f3 100644 --- a/src/components/theme-card.tsx +++ b/src/components/theme-card.tsx @@ -101,6 +101,7 @@ export function ThemeCard({ width={24} height={24} className="rounded-full bg-neutral-200 dark:bg-neutral-800" + loading="lazy" /> {author} diff --git a/src/layouts/base.astro b/src/layouts/base.astro index f6345e0..6fd9155 100644 --- a/src/layouts/base.astro +++ b/src/layouts/base.astro @@ -47,11 +47,15 @@ const ogImage = new URL(image ?? '/og.png', Astro.site).href; {pageTitle} + + {markdownUrl && } - + + + - + + + 跳到主要内容 + + +
+ +
diff --git a/src/pages/404.astro b/src/pages/404.astro index e032b53..7634932 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -4,7 +4,7 @@ import Base from '@/layouts/base.astro';
-

404

+

页面未找到

你访问的页面不存在或已被移动,请检查地址是否正确。 @@ -23,5 +23,30 @@ import Base from '@/layouts/base.astro'; 阅读文档

+ diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..3c86ee2 --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,84 @@ +--- +import Base from '@/layouts/base.astro'; +--- + + +
+

隐私政策

+

最近更新:2026 年 10 月

+
+

+ 本页说明 Mix Space 文档站(下称「本站」)在提供服务的过程中处理哪些信息,以及 + Mix Space 自托管实例的数据归属。本站是 Mix Space 开源项目的官方文档站点, + 不设注册、登录或评论功能。 +

+ +
+

+ 我们收集哪些信息 +

+

+ 本站为静态文档站点,不会主动向你收集姓名、电子邮箱、电话号码等个人身份信息。 + 你可以匿名浏览全部页面,使用本站不要求提供任何个人信息。 +

+

+ 本站不建立可识别个人身份的档案,也不主动留存除访问日志之外的数据。 +

+
+ +
+

+ 访问日志 +

+

+ 与大多数网站一样,托管服务提供方可能记录访问日志,包括 IP 地址、浏览器与操作系统 + 类型、访问时间及所请求的页面地址。此类日志仅用于保障站点可用性、排查故障与进行 + 必要的流量统计,不会用于建立用户画像或对外出售。如需了解日志的留存期限与删除方式, + 请参考托管服务提供方的说明。 +

+
+ +
+

+ Cookie 与本地存储 +

+

+ 本站不使用用于跨站跟踪的 Cookie。主题偏好(如深色模式)等设置仅保存在你的浏览器 + 本地存储中,随浏览器数据一并由你控制,不会上传到本站的服务器。 +

+
+ +
+

+ 第三方资源 +

+

+ 页面中的部分资源(如贡献者头像)由第三方服务提供。加载这些资源时,你的浏览器会 + 与对应服务方建立连接,相关请求由该服务方按其自身的隐私政策处理。 +

+
+ +
+

+ 自托管实例的数据归属 +

+

+ Mix Space 是自托管的开源内容管理系统。当你自行部署 Mix Space + 时,实例中产生的一切数据(包括你与访客的内容、日志与备份)均由实例持有者持有并 + 负责,与本站无关。实例持有者应自行确定隐私政策、保障数据安全,并遵守所在司法辖区 + 适用法律的要求。 +

+
+ +
+

+ 政策更新与联系方式 +

+

+ 本页内容可能随站点结构调整而更新,更新后在本页直接生效。对本页所述内容如有疑问, + 可通过开源仓库的 Issue 或社区讨论渠道与我们联系。 +

+
+
+
+ diff --git a/src/pages/terms.astro b/src/pages/terms.astro new file mode 100644 index 0000000..2a46968 --- /dev/null +++ b/src/pages/terms.astro @@ -0,0 +1,88 @@ +--- +import Base from '@/layouts/base.astro'; +--- + + +
+

服务条款

+

最近更新:2026 年 10 月

+
+

+ 本页约定你使用 Mix Space 文档站(下称「本站」)所提供的文档内容与相关资源时应 + 遵循的条款。访问或使用本站,即表示你已阅读并同意本页内容。 +

+ +
+

+ 内容免责声明 +

+

+ 本站的文档内容按「原样」提供。尽管我们尽力保证内容准确与及时,但不对其完整性、 + 准确性或对特定用途的适用性作出任何明示或暗示的保证。因参考或使用文档内容而产生的 + 任何后果,在法律允许的最大范围内,Mix Space 团队不承担相应责任。 +

+
+ +
+

+ 开源许可 +

+

+ Mix Space 是开源自托管项目,其各组件按各自仓库中声明的开源许可发布;本站的文档 + 内容遵循项目对应的许可条款。使用、修改或分发相关代码前,请先阅读对应仓库中的许可 + 文本,并以许可文本为准。 +

+
+ +
+

+ 文档时效性 +

+

+ Mix Space 持续演进,文档内容可能滞后于最新版本。当代码实现与文档描述存在出入时, + 以官方仓库的代码与发布说明为准;我们也欢迎通过 Issue 指出文档中的过时之处。 +

+
+ +
+

+ 外部链接 +

+

+ 本站包含指向第三方网站的链接,这些链接仅为方便查阅而提供,不代表对第三方内容的 + 认可或保证。访问第三方网站产生的任何问题,由访问者自行判断并承担。 +

+
+ +
+

+ 社区行为 +

+

+ 通过 Issue、讨论区等渠道参与项目社区时,请遵守项目仓库中的贡献指南与行为准则, + 尊重其他参与者。违反相应准则的行为,项目维护者有权依其规则处理。 +

+
+ +
+

+ 商标与标识 +

+

+ 「Mix Space」名称与相关标识归项目持有者所有。第三方在使用这些名称与标识时,应 + 遵循开源社区惯例及相应许可的要求,不得以暗示官方背书的方式进行宣传。 +

+
+ +
+

+ 条款变更 +

+

+ 本页条款可能随项目发展而更新,更新后在本页直接生效;重大变更会在站内以适当方式 + 提示。若你在条款更新后继续使用本站,即视为接受更新后的条款。 +

+
+
+
+ diff --git a/src/styles/global.css b/src/styles/global.css index e547974..ab5e691 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -12,6 +12,18 @@ --font-sans: 'Inter Variable', ui-sans-serif, system-ui, sans-serif; } +/* + * 文档区主题色覆盖:与首页品牌 teal 统一(fumadocs 默认 primary 是灰蓝/中性色)。 + * 只覆盖 primary:亮色 teal-700、暗色 teal-400(teal-600 对正文/白字仅约 3.4:1, + * 不满足 WCAG AA,故亮色取同色相加深一档),两套模式对比度均达标。 + */ +:root { + --color-fd-primary: var(--color-teal-700); +} +.dark { + --color-fd-primary: var(--color-teal-400); +} + html { scrollbar-gutter: stable; } @@ -121,10 +133,10 @@ html > body[data-scroll-locked] { animation-play-state: paused; } -/* 键盘焦点环(全站统一) */ +/* 键盘焦点环(全站统一):跟随主题 primary,文档区与首页品牌 teal 保持一致 */ @layer base { :focus-visible { - outline: 2px solid oklch(0.62 0.12 190); + outline: 2px solid var(--color-fd-primary); outline-offset: 2px; border-radius: 4px; }