+ {/* Hero Section */}
+
+ {/* Terminal-style header */}
+
+
+ ~/dewe.dev $
+ _
-
- Lightning Fast
-
-
- All processing happens locally in your browser for maximum speed and privacy
+
+
+ {SITE_CONFIG.title}
+
+
+
+ {SITE_CONFIG.subtitle}
-
-
-
-
-
+
+
+ {SITE_CONFIG.slogan} • {SITE_CONFIG.description}
+
+
+ {/* Enhanced Search */}
+
-
- Handle Large Files
-
-
- Process large text files and data with ease, no size limitations
-
-
-
-
-
-
+
+ {/* Stats */}
+
+
+
+
{SITE_CONFIG.totalTools} Tools Available
+
+
+
+
+
Zero Data Collection
+
+
+
+
+ {/* Tools Grid */}
+
+
+ {filteredTools.map((tool, index) => (
+
+
+
+ ))}
+
+
+
+ {/* No Results */}
+ {filteredTools.length === 0 && (
+
+
🔍
+
+ No tools found matching "{searchTerm}"
+
+
+ Try searching for "editor", "encode", or "format"
+
+
+ )}
+
+ {/* Features Section */}
+
+
+
+ Built for Developers
+
+
+ Every tool is crafted with developer experience and performance in mind
+
+
+
+
+
+
+
+
+
+ Lightning Fast
+
+
+ Optimized algorithms and local processing ensure instant results
+
+
+
+
+
+
+
+
+ Privacy First
+
+
+ Your data never leaves your browser. Zero tracking, zero storage
+
+
+
+
+
+
+
+
+ No Limits
+
+
+ Handle massive files and complex data without restrictions
+
+
+
+
+
+
+
+
+ Dev Focused
+
+
+ Syntax highlighting, shortcuts, and workflows developers love
+
+
-
- Developer Friendly
-
-
- Clean interface with syntax highlighting and easy copy-paste functionality
-
diff --git a/src/pages/PrivacyPolicy.js b/src/pages/PrivacyPolicy.js
new file mode 100644
index 00000000..3b466719
--- /dev/null
+++ b/src/pages/PrivacyPolicy.js
@@ -0,0 +1,265 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { ArrowLeft, Shield, Lock, Eye, Server, Cookie, BarChart3, Globe } from 'lucide-react';
+import { SITE_CONFIG } from '../config/tools';
+
+const PrivacyPolicy = () => {
+ return (
+
+
+ {/* Header */}
+
+
+
+ Back to Home
+
+
+
+
+
+
+
+
+ Privacy Policy
+
+
+ Last updated: {new Date().toLocaleDateString()}
+
+
+
+
+
+ {/* Content */}
+
+
+
+ {/* Privacy-First Commitment */}
+
+
+
+
+ Our Privacy-First Commitment
+
+
+ At {SITE_CONFIG.title}, "Privacy-First" isn't just a marketing term—it's our core architectural principle. Your data privacy is protected by design, not by policy alone.
+
+
+
+
+
+
+
+
100% Client-Side
+
All processing happens in your browser
+
+
+
+
+
+
+
+
Zero Data Upload
+
Your sensitive data never leaves your device
+
+
+
+
+
+
+
+
+
+ 1. Information We Collect
+
+
+
+
+
+ ❌ What We DON'T Collect:
+
+
+ - Your input data (JSON, CSV, URLs, text, etc.)
+ - Files you upload or paste into our tools
+ - Personal information (name, email, address)
+ - Login credentials or user accounts
+ - IP addresses or device fingerprints
+ - Browsing history or cross-site tracking
+
+
+
+
+
+ ✅ What We DO Collect (via Google Analytics):
+
+
+ - Anonymous page views and session duration
+ - Which tools are most popular (aggregated data only)
+ - General geographic region (country/state level)
+ - Browser type and device type (for compatibility)
+ - Referral sources (how you found our site)
+
+
+
+
+
+
+
+
+ 2. Google Analytics Usage
+
+
+ We use Google Analytics to understand how our tools are used and to improve the service. This helps us answer questions like:
+
+
+ - Which tools are most helpful to developers?
+ - Are there performance issues on certain devices?
+ - How can we improve the user experience?
+
+
+
+ Important: Google Analytics only sees that someone visited "dewe.dev/beautifier" - it never sees the actual JSON code you're beautifying or any data you process with our tools.
+
+
+
+
+
+
+
+ 3. How Our Tools Work
+
+
+
Technical Architecture:
+
+
+
CLIENT
+
Your browser downloads our JavaScript code
+
+
+
LOCAL
+
All processing happens locally in your browser's memory
+
+
+
SECURE
+
No data transmission to our servers for processing
+
+
+
+
+
+
+
+
+ 4. Cookies and Local Storage
+
+
+ We use minimal cookies and local storage for:
+
+
+ - Google Analytics: Anonymous tracking cookies (you can opt-out)
+ - Theme Preference: Remembering if you prefer dark/light mode
+ - No Personal Data: We never store your processed data locally
+
+
+
+
+
+
+ 5. Future Advertising (Google AdSense)
+
+
+
+ 🔮 Planned Implementation:
+
+
+ To keep our tools free, we plan to display Google AdSense advertisements. When implemented:
+
+
+ - Ads will be clearly marked and non-intrusive
+ - No impact on tool functionality or performance
+ - Google may use cookies for ad personalization
+ - You can opt-out of personalized ads via Google settings
+ - We will NEVER share your tool usage data with advertisers
+
+
+
+
+
+
+ 6. Your Rights and Controls
+
+
+
+
Analytics Opt-Out:
+
+ Install browser extensions like uBlock Origin or use Google's opt-out tools to disable analytics tracking.
+
+
+
+
Data Control:
+
+ Since we don't collect your data, there's nothing to delete or export. Your data stays with you.
+
+
+
+
+
+
+
+ 7. Third-Party Services
+
+
+
+
+
+
+ 8. Changes to This Policy
+
+
+ We may update this privacy policy from time to time. We will notify users of any material changes by updating the "Last updated" date at the top of this policy. Your continued use of the service after any changes constitutes acceptance of the new policy.
+
+
+
+
+
+ 9. Contact Us
+
+
+ If you have any questions about this Privacy Policy or our privacy practices, please contact us at{' '}
+
+ dewe.developer@gmail.com
+
+ {' '}or visit {SITE_CONFIG.domain}.
+
+
+
+
+
+
+ {/* Footer */}
+
+
+ © {SITE_CONFIG.year} {SITE_CONFIG.title} • Your privacy is our priority
+
+
+
+
+ );
+};
+
+export default PrivacyPolicy;
diff --git a/src/pages/TableEditor.js b/src/pages/TableEditor.js
index 1b80692e..a5f1c8ea 100644
--- a/src/pages/TableEditor.js
+++ b/src/pages/TableEditor.js
@@ -1276,13 +1276,45 @@ const TableEditor = () => {
})),
);
- // Auto-scroll to the right to show the new column
+ // Auto-trigger header editing for the new column
+ setEditingHeader(newColumnId);
+
+ // Auto-scroll to the right to show the new column and focus on header input
setTimeout(() => {
- const tableContainer = document.querySelector(".overflow-auto");
- if (tableContainer) {
- tableContainer.scrollLeft = tableContainer.scrollWidth;
+ // Try multiple selectors to find the table container
+ const selectors = [
+ '[class*="overflow-auto"][class*="max-h-"]',
+ '.overflow-auto',
+ 'div[class*="overflow-auto"]'
+ ];
+
+ let tableContainer = null;
+ for (const selector of selectors) {
+ tableContainer = document.querySelector(selector);
+ if (tableContainer) break;
}
- }, 100);
+
+ if (tableContainer) {
+ // Check if horizontal scrolling is needed
+ const needsScroll = tableContainer.scrollWidth > tableContainer.clientWidth;
+ if (needsScroll) {
+ // Smooth scroll to the far right to show the new column
+ tableContainer.scrollTo({
+ left: tableContainer.scrollWidth - tableContainer.clientWidth,
+ behavior: 'smooth'
+ });
+ }
+ }
+
+ // Focus on the header input field after scroll
+ setTimeout(() => {
+ const headerInput = document.querySelector(`input[value="${newColumn.name}"]`);
+ if (headerInput) {
+ headerInput.focus();
+ headerInput.select(); // Select all text for easy replacement
+ }
+ }, 100);
+ }, 200);
};
// Delete selected rows
@@ -2419,7 +2451,7 @@ const TableEditor = () => {
{editingCell?.rowId === row.id &&
editingCell?.columnId === column.id ? (
(() => {
- const cellValue = row[column.id] || "";
+ const cellValue = String(row[column.id] || "");
const isLongValue =
cellValue.length > 100 ||
cellValue.includes("\n");
@@ -2474,7 +2506,7 @@ const TableEditor = () => {
const format = detectCellFormat(
row[column.id],
);
- const cellValue = row[column.id] || "";
+ const cellValue = String(row[column.id] || "");
const isLongValue = cellValue.length > 50;
if (format) {
@@ -2540,20 +2572,25 @@ const TableEditor = () => {
))}
{/* System Row - Add Row */}
-
- |
+ |
+ {/* Sticky Add Row button on the left */}
+ |
|
+ {/* Empty cells to fill the rest of the row */}
+
+ {/* Empty space for visual consistency */}
+ |
diff --git a/src/pages/TermsOfService.js b/src/pages/TermsOfService.js
new file mode 100644
index 00000000..6d4589b9
--- /dev/null
+++ b/src/pages/TermsOfService.js
@@ -0,0 +1,170 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { ArrowLeft, Shield, Code, Globe } from 'lucide-react';
+import { SITE_CONFIG } from '../config/tools';
+
+const TermsOfService = () => {
+ return (
+
+
+ {/* Header */}
+
+
+
+ Back to Home
+
+
+
+
+
+
+
+
+ Terms of Service
+
+
+ Last updated: {new Date().toLocaleDateString()}
+
+
+
+
+
+ {/* Content */}
+
+
+
+
+
+
+ 1. Acceptance of Terms
+
+
+ By accessing and using {SITE_CONFIG.title} ("{SITE_CONFIG.domain}"), you accept and agree to be bound by the terms and provision of this agreement. If you do not agree to abide by the above, please do not use this service.
+
+
+
+
+
+
+ 2. Service Description
+
+
+ {SITE_CONFIG.title} provides a collection of developer tools including but not limited to:
+
+
+ - Object and Table Editors for JSON, CSV, and other data formats
+ - URL and Base64 Encoders/Decoders
+ - Code Beautifiers and Minifiers
+ - Text Analysis and Comparison Tools
+ - Other web-based developer utilities
+
+
+ All tools run entirely in your browser - no data is sent to our servers for processing.
+
+
+
+
+
+ 3. Privacy-First Approach
+
+
+
+ 🔒 What "Privacy-First" means at {SITE_CONFIG.title}:
+
+
+ - Client-Side Processing: All tools process your data locally in your browser
+ - No Data Upload: Your sensitive data never leaves your device
+ - No Storage: We don't store, cache, or log your input data
+ - Minimal Analytics: We only collect anonymous usage statistics via Google Analytics
+ - No Tracking: No user accounts, no personal data collection
+
+
+
+ We use Google Analytics to understand how our tools are used (page views, popular tools, etc.) but we never track or store the actual data you process with our tools.
+
+
+
+
+
+ 4. Use License
+
+
+ Permission is granted to temporarily use {SITE_CONFIG.title} for personal and commercial purposes. This is the grant of a license, not a transfer of title, and under this license you may not:
+
+
+ - Use the service for any illegal or unauthorized purpose
+ - Attempt to reverse engineer or extract source code
+ - Use automated tools to overload our servers
+ - Redistribute or resell access to the service
+
+
+
+
+
+ 5. Disclaimer
+
+
+ The materials on {SITE_CONFIG.title} are provided on an 'as is' basis. {SITE_CONFIG.title} makes no warranties, expressed or implied, and hereby disclaims and negates all other warranties including without limitation, implied warranties or conditions of merchantability, fitness for a particular purpose, or non-infringement of intellectual property or other violation of rights.
+
+
+
+
+
+ 6. Limitations
+
+
+ In no event shall {SITE_CONFIG.title} or its suppliers be liable for any damages (including, without limitation, damages for loss of data or profit, or due to business interruption) arising out of the use or inability to use the materials on {SITE_CONFIG.title}, even if {SITE_CONFIG.title} or an authorized representative has been notified orally or in writing of the possibility of such damage.
+
+
+
+
+
+ 7. Future Monetization
+
+
+
+ Transparency Notice: We plan to implement Google AdSense advertisements in the future to support the free operation of this service. When implemented, ads will be clearly marked and will not interfere with tool functionality. Our privacy-first approach will remain unchanged - we will never sell or share your usage data with advertisers.
+
+
+
+
+
+
+ 8. Revisions
+
+
+ {SITE_CONFIG.title} may revise these terms of service at any time without notice. By using this service, you are agreeing to be bound by the then current version of these terms of service.
+
+
+
+
+
+ 9. Contact Information
+
+
+ If you have any questions about these Terms of Service, please contact us at{' '}
+
+ dewe.developer@gmail.com
+
+ {' '}or through our website at {SITE_CONFIG.domain}.
+
+
+
+
+
+
+ {/* Footer */}
+
+
+ © {SITE_CONFIG.year} {SITE_CONFIG.title} • Built with ❤️ for developers worldwide
+
+
+
+
+ );
+};
+
+export default TermsOfService;
diff --git a/src/utils/analytics.js b/src/utils/analytics.js
new file mode 100644
index 00000000..38dd6bc9
--- /dev/null
+++ b/src/utils/analytics.js
@@ -0,0 +1,143 @@
+// Google Analytics utility for React SPA
+// Implements best practices for Single Page Applications
+
+// Google Analytics configuration
+const GA_MEASUREMENT_ID = 'G-S3K5P2PWV6';
+
+// Initialize Google Analytics with Consent Mode v2
+export const initGA = () => {
+ // Only initialize in production and if not already loaded
+ if (process.env.NODE_ENV !== 'production' || window.gtag) {
+ return;
+ }
+
+ // Initialize gtag function first (required for Consent Mode)
+ window.dataLayer = window.dataLayer || [];
+ function gtag() {
+ window.dataLayer.push(arguments);
+ }
+ window.gtag = gtag;
+
+ // Initialize Consent Mode v2 BEFORE loading GA script
+ const { initConsentMode, applyStoredConsent } = require('./consentManager');
+ initConsentMode();
+
+ // Create script elements
+ const gtagScript = document.createElement('script');
+ gtagScript.async = true;
+ gtagScript.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`;
+ document.head.appendChild(gtagScript);
+
+ // Configure Google Analytics after script loads
+ gtagScript.onload = () => {
+ gtag('js', new Date());
+ gtag('config', GA_MEASUREMENT_ID, {
+ // SPA-specific configurations
+ send_page_view: false, // We'll manually send page views
+ anonymize_ip: true, // Privacy-first approach
+ allow_google_signals: false, // Disable advertising features for privacy
+ allow_ad_personalization_signals: false, // Disable ad personalization
+ });
+
+ // Apply any stored consent preferences
+ applyStoredConsent();
+
+ console.log('🔍 Google Analytics initialized with Consent Mode v2');
+ };
+};
+
+// Track page views for SPA navigation
+export const trackPageView = (path, title) => {
+ if (process.env.NODE_ENV !== 'production' || !window.gtag) {
+ console.log(`📊 [DEV] Page view: ${path} - ${title}`);
+ return;
+ }
+
+ window.gtag('config', GA_MEASUREMENT_ID, {
+ page_path: path,
+ page_title: title,
+ });
+
+ console.log(`📊 Page view tracked: ${path}`);
+};
+
+// Track custom events
+export const trackEvent = (eventName, parameters = {}) => {
+ if (process.env.NODE_ENV !== 'production' || !window.gtag) {
+ console.log(`📊 [DEV] Event: ${eventName}`, parameters);
+ return;
+ }
+
+ window.gtag('event', eventName, {
+ ...parameters,
+ // Add privacy-friendly defaults
+ anonymize_ip: true,
+ });
+
+ console.log(`📊 Event tracked: ${eventName}`);
+};
+
+// Predefined events for common actions
+export const trackToolUsage = (toolName, action = 'use') => {
+ trackEvent('tool_interaction', {
+ tool_name: toolName,
+ action: action,
+ event_category: 'tools',
+ });
+};
+
+export const trackSearch = (searchTerm) => {
+ // Only track that a search happened, not the actual term for privacy
+ trackEvent('search', {
+ event_category: 'engagement',
+ // Don't send the actual search term for privacy
+ has_results: searchTerm.length > 0,
+ });
+};
+
+export const trackThemeChange = (theme) => {
+ trackEvent('theme_change', {
+ theme: theme,
+ event_category: 'preferences',
+ });
+};
+
+export const trackError = (errorType, errorMessage) => {
+ trackEvent('exception', {
+ description: `${errorType}: ${errorMessage}`,
+ fatal: false,
+ event_category: 'errors',
+ });
+};
+
+// Check if user has opted out of analytics
+export const isAnalyticsEnabled = () => {
+ // Check for common opt-out methods
+ if (navigator.doNotTrack === '1' ||
+ window.doNotTrack === '1' ||
+ navigator.msDoNotTrack === '1') {
+ return false;
+ }
+
+ // Check for ad blockers or analytics blockers
+ if (!window.gtag && process.env.NODE_ENV === 'production') {
+ return false;
+ }
+
+ return true;
+};
+
+// Privacy-friendly analytics info
+export const getAnalyticsInfo = () => {
+ return {
+ enabled: isAnalyticsEnabled(),
+ measurementId: GA_MEASUREMENT_ID,
+ environment: process.env.NODE_ENV,
+ privacyFeatures: {
+ anonymizeIp: true,
+ disableAdvertising: true,
+ disablePersonalization: true,
+ clientSideOnly: true,
+ }
+ };
+};
diff --git a/src/utils/consentManager.js b/src/utils/consentManager.js
new file mode 100644
index 00000000..3e136394
--- /dev/null
+++ b/src/utils/consentManager.js
@@ -0,0 +1,163 @@
+// GDPR Consent Management with Google Consent Mode v2
+// Implements TCF 2.2 compatible consent management
+
+// Consent categories
+export const CONSENT_CATEGORIES = {
+ NECESSARY: 'necessary',
+ ANALYTICS: 'analytics_storage',
+ ADVERTISING: 'ad_storage',
+ PERSONALIZATION: 'ad_personalization',
+ USER_DATA: 'ad_user_data'
+};
+
+// Default consent state (denied until user consents)
+const DEFAULT_CONSENT = {
+ [CONSENT_CATEGORIES.NECESSARY]: 'granted', // Always granted for essential functionality
+ [CONSENT_CATEGORIES.ANALYTICS]: 'denied',
+ [CONSENT_CATEGORIES.ADVERTISING]: 'denied',
+ [CONSENT_CATEGORIES.PERSONALIZATION]: 'denied',
+ [CONSENT_CATEGORIES.USER_DATA]: 'denied'
+};
+
+// Check if user is in EEA (European Economic Area)
+export const isEEAUser = () => {
+ // Simple timezone-based detection (not 100% accurate but good enough)
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
+ const eeaTimezones = [
+ 'Europe/', 'Atlantic/Reykjavik', 'Atlantic/Faroe', 'Atlantic/Canary',
+ 'Africa/Ceuta', 'Arctic/Longyearbyen'
+ ];
+
+ return eeaTimezones.some(tz => timezone.startsWith(tz));
+};
+
+// Initialize Google Consent Mode
+export const initConsentMode = () => {
+ if (typeof window === 'undefined' || !window.gtag) return;
+
+ // Set default consent state
+ window.gtag('consent', 'default', {
+ ...DEFAULT_CONSENT,
+ region: isEEAUser() ? ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO'] : ['US'],
+ wait_for_update: 500 // Wait 500ms for consent update
+ });
+
+ console.log('🍪 Consent Mode v2 initialized');
+};
+
+// Update consent based on user choice
+export const updateConsent = (consentChoices) => {
+ if (typeof window === 'undefined' || !window.gtag) return;
+
+ window.gtag('consent', 'update', consentChoices);
+
+ // Store consent in localStorage
+ localStorage.setItem('consent_preferences', JSON.stringify({
+ ...consentChoices,
+ timestamp: Date.now(),
+ version: '2.0'
+ }));
+
+ console.log('🍪 Consent updated:', consentChoices);
+};
+
+// Get stored consent preferences
+export const getStoredConsent = () => {
+ try {
+ const stored = localStorage.getItem('consent_preferences');
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ // Check if consent is less than 1 year old
+ if (Date.now() - parsed.timestamp < 365 * 24 * 60 * 60 * 1000) {
+ return parsed;
+ }
+ }
+ } catch (error) {
+ console.error('Error reading stored consent:', error);
+ }
+ return null;
+};
+
+// Check if consent banner should be shown
+export const shouldShowConsentBanner = () => {
+ // Only show for EEA users who haven't consented
+ return isEEAUser() && !getStoredConsent();
+};
+
+// Predefined consent configurations
+export const CONSENT_CONFIGS = {
+ // Accept all (for users who want full functionality)
+ ACCEPT_ALL: {
+ [CONSENT_CATEGORIES.NECESSARY]: 'granted',
+ [CONSENT_CATEGORIES.ANALYTICS]: 'granted',
+ [CONSENT_CATEGORIES.ADVERTISING]: 'granted',
+ [CONSENT_CATEGORIES.PERSONALIZATION]: 'granted',
+ [CONSENT_CATEGORIES.USER_DATA]: 'granted'
+ },
+
+ // Essential only (minimal consent)
+ ESSENTIAL_ONLY: {
+ [CONSENT_CATEGORIES.NECESSARY]: 'granted',
+ [CONSENT_CATEGORIES.ANALYTICS]: 'denied',
+ [CONSENT_CATEGORIES.ADVERTISING]: 'denied',
+ [CONSENT_CATEGORIES.PERSONALIZATION]: 'denied',
+ [CONSENT_CATEGORIES.USER_DATA]: 'denied'
+ },
+
+ // Analytics only (for users who want to help improve the service)
+ ANALYTICS_ONLY: {
+ [CONSENT_CATEGORIES.NECESSARY]: 'granted',
+ [CONSENT_CATEGORIES.ANALYTICS]: 'granted',
+ [CONSENT_CATEGORIES.ADVERTISING]: 'denied',
+ [CONSENT_CATEGORIES.PERSONALIZATION]: 'denied',
+ [CONSENT_CATEGORIES.USER_DATA]: 'denied'
+ }
+};
+
+// Apply stored consent on page load
+export const applyStoredConsent = () => {
+ const stored = getStoredConsent();
+ if (stored && window.gtag) {
+ const { timestamp, version, ...consentChoices } = stored;
+ window.gtag('consent', 'update', consentChoices);
+ console.log('🍪 Applied stored consent:', consentChoices);
+ }
+};
+
+// Consent banner component data
+export const getConsentBannerData = () => {
+ return {
+ title: 'We respect your privacy',
+ description: 'We use cookies and similar technologies to improve your experience, analyze site usage, and assist in our marketing efforts. Your data stays private with our client-side tools.',
+ purposes: [
+ {
+ id: CONSENT_CATEGORIES.NECESSARY,
+ name: 'Essential',
+ description: 'Required for basic site functionality',
+ required: true
+ },
+ {
+ id: CONSENT_CATEGORIES.ANALYTICS,
+ name: 'Analytics',
+ description: 'Help us understand how you use our tools (Google Analytics)',
+ required: false
+ },
+ {
+ id: CONSENT_CATEGORIES.ADVERTISING,
+ name: 'Advertising',
+ description: 'Future ad personalization (not yet implemented)',
+ required: false
+ }
+ ],
+ buttons: {
+ acceptAll: 'Accept All',
+ essentialOnly: 'Essential Only',
+ customize: 'Customize',
+ save: 'Save Preferences'
+ },
+ links: {
+ privacy: '/privacy',
+ terms: '/terms'
+ }
+ };
+};
diff --git a/src/utils/seo.js b/src/utils/seo.js
new file mode 100644
index 00000000..db02afa8
--- /dev/null
+++ b/src/utils/seo.js
@@ -0,0 +1,212 @@
+import { TOOLS, SITE_CONFIG } from '../config/tools';
+
+// SEO metadata generator
+export const generateSEOData = (path) => {
+ const baseUrl = SITE_CONFIG.domain;
+ const defaultTitle = `${SITE_CONFIG.title} - ${SITE_CONFIG.subtitle}`;
+ const defaultDescription = SITE_CONFIG.description;
+
+ // Find tool by path
+ const tool = TOOLS.find(t => t.path === path);
+
+ // Generate SEO data based on route
+ switch (path) {
+ case '/':
+ return {
+ title: defaultTitle,
+ description: `${SITE_CONFIG.totalTools} professional developer utilities. ${defaultDescription}. JSON editor, URL encoder, Base64 converter, code beautifier, and more.`,
+ keywords: 'developer tools, JSON editor, URL encoder, Base64 converter, code beautifier, text diff, web utilities, programming tools',
+ canonical: baseUrl,
+ ogType: 'website',
+ ogImage: `${baseUrl}/og-image.png`,
+ twitterCard: 'summary_large_image',
+ structuredData: {
+ '@context': 'https://schema.org',
+ '@type': 'WebSite',
+ name: SITE_CONFIG.title,
+ description: defaultDescription,
+ url: baseUrl,
+ potentialAction: {
+ '@type': 'SearchAction',
+ target: `${baseUrl}/?search={search_term_string}`,
+ 'query-input': 'required name=search_term_string'
+ },
+ publisher: {
+ '@type': 'Organization',
+ name: SITE_CONFIG.title,
+ url: baseUrl
+ }
+ }
+ };
+
+ case '/privacy':
+ return {
+ title: `Privacy Policy - ${SITE_CONFIG.title}`,
+ description: 'Our privacy-first approach to developer tools. Learn how we protect your data with 100% client-side processing and minimal analytics.',
+ keywords: 'privacy policy, data protection, client-side processing, developer tools privacy',
+ canonical: `${baseUrl}/privacy`,
+ ogType: 'article',
+ noindex: false,
+ structuredData: {
+ '@context': 'https://schema.org',
+ '@type': 'WebPage',
+ name: 'Privacy Policy',
+ description: 'Privacy policy for Dewe.Dev developer tools',
+ url: `${baseUrl}/privacy`,
+ isPartOf: {
+ '@type': 'WebSite',
+ name: SITE_CONFIG.title,
+ url: baseUrl
+ }
+ }
+ };
+
+ case '/terms':
+ return {
+ title: `Terms of Service - ${SITE_CONFIG.title}`,
+ description: 'Terms of service for using our developer tools. Professional-grade utilities with transparent policies.',
+ keywords: 'terms of service, developer tools terms, usage policy',
+ canonical: `${baseUrl}/terms`,
+ ogType: 'article',
+ noindex: false,
+ structuredData: {
+ '@context': 'https://schema.org',
+ '@type': 'WebPage',
+ name: 'Terms of Service',
+ description: 'Terms of service for Dewe.Dev developer tools',
+ url: `${baseUrl}/terms`,
+ isPartOf: {
+ '@type': 'WebSite',
+ name: SITE_CONFIG.title,
+ url: baseUrl
+ }
+ }
+ };
+
+ default:
+ if (tool) {
+ const toolKeywords = tool.tags.join(', ').toLowerCase();
+ return {
+ title: `${tool.name} - ${SITE_CONFIG.title}`,
+ description: `${tool.description}. Free online ${tool.name.toLowerCase()} tool. ${defaultDescription}.`,
+ keywords: `${toolKeywords}, ${tool.name.toLowerCase()}, developer tools, online tools, web utilities`,
+ canonical: `${baseUrl}${tool.path}`,
+ ogType: 'website',
+ ogImage: `${baseUrl}/og-tools.png`,
+ twitterCard: 'summary',
+ structuredData: {
+ '@context': 'https://schema.org',
+ '@type': 'SoftwareApplication',
+ name: tool.name,
+ description: tool.description,
+ url: `${baseUrl}${tool.path}`,
+ applicationCategory: 'DeveloperApplication',
+ operatingSystem: 'Web Browser',
+ offers: {
+ '@type': 'Offer',
+ price: '0',
+ priceCurrency: 'USD'
+ },
+ publisher: {
+ '@type': 'Organization',
+ name: SITE_CONFIG.title,
+ url: baseUrl
+ },
+ isPartOf: {
+ '@type': 'WebSite',
+ name: SITE_CONFIG.title,
+ url: baseUrl
+ },
+ keywords: toolKeywords,
+ featureList: tool.tags
+ }
+ };
+ }
+
+ // Fallback for unknown routes
+ return {
+ title: `Page Not Found - ${SITE_CONFIG.title}`,
+ description: defaultDescription,
+ keywords: 'developer tools, web utilities',
+ canonical: `${baseUrl}${path}`,
+ ogType: 'website',
+ noindex: true
+ };
+ }
+};
+
+// Generate Open Graph meta tags
+export const generateOGTags = (seoData) => {
+ return [
+ { property: 'og:type', content: seoData.ogType || 'website' },
+ { property: 'og:title', content: seoData.title },
+ { property: 'og:description', content: seoData.description },
+ { property: 'og:url', content: seoData.canonical },
+ { property: 'og:site_name', content: SITE_CONFIG.title },
+ ...(seoData.ogImage ? [{ property: 'og:image', content: seoData.ogImage }] : []),
+ { property: 'og:locale', content: 'en_US' }
+ ];
+};
+
+// Generate Twitter Card meta tags
+export const generateTwitterTags = (seoData) => {
+ return [
+ { name: 'twitter:card', content: seoData.twitterCard || 'summary' },
+ { name: 'twitter:title', content: seoData.title },
+ { name: 'twitter:description', content: seoData.description },
+ ...(seoData.ogImage ? [{ name: 'twitter:image', content: seoData.ogImage }] : [])
+ ];
+};
+
+// Generate all meta tags for a route
+export const generateMetaTags = (path) => {
+ const seoData = generateSEOData(path);
+
+ const basicMeta = [
+ { name: 'description', content: seoData.description },
+ { name: 'keywords', content: seoData.keywords },
+ { name: 'author', content: SITE_CONFIG.title },
+ { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
+ { name: 'robots', content: seoData.noindex ? 'noindex,nofollow' : 'index,follow' },
+ { name: 'googlebot', content: seoData.noindex ? 'noindex,nofollow' : 'index,follow' }
+ ];
+
+ const ogTags = generateOGTags(seoData);
+ const twitterTags = generateTwitterTags(seoData);
+
+ return {
+ title: seoData.title,
+ meta: [...basicMeta, ...ogTags, ...twitterTags],
+ link: [
+ { rel: 'canonical', href: seoData.canonical }
+ ],
+ structuredData: seoData.structuredData
+ };
+};
+
+// Core Web Vitals optimization hints
+export const getCoreWebVitalsOptimizations = () => {
+ return {
+ // Largest Contentful Paint (LCP)
+ lcp: {
+ preloadCriticalResources: true,
+ optimizeImages: true,
+ removeRenderBlockingResources: true
+ },
+
+ // First Input Delay (FID)
+ fid: {
+ minimizeJavaScript: true,
+ useWebWorkers: false, // Not needed for our tools
+ optimizeEventHandlers: true
+ },
+
+ // Cumulative Layout Shift (CLS)
+ cls: {
+ setImageDimensions: true,
+ reserveSpaceForAds: true, // Important for future AdSense
+ avoidDynamicContent: true,
+ useTransforms: true
+ }
+ };
+};
diff --git a/src/utils/sitemapGenerator.js b/src/utils/sitemapGenerator.js
new file mode 100644
index 00000000..538441b5
--- /dev/null
+++ b/src/utils/sitemapGenerator.js
@@ -0,0 +1,126 @@
+import { TOOLS, SITE_CONFIG } from '../config/tools';
+
+// Generate sitemap.xml content
+export const generateSitemap = () => {
+ const baseUrl = SITE_CONFIG.domain;
+ const currentDate = new Date().toISOString().split('T')[0]; // YYYY-MM-DD format
+
+ // Define all routes with their priorities and change frequencies
+ const routes = [
+ {
+ url: '/',
+ priority: '1.0',
+ changefreq: 'weekly',
+ lastmod: currentDate
+ },
+ // Tool pages
+ ...TOOLS.map(tool => ({
+ url: tool.path,
+ priority: '0.8',
+ changefreq: 'monthly',
+ lastmod: currentDate
+ })),
+ // Legal pages
+ {
+ url: '/privacy',
+ priority: '0.3',
+ changefreq: 'yearly',
+ lastmod: currentDate
+ },
+ {
+ url: '/terms',
+ priority: '0.3',
+ changefreq: 'yearly',
+ lastmod: currentDate
+ }
+ ];
+
+ // Generate XML sitemap
+ const sitemap = `
+
+${routes.map(route => `
+ ${baseUrl}${route.url}
+ ${route.lastmod}
+ ${route.changefreq}
+ ${route.priority}
+ `).join('\n')}
+`;
+
+ return sitemap;
+};
+
+// Generate robots.txt content
+export const generateRobotsTxt = () => {
+ const baseUrl = SITE_CONFIG.domain;
+
+ return `# Robots.txt for ${baseUrl}
+# Generated automatically
+
+User-agent: *
+Allow: /
+
+# Sitemap location
+Sitemap: ${baseUrl}/sitemap.xml
+
+# Block any future admin or private routes
+Disallow: /admin/
+Disallow: /api/
+Disallow: /.well-known/
+
+# Allow all major search engines
+User-agent: Googlebot
+Allow: /
+
+User-agent: Bingbot
+Allow: /
+
+User-agent: Slurp
+Allow: /
+
+User-agent: DuckDuckBot
+Allow: /
+
+# Crawl delay for politeness
+Crawl-delay: 1`;
+};
+
+// Build-time sitemap generation script
+export const buildSitemap = () => {
+ const fs = require('fs');
+ const path = require('path');
+
+ const publicDir = path.join(process.cwd(), 'public');
+
+ // Generate and write sitemap.xml
+ const sitemapContent = generateSitemap();
+ fs.writeFileSync(path.join(publicDir, 'sitemap.xml'), sitemapContent, 'utf8');
+
+ // Generate and write robots.txt
+ const robotsContent = generateRobotsTxt();
+ fs.writeFileSync(path.join(publicDir, 'robots.txt'), robotsContent, 'utf8');
+
+ console.log('✅ Sitemap and robots.txt generated successfully!');
+ console.log(`📍 Sitemap: ${SITE_CONFIG.domain}/sitemap.xml`);
+ console.log(`🤖 Robots: ${SITE_CONFIG.domain}/robots.txt`);
+};
+
+// Runtime sitemap data for dynamic generation
+export const getSitemapData = () => {
+ return {
+ routes: [
+ { path: '/', priority: 1.0, changefreq: 'weekly' },
+ ...TOOLS.map(tool => ({
+ path: tool.path,
+ priority: 0.8,
+ changefreq: 'monthly'
+ })),
+ { path: '/privacy', priority: 0.3, changefreq: 'yearly' },
+ { path: '/terms', priority: 0.3, changefreq: 'yearly' }
+ ],
+ baseUrl: SITE_CONFIG.domain,
+ totalUrls: TOOLS.length + 3 // tools + home + privacy + terms
+ };
+};