Commit Graph
615 Commits
Author SHA1 Message Date
iamdoubz 1dd9d57b0f feat(ui): icon-based formatting toolbar for the notes editor
Bold/Italic/Heading1/Heading2/List/ListChecks Lucide icons replace the
letter/glyph toolbar buttons (bullet "•" and checkbox "☑" in
particular); export buttons get matching icons. Section labels for
Transcript/Notes get icons too, and the reprocess spinner animates
(respecting prefers-reduced-motion).
2026-07-02 12:57:19 -05:00
iamdoubz 9052d69912 feat(ui): polish the meetings list — icons, accent colors, hover states
Search icon inside the input, Trash2/Download/RotateCcw replacing
emoji, tag chips and the selected-item state now use the semantic
accent tokens instead of a hardcoded blue hex, and list rows get a
hover state + an accent-colored left border when selected.
2026-07-02 12:57:09 -05:00
iamdoubz d50342d865 feat(ui): rebuild header + design tokens
- Full semantic color token system (surfaces, text, borders, one
  accent color) replacing the previous 4-variable set — light/dark
  designed together, every pairing checked to >=4.5:1 (WCAG AA).
- Global custom scrollbar styling (thin, theme-aware) — the default
  OS scrollbar looked out of place against the app's own chrome.
- ThemeToggle replaces the plain theme <select>.
- Record/Stop buttons and the settings/warning icons are now Lucide
  SVG icons instead of Unicode glyphs (●■⚙⚠); the recording indicator
  is an animated pulsing dot (paused under prefers-reduced-motion)
  instead of a static red dot.
