Skip to content

User-configurable 'Press Enter' voice command — custom & multilingual trigger phrases - #231

Open
pianoandglass wants to merge 1 commit into
zachlatta:mainfrom
pianoandglass:feature/multilanguage-press-enter-pr
Open

User-configurable 'Press Enter' voice command — custom & multilingual trigger phrases#231
pianoandglass wants to merge 1 commit into
zachlatta:mainfrom
pianoandglass:feature/multilanguage-press-enter-pr

Conversation

@pianoandglass

@pianoandglass pianoandglass commented Jun 12, 2026

Copy link
Copy Markdown

Description

FreeFlow already had a single, hard-coded "press enter" trigger that presses Return after a dictation. This PR makes the trigger fully user-configurable.

What it adds

  • A Settings UI to add and remove your own trigger phrases as removable tags — in any language and in custom wordings (e.g. "hit enter", "pressione enter", "send", "appuyer sur Entrée" — whatever you want).
  • A quick-add library of multilingual variations with built-in suggestions in the UI.
  • A robust extraction engine that strips the matched trigger from the end of the transcript — even across repeated triggers, multi-line endings, and trailing punctuation — before the text reaches the LLM cleanup step, recording the intent to press Enter right after the cleaned text is pasted.

SCR-20260616-ovpj --- SCR-20260616-ouqr

UI and UX

  • Tag-Based Settings Interface: Overhauled the Press Enter settings section. Users can now view active variations as persistent UI tags.
  • Suggested Variations: Integrated a quick-add library containing built-in variations for some common languages.
  • Apple HIG Compliance: Active and suggested tags are designed with native macOS characteristics, including capsule shapes, semi-transparent backgrounds (Color.accentColor.opacity(0.15)), and smooth hover states.
  • SwiftUI Performance Optimization: Hover state logic (@State) for tags was extracted into independent structures (PressEnterActiveTagView and PressEnterSuggestedTagView). This isolation prevents the primary SettingsView from continuously re-rendering during pointer interactions, ensuring high application performance.

Technical Implementation

  • Modular Regex Engine: Created MultilanguagePressEnter.swift to handle command compilation and extraction. The system compiles user-defined active commands into a single, optimized, case-insensitive NSRegularExpression.
  • Advanced Suffix Extraction: Replaced simple .hasSuffix() checks. The new engine correctly strips trigger phrases from the end of the transcript even when followed by punctuation (e.g., "?", "!"). Relevant punctuation is preserved and re-applied to the finalized transcript.
  • Consecutive Trigger Handling: The extraction loop dynamically handles repeated trigger words (e.g., "final phrase. enter, enter"), stripping them consecutively until only the intended transcript remains.
  • Input Sanitization: Automatically normalizes user input for custom variations by collapsing excess whitespaces to ensure robust regex compilation.

Summary by CodeRabbit

  • New Features

    • Added a customizable “Recognize ‘Press Enter’” setting in General Settings.
    • Users can now add, view, and remove their own press-enter phrases, with suggested phrases shown for quick setup.
    • The app now better recognizes multiple phrase variations and handles trailing punctuation more naturally.
  • Bug Fixes

    • Improved command detection so spoken “press enter” variations are stripped more reliably from transcripts.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: af40347d-cb70-4e3a-8b51-47fbdbe2caca

📥 Commits

Reviewing files that changed from the base of the PR and between 059f36a and 4a3da2c.

📒 Files selected for processing (3)
  • Sources/AppState.swift
  • Sources/MultilanguagePressEnter.swift
  • Sources/SettingsView.swift
🚧 Files skipped from review as they are similar to previous changes (3)
  • Sources/SettingsView.swift
  • Sources/MultilanguagePressEnter.swift
  • Sources/AppState.swift

📝 Walkthrough

Walkthrough

This PR replaces a hardcoded trailing “press enter” regex with user-configurable multilingual command phrases. It adds regex compilation and extraction utilities, persists custom phrases in AppState, and introduces General Settings UI for adding, removing, and suggesting command variations.

