Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm install
- run: npm run build
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# CLAUDE.md

Guidance for AI coding agents working in this repo. For human onboarding see
[`CONTRIBUTING.md`](CONTRIBUTING.md), which this file does not repeat.

## What this repo is

The **public website** for the Thoughtful AI Tools Lab (https://thoughtful-ai.com),
built with Astro 5 + React + Tailwind, deployed on Cloudflare. The lab's actual
research application is a *different* repo (`AIToolsLab/writing-tools`) — do not
look for app/model code here.

## Build & verify

- `npm install`, then `npm run build` must pass. CI (`.github/workflows/ci.yml`)
runs the build on every PR. Always run `npm run build` before claiming a change
works — there is no test suite, so a clean build is the bar.

## Agent-specific notes

- **Content is data-driven; prefer editing data over markup.** People are
per-person JSON files in `src/data/people/`; publications are an array in
`src/pages/publications.astro`. When adding similar content, follow the
existing data pattern instead of hand-writing new markup blocks.
- The page shell, SEO tags, and canonical URL live in `src/layouts/Layout.astro`.
The canonical URL is derived from the request path — don't hardcode it.
- The header is a React island (it has a mobile-nav toggle) hydrated with
`client:load` so it appears in static HTML — don't switch it back to
`client:only`. The footer is a static `.astro` component (no interactivity, no
hydration); keep static UI static rather than making it a React island.
- Match surrounding Tailwind utility classes; avoid introducing bespoke CSS.

## Why this file is separate from CONTRIBUTING.md / README

`README` orients a visitor ("what is this"), `CONTRIBUTING.md` onboards a human
contributor ("how to set up and where things live"), and `CLAUDE.md` is read
automatically by coding agents as standing instructions — so it focuses on the
constraints and verification steps an agent needs to not break things, and stays
short to keep the agent's context lean.
47 changes: 47 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Contributing

This repository is the **public website** for the Thoughtful AI Tools Lab
(https://thoughtful-ai.com). The research application itself lives in a separate
repo: [`AIToolsLab/writing-tools`](https://github.com/AIToolsLab/writing-tools).
Changes here are about how the lab presents its people, projects, vision, and
publications — not the writing tool's behavior.

## Getting set up

- Requires Node.js 18+ and npm.
- `npm install` — install dependencies.
- `npm run dev` — local dev server at http://localhost:4321.
- `npm run build` — production build into `dist/`. **CI runs this on every PR; if
it fails, the PR can't merge, so run it locally before pushing.**

## Where things live

| You want to change... | Edit... |
|-----------------------|---------|
| A team member (add yourself, mark graduated) | a JSON file in `src/data/people/` — see that folder's `README.md` |
| Publications | the `publications` array in `src/pages/publications.astro` |
| Projects | `src/pages/projects.astro` and the per-project pages in `src/pages/projects/` |
| The lab's vision statement | `src/pages/vision.md` |
| Site-wide header/footer/nav | `src/components/lab/` |
| Page shell, `<head>`, SEO tags | `src/layouts/Layout.astro` |

## Adding yourself to the team

See [`src/data/people/README.md`](src/data/people/README.md). Short version: add
your photo to `public/people/`, copy `_example.json` to `your_name.json`, fill it
in, and open a PR. One file per person means no merge conflicts when several
people add themselves at once.

## Conventions

- Styling is [Tailwind CSS](https://tailwindcss.com/) utility classes. Match the
classes already used on nearby elements rather than introducing new CSS.
- Pages are [Astro](https://docs.astro.build/) (`.astro`); interactive UI is React
(`.tsx`) under `src/components/`.
- Keep content data-driven where a pattern exists (e.g. the people JSON files) so
future contributors edit data, not markup.

## Deployment

The site is deployed on **Cloudflare** (see `wrangler.jsonc`), serving the built
`dist/` directory. You don't need to deploy manually — merging to `main` is enough.
7 changes: 3 additions & 4 deletions README-astro.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ Astro-based version of the Thoughtful AI Tools Lab website.

## Components

- `LabHeader`: Navigation header
- `LabHero`: Homepage hero
- `LabFooter`: Footer
- `Header`: Navigation header (React island)
- `Footer`: Footer (static Astro component)


## Content Management
Expand All @@ -67,5 +66,5 @@ Astro-based version of the Thoughtful AI Tools Lab website.

## Deployment

Deploy to GitHub Pages or any static host. Output: `dist/`.
Deployed on Cloudflare (see `wrangler.jsonc`), serving the built `dist/` directory.

5 changes: 2 additions & 3 deletions astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ import react from "@astrojs/react";

// https://astro.build/config
import tailwind from "@astrojs/tailwind";

const isProd = process.env.NODE_ENV === "production";
import sitemap from "@astrojs/sitemap";

// https://astro.build/config
export default defineConfig({
integrations: [react(), tailwind()],
integrations: [react(), tailwind(), sitemap()],
site: "https://thoughtful-ai.com",
base: "/",
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"license": "MIT",
"dependencies": {
"@astrojs/react": "^4.2.2",
"@astrojs/sitemap": "^3.7.3",
"@astrojs/tailwind": "^6.0.2",
"@heroicons/react": "^2.0.16",
"@tailwindcss/typography": "^0.5.16",
Expand Down
4 changes: 4 additions & 0 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
User-agent: *
Allow: /

Sitemap: https://thoughtful-ai.com/sitemap-index.xml
25 changes: 25 additions & 0 deletions src/components/lab/Footer.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
const year = new Date().getFullYear();
---
<footer class="py-2 mx-auto max-w-screen-xl px-6">
<div class="flex w-full flex-row flex-wrap items-center justify-center gap-6 px-2 md:justify-between">
<span class="text-sm font-normal text-inherit">&copy; {year} Thoughtful AI Tools Lab, Calvin University</span>
<ul class="flex items-center gap-4">
<li>
<a href="/about" class="text-sm font-normal text-inherit transition-colors hover:text-blue-500 focus:text-blue-500">About</a>
</li>
<li>
<a href="/publications" class="text-sm font-normal text-inherit transition-colors hover:text-blue-500 focus:text-blue-500">Publications</a>
</li>
<li>
<a href="/projects" class="text-sm font-normal text-inherit transition-colors hover:text-blue-500 focus:text-blue-500">Projects</a>
</li>
<li>
<a href="/vision" class="text-sm font-normal text-inherit transition-colors hover:text-blue-500 focus:text-blue-500">Vision</a>
</li>
</ul>
</div>
<div class="w-full text-center mt-2 text-xs text-gray-600">
This material is based upon work supported by the <a href="https://www.nsf.gov/awardsearch/showAward?AWD_ID=2246145" class="underline text-blue-600 hover:text-blue-800" target="_blank" rel="noopener noreferrer">National Science Foundation under Grant No. 2246145</a>.
</div>
</footer>
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function NavList() {
}


export default function LabHeader() {
export default function Header() {
const [openNav, setOpenNav] = React.useState(false);

React.useEffect(() => {
Expand Down
38 changes: 0 additions & 38 deletions src/components/lab/LabFooter.tsx

This file was deleted.

34 changes: 34 additions & 0 deletions src/data/people/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Team members

Each person on the team is one JSON file in this folder. The About page
(`src/pages/about.astro`) reads every `*.json` file here automatically and sorts
people into **Current Team** and **Alumni**, so you never edit the page itself —
you just add or change a data file.

## Add yourself (current students)

1. Add your photo to `public/people/` (e.g. `public/people/your_name.png`).
A square image around 400×400px works well. If you don't add a photo, the
site falls back to an avatar generated from your initials.
2. Copy `_example.json` to a new file named after you, e.g. `your_name.json`.
(Files starting with `_` are ignored, so `_example.json` never appears on the site.)
3. Fill in the fields (see below), then open a pull request.

Because everyone gets their own file, two students adding themselves at the same
time won't create a merge conflict.

## Fields

| Field | Required | Notes |
|----------|----------|-------|
| `name` | yes | Your full name. |
| `status` | yes | `"current"` while you're on the team, `"alumni"` after you leave. |
| `photo` | no | Path under `public/`, e.g. `/people/your_name.png`. Omit to use a generated avatar. |
| `role` | no | Defaults to "Undergraduate Researcher". |
| `order` | no | Lower numbers sort first in the Current Team list. Alumni are sorted alphabetically. |
| `link` | no | A personal site or GitHub profile; makes your name a link. |

## When you graduate

Change your own `status` from `"current"` to `"alumni"`. That's the only edit
needed — the About page moves you to the Alumni section automatically.
8 changes: 8 additions & 0 deletions src/data/people/_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "Your Name",
"photo": "/people/your_name.png",
"role": "Undergraduate Researcher",
"status": "current",
"order": 2,
"link": "https://your-website.example"
}
6 changes: 6 additions & 0 deletions src/data/people/alina_sainju.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Alina Sainju",
"photo": "/people/alina_sainju.jpg",
"role": "Undergraduate Researcher",
"status": "alumni"
}
6 changes: 6 additions & 0 deletions src/data/people/daniel_kim.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Daniel Kim",
"photo": "/people/daniel_kim.png",
"role": "Undergraduate Researcher",
"status": "alumni"
}
6 changes: 6 additions & 0 deletions src/data/people/daniel_kwon.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Daniel Kwon",
"photo": "/people/daniel_kwon.png",
"role": "Undergraduate Researcher",
"status": "alumni"
}
6 changes: 6 additions & 0 deletions src/data/people/hannah_yoo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Hannah Yoo",
"photo": "/people/hannah_yoo.png",
"role": "Undergraduate Researcher",
"status": "alumni"
}
6 changes: 6 additions & 0 deletions src/data/people/jason_chew.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Jason Chew",
"photo": "/people/jason_chew.png",
"role": "Undergraduate Researcher",
"status": "alumni"
}
7 changes: 7 additions & 0 deletions src/data/people/jiho_kim.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "Jiho Kim",
"photo": "/people/jiho_kim.png",
"role": "Research Collaborator",
"status": "current",
"order": 1
}
5 changes: 5 additions & 0 deletions src/data/people/kyle_houston.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Kyle Houston",
"role": "Undergraduate Researcher",
"status": "alumni"
}
5 changes: 5 additions & 0 deletions src/data/people/monica_zhang.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Monica Zhang",
"role": "Undergraduate Researcher",
"status": "alumni"
}
6 changes: 6 additions & 0 deletions src/data/people/ray_flanagan.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "Ray Flanagan",
"photo": "/people/ray_flanagan.png",
"role": "Undergraduate Researcher",
"status": "alumni"
}
15 changes: 8 additions & 7 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import LabHeader from "../components/lab/LabHeader";
import LabFooter from "../components/lab/LabFooter";
import Header from "../components/lab/Header";
import Footer from "../components/lab/Footer.astro";
export interface Props {
title?: string;
metaDescription?: string;
Expand All @@ -18,34 +18,35 @@ const frontmatter = Astro.props.frontmatter || {};
const title = Astro.props.title || frontmatter.title;
const metaDescription = Astro.props.metaDescription || frontmatter.description || frontmatter.metaDescription;
const prose = typeof Astro.props.prose !== 'undefined' ? Astro.props.prose : frontmatter.prose;

const canonicalURL = new URL(Astro.url.pathname, Astro.site).href;
---

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Noto+Sans:300,400,500,600,700,800|PT+Mono:300,400,500,600,700"
rel="stylesheet"
/>
<title>{title} - Thoughtful AI Tools Lab</title>
<meta name="description" content={metaDescription || "AI research group at Calvin University focused on how AI can help people think better"} />
<meta name="generator" content={Astro.generator} />
<link rel="canonical" href="https://thoughtful-ai.com/" />
<!-- Remove Google Tag Manager for lab site -->
<link rel="canonical" href={canonicalURL} />
</head>
<body class="overflow-x-hidden">
<LabHeader client:only="react" />
<Header client:load />
{prose ? (
<main class="prose prose-lg mx-auto px-4 py-12">
<slot />
</main>
) : (
<slot />
)}
<LabFooter client:only="react" />
<Footer />
</body>
<script
src="https://kit.fontawesome.com/349ee9c857.js"
Expand Down
2 changes: 1 addition & 1 deletion src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Layout from '../layouts/Layout.astro';

---

<Layout title="404 Error Page">
<Layout title="404 Error Page" metaDescription="Page not found — Thoughtful AI Tools Lab at Calvin University.">
<main>
<section class="flex flex-col items-center justify-center min-h-screen text-center">
<h1 class="text-6xl font-bold mb-4">404</h1>
Expand Down
Loading
Loading