* fix: audio.sounds template — add missing transcribed/submit and align defaults
The settings.toml template served by `toml_sections.py` (used by the web UI
and at section-save time) was missing the `[audio.sounds.transcribed]` and
`[audio.sounds.submit]` sections, and the commented default values for
several other sections did not match the actual application defaults
(e.g. `start`/`stop` showed `volume = 1.0` instead of `0.3`, `transcribing`
showed `enabled = true` instead of `false`).
Each section now also documents every available field (`enabled`, `path`,
`volume`, `focus_gated`) so users can discover the focus-gated behavior of
`transcribed` (pencil-write) and `submit` (typewriter-burst) without
reading the source.
Bump to 0.2.8.
* refactor: single source of truth for audio.sounds template
The TOML template for `[audio.sounds.*]` lived in two places: in the live
form (uncommented headers, served to the web UI) inside
`toml_sections.py`, and in an all-commented hardcoded copy inside
`create_default_config()` in `config.py`. Drift between the two was
silent — for example, the section template gained `transcribed`/`submit`
defaults that the initial-file template lacked, and `volume` defaults
diverged from the application defaults in `_default_sounds()`.
This change introduces `get_commented_section(name)` in `toml_sections.py`
which derives the all-commented form by prefixing every TOML header line
with `# `. `create_default_config()` now uses that helper for the
`audio.sounds` block, so any future change to the live template
propagates automatically to the initial-file template.
The pattern is intentionally narrow to one section in this PR — the
remaining duplicated sections (`audio.advanced`, `stt.advanced`,
`pipeline.*`, etc.) can be migrated in follow-ups using the same helper.
Bump to 0.3.0 (minor — no behavior change, refactor of generated content
path that could affect anyone using `create_default_config()` for the
first time after the bump).
* fix: brew install wins in python_path self-healing
`ensure_python_path()` previously trusted `sys.executable` unconditionally
as the new source of truth. That works for the intended case (brew upgrade
bumps the Cellar version), but turns into a self-corrupting failure mode
whenever a stray `python -m dictare …` runs from a non-brew interpreter
that happens to have dictare installed — most commonly an editable install
in pyenv left over from a past dev workflow.
Symptom: STT (and TTS) silently fall back to CPU after a launchd restart,
because `~/.dictare/python_path` was rewritten to a python that lacks
`mlx-whisper`. The user notices days or weeks later via `dictare status`.
Fix: when a Homebrew installation is detected (by resolving `dictare`
on PATH and matching the Cellar layout), the brew venv's python is
pinned regardless of which interpreter is currently running. The brew
prefix is not hard-coded — works for `/opt/homebrew`, `/usr/local`, and
custom installs. Machines without a brew install keep the previous
self-healing behavior (so dev workflows that explicitly want their own
interpreter via `dictare service install` from a venv still work).
New helper `find_brew_python()` encapsulates the discovery so other
callers can reuse it. Twenty new tests cover the matrix: brew detected
on Apple Silicon / Intel / custom prefix, dev venv not misdetected,
pyenv path not misdetected, missing python file, first install, and
dev-mode fallback when no brew is present.