Site operations — yutianpang.com
Site operations — yutianpang.com
Live site: https://yutianpang.com — GitHub Pages (branch main, root), built automatically with Jekyll from the Academic Pages template. Domain: GoDaddy DNS → 4 GitHub Pages A records on @, www CNAME → yutianpangasu.github.io. HTTPS enforced. The CNAME file keeps the custom domain across pushes — do not delete it.
Previous custom single-page design: branch custom-site.
Everyday updates
- News / about:
_pages/about.md - Publications: one Markdown file per paper in
_publications/(front matter: title, category, venue, date, excerpt = the one-sentence intro shown in lists and on the paper’s page, citation). Categories: manuscripts (Journal Articles), underreview, workingpapers, conferences, patents. - Talks:
_talks/ - CV:
_pages/cv.md+ replacefiles/Yutian_Pang_CV.pdf - Photo:
images/profile.jpg(square-cropped headshot; replace to update) - Footprint page (
/footprint/, travel log with maps and park photos):- Parks and photos: add the park to
_data/travel.yml, drop resized photos (~1600px long edge) intoimages/travel/<slug>/(folders for all 63 national parks already exist; seeimages/travel/README.md). States on the US map shade automatically from each park’sstatefield; states visited without a park entry go in_data/visited_states.yml. - Everything is pickable at
/footprint/?edit: click Interstates on the highway map (paste into_data/highways.yml), click states on the state map (paste into_data/visited_states.yml; park-derived states are locked), and tick national parks in the panel checklist (paste the generated entries into_data/travel.yml, then fill in visited dates).
- Parks and photos: add the park to
- Push to deploy:
git add -A && git commit -m "update" && git push(live in ~1 minute).
Bilingual content (中文 / ENG)
Every reader-facing page exists twice: English at the root (/, /cv/, …) and Chinese under /zh/ with the same path (/zh/, /zh/cv/, …). English is the default simply because it is what the root URLs serve; nothing is stored in the browser and nothing redirects. The switch in the upper right of the masthead is a plain link to the same page in the other language, computed by _includes/lang-switch-url.html; on an English page that has no Chinese counterpart (publication and talk pages) it links to the Chinese homepage. Each page also carries hreflang alternates in its head.
Where things live:
- Chinese pages:
_pages/zh/—about.md(→/zh/),cv.md,software.md,publications.html,footprint.html. Each haslang: zhand a/zh/…permalink;langis what flips the nav links to/zh/…, sets<html lang="zh-Hans" data-lang="zh">, and picks the Chinese strings in the shared templates. English pages need nothing extra. - Pages with heavy Liquid (Research, Footprint) keep one body include —
_includes/publications-body.html,_includes/footprint-body.html— used by both language pages, with the prose branched onpage.lang. The selected publications list on the homepage is_includes/selected-publications.md, shared by both homepages because titles and venues stay in English. - Short strings in shared templates (nav, sidebar, footer, contact panel, visitor map, publication lines):
Researchrenders one language at build time; leavezhoff to fall back to English. Nav labels taketitle_zhin_data/navigation.yml; site title, author fields and thepublication_areatitles/descriptions/topics take*_zhfields beside the English ones in_config.yml. - Chinese text is set in SimSun (
_sass/layout/_i18n.scss), with the nearest Song faces as fallbacks on machines that lack it.
Adding a page: write the English one as usual, then a _pages/zh/ twin with lang: zh and the /zh/ permalink — the switch finds it by URL. Updating the homepage news means editing both _pages/about.md and _pages/zh/about.md.
Deliberately left in English: paper titles, author lists, journal names, and the publication pages themselves; the US state names in the Footprint map tooltips; the owner-only edit panel at /footprint/?edit; and the 404 and sitemap pages.
Reach-out panel (message form)
A folded “Reach out” tab sits on the right edge of every page (_includes/contact-panel.html, included from _layouts/default.html). Clicking it slides out a panel with name, email, a category (meeting request, code request, other) and a message. Any link to #contact, #contact-code or #contact-meeting also opens it with that category preselected (the Software tab’s “reach out” link does this), and /contact/ redirects to the homepage with the panel open. The panel POSTs to the visitor worker (worker/visitor-worker.js, endpoint /contact), which stores the message in D1 and emails it to you through Resend with Reply-To set to the sender, so replying from Outlook goes straight back to them. Subjects look like [Website] [Code request] Jane Doe, handy for an Outlook rule. To change the categories, edit the <select> and hints in the include and the TOPICS table in the worker (then re-paste the worker).
Private inbox with every message and a “mark handled” toggle: <worker-url>/inbox (same admin key/cookie as the visit log; also linked from the visit log header). Messages are kept there even if the email fails to send.
One-time setup (worker → Settings → Variables and Secrets):
- https://resend.com account (created 2026-09-03 with
yutian.pang@outlook.com) → API Keys. Add the key as secretRESEND_API_KEY. - Add variable
CONTACT_TO=yutian.pang@outlook.com. It must be the Resend account’s own address: the default senderonboarding@resend.devdelivers only there (a UT address would be rejected until the domain is verified, see below). No DNS change is required for this. - Paste the current
worker/visitor-worker.jsinto the worker and Deploy. - Test: open https://yutianpang.com/#contact, send yourself a message, check Outlook (and spam) and
<worker-url>/inbox.
Optional later:
- Send from your own domain (nicer sender, lets
CONTACT_TObe any address such as the UT one, and needed before the worker could ever email visitors): Resend → Domains → Addyutianpang.com, add the TXT records it lists at GoDaddy, then set variableMAIL_FROMtoyutianpang.com <contact@yutianpang.com>. - Captcha if spam gets through the honeypot and rate limits (5 per IP per hour, 100 per day): Cloudflare → Turnstile → Add widget (hostname
yutianpang.com, Managed). Put the secret key in the worker asTURNSTILE_SECRETand the site key in_config.ymlundercontact.turnstile_site_key, then push.
Analytics (Google Analytics 4)
Create a GA4 property for https://yutianpang.com at https://analytics.google.com, copy the Measurement ID (G-XXXXXXXXXX), then in _config.yml set:
analytics:
provider: "google-analytics-4"
google:
tracking_id: "G-XXXXXXXXXX"
Geography reports (country / state / city): GA4 → Reports → User → User attributes → Demographic details.
Visitor map (bottom of the homepage)
_includes/visitor-map.html shows the MapMyVisitors widget (dashboard with per-visitor IP/time/location: mapmyvisitors.com account).
Optional upgrade to a full interactive on-page map: deploy worker/visitor-worker.js on Cloudflare (setup steps in that file), then set visitor.api in _config.yml to the worker URL. Anyone can then click dots to see visit time/city/state/country; your private IP log is at <worker-url>/admin?key=ADMIN_KEY.
The per-page beacon itself lives in _includes/visit-beacon.html and is pulled in sitewide from _includes/scripts.html, so every page counts, not only the homepage. The worker drops repeat hits from the same IP inside 30 minutes, so one reader browsing several pages is still one visit.
Counting readers in mainland China
Visitors in mainland China load the site normally, because GitHub Pages is reachable there, and then vanish: the beacon they send goes to visitor-log.yutian-pang.workers.dev, and the *.workers.dev domain is blocked. The request dies with no error anywhere on the page, so a blocked visit and no visit look identical. Confirmed on both ends: friends in China report reaching the site without a VPN, and the log holds zero CN rows out of 532 while showing Hong Kong, which sits outside the firewall.
The fix is to give the worker a custom domain. The site’s own hosting does not change at all, so the reachability your Chinese readers already have is untouched. Done 2026-09-08; the steps below are kept as a record. visits.yutianpang.com is a proxied AAAA record at 100:: plus the worker route visits.yutianpang.com/*, because the Add-domain dialog would not register the hostname. The apex stays DNS-only on GitHub Pages.
- Cloudflare → Add a site →
yutianpang.com, free plan. Let it import the existing records, then change the nameservers at GoDaddy to the two Cloudflare gives you. - In Cloudflare DNS, leave the four GitHub Pages A records (185.199.108–111.153) set to DNS only, the grey cloud. This is what keeps the site byte-for-byte as reachable as it is today. Do not proxy the apex; Cloudflare’s free edge is slower from China than GitHub Pages is.
- Worker → Settings → Domains & Routes → Add → Custom domain →
visits.yutianpang.com. Cloudflare creates the proxied record and the certificate on its own. In
_config.yml, swap the twovisitorlines:visitor: api : "https://visits.yutianpang.com" api_fallback : "https://visitor-log.yutian-pang.workers.dev"The fallback is only tried when the primary fails, and the worker tags the visits that needed it, so
/chinashows how much blocking is still happening.- Set
contact.apitohttps://visits.yutianpang.comas well. The reach-out panel POSTs to the same worker, so today a Chinese reader cannot send you a message either. Do this step only after the custom domain resolves: the contact form has no fallback.
Reading /china
<worker-url>/china (same admin key as /admin) splits visits into three tiers, deliberately kept separate rather than summed:
- In mainland China — Cloudflare placed the IP in CN. Certain, and a floor: networks that cannot reach the worker never appear.
- China-linked, VPN or overseas — a Chinese carrier or cloud network, or a
zh-CNbrowser running on anAsia/Shanghaiclock. Language and timezone are set by the browser rather than the network, so a VPN does not hide them. This is the tier that matters most: mainland academics reaching an overseas site are usually on a VPN, and their IP says Los Angeles. - Possibly China — one weak signal only. An upper bound; includes Chinese speakers anywhere.
The network table flags education networks. A hit carried by CERNET (AS4538), the Chinese university backbone, is almost certainly an academic reader, which is the signal worth watching on the faculty market.
No self-hosted tracker can give complete mainland numbers. Only analytics hosted inside China (Baidu Tongji) would, at the cost of handing the visitor log to Baidu.
