docs: fix spec issues from review

This commit is contained in:
beo3000 2026-03-20 11:15:07 +01:00
parent ebfd751e8d
commit 82ef96ea09
1 changed files with 40 additions and 20 deletions

View File

@ -52,7 +52,8 @@ def _app_dir() -> str:
return os.path.dirname(sys.executable) return os.path.dirname(sys.executable)
else: else:
# Dev mode: use script directory (git repo root) # Dev mode: use script directory (git repo root)
return os.path.dirname(os.path.abspath(__file__ + "/../../")) # config.py lives at whisper_app/config.py → two levels up = repo root
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
``` ```
Machine-local config (`config_local.json`) continues to use `%LOCALAPPDATA%\WhisperDictation` (Windows) or `~/.local/share/WhisperDictation` (Linux) — unchanged. Machine-local config (`config_local.json`) continues to use `%LOCALAPPDATA%\WhisperDictation` (Windows) or `~/.local/share/WhisperDictation` (Linux) — unchanged.
@ -62,21 +63,25 @@ Machine-local config (`config_local.json`) continues to use `%LOCALAPPDATA%\Whis
`app.py` owns a `queue.Queue[str]` and a pre-queue buffer list. `app.py` owns a `queue.Queue[str]` and a pre-queue buffer list.
```python ```python
_log_buffer: list[str] = [] # before queue is ready _log_buffer: list[str] = [] # before queue is ready, capped at 500 entries
_log_queue: queue.Queue | None = None _log_queue: queue.Queue | None = None
_log_lock = threading.Lock()
def log(msg: str) -> None: def log(msg: str) -> None:
if _log_queue is not None: with _log_lock:
_log_queue.put(msg) if _log_queue is not None:
else: _log_queue.put(msg)
_log_buffer.append(msg) else:
_log_buffer.append(msg)
def set_log_queue(q: queue.Queue) -> None: def set_log_queue(q: queue.Queue) -> None:
global _log_queue global _log_queue
_log_queue = q with _log_lock:
for msg in _log_buffer: _log_queue = q
q.put(msg) buffered = list(_log_buffer)
_log_buffer.clear() _log_buffer.clear()
for msg in buffered: # flush outside the lock to avoid deadlock
q.put_nowait(msg)
``` ```
All `print()` calls in all modules are replaced with `app.log()`. All `print()` calls in all modules are replaced with `app.log()`.
@ -106,6 +111,7 @@ Opened via tray icon left-click or "Anzeigen" menu item. Implemented in `log_win
- Close button → `withdraw()` (does not quit the app) - Close button → `withdraw()` (does not quit the app)
- 🗑 button → clears the text widget - 🗑 button → clears the text widget
- Queue polled via `root.after(100, _poll_log_queue)` - Queue polled via `root.after(100, _poll_log_queue)`
- The tkinter `root` (hidden) is always alive as long as the tray runs — `withdraw()` on the log window does not trigger mainloop exit. The app exits only via the tray "Beenden" menu item.
## Settings Window — Installation Section ## Settings Window — Installation Section
@ -117,9 +123,10 @@ Each integration shows status ("eingerichtet" / "nicht eingerichtet") and two bu
|---|---|---| |---|---|---|
| Autostart beim Login | `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` | `~/.config/autostart/whisper-dictation.desktop` | | Autostart beim Login | `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` | `~/.config/autostart/whisper-dictation.desktop` |
| Startmenü-Eintrag | `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Whisper Dictation.lnk` | `~/.local/share/applications/whisper-dictation.desktop` | | Startmenü-Eintrag | `%APPDATA%\Microsoft\Windows\Start Menu\Programs\Whisper Dictation.lnk` | `~/.local/share/applications/whisper-dictation.desktop` |
| Desktop-Verknüpfung | `%USERPROFILE%\Desktop\Whisper Dictation.lnk` | `~/Desktop/whisper-dictation.desktop` | | Desktop-Verknüpfung | `%USERPROFILE%\Desktop\Whisper Dictation.lnk` | XDG: `xdg-user-dir DESKTOP` (fallback: `~/Desktop`) |
Windows `.lnk` files are created via `pywin32` (`win32com.client.Dispatch("WScript.Shell")`). Windows `.lnk` files are created via `pywin32` (`win32com.client.Dispatch("WScript.Shell")`).
`import win32com` must be guarded: `if sys.platform == "win32"` — top-level import is forbidden to avoid import errors on Linux.
**Only available when running as a frozen binary.** In dev mode, the buttons are disabled with a tooltip "Nur im gebauten Binary verfügbar". **Only available when running as a frozen binary.** In dev mode, the buttons are disabled with a tooltip "Nur im gebauten Binary verfügbar".
@ -141,22 +148,25 @@ a = Analysis(
'ctranslate2', 'ctranslate2',
'faster_whisper', 'faster_whisper',
'sounddevice', 'sounddevice',
'pynput.keyboard._win32', # Windows 'pynput.keyboard._win32', # Windows
'pynput.keyboard._xorg', # Linux 'pynput.keyboard._xorg', # Linux X11
], 'pynput.keyboard._uinput', # Linux Wayland
datas=[
('config.json', '.'),
('vocabulary.json', '.'),
], ],
datas=[], # config.json / vocabulary.json NOT bundled — see below
) )
exe = EXE(a.pure, ..., console=False, icon='icon.ico') pyz = PYZ(a.pure)
exe = EXE(pyz, a.scripts, ..., console=False, icon='icon.ico')
coll = COLLECT(exe, a.binaries, a.datas, name='whisper-dictation')
``` ```
**config.json / vocabulary.json are NOT bundled via `datas`.**
They are user-editable files that live next to the binary. `build.py` copies them from the repo root into `dist/whisper-dictation/` after the PyInstaller run. This is the single authoritative location in frozen mode (`os.path.dirname(sys.executable)`).
### `build.py` ### `build.py`
1. Generates `icon.ico` from PIL 1. Generates `icon.ico` from PIL (must run before PyInstaller — `.spec` references it)
2. Runs PyInstaller with the `.spec` file 2. Runs PyInstaller with the `.spec` file
3. Copies `config.json` and `vocabulary.json` into `dist/whisper-dictation/` 3. Copies `config.json` and `vocabulary.json` into `dist/whisper-dictation/` **only if they don't already exist there** (to avoid overwriting user edits)
### Platform requirement ### Platform requirement
@ -172,6 +182,16 @@ PyInstaller cannot cross-compile. **Build must be run separately on each platfor
Added to `requirements-windows.txt`. Not required on Linux. Added to `requirements-windows.txt`. Not required on Linux.
## UI Language
All UI labels are in German. "WHISPER DICTATION" in the log panel header is the product name and stays as-is. All other UI text (buttons, section headers, tooltips) is German.
## Implementation Notes
- **Startup crash visibility:** With `console=False`, crashes before the tray appears produce no visible output. Implementer should wrap `main()` in a try/except that writes to a logfile (e.g., `%LOCALAPPDATA%\WhisperDictation\error.log`) as a last resort.
- **pynput hidden imports:** Only keyboard backends are needed (`_win32`, `_xorg`, `_uinput`). No mouse backends required — hotkeys are keyboard-only.
- **`_log_buffer` cap:** Enforce max 500 entries; if buffer exceeds cap, drop oldest entry before appending.
## Out of Scope ## Out of Scope
- Code signing / notarization - Code signing / notarization