Skip to content

Smooth bottom overlay hotkey-release animation#222

Open
grohith327 wants to merge 6 commits intomainfrom
fix-janky-animation-overlay
Open

Smooth bottom overlay hotkey-release animation#222
grohith327 wants to merge 6 commits intomainfrom
fix-janky-animation-overlay

Conversation

@grohith327
Copy link
Copy Markdown
Collaborator

@grohith327 grohith327 commented Mar 22, 2026

Description

Smoothen the animation of the bottom overlay

Type of Change

  • 🐞 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📝 Documentation update

Testing

  • Tested on Apple Silicon Mac
  • Tested on macOS 26.2
  • Ran linter locally: swiftlint --strict --config .swiftlint.yml Sources
  • Ran formatter locally: swiftformat --config .swiftformat Sources

Screenshots / Video

https://drive.google.com/file/d/1BLZ0QM7V-ETxc6Otd5CbAPLWtx3GSBm6/view?usp=sharing

Copy link
Copy Markdown

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

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: 1d88130442

ℹ️ 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 +2582 to +2584
guard !self.layout.usesFixedCanvas else { return }
guard measuredHeight > 0 else { return }
self.dynamicPreviewMeasuredHeight = measuredHeight
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 Reset cached preview height when the measured height drops to zero

Because BottomOverlayView is hosted once and reused across recordings, dynamicPreviewMeasuredHeight persists between sessions. This handler ignores the 0 height emitted when show() clears the preview, so a previous multi-line measurement can survive into the next quick/short dictation. When release mode starts, isBottomOverlayReleaseTransitioning then freezes the preview to that stale height, leaving a large blank transcription area visible during the processing animation until the overlay hides.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

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

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: 1dd746cdf0

ℹ️ 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 +1941 to +1945
let newlineCount = trimmed.filter { $0 == "\n" }.count
let charCapacityPerLine = max(Int((self.previewMaxWidth / max(self.layout.transFontSize * 0.56, 1)).rounded(.down)), 12)
let estimatedWrappedLines = max(1, (trimmed.count + charCapacityPerLine - 1) / charCapacityPerLine)
let maxVisibleLines = max(Int((self.previewMaxHeight / max(self.estimatedPreviewLineHeight, 1)).rounded(.down)), 1)
return min(max(estimatedWrappedLines + newlineCount, 1), maxVisibleLines)
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 Resize the dynamic preview from measured height changes

In medium mode, the preview is laid out in a fixed-width ScrollView with a proportional font, so actual wrapping depends on glyph widths and explicit line breaks, not just trimmed.count. This new bucket calculation drives whether refreshSizeForContent() runs, so two previews can keep the same bucket while their measured height changes (for example, narrow text reflowing into wider words). When that happens the reused bottom overlay keeps its old height, leaving the last line clipped or stale blank space until some unrelated state change forces another resize.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

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

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: 57996f4de7

ℹ️ 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 +1615 to +1621
let hadLivePreviewText = self.asr.partialTranscription
.trimmingCharacters(in: .whitespacesAndNewlines)
.isEmpty == false

// Give SwiftUI a chance to render the processing state before we do heavier work
// (ASR finalization + optional AI post-processing).
await Task.yield()
// Only show a processing transition when we already observed spoken text.
// If there was no spoken text, let the overlay disappear immediately on hotkey release.
if hadLivePreviewText {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the processing overlay for non-streaming recordings

hadLivePreviewText is only true when a streaming partial has already arrived, but ASRService.start() only produces those updates for supportsStreaming models and very short utterances can end before the first chunk completes. In those cases this new guard skips menuBarManager.setProcessing(true) before await asr.stop(), so MenuBarManager.handleOverlayState() hides the overlay as soon as isRunning flips to false and users lose all Transcribing…/Refining… feedback during the slowest part of the flow. This regresses Whisper Medium/Large and short dictations compared with the previous always-visible processing state.

Useful? React with 👍 / 👎.

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.

1 participant