Back to Discover

SigaoLi.github.io

connector

SigaoLi

Read-only access to Sigao Li's profile, CV and case studies. Bilingual (EN/ZH).

View on GitHub
0 starsSynced Aug 5, 2026

Install to Claude Code

/plugin marketplace add SigaoLi/SigaoLi.github.io

README

sigaoli.com

Personal website of Sigao Li — AI Product Manager · Spatial Data Scientist. From maps to models, and the products in between.

Bilingual (English at /, 中文 at /zh/), built with Astro + Tailwind CSS v4 + GSAP, deployed to GitHub Pages via GitHub Actions. Launched 2026-06-11, replacing the previous Jekyll (academicpages) site.

Highlights

  • Generative canvas effects on a map motif — an interactive particle field (home), contour terrain (work), a "river as timeline" with a flow field (CV), and a geo-network arc map (photography); all vanilla canvas/SVG, tuned to 60fps with reduced-motion and mobile fallbacks
  • Dotted world map — land sampled from Natural Earth, with 76 GPS-extracted photo footprints across 6 countries; click a marker to open that country's gallery
  • Zoe, the digital doorcat — Sigao's cat (驺虞) lives in the corner of every page as a set of AI-generated, chroma-keyed VP9-alpha video clips pinned to shared anchor poses, driven by a state machine: she dozes off when ignored, reacts to page switches, listens while you type, "types back" while the assistant streams, and keeps a few easter eggs (production handbook in docs/)
  • Built-in AI layer — a floating chat assistant (fronted by Zoe) on every page — it suggests the single most relevant page as you ask, and greets a returning visitor by name (stored only in their own browser, opt-in) — plus a personal MCP server, both fed by a build-time knowledge pack generated from the same sources as the pages (see below)
  • Machine-readable by design/llms.txt, /llms-full.txt, /resume.json (JSON Resume), /knowledge.json, /.well-known/mcp.json, JSON-LD, and a robots.txt that explicitly welcomes AI crawlers
  • Build-time translation pipeline — long-form zh content generated by LLM with hash caching; human edits are protected from re-translation
  • Lighthouse (mobile): 96–100 across all categories; zero cookies, no paid services, and a plain-language privacy notice at /privacy

Commands

CommandAction
npm run devDev server at localhost:4321 (Astro 7 runs it as a daemon — stop with npx astro dev stop)
npm run buildProduction build to dist/
npm run previewServe the production build locally
node scripts/translate.mjsRe-translate changed en content → zh (needs .env, see .env.example; manually edited zh files are never overwritten)
node scripts/check-links.mjsInternal link integrity check over dist/
node scripts/verify-nav.mjsPlaywright interaction suites (run against a local server)
npm run dev (in worker/)Chat + MCP Worker at localhost:8787 (wrangler; secrets in worker/.dev.vars, never committed)
node scripts/verify-chat.mjsE2E chat-widget test (needs both dev servers running)
node scripts/verify-zoe.mjsE2E for Zoe's action state machine (append ?zoe-fast locally to compress minute-scale timers)
node scripts/verify-typeroute.mjsE2E for the intent-driven typing clip and the bilingual 404 page

Any Playwright suite that waits on Zoe's state must pin the clock (Date.prototype.getHours = () => 14): between 23:00 and 06:00 she starts the session asleep, so state never reaches idle and the run just times out.

When adding a Zoe clip, decide who prewarms it and when at the same time. A clip that is only fetched at playback stalls on a slow connection, and the stage shows nothing until it decodes. Prewarming has been missed three times already. Note warm() takes the file name (sit-to-loaf), not the ZOE key (sitToLoaf).

Structure

