🪻 distributed transcription service thistle.dunkirk.sh
1# Thistle 2 3> [!IMPORTANT] 4> This is crazy pre-alpha and is changing really rapidly. Stuff should stabilize eventually but probably not for a month or two. 5 6```bash 7. 8├── public 9├── src 10│ ├── components 11│ ├── pages 12│ └── styles 13└── whisper-server 14 ├── main.py 15 ├── requirements.txt 16 └── README.md 17 189 directories, 3 files 19``` 20 21## What's this? 22 23Thistle is a transcription service I'm building for Cedarville's startup competition! I'm also using it as an opportunity to become more familar with web components and full stack applications. 24 25## How do I hack on it? 26 27### Development 28 29I'm just running this locally for now but getting started is super straightforward. 30 31```bash 32bun install 33bun dev 34``` 35 36Your server will be running at `http://localhost:3000` with hot module reloading. Just edit any `.ts`, `.html`, or `.css` file and watch it update in the browser. 37 38### Transcription Service 39 40Thistle requires a separate Whisper transcription server for audio processing. Set it up in the `whisper-server/` directory: 41 42```bash 43cd whisper-server 44./run.sh 45``` 46 47Or manually: 48```bash 49cd whisper-server 50pip install -r requirements.txt 51python main.py 52``` 53 54The Whisper server will run on `http://localhost:8000`. Make sure it's running before using transcription features. 55 56### Environment Setup 57 58Copy `.env.example` to `.env` and configure: 59 60```bash 61cp .env.example .env 62# Edit .env to set WHISPER_SERVICE_URL=http://localhost:8000 63``` 64 65The tech stack is pretty minimal on purpose. Lit components (~8-10KB gzipped) for things that need reactivity, vanilla JS for simple stuff, and CSS variables for theming. The goal is to keep the total JS bundle as small as possible. 66 67## How does it work? 68 69The development flow is really nice in my opinion. The server imports HTML files as route handlers. Those HTML files import TypeScript components using `<script type="module">`. The components are just Lit web components that self-register as custom elements. Bun sees all this and bundles everything automatically including linked images or assets from the public directory. 70 71```typescript 72// src/index.ts - Server imports HTML as routes 73import indexHTML from "./pages/index.html"; 74 75Bun.serve({ 76 port: 3000, 77 routes: { 78 "/": indexHTML, 79 }, 80 development: { 81 hmr: true, 82 console: true, 83 }, 84}); 85``` 86 87```html 88<!-- src/pages/index.html --> 89<!DOCTYPE html> 90<html lang="en"> 91 <head> 92 <link rel="stylesheet" href="../styles/main.css" /> 93 </head> 94 <body> 95 <counter-component></counter-component> 96 <script type="module" src="../components/counter.ts"></script> 97 </body> 98</html> 99``` 100 101```typescript 102// src/components/counter.ts 103import { LitElement, html, css } from "lit"; 104import { customElement, property } from "lit/decorators.js"; 105 106@customElement("counter-component") 107export class CounterComponent extends LitElement { 108 @property({ type: Number }) count = 0; 109 110 static styles = css` 111 :host { 112 display: block; 113 padding: 1rem; 114 } 115 `; 116 117 render() { 118 return html` 119 <div>${this.count}</div> 120 <button @click=${() => this.count++}>+</button> 121 `; 122 } 123} 124``` 125 126Oh last two points. Please please please use standard commits for my sanity and report any issues to [the tangled repo](https://tangled.org/dunkirk.sh/thistle) 127 128<p align="center"> 129 <img src="https://raw.githubusercontent.com/taciturnaxolotl/carriage/master/.github/images/line-break.svg" /> 130</p> 131 132<p align="center"> 133 &copy 2025-present <a href="https://github.com/taciturnaxolotl">Kieran Klukas</a> 134</p> 135 136<p align="center"> 137 <a href="https://github.com/taciturnaxolotl/thistle/blob/main/LICENSE.md"><img src="https://img.shields.io/static/v1.svg?style=for-the-badge&label=License&message=MIT&logoColor=d9e0ee&colorA=363a4f&colorB=b7bdf8"/></a> 138</p>