mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-15 05:35:11 +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:
@@ -61,44 +61,46 @@ export function renderAgentsHtml(
|
||||
: escapeHtml(item.title);
|
||||
|
||||
return `
|
||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</div>
|
||||
<div class="resource-meta">
|
||||
${
|
||||
item.model
|
||||
? `<span class="resource-tag tag-model">${escapeHtml(
|
||||
item.model
|
||||
)}</span>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
item.tools
|
||||
?.slice(0, 3)
|
||||
.map(
|
||||
(tool) =>
|
||||
`<span class="resource-tag">${escapeHtml(tool)}</span>`
|
||||
)
|
||||
.join("") || ""
|
||||
}
|
||||
${
|
||||
item.tools && item.tools.length > 3
|
||||
? `<span class="resource-tag">+${
|
||||
item.tools.length - 3
|
||||
} more</span>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
item.hasHandoffs
|
||||
? `<span class="resource-tag tag-handoffs">handoffs</span>`
|
||||
: ""
|
||||
}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</div>
|
||||
<div class="resource-meta">
|
||||
${
|
||||
item.model
|
||||
? `<span class="resource-tag tag-model">${escapeHtml(
|
||||
item.model
|
||||
)}</span>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
item.tools
|
||||
?.slice(0, 3)
|
||||
.map(
|
||||
(tool) =>
|
||||
`<span class="resource-tag">${escapeHtml(tool)}</span>`
|
||||
)
|
||||
.join("") || ""
|
||||
}
|
||||
${
|
||||
item.tools && item.tools.length > 3
|
||||
? `<span class="resource-tag">+${
|
||||
item.tools.length - 3
|
||||
} more</span>`
|
||||
: ""
|
||||
}
|
||||
${
|
||||
item.hasHandoffs
|
||||
? `<span class="resource-tag tag-handoffs">handoffs</span>`
|
||||
: ""
|
||||
}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="resource-actions">
|
||||
${getInstallDropdownHtml(resourceType, item.path, true)}
|
||||
${getActionButtonsHtml(item.path, true)}
|
||||
@@ -108,7 +110,7 @@ export function renderAgentsHtml(
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
@@ -59,41 +59,43 @@ export function renderHooksHtml(
|
||||
: escapeHtml(item.title);
|
||||
|
||||
return `
|
||||
<div class="resource-item" data-path="${escapeHtml(
|
||||
<article class="resource-item" data-path="${escapeHtml(
|
||||
item.readmeFile
|
||||
)}" data-hook-id="${escapeHtml(item.id)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</div>
|
||||
<div class="resource-meta">
|
||||
${item.hooks
|
||||
.map(
|
||||
(hook) =>
|
||||
`<span class="resource-tag tag-hook">${escapeHtml(
|
||||
hook
|
||||
)}</span>`
|
||||
)
|
||||
.join("")}
|
||||
${item.tags
|
||||
.map(
|
||||
(tag) =>
|
||||
`<span class="resource-tag tag-tag">${escapeHtml(
|
||||
tag
|
||||
)}</span>`
|
||||
)
|
||||
.join("")}
|
||||
${
|
||||
item.assets.length > 0
|
||||
? `<span class="resource-tag tag-assets">${
|
||||
item.assets.length
|
||||
} asset${item.assets.length === 1 ? "" : "s"}</span>`
|
||||
: ""
|
||||
}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
)}" data-hook-id="${escapeHtml(item.id)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</div>
|
||||
<div class="resource-meta">
|
||||
${item.hooks
|
||||
.map(
|
||||
(hook) =>
|
||||
`<span class="resource-tag tag-hook">${escapeHtml(
|
||||
hook
|
||||
)}</span>`
|
||||
)
|
||||
.join("")}
|
||||
${item.tags
|
||||
.map(
|
||||
(tag) =>
|
||||
`<span class="resource-tag tag-tag">${escapeHtml(
|
||||
tag
|
||||
)}</span>`
|
||||
)
|
||||
.join("")}
|
||||
${
|
||||
item.assets.length > 0
|
||||
? `<span class="resource-tag tag-assets">${
|
||||
item.assets.length
|
||||
} asset${item.assets.length === 1 ? "" : "s"}</span>`
|
||||
: ""
|
||||
}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="resource-actions">
|
||||
<button class="btn btn-primary download-hook-btn" data-hook-id="${escapeHtml(
|
||||
item.id
|
||||
@@ -108,7 +110,7 @@ export function renderHooksHtml(
|
||||
item.path
|
||||
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
@@ -55,43 +55,120 @@ export async function initHomepage(): Promise<void> {
|
||||
const resultsDiv = document.getElementById('search-results');
|
||||
|
||||
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(() => {
|
||||
const query = searchInput.value.trim();
|
||||
if (query.length < 2) {
|
||||
resultsDiv.classList.add('hidden');
|
||||
resultsDiv.innerHTML = '';
|
||||
if (statusEl) {
|
||||
statusEl.textContent = '';
|
||||
}
|
||||
hideResults();
|
||||
return;
|
||||
}
|
||||
|
||||
const results = search.search(query).slice(0, 10);
|
||||
if (results.length === 0) {
|
||||
resultsDiv.innerHTML = '<div class="search-result-empty">No results found</div>';
|
||||
if (statusEl) {
|
||||
statusEl.textContent = 'No results found.';
|
||||
}
|
||||
} else {
|
||||
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>
|
||||
<div>
|
||||
<div class="search-result-title">${search.highlight(item.title, query)}</div>
|
||||
<div class="search-result-description">${truncate(item.description, 60)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
`).join('');
|
||||
|
||||
// Add click handlers
|
||||
resultsDiv.querySelectorAll('.search-result').forEach(el => {
|
||||
if (statusEl) {
|
||||
statusEl.textContent = `${results.length} result${results.length === 1 ? '' : 's'} available.`;
|
||||
}
|
||||
|
||||
getResultButtons().forEach((el, index, buttons) => {
|
||||
el.addEventListener('click', () => {
|
||||
const path = (el as HTMLElement).dataset.path;
|
||||
const type = (el as HTMLElement).dataset.type;
|
||||
if (path && type) openFileModal(path, type);
|
||||
openResult(el);
|
||||
});
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
document.addEventListener('click', (e) => {
|
||||
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);
|
||||
|
||||
return `
|
||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''}
|
||||
${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''}
|
||||
${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${applyToText ? `<span class="resource-tag">applies to: ${escapeHtml(applyToText)}</span>` : ''}
|
||||
${item.extensions?.slice(0, 4).map((extension) => `<span class="resource-tag tag-extension">${escapeHtml(extension)}</span>`).join('') || ''}
|
||||
${item.extensions && item.extensions.length > 4 ? `<span class="resource-tag">+${item.extensions.length - 4} more</span>` : ''}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="resource-actions">
|
||||
${getInstallDropdownHtml('instructions', item.path, true)}
|
||||
${getActionButtonsHtml(item.path, true)}
|
||||
@@ -79,7 +81,7 @@ export function renderInstructionsHtml(
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
@@ -70,21 +70,23 @@ export function renderPluginsHtml(
|
||||
: escapeHtml(item.name);
|
||||
|
||||
return `
|
||||
<div class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${metaTag}
|
||||
${authorTag}
|
||||
${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>` : ''}
|
||||
<article class="resource-item${isExternal ? ' resource-item-external' : ''}" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${metaTag}
|
||||
${authorTag}
|
||||
${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>
|
||||
</button>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
@@ -65,31 +65,33 @@ export function renderSkillsHtml(
|
||||
: escapeHtml(item.title);
|
||||
|
||||
return `
|
||||
<div class="resource-item" data-path="${escapeHtml(
|
||||
<article class="resource-item" data-path="${escapeHtml(
|
||||
item.skillFile
|
||||
)}" data-skill-id="${escapeHtml(item.id)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</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>
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
)}" data-skill-id="${escapeHtml(item.id)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "No description"
|
||||
)}</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>
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="resource-actions">
|
||||
<button class="btn btn-primary download-skill-btn" data-skill-id="${escapeHtml(
|
||||
item.id
|
||||
@@ -104,7 +106,7 @@ export function renderSkillsHtml(
|
||||
item.path
|
||||
)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
@@ -56,20 +56,22 @@ export function renderWorkflowsHtml(
|
||||
: escapeHtml(item.title);
|
||||
|
||||
return `
|
||||
<div class="resource-item" data-path="${escapeHtml(item.path)}">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
<article class="resource-item" data-path="${escapeHtml(item.path)}" role="listitem">
|
||||
<button type="button" class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${titleHtml}</div>
|
||||
<div class="resource-description">${escapeHtml(item.description || 'No description')}</div>
|
||||
<div class="resource-meta">
|
||||
${item.triggers.map((trigger) => `<span class="resource-tag tag-trigger">${escapeHtml(trigger)}</span>`).join('')}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="resource-actions">
|
||||
${getActionButtonsHtml(item.path)}
|
||||
<a href="${getGitHubUrl(item.path)}" class="btn btn-secondary" target="_blank" onclick="event.stopPropagation()" title="View on GitHub">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
Reference in New Issue
Block a user