3 Commit-ok

Szerző SHA1 Üzenet Dátum
hariel1985
29d0c0e503 Update README for 1.3.0
Document the warm whisper-server, live mic-level HUD, and custom
dictionary; add them to the feature list and Settings; note the
loopback-only server and the one-time model load at startup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 16:02:20 +02:00
hariel1985
fdf8bf9743 Use English for all UI strings (HUD + dictionary)
The new HUD label and the dictionary table labels/placeholders were in
Hungarian; the rest of the app is English. Translated: "No audio input?",
"Recording…", "Dictionary (heard → replacement):", column titles
"Heard"/"Replacement", and the field placeholders/hint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 09:26:35 +02:00
hariel1985
aa241f55c8 Bump version to 1.3.0
Minor release: warm whisper-server backend (no per-dictation model
reload), live mic-level HUD with silence warning, and a custom
recognised->replacement dictionary that also biases the whisper prompt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 09:20:33 +02:00
4 fájl változott, egészen pontosan 24 új sor hozzáadva és 11 régi sor törölve

Fájl megtekintése

@@ -12,17 +12,26 @@ A simple menu bar app for voice dictation using OpenAI Whisper (local, offline).
## macOS ## macOS
### What's new in 1.3.0
-**Warm transcription server** — the Whisper model stays loaded in a background `whisper-server`, so each dictation is a fast local call (~0.5s) instead of reloading the 1.51.6 GB model on every utterance
- 📊 **Live mic-level HUD** while recording, which warns ("No audio input?") if the microphone stays silent
- 📖 **Custom dictionary** — an editable *Heard → Replacement* table that fixes names/jargon in the transcript and also biases recognition
### Features ### Features
- 🎤 Global hotkey (⌃⌥D) to start/stop recording - 🎤 Global hotkey (⌃⌥D) to start/stop recording
- ⚡ Fast transcription via a warm whisper-server (model stays loaded — no per-dictation reload), with automatic fallback to `whisper-cli`
- 📊 Live mic-level meter while recording, with a "no audio input" warning
- 📖 Custom dictionary (heard → replacement) that also biases recognition
- 🔒 Fully offline - uses local Whisper model - 🔒 Fully offline - uses local Whisper model
- Automatic paste into any focused app - ⌨️ Automatic paste into any focused app
- 📋 Clipboard preservation - your copied content is restored after paste - 📋 Clipboard preservation - your copied content is restored after paste
- ⚙️ Settings window with model selection dropdown - ⚙️ Settings window with model selection dropdown
- 📥 Built-in model downloader with progress indicator - 📥 Built-in model downloader with progress indicator
- 🚀 Launch at login support - 🚀 Launch at login support
- 🔊 Sound feedback (optional) - 🔊 Sound feedback (optional)
- 📦 Self-contained - whisper-cli bundled in app - 📦 Self-contained - whisper-cli and whisper-server bundled in the app
### Requirements ### Requirements
@@ -75,6 +84,7 @@ make install
Click the menu bar icon → Settings to configure: Click the menu bar icon → Settings to configure:
- **Language**: Auto-detect or 31 supported languages - **Language**: Auto-detect or 31 supported languages
- **Model**: Select from installed models or download new ones - **Model**: Select from installed models or download new ones
- **Dictionary**: Add *Heard → Replacement* pairs to fix names/jargon (use `\n` for a newline); the replacement terms also improve recognition
- **Sound feedback**: Toggle audio feedback on/off - **Sound feedback**: Toggle audio feedback on/off
- **Launch at login**: Start automatically when you log in - **Launch at login**: Start automatically when you log in
@@ -93,6 +103,8 @@ Download models directly from the app or manually:
Models are stored in `~/.whisper-models/` Models are stored in `~/.whisper-models/`
> With the warm server (1.3.0+), the selected model loads once at startup (a few seconds); after that each dictation skips the reload, so the speeds above are roughly the per-dictation transcription time.
### Audio Feedback ### Audio Feedback
- 🔔 **Tink** - Recording started - 🔔 **Tink** - Recording started
@@ -114,6 +126,7 @@ Grant these in System Settings → Privacy & Security:
## Security ## Security
- All processing is done locally - no data leaves your device - All processing is done locally - no data leaves your device
- The bundled `whisper-server` is bound to `127.0.0.1` (loopback only) and is never exposed on the network
- Audio files are stored in private temp directory and deleted after transcription - Audio files are stored in private temp directory and deleted after transcription
- Clipboard is cleared after paste (transcript doesn't remain accessible) - Clipboard is cleared after paste (transcript doesn't remain accessible)
- Original clipboard content is preserved and restored after paste - Original clipboard content is preserved and restored after paste

