Skip to content

Restructure Train by Voice composer into a 3-step accordion - #661

Open
shreeraman96 wants to merge 7 commits into
mainfrom
fix/train-by-voice-accordion-648
Open

Restructure Train by Voice composer into a 3-step accordion#661
shreeraman96 wants to merge 7 commits into
mainfrom
fix/train-by-voice-accordion-648

Conversation

@shreeraman96

@shreeraman96 shreeraman96 commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes #648, where Train by Voice / Teach Pronunciation under Custom Dictionary showed no interactive prompts and left the Start button permanently inactive. This replaces the flat replacement composer with a guided 3-step accordion (① Word → ② Record → ③ Verify & Save) that always surfaces the next action, and hardens the underlying step state machine.

Changes:

  • Restructure the composer into a 3-step accordion with per-step status glyphs; extract pure step-derivation + readiness logic into a new SwiftUI-free, unit-tested DictionaryTrainingStepModel (DictionaryTrainingSnapshot groups the training state)
  • Reset the hasReachedVerify latch on every teardown path (Try Again, capture removal, mode toggle) so Verify never dead-ends with Save disabled
  • Reset lastAnnouncedTrainingStep on teardown and announce any forward step edge (incl. word → verify) for correct VoiceOver cues
  • Make Record/Verify headers non-interactive while the word is empty, and keep Verify & save locked until the derived step actually reaches verify so it can't be opened with nothing recorded and Add Replacement disabled; surface save-failure errors in the Verify step
  • Simplify the accordion copy: step titles carry the instruction (Type the word to teach / Say it 3 times / Verify & save), per-step subtitles and the word-echoing readiness strings are gone
  • Move the trainingReplacement onChange onto the always-mounted accordion so programmatic word writes still reset progress
  • Add unit tests for step derivation, the readiness predicates, and edge cases (empty-word latch, case-insensitivity, pronunciation boundaries)

Type of Change

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

Related Issue or Discussion

Closes #648

Testing

  • Tested on Intel Mac
  • Tested on Apple Silicon Mac
  • Tested on macOS version: 26.5 (Tahoe)
  • Ran linter locally: swiftlint --strict --config .swiftlint.yml Sources — 0 violations
  • Ran formatter locally: swiftformat --config .swiftformat — changed files pass
  • Ran tests locally: DictionaryTrainingStepModelTests — 25/25 passing

Screenshots / Video

Step 1 : Enter the word to teach

image

Step 2 : Record

image

Step 3 : Verify & save

image

Manual Add

image

Research preview

image

Before all shown at once

image

Notes

  • No shared-package (FluidIntelligence) changes — no cross-repo impact.
  • Findings from a two-model deep review + PR bot review (latch not reset on teardown, missing Verify-step error feedback, VoiceOver announcement gaps, empty-word dead-ends) are all fixed.

@github-actions github-actions Bot added needs PR template Pull request is missing required template content. needs screenshots Pull request needs screenshot or video evidence. labels Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

The PR Policy check is blocking this PR because required template information is missing.

Please update the PR description with:

  • Screenshots / Video

Visual files detected:

  • Sources/Fluid/UI/CustomDictionaryView.swift
  • Sources/Fluid/UI/DictionaryTrainingStepModel.swift

Screenshots or video are required for UI, UX, settings, onboarding, overlay, menu bar, or visual behavior changes. If this PR has no visual changes, check the no-visual-change box in the template.

If this remains incomplete for 48 hours after opening, the PR may be closed.

