Skip to content

feat: Add support for text snippets - #150

Merged
jatinkrmalik merged 6 commits into
VocaHQ:mainfrom
Mr-Sunglasses:feat/snippets
Aug 23, 2026
Merged

feat: Add support for text snippets#150
jatinkrmalik merged 6 commits into
VocaHQ:mainfrom
Mr-Sunglasses:feat/snippets

Conversation

@Mr-Sunglasses

@Mr-Sunglasses Mr-Sunglasses commented Jun 1, 2026

Copy link
Copy Markdown
Member

fix: #149

Now user can add snippets and then when they dictate the phase, it is replaced by the snippet.

Demo

Screenshot 2026-06-01 at 4 59 52 PM
output.mp4

AI and LLM's Assistance

I've used Gemini CLI to assist with this issue, but I personally reviewed all the generated changes before submitting the PR. I also added tests to cover the code introduced in this contribution.

Model used: Auto -- Gemini CLI selects the best model for each task automatically, choosing between gemini-3.1-pro and gemini-3-flash.

@github-actions github-actions Bot added the app label Jun 1, 2026

@jatinkrmalik jatinkrmalik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mr-Sunglasses Solid first cut at snippet expansion, and the longest-trigger-first sort plus the escapedTemplate guard show you anticipated the obvious regex traps.

A few things to tighten before merge: the \b word-boundary pattern silently drops triggers ending in punctuation, the expansion logic sits in AppState rather than a service, and the bundled ServiceTests.swift CI-skip/timeout changes look unrelated and probably belong in their own PR. Details inline.

Comment thread Sources/VocaMac/Models/AppState.swift Outdated
Comment thread Sources/VocaMac/Models/AppState.swift Outdated
Comment thread Sources/VocaMac/Models/AppState.swift Outdated
Comment thread Sources/VocaMac/Models/AppState.swift Outdated
Comment thread Sources/VocaMac/Views/SettingsView.swift Outdated
Comment thread Tests/VocaMacTests/ServiceTests.swift Outdated
Comment thread Tests/VocaMacTests/SnippetTests.swift
@Mr-Sunglasses

Copy link
Copy Markdown
Member Author

@Mr-Sunglasses Solid first cut at snippet expansion, and the longest-trigger-first sort plus the escapedTemplate guard show you anticipated the obvious regex traps.

A few things to tighten before merge: the \b word-boundary pattern silently drops triggers ending in punctuation, the expansion logic sits in AppState rather than a service, and the bundled ServiceTests.swift CI-skip/timeout changes look unrelated and probably belong in their own PR. Details inline.

@jatinkrmalik I've done the suggested changes please review.....

@jatinkrmalik jatinkrmalik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up fixes. I left a few focused inline comments, mostly around persistence behavior and small polish items.

Comment thread Sources/VocaMac/Services/SnippetExpander.swift Outdated
Comment thread Sources/VocaMac/Models/AppState.swift Outdated
Comment thread Sources/VocaMac/Views/SettingsView.swift
Comment thread Tests/VocaMacTests/SnippetTests.swift
@jatinkrmalik

Copy link
Copy Markdown
Member

@Mr-Sunglasses heads up: since I merged your PR #148, you may need to rebase this branch onto the latest main as well before this one is ready to merge.

@Mr-Sunglasses

Copy link
Copy Markdown
Member Author

@Mr-Sunglasses heads up: since I merged your PR #148, you may need to rebase this branch onto the latest main as well before this one is ready to merge.

Hey @jatinkrmalik I've address all the suggestions and rebase the PR. 😊

@jatinkrmalik

Copy link
Copy Markdown
Member

@Mr-Sunglasses thanks for the Aug 4 follow-up. The willSet persistence bug, final expander, keeping expansion whitespace, and the UserDefaults cleanup in tests all look right. SnippetExpander is wired through AppState the way we asked, and the punctuation / cascade / round-trip tests cover the old holes.