Changes

Customizable multi-language press-enter command parsing

Layer / File(s) Summary
Multi-language command utility library
Sources/MultilanguagePressEnter.swift
Adds MultilanguagePressEnter with curated suggested commands, regex compilation from phrase lists, and transcript stripping that preserves punctuation while removing trailing command phrases.
AppState custom commands and caching
Sources/AppState.swift
Adds persisted custom command storage, caches a compiled regex, loads stored phrases at startup, and initializes the cache from the saved list.
Transcript parsing integration
Sources/AppState.swift
Updates transcript command parsing to use the shared extraction helper and passes the cached regex through the main stop-and-transcribe flow.
Settings UI for press-enter customization
Sources/SettingsView.swift
Adds the “Recognize 'Press Enter'” settings card, customization controls, active/suggested tag views, and input normalization for adding custom phrases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • zachlatta/freeflow#121: Introduces the earlier press-enter transcript handling path that this PR expands into configurable multilingual command detection.

Poem

A bunny typed, “Hop, tap, and run,”
With many phrases under the sun.
I nibble commands, tidy and neat,
Then press Enter when the words repeat. 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main change: configurable Press Enter voice triggers with custom multilingual phrases.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Sources/MultilanguagePressEnter.swift (1)

10-23: 💤 Low value

Single-word commands may cause false positives in edge cases.

Commands like "enter", "intro", "entrée", "retour", "eingabe", and "invio" could match legitimate sentence endings. For example, "Please type your username and then enter" would be stripped to "Please type your username and then".

