From 53ba533fd57fa37601e94898bde3b2c89d98a956 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Mon, 6 Apr 2026 22:31:07 +0000 Subject: [PATCH 1/2] feat: route "Reach Us" dropdown links to GitHub instead of email Replace mailto links with GitHub Issues/Discussions URLs for better issue tracking and feature request workflow. Email address remains visible in the footer but links to GitHub Issues. Update tests accordingly. Co-Authored-By: Claude Sonnet 4.6 --- .../components/reach-developers.test.tsx | 16 ++++---- components/reach-developers.tsx | 40 +++++++++++-------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/__tests__/components/reach-developers.test.tsx b/__tests__/components/reach-developers.test.tsx index a2f2325c..0fad24d5 100644 --- a/__tests__/components/reach-developers.test.tsx +++ b/__tests__/components/reach-developers.test.tsx @@ -24,8 +24,8 @@ describe("ReachDevelopers", () => { // Dropdown should now be visible expect(screen.getByText("Request a Feature")).toBeInTheDocument(); - expect(screen.getByText("Report a Bug")).toBeInTheDocument(); - expect(screen.getByText("General Inquiry")).toBeInTheDocument(); + expect(screen.getByText("Report an Issue")).toBeInTheDocument(); + expect(screen.getByText("Ask a Question")).toBeInTheDocument(); }); it("contains correct mailto links with subjects", async () => { @@ -38,17 +38,17 @@ describe("ReachDevelopers", () => { const featureLink = screen.getByText("Request a Feature").closest("a"); expect(featureLink).toHaveAttribute( "href", - expect.stringContaining("mailto:failproofai@exosphere.host") + expect.stringContaining("github.com/exospherehost/failproofai") ); expect(featureLink).toHaveAttribute( "href", - expect.stringContaining("subject=") + expect.stringContaining("labels=enhancement") ); - const bugLink = screen.getByText("Report a Bug").closest("a"); + const bugLink = screen.getByText("Report an Issue").closest("a"); expect(bugLink).toHaveAttribute( "href", - expect.stringContaining("mailto:failproofai@exosphere.host") + expect.stringContaining("github.com/exospherehost/failproofai") ); }); @@ -68,7 +68,7 @@ describe("ReachDevelopers", () => { // Click outside await user.click(screen.getByTestId("outside")); - expect(screen.queryByText("Request a Feature")).not.toBeInTheDocument(); + expect(screen.queryByText("Report an Issue")).not.toBeInTheDocument(); }); // ARIA attribute tests @@ -117,6 +117,6 @@ describe("ReachDevelopers", () => { expect(screen.getByText("Request a Feature")).toBeInTheDocument(); await user.keyboard("{Escape}"); - expect(screen.queryByText("Request a Feature")).not.toBeInTheDocument(); + expect(screen.queryByText("Report an Issue")).not.toBeInTheDocument(); }); }); diff --git a/components/reach-developers.tsx b/components/reach-developers.tsx index 20f73305..e20c3188 100644 --- a/components/reach-developers.tsx +++ b/components/reach-developers.tsx @@ -1,27 +1,28 @@ -/** Dropdown menu for users to reach the development team via email. */ +/** Dropdown menu for users to reach the development team via GitHub. */ "use client"; import React, { useState, useRef, useEffect, useCallback } from "react"; -import { Mail, Lightbulb, Bug, MessageSquare, ChevronDown } from "lucide-react"; +import { GitBranch, Lightbulb, Bug, MessageSquare, ChevronDown } from "lucide-react"; import { Button } from "@/components/ui/button"; +const GITHUB_REPO = "https://github.com/exospherehost/failproofai"; const CONTACT_EMAIL = "failproofai@exosphere.host"; const options = [ { label: "Request a Feature", icon: Lightbulb, - subject: "Feature Request: ", + href: `${GITHUB_REPO}/issues/new?labels=enhancement&title=Feature+Request%3A+`, }, { - label: "Report a Bug", + label: "Report an Issue", icon: Bug, - subject: "Bug Report: ", + href: `${GITHUB_REPO}/issues/new?labels=bug&title=Bug+Report%3A+`, }, { - label: "General Inquiry", + label: "Ask a Question", icon: MessageSquare, - subject: "General Inquiry: ", + href: `${GITHUB_REPO}/discussions/new?category=q-a`, }, ] as const; @@ -55,7 +56,7 @@ export const ReachDevelopers: React.FC = () => { aria-haspopup="true" className="flex items-center gap-1.5 text-muted-foreground hover:text-foreground" > - + Reach Us {

- {options.map(({ label, icon: Icon, subject }) => ( + {options.map(({ label, icon: Icon, href }) => ( setOpen(false)} @@ -85,12 +88,17 @@ export const ReachDevelopers: React.FC = () => { ))}
)} From 6120e596dc60e55f2e9f82d145dbe17a94423166 Mon Sep 17 00:00:00 2001 From: NiveditJain Date: Tue, 7 Apr 2026 01:12:06 +0000 Subject: [PATCH 2/2] feat: add --allowed-origins flag and backdrop-based dropdown close - Parse --allowed-origins CLI flag (and FAILPROOFAI_ALLOWED_DEV_ORIGINS env var) to configure Next.js allowedDevOrigins for cross-origin dev access - Refactor reach-developers dropdown to use a fixed backdrop div for click-outside detection instead of a document mousedown listener - Fix reach-developers email link to use mailto: href - Make bin/failproofai.mjs executable - Add docs for --allowed-origins flag and non-localhost dashboard access Co-Authored-By: Claude Sonnet 4.6 --- .../components/reach-developers.test.tsx | 12 ++---- bin/failproofai.mjs | 0 components/reach-developers.tsx | 37 ++++++++----------- docs/cli-reference.md | 23 ++++++++++++ docs/dashboard.md | 30 +++++++++++++++ next.config.ts | 5 +++ scripts/launch.ts | 3 +- scripts/parse-script-args.ts | 12 +++++- 8 files changed, 91 insertions(+), 31 deletions(-) mode change 100644 => 100755 bin/failproofai.mjs diff --git a/__tests__/components/reach-developers.test.tsx b/__tests__/components/reach-developers.test.tsx index 0fad24d5..840bd586 100644 --- a/__tests__/components/reach-developers.test.tsx +++ b/__tests__/components/reach-developers.test.tsx @@ -54,20 +54,16 @@ describe("ReachDevelopers", () => { it("click outside closes dropdown", async () => { const user = userEvent.setup(); - render( -
-
Outside
- -
- ); + const { container } = render(); // Open dropdown const btn = screen.getAllByRole("button")[0]; await user.click(btn); expect(screen.getByText("Request a Feature")).toBeInTheDocument(); - // Click outside - await user.click(screen.getByTestId("outside")); + // Click the backdrop overlay (the fixed inset-0 div rendered when open) + const backdrop = container.querySelector('[aria-hidden="true"]') as HTMLElement; + await user.click(backdrop); expect(screen.queryByText("Report an Issue")).not.toBeInTheDocument(); }); diff --git a/bin/failproofai.mjs b/bin/failproofai.mjs old mode 100644 new mode 100755 diff --git a/components/reach-developers.tsx b/components/reach-developers.tsx index e20c3188..3c201101 100644 --- a/components/reach-developers.tsx +++ b/components/reach-developers.tsx @@ -1,7 +1,7 @@ /** Dropdown menu for users to reach the development team via GitHub. */ "use client"; -import React, { useState, useRef, useEffect, useCallback } from "react"; +import React, { useState, useCallback } from "react"; import { GitBranch, Lightbulb, Bug, MessageSquare, ChevronDown } from "lucide-react"; import { Button } from "@/components/ui/button"; @@ -28,33 +28,30 @@ const options = [ export const ReachDevelopers: React.FC = () => { const [open, setOpen] = useState(false); - const ref = useRef(null); - useEffect(() => { - const handleClickOutside = (e: MouseEvent) => { - if (ref.current && !ref.current.contains(e.target as Node)) { - setOpen(false); - } - }; - document.addEventListener("mousedown", handleClickOutside); - return () => document.removeEventListener("mousedown", handleClickOutside); - }, []); + const close = useCallback(() => setOpen(false), []); const handleKeyDown = useCallback((e: React.KeyboardEvent) => { - if (e.key === "Escape" && open) { - setOpen(false); - } - }, [open]); + if (e.key === "Escape") setOpen(false); + }, []); return ( -
+
+ {open && ( +