diff --git a/apps/shinkai-desktop/src-tauri/src/deep_links.rs b/apps/shinkai-desktop/src-tauri/src/deep_links.rs index 2f555f900..49606d2b3 100644 --- a/apps/shinkai-desktop/src-tauri/src/deep_links.rs +++ b/apps/shinkai-desktop/src-tauri/src/deep_links.rs @@ -9,6 +9,12 @@ pub struct OAuthDeepLinkPayload { pub code: String, } +#[derive(Debug, Clone, serde::Serialize)] +pub struct StoreDeepLinkPayload { + pub tool_type: String, + pub tool_url: String, +} + pub fn setup_deep_links(app: &tauri::AppHandle) -> tauri::Result<()> { #[cfg(any(windows, target_os = "linux"))] { @@ -24,7 +30,36 @@ pub fn setup_deep_links(app: &tauri::AppHandle) -> tauri::Result<()> { for url in urls { log::debug!("handling deep link: {:?}", url); if let Some(host) = url.host() { + if host.to_string() == "store" { + // shinkai://store?type=tool&url=https://download.shinkai.app/tool/email-fetcher.zip + let query_pairs = url.query_pairs().collect::>(); + let tool_type = query_pairs + .iter() + .find(|(key, _)| key == "type") + .map(|(_, value)| value.to_string()) + .unwrap_or_default(); + let tool_url = query_pairs + .iter() + .find(|(key, _)| key == "url") + .map(|(_, value)| value.to_string()) + .unwrap_or_default(); + + let payload = StoreDeepLinkPayload { tool_type, tool_url }; + + log::debug!( + "emitting store-deep-link event to {}", + Window::Coordinator.as_str() + ); + let _ = recreate_window(app_handle.clone(), Window::Main, true); + let _ = app_handle.emit_to( + Window::Coordinator.as_str(), + "store-deep-link", + payload, + ); + } + if host.to_string() == "oauth" { + // shinkai://oauth?code=11&state=22 log::debug!("oauth deep link: {:?}", url); let query_pairs = url.query_pairs().collect::>(); let state = query_pairs diff --git a/apps/shinkai-desktop/src/pages/tools.tsx b/apps/shinkai-desktop/src/pages/tools.tsx index d3918a777..ae2a7622b 100644 --- a/apps/shinkai-desktop/src/pages/tools.tsx +++ b/apps/shinkai-desktop/src/pages/tools.tsx @@ -27,6 +27,7 @@ import { } from '@shinkai_network/shinkai-ui'; import { formatText } from '@shinkai_network/shinkai-ui/helpers'; import { cn } from '@shinkai_network/shinkai-ui/utils'; +import { listen } from '@tauri-apps/api/event'; import { BoltIcon, CloudDownloadIcon, @@ -34,7 +35,7 @@ import { SearchIcon, XIcon, } from 'lucide-react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; import { Link, useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; @@ -282,7 +283,7 @@ type ImportToolFormSchema = z.infer; function ImportToolModal() { const auth = useAuth((state) => state.auth); - + const navigate = useNavigate(); const importToolForm = useForm({ resolver: zodResolver(importToolFormSchema), @@ -309,6 +310,25 @@ function ImportToolModal() { }, }); + useEffect(() => { + const unlisten = listen('store-deep-link', (event) => { + if (!auth) return; + + const payload = event.payload as { tool_type: string; tool_url: string }; + if (payload.tool_type === 'tool') { + importTool({ + nodeAddress: auth?.node_address ?? '', + token: auth?.api_v2_key ?? '', + url: payload.tool_url, + }); + } + }); + + return () => { + unlisten.then((fn) => fn()); + }; + }, [importTool, auth]); + const onSubmit = async (data: ImportToolFormSchema) => { await importTool({ nodeAddress: auth?.node_address ?? '',