whisper.cpp's encoder always runs over a full, padded 30s mel window
(1500 encoder positions) unless audio_ctx is explicitly reduced. The
~4s live-transcription streaming windows were never setting it, so
every window paid the full 30s-equivalent encode cost on one serial
worker thread -- the actual cause of the reported 30-65s lag between
speaking and a segment appearing, not "CPU is just slow".
Scales audio_ctx proportionally to the window's real sample count
instead (audio_ctx_for_window), floored and capped to whisper.cpp's
own valid range. Verified with a controlled A/B against the real
base.en-q5_1 model: ~30% faster even under heavy concurrent CPU
contention on this machine, which is masking most of the improvement
--the fix targets the encoder's O(n^2)-ish attention cost specifically
(1500 -> ~201 positions for a 4s window), so the gain on an otherwise-
idle machine should be substantially larger.
Same speech-bubble-with-sparkles mark, now on its own gradient purple
background instead of a transparent one — cropped from the refined
logo source (mark + gradient, no wordmark text) rather than isolated
via color-keying this time, since the new source is already composed
as a clean standalone icon tile.
Cropped the speech-bubble-with-sparkles mark from the new
WhispAssist logo/promo graphic (not the full graphic — text and
illustration detail don't read at 16-32px), color-keyed its background
to transparent, and regenerated the whole icon set via `tauri icon`
(title bar, taskbar, system tray, installer). Removed the iOS/Android/
Appx/macOS outputs tauri icon generates by default — this project only
targets Windows.
reminder_set was accepted on the model but hard-coded to 0 on insert
and never included in the UPDATE. save_action_items's return type
changes from () to Vec<ActionItem> so the caller (which needs the row
id to key reminder scheduling) doesn't need a second round-trip for
newly-inserted items. Also adds list_pending_reminders for startup
reconciliation.
Reschedules anything Windows' own toast schedule lost track of
(uninstall/reinstall, a changed AppUserModelID) — safe to run every
startup since schedule() is idempotent (cancels any existing entry for
the same action item id first).
Chosen over an app-side polling timer specifically to honor NFR-RES-1
("no polling timers running when not recording") — Windows itself
delivers the toast at due_at, and a scheduled toast is cleared by the
OS after it fires, so there's no "already fired" bookkeeping needed in
this app either. Windows-only, matching hardware::dxgi's pattern: the
real WinRT implementation lives in a #[cfg(windows)] submodule behind
plain top-level functions.
Verified against the live Windows toast API (not just compiled): a
throwaway example scheduled a reminder, confirmed
GetScheduledToastNotifications() reported it back with the correct id,
then confirmed cancel() removed it.
Extracts export_meeting_to as a shared helper (was duplicated inline
in export_meeting) so single and bulk export share identical
fetch+render logic. bulk_export_stem sanitizes+suffixes filenames with
an id prefix — meeting titles are very often duplicates ("Untitled
meeting") and would otherwise silently overwrite each other in a
batch. A meeting that fails to export is skipped rather than aborting
the whole run; also fixes export_meeting's doc comment, stale since
T8.4 added pdf/docx.
Adds StartRecordingArgs.template_id + list_note_templates command;
renames built_in_template to summary_prompt_bias to disambiguate it
from the new NoteTemplate concept (same meeting-type identifiers,
different purpose). Also fixes refresh_notes_and_notify (the
rename/merge-speaker refresh path) to route through
Store::update_notes instead of a raw std::fs::write — it was bypassing
T8.2's FTS reindexing, a gap missed there because this call site uses
meeting_dir(meeting_id) without the & that my earlier grep matched on.
NewMeeting/Meeting gain template_id; finalize_meeting now returns it
(fetched via one cheap scalar SELECT) so callers that only have
segments/speakers in memory (not a fetched Meeting) can resolve the
template without a separate round-trip.
4 built-in templates (Standup, 1:1, Sales Call, Retro), keyed by the
same meeting-type ids commands::summary_prompt_bias already uses for
LLM-prompt biasing — one identifier space shared by both, but two
independent lookups (structuring notes.md vs. biasing a summary
prompt). Section headers are prepended as an empty scaffold above the
auto-rendered speaker dialogue.
Templates themselves are a built-in Rust catalog, not a DB table —
this just remembers which one a meeting picked, so re-rendering
notes.md on reprocess/resume keeps the same section structure.
Bullet items get a literal "• " prefix rather than a real Word
numbered-list definition — this is read-only generated content, not
something the user continues typing into, so numbering-continuity
machinery isn't worth the extra API surface.
Character-count word wrap (printpdf's builtin fonts expose no glyph
metrics) and per-boldness-run line groups (a bold speaker-name prefix
gets its own line above the dialogue) — simple choices that keep the
renderer self-contained with no bundled font asset.
Fills in the ExportFormat::Pdf/Docx arms that previously errored
"lands in Phase 8". markdown_to_blocks() covers headings, paragraphs,
bullet/task lists, and bold runs — everything MarkdownNotes::to_markdown
and the notes editor's toolbar actually produce, not general Markdown.
Shared by both new renderer submodules (pdf, docx).