Skip to content

Conversation

@yujonglee
Copy link
Contributor

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a new detect plugin and permissions plugin to the workspace and desktop app, registers them at Tauri startup, refactors notification/listener/local-llm plugins to reduce or remove many commands and background workers, introduces macOS Zoom mute watcher in detect crate, and centralizes permission handling in the frontend via a new hook and shared UI component.

Changes

Cohort / File(s) Summary
Workspace & desktop Tauri init
Cargo.toml, apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/src/lib.rs
Adds tauri-plugin-detect and tauri-plugin-permissions to workspace and registers both plugins in the Tauri builder chain. Also removes default-features = false from windows plugin entry.
New detect plugin (Rust + JS)
plugins/detect/* (new) — e.g. Cargo.toml, build.rs, src/lib.rs, src/commands.rs, src/events.rs, src/ext.rs, src/error.rs, js/index.ts, package.json, tsconfig.json
New Tauri plugin providing detection commands/events (list installed/mic-using apps, set/reset quit handler), event enums, Specta bindings and TypeScript bindings, error serialization, and a build script exporting commands.
Detect crate Zoom support
crates/detect/src/zoom.rs, crates/detect/src/lib.rs, crates/detect/Cargo.toml
Adds macOS Zoom mute watcher (new module, ZoomMuteWatcher, event variant) and removes macOS accessibility dependency.
Notification plugin reduction
plugins/notification/* — e.g. src/lib.rs, src/commands.rs, src/ext.rs, src/detect.rs (removed), src/event.rs (removed), src/handler.rs (removed), src/quit.rs (removed), build.rs
Removes large portions of notification worker/handler/detect lifecycle and many commands; retains show_notification and adds clear_notifications; shrinks public API and Specta command list; simplifies setup.
Listener plugin API cleanup
plugins/listener/* — e.g. build.rs, Cargo.toml, src/commands.rs, src/ext.rs, src/actors/*, src/events.rs, src/lib.rs
Removes macOS-specific permission APIs and speaker-mute APIs (commands, trait methods, PermissionStatus type), removes related dependencies, reduces supported commands and event variants, and adjusts actor messages/state.
Local-LLM plugin removals
plugins/local-llm/* — e.g. build.rs, src/commands.rs, src/events.rs, src/ext/task.rs, src/lib.rs, src/ext/mod.rs
Removes generate_title/generate_tags commands and LocalLlmTaskExt trait, removes event hook wiring and task module.
Frontend: permissions & detect integration
apps/desktop/src/hooks/use-permissions.ts, apps/desktop/src/components/shared/permission-row.tsx, apps/desktop/src/components/settings/general/permissions.tsx, apps/desktop/src/routes/app/onboarding.tsx, apps/desktop/src/components/settings/notification.tsx, apps/desktop/src/contexts/listener.tsx, apps/desktop/src/store/zustand/listener/general.ts, apps/desktop/src/store/tinybase/main.ts, apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx
Adds centralized usePermissions hook and PermissionRow component; replaces inline permission logic in onboarding and settings; switches notification UI to use detect commands/listing; introduces listener hook to handle detect events and set muted state; tinybase persister behaviour adjusted; UI shows muted icon.
Package version bumps
apps/desktop/package.json, apps/web/package.json, apps/pro/package.json, packages/*/package.json, packages/tiptap/*
Numerous dependency version updates across apps and packages (AI SDKs, tauri plugins, tooling, libs).
Stripe API version bump
apps/web/src/functions/stripe.ts, apps/web/supabase/functions/stripe/index.ts, apps/web/supabase/functions/stripe/deno.json
Updates Stripe API version string to 2025-10-29.clover and bumps stripe/hono versions in runtime config.
Misc tests / minor deletions
crates/llama/src/lib.rs, various small removals
Removes a test from llama crate and other small cleanup edits.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant DetectPlugin as tauri-plugin-detect
participant Frontend as Desktop UI
participant Listener as ListenerStore / ListenerProvider
participant NotificationAPI as tauri-plugin-notification
Note over DetectPlugin,Listener: New event flow (high-level)
DetectPlugin->>Listener: emit DetectEvent (micStarted / micStopped / micMuted)
Listener->>Listener: useHandleMuteAndStop updates store (setMuted / stop)
Listener->>NotificationAPI: notificationCommands.show_notification (delayed)
Frontend->>NotificationAPI: request via commands (show/clear)
NotificationAPI-->>Frontend: ack / persists (no event worker logic)

(Note: diagram highlights primary interactions: detect events → listener store → notification commands; background worker/notification lifecycle removed.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Potential hotspots to review:

  • plugins/notification: removal of worker/handler/quit logic (high-risk behavioral regressions).
  • plugins/listener: removal of permission/speaker APIs and actor message changes (concurrency/actor implications).
  • plugins/detect and crates/detect zoom module: correctness of event emission, Specta bindings, and macOS-specific AppleScript watcher.
  • Frontend permission hooks and onboarding/settings replacements: ensure UI state and mutation timing (restarts, refetch delays) are correct.
  • Integration points: Tauri plugin registration order in apps/desktop/src-tauri/src/lib.rs and any Specta/command mismatches between frontend calls and backend commands.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ❓ Inconclusive The pull request contains no description, leaving no information to verify whether a description relates to the changeset. While the complete absence of any description is not explicitly "off-topic," it provides zero meaningful information about the changes. Given the very lenient evaluation criteria, an empty description presents insufficient data to conclusively determine relevance. To resolve this, the author should add a pull request description explaining the main objectives and scope of the changes. At minimum, the description should clarify that this PR refactors notification functionality, extracts application detection into a new detect plugin, centralizes permission handling, and reorganizes related infrastructure across multiple plugins.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Integrate notification to the new bundle" is partially related to the changeset. The pull request does involve notification system refactoring and integration with new infrastructure (a new detect plugin and permissions system), so the title references a real aspect of the change. However, the title is vague about what "new bundle" means and doesn't clearly capture the full scope of changes, which include extracting new detect functionality, reorganizing permission handling, and simplifying multiple plugin architectures. While not misleading or off-topic, the title is somewhat generic and doesn't convey the primary restructuring work comprehensively.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5ee3a7e and ede22dd.

📒 Files selected for processing (3)
  • apps/desktop/src/components/shared/permission-row.tsx (1 hunks)
  • apps/desktop/src/contexts/listener.tsx (3 hunks)
  • apps/desktop/src/hooks/use-permissions.ts (1 hunks)

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
plugins/detect/src/ext.rs (1)

1-3: Consider adding methods or documentation for the empty trait.

The DetectPluginExt trait is empty and lacks documentation explaining its purpose. Extension traits typically provide methods that extend the functionality of types implementing them.

Please consider:

  • Adding the methods this trait is intended to provide
  • Adding documentation if this is intentionally empty for future expansion
  • Removing the trait if it's not currently needed

If this trait is a stub for future functionality, add a documentation comment:

+/// Extension trait for Tauri managers providing detect plugin functionality.
+/// 
+/// TODO: Add extension methods for detect plugin operations.
 pub trait DetectPluginExt<R: tauri::Runtime> {}
plugins/detect/src/error.rs (1)

5-6: Consider adding a comment to clarify the empty enum or defer adding error types.

The empty Error enum is valid but creates an uninhabited type, meaning Result<T> can only ever be Ok(T). If this is a placeholder for future error variants, consider adding a comment to make that clear. Alternatively, if no errors are expected, you might defer adding the error module until error handling is actually needed.

plugins/notification/src/error.rs (1)

4-22: Consider documenting or removing methods on the empty Error enum.

The Error enum is now uninhabited (has no variants), which means instances cannot be constructed. This is a valid Rust pattern for maintaining API compatibility while removing all error cases. However, the Serialize impl and as_worker_error method can never be executed since self cannot exist.

Consider either:

  1. Adding a doc comment explaining this is an uninhabited type for API compatibility
  2. Removing the impl blocks since they're unreachable

Example documentation approach:

+/// An uninhabited error type maintained for API compatibility.
+/// Since this enum has no variants, instances cannot be constructed.
 pub enum Error {}

Or remove unreachable implementations:

-impl Serialize for Error {
-    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        serializer.serialize_str(self.to_string().as_ref())
-    }
-}
-
-impl Error {
-    pub fn as_worker_error(&self) -> apalis::prelude::Error {
-        apalis::prelude::Error::Failed(std::sync::Arc::new(Box::new(std::io::Error::new(
-            std::io::ErrorKind::Other,
-            self.to_string(),
-        ))))
-    }
-}
+// Methods removed since Error is uninhabited
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 911bb84 and e057edc.

⛔ Files ignored due to path filters (21)
  • Cargo.lock is excluded by !**/*.lock
  • apps/web/supabase/functions/stripe/deno.lock is excluded by !**/*.lock
  • plugins/detect/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/detect/permissions/autogenerated/commands/list_installed_applications.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/autogenerated/commands/list_mic_using_applications.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/autogenerated/commands/reset_quit_handler.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/autogenerated/commands/set_quit_handler.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • plugins/notification/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/notification/permissions/autogenerated/commands/clear_notifications.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/commands/list_applications.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/commands/start_detect_notification.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/commands/start_event_notification.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/commands/stop_detect_notification.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/commands/stop_event_notification.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/notification/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (36)
  • Cargo.toml (1 hunks)
  • apps/desktop/package.json (3 hunks)
  • apps/desktop/src-tauri/Cargo.toml (1 hunks)
  • apps/desktop/src-tauri/capabilities/default.json (1 hunks)
  • apps/desktop/src-tauri/src/lib.rs (1 hunks)
  • apps/desktop/src/components/settings/notification.tsx (4 hunks)
  • apps/desktop/src/store/tinybase/main.ts (2 hunks)
  • apps/pro/package.json (1 hunks)
  • apps/web/package.json (2 hunks)
  • apps/web/src/functions/stripe.ts (1 hunks)
  • apps/web/supabase/functions/stripe/deno.json (1 hunks)
  • apps/web/supabase/functions/stripe/index.ts (1 hunks)
  • packages/db/package.json (1 hunks)
  • packages/tiptap/package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/utils/package.json (1 hunks)
  • plugins/detect/.gitignore (1 hunks)
  • plugins/detect/Cargo.toml (1 hunks)
  • plugins/detect/build.rs (1 hunks)
  • plugins/detect/js/index.ts (1 hunks)
  • plugins/detect/package.json (1 hunks)
  • plugins/detect/src/commands.rs (1 hunks)
  • plugins/detect/src/error.rs (1 hunks)
  • plugins/detect/src/events.rs (1 hunks)
  • plugins/detect/src/ext.rs (1 hunks)
  • plugins/detect/src/lib.rs (1 hunks)
  • plugins/detect/tsconfig.json (1 hunks)
  • plugins/notification/build.rs (1 hunks)
  • plugins/notification/src/commands.rs (1 hunks)
  • plugins/notification/src/detect.rs (0 hunks)
  • plugins/notification/src/error.rs (1 hunks)
  • plugins/notification/src/event.rs (0 hunks)
  • plugins/notification/src/ext.rs (1 hunks)
  • plugins/notification/src/handler.rs (0 hunks)
  • plugins/notification/src/lib.rs (2 hunks)
  • plugins/notification/src/quit.rs (0 hunks)
💤 Files with no reviewable changes (4)
  • plugins/notification/src/detect.rs
  • plugins/notification/src/quit.rs
  • plugins/notification/src/event.rs
  • plugins/notification/src/handler.rs
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/functions/stripe.ts
  • plugins/detect/js/index.ts
  • apps/desktop/src/components/settings/notification.tsx
  • apps/desktop/src/store/tinybase/main.ts
  • apps/web/supabase/functions/stripe/index.ts
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/desktop/src/components/settings/notification.tsx
🧠 Learnings (2)
📚 Learning: 2025-10-12T01:18:52.105Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: packages/nango/.cursor/rules/nango.mdc:0-0
Timestamp: 2025-10-12T01:18:52.105Z
Learning: Applies to packages/nango/**/{syncs,actions}/**/*.ts : Remove all debugging code and temporary artifacts before committing

