feat: configure Adsterra script, update sitemap/robots for kattoolbox.vercel.app, and add SEO meta tags

This commit is contained in:
katR 2026-09-11 22:34:23 +05:30
parent 2569487cfe
commit 0fa3165d96
8 changed files with 96 additions and 46 deletions

View file

@ -1,4 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://devtoolbox.com/sitemap.xml
Sitemap: https://kattoolbox.vercel.app/sitemap.xml

View file

@ -1,5 +1,6 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import Script from "next/script";
import "./globals.css";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
@ -15,27 +16,33 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
metadataBase: new URL("https://kattoolbox.vercel.app"),
title: {
default: "kattoolbox — Client-Side File & Developer Utilities",
template: "%s | kattoolbox",
},
description:
"Client-side file and developer tools. Compress images to size limits, strip EXIF metadata, merge and extract PDF pages, and run developer utilities directly in the browser.",
"Free client-side utilities. Compress images under Discord/Gmail limits, strip EXIF GPS metadata, merge and extract PDF pages, and run developer tools without server uploads.",
keywords: [
"file tools",
"image compression",
"discord image shrinker",
"exif scrubber",
"pdf merge",
"client-side utilities",
"compress image for discord",
"shrink image to 10mb",
"strip photo gps online",
"remove exif data",
"merge pdf client side",
"redact image online",
"developer utilities",
"kattoolbox",
],
alternates: {
canonical: "/",
},
openGraph: {
type: "website",
title: "kattoolbox — Client-Side File & Developer Utilities",
description:
"Client-side file and developer tools running entirely in the browser.",
siteName: "kattoolbox",
url: "https://kattoolbox.vercel.app",
},
twitter: {
card: "summary_large_image",
@ -56,10 +63,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<head>
<script
async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXXXXX"
crossOrigin="anonymous"
{/* Adsterra Network Script */}
<Script
src="https://pl31295441.profitableratecpmnetwork.com/fd/4a/91/fd4a9155d8cc10399821f613377ee9f1.js"
strategy="afterInteractive"
/>
</head>
<body className="dot-bg-fade flex min-h-full flex-col bg-black text-foreground">

View file

@ -3,15 +3,14 @@ import { tools } from "@/lib/tools";
export const dynamic = "force-static";
// TODO: Replace with your actual domain
const BASE_URL = "https://devtoolbox.com";
const BASE_URL = "https://kattoolbox.vercel.app";
export default function sitemap(): MetadataRoute.Sitemap {
const toolPages = tools.map((tool) => ({
url: `${BASE_URL}/tools/${tool.slug}`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.8,
priority: tool.featured ? 0.9 : 0.7,
}));
return [
@ -19,7 +18,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
url: BASE_URL,
lastModified: new Date(),
changeFrequency: "weekly",
priority: 1,
priority: 1.0,
},
...toolPages,
];

View file

@ -175,6 +175,12 @@ export default function DiscordImageShrinkerPage() {
title="File Shrinker"
description="Compress images to target file sizes for Discord, email attachments, or web delivery. Processing runs in browser memory."
>
<title>Compress Image for Discord &amp; Email — Under 10MB, 8MB, 25MB | kattoolbox</title>
<meta
name="description"
content="Free online tool to compress and shrink images under Discord's 10MB/8MB upload limit and Gmail's 25MB attachment limit. Runs 100% locally in your browser."
/>
<div className="space-y-6">
{/* Preset Selector */}
<div>
@ -319,7 +325,7 @@ export default function DiscordImageShrinkerPage() {
</div>
)}
{/* Technical details */}
{/* Technical overview */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-3 pt-4 border-t border-[#1a1a1a] text-xs text-[#555]">
<div className="border border-[#141414] p-3 bg-black">
<h4 className="text-gray-300 font-medium mb-1">Target Size Convergence</h4>
@ -334,6 +340,27 @@ export default function DiscordImageShrinkerPage() {
<p>Canvas rasterization automatically removes embedded camera metadata and GPS coordinates.</p>
</div>
</div>
{/* SEO FAQ section for search engines */}
<section className="mt-8 pt-6 border-t border-[#1a1a1a] space-y-4 text-xs text-[#666]">
<h3 className="text-xs font-mono uppercase tracking-wider text-gray-400">
Frequently Asked Questions
</h3>
<div className="space-y-3">
<div>
<h4 className="text-gray-300 font-medium mb-0.5">What is the file upload limit on Discord?</h4>
<p className="leading-relaxed">Discord enforces a 10MB upload limit for standard free users (previously 8MB) and a 50MB limit for Nitro Basic subscribers. This tool compresses your files to fall strictly beneath those limits.</p>
</div>
<div>
<h4 className="text-gray-300 font-medium mb-0.5">What is the Gmail email attachment limit?</h4>
<p className="leading-relaxed">Gmail and Google Workspace allow up to 25MB total attachments per email. Selecting the 25MB preset compresses your photo safely beneath Gmail&#39;s incoming payload limit.</p>
</div>
<div>
<h4 className="text-gray-300 font-medium mb-0.5">Are my photos uploaded to a server?</h4>
<p className="leading-relaxed">No. Compression is computed entirely inside your local browser tab using client-side JavaScript and HTML5 Canvas. Your images are never sent over the network.</p>
</div>
</div>
</section>
</div>
</ToolLayout>
);

View file

@ -122,6 +122,12 @@ export default function ExifScrubberPage() {
title="EXIF & GPS Scrubber"
description="Inspect embedded camera parameters, device identifiers, and location tags. Strip metadata client-side before sharing."
>
<title>Remove EXIF &amp; GPS Location from Photos Online | kattoolbox</title>
<meta
name="description"
content="Free tool to view hidden photo metadata and remove GPS location coordinates, camera serials, and device identifiers offline in your browser."
/>
<div className="space-y-6">
{/* Dropzone */}
<div
@ -282,6 +288,23 @@ export default function ExifScrubberPage() {
<p>Images are re-encoded locally via HTML5 canvas, ensuring stripped headers are physically unrecoverable.</p>
</div>
</div>
{/* SEO FAQ section */}
<section className="mt-8 pt-6 border-t border-[#1a1a1a] space-y-4 text-xs text-[#666]">
<h3 className="text-xs font-mono uppercase tracking-wider text-gray-400">
Frequently Asked Questions
</h3>
<div className="space-y-3">
<div>
<h4 className="text-gray-300 font-medium mb-0.5">What is EXIF metadata?</h4>
<p className="leading-relaxed">EXIF (Exchangeable Image File Format) is data embedded directly inside JPEG, PNG, and TIFF images. It includes camera model, shutter speed, focal length, date and time, and frequently GPS coordinates.</p>
</div>
<div>
<h4 className="text-gray-300 font-medium mb-0.5">Why should I remove GPS data before sharing photos?</h4>
<p className="leading-relaxed">Photos taken on iPhones or Android devices embed exact latitude and longitude coordinates. Sharing raw photos on forums or social channels can expose your home, school, or workplace address.</p>
</div>
</div>
</section>
</div>
</ToolLayout>
);

View file

@ -183,6 +183,11 @@ export default function ImageRedactorPage() {
title="Image Redactor"
description="Apply permanent blackout or pixelated blur regions over confidential details before exporting."
>
<title>Redact &amp; Black Out Sensitive Info on Images Online | kattoolbox</title>
<meta
name="description"
content="Free tool to black out or pixelate confidential text, credit cards, names, and faces in images directly in your browser. Files never leave your device."
/>
<div className="space-y-6">
{/* Controls */}
<div className="flex flex-wrap items-center justify-between gap-3 pb-2 border-b border-[#1a1a1a]">

View file

@ -213,6 +213,11 @@ export default function PdfToolsPage() {
title="PDF Toolkit"
description="Merge, extract pages, convert images, or rotate PDF documents. Operations execute locally via WebAssembly."
>
<title>Merge, Split &amp; Extract PDF Pages Online — Private &amp; Offline | kattoolbox</title>
<meta
name="description"
content="Free offline PDF tools. Merge multiple PDF documents, extract page ranges, convert images to PDF, or rotate pages 100% locally in your browser without uploading files."
/>
<div className="space-y-6">
{/* Navigation Tabs */}
<div className="flex border-b border-[#1a1a1a] gap-1">

View file

@ -1,6 +1,6 @@
"use client";
import { useEffect } from "react";
import { useEffect, useRef } from "react";
interface SidebarAdProps {
slotId?: string;
@ -8,49 +8,33 @@ interface SidebarAdProps {
className?: string;
}
export default function SidebarAd({ slotId, format = "vertical", className = "" }: SidebarAdProps) {
export default function SidebarAd({ format = "vertical", className = "" }: SidebarAdProps) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
try {
if (typeof window !== "undefined") {
// @ts-expect-error Google ads window object
(window.adsbygoogle = window.adsbygoogle || []).push({});
}
const script = document.createElement("script");
script.src = "https://pl31295441.profitableratecpmnetwork.com/fd/4a/91/fd4a9155d8cc10399821f613377ee9f1.js";
script.async = true;
containerRef.current.appendChild(script);
} catch {
// Ignore if AdSense blocked by user or not configured
// Ignore network blocks
}
}, []);
return (
<aside
aria-label="Advertisement"
className={`relative flex flex-col items-center justify-center border border-[#1a1a1a] bg-[#070707] p-3 text-center transition-colors hover:border-[#262626] ${
className={`relative flex flex-col items-center justify-center border border-[#161616] bg-[#070707] p-2 text-center transition-colors hover:border-[#222] ${
format === "vertical" ? "w-[170px] min-h-[500px]" : "w-full min-h-[250px]"
} ${className}`}
>
<span className="absolute top-1.5 right-2 text-[10px] uppercase tracking-wider text-[#404040]">
<span className="absolute top-1.5 right-2 text-[9px] uppercase tracking-wider text-[#333]">
Ad
</span>
{/* Actual AdSense Slot (active when ca-pub and slot ID are configured) */}
<ins
className="adsbygoogle block w-full h-full"
style={{ display: "block" }}
data-ad-client="ca-pub-XXXXXXXXXXXXXXXX"
data-ad-slot={slotId || "1234567890"}
data-ad-format={format === "vertical" ? "vertical" : "auto"}
data-full-width-responsive="true"
/>
{/* Elegant Minimalist Placeholder shown when ads are not yet loaded */}
<div className="flex flex-col items-center justify-center gap-2 text-xs text-[#444] pointer-events-none select-none my-auto">
<div className="w-8 h-8 rounded border border-[#1f1f1f] flex items-center justify-center text-[10px] text-[#555]">
ADS
</div>
<span className="text-[11px] text-[#4a4a4a] font-mono">Sponsor Space</span>
<span className="text-[10px] text-[#333] max-w-[130px] leading-tight">
Non-intrusive side placement
</span>
</div>
<div ref={containerRef} className="w-full flex items-center justify-center min-h-[250px]" />
</aside>
);
}