mirror of
https://github.com/github/awesome-copilot.git
synced 2026-06-17 05:01:19 +00:00
chore: publish from staged
This commit is contained in:
@@ -36,6 +36,17 @@ const initialItems = sortExtensions(extensionsData.items, 'title');
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-list" id="resource-list" role="list" set:html={renderExtensionsHtml(initialItems)}></div>
|
||||
<div id="extension-preview-modal" class="extension-preview-modal hidden" aria-hidden="true">
|
||||
<div class="extension-preview-dialog" role="dialog" aria-modal="true" aria-labelledby="extension-preview-title">
|
||||
<div class="extension-preview-header">
|
||||
<h3 id="extension-preview-title" class="extension-preview-title">Extension preview</h3>
|
||||
<button id="extension-preview-close" class="btn btn-secondary extension-preview-close" type="button" aria-label="Close preview">Close</button>
|
||||
</div>
|
||||
<div class="extension-preview-body">
|
||||
<img id="extension-preview-image" class="extension-preview-image" src="" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContributeCTA resourceType="extensions" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,11 @@ const base = import.meta.env.BASE_URL;
|
||||
Community-contributed agents, instructions, and skills to enhance your
|
||||
GitHub Copilot experience
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/github/awesome-copilot/" target="_blank" rel="noopener noreferrer">
|
||||
View the Awesome Copilot repository on GitHub
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -3,9 +3,15 @@ import { escapeHtml, getGitHubUrl, getLastUpdatedHtml } from "../utils";
|
||||
export interface RenderableExtension {
|
||||
id: string;
|
||||
name: string;
|
||||
path: string;
|
||||
ref: string;
|
||||
path?: string | null;
|
||||
ref?: string | null;
|
||||
description?: string;
|
||||
lastUpdated?: string | null;
|
||||
imageUrl?: string | null;
|
||||
assetPath?: string | null;
|
||||
installUrl?: string | null;
|
||||
sourceUrl?: string | null;
|
||||
external?: boolean;
|
||||
}
|
||||
|
||||
export type ExtensionSortOption = "title" | "lastUpdated";
|
||||
@@ -36,37 +42,62 @@ export function renderExtensionsHtml(items: RenderableExtension[]): string {
|
||||
}
|
||||
|
||||
return items
|
||||
.map(
|
||||
(item) => `
|
||||
.map((item) => {
|
||||
const installUrl =
|
||||
item.installUrl ||
|
||||
(item.path && item.ref
|
||||
? `https://github.com/github/awesome-copilot/tree/${item.ref}/${item.path.replace(
|
||||
/\\/g,
|
||||
"/"
|
||||
)}`
|
||||
: "");
|
||||
const sourceUrl =
|
||||
item.sourceUrl || (item.path ? getGitHubUrl(item.path) : "");
|
||||
|
||||
return `
|
||||
<article class="resource-item" role="listitem">
|
||||
<div class="resource-preview">
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${escapeHtml(item.name)}</div>
|
||||
<div class="resource-description">Canvas extension</div>
|
||||
<div class="resource-meta">
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${
|
||||
item.imageUrl
|
||||
? `<button type="button" class="resource-thumbnail-btn" data-preview-url="${escapeHtml(item.imageUrl)}" data-preview-alt="${escapeHtml(item.name)} preview" aria-label="Open ${escapeHtml(item.name)} preview">
|
||||
<img class="resource-thumbnail" src="${escapeHtml(item.imageUrl)}" alt="${escapeHtml(item.name)} preview" loading="lazy" />
|
||||
</button>`
|
||||
: `<div class="resource-thumbnail resource-thumbnail-placeholder" aria-hidden="true">Canvas</div>`
|
||||
}
|
||||
<div class="resource-info">
|
||||
<div class="resource-title">${escapeHtml(item.name)}</div>
|
||||
<div class="resource-description">${escapeHtml(
|
||||
item.description || "Canvas extension"
|
||||
)}</div>
|
||||
<div class="resource-meta">
|
||||
${
|
||||
item.external
|
||||
? '<span class="resource-tag">External</span>'
|
||||
: ""
|
||||
}
|
||||
${getLastUpdatedHtml(item.lastUpdated)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-actions">
|
||||
<button
|
||||
class="btn btn-primary btn-small copy-install-url-btn"
|
||||
data-install-url="${escapeHtml(
|
||||
`https://github.com/github/awesome-copilot/tree/${item.ref}/${item.path.replace(
|
||||
/\\/g,
|
||||
"/"
|
||||
)}`
|
||||
)}"
|
||||
data-install-url="${escapeHtml(installUrl)}"
|
||||
title="Copy install URL"
|
||||
${installUrl ? "" : "disabled"}
|
||||
>
|
||||
Install
|
||||
</button>
|
||||
<a href="${getGitHubUrl(
|
||||
item.path
|
||||
)}" class="btn btn-secondary btn-small" target="_blank" rel="noopener noreferrer" title="View on GitHub">GitHub</a>
|
||||
${
|
||||
sourceUrl
|
||||
? `<a href="${escapeHtml(
|
||||
sourceUrl
|
||||
)}" class="btn btn-secondary btn-small" target="_blank" rel="noopener noreferrer" title="View source">Source</a>`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
</article>
|
||||
`
|
||||
)
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
@@ -27,6 +27,30 @@ let allItems: Extension[] = [];
|
||||
let currentSort: ExtensionSortOption = "title";
|
||||
let actionHandlersReady = false;
|
||||
|
||||
function openPreviewModal(url: string, alt: string): void {
|
||||
const modal = document.getElementById("extension-preview-modal");
|
||||
const image = document.getElementById("extension-preview-image") as HTMLImageElement | null;
|
||||
const title = document.getElementById("extension-preview-title");
|
||||
|
||||
if (!modal || !image || !title) return;
|
||||
|
||||
image.src = url;
|
||||
image.alt = alt;
|
||||
title.textContent = alt.replace(/ preview$/i, "");
|
||||
modal.classList.remove("hidden");
|
||||
modal.setAttribute("aria-hidden", "false");
|
||||
document.body.style.overflow = "hidden";
|
||||
}
|
||||
|
||||
function closePreviewModal(): void {
|
||||
const modal = document.getElementById("extension-preview-modal");
|
||||
if (!modal) return;
|
||||
|
||||
modal.classList.add("hidden");
|
||||
modal.setAttribute("aria-hidden", "true");
|
||||
document.body.style.overflow = "";
|
||||
}
|
||||
|
||||
function applySortAndRender(): void {
|
||||
const countEl = document.getElementById("results-count");
|
||||
const results = sortExtensions(allItems, currentSort);
|
||||
@@ -49,6 +73,20 @@ function setupActionHandlers(list: HTMLElement | null): void {
|
||||
|
||||
list.addEventListener("click", async (event) => {
|
||||
const target = event.target as HTMLElement;
|
||||
const thumbnailButton = target.closest(
|
||||
".resource-thumbnail-btn"
|
||||
) as HTMLButtonElement | null;
|
||||
|
||||
if (thumbnailButton) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
openPreviewModal(
|
||||
thumbnailButton.dataset.previewUrl || "",
|
||||
thumbnailButton.dataset.previewAlt || "Extension preview"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const installButton = target.closest(
|
||||
".copy-install-url-btn"
|
||||
) as HTMLButtonElement | null;
|
||||
@@ -57,6 +95,10 @@ function setupActionHandlers(list: HTMLElement | null): void {
|
||||
|
||||
event.stopPropagation();
|
||||
const installUrl = installButton.dataset.installUrl || "";
|
||||
if (!installUrl) {
|
||||
showToast("No install URL available for this extension", "error");
|
||||
return;
|
||||
}
|
||||
const success = await copyToClipboard(installUrl);
|
||||
showToast(
|
||||
success ? "Install URL copied!" : "Failed to copy install URL",
|
||||
@@ -64,6 +106,27 @@ function setupActionHandlers(list: HTMLElement | null): void {
|
||||
);
|
||||
});
|
||||
|
||||
const modal = document.getElementById("extension-preview-modal");
|
||||
const closeButton = document.getElementById("extension-preview-close");
|
||||
|
||||
if (modal) {
|
||||
modal.addEventListener("click", (event) => {
|
||||
if (event.target === modal) {
|
||||
closePreviewModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener("click", closePreviewModal);
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape") {
|
||||
closePreviewModal();
|
||||
}
|
||||
});
|
||||
|
||||
actionHandlersReady = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1863,7 +1863,9 @@ body:has(#main-content) {
|
||||
.resource-preview {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -1874,7 +1876,56 @@ body:has(#main-content) {
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.resource-thumbnail-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.resource-thumbnail-btn:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 4px;
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.resource-thumbnail {
|
||||
width: clamp(120px, 24vw, 160px);
|
||||
aspect-ratio: 16 / 10;
|
||||
object-fit: cover;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-bg-tertiary);
|
||||
flex-shrink: 0;
|
||||
box-shadow: var(--shadow);
|
||||
transition: transform var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.resource-thumbnail-btn:hover .resource-thumbnail,
|
||||
.resource-thumbnail-btn:focus-visible .resource-thumbnail {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.resource-thumbnail-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
background:
|
||||
linear-gradient(135deg, rgba(133, 52, 243, 0.18), rgba(254, 76, 37, 0.08)),
|
||||
var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
.resource-preview:focus-visible {
|
||||
outline: 2px solid var(--color-accent);
|
||||
outline-offset: 4px;
|
||||
@@ -1930,6 +1981,72 @@ body:has(#main-content) {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.extension-preview-modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 100000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
background: rgba(5, 7, 15, 0.72);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.extension-preview-modal.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.extension-preview-dialog {
|
||||
width: min(100%, 980px);
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
background: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
border-radius: var(--border-radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.extension-preview-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 16px 18px 0;
|
||||
}
|
||||
|
||||
.extension-preview-title {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
color: var(--color-text-emphasis);
|
||||
}
|
||||
|
||||
.extension-preview-close {
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
.extension-preview-body {
|
||||
padding: 0 18px 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.extension-preview-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 920px;
|
||||
max-height: 72vh;
|
||||
object-fit: contain;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--color-glass-border);
|
||||
background: var(--color-bg-tertiary);
|
||||
}
|
||||
|
||||
/* Last Updated */
|
||||
.last-updated {
|
||||
font-size: 12px;
|
||||
@@ -2081,6 +2198,20 @@ body:has(#main-content) {
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.resource-preview {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.resource-thumbnail-btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.resource-thumbnail {
|
||||
width: min(100%, 320px);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.resource-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user