diff --git a/website/public/data/manifest.json b/website/public/data/manifest.json
index 6d9bc0bb..1da92aff 100644
--- a/website/public/data/manifest.json
+++ b/website/public/data/manifest.json
@@ -1,5 +1,5 @@
{
- "generated": "2026-01-28T22:33:37.643Z",
+ "generated": "2026-01-28T22:44:20.356Z",
"counts": {
"agents": 140,
"prompts": 134,
diff --git a/website/src/components/Modal.astro b/website/src/components/Modal.astro
index 2f69cfb8..cd0761d1 100644
--- a/website/src/components/Modal.astro
+++ b/website/src/components/Modal.astro
@@ -14,6 +14,18 @@
Copy
+
+
Install
diff --git a/website/src/scripts/modal.ts b/website/src/scripts/modal.ts
index e71e6c03..e31cf86d 100644
--- a/website/src/scripts/modal.ts
+++ b/website/src/scripts/modal.ts
@@ -2,7 +2,7 @@
* Modal functionality for file viewing
*/
-import { fetchFileContent, getVSCodeInstallUrl, copyToClipboard, showToast } from './utils';
+import { fetchFileContent, getVSCodeInstallUrl, copyToClipboard, showToast, downloadFile, shareFile } from './utils';
// Modal state
let currentFilePath: string | null = null;
@@ -16,6 +16,8 @@ export function setupModal(): void {
const modal = document.getElementById('file-modal');
const closeBtn = document.getElementById('close-modal');
const copyBtn = document.getElementById('copy-btn');
+ const downloadBtn = document.getElementById('download-btn');
+ const shareBtn = document.getElementById('share-btn');
if (!modal) return;
@@ -38,6 +40,20 @@ export function setupModal(): void {
}
});
+ downloadBtn?.addEventListener('click', async () => {
+ if (currentFilePath) {
+ const success = await downloadFile(currentFilePath);
+ showToast(success ? 'Download started!' : 'Download failed', success ? 'success' : 'error');
+ }
+ });
+
+ shareBtn?.addEventListener('click', async () => {
+ if (currentFilePath) {
+ const success = await shareFile(currentFilePath);
+ showToast(success ? 'Link copied to clipboard!' : 'Failed to copy link', success ? 'success' : 'error');
+ }
+ });
+
// Setup install dropdown toggle
setupInstallDropdown('install-dropdown');
}
diff --git a/website/src/scripts/pages/agents.ts b/website/src/scripts/pages/agents.ts
index af52fce5..566a7ca0 100644
--- a/website/src/scripts/pages/agents.ts
+++ b/website/src/scripts/pages/agents.ts
@@ -3,7 +3,7 @@
*/
import { createChoices, getChoicesValues, type Choices } from '../choices';
import { FuzzySearch } from '../search';
-import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers } from '../utils';
+import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers, getActionButtonsHtml, setupActionHandlers } from '../utils';
import { setupModal, openFileModal } from '../modal';
interface Agent {
@@ -103,6 +103,7 @@ function renderItems(items: Agent[], query = ''): void {
${getInstallDropdownHtml(resourceType, item.path, true)}
+ ${getActionButtonsHtml(item.path, true)}
GitHub
@@ -170,6 +171,7 @@ export async function initAgentsPage(): Promise
{
setupModal();
setupDropdownCloseHandlers();
+ setupActionHandlers();
}
// Auto-initialize when DOM is ready
diff --git a/website/src/scripts/pages/instructions.ts b/website/src/scripts/pages/instructions.ts
index 019503f3..7eba6393 100644
--- a/website/src/scripts/pages/instructions.ts
+++ b/website/src/scripts/pages/instructions.ts
@@ -3,7 +3,7 @@
*/
import { createChoices, getChoicesValues, type Choices } from '../choices';
import { FuzzySearch } from '../search';
-import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers } from '../utils';
+import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers, getActionButtonsHtml, setupActionHandlers } from '../utils';
import { setupModal, openFileModal } from '../modal';
interface Instruction {
@@ -73,6 +73,7 @@ function renderItems(items: Instruction[], query = ''): void {
${getInstallDropdownHtml('instructions', item.path, true)}
+ ${getActionButtonsHtml(item.path, true)}
GitHub
@@ -122,6 +123,7 @@ export async function initInstructionsPage(): Promise
{
setupModal();
setupDropdownCloseHandlers();
+ setupActionHandlers();
}
// Auto-initialize when DOM is ready
diff --git a/website/src/scripts/pages/prompts.ts b/website/src/scripts/pages/prompts.ts
index bdb05b0b..d03edf3d 100644
--- a/website/src/scripts/pages/prompts.ts
+++ b/website/src/scripts/pages/prompts.ts
@@ -3,7 +3,7 @@
*/
import { createChoices, getChoicesValues, type Choices } from '../choices';
import { FuzzySearch } from '../search';
-import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers } from '../utils';
+import { fetchData, debounce, escapeHtml, getGitHubUrl, getInstallDropdownHtml, setupDropdownCloseHandlers, getActionButtonsHtml, setupActionHandlers } from '../utils';
import { setupModal, openFileModal } from '../modal';
interface Prompt {
@@ -68,6 +68,7 @@ function renderItems(items: Prompt[], query = ''): void {
${getInstallDropdownHtml(resourceType, item.path, true)}
+ ${getActionButtonsHtml(item.path, true)}
GitHub
@@ -117,6 +118,7 @@ export async function initPromptsPage(): Promise
{
setupModal();
setupDropdownCloseHandlers();
+ setupActionHandlers();
}
// Auto-initialize when DOM is ready
diff --git a/website/src/scripts/utils.ts b/website/src/scripts/utils.ts
index 1d885154..418d3c13 100644
--- a/website/src/scripts/utils.ts
+++ b/website/src/scripts/utils.ts
@@ -114,6 +114,43 @@ export function getRawGitHubUrl(filePath: string): string {
return `${REPO_BASE_URL}/${filePath}`;
}
+/**
+ * Download a file from its path
+ */
+export async function downloadFile(filePath: string): Promise {
+ try {
+ const response = await fetch(`${REPO_BASE_URL}/${filePath}`);
+ if (!response.ok) throw new Error('Failed to fetch file');
+
+ const content = await response.text();
+ const filename = filePath.split('/').pop() || 'file.md';
+
+ const blob = new Blob([content], { type: 'text/markdown' });
+ const url = URL.createObjectURL(blob);
+
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+
+ return true;
+ } catch (error) {
+ console.error('Download failed:', error);
+ return false;
+ }
+}
+
+/**
+ * Share/copy link to clipboard
+ */
+export async function shareFile(filePath: string): Promise {
+ const url = getGitHubUrl(filePath);
+ return copyToClipboard(url);
+}
+
/**
* Show a toast notification
*/
@@ -254,3 +291,41 @@ export function setupDropdownCloseHandlers(): void {
}
});
}
+
+/**
+ * Generate HTML for action buttons (download, share) in list view
+ */
+export function getActionButtonsHtml(filePath: string, small = false): string {
+ const btnClass = small ? 'btn-small' : '';
+ const iconSize = small ? 14 : 16;
+ const escapedPath = filePath.replace(/'/g, "\\'");
+
+ return `
+
+
+ `;
+}
+
+/**
+ * Setup global action handlers for download and share buttons
+ */
+export function setupActionHandlers(): void {
+ // Expose functions globally for inline onclick handlers
+ (window as Window & { __downloadFile?: (path: string) => void; __shareFile?: (path: string) => void }).__downloadFile = async (path: string) => {
+ const success = await downloadFile(path);
+ showToast(success ? 'Download started!' : 'Download failed', success ? 'success' : 'error');
+ };
+
+ (window as Window & { __downloadFile?: (path: string) => void; __shareFile?: (path: string) => void }).__shareFile = async (path: string) => {
+ const success = await shareFile(path);
+ showToast(success ? 'Link copied!' : 'Failed to copy link', success ? 'success' : 'error');
+ };
+}