渲染器架构

Renderer 是 Clarify 的客户端运行时。它消费 @clarify-labs/cli 内部引擎生成的已处理内容(路由、MDX 组件、API 数据),并将其渲染为交互式文档站点。


渲染理念

  • 静态优先 + 客户端 Hydration:构建时生成 HTML,浏览器端再 Hydration 为 React Router 应用
  • 静态构建时数据:CLI 在构建时生成路由清单、导航树、配置和 OpenAPI 注册表。渲染器在运行时消费这些静态产物
  • 组件驱动 UI:MDX 元素映射到 @clarify-labs/renderer 原语,应用壳负责导航、搜索、主题切换和内容操作
  • 零运行时配置解析:所有配置在构建时解析并烘焙到虚拟模块中

运行时架构

客户端入口

CLI 生成 virtual:clarify-entry-client,并在 HTML 中注入该入口。入口会导入渲染器样式、虚拟模块数据并调用 render

import '@clarify-labs/renderer/style.css'
import { render } from '@clarify-labs/renderer/client'
import { routes, navigation } from 'virtual:clarify-routes'
import { config } from 'virtual:clarify-config'
import { openApis } from 'virtual:clarify-openapi-registry'

render({ config, routes, navigation, openApis })

用户项目不需要维护 source/main.tsx 或自定义 Vite 入口。

服务端入口

静态生成阶段使用临时 SSR 入口调用 renderToHTML

import { renderToHTML } from '@clarify-labs/renderer/server'
import { routes, navigation } from 'virtual:clarify-routes/server'
import { config } from 'virtual:clarify-config'
import { openApis } from 'virtual:clarify-openapi-registry'

export function render(url: string) {
  return renderToHTML({ config, routes, navigation, openApis, url })
}

应用壳

AppShell 是渲染器的顶级壳,接收配置、路由和导航树:

<AppShell config={config} routes={routes} navigation={navigation} />

布局区域

区域职责固定?
Header搜索、顶部导航、语言切换、主题切换是(顶部)
Navigation来自 navigation 的层级侧边栏是(左侧桌面端,移动端抽屉)
Main渲染的页面内容(MDX 或 API 页面)可滚动
ContentActions复制页面内容、复制原始 Markdown / OpenAPI 等操作是(内容顶部)

虚拟模块系统

渲染器依赖构建时由插件生成的虚拟模块。它们是插件和渲染器之间唯一的桥梁。

virtual:clarify-routes

路由清单和导航树。

export const routes: Array<{
  path: string
  title: string
  component: React.ComponentType
  kind: 'mdx' | 'openapi'
  sections?: Array<{ id: string; title: string }>
  contentArtifactUrl?: string
}>

export const navigation: NavigationTree

virtual:clarify-openapi-registry

解析后的 OpenAPI 规范注册表。

export const openApis: Record<string, OpenAPISpec>

virtual:clarify-config

解析后的项目配置和构建选项。

export const config: ResolvedProjectConfig & ResolvedBuildOptions

virtual:clarify-page/*

每个 MDX / Markdown 页面是一个独立的虚拟模块:

// virtual:clarify-page/getting-started
export { default } from '/absolute/path/to/source/getting-started.mdx'

Renderer 消费的路由数据

Renderer 不关心文件系统,只消费 CLI 生成的 route item:

字段用途
pathReact Router 匹配和导航 active 状态。
title页面标题和导航显示。
description / keywordsHydration 后的运行时 metadata 更新。
kind区分 MDX 和 OpenAPI 页面。
localebasePathalternatesisFallback语言上下文和语言切换。
sections侧边栏锚点和页面内导航。
contentArtifactUrlraw Markdown/OpenAPI 内容复制和链接操作。
component / lazy页面组件导入策略。

如果需要新增渲染器消费的 route 字段,应先确认 CLI 能同时为客户端和服务端虚拟模块稳定生成该字段。


主题与界面语义层

Renderer 的视觉系统分为两层:

  • 主题 token 层:由项目配置和内置主题解析得到,表达品牌色、表面色、边框、圆角和布局宽度等基础设计值。
  • 界面语义层:由渲染器根据主题 token 派生,表达菜单、导航、搜索、按钮状态、说明文字等组件角色。

组件编写遵循以下原则:

  • 基础属性可以直接使用主题 token:背景、边框、圆角、品牌色、内容前景色等稳定基础值,可以直接消费主题 token。
  • 组件角色应使用界面语义层:菜单项、导航链接、搜索结果、说明文字、hover / active 状态、菜单宽度等跨组件复用规则,应通过语义类或语义变量表达。
  • 避免在组件内重复设计规则:不要在多个组件里分散定义颜色混合、透明度、字号、行高或宽度;这些规则应收敛到渲染器样式层统一维护。
  • 优先使用自适应布局:使用流式 grid、minmax(0, 1fr)w-fullh-full 和明确的 overflow 边界,不在 JSX 中写固定预览宽高或一次性尺寸。
  • 不要直接使用 Tailwind 任意值:字号、行高、mask、grid track、宽高等如果需要超出默认 scale,应移入渲染器 CSS class,让组件结构保持清晰。
  • 不要过度变量化:只有公开主题能力、用户可配置项,或跨多个组件复用的语义规则才需要提升为 CSS 变量;没有复用计划、也不准备开放配置的私有实现值,应保留为内部 class 的普通 CSS 声明。
  • 预览也必须遵守体系:preview / marketing 相关的 renderer 入口可以裁切溢出内容,但私有测量值应留在 renderer CSS 中,而不是散落在组件 JSX 中。
  • 最小可读区域通过布局表达:组件需要可读宽度时,应通过响应式布局、内部 class 和裁切行为表达,而不是固定 w-* 快照。
  • 保持主题系统单一来源:公开主题只负责基础设计能力;内部语义层负责把基础 token 翻译成具体 UI 角色,确保 Header、Navigation、Search、移动端抽屉和 Portal 菜单在不同主题下保持一致。
  • 谨慎扩展公开主题能力:如果未来需要开放更细粒度的视觉控制,应先确认它是否属于用户可配置的主题能力,再提升到主题 token 层;否则继续作为渲染器内部语义规则维护。