Initial release v1.0.0

Open-source marketplace for AI Agent skills.

Features:
- Next.js 15 web app with i18n (en/fa)
- CLI tool for skill installation (npx skillhub)
- GitHub crawler/indexer with multi-strategy discovery
- Security scanning for all indexed skills
- Self-hostable with Docker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-12 06:08:51 +03:30
commit 97b427831a
227 changed files with 48411 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
'use client';
import { useRouter, useSearchParams } from 'next/navigation';
import { Sparkles, RefreshCw } from 'lucide-react';
interface NewSkillsTabsProps {
activeTab: 'new' | 'updated';
newCount: number;
updatedCount: number;
locale: string;
translations: {
new: string;
updated: string;
newDescription: string;
updatedDescription: string;
};
}
export function NewSkillsTabs({
activeTab,
newCount,
updatedCount,
locale,
translations,
}: NewSkillsTabsProps) {
const router = useRouter();
const searchParams = useSearchParams();
const handleTabChange = (tab: 'new' | 'updated') => {
const params = new URLSearchParams(searchParams.toString());
params.set('tab', tab);
params.delete('page'); // Reset to page 1 when switching tabs
router.push(`/${locale}/new?${params.toString()}`);
};
const formatCount = (count: number) => {
if (locale === 'fa') {
return count.toLocaleString('fa-IR');
}
return count.toLocaleString('en-US');
};
return (
<div className="mb-8">
{/* Tabs */}
<div className="flex justify-center gap-2 mb-4">
<button
onClick={() => handleTabChange('new')}
className={`
flex items-center gap-2 px-6 py-3 rounded-full font-medium transition-all
${activeTab === 'new'
? 'bg-primary-600 text-white shadow-lg shadow-primary-500/25'
: 'bg-surface-elevated text-text-secondary hover:bg-surface-subtle hover:text-text-primary'
}
`}
>
<Sparkles className="w-4 h-4" />
<span>{translations.new}</span>
<span className={`
px-2 py-0.5 rounded-full text-xs
${activeTab === 'new'
? 'bg-white/20 text-white'
: 'bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400'
}
`}>
{formatCount(newCount)}
</span>
</button>
<button
onClick={() => handleTabChange('updated')}
className={`
flex items-center gap-2 px-6 py-3 rounded-full font-medium transition-all
${activeTab === 'updated'
? 'bg-primary-600 text-white shadow-lg shadow-primary-500/25'
: 'bg-surface-elevated text-text-secondary hover:bg-surface-subtle hover:text-text-primary'
}
`}
>
<RefreshCw className="w-4 h-4" />
<span>{translations.updated}</span>
<span className={`
px-2 py-0.5 rounded-full text-xs
${activeTab === 'updated'
? 'bg-white/20 text-white'
: 'bg-primary-100 dark:bg-primary-900/30 text-primary-600 dark:text-primary-400'
}
`}>
{formatCount(updatedCount)}
</span>
</button>
</div>
{/* Description */}
<p className="text-center text-text-muted text-sm">
{activeTab === 'new' ? translations.newDescription : translations.updatedDescription}
</p>
</div>
);
}