mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
docs: fix reference sidebar ordering and group the subsystem pages
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.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-12-documentation-site-navigation-and-chrome.md
|
||||
2026-08-12-documentation-site-navigation-and-chrome.md: 1b1868a011744decf1c0a25a825fd022ea3609ab
|
||||
2026-08-12-documentation-site-navigation-and-chrome.zh.md: 0fd685ec8c6b7f3f2fd93013ebaf9266a70b2c81
|
||||
@@ -0,0 +1,37 @@
|
||||
# Agent Note: Documentation-site navigation and repository chrome
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-08-12-documentation-site-navigation-and-chrome.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
The reference sidebar rendered its 43 subsystem pages first, ahead of every other group: `sectionOrder` in the VitePress config listed no position for `子系统`/`Subsystems` — nor for `其他接口`/`Other interfaces` — so `indexOf` returned `-1` and sorted them ahead of the ordered sections. Clicking the `参考` navigation item landed on the architecture page whose own sidebar entry was link 44 of 62, 1549px down a 2478px sidebar — outside the viewport. Four subsystem pages carried `order` values already taken by other pages in the same section, resolved only by `Array.prototype.sort` stability and the order the manifest's arrays happened to be concatenated.
|
||||
|
||||
Separately, every canonical page carries lines written for its GitHub reader — a language switcher under the heading, and for some, a repository badge — which the site projected verbatim even though its navigation bar already offers both.
|
||||
|
||||
## Decision
|
||||
|
||||
[website/docs.ts](../../../../website/docs.ts) owns section placement. `sections` declares the groups per locale, and `sectionSpec(locale, label)` returns a group's position and collapse behavior, throwing when a locale declares no placement for a label. A group absent from the declaration now fails the build instead of sorting silently to the top. Placement is per locale because the two sidebars name their groups independently: one shared list ordered both label sets by convention and accepted a label missing from either without complaint.
|
||||
|
||||
Subsystem pages are grouped by concern — overview, core and scopes, sessions and persistence, model and context, execution and tools, policy and interaction, platform and access — and the six topical groups render collapsed until one holds the page being read. The groups sort last within the reference sidebar: expanded, they outnumber every other group combined, so anything placed after them is reachable only by scrolling past the whole list. Page `order` derives from array position rather than a hand-written number.
|
||||
|
||||
`projectedPageContent` in [scripts/project-doc-site.ts](../../../../scripts/project-doc-site.ts) drops the language-switcher line and the repository badge. The switcher match is confined to the first eight lines so a tutorial that shows the convention still renders its example.
|
||||
|
||||
The navigation-bar title is the DeepSeek wordmark inlined into `siteTitle`, which VitePress renders as HTML. Inlining is what lets the mark's `currentColor` fills follow the active theme; `themeConfig.logo` renders an `<img>`, which freezes the mark at the colors its file declares and would need one asset per theme. The sidebar scrollbar rests invisible and appears while scrolling, marked by a `data-` attribute rather than a class because Vue rewrites `class` wholesale when it patches the element.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
**A search tokenizer for Chinese queries.** Built and reverted. The premise — that MiniSearch leaves Chinese prose as untokenizable whole sentences — was tested against a term (`子代理`) that appears nowhere in the corpus; the Chinese pages write `Subagent` and `子 agent`. Measured against the unmodified index, `插件配置` returns 120 hits, `会话持久化` 85, `工作流` 28, `沙箱` 12, each ranking its own page first: `prefix: true` already reaches Chinese terms through the short tokens punctuation produces. Adjacent-character pairs grew the Chinese index from 1.23MB to 2.12MB for no gain. The attempt also surfaced a trap worth keeping: VitePress ships search-option functions to the browser through `Function.prototype.toString` and rebuilds them with `new Function`, so any such function that closes over a module-level constant throws in an empty scope and silently returns no results.
|
||||
|
||||
**Placing the subsystem groups directly after `概念`.** Rejected: it restores the architecture page to the top but leaves generated reference, the Cordis API, and the cookbook below 43 rows.
|
||||
|
||||
**Rewriting filename link text during projection.** The subsystem index table writes `[core.md](core.md)`, which reads as a repository file index on the site. `scripts/project-doc-site.spec.ts` asserts that exact row format, so the filenames are a deliberate convention rather than an oversight; changing what the site displays means changing the convention and its gate together, not working around them in the projector.
|
||||
|
||||
## Consequences
|
||||
|
||||
The reference sidebar measures 1452px with every subsystem group collapsed, against 2478px before, and the architecture page is its first entry. Section placement and collapse are declared in one manifest instead of split between the manifest and the config, and `scripts/project-doc-site.spec.ts` pins three invariants: every sidebar-owning page resolves a placement, an undeclared section is refused, and no two pages share an `order` within a section.
|
||||
|
||||
Canonical Markdown is unchanged by the chrome stripping — the switcher and badge still serve GitHub readers. The cost is that the projector now knows two presentation conventions of the source corpus, which a page written with a different switcher wording would not match.
|
||||
|
||||
The wordmark is a second copy of a mark that also lives in `apps/web/public/favicon.svg` and `packages/client/ui-primitives/src/FishLogo.tsx`, each carrying its own presentation. A change to the DeepSeek wordmark reaches the documentation site only by updating this copy.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Agent Note: 文档站导航与仓库 chrome
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-08-12-documentation-site-navigation-and-chrome.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
参考侧边栏把 43 个子系统页排在了所有其他分组之前:VitePress 配置中的 `sectionOrder` 既没有为 `子系统`/`Subsystems` 也没有为 `其他接口`/`Other interfaces` 声明位置,`indexOf` 返回 `-1`,于是它们排到了所有已排序分区的前面。点击 `参考` 导航项落在架构页,而该页自己的侧边栏条目是 62 条中的第 44 条,位于 2478px 侧边栏的 1549px 处——在视口之外。四个子系统页所用的 `order` 值已被同一分区内的其他页占用,只靠 `Array.prototype.sort` 的稳定性和 manifest 数组恰好的拼接顺序才没有错乱。
|
||||
|
||||
另外,每个规范页面都带有写给 GitHub 读者的行——标题下的语言切换行,部分页面还有仓库徽章——站点原样投影了它们,尽管其导航栏已经提供了这两者。
|
||||
|
||||
## 决定
|
||||
|
||||
[website/docs.ts](../../../../website/docs.ts) 拥有分区位置。`sections` 按 locale 声明各分组,`sectionSpec(locale, label)` 返回分组的位置与折叠行为,当某 locale 未为该 label 声明位置时抛错。未出现在声明中的分组现在会让构建失败,而不是静默排到最前。位置按 locale 声明,是因为两侧侧边栏各自命名分组:单一共享列表既要按约定排列两套标签,又会对任一侧缺失的标签毫无反应。
|
||||
|
||||
子系统页按关注点分组——总览、内核与作用域、会话与持久化、模型与上下文、执行与工具、策略与交互、平台与接入——其中六个主题组保持折叠,直到某一组包含正在阅读的页面。这些分组排在参考侧边栏的最后:展开时它们的数量超过其余所有分组之和,因此排在它们之后的任何内容都只能靠滚过整个列表才能到达。页面 `order` 由数组位置推导,不再手写数字。
|
||||
|
||||
[scripts/project-doc-site.ts](../../../../scripts/project-doc-site.ts) 中的 `projectedPageContent` 会丢弃语言切换行和仓库徽章。切换行的匹配被限制在前八行内,因此展示该约定的教程仍能渲染出它的示例。
|
||||
|
||||
导航栏标题是内联进 `siteTitle` 的 DeepSeek 字标,VitePress 会将其按 HTML 渲染。内联正是让字标的 `currentColor` 填充跟随当前主题的原因;`themeConfig.logo` 渲染为 `<img>`,会把字标固定为文件声明的颜色,并且需要为每套主题各准备一份资源。侧边栏滚动条平时不可见,滚动时出现,通过 `data-` 属性而非 class 标记,因为 Vue 在 patch 该元素时会整体重写 `class`。
|
||||
|
||||
## 考虑过的替代方案
|
||||
|
||||
**为中文查询定制搜索分词器。** 已实现并撤回。其前提——MiniSearch 会把中文散文留作无法切分的整句——是用一个语料中根本不存在的词(`子代理`)验证的;中文页面写的是 `Subagent` 和 `子 agent`。在未改动的索引上实测,`插件配置` 返回 120 条命中、`会话持久化` 85 条、`工作流` 28 条、`沙箱` 12 条,且各自的页面均排在首位:`prefix: true` 已经能通过标点切出的短 token 命中中文词。相邻字符二元组把中文索引从 1.23MB 增至 2.12MB,却没有带来收益。该尝试还暴露出一个值得保留的陷阱:VitePress 通过 `Function.prototype.toString` 把搜索选项中的函数送到浏览器,再用 `new Function` 重建,因此任何闭包引用了模块级常量的此类函数都会在空作用域中抛错,并静默地返回零结果。
|
||||
|
||||
**把子系统分组直接放在 `概念` 之后。** 已否决:这样能让架构页回到顶部,但生成参考、Cordis API 和开发手册仍处在 43 行之下。
|
||||
|
||||
**在投影时重写文件名链接文字。** 子系统索引表写的是 `[core.md](core.md)`,在站点上读起来像仓库文件索引。`scripts/project-doc-site.spec.ts` 断言了该行的确切格式,因此这些文件名是刻意的约定而非疏漏;要改变站点显示的内容,就要连同该约定及其门禁一起改,而不是在投影器里绕开它们。
|
||||
|
||||
## 影响
|
||||
|
||||
在所有子系统分组折叠时,参考侧边栏高度为 1452px,此前为 2478px,且架构页是它的第一个条目。分区位置与折叠行为声明在同一份 manifest 中,不再分散于 manifest 与配置之间;`scripts/project-doc-site.spec.ts` 固定了三条不变式:每个拥有侧边栏的页面都能解析到位置、未声明的分区会被拒绝、同一分区内没有两个页面共用 `order`。
|
||||
|
||||
剥离 chrome 不改动规范 Markdown——切换行与徽章仍服务于 GitHub 读者。代价是投影器现在知晓源语料的两项呈现约定,而采用不同切换行措辞的页面将不会被匹配到。
|
||||
|
||||
字标是同一图形的第二份副本,另两份位于 `apps/web/public/favicon.svg` 和 `packages/client/ui-primitives/src/FishLogo.tsx`,各自承载自己的呈现方式。DeepSeek 字标的变更只有通过更新这份副本才能到达文档站。
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/index.md
|
||||
index.md: a10a0f93fde4f710af2ab14f74b854ee07d7c03f
|
||||
index.zh.md: fb2c4f0959eab8c7a072c44207943c31b0bed8ea
|
||||
index.md: dc9bc13c80885857d42bbc32532f678d8942a40d
|
||||
index.zh.md: 4bd3837d7df0c9bcc1d512e1505c0934b56cf0a7
|
||||
|
||||
@@ -8,6 +8,8 @@ The audience is agent developers. You do not need deep TypeScript experience; th
|
||||
|
||||
If you want the condensed concept reference instead of a walkthrough, read the [Cordis primer](../cordis-primer.md). The exhaustive API reference lives in the generated `cordis-surface` regions on the [subsystem pages](../subsystems/core.md) and the [Cordis core API](../cordis-api/context.md) pages.
|
||||
|
||||
To write plugins for the harness itself — loaded from a `cordis.yml` and driven from the Web UI rather than the launcher below — start from [your first Harness plugin](../user/develop/basic/index.md).
|
||||
|
||||
## Setup
|
||||
|
||||
You need a clone of this repository with dependencies installed; the [development guide](../development.md#setup-tutorial) lists the prerequisites. No API key is needed for this tutorial; every example runs keylessly.
|
||||
|
||||
@@ -8,6 +8,8 @@ Cordis 是 DeepSeek Harness SDK 底层的插件框架:它是一个小型运行
|
||||
|
||||
如果你想阅读精简的概念参考,而不是逐步实践,请参阅 [Cordis 入门](../cordis-primer.md)。详尽的 API 参考见[子系统页面](../subsystems/core.md)上生成的 `cordis-surface` 区块,以及 [Cordis 核心 API](../cordis-api/context.md)页面。
|
||||
|
||||
如果你要为 harness 本身编写插件——由 `cordis.yml` 加载、在 Web UI 中驱动,而不是下面这个启动器——请从[第一个 Harness 插件](../user/develop/basic/index.md)开始。
|
||||
|
||||
## 准备工作
|
||||
|
||||
你需要克隆本仓库并安装依赖;[开发指南](../development.md#setup-tutorial)列出了前置条件。本教程不需要 API 密钥;所有示例均可在无密钥环境中运行。
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md
|
||||
index.md: 71b5bd5ef5d296999420c40d3b8c9cf46c918841
|
||||
index.zh.md: 5dafe8bf0938337fa1f38634088acf00a2fcab46
|
||||
index.md: 494b7869be6ffdf5767fac260b36b2585305b516
|
||||
index.zh.md: 92b5ad4e876b31bc10d57a19d45b1bfdf2fdb9ba
|
||||
|
||||
@@ -139,3 +139,4 @@ Function form is sufficient in most cases. Use class form when the plugin provid
|
||||
|
||||
- [Build a tool](./tool.md) — learn the tool definition DSL
|
||||
- [Plugin configuration](./config.md) — accept user configuration
|
||||
- [Cordis tutorial](../../../cordis-tutorial/index.md) — the plugin framework underneath, built from a scratch directory with no API key
|
||||
|
||||
@@ -139,3 +139,4 @@ export default class MyService extends Service {
|
||||
|
||||
- [开发一个工具](./tool.md) — 详细了解工具定义 DSL
|
||||
- [插件配置](./config.md) — 让插件接受用户配置
|
||||
- [Cordis 框架教程](../../../cordis-tutorial/index.md) — 底层的插件框架,在临时目录中动手构建,无需 API 密钥
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/index.md
|
||||
index.md: 85701ce281d92da0c805b39291179df73eb65f51
|
||||
index.zh.md: 871aa55ef81a7dcbfe3cbde5986244220ee32f98
|
||||
index.md: 8cc673148d7fec4f7d9b994907e17293bc3a6a97
|
||||
index.zh.md: 1a1f7feb8685e124babb182544bde332b52da42c
|
||||
|
||||
@@ -134,3 +134,4 @@ effect cleaned up
|
||||
|
||||
- [Services and dependencies](./service.md) — expose a capability to other plugins
|
||||
- [Event system](./events.md) — communicate between plugins
|
||||
- [Cordis tutorial](../../../cordis-tutorial/index.md) — the same lifecycle, services, and events built step by step against the Cordis runtime
|
||||
|
||||
@@ -134,3 +134,4 @@ effect cleaned up
|
||||
|
||||
- [服务与依赖](./service.md) — 让插件向其他插件提供能力
|
||||
- [事件系统](./events.md) — 在插件之间通信
|
||||
- [Cordis 框架教程](../../../cordis-tutorial/index.md) — 在 Cordis 运行时上逐步搭出同一套生命周期、服务与事件
|
||||
|
||||
@@ -5,7 +5,7 @@ import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, realpathSyn
|
||||
import { tmpdir } from 'node:os'
|
||||
import { basename, join, resolve } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { docsPages, type DocsPage } from '../website/docs.ts'
|
||||
import { docsPages, sectionSpec, type DocsPage } from '../website/docs.ts'
|
||||
import {
|
||||
addProjectionFrontmatter, projectedPageContent, publishableImage, rewriteMarkdown,
|
||||
} from './project-doc-site.ts'
|
||||
@@ -364,6 +364,50 @@ describe('docsPages locale routes', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('sidebar ordering', () => {
|
||||
it('places every section a sidebar collection owns', () => {
|
||||
for (const page of docsPages) {
|
||||
if (page.sidebar === null) continue
|
||||
expect(() => sectionSpec(page.locale, page.section), page.route).not.toThrow()
|
||||
}
|
||||
})
|
||||
|
||||
it('refuses a section with no declared placement', () => {
|
||||
expect(() => sectionSpec('root', '数据结构'))
|
||||
.toThrow('Sidebar section "数据结构" has no placement in the root locale.')
|
||||
})
|
||||
|
||||
it('declares placements per locale rather than in one shared list', () => {
|
||||
// Each locale ranks only its own labels, so a label one locale never uses
|
||||
// cannot borrow a rank from the other.
|
||||
expect(sectionSpec('root', '入门').index).toBe(0)
|
||||
expect(sectionSpec('en', 'Guide').index).toBe(0)
|
||||
expect(() => sectionSpec('en', '入门')).toThrow()
|
||||
expect(() => sectionSpec('root', 'Guide')).toThrow()
|
||||
})
|
||||
|
||||
it('collapses the subsystem groups and leaves the smaller ones open', () => {
|
||||
expect(sectionSpec('root', '执行与工具').collapsed).toBe(true)
|
||||
expect(sectionSpec('en', 'Execution and tools').collapsed).toBe(true)
|
||||
expect(sectionSpec('root', '概念').collapsed).toBeUndefined()
|
||||
})
|
||||
|
||||
it('gives each page its own position within a section', () => {
|
||||
// Sidebar entries sort by order alone, so a shared value leaves the two
|
||||
// pages ranked by whichever manifest block happens to be concatenated
|
||||
// first rather than by an intent the manifest states.
|
||||
const taken = new Map<string, string>()
|
||||
const collisions: string[] = []
|
||||
for (const page of docsPages) {
|
||||
const slot = `${page.locale}/${String(page.sidebar)}/${page.section}#${page.order}`
|
||||
const holder = taken.get(slot)
|
||||
if (holder === undefined) taken.set(slot, page.label)
|
||||
else collisions.push(`${slot}: ${holder} / ${page.label}`)
|
||||
}
|
||||
expect(collisions).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('addProjectionFrontmatter', () => {
|
||||
it('adds frontmatter to an ordinary Markdown page', () => {
|
||||
expect(addProjectionFrontmatter('# Guide\n', { source: 'docs/guide.md' })).toBe(
|
||||
@@ -411,6 +455,25 @@ describe('projectedPageContent', () => {
|
||||
expect(projectedPageContent(markdown, page('zh-guide'))).toBe(markdown)
|
||||
})
|
||||
|
||||
it('drops the language switcher the navigation bar already offers', () => {
|
||||
expect(projectedPageContent('# Guide\n\nEnglish | [中文](./en/guide)\n\nBody.\n', page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
expect(projectedPageContent('# 指南\n\n[English](./en/guide) | 中文\n\n正文。\n', page('zh-guide')))
|
||||
.toBe('# 指南\n\n正文。\n')
|
||||
})
|
||||
|
||||
it('drops the repository badge every page links from its footer', () => {
|
||||
const badge = '[](https://github.com/deepseek-ai/deepseek-harness)'
|
||||
expect(projectedPageContent(`# Guide\n\nBody.\n\n${badge}\n`, page('zh-guide')))
|
||||
.toBe('# Guide\n\nBody.\n')
|
||||
})
|
||||
|
||||
it('keeps a switcher-shaped line that is not the page header', () => {
|
||||
// A tutorial showing the convention must still render the example.
|
||||
const sample = '# Guide\n\nA\n\nB\n\nC\n\nD\n\nE\n\nEnglish | [中文](./x)\n'
|
||||
expect(projectedPageContent(sample, page('zh-guide'))).toBe(sample)
|
||||
})
|
||||
|
||||
it('rejects a locale home source without frontmatter', () => {
|
||||
expect(() => projectedPageContent('# Harness\n', page(null)))
|
||||
.toThrow('locale home source "docs/index.zh.md" must start with YAML frontmatter')
|
||||
|
||||
@@ -292,6 +292,37 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
return `---\n${fields}\n---\n\n${markdown}`
|
||||
}
|
||||
|
||||
/** The switcher line a canonical page carries so its GitHub reader can reach the other language. */
|
||||
const LANGUAGE_SWITCHER = /^(?:English \| \[中文\]\([^)]*\)|\[English\]\([^)]*\) \| 中文)$/
|
||||
|
||||
/** The repository badge a canonical page carries for its GitHub reader. */
|
||||
const REPOSITORY_BADGE = /^\[!\[[^\]]*\]\(https:\/\/img\.shields\.io\/[^)]*\)\]\([^)]*\)$/
|
||||
|
||||
/**
|
||||
* Drop the lines that address a canonical page's GitHub reader.
|
||||
*
|
||||
* The site carries a locale switcher in its navigation bar and links the
|
||||
* repository from every page, so projecting these lines would repeat both — the
|
||||
* switcher as the first element under each heading.
|
||||
*
|
||||
* @param markdown Rewritten canonical Markdown content.
|
||||
* @returns The content without the switcher line or the repository badge.
|
||||
*/
|
||||
function withoutRepositoryChrome(markdown: string): string {
|
||||
const lines = markdown.split('\n')
|
||||
const switcher = lines.findIndex(line => LANGUAGE_SWITCHER.test(line))
|
||||
// Only the switcher introducing the page qualifies; further down the same
|
||||
// text is prose or a sample rather than the page's own header.
|
||||
if (switcher !== -1 && switcher < 8) {
|
||||
lines.splice(switcher, lines[switcher + 1] === '' ? 2 : 1)
|
||||
}
|
||||
const badge = lines.findLastIndex(line => REPOSITORY_BADGE.test(line))
|
||||
if (badge !== -1) {
|
||||
lines.splice(lines[badge - 1] === '' ? badge - 1 : badge, lines[badge - 1] === '' ? 2 : 1)
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the Markdown rendered for one published page.
|
||||
*
|
||||
@@ -300,7 +331,7 @@ export function addProjectionFrontmatter(markdown: string, page: Pick<DocsPage,
|
||||
* @returns Full Markdown for ordinary pages or frontmatter-only Markdown for a locale home page.
|
||||
*/
|
||||
export function projectedPageContent(markdown: string, page: DocsPage): string {
|
||||
if (page.sidebar !== null) return markdown
|
||||
if (page.sidebar !== null) return withoutRepositoryChrome(markdown)
|
||||
if (!markdown.startsWith('---\n')) {
|
||||
throw new Error(`project-doc-site: locale home source ${JSON.stringify(page.source)} must start with YAML frontmatter.`)
|
||||
}
|
||||
|
||||
@@ -1,52 +1,37 @@
|
||||
/** VitePress configuration for the locally projected documentation site. */
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import type { DefaultTheme, PageData } from 'vitepress'
|
||||
import type { ViteDevServer } from 'vite'
|
||||
import { withMermaid } from 'vitepress-plugin-mermaid'
|
||||
import { docsPages, type DocsPage } from '../docs.ts'
|
||||
import { docsPages, sectionSpec, type DocsLocale, type DocsPage } from '../docs.ts'
|
||||
import { docsSourceFiles, projectDocs } from '../../scripts/project-doc-site.ts'
|
||||
|
||||
projectDocs()
|
||||
|
||||
const sectionOrder = [
|
||||
'入门',
|
||||
'基础',
|
||||
'框架能力',
|
||||
'实战',
|
||||
'Cordis 教程',
|
||||
'概念',
|
||||
'生成参考',
|
||||
'Cordis API',
|
||||
'数据结构',
|
||||
'开发手册',
|
||||
'Guide',
|
||||
'Basics',
|
||||
'Framework',
|
||||
'Practice',
|
||||
'Cordis tutorial',
|
||||
'Concepts',
|
||||
'Generated reference',
|
||||
'Cordis Core API',
|
||||
'Data structures',
|
||||
'Cookbook',
|
||||
]
|
||||
|
||||
function sidebar(collection: DocsPage['sidebar']): DefaultTheme.SidebarItem[] {
|
||||
const pages = docsPages.filter(page => page.sidebar === collection)
|
||||
const sections = new Map<string, DocsPage[]>()
|
||||
function sidebar(locale: DocsLocale, collection: DocsPage['sidebar']): DefaultTheme.SidebarItem[] {
|
||||
const pages = docsPages.filter(page => page.locale === locale && page.sidebar === collection)
|
||||
const groups = new Map<string, DocsPage[]>()
|
||||
for (const page of pages) {
|
||||
const entries = sections.get(page.section) ?? []
|
||||
const entries = groups.get(page.section) ?? []
|
||||
entries.push(page)
|
||||
sections.set(page.section, entries)
|
||||
groups.set(page.section, entries)
|
||||
}
|
||||
return [...sections.entries()]
|
||||
.sort(([left], [right]) => sectionOrder.indexOf(left) - sectionOrder.indexOf(right))
|
||||
.map(([text, entries]) => ({
|
||||
text,
|
||||
items: entries
|
||||
.sort((left, right) => left.order - right.order)
|
||||
.map(page => ({ text: page.label, link: `/${page.route.replace(/(?:index)?\.md$/, '')}` })),
|
||||
}))
|
||||
return [...groups.entries()]
|
||||
.sort(([left], [right]) => sectionSpec(locale, left).index - sectionSpec(locale, right).index)
|
||||
.map(([text, entries]) => {
|
||||
const { collapsed } = sectionSpec(locale, text)
|
||||
return {
|
||||
text,
|
||||
// A present `collapsed` is what makes the default theme render the
|
||||
// group as collapsible at all, so an open group must omit the key.
|
||||
...(collapsed === undefined ? {} : { collapsed }),
|
||||
items: entries
|
||||
.sort((left, right) => left.order - right.order)
|
||||
.map(page => ({ text: page.label, link: `/${page.route.replace(/(?:index)?\.md$/, '')}` })),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function watchCanonicalDocs(server: ViteDevServer): void {
|
||||
@@ -107,10 +92,102 @@ const sharedTheme: Pick<DefaultTheme.Config, 'search' | 'socialLinks' | 'editLin
|
||||
},
|
||||
}
|
||||
|
||||
/** Site base path, carrying the leading and trailing slashes VitePress requires. */
|
||||
const base = process.env.DOCS_BASE ?? '/'
|
||||
|
||||
/**
|
||||
* The DeepSeek wordmark, inlined so its `currentColor` fills follow the active
|
||||
* theme. An `<img>` would freeze the mark at the colors the file declares.
|
||||
*/
|
||||
const wordmark = readFileSync(resolve(import.meta.dirname, '../public/wordmark.svg'), 'utf8')
|
||||
.trim()
|
||||
.replace('<svg ', '<svg class="dsh-wordmark" ')
|
||||
|
||||
/**
|
||||
* Styles the default theme does not provide, carried inline because the site
|
||||
* runs the stock theme with no theme directory of its own.
|
||||
*
|
||||
* The navigation-bar lockup pairs with `siteTitle`. The scrollbar rules replace
|
||||
* the sidebar's platform bar, which reserves 15px of a 265px column and draws a
|
||||
* track the rest of the navigation has no border for; `scrollbarScript` supplies
|
||||
* the marker that reveals the thumb. Chrome drops `::-webkit-scrollbar` once
|
||||
* `scrollbar-width` is set to anything but `auto`, so the standard properties
|
||||
* stay behind a query only Firefox answers.
|
||||
*/
|
||||
const siteStyle = `
|
||||
.dsh-lockup { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
.dsh-wordmark { display: block; height: 22px; width: auto; color: var(--vp-c-text-1); }
|
||||
.dsh-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--vp-c-brand-soft);
|
||||
border-radius: 999px;
|
||||
padding: 1px 9px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.VPSidebar::-webkit-scrollbar { width: 6px; }
|
||||
.VPSidebar::-webkit-scrollbar-track { background: transparent; }
|
||||
.VPSidebar::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
border-radius: 3px;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
.VPSidebar[data-scrolling]::-webkit-scrollbar-thumb { background-color: var(--vp-c-text-3); }
|
||||
@supports not selector(::-webkit-scrollbar) {
|
||||
.VPSidebar { scrollbar-width: thin; scrollbar-color: transparent transparent; }
|
||||
.VPSidebar[data-scrolling] { scrollbar-color: var(--vp-c-text-3) transparent; }
|
||||
}
|
||||
`
|
||||
|
||||
/**
|
||||
* Mark the sidebar while it scrolls, so its scrollbar rests invisible.
|
||||
*
|
||||
* A sized `::-webkit-scrollbar` opts the element out of the platform's
|
||||
* self-hiding overlay bar, leaving one painted at all times; nothing in CSS
|
||||
* reports that an element is scrolling. The listener captures instead of
|
||||
* bubbling because scroll events do not bubble, and marks a `data-` attribute
|
||||
* rather than a class because Vue rewrites `class` wholesale when it patches
|
||||
* the element.
|
||||
*/
|
||||
const scrollbarScript = `
|
||||
(() => {
|
||||
let idle
|
||||
addEventListener('scroll', (event) => {
|
||||
const target = event.target
|
||||
if (!(target instanceof Element) || !target.classList.contains('VPSidebar')) return
|
||||
target.dataset.scrolling = ''
|
||||
clearTimeout(idle)
|
||||
idle = setTimeout(() => delete target.dataset.scrolling, 800)
|
||||
}, true)
|
||||
})()
|
||||
`
|
||||
|
||||
/**
|
||||
* Navigation-bar title: the DeepSeek wordmark and the release-stage tag.
|
||||
* VitePress renders `siteTitle` as HTML.
|
||||
*
|
||||
* @param previewTag - Localized release-stage label.
|
||||
* @returns Markup placed beside the navigation-bar home link.
|
||||
*/
|
||||
function siteTitle(previewTag: string): string {
|
||||
return `<span class="dsh-lockup">${wordmark}<span class="dsh-tag">${previewTag}</span></span>`
|
||||
}
|
||||
|
||||
export default withMermaid({
|
||||
title: 'DeepSeek Harness',
|
||||
description: '用于构建 Agent Harness 的插件化 SDK',
|
||||
base: process.env.DOCS_BASE ?? '/',
|
||||
base,
|
||||
head: [
|
||||
// VitePress leaves head hrefs untouched, so the base belongs here explicitly.
|
||||
['link', { rel: 'icon', type: 'image/svg+xml', href: `${base}favicon.svg` }],
|
||||
['style', {}, siteStyle],
|
||||
['script', {}, scrollbarScript],
|
||||
],
|
||||
cleanUrls: true,
|
||||
srcDir: '.generated',
|
||||
cacheDir: '.cache',
|
||||
@@ -120,15 +197,16 @@ export default withMermaid({
|
||||
label: '简体中文',
|
||||
lang: 'zh-CN',
|
||||
themeConfig: {
|
||||
siteTitle: siteTitle('技术预览'),
|
||||
nav: [
|
||||
{ text: '入门', link: '/guide/', activeMatch: '^/guide/' },
|
||||
{ text: '开发', link: '/develop/basic/', activeMatch: '^/develop/' },
|
||||
{ text: '参考', link: '/reference/', activeMatch: '^/reference/' },
|
||||
],
|
||||
sidebar: {
|
||||
'/guide/': sidebar('zh-guide'),
|
||||
'/develop/': sidebar('zh-develop'),
|
||||
'/reference/': sidebar('zh-reference'),
|
||||
'/guide/': sidebar('root', 'zh-guide'),
|
||||
'/develop/': sidebar('root', 'zh-develop'),
|
||||
'/reference/': sidebar('root', 'zh-reference'),
|
||||
},
|
||||
outline: { label: '本页目录' },
|
||||
docFooter: { prev: '上一篇', next: '下一篇' },
|
||||
@@ -146,15 +224,16 @@ export default withMermaid({
|
||||
lang: 'en-US',
|
||||
link: '/en/',
|
||||
themeConfig: {
|
||||
siteTitle: siteTitle('Preview'),
|
||||
nav: [
|
||||
{ text: 'Guide', link: '/en/guide/', activeMatch: '^/en/guide/' },
|
||||
{ text: 'Develop', link: '/en/develop/basic/', activeMatch: '^/en/develop/' },
|
||||
{ text: 'Reference', link: '/en/reference/', activeMatch: '^/en/reference/' },
|
||||
],
|
||||
sidebar: {
|
||||
'/en/guide/': sidebar('en-guide'),
|
||||
'/en/develop/': sidebar('en-develop'),
|
||||
'/en/reference/': sidebar('en-reference'),
|
||||
'/en/guide/': sidebar('en', 'en-guide'),
|
||||
'/en/develop/': sidebar('en', 'en-develop'),
|
||||
'/en/reference/': sidebar('en', 'en-reference'),
|
||||
},
|
||||
editLink: {
|
||||
pattern: ({ frontmatter }: PageData) => {
|
||||
@@ -171,6 +250,9 @@ export default withMermaid({
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
// `srcDir` puts the Vite root inside the disposable generated tree, whose
|
||||
// own `public/` no tracked asset can live in.
|
||||
publicDir: resolve(import.meta.dirname, '../public'),
|
||||
plugins: [
|
||||
{
|
||||
name: 'deepseek-harness-doc-projector',
|
||||
|
||||
204
website/docs.ts
204
website/docs.ts
@@ -144,7 +144,7 @@ const develop = pairedPages([
|
||||
{
|
||||
source: 'docs/user/develop/basic/index.md',
|
||||
route: 'develop/basic/index.md',
|
||||
label: { root: '第一个插件', en: 'First plugin' },
|
||||
label: { root: '第一个 Harness 插件', en: 'Your first Harness plugin' },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: '基础', en: 'Basics' },
|
||||
order: 1,
|
||||
@@ -219,7 +219,7 @@ const develop = pairedPages([
|
||||
])
|
||||
|
||||
const cordisTutorial = pairedPages(([
|
||||
['index.md', 'Cordis 教程', 'Cordis tutorial'],
|
||||
['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'],
|
||||
@@ -232,7 +232,7 @@ const cordisTutorial = pairedPages(([
|
||||
route: `develop/cordis-tutorial/${file}`,
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-develop', en: 'en-develop' },
|
||||
section: { root: 'Cordis 教程', en: 'Cordis tutorial' },
|
||||
section: { root: 'Cordis 框架教程', en: 'Cordis framework tutorial' },
|
||||
order,
|
||||
...(file === 'index.md' ? { sourceAliases: ['docs/cordis-tutorial'] } : {}),
|
||||
})))
|
||||
@@ -248,55 +248,84 @@ const cordisPrimerReference = pairedPages([
|
||||
},
|
||||
])
|
||||
|
||||
const subsystemsReference = pairedPages(([
|
||||
['README.md', '子系统', 'Subsystems', 0],
|
||||
['core.md', '核心', 'Core', 1],
|
||||
['scope.md', '作用域', 'Scopes', 2],
|
||||
['typert.md', 'TypeRT', 'TypeRT', 39],
|
||||
['session.md', '会话', 'Sessions', 3],
|
||||
['session-query.md', '会话查询', 'Session query', 4],
|
||||
['session-reference.md', '会话引用', 'Session references', 5],
|
||||
['session-title.md', '会话标题', 'Session titles', 6],
|
||||
['settings.md', '用户设置', 'User settings', 7],
|
||||
['credentials.md', '用户凭据', 'User credentials', 8],
|
||||
['system-prompt.md', '系统提示词', 'System prompts', 9],
|
||||
['tools.md', '工具', 'Tools', 10],
|
||||
['llm-streaming.md', 'LLM 流式响应', 'LLM streaming', 11],
|
||||
['token-meter.md', 'Token 计量', 'Token metering', 12],
|
||||
['bash.md', 'Bash 执行', 'Bash execution', 13],
|
||||
['subprocess.md', '子进程', 'Subprocesses', 14],
|
||||
['tasks.md', '后台任务', 'Background tasks', 15],
|
||||
['filesystem.md', '文件系统', 'Filesystem', 16],
|
||||
['lsp.md', 'LSP 导航', 'LSP navigation', 17],
|
||||
['code-runtime.md', '代码运行时', 'Code runtime', 18],
|
||||
['compaction.md', '上下文压缩', 'Compaction', 19],
|
||||
['subagent.md', '子代理', 'Subagents', 20],
|
||||
['workflow.md', '工作流', 'Workflows', 21],
|
||||
['skills.md', '技能', 'Skills', 22],
|
||||
['approval.md', '审批', 'Approvals', 23],
|
||||
['permission.md', '权限预设', 'Permission presets', 24],
|
||||
['plan.md', '计划模式', 'Plan mode', 25],
|
||||
['user-interaction.md', '用户交互', 'User interaction', 26],
|
||||
['sandbox.md', '沙箱', 'Sandboxing', 27],
|
||||
['web.md', 'Web 访问', 'Web access', 28],
|
||||
['spill.md', 'Spill 存储', 'Spill storage', 29],
|
||||
['persistence.md', '会话持久化', 'Session persistence', 30],
|
||||
['storage.md', '存储', 'Storage', 31],
|
||||
['workspace.md', '工作区', 'Workspaces', 32],
|
||||
['http-server.md', 'HTTP 服务器', 'HTTP server', 33],
|
||||
['client-modules.md', '客户端模块', 'Client modules', 34],
|
||||
['invariants.md', '运行时不变式', 'Runtime invariants', 36],
|
||||
['session-projection.md', '会话投影', 'Session projections', 37],
|
||||
['telemetry.md', '遥测', 'Telemetry', 38],
|
||||
] as const).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: '子系统', en: 'Subsystems' },
|
||||
order,
|
||||
...(file === 'README.md' ? { sourceAliases: ['docs/subsystems'] } : {}),
|
||||
})))
|
||||
/**
|
||||
* 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(([
|
||||
@@ -359,19 +388,6 @@ const reference = [
|
||||
section: { root: 'Cordis API', en: 'Cordis Core API' },
|
||||
order: order + 5,
|
||||
}))),
|
||||
...pairedPages(([
|
||||
['goal.md', '目标', 'Goals', 14],
|
||||
['schedule.md', '定时提醒', 'Scheduled reminders', 15],
|
||||
['pty.md', 'PTY 会话', 'PTY sessions', 26],
|
||||
['commands.md', '命令', 'Human commands', 38],
|
||||
] as const).map(([file, rootLabel, enLabel, order]): PairedPage => ({
|
||||
source: `docs/subsystems/${file}`,
|
||||
route: `reference/subsystems/${file}`,
|
||||
label: { root: rootLabel, en: enLabel },
|
||||
sidebar: { root: 'zh-reference', en: 'en-reference' },
|
||||
section: { root: '子系统', en: 'Subsystems' },
|
||||
order,
|
||||
}))),
|
||||
...pairedPages(([
|
||||
['adding-a-package.md', '新增 Package', 'Adding a package'],
|
||||
['adding-a-tool.md', '新增 Tool', 'Adding a tool'],
|
||||
@@ -395,6 +411,64 @@ const reference = [
|
||||
}]),
|
||||
]
|
||||
|
||||
/** 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,
|
||||
|
||||
3
website/public/favicon.svg
Normal file
3
website/public/favicon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50" fill="none">
|
||||
<path d="M48.8354 10.0479C48.3232 9.79199 48.1025 10.2798 47.8032 10.5278C47.7007 10.6079 47.6143 10.7119 47.5273 10.8076C46.7793 11.624 45.9048 12.1597 44.7622 12.0957C43.0923 12 41.666 12.5356 40.4058 13.8398C40.1377 12.2319 39.2476 11.272 37.8926 10.6558C37.1836 10.3359 36.4668 10.0156 35.9702 9.31982C35.6235 8.82373 35.5293 8.27197 35.356 7.72754C35.2456 7.3999 35.1353 7.06396 34.7651 7.00781C34.3633 6.94385 34.2056 7.2876 34.0479 7.57568C33.418 8.75195 33.1733 10.0479 33.1973 11.3599C33.2524 14.312 34.4736 16.6641 36.8999 18.3359C37.1758 18.5278 37.2466 18.7197 37.1597 19C36.9946 19.5757 36.7974 20.1357 36.624 20.7119C36.5137 21.0801 36.3486 21.1597 35.9624 21C34.6309 20.4321 33.481 19.5918 32.4644 18.5757C30.7393 16.8721 29.1792 14.9917 27.2334 13.52C26.7764 13.1758 26.3193 12.856 25.8467 12.5518C23.8618 10.584 26.1069 8.96777 26.627 8.77588C27.1704 8.57568 26.8159 7.8877 25.0591 7.896C23.3022 7.90381 21.6953 8.50391 19.647 9.30371C19.3477 9.42383 19.0322 9.51172 18.7095 9.58398C16.8501 9.22363 14.9199 9.14355 12.9033 9.37598C9.10596 9.80762 6.07275 11.6396 3.84326 14.7681C1.16455 18.5278 0.53418 22.7998 1.30664 27.2559C2.11768 31.9521 4.46582 35.8398 8.07373 38.8799C11.8159 42.0322 16.1255 43.5762 21.041 43.2803C24.0269 43.104 27.3516 42.6963 31.1016 39.4561C32.0469 39.936 33.0396 40.1279 34.686 40.272C35.9546 40.3921 37.1758 40.208 38.1211 40.0078C39.6021 39.688 39.4995 38.2881 38.9639 38.0322C34.623 35.9678 35.5762 36.8081 34.71 36.1279C36.9155 33.4639 40.2402 30.6958 41.54 21.728C41.6426 21.0161 41.5557 20.5679 41.54 19.9917C41.5322 19.6396 41.6108 19.5039 42.0049 19.4639C43.0923 19.3359 44.1479 19.0317 45.1167 18.4878C47.9292 16.9199 49.064 14.3438 49.3315 11.2559C49.3711 10.7837 49.3237 10.2959 48.8354 10.0479ZM24.3262 37.8398C20.1196 34.4639 18.0791 33.3521 17.2358 33.3999C16.4482 33.4482 16.5898 34.3682 16.7632 34.9678C16.9443 35.5601 17.1812 35.9683 17.5117 36.4878C17.7402 36.832 17.8979 37.3442 17.2832 37.728C15.9282 38.584 13.5728 37.4399 13.4624 37.3838C10.7207 35.7358 8.42822 33.5601 6.81348 30.584C5.25342 27.7197 4.34766 24.6479 4.19775 21.3677C4.1582 20.5757 4.38672 20.2959 5.15869 20.1519C6.17529 19.96 7.22314 19.9199 8.23926 20.0718C12.5327 20.7119 16.1885 22.6719 19.2529 25.7759C21.002 27.5439 22.3252 29.6558 23.6885 31.7202C25.1377 33.9121 26.6978 36 28.6831 37.7119C29.3843 38.312 29.9434 38.7681 30.479 39.104C28.8643 39.2881 26.1699 39.3281 24.3262 37.8398ZM26.3433 24.6001C26.3433 24.248 26.6191 23.9678 26.9658 23.9678C27.0444 23.9678 27.1152 23.9839 27.1782 24.0078C27.2651 24.04 27.3438 24.0879 27.4067 24.1602C27.5171 24.272 27.5801 24.4321 27.5801 24.6001C27.5801 24.9521 27.3042 25.2319 26.9575 25.2319C26.6108 25.2319 26.3433 24.9521 26.3433 24.6001ZM32.6064 27.8799C32.2046 28.0479 31.8027 28.1919 31.4165 28.208C30.8179 28.2397 30.1641 27.9922 29.8096 27.688C29.2583 27.2158 28.8643 26.9521 28.6987 26.1279C28.6279 25.7759 28.6675 25.2319 28.7305 24.9199C28.8721 24.248 28.7144 23.8159 28.2495 23.4238C27.8716 23.104 27.3911 23.0161 26.8633 23.0161C26.666 23.0161 26.4849 22.9277 26.3511 22.856C26.1304 22.7441 25.9492 22.4639 26.1226 22.1201C26.1777 22.0078 26.4458 21.7358 26.5088 21.688C27.2256 21.272 28.0527 21.4077 28.8169 21.7197C29.5259 22.0161 30.0615 22.5601 30.834 23.3281C31.6216 24.2559 31.7632 24.5117 32.2124 25.208C32.5669 25.752 32.8901 26.312 33.1104 26.9521C33.2446 27.3521 33.0713 27.6802 32.6064 27.8799Z" fill="#4D6BFE" fill-rule="nonzero"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
19
website/public/wordmark.svg
Normal file
19
website/public/wordmark.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="143" height="23" viewBox="0 0 143 23" fill="none">
|
||||
<path d="M78.6784 18.6813H77.1077V16.2462H78.6784C79.6513 16.2462 80.6341 16.0037 81.2672 15.3298C81.9009 14.6559 82.14 13.6222 82.14 12.589C82.14 11.5559 81.9109 10.5222 81.2672 9.84884C80.6246 9.17496 79.6513 8.93245 78.6784 8.93245C77.7056 8.93245 76.7227 9.17496 76.0885 9.84884C75.4549 10.5227 75.2157 11.5559 75.2157 12.589V22.5899H72.4604V6.50684H75.2157V7.53106H75.7209C75.7756 7.46792 75.8304 7.41428 75.8857 7.36064C76.5752 6.73146 77.6307 6.50684 78.6684 6.50684C80.2944 6.50684 81.9193 6.91138 82.9849 8.03451C84.0499 9.15764 84.4265 10.8826 84.4265 12.5991C84.4265 14.3156 84.0404 16.0316 82.9849 17.1637C81.9288 18.2958 80.2944 18.6824 78.6784 18.6824V18.6813Z" fill="currentColor"/>
|
||||
<path d="M36.7486 6.93999H38.3188V9.37511H36.7486C35.7752 9.37511 34.7929 9.61762 34.1593 10.2915C33.5256 10.9654 33.287 11.9991 33.287 13.0323C33.287 14.0654 33.5167 15.0986 34.1593 15.7725C34.8019 16.4463 35.7752 16.6888 36.7486 16.6888C37.722 16.6888 38.7049 16.4463 39.3385 15.7725C39.9722 15.0986 40.2108 14.0654 40.2108 13.0323V3.02246H42.9655V19.115H40.2108V18.0908H39.7056C39.6503 18.1534 39.5955 18.2076 39.5402 18.2612C38.8513 18.8898 37.7952 19.115 36.7576 19.115C35.1321 19.115 33.5066 18.711 32.4416 17.5879C31.3766 16.4648 31 14.7393 31 13.0233C31 11.3073 31.3856 9.5908 32.4416 8.45873C33.5066 7.3356 35.1321 6.93999 36.7486 6.93999Z" fill="currentColor"/>
|
||||
<path d="M56.7855 12.8145V13.794H49.4483V11.8445H54.3151C54.2051 11.1348 53.948 10.4699 53.4887 9.98433C52.8277 9.28363 51.8079 9.03218 50.7982 9.03218C49.7886 9.03218 48.7688 9.28363 48.1078 9.98433C47.4468 10.685 47.2076 11.7545 47.2076 12.8151C47.2076 13.8756 47.4462 14.9535 48.1078 15.6452C48.7688 16.337 49.788 16.5979 50.7982 16.5979C51.8085 16.5979 52.8277 16.3465 53.4887 15.6452C53.5804 15.5463 53.6631 15.4385 53.7458 15.3306H56.4642C56.2256 16.1755 55.849 16.9393 55.2796 17.5322C54.1777 18.6911 52.479 19.1135 50.7982 19.1135C49.1175 19.1135 47.4188 18.7 46.3169 17.5322C45.215 16.3644 44.811 14.5852 44.811 12.8151C44.811 11.0449 45.2061 9.25681 46.3169 8.09792C47.4283 6.93903 49.1175 6.5166 50.7982 6.5166C52.479 6.5166 54.1777 6.93009 55.2796 8.09792C56.3904 9.26575 56.7855 11.0449 56.7855 12.8151V12.8145Z" fill="currentColor"/>
|
||||
<path d="M70.6151 12.8145V13.794H63.2779V11.8445H68.1447C68.0341 11.1348 67.7776 10.4699 67.3183 9.98433C66.6573 9.28363 65.6375 9.03218 64.6278 9.03218C63.6181 9.03218 62.5984 9.28363 61.9374 9.98433C61.2763 10.685 61.0372 11.7545 61.0372 12.8151C61.0372 13.8756 61.2758 14.9535 61.9374 15.6452C62.5984 16.337 63.6181 16.5979 64.6278 16.5979C65.6375 16.5979 66.6573 16.3465 67.3183 15.6452C67.4105 15.5463 67.4927 15.4385 67.5748 15.3306H70.2938C70.0546 16.1755 69.678 16.9393 69.1086 17.5322C68.0067 18.6911 66.3081 19.1135 64.6278 19.1135C62.9476 19.1135 61.2484 18.7 60.1465 17.5322C59.0446 16.3644 58.6406 14.5852 58.6406 12.8151C58.6406 11.0449 59.0357 9.25681 60.1465 8.09792C61.2579 6.93903 62.9471 6.5166 64.6278 6.5166C66.3086 6.5166 68.0067 6.93009 69.1086 8.09792C70.22 9.26575 70.6151 11.0449 70.6151 12.8151V12.8145Z" fill="currentColor"/>
|
||||
<path d="M92.2781 19.1146C93.9589 19.1146 95.657 18.8721 96.7589 18.1804C97.8607 17.4886 98.2653 16.437 98.2653 15.3949C98.2653 14.3528 97.8697 13.2922 96.7589 12.6094C95.657 11.9266 93.9583 11.6746 92.2781 11.6746C91.5612 11.6746 90.9002 11.5757 90.4319 11.3153C89.9637 11.0454 89.7893 10.6414 89.7893 10.2369C89.7893 9.83234 89.9547 9.41941 90.4319 9.15846C90.9002 8.88858 91.626 8.79917 92.3418 8.79917C93.0576 8.79917 93.7834 8.89808 94.2528 9.15846C94.721 9.42835 94.8954 9.83234 94.8954 10.2369H97.6959C97.6959 9.19422 97.3383 8.13424 96.3375 7.45142C95.3368 6.76861 93.803 6.5166 92.2786 6.5166C90.7543 6.5166 89.2211 6.75911 88.2197 7.45142C87.219 8.14318 86.8603 9.19422 86.8603 10.2369C86.8603 11.2796 87.2184 12.3395 88.2197 13.0224C89.2205 13.7052 90.7538 13.9572 92.2786 13.9572C93.0682 13.9572 93.941 14.0561 94.464 14.3165C94.9881 14.5774 95.1714 14.9903 95.1714 15.3949C95.1714 15.7994 94.9881 16.2124 94.464 16.4733C93.941 16.7337 93.1419 16.8326 92.3524 16.8326C91.5629 16.8326 90.7543 16.7337 90.2397 16.4733C89.7256 16.2129 89.5323 15.7994 89.5323 15.3949H86.2998C86.2998 16.4376 86.6943 17.4975 87.8063 18.1804C88.9171 18.8632 90.5979 19.1146 92.2786 19.1146H92.2781Z" fill="currentColor"/>
|
||||
<path d="M112.094 12.8145V13.794H104.757V11.8445H109.624C109.514 11.1348 109.257 10.4699 108.798 9.98433C108.136 9.28363 107.117 9.03218 106.106 9.03218C105.095 9.03218 104.077 9.28363 103.416 9.98433C102.755 10.685 102.517 11.7545 102.517 12.8151C102.517 13.8756 102.755 14.9535 103.416 15.6452C104.077 16.337 105.097 16.5979 106.106 16.5979C107.116 16.5979 108.136 16.3465 108.798 15.6452C108.889 15.5463 108.972 15.4385 109.054 15.3306H111.772C111.533 16.1755 111.157 16.9393 110.588 17.5322C109.486 18.6911 107.787 19.1135 106.106 19.1135C104.425 19.1135 102.727 18.7 101.625 17.5322C100.524 16.3644 100.12 14.5852 100.12 12.8151C100.12 11.0449 100.515 9.25681 101.625 8.09792C102.737 6.93903 104.427 6.5166 106.106 6.5166C107.786 6.5166 109.486 6.93009 110.588 8.09792C111.699 9.26575 112.093 11.0449 112.093 12.8151L112.094 12.8145Z" fill="currentColor"/>
|
||||
<path d="M125.924 12.8145V13.794H118.586V11.8445H123.453C123.344 11.1348 123.086 10.4699 122.627 9.98433C121.966 9.28363 120.947 9.03218 119.936 9.03218C118.926 9.03218 117.907 9.28363 117.246 9.98433C116.585 10.685 116.346 11.7545 116.346 12.8151C116.346 13.8756 116.585 14.9535 117.246 15.6452C117.907 16.337 118.927 16.5979 119.936 16.5979C120.946 16.5979 121.966 16.3465 122.627 15.6452C122.719 15.5463 122.801 15.4385 122.884 15.3306H125.602C125.363 16.1755 124.987 16.9393 124.418 17.5322C123.316 18.6911 121.617 19.1135 119.936 19.1135C118.256 19.1135 116.558 18.7 115.456 17.5322C114.354 16.3644 113.949 14.5852 113.949 12.8151C113.949 11.0449 114.344 9.25681 115.456 8.09792C116.566 6.93903 118.256 6.5166 119.936 6.5166C121.617 6.5166 123.315 6.93009 124.418 8.09792C125.529 9.26575 125.924 11.0449 125.924 12.8151V12.8145Z" fill="currentColor"/>
|
||||
<path d="M130.524 3.02246H127.77V19.115H130.524V3.02246Z" fill="currentColor"/>
|
||||
<path d="M135.227 12.4374L139.744 19.1136H136.337L131.819 12.4374L136.337 7.07324H139.744L135.227 12.4374Z" fill="currentColor"/>
|
||||
<g clip-path="url(#dsh-wordmark-clip)">
|
||||
<path d="M26.5174 3.39471C26.235 3.2567 26.1137 3.52006 25.9487 3.65346C25.8923 3.69659 25.8446 3.75294 25.7969 3.80469C25.3846 4.24516 24.9027 4.53439 24.2737 4.49989C23.3536 4.44814 22.5682 4.73737 21.8735 5.44119C21.7258 4.57349 21.2353 4.0554 20.4889 3.72304C20.0985 3.55054 19.7034 3.37746 19.4297 3.00197C19.2388 2.73459 19.1865 2.43673 19.091 2.14289C19.0301 1.96579 18.9697 1.78466 18.7656 1.75418C18.5442 1.71968 18.4574 1.90541 18.3705 2.06067C18.0232 2.69549 17.8887 3.39471 17.9019 4.10313C17.9324 5.6965 18.6051 6.96556 19.9421 7.86834C20.0939 7.97184 20.133 8.07535 20.0852 8.22658C19.9938 8.53766 19.8857 8.83955 19.7903 9.15063C19.7293 9.34901 19.6384 9.39271 19.4257 9.30588C18.692 8.9994 18.0583 8.54571 17.4982 7.99772C16.5477 7.07827 15.6881 6.06336 14.6162 5.26869C14.3644 5.08296 14.1125 4.91045 13.8521 4.746C12.7584 3.68394 13.9952 2.81164 14.2816 2.70814C14.5812 2.60003 14.3857 2.22857 13.4179 2.23317C12.4502 2.2372 11.5646 2.56151 10.4359 2.99335C10.2708 3.05832 10.0972 3.10547 9.91951 3.14457C8.8954 2.95022 7.83162 2.90709 6.72069 3.03245C4.62877 3.26533 2.95777 4.25436 1.72954 5.94261C0.254043 7.97184 -0.0932678 10.2777 0.33167 12.6824C0.778458 15.2171 2.07225 17.3153 4.06008 18.9558C6.12152 20.6567 8.49577 21.4905 11.2047 21.3306C12.8498 21.2358 14.6812 21.0155 16.7473 19.2669C17.2682 19.5262 17.8151 19.6297 18.7219 19.7074C19.4205 19.7723 20.0933 19.6729 20.6143 19.5648C21.4302 19.3923 21.3739 18.6367 21.0789 18.4981C18.6874 17.3843 19.2124 17.8374 18.7351 17.4706C19.9501 16.033 21.8063 13.4776 22.379 9.99821C22.4353 9.61409 22.5072 9.073 22.4986 8.76192C22.494 8.57216 22.5377 8.49856 22.7545 8.47671C23.3536 8.40771 23.935 8.24383 24.4692 7.94999C26.0188 7.10357 26.6439 5.71318 26.7911 4.04678C26.8129 3.79204 26.7865 3.52869 26.5174 3.39471ZM13.0143 18.3946C10.6964 16.5724 9.5722 15.9726 9.10816 15.9985C8.67402 16.0244 8.75222 16.5212 8.84768 16.8449C8.94773 17.1646 9.07768 17.3849 9.25996 17.6655C9.38589 17.8512 9.47272 18.1272 9.13404 18.3348C8.38766 18.7965 7.08985 18.1796 7.0289 18.1491C5.51833 17.2595 4.25559 16.0853 3.36546 14.4793C2.50581 12.9337 2.0067 11.2753 1.92447 9.50542C1.90262 9.07818 2.02855 8.92695 2.45406 8.84932C3.01413 8.74582 3.59144 8.72397 4.15093 8.80619C6.51656 9.15178 8.53027 10.2092 10.2185 11.8848C11.1822 12.8388 11.9114 13.979 12.6623 15.0929C13.461 16.2757 14.3201 17.4027 15.4144 18.3268C15.8008 18.6505 16.109 18.8966 16.404 19.0783C15.5144 19.1778 14.0297 19.1991 13.0143 18.3958V18.3946ZM14.1252 11.2489C14.1252 11.0591 14.277 10.9079 14.4679 10.9079C14.511 10.9079 14.5501 10.9165 14.5852 10.9292C14.6329 10.9464 14.6766 10.9723 14.7111 11.0114C14.7721 11.0718 14.8066 11.158 14.8066 11.2489C14.8066 11.4386 14.6548 11.5899 14.4639 11.5899C14.273 11.5899 14.1252 11.4386 14.1252 11.2489ZM17.5759 13.0188C17.3545 13.1096 17.1331 13.1873 16.9203 13.1959C16.5903 13.2131 16.2303 13.0791 16.0348 12.9153C15.7312 12.6605 15.5139 12.5179 15.423 12.0734C15.3839 11.8837 15.4057 11.5899 15.4402 11.4214C15.5185 11.0585 15.4316 10.8257 15.1757 10.614C14.9676 10.4415 14.7025 10.3938 14.4115 10.3938C14.3029 10.3938 14.2034 10.3461 14.1292 10.3076C14.0079 10.2472 13.9078 10.096 14.0033 9.91023C14.0338 9.84985 14.1815 9.70322 14.216 9.67734C14.6111 9.45251 15.0665 9.52612 15.488 9.6946C15.8784 9.85445 16.174 10.1477 16.5989 10.5623C17.033 11.0631 17.1112 11.2011 17.3585 11.5772C17.554 11.871 17.7317 12.1729 17.8536 12.5185C17.9272 12.7341 17.8317 12.9107 17.5759 13.0188Z" fill="currentColor"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="dsh-wordmark-clip">
|
||||
<rect width="26.634" height="19.6" fill="white" transform="translate(0.163086 1.75)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.8 KiB |
Reference in New Issue
Block a user