- Elevation (shadow tokens) and consistent radius scale.
2026-07-02 12:57:00 -05:00
iamdoubz 7ae146f99f feat(ui): add ThemeToggle — segmented Monitor/Sun/Moon control
Replaces the plain <select> theme dropdown with three icon buttons.
Defaults to "system" (Monitor), matching the existing behavior of
following the OS preference until the user picks an explicit override.
2026-07-02 12:56:51 -05:00
iamdoubz 4c40ebc939 feat(ui): add @lucide/svelte for consistent SVG icons
Replaces ad-hoc emoji/Unicode-glyph icons (record/stop dots, trash,
checkbox, bell, etc.) throughout the UI — explicitly recommended by
the ui-ux-pro-max design skill over font-dependent emoji icons, which
render inconsistently across systems and can't be themed/sized via CSS.
2026-07-02 12:56:34 -05:00
iamdoubz 09db187f13 Merge pull request 'Hermes 008' (#8) from hermes-008 into main
Reviewed-on: #8
2026-07-02 12:05:55 -05:00
iamdoubz 0f7526770d feat(ui): due-date input + remind-me checkbox per action item (T8.6, FR-CAL-5)
The due-date input is new too — action items previously had no way to
set one, which would have made the reminder checkbox unusable (a
reminder needs a due date to schedule against).
2026-07-02 11:59:34 -05:00
iamdoubz f27c19d8de feat(ui): add ActionItem.reminder_set to the TS type (T8.6, FR-CAL-5) 2026-07-02 11:59:27 -05:00
iamdoubz e04e95d6da docs: document ActionItem.reminder_set and confirm_action_items' reminder side effect (T8.6, FR-CAL-5) 2026-07-02 11:59:22 -05:00
iamdoubz 8a28854250 feat(reminders): schedule/cancel from confirm_action_items (T8.6, FR-CAL-5) 2026-07-02 11:59:15 -05:00
iamdoubz 7f97328a2d feat(reminders): persist reminder_set; save_action_items returns saved rows (T8.6, FR-CAL-5)
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.
2026-07-02 11:59:09 -05:00
iamdoubz 994ace4b3f feat(reminders): initialize reminder_set: false on drafted action items (T8.6) 2026-07-02 11:59:02 -05:00
iamdoubz 47706cc0f3 feat(reminders): add ActionItem.reminder_set field (T8.6, FR-CAL-5) 2026-07-02 11:58:56 -05:00
iamdoubz 26ad920182 feat(reminders): register module; init + startup reconcile (T8.6, FR-CAL-5)
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).
2026-07-02 11:58:50 -05:00
iamdoubz f6e0de5486 feat(reminders): schedule/cancel via Windows scheduled toast notifications (T8.6, FR-CAL-5)
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.
2026-07-02 11:58:44 -05:00
iamdoubz 5b3ebb6cf0 feat(reminders): add windows-rs WinRT notification features (T8.6, FR-CAL-5)
Win32_UI_Shell (AppUserModelID), UI_Notifications + Data_Xml_Dom +
Foundation + Foundation_Collections (scheduled toast notifications and
IVectorView iteration for GetScheduledToastNotifications).
2026-07-02 11:58:34 -05:00
iamdoubz 5f033e90f2 feat(ui): bulk export button, reusing the tag/date filter state (T8.5, FR-STORE-4) 2026-07-02 11:35:46 -05:00
iamdoubz 3a4b1a5d2f feat(ui): add bulkExportMeetings client wrapper (T8.5, FR-STORE-4) 2026-07-02 11:35:41 -05:00
iamdoubz 368763cb1d docs: add bulk_export_meetings contract (T8.5, FR-STORE-4) 2026-07-02 11:35:36 -05:00
iamdoubz 10f0e31903 feat(export): register bulk_export_meetings command (T8.5, FR-STORE-4) 2026-07-02 11:35:31 -05:00
iamdoubz 5a5afa51e4 feat(export): add bulk_export_meetings (T8.5, FR-STORE-4)
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.
2026-07-02 11:35:24 -05:00
iamdoubz db838d9741 feat(ui): note-template picker before starting a recording (T8.1, FR-NOTE-5) 2026-07-02 11:26:20 -05:00
iamdoubz 21d2b40961 feat(ui): thread templateId through recording.start() (T8.1, FR-NOTE-5) 2026-07-02 11:26:14 -05:00
iamdoubz 181e874a2a feat(ui): add NoteTemplate type, listNoteTemplates, templateId param (T8.1, FR-NOTE-5) 2026-07-02 11:26:09 -05:00
iamdoubz 04628bcb46 docs: document template_id column and list_note_templates/start_recording (T8.1, FR-NOTE-5) 2026-07-02 11:26:05 -05:00
iamdoubz 31e644a671 test: fix calendar_link_smoke_test for NewMeeting.template_id (T8.1) 2026-07-02 11:25:59 -05:00
iamdoubz 9a2177dd62 feat(templates): register list_note_templates command (T8.1, FR-NOTE-5) 2026-07-02 11:25:53 -05:00
iamdoubz 2f56891d40 feat(templates): apply templates at all 4 notes-render call sites (T8.1, FR-NOTE-5)
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.
2026-07-02 11:25:48 -05:00
iamdoubz 6b5b359438 feat(templates): persist template_id; finalize_meeting returns it (T8.1, FR-NOTE-5)
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.
2026-07-02 11:25:41 -05:00
iamdoubz f63076ad06 feat(templates): NoteTemplate catalog + apply in to_markdown (T8.1, FR-NOTE-5)
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.
2026-07-02 11:25:34 -05:00
iamdoubz 67354642ab feat(templates): add meetings.template_id column (T8.1, FR-NOTE-5)
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.
2026-07-02 11:25:27 -05:00
iamdoubz fd220641ac feat(ui): add Export PDF / Export Word buttons (T8.4, FR-NOTE-4) 2026-07-02 11:03:01 -05:00
iamdoubz 7b03b12f25 feat(ui): extend exportMeeting's format type to include pdf/docx (T8.4, FR-NOTE-4) 2026-07-02 11:02:57 -05:00
iamdoubz 06bdf6d469 feat(export): wire pdf/docx formats into export_meeting (T8.4, FR-NOTE-4) 2026-07-02 11:02:52 -05:00
iamdoubz 0425f64cfe feat(export): DOCX renderer via docx-rs (T8.4, FR-NOTE-4)
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.
2026-07-02 11:02:46 -05:00
iamdoubz ec7ae5c01f feat(export): PDF renderer via printpdf + built-in Helvetica (T8.4, FR-NOTE-4)
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.
2026-07-02 11:02:41 -05:00
iamdoubz f6a5b9583a feat(export): parse notes Markdown into a block model for PDF/Word (T8.4, FR-NOTE-4)
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).
2026-07-02 11:02:35 -05:00
iamdoubz 3dec299c4c feat(export): add printpdf/docx-rs/pulldown-cmark deps (T8.4, FR-NOTE-4)
Pure-Rust PDF/Word generation and Markdown parsing — no external
binary or cloud conversion service, consistent with the fully-local
invariant.
2026-07-02 11:02:29 -05:00
iamdoubz 8f08b17be6 feat(ui): editable tags section in the meeting detail panel (T8.3, FR-SEARCH-2) 2026-07-02 10:38:59 -05:00
iamdoubz bf74a3cc92 feat(ui): tag/date filters and tag chips in the meetings list (T8.3, FR-SEARCH-2) 2026-07-02 10:38:54 -05:00
iamdoubz eb17cc68e0 feat(ui): add filter/tag state and setTags/loadTags to the meetings store (T8.3, FR-SEARCH-2) 2026-07-02 10:38:50 -05:00
iamdoubz 15f72aaca6 feat(ui): add MeetingFilter type + setTags/listTags wrappers (T8.3, FR-SEARCH-2) 2026-07-02 10:38:45 -05:00
iamdoubz d41ecc5106 docs: update list_meetings signature; add list_tags contract (T8.3, FR-SEARCH-2)
Drops limit/offset (never implemented, no pagination need yet at local
meeting counts) in favor of from/to date filters, matching FR-SEARCH-2's
"filter by date, tag, or participant" and what was actually built.
2026-07-02 10:38:41 -05:00
iamdoubz e6e209e1c4 feat(tags): register set_tags/list_tags commands (T8.3, FR-SEARCH-2) 2026-07-02 10:38:36 -05:00
iamdoubz d1ca2f63dd feat(tags): add set_tags/list_tags commands; extend list_meetings (T8.3, FR-SEARCH-2) 2026-07-02 10:38:31 -05:00
iamdoubz fc6a2fa0aa feat(tags): wire tags + list filters (T8.3, FR-SEARCH-2)
tags/meeting_tags existed since migration 0001, completely unused.
Adds upsert_tag/tags_for_meeting (mirroring upsert_participant's
find-or-create pattern) and real set_tags/list_tags implementations.

