🪻 distributed transcription service thistle.dunkirk.sh
1# Thistle - Project Guidelines 2 3This is a Bun-based transcription service using the [Bun fullstack pattern](https://bun.com/docs/bundler/fullstack) for routing and bundled HTML. 4 5## Project Info 6 7- Name: Thistle 8- Purpose: Transcription service 9- Runtime: Bun (NOT Node.js) 10- Language: TypeScript with strict mode 11- Frontend: Vanilla HTML/CSS/JS with lightweight helpers on top of web components 12 13## Design System 14 15ALWAYS use the project's CSS variables for colors: 16 17```css 18:root { 19 /* Color palette */ 20 --gunmetal: #2d3142ff; /* dark blue-gray */ 21 --paynes-gray: #4f5d75ff; /* medium blue-gray */ 22 --silver: #bfc0c0ff; /* light gray */ 23 --white: #ffffffff; /* white */ 24 --coral: #ef8354ff; /* warm orange */ 25 26 /* Semantic color assignments */ 27 --text: var(--gunmetal); 28 --background: var(--white); 29 --primary: var(--paynes-gray); 30 --secondary: var(--silver); 31 --accent: var(--coral); 32} 33``` 34 35**Color usage:** 36- NEVER hardcode colors like `#4f46e5`, `white`, `red`, etc. 37- Always use semantic variables (`var(--primary)`, `var(--background)`, `var(--accent)`, etc.) or named color variables (`var(--gunmetal)`, `var(--coral)`, etc.) 38 39**Dimensions:** 40- Use `rem` for all sizes, spacing, and widths (not `px`) 41- Base font size is 16px (1rem = 16px) 42- Common values: `0.5rem` (8px), `1rem` (16px), `2rem` (32px), `3rem` (48px) 43- Max widths: `48rem` (768px) for content, `56rem` (896px) for forms/data 44- Spacing scale: `0.25rem`, `0.5rem`, `0.75rem`, `1rem`, `1.5rem`, `2rem`, `3rem` 45 46## NO FRAMEWORKS 47 48NEVER use React, Vue, Svelte, or any heavy framework. 49 50This project prioritizes: 51- Speed: Minimal JavaScript, fast load times 52- Small bundle sizes: Keep bundles tiny 53- Native web platform: Use web standards (Web Components, native DOM APIs) 54- Simplicity: Vanilla HTML, CSS, and JavaScript 55 56Allowed lightweight helpers: 57- Lit (~8-10KB gzipped) for reactive web components 58- Native Web Components 59- Plain JavaScript/TypeScript 60 61Explicitly forbidden: 62- React, React DOM 63- Vue 64- Svelte 65- Angular 66- Any framework with a virtual DOM or large runtime 67 68## Commands 69 70```bash 71# Install dependencies 72bun install 73 74# Development server with hot reload 75bun dev 76 77# Run tests 78bun test 79 80# Build files 81bun build <file.html|file.ts|file.css> 82``` 83 84Development workflow: `bun dev` runs the server with hot module reloading. Changes to TypeScript, HTML, or CSS files automatically reload. 85 86**IMPORTANT**: NEVER run `bun dev` yourself - the user always has it running already. 87 88## Bun Usage 89 90Default to using Bun instead of Node.js. 91 92- Use `bun <file>` instead of `node <file>` or `ts-node <file>` 93- Use `bun test` instead of `jest` or `vitest` 94- Use `bun build <file>` instead of `webpack` or `esbuild` 95- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` 96- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` 97- Bun automatically loads .env, so don't use dotenv 98 99## Bun APIs 100 101Use Bun's built-in APIs instead of npm packages: 102 103- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`. 104- `bun:sqlite` for SQLite. Don't use `better-sqlite3`. 105- `Bun.redis` for Redis. Don't use `ioredis`. 106- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`. 107- `WebSocket` is built-in. Don't use `ws`. 108- Prefer `Bun.file` over `node:fs`'s readFile/writeFile 109- `Bun.$\`ls\`` instead of execa 110 111## Server Setup 112 113Use `Bun.serve()` with the routes pattern: 114 115```ts 116import index from "./index.html" 117 118Bun.serve({ 119 routes: { 120 "/": index, 121 "/api/users/:id": { 122 GET: (req) => { 123 return new Response(JSON.stringify({ id: req.params.id })); 124 }, 125 }, 126 }, 127 // optional websocket support 128 websocket: { 129 open: (ws) => { 130 ws.send("Hello, world!"); 131 }, 132 message: (ws, message) => { 133 ws.send(message); 134 }, 135 close: (ws) => { 136 // handle close 137 } 138 }, 139 development: { 140 hmr: true, 141 console: true, 142 } 143}) 144``` 145 146## Frontend Pattern 147 148Don't use Vite or any build tools. Use HTML imports with `Bun.serve()`. 149 150HTML files can directly import `.ts` or `.js` files: 151 152```html 153<!DOCTYPE html> 154<html lang="en"> 155 156<head> 157 <meta charset="UTF-8"> 158 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 159 <title>Page Title - Thistle</title> 160 <link rel="icon" 161 href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='0.9em' font-size='90'>🪻</text></svg>"> 162 <link rel="stylesheet" href="../styles/main.css"> 163</head> 164 165<body> 166 <auth-component></auth-component> 167 168 <main> 169 <h1>Page Title</h1> 170 <my-component></my-component> 171 </main> 172 173 <script type="module" src="../components/auth.ts"></script> 174 <script type="module" src="../components/my-component.ts"></script> 175</body> 176 177</html> 178``` 179 180**Standard HTML template:** 181- Always include the `<auth-component>` element for consistent login/logout UI 182- Always include the thistle emoji favicon 183- Always include proper meta tags (charset, viewport) 184- Structure: auth component, then main content, then scripts 185- Import `auth.ts` on every page for authentication UI 186 187Bun's bundler will transpile and bundle automatically. `<link>` tags pointing to stylesheets work with Bun's CSS bundler. 188 189Frontend TypeScript (vanilla or with Lit web components): 190 191```ts 192import { LitElement, html, css } from 'lit'; 193import { customElement, property } from 'lit/decorators.js'; 194 195// Define a Lit web component 196@customElement('my-component') 197export class MyComponent extends LitElement { 198 @property({ type: String }) name = 'World'; 199 200 // Scoped styles using css tagged template 201 static styles = css` 202 :host { 203 display: block; 204 padding: 1rem; 205 } 206 .greeting { 207 color: blue; 208 } 209 `; 210 211 // Render using html tagged template 212 render() { 213 return html` 214 <div class="greeting"> 215 Hello, ${this.name}! 216 </div> 217 `; 218 } 219} 220 221// Or use plain DOM manipulation for simple interactions 222document.querySelector('h1')?.addEventListener('click', () => { 223 console.log('Clicked!'); 224}); 225``` 226 227**When to use Lit:** 228- Components with reactive properties (auto-updates when data changes) 229- Complex components needing scoped styles 230- Form controls with internal state 231- Components with lifecycle needs 232 233**When to skip Lit:** 234- Static content (use plain HTML) 235- Simple one-off interactions (use vanilla JS) 236- Anything without reactive state 237 238Lit provides: 239- `@customElement` decorator to register components 240- `@property` decorator for reactive properties 241- `html` tagged template for declarative rendering 242- `css` tagged template for scoped styles 243- Automatic re-rendering when properties change 244- Size: ~8-10KB minified+gzipped 245 246## Testing 247 248Use `bun test` to run tests. 249 250### Basic Test Structure 251 252```ts 253import { test, expect } from "bun:test"; 254 255test("hello world", () => { 256 expect(1).toBe(1); 257}); 258``` 259 260### Test File Naming 261 262- Place tests next to the code they test: `foo.ts``foo.test.ts` 263- This keeps tests close to implementation for easy maintenance 264- Bun automatically discovers `*.test.ts` files 265 266### Writing Good Tests 267 268**Test security-critical code:** 269- File path operations (directory traversal, injection) 270- User input validation 271- Authentication/authorization 272- API endpoint security 273 274**Test edge cases:** 275- Empty strings, null, undefined 276- Very large inputs (size limits) 277- Invalid formats 278- Boundary conditions 279 280**Test async operations:** 281```ts 282test("async function", async () => { 283 const result = await someAsyncFunction(); 284 expect(result).toBe("expected value"); 285}); 286``` 287 288**Test error conditions:** 289```ts 290test("rejects invalid input", async () => { 291 await expect(dangerousFunction("../../../etc/passwd")).rejects.toThrow(); 292 await expect(dangerousFunction("invalid")).rejects.toThrow("Invalid format"); 293}); 294``` 295 296**Example: Security-focused tests** 297```ts 298test("prevents directory traversal", async () => { 299 const maliciousIds = [ 300 "../../../etc/passwd", 301 "../../secret.txt", 302 "test/../../../config", 303 ]; 304 305 for (const id of maliciousIds) { 306 await expect(loadFile(id)).rejects.toThrow(); 307 } 308}); 309 310test("validates input format", async () => { 311 const invalidInputs = [ 312 "test; rm -rf /", 313 "test`whoami`", 314 "test\x00null", 315 ]; 316 317 for (const input of invalidInputs) { 318 await expect(processInput(input)).rejects.toThrow("Invalid format"); 319 } 320}); 321``` 322 323### Running Tests 324 325```bash 326# Run all tests 327bun test 328 329# Run specific test file 330bun test src/lib/auth.test.ts 331 332# Watch mode (re-run on changes) 333bun test --watch 334``` 335 336### What to Test 337 338**Always test:** 339- Security-critical functions (file I/O, user input) 340- Complex business logic 341- Edge cases and error handling 342- Public API functions 343 344**Don't need to test:** 345- Simple getters/setters 346- Framework/library code 347- UI components (unless complex logic) 348- One-line utility functions 349 350## TypeScript Configuration 351 352Strict mode is enabled with these settings: 353 354```json 355{ 356 "strict": true, 357 "noFallthroughCasesInSwitch": true, 358 "noUncheckedIndexedAccess": true, 359 "noImplicitOverride": true 360} 361``` 362 363Deliberately disabled: 364- `noUnusedLocals`: false 365- `noUnusedParameters`: false 366- `noPropertyAccessFromIndexSignature`: false 367 368Module system: 369- `moduleResolution`: "bundler" 370- `module`: "Preserve" 371- JSX: `preserve` (NOT react-jsx - we don't use React) 372- Allows importing `.ts` extensions directly 373 374## Frontend Technologies 375 376Core (always use): 377- Vanilla HTML, CSS, JavaScript/TypeScript 378- Native Web Components API 379- Native DOM APIs (querySelector, addEventListener, etc.) 380 381Lightweight helpers: 382- Lit (~8-10KB gzipped): For reactive web components with state management 383 384Bundle size philosophy: 385- Start with vanilla JS 386- Add helpers only when they significantly reduce complexity 387- Measure bundle size impact before adding any library 388- Target: Keep total JS bundle under 50KB 389 390## Project Structure 391 392Based on Bun fullstack pattern: 393- `src/index.ts`: Server imports HTML files as modules 394- `src/pages/`: HTML files (route entry points) 395- `src/components/`: Lit web components 396- `src/styles/`: CSS files 397- `public/`: Static assets (images, fonts, etc.) 398 399**File flow:** 4001. Server imports HTML: `import indexHTML from "./pages/index.html"` 4012. HTML imports components: `<script type="module" src="../components/counter.ts"></script>` 4023. HTML links styles: `<link rel="stylesheet" href="../styles/main.css">` 4034. Components self-register as custom elements 4045. Bun bundles everything automatically 405 406## File Organization 407 408- `src/index.ts`: Main server entry point with `Bun.serve()` routes 409- `src/pages/*.html`: Route entry points (imported as modules) 410- `src/components/*.ts`: Lit web components 411- `src/styles/*.css`: Stylesheets (linked from HTML) 412- `public/`: Static assets directory 413- Tests: `*.test.ts` files 414 415**Current structure example:** 416``` 417src/ 418 index.ts # Imports HTML, defines routes 419 pages/ 420 index.html # Imports components via <script type="module"> 421 components/ 422 counter.ts # Lit component with @customElement 423 styles/ 424 main.css # Linked from HTML with <link> 425``` 426 427## Naming Conventions 428 429Follow TypeScript conventions: 430- PascalCase for components and classes 431- camelCase for functions and variables 432- kebab-case for file names 433 434## Development Workflow 435 4361. Make changes to `.ts`, `.html`, or `.css` files 4372. Bun's HMR automatically reloads changes 4383. Write tests in `*.test.ts` files 4394. Run `bun test` to verify 440 441## IDE Setup 442 443Biome LSP is configured in `crush.json` for linting and formatting support. 444 445## Common Tasks 446 447### Adding a new route 448Add to the `routes` object in `Bun.serve()` configuration 449 450### Adding a new page 451Create an HTML file, import it in the server, add to routes 452 453### Adding frontend functionality 454Import TS/JS files directly from HTML using `<script type="module" src="../components/my-component.ts"></script>`. Use Lit for reactive components or vanilla JS for simple interactions. Never React. 455 456### Adding WebSocket support 457Add `websocket` configuration to `Bun.serve()` 458 459## Important Notes 460 4611. No npm scripts needed: Bun is fast enough to run commands directly 4622. Private package: `package.json` has `"private": true` 4633. No build step for development: Hot reload handles everything 4644. Module type: Package uses `"type": "module"` (ESM) 4655. Bun types: Available via `@types/bun` (check `node_modules/bun-types/docs/**.md` for API docs) 466 467## Gotchas 468 4691. Don't use Node.js commands: Use `bun` instead of `node`, `npm`, `npx`, etc. 4702. Don't install Express/Vite/other tools: Bun has built-in equivalents 4713. NEVER EVER use React: This project is vanilla JS/TS with web components only. React is explicitly forbidden. 4724. Import .ts extensions: Bun allows importing `.ts` files directly 4735. No dotenv needed: Bun loads `.env` automatically 4746. HTML imports are special: They trigger Bun's bundler, don't treat them as static files 4757. Bundle size matters: Always consider the size impact before adding any library 476 477## Documentation Lookup 478 479Use Context7 MCP for looking up official documentation for libraries and frameworks. 480 481## Resources 482 483- [Bun Fullstack Documentation](https://bun.com/docs/bundler/fullstack) 484- [Lit Documentation](https://lit.dev/) 485- [Web Components MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components) 486- Bun API docs in `node_modules/bun-types/docs/**.md` 487 488## Future Additions 489 490As the codebase grows, document: 491- Database schema and migrations 492- API endpoint patterns 493- Authentication/authorization approach 494- Transcription service integration details 495- Deployment process 496- Environment variables needed