Prhub

#32835 [docs] Rotate popular models on the landing pages, lead the Cookbook nav with Kimi

原始 PR 作者 zijiexia 合并时间 2026-07-30 08:46 文件变更 6 提交数 2 评论 3 代码增减 +587 / -229

执行摘要

文档首页和导航引入模型轮播组件并重新排序

PR body 指出三个问题:

1) 首页硬编码 Kimi-K3 hero,更换模型需重写页面;
2) Cookbook 首页无模型入口;
3) 导航以 Qwen 为首,希望突出热门模型。

对于文档和前端开发者,值得精读:数据与展示分离、可访问性处理、Mintlify 平台限制下的组件设计。对于核心引擎开发者可忽略。

讨论亮点

PR 无 review 评论,但 PR body 详细说明了设计权衡:因为 Mintlify 在 hydration 时分别求值每个导出组件,所以两个变体(hero/strip)必须共享一个组件内部的轮播状态和定时器,而非模块级辅助函数;只让活动 slide 不透明以避免中途看到两个半绘制卡片;控制按钮(‹ › 和圆点)放在卡片边框内而非 slide 内容中,避免内部箭头被误解;prefers-reduced-motion 用户完全禁用 auto-play 和 transition。

实现拆解

  1. 创建数据合约:新增 docs_new/src/snippets/configs/popular-models.jsx,导出 popularModels 数组,每项包含模型名称、厂商、链接、logo、标签和 hero 块(用于首页 banner)。
  2. 创建轮播组件:新增 docs_new/src/snippets/_popular_models.jsx,导出 PopularModels 组件,支持 variant="hero"(全宽 banner)和默认的 variant="strip"(紧凑行)。组件内部通过 React state 管理索引、暂停和 prefers-reduced-motion,定时轮播每 9s/6s 切换,暂停于悬停/聚焦,且无动画降级。
  3. 替换首页硬编码:修改 docs_new/index.mdx,将约 180 行的内联 Kimi-K3 hero 替换为 <PopularModels models={popularModels} variant="hero" />,导入新组件和数据。
  4. 在 Cookbook 首页添加入口:修改 docs_new/cookbook/intro.mdx,导入并放置 <PopularModels models={popularModels} />(strip 变体),使 Cookbook 首页展示热门模型列表。
  5. 重新排序导航和卡片网格:修改 docs_new/docs.json,将 Kimi (Moonshot AI)、Thinking Machines、GLM 三组提到 Autoregressive Models 导航的顶部(原 Qwen 之前);同步更新 docs_new/cookbook/autoregressive/intro.mdx 中的卡片顺序以匹配导航。
文件 模块 状态 重要度
docs_new/src/snippets/_popular_models.jsx 文档组件 added 9.36
docs_new/src/snippets/configs/popular-models.jsx 文档数据 added 7.11
docs_new/index.mdx 文档页面 modified 5.11
docs_new/docs.json 文档配置 modified 4.95
docs_new/cookbook/autoregressive/intro.mdx 文档页面 modified 3.98
docs_new/cookbook/intro.mdx 文档页面 modified 3.01

关键符号

PopularModels

关键源码片段

docs_new/src/snippets/_popular_models.jsx data-contract

核心轮播组件,支持 hero 和 strip 两种变体,处理轮播逻辑、可访问性和动画降级。

