Files
Zane Chen 18aa162942 Feat/web UI (#2)
* feat: add learning website with Next.js

Add interactive web interface for the Build Your Own OpenClaw tutorial:
- Next.js app with TypeScript and Tailwind CSS
- Theme provider with dark/light mode toggle
- Step card components with diff visualization
- Utilities for loading step metadata and file diffs

* chore: ignore worktrees directory

* feat(web): implement learning website with step navigation and diff viewer

- Landing page with hero section and steps overview grouped by phase
- Step detail pages with README rendering and syntax highlighting (Shiki)
- Diff comparison pages with side-by-side GitHub-style viewer
- Custom 404 Not Found page
- Components: ReadmeRenderer, CodeBlock, DiffViewer, DiffSelector
- Generates 175 static pages (18 steps + 153 diff combinations)

* feat(web): enhance UI with zane-portfolio theme and improved UX

- Update color scheme to match zane-portfolio style (warm beige light, pure black dark)
- Add scrollbar styling for light/dark modes
- Fix card border visibility in dark mode
- Add GitHub CTA section with clone command and copy button
- Add star on GitHub call-to-action
- Use favicons from zane-portfolio
- Improve diff viewer with scroll sync and collapsible files
- Add sticky header with file navigation dropdown
- Rewrite README links to GitHub and internal step pages
- Remove default create-next-app template files

* ci: add GitHub Pages deployment workflow

- Add workflow to build and deploy Next.js static site
- Configure custom domain (build-your-own-openclaw.kiyo-n-zane.com)
- Trigger on push to main branch
2026-03-14 17:47:12 -04:00

51 lines
1.5 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(__dirname, '..', '..')
const publicDir = path.resolve(__dirname, '..', 'public', 'steps')
// Ensure public/steps directory exists
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true })
}
// Get all step directories
const stepDirs = fs.readdirSync(rootDir).filter(dir => {
const fullPath = path.join(rootDir, dir)
return fs.statSync(fullPath).isDirectory() && /^\d{2}-/.test(dir)
})
console.log(`Found ${stepDirs.length} step directories`)
// Copy SVG and other asset files from each step directory
let copiedCount = 0
for (const stepDir of stepDirs) {
const stepPath = path.join(rootDir, stepDir)
const files = fs.readdirSync(stepPath)
// Create destination directory for this step
const destStepDir = path.join(publicDir, stepDir)
if (!fs.existsSync(destStepDir)) {
fs.mkdirSync(destStepDir, { recursive: true })
}
// Copy image files (svg, png, jpg, gif)
const imageFiles = files.filter(file =>
/\.(svg|png|jpe?g|gif|webp)$/i.test(file)
)
for (const file of imageFiles) {
const srcPath = path.join(stepPath, file)
const destPath = path.join(destStepDir, file)
fs.copyFileSync(srcPath, destPath)
copiedCount++
console.log(`Copied: ${stepDir}/${file}`)
}
}
console.log(`\nDone! Copied ${copiedCount} files to public/steps/`)