Applied to files:

  • plugins/detect/.gitignore
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{ts,tsx} : After a substantial amount of TypeScript changes, run `pnpm -r typecheck`

Applied to files:

  • packages/tiptap/package.json
  • plugins/detect/tsconfig.json
🧬 Code graph analysis (9)
plugins/detect/build.rs (2)
plugins/notification/build.rs (1)
  • main (3-5)
apps/desktop/src-tauri/src/lib.rs (1)
  • main (6-116)
apps/desktop/src-tauri/src/lib.rs (1)
plugins/detect/src/lib.rs (1)
  • init (33-48)
plugins/detect/src/commands.rs (2)
crates/intercept/src/lib.rs (1)
  • setup_quit_handler (10-21)
plugins/detect/js/bindings.gen.ts (1)
  • InstalledApp (60-60)
plugins/detect/src/events.rs (1)
plugins/detect/js/bindings.gen.ts (2)
  • DetectEvent (59-59)
  • InstalledApp (60-60)
plugins/notification/src/commands.rs (1)
plugins/notification/src/ext.rs (2)
  • clear_notifications (5-5)
  • clear_notifications (16-19)
plugins/notification/src/ext.rs (1)
plugins/notification/src/commands.rs (1)
  • clear_notifications (14-18)
plugins/detect/src/lib.rs (2)
plugins/detect/js/bindings.gen.ts (3)
  • commands (9-42)
  • events (47-51)
  • DetectEvent (59-59)
