diff --git a/src/layouts/DocsLayout.astro b/src/layouts/DocsLayout.astro index 0deb5dc..681def7 100644 --- a/src/layouts/DocsLayout.astro +++ b/src/layouts/DocsLayout.astro @@ -3,6 +3,7 @@ import { productName } from "~/content/docs/_meta"; import Base from "./Base.astro"; import SiteHeader from "~/components/SiteHeader.astro"; import SiteFooter from "~/components/SiteFooter.astro"; +import NeonMotion from "~/components/NeonMotion.astro"; import { docHref, getProduct, @@ -41,6 +42,8 @@ interface Props { modified?: string; /** Where the page's text is edited, when not with the product's other docs */ editUrl?: string; + /** The documentation home: full width, no sidebar or table of contents, search across every product */ + hub?: boolean; } const { @@ -57,6 +60,7 @@ const { published, modified, editUrl, + hub = false, } = Astro.props; const lang = getLang(Astro); const zh = lang === "zh-CN"; @@ -81,9 +85,10 @@ const tocHeadings = headings .filter((h) => h.depth === 2 || h.depth === 3) .map((h) => ({ ...h, text: h.text.replace(/#+\s*$/, "").trim() })); -const itemBase = "flex items-center justify-between gap-2 rounded-lg px-3 text-[15px] transition-colors"; -const itemActive = "text-[var(--color-brand-1)] font-medium"; -const itemIdle = "text-[var(--color-text)]/80 hover:text-[var(--color-text)]"; +const navLabel = zh ? `${productName(p, lang)} 文档导航` : `${productName(p, lang)} documentation`; +const editLabel = zh ? "在 GitHub 上编辑" : "Edit on GitHub"; +const updated = modified?.slice(0, 10); +const updatedLabel = zh ? `最后更新于 ${updated}` : `Last updated ${updated}`; --- - -
-
-
- -
+ )} - {(prev || next) && ( - - )} - - - - {tocHeadings.length > 0 ? ( - + + + + + + {tocHeadings.length > 0 ? ( + + ) : } +
+ + )} +
+ @@ -424,22 +649,28 @@ const itemIdle = "text-[var(--color-text)]/80 hover:text-[var(--color-text)]"; diff --git a/src/pages/docs/_DocArticle.astro b/src/pages/docs/_DocArticle.astro index c4d32e1..20861b6 100644 --- a/src/pages/docs/_DocArticle.astro +++ b/src/pages/docs/_DocArticle.astro @@ -104,24 +104,59 @@ const homeCards = slug ? [] : p.docs.filter((d) => d.slug); {/* The "Import links" page ends with its link builder, which a markdown file cannot hold */} {product === "lite" && slug === "import-links" && } {synced && ( -

+

{zh ? "本页取自 ThinkWatch Core 仓库 " : "This page is published from "} {synced.source} {zh ? `(${synced.ref})。` : ` in the ThinkWatch Core repository, at ${synced.ref}.`}

)} {homeCards.length > 0 && ( -
- {homeCards.map((d) => ( - -
{d.label[lang]}
-

{d.summary?.[lang]}

-
- ))} -
+ )} + + diff --git a/src/pages/docs/_DocsHome.astro b/src/pages/docs/_DocsHome.astro index c61b777..27fa449 100644 --- a/src/pages/docs/_DocsHome.astro +++ b/src/pages/docs/_DocsHome.astro @@ -1,7 +1,8 @@ --- import { productName } from "~/content/docs/_meta"; -// Documentation home (/docs, /zh-CN/docs): intro, one card per product listing -// its docs. It is also ThinkWatch's docs home, so the sidebar shows ThinkWatch. +// Documentation home (/docs, /zh-CN/docs): a short hero with search across +// every product, one tile per product listing its docs, then other resources. +// It is also ThinkWatch Enterprise's docs home ("Overview" in its sidebar). import DocsLayout from "~/layouts/DocsLayout.astro"; import { docHref, products, productHomeHref } from "~/content/docs/_meta"; import { localePath, type Lang } from "~/i18n"; @@ -18,84 +19,210 @@ const copy = zh title: "文档 · ThinkWatch", description: "ThinkWatch 企业版、ThinkWatch Lite 与 ThinkWatch Core 的文档,涵盖架构、部署、配置、API 参考、安全模型、桌面应用与共享核心。", - h1: "文档", + label: "文档", + h1a: "ThinkWatch ", + h1b: "文档", intro: - "三个产品分别提供文档:面向组织的 ThinkWatch 企业版;桌面应用 ThinkWatch Lite,在本机运行网关,也可以连接服务器上的 ThinkWatch Core;以及 ThinkWatch Core,即 ThinkWatch Lite 所基于的网关引擎与 Rust crate,ThinkWatch 企业版也使用其中的四个 crate。可通过上方的切换器选择产品,或从下方卡片进入。", + "三个产品分别提供文档:面向组织的 ThinkWatch 企业版;桌面应用 ThinkWatch Lite,在本机运行网关,也可以连接服务器上的 ThinkWatch Core;以及 ThinkWatch Core,即 ThinkWatch Lite 所基于的网关引擎与 Rust crate,ThinkWatch 企业版也使用其中的四个 crate。", + search: "搜索全部文档", open: (name: string) => `进入 ${name} 文档`, more: "其他资源", + resources: [ + { href: "https://github.com/ThinkWatchProject", title: "GitHub", text: "三个产品的源码、Issues 与 Release。" }, + { href: localePath(lang, "/changelog"), title: "更新日志", text: "各发布版本的变更记录。" }, + { + href: "https://github.com/ThinkWatchProject/ThinkWatch/blob/main/LICENSING.md", + title: "LICENSING.md", + text: "ThinkWatch 企业版的生产环境阈值与商业授权。", + }, + ], } : { title: "Documentation · ThinkWatch", description: "Documentation for ThinkWatch Enterprise, ThinkWatch Lite, and ThinkWatch Core, covering architecture, deployment, configuration, API reference, the security model, the desktop application, and the shared core.", - h1: "Documentation", + label: "Documentation", + h1a: "ThinkWatch ", + h1b: "documentation", intro: - "Documentation is provided for three products: ThinkWatch Enterprise, for organizations; ThinkWatch Lite, a desktop application that runs the gateway locally or connects to ThinkWatch Core on a server; and ThinkWatch Core, the gateway engine and Rust crates that ThinkWatch Lite is built on, three of which ThinkWatch Enterprise also uses. Select a product with the switcher above or from the cards below.", + "Documentation is provided for three products: ThinkWatch Enterprise, for organizations; ThinkWatch Lite, a desktop application that runs the gateway locally or connects to ThinkWatch Core on a server; and ThinkWatch Core, the gateway engine and Rust crates that ThinkWatch Lite is built on, four of which ThinkWatch Enterprise also uses.", + search: "Search all docs", open: (name: string) => `Open ${name} docs`, more: "Additional resources", + resources: [ + { href: "https://github.com/ThinkWatchProject", title: "GitHub", text: "Source, issues and releases for all three products." }, + { href: localePath(lang, "/changelog"), title: "Changelog", text: "Release notes for each version." }, + { + href: "https://github.com/ThinkWatchProject/ThinkWatch/blob/main/LICENSING.md", + title: "LICENSING.md", + text: "Production thresholds and commercial tiers for ThinkWatch Enterprise.", + }, + ], }; --- - -

{copy.h1}

-

{copy.intro}

+ +
+

{copy.label}

+

{copy.h1a}{copy.h1b}

+

{copy.intro}

+ +
- -
-
- {products.map((p) => { +
+ {products.map((p, i) => { const docs = p.docs.filter((d) => d.slug); return ( -
-

- {productName(p, lang)} +
+

+ {productName(p, lang)}

-

{p.tagline[lang]}

-
); })}

-
-

{copy.more}

- {zh ? ( - - ) : ( - - )} -
-
+
+

{copy.more}

+
+ {copy.resources.map((r) => ( + + {r.title} + {r.text} + + ))} +
+
+ + diff --git a/src/styles/global.css b/src/styles/global.css index 41c52de..e3567ce 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -265,9 +265,13 @@ scroll-margin-top: 5rem; position: relative; } -.prose-tw h1 { font-size: 2.25rem; line-height: 1.15; margin-top: 0; margin-bottom: 0.5em; letter-spacing: -0.025em; } -.prose-tw h2 { font-size: 1.6rem; line-height: 1.25; margin-top: 2.4em; margin-bottom: 0.6em; padding-top: 1.4em; border-top: 1px solid rgba(255,255,255,0.06); } -.prose-tw h3 { font-size: 1.2rem; line-height: 1.35; margin-top: 1.9em; margin-bottom: 0.5em; } +.prose-tw h1 { font-size: clamp(2rem, 3.4vw, 2.75rem); line-height: 1.1; margin-top: 0; margin-bottom: 0.45em; letter-spacing: -0.035em; text-wrap: balance; } +:lang(zh-CN) .prose-tw h1 { line-height: 1.22; letter-spacing: -0.01em; } +/* The paragraph under the title reads as the page's lede */ +.prose-tw > h1 + p { font-size: 1.1rem; line-height: 1.7; color: var(--color-muted); } +.prose-tw h2 { font-size: 1.55rem; line-height: 1.25; margin-top: 2.6em; margin-bottom: 0.6em; letter-spacing: -0.025em; } +:lang(zh-CN) .prose-tw h2 { letter-spacing: -0.005em; } +.prose-tw h3 { font-size: 1.15rem; line-height: 1.35; margin-top: 2em; margin-bottom: 0.5em; } .prose-tw h4 { font-size: 1.0rem; line-height: 1.4; margin-top: 1.6em; margin-bottom: 0.4em; color: #e5e7eb; } .prose-tw p, @@ -297,16 +301,17 @@ .prose-tw ul { list-style: disc; } .prose-tw ol { list-style: decimal; } .prose-tw li { margin-top: 0.4em; } -.prose-tw li::marker { color: var(--color-brand-2); } +.prose-tw li::marker { color: var(--color-dim); } +/* A note: the mark's cyan as a rule down its side */ .prose-tw blockquote { - border-left: 2px solid var(--color-brand-2); - padding: 0.25em 0 0.25em 1em; - background: color-mix(in oklab, var(--color-brand-2) 5%, transparent); - border-radius: 0 8px 8px 0; - font-style: italic; + border-left: 2px solid rgb(var(--a1)); + padding: 0.8em 1.1em; + background: rgb(var(--a1) / 0.05); + border-radius: 0 10px 10px 0; } -.prose-tw blockquote p { color: #e5e7eb; } +.prose-tw blockquote p { color: #d4d7dc; } +.prose-tw blockquote > * + * { margin-top: 0.6em; } .prose-tw hr { border: 0; @@ -318,18 +323,18 @@ .prose-tw :not(pre) > code { font-family: var(--font-mono); font-size: 0.85em; - color: var(--color-brand-1); - background: rgba(34, 229, 242, 0.08); - border: 1px solid rgba(34, 229, 242, 0.2); + color: #e6e8eb; + background: rgba(255, 255, 255, 0.06); + border: 1px solid rgba(255, 255, 255, 0.1); padding: 0.1em 0.4em; - border-radius: 4px; + border-radius: 5px; } /* Code blocks (Shiki output) */ .prose-tw pre.shiki, .prose-tw pre.astro-code { - background: #0c0d0f !important; - border: 1px solid rgba(255,255,255,0.08); + background: #0b0c0e !important; + border: 1px solid rgba(255,255,255,0.1); border-radius: 12px; padding: 1.1rem 1.25rem; font-size: 0.81rem; @@ -358,6 +363,8 @@ .prose-tw .table-scroll { overflow-x: auto; margin: 1.5em 0; + border: 1px solid rgba(255,255,255,0.1); + border-radius: 12px; } .prose-tw .table-scroll > table { margin: 0; } @media (max-width: 40rem) { @@ -379,9 +386,10 @@ } .prose-tw td { padding: 0.6em 1em; - border-bottom: 1px solid rgba(255,255,255,0.05); + border-bottom: 1px solid rgba(255,255,255,0.06); color: #c3c7ce; } +.prose-tw tbody tr:last-child td { border-bottom: 0; } .prose-tw tr:hover td { background: rgba(255,255,255,0.02); } /* Heading anchor link (rehype-autolink-headings) */ @@ -402,7 +410,7 @@ /* Images */ .prose-tw img { max-width: 100%; - border-radius: 10px; + border-radius: 12px; border: 1px solid rgba(255,255,255,0.08); margin: 1.5em 0; }