Skip to content

fix(widget): hide dictation pill when idle, show only when activated - #83

Merged
debpalash merged 4 commits into
mainfrom
fix/widget-hide-when-idle
May 18, 2026
Merged

fix(widget): hide dictation pill when idle, show only when activated#83
debpalash merged 4 commits into
mainfrom
fix/widget-hide-when-idle

Conversation

@debpalash

@debpalash debpalash commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

  • Removes the auto-show of the dictation pill widget on pill-mode launch — the widget no longer displays "Ready — hold shortcut to speak" idly.
  • Widget is now pre-positioned at top-center on launch (avoiding first-show animation flicker) but stays hidden until activated.
  • Tray "Start Dictation" menu item now shows + focuses the widget BEFORE emitting tray-dictate, mirroring the global-shortcut handler — without this, starting dictation from the tray would record silently.

Why

User reported during the v0.3.x stabilization review session that the idle "Ready" pill state should not be visible. Widget should appear only when actively used (recording / transcribing).

Trade-off acknowledged

The original code intentionally auto-showed the widget on launch to avoid a "looks-launch-failed" first-run experience (relevant when a user hasn't granted Accessibility permission for the global shortcut). That trade-off is accepted here because:

  • The tray icon + "OmniVoice Dictation" tooltip provide app-running signal
  • Auto-hide on done (CaptureWidget.jsx:128-149) was already implemented — this PR aligns launch behavior with the rest of the state machine
  • A first-launch onboarding toast can be added later if support requests indicate confusion

Scope

File Lines Change
frontend/src-tauri/src/lib.rs +27 / -11 Two surgical edits: pill-mode setup (remove win.show() + win.set_focus()), tray dictate handler (add position+show+focus before emit)

Untouched: global-shortcut handler (already shows widget correctly), CaptureWidget.jsx (auto-hide on done already works), tauri.conf.json (widget visible: false, create: false is correct).

Test plan

  • cargo check --manifest-path frontend/src-tauri/Cargo.toml → exit 0 (~49s)
  • Manual: bun desktop in pill mode → widget should NOT appear on launch, only tray icon visible
  • Manual: press global shortcut → widget appears at top-center, starts recording
  • Manual: tray menu → "Start Dictation" → widget appears + recording starts (was previously silent)
  • Manual: recording completes → widget auto-hides after 1.5s (no regression)
  • Manual: studio mode unchanged (widget hidden by default, shortcut still works)

Out of scope

The open_studio tray handler still does Command::new(exe).spawn() + app.exit(0) which produces a blank dev window in bun desktop mode (tracked separately as a known-issue). Production users on bundled installers don't hit it. Fix coming in a follow-up PR.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Improvements
    • The dictation pill widget now remains hidden at startup and only appears when you activate dictation from the tray menu or global shortcut, reducing visual clutter during idle periods. Widget positioning logic has been refined for optimal placement near the screen's top-center.

Review Change Stack

debpalash and others added 4 commits May 18, 2026 15:40
Addresses pre-existing pill-mode UX where the idle "Ready" state was
always visible on screen — cluttering the desktop for users who only
invoke dictation via the global shortcut (Cmd+Shift+Space) or tray menu.
User direction during the PR #51 review session: "dont show it if not
activated".

Widget now appears only when activated by the global shortcut or the
tray "Start Dictation" menu item. Two surgical edits to
frontend/src-tauri/src/lib.rs:

1. pill_mode_setup branch (~lines 384-406): drop win.show() and
   win.set_focus() on the widget window. Keep the top-center
   pre-positioning so the first show (via shortcut or tray) appears
   at the right location without a frame-jump on first show. Update
   the surrounding comment to document the new hidden-until-activated
   contract.

2. Tray "Start Dictation" handler (~lines 331-343): show + position +
   focus the widget BEFORE emitting tray-dictate. Previously the start
   branch only emitted the event, so triggering dictation from the
   tray menu recorded silently with no visible UI. The inline
   position+show+focus block mirrors the global-shortcut handler at
   lines 184-199; helper extraction was rejected to keep the diff
   minimal (see plan risks: "Keep the diff minimal").

The global shortcut handler (lines 184-199) and CaptureWidget.jsx
auto-hide-on-done logic are unchanged — both were already correct.

Trade-off vs the original "looks-launch-failed" first-run concern is
accepted: the tray icon + "OmniVoice Dictation" tooltip (lib.rs:289)
provide sufficient signal that the app is running. Idle on-screen pill
is not required for that.

Verified: cargo check --manifest-path frontend/src-tauri/Cargo.toml
exits 0 with no warnings on our crate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Companion to commit 001d975 (the lib.rs behavior change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds planning documentation and implements widget visibility behavior changes in the Tauri-based dictation system: the pill widget is pre-positioned but remains hidden during pill-mode initialization, and the tray "dictate" menu handler now positions, shows, and focuses the widget before emitting events.

Changes

Dictation pill widget idle/activation behavior

Layer / File(s) Summary
Planning and task coordination
.planning/STATE.md, .planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-PLAN.md, .planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-SUMMARY.md
Task state updated to point to 260518-lp7 with new table entry. Comprehensive plan specifies two targeted lib.rs edits: pill-mode setup to keep widget pre-positioned but hidden, and tray dictate handler to show+position+focus before emitting events. Summary documents the implementation, verification steps (cargo check, grep validation), and manual test procedures covering pill-mode launch, global shortcut, tray activation, and multi-monitor positioning.
Tray dictate handler and pill-mode setup implementation
frontend/src-tauri/src/lib.rs
Tray "dictate" event handler reordered to position, show, and focus the widget window before emitting tray-dictate when not currently visible; emits tray-dictate-stop when already visible; logs a warning if widget cannot be found. Pill-mode initialization comments updated to state widget remains hidden until activated and is pre-positioned to avoid first-show flicker, with added info log confirming pre-positioning.

Sequence Diagram

sequenceDiagram
  participant PillInit as Pill-mode Init
  participant TrayMenu as Tray Menu
  participant WidgetWindow as Widget Window
  participant EventEmit as Event Emitter

  PillInit->>WidgetWindow: Position widget at top-center (y=60)
  PillInit->>WidgetWindow: Log: pill-mode widget pre-positioned
  PillInit->>WidgetWindow: Keep hidden (no show/focus)

  TrayMenu->>WidgetWindow: Check if widget is visible
  alt Widget not visible
    TrayMenu->>WidgetWindow: Position at top-center (y=60 or center)
    TrayMenu->>WidgetWindow: Show widget
    TrayMenu->>WidgetWindow: Focus widget
    TrayMenu->>EventEmit: Emit tray-dictate
  else Widget already visible
    TrayMenu->>EventEmit: Emit tray-dictate-stop
  else Widget not found
    TrayMenu->>WidgetWindow: Log warning: widget window missing
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

The PR primarily updates planning documentation (365 lines) with clear, structured specification and introduces a focused implementation change in one file (38 lines modified) affecting two code paths: pill-mode widget initialization and tray menu event handler. The logic is straightforward positioning and visibility state management without complex branching or edge-case handling beyond the documented scenarios.

Possibly related PRs

  • debpalash/OmniVoice-Studio#40: Modifies frontend/src-tauri/src/lib.rs to control widget show/focus behavior for global shortcut toggling, directly related to this PR's tray menu widget activation flow.

Poem

🐰 A pill that hides, then shows with grace,
In tray-made touch, finds perfect place,
Pre-positioned yet demurely still,
Till activation sets the widget's will! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: hiding the dictation pill widget when idle and showing it only when activated.
Description check ✅ Passed The description comprehensively covers all required template sections including summary, changes, type (bug fix), testing details, and relevant context with clear scope documentation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/widget-hide-when-idle

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 and usage tips.

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

🧹 Nitpick comments (2)
.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-PLAN.md (1)

147-178: ⚡ Quick win

Add a language tag to the fenced commit block.

The fenced block is missing a language identifier, which triggers MD040 and can cause docs-lint noise/failures.

🤖 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
@.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-PLAN.md
around lines 147 - 178, The fenced commit block lacks a language tag which
triggers MD040; update the triple-backtick fence that wraps the git commit (the
block starting with git commit -m "$(cat <<'EOF' ... EOF )") to include a shell
language identifier (e.g., ```sh or ```bash) so the block is linted correctly
and MD040 warnings are resolved.
.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-SUMMARY.md (1)

71-76: ⚡ Quick win

Specify fence languages for command/output blocks.

These fenced blocks are untyped and trigger MD040 warnings; please annotate them (e.g., bash for commands, text for output).

Also applies to: 80-87, 91-95, 101-114

🤖 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
@.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-SUMMARY.md
around lines 71 - 76, The fenced code blocks showing the cargo command and its
output are missing language annotations (e.g., the block containing "$ cargo
check --manifest-path frontend/src-tauri/Cargo.toml 2>&1 | tail -3" and its
result); update those fences to include appropriate languages (use ```bash for
the command lines and ```text or ```output for the command output) and apply the
same fix to the other untyped fences noted (around the sections corresponding to
the ranges shown: 80-87, 91-95, 101-114) so MD040 warnings are resolved.
🤖 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 `@frontend/src-tauri/src/lib.rs`:
- Around line 356-360: The current fallback emits "tray-dictate" even when the
widget window is missing (in the block that logs "Tray dictate: widget window
not found — emitting tray-dictate without visible UI"), which restarts
dictation; instead stop emitting that event when the window is absent. Modify
the code path around the log and the call to app.emit("tray-dictate", ()) so
that if the widget window lookup fails you only log the warning and return/abort
the tray-start flow (do not call app.emit), ensuring functions that handle
"tray-dictate" (and any dictation start logic) are not invoked when the widget
window is not found.

---

Nitpick comments:
In
@.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-PLAN.md:
- Around line 147-178: The fenced commit block lacks a language tag which
triggers MD040; update the triple-backtick fence that wraps the git commit (the
block starting with git commit -m "$(cat <<'EOF' ... EOF )") to include a shell
language identifier (e.g., ```sh or ```bash) so the block is linted correctly
and MD040 warnings are resolved.

In
@.planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-SUMMARY.md:
- Around line 71-76: The fenced code blocks showing the cargo command and its
output are missing language annotations (e.g., the block containing "$ cargo
check --manifest-path frontend/src-tauri/Cargo.toml 2>&1 | tail -3" and its
result); update those fences to include appropriate languages (use ```bash for
the command lines and ```text or ```output for the command output) and apply the
same fix to the other untyped fences noted (around the sections corresponding to
the ranges shown: 80-87, 91-95, 101-114) so MD040 warnings are resolved.
🪄 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: ea5d4252-cf55-4391-b4e3-26927fb4de6d

📥 Commits

Reviewing files that changed from the base of the PR and between 21c338c and 90d827b.

📒 Files selected for processing (4)
  • .planning/STATE.md
  • .planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-PLAN.md
  • .planning/quick/260518-lp7-hide-dictation-pill-widget-when-idle-sho/260518-lp7-SUMMARY.md
  • frontend/src-tauri/src/lib.rs

Comment on lines +356 to 360
log::warn!(
"Tray dictate: widget window not found — \
emitting tray-dictate without visible UI"
);
let _ = app.emit("tray-dictate", ());

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t start dictation when the widget window is missing.

This fallback still emits tray-dictate, which can reintroduce silent recording in the exact tray-start path this PR is trying to fix.

Suggested fix
                             } else {
                                 log::warn!(
                                     "Tray dictate: widget window not found — \
-                                     emitting tray-dictate without visible UI"
+                                     refusing to start dictation without visible UI"
                                 );
-                                let _ = app.emit("tray-dictate", ());
+                                // Intentionally do not emit tray-dictate here.
                             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log::warn!(
"Tray dictate: widget window not found — \
emitting tray-dictate without visible UI"
);
let _ = app.emit("tray-dictate", ());
log::warn!(
"Tray dictate: widget window not found — \
refusing to start dictation without visible UI"
);
// Intentionally do not emit tray-dictate here.
🤖 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 `@frontend/src-tauri/src/lib.rs` around lines 356 - 360, The current fallback
emits "tray-dictate" even when the widget window is missing (in the block that
logs "Tray dictate: widget window not found — emitting tray-dictate without
visible UI"), which restarts dictation; instead stop emitting that event when
the window is absent. Modify the code path around the log and the call to
app.emit("tray-dictate", ()) so that if the widget window lookup fails you only
log the warning and return/abort the tray-start flow (do not call app.emit),
ensuring functions that handle "tray-dictate" (and any dictation start logic)
are not invoked when the widget window is not found.

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