plugins/detect/src/commands.rs (4)
  • set_quit_handler (3-8)
  • reset_quit_handler (12-17)
  • list_installed_applications (21-25)
  • list_mic_using_applications (29-33)
plugins/notification/src/lib.rs (3)
plugins/notification/js/bindings.gen.ts (1)
  • commands (9-26)
plugins/notification/src/commands.rs (1)
  • clear_notifications (14-18)
plugins/notification/src/ext.rs (2)
  • clear_notifications (5-5)
  • clear_notifications (16-19)
apps/desktop/src/store/tinybase/main.ts (1)
apps/desktop/src/utils/index.ts (1)
  • DEFAULT_USER_ID (6-6)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (33)
packages/ui/package.json (1)

54-54: Minor version bump is safe; consider whether newer @types/node versions align with project requirements.

The version ^22.18.13 exists on npm and the patch-level bump is safe for a devDependency. However, the latest available version is significantly newer (24.3.1). While this likely indicates an intentional choice to remain on the 22.x line for compatibility, confirm this aligns with your project's type definitions requirements. After this change, run pnpm -r typecheck to ensure type consistency across the monorepo.

packages/tiptap/package.json (1)

23-56: All package versions verified and security-checked—no issues found.

All three package versions exist with expected values and no known security vulnerabilities have been reported for @tiptap packages at version 3.9.1. The dependency updates are consistent and well-scoped, with all @tiptap packages maintaining the same minor version (v3.9.1) and using caret ranges that allow safe minor and patch updates.

packages/db/package.json (1)

23-24: LGTM!

The devDependency version bumps are minor patch updates that should be safe.

plugins/detect/tsconfig.json (1)

1-5: LGTM!

The TypeScript configuration is standard and correctly structured for the detect plugin.

As per coding guidelines, please run pnpm -r typecheck after these TypeScript changes to ensure type correctness across the workspace.

packages/utils/package.json (1)

7-7: LGTM!

The dependency version bumps are minor patch updates that should maintain compatibility.

Also applies to: 14-15

apps/desktop/src/store/tinybase/main.ts (2)

93-102: Verify that startAutoPersisting loads initial persisted data.

The persister initialization has been simplified from an explicit load/save/load lifecycle to directly calling startAutoPersisting(). Please confirm that startAutoPersisting() internally loads any existing persisted data before beginning auto-persistence. If it doesn't, this would prevent the application from restoring previously saved state.


66-66: No action needed - initialization order is safe and correct.

Setting user_id to the default before the persister loads is safe. Tinybase's startAutoPersisting() automatically calls startAutoLoad(), which loads any persisted data and overwrites the default value if data exists. If no persisted data is found, the default persists normally. This pattern correctly preserves existing persisted user_id values while providing a fallback default.

apps/web/supabase/functions/stripe/deno.json (1)

4-6: LGTM!

The dependency version updates are routine maintenance with appropriate semantic versioning ranges.

apps/pro/package.json (1)

17-17: LGTM!

The dependency updates are minor patch-level increments.

Also applies to: 22-22

apps/desktop/src-tauri/capabilities/default.json (1)

66-66: LGTM!

The detect:default permission correctly enables the newly integrated detect plugin. The permission name follows the standard Tauri plugin convention.

plugins/detect/build.rs (1)

1-10: LGTM!

The build script follows the standard Tauri plugin pattern and correctly defines the four commands that will be exposed by the detect plugin.

plugins/detect/.gitignore (1)

1-17: LGTM!

Comprehensive .gitignore patterns covering IDE files, OS metadata, build artifacts, and dependencies appropriate for a Tauri plugin.

plugins/detect/Cargo.toml (1)

1-29: LGTM!

The Cargo manifest is correctly configured for a Tauri plugin with TypeScript bindings. The links field ensures only one version of the plugin can be linked at build time, which is the correct approach.

apps/web/package.json (1)

13-61: LGTM!

The dependency updates are all minor and patch versions, following semver conventions. These routine updates should maintain backward compatibility.

apps/desktop/package.json (2)

35-35: LGTM!

Correctly adds the new detect plugin as a workspace dependency, aligning with the plugin integration objectives of this PR.


18-102: LGTM!

The dependency updates are minor and patch versions, maintaining backward compatibility. These routine updates keep the desktop app dependencies current.

plugins/detect/src/events.rs (3)

1-7: LGTM!

The macro provides a clean way to apply consistent derives for serialization and TypeScript bindings across event types.


9-17: LGTM!

The DetectEvent enum is well-structured with proper serde tagging for TypeScript interop and appropriate field naming conventions.


19-26: LGTM!

The From implementation correctly converts between the hypr_detect crate's events and the plugin's event types.

plugins/detect/src/commands.rs (3)

10-17: LGTM!

The reset_quit_handler command correctly resets the quit handler to restore normal quit behavior.


19-25: LGTM!

The command correctly delegates to the hypr_detect crate to retrieve the list of installed applications.


