feat: implement frameless OS-level floating dictation widget (#40)

* feat: implement frameless OS-level floating dictation widget

- Refactor CaptureButton into standalone CaptureWidget
- Add secondary transparent Tauri window configuration
- Map global hotkey to show/hide widget instead of focusing main app
- Implement auto-hide post-paste
- Add social preview image

* docs: up the game with enhanced README

- Use the high-quality social preview image as the hero image
- Bump download release links to v0.2.7
- Highlight the new Frameless Dictation Widget feature

* docs: complete README overhaul for maximum virality

- Add Highlights section with 2-column feature grid
- Move Quickstart to top with one-command install
- Collapse technical details into expandable sections
- Add 'Up Next' roadmap with concrete upcoming features
- Add star call-to-action banner
- Tighten navigation links and section hierarchy

* docs: add beta warning banner

* docs: add star request to beta banner

* docs: rewrite README with cognitive hooks, remove redundant CTAs

- Remove 2 premature star asks (beta banner + highlights)
- Rewrite highlights with loss-aversion framing
- Keep single earned CTA at the very bottom
- Use action-oriented headings that describe outcomes

* docs: rename section to 'Why OmniVoice Studio?'

* docs: rename 'What you get' to 'Features'

* docs: concise scannable features, remove duplicate section

- Each feature is one punchy emoji-led line
- No verbose paragraphs, no redundant collapsibles
- Removed duplicate Features section from merge

* docs: 3-column feature card grid for visual impact

Replaces flat bullet list with 4x3 HTML table grid.
Each feature gets its own visual cell with emoji header,
bold keywords, and 2-line description. Pops on dark mode.

* docs: fix feature grid vertical alignment

* chore: bump version to 0.2.7, add changelog entry

* fix: apply CodeRabbit auto-fixes

Fixed 1 file(s) based on 1 unresolved review comment.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
This commit is contained in:
Palash Debnath
2026-05-03 08:50:34 +05:30
committed by GitHub
co-authored by CodeRabbit coderabbitai[bot]
parent fba066c3d0
commit 6277561639
12 changed files with 263 additions and 221 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

+12
View File
@@ -6,6 +6,18 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/).
Versions track the desktop app (`tauri.conf.json` + `frontend/src-tauri/Cargo.toml`).
The bundled TTS model package (`pyproject.toml`) is versioned independently.
## [0.2.7] — Unreleased
### Added
- **Frameless dictation widget.** Global dictation upgraded from an in-app FAB to a true OS-level floating widget that hovers over any application. Transparent, decorations-free, always-on-top secondary Tauri window activated by `⌘+⇧+Space`. Auto-hides 2.5 s after a successful paste.
- **Standalone `CaptureWidget` component.** Refactored `CaptureButton` into `CaptureWidget`, running on an isolated route (`/?window=widget`).
- **Social preview image.** Added `social-preview.png` for GitHub SEO.
### Changed
- **README overhaul.** Compact 3-column feature grid, reorganized Quickstart (one-command install, Docker, Desktop App tips), updated comparison table, roadmap, and footer CTA.
---
## [0.2.6] — Unreleased
### License
+174 -171
View File
@@ -1,8 +1,9 @@
<div align="center">
<img src="docs/logo.png" alt="OmniVoice Logo" width="160" />
<img src="docs/logo.png" alt="OmniVoice Logo" width="120" />
<h1>OmniVoice Studio</h1>
<p><b>The open-source ElevenLabs alternative.</b></p>
<p>Voice cloning · Voice design · Video dubbing — 646 languages, runs 100% locally, forever free.</p>
<h3>The open-source ElevenLabs alternative.</h3>
<p>Real-time dictation, zero-shot voice cloning, and cinematic video dubbing — all on your desktop.<br/>Open-source, no API keys, fully local. <b>646 languages.</b></p>
<p>
<a href="https://github.com/debpalash/OmniVoice-Studio/stargazers"><img src="https://img.shields.io/github/stars/debpalash/OmniVoice-Studio?style=flat-square&color=f59e0b" alt="Stars" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/latest"><img src="https://img.shields.io/github/v/release/debpalash/OmniVoice-Studio?style=flat-square&color=10b981" alt="Release" /></a>
@@ -10,31 +11,176 @@
<a href="https://github.com/debpalash/OmniVoice-Studio/issues"><img src="https://img.shields.io/github/issues/debpalash/OmniVoice-Studio?style=flat-square&color=ef4444" alt="Issues" /></a>
<a href="https://discord.gg/aRRdVj3de7"><img src="https://img.shields.io/badge/Discord-Join_Community-5865F2?style=flat-square&logo=discord&logoColor=white" alt="Discord" /></a>
</p>
<p>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/latest">Download</a> ·
<a href="#features">Features</a> ·
<a href="#quickstart">Quickstart</a> ·
<a href="#why-open-source">Why Open Source?</a> ·
<a href="#roadmap">Roadmap</a>
<a href="#features">Features</a> ·
<a href="#why-omnivoice-studio">Why OmniVoice Studio?</a> ·
<a href="#tts-engines">TTS Engines</a> ·
<a href="#contributing">Contributing</a> ·
<a href="https://discord.gg/aRRdVj3de7">Discord</a>
</p>
<p>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.5/OmniVoice.Studio_0.2.5_aarch64.dmg"><img src="https://img.shields.io/badge/macOS-DMG_(Apple_Silicon)-000?style=for-the-badge&logo=apple&logoColor=white" alt="Download macOS DMG" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.5/OmniVoice.Studio_0.2.5_x64_en-US.msi"><img src="https://img.shields.io/badge/Windows-MSI_(x64)-0078D4?style=for-the-badge&logo=windows&logoColor=white" alt="Download Windows MSI" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.5/OmniVoice.Studio_0.2.5_amd64.AppImage"><img src="https://img.shields.io/badge/Linux-AppImage_(x64)-FCC624?style=for-the-badge&logo=linux&logoColor=black" alt="Download Linux AppImage" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.5/OmniVoice.Studio_0.2.5_amd64.deb"><img src="https://img.shields.io/badge/Debian-.deb-A81D33?style=for-the-badge&logo=debian&logoColor=white" alt="Download Debian .deb" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.6/OmniVoice.Studio_0.2.6_aarch64.dmg"><img src="https://img.shields.io/badge/macOS-DMG_(Apple_Silicon)-000?style=for-the-badge&logo=apple&logoColor=white" alt="Download macOS DMG" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.6/OmniVoice.Studio_0.2.6_x64_en-US.msi"><img src="https://img.shields.io/badge/Windows-MSI_(x64)-0078D4?style=for-the-badge&logo=windows&logoColor=white" alt="Download Windows MSI" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.6/OmniVoice.Studio_0.2.6_amd64.AppImage"><img src="https://img.shields.io/badge/Linux-AppImage_(x64)-FCC624?style=for-the-badge&logo=linux&logoColor=black" alt="Download Linux AppImage" /></a>
<a href="https://github.com/debpalash/OmniVoice-Studio/releases/download/v0.2.6/OmniVoice.Studio_0.2.6_amd64.deb"><img src="https://img.shields.io/badge/Debian-.deb-A81D33?style=for-the-badge&logo=debian&logoColor=white" alt="Download Debian .deb" /></a>
</p>
</div>
<br/>
<div align="center">
<img src="docs/preview.png" alt="OmniVoice Studio — Launchpad" width="100%"/>
<br/>
<sub>Launchpad — Voice Clone · Voice Design · Video Dubbing, all in one place.</sub>
<img src=".github/assets/social-preview.png" alt="OmniVoice Studio — The open-source ElevenLabs alternative" width="100%"/>
</div>
> [!WARNING]
> **OmniVoice Studio is in active beta.** Things may break between releases. For the latest features and fixes, clone the repo and run from source rather than using pre-built installers. Bug reports and PRs are very welcome — [open an issue](https://github.com/debpalash/OmniVoice-Studio/issues) or [join Discord](https://discord.gg/aRRdVj3de7).
<br/>
## Features
<table>
<tr>
<td align="center" width="33%">
<h3>🎙️ Voice Cloning</h3>
<p>3-second clip → mirror any voice.<br/><b>646 languages</b>, zero-shot.</p>
</td>
<td align="center" width="33%">
<h3>🎨 Voice Design</h3>
<p>Gender, age, accent, pitch, speed,<br/>emotion, dialect — <b>dial it in</b>.</p>
</td>
<td align="center" width="33%">
<h3>🎬 Video Dubbing</h3>
<p>YouTube URL or file → transcribe →<br/>translate → re-voice → <b>MP4</b>.</p>
</td>
</tr>
<tr>
<td align="center" valign="top">
<h3>⌨️ Dictation Widget</h3>
<p><code>⌘+⇧+Space</code> from <b>any app</b>.<br/>Transcribes, auto-pastes, disappears.</p>
</td>
<td align="center" valign="top">
<h3>🔊 Vocal Isolation</h3>
<p>Demucs-powered. Splits speech<br/>from music, <b>keeps the background</b>.</p>
</td>
<td align="center" valign="top">
<h3>👥 Speaker Diarization</h3>
<p>Pyannote + WhisperX.<br/><b>Auto-identifies</b> who said what.</p>
</td>
</tr>
<tr>
<td align="center" valign="top">
<h3>📦 Batch Queue</h3>
<p>Drop <b>50 videos</b>, walk away.<br/>Progress bars per job.</p>
</td>
<td align="center" valign="top">
<h3>🤖 MCP Server</h3>
<p>Use OmniVoice from <b>Claude</b>,<br/>Cursor, or any MCP client.</p>
</td>
<td align="center" valign="top">
<h3>🛡️ AI Watermark</h3>
<p>AudioSeal (Meta). <b>Invisible</b>,<br/>survives compression.</p>
</td>
</tr>
<tr>
<td align="center" valign="top">
<h3>🔐 100% Local</h3>
<p>No keys, no cloud, no accounts.<br/><b>Your machine only</b>.</p>
</td>
<td align="center" valign="top">
<h3>⚡ GPU Auto-Detect</h3>
<p>CUDA · MPS · ROCm · CPU.<br/>≤8 GB? <b>Auto-offloads</b>.</p>
</td>
<td align="center" valign="top">
<h3>🧩 Extensible</h3>
<p>Subclass <code>TTSBackend</code>,<br/>add any engine in <b>~50 lines</b>.</p>
</td>
</tr>
</table>
---
## Quickstart
### One-command install
```bash
git clone https://github.com/debpalash/OmniVoice-Studio.git && cd OmniVoice-Studio && bun install && bun run dev
```
That's it. Open [localhost:3901](http://localhost:3901) and start cloning voices.
### Docker
```bash
# CPU mode
docker compose up --build -d
# Or with NVIDIA GPU
docker compose --profile gpu up --build -d
```
Open [http://localhost:3900](http://localhost:3900) once the health check passes. First run downloads ~4 GB of model weights — progress is shown in `docker compose logs -f`.
> **Network access:** the container binds to `127.0.0.1` only. To reach OmniVoice from another machine on your LAN, change the port mapping in `docker-compose.yml` to `"0.0.0.0:3900:3900"`. OmniVoice ships no built-in authentication — when exposing it beyond your machine, put it behind a reverse proxy with auth (Caddy `basic_auth`, nginx + htpasswd, Tailscale, etc.).
### Desktop App
Pre-built installers (~68 MB) are available on the [**Releases**](https://github.com/debpalash/OmniVoice-Studio/releases/latest) page. On first launch, the app bootstraps a Python environment and downloads model weights automatically — the splash screen shows progress.
```bash
bun run desktop # Build from source (macOS / Windows / Linux)
```
<details>
<summary><b>macOS — "app is damaged and can't be opened"</b></summary>
<br/>
macOS quarantines apps downloaded outside the App Store. After dragging to `/Applications`:
```bash
xattr -cr /Applications/OmniVoice\ Studio.app
```
Open normally after. One-time fix.
</details>
<details>
<summary><b>Windows — first launch takes 510 minutes</b></summary>
<br/>
The app bootstraps a Python virtual environment, installs dependencies, and downloads ffmpeg on first run. The splash screen shows each step. Subsequent launches start in seconds.
</details>
<details>
<summary><b>Linux — AppImage needs FUSE</b></summary>
<br/>
If FUSE isn't available, use the `.deb` package or extract-and-run:
```bash
chmod +x OmniVoice.Studio_*.AppImage
./OmniVoice.Studio_*.AppImage --appimage-extract-and-run
```
</details>
> [!NOTE]
> First run downloads model weights (~2.4 GB). This works out of the box — no account needed. For faster downloads, optionally set `HF_TOKEN=hf_...` in your environment ([get a free token here](https://huggingface.co/settings/tokens)).
>
> **Having issues?** Join our [Discord](https://discord.gg/aRRdVj3de7) for setup help and troubleshooting.
| Service | URL | Stack |
|---------|-----|-------|
| **Backend** | `localhost:3900` | FastAPI · 97 endpoints · WhisperX · Demucs · OmniVoice |
| **Frontend** | `localhost:3901` | React · Vite · Waveform timeline · Glassmorphism UI |
---
## Screenshots
<table>
<tr>
<td align="center" width="50%">
@@ -83,7 +229,7 @@
---
## Why Open Source?
## Why OmniVoice Studio?
ElevenLabs charges **$5$330/mo** and processes your audio on their servers. OmniVoice Studio runs **on your hardware, with no usage limits.**
@@ -100,152 +246,7 @@ ElevenLabs charges **$5$330/mo** and processes your audio on their servers. O
| **Desktop App** | ❌ | ✅ macOS · Windows · Linux |
| **Customizable** | ❌ Closed | ✅ Fork it, extend it, ship it |
Built on the [OmniVoice](https://github.com/k2-fsa/OmniVoice) 600-language zero-shot diffusion TTS model. Upload a video, get broadcast-quality dubs in any language with the original speaker's voice preserved.
## Features
### Core Pipeline
- **Video Dubbing** — Transcribe → translate → synthesize → mux back to MP4. One-click end-to-end.
- **Vocal Isolation** — Demucs-powered speech/music separation. Background audio preserved automatically.
- **Voice Cloning** — Clone any voice from a 3-second clip. Zero-shot, 600+ languages.
- **Multi-Speaker Diarization** — Pyannote + WhisperX fusion auto-identifies speakers and assigns unique voice profiles.
### Studio Tools
- **Voice Capture** — Press `⌘+⇧+Space` **from any app** to dictate. Global system-wide hotkey records, transcribes, and auto-pastes into the active text field. Live partial results stream via WebSocket while you speak.
- **Speaker Casting** — Visual speaker-to-voice assignment grid. Auto-cast from video clones or assign saved profiles.
- **Voice Preview** — Floating widget for instant 8-step TTS testing. Try voices without leaving the workspace.
- **Real-time Dub Preview** — Edit a segment's text, preview the audio instantly without full re-render.
- **Multi-Language Batch** — Select multiple target languages, dub to all in one pass.
- **Batch Queue** — Drag-and-drop bulk video processing. Full pipeline: extract → transcribe → translate → generate → mix → export. Real-time progress bars per job.
- **Voice Library** — Browse, favorite, tag, and convert gallery clips into permanent voice profiles.
- **A/B Comparison** — Side-by-side voice audition for casting decisions.
### Production Export
- **Selective Track Export** — Choose which language tracks to include in the final MP4.
- **Subtitle Export** — SRT and VTT generation alongside dubbed video.
- **Stem Export** — Separate vocals and background audio as individual files.
- **Per-Segment Mixing** — 0200% gain control per segment for broadcast-quality balancing.
### Technical
- **Cross-Platform GPU** — Auto-detects CUDA, Apple Silicon (MPS), ROCm, or CPU. Includes automatic cuDNN 8/9 compatibility handling.
- **VRAM-Aware** — Automatically offloads TTS to CPU during transcription on ≤8 GB GPUs. Zero config.
- **Streaming ASR** — WebSocket-based speech-to-text (`/ws/transcribe`) delivers live partial results during recording. 2s buffer interval, configurable.
- **Auto-Paste** — Dictated text is automatically pasted into the active app via system keyboard simulation (macOS Accessibility / Windows SendInput).
- **Live Telemetry** — Real-time CPU/RAM/VRAM stats with model warm-up indicator.
- **Keyboard-First** — `⌘+Enter` generate, `⌘+S` save, `⌘+Z`/`⌘+⇧+Z` undo/redo.
### AI Provenance
- **Invisible Watermark** — AudioSeal-powered (Meta) neural watermark embedded in every generated audio. Imperceptible, survives compression/editing.
- **Detection API** — Upload any audio to `/watermark/detect` to verify OmniVoice origin with confidence score.
- **Video Branding** — Optional logo overlay on exported MP4s (5s fade-out, bottom-right).
- **Configurable** — Toggle invisible/visible watermarks independently in Settings → Privacy.
### MCP Server (AI Agent Integration)
- **Model Context Protocol** — Expose OmniVoice as an AI agent tool for Claude, Cursor, and any MCP-compatible client.
- **5 Tools** — `generate_speech`, `list_voices`, `list_personalities`, `list_languages`, `check_health`.
- **stdio + SSE** — Works locally (Claude Desktop) or remotely (networked agents).
- **Zero config** — Drop `mcp.json` into your client config and go. See [`mcp.json`](docs/mcp.json).
### Audio Effects Chain
- **6 presets** — Broadcast 📻, Cinematic 🎬, Podcast 🎙️, Warm ☀️, Bright ✨, Raw 🔇.
- **Pedalboard-powered** — Spotify's production-grade DSP (EQ, compressor, reverb, noise gate, limiter).
- **API-driven** — `GET /tools/effects` returns presets; custom chains via `apply_effects_chain()`.
### Plugin SDK (Third-Party TTS Engines)
- **Abstract interface** — Subclass `TTSPlugin` to add any TTS engine in ~50 lines.
- **Built-in plugins** — ElevenLabs (cloud) and Bark (local) ship out of the box.
- **Auto-discovery** — Drop a `.py` file in `backend/plugins/`, it registers automatically.
- **API** — `GET /tools/plugins` lists all engines and their availability status.
### GPU Safety
- **Crash sandbox** — GPU-intensive ops can run in subprocess isolation. A CUDA OOM or driver crash kills the worker, not the server.
- **6 color themes** — Gruvbox (default), Midnight Blue, Nord, Solarized, Rosé Pine, Catppuccin Mocha.
---
## Quickstart
### Docker (recommended)
```bash
git clone https://github.com/debpalash/OmniVoice-Studio.git
cd OmniVoice-Studio
# CPU mode
docker compose up --build -d
# Or with NVIDIA GPU
docker compose --profile gpu up --build -d
```
Open [http://localhost:3900](http://localhost:3900) once the health check passes. First run downloads ~4 GB of model weights — progress is shown in `docker compose logs -f`.
> **Network access:** the container binds to `127.0.0.1` only. To reach OmniVoice from another machine on your LAN, change the port mapping in `docker-compose.yml` to `"0.0.0.0:3900:3900"`. OmniVoice ships no built-in authentication — when exposing it beyond your machine, put it behind a reverse proxy with auth (Caddy `basic_auth`, nginx + htpasswd, Tailscale, etc.).
### Local Development
**Prerequisites:** [ffmpeg](https://ffmpeg.org/), [Bun](https://bun.sh/), [uv](https://docs.astral.sh/uv/)
```bash
git clone https://github.com/debpalash/OmniVoice-Studio.git
cd OmniVoice-Studio
bun install
bun run dev
```
This boots both services:
| Service | URL | Stack |
|---------|-----|-------|
| **Backend** | `localhost:3900` | FastAPI · 97 endpoints · WhisperX · Demucs · OmniVoice |
| **Frontend** | `localhost:3901` | React · Vite · Waveform timeline · Glassmorphism UI |
> [!NOTE]
> First run downloads model weights (~2.4 GB). This works out of the box — no account needed. For faster downloads, optionally set `HF_TOKEN=hf_...` in your environment ([get a free token here](https://huggingface.co/settings/tokens)).
>
> **Having issues?** Join our [Discord](https://discord.gg/aRRdVj3de7) for setup help and troubleshooting.
### Desktop App
Pre-built installers (~68 MB) are available on the [**Releases**](https://github.com/debpalash/OmniVoice-Studio/releases/latest) page. On first launch, the app bootstraps a Python environment and downloads model weights automatically — the splash screen shows progress.
To build from source instead:
```bash
bun run desktop # Launches Tauri native app (macOS / Windows / Linux)
```
<details>
<summary><b>macOS — "app is damaged and can't be opened"</b></summary>
<br/>
macOS quarantines apps downloaded outside the App Store. After dragging to `/Applications`:
```bash
xattr -cr /Applications/OmniVoice\ Studio.app
```
Open normally after. One-time fix.
</details>
<details>
<summary><b>Windows — first launch takes 510 minutes</b></summary>
<br/>
The app bootstraps a Python virtual environment, installs dependencies, and downloads ffmpeg on first run. The splash screen shows each step. Subsequent launches start in seconds.
</details>
<details>
<summary><b>Linux — AppImage needs FUSE</b></summary>
<br/>
If FUSE isn't available, use the `.deb` package or extract-and-run:
```bash
chmod +x OmniVoice.Studio_*.AppImage
./OmniVoice.Studio_*.AppImage --appimage-extract-and-run
```
</details>
OmniVoice Studio gives you professional-grade AI tools without the subscription or the cloud.
---
@@ -317,18 +318,15 @@ OmniVoice ships a multi-engine TTS backend. The default engine (OmniVoice) is al
| **State Management** | Zustand store migration — `uiSlice`, `pillSlice`, `dubSlice`, `generateSlice`, `prefsSlice`, `glossarySlice` |
| **Desktop** | Cross-platform Tauri installers (macOS DMG, Windows MSI, Linux deb/AppImage), auto-update infrastructure |
| **Windows Hardening** | Cross-platform log paths, Triton workaround, HF symlink bypass, 300s health check timeout |
| **Dictation** | Global system-wide hotkey (`⌘+⇧+Space`), streaming ASR via WebSocket, auto-paste into active app |
| **Dictation** | Global system-wide hotkey (`⌘+⇧+Space`), frameless floating widget, streaming ASR via WebSocket, auto-paste |
| **Batch Pipeline** | Full batch TTS: extract → transcribe → translate → generate → mix → export, with live progress tracking |
### 🔜 Roadmap — completed ✅
### 🔜 Up Next
**All planned features have been shipped.**
- ~~Onboarding sample clip~~ · ~~Docker DX~~ · ~~Auto-updater~~ · ~~Deferred disk writes~~
- ~~MCP server~~ · ~~Voice personalities~~ · ~~Audio effects chain~~ · ~~i18n framework~~
- ~~Global hotkey dictation~~ · ~~Real-time dub preview~~ · ~~Speaker casting view~~
- ~~Theme system~~ · ~~Plugin SDK~~ · ~~GPU crash sandbox~~ · ~~Waveform v2~~
- ~~Batched TTS~~ · ~~Cold start optimization~~ · ~~Audiobook editor~~ · ~~Context-aware pipeline~~
- 🎬 **Lip-sync v2** — visual speech timing with wav2lip
- 📖 **Audiobook Editor** — chapter-aware long-form narration
- 🌐 **Hosted Demo** — try OmniVoice without installing anything
- 🔌 **Plugin Marketplace** — community-contributed TTS engines and effects
---
@@ -412,7 +410,12 @@ OmniVoice Studio is built on the shoulders of exceptional open-source work:
<div align="center">
**[⭐ Star on GitHub](https://github.com/debpalash/OmniVoice-Studio)** to follow updates.
<br/>
If you read this far, you're our kind of person.<br/>
**[⭐ Star this repo](https://github.com/debpalash/OmniVoice-Studio)** so others can find it too.
<br/>
<a href="https://star-history.com/#debpalash/OmniVoice-Studio&Date">
<picture>
+12 -12
View File
@@ -15,7 +15,7 @@
},
"frontend": {
"name": "omnivoice-studio",
"version": "0.2.6",
"version": "0.2.7",
"dependencies": {
"@fontsource-variable/inter": "^5.2.8",
"@fontsource-variable/source-serif-4": "^5.2.9",
@@ -29,40 +29,40 @@
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@tailwindcss/vite": "4",
"@tanstack/react-query": "^5.100.4",
"@tailwindcss/vite": "^4.2.4",
"@tanstack/react-query": "^5.100.8",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.24",
"@tauri-apps/plugin-dialog": "^2.7.0",
"@tauri-apps/plugin-opener": "^2.5.3",
"@tauri-apps/plugin-dialog": "^2.7.1",
"@tauri-apps/plugin-opener": "^2.5.4",
"@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.10.1",
"@tauri-apps/plugin-window-state": "^2.4.1",
"i18next": "^26.0.8",
"i18next-browser-languagedetector": "^8.2.1",
"lucide-react": "^1.8.0",
"lucide-react": "^1.14.0",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"react-hot-toast": "^2.6.0",
"react-i18next": "^17.0.6",
"react-window": "^2.2.7",
"tailwindcss": "4",
"tailwindcss": "^4.2.4",
"wavesurfer.js": "^7.12.6",
"zustand": "^5.0.12",
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@tauri-apps/api": "^2.10.1",
"@tauri-apps/cli": "^2.10.1",
"@tauri-apps/api": "^2.11.0",
"@tauri-apps/cli": "^2.11.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^10.2.1",
"eslint": "^10.3.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.5.0",
"globals": "^17.6.0",
"typescript": "^6.0.3",
"vite": "^8.0.9",
"vite": "^8.0.10",
},
},
},
+1 -1
View File
@@ -2757,7 +2757,7 @@ dependencies = [
[[package]]
name = "omnivoice-studio"
version = "0.2.6"
version = "0.2.7"
dependencies = [
"enigo",
"libc",
+1 -1
View File
@@ -126,7 +126,7 @@ pub fn run() {
.with_handler(move |app_handle, _shortcut, event| {
if event.state == ShortcutState::Pressed {
log::info!("Global shortcut triggered: dictation");
if let Some(win) = app_handle.get_webview_window("main") {
if let Some(win) = app_handle.get_webview_window("widget") {
let _ = win.show();
let _ = win.set_focus();
}
+14
View File
@@ -23,6 +23,20 @@
"fullscreen": false,
"titleBarStyle": "Overlay",
"hiddenTitle": true
},
{
"label": "widget",
"title": "Dictation Widget",
"url": "/?window=widget",
"width": 350,
"height": 220,
"resizable": false,
"fullscreen": false,
"transparent": true,
"decorations": false,
"alwaysOnTop": true,
"visible": false,
"skipTaskbar": true
}
],
"security": {
+2 -2
View File
@@ -28,7 +28,7 @@ import Header from './components/Header';
import NavRail from './components/NavRail';
import ErrorBoundary from './components/ErrorBoundary';
import FloatingPill from './components/FloatingPill';
import CaptureButton from './components/CaptureButton';
import useRealtimeEvents from './hooks/useRealtimeEvents';
import { BootstrapSplash, useBootstrapStage } from './components/BootstrapSplash';
@@ -1767,7 +1767,7 @@ function App() {
}}/>
<FloatingPill />
<CaptureButton />
<Header
mode={mode} setMode={setMode}
@@ -1,16 +1,14 @@
/* ── CaptureButton — Global dictation FAB ─────────────────────────────── */
.capture-widget {
position: fixed;
/* Sit above the footer status bar (28px collapsed, expands via CSS var) */
bottom: calc(var(--logs-footer-height, 28px) + 12px);
right: 18px;
z-index: 9000;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 8px;
pointer-events: none;
align-items: center;
justify-content: center;
padding: 10px;
box-sizing: border-box;
}
.capture-widget > * {
@@ -69,8 +67,11 @@
border: 1px solid color-mix(in srgb, var(--chrome-border, #3c3836) 60%, transparent);
border-radius: 16px;
padding: 14px 16px;
min-width: 260px;
max-width: 320px;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
box-sizing: border-box;
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.35);
animation: capture-slide-up 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Mic, MicOff, Clipboard, X, Loader, Zap, Target, Check } from 'lucide-react';
import { toast } from 'react-hot-toast';
import { useAppStore } from '../store';
import './CaptureButton.css';
import './CaptureWidget.css';
import { API as API_BASE } from '../api/client';
import { addTranscription } from '../pages/Transcriptions';
@@ -33,11 +33,10 @@ const LS_AUTO_COPY = 'omni_capture_auto_copy';
*
* Auto-copies to clipboard so users can immediately V into any app.
*/
export default function CaptureButton() {
export default function CaptureWidget() {
const [state, setState] = useState('idle'); // idle | recording | transcribing | done | error
const [transcript, setTranscript] = useState('');
const [duration, setDuration] = useState(0);
const [expanded, setExpanded] = useState(false);
const [captureMode, setCaptureMode] = useState(() =>
localStorage.getItem(LS_CAPTURE_MODE) || 'fast'
);
@@ -137,6 +136,19 @@ export default function CaptureButton() {
} catch {
toast.success('Copied to clipboard — paste with ⌘V', { duration: 2000 });
}
// Auto-dismiss the floating widget after 2.5 seconds so it gets out of the way
setTimeout(async () => {
setState('idle');
setTranscript('');
setDuration(0);
setCopied(false);
try {
const { getCurrentWindow } = await import('@tauri-apps/api/window');
await getCurrentWindow().hide();
} catch { /* not in Tauri */ }
}, 2500);
} catch { /* clipboard API may fail in some contexts */ }
}
}, [autoCopy]);
@@ -371,12 +383,15 @@ export default function CaptureButton() {
});
}, [transcript]);
const dismiss = () => {
const dismiss = async () => {
setState('idle');
setTranscript('');
setExpanded(false);
setDuration(0);
setCopied(false);
try {
const { getCurrentWindow } = await import('@tauri-apps/api/window');
await getCurrentWindow().hide();
} catch { /* not in Tauri */ }
};
const toggleCapture = () => {
@@ -395,11 +410,9 @@ export default function CaptureButton() {
};
return (
<div className={`capture-widget ${expanded ? 'capture-widget--expanded' : ''}`}>
{/* Expanded panel */}
{expanded && (
<div className="capture-widget">
<div className="capture-panel">
<div className="capture-panel__header">
<div className="capture-panel__header" data-tauri-drag-region>
<span className="capture-panel__title">
{state === 'recording' && '🎙️ Listening…'}
{state === 'transcribing' && '📝 Transcribing…'}
@@ -491,22 +504,10 @@ export default function CaptureButton() {
</button>
</div>
<div className="capture-panel__hint">
<div className="capture-panel__hint" data-tauri-drag-region>
<kbd>{navigator.platform?.includes('Mac') ? '⌘' : 'Ctrl'}</kbd>+<kbd></kbd>+<kbd>Space</kbd>
</div>
</div>
)}
{/* Main FAB button */}
<button
className={`capture-fab ${state === 'recording' ? 'capture-fab--recording' : ''} ${state === 'transcribing' ? 'capture-fab--busy' : ''}`}
onClick={toggleCapture}
disabled={state === 'transcribing'}
title={state === 'recording' ? 'Stop recording' : 'Start dictation (⌘+⇧+Space)'}
aria-label={state === 'recording' ? 'Stop recording' : 'Start voice dictation'}
>
{state === 'recording' ? <MicOff size={20} /> : state === 'transcribing' ? <Loader size={20} className="spinner" /> : <Mic size={20} />}
</button>
</div>
);
}
+12 -1
View File
@@ -27,11 +27,22 @@ const queryClient = new QueryClient({
},
});
import { Suspense, lazy } from 'react';
const CaptureWidget = lazy(() => import('./components/CaptureWidget.jsx'));
export function bootstrapApp() {
const isWidget = window.location.search.includes('window=widget');
createRoot(document.getElementById('root')).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
{isWidget ? (
<Suspense fallback={<div>Loading...</div>}>
<CaptureWidget />
</Suspense>
) : (
<App />
)}
</QueryClientProvider>
</StrictMode>,
);
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "omnivoice"
version = "0.2.4"
version = "0.2.7"
description = "OmniVoice: Towards Omnilingual Zero-Shot Text-to-Speech with Diffusion Language Models"
readme = "README.md"
license = "Apache-2.0"