mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-23 20:05: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:
@@ -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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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('') || ''}
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="resource-meta">
|
||||
${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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="resource-meta">
|
||||
${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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html" class="active">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -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,11 +26,21 @@
|
||||
<a href="tools.html">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -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>
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="resource-meta">
|
||||
<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,11 +26,21 @@
|
||||
<a href="tools.html" class="active">Tools</a>
|
||||
<a href="samples.html">Samples</a>
|
||||
</nav>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
<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>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user