27-33: LGTM!

The command correctly delegates to the hypr_detect crate to retrieve applications currently using the microphone.

apps/desktop/src-tauri/Cargo.toml (1)

27-27: LGTM!

The dependency addition follows the established pattern and is properly positioned alphabetically.

plugins/detect/js/index.ts (1)

1-1: LGTM!

Standard barrel export pattern for re-exporting generated TypeScript bindings from the Tauri plugin.

apps/desktop/src-tauri/src/lib.rs (1)

60-60: LGTM!

The detect plugin is properly registered in the plugin chain. The placement before the notification plugin (line 61) is logical given the refactoring that separated detection responsibilities.

plugins/detect/package.json (1)

1-11: LGTM!

The package manifest follows Tauri plugin conventions. The codegen script correctly generates TypeScript bindings via cargo test, and the dependency on @tauri-apps/api is appropriate.

Cargo.toml (1)

116-116: LGTM!

The workspace dependency declaration is correctly formatted and positioned alphabetically with other plugin dependencies.

plugins/notification/build.rs (1)

1-1: LGTM! Clean command surface reduction.

The command list has been appropriately narrowed to the core notification operations, aligning with the refactor that moves detection capabilities to the new tauri-plugin-detect.

plugins/notification/src/ext.rs (1)

1-20: LGTM! Clean trait simplification.

The NotificationPluginExt trait has been appropriately simplified to only the core notification operations. The implementations correctly delegate to the hypr_notification crate functions.

plugins/notification/src/commands.rs (1)

1-18: LGTM! Clean command implementations.

Both commands follow consistent patterns and correctly delegate to the extension trait methods with appropriate error mapping.

plugins/notification/src/lib.rs (3)

12-20: LGTM! Specta builder correctly reflects the reduced command surface.

The command collection now properly includes only show_notification and clear_notifications, matching the changes in build.rs and commands.rs.


22-43: LGTM! Clean stateless initialization with preserved UX behavior.

The simplified setup removes all state management, which aligns with the refactor goals. The on_event handler correctly preserves the existing behavior of clearing notifications when the main window gains focus, ensuring good UX continuity.


1-43: All removed commands have been verified safe to remove.

Verification confirms no code outside the notification plugin references the removed commands (list_applications, start_detect_notification, stop_detect_notification, start_event_notification, stop_event_notification) or their associated types (DetectNotificationParams, EventNotificationParams).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

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

⚠️ Outside diff range comments (1)
plugins/listener/src/actors/source.rs (1)

283-286: Move ProcessorActor lookup outside the loop.

Same issue as in the mixed path above—looking up the ProcessorActor on every iteration is inefficient and can create a busy loop.

Apply the same refactor as suggested for lines 219-223.

🧹 Nitpick comments (11)
apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx (1)

42-67: Consider extracting the duplicated SoundIndicator configuration.

The SoundIndicator component is rendered with identical props in both the muted (lines 46-53) and non-muted (lines 58-65) branches, leading to duplication.

Apply this diff to extract the common configuration:

     className={cn([
       "text-red-500 hover:text-red-600",
       "bg-red-50 hover:bg-red-100",
       "w-[75px]",
     ])}
   >
+    {(() => {
+      const soundIndicator = (
+        <SoundIndicator
+          value={[amplitude.mic, amplitude.speaker]}
+          color="#ef4444"
+          size="long"
+          height={16}
+          width={32}
+          stickWidth={2}
+          gap={1}
+        />
+      );
+
+      if (hovered) {
+        return (
+          <div className="flex items-center gap-1.5">
+            <span className="w-3 h-3 bg-red-500 rounded-none" />
+            <span>Stop</span>
+          </div>
+        );
+      }
+
+      if (muted) {
+        return (
+          <div className="flex items-center gap-1.5">
+            <MicOff size={14} />
+            {soundIndicator}
+          </div>
+        );
+      }
+
+      return soundIndicator;
+    })()}
-    {hovered
-      ? (
-        <div className="flex items-center gap-1.5">
-          <span className="w-3 h-3 bg-red-500 rounded-none" />
-          <span>Stop</span>
-        </div>
-      )
-      : muted
-      ? (
-        <div className="flex items-center gap-1.5">
-          <MicOff size={14} />
-          <SoundIndicator
-            value={[amplitude.mic, amplitude.speaker]}
-            color="#ef4444"
-            size="long"
-            height={16}
-            width={32}
-            stickWidth={2}
-            gap={1}
-          />
-        </div>
-      )
-      : (
-        <SoundIndicator
-          value={[amplitude.mic, amplitude.speaker]}
-          color="#ef4444"
-          size="long"
-          height={16}
-          width={32}
-          stickWidth={2}
-          gap={1}
-        />
-      )}
   </Button>
plugins/listener/src/actors/source.rs (3)

204-210: Handle audio input creation errors gracefully.

Using .unwrap() on lines 204 and 210 will cause the task to panic if audio input creation fails. Consider propagating or logging these errors instead.

-            let mut mic_input = AudioInput::from_mic(mic_device).unwrap();
+            let mut mic_input = match AudioInput::from_mic(mic_device) {
+                Ok(input) => input,
+                Err(e) => {
+                    tracing::error!("Failed to create mic input: {}", e);
+                    return;
+                }
+            };

208-208: Document the purpose of the 50ms delay.

The 50ms sleep between creating mic and speaker streams (also at line 274) lacks explanation. If this is a timing workaround for hardware initialization or to avoid conflicts, please add a comment.


301-322: Note the behavioral difference from the mixed path.

In this non-mixed path, speaker data is always sent (line 317), regardless of mic mute status. This differs from the mixed path (lines 250-259) where speaker data is only sent when the mic is muted. While this might be intentional, the inconsistency could lead to confusion during maintenance.

Consider documenting why the two paths handle speaker data differently.

apps/desktop/src/contexts/listener.tsx (1)

76-94: Handle detect subscription errors and early cleanup.

detectEvents.detectEvent.listen resolves asynchronously. If it rejects (plugin unavailable, IPC failure) we get an unhandled promise rejection, and if the component unmounts before the promise resolves the listener stays registered because unlisten is assigned after cleanup runs. Please guard the lifecycle and surface failures.

