feat: enhance web app with fuzzy search, syntax highlighting, and pagination
- Expand README with detailed Web App section (English) - Improve SEO meta tags in index.html - Add rehype-highlight for code syntax highlighting in skill details - Implement fuzzy search with scoring (name > category > description) - Add clear search button and result counter - Implement Load More pagination (24 items initially) for 950+ skills - Add rehype-highlight and highlight.js dependencies Made-with: Cursor
This commit is contained in:
51
README.md
51
README.md
@@ -351,21 +351,52 @@ We have moved the full skill registry to a dedicated catalog to keep this README
|
||||
|
||||
### 🌐 Interactive Skills Web App
|
||||
|
||||
You can now easily search, filter, and discover the perfect skills for your agent using our local Web App.
|
||||
A modern web interface to explore, search, and use the 950+ skills directly from your browser.
|
||||
|
||||
To launch the app:
|
||||
#### ✨ Features
|
||||
|
||||
1. Double-click the `START_APP.bat` file in the root directory (Windows) or run it from your terminal.
|
||||
2. The app will automatically configure everything and open in your default browser.
|
||||
- 🔍 **Full-text search** – Search skills by name, description, or content
|
||||
- 🏷️ **Category filters** – Frontend, Backend, Security, DevOps, etc.
|
||||
- 📝 **Markdown rendering** – View complete documentation with syntax highlighting
|
||||
- 📋 **Copy buttons** – Copy `@skill-name` or full content in 1 click
|
||||
- 🛠️ **Prompt Builder** – Add custom context before copying
|
||||
- 🌙 **Dark mode** – Adaptive light/dark interface
|
||||
- ⚡ **Auto-update** – Automatically syncs with upstream repo
|
||||
|
||||
#### 🛠️ New: Interactive Prompt Builder
|
||||
#### 🚀 Quick Start
|
||||
|
||||
The web app is no longer just a static catalog! When you click on any skill, you will see an **Interactive Prompt Builder** box.
|
||||
Instead of manually copying `@skill-name` and writing your requirements separately in your IDE:
|
||||
**Windows:**
|
||||
```bash
|
||||
# Double-click or terminal
|
||||
START_APP.bat
|
||||
```
|
||||
|
||||
1. Type your specific project constraints into the text box (e.g., "Use React 19 and Tailwind").
|
||||
2. Click **Copy Prompt**.
|
||||
3. Your clipboard now has a fully formatted, ready-to-run prompt combining the skill invocation and your custom context!
|
||||
**macOS/Linux:**
|
||||
```bash
|
||||
# 1. Install dependencies (first time)
|
||||
cd web-app && npm install
|
||||
|
||||
# 2. Setup assets and launch
|
||||
npm run app:dev
|
||||
```
|
||||
|
||||
**Available npm commands:**
|
||||
```bash
|
||||
npm run app:setup # Copy skills to web-app/public/
|
||||
npm run app:dev # Start dev server
|
||||
npm run app:build # Production build
|
||||
npm run app:preview # Preview production build
|
||||
```
|
||||
|
||||
The app automatically opens at `http://localhost:5173` (or alternative port).
|
||||
|
||||
#### 🛠️ Prompt Builder
|
||||
|
||||
On each skill page you'll find the **Prompt Builder**:
|
||||
1. Write specific requirements (e.g., "Use React 19, TypeScript and Tailwind")
|
||||
2. Click **Copy Prompt** – copies `@skill-name + context`
|
||||
3. Or **Copy Full Content** – copies the full documentation
|
||||
4. Paste into your AI assistant (Claude, Cursor, Gemini, etc.)
|
||||
|
||||
👉 **[View the Complete Skill Catalog (CATALOG.md)](CATALOG.md)**
|
||||
|
||||
|
||||
@@ -4,7 +4,14 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>web-app</title>
|
||||
<meta name="description" content="Antigravity Awesome Skills - 950+ agentic skills catalog for Claude Code, Gemini, Cursor, Copilot. Search, filter, and copy prompts instantly." />
|
||||
<meta name="keywords" content="AI skills, Claude Code, Gemini CLI, Cursor, Copilot, agentic skills, coding assistant" />
|
||||
<meta name="author" content="Niccolò Abate (@sickn33)" />
|
||||
<meta property="og:title" content="Antigravity Awesome Skills Catalog" />
|
||||
<meta property="og:description" content="Browse 950+ battle-tested agentic skills for AI coding assistants" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<title>Antigravity Skills | 950+ AI Agentic Skills Catalog</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
97
web-app/package-lock.json
generated
97
web-app/package-lock.json
generated
@@ -11,11 +11,13 @@
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.34.2",
|
||||
"github-markdown-css": "^5.9.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"lucide-react": "^0.574.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-router-dom": "^7.13.0",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -78,7 +80,6 @@
|
||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.29.0",
|
||||
"@babel/generator": "^7.29.0",
|
||||
@@ -1764,7 +1765,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.2.2"
|
||||
}
|
||||
@@ -1818,7 +1818,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -1971,7 +1970,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -2332,7 +2330,6 @@
|
||||
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -2738,6 +2735,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-is-element": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
|
||||
"integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-to-jsx-runtime": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
|
||||
@@ -2765,6 +2775,22 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-to-text": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
|
||||
"integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/unist": "^3.0.0",
|
||||
"hast-util-is-element": "^3.0.0",
|
||||
"unist-util-find-after": "^5.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hast-util-whitespace": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
|
||||
@@ -2795,6 +2821,15 @@
|
||||
"hermes-estree": "0.25.1"
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.11.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
|
||||
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-url-attributes": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
|
||||
@@ -3329,6 +3364,21 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/lowlight": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
|
||||
"integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"highlight.js": "~11.11.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
@@ -4141,7 +4191,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -4169,7 +4218,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -4221,7 +4269,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -4231,7 +4278,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
|
||||
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -4314,6 +4360,23 @@
|
||||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/rehype-highlight": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz",
|
||||
"integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
"hast-util-to-text": "^4.0.0",
|
||||
"lowlight": "^3.0.0",
|
||||
"unist-util-visit": "^5.0.0",
|
||||
"vfile": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-parse": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
|
||||
@@ -4631,6 +4694,20 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/unist-util-find-after": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
|
||||
"integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^3.0.0",
|
||||
"unist-util-is": "^6.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/unist-util-is": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz",
|
||||
@@ -4774,7 +4851,6 @@
|
||||
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -4896,7 +4972,6 @@
|
||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
"clsx": "^2.1.1",
|
||||
"framer-motion": "^12.34.2",
|
||||
"github-markdown-css": "^5.9.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"lucide-react": "^0.574.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-router-dom": "^7.13.0",
|
||||
"rehype-highlight": "^7.0.2",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -10,6 +10,7 @@ export function Home() {
|
||||
const [search, setSearch] = useState('');
|
||||
const [categoryFilter, setCategoryFilter] = useState('all');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [displayCount, setDisplayCount] = useState(24); // Show 24 initially (6 rows of 4)
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/skills.json')
|
||||
@@ -25,15 +26,39 @@ export function Home() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Fuzzy search with scoring
|
||||
const calculateScore = (skill, terms) => {
|
||||
let score = 0;
|
||||
const nameLower = skill.name.toLowerCase();
|
||||
const descLower = (skill.description || '').toLowerCase();
|
||||
const catLower = (skill.category || '').toLowerCase();
|
||||
|
||||
for (const term of terms) {
|
||||
// Exact name match (highest priority)
|
||||
if (nameLower === term) score += 100;
|
||||
// Name starts with term
|
||||
else if (nameLower.startsWith(term)) score += 50;
|
||||
// Name contains term
|
||||
else if (nameLower.includes(term)) score += 30;
|
||||
// Category match
|
||||
else if (catLower.includes(term)) score += 20;
|
||||
// Description contains term
|
||||
else if (descLower.includes(term)) score += 10;
|
||||
}
|
||||
return score;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let result = skills;
|
||||
|
||||
if (search) {
|
||||
const lowerSearch = search.toLowerCase();
|
||||
result = result.filter(skill =>
|
||||
skill.name.toLowerCase().includes(lowerSearch) ||
|
||||
skill.description.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
const terms = search.toLowerCase().trim().split(/\s+/).filter(t => t.length > 0);
|
||||
if (terms.length > 0) {
|
||||
result = result
|
||||
.map(skill => ({ ...skill, _score: calculateScore(skill, terms) }))
|
||||
.filter(skill => skill._score > 0)
|
||||
.sort((a, b) => b._score - a._score);
|
||||
}
|
||||
}
|
||||
|
||||
if (categoryFilter !== 'all') {
|
||||
@@ -43,6 +68,11 @@ export function Home() {
|
||||
setFilteredSkills(result);
|
||||
}, [search, categoryFilter, skills]);
|
||||
|
||||
// Reset display count when search/filter changes
|
||||
useEffect(() => {
|
||||
setDisplayCount(24);
|
||||
}, [search, categoryFilter]);
|
||||
|
||||
const categories = ['all', ...new Set(skills.map(s => s.category).filter(Boolean))];
|
||||
|
||||
return (
|
||||
@@ -50,7 +80,11 @@ export function Home() {
|
||||
<div className="flex flex-col space-y-4 md:flex-row md:items-center md:justify-between md:space-y-0">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight text-slate-900 dark:text-slate-100 mb-2">Explore Skills</h1>
|
||||
<p className="text-slate-500 dark:text-slate-400">Discover {skills.length} agentic capabilities for your AI assistant.</p>
|
||||
<p className="text-slate-500 dark:text-slate-400">
|
||||
{search || categoryFilter !== 'all'
|
||||
? `Showing ${filteredSkills.length} of ${skills.length} skills`
|
||||
: `Discover ${skills.length} agentic capabilities for your AI assistant.`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -59,11 +93,20 @@ export function Home() {
|
||||
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-500" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search skills (e.g., 'react', 'security', 'python')..."
|
||||
className="w-full rounded-md border border-slate-200 bg-slate-50 px-9 py-2 text-sm outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50"
|
||||
placeholder="Search skills (e.g., 'react', 'security', 'python', 'testing')..."
|
||||
className="w-full rounded-md border border-slate-200 bg-slate-50 px-9 py-2 text-sm outline-none focus:border-indigo-500 focus:ring-1 focus:ring-indigo-500 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50 pr-9"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
/>
|
||||
{search && (
|
||||
<button
|
||||
onClick={() => setSearch('')}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300"
|
||||
title="Clear search"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2 overflow-x-auto pb-2 md:pb-0 scrollbar-hide">
|
||||
<Filter className="h-4 w-4 text-slate-500 shrink-0" />
|
||||
@@ -93,7 +136,7 @@ export function Home() {
|
||||
<p className="mt-2 text-slate-500 dark:text-slate-400">Try adjusting your search or filter.</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredSkills.map((skill) => (
|
||||
filteredSkills.slice(0, displayCount).map((skill) => (
|
||||
<motion.div
|
||||
key={skill.id}
|
||||
layout
|
||||
@@ -133,6 +176,21 @@ export function Home() {
|
||||
))
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Load More Button */}
|
||||
{!loading && filteredSkills.length > displayCount && (
|
||||
<div className="col-span-full flex justify-center py-8">
|
||||
<button
|
||||
onClick={() => setDisplayCount(prev => prev + 24)}
|
||||
className="flex items-center space-x-2 px-6 py-3 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg font-medium text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors shadow-sm"
|
||||
>
|
||||
<span>Load More</span>
|
||||
<span className="text-sm text-slate-500 dark:text-slate-400">
|
||||
({filteredSkills.length - displayCount} remaining)
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useParams, Link } from 'react-router-dom';
|
||||
import Markdown from 'react-markdown';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
import { ArrowLeft, Copy, Check, FileCode, AlertTriangle } from 'lucide-react';
|
||||
import 'highlight.js/styles/github-dark.css';
|
||||
|
||||
export function SkillDetail() {
|
||||
const { id } = useParams();
|
||||
@@ -157,8 +159,8 @@ export function SkillDetail() {
|
||||
</div>
|
||||
|
||||
<div className="p-6 sm:p-8">
|
||||
<div className="prose prose-slate dark:prose-invert max-w-none">
|
||||
<Markdown>{content}</Markdown>
|
||||
<div className="prose prose-slate dark:prose-invert max-w-none prose-code:before:content-none prose-code:after:content-none">
|
||||
<Markdown rehypePlugins={[rehypeHighlight]}>{content}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user