-
Notifications
You must be signed in to change notification settings - Fork 414
Integrate notification to the new bundle #1606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds 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
Sequence Diagram(s)mermaid (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:
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
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. Comment |
There was a problem hiding this 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
DetectPluginExttrait 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
Errorenum is valid but creates an uninhabited type, meaningResult<T>can only ever beOk(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
Errorenum 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, theSerializeimpl andas_worker_errormethod can never be executed sinceselfcannot exist.Consider either:
- Adding a doc comment explaining this is an uninhabited type for API compatibility
- 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
⛔ Files ignored due to path filters (21)
Cargo.lockis excluded by!**/*.lockapps/web/supabase/functions/stripe/deno.lockis excluded by!**/*.lockplugins/detect/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/detect/permissions/autogenerated/commands/list_installed_applications.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/autogenerated/commands/list_mic_using_applications.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/autogenerated/commands/reset_quit_handler.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/autogenerated/commands/set_quit_handler.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/detect/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**plugins/notification/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/notification/permissions/autogenerated/commands/clear_notifications.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/commands/list_applications.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/commands/start_detect_notification.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/commands/start_event_notification.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/commands/stop_detect_notification.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/commands/stop_event_notification.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/notification/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/notification/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**pnpm-lock.yamlis 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.tsplugins/detect/js/index.tsapps/desktop/src/components/settings/notification.tsxapps/desktop/src/store/tinybase/main.tsapps/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, usecnfrom@hypr/utils
When usingcn, always pass an array of class segments
When usingcn, split entries by logical grouping for readability
Usemotion/reactinstead offramer-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.jsonplugins/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.13exists 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, runpnpm -r typecheckto 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 typecheckafter 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 thatstartAutoPersisting()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_idto the default before the persister loads is safe. Tinybase'sstartAutoPersisting()automatically callsstartAutoLoad(), 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 persisteduser_idvalues 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:defaultpermission 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
linksfield 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/apiis 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
NotificationPluginExttrait has been appropriately simplified to only the core notification operations. The implementations correctly delegate to thehypr_notificationcrate 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_notificationandclear_notifications, matching the changes inbuild.rsandcommands.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_eventhandler 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).
There was a problem hiding this 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
SoundIndicatorcomponent 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.listenresolves 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 becauseunlistenis 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: CallsetMicMutedoutsidesetand catch failures.
listenerCommands.setMicMutedreturns a promise; invoking it insidemutatewithout 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 forMicStopped.The empty struct syntax
MicStopped {}works but a unit variantMicStoppedwould 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
setTimeoutdelays (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
actionprop creates a "next" text link at the bottom (rendered byOnboardingContainer)- Lines 149-153: A primary "Continue" button
This provides two identical ways to proceed. Consider either:
- Removing the action prop when permissions are granted
- 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
⛔ Files ignored due to path filters (44)
Cargo.lockis excluded by!**/*.lockplugins/detect/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/detect/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/detect/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/detect/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**plugins/listener/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/listener/permissions/autogenerated/commands/check_microphone_access.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/check_system_audio_access.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/get_speaker_muted.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/open_microphone_access_settings.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/open_system_audio_access_settings.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/request_microphone_access.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/request_system_audio_access.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/commands/set_speaker_muted.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/listener/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/listener/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**plugins/local-llm/js/bindings.gen.tsis excluded by!**/*.gen.tsplugins/local-llm/permissions/autogenerated/commands/generate_tags.tomlis excluded by!plugins/**/permissions/**plugins/local-llm/permissions/autogenerated/commands/generate_title.tomlis excluded by!plugins/**/permissions/**plugins/local-llm/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/local-llm/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/local-llm/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**plugins/permissions/.gitignoreis excluded by!plugins/**/permissions/**plugins/permissions/Cargo.tomlis excluded by!plugins/**/permissions/**plugins/permissions/build.rsis excluded by!plugins/**/permissions/**plugins/permissions/js/bindings.gen.tsis excluded by!**/*.gen.ts,!plugins/**/permissions/**plugins/permissions/js/index.tsis excluded by!plugins/**/permissions/**plugins/permissions/package.jsonis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/check_accessibility_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/check_microphone_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/check_system_audio_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/request_accessibility_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/request_microphone_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/commands/request_system_audio_permission.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/autogenerated/reference.mdis excluded by!plugins/**/permissions/**plugins/permissions/permissions/default.tomlis excluded by!plugins/**/permissions/**plugins/permissions/permissions/schemas/schema.jsonis excluded by!plugins/**/permissions/**plugins/permissions/src/commands.rsis excluded by!plugins/**/permissions/**plugins/permissions/src/error.rsis excluded by!plugins/**/permissions/**plugins/permissions/src/lib.rsis excluded by!plugins/**/permissions/**plugins/permissions/src/models.rsis excluded by!plugins/**/permissions/**plugins/permissions/tsconfig.jsonis excluded by!plugins/**/permissions/**pnpm-lock.yamlis 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.tsapps/desktop/src/contexts/listener.tsxapps/desktop/src/components/shared/permission-row.tsxapps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsxapps/desktop/src/hooks/use-permissions.tsapps/desktop/src/routes/app/onboarding.tsxapps/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, usecnfrom@hypr/utils
When usingcn, always pass an array of class segments
When usingcn, split entries by logical grouping for readability
Usemotion/reactinstead offramer-motion
Files:
apps/desktop/src/contexts/listener.tsxapps/desktop/src/components/shared/permission-row.tsxapps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsxapps/desktop/src/routes/app/onboarding.tsxapps/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
MicOffimport is correctly added and used in the muted state rendering.
12-17: LGTM!The state selection correctly includes the
mutedproperty 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
LocalLlmPluginExttrait methods.
1-1: Import cleanup verified—no dangling references.The removal of
LocalLlmTaskExtfrom 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-detectand@hypr/plugin-permissionsaligns 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
SourceMsgenum andSourceStatestruct 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
Fromimplementation 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
usePermissionshook and sharedPermissionRowcomponent 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
allPermissionsGrantedcheck 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.
apps/desktop/src/components/main/body/sessions/outer-header/in-meeting-indicator.tsx
Show resolved
Hide resolved
There was a problem hiding this 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
onSubmitfunction 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
MicMuteStateChangedis shortened tomicMutedin the JSON/TypeScript layer, which breaks the naming pattern established by the other variants (e.g.,MicStarted→micStarted). Additionally,micMutedreads more like a state property than an event name.Consider renaming either the variant to
MicMutedor the serde rename tomicMuteStateChangedfor 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
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.lockplugins/detect/js/bindings.gen.tsis 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.tsxapps/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, usecnfrom@hypr/utils
When usingcn, always pass an array of class segments
When usingcn, split entries by logical grouping for readability
Usemotion/reactinstead offramer-motion
Files:
apps/desktop/src/components/settings/notification.tsxapps/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
useQueryand the dual import of bothdetectCommandsandnotificationCommandsalign 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
selectfunction throws an error whenresult.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
erroris truthy.
173-176: LGTM: Cleaner popover integration.Simplifying
onOpenChangeto just toggle state while letting theuseQueryenabledflag 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
cnwith 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 expectedDurationtype 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 typecheckto ensure type correctness across the codebase.
No description provided.