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
{
)}
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(
-
- );
+ 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 && (
+
+ )}