Project note: this repository is also a vibe-coding experiment. The initial version of the site was scaffolded with Lovable, then revised by @uujianghhh, and it is currently being iterated with Codex-based vibe coding. Hope you enjoy it.
This repository maintains the frontend for an academic lab website, along with several scripts and GitHub Actions workflows used to update publications, activities, and social/news content.
The project is now maintained and deployed as a standard GitHub repository. The frontend is built with Vite + React + TypeScript + Tailwind CSS, and the site is deployed to GitHub Pages.
The site currently includes the following main routes and content areas:
/home page and lab overview/aboutprofessor and lab introduction/projectsongoing and completed projects/publicationspublication list and publication cards/peoplefaculty, PhD students, graduate students, undergraduates, part-time members, and alumni/activitiesacademic activities, reading club, and lab news/joinadmissions and joining information/intranetinternal portal
This is a static website project. Most content is loaded from structured data in src/data/, public/content/, public/people/, public/publications/, and other static assets under public/.
- Vite
- React 18
- TypeScript
- React Router
- Tailwind CSS
- Radix UI / shadcn-style components
- GitHub Actions
- Python scripts for publications, reading club, and news updates
Recommended environment:
- Node.js 20 or newer
- npm
- Python 3.11 or similar for update scripts
Install dependencies:
npm installStart the development server:
npm run devIf you want to match the repository's default local verification flow, you can explicitly set the host and port:
npm run dev -- --host 127.0.0.1 --port 4173Common commands:
npm run dev
npm run build
npm run preview
npm run lintThe production build is written to dist/. For local preview:
npm run preview -- --host 127.0.0.1 --port 4173.
├── .github/workflows/ # GitHub Actions for deployment and content updates
├── public/
│ ├── content/ # Project content JSON and project images
│ ├── people/ # People JSON files and profile images
│ ├── publications/ # Publication data, card feeds, generated media assets
│ ├── reading_club_covers/ # Reading club cover images
│ ├── xhs_news_images/ # Cached news images
│ └── xhs_news_videos/ # Cached news videos
├── scripts/ # General content update scripts
├── src/
│ ├── backend/ # Publication update and card-generation scripts
│ ├── components/ # Shared UI and layout components
│ ├── data/ # Structured site data
│ └── pages/ # Route-level pages
├── MediaCrawler/ # Dependency used for Xiaohongshu/XHS crawling
└── run_xhs_pipeline_mediacrawler.py
- Route-level pages are in
src/pages/ - Shared layout and UI components are in
src/components/ - Structured site data is in
src/data/
Content that is commonly edited directly includes:
src/data/activities.tssrc/data/labLife.tssrc/data/readingClub.jsonpublic/content/*.jsonpublic/people/*.json
The projects page mainly depends on:
public/content/ongoing-projects.jsonpublic/content/completed-projects.jsonpublic/content/ongoing_image/public/content/completed_image/
If you are only updating project descriptions, links, or images, you usually do not need to modify the React components.
The people page mainly depends on:
public/people/faculty.jsonpublic/people/phd.jsonpublic/people/graduate.jsonpublic/people/Undergraduate.jsonpublic/people/part-time.jsonpublic/people/alumni.jsonpublic/people/people_image/
The main publication-related frontend inputs are:
public/publications/publication_updated.jsonpublic/publications/recent_publications.jsonpublic/publications/project_publications.jsonpublic/publications/recent_images/
In practice:
publication_updated.jsonis the complete publication datasetrecent_publications.jsonpowers recent publication cards on the homepageproject_publications.jsonpowers project-linked publication cardsrecent_images/stores images, videos, and poster assets used by publication cards
The publication update scripts live in src/backend/. The current local workflow is:
python3 src/backend/build_publication_updated.py
python3 src/backend/update_recent_publications_cards.py
python3 src/backend/update_project_publications_cards.pyBefore running them locally, you will typically need:
python3 -m pip install requests beautifulsoup4 lxml pillowAfter the update, verify that these outputs were refreshed consistently:
public/publications/publication_updated.jsonpublic/publications/recent_publications.jsonpublic/publications/project_publications.jsonpublic/publications/recent_images/
The publication workflow in this repository follows these rules:
- Google Scholar is the primary source
IHPDEP Selected Publicationsacts as a curated overlay- If a publication appears in both sources, the metadata from
IHPDEP Selected Publicationstakes precedence - If a selected publication does not appear in Google Scholar, it should still remain in the final output
- If
project_webpagereturns404, the downstream card feed should not keep that broken entry alive
Reading club data is automatically fetched and written back into the repository with:
python3 scripts/update_reading_club.pyThis script updates:
src/data/readingClub.jsonpublic/reading_club_covers/
The current source is a designated Bilibili series API.
Phase 1 of the local XHS studio uses the same Python pipeline, but the intended operator flow is now local rather than GitHub-hosted:
- Start the local studio API:
npm run studio:install
npm run studio:server- Start the site locally:
npm run dev- Open
http://127.0.0.1:5173/studio/news(or the port Vite prints) - Run a local preview sync, review the draft, then merge / publish
- Optionally sync the freshest local XHS cookie cache back to GitHub secrets from the studio UI
- This uses the local GitHub CLI session, so run
gh auth loginon your machine first - The studio keeps this separate from normal content
Commit and push, so secret rotation and content publishing stay independently recoverable
- This uses the local GitHub CLI session, so run
- Commit and push when the local result looks right
The pipeline entry point remains:
python3 run_xhs_pipeline_mediacrawler.pyInputs and dependencies:
MediaCrawler/secrets/xhs_cookies.txtor theXHS_COOKIESenvironment variable- Preferred:
secrets/xhs_creator_url.txtorXHS_CREATOR_URL- The local studio now uses creator mode as the primary sync strategy; a full creator profile URL is preferred because it can carry the freshest
xseccontext
- The local studio now uses creator mode as the primary sync strategy; a full creator profile URL is preferred because it can carry the freshest
- Fallback:
secrets/xhs_creator_id.txtorXHS_CREATOR_ID- A 24-character internal creator user ID also works when a full profile URL is not available
- Optional:
secrets/xhs_target_user_id.txtorXHS_TARGET_USER_ID- Used as the stable local filter and identity contract for the VPX account; supports one or more IDs separated by commas or new lines
- Optional:
secrets/xhs_target_nicknames.txtorXHS_TARGET_NICKNAMES- Fallback nickname filter when needed
- Optional:
secrets/xhs_search_keywords.txtorXHS_SEARCH_KEYWORDS- Search mode still exists for debugging, but it is no longer the primary studio flow
This script updates:
public/news.jsonpublic/xhs_news_images/public/xhs_news_videos/
Publishing from the local studio now merges by note id:
- New Xiaohongshu note IDs are added to
public/news.json - Existing items are kept unchanged, so manual edits in
public/news.jsonare not overwritten by a later sync
The local studio also writes preview and operator state under:
.local/studio/news/
During local preview sync, the studio also refreshes:
secrets/xhs_cookies.txt- exported from the local browser session after a successful Xiaohongshu login
- stores the full current cookie string, not just
web_session - can then be synced back into the repository's
XHS_COOKIESGitHub secret from the studio UI
The GitHub credential sync in the studio intentionally updates only the stable pieces needed for the backup workflow:
XHS_COOKIESXHS_CREATOR_IDXHS_CREATOR_URL- written with the stable creator user ID to override any stale URL-style secret without reintroducing volatile
xsec_tokenvalues
- written with the stable creator user ID to override any stale URL-style secret without reintroducing volatile
XHS_TARGET_USER_IDXHS_TARGET_NICKNAMES
The GitHub workflow remains available for low-frequency backup runs, but the primary Xiaohongshu operator flow is the local studio.
The workflows most directly related to site maintenance are:
.github/workflows/deploy.yml- Builds and deploys the site to GitHub Pages whenever
mainis updated
- Builds and deploys the site to GitHub Pages whenever
.github/workflows/update_publications.yml- Updates publication data monthly and can also be triggered manually
.github/workflows/update-reading-club.yml- Updates reading club data and covers daily
.github/workflows/update_xhs_news.yml- Low-frequency backup XHS path; the normal news publishing path is now local studio -> local verification -> git push -> deploy
The deployment workflow copies dist/index.html to dist/404.html after build time so SPA route refreshes work correctly on GitHub Pages.
The current deployment target is GitHub Pages:
- Make and verify local changes
- Run
npm run build - Commit and push to
main - GitHub Actions builds and deploys
dist/
Even for content-only changes, it is still recommended to run a local build before pushing so that data-loading issues are caught early.
For most changes, the recommended sequence is:
- Update pages, JSON data, or scripts
- Run
npm run lintif frontend code changed - Run
npm run build - Review the site locally with
npm run preview -- --host 127.0.0.1 --port 4173 - Push to
mainafter everything looks correct
dist/is a build artifact and is not intended for manual editing- Some files under
public/publications/are generated outputs, so publication-pipeline changes should always be checked against the generated results - Most site maintenance can still be done through normal git-based edits, but XHS news publishing is now designed around the local studio workflow rather than GitHub-hosted crawling