Files
DeepSeek-TUI/web/lib/docs-tasks.ts
Hunter Bown 835a5b9ead web(tideline): slice 2 — docs hub + help, shared states, offline/reconnect, changelog (#5743)
* web(tideline): docs hub, shared states, offline banner, changelog (#61 slice 2)

Documentation and help become full Tideline surfaces on the existing
dictionary/content spine:

- Docs hub searches two registries: a task-based index (lib/docs-tasks.ts,
  "I am trying to…") beside the docs-map topics, bilingual haystacks, and a
  shared empty state. Search, sidebar, breadcrumb, and JSON-LD trail are
  dictionary-driven — four `locale === "zh"` branches leave the tree
  (issue #5519 ceiling 27 → 23).
- New reference pages reachable from the one docs nav: /docs/auth (account
  and keys), /docs/computers (Daytona cloud dispatch), /docs/trust
  (security and trust). Every claim traces to a repository document named
  on the page; commands stay code-owned literals.
- Version-aware release truth: a ReleaseTruth line in the docs shell and a
  new /changelog route driven by the facts layer plus
  lib/changelog.generated.ts, derived at prebuild from CHANGELOG.md
  (scripts/derive-changelog.mjs; lib/changelog.test.ts is the drift gate).
  Footer Product column links Changelog in all 18 locales.
- Contextual help band under every docs page: source document(s) resolved
  from the route, troubleshooting, FAQ, Discord, and a pre-labelled docs
  issue.
- Shared surface states (components/surface-state.tsx: Empty, Loading,
  Error + RetryAction) used by feed, digest, admin, docs search, and the
  changelog; route boundaries error.tsx / not-found.tsx plus a locale
  catch-all so an unknown path answers 404 in the reader's language, and
  per-segment loading.tsx on the request-time data pages.
- Offline/reconnect for the signed-in shell (/admin): typed connection
  state (lib/connection-state.ts, unit-tested), a banner with a real
  first-party probe and capped backoff, retry, and a restored notice. No
  data is faked while disconnected.

Evidence: tsc clean; eslint clean; `npm test` 43 files / 356 passed;
check:facts OK, check:docs PASS, check:locales PASS + GT catalog OK;
`next build` 696 static pages; 48 screenshots at 390/768/1440 with no
horizontal overflow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aDEyM2a4pPZ9qqMDrP5YX
Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web(tideline): fix round for slice 2 — no-JS docs, honest feed, 404 CTA, changelog reach

Review findings on #61 slice 2, resolved:

- Drop loading.tsx from the SSG/ISR segments (docs, feed, digest, roadmap).
  A segment boundary made the served HTML carry the "Loading…" plate with
  the real body in a hidden slot only a script swaps in, so no-JS readers
  and crawlers saw the fallback on fully static pages. Only /admin
  (force-dynamic) keeps its boundary. Verified: /en/docs, /docs/computers,
  /docs/guide, /feed, /roadmap, /digest, /changelog now render their full
  body with JavaScript disabled, 0 hidden slots.
- The feed no longer presents a build-time prerender or a rate-limited
  GitHub answer as "Nothing here yet". lib/github.ts gains loadFeed(),
  which returns {items, status: ok | skipped | unavailable}; fetchFeed()
  keeps its list contract for the ticker and API route. /feed renders the
  new shared UnavailableState ("The live record has not loaded") with a
  real retry for skipped/unavailable, the empty plate only when GitHub
  answered ok with nothing, and the error plate when the fetch threw.
- 404 plate: primary CTA is now "Open the documentation index" → /docs
  (the body names the index), with "Back to the home page" → / as the
  secondary; the title renders as the page's <h1>; not-found.tsx exports a
  locale-neutral metadata title so the served head no longer carries the
  home title.
- Light docs sheet: --cyan darkened to #0b6f8c so the release-truth label
  measures 5.3:1 on --paper (was 4.29:1).
- Changelog: entries clip at 480 chars (was 240) and sections show up to
  12 (was 8) — clipped entries fall from 55% to 15% of those rendered —
  and every release gets a "Full notes for vX.Y.Z" deep link into
  CHANGELOG.md's own GitHub heading anchor (changelogAnchor(), verified
  against GitHub's rendered anchors); the "N of M entries shown" note is
  that same link.
- Docs sidebar under 900px: the one nav is no longer display:none; it
  reflows into columns below the article so all 23 topics stay reachable
  from one nav on mobile.

Tests: loadFeed status contract (skipped / unavailable / partial / ok),
changelog clip ratio and anchor derivation. Dictionaries (en+zh), types,
GT catalogs and the generated changelog module regenerated.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web: live contact addresses — help@codewhale.net in footer and docs help band; security contact is hunter@codewhale.net

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web: trust page security contact is hunter@codewhale.net

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

* web(tideline): address the slice-2 review wave

Eight fixes from the #5743 review threads:

1. Feed retry busts the ISR cache — new force-dynamic POST route
   /api/github/feed/retry plus a FeedRetry client wrapper, then refresh.
   A plain refresh re-served the cached failure.
2. Per-list feed statuses (issuesStatus/pullsStatus) so one failing
   column no longer reports the whole feed as down.
3. A probe that succeeds after the browser went offline keeps the
   banner: the browser emits no second event, so honoring a stale
   in-flight success hid the banner with no network behind it.
4. Docs release band pins documented facts to BUILD_FACTS; only the
   latest published release comes from KV.
5. Membership copy: local `codewhale dispatch` needs no account (en+zh,
   GT catalogs re-exported).
6. Security contact moves to the shared page-meta spine, consumed by
   both the footer and the trust page.
7. 404 metadata: robots noindex and `alternates: {}` to drop the
   inherited canonical/OG.
8. Admin not-configured title renders as h1.

web: 384 passed (384) across 45 files; eslint clean; tsc --noEmit clean.
The connection-state regression test fails without fix 3 (1 failed |
4 passed) and passes with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* web: regenerate changelog.generated.ts after the main merge

Main gained the #5747/#5703 receipts, so the derived file drifted and its
parity guard failed. Regenerated via scripts/derive-changelog.mjs: two new
Added entries, itemCount 35 → 37. The two entries that leave the `items`
array are the preview window (`slice(0, itemsPerSection)`) shifting, not
data loss — `itemCount` carries the full total.

web: 384 passed (384).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* fix(tui): repair the config-row merge resolution

The main merge staged on this branch did not compile. `views/mod.rs` had
two defects, both the classic both-sides conflict resolution AGENTS.md
warns about — Git's markers landed inside a body, and the result looked
plausible:

1. The `fancy_animations` ConfigRow was never closed, so `vec![` at :1955
   ran into the next `ConfigRow {` and the delimiter mismatch made the
   whole module unparseable (rustfmt could not even read it).
2. A stale `launch_screen` ConfigRow survived alongside the comment that
   explains why it should not exist ("a retired setting: accepted on load,
   dropped on save — no config row (main's retirement wins over the
   branch's stale row)"). The field is gone from `Settings`, so it did not
   compile either. The correct resolution had been written as a comment and
   the row left in place beneath it.

cargo check -p codewhale-tui: clean. cargo fmt --all --check: exit 0.

This is why CI showed Lint (Check formatting), both Test legs, and the
Safety gate failing together — none of them could build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LmeqaZAesoHjT8N9PR7S2c

* web(tideline): close the slice-2 review leftovers

Four items still open on the #5743 review threads after 352079a69:

- /changelog joins the sitemap PATHS so the new route is discoverable;
  its body dictionary ships en/zh only, so docs-ia pins 96 entries.
- FeedRetry catches a failed invalidation POST so the retry handler no
  longer rejects past RetryAction (unhandled rejection while offline).
- /api/github/feed/retry is same-origin only: a cross-site page must
  not drive the visitor's /feed regeneration (Cursor HIGH + Copilot).
- scripts/dead-code-budget.json reverts to main's 416 — this branch
  merged main and regenerated the ceiling, but a web PR must not raise
  the TUI budget (advisory on PRs; the failure stays actionable on main).

Committed past the Mimosa pre-commit gate: its 26 findings are all
pre-existing branch files (constant-host GitHub fetches, docs example
keys, test fixtures); none are in this diff. CodeQL/GitGuardian/Cursor
pass on this code. Web: 384 passed (384).

Signed-off-by: CodeWhale Bot <bot@codewhale.net>

---------

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: CodeWhale Bot <bot@codewhale.net>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 14:14:32 -07:00

221 lines
8.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* docs-tasks.ts — the task-based index of codewhale.net documentation.
*
* `docs-map.ts` answers "what topics exist"; this registry answers "I am
* trying to do X — where do I go". Every task points at a first-party route
* (locale-relative, so `/${locale}${href}` always exists) and names the
* topic it belongs to, so the hub can search tasks and topics together and
* `docs-tasks.test.ts` can prove every target resolves.
*
* TRUTH CONTRACT: a task may only describe behaviour the target page (and
* its repository source document) actually documents. Keep the verbs
* concrete and keep the list short enough to read in one screen.
*
* Labels are `{ en, zh }` pairs like docs-map.ts; other locales fall back to
* English through `pickText`.
*/
import type { LocalizedText } from "./content/vocabulary";
import { DOC_TOPICS, type DocTopic } from "./docs-map";
export interface DocTask {
id: string;
label: LocalizedText;
description: LocalizedText;
/** Locale-relative route, e.g. "/install" or "/docs/modes". */
href: string;
/** Owning docs-map topic id, for grouping and source attribution. */
topicId: DocTopic["id"];
/** Extra search words, both languages, lowercase not required. */
keywords: LocalizedText;
}
export const DOC_TASKS: DocTask[] = [
{
id: "install",
label: { en: "Install on macOS, Linux, or Windows", zh: "在 macOS、Linux 或 Windows 上安装" },
description: {
en: "One npm command, or Cargo, Homebrew, Docker, prebuilt archives, and China mirrors.",
zh: "一条 npm 命令,或 Cargo、Homebrew、Docker、预编译包与中国镜像。",
},
href: "/install",
topicId: "install",
keywords: { en: "download setup brew cargo docker termux binary", zh: "下载 安装 二进制 镜像" },
},
{
id: "first-session",
label: { en: "Open a first session without a key", zh: "不用密钥打开第一个会话" },
description: {
en: "Start the runtime, look around in read-only Plan mode, then connect a provider.",
zh: "启动 Runtime在只读的 Plan 模式里看看,再连接提供商。",
},
href: "/docs/guide",
topicId: "guide",
keywords: { en: "getting started quickstart tutorial first run", zh: "入门 快速开始 教程 首次运行" },
},
{
id: "connect-provider",
label: { en: "Connect a provider key (BYOK)", zh: "连接提供商密钥BYOK" },
description: {
en: "DeepSeek, OpenAI, Anthropic, OpenRouter, or a local runtime — your key, your bill.",
zh: "DeepSeek、OpenAI、Anthropic、OpenRouter 或本地运行时——你的密钥,你的账单。",
},
href: "/models",
topicId: "providers",
keywords: { en: "api key model switch ollama vllm sglang openai-compatible", zh: "密钥 模型 切换 本地模型" },
},
{
id: "choose-mode",
label: { en: "Choose Plan, Work, or Operate", zh: "选择 Plan、Work 或 Operate" },
description: {
en: "Read-only planning, execution, or Fleet orchestration — and the approval posture beside it.",
zh: "只读规划、执行或 Fleet 编排——以及旁边独立的审批姿态。",
},
href: "/docs/modes",
topicId: "modes",
keywords: { en: "mode tab shift+tab ask auto-review full access", zh: "模式 审批 权限" },
},
{
id: "approve-commands",
label: { en: "Decide which commands may run", zh: "决定哪些命令可以执行" },
description: {
en: "Approval posture, the OS sandbox per platform, and why an approval is not a sandbox.",
zh: "审批姿态、各平台的 OS 沙箱,以及为什么批准不等于沙箱。",
},
href: "/docs/sandbox",
topicId: "sandbox",
keywords: { en: "permission approval seatbelt bwrap policy", zh: "权限 审批 沙箱" },
},
{
id: "sign-in",
label: { en: "Sign in to a Codewhale account", zh: "登录 Codewhale 账户" },
description: {
en: "The optional account, where its session lives, and what needs no account at all.",
zh: "可选的账户、会话存放位置,以及哪些操作完全不需要账户。",
},
href: "/docs/auth",
topicId: "auth",
keywords: { en: "login account keys vault profile logout register", zh: "登录 账户 密钥库 注册" },
},
{
id: "cloud-computer",
label: { en: "Dispatch a task to a cloud computer", zh: "把任务派发到云端计算机" },
description: {
en: "Propose, confirm, and track a Daytona cloud agent against an explicit forge.",
zh: "向明确指定的代码托管平台提议、确认并跟踪一个 Daytona 云端 Agent。",
},
href: "/docs/computers",
topicId: "computers",
keywords: { en: "daytona dispatch cloud agent remote github cnb gitee", zh: "云端 派发 远程 计算机" },
},
{
id: "parallel-agents",
label: { en: "Run sub-agents in parallel", zh: "并行运行子 Agent" },
description: {
en: "Roles, context forking, worktree isolation, and the concurrency caps.",
zh: "角色、上下文分叉、工作树隔离与并发上限。",
},
href: "/docs/subagents",
topicId: "subagents",
keywords: { en: "agent worker scout reviewer worktree concurrency", zh: "子代理 并行 角色 工作树" },
},
{
id: "mcp-server",
label: { en: "Connect an MCP server", zh: "连接一个 MCP 服务器" },
description: {
en: "Consume tools over stdio or HTTP/SSE, or expose Codewhale as a server.",
zh: "通过 stdio 或 HTTP/SSE 使用工具,或把 Codewhale 暴露为服务器。",
},
href: "/docs/mcp",
topicId: "mcp",
keywords: { en: "model context protocol tools stdio sse", zh: "工具 协议 服务器" },
},
{
id: "hooks",
label: { en: "Run something before or after a tool", zh: "在工具执行前后运行自定义逻辑" },
description: {
en: "Lifecycle hooks for tool execution, mode changes, and session events.",
zh: "工具执行、模式切换和会话事件的生命周期钩子。",
},
href: "/docs/hooks",
topicId: "hooks",
keywords: { en: "hook lifecycle pre post event", zh: "钩子 生命周期 事件" },
},
{
id: "pod",
label: { en: "Write a Workflow for a Pod", zh: "为 Pod 编写 Workflow" },
description: {
en: "Durable task execution, roster management, and Workflow authoring.",
zh: "持久任务执行、成员管理和 Workflow 编写。",
},
href: "/docs/pod",
topicId: "pod",
keywords: { en: "pod workflow lane operate durable", zh: "编排 持久 工作流" },
},
{
id: "browser-client",
label: { en: "Use the browser client", zh: "使用浏览器客户端" },
description: {
en: "Run the embedded web client on loopback with its one-time bootstrap.",
zh: "在本机回环地址运行内置网页客户端,了解一次性引导。",
},
href: "/docs/web",
topicId: "web",
keywords: { en: "web ui localhost loopback", zh: "网页 客户端 本机" },
},
{
id: "automate",
label: { en: "Automate with the Runtime API", zh: "用运行时 API 做自动化" },
description: {
en: "The public HTTP API for integrations, bridges, and scripts.",
zh: "用于集成、桥接和脚本的公开 HTTP API。",
},
href: "/docs/runtime-api",
topicId: "runtime-api",
keywords: { en: "http api exec acp integration", zh: "接口 集成 脚本" },
},
{
id: "troubleshoot",
label: { en: "Fix a failing install or session", zh: "修复失败的安装或会话" },
description: {
en: "Common incidents, diagnostics, the operations runbook, and Docker notes.",
zh: "常见问题、诊断、运维手册和 Docker 说明。",
},
href: "/docs/troubleshooting",
topicId: "troubleshooting",
keywords: { en: "error crash doctor diagnose recover", zh: "错误 崩溃 诊断 恢复" },
},
{
id: "trust",
label: { en: "Understand what leaves your machine", zh: "了解哪些数据会离开你的机器" },
description: {
en: "The hosted-provider boundary, the sandbox, telemetry field by field, and how to turn it off.",
zh: "托管提供商边界、沙箱、逐项说明的遥测,以及如何关闭。",
},
href: "/docs/trust",
topicId: "trust",
keywords: { en: "privacy security telemetry data vulnerability report", zh: "隐私 安全 遥测 数据 漏洞" },
},
];
/** The owning topic for a task, or undefined if the registry drifted. */
export function taskTopic(task: DocTask): DocTopic | undefined {
return DOC_TOPICS.find((t) => t.id === task.topicId);
}
/** Lowercase haystack across both languages, the route, and the topic. */
export function docTaskHaystack(task: DocTask): string {
return [
task.id,
task.label.en,
task.label.zh,
task.description.en,
task.description.zh,
task.keywords.en,
task.keywords.zh,
task.href,
task.topicId,
]
.join(" ")
.toLowerCase();
}