Magic Coin - a floating ring for macOS. MA transcribes, BA reads what is under it.
# Magic Coin A ring of white light that floats above every other window. Two faces: **MA** transcribes what you say, **BA** reads aloud whatever is underneath it. The middle is a real hole — clicks pass straight through, so it can sit on top of a document you are still working in. --- ## Three things to know before you build it **1. This is not a macOS Widget, and it cannot be.** Widgets (WidgetKit) live in Notification Center or pinned to the desktop. They cannot be dragged anywhere on screen, cannot float above other applications, and cannot see what is behind them. Everything you asked for needs a real window. So Magic Coin is a background agent: no Dock icon, no menu bar item, one borderless transparent panel. It behaves the way you described; it just is not the thing Apple calls a widget. **2. macOS has no vibration.** There is no vibration motor in a Mac. "Vibrating" here is a visual shudder — the ring wobbles and a second faint ring pulses inside it — plus a real haptic tap on Force Touch trackpads, which does nothing on a desktop. That is the closest honest equivalent. **3. Reading the screen needs permission, and Croatian OCR is weak.** The BA side screenshots the area under the coin and runs Apple's Vision OCR on it. macOS will ask for **Screen Recording** permission the first time. Vision's language list does not include Croatian, so Croatian text is read with mistakes on the diacritics. English is accurate. The speech itself handles Croatian properly — it is only the reading-from-pixels step that is limited. I could not compile this here; there is no Swift toolchain in the environment I built it in. The GitHub Actions run is therefore the first real compile. If it fails, send me the log and I will fix it — the errors will be small and specific, not structural. --- ## Build it 1. Make a new empty repository on GitHub, e.g. `magic-coin`. 2. Drop these files in and push to `main`. 3. The Actions tab will show a **build** run on a real macOS machine. 4. When it finishes, download **MagicCoin.zip** from the run's Artifacts. For a permanent download instead of an artifact, push a tag: ``` git tag v1.0 git push origin v1.0 ``` That attaches `MagicCoin.zip` to a GitHub Release. **No personal access token is needed.** Actions gives the job a built-in `GITHUB_TOKEN` with exactly the rights required. Please do not paste a personal token into a chat — anything pasted there should be treated as compromised, and this build does not want one. --- ## First run Unzip and drag `MagicCoin.app` to `/Applications`. The build is ad-hoc signed, not notarised, so Gatekeeper will refuse the first double-click. Right-click the app → **Open** → **Open**. Once only. The coin appears near the top right of your screen. --- ## Using it | action | what happens | |---|---| | drag the ring | move it anywhere | | double-click | flip the coin, MA ↔ BA | | click on **MA** | start recording; click again to send | | click on **BA** | read what is under the coin; click again to stop | | right-click | the menu: keys, size, quit | | click through the hole | nothing — the app below gets it | **MA** turns the ring red while recording, amber while uploading, and puts the transcript **on your clipboard** when it is done. A floating coin over another app exists so you can paste. **BA** turns amber while it looks and green while it speaks, and shows **one word at a time inside the ring** as it is said. The words come from `AVSpeechSynthesizer`, which reports the exact character range it is about to speak — so the highlighting is measured by the speech engine itself rather than reconstructed afterwards. --- ## The settings, which are only API keys Right-click the coin → **Load API keys from a file…** Pick any text file. The keys are lifted out of it and everything else is ignored. They are written to: ``` ~/Documents/maha_api_keys.txt ``` **This is the same file the Maha web app uses**, with the same format and the same parser rules. Set the keys up in either place and both find them. Right-click → **Show the key file in Finder** to see or delete it. Only an AssemblyAI key is needed. The BA side speaks with macOS's built-in voices and needs no key at all, so the coin is useful before you have set anything up. Parser rules, unchanged from the web app: * prefixed keys identify themselves — `sk-ant-`, `AIza`, `gsk_` * a bare 32-character hex string is AssemblyAI's format, and also an MD5, so it is only taken when the file says `assemblyai` above it or on the line, or when the file names no provider at all --- ## Files ``` Package.swift SwiftPM, macOS 14+ Sources/MagicCoin/App.swift the panel, the menu, the wiring Sources/MagicCoin/CoinView.swift the ring, the hole, the shudder, the word Sources/MagicCoin/Reader.swift screen capture, OCR, speech (BA) Sources/MagicCoin/Transcriber.swift microphone, AssemblyAI (MA) Sources/MagicCoin/Keys.swift the key file and its parser .github/workflows/build.yml the macOS build ``` The entry point is in `App.swift`, not `main.swift`, deliberately: Swift treats a file called `main.swift` as top-level code and then rejects `@main` anywhere in the module. The AssemblyAI request sends a **model list**, `["universal-3-5-pro", "universal-2"]`, not a single name. Universal-3.5 Pro covers 18 languages and Croatian is not among them, so a single-model request is rejected outright on Croatian audio. Sending the list lets AssemblyAI route and fall back.