Fájl megtekintése

@@ -2,7 +2,7 @@
APP_NAME = WhisperDictate APP_NAME = WhisperDictate
APP_BUNDLE = $(APP_NAME).app APP_BUNDLE = $(APP_NAME).app
VERSION = 1.2.1 VERSION = 1.3.0
# Directories # Directories
SRC_DIR = src SRC_DIR = src

Fájl megtekintése

@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2.1</string> <string>1.3.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2</string> <string>3</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>

Fájl megtekintése

@@ -328,7 +328,7 @@ final class MeterView: NSView {
NSColor(calibratedWhite: 0.10, alpha: 0.93).setFill() NSColor(calibratedWhite: 0.10, alpha: 0.93).setFill()
bg.fill() bg.fill()
let label = silent ? "🎤 Nincs hangbemenet?" : "🎤 Felvétel" let label = silent ? "🎤 No audio input?" : "🎤 Recording"
label.draw(at: NSPoint(x: 16, y: bounds.height - 26), withAttributes: [ label.draw(at: NSPoint(x: 16, y: bounds.height - 26), withAttributes: [
.font: NSFont.systemFont(ofSize: 12, weight: .medium), .font: NSFont.systemFont(ofSize: 12, weight: .medium),
.foregroundColor: silent ? NSColor.systemRed : NSColor.white .foregroundColor: silent ? NSColor.systemRed : NSColor.white
@@ -729,7 +729,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate,
contentView.addSubview(loginCheck) contentView.addSubview(loginCheck)
// Custom dictionary (recognised -> replacement) // Custom dictionary (recognised -> replacement)
let dictLabel = NSTextField(labelWithString: "Szótár (felismert → csere):") let dictLabel = NSTextField(labelWithString: "Dictionary (heard → replacement):")
dictLabel.frame = NSRect(x: 20, y: 304, width: 300, height: 20) dictLabel.frame = NSRect(x: 20, y: 304, width: 300, height: 20)
contentView.addSubview(dictLabel) contentView.addSubview(dictLabel)
@@ -740,9 +740,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate,
table.usesAlternatingRowBackgroundColors = true table.usesAlternatingRowBackgroundColors = true
table.rowHeight = 22 table.rowHeight = 22
let colFrom = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("from")) let colFrom = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("from"))
colFrom.title = "Felismert"; colFrom.width = 195; colFrom.isEditable = true colFrom.title = "Heard"; colFrom.width = 195; colFrom.isEditable = true
let colTo = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("to")) let colTo = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("to"))
colTo.title = "Helyette"; colTo.width = 195 colTo.title = "Replacement"; colTo.width = 195
table.addTableColumn(colFrom) table.addTableColumn(colFrom)
table.addTableColumn(colTo) table.addTableColumn(colTo)
table.dataSource = self table.dataSource = self
@@ -760,7 +760,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate,
delBtn.bezelStyle = .rounded delBtn.bezelStyle = .rounded
contentView.addSubview(delBtn) contentView.addSubview(delBtn)
let dictHint = NSTextField(labelWithString: "A „Helyette” oszlop a felismerést is pontosítja. \\n = új sor.") let dictHint = NSTextField(labelWithString: "The Replacement column also improves recognition. \\n = newline.")
dictHint.frame = NSRect(x: 100, y: 74, width: 330, height: 18) dictHint.frame = NSRect(x: 100, y: 74, width: 330, height: 18)
dictHint.font = NSFont.systemFont(ofSize: 10) dictHint.font = NSFont.systemFont(ofSize: 10)
dictHint.textColor = .tertiaryLabelColor dictHint.textColor = .tertiaryLabelColor
@@ -882,7 +882,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, URLSessionDownloadDelegate,
field.font = NSFont.systemFont(ofSize: 12) field.font = NSFont.systemFont(ofSize: 12)
field.lineBreakMode = .byTruncatingTail field.lineBreakMode = .byTruncatingTail
field.delegate = self field.delegate = self
field.placeholderString = id.rawValue == "from" ? "felismert szó" : "csere (\\n = új sor)" field.placeholderString = id.rawValue == "from" ? "heard word" : "replacement (\\n = newline)"
} }
field.stringValue = replacements[row][id.rawValue] ?? "" field.stringValue = replacements[row][id.rawValue] ?? ""
return field return field