@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: 903a026ba3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 715 to 720
private var trainingVerifyStepBody: some View {
VStack(alignment: .leading, spacing: self.theme.metrics.spacing.sm) {
self.trainingFinalOutputPanel

Button {
Task { await self.addTrainedReplacement() }

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 Show verification errors in the Verify step

When advanced voice matching is enabled and addTrainedReplacement() fails to save the pronunciation profile, it sets trainingHasError and trainingStatusMessage while leaving the accordion on Verify. Because the footer that renders those errors is now only included in trainingRecordStepBody, this Verify body shows no failure feedback after the Add Replacement attempt fails, unlike the previous always-visible footer. Include the error/footer in this step as well so the user can see and recover from save failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in d7381c4, though not by duplicating the footer into the Verify body.

The error label is hoisted out of the collapsible step bodies and rendered once in the accordion container (CustomDictionaryView.swift:2303-2310), directly after the three headers:

// Always-visible error label: rendered once outside the collapsible step
// bodies so a recording/processing failure is surfaced regardless of
// which step is expanded.
if self.trainingHasError {
    Label(self.trainingStatusMessage, systemImage: "exclamationmark.triangle.fill")
        .font(self.theme.typography.caption)
        .foregroundStyle(self.theme.palette.warning)
}

So a failed addTrainedReplacement() with advanced voice matching enabled surfaces trainingStatusMessage while the accordion sits on Verify, and the same holds for record/processing failures on any expanded step — one rendering site instead of per-step copies.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces the flat "Train by Voice" composer with a guided 3-step accordion (Word → Record → Verify & Save) and fixes the root cause of #648, where the Start button was permanently inactive and no interactive prompts were shown. Step-derivation and readiness predicates are extracted into a new SwiftUI-free DictionaryTrainingStepModel backed by 25 unit tests.

  • Latch hardening: all teardown paths (resetTrainingVerificationAttempts, removeTrainingVariant, mode toggle, word edit) now call resetTrainingStepLatches(), which resets both hasReachedVerifyStep and lastAnnouncedTrainingStep, preventing Verify from dead-ending with Save disabled and VoiceOver from silently skipping re-advance announcements.
  • Step gating: Record/Verify headers are non-interactive while the word is empty; Verify remains locked until the derived step reaches it; errors surface outside all step bodies so they are visible regardless of which step is expanded.
  • Announcements: announceTrainingStepEdgeIfNeeded lowers lastAnnouncedTrainingStep on backward edges, ensuring every subsequent re-advance to a later step fires a fresh VoiceOver cue.

Confidence Score: 5/5

Safe to merge. The changes are tightly scoped to the Train by Voice accordion; no shared-package or cross-repo paths are touched.

The latch reset, step gating, and announcement logic are all sound. The DictionaryTrainingStepModel extraction keeps derivation logic pure and testable, and the 25 tests cover the critical branches (post-ready miss latch, empty-word guard precedence, case-insensitive comparisons, recording-lock priority, and the pronunciation vs. consecutive-coverage paths). All teardown paths reset both state latches. No regressions to existing non-training code paths were identified.

Files Needing Attention: No files require special attention.

Reviews (9): Last reviewed commit: "style: cut step-model comment density" | Re-trigger Greptile

Comment thread Sources/Fluid/UI/CustomDictionaryView.swift Outdated
Comment thread Sources/Fluid/UI/CustomDictionaryView.swift
Comment thread Sources/Fluid/UI/DictionaryTrainingStepModel.swift Outdated
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown

FluidVoice PR build ready

Download FluidVoice-PR-661-edf8f591b084

The artifact contains the ad-hoc-signed app ZIP, Xcode archive, build manifest, and installation instructions. It expires 5 days after the build.

Install the app

  1. Extract the downloaded artifact, then extract FluidVoice-PR-661.app.zip.

  2. Move FluidVoice Restructure Train by Voice composer into a 3-step accordion #661.app into the /Applications folder.

  3. Open Terminal and remove the download quarantine marker:

    xattr -dr com.apple.quarantine "/Applications/FluidVoice #661.app"
    
  4. In Applications, Control-click FluidVoice Restructure Train by Voice composer into a 3-step accordion #661.app and choose Open.

  5. If macOS still blocks it, open System Settings → Privacy & Security, click Open Anyway, and confirm.

This build has its own app identity, so its permissions are separate from the release version of FluidVoice.

View workflow run

@github-actions github-actions Bot removed needs PR template Pull request is missing required template content. needs screenshots Pull request needs screenshot or video evidence. labels Jul 20, 2026
@shreeraman96
shreeraman96 force-pushed the fix/train-by-voice-accordion-648 branch from 6cac7d6 to a99cfbd Compare July 20, 2026 23:51
@github-actions

Copy link
Copy Markdown

This pull request has been marked stale because it has had no activity for 5 days. It will be closed in 2 days if there is no further activity. Add the keep-open or pinned label to prevent automatic closure.

@github-actions github-actions Bot added the stale label Jul 26, 2026
@shreeraman96
shreeraman96 force-pushed the fix/train-by-voice-accordion-648 branch from a99cfbd to b9ddb2e Compare July 26, 2026 21:00

@grohith327 grohith327 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Had a quick glance, added few comments. A few more requests for changes:

When someone types a word, it shows up in many places, not needed since they know what they are typing

Image

The steps in record tab are too detailed, I would recommend shrinking it a bit to be specific

Image

These are probably not entirely related to your changes but would recommend doing the clean up in one go

Comment thread Sources/Fluid/UI/CustomDictionaryView.swift Outdated
Comment thread Sources/Fluid/UI/CustomDictionaryView.swift Outdated
@grohith327 grohith327 removed the stale label Jul 29, 2026
shreeraman arunachalam karikalan and others added 5 commits July 31, 2026 09:47
Splits the flat replacement composer into Word -> Record -> Verify & Save
steps with an extracted, unit-tested step-derivation model.

- Extract pure step logic into DictionaryTrainingStepModel (SwiftUI-free)
- Render composer as a 3-step accordion with per-step status glyphs
- Reset hasReachedVerify latch on every teardown path (Try Again, capture
  removal, mode toggle) so Verify never dead-ends with Save disabled
- Reset lastAnnouncedTrainingStep and announce any forward step edge
  (including word->verify) for correct VoiceOver cues
- Make Record/Verify headers non-interactive while the word is empty
- Move trainingReplacement onChange to the always-mounted accordion so
  programmatic word writes still reset progress
- Add unit tests for step derivation, readiness predicates, and edges
- Reset lastAnnouncedTrainingStep on all teardown paths so the step-3
  VoiceOver cue fires again on retry (resetTrainingVerificationAttempts,
  removeTrainingVariant)
- Show save-failure errors in the Verify step body (footer previously
  lived only in the Record body)
- Drop the unused trainingVariantsIsEmpty param from isOutputCovered
- Introduce DictionaryTrainingSnapshot to collapse the step-model param
  packs (fixes function_parameter_count) and move accordion helpers into
  an extension (fixes type_body_length); no behavior change
- Show real progress in Record subtitle when Verify is latched after a miss
- Render heard-variant chips and already-correct caption in the Verify body
- Move training error label to an always-visible spot below the accordion
- Distinguish 'No replacement needed' from 'Ready to save' in Verify header
- Lower VoiceOver announcement latch on backward step transitions
- Defer word-field focus until the step body is mounted
- Dedupe coverage and recording-lock predicates; extract resetTrainingStepLatches()
- Pin latch-state subtitle copy in tests; use production readyCoveredCount
Typing a word made the "Verify & save" header tappable with zero
recordings, stranding the user on an empty final-output panel with
Add Replacement disabled.

- Move the header tap predicate into DictionaryTrainingStepModel.isStepInteractive
  and gate .verify on derived == .verify (the latch keeps Try Again reachable
  after a post-ready miss).
- Drop per-step subtitles; step titles now carry the instruction
  ("Type the word to teach" / "Say it 3 times" / "Verify & save"), so
  DictionaryTrainingStepCopy is gone.
- Stop echoing the typed word back in readiness and instruction copy.
- Replace the subtitle tests with isStepInteractive coverage.
@shreeraman96
shreeraman96 force-pushed the fix/train-by-voice-accordion-648 branch from 9810979 to 32aa246 Compare July 31, 2026 22:13
@shreeraman96
shreeraman96 requested a review from grohith327 July 31, 2026 22:24
Collapse multi-line comment blocks in the accordion to single lines and drop
the ones narrating the change rather than the code.
Drops the file banner the type below already states, three near-identical
"single source of truth" delegation notes, and test comments that restated
their own test name. Compresses the rest to one line each, keeping the
non-obvious contracts: the empty-word guard outranking the verify latch, why a
post-ready miss must not snap back, and the readiness-progress mirror.

Model 19 comment lines to 8, tests 38 to 20.
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] train by voice and teach pronunciation not working

2 participants