Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ const nextConfig: NextConfig = {
},
async redirects() {
return [
{
source: "/demo",
destination: "https://bugdrop-widget-test.vercel.app",
permanent: false,
},
{
source: "/:path*",
has: [{ type: "host", value: "www.bugdrop.dev" }],
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
"lint": "eslint",
"indexnow": "node scripts/submit-indexnow.mjs"
},
"dependencies": {
"@base-ui/react": "^1.3.0",
Expand Down
1 change: 1 addition & 0 deletions public/9e9b2f84b7c74e7c95ff41b4d1a4d10f.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9e9b2f84b7c74e7c95ff41b4d1a4d10f
29 changes: 29 additions & 0 deletions scripts/submit-indexnow.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const key = "9e9b2f84b7c74e7c95ff41b4d1a4d10f";
const host = "bugdrop.dev";
const keyLocation = `https://${host}/${key}.txt`;

const sitemap = await fetch(`https://${host}/sitemap.xml`).then((response) => {
if (!response.ok) {
throw new Error(`Failed to fetch sitemap: ${response.status}`);
}
return response.text();
});

const urlList = [...sitemap.matchAll(/<loc>(.*?)<\/loc>/g)].map((match) => match[1]);

const response = await fetch("https://api.indexnow.org/IndexNow", {
method: "POST",
headers: { "content-type": "application/json; charset=utf-8" },
body: JSON.stringify({
host,
key,
keyLocation,
urlList,
}),
});

if (!response.ok) {
throw new Error(`IndexNow submission failed: ${response.status} ${await response.text()}`);
}

console.log(`Submitted ${urlList.length} URLs to IndexNow.`);
25 changes: 20 additions & 5 deletions src/app/compare/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { notFound } from "next/navigation";
import { compareNav } from "@/lib/compare-nav";
import { MARKETPLACE_URL } from "@/lib/links";
import Link from "next/link";
import { JsonLd } from "@/components/json-ld";
import { articleSchema, breadcrumbSchema, pageMetadata } from "@/lib/seo";

export function generateStaticParams() {
return compareNav.map((c) => ({ slug: c.slug }));
Expand All @@ -17,13 +19,12 @@ export async function generateMetadata({
const comparison = compareNav.find((c) => c.slug === slug);
if (!comparison) notFound();

return {
return pageMetadata({
title: `${comparison.title} — BugDrop`,
description: comparison.description,
alternates: {
canonical: `/compare/${slug}`,
},
};
path: `/compare/${slug}`,
type: "article",
});
}

export default async function ComparePage({
Expand All @@ -38,6 +39,20 @@ export default async function ComparePage({
const Content = (await import(`@/content/compare/${slug}.mdx`)).default;
return (
<div>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Compare", path: "/compare" },
{ name: comparison.title, path: `/compare/${slug}` },
])}
/>
<JsonLd
data={articleSchema({
title: `${comparison.title} — BugDrop`,
description: comparison.description,
path: `/compare/${slug}`,
})}
/>
<Link
href="/compare"
className="text-accent-cyan hover:underline text-sm mb-6 block"
Expand Down
34 changes: 26 additions & 8 deletions src/app/compare/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import Link from "next/link";
import type { Metadata } from "next";
import { compareNav } from "@/lib/compare-nav";
import { JsonLd } from "@/components/json-ld";
import { articleSchema, breadcrumbSchema, pageMetadata } from "@/lib/seo";

export const metadata: Metadata = {
title: "Compare — BugDrop",
description: "See how BugDrop compares to other feedback tools.",
alternates: {
canonical: "/compare",
},
};
const description =
"Compare BugDrop with Userback, Canny, Sentry User Feedback, Marker.io, BugHerd, Usersnap, and other website feedback tools.";

export const metadata: Metadata = pageMetadata({
title: "Compare BugDrop",
description,
path: "/compare",
type: "article",
});

export default function CompareIndex() {
return (
<div>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Compare", path: "/compare" },
])}
/>
<JsonLd
data={articleSchema({
title: "Compare BugDrop",
description,
path: "/compare",
})}
/>
<h1 className="text-3xl font-bold text-text-primary mb-2">Compare</h1>
<p className="text-text-subtle mb-10">
See how BugDrop compares to other feedback tools.
Compare BugDrop with visual feedback, roadmap, and customer feedback
tools to choose the right workflow for GitHub-native bug reports.
</p>
<div className="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-6">
{compareNav.map((c) => (
Expand Down
112 changes: 112 additions & 0 deletions src/app/demo/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { Metadata } from "next";
import { ArrowUpRight, CirclePlay, Code2, MessageSquare } from "lucide-react";
import { JsonLd } from "@/components/json-ld";
import { DEMO_URL, GITHUB_REPO_URL, MARKETPLACE_URL } from "@/lib/links";
import { breadcrumbSchema, pageMetadata, videoSchema } from "@/lib/seo";

const description =
"Try the BugDrop demo to test a website feedback widget that captures screenshots, annotations, redaction, system info, and GitHub Issues.";

export const metadata: Metadata = pageMetadata({
title: "BugDrop Demo",
description,
path: "/demo",
});

export default function DemoPage() {
return (
<main>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Demo", path: "/demo" },
])}
/>
<JsonLd data={videoSchema()} />
<section className="mb-12">
<p className="mb-3 text-sm font-medium text-accent-cyan">Live demo</p>
<h1 className="mb-5 text-4xl font-semibold leading-tight text-text-primary max-sm:text-3xl">
Try BugDrop on a sample app
</h1>
<p className="max-w-[720px] text-lg leading-relaxed text-text-subtle">
The BugDrop demo shows the full website feedback flow: open the widget,
capture a screenshot, annotate the problem, redact sensitive regions,
and submit a GitHub Issue with browser and page context.
</p>
</section>

<div className="mb-14 grid gap-4 md:grid-cols-3">
<a
href={DEMO_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center gap-2 rounded-[10px] bg-accent-cyan px-5 py-3 font-medium text-bg-deep transition-all hover:-translate-y-0.5"
>
<ArrowUpRight className="size-4" aria-hidden="true" />
Open Live Demo
</a>
<a
href={MARKETPLACE_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center gap-2 rounded-[10px] bg-bg-surface px-5 py-3 font-medium text-text-primary transition-all hover:-translate-y-0.5"
>
<MessageSquare className="size-4" aria-hidden="true" />
Install BugDrop
</a>
<a
href={GITHUB_REPO_URL}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center justify-center gap-2 rounded-[10px] border border-border px-5 py-3 font-medium text-text-primary transition-all hover:-translate-y-0.5 hover:bg-bg-surface"
>
<Code2 className="size-4" aria-hidden="true" />
View Source
</a>
</div>

<section className="mb-16 grid gap-6 md:grid-cols-2">
<div>
<h2 className="mb-3 text-2xl font-semibold text-text-primary">
What to test
</h2>
<ul className="list-disc space-y-2 pl-5 text-text-subtle">
<li>Open the feedback widget from the floating button.</li>
<li>Attach a screenshot and mark up the broken UI state.</li>
<li>Cover private screenshot regions before submitting.</li>
<li>Confirm the report includes URL, browser, viewport, and OS data.</li>
<li>Review the GitHub Issue format your team would receive.</li>
</ul>
</div>
<div>
<h2 className="mb-3 text-2xl font-semibold text-text-primary">
Why it matters
</h2>
<p className="text-text-subtle">
BugDrop is built for teams that already work in GitHub Issues. The
demo makes the workflow concrete: reporters do not need accounts,
developers get visual context, and sensitive page data can be masked
before the screenshot lands in your repository.
</p>
</div>
</section>

<section>
<h2 className="mb-6 flex items-center gap-3 text-2xl font-semibold text-text-primary">
<CirclePlay className="size-6 text-accent-cyan" aria-hidden="true" />
Watch the flow
</h2>
<div className="max-w-[640px] overflow-hidden rounded-xl border border-border bg-black">
<iframe
src="https://www.youtube.com/embed/VkLvP1xmRzo"
title="BugDrop Demo Video"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
loading="lazy"
className="block aspect-[9/16] max-h-[520px] w-full border-none"
/>
</div>
</section>
</main>
);
}
36 changes: 31 additions & 5 deletions src/app/docs/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { docsNav } from "@/lib/docs-nav";
import Link from "next/link";
import { JsonLd } from "@/components/json-ld";
import {
articleSchema,
breadcrumbSchema,
faqSchema,
pageMetadata,
videoSchema,
} from "@/lib/seo";

export function generateStaticParams() {
return docsNav
Expand All @@ -18,12 +26,12 @@ export async function generateMetadata({
const doc = docsNav.find((d) => d.slug === slug);
if (!doc) notFound();

return {
return pageMetadata({
title: `${doc.title} — BugDrop Docs`,
alternates: {
canonical: `/docs/${slug}`,
},
};
description: doc.description,
path: `/docs/${slug}`,
type: "article",
});
}

export default async function DocPage({
Expand All @@ -34,6 +42,7 @@ export default async function DocPage({
const { slug } = await params;
const currentIndex = docsNav.findIndex((d) => d.slug === slug);
if (currentIndex === -1) notFound();
const doc = docsNav[currentIndex];

try {
const Content = (await import(`@/content/docs/${slug}.mdx`)).default;
Expand All @@ -42,6 +51,23 @@ export default async function DocPage({
currentIndex < docsNav.length - 1 ? docsNav[currentIndex + 1] : null;
return (
<div>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Docs", path: "/docs" },
{ name: doc.title, path: `/docs/${slug}` },
])}
/>
<JsonLd
data={articleSchema({
title: `${doc.title} — BugDrop Docs`,
description: doc.description,
path: `/docs/${slug}`,
type: "TechArticle",
})}
/>
{slug === "faq" ? <JsonLd data={faqSchema()} /> : null}
{slug === "demo" ? <JsonLd data={videoSchema()} /> : null}
<Content />
<nav className="flex justify-between mt-12 pt-6 border-t border-border">
{prev ? (
Expand Down
32 changes: 25 additions & 7 deletions src/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,36 @@ import Link from "next/link";
import type { Metadata } from "next";
import { PlayCircle } from "lucide-react";
import { DEMO_PATH } from "@/lib/links";
import { JsonLd } from "@/components/json-ld";
import { articleSchema, breadcrumbSchema, pageMetadata } from "@/lib/seo";

export const metadata: Metadata = {
title: "Docs — BugDrop",
description: "BugDrop documentation.",
alternates: {
canonical: "/docs",
},
};
const description =
"Documentation for installing, configuring, styling, testing, securing, and self-hosting the BugDrop website feedback widget.";

export const metadata: Metadata = pageMetadata({
title: "BugDrop Docs",
description,
path: "/docs",
type: "article",
});

export default function DocsIndex() {
return (
<div>
<JsonLd
data={breadcrumbSchema([
{ name: "Home", path: "/" },
{ name: "Docs", path: "/docs" },
])}
/>
<JsonLd
data={articleSchema({
title: "BugDrop Docs",
description,
path: "/docs",
type: "TechArticle",
})}
/>
<h1 className="text-3xl font-bold text-text-primary mb-4">Getting Started</h1>
<p className="text-text-subtle mb-6 leading-relaxed">
BugDrop is an open-source feedback widget that turns user bug reports into GitHub issues.
Expand Down
Loading
Loading