From e8a6c07a108770c7278fd3f96d0dcb99c1414b7c Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Wed, 15 Apr 2026 21:29:33 -0400 Subject: [PATCH] feat: add extension section in settings and fix presence script - Add "Browser extension" card in settings that detects whether the DiffKit extension is installed and prompts to install if not - Guard dashboard-presence.js with an origin check so it only sets data-diffkit-extension on DiffKit origins (fixes Firefox injecting the script on all sites when broader host permissions are granted) - Update Firefox extension store URL to canonical /addon/diffkit/ path --- apps/dashboard/src/lib/extension-store-url.ts | 2 +- .../src/routes/_protected/settings/index.tsx | 76 ++++++++++++++++++- .../diffkit-redirect/dashboard-presence.js | 12 ++- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/lib/extension-store-url.ts b/apps/dashboard/src/lib/extension-store-url.ts index 2aadb20..9f74170 100644 --- a/apps/dashboard/src/lib/extension-store-url.ts +++ b/apps/dashboard/src/lib/extension-store-url.ts @@ -3,7 +3,7 @@ export const CHROME_EXTENSION_STORE_URL = "https://chromewebstore.google.com/detail/celjddfjncnnkgfgldobcahfiimlebll/" as const; export const FIREFOX_EXTENSION_STORE_URL = - "https://addons.mozilla.org/en-US/firefox/addon/diffkit/" as const; + "https://addons.mozilla.org/addon/diffkit/" as const; function isFirefoxFamilyUserAgent(ua: string): boolean { // Desktop Firefox: "Firefox/123"; Firefox iOS: "FxiOS/123"; avoid relying on siteConfig. diff --git a/apps/dashboard/src/routes/_protected/settings/index.tsx b/apps/dashboard/src/routes/_protected/settings/index.tsx index 2372b50..752c567 100644 --- a/apps/dashboard/src/routes/_protected/settings/index.tsx +++ b/apps/dashboard/src/routes/_protected/settings/index.tsx @@ -1,9 +1,19 @@ -import { MoonIcon, SunIcon, SystemIcon } from "@diffkit/icons"; +import { + CheckIcon, + DownloadIcon, + MoonIcon, + SunIcon, + SystemIcon, +} from "@diffkit/icons"; import { Button } from "@diffkit/ui/components/button"; +import { Logo } from "@diffkit/ui/components/logo"; import { cn } from "@diffkit/ui/lib/utils"; import { createFileRoute, Link } from "@tanstack/react-router"; import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; import { signOutToLogin } from "#/lib/auth-actions"; +import { isDiffKitExtensionPresent } from "#/lib/diffkit-extension-detect"; +import { getExtensionStoreInstallUrl } from "#/lib/extension-store-url"; import { useHasMounted } from "#/lib/use-has-mounted"; const themeOptions = [ @@ -40,6 +50,13 @@ function GeneralSettingsPage() { + + + + ); } + +function ExtensionCard() { + const hasMounted = useHasMounted(); + const [installed, setInstalled] = useState(false); + + useEffect(() => { + if (!hasMounted) return; + + setInstalled(isDiffKitExtensionPresent()); + + if (isDiffKitExtensionPresent()) return; + + const el = document.documentElement; + const observer = new MutationObserver(() => { + if (isDiffKitExtensionPresent()) { + setInstalled(true); + observer.disconnect(); + } + }); + observer.observe(el, { + attributes: true, + attributeFilter: ["data-diffkit-extension"], + }); + return () => observer.disconnect(); + }, [hasMounted]); + + const installHref = getExtensionStoreInstallUrl(); + + return ( +
+
+ +
+

DiffKit Extension

+

+ {installed + ? "The extension is installed and active." + : "Redirect GitHub PRs, issues, and matching pages to DiffKit."} +

+
+
+ {installed ? ( + + + Installed + + ) : ( + + )} +
+ ); +} diff --git a/extensions/diffkit-redirect/dashboard-presence.js b/extensions/diffkit-redirect/dashboard-presence.js index 8a202e6..68a5f58 100644 --- a/extensions/diffkit-redirect/dashboard-presence.js +++ b/extensions/diffkit-redirect/dashboard-presence.js @@ -2,10 +2,20 @@ * Runs on DiffKit web app origins so the site can detect the extension via DOM * (`data-diffkit-extension` on ). Content scripts cannot share `window` * with the page, but DOM attributes are visible to the app. + * + * Guard: Firefox may inject content scripts beyond the manifest `matches` when + * the user grants broader host permissions, so we verify the origin explicitly. */ (function markDiffKitExtensionPresent() { try { - document.documentElement.dataset.diffkitExtension = "1"; + const origin = location.origin; + if ( + origin === "https://diff-kit.com" || + origin === "http://localhost:3000" || + origin === "http://127.0.0.1:3000" + ) { + document.documentElement.dataset.diffkitExtension = "1"; + } } catch { // ignore }