Rewrite README; fix fd leak, StopIteration crash, add pip Dependabot

- README.md: full rewrite with architecture, quickstart, config examples,
  privacy model, and contributing section (deduced from source)
- recorder.py: close leaked fd from mkstemp (SEC-2)
- gtkui.py: guard next() with default to prevent StopIteration (CQ-4)
- dependabot.yml: add pip ecosystem for linux/ deps (SEC-5)
This commit is contained in:
mARTin-B78 2026-06-05 16:01:31 +02:00
parent b72936be31
commit 80038a37ea
4 changed files with 308 additions and 243 deletions

View File

@ -5,3 +5,7 @@ updates:
directory: /
schedule:
interval: weekly
- package-ecosystem: pip
directory: /linux
schedule:
interval: weekly

539
README.md
View File

@ -1,212 +1,251 @@
# Blitztext App Linux
# Blitztext App Linux
Blitztext App Linux is a native Linux dictation and writing assistant. Focus any
text field, trigger a workflow, speak, and Blitztext types the result back into
the application you were already using.
**Speak into any text field on your Linux desktop — instantly.**
It can type a plain transcript, rewrite rough speech with an OpenAI-compatible
LLM, route spoken commands to different workflows, benchmark STT engines, and
stream live words from a Riva/NIM realtime speech server.
Blitztext is a native Linux dictation tool that captures your voice, transcribes it locally with [faster-whisper](https://github.com/SYSTRAN/faster-whisper), optionally rewrites the text through an LLM, and types the result directly into whatever application has focus. Think macOS Dictation, but open-source, extensible, and designed for power users who want full control over their speech-to-text pipeline.
This is a host desktop app, not a browser app and not a hosted service. The
current Linux version is built around GTK, AppIndicator, global hotkeys,
`faster-whisper`, optional OpenAI-compatible endpoints, optional Riva/NIM
realtime STT, and `xdotool` text delivery.
> **Status:** Experimental open-source Linux/X11 desktop app (v1.1.0).
> No hosted backend — bring your own models and endpoints.
> Status: experimental Linux/X11 app. Bring your own local models or endpoints.
> No hosted Blitztext backend is included.
---
## Inspiration
## Inspiration & Credits
Blitztext App Linux is inspired by
[cmagnussen/blitztext-app](https://github.com/cmagnussen/blitztext-app), the
original macOS menu-bar app for turning speech into text and cleaner writing.
This repository keeps the spirit of that workflow while implementing a
Linux-native version with Linux desktop APIs and Linux-friendly model backends.
Blitztext App Linux is inspired by [cmagnussen/blitztext-app](https://github.com/cmagnussen/blitztext-app), the original macOS menu-bar app for turning speech into text and cleaner writing. This Linux version recreates the workflow using Linux-native components:
## Features
| Upstream (macOS) | This project (Linux) |
|---|---|
| Swift / SwiftUI | Python 3.11+ / GTK 3 |
| WhisperKit / CoreML | [faster-whisper](https://github.com/SYSTRAN/faster-whisper) (CTranslate2) |
| macOS Accessibility API | `xdotool` (X11) |
| Menu bar app | AppIndicator tray + GTK control panel |
| — | Riva/NIM realtime WebSocket STT |
| — | Voice-keyword routing |
| — | Built-in STT benchmark |
- **Native Linux tray app**: run Blitztext from the system tray, open the GTK
control panel, or run it headless with global hotkeys only.
- **Plain dictation**: record speech, transcribe it, and type the raw text into
the focused X11 window.
- **Rewrite workflows**: turn rough speech into a nicer email, improved text, a
calmer message, emoji-rich text, or any custom prompt you define.
- **Realtime STT streaming**: use a `riva_realtime` STT engine, such as
Nemotron ASR Streaming, to type stable words while you are still speaking.
- **Voice-keyword routing**: use one routing hotkey and say a workflow keyword
at the start or end of your speech to choose the preset.
- **Two input styles**: use direct workflow hotkeys, or the modifier workflow:
Ctrl+Super to start, Ctrl to stop and type, Alt to stop/type/send, Esc to
cancel.
- **Push-to-talk option**: hold the start modifier to record and release it to
finish.
- **Multiple STT engines**: local `faster-whisper`, OpenAI-compatible batch STT
endpoints, and Riva/NIM realtime WebSocket STT.
- **Multiple LLM engines**: OpenAI-compatible chat endpoints for OpenAI, local
vLLM, llama-swap, Ollama `/v1`, LM Studio, Groq, OpenRouter, and similar
servers.
- **STT benchmark tab**: compare configured STT engines against a reference WAV
and transcript.
- **Settings UI**: edit workflows, engines, input mode, microphone, language,
benchmark files, autostart, logs, and About metadata.
- **About page**: shows the app version, source repository, changelog, license,
and legal notes.
- **Debian package**: build a local `.deb` with the launcher, icon, bundled
Python environment, and system dependency declarations.
Credit to the [SYSTRAN/faster-whisper](https://github.com/SYSTRAN/faster-whisper) project for the local Whisper inference engine, and to the [pynput](https://pypi.org/project/pynput/) library for global hotkey handling.
## How It Works
---
```text
Batch workflows
---------------
focused app -> global hotkey -> record mic -> STT engine
|
+-> mode "transcribe" -> type/paste text
|
+-> mode "rewrite" -> LLM -> type/paste text
## Core Functionality & Key Differentiators
Realtime workflow
-----------------
focused app -> stream workflow -> mic PCM chunks -> Riva/NIM WebSocket
|
+-> stable partial text -> xdotool
### What it does
```
Batch: hotkey → record mic → faster-whisper (local) → [optional LLM rewrite] → xdotool types it
Stream: hotkey → mic PCM chunks → Riva/NIM WebSocket → live words typed as you speak
```
Blitztext remembers the focused X11 window when recording starts, then delivers
the final text back to that window. Delivery can be direct typing or clipboard
paste, depending on your configuration.
1. **Focus any text field** — terminal, browser, email client, IDE, chat app.
2. **Press a hotkey** (or use modifier keys, or click the tray menu).
3. **Speak naturally.**
4. **Text appears** where your cursor is — plain transcript, polished email, calmed-down message, or emoji-enriched text.
### What makes it different
- **Runs on the host, not in a browser.** Because it uses `xdotool`, it can type into *any* X11 application — not just a web page or Electron app.
- **Fully local STT.** Batch transcription via `faster-whisper` never leaves your machine. No cloud account needed for basic dictation.
- **Pluggable engines.** Configure multiple STT and LLM backends as named presets — local `faster-whisper`, remote OpenAI-compatible batch endpoints, Riva/NIM realtime WebSocket servers, and any OpenAI-compatible chat API (OpenAI, vLLM, llama-swap, Ollama, LM Studio, Groq, OpenRouter).
- **Voice-keyword routing.** One hotkey, multiple workflows. Say "nicer email" at the start or end of your speech and the email-rewrite preset activates automatically (fuzzy-matched, ASR-tolerant).
- **Quality gate.** Silent clips, too-short recordings, and Whisper hallucinations ("Thank you.", "Untertitel…") are caught and rejected before they reach your text field.
- **Realtime streaming.** Connect a Riva/NIM realtime STT server and see stable words typed live as you speak.
- **Built-in benchmarking.** Compare all your configured STT engines against a reference WAV + transcript to find the fastest and most accurate.
---
## Target Use Cases
| Scenario | How Blitztext helps |
|---|---|
| **Quick replies** | Dictate an email or chat message instead of typing it |
| **Rough-to-polished** | Speak freely, let the LLM rewrite it into a professional email |
| **Multilingual dictation** | faster-whisper supports 99 languages; set `language = "de"` or `"en"` |
| **Local-only transcription** | Use `faster-whisper` with no network calls at all |
| **Live captioning** | Stream mode types words as you speak (with a Riva/NIM server) |
| **Voice-driven workflows** | Trigger different presets by speaking a keyword |
| **STT engine comparison** | Benchmark tab compares speed and accuracy across all configured engines |
| **GPU or CPU** | Works on CPU (`int8`) out of the box; add a CUDA CTranslate2 build for GPU |
---
## Requirements
- Linux desktop running an **X11 session**.
- Python 3.11+ when running from source.
- `xdotool` for text delivery.
- `notify-send` from `libnotify-bin` for desktop notifications.
- One recorder: `pw-record`, `parecord`, or `arecord`.
- `python3-gi` and AppIndicator/Ayatana typelibs for the GTK tray.
- Optional: CUDA-capable `faster-whisper`/CTranslate2 setup for GPU local STT.
- Optional: an OpenAI-compatible chat endpoint for rewrite workflows.
- Optional: a Riva/NIM realtime server for live streaming STT.
- **Linux desktop with an X11 session** (Wayland needs `ydotool`/`wtype` — not yet supported)
- **Python 3.11+** (for source installs)
- **Host tools:**
On Ubuntu/Debian:
```bash
sudo apt install xdotool libnotify-bin pipewire-bin python3-gi
```
- `xdotool` — text delivery into the focused window
- `libnotify-bin` — desktop notifications (`notify-send`)
- `pipewire-bin` — microphone recording (`pw-record`); alternatives: `pulseaudio-utils` (`parecord`) or `alsa-utils` (`arecord`)
- `python3-gi` — GTK 3 / AppIndicator system tray
- **Optional:** An OpenAI-compatible chat endpoint for rewrite workflows
- **Optional:** A Riva/NIM realtime server for live streaming STT
---
## Installation & Setup
### Option A — Debian Package (recommended for Ubuntu/Debian)
Build a `.deb` and install it. No pip, no network needed at install time:
```bash
sudo apt install xdotool libnotify-bin pipewire-bin python3-gi
```
Wayland is not the primary target yet. The current app uses `xdotool`, so use an
X11 session for reliable typing into other applications.
## Install
### Debian Package
The recommended Ubuntu/Debian path is to build and install the local package:
```bash
cd linux
bash packaging/build-deb.sh
git clone https://github.com/mARTin-B78/blitztext-app-linux.git
cd blitztext-app-linux/linux
bash packaging/build-deb.sh # → dist/blitztext_<ver>_<arch>.deb
sudo apt install ./dist/blitztext_*.deb
```
This installs Blitztext under `/opt/blitztext`, adds a desktop launcher, installs
the app icon, and provides the `blitztext` command.
This installs Blitztext to `/opt/blitztext`, adds a **Blitztext** entry to your application menu, installs the app icon, and pulls in system dependencies automatically. Remove with `sudo apt remove blitztext`.
Start it with:
### Option B — Run from Source (venv)
```bash
blitztext tray
```
### From Source
```bash
cd linux
git clone https://github.com/mARTin-B78/blitztext-app-linux.git
cd blitztext-app-linux/linux
./install.sh
```
`install.sh` creates a `.venv` with `--system-site-packages` (so it sees the system `python3-gi` for the GTK tray), installs dependencies from `requirements.txt`, and writes the default config.
> **Important:** Build the venv from `/usr/bin/python3`, not a conda/miniforge Python.
> A conda Python can't see the apt-installed `python3-gi`, so the tray won't start.
> The `.deb` package avoids this issue entirely.
### Environment Variables
The only environment variable needed is for rewrite workflows that use a cloud LLM:
```bash
export OPENAI_API_KEY=sk-... # only if using OpenAI or a keyed endpoint
```
The config file (`~/.config/blitztext/config.toml`) references environment variable *names*, never the keys themselves. Local STT and local LLM endpoints typically require no key.
---
## Quickstart Tutorial
**Goal:** Go from zero to dictating text into a terminal in under 5 minutes.
### 1. Install
```bash
# Ubuntu/Debian — install host tools
sudo apt install xdotool libnotify-bin pipewire-bin python3-gi
# Clone and set up
git clone https://github.com/mARTin-B78/blitztext-app-linux.git
cd blitztext-app-linux/linux
./install.sh
```
### 2. Launch
```bash
.venv/bin/python -m blitztext tray
```
`install.sh` creates a virtual environment with `--system-site-packages` so the
app can see the system `python3-gi` package used by GTK/AppIndicator.
A microphone icon appears in your system tray. The first launch downloads the Whisper `small` model (~460 MB) — wait for the "Ready" notification.
## Run
### 3. Dictate
1. **Open a text editor** (gedit, VS Code, a terminal, a browser text field — anything).
2. **Click in the text field** so it has focus.
3. **Press `Ctrl+Super`** (Ctrl + Windows key) to start recording.
4. **Speak** your text naturally.
5. **Press `Ctrl`** to stop, transcribe, and type the result.
That's it. The transcribed text appears where your cursor was.
### 4. Try a rewrite workflow
```bash
blitztext tray # tray app with workflow menu, default installed mode
blitztext gui # GTK control panel
blitztext run # headless hotkey daemon
blitztext config-path # print config file path
blitztext --version # print app version
export OPENAI_API_KEY=sk-... # or point at a local LLM in Settings
```
From a source checkout, use:
1. Press `Ctrl+Super` → speak something rough like "hey john can you send me the report"
2. Press `Ctrl` — the text appears as a plain transcript.
Now try with voice routing:
1. Press `Ctrl+Alt+Space` → say **"nicer email** hey john can you send me the report"
2. Press `Ctrl` — Blitztext detects the keyword, runs the "Nicer email" rewrite, and types a polished email.
### 5. Explore Settings
Click the ⚙️ gear icon in the panel header, or right-click the tray → **Settings…**
- **Presets** — edit workflows, hotkeys, prompts, keywords
- **Engines** — manage STT and LLM backends, check online status, run tests
- **Input** — switch between modifier keys and direct hotkeys
- **General** — choose microphone, output mode, language, autostart
- **Benchmark** — compare STT engines with a reference WAV
- **Log** — inspect runtime messages
- **About** — version, changelog, license
---
## CLI Reference
```bash
blitztext tray # System tray + hotkeys (default)
blitztext gui # GTK control panel window
blitztext run # Headless daemon, hotkeys only
blitztext transcribe f.wav # One-shot transcription, prints text
blitztext config-path # Print config file location
blitztext --version # Print version
```
From a source checkout, prefix with `.venv/bin/python -m`:
```bash
cd linux
.venv/bin/python -m blitztext tray
.venv/bin/python -m blitztext gui
.venv/bin/python -m blitztext run
.venv/bin/python -m blitztext transcribe sample.wav
```
---
## Default Workflows
| Workflow | Mode | Default trigger | Result |
| --- | --- | --- | --- |
| Transcribe | `transcribe` | voice-routing default | Types the raw transcript |
| Nicer email | `rewrite` | `Ctrl+Alt+E` | Turns rough notes into a polished email |
| Improve text | `rewrite` | `Ctrl+Alt+I` | Cleans spelling, grammar, and wording |
| Calm down | `rewrite` | `Ctrl+Alt+C` | Rewrites frustrated speech into a calm message |
| Add emojis | `rewrite` | `Ctrl+Alt+J` | Keeps the text and adds fitting emojis |
| STT Streaming | `stream` | user-created / optional | Types live words from realtime STT |
| Hotkey | Workflow | Mode | What it does |
|---|---|---|---|
| `Ctrl+Alt+Space` | *(voice routing)* | auto | Routes to a preset by spoken keyword |
| `Ctrl+Alt+E` | Nicer email | `rewrite` | Turns rough speech into a polished email |
| `Ctrl+Alt+I` | Improve text | `rewrite` | Proofreads and improves wording |
| `Ctrl+Alt+C` | Calm down | `rewrite` | Rewrites frustrated speech into a calm message |
| `Ctrl+Alt+J` | Add emojis | `rewrite` | Adds fitting emojis to the text |
The default config uses voice-keyword routing as the primary input style. Press
the routing hotkey, speak normally, and optionally say a preset keyword such as
"nicer email" or "calm down" at the beginning or end of the utterance.
With the default `modifiers` input mode:
| Key | Action |
|---|---|
| `Ctrl+Super` | Start recording |
| `Ctrl` | Stop → transcribe → type |
| `Alt` | Stop → transcribe → type → press Enter |
| `Esc` | Cancel (discard recording) |
---
## Configuration
Blitztext writes its configuration to:
All settings live in `~/.config/blitztext/config.toml`. Edit through the Settings UI or directly as TOML.
```text
~/.config/blitztext/config.toml
```
You can edit it through **Settings** or directly as TOML. The settings window
contains:
- **Presets**: workflow name, icon, description, mode, hotkey, keywords, prompt,
model override, and temperature override.
- **Engines**: STT and LLM engine presets, status checks, and STT test.
- **Input**: modifier mode, direct hotkey mode, push-to-talk, microphone, output
method, typing delay, language, and quality gates.
- **General**: notifications, autostart, and app behavior.
- **Benchmark**: compare STT engines with a reference WAV and text file.
- **Log**: inspect runtime messages.
- **About**: version, changelog, source, license, and legal information.
## STT Engines
Local transcription uses `faster-whisper`:
### Local Whisper (batch STT)
```toml
[stt]
active = "Local faster-whisper"
[[stt_engine]]
name = "Local faster-whisper"
type = "local"
[whisper]
model = "small"
device = "auto"
compute_type = "auto"
model = "small" # tiny | base | small | medium | large-v3
device = "auto" # auto | cuda | cpu
compute_type = "auto" # auto | int8 | float16
beam_size = 5
```
Batch remote transcription uses OpenAI-compatible `/audio/transcriptions`
servers:
### Remote batch STT
```toml
[[stt_engine]]
@ -214,60 +253,25 @@ name = "faster-whisper-server"
type = "openai"
url = "http://localhost:8010/v1"
model = "Systran/faster-whisper-base"
api_key_env = ""
```
Realtime streaming uses a Riva/NIM realtime WebSocket server:
### Realtime STT streaming
```toml
[[stt_engine]]
name = "Nemotron ASR Streaming"
type = "riva_realtime"
url = "http://127.0.0.1:8006/v1"
model = ""
api_key_env = ""
```
Streaming engines are live-only. They are used by workflows with
`mode = "stream"`, not by batch transcription or the benchmark tab.
## Realtime STT Streaming
To use Nemotron ASR Streaming or another compatible Riva/NIM realtime server:
1. Start the realtime STT server.
2. Open **Settings > Engines**.
3. Click **+ Stream** to add the Nemotron ASR Streaming preset.
4. Save and restart Blitztext if prompted.
5. Create or edit a workflow with `mode = "stream"`.
Example:
```toml
[general]
language = "en-US"
[stt]
active = "Nemotron ASR Streaming"
[[workflow]]
name = "STT Streaming"
description = "Live words while you speak."
hotkey = "<ctrl>+<alt>+s"
mode = "stream"
```
The tested Nemotron ASR Streaming NIM exposes an English `en-US` model. For that
server, use `language = "en"` or `language = "en-US"`.
## Rewrite Engines
Rewrite workflows send the transcript to an OpenAI-compatible chat endpoint:
### LLM rewrite endpoint
```toml
[llm]
active = "Default"
[[llm_engine]]
name = "Default"
type = "cloud"
@ -277,7 +281,7 @@ api_key_env = "OPENAI_API_KEY"
temperature = 0.3
```
For local rewriting, point the URL at a local OpenAI-compatible server:
For local rewriting, point at a local server:
```toml
[[llm_engine]]
@ -286,81 +290,134 @@ type = "local"
url = "http://localhost:28080/v1"
model = "Qwen3.5-4B"
api_key_env = ""
temperature = 0.3
```
---
## Privacy
Blitztext App Linux does not include a hosted backend. Where your audio or text
goes depends on the engines you configure:
Blitztext does **not** include a hosted backend. Where your data goes depends on what you configure:
```text
Local STT: your desktop -> local faster-whisper
Remote batch STT: your desktop -> configured /audio/transcriptions endpoint
Realtime STT: your desktop -> configured Riva/NIM realtime endpoint
Rewrite workflows: your desktop -> configured OpenAI-compatible chat endpoint
Text delivery: Blitztext -> xdotool -> focused X11 window
```
Local STT: microphone → local faster-whisper (never leaves your machine)
Remote batch STT: microphone → your configured /audio/transcriptions endpoint
Realtime STT: microphone → your configured Riva/NIM realtime endpoint
Rewrite: transcript → your configured OpenAI-compatible chat endpoint
Delivery: text → xdotool → focused X11 window
```
Do not use remote endpoints with sensitive content unless you understand and
accept the data handling of those services. See [docs/privacy.md](docs/privacy.md)
for more detail.
API keys are stored as environment variable *names* in the config, never as values. See [docs/privacy.md](docs/privacy.md) for the full privacy model.
---
## Project Structure
```text
```
linux/
blitztext/ Linux app package: GTK UI, tray, daemon, STT, LLM, config
packaging/ Debian packaging, desktop file, app icons
README.md Detailed Linux usage guide
CHANGELOG.md Linux app changelog
docs/ Setup, privacy, roadmap, release, and project notes
BlitztextMac/ Legacy/upstream macOS reference code kept for context
README.md This Linux-first project overview
blitztext/ Python package: GTK UI, tray, daemon, STT, LLM, config
packaging/ Debian packaging, desktop entry, app icons
install.sh Venv setup script
requirements.txt Python dependencies
CHANGELOG.md Linux app changelog
README.md Detailed Linux usage guide
docs/ Setup, privacy, and project documentation
.github/ CI workflows, issue templates, Dependabot, secret scan
README.md ← you are here
```
## Development
---
Useful checks:
## Run on Login
### Autostart toggle (recommended)
Open **Settings → General → Launch on login** and enable it. This writes a freedesktop `.desktop` entry to `~/.config/autostart/`.
### systemd user service
```bash
mkdir -p ~/.config/systemd/user
cp linux/blitztext.service ~/.config/systemd/user/
# edit ExecStart if your checkout path differs
systemctl --user daemon-reload
systemctl --user enable --now blitztext
```
---
## Current Limitations
- **X11 only.** Text delivery uses `xdotool`. Wayland support (`wtype`/`ydotool`) is planned but not implemented.
- **No automated tests yet.** Contributions welcome (routing, quality gate, config parsing are all highly testable).
- **Realtime streaming** requires a compatible Riva/NIM server.
- **Local STT speed** depends on your hardware, Whisper model size, and CTranslate2 build (CPU `int8` by default).
- This is experimental software provided as-is.
---
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
**Good first contributions:**
- Add unit tests for `routing.py`, `quality.py`, `config.py`, `benchmark.py`
- Add Linux screenshots
- Improve error messages and first-run setup
- Document known-good STT/LLM engine configurations
- Add Wayland support via `wtype` or `ydotool`
**Quick development loop:**
```bash
cd linux
.venv/bin/python -m py_compile blitztext/*.py
.venv/bin/python -m blitztext --version
.venv/bin/python -m blitztext config-path
./install.sh
.venv/bin/python -m py_compile blitztext/*.py # syntax check
.venv/bin/python -m blitztext --version # smoke test
.venv/bin/python -m blitztext gui # run the GUI
```
Useful docs:
Please read [SECURITY.md](SECURITY.md) before reporting vulnerabilities.
- [linux/README.md](linux/README.md) for the detailed Linux guide.
- [docs/setup.md](docs/setup.md) for setup notes.
- [linux/CHANGELOG.md](linux/CHANGELOG.md) for app changes.
- [ROADMAP.md](ROADMAP.md) for planned work.
- [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
## Current Limits
- X11 is required for reliable text delivery through `xdotool`.
- Realtime streaming depends on a compatible Riva/NIM realtime server.
- Rewrite workflows depend on the LLM endpoint you configure.
- Local STT speed depends on your `faster-whisper` model, CPU/GPU, and
CTranslate2 build.
- This is experimental open-source software provided as-is.
---
## License
Code is released under the MIT License. See [LICENSE](LICENSE).
This project is released under the **MIT License**. See [LICENSE](LICENSE).
Project names, logos, and app icons are not automatically granted as trademarks
or brand assets. See [TRADEMARKS.md](TRADEMARKS.md).
```
MIT License
Copyright (c) 2026 Blitztext contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
Project names, logos, and app icons are not automatically granted as trademarks or brand assets. See [TRADEMARKS.md](TRADEMARKS.md).
---
## Legal / Impressum & Datenschutz
This is an experimental, non-commercial open-source project, provided as-is under
the MIT License without warranty or support. Nothing is sold here and no
installation or operation is performed on your behalf.
This is an experimental, non-commercial open-source project, provided as-is under the MIT License without warranty or support. Nothing is sold here and no installation or operation is performed on your behalf.
The companion website (blitztext.de) is operated by Blackboat Internet GmbH:
- Impressum: https://martin-bierschenk.de/impressum/
- Datenschutz / Privacy: https://martin-bierschenk.de/datenschutz/

View File

@ -249,7 +249,8 @@ class App:
for nm, r in self._rows.items():
cls(nm, None, "dim", "recording")
r["pill"].get_style_context().remove_class("rec")
r["pill"].set_text(pretty_hotkey(next(w.hotkey for w in self.cfg.workflows if w.name == nm)))
wf_match = next((w for w in self.cfg.workflows if w.name == nm), None)
r["pill"].set_text(pretty_hotkey(wf_match.hotkey) if wf_match else "")
return False # one-shot idle
# -- panel / settings / lifecycle -----------------------------------------

View File

@ -6,6 +6,7 @@ available — no Python audio bindings or device configuration required.
from __future__ import annotations
import os
import shutil
import signal
import subprocess
@ -42,7 +43,9 @@ class Recording:
"""A single in-progress recording. Start on construction, call stop()."""
def __init__(self, recorder: str, device: str = ""):
self._tmp = Path(tempfile.mkstemp(prefix="blitztext-", suffix=".wav")[1])
fd, path = tempfile.mkstemp(prefix="blitztext-", suffix=".wav")
os.close(fd)
self._tmp = Path(path)
argv = list(_RECORDERS[recorder])
flag = _DEVICE_FLAG.get(recorder, [])
if device and flag: