An interactive web-based tutorial environment for introduction to digital humanities. Features personalized learning pathways, in-browser Python code execution, note-taking, and Obsidian export.
All data stays on your device. No accounts, no tracking, no servers.
- Node.js 18+
- npm (included with Node.js)
# Install dependencies
npm install
# Start the dev server
npm run devThe app will be available at http://localhost:5173.
# Run all tests once
npm test
# Run tests in watch mode
npm run test:watchnpm run buildOutput goes to the dist/ directory.
The build output is a static site (HTML + JS + CSS). No backend server is needed.
Important — SPA routing: This app uses client-side routing (React Router with the HTML5 History API). When a user refreshes the page, navigates directly to a URL like
/lesson/python-basics-01, or uses the browser back/forward buttons after a hard navigation, the browser makes a real HTTP request to the server. The server must respond withindex.htmlfor every path instead of returning a 404. Each section below explains how to configure this.
- Connect your repository.
- Set the build command to
npm run build. - Set the publish directory to
dist. - Deploy.
The public/_redirects file in this repository (/* /index.html 200) is automatically picked up by Netlify, Cloudflare Pages, and Render — no extra configuration needed.
- Connect your repository.
- Set the build command to
npm run build. - Set the output directory to
dist. - Add a
vercel.jsonat the project root:
{
"rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
}GitHub Pages does not natively support SPA fallback routing. The recommended approach is to copy index.html as 404.html in your build output so that GitHub Pages serves it for unknown paths:
npm run build
cp dist/index.html dist/404.htmlThen push the dist/ folder to a gh-pages branch, or use a GitHub Action to automate this step.
Note: if the app is served from a sub-path (e.g. https://user.github.io/repo/), set the base option in vite.config.ts to match:
export default defineConfig({
base: '/repo/',
// ...
})Serve the dist/ directory with nginx, configured to fall back to index.html for all routes:
server {
listen 80;
root /path/to/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}Add an .htaccess file inside dist/:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>Use Vite's built-in preview server, which handles SPA routing automatically:
npm run previewNote:
python3 -m http.serverdoes not support SPA fallback routing and will return 404 on refresh or direct URL access. Usenpm run previewinstead.
- React 19, TypeScript, Vite
- Tailwind CSS v4
- Zustand (state management, persisted to localStorage)
- React Router v7
- react-markdown + remark-gfm
- JSZip (Obsidian export)
- Vitest + React Testing Library