···11+# Thistle - Project Guidelines
22+33+This is a Bun-based transcription service using the [Bun fullstack pattern](https://bun.com/docs/bundler/fullstack) for routing and bundled HTML.
44+55+## Project Info
66+77+- Name: Thistle
88+- Purpose: Transcription service
99+- Runtime: Bun (NOT Node.js)
1010+- Language: TypeScript with strict mode
1111+- Frontend: Vanilla HTML/CSS/JS with lightweight helpers on top of web components
1212+1313+## NO FRAMEWORKS
1414+1515+NEVER use React, Vue, Svelte, or any heavy framework.
1616+1717+This project prioritizes:
1818+- Speed: Minimal JavaScript, fast load times
1919+- Small bundle sizes: Keep bundles tiny
2020+- Native web platform: Use web standards (Web Components, native DOM APIs)
2121+- Simplicity: Vanilla HTML, CSS, and JavaScript
2222+2323+Allowed lightweight helpers:
2424+- Lit (~8-10KB gzipped) for reactive web components
2525+- Native Web Components
2626+- Plain JavaScript/TypeScript
2727+2828+Explicitly forbidden:
2929+- React, React DOM
3030+- Vue
3131+- Svelte
3232+- Angular
3333+- Any framework with a virtual DOM or large runtime
3434+3535+## Commands
3636+3737+```bash
3838+# Install dependencies
3939+bun install
4040+4141+# Development server with hot reload
4242+bun dev
4343+4444+# Run tests
4545+bun test
4646+4747+# Build files
4848+bun build <file.html|file.ts|file.css>
4949+```
5050+5151+Development workflow: `bun dev` runs the server with hot module reloading. Changes to TypeScript, HTML, or CSS files automatically reload.
5252+5353+**IMPORTANT**: NEVER run `bun dev` yourself - the user always has it running already.
5454+5555+## Bun Usage
5656+5757+Default to using Bun instead of Node.js.
5858+5959+- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
6060+- Use `bun test` instead of `jest` or `vitest`
6161+- Use `bun build <file>` instead of `webpack` or `esbuild`
6262+- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
6363+- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>`
6464+- Bun automatically loads .env, so don't use dotenv
6565+6666+## Bun APIs
6767+6868+Use Bun's built-in APIs instead of npm packages:
6969+7070+- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
7171+- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
7272+- `Bun.redis` for Redis. Don't use `ioredis`.
7373+- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
7474+- `WebSocket` is built-in. Don't use `ws`.
7575+- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
7676+- `Bun.$\`ls\`` instead of execa
7777+7878+## Server Setup
7979+8080+Use `Bun.serve()` with the routes pattern:
8181+8282+```ts
8383+import index from "./index.html"
8484+8585+Bun.serve({
8686+ routes: {
8787+ "/": index,
8888+ "/api/users/:id": {
8989+ GET: (req) => {
9090+ return new Response(JSON.stringify({ id: req.params.id }));
9191+ },
9292+ },
9393+ },
9494+ // optional websocket support
9595+ websocket: {
9696+ open: (ws) => {
9797+ ws.send("Hello, world!");
9898+ },
9999+ message: (ws, message) => {
100100+ ws.send(message);
101101+ },
102102+ close: (ws) => {
103103+ // handle close
104104+ }
105105+ },
106106+ development: {
107107+ hmr: true,
108108+ console: true,
109109+ }
110110+})
111111+```
112112+113113+## Frontend Pattern
114114+115115+Don't use Vite or any build tools. Use HTML imports with `Bun.serve()`.
116116+117117+HTML files can directly import `.ts` or `.js` files:
118118+119119+```html
120120+<!DOCTYPE html>
121121+<html>
122122+ <head>
123123+ <link rel="stylesheet" href="./styles.css">
124124+ </head>
125125+ <body>
126126+ <h1>Hello, world!</h1>
127127+ <my-component></my-component>
128128+ <script type="module" src="./frontend.ts"></script>
129129+ </body>
130130+</html>
131131+```
132132+133133+Bun's bundler will transpile and bundle automatically. `<link>` tags pointing to stylesheets work with Bun's CSS bundler.
134134+135135+Frontend TypeScript (vanilla or with Lit web components):
136136+137137+```ts
138138+import { LitElement, html, css } from 'lit';
139139+import { customElement, property } from 'lit/decorators.js';
140140+141141+// Define a Lit web component
142142+@customElement('my-component')
143143+export class MyComponent extends LitElement {
144144+ @property({ type: String }) name = 'World';
145145+146146+ // Scoped styles using css tagged template
147147+ static styles = css`
148148+ :host {
149149+ display: block;
150150+ padding: 1rem;
151151+ }
152152+ .greeting {
153153+ color: blue;
154154+ }
155155+ `;
156156+157157+ // Render using html tagged template
158158+ render() {
159159+ return html`
160160+ <div class="greeting">
161161+ Hello, ${this.name}!
162162+ </div>
163163+ `;
164164+ }
165165+}
166166+167167+// Or use plain DOM manipulation for simple interactions
168168+document.querySelector('h1')?.addEventListener('click', () => {
169169+ console.log('Clicked!');
170170+});
171171+```
172172+173173+**When to use Lit:**
174174+- Components with reactive properties (auto-updates when data changes)
175175+- Complex components needing scoped styles
176176+- Form controls with internal state
177177+- Components with lifecycle needs
178178+179179+**When to skip Lit:**
180180+- Static content (use plain HTML)
181181+- Simple one-off interactions (use vanilla JS)
182182+- Anything without reactive state
183183+184184+Lit provides:
185185+- `@customElement` decorator to register components
186186+- `@property` decorator for reactive properties
187187+- `html` tagged template for declarative rendering
188188+- `css` tagged template for scoped styles
189189+- Automatic re-rendering when properties change
190190+- Size: ~8-10KB minified+gzipped
191191+192192+## Testing
193193+194194+Use `bun test` to run tests.
195195+196196+```ts
197197+import { test, expect } from "bun:test";
198198+199199+test("hello world", () => {
200200+ expect(1).toBe(1);
201201+});
202202+```
203203+204204+## TypeScript Configuration
205205+206206+Strict mode is enabled with these settings:
207207+208208+```json
209209+{
210210+ "strict": true,
211211+ "noFallthroughCasesInSwitch": true,
212212+ "noUncheckedIndexedAccess": true,
213213+ "noImplicitOverride": true
214214+}
215215+```
216216+217217+Deliberately disabled:
218218+- `noUnusedLocals`: false
219219+- `noUnusedParameters`: false
220220+- `noPropertyAccessFromIndexSignature`: false
221221+222222+Module system:
223223+- `moduleResolution`: "bundler"
224224+- `module`: "Preserve"
225225+- JSX: `preserve` (NOT react-jsx - we don't use React)
226226+- Allows importing `.ts` extensions directly
227227+228228+## Frontend Technologies
229229+230230+Core (always use):
231231+- Vanilla HTML, CSS, JavaScript/TypeScript
232232+- Native Web Components API
233233+- Native DOM APIs (querySelector, addEventListener, etc.)
234234+235235+Lightweight helpers:
236236+- Lit (~8-10KB gzipped): For reactive web components with state management
237237+238238+Bundle size philosophy:
239239+- Start with vanilla JS
240240+- Add helpers only when they significantly reduce complexity
241241+- Measure bundle size impact before adding any library
242242+- Target: Keep total JS bundle under 50KB
243243+244244+## Project Structure
245245+246246+Based on Bun fullstack pattern:
247247+- `src/index.ts`: Server imports HTML files as modules
248248+- `src/pages/`: HTML files (route entry points)
249249+- `src/components/`: Lit web components
250250+- `src/styles/`: CSS files
251251+- `public/`: Static assets (images, fonts, etc.)
252252+253253+**File flow:**
254254+1. Server imports HTML: `import indexHTML from "./pages/index.html"`
255255+2. HTML imports components: `<script type="module" src="../components/counter.ts"></script>`
256256+3. HTML links styles: `<link rel="stylesheet" href="../styles/main.css">`
257257+4. Components self-register as custom elements
258258+5. Bun bundles everything automatically
259259+260260+## File Organization
261261+262262+- `src/index.ts`: Main server entry point with `Bun.serve()` routes
263263+- `src/pages/*.html`: Route entry points (imported as modules)
264264+- `src/components/*.ts`: Lit web components
265265+- `src/styles/*.css`: Stylesheets (linked from HTML)
266266+- `public/`: Static assets directory
267267+- Tests: `*.test.ts` files
268268+269269+**Current structure example:**
270270+```
271271+src/
272272+ index.ts # Imports HTML, defines routes
273273+ pages/
274274+ index.html # Imports components via <script type="module">
275275+ components/
276276+ counter.ts # Lit component with @customElement
277277+ styles/
278278+ main.css # Linked from HTML with <link>
279279+```
280280+281281+## Naming Conventions
282282+283283+Follow TypeScript conventions:
284284+- PascalCase for components and classes
285285+- camelCase for functions and variables
286286+- kebab-case for file names
287287+288288+## Development Workflow
289289+290290+1. Make changes to `.ts`, `.html`, or `.css` files
291291+2. Bun's HMR automatically reloads changes
292292+3. Write tests in `*.test.ts` files
293293+4. Run `bun test` to verify
294294+295295+## IDE Setup
296296+297297+Biome LSP is configured in `crush.json` for linting and formatting support.
298298+299299+## Common Tasks
300300+301301+### Adding a new route
302302+Add to the `routes` object in `Bun.serve()` configuration
303303+304304+### Adding a new page
305305+Create an HTML file, import it in the server, add to routes
306306+307307+### Adding frontend functionality
308308+Import 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.
309309+310310+### Adding WebSocket support
311311+Add `websocket` configuration to `Bun.serve()`
312312+313313+## Important Notes
314314+315315+1. No npm scripts needed: Bun is fast enough to run commands directly
316316+2. Private package: `package.json` has `"private": true`
317317+3. No build step for development: Hot reload handles everything
318318+4. Module type: Package uses `"type": "module"` (ESM)
319319+5. Bun types: Available via `@types/bun` (check `node_modules/bun-types/docs/**.md` for API docs)
320320+321321+## Gotchas
322322+323323+1. Don't use Node.js commands: Use `bun` instead of `node`, `npm`, `npx`, etc.
324324+2. Don't install Express/Vite/other tools: Bun has built-in equivalents
325325+3. NEVER EVER use React: This project is vanilla JS/TS with web components only. React is explicitly forbidden.
326326+4. Import .ts extensions: Bun allows importing `.ts` files directly
327327+5. No dotenv needed: Bun loads `.env` automatically
328328+6. HTML imports are special: They trigger Bun's bundler, don't treat them as static files
329329+7. Bundle size matters: Always consider the size impact before adding any library
330330+331331+## Documentation Lookup
332332+333333+Use Context7 MCP for looking up official documentation for libraries and frameworks.
334334+335335+## Resources
336336+337337+- [Bun Fullstack Documentation](https://bun.com/docs/bundler/fullstack)
338338+- [Lit Documentation](https://lit.dev/)
339339+- [Web Components MDN](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
340340+- Bun API docs in `node_modules/bun-types/docs/**.md`
341341+342342+## Future Additions
343343+344344+As the codebase grows, document:
345345+- Database schema and migrations
346346+- API endpoint patterns
347347+- Authentication/authorization approach
348348+- Transcription service integration details
349349+- Deployment process
350350+- Environment variables needed