Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
b43df8e
Migrate ThinkWatch docs into the marketing site
fylorn Apr 6, 2026
ee86a84
Sync new logo and brand assets from main repo
fylorn Apr 6, 2026
860e6e5
Docs polish: TOC, prev/next, copy buttons, search, i18n 404
fylorn Apr 6, 2026
bde2db9
Fix docs TOC: scroll-based scrollspy + auto-scroll inner list
fylorn Apr 6, 2026
7f54a9a
Header/footer anchor links work from non-home pages
fylorn Apr 6, 2026
12e8fef
SEO: richer sitemap, hreflang, JSON-LD, Google Analytics
fylorn Apr 8, 2026
37903ea
Mobile optimization
fylorn Apr 8, 2026
5285f51
feat: v0.2.0 changelog + landing page copy sync + fix in-page anchor …
fylorn Apr 10, 2026
ae5fc37
fix(changelog): replace ## headings with bold lead-ins in v0.2.0 entry
fylorn Apr 10, 2026
2d6f625
fix(nav): correct isHome for zh-CN trailing slash + robust anchor int…
fylorn Apr 10, 2026
b772511
feat(i18n): changelog zh-CN content + co-located en/zh-CN structure
fylorn Apr 10, 2026
911f80d
fix(changelog): use ## headings in body + hide anchor # + style h2
fylorn Apr 10, 2026
585b3e3
feat(nav): add Pricing / 定价 link to header
fylorn Apr 10, 2026
d2c9146
feat(header): fetch GitHub star count live on page load
fylorn Apr 12, 2026
3100213
feat(hero): change particle flow to inbound→gateway→outbound
fylorn Apr 12, 2026
8e47550
feat: add MCP store template registry JSON
fylorn Apr 13, 2026
c879ce1
feat: registry v2 — bilingual templates + knowledge/productivity cate…
fylorn Apr 13, 2026
78c53b6
feat: update mcp-templates.json with new endpoint URLs and transport …
fylorn Apr 13, 2026
06bfb43
feat(landing): cinematic polish — stats strip, live pill, pipeline rail
fylorn Apr 20, 2026
a0cb515
feat(mcp): per-user OAuth pitch — comparison section + v0.3.0 changelog
fylorn May 8, 2026
04b75e7
feat(mcp registry): drop name-only shells, add 5 real templates
fylorn May 12, 2026
04d50f6
feat(mcp registry): curate gateway-rational catalog (41 templates, +2…
fylorn May 12, 2026
99e9b6a
feat(mcp registry): drop approved-client-allowlist providers, revert …
fylorn May 13, 2026
2d6329f
feat(landing): v0.4.0 — body capture, MCP streaming, per-model routing
fylorn May 23, 2026
41ea367
docs(zh-CN): translate secret-rotation guide
fylorn May 23, 2026
83a787f
feat: redesign the site around ThinkWatch and ThinkWatch Lite (#1)
fylorn Sep 14, 2026
bbb9f45
fix: use the product sans for headings and write the copy formally (#2)
fylorn Sep 14, 2026
8e20f03
feat: Enterprise nav label, and the request flow in the hero (#3)
fylorn Sep 14, 2026
46b6548
fix: call the server edition ThinkWatch Enterprise wherever editions …
fylorn Sep 14, 2026
5308f5e
feat: bring back the 3D particle hero in brand teal, with the termina…
fylorn Sep 14, 2026
2f90d13
perf: render the 3D hero at 30 fps, lower resolution, and dim its glo…
fylorn Sep 14, 2026
d3a5ead
docs: compare against the category, not against named vendors
fylorn Sep 15, 2026
10086cb
feat(lite): describe Lite as a released app, with screenshots on the …
fylorn Sep 20, 2026
aa7bd70
feat(lite): the English pages show the English interface (#9)
fylorn Sep 20, 2026
4131e38
feat(lite): Windows on the Lite page and in its docs (#10)
fylorn Sep 23, 2026
748da71
docs(lite): notices have one setting, and the interface is in English…
fylorn Sep 23, 2026
999d2a3
feat(lite): a direct disk-image download, and Windows 10 21H2 as the …
fylorn Sep 24, 2026
ff3fea5
feat(lite): download in one click from the home page and the Lite pag…
fylorn Sep 24, 2026
6295fb8
feat(lite): Linux on the Lite page, the one-click download and the do…
fylorn Sep 24, 2026
947a640
feat(lite): copy buttons on the install commands (#15)
fylorn Sep 24, 2026
4a34ce1
feat(seo): structured data and share images per page, sitemap dates f…
fylorn Sep 25, 2026
7502d64
feat(seo): social preview images for the GitHub repositories (#17)
fylorn Sep 25, 2026
bb320df
perf(fonts): serve Geist from the site instead of Google Fonts (#20)
fylorn Sep 25, 2026
6f79929
fix(license): use a contact address that receives mail (#21)
fylorn Sep 25, 2026
a0abf43
feat(core): server deployment on the Core page, the home page and the…
fylorn Sep 25, 2026
88b329b
Lite page and docs: the current app, remote core, and the licensing c…
fylorn Sep 25, 2026
8712be5
feat(enterprise): remove the star history chart (#22)
fylorn Sep 25, 2026
43dac08
docs(core): server guide from ThinkWatch Core v0.49.1 (#23)
fylorn Sep 25, 2026
5a51ffb
docs(lite): clients, quota and WSL as of ThinkWatch Lite 2026.9.18 (#24)
fylorn Sep 26, 2026
3f85d37
docs(lite): describe the menu bar menu without the undo item (#25)
fylorn Sep 26, 2026
23c5d66
Import links: web form and relay documentation (#26)
fylorn Sep 29, 2026
d3da784
Lite page, home and docs: concise copy that leads with the benefits (…
fylorn Sep 30, 2026
c861d46
Lite page: label the status card (platforms, interface, updates, lice…
fylorn Sep 30, 2026
1b27925
Name the relay threat: keys replaced, malicious tool calls cut off (#29)
fylorn Sep 30, 2026
fab8f98
Lite page: redesign the install section (#30)
fylorn Sep 30, 2026
b7d3e30
Redesign the Lite architecture diagram; tidy the home page (#31)
fylorn Sep 30, 2026
1ece352
Core and Enterprise pages: current facts, full commands, tidier grids…
fylorn Sep 30, 2026
6d2c7b8
Correct the Enterprise page claims against ThinkWatch v2.1.0 (#33)
fylorn Sep 30, 2026
a7d542f
Drop unused Enterprise copy that no page renders (#34)
fylorn Sep 30, 2026
bd12c5b
Site redesign: near-black ground, the desktop app's neon mark, the Li…
fylorn Sep 30, 2026
bb2fb91
Home, Core and Enterprise pages in the neon layout (#36)
fylorn Sep 30, 2026
2a90550
Fix the Lite bento layout, unframe screenshots, stack notifications (…
fylorn Oct 1, 2026
7767c84
Docs in the neon layout (#38)
fylorn Oct 1, 2026
ff81205
Give ThinkWatch Enterprise its own docs home (#39)
fylorn Oct 1, 2026
b326f26
Home: ThinkWatch Lite before ThinkWatch Enterprise; no MIT chip (#40)
fylorn Oct 1, 2026
a64b93e
Changelog and license pages in the neon layout (#41)
fylorn Oct 1, 2026
e93107b
Refresh the Core documents snapshot to v0.56.0 (#42)
fylorn Oct 1, 2026
eac098b
Changelog: show the notes published with each release on GitHub (#43)
fylorn Oct 1, 2026
2d0d3e5
Changelog: mark Chinese-only notes on the English page (#44)
fylorn Oct 1, 2026
2774fa5
Call the license page "Pricing"; Lite and Core are "free to use" (#45)
fylorn Oct 1, 2026
54068c7
Move the pricing page to /pricing (#46)
fylorn Oct 1, 2026
b016055
404 page in the neon layout (#47)
fylorn Oct 1, 2026
80238a1
404 page: the site header and footer follow the visitor's language to…
fylorn Oct 1, 2026
5fe2f71
Show download sizes next to the installers and binaries (#49)
fylorn Oct 1, 2026
958f167
Lite docs and page for 2026.10.0: five more clients, search, check-up…
fylorn Oct 1, 2026
826f745
Sponsor line on the Lite page and in the site footer (#51)
fylorn Oct 2, 2026
3438f5f
Lite docs for 2026.10.1: plugins and the session conversation (#53)
fylorn Oct 3, 2026
19afd9e
Docs for the three request guards (Core #268, Enterprise #72) (#52)
fylorn Oct 3, 2026
6039987
Lite page: plugins tile in the bento, and on the home page (#55)
fylorn Oct 3, 2026
222922c
Pin pnpm through packageManager (#56)
fylorn Oct 3, 2026
66817ef
docs(lite): auxiliary requests are answered locally or forwarded (#54)
fylorn Oct 3, 2026
36728f0
Lite: Windows portable build and the new release file names (#57)
fylorn Oct 3, 2026
0629078
Header: the GitHub pill shows ThinkWatch Lite, the enterprise reposit…
fylorn Oct 4, 2026
d241e30
Shorter meta descriptions so search results show them whole (#59)
fylorn Oct 4, 2026
aba60b9
llms.txt, llms-full.txt and IndexNow on deploy (#60)
fylorn Oct 4, 2026
26509cd
Lite docs: seven guides, a comparison with CC Switch and an FAQ (#62)
fylorn Oct 4, 2026
f669879
Lite 2026.10.5 docs: model aliases, specified models, ThinkWatch's ow…
fylorn Oct 5, 2026
5bc130e
Lite guides: GLM's OpenAI-compatible address, usable from 2026.10.5 (…
fylorn Oct 5, 2026
1fc21e0
Docs for Lite 2026.10.6, core 0.63.0 and ThinkWatch 3.2.0 (#64)
fylorn Oct 5, 2026
3733833
Docs for Lite 2026.10.9, core 0.65.0 and ThinkWatch 3.2.1 (#65)
fylorn Oct 8, 2026
6882822
Docs for Lite 2026.10.10 and core 0.66.0 (#66)
fylorn Oct 9, 2026
0e1c0c4
Docs for Lite 2026.10.11, core 0.67.1 and ThinkWatch 3.3.0 (#67)
fylorn Oct 9, 2026
e796f53
Docs for Lite 2026.10.12, core 0.68.0 and ThinkWatch 3.3.1 (#68)
fylorn Oct 10, 2026
6176dcc
Docs for Lite 2026.10.13, core 0.69.0 and ThinkWatch 3.4.0 (#69)
fylorn Oct 10, 2026
638fd4f
Docs for Lite 2026.10.14, core 0.70.0 and ThinkWatch 3.5.0 (#70)
fylorn Oct 10, 2026
2cf6839
Docs for Lite 2026.10.15 (#71)
fylorn Oct 10, 2026
bb58afd
Import links v2: per-service parameters, grouped import page, builder…
fylorn Oct 11, 2026
b7838f7
Drop a local path from a source comment
fylorn Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Check

# Builds the site for every pull request, the same way the deploy workflow does,
# so that a change that breaks the build (a share-image headline that no longer
# fits, a page that fails to render) shows up before it reaches main.

on:
pull_request:
branches: [main]

permissions:
contents: read

concurrency:
group: check-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# The sitemap's <lastmod> and the dates on doc pages come from git history.
fetch-depth: 0

# The pnpm version comes from package.json's packageManager, the same one
# a local `pnpm` switches to
- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build site
run: pnpm build
env:
GITHUB_TOKEN: ${{ github.token }}
57 changes: 51 additions & 6 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ on:
push:
branches: [main]
workflow_dispatch:
# Lite 页面上的下载按钮指向构建时查到的那一版安装包。Lite 发了新版之后,
# 不重新构建的话按钮会一直指着旧版本 —— 旧版照样能装、装上会自己更新,
# 但没理由让它停在那里。定时重建一次,最多晚三个小时跟上。
schedule:
- cron: "41 */3 * * *"

permissions:
contents: read
pages: write
id-token: write
# Each job gets only what it needs (below). The build runs third-party code from
# npm with GITHUB_TOKEN in its environment, so it may only read; the Pages
# permissions belong to the deploy job alone.
permissions: {}

concurrency:
group: pages
Expand All @@ -17,14 +22,20 @@ concurrency:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# The sitemap's <lastmod> and the dates on doc pages come from git
# history; a shallow clone would leave them out.
fetch-depth: 0

# The pnpm version comes from package.json's packageManager, the same one
# a local `pnpm` switches to
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10

- name: Setup Node
uses: actions/setup-node@v4
Expand All @@ -37,6 +48,10 @@ jobs:

- name: Build site
run: pnpm build
env:
# The build reads versions and releases from the GitHub API; with a
# token the anonymous rate limit does not apply.
GITHUB_TOKEN: ${{ github.token }}

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
Expand All @@ -46,10 +61,40 @@ jobs:
deploy:
needs: build
runs-on: ubuntu-latest
permissions:
pages: write # to deploy to GitHub Pages
id-token: write # to verify that the deployment comes from this workflow
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

# Tells the search engines that take IndexNow (Bing, which ChatGPT search
# and Copilot also use, Yandex and others) every address in the sitemap,
# so new and changed pages are crawled soon after a deploy. The key file
# in public/ shows the site is ours. Only for a push or a manual run:
# the scheduled rebuild every three hours would resubmit unchanged pages.
# A failed notification does not fail the deploy.
- name: Notify IndexNow
if: github.event_name != 'schedule'
continue-on-error: true
env:
INDEXNOW_KEY: f7c3fcca3acfc15fb46eca5e1fbde518
run: |
set -euo pipefail
curl -fsSL https://thinkwat.ch/sitemap-0.xml \
| grep -o '<loc>[^<]*</loc>' | sed -e 's/<loc>//' -e 's|</loc>||' > urls.txt
echo "$(wc -l < urls.txt) addresses"
python3 - <<'PY'
import json, os
urls = [u.strip() for u in open("urls.txt") if u.strip()]
key = os.environ["INDEXNOW_KEY"]
json.dump({"host": "thinkwat.ch", "key": key,
"keyLocation": f"https://thinkwat.ch/{key}.txt", "urlList": urls},
open("indexnow.json", "w"))
PY
curl -fsS -o /dev/null -w "IndexNow answered %{http_code}\n" -X POST https://api.indexnow.org/indexnow \
-H "Content-Type: application/json; charset=utf-8" --data @indexnow.json
130 changes: 128 additions & 2 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,22 +1,148 @@
// @ts-check
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import sitemap from '@astrojs/sitemap';
import sitemap, { ChangeFreqEnum } from '@astrojs/sitemap';
import tailwindcss from '@tailwindcss/vite';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import { lastModified, pageReleases, pageSources } from './src/lib/lastmod.ts';
import { getReleases } from './src/lib/github.ts';

/**
* Wraps every markdown table in a container that scrolls sideways. The page
* clips horizontal overflow (global.css), so a table wider than a phone screen
* would otherwise be cut off rather than scroll.
*/
function rehypeScrollingTables() {
/** @param {any} node */
const visit = (node) => {
if (!Array.isArray(node.children)) return;
node.children = node.children.map((/** @type {any} */ child) => {
if (child.type === 'element' && child.tagName === 'table') {
// Focusable, like the code blocks, so that it can be scrolled from the keyboard.
return { type: 'element', tagName: 'div', properties: { className: ['table-scroll'], tabIndex: 0 }, children: [child] };
}
visit(child);
return child;
});
};
return (/** @type {any} */ tree) => visit(tree);
}

// https://astro.build/config
export default defineConfig({
site: 'https://thinkwat.ch',
// Pages that moved. GitHub Pages cannot answer with a redirect, so each old
// address is built as a page that sends the browser on (and is not indexed).
redirects: {
'/license': '/pricing',
'/zh-CN/license': '/zh-CN/pricing',
},
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh-CN'],
routing: {
prefixDefaultLocale: false,
},
},
integrations: [react(), sitemap({ i18n: { defaultLocale: 'en', locales: { en: 'en', 'zh-CN': 'zh-CN' } } })],
integrations: [
react(),
sitemap({
i18n: {
defaultLocale: 'en',
locales: { en: 'en', 'zh-CN': 'zh-CN' },
},
// The import page only reads the parameters of a link; it has nothing to index.
filter: (page) => !page.includes('/404') && !/\/(zh-CN\/)?import\/?$/.test(new URL(page).pathname),
async serialize(item) {
const url = new URL(item.url);
const pathname = url.pathname.replace(/\/$/, '') || '/';
const isHome = pathname === '/' || pathname === '/zh-CN';
const isDoc = pathname.startsWith('/docs/') || pathname.startsWith('/zh-CN/docs/');
const isDocsIndex = pathname === '/docs' || pathname === '/zh-CN/docs';
const isChangelog = pathname === '/changelog' || pathname === '/zh-CN/changelog';
// Product landing pages: ThinkWatch, ThinkWatch Lite, ThinkWatch Core.
const productPaths = ['/thinkwatch', '/lite', '/core'];
const isProduct = productPaths.some((p) => pathname === p || pathname === `/zh-CN${p}`);
const isPricing = pathname === '/pricing' || pathname === '/zh-CN/pricing';

if (isHome) {
item.priority = 1.0;
item.changefreq = ChangeFreqEnum.WEEKLY;
} else if (isProduct) {
item.priority = 0.9;
item.changefreq = ChangeFreqEnum.WEEKLY;
} else if (isDocsIndex) {
item.priority = 0.9;
item.changefreq = ChangeFreqEnum.WEEKLY;
} else if (isDoc) {
// Includes the product doc sets under /docs/lite/ and /docs/core/.
item.priority = 0.8;
item.changefreq = ChangeFreqEnum.MONTHLY;
} else if (isChangelog) {
item.priority = 0.7;
item.changefreq = ChangeFreqEnum.WEEKLY;
} else if (isPricing) {
item.priority = 0.6;
item.changefreq = ChangeFreqEnum.MONTHLY;
} else {
item.priority = 0.5;
item.changefreq = ChangeFreqEnum.MONTHLY;
}

// When the page's own files last changed in git, or the latest release
// of a product the page shows came out, whichever is later. Left out
// without git history: a release date alone does not say when the
// page itself last changed.
const committed = lastModified(pageSources(pathname));
if (committed) {
let latest = Date.parse(committed);
const products = pageReleases(pathname);
if (products.length) {
// Newest first: the first match is the latest release among them.
const release = (await getReleases()).find((r) => products.includes(r.product));
if (release) latest = Math.max(latest, Date.parse(release.date));
}
item.lastmod = new Date(latest).toISOString();
}
return item;
},
}),
],
markdown: {
shikiConfig: {
theme: 'github-dark-default',
wrap: false,
},
rehypePlugins: [
rehypeScrollingTables,
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: 'append',
properties: {
className: ['heading-anchor'],
ariaLabel: 'Link to this section',
},
content: {
type: 'text',
value: '#',
},
},
],
],
},
vite: {
plugins: [tailwindcss()],
build: {
// Font files are always emitted as files (src/components/Fonts.astro).
// Vite would otherwise inline those under 4 KB, the smaller Unicode
// subsets of Geist, into the stylesheet as base64, and every page would
// download subsets it never uses before its first paint. Anything else
// keeps the default limit.
assetsInlineLimit: (file) => (/\.woff2?$/.test(file) ? false : undefined),
},
},
build: {
inlineStylesheets: 'auto',
Expand Down
22 changes: 16 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,42 @@
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"prebuild": "node scripts/generate-og.mjs && node scripts/generate-favicons.mjs",
"build": "astro build",
"prebuild": "node scripts/generate-favicons.mjs",
"build": "astro build && pnpm run pagefind",
"pagefind": "pagefind --site dist --output-path dist/pagefind",
"preview": "astro preview",
"og": "node scripts/generate-og.mjs",
"releases": "node scripts/update-releases.mjs",
"core-docs": "node scripts/update-core-docs.mjs",
"favicons": "node scripts/generate-favicons.mjs",
"astro": "astro"
},
"dependencies": {
"@astrojs/react": "^4.2.0",
"@astrojs/react": "^5.0.2",
"@astrojs/rss": "^4.0.10",
"@astrojs/sitemap": "^3.2.1",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.5.0",
"@tailwindcss/vite": "^4.0.0",
"astro": "^5.1.0",
"astro": "^6.1.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwindcss": "^4.0.0",
"three": "^0.183.2"
},
"devDependencies": {
"@fontsource/geist": "^5.3.0",
"@fontsource/geist-mono": "^5.3.0",
"@types/fontkit": "^2.0.9",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/three": "^0.183.1",
"fontkit": "^2.0.4",
"pagefind": "^1.5.0",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"sharp": "^0.34.5",
"shiki": "^4.0.2",
"typescript": "^5.7.0"
}
},
"packageManager": "pnpm@10.34.6"
}
Loading
Loading