The VitePress config declared no position for the subsystem or other-interface sections, so `indexOf` returned -1 and sorted them ahead of every declared group: the reference landing page's own sidebar entry sat 1549px below the fold. Four subsystem pages also shared `order` values with pages in the same section, resolved only by sort stability and array concatenation order. Section placement and collapse move into the manifest as a per-locale declaration, and `sectionSpec` throws for an undeclared section instead of sorting it silently to the top. Subsystem pages are grouped by concern, the six topical groups collapse until one holds the page being read, and page order derives from array position. The projector drops the language-switcher line and repository badge the canonical pages carry for their GitHub readers. The navigation bar gains the DeepSeek wordmark, a release-stage tag, and a favicon; the sidebar scrollbar rests invisible and appears while scrolling. Subsystem pages carry a two-level outline, and the two plugin-development tracks now cross-link.
481 lines
19 KiB
TypeScript
481 lines
19 KiB
TypeScript
/**
|
|
* Canonical publication manifest for the documentation website.
|
|
*
|
|
* Markdown stays in its owning repository tier. This manifest maps each
|
|
* canonical source into matching route trees for both site locales; when a
|
|
* translation is absent, both routes intentionally project the available
|
|
* source instead of copying Markdown.
|
|
*/
|
|
|
|
/** Locale key used by the VitePress site. */
|
|
export type DocsLocale = 'root' | 'en'
|
|
|
|
/** Sidebar collection rendered for one locale and top-level module. */
|
|
type DocsSidebar =
|
|
| 'zh-guide'
|
|
| 'zh-develop'
|
|
| 'zh-reference'
|
|
| 'en-guide'
|
|
| 'en-develop'
|
|
| 'en-reference'
|
|
|
|
/** A page projected into the VitePress source tree. */
|
|
export interface DocsPage {
|
|
/** VitePress locale whose route tree owns this projection. */
|
|
locale: DocsLocale
|
|
/** Language of the canonical source currently projected at this route. */
|
|
contentLocale: 'zh-CN' | 'en-US'
|
|
/** Repository-relative canonical Markdown source. */
|
|
source: string
|
|
/** VitePress route, including the `.md` suffix. */
|
|
route: string
|
|
/** Navigation label shown in the sidebar. */
|
|
label: string
|
|
/** Sidebar collection that owns the page, or null for a locale home page. */
|
|
sidebar: DocsSidebar | null
|
|
/** Section label within the sidebar. */
|
|
section: string
|
|
/** Stable order within the section. */
|
|
order: number
|
|
/** Heading levels included in this page's VitePress outline. */
|
|
outline?: number | readonly [number, number] | 'deep' | false
|
|
/** Additional repository paths that resolve to this page. */
|
|
sourceAliases?: string[]
|
|
}
|
|
|
|
interface MirroredPage {
|
|
source: string | Record<DocsLocale, string>
|
|
route: string
|
|
contentLocale: DocsPage['contentLocale'] | Record<DocsLocale, DocsPage['contentLocale']>
|
|
label: Record<DocsLocale, string>
|
|
sidebar: Record<DocsLocale, DocsSidebar | null>
|
|
section: Record<DocsLocale, string>
|
|
order: number
|
|
outline?: DocsPage['outline']
|
|
sourceAliases?: string[] | Partial<Record<DocsLocale, string[]>>
|
|
}
|
|
|
|
type PairedPage = Omit<MirroredPage, 'source' | 'contentLocale' | 'sourceAliases'> & {
|
|
/** English side of a sibling `foo.md` / `foo.zh.md` pair. */
|
|
source: string
|
|
/** Language-neutral repository aliases, such as the directory of an index page. */
|
|
sourceAliases?: string[]
|
|
}
|
|
|
|
function localized<T>(value: T | Record<DocsLocale, T>, locale: DocsLocale): T {
|
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
? (value as Record<DocsLocale, T>)[locale]
|
|
: value
|
|
}
|
|
|
|
function mirroredPages(pages: MirroredPage[]): DocsPage[] {
|
|
return pages.flatMap(page => (['root', 'en'] as const).map((locale) => {
|
|
const aliases = page.sourceAliases === undefined
|
|
? undefined
|
|
: Array.isArray(page.sourceAliases) ? page.sourceAliases : page.sourceAliases[locale]
|
|
return {
|
|
locale,
|
|
contentLocale: localized(page.contentLocale, locale),
|
|
source: localized(page.source, locale),
|
|
route: locale === 'root' ? page.route : `en/${page.route}`,
|
|
label: page.label[locale],
|
|
sidebar: page.sidebar[locale],
|
|
section: page.section[locale],
|
|
order: page.order,
|
|
...(page.outline === undefined ? {} : { outline: page.outline }),
|
|
...(aliases === undefined ? {} : { sourceAliases: aliases }),
|
|
}
|
|
}))
|
|
}
|
|
|
|
function pairedPages(pages: PairedPage[]): DocsPage[] {
|
|
return mirroredPages(pages.map((page) => {
|
|
const chineseSource = page.source.replace(/\.md$/, '.zh.md')
|
|
const sharedAliases = page.sourceAliases ?? []
|
|
return {
|
|
...page,
|
|
source: { root: chineseSource, en: page.source },
|
|
contentLocale: { root: 'zh-CN', en: 'en-US' },
|
|
sourceAliases: {
|
|
root: [...sharedAliases, page.source],
|
|
en: [...sharedAliases, chineseSource],
|
|
},
|
|
}
|
|
}))
|
|
}
|
|
|
|
const homeAndGuide = pairedPages([
|
|
{
|
|
source: 'docs/user/index.md',
|
|
route: 'index.md',
|
|
label: { root: 'DeepSeek Harness', en: 'DeepSeek Harness' },
|
|
sidebar: { root: null, en: null },
|
|
section: { root: '首页', en: 'Home' },
|
|
order: 0,
|
|
},
|
|
{
|
|
source: 'docs/user/guide/index.md',
|
|
route: 'guide/quickstart.md',
|
|
label: { root: '使用 Web UI', en: 'Use the Web UI' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/guide'],
|
|
},
|
|
{
|
|
source: 'docs/user/guide/providers.md',
|
|
route: 'guide/providers.md',
|
|
label: { root: '配置模型', en: 'Configure models' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '入门', en: 'Guide' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/guide/python-sdk.md',
|
|
route: 'guide/python-sdk.md',
|
|
label: { root: 'Python SDK', en: 'Python SDK' },
|
|
sidebar: { root: 'zh-guide', en: 'en-guide' },
|
|
section: { root: '其他接口', en: 'Other interfaces' },
|
|
order: 1,
|
|
},
|
|
])
|
|
|
|
const develop = pairedPages([
|
|
{
|
|
source: 'docs/user/develop/basic/index.md',
|
|
route: 'develop/basic/index.md',
|
|
label: { root: '第一个 Harness 插件', en: 'Your first Harness plugin' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/basic'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/basic/tool.md',
|
|
route: 'develop/basic/tool.md',
|
|
label: { root: '开发一个 Tool', en: 'Build a tool' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/basic/config.md',
|
|
route: 'develop/basic/config.md',
|
|
label: { root: '插件配置', en: 'Plugin configuration' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 3,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/basic/publish.md',
|
|
route: 'develop/basic/publish.md',
|
|
label: { root: '打包与安装插件', en: 'Package and install' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '基础', en: 'Basics' },
|
|
order: 4,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/index.md',
|
|
route: 'develop/framework/index.md',
|
|
label: { root: '插件与生命周期', en: 'Plugin lifecycle' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/framework'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/service.md',
|
|
route: 'develop/framework/service.md',
|
|
label: { root: '服务与依赖', en: 'Services and dependencies' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 2,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/framework/events.md',
|
|
route: 'develop/framework/events.md',
|
|
label: { root: '事件系统', en: 'Event system' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '框架能力', en: 'Framework' },
|
|
order: 3,
|
|
},
|
|
{
|
|
source: 'docs/user/develop/practice/index.md',
|
|
route: 'develop/practice/index.md',
|
|
label: { root: '能力的三层拆分', en: 'Capability layering' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '实战', en: 'Practice' },
|
|
order: 1,
|
|
sourceAliases: ['docs/user/develop/practice'],
|
|
},
|
|
{
|
|
source: 'docs/user/develop/practice/llm-adapter.md',
|
|
route: 'develop/practice/llm-adapter.md',
|
|
label: { root: 'LLM 适配器', en: 'LLM adapter' },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: '实战', en: 'Practice' },
|
|
order: 2,
|
|
},
|
|
])
|
|
|
|
const cordisTutorial = pairedPages(([
|
|
['index.md', '总览', 'Overview'],
|
|
['01-first-plugin.md', '1. 第一个插件', '1. Your first plugin'],
|
|
['02-lifecycle-and-effects.md', '2. 生命周期与副作用', '2. Lifecycle and effects'],
|
|
['03-services.md', '3. 服务', '3. Services'],
|
|
['04-events.md', '4. 事件', '4. Events'],
|
|
['05-config.md', '5. 配置', '5. Configuration'],
|
|
['06-composition-and-hmr.md', '6. 组合与热重载', '6. Composition and HMR'],
|
|
['07-into-the-harness.md', '7. 进入 Harness', '7. Into the harness'],
|
|
] as const).map(([file, rootLabel, enLabel], order): PairedPage => ({
|
|
source: `docs/cordis-tutorial/${file}`,
|
|
route: `develop/cordis-tutorial/${file}`,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
|
section: { root: 'Cordis 框架教程', en: 'Cordis framework tutorial' },
|
|
order,
|
|
...(file === 'index.md' ? { sourceAliases: ['docs/cordis-tutorial'] } : {}),
|
|
})))
|
|
|
|
const cordisPrimerReference = pairedPages([
|
|
{
|
|
source: 'docs/cordis-primer.md',
|
|
route: 'reference/cordis-primer.md',
|
|
label: { root: 'Cordis 入门', en: 'Cordis primer' },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '概念', en: 'Concepts' },
|
|
order: 1,
|
|
},
|
|
])
|
|
|
|
/**
|
|
* Subsystem pages grouped by the concern they document, as `[Chinese section,
|
|
* English section, pages]`. One flat list of every subsystem pushed the rest of
|
|
* the reference sidebar below the fold.
|
|
*/
|
|
const subsystemGroups = [
|
|
['总览', 'Overview', [
|
|
['README.md', '子系统', 'Subsystems'],
|
|
]],
|
|
['内核与作用域', 'Core and scopes', [
|
|
['core.md', '核心', 'Core'],
|
|
['scope.md', '作用域', 'Scopes'],
|
|
['invariants.md', '运行时不变式', 'Runtime invariants'],
|
|
]],
|
|
['会话与持久化', 'Sessions and persistence', [
|
|
['session.md', '会话', 'Sessions'],
|
|
['session-query.md', '会话查询', 'Session query'],
|
|
['session-reference.md', '会话引用', 'Session references'],
|
|
['session-title.md', '会话标题', 'Session titles'],
|
|
['session-projection.md', '会话投影', 'Session projections'],
|
|
['persistence.md', '会话持久化', 'Session persistence'],
|
|
['spill.md', 'Spill 存储', 'Spill storage'],
|
|
['telemetry.md', '遥测', 'Telemetry'],
|
|
]],
|
|
['模型与上下文', 'Model and context', [
|
|
['llm-streaming.md', 'LLM 流式响应', 'LLM streaming'],
|
|
['token-meter.md', 'Token 计量', 'Token metering'],
|
|
['system-prompt.md', '系统提示词', 'System prompts'],
|
|
['compaction.md', '上下文压缩', 'Compaction'],
|
|
]],
|
|
['执行与工具', 'Execution and tools', [
|
|
['tools.md', '工具', 'Tools'],
|
|
['bash.md', 'Bash 执行', 'Bash execution'],
|
|
['subprocess.md', '子进程', 'Subprocesses'],
|
|
['pty.md', 'PTY 会话', 'PTY sessions'],
|
|
['tasks.md', '后台任务', 'Background tasks'],
|
|
['filesystem.md', '文件系统', 'Filesystem'],
|
|
['lsp.md', 'LSP 导航', 'LSP navigation'],
|
|
['code-runtime.md', '代码运行时', 'Code runtime'],
|
|
['web.md', 'Web 访问', 'Web access'],
|
|
['skills.md', '技能', 'Skills'],
|
|
['workflow.md', '工作流', 'Workflows'],
|
|
['subagent.md', '子代理', 'Subagents'],
|
|
]],
|
|
['策略与交互', 'Policy and interaction', [
|
|
['approval.md', '审批', 'Approvals'],
|
|
['permission.md', '权限预设', 'Permission presets'],
|
|
['sandbox.md', '沙箱', 'Sandboxing'],
|
|
['plan.md', '计划模式', 'Plan mode'],
|
|
['user-interaction.md', '用户交互', 'User interaction'],
|
|
['commands.md', '命令', 'Human commands'],
|
|
['goal.md', '目标', 'Goals'],
|
|
['schedule.md', '定时提醒', 'Scheduled reminders'],
|
|
]],
|
|
['平台与接入', 'Platform and access', [
|
|
['http-server.md', 'HTTP 服务器', 'HTTP server'],
|
|
['typert.md', 'TypeRT', 'TypeRT'],
|
|
['client-modules.md', '客户端模块', 'Client modules'],
|
|
['storage.md', '存储', 'Storage'],
|
|
['workspace.md', '工作区', 'Workspaces'],
|
|
['settings.md', '用户设置', 'User settings'],
|
|
['credentials.md', '用户凭据', 'User credentials'],
|
|
]],
|
|
] as const
|
|
|
|
const subsystemsReference = subsystemGroups.flatMap(([rootSection, enSection, files]) => pairedPages(
|
|
files.map(([file, rootLabel, enLabel], order): PairedPage => ({
|
|
source: `docs/subsystems/${file}`,
|
|
route: file === 'README.md' ? 'reference/subsystems/index.md' : `reference/subsystems/${file}`,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: rootSection, en: enSection },
|
|
order,
|
|
// Subsystem pages carry long third-level sections a two-level outline reaches.
|
|
outline: [2, 3],
|
|
...(file === 'README.md' ? { sourceAliases: ['docs/subsystems'] } : {}),
|
|
})),
|
|
))
|
|
|
|
const reference = [
|
|
...pairedPages(([
|
|
['docs/architecture.md', 'reference/index.md', '架构', 'Architecture', 0],
|
|
] as const).map(([source, route, rootLabel, enLabel, order]): PairedPage => ({
|
|
source,
|
|
route,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '概念', en: 'Concepts' },
|
|
order,
|
|
}))),
|
|
...pairedPages(([
|
|
['docs/capability-seams.md', 'reference/capability-seams.md', '能力服务', 'Capability services', 2],
|
|
['docs/agent-lifecycle.md', 'reference/agent-lifecycle.md', 'Agent 生命周期', 'Agent lifecycle', 3],
|
|
['docs/tool-execution-pipeline.md', 'reference/tool-execution-pipeline.md', 'Tool 执行', 'Tool execution', 4],
|
|
] as const).map(([source, route, rootLabel, enLabel, order]): PairedPage => ({
|
|
source,
|
|
route,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '概念', en: 'Concepts' },
|
|
order,
|
|
}))),
|
|
...pairedPages(([
|
|
['docs/config-catalog.md', 'reference/config-catalog.md', '插件配置', 'Plugin configuration'],
|
|
['docs/tool-catalog.md', 'reference/tool-catalog.md', 'Tool Schema', 'Tool schemas'],
|
|
['docs/persistence-catalog.md', 'reference/persistence-catalog.md', '持久化事件', 'Persistence events', 'deep'],
|
|
] as const).map(([source, route, rootLabel, enLabel, outline], order): PairedPage => ({
|
|
source,
|
|
route,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '生成参考', en: 'Generated reference' },
|
|
order,
|
|
...(outline === undefined ? {} : { outline }),
|
|
}))),
|
|
...pairedPages(([
|
|
['context.md', 'Context', 'Context'],
|
|
['events.md', 'Events', 'Events'],
|
|
['fiber.md', 'Fiber', 'Fiber'],
|
|
['registry.md', 'Plugin Registry', 'Plugin Registry'],
|
|
['service.md', 'Service', 'Service'],
|
|
] as const).map(([file, rootLabel, enLabel], order): PairedPage => ({
|
|
source: `docs/cordis-api/${file}`,
|
|
route: `reference/cordis-api/${file}`,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: 'Cordis API', en: 'Cordis Core API' },
|
|
order,
|
|
}))),
|
|
...mirroredPages(([
|
|
['inherited.md', '继承接口面', 'Inherited surface'],
|
|
] as const).map(([file, rootLabel, enLabel], order): MirroredPage => ({
|
|
source: `docs/cordis-api/${file}`,
|
|
route: `reference/cordis-api/${file}`,
|
|
contentLocale: 'en-US',
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: 'Cordis API', en: 'Cordis Core API' },
|
|
order: order + 5,
|
|
}))),
|
|
...pairedPages(([
|
|
['adding-a-package.md', '新增 Package', 'Adding a package'],
|
|
['adding-a-tool.md', '新增 Tool', 'Adding a tool'],
|
|
['adding-an-llm-adapter.md', '新增 LLM Adapter', 'Adding an LLM adapter'],
|
|
['extension-cookbook.md', '扩展模式', 'Extension patterns'],
|
|
] as const).map(([file, rootLabel, enLabel], order): PairedPage => ({
|
|
source: `docs/cookbook/${file}`,
|
|
route: `reference/cookbook/${file}`,
|
|
label: { root: rootLabel, en: enLabel },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '开发手册', en: 'Cookbook' },
|
|
order,
|
|
}))),
|
|
...pairedPages([{
|
|
source: 'docs/cookbook/adding-a-conversation-node.md',
|
|
route: 'reference/cookbook/adding-a-conversation-node.md',
|
|
label: { root: '新增 Conversation Node', en: 'Adding a Conversation Node' },
|
|
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
|
section: { root: '开发手册', en: 'Cookbook' },
|
|
order: 4,
|
|
}]),
|
|
]
|
|
|
|
/** A sidebar group, matched to pages by `label`. */
|
|
export interface DocsSection {
|
|
/** Group heading, equal to the `section` field of every page it holds. */
|
|
label: string
|
|
/** Render the group collapsed until it holds the page being read. */
|
|
collapsed?: boolean
|
|
}
|
|
|
|
/**
|
|
* Every sidebar group, in the order its locale renders it.
|
|
*
|
|
* The subsystem groups collapse because together they outnumber the rest of the
|
|
* reference sidebar; expanded, they push every other group below the fold.
|
|
*/
|
|
const sections: Record<DocsLocale, readonly DocsSection[]> = {
|
|
root: [
|
|
{ label: '入门' }, { label: '其他接口' },
|
|
{ label: '基础' }, { label: '框架能力' }, { label: '实战' }, { label: 'Cordis 框架教程' },
|
|
{ label: '概念' }, { label: '生成参考' }, { label: 'Cordis API' }, { label: '开发手册' },
|
|
{ label: '总览' },
|
|
{ label: '内核与作用域', collapsed: true },
|
|
{ label: '会话与持久化', collapsed: true },
|
|
{ label: '模型与上下文', collapsed: true },
|
|
{ label: '执行与工具', collapsed: true },
|
|
{ label: '策略与交互', collapsed: true },
|
|
{ label: '平台与接入', collapsed: true },
|
|
],
|
|
en: [
|
|
{ label: 'Guide' }, { label: 'Other interfaces' },
|
|
{ label: 'Basics' }, { label: 'Framework' }, { label: 'Practice' }, { label: 'Cordis framework tutorial' },
|
|
{ label: 'Concepts' }, { label: 'Generated reference' }, { label: 'Cordis Core API' }, { label: 'Cookbook' },
|
|
{ label: 'Overview' },
|
|
{ label: 'Core and scopes', collapsed: true },
|
|
{ label: 'Sessions and persistence', collapsed: true },
|
|
{ label: 'Model and context', collapsed: true },
|
|
{ label: 'Execution and tools', collapsed: true },
|
|
{ label: 'Policy and interaction', collapsed: true },
|
|
{ label: 'Platform and access', collapsed: true },
|
|
],
|
|
}
|
|
|
|
/**
|
|
* Placement and collapse behavior of one sidebar group.
|
|
*
|
|
* @param locale - Route tree whose sidebar is being built.
|
|
* @param label - Section label carried by the pages in the group.
|
|
* @returns The declared group, plus its zero-based position in the locale.
|
|
* @throws When the locale declares no placement for the label. Ranking by list
|
|
* membership alone would sort an undeclared group silently ahead of every
|
|
* declared one.
|
|
*/
|
|
export function sectionSpec(locale: DocsLocale, label: string): DocsSection & { index: number } {
|
|
const declared = sections[locale]
|
|
const section = declared.find(candidate => candidate.label === label)
|
|
if (section === undefined) throw new Error(`Sidebar section "${label}" has no placement in the ${locale} locale.`)
|
|
return { ...section, index: declared.indexOf(section) }
|
|
}
|
|
|
|
/** Every canonical page published by the documentation website. */
|
|
export const docsPages: DocsPage[] = [
|
|
...homeAndGuide,
|
|
...develop,
|
|
...cordisTutorial,
|
|
...cordisPrimerReference,
|
|
...subsystemsReference,
|
|
...reference,
|
|
]
|