mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 02:15:12 +00:00
Add multi-select filters, light/dark theme, and skill ZIP downloads
- Add multi-select dropdown component for all filter fields - Implement light/dark theme toggle with system preference detection - Add client-side ZIP download for skills using JSZip - Include file lists in skills metadata for download feature - Add title tooltips to multi-select options for long values - Update all pages with consistent theme toggle in header
This commit is contained in:
@@ -80,17 +80,34 @@ function generateAgentsData() {
|
||||
.readdirSync(AGENTS_DIR)
|
||||
.filter((f) => f.endsWith(".agent.md"));
|
||||
|
||||
// Track all unique values for filters
|
||||
const allModels = new Set();
|
||||
const allTools = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(AGENTS_DIR, file);
|
||||
const frontmatter = parseFrontmatter(filePath);
|
||||
const relativePath = path.relative(ROOT_FOLDER, filePath).replace(/\\/g, "/");
|
||||
|
||||
const model = frontmatter?.model || null;
|
||||
const tools = frontmatter?.tools || [];
|
||||
const handoffs = frontmatter?.handoffs || [];
|
||||
|
||||
// Track unique values
|
||||
if (model) allModels.add(model);
|
||||
tools.forEach((t) => allTools.add(t));
|
||||
|
||||
agents.push({
|
||||
id: file.replace(".agent.md", ""),
|
||||
title: extractTitle(filePath, frontmatter),
|
||||
description: frontmatter?.description || "",
|
||||
model: frontmatter?.model || null,
|
||||
tools: frontmatter?.tools || [],
|
||||
model: model,
|
||||
tools: tools,
|
||||
hasHandoffs: handoffs.length > 0,
|
||||
handoffs: handoffs.map((h) => ({
|
||||
label: h.label || "",
|
||||
agent: h.agent || "",
|
||||
})),
|
||||
mcpServers: frontmatter?.["mcp-servers"]
|
||||
? Object.keys(frontmatter["mcp-servers"])
|
||||
: [],
|
||||
@@ -99,7 +116,16 @@ function generateAgentsData() {
|
||||
});
|
||||
}
|
||||
|
||||
return agents.sort((a, b) => a.title.localeCompare(b.title));
|
||||
// Sort and return with filter metadata
|
||||
const sortedAgents = agents.sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
return {
|
||||
items: sortedAgents,
|
||||
filters: {
|
||||
models: ["(none)", ...Array.from(allModels).sort()],
|
||||
tools: Array.from(allTools).sort(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,24 +137,73 @@ function generatePromptsData() {
|
||||
.readdirSync(PROMPTS_DIR)
|
||||
.filter((f) => f.endsWith(".prompt.md"));
|
||||
|
||||
// Track all unique tools for filters
|
||||
const allTools = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(PROMPTS_DIR, file);
|
||||
const frontmatter = parseFrontmatter(filePath);
|
||||
const relativePath = path.relative(ROOT_FOLDER, filePath).replace(/\\/g, "/");
|
||||
|
||||
const tools = frontmatter?.tools || [];
|
||||
tools.forEach((t) => allTools.add(t));
|
||||
|
||||
prompts.push({
|
||||
id: file.replace(".prompt.md", ""),
|
||||
title: extractTitle(filePath, frontmatter),
|
||||
description: frontmatter?.description || "",
|
||||
agent: frontmatter?.agent || null,
|
||||
model: frontmatter?.model || null,
|
||||
tools: frontmatter?.tools || [],
|
||||
tools: tools,
|
||||
path: relativePath,
|
||||
filename: file,
|
||||
});
|
||||
}
|
||||
|
||||
return prompts.sort((a, b) => a.title.localeCompare(b.title));
|
||||
const sortedPrompts = prompts.sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
return {
|
||||
items: sortedPrompts,
|
||||
filters: {
|
||||
tools: Array.from(allTools).sort(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse applyTo field into an array of patterns
|
||||
*/
|
||||
function parseApplyToPatterns(applyTo) {
|
||||
if (!applyTo) return [];
|
||||
|
||||
// Handle array format
|
||||
if (Array.isArray(applyTo)) {
|
||||
return applyTo.map(p => p.trim()).filter(p => p.length > 0);
|
||||
}
|
||||
|
||||
// Handle string format (comma-separated)
|
||||
if (typeof applyTo === 'string') {
|
||||
return applyTo.split(',').map(p => p.trim()).filter(p => p.length > 0);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract file extension from a glob pattern
|
||||
*/
|
||||
function extractExtensionFromPattern(pattern) {
|
||||
// Match patterns like **.ts, **/*.js, *.py, etc.
|
||||
const match = pattern.match(/\*\.(\w+)$/);
|
||||
if (match) return `.${match[1]}`;
|
||||
|
||||
// Match patterns like **/*.{ts,tsx}
|
||||
const braceMatch = pattern.match(/\*\.\{([^}]+)\}$/);
|
||||
if (braceMatch) {
|
||||
return braceMatch[1].split(',').map(ext => `.${ext.trim()}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,22 +215,75 @@ function generateInstructionsData() {
|
||||
.readdirSync(INSTRUCTIONS_DIR)
|
||||
.filter((f) => f.endsWith(".instructions.md"));
|
||||
|
||||
// Track all unique patterns and extensions for filters
|
||||
const allPatterns = new Set();
|
||||
const allExtensions = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(INSTRUCTIONS_DIR, file);
|
||||
const frontmatter = parseFrontmatter(filePath);
|
||||
const relativePath = path.relative(ROOT_FOLDER, filePath).replace(/\\/g, "/");
|
||||
|
||||
const applyToRaw = frontmatter?.applyTo || null;
|
||||
const applyToPatterns = parseApplyToPatterns(applyToRaw);
|
||||
|
||||
// Extract extensions from patterns
|
||||
const extensions = [];
|
||||
for (const pattern of applyToPatterns) {
|
||||
allPatterns.add(pattern);
|
||||
const ext = extractExtensionFromPattern(pattern);
|
||||
if (ext) {
|
||||
if (Array.isArray(ext)) {
|
||||
ext.forEach(e => {
|
||||
extensions.push(e);
|
||||
allExtensions.add(e);
|
||||
});
|
||||
} else {
|
||||
extensions.push(ext);
|
||||
allExtensions.add(ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instructions.push({
|
||||
id: file.replace(".instructions.md", ""),
|
||||
title: extractTitle(filePath, frontmatter),
|
||||
description: frontmatter?.description || "",
|
||||
applyTo: frontmatter?.applyTo || null,
|
||||
applyTo: applyToRaw,
|
||||
applyToPatterns: applyToPatterns,
|
||||
extensions: [...new Set(extensions)],
|
||||
path: relativePath,
|
||||
filename: file,
|
||||
});
|
||||
}
|
||||
|
||||
return instructions.sort((a, b) => a.title.localeCompare(b.title));
|
||||
const sortedInstructions = instructions.sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
return {
|
||||
items: sortedInstructions,
|
||||
filters: {
|
||||
patterns: Array.from(allPatterns).sort(),
|
||||
extensions: ["(none)", ...Array.from(allExtensions).sort()],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize a skill based on its name and description
|
||||
*/
|
||||
function categorizeSkill(name, description) {
|
||||
const text = `${name} ${description}`.toLowerCase();
|
||||
|
||||
if (text.includes('azure') || text.includes('appinsights')) return 'Azure';
|
||||
if (text.includes('github') || text.includes('gh-cli') || text.includes('git-commit') || text.includes('git ')) return 'Git & GitHub';
|
||||
if (text.includes('vscode') || text.includes('vs code')) return 'VS Code';
|
||||
if (text.includes('test') || text.includes('qa') || text.includes('playwright')) return 'Testing';
|
||||
if (text.includes('microsoft') || text.includes('m365') || text.includes('workiq')) return 'Microsoft';
|
||||
if (text.includes('cli') || text.includes('command')) return 'CLI Tools';
|
||||
if (text.includes('diagram') || text.includes('plantuml') || text.includes('visual')) return 'Diagrams';
|
||||
if (text.includes('nuget') || text.includes('dotnet') || text.includes('.net')) return '.NET';
|
||||
|
||||
return 'Other';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,19 +293,26 @@ function generateSkillsData() {
|
||||
const skills = [];
|
||||
|
||||
if (!fs.existsSync(SKILLS_DIR)) {
|
||||
return skills;
|
||||
return { items: [], filters: { categories: [], hasAssets: ['Yes', 'No'] } };
|
||||
}
|
||||
|
||||
const folders = fs
|
||||
.readdirSync(SKILLS_DIR)
|
||||
.filter((f) => fs.statSync(path.join(SKILLS_DIR, f)).isDirectory());
|
||||
|
||||
const allCategories = new Set();
|
||||
|
||||
for (const folder of folders) {
|
||||
const skillPath = path.join(SKILLS_DIR, folder);
|
||||
const metadata = parseSkillMetadata(skillPath);
|
||||
|
||||
if (metadata) {
|
||||
const relativePath = path.relative(ROOT_FOLDER, skillPath).replace(/\\/g, "/");
|
||||
const category = categorizeSkill(metadata.name, metadata.description);
|
||||
allCategories.add(category);
|
||||
|
||||
// Get all files in the skill folder recursively
|
||||
const files = getSkillFiles(skillPath, relativePath);
|
||||
|
||||
skills.push({
|
||||
id: folder,
|
||||
@@ -188,13 +323,55 @@ function generateSkillsData() {
|
||||
.join(" "),
|
||||
description: metadata.description,
|
||||
assets: metadata.assets,
|
||||
hasAssets: metadata.assets.length > 0,
|
||||
assetCount: metadata.assets.length,
|
||||
category: category,
|
||||
path: relativePath,
|
||||
skillFile: `${relativePath}/SKILL.md`,
|
||||
files: files,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return skills.sort((a, b) => a.title.localeCompare(b.title));
|
||||
const sortedSkills = skills.sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
return {
|
||||
items: sortedSkills,
|
||||
filters: {
|
||||
categories: Array.from(allCategories).sort(),
|
||||
hasAssets: ['Yes', 'No'],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all files in a skill folder recursively
|
||||
*/
|
||||
function getSkillFiles(skillPath, relativePath) {
|
||||
const files = [];
|
||||
|
||||
function walkDir(dir, relDir) {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
walkDir(fullPath, relPath);
|
||||
} else {
|
||||
// Get file size
|
||||
const stats = fs.statSync(fullPath);
|
||||
files.push({
|
||||
path: `${relativePath}/${relPath}`,
|
||||
name: relPath,
|
||||
size: stats.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walkDir(skillPath, '');
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,17 +388,23 @@ function generateCollectionsData() {
|
||||
.readdirSync(COLLECTIONS_DIR)
|
||||
.filter((f) => f.endsWith(".collection.yml"));
|
||||
|
||||
// Track all unique tags
|
||||
const allTags = new Set();
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(COLLECTIONS_DIR, file);
|
||||
const data = parseCollectionYaml(filePath);
|
||||
const relativePath = path.relative(ROOT_FOLDER, filePath).replace(/\\/g, "/");
|
||||
|
||||
if (data) {
|
||||
const tags = data.tags || [];
|
||||
tags.forEach((t) => allTags.add(t));
|
||||
|
||||
collections.push({
|
||||
id: file.replace(".collection.yml", ""),
|
||||
name: data.name || file.replace(".collection.yml", ""),
|
||||
description: data.description || "",
|
||||
tags: data.tags || [],
|
||||
tags: tags,
|
||||
featured: data.featured || false,
|
||||
items: (data.items || []).map((item) => ({
|
||||
path: item.path,
|
||||
@@ -235,11 +418,18 @@ function generateCollectionsData() {
|
||||
}
|
||||
|
||||
// Sort with featured first, then alphabetically
|
||||
return collections.sort((a, b) => {
|
||||
const sortedCollections = collections.sort((a, b) => {
|
||||
if (a.featured && !b.featured) return -1;
|
||||
if (!a.featured && b.featured) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
|
||||
return {
|
||||
items: sortedCollections,
|
||||
filters: {
|
||||
tags: Array.from(allTags).sort(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,20 +506,25 @@ async function main() {
|
||||
ensureDataDir();
|
||||
|
||||
// Generate all data
|
||||
const agents = generateAgentsData();
|
||||
console.log(`✓ Generated ${agents.length} agents`);
|
||||
const agentsData = generateAgentsData();
|
||||
const agents = agentsData.items;
|
||||
console.log(`✓ Generated ${agents.length} agents (${agentsData.filters.models.length} models, ${agentsData.filters.tools.length} tools)`);
|
||||
|
||||
const prompts = generatePromptsData();
|
||||
console.log(`✓ Generated ${prompts.length} prompts`);
|
||||
const promptsData = generatePromptsData();
|
||||
const prompts = promptsData.items;
|
||||
console.log(`✓ Generated ${prompts.length} prompts (${promptsData.filters.tools.length} tools)`);
|
||||
|
||||
const instructions = generateInstructionsData();
|
||||
console.log(`✓ Generated ${instructions.length} instructions`);
|
||||
const instructionsData = generateInstructionsData();
|
||||
const instructions = instructionsData.items;
|
||||
console.log(`✓ Generated ${instructions.length} instructions (${instructionsData.filters.extensions.length} extensions)`);
|
||||
|
||||
const skills = generateSkillsData();
|
||||
console.log(`✓ Generated ${skills.length} skills`);
|
||||
const skillsData = generateSkillsData();
|
||||
const skills = skillsData.items;
|
||||
console.log(`✓ Generated ${skills.length} skills (${skillsData.filters.categories.length} categories)`);
|
||||
|
||||
const collections = generateCollectionsData();
|
||||
console.log(`✓ Generated ${collections.length} collections`);
|
||||
const collectionsData = generateCollectionsData();
|
||||
const collections = collectionsData.items;
|
||||
console.log(`✓ Generated ${collections.length} collections (${collectionsData.filters.tags.length} tags)`);
|
||||
|
||||
const searchIndex = generateSearchIndex(agents, prompts, instructions, skills, collections);
|
||||
console.log(`✓ Generated search index with ${searchIndex.length} items`);
|
||||
@@ -337,27 +532,27 @@ async function main() {
|
||||
// Write JSON files
|
||||
fs.writeFileSync(
|
||||
path.join(WEBSITE_DATA_DIR, "agents.json"),
|
||||
JSON.stringify(agents, null, 2)
|
||||
JSON.stringify(agentsData, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(WEBSITE_DATA_DIR, "prompts.json"),
|
||||
JSON.stringify(prompts, null, 2)
|
||||
JSON.stringify(promptsData, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(WEBSITE_DATA_DIR, "instructions.json"),
|
||||
JSON.stringify(instructions, null, 2)
|
||||
JSON.stringify(instructionsData, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(WEBSITE_DATA_DIR, "skills.json"),
|
||||
JSON.stringify(skills, null, 2)
|
||||
JSON.stringify(skillsData, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(WEBSITE_DATA_DIR, "collections.json"),
|
||||
JSON.stringify(collections, null, 2)
|
||||
JSON.stringify(collectionsData, null, 2)
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* CSS Variables and Base Styles */
|
||||
:root {
|
||||
/* Dark theme (default) */
|
||||
--color-bg: #0d1117;
|
||||
--color-bg-secondary: #161b22;
|
||||
--color-bg-tertiary: #21262d;
|
||||
@@ -25,9 +26,8 @@
|
||||
--header-height: 64px;
|
||||
}
|
||||
|
||||
/* Light mode support */
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
/* Light theme */
|
||||
[data-theme="light"] {
|
||||
--color-bg: #ffffff;
|
||||
--color-bg-secondary: #f6f8fa;
|
||||
--color-bg-tertiary: #f0f3f6;
|
||||
@@ -39,6 +39,26 @@
|
||||
--color-link-hover: #0550ae;
|
||||
--color-card-bg: #ffffff;
|
||||
--color-card-hover: #f6f8fa;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 8px 24px rgba(0,0,0,0.08);
|
||||
--shadow-lg: 0 8px 24px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
/* Auto theme based on system preference */
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root:not([data-theme="dark"]) {
|
||||
--color-bg: #ffffff;
|
||||
--color-bg-secondary: #f6f8fa;
|
||||
--color-bg-tertiary: #f0f3f6;
|
||||
--color-border: #d0d7de;
|
||||
--color-text: #24292f;
|
||||
--color-text-muted: #57606a;
|
||||
--color-text-emphasis: #1f2328;
|
||||
--color-link: #0969da;
|
||||
--color-link-hover: #0550ae;
|
||||
--color-card-bg: #ffffff;
|
||||
--color-card-hover: #f6f8fa;
|
||||
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 8px 24px rgba(0,0,0,0.08);
|
||||
--shadow-lg: 0 8px 24px rgba(0,0,0,0.15);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +160,69 @@ a:hover {
|
||||
color: var(--color-text-emphasis);
|
||||
}
|
||||
|
||||
/* Theme Toggle */
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text-emphasis);
|
||||
}
|
||||
|
||||
.theme-toggle svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.theme-toggle .icon-sun,
|
||||
.theme-toggle .icon-moon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show sun icon in dark mode (click to switch to light) */
|
||||
:root:not([data-theme="light"]) .theme-toggle .icon-sun {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:root:not([data-theme="light"]) .theme-toggle .icon-moon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show moon icon in light mode (click to switch to dark) */
|
||||
[data-theme="light"] .theme-toggle .icon-sun {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme="light"] .theme-toggle .icon-moon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Handle auto mode with prefers-color-scheme */
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root:not([data-theme="dark"]):not([data-theme="light"]) .theme-toggle .icon-sun {
|
||||
display: none;
|
||||
}
|
||||
:root:not([data-theme="dark"]):not([data-theme="light"]) .theme-toggle .icon-moon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
background: linear-gradient(180deg, var(--color-bg-secondary) 0%, var(--color-bg) 100%);
|
||||
@@ -452,6 +535,26 @@ a:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
/* Spinner animation */
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* Button states */
|
||||
.btn:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal {
|
||||
position: fixed;
|
||||
@@ -570,6 +673,308 @@ a:hover {
|
||||
border-color: var(--color-link);
|
||||
}
|
||||
|
||||
/* Filters Bar */
|
||||
.filters-bar {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
font-size: 13px;
|
||||
color: var(--color-text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-group select {
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
min-width: 150px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.filter-group select:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-link);
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-small {
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Multi-Select Component */
|
||||
.multi-select {
|
||||
position: relative;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.multi-select-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 6px 12px;
|
||||
font-size: 13px;
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.multi-select-trigger:hover {
|
||||
border-color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.multi-select.is-open .multi-select-trigger {
|
||||
border-color: var(--color-link);
|
||||
}
|
||||
|
||||
.multi-select-display {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.multi-select-display.has-value {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.multi-select-arrow {
|
||||
flex-shrink: 0;
|
||||
transition: transform var(--transition);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.multi-select.is-open .multi-select-arrow {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.multi-select-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-top: 4px;
|
||||
background-color: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--shadow-lg);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
max-height: 320px;
|
||||
}
|
||||
|
||||
.multi-select.is-open .multi-select-dropdown {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.multi-select-search-wrapper {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.multi-select-search {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
font-size: 13px;
|
||||
background-color: var(--color-bg);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.multi-select-search:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-link);
|
||||
}
|
||||
|
||||
.multi-select-options {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.multi-select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color var(--transition);
|
||||
}
|
||||
|
||||
.multi-select-option:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
.multi-select-option input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.multi-select-checkbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 3px;
|
||||
background-color: var(--color-bg);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.multi-select-option input[type="checkbox"]:checked + .multi-select-checkbox {
|
||||
background-color: var(--color-link);
|
||||
border-color: var(--color-link);
|
||||
}
|
||||
|
||||
.multi-select-option input[type="checkbox"]:checked + .multi-select-checkbox::after {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z'/%3E%3C/svg%3E");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.multi-select-label {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: var(--color-text);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.multi-select-empty {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.multi-select-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px;
|
||||
border-top: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.multi-select-actions button {
|
||||
flex: 1;
|
||||
padding: 6px 12px;
|
||||
font-size: 12px;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
transition: all var(--transition);
|
||||
}
|
||||
|
||||
.multi-select-clear {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.multi-select-clear:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.multi-select-done {
|
||||
background-color: var(--color-link);
|
||||
border: 1px solid var(--color-link);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.multi-select-done:hover {
|
||||
background-color: var(--color-link-hover);
|
||||
border-color: var(--color-link-hover);
|
||||
}
|
||||
|
||||
/* Tag variants */
|
||||
.tag-model {
|
||||
background-color: rgba(88, 166, 255, 0.15);
|
||||
color: var(--color-link);
|
||||
}
|
||||
|
||||
.tag-none {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.tag-handoffs {
|
||||
background-color: rgba(210, 153, 34, 0.15);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.tag-extension {
|
||||
background-color: rgba(35, 134, 54, 0.15);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.tag-category {
|
||||
background-color: rgba(130, 80, 223, 0.15);
|
||||
color: #a371f7;
|
||||
}
|
||||
|
||||
.tag-assets {
|
||||
background-color: rgba(88, 166, 255, 0.15);
|
||||
color: var(--color-link);
|
||||
}
|
||||
|
||||
.tag-collection {
|
||||
background-color: rgba(210, 153, 34, 0.15);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.tag-featured {
|
||||
background-color: rgba(210, 153, 34, 0.2);
|
||||
color: var(--color-warning);
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.results-count {
|
||||
font-size: 14px;
|
||||
color: var(--color-text-muted);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
[
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "awesome-copilot",
|
||||
"name": "Awesome Copilot",
|
||||
@@ -1976,4 +1977,153 @@
|
||||
"path": "collections/typespec-m365-copilot.collection.yml",
|
||||
"filename": "typespec-m365-copilot.collection.yml"
|
||||
}
|
||||
]
|
||||
],
|
||||
"filters": {
|
||||
"tags": [
|
||||
"a11y",
|
||||
"accessibility",
|
||||
"actor",
|
||||
"adaptive-cards",
|
||||
"agent-development",
|
||||
"agents",
|
||||
"ai",
|
||||
"ai-ethics",
|
||||
"angular",
|
||||
"api",
|
||||
"api-plugins",
|
||||
"architecture",
|
||||
"aspnet",
|
||||
"assumption-testing",
|
||||
"async",
|
||||
"async-await",
|
||||
"attributes",
|
||||
"automation",
|
||||
"azure",
|
||||
"best-practices",
|
||||
"bicep",
|
||||
"business-intelligence",
|
||||
"cast-imaging",
|
||||
"cicd",
|
||||
"clojure",
|
||||
"cloud",
|
||||
"code-apps",
|
||||
"code-generation",
|
||||
"code-quality",
|
||||
"component-framework",
|
||||
"composer",
|
||||
"concurrency",
|
||||
"connectors",
|
||||
"copilot-sdk",
|
||||
"copilot-studio",
|
||||
"csharp",
|
||||
"css",
|
||||
"custom-connector",
|
||||
"data-management",
|
||||
"data-modeling",
|
||||
"database",
|
||||
"dataverse",
|
||||
"dax",
|
||||
"dba",
|
||||
"declarative-agents",
|
||||
"devops",
|
||||
"discovery",
|
||||
"dotnet",
|
||||
"enterprise",
|
||||
"epic",
|
||||
"fastapi",
|
||||
"fastmcp",
|
||||
"feature",
|
||||
"feature-flags",
|
||||
"frontend",
|
||||
"gem",
|
||||
"github-copilot",
|
||||
"go",
|
||||
"golang",
|
||||
"html",
|
||||
"impact-analysis",
|
||||
"implementation",
|
||||
"incident-response",
|
||||
"infrastructure",
|
||||
"integration",
|
||||
"interactive-programming",
|
||||
"ios",
|
||||
"java",
|
||||
"javadoc",
|
||||
"javascript",
|
||||
"jest",
|
||||
"jpa",
|
||||
"json-rpc",
|
||||
"junit",
|
||||
"kotlin",
|
||||
"kotlin-multiplatform",
|
||||
"ktor",
|
||||
"m365-copilot",
|
||||
"macos",
|
||||
"macros",
|
||||
"mcp",
|
||||
"meta",
|
||||
"microsoft-365",
|
||||
"migration",
|
||||
"model-context-protocol",
|
||||
"nestjs",
|
||||
"nodejs",
|
||||
"nunit",
|
||||
"observability",
|
||||
"oncall",
|
||||
"openapi",
|
||||
"optimization",
|
||||
"owasp",
|
||||
"pcf",
|
||||
"performance",
|
||||
"php",
|
||||
"planning",
|
||||
"playwright",
|
||||
"postgresql",
|
||||
"power-apps",
|
||||
"power-bi",
|
||||
"power-platform",
|
||||
"product",
|
||||
"project-management",
|
||||
"prompt-engineering",
|
||||
"python",
|
||||
"quality",
|
||||
"quarkus",
|
||||
"queries",
|
||||
"rails",
|
||||
"react",
|
||||
"reactive-streams",
|
||||
"reactor",
|
||||
"repl",
|
||||
"research",
|
||||
"rmcp",
|
||||
"ruby",
|
||||
"rust",
|
||||
"sdk",
|
||||
"security",
|
||||
"server-development",
|
||||
"serverless",
|
||||
"software-analysis",
|
||||
"spring-boot",
|
||||
"springboot",
|
||||
"sql",
|
||||
"sql-server",
|
||||
"swift",
|
||||
"task",
|
||||
"tasks",
|
||||
"tdd",
|
||||
"team",
|
||||
"technical-spike",
|
||||
"terraform",
|
||||
"testing",
|
||||
"tokio",
|
||||
"typescript",
|
||||
"typespec",
|
||||
"unit-tests",
|
||||
"ux",
|
||||
"validation",
|
||||
"visualization",
|
||||
"vue",
|
||||
"web"
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated": "2026-01-28T02:42:05.621Z",
|
||||
"generated": "2026-01-28T03:53:29.513Z",
|
||||
"counts": {
|
||||
"agents": 140,
|
||||
"prompts": 134,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "dotnet-upgrade",
|
||||
"title": ".NET Upgrade Analysis Prompts",
|
||||
@@ -1939,4 +1940,84 @@
|
||||
"path": "prompts/write-coding-standards-from-file.prompt.md",
|
||||
"filename": "write-coding-standards-from-file.prompt.md"
|
||||
}
|
||||
]
|
||||
],
|
||||
"filters": {
|
||||
"tools": [
|
||||
"Microsoft Docs",
|
||||
"agent",
|
||||
"azure_get_schema_for_Bicep",
|
||||
"bicepschema",
|
||||
"changes",
|
||||
"codebase",
|
||||
"context7/*",
|
||||
"createFile",
|
||||
"create_issue",
|
||||
"create_pull_request",
|
||||
"edit",
|
||||
"edit/createFile",
|
||||
"edit/editFiles",
|
||||
"editFiles",
|
||||
"execute",
|
||||
"extensions",
|
||||
"fetch",
|
||||
"findTestFiles",
|
||||
"get_issue",
|
||||
"get_issue_comments",
|
||||
"get_me",
|
||||
"get_pull_request",
|
||||
"get_pull_request_comments",
|
||||
"get_pull_request_diff",
|
||||
"get_pull_request_files",
|
||||
"get_pull_request_reviews",
|
||||
"get_pull_request_status",
|
||||
"github",
|
||||
"githubRepo",
|
||||
"grep_search",
|
||||
"list_dir",
|
||||
"list_issues",
|
||||
"list_pull_requests",
|
||||
"microsoft.docs.mcp",
|
||||
"new",
|
||||
"openSimpleBrowser",
|
||||
"playwright",
|
||||
"playwright/*",
|
||||
"problems",
|
||||
"pylanceRunCodeSnippet",
|
||||
"read",
|
||||
"read_file",
|
||||
"replace_string_in_file",
|
||||
"request_copilot_review",
|
||||
"runCommands",
|
||||
"runCommands/getTerminalOutput",
|
||||
"runCommands/runInTerminal",
|
||||
"runCommands/terminalLastCommand",
|
||||
"runCommands/terminalSelection",
|
||||
"runInTerminal",
|
||||
"runInTerminal2",
|
||||
"runNotebooks",
|
||||
"runTasks",
|
||||
"runTests",
|
||||
"run_in_terminal",
|
||||
"search",
|
||||
"search/codebase",
|
||||
"search/readFile",
|
||||
"search/searchResults",
|
||||
"search/textSearch",
|
||||
"search_issues",
|
||||
"terminalCommand",
|
||||
"testFailure",
|
||||
"think",
|
||||
"todo",
|
||||
"todos",
|
||||
"update_issue",
|
||||
"update_pull_request",
|
||||
"upstash/context7/*",
|
||||
"usages",
|
||||
"vscode",
|
||||
"vscodeAPI",
|
||||
"web",
|
||||
"web/fetch",
|
||||
"writeTest"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
[
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "agentic-eval",
|
||||
"name": "agentic-eval",
|
||||
"title": "Agentic Eval",
|
||||
"description": "Patterns and techniques for evaluating and improving AI agent outputs. Use this skill when:\n- Implementing self-critique and reflection loops\n- Building evaluator-optimizer pipelines for quality-critical generation\n- Creating test-driven code refinement workflows\n- Designing rubric-based or LLM-as-judge evaluation systems\n- Adding iterative improvement to agent outputs (code, reports, analysis)\n- Measuring and improving agent response quality",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Testing",
|
||||
"path": "skills/agentic-eval",
|
||||
"skillFile": "skills/agentic-eval/SKILL.md"
|
||||
"skillFile": "skills/agentic-eval/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/agentic-eval/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 5940
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "appinsights-instrumentation",
|
||||
@@ -22,8 +33,53 @@
|
||||
"references/PYTHON.md",
|
||||
"scripts/appinsights.ps1"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 7,
|
||||
"category": "Azure",
|
||||
"path": "skills/appinsights-instrumentation",
|
||||
"skillFile": "skills/appinsights-instrumentation/SKILL.md"
|
||||
"skillFile": "skills/appinsights-instrumentation/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/LICENSE.txt",
|
||||
"name": "LICENSE.txt",
|
||||
"size": 1078
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 2462
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/examples/appinsights.bicep",
|
||||
"name": "examples/appinsights.bicep",
|
||||
"size": 759
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/references/ASPNETCORE.md",
|
||||
"name": "references/ASPNETCORE.md",
|
||||
"size": 1711
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/references/AUTO.md",
|
||||
"name": "references/AUTO.md",
|
||||
"size": 891
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/references/NODEJS.md",
|
||||
"name": "references/NODEJS.md",
|
||||
"size": 1815
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/references/PYTHON.md",
|
||||
"name": "references/PYTHON.md",
|
||||
"size": 1812
|
||||
},
|
||||
{
|
||||
"path": "skills/appinsights-instrumentation/scripts/appinsights.ps1",
|
||||
"name": "scripts/appinsights.ps1",
|
||||
"size": 1221
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "azure-deployment-preflight",
|
||||
@@ -35,8 +91,33 @@
|
||||
"references/REPORT-TEMPLATE.md",
|
||||
"references/VALIDATION-COMMANDS.md"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 3,
|
||||
"category": "Azure",
|
||||
"path": "skills/azure-deployment-preflight",
|
||||
"skillFile": "skills/azure-deployment-preflight/SKILL.md"
|
||||
"skillFile": "skills/azure-deployment-preflight/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/azure-deployment-preflight/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 7490
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-deployment-preflight/references/ERROR-HANDLING.md",
|
||||
"name": "references/ERROR-HANDLING.md",
|
||||
"size": 8896
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-deployment-preflight/references/REPORT-TEMPLATE.md",
|
||||
"name": "references/REPORT-TEMPLATE.md",
|
||||
"size": 7458
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-deployment-preflight/references/VALIDATION-COMMANDS.md",
|
||||
"name": "references/VALIDATION-COMMANDS.md",
|
||||
"size": 8379
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "azure-devops-cli",
|
||||
@@ -44,8 +125,18 @@
|
||||
"title": "Azure Devops Cli",
|
||||
"description": "Manage Azure DevOps resources via CLI including projects, repos, pipelines, builds, pull requests, work items, artifacts, and service endpoints. Use when working with Azure DevOps, az commands, devops automation, CI/CD, or when user mentions Azure DevOps CLI.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Azure",
|
||||
"path": "skills/azure-devops-cli",
|
||||
"skillFile": "skills/azure-devops-cli/SKILL.md"
|
||||
"skillFile": "skills/azure-devops-cli/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/azure-devops-cli/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 55003
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "azure-resource-visualizer",
|
||||
@@ -56,8 +147,28 @@
|
||||
"LICENSE.txt",
|
||||
"assets/template-architecture.md"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 2,
|
||||
"category": "Azure",
|
||||
"path": "skills/azure-resource-visualizer",
|
||||
"skillFile": "skills/azure-resource-visualizer/SKILL.md"
|
||||
"skillFile": "skills/azure-resource-visualizer/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/azure-resource-visualizer/LICENSE.txt",
|
||||
"name": "LICENSE.txt",
|
||||
"size": 1078
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-resource-visualizer/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 9772
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-resource-visualizer/assets/template-architecture.md",
|
||||
"name": "assets/template-architecture.md",
|
||||
"size": 970
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "azure-role-selector",
|
||||
@@ -67,8 +178,23 @@
|
||||
"assets": [
|
||||
"LICENSE.txt"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 1,
|
||||
"category": "Azure",
|
||||
"path": "skills/azure-role-selector",
|
||||
"skillFile": "skills/azure-role-selector/SKILL.md"
|
||||
"skillFile": "skills/azure-role-selector/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/azure-role-selector/LICENSE.txt",
|
||||
"name": "LICENSE.txt",
|
||||
"size": 1078
|
||||
},
|
||||
{
|
||||
"path": "skills/azure-role-selector/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 983
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "azure-static-web-apps",
|
||||
@@ -76,8 +202,18 @@
|
||||
"title": "Azure Static Web Apps",
|
||||
"description": "Helps create, configure, and deploy Azure Static Web Apps using the SWA CLI. Use when deploying static sites to Azure, setting up SWA local development, configuring staticwebapp.config.json, adding Azure Functions APIs to SWA, or setting up GitHub Actions CI/CD for Static Web Apps.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Azure",
|
||||
"path": "skills/azure-static-web-apps",
|
||||
"skillFile": "skills/azure-static-web-apps/SKILL.md"
|
||||
"skillFile": "skills/azure-static-web-apps/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/azure-static-web-apps/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 9499
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "chrome-devtools",
|
||||
@@ -85,8 +221,18 @@
|
||||
"title": "Chrome Devtools",
|
||||
"description": "Expert-level browser automation, debugging, and performance analysis using Chrome DevTools MCP. Use for interacting with web pages, capturing screenshots, analyzing network traffic, and profiling performance.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Other",
|
||||
"path": "skills/chrome-devtools",
|
||||
"skillFile": "skills/chrome-devtools/SKILL.md"
|
||||
"skillFile": "skills/chrome-devtools/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/chrome-devtools/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 4145
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "gh-cli",
|
||||
@@ -94,8 +240,18 @@
|
||||
"title": "Gh Cli",
|
||||
"description": "GitHub CLI (gh) comprehensive reference for repositories, issues, pull requests, Actions, projects, releases, gists, codespaces, organizations, extensions, and all GitHub operations from the command line.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Git & GitHub",
|
||||
"path": "skills/gh-cli",
|
||||
"skillFile": "skills/gh-cli/SKILL.md"
|
||||
"skillFile": "skills/gh-cli/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/gh-cli/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 40503
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "git-commit",
|
||||
@@ -103,8 +259,18 @@
|
||||
"title": "Git Commit",
|
||||
"description": "Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, or mentions \"/commit\". Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Interactive commit with optional type/scope/description overrides, (4) Intelligent file staging for logical grouping",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Git & GitHub",
|
||||
"path": "skills/git-commit",
|
||||
"skillFile": "skills/git-commit/SKILL.md"
|
||||
"skillFile": "skills/git-commit/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/git-commit/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 3198
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-issues",
|
||||
@@ -114,8 +280,23 @@
|
||||
"assets": [
|
||||
"references/templates.md"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 1,
|
||||
"category": "Git & GitHub",
|
||||
"path": "skills/github-issues",
|
||||
"skillFile": "skills/github-issues/SKILL.md"
|
||||
"skillFile": "skills/github-issues/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/github-issues/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 4783
|
||||
},
|
||||
{
|
||||
"path": "skills/github-issues/references/templates.md",
|
||||
"name": "references/templates.md",
|
||||
"size": 1384
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "image-manipulation-image-magick",
|
||||
@@ -123,8 +304,18 @@
|
||||
"title": "Image Manipulation Image Magick",
|
||||
"description": "Process and manipulate images using ImageMagick. Supports resizing, format conversion, batch processing, and retrieving image metadata. Use when working with images, creating thumbnails, resizing wallpapers, or performing batch image operations.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Other",
|
||||
"path": "skills/image-manipulation-image-magick",
|
||||
"skillFile": "skills/image-manipulation-image-magick/SKILL.md"
|
||||
"skillFile": "skills/image-manipulation-image-magick/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/image-manipulation-image-magick/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 6963
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "legacy-circuit-mockups",
|
||||
@@ -153,8 +344,118 @@
|
||||
"references/minipro.md",
|
||||
"references/t48eeprom-programmer.md"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 20,
|
||||
"category": "Diagrams",
|
||||
"path": "skills/legacy-circuit-mockups",
|
||||
"skillFile": "skills/legacy-circuit-mockups/SKILL.md"
|
||||
"skillFile": "skills/legacy-circuit-mockups/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 9249
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/28256-eeprom.md",
|
||||
"name": "references/28256-eeprom.md",
|
||||
"size": 4667
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/555.md",
|
||||
"name": "references/555.md",
|
||||
"size": 33114
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/6502.md",
|
||||
"name": "references/6502.md",
|
||||
"size": 5807
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/6522.md",
|
||||
"name": "references/6522.md",
|
||||
"size": 5881
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/6C62256.md",
|
||||
"name": "references/6C62256.md",
|
||||
"size": 4214
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/7400-series.md",
|
||||
"name": "references/7400-series.md",
|
||||
"size": 4759
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/assembly-compiler.md",
|
||||
"name": "references/assembly-compiler.md",
|
||||
"size": 4860
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/assembly-language.md",
|
||||
"name": "references/assembly-language.md",
|
||||
"size": 5359
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/basic-electronic-components.md",
|
||||
"name": "references/basic-electronic-components.md",
|
||||
"size": 2784
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/breadboard.md",
|
||||
"name": "references/breadboard.md",
|
||||
"size": 5025
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/common-breadboard-components.md",
|
||||
"name": "references/common-breadboard-components.md",
|
||||
"size": 6565
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/connecting-electronic-components.md",
|
||||
"name": "references/connecting-electronic-components.md",
|
||||
"size": 15302
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/emulator-28256-eeprom.md",
|
||||
"name": "references/emulator-28256-eeprom.md",
|
||||
"size": 5198
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/emulator-6502.md",
|
||||
"name": "references/emulator-6502.md",
|
||||
"size": 5853
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/emulator-6522.md",
|
||||
"name": "references/emulator-6522.md",
|
||||
"size": 6698
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/emulator-6C62256.md",
|
||||
"name": "references/emulator-6C62256.md",
|
||||
"size": 4869
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/emulator-lcd.md",
|
||||
"name": "references/emulator-lcd.md",
|
||||
"size": 5118
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/lcd.md",
|
||||
"name": "references/lcd.md",
|
||||
"size": 5291
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/minipro.md",
|
||||
"name": "references/minipro.md",
|
||||
"size": 4130
|
||||
},
|
||||
{
|
||||
"path": "skills/legacy-circuit-mockups/references/t48eeprom-programmer.md",
|
||||
"name": "references/t48eeprom-programmer.md",
|
||||
"size": 4398
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "make-skill-template",
|
||||
@@ -162,8 +463,18 @@
|
||||
"title": "Make Skill Template",
|
||||
"description": "Create new Agent Skills for GitHub Copilot from prompts or by duplicating this template. Use when asked to \"create a skill\", \"make a new skill\", \"scaffold a skill\", or when building specialized AI capabilities with bundled resources. Generates SKILL.md files with proper frontmatter, directory structure, and optional scripts/references/assets folders.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Git & GitHub",
|
||||
"path": "skills/make-skill-template",
|
||||
"skillFile": "skills/make-skill-template/SKILL.md"
|
||||
"skillFile": "skills/make-skill-template/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/make-skill-template/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 5368
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "mcp-cli",
|
||||
@@ -171,8 +482,18 @@
|
||||
"title": "Mcp Cli",
|
||||
"description": "Interface for MCP (Model Context Protocol) servers via CLI. Use when you need to interact with external tools, APIs, or data sources through MCP servers, list available MCP servers/tools, or call MCP tools from command line.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "CLI Tools",
|
||||
"path": "skills/mcp-cli",
|
||||
"skillFile": "skills/mcp-cli/SKILL.md"
|
||||
"skillFile": "skills/mcp-cli/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/mcp-cli/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 2539
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "microsoft-code-reference",
|
||||
@@ -180,8 +501,18 @@
|
||||
"title": "Microsoft Code Reference",
|
||||
"description": "Look up Microsoft API references, find working code samples, and verify SDK code is correct. Use when working with Azure SDKs, .NET libraries, or Microsoft APIs—to find the right method, check parameters, get working examples, or troubleshoot errors. Catches hallucinated methods, wrong signatures, and deprecated patterns by querying official docs.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Azure",
|
||||
"path": "skills/microsoft-code-reference",
|
||||
"skillFile": "skills/microsoft-code-reference/SKILL.md"
|
||||
"skillFile": "skills/microsoft-code-reference/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/microsoft-code-reference/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 3353
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "microsoft-docs",
|
||||
@@ -189,8 +520,18 @@
|
||||
"title": "Microsoft Docs",
|
||||
"description": "Query official Microsoft documentation to understand concepts, find tutorials, and learn how services work. Use for Azure, .NET, Microsoft 365, Windows, Power Platform, and all Microsoft technologies. Get accurate, current information from learn.microsoft.com and other official Microsoft websites—architecture overviews, quickstarts, configuration guides, limits, and best practices.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Azure",
|
||||
"path": "skills/microsoft-docs",
|
||||
"skillFile": "skills/microsoft-docs/SKILL.md"
|
||||
"skillFile": "skills/microsoft-docs/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/microsoft-docs/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 2142
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nuget-manager",
|
||||
@@ -198,8 +539,18 @@
|
||||
"title": "Nuget Manager",
|
||||
"description": "Manage NuGet packages in .NET projects/solutions. Use this skill when adding, removing, or updating NuGet package versions. It enforces using `dotnet` CLI for package management and provides strict procedures for direct file edits only when updating versions.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "CLI Tools",
|
||||
"path": "skills/nuget-manager",
|
||||
"skillFile": "skills/nuget-manager/SKILL.md"
|
||||
"skillFile": "skills/nuget-manager/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/nuget-manager/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 3418
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "plantuml-ascii",
|
||||
@@ -207,8 +558,18 @@
|
||||
"title": "Plantuml Ascii",
|
||||
"description": "Generate ASCII art diagrams using PlantUML text mode. Use when user asks to create ASCII diagrams, text-based diagrams, terminal-friendly diagrams, or mentions plantuml ascii, text diagram, ascii art diagram. Supports: Converting PlantUML diagrams to ASCII art, Creating sequence diagrams, class diagrams, flowcharts in ASCII format, Generating Unicode-enhanced ASCII art with -utxt flag",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Diagrams",
|
||||
"path": "skills/plantuml-ascii",
|
||||
"skillFile": "skills/plantuml-ascii/SKILL.md"
|
||||
"skillFile": "skills/plantuml-ascii/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/plantuml-ascii/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 6096
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "prd",
|
||||
@@ -216,8 +577,18 @@
|
||||
"title": "Prd",
|
||||
"description": "Generate high-quality Product Requirements Documents (PRDs) for software systems and AI-powered features. Includes executive summaries, user stories, technical specifications, and risk analysis.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Other",
|
||||
"path": "skills/prd",
|
||||
"skillFile": "skills/prd/SKILL.md"
|
||||
"skillFile": "skills/prd/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/prd/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 4307
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "refactor",
|
||||
@@ -225,8 +596,18 @@
|
||||
"title": "Refactor",
|
||||
"description": "Surgical code refactoring to improve maintainability without changing behavior. Covers extracting functions, renaming variables, breaking down god functions, improving type safety, eliminating code smells, and applying design patterns. Less drastic than repo-rebuilder; use for gradual improvements.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Other",
|
||||
"path": "skills/refactor",
|
||||
"skillFile": "skills/refactor/SKILL.md"
|
||||
"skillFile": "skills/refactor/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/refactor/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 16842
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "scoutqa-test",
|
||||
@@ -234,8 +615,18 @@
|
||||
"title": "Scoutqa Test",
|
||||
"description": "This skill should be used when the user asks to \"test this website\", \"run exploratory testing\", \"check for accessibility issues\", \"verify the login flow works\", \"find bugs on this page\", or requests automated QA testing. Triggers on web application testing scenarios including smoke tests, accessibility audits, e-commerce flows, and user flow validation using ScoutQA CLI. IMPORTANT: Use this skill proactively after implementing web application features to verify they work correctly - don't wait for the user to ask for testing.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Testing",
|
||||
"path": "skills/scoutqa-test",
|
||||
"skillFile": "skills/scoutqa-test/SKILL.md"
|
||||
"skillFile": "skills/scoutqa-test/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/scoutqa-test/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 12001
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "snowflake-semanticview",
|
||||
@@ -243,8 +634,18 @@
|
||||
"title": "Snowflake Semanticview",
|
||||
"description": "Create, alter, and validate Snowflake semantic views using Snowflake CLI (snow). Use when asked to build or troubleshoot semantic views/semantic layer definitions with CREATE/ALTER SEMANTIC VIEW, to validate semantic-view DDL against Snowflake via CLI, or to guide Snowflake CLI installation and connection setup.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "CLI Tools",
|
||||
"path": "skills/snowflake-semanticview",
|
||||
"skillFile": "skills/snowflake-semanticview/SKILL.md"
|
||||
"skillFile": "skills/snowflake-semanticview/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/snowflake-semanticview/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 4411
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "vscode-ext-commands",
|
||||
@@ -252,8 +653,18 @@
|
||||
"title": "Vscode Ext Commands",
|
||||
"description": "Guidelines for contributing commands in VS Code extensions. Indicates naming convention, visibility, localization and other relevant attributes, following VS Code extension development guidelines, libraries and good practices",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "VS Code",
|
||||
"path": "skills/vscode-ext-commands",
|
||||
"skillFile": "skills/vscode-ext-commands/SKILL.md"
|
||||
"skillFile": "skills/vscode-ext-commands/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/vscode-ext-commands/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 1545
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "vscode-ext-localization",
|
||||
@@ -261,8 +672,18 @@
|
||||
"title": "Vscode Ext Localization",
|
||||
"description": "Guidelines for proper localization of VS Code extensions, following VS Code extension development guidelines, libraries and good practices",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "VS Code",
|
||||
"path": "skills/vscode-ext-localization",
|
||||
"skillFile": "skills/vscode-ext-localization/SKILL.md"
|
||||
"skillFile": "skills/vscode-ext-localization/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/vscode-ext-localization/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 1473
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "web-design-reviewer",
|
||||
@@ -273,8 +694,28 @@
|
||||
"references/framework-fixes.md",
|
||||
"references/visual-checklist.md"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 2,
|
||||
"category": "Diagrams",
|
||||
"path": "skills/web-design-reviewer",
|
||||
"skillFile": "skills/web-design-reviewer/SKILL.md"
|
||||
"skillFile": "skills/web-design-reviewer/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/web-design-reviewer/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 10520
|
||||
},
|
||||
{
|
||||
"path": "skills/web-design-reviewer/references/framework-fixes.md",
|
||||
"name": "references/framework-fixes.md",
|
||||
"size": 7437
|
||||
},
|
||||
{
|
||||
"path": "skills/web-design-reviewer/references/visual-checklist.md",
|
||||
"name": "references/visual-checklist.md",
|
||||
"size": 5989
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "webapp-testing",
|
||||
@@ -284,8 +725,23 @@
|
||||
"assets": [
|
||||
"test-helper.js"
|
||||
],
|
||||
"hasAssets": true,
|
||||
"assetCount": 1,
|
||||
"category": "Testing",
|
||||
"path": "skills/webapp-testing",
|
||||
"skillFile": "skills/webapp-testing/SKILL.md"
|
||||
"skillFile": "skills/webapp-testing/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/webapp-testing/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 3311
|
||||
},
|
||||
{
|
||||
"path": "skills/webapp-testing/test-helper.js",
|
||||
"name": "test-helper.js",
|
||||
"size": 1521
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "workiq-copilot",
|
||||
@@ -293,7 +749,34 @@
|
||||
"title": "Workiq Copilot",
|
||||
"description": "Guides the Copilot CLI on how to use the WorkIQ CLI/MCP server to query Microsoft 365 Copilot data (emails, meetings, docs, Teams, people) for live context, summaries, and recommendations.",
|
||||
"assets": [],
|
||||
"hasAssets": false,
|
||||
"assetCount": 0,
|
||||
"category": "Microsoft",
|
||||
"path": "skills/workiq-copilot",
|
||||
"skillFile": "skills/workiq-copilot/SKILL.md"
|
||||
"skillFile": "skills/workiq-copilot/SKILL.md",
|
||||
"files": [
|
||||
{
|
||||
"path": "skills/workiq-copilot/SKILL.md",
|
||||
"name": "SKILL.md",
|
||||
"size": 5539
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"filters": {
|
||||
"categories": [
|
||||
"Azure",
|
||||
"CLI Tools",
|
||||
"Diagrams",
|
||||
"Git & GitHub",
|
||||
"Microsoft",
|
||||
"Other",
|
||||
"Testing",
|
||||
"VS Code"
|
||||
],
|
||||
"hasAssets": [
|
||||
"Yes",
|
||||
"No"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Community-contributed instructions, prompts, agents, and skills for GitHub Copilot">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🤖</text></svg>">
|
||||
<script src="js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="pages/tools.html">Tools</a>
|
||||
<a href="pages/samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
13
website/js/jszip.min.js
vendored
Normal file
13
website/js/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
209
website/js/multi-select.js
Normal file
209
website/js/multi-select.js
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* Multi-select dropdown component
|
||||
* Creates a dropdown with checkboxes for multiple selections
|
||||
*/
|
||||
class MultiSelect {
|
||||
constructor(container, options = {}) {
|
||||
this.container = typeof container === 'string' ? document.querySelector(container) : container;
|
||||
this.options = {
|
||||
placeholder: options.placeholder || 'Select...',
|
||||
searchable: options.searchable !== false,
|
||||
onChange: options.onChange || (() => {}),
|
||||
maxDisplay: options.maxDisplay || 2,
|
||||
};
|
||||
this.items = [];
|
||||
this.selected = new Set();
|
||||
this.isOpen = false;
|
||||
this.searchQuery = '';
|
||||
|
||||
this.render();
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
render() {
|
||||
this.container.classList.add('multi-select');
|
||||
this.container.innerHTML = `
|
||||
<button type="button" class="multi-select-trigger" aria-haspopup="listbox" aria-expanded="false">
|
||||
<span class="multi-select-display">${this.options.placeholder}</span>
|
||||
<svg class="multi-select-arrow" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M4.427 7.427l3.396 3.396a.25.25 0 00.354 0l3.396-3.396A.25.25 0 0011.396 7H4.604a.25.25 0 00-.177.427z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="multi-select-dropdown" role="listbox" aria-multiselectable="true">
|
||||
${this.options.searchable ? `
|
||||
<div class="multi-select-search-wrapper">
|
||||
<input type="text" class="multi-select-search" placeholder="Search..." autocomplete="off">
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="multi-select-options"></div>
|
||||
<div class="multi-select-actions">
|
||||
<button type="button" class="multi-select-clear">Clear</button>
|
||||
<button type="button" class="multi-select-done">Done</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.trigger = this.container.querySelector('.multi-select-trigger');
|
||||
this.display = this.container.querySelector('.multi-select-display');
|
||||
this.dropdown = this.container.querySelector('.multi-select-dropdown');
|
||||
this.optionsContainer = this.container.querySelector('.multi-select-options');
|
||||
this.searchInput = this.container.querySelector('.multi-select-search');
|
||||
this.clearBtn = this.container.querySelector('.multi-select-clear');
|
||||
this.doneBtn = this.container.querySelector('.multi-select-done');
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
// Toggle dropdown
|
||||
this.trigger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.toggle();
|
||||
});
|
||||
|
||||
// Search
|
||||
if (this.searchInput) {
|
||||
this.searchInput.addEventListener('input', () => {
|
||||
this.searchQuery = this.searchInput.value.toLowerCase();
|
||||
this.renderOptions();
|
||||
});
|
||||
this.searchInput.addEventListener('click', (e) => e.stopPropagation());
|
||||
}
|
||||
|
||||
// Clear selection
|
||||
this.clearBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.clearSelection();
|
||||
});
|
||||
|
||||
// Done button
|
||||
this.doneBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.close();
|
||||
});
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!this.container.contains(e.target)) {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation
|
||||
this.container.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setItems(items) {
|
||||
this.items = items.map(item => {
|
||||
if (typeof item === 'string') {
|
||||
return { value: item, label: item };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.renderOptions();
|
||||
}
|
||||
|
||||
renderOptions() {
|
||||
const filteredItems = this.items.filter(item => {
|
||||
if (!this.searchQuery) return true;
|
||||
return item.label.toLowerCase().includes(this.searchQuery);
|
||||
});
|
||||
|
||||
if (filteredItems.length === 0) {
|
||||
this.optionsContainer.innerHTML = '<div class="multi-select-empty">No options found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
this.optionsContainer.innerHTML = filteredItems.map(item => `
|
||||
<label class="multi-select-option" data-value="${this.escapeHtml(item.value)}" title="${this.escapeHtml(item.label)}">
|
||||
<input type="checkbox" ${this.selected.has(item.value) ? 'checked' : ''}>
|
||||
<span class="multi-select-checkbox"></span>
|
||||
<span class="multi-select-label">${this.escapeHtml(item.label)}</span>
|
||||
</label>
|
||||
`).join('');
|
||||
|
||||
// Add change listeners to checkboxes
|
||||
this.optionsContainer.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', (e) => {
|
||||
const value = e.target.closest('.multi-select-option').dataset.value;
|
||||
if (e.target.checked) {
|
||||
this.selected.add(value);
|
||||
} else {
|
||||
this.selected.delete(value);
|
||||
}
|
||||
this.updateDisplay();
|
||||
this.options.onChange(this.getSelected());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateDisplay() {
|
||||
const selected = this.getSelected();
|
||||
if (selected.length === 0) {
|
||||
this.display.textContent = this.options.placeholder;
|
||||
this.display.classList.remove('has-value');
|
||||
} else if (selected.length <= this.options.maxDisplay) {
|
||||
this.display.textContent = selected.join(', ');
|
||||
this.display.classList.add('has-value');
|
||||
} else {
|
||||
this.display.textContent = `${selected.length} selected`;
|
||||
this.display.classList.add('has-value');
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.isOpen) {
|
||||
this.close();
|
||||
} else {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
|
||||
open() {
|
||||
this.isOpen = true;
|
||||
this.container.classList.add('is-open');
|
||||
this.trigger.setAttribute('aria-expanded', 'true');
|
||||
if (this.searchInput) {
|
||||
this.searchInput.value = '';
|
||||
this.searchQuery = '';
|
||||
this.renderOptions();
|
||||
setTimeout(() => this.searchInput.focus(), 10);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isOpen = false;
|
||||
this.container.classList.remove('is-open');
|
||||
this.trigger.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
getSelected() {
|
||||
return Array.from(this.selected);
|
||||
}
|
||||
|
||||
setSelected(values) {
|
||||
this.selected = new Set(values);
|
||||
this.renderOptions();
|
||||
this.updateDisplay();
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
this.selected.clear();
|
||||
this.renderOptions();
|
||||
this.updateDisplay();
|
||||
this.options.onChange(this.getSelected());
|
||||
}
|
||||
|
||||
escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for module usage
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = MultiSelect;
|
||||
}
|
||||
74
website/js/theme.js
Normal file
74
website/js/theme.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Theme management for the Awesome Copilot website
|
||||
* Supports light/dark mode with user preference storage
|
||||
*/
|
||||
|
||||
const THEME_KEY = 'awesome-copilot-theme';
|
||||
|
||||
/**
|
||||
* Get the current theme preference
|
||||
* Priority: localStorage > system preference > dark (default)
|
||||
*/
|
||||
function getThemePreference() {
|
||||
const stored = localStorage.getItem(THEME_KEY);
|
||||
if (stored === 'light' || stored === 'dark') {
|
||||
return stored;
|
||||
}
|
||||
// Check system preference
|
||||
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) {
|
||||
return 'light';
|
||||
}
|
||||
return 'dark';
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply theme to the document
|
||||
*/
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'light') {
|
||||
document.documentElement.setAttribute('data-theme', 'light');
|
||||
} else {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle between light and dark theme
|
||||
*/
|
||||
function toggleTheme() {
|
||||
const current = document.documentElement.getAttribute('data-theme');
|
||||
const newTheme = current === 'light' ? 'dark' : 'light';
|
||||
applyTheme(newTheme);
|
||||
localStorage.setItem(THEME_KEY, newTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize theme on page load
|
||||
*/
|
||||
function initTheme() {
|
||||
// Apply theme immediately to prevent flash
|
||||
const theme = getThemePreference();
|
||||
applyTheme(theme);
|
||||
|
||||
// Listen for system theme changes
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => {
|
||||
// Only auto-switch if user hasn't set a preference
|
||||
const stored = localStorage.getItem(THEME_KEY);
|
||||
if (!stored) {
|
||||
applyTheme(e.matches ? 'light' : 'dark');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize theme immediately (before DOM ready to prevent flash)
|
||||
initTheme();
|
||||
|
||||
// Setup toggle button after DOM ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const toggleBtn = document.getElementById('theme-toggle');
|
||||
if (toggleBtn) {
|
||||
toggleBtn.addEventListener('click', toggleTheme);
|
||||
}
|
||||
});
|
||||
@@ -78,6 +78,13 @@ function getGitHubUrl(filePath) {
|
||||
return `${REPO_GITHUB_URL}/${filePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw GitHub URL for a file (for fetching content)
|
||||
*/
|
||||
function getRawGitHubUrl(filePath) {
|
||||
return `${REPO_BASE_URL}/${filePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a toast notification
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Custom agents for specialized GitHub Copilot experiences">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🤖</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -47,6 +58,26 @@
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Search agents..." autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="filters-bar" id="filters-bar">
|
||||
<div class="filter-group">
|
||||
<label>Model:</label>
|
||||
<div id="filter-model" class="multi-select-container"></div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label>Tool:</label>
|
||||
<div id="filter-tool" class="multi-select-container"></div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="filter-handoffs">
|
||||
Has Handoffs
|
||||
</label>
|
||||
</div>
|
||||
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
|
||||
</div>
|
||||
|
||||
<div class="results-count" id="results-count"></div>
|
||||
<div class="resource-list" id="resource-list">
|
||||
<div class="loading">Loading agents...</div>
|
||||
@@ -100,45 +131,131 @@
|
||||
|
||||
<script src="../js/utils.js"></script>
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/multi-select.js"></script>
|
||||
<script src="../js/app.js"></script>
|
||||
<script>
|
||||
// Page-specific initialization
|
||||
const resourceType = 'agent';
|
||||
const dataFile = 'agents.json';
|
||||
let allItems = [];
|
||||
let filters = { models: [], tools: [] };
|
||||
let search = new FuzzySearch();
|
||||
let modelSelect, toolSelect;
|
||||
|
||||
// Current filter state
|
||||
let currentFilters = {
|
||||
models: [],
|
||||
tools: [],
|
||||
hasHandoffs: false,
|
||||
};
|
||||
|
||||
async function initPage() {
|
||||
const list = document.getElementById('resource-list');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const handoffsCheckbox = document.getElementById('filter-handoffs');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
// Load data
|
||||
const data = await fetchData(dataFile);
|
||||
if (!data) {
|
||||
if (!data || !data.items) {
|
||||
list.innerHTML = '<div class="empty-state"><h3>Failed to load data</h3></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
allItems = data;
|
||||
allItems = data.items;
|
||||
filters = data.filters;
|
||||
search.setItems(allItems);
|
||||
|
||||
// Initialize multi-select filters
|
||||
modelSelect = new MultiSelect('#filter-model', {
|
||||
placeholder: 'All Models',
|
||||
onChange: (selected) => {
|
||||
currentFilters.models = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
modelSelect.setItems(filters.models);
|
||||
|
||||
toolSelect = new MultiSelect('#filter-tool', {
|
||||
placeholder: 'All Tools',
|
||||
onChange: (selected) => {
|
||||
currentFilters.tools = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
toolSelect.setItems(filters.tools);
|
||||
|
||||
// Render all items
|
||||
renderItems(allItems);
|
||||
countEl.textContent = `${allItems.length} agents`;
|
||||
applyFiltersAndRender();
|
||||
|
||||
// Setup search
|
||||
searchInput.addEventListener('input', debounce((e) => {
|
||||
const query = e.target.value;
|
||||
const results = query ? search.search(query) : allItems;
|
||||
renderItems(results, query);
|
||||
countEl.textContent = `${results.length} of ${allItems.length} agents`;
|
||||
}, 200));
|
||||
searchInput.addEventListener('input', debounce(() => applyFiltersAndRender(), 200));
|
||||
|
||||
// Setup filter listeners
|
||||
handoffsCheckbox.addEventListener('change', () => {
|
||||
currentFilters.hasHandoffs = handoffsCheckbox.checked;
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = { models: [], tools: [], hasHandoffs: false };
|
||||
modelSelect.clearSelection();
|
||||
toolSelect.clearSelection();
|
||||
handoffsCheckbox.checked = false;
|
||||
searchInput.value = '';
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
// Setup modal
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const query = searchInput.value;
|
||||
|
||||
// Start with all items or search results
|
||||
let results = query ? search.search(query) : [...allItems];
|
||||
|
||||
// Apply model filter (OR logic - match any selected model)
|
||||
if (currentFilters.models.length > 0) {
|
||||
results = results.filter(item => {
|
||||
if (currentFilters.models.includes('(none)') && !item.model) {
|
||||
return true;
|
||||
}
|
||||
return currentFilters.models.includes(item.model);
|
||||
});
|
||||
}
|
||||
|
||||
// Apply tool filter (OR logic - match any selected tool)
|
||||
if (currentFilters.tools.length > 0) {
|
||||
results = results.filter(item =>
|
||||
item.tools?.some(tool => currentFilters.tools.includes(tool))
|
||||
);
|
||||
}
|
||||
|
||||
// Apply handoffs filter
|
||||
if (currentFilters.hasHandoffs) {
|
||||
results = results.filter(item => item.hasHandoffs);
|
||||
}
|
||||
|
||||
renderItems(results, query);
|
||||
|
||||
// Update count with filter info
|
||||
const activeFilters = [];
|
||||
if (currentFilters.models.length > 0) activeFilters.push(`models: ${currentFilters.models.length}`);
|
||||
if (currentFilters.tools.length > 0) activeFilters.push(`tools: ${currentFilters.tools.length}`);
|
||||
if (currentFilters.hasHandoffs) activeFilters.push('has handoffs');
|
||||
|
||||
let countText = `${results.length} of ${allItems.length} agents`;
|
||||
if (activeFilters.length > 0) {
|
||||
countText += ` (filtered by ${activeFilters.join(', ')})`;
|
||||
}
|
||||
countEl.textContent = countText;
|
||||
}
|
||||
|
||||
function renderItems(items, query = '') {
|
||||
const list = document.getElementById('resource-list');
|
||||
|
||||
@@ -146,7 +263,7 @@
|
||||
list.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<h3>No agents found</h3>
|
||||
<p>Try a different search term</p>
|
||||
<p>Try a different search term or adjust filters</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -157,12 +274,12 @@
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${query ? search.highlight(item.title, query) : escapeHtml(item.title)}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
${item.tools?.length || item.mcpServers?.length ? `
|
||||
<div class="resource-meta">
|
||||
${item.model ? `<span class="resource-tag">Model: ${escapeHtml(item.model)}</span>` : ''}
|
||||
${item.mcpServers?.slice(0, 3).map(s => `<span class="resource-tag">MCP: ${escapeHtml(s)}</span>`).join('') || ''}
|
||||
${item.model ? `<span class="resource-tag tag-model">${escapeHtml(item.model)}</span>` : '<span class="resource-tag tag-none">No model</span>'}
|
||||
${item.hasHandoffs ? '<span class="resource-tag tag-handoffs">Has Handoffs</span>' : ''}
|
||||
${item.tools?.length ? `<span class="resource-tag">${item.tools.length} tools</span>` : ''}
|
||||
${item.mcpServers?.length ? `<span class="resource-tag">MCP: ${item.mcpServers.length}</span>` : ''}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="resource-actions">
|
||||
<a href="${getVSCodeInstallUrl(resourceType, item.path)}" class="btn btn-primary" onclick="event.stopPropagation()">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Curated collections of prompts, instructions, and agents for specific workflows">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📦</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -47,6 +58,22 @@
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Search collections..." autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="filters-bar" id="filters-bar">
|
||||
<div class="filter-group">
|
||||
<label>Tag:</label>
|
||||
<div id="filter-tag" class="multi-select-container"></div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="filter-featured">
|
||||
Featured Only
|
||||
</label>
|
||||
</div>
|
||||
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
|
||||
</div>
|
||||
|
||||
<div class="results-count" id="results-count"></div>
|
||||
<div class="resource-list" id="resource-list">
|
||||
<div class="loading">Loading collections...</div>
|
||||
@@ -96,43 +123,109 @@
|
||||
|
||||
<script src="../js/utils.js"></script>
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/multi-select.js"></script>
|
||||
<script src="../js/app.js"></script>
|
||||
<script>
|
||||
const resourceType = 'collection';
|
||||
const dataFile = 'collections.json';
|
||||
let allItems = [];
|
||||
let filters = { tags: [] };
|
||||
let search = new FuzzySearch();
|
||||
let tagSelect;
|
||||
|
||||
// Current filter state
|
||||
let currentFilters = {
|
||||
tags: [],
|
||||
featured: false,
|
||||
};
|
||||
|
||||
async function initPage() {
|
||||
const list = document.getElementById('resource-list');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const featuredCheckbox = document.getElementById('filter-featured');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
const data = await fetchData(dataFile);
|
||||
if (!data) {
|
||||
if (!data || !data.items) {
|
||||
list.innerHTML = '<div class="empty-state"><h3>Failed to load data</h3></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
allItems = data;
|
||||
allItems = data.items;
|
||||
filters = data.filters;
|
||||
search.setItems(allItems.map(item => ({
|
||||
...item,
|
||||
title: item.name,
|
||||
searchText: `${item.name} ${item.description} ${item.tags?.join(' ') || ''}`.toLowerCase()
|
||||
})));
|
||||
renderItems(allItems);
|
||||
countEl.textContent = `${allItems.length} collections`;
|
||||
|
||||
searchInput.addEventListener('input', debounce((e) => {
|
||||
const query = e.target.value;
|
||||
const results = query ? search.search(query) : allItems;
|
||||
renderItems(results, query);
|
||||
countEl.textContent = `${results.length} of ${allItems.length} collections`;
|
||||
}, 200));
|
||||
// Initialize multi-select filter
|
||||
tagSelect = new MultiSelect('#filter-tag', {
|
||||
placeholder: 'All Tags',
|
||||
onChange: (selected) => {
|
||||
currentFilters.tags = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
tagSelect.setItems(filters.tags);
|
||||
|
||||
// Render all items
|
||||
applyFiltersAndRender();
|
||||
|
||||
// Setup search
|
||||
searchInput.addEventListener('input', debounce(() => applyFiltersAndRender(), 200));
|
||||
|
||||
featuredCheckbox.addEventListener('change', () => {
|
||||
currentFilters.featured = featuredCheckbox.checked;
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = { tags: [], featured: false };
|
||||
tagSelect.clearSelection();
|
||||
featuredCheckbox.checked = false;
|
||||
searchInput.value = '';
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const query = searchInput.value;
|
||||
|
||||
// Start with all items or search results
|
||||
let results = query ? search.search(query) : [...allItems];
|
||||
|
||||
// Apply tag filter (OR logic)
|
||||
if (currentFilters.tags.length > 0) {
|
||||
results = results.filter(item =>
|
||||
item.tags?.some(tag => currentFilters.tags.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
// Apply featured filter
|
||||
if (currentFilters.featured) {
|
||||
results = results.filter(item => item.featured);
|
||||
}
|
||||
|
||||
renderItems(results, query);
|
||||
|
||||
// Update count with filter info
|
||||
const activeFilters = [];
|
||||
if (currentFilters.tags.length > 0) activeFilters.push(`${currentFilters.tags.length} tag${currentFilters.tags.length > 1 ? 's' : ''}`);
|
||||
if (currentFilters.featured) activeFilters.push('featured');
|
||||
|
||||
let countText = `${results.length} of ${allItems.length} collections`;
|
||||
if (activeFilters.length > 0) {
|
||||
countText += ` (filtered by ${activeFilters.join(', ')})`;
|
||||
}
|
||||
countEl.textContent = countText;
|
||||
}
|
||||
|
||||
function renderItems(items, query = '') {
|
||||
const list = document.getElementById('resource-list');
|
||||
|
||||
@@ -140,7 +233,7 @@
|
||||
list.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<h3>No collections found</h3>
|
||||
<p>Try a different search term</p>
|
||||
<p>Try a different search term or adjust filters</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -150,11 +243,11 @@
|
||||
<div class="resource-item" onclick="openFileModal('${item.path}', '${resourceType}')">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">
|
||||
${item.featured ? '⭐ ' : ''}${query ? search.highlight(item.name, query) : escapeHtml(item.name)}
|
||||
${item.featured ? '<span class="tag-featured">⭐ Featured</span> ' : ''}${query ? search.highlight(item.name, query) : escapeHtml(item.name)}
|
||||
</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${item.tags?.map(tag => `<span class="resource-tag">${escapeHtml(tag)}</span>`).join('') || ''}
|
||||
${item.tags?.map(tag => `<span class="resource-tag tag-collection">${escapeHtml(tag)}</span>`).join('') || ''}
|
||||
<span class="resource-tag">${item.items?.length || 0} items</span>
|
||||
</div>
|
||||
${item.items?.length ? `
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Coding standards and best practices for GitHub Copilot">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📋</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -47,6 +58,16 @@
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Search instructions..." autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="filters-bar" id="filters-bar">
|
||||
<div class="filter-group">
|
||||
<label>File Extension:</label>
|
||||
<div id="filter-extension" class="multi-select-container"></div>
|
||||
</div>
|
||||
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
|
||||
</div>
|
||||
|
||||
<div class="results-count" id="results-count"></div>
|
||||
<div class="resource-list" id="resource-list">
|
||||
<div class="loading">Loading instructions...</div>
|
||||
@@ -100,39 +121,91 @@
|
||||
|
||||
<script src="../js/utils.js"></script>
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/multi-select.js"></script>
|
||||
<script src="../js/app.js"></script>
|
||||
<script>
|
||||
const resourceType = 'instruction';
|
||||
const dataFile = 'instructions.json';
|
||||
let allItems = [];
|
||||
let filters = { extensions: [], patterns: [] };
|
||||
let search = new FuzzySearch();
|
||||
let extensionSelect;
|
||||
|
||||
// Current filter state
|
||||
let currentFilters = {
|
||||
extensions: [],
|
||||
};
|
||||
|
||||
async function initPage() {
|
||||
const list = document.getElementById('resource-list');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
const data = await fetchData(dataFile);
|
||||
if (!data) {
|
||||
if (!data || !data.items) {
|
||||
list.innerHTML = '<div class="empty-state"><h3>Failed to load data</h3></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
allItems = data;
|
||||
allItems = data.items;
|
||||
filters = data.filters;
|
||||
search.setItems(allItems);
|
||||
renderItems(allItems);
|
||||
countEl.textContent = `${allItems.length} instructions`;
|
||||
|
||||
searchInput.addEventListener('input', debounce((e) => {
|
||||
const query = e.target.value;
|
||||
const results = query ? search.search(query) : allItems;
|
||||
renderItems(results, query);
|
||||
countEl.textContent = `${results.length} of ${allItems.length} instructions`;
|
||||
}, 200));
|
||||
// Initialize multi-select filter
|
||||
extensionSelect = new MultiSelect('#filter-extension', {
|
||||
placeholder: 'All Extensions',
|
||||
onChange: (selected) => {
|
||||
currentFilters.extensions = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
extensionSelect.setItems(filters.extensions);
|
||||
|
||||
// Render all items
|
||||
applyFiltersAndRender();
|
||||
|
||||
// Setup search
|
||||
searchInput.addEventListener('input', debounce(() => applyFiltersAndRender(), 200));
|
||||
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = { extensions: [] };
|
||||
extensionSelect.clearSelection();
|
||||
searchInput.value = '';
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const query = searchInput.value;
|
||||
|
||||
// Start with all items or search results
|
||||
let results = query ? search.search(query) : [...allItems];
|
||||
|
||||
// Apply extension filter (OR logic)
|
||||
if (currentFilters.extensions.length > 0) {
|
||||
results = results.filter(item => {
|
||||
if (currentFilters.extensions.includes('(none)') && (!item.extensions || item.extensions.length === 0)) {
|
||||
return true;
|
||||
}
|
||||
return item.extensions?.some(ext => currentFilters.extensions.includes(ext));
|
||||
});
|
||||
}
|
||||
|
||||
renderItems(results, query);
|
||||
|
||||
// Update count with filter info
|
||||
let countText = `${results.length} of ${allItems.length} instructions`;
|
||||
if (currentFilters.extensions.length > 0) {
|
||||
countText += ` (filtered by ${currentFilters.extensions.length} extension${currentFilters.extensions.length > 1 ? 's' : ''})`;
|
||||
}
|
||||
countEl.textContent = countText;
|
||||
}
|
||||
|
||||
function renderItems(items, query = '') {
|
||||
const list = document.getElementById('resource-list');
|
||||
|
||||
@@ -140,7 +213,7 @@
|
||||
list.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<h3>No instructions found</h3>
|
||||
<p>Try a different search term</p>
|
||||
<p>Try a different search term or adjust filters</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -151,11 +224,9 @@
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${query ? search.highlight(item.title, query) : escapeHtml(item.title)}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
${item.applyTo ? `
|
||||
<div class="resource-meta">
|
||||
<span class="resource-tag">Applies to: ${escapeHtml(item.applyTo)}</span>
|
||||
${item.extensions?.length ? item.extensions.map(ext => `<span class="resource-tag tag-extension">${escapeHtml(ext)}</span>`).join('') : '<span class="resource-tag tag-none">All files</span>'}
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="resource-actions">
|
||||
<a href="${getVSCodeInstallUrl('instructions', item.path)}" class="btn btn-primary" onclick="event.stopPropagation()">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Ready-to-use prompt templates for development tasks with GitHub Copilot">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎯</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -47,6 +58,16 @@
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Search prompts..." autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="filters-bar" id="filters-bar">
|
||||
<div class="filter-group">
|
||||
<label>Tool:</label>
|
||||
<div id="filter-tool" class="multi-select-container"></div>
|
||||
</div>
|
||||
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
|
||||
</div>
|
||||
|
||||
<div class="results-count" id="results-count"></div>
|
||||
<div class="resource-list" id="resource-list">
|
||||
<div class="loading">Loading prompts...</div>
|
||||
@@ -100,39 +121,88 @@
|
||||
|
||||
<script src="../js/utils.js"></script>
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/multi-select.js"></script>
|
||||
<script src="../js/app.js"></script>
|
||||
<script>
|
||||
const resourceType = 'prompt';
|
||||
const dataFile = 'prompts.json';
|
||||
let allItems = [];
|
||||
let filters = { tools: [] };
|
||||
let search = new FuzzySearch();
|
||||
let toolSelect;
|
||||
|
||||
// Current filter state
|
||||
let currentFilters = {
|
||||
tools: [],
|
||||
};
|
||||
|
||||
async function initPage() {
|
||||
const list = document.getElementById('resource-list');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
const data = await fetchData(dataFile);
|
||||
if (!data) {
|
||||
if (!data || !data.items) {
|
||||
list.innerHTML = '<div class="empty-state"><h3>Failed to load data</h3></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
allItems = data;
|
||||
allItems = data.items;
|
||||
filters = data.filters;
|
||||
search.setItems(allItems);
|
||||
renderItems(allItems);
|
||||
countEl.textContent = `${allItems.length} prompts`;
|
||||
|
||||
searchInput.addEventListener('input', debounce((e) => {
|
||||
const query = e.target.value;
|
||||
const results = query ? search.search(query) : allItems;
|
||||
renderItems(results, query);
|
||||
countEl.textContent = `${results.length} of ${allItems.length} prompts`;
|
||||
}, 200));
|
||||
// Initialize multi-select filter
|
||||
toolSelect = new MultiSelect('#filter-tool', {
|
||||
placeholder: 'All Tools',
|
||||
onChange: (selected) => {
|
||||
currentFilters.tools = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
toolSelect.setItems(filters.tools);
|
||||
|
||||
// Render all items
|
||||
applyFiltersAndRender();
|
||||
|
||||
// Setup search
|
||||
searchInput.addEventListener('input', debounce(() => applyFiltersAndRender(), 200));
|
||||
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = { tools: [] };
|
||||
toolSelect.clearSelection();
|
||||
searchInput.value = '';
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const query = searchInput.value;
|
||||
|
||||
// Start with all items or search results
|
||||
let results = query ? search.search(query) : [...allItems];
|
||||
|
||||
// Apply tool filter (OR logic)
|
||||
if (currentFilters.tools.length > 0) {
|
||||
results = results.filter(item =>
|
||||
item.tools?.some(tool => currentFilters.tools.includes(tool))
|
||||
);
|
||||
}
|
||||
|
||||
renderItems(results, query);
|
||||
|
||||
// Update count with filter info
|
||||
let countText = `${results.length} of ${allItems.length} prompts`;
|
||||
if (currentFilters.tools.length > 0) {
|
||||
countText += ` (filtered by ${currentFilters.tools.length} tool${currentFilters.tools.length > 1 ? 's' : ''})`;
|
||||
}
|
||||
countEl.textContent = countText;
|
||||
}
|
||||
|
||||
function renderItems(items, query = '') {
|
||||
const list = document.getElementById('resource-list');
|
||||
|
||||
@@ -140,7 +210,7 @@
|
||||
list.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<h3>No prompts found</h3>
|
||||
<p>Try a different search term</p>
|
||||
<p>Try a different search term or adjust filters</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -152,8 +222,8 @@
|
||||
<div class="resource-title">${query ? search.highlight(item.title, query) : escapeHtml(item.title)}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${item.model ? `<span class="resource-tag">Model: ${escapeHtml(item.model)}</span>` : ''}
|
||||
${item.agent ? `<span class="resource-tag">Agent: ${escapeHtml(item.agent)}</span>` : ''}
|
||||
${item.model ? `<span class="resource-tag tag-model">${escapeHtml(item.model)}</span>` : ''}
|
||||
${item.tools?.length ? `<span class="resource-tag">${item.tools.length} tools</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-actions">
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Code samples and examples for building with GitHub Copilot">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📚</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html" class="active">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="Self-contained agent skills with instructions and bundled resources">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
@@ -47,6 +58,22 @@
|
||||
<div class="search-bar">
|
||||
<input type="text" id="search-input" placeholder="Search skills..." autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="filters-bar" id="filters-bar">
|
||||
<div class="filter-group">
|
||||
<label>Category:</label>
|
||||
<div id="filter-category" class="multi-select-container"></div>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox" id="filter-has-assets">
|
||||
Has Bundled Assets
|
||||
</label>
|
||||
</div>
|
||||
<button id="clear-filters" class="btn btn-secondary btn-small">Clear Filters</button>
|
||||
</div>
|
||||
|
||||
<div class="results-count" id="results-count"></div>
|
||||
<div class="resource-list" id="resource-list">
|
||||
<div class="loading">Loading skills...</div>
|
||||
@@ -99,39 +126,104 @@
|
||||
|
||||
<script src="../js/utils.js"></script>
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/multi-select.js"></script>
|
||||
<script src="../js/jszip.min.js"></script>
|
||||
<script src="../js/app.js"></script>
|
||||
<script>
|
||||
const resourceType = 'skill';
|
||||
const dataFile = 'skills.json';
|
||||
let allItems = [];
|
||||
let filters = { categories: [], hasAssets: [] };
|
||||
let search = new FuzzySearch();
|
||||
let categorySelect;
|
||||
|
||||
// Current filter state
|
||||
let currentFilters = {
|
||||
categories: [],
|
||||
hasAssets: false,
|
||||
};
|
||||
|
||||
async function initPage() {
|
||||
const list = document.getElementById('resource-list');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const hasAssetsCheckbox = document.getElementById('filter-has-assets');
|
||||
const clearFiltersBtn = document.getElementById('clear-filters');
|
||||
|
||||
const data = await fetchData(dataFile);
|
||||
if (!data) {
|
||||
if (!data || !data.items) {
|
||||
list.innerHTML = '<div class="empty-state"><h3>Failed to load data</h3></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
allItems = data;
|
||||
allItems = data.items;
|
||||
filters = data.filters;
|
||||
search.setItems(allItems);
|
||||
renderItems(allItems);
|
||||
countEl.textContent = `${allItems.length} skills`;
|
||||
|
||||
searchInput.addEventListener('input', debounce((e) => {
|
||||
const query = e.target.value;
|
||||
const results = query ? search.search(query) : allItems;
|
||||
renderItems(results, query);
|
||||
countEl.textContent = `${results.length} of ${allItems.length} skills`;
|
||||
}, 200));
|
||||
// Initialize multi-select filter
|
||||
categorySelect = new MultiSelect('#filter-category', {
|
||||
placeholder: 'All Categories',
|
||||
onChange: (selected) => {
|
||||
currentFilters.categories = selected;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
});
|
||||
categorySelect.setItems(filters.categories);
|
||||
|
||||
// Render all items
|
||||
applyFiltersAndRender();
|
||||
|
||||
// Setup search
|
||||
searchInput.addEventListener('input', debounce(() => applyFiltersAndRender(), 200));
|
||||
|
||||
hasAssetsCheckbox.addEventListener('change', () => {
|
||||
currentFilters.hasAssets = hasAssetsCheckbox.checked;
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
clearFiltersBtn.addEventListener('click', () => {
|
||||
currentFilters = { categories: [], hasAssets: false };
|
||||
categorySelect.clearSelection();
|
||||
hasAssetsCheckbox.checked = false;
|
||||
searchInput.value = '';
|
||||
applyFiltersAndRender();
|
||||
});
|
||||
|
||||
setupModal();
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const countEl = document.getElementById('results-count');
|
||||
const query = searchInput.value;
|
||||
|
||||
// Start with all items or search results
|
||||
let results = query ? search.search(query) : [...allItems];
|
||||
|
||||
// Apply category filter (OR logic)
|
||||
if (currentFilters.categories.length > 0) {
|
||||
results = results.filter(item => currentFilters.categories.includes(item.category));
|
||||
}
|
||||
|
||||
// Apply has assets filter
|
||||
if (currentFilters.hasAssets) {
|
||||
results = results.filter(item => item.hasAssets);
|
||||
}
|
||||
|
||||
renderItems(results, query);
|
||||
|
||||
// Update count with filter info
|
||||
const activeFilters = [];
|
||||
if (currentFilters.categories.length > 0) activeFilters.push(`${currentFilters.categories.length} categor${currentFilters.categories.length > 1 ? 'ies' : 'y'}`);
|
||||
if (currentFilters.hasAssets) activeFilters.push('has assets');
|
||||
|
||||
let countText = `${results.length} of ${allItems.length} skills`;
|
||||
if (activeFilters.length > 0) {
|
||||
countText += ` (filtered by ${activeFilters.join(', ')})`;
|
||||
}
|
||||
countEl.textContent = countText;
|
||||
}
|
||||
|
||||
function renderItems(items, query = '') {
|
||||
const list = document.getElementById('resource-list');
|
||||
|
||||
@@ -139,7 +231,7 @@
|
||||
list.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<h3>No skills found</h3>
|
||||
<p>Try a different search term</p>
|
||||
<p>Try a different search term or adjust filters</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
@@ -150,13 +242,20 @@
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${query ? search.highlight(item.title, query) : escapeHtml(item.title)}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
${item.assets?.length ? `
|
||||
<div class="resource-meta">
|
||||
<span class="resource-tag">${item.assets.length} bundled asset${item.assets.length === 1 ? '' : 's'}</span>
|
||||
<span class="resource-tag tag-category">${escapeHtml(item.category)}</span>
|
||||
${item.hasAssets ? `<span class="resource-tag tag-assets">${item.assetCount} asset${item.assetCount === 1 ? '' : 's'}</span>` : ''}
|
||||
<span class="resource-tag">${item.files.length} file${item.files.length === 1 ? '' : 's'}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
<div class="resource-actions">
|
||||
<button class="btn btn-primary" onclick="event.stopPropagation(); downloadSkill('${item.id}')" title="Download as ZIP">
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"/>
|
||||
<path d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.749.749 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06l1.97 1.969Z"/>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
<a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()">
|
||||
View Folder
|
||||
</a>
|
||||
@@ -165,6 +264,103 @@
|
||||
`).join('');
|
||||
}
|
||||
|
||||
// Download skill as ZIP file
|
||||
async function downloadSkill(skillId) {
|
||||
const skill = allItems.find(item => item.id === skillId);
|
||||
if (!skill || !skill.files || skill.files.length === 0) {
|
||||
alert('No files found for this skill');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
const btn = event.target.closest('button');
|
||||
const originalContent = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = `
|
||||
<svg class="spinner" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M8 0a8 8 0 1 0 8 8h-1.5A6.5 6.5 0 1 1 8 1.5V0z"/>
|
||||
</svg>
|
||||
Preparing...
|
||||
`;
|
||||
|
||||
try {
|
||||
const zip = new JSZip();
|
||||
const folder = zip.folder(skill.id);
|
||||
|
||||
// Fetch all files in parallel
|
||||
const fetchPromises = skill.files.map(async (file) => {
|
||||
const url = getRawGitHubUrl(file.path);
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.warn(`Failed to fetch ${file.path}: ${response.status}`);
|
||||
return null;
|
||||
}
|
||||
const content = await response.text();
|
||||
return { name: file.name, content };
|
||||
} catch (err) {
|
||||
console.warn(`Error fetching ${file.path}:`, err);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(fetchPromises);
|
||||
|
||||
// Add successfully fetched files to zip
|
||||
let addedFiles = 0;
|
||||
for (const result of results) {
|
||||
if (result) {
|
||||
folder.file(result.name, result.content);
|
||||
addedFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
if (addedFiles === 0) {
|
||||
throw new Error('Failed to fetch any files');
|
||||
}
|
||||
|
||||
// Generate and download zip
|
||||
const blob = await zip.generateAsync({ type: 'blob' });
|
||||
const downloadUrl = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = downloadUrl;
|
||||
link.download = `${skill.id}.zip`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
URL.revokeObjectURL(downloadUrl);
|
||||
|
||||
// Show success
|
||||
btn.innerHTML = `
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0z"/>
|
||||
</svg>
|
||||
Downloaded!
|
||||
`;
|
||||
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalContent;
|
||||
}, 2000);
|
||||
|
||||
} catch (err) {
|
||||
console.error('Download failed:', err);
|
||||
btn.innerHTML = `
|
||||
<svg viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
||||
<path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.75.75 0 1 1 1.06 1.06L9.06 8l3.22 3.22a.75.75 0 0 1-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 0 1-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06z"/>
|
||||
</svg>
|
||||
Failed
|
||||
`;
|
||||
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalContent;
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initPage);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<meta name="description" content="MCP servers and developer tools for GitHub Copilot">
|
||||
<link rel="stylesheet" href="../css/styles.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔧</text></svg>">
|
||||
<script src="../js/theme.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
@@ -25,6 +26,15 @@
|
||||
<a href="tools.html" class="active">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
||||
<svg class="icon-sun" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0V.75A.75.75 0 0 1 8 0zm0 13a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5A.75.75 0 0 1 8 13zM2.343 2.343a.75.75 0 0 1 1.061 0l1.06 1.061a.75.75 0 0 1-1.06 1.06l-1.06-1.06a.75.75 0 0 1 0-1.06zm9.193 9.193a.75.75 0 0 1 1.06 0l1.061 1.06a.75.75 0 0 1-1.06 1.061l-1.061-1.06a.75.75 0 0 1 0-1.061zM0 8a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H.75A.75.75 0 0 1 0 8zm13 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5h-1.5A.75.75 0 0 1 13 8zM2.343 13.657a.75.75 0 0 1 0-1.061l1.06-1.06a.75.75 0 0 1 1.061 1.06l-1.06 1.06a.75.75 0 0 1-1.061 0zm9.193-9.193a.75.75 0 0 1 0-1.06l1.061-1.061a.75.75 0 0 1 1.06 1.06l-1.06 1.061a.75.75 0 0 1-1.061 0z"/>
|
||||
</svg>
|
||||
<svg class="icon-moon" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M9.598 1.591a.75.75 0 0 1 .785-.175 7 7 0 1 1-8.967 8.967.75.75 0 0 1 .961-.96 5.5 5.5 0 0 0 7.046-7.046.75.75 0 0 1 .175-.786zm1.616 1.945a7 7 0 0 1-7.678 7.678 5.5 5.5 0 1 0 7.678-7.678z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<a href="https://github.com/github/awesome-copilot" class="github-link" target="_blank" rel="noopener">
|
||||
<svg viewBox="0 0 16 16" width="24" height="24" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
|
||||
@@ -32,6 +42,7 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
|
||||
Reference in New Issue
Block a user