From 44990b490a42d02530770b13870865223b8be50d Mon Sep 17 00:00:00 2001 From: plyght Date: Sun, 27 Jul 2025 12:22:10 -0400 Subject: [PATCH 1/3] init --- apps/desktop/src/components/editor-area/index.tsx | 8 ++++++++ plugins/notification/js/bindings.gen.ts | 3 +++ plugins/notification/src/commands.rs | 9 +++++++++ plugins/notification/src/ext.rs | 15 +++++++++++++++ plugins/notification/src/lib.rs | 1 + 5 files changed, 36 insertions(+) diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index bfc7a2bb1..470550ebc 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -13,6 +13,7 @@ import { commands as analyticsCommands } from "@hypr/plugin-analytics"; import { commands as connectorCommands } from "@hypr/plugin-connector"; import { commands as dbCommands } from "@hypr/plugin-db"; import { commands as miscCommands } from "@hypr/plugin-misc"; +import { commands as notificationCommands } from "@hypr/plugin-notification"; import { commands as templateCommands, type Grammar } from "@hypr/plugin-template"; import Editor, { type TiptapEditor } from "@hypr/tiptap/editor"; import Renderer from "@hypr/tiptap/renderer"; @@ -458,6 +459,13 @@ export function useEnhanceMutation({ if (actualIsLocalLlm) { setProgress(0); + + // Show native notification for local AI model summarization completion + if (sessionId !== onboardingSessionId) { + notificationCommands.showSummarizationCompleteNotification().catch(error => { + console.error("Failed to show summarization complete notification:", error); + }); + } } setEnhanceController(null); diff --git a/plugins/notification/js/bindings.gen.ts b/plugins/notification/js/bindings.gen.ts index 98b9d2659..0d2606ece 100644 --- a/plugins/notification/js/bindings.gen.ts +++ b/plugins/notification/js/bindings.gen.ts @@ -39,6 +39,9 @@ async startEventNotification() : Promise { }, async stopEventNotification() : Promise { return await TAURI_INVOKE("plugin:notification|stop_event_notification"); +}, +async showSummarizationCompleteNotification() : Promise { + return await TAURI_INVOKE("plugin:notification|show_summarization_complete_notification"); } } diff --git a/plugins/notification/src/commands.rs b/plugins/notification/src/commands.rs index 23fd5a835..496344683 100644 --- a/plugins/notification/src/commands.rs +++ b/plugins/notification/src/commands.rs @@ -98,3 +98,12 @@ pub(crate) async fn stop_event_notification( ) -> Result<(), String> { app.stop_event_notification().map_err(|e| e.to_string()) } + +#[tauri::command] +#[specta::specta] +pub(crate) async fn show_summarization_complete_notification( + app: tauri::AppHandle, +) -> Result<(), String> { + app.show_summarization_complete_notification() + .map_err(|e| e.to_string()) +} diff --git a/plugins/notification/src/ext.rs b/plugins/notification/src/ext.rs index 0d41de5f3..77c1bd23d 100644 --- a/plugins/notification/src/ext.rs +++ b/plugins/notification/src/ext.rs @@ -24,6 +24,8 @@ pub trait NotificationPluginExt { fn check_notification_permission( &self, ) -> impl Future>; + + fn show_summarization_complete_notification(&self) -> Result<(), Error>; } impl> NotificationPluginExt for T { @@ -162,4 +164,17 @@ impl> NotificationPluginExt for T { .await .map_err(|_| Error::PermissionTimeout)? } + + #[tracing::instrument(skip(self))] + fn show_summarization_complete_notification(&self) -> Result<(), Error> { + let notif = hypr_notification2::Notification { + title: "Meeting Summary Complete".to_string(), + message: "Your meeting has been summarized by local AI".to_string(), + url: Some("hypr://hyprnote.com/notification".to_string()), + timeout: Some(std::time::Duration::from_secs(5)), + }; + + hypr_notification2::show(notif); + Ok(()) + } } diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index 6ca9b3ae1..e8167a1b8 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -36,6 +36,7 @@ fn make_specta_builder() -> tauri_specta::Builder { commands::stop_detect_notification::, commands::start_event_notification::, commands::stop_event_notification::, + commands::show_summarization_complete_notification::, ]) .error_handling(tauri_specta::ErrorHandlingMode::Throw) } From ee01bdd4ca28cb51f35179bf9fe49448c60dda6b Mon Sep 17 00:00:00 2001 From: plyght Date: Sun, 27 Jul 2025 12:27:58 -0400 Subject: [PATCH 2/3] coderabbit fix --- apps/desktop/src/components/editor-area/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 470550ebc..55ab89076 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -462,9 +462,7 @@ export function useEnhanceMutation({ // Show native notification for local AI model summarization completion if (sessionId !== onboardingSessionId) { - notificationCommands.showSummarizationCompleteNotification().catch(error => { - console.error("Failed to show summarization complete notification:", error); - }); + notificationCommands.showSummarizationCompleteNotification(); } } From 9bacc2d2cb52184c97061721216c6227f9d0f76a Mon Sep 17 00:00:00 2001 From: plyght Date: Sun, 27 Jul 2025 16:26:21 -0400 Subject: [PATCH 3/3] add toggle --- .../src/components/editor-area/index.tsx | 6 ++- .../settings/views/notifications.tsx | 49 ++++++++++++++++++- apps/desktop/src/locales/en/messages.po | 16 ++++-- apps/desktop/src/locales/ko/messages.po | 16 ++++-- plugins/notification/js/bindings.gen.ts | 6 +++ plugins/notification/src/commands.rs | 19 +++++++ plugins/notification/src/ext.rs | 20 ++++++++ plugins/notification/src/lib.rs | 2 + plugins/notification/src/store.rs | 1 + 9 files changed, 125 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/components/editor-area/index.tsx b/apps/desktop/src/components/editor-area/index.tsx index 55ab89076..a11fb3126 100644 --- a/apps/desktop/src/components/editor-area/index.tsx +++ b/apps/desktop/src/components/editor-area/index.tsx @@ -462,7 +462,11 @@ export function useEnhanceMutation({ // Show native notification for local AI model summarization completion if (sessionId !== onboardingSessionId) { - notificationCommands.showSummarizationCompleteNotification(); + notificationCommands.getSummarizationNotification().then(enabled => { + if (enabled) { + notificationCommands.showSummarizationCompleteNotification(); + } + }); } } diff --git a/apps/desktop/src/components/settings/views/notifications.tsx b/apps/desktop/src/components/settings/views/notifications.tsx index 7e65ba6e9..403ba2721 100644 --- a/apps/desktop/src/components/settings/views/notifications.tsx +++ b/apps/desktop/src/components/settings/views/notifications.tsx @@ -12,6 +12,7 @@ import { Switch } from "@hypr/ui/components/ui/switch"; const schema = z.object({ detect: z.boolean().optional(), event: z.boolean().optional(), + summarization: z.boolean().optional(), }); type Schema = z.infer; @@ -27,11 +28,17 @@ export default function NotificationsComponent() { queryFn: () => notificationCommands.getDetectNotification(), }); + const summarizationNotification = useQuery({ + queryKey: ["notification", "summarization"], + queryFn: () => notificationCommands.getSummarizationNotification(), + }); + const form = useForm({ resolver: zodResolver(schema), values: { detect: detectNotification.data ?? false, event: eventNotification.data ?? false, + summarization: summarizationNotification.data ?? true, }, }); @@ -77,6 +84,16 @@ export default function NotificationsComponent() { }, }); + const summarizationMutation = useMutation({ + mutationFn: async (v: Schema) => { + notificationCommands.setSummarizationNotification(v.summarization ?? true); + return v.summarization; + }, + onSuccess: () => { + summarizationNotification.refetch(); + }, + }); + useEffect(() => { const subscription = form.watch((value, { name }) => { if (name === "detect") { @@ -85,10 +102,13 @@ export default function NotificationsComponent() { if (name === "event") { eventMutation.mutate(value); } + if (name === "summarization") { + summarizationMutation.mutate(value); + } }); return () => subscription.unsubscribe(); - }, [eventMutation, detectMutation]); + }, [eventMutation, detectMutation, summarizationMutation]); return (
@@ -138,6 +158,33 @@ export default function NotificationsComponent() {
+ + + + + + )} + /> + ( + +
+
+ + Summarization complete notifications + + + + Show notifications when local AI finishes summarizing your meetings. + + +
+ { }, async showSummarizationCompleteNotification() : Promise { return await TAURI_INVOKE("plugin:notification|show_summarization_complete_notification"); +}, +async getSummarizationNotification() : Promise { + return await TAURI_INVOKE("plugin:notification|get_summarization_notification"); +}, +async setSummarizationNotification(enabled: boolean) : Promise { + return await TAURI_INVOKE("plugin:notification|set_summarization_notification", { enabled }); } } diff --git a/plugins/notification/src/commands.rs b/plugins/notification/src/commands.rs index 496344683..a871e4780 100644 --- a/plugins/notification/src/commands.rs +++ b/plugins/notification/src/commands.rs @@ -107,3 +107,22 @@ pub(crate) async fn show_summarization_complete_notification( app.show_summarization_complete_notification() .map_err(|e| e.to_string()) } + +#[tauri::command] +#[specta::specta] +pub(crate) async fn get_summarization_notification( + app: tauri::AppHandle, +) -> Result { + app.get_summarization_notification() + .map_err(|e| e.to_string()) +} + +#[tauri::command] +#[specta::specta] +pub(crate) async fn set_summarization_notification( + app: tauri::AppHandle, + enabled: bool, +) -> Result<(), String> { + app.set_summarization_notification(enabled) + .map_err(|e| e.to_string()) +} diff --git a/plugins/notification/src/ext.rs b/plugins/notification/src/ext.rs index 77c1bd23d..125a7ccca 100644 --- a/plugins/notification/src/ext.rs +++ b/plugins/notification/src/ext.rs @@ -13,6 +13,9 @@ pub trait NotificationPluginExt { fn get_detect_notification(&self) -> Result; fn set_detect_notification(&self, enabled: bool) -> Result<(), Error>; + fn get_summarization_notification(&self) -> Result; + fn set_summarization_notification(&self, enabled: bool) -> Result<(), Error>; + fn start_event_notification(&self) -> impl Future>; fn stop_event_notification(&self) -> Result<(), Error>; @@ -67,6 +70,23 @@ impl> NotificationPluginExt for T { .map_err(Error::Store) } + #[tracing::instrument(skip(self))] + fn get_summarization_notification(&self) -> Result { + let store = self.notification_store(); + store + .get(crate::StoreKey::SummarizationNotification) + .map_err(Error::Store) + .map(|v| v.unwrap_or(true)) // Default to enabled + } + + #[tracing::instrument(skip(self))] + fn set_summarization_notification(&self, enabled: bool) -> Result<(), Error> { + let store = self.notification_store(); + store + .set(crate::StoreKey::SummarizationNotification, enabled) + .map_err(Error::Store) + } + #[tracing::instrument(skip(self))] async fn start_event_notification(&self) -> Result<(), Error> { let db_state = self.state::(); diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index e8167a1b8..f89c149da 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -37,6 +37,8 @@ fn make_specta_builder() -> tauri_specta::Builder { commands::start_event_notification::, commands::stop_event_notification::, commands::show_summarization_complete_notification::, + commands::get_summarization_notification::, + commands::set_summarization_notification::, ]) .error_handling(tauri_specta::ErrorHandlingMode::Throw) } diff --git a/plugins/notification/src/store.rs b/plugins/notification/src/store.rs index 479cf174f..e9ffa4220 100644 --- a/plugins/notification/src/store.rs +++ b/plugins/notification/src/store.rs @@ -4,6 +4,7 @@ use tauri_plugin_store2::ScopedStoreKey; pub enum StoreKey { EventNotification, DetectNotification, + SummarizationNotification, } impl ScopedStoreKey for StoreKey {}