Apply this diff:

   useEffect(() => {
-    let unlisten: (() => void) | undefined;
-
-    detectEvents.detectEvent.listen(({ payload }) => {
+    let unlisten: (() => void) | undefined;
+    let disposed = false;
+
+    detectEvents.detectEvent
+      .listen(({ payload }) => {
         if (payload.type === "micStarted") {
           console.log("mic_started", payload.apps);
         } else if (payload.type === "micStopped") {
           stop();
         } else if (payload.type === "micMuted") {
           setMuted(payload.value);
         }
-    }).then((fn) => {
-      unlisten = fn;
-    });
+      })
+      .then((fn) => {
+        if (disposed) {
+          fn();
+          return;
+        }
+        unlisten = fn;
+      })
+      .catch((error) => {
+        console.error("failed to subscribe detectEvent", error);
+      });
 
     return () => {
+      disposed = true;
       unlisten?.();
     };
   }, [stop, setMuted]);
apps/desktop/src/store/zustand/listener/general.ts (1)

190-197: Call setMicMuted outside set and catch failures.

listenerCommands.setMicMuted returns a promise; invoking it inside mutate without handling the result risks unhandled rejections (e.g., when no session is active) and hides the async side effect inside state mutation. Update state first, then call the command (guarding/logging errors) so we keep Zustand synchronous.

Apply this diff:

   setMuted: (value) => {
-    set((state) =>
-      mutate(state, (draft) => {
-        draft.muted = value;
-        listenerCommands.setMicMuted(value);
-      })
-    );
+    set((state) =>
+      mutate(state, (draft) => {
+        draft.muted = value;
+      }),
+    );
+
+    void listenerCommands.setMicMuted(value).catch((error) => {
+      console.error("failed to set mic mute state", error);
+    });
   },
plugins/detect/src/events.rs (1)

15-15: Consider using a unit variant for MicStopped.

The empty struct syntax MicStopped {} works but a unit variant MicStopped would be more idiomatic for variants with no associated data.

-        MicStopped {},
+        MicStopped,
crates/detect/src/zoom.rs (1)

20-34: Consider if 1-second polling interval is optimal.

The 1-second poll interval will continuously execute AppleScript every second while Zoom is using the mic. Depending on system load and battery impact considerations, you might want to consider a longer interval (e.g., 2-3 seconds) or make it configurable.

apps/desktop/src/hooks/use-permissions.ts (2)

9-44: Consider optimizing the polling frequency.

All three permission queries use a 1-second refetchInterval, resulting in 3 requests per second when all are active. Depending on the performance requirements and system load, you might want to:

  • Increase the interval to 2-3 seconds
  • Use event-based updates instead of polling where possible
  • Make the interval configurable

46-80: Extract hardcoded timeout values to constants.

The setTimeout delays (1000ms on lines 49 and 75, 2000ms on line 65) are magic numbers that could be extracted as named constants for better maintainability.

Example:

const PERMISSION_REFETCH_DELAY = 1000;
const RELAUNCH_DELAY = 2000;

// Then use:
setTimeout(() => {
  micPermissionStatus.refetch();
}, PERMISSION_REFETCH_DELAY);
apps/desktop/src/routes/app/onboarding.tsx (1)

120-120: Potential redundant "Continue" UI elements.

When all permissions are granted, both elements render:

  • Line 120: The action prop creates a "next" text link at the bottom (rendered by OnboardingContainer)
  • Lines 149-153: A primary "Continue" button

This provides two identical ways to proceed. Consider either:

  1. Removing the action prop when permissions are granted
  2. Removing the separate Button component

Apply this diff to remove the redundancy:

     <OnboardingContainer
       title="Just three quick permissions before we begin"
       description="After you grant system audio access, app will restart to apply the changes"
-      action={{ kind: "next", hide: !allPermissionsGranted, onClick: () => goNext() }}
     >

Also applies to: 149-153

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e057edc and b89e75e.