I still can't review this as a mergeable PR. GitHub is CONFLICTING (dirty, rebaseable: false). The Aug 4 commit merged a0470b2 (the 0.7.2 bump), not current main. Since then #207 replaced the tabbed settings shell with the searchable sidebar and put output through DictationOutputFormatter plus injectResult. Please rebase onto latest main. Rebase, not another merge of a stale tip.

When you resolve conflicts:

  • Keep main's settings window size (760×560 / minWidth: 720). The 700pt "seven tabs" change does not apply to the sidebar shell and would shrink the window.
  • Do not three-way-merge SettingsView.swift back into a TabView. Keep the sidebar, and add Snippets as a SettingsPage (or a section on Dictation) with a SettingsSearchIndex entry.
  • On the inject path, expand snippets then run DictationOutputFormatter.apply, and keep the injectResult / settings-test branch. Taking the PR side as-is drops polish and test dictation.

On the expander, two leftovers after the \b fix:

  1. Character.isWordCharacter is Unicode (isLetter / isNumber), but NSRegularExpression's \b without .useUnicodeWordBoundaries is ASCII \w. Triggers like über or a trailing café can fail the same way @home used to. Easiest path: always use the whitespace lookarounds, or pass .useUnicodeWordBoundaries, and add a test.
  2. SnippetExpander still swallows regex compile failure with try?. Same logging request as the old encode/decode path.

Smaller: duplicate triggers can be saved with no warning; SnippetsSettingsTab still has unused newTrigger / newExpansion; please drop commit 4afc09d on rebase so the old ServiceTests.swift CI skips do not come back.

- Auto-save sink now encodes the emitted array: @published fires on
  willSet, so re-reading self.snippets saved the previous state and
  left the latest change (e.g. deleting all snippets) unpersisted.
  Added testAutoSavePersistsDeletion to pin this down.
- Store the expansion as entered (trim only for validation and the
  trigger) so intentional leading/trailing whitespace survives.
- Make SnippetExpander final.
- SnippetTests now clears the vocamac.snippets defaults key in
  setUp/tearDown so state cannot leak between tests; the round-trip
  test no longer clears snippets mid-test (that clear relied on the
  old one-save-behind behavior to pass).
Upstream replaced the settings TabView with a sidebar/detail shell, so
the snippets page needed reworking rather than a straight port.

- Expand snippets *after* DictationOutputFormatter, not before. Snippet
  expansions are literal text the user authored, so auto-capitalization
  must not rewrite them — expanding first turned an email snippet at the
  start of a sentence into "Me@example.com". Trigger matching is
  case-insensitive, so capitalizing the trigger first still matches.
  Pinned by testSnippetExpansionIsNotRewrittenByAutoCapitalize.
- Rebuild SnippetsSettingsTab as a grouped Form to match every other
  detail page; the old List(.inset) plus bottom action bar was styled
  for the tab layout and looked foreign in the sidebar.
- Give each row an explicit destructive remove button. Deletion
  previously relied on .onDelete with no selection binding, which has no
  affordance on macOS. Mirrors the existing Auto-Pause app list.
- Drop the unused newTrigger/newExpansion state.
@netlify

netlify Bot commented Aug 23, 2026

Copy link
Copy Markdown

Deploy Preview for voca-mac canceled.

Name Link
🔨 Latest commit f27adc2
🔍 Latest deploy log https://app.netlify.com/projects/voca-mac/deploys/6a8b45ab9b31e000085bcc72

@Mr-Sunglasses

Copy link
Copy Markdown
Member Author

@jatinkrmalik I have tested this and we are good to go. 🔥

@jatinkrmalik
jatinkrmalik merged commit 7c2453e into VocaHQ:main Aug 23, 2026
9 checks passed
@jatinkrmalik jatinkrmalik mentioned this pull request Aug 24, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add Support for Text Snippets

2 participants