The PR objectives indicate this is a known trade-off (commands must be at the absolute end), but consider whether users in specific locales might frequently end sentences with these words. If this becomes an issue, these single-word variants could be removed, requiring the verb prefix (e.g., "press enter" but not bare "enter").

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/MultilanguagePressEnter.swift` around lines 10 - 23, The commands
array contains single-word entries that cause false positives; update the static
let commands in MultilanguagePressEnter.swift to remove the bare single-word
variants ("enter", "intro", "entrée", "retour", "eingabe", "invio") so only
multi-word/verb-prefixed forms remain (e.g., "press enter", "presionar enter",
etc.); locate the static let commands declaration and delete those standalone
tokens (or comment them out) to ensure matches require the verb prefix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@Sources/MultilanguagePressEnter.swift`:
- Around line 10-23: The commands array contains single-word entries that cause
false positives; update the static let commands in MultilanguagePressEnter.swift
to remove the bare single-word variants ("enter", "intro", "entrée", "retour",
"eingabe", "invio") so only multi-word/verb-prefixed forms remain (e.g., "press
enter", "presionar enter", etc.); locate the static let commands declaration and
delete those standalone tokens (or comment them out) to ensure matches require
the verb prefix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 05a34741-f53d-4cdb-b32f-4cea49af9701

📥 Commits

Reviewing files that changed from the base of the PR and between 7089649 and a4b68de.

📒 Files selected for processing (2)
  • Sources/AppState.swift
  • Sources/MultilanguagePressEnter.swift

@pianoandglass
pianoandglass marked this pull request as draft June 13, 2026 20:55
@pianoandglass pianoandglass changed the title feat: add multilanguage PressEnter command support and improved regex cleaning feat: Enhanced Press Enter Command Settings for Variations and Multilanguage Support Jun 16, 2026
@pianoandglass
pianoandglass marked this pull request as ready for review June 16, 2026 20:21
@pianoandglass
pianoandglass force-pushed the feature/multilanguage-press-enter-pr branch from 6c793d1 to 235b6e5 Compare June 16, 2026 20:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Sources/MultilanguagePressEnter.swift (1)

140-147: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid duplicating restored sentence punctuation.

If the transcript already keeps punctuation before the command, e.g. Are you coming? press enter?, these branches append another ? / ! because they only remove , and ..

Proposed fix
-            if savedPunctuation == "?" {
-                // If the person asked a question, we make sure the saved question mark goes back to the end of the text.
-                if currentTranscript.hasSuffix(",") || currentTranscript.hasSuffix(".") { currentTranscript.removeLast() }
-                currentTranscript.append("?")
-            } else if savedPunctuation == "!" {
-                // If the person made an exclamation, we return the exclamation mark to the text.
-                if currentTranscript.hasSuffix(",") || currentTranscript.hasSuffix(".") { currentTranscript.removeLast() }
-                currentTranscript.append("!")
+            if savedPunctuation == "?" || savedPunctuation == "!" {
+                // Replace any existing terminal punctuation before restoring the saved punctuation.
+                if let last = currentTranscript.last, ",.!?".contains(last) {
+                    currentTranscript.removeLast()
+                }
+                currentTranscript.append(savedPunctuation)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/MultilanguagePressEnter.swift` around lines 140 - 147, The
punctuation restoration logic in the savedPunctuation conditionals only checks
for and removes "," and "." before appending "?" or "!", but does not account
for cases where the transcript already ends with the same punctuation mark being
restored. This causes duplicate punctuation like "Are you coming??" when the
saved punctuation is already present. Modify both the `savedPunctuation == "?"`
and `savedPunctuation == "!"` branches to also check if currentTranscript
already ends with that same punctuation mark using hasSuffix, and skip appending
it if it already exists, or alternatively remove any existing punctuation mark
before appending the restored one.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/MultilanguagePressEnter.swift`:
- Line 71: The regexPattern variable lacks word boundaries, causing commands to
match as substrings within other words (e.g., "enter" matching inside
"carpenter"). Add word boundary markers by wrapping the joinedCommands group
with \b anchors. Change the non-capturing group from (?:\(joinedCommands)) to
(?:\b(?:\(joinedCommands))\b) to ensure commands only match as complete words,
preventing partial word matches while still allowing the pattern to function
correctly.

In `@Sources/SettingsView.swift`:
- Around line 2853-2867: The remove button is only created and rendered when
isHovering is true, making it inaccessible to keyboard and VoiceOver users.
Refactor the code so that the Button wrapping the remove action is always
present and focusable, rather than conditionally rendered in the overlay. Keep
the visual switching between the command text and xmark icon using opacity
changes based on isHovering, but ensure the Button itself exists at all times.
This makes the control accessible via keyboard navigation and assistive
technologies while maintaining the same hover-based visual feedback for pointer
users.
- Around line 1248-1251: The closing `})` on line 1251 lacks a matching opening
`Button(action: {`. Wrap the withAnimation block (which toggles
showPressEnterCustomization) with the missing Button(action:) initializer so
that the entire animation block and subsequent button label content are enclosed
within Button(action: { ... }) syntax, fixing the compile error.

---

Outside diff comments:
In `@Sources/MultilanguagePressEnter.swift`:
- Around line 140-147: The punctuation restoration logic in the savedPunctuation
conditionals only checks for and removes "," and "." before appending "?" or
"!", but does not account for cases where the transcript already ends with the
same punctuation mark being restored. This causes duplicate punctuation like
"Are you coming??" when the saved punctuation is already present. Modify both
the `savedPunctuation == "?"` and `savedPunctuation == "!"` branches to also
check if currentTranscript already ends with that same punctuation mark using
hasSuffix, and skip appending it if it already exists, or alternatively remove
any existing punctuation mark before appending the restored one.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d519eeb-a37c-4261-8328-175e41d394fa

📥 Commits

Reviewing files that changed from the base of the PR and between a4b68de and 6c793d1.

📒 Files selected for processing (3)
  • Sources/AppState.swift
  • Sources/MultilanguagePressEnter.swift
  • Sources/SettingsView.swift

Comment thread Sources/MultilanguagePressEnter.swift Outdated
Comment thread Sources/SettingsView.swift
Comment thread Sources/SettingsView.swift
@pianoandglass

Copy link
Copy Markdown
Author

Thanks @coderabbitai! All 3 issues have been addressed:

  • Fixed duplicate punctuation restoration logic
  • Added \b word boundaries to regex matching to prevent sub-word matching
  • Refactored ActiveTag remove button for keyboard accessibility and fixed syntax errors

@pianoandglass
pianoandglass force-pushed the feature/multilanguage-press-enter-pr branch from 235b6e5 to 56ca120 Compare June 16, 2026 20:36
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@pianoandglass: Great work on all three fixes! The \b word boundaries in particular were an important safety net — they'll prevent partial-word matches like "reenter" or "centered" from accidentally triggering the command. The keyboard-accessible remove button is also a nice touch for accessibility. The UI looks clean in both light and dark themes. 🐰✨


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@pianoandglass

Copy link
Copy Markdown
Author

Pushed a small follow-up commit (059f36a) that makes the new compiledRegex parameter of parseTranscriptCommands(...) optional (NSRegularExpression? = nil), so existing two-argument callers stay source-compatible.

MultilanguagePressEnter.extractCommand(from:withRegex:) already treats a nil regex as a no-op failsafe (returns the transcript unchanged), so there is no behavior change — normal dictation still passes the compiled regex explicitly. This keeps the function backward-compatible for callers that do not precompile a regex.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

The PR description images show a polished "Recognize 'Press Enter'" settings card in both light and dark themes — active variation pill tags (press enter, hit enter, presionar enter, 回車), a collapsible Customize section with a text input + Add button, and a multilanguage Suggested variations grid (English, Portuguese, Spanish, French, German, Italian, Dutch, Russian, Japanese, Chinese).

@pianoandglass: Thanks for the context on commit 059f36a! Making compiledRegex default to nil is a clean backward-compatibility move — it keeps existing call sites source-compatible without touching their signatures, while extractCommand(from:withRegex:) already handles the nil case gracefully as a no-op. I'll kick off a full review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pianoandglass pianoandglass changed the title feat: Enhanced Press Enter Command Settings for Variations and Multilanguage Support feat: Enhanced Press Enter Command Settings Jun 21, 2026
@pianoandglass pianoandglass changed the title feat: Enhanced Press Enter Command Settings Enhanced Press Enter Command Settings Jun 21, 2026
@pianoandglass
pianoandglass marked this pull request as draft June 23, 2026 14:54
…iations

Enhanced Press Enter command settings: configurable command variations and
multilanguage support, so the spoken 'press enter' (and localized/again
equivalents) trigger a Return keypress. Includes the CodeRabbit review fixes
(word-boundary regex, accessibility, punctuation) and the compiledRegex
optional (= nil) fix, folded in from 059f36a.

Modified files:
- Sources/AppState.swift
- Sources/MultilanguagePressEnter.swift (new)
- Sources/SettingsView.swift
@pianoandglass
pianoandglass force-pushed the feature/multilanguage-press-enter-pr branch from 059f36a to 4a3da2c Compare June 24, 2026 05:07
@pianoandglass pianoandglass changed the title Enhanced Press Enter Command Settings User-configurable 'Press Enter' voice command — custom & multilingual trigger phrases Jun 24, 2026
@pianoandglass
pianoandglass marked this pull request as ready for review June 24, 2026 05:08
@pianoandglass

Copy link
Copy Markdown
Author

Rebased onto the latest main — this PR is now conflict-free and cleanly mergeable against current upstream.

It's intentionally self-contained: scoped to compile standalone against main with a minimal footprint on shared files, so it does not depend on — or conflict with — my other open PRs. Each can be reviewed and merged independently, in any order.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

The PR description images show the polished "Recognize 'Press Enter'" settings card — light theme displaying active variation pills (press enter, hit enter, presionar enter, 回车) with a collapsible Customize chevron, and the dark theme showing the same card with the Customize section expanded, a text input + Add button, and the full multilanguage Suggested variations grid.

@pianoandglass: Thanks for the clean rebase! Good to know the PR compiles standalone against main with a minimal footprint on shared files and is independently mergeable. Kicking off a fresh review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant