OpenSpace project-scaffold
Scaffold a Vite + TypeScript personal dashboard project with the correct directory structure, dependencies, and configuration files.
install
source · Clone the upstream repo
git clone https://github.com/HKUDS/OpenSpace
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/HKUDS/OpenSpace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/showcase/skills/project-scaffold" ~/.claude/skills/hkuds-openspace-project-scaffold && rm -rf "$T"
manifest:
showcase/skills/project-scaffold/SKILL.mdsource content
Project Scaffold
Set up a new Vite + TypeScript project for a personal daily monitoring dashboard.
Directory Structure
my-daily-monitor/ ├── index.html ├── package.json ├── tsconfig.json ├── vite.config.ts ├── api/ # Serverless API proxy endpoints (Vercel) │ └── stocks.ts ├── src/ │ ├── main.ts # Entry point │ ├── components/ # Panel components │ │ ├── Panel.ts # Base panel class │ │ ├── StockPanel.ts │ │ ├── NewsPanel.ts │ │ └── index.ts │ ├── services/ # Data fetching services │ │ ├── stock-market.ts │ │ ├── news.ts │ │ └── index.ts │ ├── utils/ # Utilities │ │ ├── circuit-breaker.ts │ │ ├── sparkline.ts │ │ ├── format.ts │ │ └── index.ts │ ├── config/ # Panel config, API endpoints │ │ └── panels.ts │ └── styles/ │ └── main.css └── skills/ # OpenSpace skill files
package.json
{ "name": "my-daily-monitor", "private": true, "version": "0.1.0", "type": "module", "scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview" }, "devDependencies": { "typescript": "^5.7.2", "vite": "^6.0.7" } }
tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "moduleResolution": "bundler", "strict": true, "jsx": "preserve", "resolveJsonModule": true, "isolatedModules": true, "esModuleInterop": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], "skipLibCheck": true, "noEmit": true, "paths": { "@/*": ["./src/*"] }, "baseUrl": "." }, "include": ["src"] }
vite.config.ts
import { defineConfig } from 'vite'; import { resolve } from 'path'; export default defineConfig({ resolve: { alias: { '@': resolve(__dirname, 'src'), }, }, server: { port: 5173, }, });
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>My Daily Monitor</title> <link rel="stylesheet" href="/src/styles/main.css" /> </head> <body> <div id="app"> <header class="app-header"> <h1>My Daily Monitor</h1> <span class="app-clock" id="headerClock"></span> </header> <main class="main-content"> <div class="panels-grid" id="panelsGrid"></div> </main> </div> <script type="module" src="/src/main.ts"></script> </body> </html>
src/main.ts (Entry Point)
import './styles/main.css'; // Import panels import { StockPanel } from './components/StockPanel'; import { NewsPanel } from './components/NewsPanel'; // ... other panels const grid = document.getElementById('panelsGrid')!; // Instantiate and mount panels const panels = [ new StockPanel(), new NewsPanel(), // ... more panels ]; for (const panel of panels) { grid.appendChild(panel.getElement()); } // Header clock function updateClock(): void { const el = document.getElementById('headerClock'); if (el) el.textContent = new Date().toLocaleTimeString(); } updateClock(); setInterval(updateClock, 1000);
Key Conventions
- No framework — vanilla TypeScript with DOM API
- Each panel is a class in
src/components/ - Each service is a module in
src/services/ - CSS uses custom properties for theming
- Vite handles bundling, HMR, and path aliases
- API proxy endpoints go in
for serverless deploymentapi/ - Panel config in
controls which panels are enabledsrc/config/panels.ts