Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions eng/generate-website-data.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,25 @@ function normalizeText(value, fallback = "") {
return typeof value === "string" ? value.trim() : fallback;
}

/**
* Normalize an author value (npm string form or { name, url } object) to
* { name, url? } | null. Returns null when no usable name is present.
*/
function normalizeAuthor(value) {
if (!value) return null;
if (typeof value === "string") {
const name = value.trim();
return name ? { name } : null;
}
if (typeof value === "object") {
const name = normalizeText(value.name);
if (!name) return null;
const url = normalizeText(value.url);
return url ? { name, url } : { name };
}
return null;
}

/**
* Find the latest git-modified date for any file under a directory.
*/
Expand Down Expand Up @@ -1002,6 +1021,10 @@ function generateCanvasManifest(gitDates, commitSha) {
const packageJson = fs.existsSync(packageJsonPath)
? JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"))
: {};
const canvasJsonPath = path.join(extensionDir, "canvas.json");
const canvasJson = fs.existsSync(canvasJsonPath)
? JSON.parse(fs.readFileSync(canvasJsonPath, "utf-8"))
: {};
const keywords = Array.isArray(packageJson.keywords)
? [...new Set(packageJson.keywords.filter((keyword) => typeof keyword === "string").map((keyword) => keyword.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b))
: [];
Expand Down Expand Up @@ -1044,6 +1067,7 @@ function generateCanvasManifest(gitDates, commitSha) {
installUrl,
sourceUrl: null,
external: false,
author: normalizeAuthor(canvasJson.author),
keywords,
});
}
Expand Down Expand Up @@ -1116,6 +1140,7 @@ function generateCanvasManifest(gitDates, commitSha) {
installUrl,
sourceUrl: sourceUrl || null,
external: true,
author: normalizeAuthor(ext?.author),
keywords,
});
}
Expand Down Expand Up @@ -1199,6 +1224,7 @@ function writePerExtensionCanvasManifests(canvasManifestData) {
name: item.name,
description: item.description || "Canvas extension",
version: item.version || "1.0.0",
...(item.author ? { author: item.author } : {}),
keywords: Array.isArray(item.keywords)
? [...new Set(item.keywords)].sort((a, b) => a.localeCompare(b))
: [],
Expand Down
4 changes: 4 additions & 0 deletions extensions/accessibility-kanban/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Accessibility Kanban",
"description": "Kanban board to manage accessibility issues, allow you to plan, track, and complete remediation work.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"accessibility",
"github-issues",
Expand Down
4 changes: 4 additions & 0 deletions extensions/arcade-canvas/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Agent Arcade",
"description": "Play five retro Phaser mini-games in a Copilot canvas while agents work.",
"version": "1.0.0",
"author": {
"name": "Dan Wahlin",
"url": "https://github.com/DanWahlin"
},
"keywords": [
"arcade-games",
"copilot-canvas",
Expand Down
4 changes: 4 additions & 0 deletions extensions/backlog-swipe-triage/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Backlog Swipe Triage",
"description": "Quickly swipe through backlog issues to triage decisions like assign, needs-info, defer, close, or ignore.",
"version": "1.0.0",
"author": {
"name": "James Montemagno",
"url": "https://github.com/jamesmontemagno"
},
"keywords": [
"agent-assignment",
"backlog-triage",
Expand Down
4 changes: 4 additions & 0 deletions extensions/chromium-control-canvas/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Chromium Control Canvas",
"description": "Opens a real Chromium window you can navigate and interact with from a Copilot canvas control panel and agent actions.",
"version": "1.0.0",
"author": {
"name": "Andrea Griffiths",
"url": "https://github.com/AndreaGriffiths11"
},
"keywords": [
"browser-control",
"chromium-browser",
Expand Down
4 changes: 4 additions & 0 deletions extensions/color-orb/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Color Orb",
"description": "A visual orb that users can ask the agent to recolor while showing a live activity log in the canvas.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"agent-actions",
"color-picker",
Expand Down
4 changes: 4 additions & 0 deletions extensions/diagram-viewer/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Diagram Explorer",
"description": "Render diagrams, click nodes to drill down, and view agent-generated explanations directly in the canvas.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"architecture-mapping",
"canvas-navigation",
Expand Down
1 change: 1 addition & 0 deletions extensions/external.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"id": "coffilot",
"name": "Coffilot",
"description": "Java-focused Copilot canvas extension from jdubois.",
"author": { "name": "Julien Dubois", "url": "https://github.com/jdubois" },
"keywords": [
"java",
"canvas",
Expand Down
4 changes: 4 additions & 0 deletions extensions/feedback-themes/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Feedback Themes",
"description": "Explore grouped customer feedback signals by impact and drill into a theme to guide product next steps.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"customer-feedback",
"impact-prioritization",
Expand Down
4 changes: 4 additions & 0 deletions extensions/gesture-review/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Gesture PR Review",
"description": "Review pull requests with a live camera feed and approve or reject using thumbs-up/thumbs-down gestures.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"camera-input",
"gesture-control",
Expand Down
4 changes: 4 additions & 0 deletions extensions/release-notes-showcase/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Release Notes Showcase",
"description": "Compose and refine launch-ready release notes with contributor callouts and export-friendly output.",
"version": "1.0.0",
"author": {
"name": "James Montemagno",
"url": "https://github.com/jamesmontemagno"
},
"keywords": [
"changelog",
"contributor-callouts",
Expand Down
4 changes: 4 additions & 0 deletions extensions/where-was-i/canvas.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"name": "Where Was I?",
"description": "Reconstruct your dev context (branch, commits, uncommitted work, PR clues) and trigger a resume prompt to continue quickly.",
"version": "1.0.0",
"author": {
"name": "Aaron Powell",
"url": "https://github.com/aaronpowell"
},
"keywords": [
"branch-state",
"developer-context",
Expand Down
28 changes: 27 additions & 1 deletion website/src/scripts/pages/extensions-render.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { escapeHtml, getGitHubUrl, getLastUpdatedHtml } from "../utils";
import {
escapeHtml,
getGitHubHandle,
getGitHubUrl,
getLastUpdatedHtml,
sanitizeUrl,
} from "../utils";
import { renderEmptyStateHtml, renderSharedCardHtml } from "./card-render";

export interface RenderableExtension {
Expand Down Expand Up @@ -34,6 +40,7 @@ export interface RenderableExtension {
installUrl?: string | null;
sourceUrl?: string | null;
external?: boolean;
author?: { name: string; url?: string } | null;
}

export type ExtensionSortOption = "title" | "lastUpdated";
Expand Down Expand Up @@ -92,8 +99,27 @@ export function renderExtensionsHtml(items: RenderableExtension[]): string {
</div>
`;

const authorName = item.author?.name;
const authorUrl = item.author?.url;
const authorHandle =
authorName && authorUrl
? getGitHubHandle(authorUrl, authorName)
: authorName || "";
const authorHtml = authorName
? `<span class="resource-tag resource-author">by ${
authorUrl
? `<a href="${escapeHtml(
sanitizeUrl(authorUrl)
)}" target="_blank" rel="noopener noreferrer" title="${escapeHtml(
authorName
)}">${escapeHtml(authorHandle)}</a>`
: escapeHtml(authorName)
}</span>`
: "";

const metaHtml = `
${item.external ? '<span class="resource-tag">External</span>' : ""}
${authorHtml}
${getLastUpdatedHtml(item.lastUpdated)}
`;

Expand Down
20 changes: 20 additions & 0 deletions website/src/scripts/pages/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import {
copyToClipboard,
fetchData,
formatRelativeTime,
getGitHubHandle,
getGitHubUrl,
getQueryParam,
getQueryParamValues,
sanitizeUrl,
showToast,
updateQueryParams,
} from "../utils";
Expand Down Expand Up @@ -178,6 +180,24 @@ function openDetailsModal(
if (item.external) {
metaParts.push('<span class="resource-tag">External</span>');
}
if (item.author?.name) {
const authorName = item.author.name;
const authorUrl = item.author.url;
const authorHandle = authorUrl
? getGitHubHandle(authorUrl, authorName)
: authorName;
metaParts.push(
authorUrl
? `<span class="resource-author">by <a href="${escapeHtml(
sanitizeUrl(authorUrl)
)}" target="_blank" rel="noopener noreferrer" title="${escapeHtml(
authorName
)}">${escapeHtml(authorHandle)}</a></span>`
: `<span class="resource-author">by ${escapeHtml(
authorName
)}</span>`
);
}
if (item.lastUpdated) {
metaParts.push(
`<span class="last-updated">Updated ${escapeHtml(
Expand Down
25 changes: 25 additions & 0 deletions website/src/scripts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,31 @@ export function sanitizeUrl(url: string | null | undefined): string {
return "#";
}

/**
* Derive a GitHub @handle from a profile URL
* (e.g. "https://github.com/aaronpowell" -> "@aaronpowell").
* Falls back to the provided value when the URL is not a github.com profile.
*/
export function getGitHubHandle(
url: string | null | undefined,
fallback = ""
): string {
if (!url) return fallback;
try {
const parsed = new URL(url);
const host = parsed.hostname.replace(/^www\./, "");
if (host === "github.com") {
const segments = parsed.pathname.split("/").filter(Boolean);
if (segments.length === 1) {
return `@${segments[0]}`;
}
}
} catch {
// Invalid URL
}
return fallback;
}

/**
* Truncate text with ellipsis
*/
Expand Down
Loading