From dfddaef260ebadd2e2fcf9d326e01309f8772978 Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Mon, 18 May 2026 16:46:58 +0800 Subject: [PATCH] feat: add /tools index page and fix navbar links on subpages (#148) - Add /tools hub page listing all 52 tools grouped by category with CollectionPage + ItemList schema markup - Include /tools in dynamic sitemap at priority 0.9 - Fix Features and Pricing nav links using relative anchors (#features, #pricing) that broke on subpages by prefixing with / --- apps/landing/src/app/sitemap.ts | 1 + apps/landing/src/app/tools/page.tsx | 208 +++++++++++++++++++++++++ apps/landing/src/components/navbar.tsx | 4 +- 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 apps/landing/src/app/tools/page.tsx diff --git a/apps/landing/src/app/sitemap.ts b/apps/landing/src/app/sitemap.ts index a0581194..ecdbcae0 100644 --- a/apps/landing/src/app/sitemap.ts +++ b/apps/landing/src/app/sitemap.ts @@ -22,6 +22,7 @@ export default function sitemap(): MetadataRoute.Sitemap { priority: 0.3, }, { url: `${base}/terms`, lastModified: new Date(), changeFrequency: "yearly", priority: 0.3 }, + { url: `${base}/tools`, lastModified: new Date(), changeFrequency: "weekly", priority: 0.9 }, ]; const toolPages: MetadataRoute.Sitemap = TOOLS.map((tool) => ({ diff --git a/apps/landing/src/app/tools/page.tsx b/apps/landing/src/app/tools/page.tsx new file mode 100644 index 00000000..fd062e46 --- /dev/null +++ b/apps/landing/src/app/tools/page.tsx @@ -0,0 +1,208 @@ +import { CATEGORIES, TOOLS } from "@snapotter/shared"; +import { ArrowRight } from "lucide-react"; +import type { Metadata } from "next"; + +import { FadeIn } from "@/components/fade-in"; +import { Footer } from "@/components/footer"; +import { JsonLd } from "@/components/json-ld"; +import { Navbar } from "@/components/navbar"; +import { ToolIcon } from "@/components/tool-icon"; + +export const metadata: Metadata = { + title: "All 52 Image Processing Tools | SnapOtter", + description: + "Browse all 52 self-hosted image tools: resize, compress, convert, remove backgrounds, upscale with AI, add watermarks, generate QR codes, and more. Free, open source, runs on your hardware.", + alternates: { canonical: "https://snapotter.com/tools" }, + openGraph: { + title: "All 52 Image Processing Tools | SnapOtter", + description: "Browse all 52 self-hosted image tools. Free, open source, runs on your hardware.", + url: "https://snapotter.com/tools", + siteName: "SnapOtter", + type: "website", + locale: "en_US", + images: [ + { + url: "/og-image.png", + width: 1280, + height: 640, + alt: "SnapOtter - 52 Image Processing Tools", + }, + ], + }, +}; + +const categoryMap = new Map(CATEGORIES.map((c) => [c.id, c])); + +function getToolsByCategory() { + return CATEGORIES.map((cat) => ({ + category: cat, + tools: TOOLS.filter((t) => t.category === cat.id), + })).filter((group) => group.tools.length > 0); +} + +export default function ToolsIndexPage() { + const groups = getToolsByCategory(); + + const breadcrumbJsonLd = { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { + "@type": "ListItem", + position: 1, + name: "Home", + item: "https://snapotter.com", + }, + { + "@type": "ListItem", + position: 2, + name: "Tools", + item: "https://snapotter.com/tools", + }, + ], + }; + + const collectionJsonLd = { + "@context": "https://schema.org", + "@type": "CollectionPage", + name: "SnapOtter Image Processing Tools", + description: + "52 self-hosted image processing tools with local AI. Resize, compress, convert, remove backgrounds, upscale, OCR, and more.", + url: "https://snapotter.com/tools", + isPartOf: { + "@type": "WebSite", + name: "SnapOtter", + url: "https://snapotter.com", + }, + numberOfItems: TOOLS.length, + mainEntity: { + "@type": "ItemList", + numberOfItems: TOOLS.length, + itemListElement: TOOLS.map((tool, i) => ({ + "@type": "ListItem", + position: i + 1, + name: tool.name, + url: `https://snapotter.com/tools/${tool.id}`, + })), + }, + }; + + return ( + <> + + + +
+ {/* Breadcrumb */} + + + {/* Hero */} +
+ +

+ All {TOOLS.length} tools +

+

+ Every tool runs 100% on your hardware. No cloud uploads, no usage limits, no + watermarks. Deploy once with Docker and use them all. +

+
+
+ + {/* Tool categories */} + {groups.map((group) => ( +
+
+ +
+
+ +
+

+ {group.category.name} +

+ + {group.tools.length} {group.tools.length === 1 ? "tool" : "tools"} + +
+ +
+
+
+ ))} + + {/* Bottom CTA */} +
+
+ +

+ Deploy all {TOOLS.length} tools with one command +

+

+ A single Docker container. No external services. Open source and free forever. +

+ +
+
+
+
+