refactor: externalize AppPage component and streamline all tool pages

This commit is contained in:
2026-02-25 18:04:32 +01:00
parent 71c22e465e
commit 7eeb8399b3
13 changed files with 113 additions and 139 deletions

View File

@@ -1,17 +1,13 @@
import { FigletConverter } from '@/components/figlet/FigletConverter';
import { AppPage } from '@/components/layout/AppPage';
export default function FigletPage() {
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Figlet ASCII</h1>
<p className="text-muted-foreground">
ASCII Art Text Generator with 373 Fonts
</p>
</div>
<AppPage
title="Figlet ASCII"
description="ASCII Art Text Generator with 373 Fonts"
>
<FigletConverter />
</div>
</div>
</AppPage>
);
}

View File

@@ -1,17 +1,13 @@
import { FileConverter } from '@/components/media/FileConverter';
import { AppPage } from '@/components/layout/AppPage';
export default function MediaPage() {
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Media Converter</h1>
<p className="text-muted-foreground">
Professional browser-based media conversion for video, audio, and images
</p>
</div>
<AppPage
title="Media Converter"
description="Professional browser-based media conversion for video, audio, and images"
>
<FileConverter />
</div>
</div>
</AppPage>
);
}

View File

@@ -14,6 +14,7 @@ import { Textarea } from '@/components/ui/textarea';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PaletteGrid } from '@/components/pastel/PaletteGrid';
import { ExportMenu } from '@/components/pastel/ExportMenu';
import { AppPage } from '@/components/layout/AppPage';
import { useLighten, useDarken, useSaturate, useDesaturate, useRotate } from '@/lib/pastel/api/queries';
import { Loader2, Upload, Download } from 'lucide-react';
import { toast } from 'sonner';
@@ -92,15 +93,10 @@ export default function BatchPage() {
rotateMutation.isPending;
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Batch Operations</h1>
<p className="text-muted-foreground">
Process multiple colors at once with manipulation operations
</p>
</div>
<AppPage
title="Batch Operations"
description="Process multiple colors at once with manipulation operations"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input */}
<div className="space-y-6">
@@ -213,7 +209,6 @@ export default function BatchPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -12,6 +12,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useSimulateColorBlindness } from '@/lib/pastel/api/queries';
import { Loader2, Eye, Plus, X } from 'lucide-react';
import { toast } from 'sonner';
@@ -66,15 +67,10 @@ export default function ColorBlindPage() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Color Blindness Simulator</h1>
<p className="text-muted-foreground">
Simulate how colors appear with different types of color blindness
</p>
</div>
<AppPage
title="Color Blindness Simulator"
description="Simulate how colors appear with different types of color blindness"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */}
<div className="space-y-6">
@@ -229,7 +225,6 @@ export default function ColorBlindPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -5,6 +5,7 @@ import { ColorPicker } from '@/components/pastel/ColorPicker';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { getContrastRatio, hexToRgb, checkWCAGCompliance } from '@/lib/pastel/utils/color';
import { ArrowLeftRight, Check, X } from 'lucide-react';
@@ -57,15 +58,10 @@ export default function ContrastPage() {
);
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Contrast Checker</h1>
<p className="text-muted-foreground">
Test color combinations for WCAG 2.1 compliance
</p>
</div>
<AppPage
title="Contrast Checker"
description="Test color combinations for WCAG 2.1 compliance"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Color Pickers */}
<div className="space-y-6">
@@ -183,7 +179,6 @@ export default function ContrastPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -13,6 +13,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useGenerateDistinct } from '@/lib/pastel/api/queries';
import { Loader2 } from 'lucide-react';
import { toast } from 'sonner';
@@ -38,15 +39,10 @@ export default function DistinctPage() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Distinct Colors Generator</h1>
<p className="text-muted-foreground">
Generate visually distinct colors using simulated annealing
</p>
</div>
<AppPage
title="Distinct Colors Generator"
description="Generate visually distinct colors using simulated annealing"
>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Controls */}
<div className="lg:col-span-1">
@@ -137,7 +133,6 @@ export default function DistinctPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -7,6 +7,7 @@ import { ExportMenu } from '@/components/pastel/ExportMenu';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useGenerateGradient } from '@/lib/pastel/api/queries';
import { Loader2, Plus, X } from 'lucide-react';
import { toast } from 'sonner';
@@ -48,15 +49,10 @@ export default function GradientPage() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Gradient Creator</h1>
<p className="text-muted-foreground">
Create smooth color gradients with multiple stops
</p>
</div>
<AppPage
title="Gradient Creator"
description="Create smooth color gradients with multiple stops"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */}
<div className="space-y-6">
@@ -169,7 +165,6 @@ export default function GradientPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -13,6 +13,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useGeneratePalette } from '@/lib/pastel/api/queries';
import { Loader2, Palette } from 'lucide-react';
import { toast } from 'sonner';
@@ -59,15 +60,10 @@ export default function HarmonyPage() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Harmony Palette Generator</h1>
<p className="text-muted-foreground">
Create color harmonies based on color theory principles
</p>
</div>
<AppPage
title="Harmony Palette Generator"
description="Create color harmonies based on color theory principles"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Controls */}
<div className="space-y-6">
@@ -160,7 +156,6 @@ export default function HarmonyPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -11,6 +11,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { Card, CardContent } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useNamedColors } from '@/lib/pastel/api/queries';
import { Loader2 } from 'lucide-react';
import { parse_color } from '@valknarthing/pastel-wasm';
@@ -44,15 +45,11 @@ export default function NamedColorsPage() {
}, [data, search, sortBy]);
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Named Colors</h1>
<p className="text-muted-foreground">
Explore 148 CSS/X11 named colors
</p>
</div>
<AppPage
title="Named Colors"
description="Explore 148 CSS/X11 named colors"
>
<div className="space-y-8">
{/* Search and Filters */}
<div className="flex flex-col sm:flex-row gap-4">
<div className="flex-1">
@@ -120,6 +117,6 @@ export default function NamedColorsPage() {
</CardContent>
</Card>
</div>
</div>
</AppPage>
);
}

