- Fix: index.html <link> hrefs must be relative for Bun's HTML loader (absolute URLs failed the build and dev serve); ensureIcons() now runs before bundling in both scripts/build.ts and src/index.ts - tests/offline.md: manual airplane-mode verification procedure - WorkBreakdown: M3 checked ([~] on the manual browser run), M4 all done with notes; M5 retargeted to faster-whisper sidecar - Architecture/CLAUDE.md/Decisions: server engine = faster-whisper (CTranslate2 Python sidecar, D-013); browser stays Transformers.js (no WASM build of CTranslate2 exists) - bun test: 83 pass, tsc clean, production curl smoke-test green Co-Authored-By: Claude Fable 5 <[email protected]>
3.8 KiB
CLAUDE.md — Read this first, every session
This is the single source of truth for any agent working in this repo. If anything you're told conflicts with this file, this file wins.
1. What we are building
A browser-first speech-to-text web app powered by OpenAI Whisper, running through
Transformers.js (@huggingface/transformers).
Anonymous users (no login) can:
- Run Whisper models fully in the browser (no data leaves the device).
- Choose which Whisper model to use (tiny → large, English-only and multilingual, quantized variants).
- Keep working when the network — or our own server — is offline (installable PWA + cached model weights).
- Accelerate inference with WebGPU, falling back to WASM automatically.
- Transcribe from an uploaded audio/video file.
Logged-in users (Passport.js) get all of the above, plus:
- A toggle to choose where inference runs: their own device (browser engine) or the server the app is deployed on (server engine).
- The server engine MUST use faster-whisper (CTranslate2) for inference — not Transformers.js (see Decisions.md D-013). faster-whisper cannot run in a browser (no WASM build of CTranslate2), so the browser engine stays on Transformers.js.
2 How to run things
Default to using Bun instead of Node.js.
- Use
bun <file>instead ofnode <file>orts-node <file> - Use
bun testinstead ofjestorvitest - Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuild - Use
bun installinstead ofnpm installoryarn installorpnpm install - Use
bun run <script>instead ofnpm run <script>oryarn run <script>orpnpm run <script> - Use
bunx <package> <command>instead ofnpx <package> <command> - Bun automatically loads .env, so don't use dotenv.
APIs
Bun.serve()supports WebSockets, HTTPS, and routes. Don't useexpress.bun:sqlitefor SQLite. Don't usebetter-sqlite3.Bun.redisfor Redis. Don't useioredis.Bun.sqlfor Postgres. Don't usepgorpostgres.js.WebSocketis built-in. Don't usews.- Prefer
Bun.fileovernode:fs's readFile/writeFile - Bun.$
lsinstead of execa.
Testing
Use bun test to run tests.
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
Frontend
Use HTML imports with Bun.serve(). Don't use vite. HTML imports fully support React, CSS, Tailwind.
Server:
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. <link> tags can point to stylesheets and Bun's CSS bundler will bundle.
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
With the following frontend.tsx:
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
Then, run index.ts
bun --hot ./index.ts
For more information, read the Bun API docs in node_modules/bun-types/docs/**.mdx.