Extends list_meetings with tag/participant/date filters via a new
MeetingFilter struct, replacing the old query-only signature — the
participant filter matches either a speaker mapped to that participant
or an attendee of the meeting's linked calendar event, since neither
signal alone covers "who was in this meeting." Tags are populated on
Meeting/MeetingListItem/SearchHit via a per-row lookup (N+1, but
meeting counts are small enough that this is simpler than GROUP_CONCAT
string-splitting).
2026-07-02 10:38:26 -05:00
iamdoubz c2d282e5ba feat(tags): add tags fields to MeetingListItem/SearchHit (T8.3, FR-SEARCH-2) 2026-07-02 10:38:19 -05:00
iamdoubz 66d4c614bb feat(ui): replace title-only filter with full-text search (T8.2, FR-SEARCH-1)
The search box now searches transcripts + notes (not just titles) and
shows a matched snippet per result. FTS5 is word-based rather than
substring-based, a deliberate tradeoff for gaining full-content search.
2026-07-02 10:08:12 -05:00
iamdoubz 3d1129ff1e feat(ui): add search state + method to the meetings store (T8.2, FR-SEARCH-1) 2026-07-02 10:08:02 -05:00
iamdoubz 354588e1ab feat(ui): add SearchHit type + search() client wrapper (T8.2, FR-SEARCH-1) 2026-07-02 10:07:55 -05:00