src/
├── pages/            # en routes + zh/ mirrors; llms.txt / resume.json / knowledge.json endpoints
├── components/       # Nav, Hero, WorldMap, Lightbox, CommandK, ChatWidget …
│   └── pages/        # shared page bodies rendered by both locales
├── content/          # cases & research (en) + cases-zh & research-zh (generated, reviewed)
├── data/             # cv.json / cv.zh.json / photos.json (GPS + bilingual alts)
│   └── knowledge/    # persona sources for the AI assistant (about / faq / guidelines / boundaries)
├── lib/              # i18n dict, GSAP lifecycle helper, site config
│   └── knowledge/    # knowledge-pack pipeline (same-source layers + build-time privacy guard)
└── assets/           # photo originals (optimized at build; originals never shipped)
worker/               # Cloudflare Worker: /chat (SSE) + /classify (intent) + /mcp (MCP server)
└── src/core/         # runtime-agnostic logic; Cloudflare specifics live only in src/adapter/
public/zoe/           # Zoe's clip library (600p VP9 alpha, lazy-loaded; idle loads first)
docs/                 # zoe-production-handbook.md — clip production specs & prompt cards

AI layer

One knowledge layer, three outlets: /llms-full.txt for passive crawlers, a chat assistant (POST /chat, SSE) for humans, and an MCP server (/mcp, Streamable HTTP, no auth — tools: get_profile / list_experience / get_case_study) for visiting agents, both served from api.sigaoli.com (Cloudflare Worker, code in worker/). The knowledge pack (/knowledge.json) is assembled at build time from the same sources as the pages — persona markdown, cv.json, case studies, photo stats — so any content edit propagates to all three outlets on the next deploy, no manual step. A privacy guard fails the build if sensitive patterns (phone numbers, IDs, coordinates) ever leak into the pack.

Alongside each reply the chat runs a lightweight intent classifier (POST /classify, a small model) to suggest the single most relevant page, and can remember a returning visitor's name — both kept entirely in the visitor's own browser (opt-in, clearable via "Forget me"), never on a server. Visitors in the EU/EEA/UK have their chat and classification routed to an EU-hosted provider, never the China-direct API. What the site stores and sends is described in plain language at /privacy.

Editing content

  • Case studies / research: edit src/content/cases/*.md (en), then run the translate script — or edit the -zh files directly (they're override-protected afterwards).
  • CV: edit src/data/cv.json (+ cv.zh.json); the timeline, /resume.json and /llms-full.txt all render from it. Replace public/files/pdf/CV__Sigao_Li.pdf alongside.
  • UI strings & hero copy: hand-written bilingual dictionary in src/lib/i18n.ts.
  • Photos: drop JPGs into src/assets/photos/<country>/, add entries to src/data/photos.json (run node scripts/extract-gps.mjs for coordinates). Photo stats in the AI knowledge pack update automatically.
  • AI assistant persona: edit src/data/knowledge/*.md; the knowledge pack rebuilds on every deploy and the assistant follows within ~10 minutes (Worker-side cache TTL).
  • Zoe's actions: source clips live outside the repo; the pipeline (scripts/zoe-board2.mjszoe-qc2.mjszoe-prod2.mjs) keys, QCs, mirrors and encodes them into public/zoe/. New actions = one clip + one row in the ZOE table in ChatWidget.astro; specs and prompt cards in docs/zoe-production-handbook.md.

Deployment

Push to master → GitHub Actions (.github/workflows/deploy.yml) audits, builds and deploys to Pages. Pushes to v2 build without deploying (verification).

The Worker deploys separately: cd worker && npx wrangler deploy (secrets via wrangler secret put; custom domain api.sigaoli.com bound in the Cloudflare dashboard). When a batch changes both, deploy the Worker first — the chat UI calls its endpoints, so a site push ahead of the Worker leaves a brief window where those calls 404.

⚠️ Never click "Sync fork". This repository began as an academicpages fork; syncing would reset master to the upstream template. If that ever happens again: git push --force origin <good-commit>:master.

Rendered live from SigaoLi/SigaoLi.github.io's GitHub README — not stored, always reflects the source repo.

1 Install Method

NameDescriptionCategorySource
streamable-http remoteHosted streamable-http endpointmcp-serverhttps://api.sigaoli.com/mcp

0 Comments

Login required
Log in to post a comment or update on this repo.

No comments yet — be the first to share an update.