refactor: externalize tool definitions and polish app shell

- Create lib/tools.tsx as single source of truth for all tool metadata
  (title, shortTitle, navTitle, description, summary, icon, etc.)
- Update AppSidebar to render nav from centralized tools list with
  descriptions, remove collapse footer button
- Update AppHeader with sidebar collapse toggle, tool short title,
  and app logo; remove breadcrumbs
- Update AppPage to auto-resolve tool icon from pathname
- Update ToolsGrid/ToolCard to use shared tools data, remove per-card
  gradients for uniform styling
- Add per-tool HTML title via metadata exports (title template in root
  layout)
- Style landing page and 404 headings with primary theme color
- Add Toolbox icon to hero CTA, GitFork icon link in footer
- Remove footer from error page and "View on Dev" buttons
- Extract ColorPage client component for RSC metadata compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 17:46:54 +01:00
parent 5a0d1863ec
commit a400f694fe
18 changed files with 679 additions and 912 deletions

View File

@@ -2,55 +2,7 @@
import { motion } from 'framer-motion';
import ToolCard from './ToolCard';
import { ColorIcon, UnitsIcon, ASCIIIcon, MediaIcon, FaviconIcon } from '@/components/AppIcons';
const tools = [
{
title: 'Color',
description: 'Modern color manipulation toolkit with palette generation, accessibility testing, and format conversion. Supports hex, RGB, HSL, Lab, and more.',
url: '/color',
gradient: 'gradient-indigo-purple',
accentColor: '#a855f7',
badges: ['Open Source', 'WCAG', 'Free'],
icon: <ColorIcon className="w-12 h-12 text-white" />,
},
{
title: 'Units',
description: 'Smart unit converter with 187 units across 23 categories. Real-time bidirectional conversion with fuzzy search.',
url: '/units',
gradient: 'gradient-cyan-purple',
accentColor: '#2dd4bf',
badges: ['Open Source', 'Real-time', 'Free'],
icon: <UnitsIcon className="w-12 h-12 text-white" />,
},
{
title: 'ASCII',
description: 'ASCII art text generator with 373 fonts. Create stunning text banners, terminal art, and retro designs with live preview and multiple export formats.',
url: '/ascii',
gradient: 'gradient-yellow-amber',
accentColor: '#eab308',
badges: ['Open Source', 'ASCII Art', 'Free'],
icon: <ASCIIIcon className="w-12 h-12 text-white" />,
},
{
title: 'Media',
description: 'Modern browser-based file converter powered by WebAssembly. Convert videos, images, and audio locally without server uploads. Privacy-first with no file size limits.',
url: '/media',
gradient: 'gradient-green-teal',
accentColor: '#10b981',
badges: ['Open Source', 'Converter', 'Free'],
icon: <MediaIcon className="w-12 h-12 text-white" />,
},
{
title: 'Favicon',
description: 'Generate a complete set of favicons for your website. Includes PWA manifest and HTML embed code. All processing happens locally in your browser.',
url: '/favicon',
gradient: 'gradient-blue-cyan',
accentColor: '#3b82f6',
badges: ['Open Source', 'Generator', 'Free'],
icon: <FaviconIcon className="w-12 h-12 text-white" />,
},
];
import { tools } from '@/lib/tools';
export default function ToolsGrid() {
return (
@@ -74,19 +26,20 @@ export default function ToolsGrid() {
{/* Tools grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{tools.map((tool, index) => (
<ToolCard
key={tool.title}
title={tool.title}
description={tool.description}
icon={tool.icon}
url={tool.url}
gradient={tool.gradient}
accentColor={tool.accentColor}
badges={tool.badges}
index={index}
/>
))}
{tools.map((tool, index) => {
const Icon = tool.icon;
return (
<ToolCard
key={tool.href}
title={tool.title}
description={tool.summary}
icon={<Icon className="w-12 h-12" />}
url={tool.href}
badges={tool.badges}
index={index}
/>
);
})}
</div>
</div>
</section>