From 9da53a279eddb5d5fb4fe3e2c10991125ae29ed2 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Thu, 29 Jan 2026 10:34:06 +1100 Subject: [PATCH] feat: add deep linking support for file modal - Update URL hash when opening/closing modal (#file=path) - Handle browser back/forward navigation - Open modal on page load if hash is present - Share button now copies deep link URL instead of GitHub URL --- website/public/data/manifest.json | 2 +- website/src/scripts/modal.ts | 62 +++++++++++++++++++++++++++++-- website/src/scripts/utils.ts | 6 +-- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/website/public/data/manifest.json b/website/public/data/manifest.json index d0b5f935..48d20815 100644 --- a/website/public/data/manifest.json +++ b/website/public/data/manifest.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-28T22:56:09.687Z", + "generated": "2026-01-28T23:33:55.118Z", "counts": { "agents": 140, "prompts": 134, diff --git a/website/src/scripts/modal.ts b/website/src/scripts/modal.ts index e31cf86d..4f2b2409 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, downloadFile, shareFile } from './utils'; +import { fetchFileContent, getVSCodeInstallUrl, copyToClipboard, showToast, downloadFile, shareFile, getResourceType } from './utils'; // Modal state let currentFilePath: string | null = null; @@ -56,6 +56,48 @@ export function setupModal(): void { // Setup install dropdown toggle setupInstallDropdown('install-dropdown'); + + // Handle browser back/forward navigation + window.addEventListener('hashchange', handleHashChange); + + // Check for deep link on initial load + handleHashChange(); +} + +/** + * Handle hash changes for deep linking + */ +function handleHashChange(): void { + const hash = window.location.hash; + + if (hash && hash.startsWith('#file=')) { + const filePath = decodeURIComponent(hash.slice(6)); + if (filePath && filePath !== currentFilePath) { + const type = getResourceType(filePath); + openFileModal(filePath, type, false); // Don't update hash since we're responding to it + } + } else if (!hash || hash === '#') { + // No hash or empty hash - close modal if open + if (currentFilePath) { + closeModal(false); // Don't update hash since we're responding to it + } + } +} + +/** + * Update URL hash for deep linking + */ +function updateHash(filePath: string | null): void { + if (filePath) { + const newHash = `#file=${encodeURIComponent(filePath)}`; + if (window.location.hash !== newHash) { + history.pushState(null, '', newHash); + } + } else { + if (window.location.hash) { + history.pushState(null, '', window.location.pathname + window.location.search); + } + } } /** @@ -90,8 +132,11 @@ export function setupInstallDropdown(containerId: string): void { /** * Open file viewer modal + * @param filePath - Path to the file + * @param type - Resource type (agent, prompt, instruction, etc.) + * @param updateUrl - Whether to update the URL hash (default: true) */ -export async function openFileModal(filePath: string, type: string): Promise { +export async function openFileModal(filePath: string, type: string, updateUrl = true): Promise { const modal = document.getElementById('file-modal'); const title = document.getElementById('modal-title'); const contentEl = document.getElementById('modal-content')?.querySelector('code'); @@ -105,6 +150,11 @@ export async function openFileModal(filePath: string, type: string): Promise { } /** - * Share/copy link to clipboard + * Share/copy link to clipboard (deep link to current page with file hash) */ export async function shareFile(filePath: string): Promise { - const url = getGitHubUrl(filePath); - return copyToClipboard(url); + const deepLinkUrl = `${window.location.origin}${window.location.pathname}#file=${encodeURIComponent(filePath)}`; + return copyToClipboard(deepLinkUrl); } /**