⛔ Files ignored due to path filters (44)
  • Cargo.lock is excluded by !**/*.lock
  • plugins/detect/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/detect/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/detect/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • plugins/listener/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/listener/permissions/autogenerated/commands/check_microphone_access.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/check_system_audio_access.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/get_speaker_muted.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/open_microphone_access_settings.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/open_system_audio_access_settings.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/request_microphone_access.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/request_system_audio_access.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/commands/set_speaker_muted.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/listener/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • plugins/local-llm/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/local-llm/permissions/autogenerated/commands/generate_tags.toml is excluded by !plugins/**/permissions/**
  • plugins/local-llm/permissions/autogenerated/commands/generate_title.toml is excluded by !plugins/**/permissions/**
  • plugins/local-llm/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/local-llm/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/local-llm/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • plugins/permissions/.gitignore is excluded by !plugins/**/permissions/**
  • plugins/permissions/Cargo.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/build.rs is excluded by !plugins/**/permissions/**
  • plugins/permissions/js/bindings.gen.ts is excluded by !**/*.gen.ts, !plugins/**/permissions/**
  • plugins/permissions/js/index.ts is excluded by !plugins/**/permissions/**
  • plugins/permissions/package.json is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/check_accessibility_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/check_microphone_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/check_system_audio_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/request_accessibility_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/request_microphone_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/commands/request_system_audio_permission.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/permissions/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • plugins/permissions/src/commands.rs is excluded by !plugins/**/permissions/**
  • plugins/permissions/src/error.rs is excluded by !plugins/**/permissions/**
  • plugins/permissions/src/lib.rs is excluded by !plugins/**/permissions/**
  • plugins/permissions/src/models.rs is excluded by !plugins/**/permissions/**
  • plugins/permissions/tsconfig.json is excluded by !plugins/**/permissions/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (34)
  • Cargo.toml (1 hunks)
  • apps/desktop/package.json (3 hunks)
  • apps/desktop/src-tauri/Cargo.toml (2 hunks)
  • apps/desktop/src-tauri/capabilities/default.json (1 hunks)
  • apps/desktop/src-tauri/src/lib.rs (2 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx (3 hunks)
  • apps/desktop/src/components/settings/general/permissions.tsx (2 hunks)
  • apps/desktop/src/components/shared/permission-row.tsx (1 hunks)
  • apps/desktop/src/contexts/listener.tsx (3 hunks)
  • apps/desktop/src/hooks/use-permissions.ts (1 hunks)
  • apps/desktop/src/routes/app/onboarding.tsx (4 hunks)
  • apps/desktop/src/store/zustand/listener/general.ts (4 hunks)
  • crates/detect/Cargo.toml (0 hunks)
  • crates/detect/src/lib.rs (2 hunks)
  • crates/detect/src/zoom.rs (1 hunks)
  • crates/llama/src/lib.rs (0 hunks)
  • plugins/detect/Cargo.toml (1 hunks)
  • plugins/detect/src/commands.rs (1 hunks)
  • plugins/detect/src/events.rs (1 hunks)
  • plugins/detect/src/lib.rs (1 hunks)
  • plugins/listener/Cargo.toml (0 hunks)
  • plugins/listener/build.rs (0 hunks)
  • plugins/listener/src/actors/session.rs (0 hunks)
  • plugins/listener/src/actors/source.rs (2 hunks)
  • plugins/listener/src/commands.rs (1 hunks)
  • plugins/listener/src/events.rs (0 hunks)
  • plugins/listener/src/ext.rs (0 hunks)
  • plugins/listener/src/lib.rs (0 hunks)
  • plugins/local-llm/build.rs (0 hunks)
  • plugins/local-llm/src/commands.rs (1 hunks)
  • plugins/local-llm/src/events.rs (0 hunks)
  • plugins/local-llm/src/ext/mod.rs (0 hunks)
  • plugins/local-llm/src/ext/task.rs (0 hunks)
  • plugins/local-llm/src/lib.rs (0 hunks)
💤 Files with no reviewable changes (13)
  • plugins/local-llm/src/ext/mod.rs
  • plugins/listener/src/events.rs
  • crates/llama/src/lib.rs
  • plugins/listener/src/lib.rs
  • plugins/listener/src/actors/session.rs
  • plugins/listener/Cargo.toml
  • crates/detect/Cargo.toml
  • plugins/local-llm/src/events.rs
  • plugins/local-llm/src/ext/task.rs
  • plugins/local-llm/src/lib.rs
  • plugins/listener/build.rs
  • plugins/listener/src/ext.rs
  • plugins/local-llm/build.rs
🚧 Files skipped from review as they are similar to previous changes (4)
  • Cargo.toml
  • plugins/detect/src/commands.rs
  • apps/desktop/src-tauri/capabilities/default.json
  • apps/desktop/src-tauri/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/desktop/src/store/zustand/listener/general.ts
  • apps/desktop/src/contexts/listener.tsx
  • apps/desktop/src/components/shared/permission-row.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx
  • apps/desktop/src/hooks/use-permissions.ts
  • apps/desktop/src/routes/app/onboarding.tsx
  • apps/desktop/src/components/settings/general/permissions.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/desktop/src/contexts/listener.tsx
  • apps/desktop/src/components/shared/permission-row.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx
  • apps/desktop/src/routes/app/onboarding.tsx
  • apps/desktop/src/components/settings/general/permissions.tsx
🧠 Learnings (1)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx
🧬 Code graph analysis (12)
apps/desktop/src-tauri/src/lib.rs (2)
plugins/detect/src/lib.rs (1)
  • init (36-60)
plugins/permissions/src/lib.rs (1)
  • init (24-31)
apps/desktop/src/contexts/listener.tsx (4)
apps/desktop/src/store/zustand/listener/index.ts (1)
  • ListenerStore (10-10)
crates/detect/src/lib.rs (2)
  • stop (39-39)
  • stop (57-62)
crates/detect/src/zoom.rs (1)
  • stop (140-142)
plugins/listener/src/ext.rs (2)
  • state (95-95)
  • state (118-118)
crates/detect/src/zoom.rs (2)
plugins/detect/js/bindings.gen.ts (1)
  • DetectEvent (59-59)
crates/detect/src/lib.rs (4)
  • start (38-38)
  • start (50-55)
  • stop (39-39)
  • stop (57-62)
plugins/detect/src/events.rs (1)
plugins/detect/js/bindings.gen.ts (2)
  • DetectEvent (59-59)
  • InstalledApp (60-60)
plugins/local-llm/src/commands.rs (1)
plugins/local-llm/js/bindings.gen.ts (4)
  • CustomModelInfo (142-142)
  • ModelInfo (144-144)
  • ModelSelection (145-145)
  • SupportedModel (146-146)
apps/desktop/src/components/shared/permission-row.tsx (4)
plugins/permissions/js/bindings.gen.ts (1)
  • PermissionStatus (70-70)
packages/utils/src/cn.ts (1)
  • cn (20-22)
packages/ui/src/components/ui/button.tsx (1)
  • Button (53-53)
packages/ui/src/components/ui/spinner.tsx (1)
  • Spinner (58-58)
apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx (2)
apps/desktop/src/contexts/listener.tsx (1)
  • useListener (34-48)
apps/desktop/src/components/main/body/shared.tsx (1)
  • SoundIndicator (134-164)
apps/desktop/src/routes/app/onboarding.tsx (2)
apps/desktop/src/hooks/use-permissions.ts (1)
  • usePermissions (8-141)
apps/desktop/src/components/shared/permission-row.tsx (1)
  • PermissionRow (8-139)
plugins/listener/src/actors/source.rs (2)
crates/audio/src/lib.rs (2)
  • from_mic (111-120)
  • from_speaker (122-129)
plugins/listener/src/actors/processor.rs (2)
  • new (153-158)
  • name (40-42)
crates/detect/src/lib.rs (5)
plugins/detect/js/bindings.gen.ts (2)
  • DetectEvent (59-59)
  • InstalledApp (60-60)
crates/detect/src/zoom.rs (2)
  • start (95-138)
  • stop (140-142)
crates/detect/src/mic/mod.rs (2)
  • start (17-19)
  • stop (20-22)
crates/detect/src/app/mod.rs (2)
  • start (17-19)
  • stop (20-22)
crates/detect/src/utils.rs (2)
  • start (37-54)
  • stop (56-68)
apps/desktop/src/components/settings/general/permissions.tsx (2)
apps/desktop/src/hooks/use-permissions.ts (1)
  • usePermissions (8-141)
apps/desktop/src/components/shared/permission-row.tsx (1)
  • PermissionRow (8-139)
plugins/detect/src/lib.rs (3)
plugins/detect/src/commands.rs (4)
  • set_quit_handler (5-20)
  • reset_quit_handler (24-29)
  • list_installed_applications (33-37)
  • list_mic_using_applications (41-45)
crates/detect/src/lib.rs (1)
  • new_callback (30-35)
plugins/detect/src/events.rs (1)
  • from (22-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (23)
apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx (2)

2-2: LGTM!

The MicOff import is correctly added and used in the muted state rendering.


12-17: LGTM!

The state selection correctly includes the muted property following the existing pattern.

plugins/local-llm/src/commands.rs (2)

3-142: Remaining commands look solid.

All 16 Tauri commands are properly structured with correct annotations, consistent error handling, and clear responsibilities. The code follows established patterns and integrates well with the LocalLlmPluginExt trait methods.


1-1: Import cleanup verified—no dangling references.

The removal of LocalLlmTaskExt from the imports is correct. Verification confirms no remaining references to the removed trait or functions (generate_title, generate_tags) exist in the codebase. All remaining imports are actively used in the command definitions.

apps/desktop/package.json (3)

35-36: New workspace dependencies align with PR objectives.

The addition of @hypr/plugin-detect and @hypr/plugin-permissions aligns with the PR's goal of integrating detection and permission handling into the desktop app. Verify that these dependencies are properly initialized in the Tauri configuration and wired into the app startup sequence.


18-25: Coordinated dependency version bumps across AI SDKs, Tauri plugins, and utilities.

Multiple dependencies have been updated with consistent patch/minor version increments (AI SDK packages, Tauri plugins, router, state management, and dev tooling). These appear intentional and coordinated. Ensure that these updates have been tested together to avoid incompatibilities.

Confirm that the integrated test suite (if available) passes with all these updated versions.

Also applies to: 42-42, 49-49, 53-53, 56-62, 66-67, 81-81, 83-83, 90-92, 94-94, 101-101


26-26: No breaking changes documented for @electric-sql/client 1.1.1.

Version 1.1.1 is a patch-level release with no documented breaking changes. Recommended actions: update the package, regenerate your generated client if applicable, run your test suite, and verify runtime integrations (shapes, wasm bundling, and db.raw → db.rawQuery/db.unsafeExec if you previously relied on the older APIs).

plugins/listener/src/commands.rs (1)

1-73: LGTM! Clean command surface reduction.

The remaining commands are well-structured and consistent. The removal of permission-related and speaker mute commands aligns with the broader architectural changes in this PR.

plugins/listener/src/actors/source.rs (4)

18-41: LGTM! Type definitions align with speaker mute removal.

The SourceMsg enum and SourceState struct correctly reflect the removal of speaker mute functionality, focusing only on microphone control.


56-124: LGTM! Initialization is correct.

The state initialization correctly omits speaker mute fields and properly sets up device monitoring and audio streams.


126-161: LGTM! Message handling is correct.

The handler properly manages mic mute state and device changes, including restarting the source loop when the device changes.


237-259: Verify the conditional speaker data routing logic.

The current logic sends speaker data only when the microphone is muted (lines 252-255), and when the mic is not muted, only Mixed data is sent (line 243). This asymmetry is unusual:

  • When mic unmuted: send ProcMsg::Mixed (presumably mic + speaker combined)
  • When mic muted: send ProcMsg::Mic (zeros) + ProcMsg::Speaker (separate)

Please confirm this is the intended behavior. It would be clearer if the logic and the reason for this conditional routing were documented.

plugins/detect/src/events.rs (1)

21-32: LGTM!

The From implementation correctly maps platform-specific events and aligns with the TypeScript bindings.

apps/desktop/src/components/settings/general/permissions.tsx (1)

1-44: LGTM! Clean refactoring to centralized permissions.

The migration to the usePermissions hook and shared PermissionRow component successfully consolidates permission logic and reduces code duplication.

crates/detect/src/zoom.rs (2)

36-87: LGTM! Robust AppleScript-based state detection.

The function properly handles all expected states (muted, unmuted, not running, unknown) with appropriate logging and error handling.


94-143: LGTM! Well-structured async observer pattern.

The implementation correctly:

  • Prevents redundant event emissions by tracking last state
  • Clears state when Zoom stops using the mic
  • Uses tokio::select! for graceful shutdown
  • Handles the full lifecycle appropriately
apps/desktop/src/hooks/use-permissions.ts (1)

82-141: LGTM! Well-structured action handlers.

The helper functions and action handlers correctly distinguish between denied status (opening system settings) and other states (triggering permission requests). The returned API is comprehensive and well-organized.

plugins/detect/src/lib.rs (3)

18-21: LGTM: State struct correctly keeps detector alive.

The allow(dead_code) attribute is appropriate here. The detector field is never directly accessed after initialization, but storing it in the managed state keeps the detector running by preventing it from being dropped.


36-60: LGTM: Plugin initialization correctly manages detector lifecycle.

The initialization sequence properly:

  • Starts the detector with an event-emitting callback
  • Stores the detector in managed state to keep it alive
  • Uses a cloned app_handle to avoid potential ownership issues

The detector will continue running as long as the managed state exists.


62-78: LGTM: TypeScript bindings export is properly configured.

The test correctly generates TypeScript bindings with appropriate formatting and type handling for the plugin's commands and events.

apps/desktop/src/routes/app/onboarding.tsx (3)

3-4: LGTM: Imports correctly support the centralized permission architecture.

The new imports enable the shared PermissionRow component and centralized permission management, improving code reusability.

Also applies to: 11-12


100-114: LGTM: Permission logic correctly centralized and validated.

The hook integration properly manages all three permissions (microphone, system audio, accessibility) and the allPermissionsGranted check correctly requires all three to proceed.


139-146: LGTM: Accessibility permission properly integrated.

The new accessibility permission row follows the same pattern as the other permissions, with clear messaging about its purpose for detecting active applications.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/desktop/src/components/settings/notification.tsx (1)

70-75: Critical: Notification commands not invoked—feature is broken.

The onSubmit function only persists settings but does not invoke the actual notification start/stop commands. Users can toggle switches, but no notifications will start or stop because the underlying services are never called.

This was flagged in a previous review and must be fixed before merging.

Restore the notification command invocations:

    onSubmit: async ({ value }) => {
      handleSetNotificationEvent(value.notification_event);
      handleSetNotificationDetect(value.notification_detect);
      handleSetRespectDnd(value.respect_dnd);
      handleSetIgnoredPlatforms(value.ignored_platforms);
+
+     // Start/stop event notifications
+     if (value.notification_event) {
+       await notificationCommands.startEventNotification();
+     } else {
+       await notificationCommands.stopEventNotification();
+     }
+
+     // Start/stop detect notifications
+     if (value.notification_detect) {
+       await notificationCommands.startDetectNotification({
+         respect_do_not_disturb: value.respect_dnd,
+         ignored_platforms: value.ignored_platforms,
+       });
+     } else {
+       await notificationCommands.stopDetectNotification();
+     }
    },
🧹 Nitpick comments (3)
plugins/detect/Cargo.toml (1)

4-4: Fill in placeholder author and description metadata.

Lines 4 and 8 contain placeholder/empty values that should be replaced with actual author information and a meaningful description of the plugin's purpose.

-authors = ["You"]
+authors = ["Your Name/Organization"]
 
-description = ""
+description = "Tauri plugin for detecting system state or events"

Also applies to: 8-8

plugins/detect/src/events.rs (1)

19-20: Consider renaming for consistency.

The variant name MicMuteStateChanged is shortened to micMuted in the JSON/TypeScript layer, which breaks the naming pattern established by the other variants (e.g., MicStartedmicStarted). Additionally, micMuted reads more like a state property than an event name.

Consider renaming either the variant to MicMuted or the serde rename to micMuteStateChanged for better consistency across the Rust and TypeScript boundaries.

apps/desktop/src/contexts/listener.tsx (1)

85-86: Consider more descriptive notification text.

The title and message are nearly identical ("Mic Started" vs "Mic started"). Consider making the message more descriptive or removing it if the title is sufficient.

Example:

 notificationCommands.showNotification({
   key: payload.key,
   title: "Mic Started",
-  message: "Mic started",
+  message: "Your microphone is now active",
   url: null,
   timeout: { secs: 8, nanos: 0 },
 });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b89e75e and 5ee3a7e.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • plugins/detect/js/bindings.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (4)
  • apps/desktop/src/components/settings/notification.tsx (5 hunks)
  • apps/desktop/src/contexts/listener.tsx (3 hunks)
  • plugins/detect/Cargo.toml (1 hunks)
  • plugins/detect/src/events.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/desktop/src/components/settings/notification.tsx
  • apps/desktop/src/contexts/listener.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/desktop/src/components/settings/notification.tsx
  • apps/desktop/src/contexts/listener.tsx
🧬 Code graph analysis (2)
plugins/detect/src/events.rs (1)
plugins/detect/js/bindings.gen.ts (2)
  • DetectEvent (59-59)
  • InstalledApp (60-60)
apps/desktop/src/contexts/listener.tsx (1)
apps/desktop/src/store/zustand/listener/index.ts (1)
  • ListenerStore (10-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (13)
plugins/detect/Cargo.toml (1)

10-31: Dependency configuration looks good.

The workspace-managed dependencies and feature selections are appropriate for a Tauri plugin with specta-based TypeScript binding generation and async runtime support.

apps/desktop/src/components/settings/notification.tsx (6)

1-7: LGTM: Imports are appropriate.

The addition of useQuery and the dual import of both detectCommands and notificationCommands align with the refactored implementation.


21-28: Verify immediate cleanup on mount.

The cleanup function runs both on mount and unmount, which means existing notifications are cleared as soon as the settings page loads. Confirm this is intentional—it may discard active notifications before the user makes any changes.

Consider removing the immediate cleanup() call on line 26 if notifications should only be cleared when leaving the settings page:

  useEffect(() => {
    const cleanup = () => {
      notificationCommands.clearNotifications();
    };

-   cleanup();
    return cleanup;
  }, []);

78-89: Verify error handling for query failures.

The select function throws an error when result.status === "error", which will put the query into an error state. Confirm that an error boundary or error UI exists to inform users if fetching installed applications fails.

Consider displaying the error state in the UI:

const { data: installedApps, error } = useQuery({
  enabled: popoverOpen,
  queryKey: ["installed-applications"],
  queryFn: detectCommands.listInstalledApplications,
  select: (result) => {
    if (result.status === "error") {
      throw new Error(result.error);
    }
    return result.data.map((app) => app.name);
  },
});

Then show the error near the popover or command list if error is truthy.


173-176: LGTM: Cleaner popover integration.

Simplifying onOpenChange to just toggle state while letting the useQuery enabled flag handle data fetching is a good refactor.


216-225: LGTM: Safe data handling.

The use of installedApps ?? [] provides a safe fallback, and the filtering logic correctly excludes already-ignored platforms.


145-145: LGTM: Follows coding guidelines.

The use of cn with an array notation complies with the project's coding guidelines.

As per coding guidelines

plugins/detect/src/events.rs (1)

1-7: LGTM!

The macro correctly applies the necessary derives for Tauri event serialization and TypeScript type generation.

apps/desktop/src/contexts/listener.tsx (5)

5-5: LGTM!

The new imports are correctly structured and necessary for the event detection and notification functionality.

Also applies to: 7-7


21-21: LGTM!

The hook is correctly invoked with the store parameter within the ListenerProvider component.


73-75: LGTM!

The hook definition and store selectors are correctly structured.


88-88: The timeout format is correct.

The timeout value { secs: 8, nanos: 0 } matches the expected Duration type defined in the notification plugin bindings ({ secs: number; nanos: number }). No changes are needed.


5-104: Run typecheck to verify type safety.

After these TypeScript changes—particularly with the new plugin integrations and generic hook types—run pnpm -r typecheck to ensure type correctness across the codebase.

@yujonglee yujonglee merged commit 0ddcbba into main Oct 30, 2025
3 checks passed
@yujonglee yujonglee deleted the integrate-notif branch October 30, 2025 09:17
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.

2 participants