mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-12 04:05:12 +00:00
Improve website accessibility (#979)
* Improve website accessibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refine homepage search semantics Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -11,7 +11,7 @@ const initialItems = sortAgents(agentsData.items, 'title');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Custom Agents', description: 'Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Custom Agents', description: 'Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="🤖 Custom Agents" description="Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains" />
|
<PageHeader title="🤖 Custom Agents" description="Specialized agents that enhance GitHub Copilot for specific technologies, workflows, and domains" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -54,7 +54,7 @@ const initialItems = sortAgents(agentsData.items, 'title');
|
|||||||
<ContributeCTA resourceType="agents" />
|
<ContributeCTA resourceType="agents" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
<EmbeddedPageData filename="agents.json" data={agentsData} />
|
<EmbeddedPageData filename="agents.json" data={agentsData} />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import PageHeader from '../components/PageHeader.astro';
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Contributors', description: 'The wonderful people who have contributed to Awesome GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Contributors', description: 'The wonderful people who have contributed to Awesome GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="🌟 Contributors" description="The wonderful people who have contributed to Awesome GitHub Copilot" />
|
<PageHeader title="🌟 Contributors" description="The wonderful people who have contributed to Awesome GitHub Copilot" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -281,7 +281,7 @@ import PageHeader from '../components/PageHeader.astro';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const initialItems = sortHooks(hooksData.items, 'title');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Hooks', description: 'Automated workflows triggered by Copilot coding agent events', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Hooks', description: 'Automated workflows triggered by Copilot coding agent events', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="🪝 Hooks" description="Automated workflows triggered by Copilot coding agent events" />
|
<PageHeader title="🪝 Hooks" description="Automated workflows triggered by Copilot coding agent events" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -47,7 +47,7 @@ const initialItems = sortHooks(hooksData.items, 'title');
|
|||||||
<ContributeCTA resourceType="hooks" />
|
<ContributeCTA resourceType="hooks" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const base = import.meta.env.BASE_URL;
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Awesome GitHub Copilot', template: 'splash', pagefind: false, prev: false, next: false, editUrl: false }} hasSidebar={false}>
|
<StarlightPage frontmatter={{ title: 'Awesome GitHub Copilot', template: 'splash', pagefind: false, prev: false, next: false, editUrl: false }} hasSidebar={false}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<!-- Hero Section -->
|
<!-- Hero Section -->
|
||||||
<section class="hero" aria-labelledby="hero-heading">
|
<section class="hero" aria-labelledby="hero-heading">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -14,8 +14,18 @@ const base = import.meta.env.BASE_URL;
|
|||||||
<p class="hero-subtitle">Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience</p>
|
<p class="hero-subtitle">Community-contributed agents, instructions, and skills to enhance your GitHub Copilot experience</p>
|
||||||
<div class="hero-search">
|
<div class="hero-search">
|
||||||
<label for="global-search" class="sr-only">Search all resources</label>
|
<label for="global-search" class="sr-only">Search all resources</label>
|
||||||
<input type="text" id="global-search" placeholder="Search all resources..." autocomplete="off" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="search-results">
|
<p id="global-search-help" class="sr-only">
|
||||||
<div id="search-results" class="search-results hidden" role="listbox" aria-label="Search results"></div>
|
Type at least two characters to show matching resources, then press the Down Arrow key to move into the results.
|
||||||
|
</p>
|
||||||
|
<p id="global-search-status" class="sr-only" aria-live="polite"></p>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="global-search"
|
||||||
|
placeholder="Search all resources..."
|
||||||
|
autocomplete="off"
|
||||||
|
aria-describedby="global-search-help global-search-status"
|
||||||
|
>
|
||||||
|
<div id="search-results" class="search-results hidden" aria-label="Search results"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -92,7 +102,7 @@ const base = import.meta.env.BASE_URL;
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const initialItems = sortInstructions(instructionsData.items, 'title');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Instructions', description: 'Coding standards and best practices for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Instructions', description: 'Coding standards and best practices for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="📋 Instructions" description="Coding standards and best practices for GitHub Copilot" />
|
<PageHeader title="📋 Instructions" description="Coding standards and best practices for GitHub Copilot" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -43,7 +43,7 @@ const initialItems = sortInstructions(instructionsData.items, 'title');
|
|||||||
<ContributeCTA resourceType="instructions" />
|
<ContributeCTA resourceType="instructions" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
<EmbeddedPageData filename="instructions.json" data={instructionsData} />
|
<EmbeddedPageData filename="instructions.json" data={instructionsData} />
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const initialItems = pluginsData.items;
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Plugins', description: 'Curated plugins of agents, hooks, and skills for specific workflows', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Plugins', description: 'Curated plugins of agents, hooks, and skills for specific workflows', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="🔌 Plugins" description="Curated plugins of agents, hooks, and skills for specific workflows" />
|
<PageHeader title="🔌 Plugins" description="Curated plugins of agents, hooks, and skills for specific workflows" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -45,7 +45,7 @@ const initialItems = pluginsData.items;
|
|||||||
<ContributeCTA resourceType="plugins" />
|
<ContributeCTA resourceType="plugins" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
<EmbeddedPageData filename="plugins.json" data={pluginsData} />
|
<EmbeddedPageData filename="plugins.json" data={pluginsData} />
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const initialItems = sortSkills(skillsData.items, 'title');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Skills', description: 'Self-contained agent skills with instructions and bundled resources', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Skills', description: 'Self-contained agent skills with instructions and bundled resources', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="⚡ Skills" description="Self-contained agent skills with instructions and bundled resources" />
|
<PageHeader title="⚡ Skills" description="Self-contained agent skills with instructions and bundled resources" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -49,7 +49,7 @@ const initialItems = sortSkills(skillsData.items, 'title');
|
|||||||
<ContributeCTA resourceType="skills" />
|
<ContributeCTA resourceType="skills" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const initialItems = toolsData.items.map((item) => ({
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Tools', description: 'MCP servers and developer tools for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Tools', description: 'MCP servers and developer tools for GitHub Copilot', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="🔧 Tools" description="MCP servers and developer tools for GitHub Copilot" />
|
<PageHeader title="🔧 Tools" description="MCP servers and developer tools for GitHub Copilot" />
|
||||||
|
|
||||||
<div class="page-content">
|
<div class="page-content">
|
||||||
@@ -55,7 +55,7 @@ const initialItems = toolsData.items.map((item) => ({
|
|||||||
<ContributeCTA resourceType="tools" />
|
<ContributeCTA resourceType="tools" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<EmbeddedPageData filename="tools.json" data={toolsData} />
|
<EmbeddedPageData filename="tools.json" data={toolsData} />
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const initialItems = sortWorkflows(workflowsData.items, 'title');
|
|||||||
---
|
---
|
||||||
|
|
||||||
<StarlightPage frontmatter={{ title: 'Agentic Workflows', description: 'AI-powered repository automations that run coding agents in GitHub Actions', template: 'splash', prev: false, next: false, editUrl: false }}>
|
<StarlightPage frontmatter={{ title: 'Agentic Workflows', description: 'AI-powered repository automations that run coding agents in GitHub Actions', template: 'splash', prev: false, next: false, editUrl: false }}>
|
||||||
<main id="main-content">
|
<div id="main-content">
|
||||||
<PageHeader title="⚡ Agentic Workflows" description="">
|
<PageHeader title="⚡ Agentic Workflows" description="">
|
||||||
AI-powered repository automations that run coding agents in <a href="https://gh.io/gh-aw" target="_blank" rel="noopener">GitHub Actions</a>
|
AI-powered repository automations that run coding agents in <a href="https://gh.io/gh-aw" target="_blank" rel="noopener">GitHub Actions</a>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
@@ -45,7 +45,7 @@ const initialItems = sortWorkflows(workflowsData.items, 'title');
|
|||||||
<ContributeCTA resourceType="workflows" />
|
<ContributeCTA resourceType="workflows" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
|
||||||
<Modal />
|
<Modal />
|
||||||
<EmbeddedPageData filename="workflows.json" data={workflowsData} />
|
<EmbeddedPageData filename="workflows.json" data={workflowsData} />
|
||||||
|
|||||||
@@ -61,44 +61,46 @@ export function renderAgentsHtml(
|
|||||||
: escapeHtml(item.title);
|
: escapeHtml(item.title);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(
|
<div class="resource-title">${titleHtml}</div>
|
||||||
item.description || "No description"
|
<div class="resource-description">${escapeHtml(
|
||||||
)}</div>
|
item.description || "No description"
|
||||||
<div class="resource-meta">
|
)}</div>
|
||||||
${
|
<div class="resource-meta">
|
||||||
item.model
|
${
|
||||||
? `<span class="resource-tag tag-model">${escapeHtml(
|
item.model
|
||||||
item.model
|
? `<span class="resource-tag tag-model">${escapeHtml(
|
||||||
)}</span>`
|
item.model
|
||||||
: ""
|
)}</span>`
|
||||||
}
|
: ""
|
||||||
${
|
}
|
||||||
item.tools
|
${
|
||||||
?.slice(0, 3)
|
item.tools
|
||||||
.map(
|
?.slice(0, 3)
|
||||||
(tool) =>
|
.map(
|
||||||
`<span class="resource-tag">${escapeHtml(tool)}</span>`
|
(tool) =>
|
||||||
)
|
`<span class="resource-tag">${escapeHtml(tool)}</span>`
|
||||||
.join("") || ""
|
)
|
||||||
}
|
.join("") || ""
|
||||||
${
|
}
|
||||||
item.tools && item.tools.length > 3
|
${
|
||||||
? `<span class="resource-tag">+${
|
item.tools && item.tools.length > 3
|
||||||
item.tools.length - 3
|
? `<span class="resource-tag">+${
|
||||||
} more</span>`
|
item.tools.length - 3
|
||||||
: ""
|
} more</span>`
|
||||||
}
|
: ""
|
||||||
${
|
}
|
||||||
item.hasHandoffs
|
${
|
||||||
? `<span class="resource-tag tag-handoffs">handoffs</span>`
|
item.hasHandoffs
|
||||||
: ""
|
? `<span class="resource-tag tag-handoffs">handoffs</span>`
|
||||||
}
|
: ""
|
||||||
${getLastUpdatedHtml(item.lastUpdated)}
|
}
|
||||||
|
${getLastUpdatedHtml(item.lastUpdated)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
${getInstallDropdownHtml(resourceType, item.path, true)}
|
${getInstallDropdownHtml(resourceType, item.path, true)}
|
||||||
${getActionButtonsHtml(item.path, true)}
|
${getActionButtonsHtml(item.path, true)}
|
||||||
@@ -108,7 +110,7 @@ export function renderAgentsHtml(
|
|||||||
GitHub
|
GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|||||||
@@ -59,41 +59,43 @@ export function renderHooksHtml(
|
|||||||
: escapeHtml(item.title);
|
: escapeHtml(item.title);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item" data-path="${escapeHtml(
|
<article class="resource-item" data-path="${escapeHtml(
|
||||||
item.readmeFile
|
item.readmeFile
|
||||||
)}" data-hook-id="${escapeHtml(item.id)}">
|
)}" data-hook-id="${escapeHtml(item.id)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(
|
<div class="resource-title">${titleHtml}</div>
|
||||||
item.description || "No description"
|
<div class="resource-description">${escapeHtml(
|
||||||
)}</div>
|
item.description || "No description"
|
||||||
<div class="resource-meta">
|
)}</div>
|
||||||
${item.hooks
|
<div class="resource-meta">
|
||||||
.map(
|
${item.hooks
|
||||||
(hook) =>
|
.map(
|
||||||
`<span class="resource-tag tag-hook">${escapeHtml(
|
(hook) =>
|
||||||
hook
|
`<span class="resource-tag tag-hook">${escapeHtml(
|
||||||
)}</span>`
|
hook
|
||||||
)
|
)}</span>`
|
||||||
.join("")}
|
)
|
||||||
${item.tags
|
.join("")}
|
||||||
.map(
|
${item.tags
|
||||||
(tag) =>
|
.map(
|
||||||
`<span class="resource-tag tag-tag">${escapeHtml(
|
(tag) =>
|
||||||
tag
|
`<span class="resource-tag tag-tag">${escapeHtml(
|
||||||
)}</span>`
|
tag
|
||||||
)
|
)}</span>`
|
||||||
.join("")}
|
)
|
||||||
${
|
.join("")}
|
||||||
item.assets.length > 0
|
${
|
||||||
? `<span class="resource-tag tag-assets">${
|
item.assets.length > 0
|
||||||
item.assets.length
|
? `<span class="resource-tag tag-assets">${
|
||||||
} asset${item.assets.length === 1 ? "" : "s"}</span>`
|
item.assets.length
|
||||||
: ""
|
} asset${item.assets.length === 1 ? "" : "s"}</span>`
|
||||||
}
|
: ""
|
||||||
${getLastUpdatedHtml(item.lastUpdated)}
|
}
|
||||||
|
${getLastUpdatedHtml(item.lastUpdated)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
<button class="btn btn-primary download-hook-btn" data-hook-id="${escapeHtml(
|
<button class="btn btn-primary download-hook-btn" data-hook-id="${escapeHtml(
|
||||||
item.id
|
item.id
|
||||||
@@ -108,7 +110,7 @@ export function renderHooksHtml(
|
|||||||
item.path
|
item.path
|
||||||
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|||||||
@@ -55,43 +55,120 @@ export async function initHomepage(): Promise<void> {
|
|||||||
const resultsDiv = document.getElementById('search-results');
|
const resultsDiv = document.getElementById('search-results');
|
||||||
|
|
||||||
if (searchInput && resultsDiv) {
|
if (searchInput && resultsDiv) {
|
||||||
|
const statusEl = document.getElementById("global-search-status");
|
||||||
|
|
||||||
|
const hideResults = (): void => {
|
||||||
|
resultsDiv.classList.add("hidden");
|
||||||
|
};
|
||||||
|
|
||||||
|
const showResults = (): void => {
|
||||||
|
resultsDiv.classList.remove("hidden");
|
||||||
|
};
|
||||||
|
|
||||||
|
const getResultButtons = (): HTMLButtonElement[] =>
|
||||||
|
Array.from(
|
||||||
|
resultsDiv.querySelectorAll<HTMLButtonElement>(".search-result")
|
||||||
|
);
|
||||||
|
|
||||||
|
const openResult = (resultEl: HTMLElement): void => {
|
||||||
|
const path = resultEl.dataset.path;
|
||||||
|
const type = resultEl.dataset.type;
|
||||||
|
if (path && type) {
|
||||||
|
hideResults();
|
||||||
|
openFileModal(path, type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
searchInput.addEventListener('input', debounce(() => {
|
searchInput.addEventListener('input', debounce(() => {
|
||||||
const query = searchInput.value.trim();
|
const query = searchInput.value.trim();
|
||||||
if (query.length < 2) {
|
if (query.length < 2) {
|
||||||
resultsDiv.classList.add('hidden');
|
resultsDiv.innerHTML = '';
|
||||||
|
if (statusEl) {
|
||||||
|
statusEl.textContent = '';
|
||||||
|
}
|
||||||
|
hideResults();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = search.search(query).slice(0, 10);
|
const results = search.search(query).slice(0, 10);
|
||||||
if (results.length === 0) {
|
if (results.length === 0) {
|
||||||
resultsDiv.innerHTML = '<div class="search-result-empty">No results found</div>';
|
resultsDiv.innerHTML = '<div class="search-result-empty">No results found</div>';
|
||||||
|
if (statusEl) {
|
||||||
|
statusEl.textContent = 'No results found.';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resultsDiv.innerHTML = results.map(item => `
|
resultsDiv.innerHTML = results.map(item => `
|
||||||
<div class="search-result" data-path="${escapeHtml(item.path)}" data-type="${escapeHtml(item.type)}">
|
<button type="button" class="search-result" data-path="${escapeHtml(item.path)}" data-type="${escapeHtml(item.type)}">
|
||||||
<span class="search-result-type">${getResourceIcon(item.type)}</span>
|
<span class="search-result-type">${getResourceIcon(item.type)}</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="search-result-title">${search.highlight(item.title, query)}</div>
|
<div class="search-result-title">${search.highlight(item.title, query)}</div>
|
||||||
<div class="search-result-description">${truncate(item.description, 60)}</div>
|
<div class="search-result-description">${truncate(item.description, 60)}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// Add click handlers
|
if (statusEl) {
|
||||||
resultsDiv.querySelectorAll('.search-result').forEach(el => {
|
statusEl.textContent = `${results.length} result${results.length === 1 ? '' : 's'} available.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getResultButtons().forEach((el, index, buttons) => {
|
||||||
el.addEventListener('click', () => {
|
el.addEventListener('click', () => {
|
||||||
const path = (el as HTMLElement).dataset.path;
|
openResult(el);
|
||||||
const type = (el as HTMLElement).dataset.type;
|
});
|
||||||
if (path && type) openFileModal(path, type);
|
|
||||||
|
el.addEventListener("keydown", (event) => {
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowDown":
|
||||||
|
event.preventDefault();
|
||||||
|
buttons[(index + 1) % buttons.length]?.focus();
|
||||||
|
break;
|
||||||
|
case "ArrowUp":
|
||||||
|
event.preventDefault();
|
||||||
|
if (index === 0) {
|
||||||
|
searchInput.focus();
|
||||||
|
} else {
|
||||||
|
buttons[index - 1]?.focus();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Home":
|
||||||
|
event.preventDefault();
|
||||||
|
buttons[0]?.focus();
|
||||||
|
break;
|
||||||
|
case "End":
|
||||||
|
event.preventDefault();
|
||||||
|
buttons[buttons.length - 1]?.focus();
|
||||||
|
break;
|
||||||
|
case "Escape":
|
||||||
|
event.preventDefault();
|
||||||
|
hideResults();
|
||||||
|
searchInput.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resultsDiv.classList.remove('hidden');
|
|
||||||
|
showResults();
|
||||||
}, 200));
|
}, 200));
|
||||||
|
|
||||||
|
searchInput.addEventListener("keydown", (event) => {
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
const firstResult = getResultButtons()[0];
|
||||||
|
if (firstResult) {
|
||||||
|
event.preventDefault();
|
||||||
|
firstResult.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
hideResults();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Close results when clicking outside
|
// Close results when clicking outside
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
if (!searchInput.contains(e.target as Node) && !resultsDiv.contains(e.target as Node)) {
|
if (!searchInput.contains(e.target as Node) && !resultsDiv.contains(e.target as Node)) {
|
||||||
resultsDiv.classList.add('hidden');
|
hideResults();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,17 +61,19 @@ export function renderInstructionsHtml(
|
|||||||
: escapeHtml(item.title);
|
: escapeHtml(item.title);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
<div class="resource-title">${titleHtml}</div>
|
||||||
<div class="resource-meta">
|
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||||
${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''}
|
<div class="resource-meta">
|
||||||
${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''}
|
${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''}
|
||||||
${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''}
|
${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''}
|
||||||
${getLastUpdatedHtml(item.lastUpdated)}
|
${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''}
|
||||||
|
${getLastUpdatedHtml(item.lastUpdated)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
${getInstallDropdownHtml('instructions', item.path, true)}
|
${getInstallDropdownHtml('instructions', item.path, true)}
|
||||||
${getActionButtonsHtml(item.path, true)}
|
${getActionButtonsHtml(item.path, true)}
|
||||||
@@ -79,7 +81,7 @@ export function renderInstructionsHtml(
|
|||||||
GitHub
|
GitHub
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|||||||
@@ -70,21 +70,23 @@ export function renderPluginsHtml(
|
|||||||
: escapeHtml(item.name);
|
: escapeHtml(item.name);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}">
|
<article class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
<div class="resource-title">${titleHtml}</div>
|
||||||
<div class="resource-meta">
|
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||||
${metaTag}
|
<div class="resource-meta">
|
||||||
${authorTag}
|
${metaTag}
|
||||||
${item.tags?.slice(0, 4).map((tag) => `<span class="resource-tag">${escapeHtml(tag)}</span>`).join('') || ''}
|
${authorTag}
|
||||||
${item.tags && item.tags.length > 4 ? `<span class="resource-tag">+${item.tags.length - 4} more</span>` : ''}
|
${item.tags?.slice(0, 4).map((tag) => `<span class="resource-tag">${escapeHtml(tag)}</span>`).join('') || ''}
|
||||||
|
${item.tags && item.tags.length > 4 ? `<span class="resource-tag">+${item.tags.length - 4} more</span>` : ''}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
<a href="${githubHref}" class="btn btn-secondary" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()" title="${isExternal ? 'View repository' : 'View on GitHub'}">${isExternal ? 'Repository' : 'GitHub'}</a>
|
<a href="${githubHref}" class="btn btn-secondary" target="_blank" rel="noopener noreferrer" onclick="event.stopPropagation()" title="${isExternal ? 'View repository' : 'View on GitHub'}">${isExternal ? 'Repository' : 'GitHub'}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|||||||
@@ -65,31 +65,33 @@ export function renderSkillsHtml(
|
|||||||
: escapeHtml(item.title);
|
: escapeHtml(item.title);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item" data-path="${escapeHtml(
|
<article class="resource-item" data-path="${escapeHtml(
|
||||||
item.skillFile
|
item.skillFile
|
||||||
)}" data-skill-id="${escapeHtml(item.id)}">
|
)}" data-skill-id="${escapeHtml(item.id)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(
|
<div class="resource-title">${titleHtml}</div>
|
||||||
item.description || "No description"
|
<div class="resource-description">${escapeHtml(
|
||||||
)}</div>
|
item.description || "No description"
|
||||||
<div class="resource-meta">
|
)}</div>
|
||||||
<span class="resource-tag tag-category">${escapeHtml(
|
<div class="resource-meta">
|
||||||
item.category
|
<span class="resource-tag tag-category">${escapeHtml(
|
||||||
)}</span>
|
item.category
|
||||||
${
|
)}</span>
|
||||||
item.hasAssets
|
${
|
||||||
? `<span class="resource-tag tag-assets">${
|
item.hasAssets
|
||||||
item.assetCount
|
? `<span class="resource-tag tag-assets">${
|
||||||
} asset${item.assetCount === 1 ? "" : "s"}</span>`
|
item.assetCount
|
||||||
: ""
|
} asset${item.assetCount === 1 ? "" : "s"}</span>`
|
||||||
}
|
: ""
|
||||||
<span class="resource-tag">${item.files.length} file${
|
}
|
||||||
item.files.length === 1 ? "" : "s"
|
<span class="resource-tag">${item.files.length} file${
|
||||||
}</span>
|
item.files.length === 1 ? "" : "s"
|
||||||
${getLastUpdatedHtml(item.lastUpdated)}
|
}</span>
|
||||||
|
${getLastUpdatedHtml(item.lastUpdated)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
<button class="btn btn-primary download-skill-btn" data-skill-id="${escapeHtml(
|
<button class="btn btn-primary download-skill-btn" data-skill-id="${escapeHtml(
|
||||||
item.id
|
item.id
|
||||||
@@ -104,7 +106,7 @@ export function renderSkillsHtml(
|
|||||||
item.path
|
item.path
|
||||||
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|||||||
@@ -56,20 +56,22 @@ export function renderWorkflowsHtml(
|
|||||||
: escapeHtml(item.title);
|
: escapeHtml(item.title);
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||||
<div class="resource-info">
|
<button type="button" class="resource-preview">
|
||||||
<div class="resource-title">${titleHtml}</div>
|
<div class="resource-info">
|
||||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
<div class="resource-title">${titleHtml}</div>
|
||||||
<div class="resource-meta">
|
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||||
${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')}
|
<div class="resource-meta">
|
||||||
${getLastUpdatedHtml(item.lastUpdated)}
|
${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')}
|
||||||
|
${getLastUpdatedHtml(item.lastUpdated)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</button>
|
||||||
<div class="resource-actions">
|
<div class="resource-actions">
|
||||||
${getActionButtonsHtml(item.path)}
|
${getActionButtonsHtml(item.path)}
|
||||||
<a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
<a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|||||||
@@ -275,11 +275,16 @@ body:has(#main-content) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
|
width: 100%;
|
||||||
padding: 14px 18px;
|
padding: 14px 18px;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-bottom: 1px solid var(--color-glass-border);
|
border-bottom: 1px solid var(--color-glass-border);
|
||||||
transition: all var(--transition);
|
transition: all var(--transition);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result:last-child,
|
.search-result:last-child,
|
||||||
@@ -288,10 +293,16 @@ body:has(#main-content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-result:hover,
|
.search-result:hover,
|
||||||
|
.search-result:focus-visible,
|
||||||
.search-result-item:hover {
|
.search-result-item:hover {
|
||||||
background: var(--color-bg-tertiary);
|
background: var(--color-bg-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-result:focus-visible {
|
||||||
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
.search-result-type {
|
.search-result-type {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -1464,7 +1475,6 @@ body:has(#main-content) {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
transition: all var(--transition);
|
transition: all var(--transition);
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1481,21 +1491,45 @@ body:has(#main-content) {
|
|||||||
transition: opacity var(--transition), left var(--transition);
|
transition: opacity var(--transition), left var(--transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-item:hover {
|
.resource-item:hover,
|
||||||
|
.resource-item:focus-within {
|
||||||
background: var(--color-card-hover);
|
background: var(--color-card-hover);
|
||||||
transform: translateX(4px);
|
transform: translateX(4px);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
border-radius: 0px var(--border-radius-lg) var(--border-radius-lg) 0px;
|
border-radius: 0px var(--border-radius-lg) var(--border-radius-lg) 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-item:hover::before {
|
.resource-item:hover::before,
|
||||||
|
.resource-item:focus-within::before {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
left: -4px;
|
left: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resource-preview {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-preview:focus-visible {
|
||||||
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: 4px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
.resource-info {
|
.resource-info {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resource-title {
|
.resource-title {
|
||||||
|
|||||||
Reference in New Issue
Block a user