Skip to content

fix: skip forced capitalization when the cursor context is unknown - #855

Open
abhiramasonny wants to merge 1 commit into
altic-dev:mainfrom
abhiramasonny:fix/840-smartCapitalizationContext
Open

fix: skip forced capitalization when the cursor context is unknown#855
abhiramasonny wants to merge 1 commit into
altic-dev:mainfrom
abhiramasonny:fix/840-smartCapitalizationContext

Conversation

@abhiramasonny

Copy link
Copy Markdown

Description

Smart capitalization was always uppercasing the first dictated word, even while in mid-sentence, bc TypingService.textBeforeCursorInFocusedField() returned "" both when there wasnt any text before the cursor and also when it couldn’t read the focused field at all, which is common in apps that don’t expose the AX value or selected range (example, Slack).

my PR changes the capture method to return nil when the preceding text can’t be read, and skips capitalization and spacing adjustments when that context is unavailable so the transcription is inserted as-is.

Type of Change

  • 🐞 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 🧹 Chore
  • 📝 Documentation update

Related Issue or Discussion

Closes #840

Testing

  • Tested on Intel Mac
  • Tested on Apple Silicon Mac
  • Tested on macOS version: 26.5.2 (25F84)
  • Ran linter locally: swiftlint --strict --config .swiftlint.yml Sources
  • Ran formatter locally: swiftformat --config .swiftformat Sources
  • Ran tests locally: xcodebuild test (full suite excluding flaky Tiny Whisper E2E) with 244 tests and 0 failures

Screenshots / Video

  • No UI/visual changes; screenshots/video are not applicable.

Notes

pretty minimal update

Copilot AI lite review requested due to automatic review settings August 13, 2026 18:51
@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR distinguishes an empty field from unavailable cursor context so dictation formatting does not force capitalization or spacing when the focused app cannot expose its text state.

  • Changes cursor-context capture to return an optional string.
  • Propagates unavailable context through live dictation, history output, and reprocessing flows.
  • Adds coverage for unknown, empty, and mid-sentence capitalization contexts.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete regressions identified in the changed dictation-formatting paths.

The optional context is propagated consistently, valid empty prefixes still trigger sentence-start capitalization, and unavailable context now bypasses only the adjustments that depend on knowing the cursor position.

Fix All in Greploop

Reviews (1): Last reviewed commit: "fix: skip forced capitalization when the..." | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1423706309

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let spacingEnabled = SettingsStore.shared.continuousDictationSpacingEnabled
let smartCapsEnabled = SettingsStore.shared.contextAwareCapitalizationEnabled
guard spacingEnabled || smartCapsEnabled else { return text }
guard spacingEnabled || smartCapsEnabled, let precedingText else { return text }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve trailing spacing without cursor context

When Space Between Dictations is enabled but the focused app does not expose readable cursor context, this guard returns before the spacing block because precedingText is nil. That removes the trailing separator the spacing setting is supposed to add, so repeated dictations in apps with unavailable AX context can concatenate text instead of chaining cleanly. Consider skipping only the context-dependent capitalization/leading-space behavior while still appending the trailing space when spacing is enabled.

Useful? React with 👍 / 👎.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes Smart Capitalization incorrectly uppercasing the first dictated word when the app can’t read the focused field context (e.g., apps that don’t expose accessibility value/selection), by distinguishing “empty preceding text” from “unknown preceding text” and skipping context-based formatting when context is unavailable.

Changes:

  • Change TypingService.textBeforeCursorInFocusedField() to return nil (instead of "") when the focused field context can’t be read.
  • Update Continuous Dictation formatting to skip spacing/capitalization adjustments when precedingText is nil.
  • Add an integration test covering the capitalization behavior difference between precedingText == nil vs precedingText == "".

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
Tests/FluidDictationIntegrationTests/DictationE2ETests.swift Adds coverage for precedingText == nil vs empty string behavior in continuous dictation formatting.
Sources/Fluid/Services/TypingService.swift Returns nil when focused-field/cursor context can’t be captured, avoiding ambiguous empty-string results.
Sources/Fluid/Services/ASRService.swift Makes continuous dictation formatting context-aware by bailing out when precedingText is unavailable.
Sources/Fluid/ContentView.swift Plumbs optional preceding-text context through capture/storage and formatting calls.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 1648 to 1651
DebugLogger.shared.debug(
"Captured preceding text for continuous dictation (chars=\(self.recordingPrecedingText.count))",
"Captured preceding text for continuous dictation (chars=\(self.recordingPrecedingText?.count ?? -1))",
source: "ContentView"
)
Comment on lines +380 to +401
func testContinuousDictationFormattingSkipsCapitalizationWhenPrecedingTextUnknown() {
self.withRestoredDefaults(keys: [
"ContinuousDictationSpacingEnabled",
"ContextAwareCapitalizationEnabled",
]) {
UserDefaults.standard.set(false, forKey: "ContinuousDictationSpacingEnabled")
UserDefaults.standard.set(true, forKey: "ContextAwareCapitalizationEnabled")

XCTAssertEqual(
ASRService.applyContinuousDictationFormatting("and then i continued", precedingText: nil),
"and then i continued"
)
XCTAssertEqual(
ASRService.applyContinuousDictationFormatting("and then i continued", precedingText: "i am speaking"),
"and then i continued"
)
XCTAssertEqual(
ASRService.applyContinuousDictationFormatting("and then i continued", precedingText: ""),
"And then i continued"
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Smart capitalization always capitalizes the first dictated word

2 participants