View File

@@ -7,6 +7,7 @@ import { ColorDisplay } from '@/components/pastel/ColorDisplay';
import { ColorInfo } from '@/components/pastel/ColorInfo';
import { ManipulationPanel } from '@/components/pastel/ManipulationPanel';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useColorInfo } from '@/lib/pastel/api/queries';
import { useColorHistory } from '@/lib/pastel/stores/historyStore';
import { Loader2, Share2, History, X } from 'lucide-react';
@@ -73,15 +74,10 @@ function PlaygroundContent() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Pastel</h1>
<p className="text-muted-foreground">
Interactive color manipulation and analysis tool
</p>
</div>
<AppPage
title="Pastel"
description="Interactive color manipulation and analysis tool"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Left Column: Color Picker and Display */}
<div className="space-y-6">
@@ -197,8 +193,7 @@ function PlaygroundContent() {
</Card>
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -5,6 +5,7 @@ import { ColorPicker } from '@/components/pastel/ColorPicker';
import { ColorDisplay } from '@/components/pastel/ColorDisplay';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppPage } from '@/components/layout/AppPage';
import { useTextColor } from '@/lib/pastel/api/queries';
import { Loader2, Palette, Plus, X, CheckCircle2, XCircle } from 'lucide-react';
import { toast } from 'sonner';
@@ -55,15 +56,10 @@ export default function TextColorPage() {
};
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Text Color Optimizer</h1>
<p className="text-muted-foreground">
Automatically find the best text color (black or white) for any background color
</p>
</div>
<AppPage
title="Text Color Optimizer"
description="Automatically find the best text color (black or white) for any background color"
>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Input */}
<div className="space-y-6">
@@ -226,7 +222,6 @@ export default function TextColorPage() {
)}
</div>
</div>
</div>
</div>
</AppPage>
);
}

View File

@@ -1,17 +1,13 @@
import MainConverter from '@/components/units/MainConverter';
import { AppPage } from '@/components/layout/AppPage';
export default function UnitsPage() {
return (
<div className="min-h-screen py-12">
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">Units Converter</h1>
<p className="text-muted-foreground">
Smart unit converter with 187 units across 23 categories
</p>
</div>
<AppPage
title="Units Converter"
description="Smart unit converter with 187 units across 23 categories"
>
<MainConverter />
</div>
</div>
</AppPage>
);
}

View File

@@ -0,0 +1,29 @@
'use client';
import * as React from 'react';
import { cn } from '@/lib/utils';
interface AppPageProps {
title: string;
description?: string;
children: React.ReactNode;
className?: string;
}
export function AppPage({ title, description, children, className }: AppPageProps) {
return (
<div className={cn("min-h-screen py-12", className)}>
<div className="max-w-7xl mx-auto px-8 space-y-8">
<div>
<h1 className="text-4xl font-bold mb-2">{title}</h1>
{description && (
<p className="text-muted-foreground">
{description}
</p>
)}
</div>
{children}
</div>
</div>
);
}