feat (v1.0.0): initial refactor and redesign

This commit is contained in:
Aidan 2025-10-09 04:12:05 -04:00
parent 3058aa1ab4
commit fe9b50b30e
134 changed files with 17792 additions and 3670 deletions

View file

@ -32,63 +32,71 @@ export default function AIStack({ tools }: AIStackProps) {
}
return (
<section className="p-4 sm:p-8 border-2 border-gray-700 rounded-lg hover:border-gray-600 transition-colors duration-300">
<div className="flex flex-row justify-between">
<h2 className="text-2xl font-semibold mb-6 text-gray-200 flex items-center gap-2">
<TbStack2 size={24} />
<section className="p-4 sm:p-6 lg:p-8 border-2 border-gray-700 rounded-lg hover:border-gray-600 transition-colors duration-300">
<div className="flex flex-col sm:flex-row sm:justify-between gap-2 mb-4 sm:mb-6">
<h2 className="text-xl sm:text-2xl font-semibold text-gray-200 flex items-center gap-2">
<TbStack2 size={20} className="sm:w-6 sm:h-6" />
My AI Stack
</h2>
<p className="text-muted-foreground">The AI tools I use as a part of my routine and workflow.</p>
<p className="text-muted-foreground text-xs sm:text-sm">The AI tools I use as a part of my routine and workflow.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4">
{tools.map((tool, index) => (
<div key={index} className="p-4 border border-gray-700 rounded-lg hover:border-gray-500 transition-all duration-300 flex flex-col">
<div className="flex items-start justify-between mb-3 flex-1">
<div className="flex items-center gap-3 flex-1">
{tool.icon && <tool.icon className="text-2xl text-gray-300" />}
<div key={index} className="p-3 sm:p-4 border border-gray-700 rounded-lg hover:border-gray-500 transition-all duration-300 flex flex-col">
<div className="flex items-start justify-between mb-2 sm:mb-3 flex-1">
<div className="flex items-center gap-2 sm:gap-3 flex-1 min-w-0">
{tool.icon && <tool.icon className="text-xl sm:text-2xl text-gray-300 flex-shrink-0" />}
{tool.svg && (
<div className="w-6 h-6 text-gray-300 fill-current">
<div className="w-5 h-5 sm:w-6 sm:h-6 text-gray-300 fill-current flex-shrink-0">
{tool.svg}
</div>
)}
<div className="flex-1">
<div className="flex items-center justify-between">
<h3 className="font-semibold text-gray-200">{tool.name}</h3>
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between gap-2">
<h3 className="font-semibold text-sm sm:text-base text-gray-200 truncate">{tool.name}</h3>
{tool.price !== undefined && (
<div className="flex items-center gap-2">
<div className="flex items-center gap-1 sm:gap-2 flex-shrink-0">
{tool.discountedPrice !== undefined ? (
<>
<span className="text-gray-500 line-through">
<span className="text-xs sm:text-sm text-gray-500 line-through">
{formatPrice(tool.price)}
</span>
<span className="text-gray-200">
<span className="text-xs sm:text-sm text-gray-200">
{formatPrice(tool.discountedPrice)}
</span>
</>
) : (
<span className="text-gray-200">
<span className="text-xs sm:text-sm text-gray-200">
{formatPrice(tool.price)}
</span>
)}
</div>
)}
</div>
<p className="text-sm text-gray-400">{tool.description}</p>
<p className="text-xs sm:text-sm text-gray-400 line-clamp-2">{tool.description}</p>
</div>
</div>
</div>
<div className="flex items-center justify-between mt-auto">
<span className={`text-xs px-2 py-1 rounded-full border ${getStatusColor(tool.status)}`}>
<div className="flex items-center justify-between mt-auto pt-2 gap-2">
<span className={`text-xs px-2 py-0.5 sm:py-1 rounded-full border whitespace-nowrap ${getStatusColor(tool.status)}`}>
{getStatusLabel(tool.status)}
</span>
<span className="flex flex-row items-center gap-4">
<span className="flex flex-row items-center gap-2 sm:gap-4">
{tool.link && (
<Link href={tool.link} className="text-blue-400 hover:text-blue-300 text-sm" target="_blank" rel="noopener noreferrer">
<Link
href={tool.link}
className="text-xs sm:text-sm hover:text-blue-300 whitespace-nowrap"
target="_blank"
rel="noopener noreferrer"
>
Visit
</Link>
)}
{tool.usage && (
<Link href={tool.usage} className="text-blue-400 hover:text-blue-300 text-sm">
{(tool.usage || tool.hasUsage) && (
<Link
href={tool.usage ?? '/ai/usage'}
className="text-xs sm:text-sm hover:text-blue-300 whitespace-nowrap"
>
Usage
</Link>
)}

View file

@ -1,4 +1,5 @@
import { Brain, Star } from 'lucide-react'
import { Brain } from 'lucide-react'
import PaginatedCardList from '@/components/ui/PaginatedCardList'
import type { FavoriteModel } from '../types'
interface FavoriteModelsProps {
@ -7,36 +8,29 @@ interface FavoriteModelsProps {
export default function FavoriteModels({ models }: FavoriteModelsProps) {
return (
<section className="p-4 sm:p-8 border-2 border-gray-700 rounded-lg hover:border-gray-600 transition-colors duration-300">
<div className="flex flex-row justify-between">
<h2 className="text-2xl font-semibold mb-6 text-gray-200 flex items-center gap-2">
<Brain size={24} />
Favorite Models
</h2>
<p className="text-muted-foreground italic text-sm">Based on personal preference</p>
</div>
<div className="space-y-4">
{models.map((model, index) => (
<div key={index} className="p-4 bg-gray-800/50 rounded-lg">
<div className="flex justify-between items-start mb-2">
<div>
<h3 className="font-semibold text-gray-200">{model.name}</h3>
<p className="text-sm text-gray-400">{model.provider}</p>
</div>
<div className="flex gap-1">
{[...Array(5)].map((_, i) => (
<Star
key={i}
size={14}
className={i < model.rating ? 'fill-yellow-400 text-yellow-400' : 'text-gray-600'}
/>
))}
</div>
<PaginatedCardList
items={models}
title="Favorite Models"
icon={<Brain size={24} />}
subtitle="Based on personal preference"
itemsPerPage={5}
getItemKey={(model) => model.name}
renderItem={(model) => (
<div className="p-3 sm:p-4 bg-gray-800/50 rounded-lg">
<div className="flex justify-between items-start gap-2 mb-2">
<div className="min-w-0 flex-1">
<h3 className="font-semibold text-sm sm:text-base text-gray-200 truncate">{model.name}</h3>
<p className="text-xs sm:text-sm text-gray-400">{model.provider}</p>
</div>
<div className="flex items-center gap-1 px-2 sm:px-3 py-0.5 sm:py-1 bg-yellow-400/10 border border-yellow-400/20 rounded-md flex-shrink-0">
<span className="text-base sm:text-lg font-bold text-yellow-400">
{model.rating.toFixed(1)}
</span>
</div>
<p className="text-sm text-gray-300">{model.review}</p>
</div>
))}
</div>
</section>
<p className="text-xs sm:text-sm text-gray-300 leading-relaxed">{model.review}</p>
</div>
)}
/>
)
}

View file

@ -1,5 +1,5 @@
import { Star } from 'lucide-react'
import { TbTool } from 'react-icons/tb'
import PaginatedCardList from '@/components/ui/PaginatedCardList'
import type { AIReview } from '../types'
interface FavoriteToolsProps {
@ -8,51 +8,44 @@ interface FavoriteToolsProps {
export default function FavoriteTools({ reviews }: FavoriteToolsProps) {
return (
<section className="p-4 sm:p-8 border-2 border-gray-700 rounded-lg hover:border-gray-600 transition-colors duration-300">
<div className="flex flex-row justify-between">
<h2 className="text-2xl font-semibold mb-6 text-gray-200 flex items-center gap-2">
<TbTool size={24} />
Favorite Tools
</h2>
<p className="text-muted-foreground italic text-sm">Based on personal preference</p>
</div>
<div className="space-y-4">
{reviews.map((review, index) => (
<div key={index} className="p-4 bg-gray-800/50 rounded-lg">
<div className="flex justify-between items-center mb-3">
<h3 className="font-semibold text-gray-200">{review.tool}</h3>
<div className="flex gap-1">
{[...Array(5)].map((_, i) => (
<Star
key={i}
size={14}
className={i < review.rating ? 'fill-yellow-400 text-yellow-400' : 'text-gray-600'}
/>
))}
</div>
<PaginatedCardList
items={reviews}
title="Favorite Tools"
icon={<TbTool size={24} />}
subtitle="Based on personal preference"
itemsPerPage={3}
getItemKey={(review) => review.tool}
renderItem={(review) => (
<div className="p-3 sm:p-4 bg-gray-800/50 rounded-lg">
<div className="flex justify-between items-center gap-2 mb-2 sm:mb-3">
<h3 className="font-semibold text-sm sm:text-base text-gray-200 truncate flex-1">{review.tool}</h3>
<div className="flex items-center gap-1 px-2 sm:px-3 py-0.5 sm:py-1 bg-yellow-400/10 border border-yellow-400/20 rounded-md flex-shrink-0">
<span className="text-base sm:text-lg font-bold text-yellow-400">
{review.rating.toFixed(1)}
</span>
</div>
<div className="grid grid-cols-2 gap-2 mb-2 text-sm">
<div>
<p className="text-green-400 font-medium mb-1">Pros:</p>
<ul className="text-gray-300 space-y-1">
{review.pros.map((pro, i) => (
<li key={i} className="text-xs"> {pro}</li>
))}
</ul>
</div>
<div>
<p className="text-red-400 font-medium mb-1">Cons:</p>
<ul className="text-gray-300 space-y-1">
{review.cons.map((con, i) => (
<li key={i} className="text-xs"> {con}</li>
))}
</ul>
</div>
</div>
<p className="text-sm text-blue-400 font-medium">{review.verdict}</p>
</div>
))}
</div>
</section>
<div className="grid grid-cols-2 gap-2 mb-2 text-xs sm:text-sm">
<div>
<p className="text-green-400 font-medium mb-1 text-xs sm:text-sm">Pros:</p>
<ul className="text-gray-300 space-y-0.5 sm:space-y-1">
{review.pros.map((pro, i) => (
<li key={i} className="text-xs leading-tight"> {pro}</li>
))}
</ul>
</div>
<div>
<p className="text-red-400 font-medium mb-1 text-xs sm:text-sm">Cons:</p>
<ul className="text-gray-300 space-y-0.5 sm:space-y-1">
{review.cons.map((con, i) => (
<li key={i} className="text-xs leading-tight"> {con}</li>
))}
</ul>
</div>
</div>
<p className="text-xs sm:text-sm text-blue-400 font-medium">{review.verdict}</p>
</div>
)}
/>
)
}

View file

@ -1,41 +1,44 @@
import { Trophy, ChevronRight } from 'lucide-react'
import { SiClaude } from 'react-icons/si'
import Link from '@/components/objects/Link'
import { surfaces, colors } from '@/lib/theme'
export default function TopPick() {
return (
<div className="px-4 mb-4">
<h2 className="text-4xl font-semibold mb-6 text-gray-200 flex items-center gap-2">
<Trophy size={32} className="text-orange-300" />
Top Pick of <i className="-ml-[1.55px]">2025</i>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-semibold mb-4 sm:mb-6 text-gray-200 flex items-center gap-2">
<Trophy size={24} className="sm:w-8 sm:h-8 text-orange-300" />
<span className="flex items-center gap-1">
Top Pick of <i className="-ml-[1.55px]">2025</i>
</span>
</h2>
<div className="p-6 sm:p-8 border-2 border-[#c15f3c] rounded-lg bg-orange-500/5">
<div className="grid md:grid-cols-2 gap-6">
<div className="flex items-center gap-4">
<SiClaude className="text-6xl text-[#c15f3c]" />
<div>
<h3 className="text-3xl font-bold text-gray-100">Claude</h3>
<p className="text-gray-400">by Anthropic</p>
<div className="flex items-center gap-2 mt-2">
<Link href="https://claude.ai" className="text-blue-400 hover:text-blue-300 flex items-center gap-1">
Visit <ChevronRight size={16} />
<div className={surfaces.card.featured}>
<div className="grid md:grid-cols-2 gap-4 sm:gap-6">
<div className="flex items-center gap-3 sm:gap-4">
<SiClaude className="text-4xl sm:text-5xl md:text-6xl flex-shrink-0" style={{ color: colors.accents.ai }} />
<div className="min-w-0">
<h3 className="text-2xl sm:text-3xl font-bold text-gray-100">Claude</h3>
<p className="text-sm sm:text-base text-gray-400">by Anthropic</p>
<div className="flex flex-wrap items-center gap-2 mt-2">
<Link href="https://claude.ai" className="flex items-center gap-1 text-sm sm:text-base hover:text-blue-300">
Visit <ChevronRight size={14} className="sm:w-4 sm:h-4" />
</Link>
<Link href="/ai/claude" className="text-blue-400 hover:text-blue-300 flex items-center gap-1">
My Usage <ChevronRight size={16} />
<Link href="/ai/usage" className="flex items-center gap-1 text-sm sm:text-base hover:text-blue-300">
My Usage <ChevronRight size={14} className="sm:w-4 sm:h-4" />
</Link>
</div>
</div>
</div>
<div className="space-y-2">
<p className="text-gray-300">
<div className="space-y-2 sm:space-y-3">
<p className="text-sm sm:text-base text-gray-300 leading-relaxed">
Claude has become my go-to AI assistant for coding, writing, and learning very quickly.
I believe their Max 5x ($100/mo) is the best value for budget-conscious consumers like myself.
</p>
<div className='flex flex-col items-center gap-y-6 sm:flex-row sm:justify-between'>
<div className="flex gap-2 flex-wrap">
<span className="px-2 py-1 bg-gray-700 rounded text-xs text-gray-300">Top-Tier Tool Calling</span>
<span className="px-2 py-1 bg-gray-700 rounded text-xs text-gray-300">High-Value Plans</span>
<span className="px-2 py-1 bg-gray-700 rounded text-xs text-gray-300">Good Speed</span>
<div className="flex gap-2 flex-wrap justify-center sm:justify-start">
<span className={surfaces.badge.default}>Top-Tier Tool Calling</span>
<span className={surfaces.badge.default}>High-Value Plans</span>
<span className={surfaces.badge.default}>Good Speed</span>
</div>
</div>
</div>