diff --git a/frontend/src/components/Explainer.tsx b/frontend/src/components/Explainer.tsx index 3a34c6d2..4a42b2d3 100644 --- a/frontend/src/components/Explainer.tsx +++ b/frontend/src/components/Explainer.tsx @@ -3,6 +3,7 @@ import { ArrowRight, BotIcon, LockIcon, MinusIcon, ServerIcon, SmartphoneIcon } import { useEffect, useState } from "react"; import { Loader2, CheckCircle, XCircle } from "lucide-react"; import { useOpenSecret } from "@opensecret/react"; +import { openExternalLink } from "@/utils/externalLinks"; function ArrowAndLock() { return ( @@ -90,14 +91,13 @@ export function InfoContent() {
- openExternalLink("https://blog.trymaple.ai")} + role="link" className="text-center hover:underline font-medium text-sm" - target="_blank" - rel="noopener noreferrer" > Learn more - +
); diff --git a/frontend/src/components/SimplifiedFooter.tsx b/frontend/src/components/SimplifiedFooter.tsx index 6b4e0df7..cf7c1bc1 100644 --- a/frontend/src/components/SimplifiedFooter.tsx +++ b/frontend/src/components/SimplifiedFooter.tsx @@ -1,17 +1,18 @@ +import { openExternalLink } from "@/utils/externalLinks"; + export function SimplifiedFooter() { return (

© {new Date().getFullYear()} Maple AI. All rights reserved. Powered by{" "} - openExternalLink("https://opensecret.cloud")} + role="link" + className="text-[hsl(var(--purple))] hover:text-[hsl(var(--purple))]/80 dark:text-[hsl(var(--blue))] dark:hover:text-[hsl(var(--blue))]/80 transition-colors underline" > OpenSecret - +

Downloads - openExternalLink("https://opensecret.cloud/terms")} + role="link" className="text-[hsl(var(--marketing-text-muted))] hover:text-foreground text-sm transition-colors" > Terms of Service - - +
diff --git a/frontend/src/utils/externalLinks.ts b/frontend/src/utils/externalLinks.ts new file mode 100644 index 00000000..e4863b4a --- /dev/null +++ b/frontend/src/utils/externalLinks.ts @@ -0,0 +1,37 @@ +/** + * Opens an external URL using the Tauri opener plugin when running on iOS in a Tauri environment, + * with fallback to window.open for web environments and other platforms. + * + * @param url - The external URL to open + */ +export const openExternalLink = async (url: string): Promise => { + try { + // Check if we're in a Tauri environment first + const { isTauri } = await import("@tauri-apps/api/core"); + const tauriEnv = await isTauri(); + + if (tauriEnv) { + // Check if we're on iOS + const { type } = await import("@tauri-apps/plugin-os"); + const platform = await type(); + + if (platform === "ios") { + // Use Tauri opener plugin for iOS + const { invoke } = await import("@tauri-apps/api/core"); + await invoke("plugin:opener|open_url", { url }); + return; + } + } + + // Fallback for non-iOS or non-Tauri environments + window.open(url, "_blank", "noopener,noreferrer"); + } catch (error) { + // Final fallback if everything fails + console.warn("Failed to open URL with Tauri opener, falling back to window.open:", error); + try { + window.open(url, "_blank", "noopener,noreferrer"); + } catch (windowError) { + console.error("Failed to open URL with window.open:", windowError); + } + } +};