chore: publish from main

This commit is contained in:
github-actions[bot]
2026-07-10 03:24:21 +00:00
parent 9a8e446f02
commit 51b6fa0253
56 changed files with 4496 additions and 992 deletions
+116
View File
@@ -0,0 +1,116 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import agentsData from "../../../public/data/agents.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Main from "../../components/pages/Main.astro";
import InstallButtons from "../../components/pages/InstallButtons.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import { loadDetailPage } from "../../lib/detail-page";
interface AgentItem extends HeaderItem {
id: string;
title: string;
description?: string;
model?: string | string[];
tools?: string[];
hasHandoffs?: boolean;
handoffs?: string[];
mcpServers?: string[];
path: string;
filename?: string;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (agentsData.items as AgentItem[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: AgentItem };
const {
vscodeUrl,
insidersUrl,
githubUrl,
markdownHtml,
frontmatterText,
rawMarkdown,
lastUpdated,
} = loadDetailPage(item, "agent");
const models = Array.isArray(item.model)
? item.model
: item.model
? [item.model]
: [];
const tools = item.tools ?? [];
const handoffs = item.handoffs ?? [];
const mcpServers = item.mcpServers ?? [];
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page"
data-resource-detail
data-path={item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Agents", href: "/agents/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<Main markdownHtml={markdownHtml} githubUrl={githubUrl} />
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={[
{ title: "Models", items: models },
{ title: "Handoffs", items: handoffs },
{ title: "MCP Servers", items: mcpServers },
{ title: "Tools", items: tools },
]}
>
<InstallButtons
vscodeUrl={vscodeUrl}
insidersUrl={insidersUrl}
rawMarkdown={rawMarkdown}
slot="install"
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
-2
View File
@@ -5,7 +5,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import { renderAgentsHtml, sortAgents } from '../scripts/pages/agents-render';
const initialItems = sortAgents(agentsData.items, 'title');
@@ -43,7 +42,6 @@ const initialItems = sortAgents(agentsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="agents.json" data={agentsData} />
<script>
+301
View File
@@ -0,0 +1,301 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import extensionsData from "../../../public/data/extensions.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import PluginInstall from "../../components/pages/PluginInstall.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
import { sanitizeHttpUrl } from "../../lib/external-source";
const GITHUB_TREE = "https://github.com/github/awesome-copilot/tree/main";
interface ExtensionScreenshot {
path?: string | null;
type?: string | null;
}
interface ExtensionAuthor {
name: string;
url?: string;
}
interface ExtensionEntry extends HeaderItem {
id: string;
canvasId?: string;
extensionId?: string;
extensionName?: string;
pluginName?: string | null;
name: string;
title?: string;
version?: string | null;
readmeFile?: string | null;
description?: string;
path?: string | null;
ref?: string | null;
lastUpdated?: string | null;
screenshots?: {
icon?: ExtensionScreenshot | null;
gallery?: ExtensionScreenshot | ExtensionScreenshot[] | null;
} | null;
imageUrl?: string | null;
assetPath?: string | null;
installUrl?: string | null;
installCommand?: string | null;
sourceUrl?: string | null;
external?: boolean;
author?: ExtensionAuthor | null;
keywords?: string[];
}
export function getStaticPaths() {
return (extensionsData.items as ExtensionEntry[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: ExtensionEntry };
const headerItem = { ...item, title: item.title ?? item.name };
const isExternal = item.external === true;
// Allow only http(s) URLs from external/generated data in href/src contexts;
// unsafe values collapse to "" so downstream truthiness guards still hold.
const safeUrl = (value?: string | null): string => {
const sanitized = sanitizeHttpUrl(value);
return sanitized === "#" ? "" : sanitized;
};
const readmePath = item.readmeFile ?? null;
const { markdownHtml, frontmatterText, rawMarkdown } = readmePath
? readResourceMarkdown(readmePath)
: { markdownHtml: "", frontmatterText: "", rawMarkdown: "" };
const lastUpdated = formatLastUpdated(item.lastUpdated ?? null);
// Resolve preview images (icon + gallery), converting repo paths to raw URLs.
function toRawAssetUrl(assetPath?: string | null): string {
if (!assetPath) return "";
if (/^https?:\/\//i.test(assetPath)) return assetPath;
if (!item.ref) return "";
return `https://raw.githubusercontent.com/github/awesome-copilot/${item.ref}/${assetPath.replace(
/\\/g,
"/"
)}`;
}
function normalizeScreenshotEntries(
value: ExtensionScreenshot | ExtensionScreenshot[] | null | undefined
): ExtensionScreenshot[] {
if (!value) return [];
return Array.isArray(value) ? value.filter(Boolean) : [value];
}
const galleryImages: string[] = (() => {
const images: string[] = [];
if (item.imageUrl) images.push(item.imageUrl);
const iconUrl = toRawAssetUrl(item.screenshots?.icon?.path);
if (iconUrl) images.push(iconUrl);
for (const entry of normalizeScreenshotEntries(item.screenshots?.gallery)) {
const url = toRawAssetUrl(entry.path);
if (url) images.push(url);
}
return Array.from(new Set(images.map(safeUrl).filter(Boolean)));
})();
const installUrl = safeUrl(
item.installUrl ||
(item.path && item.ref
? `https://github.com/github/awesome-copilot/tree/${item.ref}/${item.path.replace(
/\\/g,
"/"
)}`
: "")
);
const sourceUrl = safeUrl(item.sourceUrl);
const installCommand =
item.installCommand ||
(item.pluginName
? `copilot plugin install ${item.pluginName}@awesome-copilot`
: "");
const githubUrl = isExternal
? sourceUrl || installUrl || GITHUB_TREE
: item.path
? `${GITHUB_TREE}/${item.path}`
: installUrl || GITHUB_TREE;
// Sidebar "Source" shows item.path; external extensions have no repo path.
const sidebarItem =
isExternal && (sourceUrl || installUrl)
? { ...item, path: sourceUrl || installUrl }
: item;
const shortHash = (value: string) =>
/^[0-9a-f]{40}$/i.test(value) ? value.slice(0, 7) : value;
const chips: {
title: string;
items: string[];
filterBase?: string;
filterParam?: string;
}[] = [];
if (item.version) {
chips.push({ title: "Version", items: [item.version] });
}
if (item.canvasId) {
chips.push({ title: "Canvas ID", items: [item.canvasId] });
}
if (item.keywords && item.keywords.length > 0) {
chips.push({
title: "Keywords",
items: item.keywords,
filterBase: "/extensions/",
filterParam: "keyword",
});
}
if (item.author?.name) {
chips.push({ title: "Author", items: [item.author.name] });
}
if (!isExternal && item.ref) {
chips.push({ title: "Commit", items: [shortHash(item.ref)] });
}
---
<StarlightPage
frontmatter={{
title: item.title ?? item.name,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page extension-detail-page"
data-resource-detail
data-path={item.readmeFile ?? item.path ?? ""}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Canvas Extensions", href: "/extensions/" },
]}
title={item.title ?? item.name}
/>
<DetailHeader item={headerItem} />
<div class="detail-layout">
<div class="detail-main">
{
galleryImages.length > 0 && (
<section class="detail-section extension-gallery">
<h2>Preview</h2>
<div class="extension-gallery-main">
<img
id="extension-gallery-image"
src={galleryImages[0]}
alt={`${item.name} preview`}
loading="eager"
/>
</div>
{galleryImages.length > 1 && (
<div class="extension-gallery-thumbs" role="list">
{galleryImages.map((url, index) => (
<button
type="button"
class={`extension-gallery-thumb${index === 0 ? " active" : ""}`}
data-gallery-url={url}
role="listitem"
aria-label={`Show image ${index + 1}`}
aria-current={index === 0 ? "true" : undefined}
>
<img src={url} alt={`Gallery image ${index + 1}`} loading="lazy" />
</button>
))}
</div>
)}
</section>
)
}
{
markdownHtml ? (
<section class="detail-section" aria-labelledby="doc-heading">
<h2 id="doc-heading">Documentation</h2>
<div class="article-content" set:html={markdownHtml} />
</section>
) : (
<section class="detail-section">
<h2>About</h2>
<p>{item.description}</p>
<p class="detail-empty">
{isExternal
? "This is a third-party canvas extension hosted outside this repository."
: "No README is bundled with this extension."}{" "}
View it{" "}
<a href={githubUrl} target="_blank" rel="noopener">
on GitHub
</a>
.
</p>
</section>
)
}
</div>
<Sidebar
item={sidebarItem}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
{
isExternal ? (
<div class="skill-install" slot="install">
<p class="skill-install-label">Install this extension</p>
<p class="skill-install-note">
This extension is maintained in an external repository.
</p>
{installUrl && (
<button
type="button"
class="btn btn-secondary skill-install-url-btn"
data-action="copy-install-url"
data-install-url={installUrl}
>
Copy install URL
</button>
)}
</div>
) : (
<PluginInstall
slot="install"
pluginId={item.pluginName ?? item.id}
installCommand={installCommand || null}
label="Install"
/>
)
}
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
import "../../scripts/pages/extension-gallery";
</script>
</StarlightPage>
-2
View File
@@ -5,7 +5,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import { renderExtensionsHtml, sortExtensions } from '../scripts/pages/extensions-render';
const initialItems = sortExtensions(extensionsData.items, 'title');
@@ -53,7 +52,6 @@ const initialItems = sortExtensions(extensionsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="extensions.json" data={extensionsData} />
<script>
+159
View File
@@ -0,0 +1,159 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import hooksData from "../../../public/data/hooks.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import FileBrowser from "../../components/pages/FileBrowser.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
const GITHUB_BASE = "https://github.com/github/awesome-copilot/blob/main";
const GITHUB_TREE_BASE = "https://github.com/github/awesome-copilot/tree/main";
interface HookFile {
name: string;
path: string;
size?: number;
}
interface HookItem extends HeaderItem {
id: string;
title: string;
description?: string;
path: string;
readmeFile: string;
readmeFileName?: string;
files: HookFile[];
hooks: string[];
tags: string[];
assets: string[];
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (hooksData.items as HookItem[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: HookItem };
const { markdownHtml, frontmatterText, rawMarkdown } = readResourceMarkdown(
item.readmeFile
);
const lastUpdated = formatLastUpdated(item.lastUpdated);
const githubUrl = `${GITHUB_TREE_BASE}/${item.path}`;
const readmeFileName = item.readmeFileName ?? "README.md";
const fileCount = item.files.length;
const chips: {
title: string;
items: string[];
filterBase?: string;
filterParam?: string;
}[] = [];
if (item.hooks.length > 0) {
chips.push({ title: "Hook events", items: item.hooks });
}
chips.push({
title: "Files",
items: [`${fileCount} file${fileCount === 1 ? "" : "s"}`],
});
if (item.tags.length > 0) {
chips.push({
title: "Tags",
items: item.tags,
filterBase: "/hooks/",
filterParam: "tag",
});
}
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page hook-detail-page"
data-file-browser-page
data-bundle-id={item.id}
data-path={item.readmeFile}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Hooks", href: "/hooks/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<div class="detail-main">
<FileBrowser
files={item.files}
primaryPath={item.readmeFile}
primaryName={readmeFileName}
primaryHtml={markdownHtml}
githubBase={GITHUB_BASE}
/>
</div>
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<div class="skill-install" slot="install">
<p class="skill-install-label">Install this hook</p>
<p class="skill-install-note">
Hooks are multi-file bundles with no CLI installer. Download the
ZIP and copy the files into your project's hooks directory.
</p>
<button
type="button"
class="btn btn-primary skill-download-btn"
data-action="download-zip"
>
<svg
viewBox="0 0 16 16"
width="16"
height="16"
fill="currentColor"
aria-hidden="true"
><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><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"
></path></svg
>
Download ZIP
</button>
</div>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/file-browser";
</script>
</StarlightPage>
-2
View File
@@ -4,7 +4,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import PageHeader from '../components/PageHeader.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import hooksData from '../../public/data/hooks.json';
import { renderHooksHtml, sortHooks } from '../scripts/pages/hooks-render';
@@ -48,7 +47,6 @@ const initialItems = sortHooks(hooksData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="hooks.json" data={hooksData} />
<script>
+115
View File
@@ -0,0 +1,115 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import instructionsData from "../../../public/data/instructions.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Main from "../../components/pages/Main.astro";
import InstallButtons from "../../components/pages/InstallButtons.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import { loadDetailPage } from "../../lib/detail-page";
interface InstructionItem extends HeaderItem {
id: string;
title: string;
description?: string;
applyTo?: string | string[] | null;
applyToPatterns?: string[];
extensions?: string[];
path: string;
filename?: string;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (instructionsData.items as InstructionItem[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: InstructionItem };
const {
vscodeUrl,
insidersUrl,
githubUrl,
markdownHtml,
frontmatterText,
rawMarkdown,
lastUpdated,
} = loadDetailPage(item, "instruction");
const applyToList = Array.isArray(item.applyTo)
? item.applyTo
: item.applyTo
? [item.applyTo]
: (item.applyToPatterns ?? []);
const extensions = item.extensions ?? [];
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page"
data-resource-detail
data-path={item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Instructions", href: "/instructions/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<Main markdownHtml={markdownHtml} githubUrl={githubUrl} />
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={[
{ title: "Applies to", items: applyToList },
{
title: "Extensions",
items: extensions,
filterBase: "/instructions/",
filterParam: "extension",
},
]}
>
<InstallButtons
vscodeUrl={vscodeUrl}
insidersUrl={insidersUrl}
rawMarkdown={rawMarkdown}
slot="install"
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
-2
View File
@@ -5,7 +5,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import { renderInstructionsHtml, sortInstructions } from '../scripts/pages/instructions-render';
const initialItems = sortInstructions(instructionsData.items, 'title');
@@ -48,7 +47,6 @@ const initialItems = sortInstructions(instructionsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="instructions.json" data={instructionsData} />
<script>
+235
View File
@@ -0,0 +1,235 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import pluginsData from "../../../public/data/plugins.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import IncludedItems from "../../components/pages/IncludedItems.astro";
import PluginInstall from "../../components/pages/PluginInstall.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
import { externalRepoUrl } from "../../lib/external-source";
const GITHUB_TREE = "https://github.com/github/awesome-copilot/tree/main";
interface PluginItemRef {
kind: string;
path: string;
title?: string;
detailUrl?: string | null;
}
interface PluginAuthor {
name: string;
url?: string;
}
interface PluginSource {
source: string;
repo?: string;
path?: string;
ref?: string;
sha?: string;
}
interface PluginEntry extends HeaderItem {
id: string;
name: string;
title?: string;
description?: string;
path: string;
readmeFile?: string | null;
version?: string | null;
tags?: string[];
itemCount: number;
items: PluginItemRef[];
external?: boolean;
generatedFromExtension?: boolean;
repository?: string | null;
homepage?: string | null;
author?: PluginAuthor | null;
source?: PluginSource | null;
license?: string | null;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (pluginsData.items as PluginEntry[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: PluginEntry };
// DetailHeader reads item.title; plugins only have a name.
const headerItem = { ...item, title: item.title ?? item.name };
const isExternal = item.external === true;
const readmePath = item.readmeFile ?? null;
const { markdownHtml, frontmatterText, rawMarkdown } = readmePath
? readResourceMarkdown(readmePath)
: { markdownHtml: "", frontmatterText: "", rawMarkdown: "" };
const lastUpdated = formatLastUpdated(item.lastUpdated);
function pluginRepoUrl(): string {
return externalRepoUrl(item.source, [
item.repository,
item.homepage,
GITHUB_TREE,
]);
}
const githubUrl = isExternal
? pluginRepoUrl()
: `${GITHUB_TREE}/${item.path}`;
// Install wiring
const externalSourceRef = item.source?.repo || item.repository || "";
const installCommand = isExternal
? null
: `copilot plugin install ${item.id}@awesome-copilot`;
// The sidebar "Source" shows item.path; for external plugins that repo-relative
// path doesn't exist here, so surface the upstream source reference instead.
const sidebarItem =
isExternal && externalSourceRef
? { ...item, path: externalSourceRef }
: item;
const chips: {
title: string;
items: string[];
filterBase?: string;
filterParam?: string;
}[] = [];
if (item.version) {
chips.push({ title: "Version", items: [item.version] });
}
if (item.tags && item.tags.length > 0) {
chips.push({
title: "Tags",
items: item.tags,
filterBase: "/plugins/",
filterParam: "tag",
});
}
if (!isExternal && item.itemCount > 0) {
chips.push({
title: "Bundled items",
items: [`${item.itemCount} item${item.itemCount === 1 ? "" : "s"}`],
});
}
if (isExternal && item.author?.name) {
chips.push({ title: "Author", items: [item.author.name] });
}
if (isExternal && item.license) {
chips.push({ title: "License", items: [item.license] });
}
if (isExternal) {
const shortHash = (value: string) =>
/^[0-9a-f]{40}$/i.test(value) ? value.slice(0, 7) : value;
const ref = item.source?.ref;
const sha = item.source?.sha;
if (ref) {
chips.push({ title: "Ref", items: [shortHash(ref)] });
}
if (sha) {
chips.push({ title: "Commit", items: [shortHash(sha)] });
}
}
---
<StarlightPage
frontmatter={{
title: item.title ?? item.name,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page plugin-detail-page"
data-resource-detail
data-path={item.readmeFile ?? item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Plugins", href: "/plugins/" },
]}
title={item.title ?? item.name}
/>
<DetailHeader item={headerItem} />
<div class="detail-layout">
<div class="detail-main">
{
markdownHtml ? (
<section class="detail-section" aria-labelledby="doc-heading">
<h2 id="doc-heading">Documentation</h2>
<div class="article-content" set:html={markdownHtml} />
</section>
) : (
<section class="detail-section">
<h2>Overview</h2>
<p>{item.description}</p>
<p class="detail-empty">
{isExternal
? "This is a third-party plugin hosted outside this repository."
: "No README is bundled with this plugin."}{" "}
View it{" "}
<a href={githubUrl} target="_blank" rel="noopener">
on GitHub
</a>
.
</p>
</section>
)
}
<IncludedItems
items={item.items}
pluginPath={item.path}
githubBase={GITHUB_TREE}
/>
</div>
<Sidebar
item={sidebarItem}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<PluginInstall
slot="install"
isExternal={isExternal}
pluginId={item.id}
externalSource={externalSourceRef || null}
installCommand={installCommand}
label={isExternal ? "Install this plugin" : "Install"}
note={isExternal
? "This plugin is maintained in an external repository. Installing it adds the third-party marketplace before installing."
: undefined}
/>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
-2
View File
@@ -5,7 +5,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import { renderPluginsHtml, sortPlugins } from '../scripts/pages/plugins-render';
const initialItems = sortPlugins(pluginsData.items, 'title');
@@ -57,7 +56,6 @@ const initialItems = sortPlugins(pluginsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="plugins.json" data={pluginsData} />
<script>
+166
View File
@@ -0,0 +1,166 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import skillsData from "../../../public/data/skills.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import FileBrowser from "../../components/pages/FileBrowser.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
const GITHUB_BASE = "https://github.com/github/awesome-copilot/blob/main";
const GITHUB_TREE_BASE = "https://github.com/github/awesome-copilot/tree/main";
const REPO_IDENTIFIER = "github/awesome-copilot";
interface SkillFile {
name: string;
path: string;
size?: number;
}
interface SkillItem extends HeaderItem {
id: string;
title: string;
description?: string;
path: string;
skillFile: string;
files: SkillFile[];
hasAssets?: boolean;
assetCount?: number;
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (skillsData.items as SkillItem[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: SkillItem };
const { markdownHtml, frontmatterText, rawMarkdown } = readResourceMarkdown(
item.skillFile
);
const lastUpdated = formatLastUpdated(item.lastUpdated);
const githubUrl = `${GITHUB_TREE_BASE}/${item.path}`;
const installCommand = `gh skills install ${REPO_IDENTIFIER} ${item.id}`;
const skillFileName = item.skillFile.split("/").pop() ?? "SKILL.md";
const fileCount = item.files.length;
const chips: { title: string; items: string[] }[] = [
{ title: "Files", items: [`${fileCount} file${fileCount === 1 ? "" : "s"}`] },
];
if (item.hasAssets && item.assetCount) {
chips.push({
title: "Assets",
items: [`${item.assetCount} asset${item.assetCount === 1 ? "" : "s"}`],
});
}
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page skill-detail-page"
data-file-browser-page
data-bundle-id={item.id}
data-path={item.skillFile}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Skills", href: "/skills/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<div class="detail-main">
<FileBrowser
files={item.files}
primaryPath={item.skillFile}
primaryName={skillFileName}
primaryHtml={markdownHtml}
githubBase={GITHUB_BASE}
/>
</div>
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<div class="skill-install" slot="install">
<p class="skill-install-label">Install with the GitHub CLI</p>
<div class="skill-install-command" data-install-command={installCommand}>
<code tabindex="0">{installCommand}</code>
<button
type="button"
class="skill-install-copy"
data-action="copy-install"
title="Copy install command"
aria-label="Copy install command"
>
<svg
viewBox="0 0 16 16"
width="15"
height="15"
fill="currentColor"
aria-hidden="true"
><path
d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"
></path><path
d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"
></path></svg
>
</button>
</div>
<button
type="button"
class="btn btn-primary skill-download-btn"
data-action="download-zip"
>
<svg
viewBox="0 0 16 16"
width="16"
height="16"
fill="currentColor"
aria-hidden="true"
><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><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"
></path></svg
>
Download ZIP
</button>
</div>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/file-browser";
</script>
</StarlightPage>
-2
View File
@@ -4,7 +4,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import PageHeader from '../components/PageHeader.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import skillsData from '../../public/data/skills.json';
import { renderSkillsHtml, sortSkills } from '../scripts/pages/skills-render';
@@ -43,7 +42,6 @@ const initialItems = sortSkills(skillsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="skills.json" data={skillsData} />
<script>
+131
View File
@@ -0,0 +1,131 @@
---
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
import BackToTop from "../../components/BackToTop.astro";
import workflowsData from "../../../public/data/workflows.json";
import RawMarkdown from "../../components/pages/RawMarkdown.astro";
import type { HeaderItem } from "../../components/pages/Header.astro";
import DetailHeader from "../../components/pages/Header.astro";
import Breadcrumb from "../../components/pages/Breadcrumb.astro";
import Main from "../../components/pages/Main.astro";
import Sidebar from "../../components/pages/Sidebar.astro";
import { readResourceMarkdown, formatLastUpdated } from "../../lib/detail-page";
const GITHUB_BASE = "https://github.com/github/awesome-copilot/blob/main";
const workflowGuideHref = "/learning-hub/agentic-workflows/";
interface WorkflowItem extends HeaderItem {
id: string;
title: string;
description?: string;
path: string;
triggers: string[];
lastUpdated?: string | null;
}
export function getStaticPaths() {
return (workflowsData.items as WorkflowItem[]).map((item) => ({
params: { id: item.id },
props: { item },
}));
}
const { item } = Astro.props as { item: WorkflowItem };
const { markdownHtml, frontmatterText, rawMarkdown } = readResourceMarkdown(
item.path
);
const lastUpdated = formatLastUpdated(item.lastUpdated);
const githubUrl = `${GITHUB_BASE}/${item.path}`;
const chips: { title: string; items: string[] }[] = [];
if (item.triggers.length > 0) {
chips.push({ title: "Triggers", items: item.triggers });
}
---
<StarlightPage
frontmatter={{
title: item.title,
description: item.description,
template: "splash",
prev: false,
next: false,
editUrl: false,
}}
>
<div
id="main-content"
class="resource-detail-page"
data-resource-detail
data-path={item.path}
>
<RawMarkdown markdown={rawMarkdown} />
<div class="container">
<Breadcrumb
paths={[
{ name: "Home", href: "/" },
{ name: "Agentic Workflows", href: "/workflows/" },
]}
title={item.title}
/>
<DetailHeader item={item} />
<div class="detail-layout">
<Main markdownHtml={markdownHtml} githubUrl={githubUrl} />
<Sidebar
item={item}
lastUpdated={lastUpdated ?? undefined}
frontmatterText={frontmatterText || undefined}
githubUrl={githubUrl}
chips={chips}
>
<div class="skill-install" slot="install">
<p class="skill-install-label">Install this workflow</p>
<p class="skill-install-note">
Install the <code>gh aw</code> CLI{" "}
(<code>gh extension install github/gh-aw</code>), then copy this
file into <code>.github/workflows/</code> and run{" "}
<code>gh aw compile</code> to generate the lock file. See the{" "}
<a href={workflowGuideHref}>Agentic Workflows guide</a>{" "}
for full setup steps.
</p>
<button
type="button"
class="btn btn-primary skill-download-btn"
data-action="download"
>
<svg
viewBox="0 0 16 16"
width="16"
height="16"
fill="currentColor"
aria-hidden="true"
><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><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"
></path></svg
>
Download workflow
</button>
<button
type="button"
class="btn btn-secondary skill-download-btn"
data-action="copy-markdown"
>
Copy markdown
</button>
</div>
</Sidebar>
</div>
</div>
</div>
<BackToTop />
<script>
import "../../scripts/pages/resource-detail";
</script>
</StarlightPage>
-2
View File
@@ -5,7 +5,6 @@ import ContributeCTA from '../components/ContributeCTA.astro';
import EmbeddedPageData from '../components/EmbeddedPageData.astro';
import PageHeader from '../components/PageHeader.astro';
import BackToTop from '../components/BackToTop.astro';
import Modal from '../components/Modal.astro';
import { renderWorkflowsHtml, sortWorkflows } from '../scripts/pages/workflows-render';
const initialItems = sortWorkflows(workflowsData.items, 'title');
@@ -48,7 +47,6 @@ const initialItems = sortWorkflows(workflowsData.items, 'title');
</div>
<BackToTop />
<Modal />
<EmbeddedPageData filename="workflows.json" data={workflowsData} />
<script>