// Auto-rotating carousel shared by docs home (variant="hero") and Cookbook home (variant="strip").
// Rotation pauses on hover/focus and respects prefers-reduced-motion.
// One export for both shapes: Mintlify evaluates each component independently at hydration,
// so shared state (timer, index, controls) must sit inside one component.export const PopularModels = ({
  models = [],
  variant = "strip",
  interval = variant === "hero" ? 9000 : 6000, // hero blurb needs more time
  label = "Popular models",
}) => {
  const [index, setIndex] = useState(0);
  const [paused, setPaused] = useState(false);
  const [reduceMotion, setReduceMotion] = useState(false);  // Listen for prefers-reduced-motion changes
  useEffect(() => {
    if (typeof window === "undefined" || !window.matchMedia) return;
    const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
    const sync = () => setReduceMotion(mq.matches);
    sync();
    mq.addEventListener("change", sync);
    return () => mq.removeEventListener("change", sync);
  }, []);  const count = models.length;
  const isHero = variant === "hero";  // Functional update to avoid stale closure over index
  useEffect(() => {
    if (count < 2 || paused || reduceMotion) return;
    const id = window.setInterval(
      () => setIndex((i) => (i + 1) % count),
      Math.max(2000, interval)
    );
    return () => window.clearInterval(id);
  }, [count, paused, reduceMotion, interval]);  // Clamp active index in case list shortened mid-rotation
  const active = count ? Math.min(index, count - 1) : 0;  if (!count) return null;  // Only active slide is opaque to prevent seeing two half-drawn cards mid-transition
  const slideStyle = (i) => ({
    flex: "0 0 100%",
    minWidth: 0,
    opacity: i === active ? 1 : 0,
    pointerEvents: i === active ? "auto" : "none",
    transition: reduceMotion ? "none" : "opacity 0.3s ease",
  });  // Tag chip component used inside slides
  const tagChip = (t, big) => (
    <span key={t} style={{ padding: big ? "0.35rem 0.65rem" : "0.15rem 0.45rem", borderRadius: "999px", background: big ? "rgba(255, 255, 255, 0.1)" : "rgba(255, 255, 255, 0.12)", color: "rgba(255, 255, 255, 0.9)", fontSize: big ? "0.78rem" : "0.72rem", fontWeight: 650 }}>{t}</span>
  );  // ... (heroSlide, stripSlide, controls, and render logic omitted for brevity)
  return ( /* layout */ );
};
docs_new/src/snippets/configs/popular-models.jsx data-contract

数据源文件,定义三个热门模型(Kimi-K3、Inkling、GLM-5.2)的展示信息,统一维护。

// Single literal export: Mintlify re-evaluates at hydration, so no spreads/calls/IIFE.
// Both docs home (variant="hero") and Cookbook home (strip) walk this list in order.export const popularModels = [
  {
    name: "Kimi-K3",
    vendor: "Moonshot AI",
    href: "/cookbook/autoregressive/Moonshotai/Kimi-K3",
    logo: "/cards/logos/moonshotai.png",
    badge: "New",
    tags: ["8 platforms", "PD disagg", "DSPARK"],
    hero: {
      eyebrow: "Featured model · New",
      headline: "Meet Kimi-K3 on SGLang",
      blurb: "SGLang natively implements and deeply optimizes K3's new architecture with fused KDA decode kernels, DP attention, MTP, PD disaggregation, and KDA-aware prefix caching. Kimi-K3 is supported on both NVIDIA and AMD GPUs.",
      tags: ["2.8T parameters", "Fused KDA decode", "NVIDIA + AMD"],
      cta: "Open the Kimi-K3 cookbook",
      caption: "Kimi-K3 deployment guide",
    },
  },
  // Inkling and GLM-5.2 entries follow a similar structure
];

评论区精华

Mintlify 组件共享限制与设计权衡 设计

PR 作者在 body 中说明,因为 Mintlify 分别求值每个导出组件,所以两个变体必须共享一个组件内部的轮播状态和定时器,不能使用模块级辅助函数。此外,控制按钮放在卡片边框内而非 slide 内容中,避免内部箭头被误解;只让 active slide 透明以避免中途看到两个半绘制卡片。

结论:作者决定将两种变体放在同一个 PopularModels 组件中,通过 variant 参数区分,内部通过条件渲染实现不同布局。 · 已解决

风险与影响

这是纯文档变更,无运行时性能或安全风险。主要风险是 Mintlify 对 JSX 组件的兼容性限制——组件必须在单个导出中,已在设计中规避。无测试覆盖,但作者已验证本地 mint dev 渲染通过。导航重新排序虽只移动展示顺序不改变 URL,但若未来文档结构变化可能需同步维护。

用户侧:文档首页和 Cookbook 首页展示体验提升,热门模型更容易被发现;导航顺序变化让新用户先看到 Kimi、Thinking Machines、GLM 等主推模型。团队侧:新增模型只需编辑 popular-models.jsx 列表,无需改动页面模板;模块化设计降低了维护成本。影响范围仅限 docs 站点,无功能影响。

无测试覆盖 Mintlify 组件兼容性约束

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论