github-code 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/parsers/file-parser.ts", "../src/parsers/url-parser.ts", "../src/fetching/code-fetcher.ts", "../src/fetching/highlightjs-loader.ts", "../src/styles/base-light.css", "../src/styles/base-dark.css", "../src/styles/tab.css", "../src/styles/stylesheet-manager.ts", "../src/rendering/html-generators.ts", "../src/rendering/template-generators.ts", "../src/rendering/syntax-highlighter.ts", "../src/theme/theme-resolver.ts", "../src/tabs/tab-state.ts", "../src/tabs/tab-controller.ts", "../src/github-code.ts", "../src/index.ts"],
4
+ "sourcesContent": ["/**\n * Parses the comma-separated file attribute into individual URLs\n */\nexport function parseFileAttribute(fileAttr: string): string[] {\n return fileAttr\n .split(',')\n .map((url) => url.trim())\n .filter((url) => url.length > 0);\n}\n", "import type { GitHubUrlParts } from '../types';\n\n/**\n * Regex pattern to validate GitHub blob URLs\n */\n// eslint-disable-next-line no-useless-escape\nconst GITHUB_BLOB_PATTERN = /^https:\\/\\/github\\.com\\/[^\\/]+\\/[^\\/]+\\/blob\\/.+/;\n\n/**\n * Checks if a URL is a valid GitHub blob URL\n */\nexport function isValidGitHubUrl(url: string): boolean {\n return GITHUB_BLOB_PATTERN.test(url);\n}\n\n/**\n * Parses a GitHub blob URL into its components\n * @throws {Error} If the URL cannot be parsed\n */\nexport function parseGitHubUrl(url: string): GitHubUrlParts {\n // eslint-disable-next-line no-useless-escape\n const match = /^https:\\/\\/github\\.com\\/([^\\/]+)\\/([^\\/]+)\\/blob\\/([^\\/]+)\\/(.+)$/.exec(url);\n\n if (!match) {\n throw new Error('Failed to parse GitHub URL');\n }\n\n const owner = match[1];\n const repo = match[2];\n const commit = match[3];\n const path = match[4];\n\n if (!owner || !repo || !commit || !path) {\n throw new Error('Failed to parse GitHub URL');\n }\n\n const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${commit}/${path}`;\n\n const filename = path.split('/').pop() || 'unknown';\n\n return {\n rawUrl,\n filename,\n };\n}\n\n/**\n * Extracts filename from any URL (fallback for non-standard URLs)\n */\nexport function extractFilenameFromUrl(url: string): string {\n try {\n // eslint-disable-next-line no-useless-escape\n const match = /\\/([^\\/]+)$/.exec(url);\n return match?.[1] ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n", "import type { FileMetadata } from '../types';\n\n/**\n * Fetches code content from a URL\n * @throws {Error} If the fetch fails (network error, HTTP error, CORS error)\n */\nexport async function fetchCode(url: string): Promise<string> {\n try {\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch code (HTTP ${response.status}). Please check if the URL is accessible.`);\n }\n\n return await response.text();\n } catch (error) {\n // Detect CORS errors specifically\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw new Error(\n `Failed to fetch code from ${url}. ` +\n `This is likely a CORS (Cross-Origin Resource Sharing) error. ` +\n `The server needs to allow requests from this origin. ` +\n `GitHub's raw.githubusercontent.com should work without CORS issues.`\n );\n }\n throw error;\n }\n}\n\n/**\n * Ensures a file's content is loaded, fetching it if necessary\n * @param file The file metadata object to load\n * @param isRetry Whether this is a retry attempt (will force re-fetch)\n */\nexport async function ensureFileLoaded(file: FileMetadata, isRetry = false): Promise<void> {\n // Already loaded (skip if not a retry)\n if (file.loaded && !isRetry) {\n return;\n }\n\n // If retrying, reset the loaded flag to allow re-fetch\n if (isRetry) {\n file.loaded = false;\n file.error = null;\n }\n\n try {\n const code = await fetchCode(file.rawUrl);\n file.code = code;\n file.error = null;\n file.loaded = true;\n } catch (error) {\n file.code = null;\n file.error = error instanceof Error ? error.message : String(error);\n file.loaded = true; // Mark as loaded so we show error (can retry later)\n }\n}\n", "/**\n * Shared promise to prevent multiple simultaneous loads of highlight.js\n */\nlet highlightJSLoadingPromise: Promise<void> | null = null;\n\n/**\n * Type definition for highlight.js on window object\n */\ndeclare global {\n interface Window {\n hljs?: {\n highlightAuto: (code: string) => { value: string };\n };\n }\n}\n\n/**\n * Loads highlight.js library from CDN if not already loaded.\n * Uses a singleton pattern to ensure only one load attempt happens at a time.\n */\nexport async function loadHighlightJS(): Promise<void> {\n // If already loaded, return immediately\n if (window.hljs) {\n return;\n }\n\n // If currently loading, return the existing promise\n if (highlightJSLoadingPromise) {\n return highlightJSLoadingPromise;\n }\n\n // Create new loading promise\n highlightJSLoadingPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js';\n script.integrity = 'sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU';\n script.crossOrigin = 'anonymous';\n script.onload = () => {\n highlightJSLoadingPromise = null; // Clear after successful load\n resolve();\n };\n script.onerror = () => {\n highlightJSLoadingPromise = null; // Clear on error to allow retry\n const errorMsg =\n 'Failed to load highlight.js library. ' +\n 'If you have a Content Security Policy (CSP), ensure it allows:\\n' +\n ' script-src https://cdnjs.cloudflare.com\\n' +\n ' style-src https://cdnjs.cloudflare.com';\n reject(new Error(errorMsg));\n };\n document.head.appendChild(script);\n });\n\n return highlightJSLoadingPromise;\n}\n", "export default \":host {\\n --border-color: #d0d7de;\\n --header-background: #f6f8fa;\\n --header-text-color: #24292f;\\n --line-number-color: #57606a;\\n --tab-color: #57606a;\\n --tab-hover-border: #d0d7de;\\n --tabs-background: #f6f8fa;\\n --code-background: #ffffff;\\n --skeleton-base: #e0e0e0;\\n --skeleton-highlight: #f0f0f0;\\n --error-text-color: #cf222e;\\n --error-background: #ffebe9;\\n --error-border: #ff8182;\\n --button-background: #f6f8fa;\\n --button-text-color: #24292f;\\n --button-border: #d0d7de;\\n --button-hover-background: #f3f4f6;\\n --font-family-base:\\n -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif, 'Apple Color Emoji',\\n 'Segoe UI Emoji';\\n --font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;\\n\\n display: block;\\n min-width: 0;\\n font-family: var(--font-family-base);\\n}\\n\\narticle {\\n border: 1px solid var(--border-color);\\n border-radius: 6px;\\n overflow: hidden;\\n background-color: var(--code-background);\\n}\\n\\nheader {\\n background-color: var(--header-background);\\n padding: 8px 16px;\\n border-bottom: 1px solid var(--border-color);\\n color: var(--header-text-color);\\n font-weight: 600;\\n font-size: 14px;\\n}\\n\\n.code-wrapper {\\n width: 100%;\\n margin: 0;\\n overflow-x: auto;\\n background-color: var(--code-background);\\n}\\n\\n.code-table {\\n display: flex;\\n flex-direction: column;\\n width: 100%;\\n min-width: 0;\\n}\\n\\n.code-row {\\n display: flex;\\n}\\n\\n.line-number {\\n flex-shrink: 0;\\n text-align: right;\\n padding-right: 6px;\\n padding-left: 8px;\\n color: var(--line-number-color);\\n user-select: none;\\n border-right: 1px solid var(--border-color);\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 20px;\\n min-width: 40px;\\n}\\n\\n.code-cell {\\n flex: 1;\\n min-width: 0;\\n padding-left: 2px;\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 20px;\\n white-space: pre;\\n}\\n\\n.error {\\n padding: 16px;\\n color: var(--error-text-color);\\n background-color: var(--error-background);\\n border: 1px solid var(--error-border);\\n border-radius: 6px;\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 1.5;\\n margin: 16px;\\n}\\n\\n.retry-button {\\n margin-top: 12px;\\n padding: 8px 16px;\\n background: var(--button-background);\\n color: var(--button-text-color);\\n border: 1px solid var(--button-border);\\n border-radius: 6px;\\n cursor: pointer;\\n font-size: 14px;\\n font-family: var(--font-family-base);\\n font-weight: 500;\\n transition: background-color 0.2s ease;\\n}\\n\\n.retry-button:hover {\\n background-color: var(--button-hover-background);\\n}\\n\\n/* Skeleton loader styles */\\n.skeleton-line {\\n height: 12px;\\n background: linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-highlight) 50%, var(--skeleton-base) 75%);\\n background-size: 200% 100%;\\n animation: loading 1.5s ease-in-out infinite;\\n border-radius: 4px;\\n margin: 4px 0;\\n}\\n\\n@keyframes loading {\\n 0% {\\n background-position: 200% 0;\\n }\\n 100% {\\n background-position: -200% 0;\\n }\\n}\\n\\n.skeleton-loading .line-number {\\n opacity: 0.3;\\n}\\n\"", "export default \":host {\\n --border-color: #30363d;\\n --header-background: #161b22;\\n --header-text-color: #c9d1d9;\\n --line-number-color: #8b949e;\\n --tab-color: #8b949e;\\n --tab-hover-border: #30363d;\\n --tabs-background: #161b22;\\n --code-background: #0d1117;\\n --skeleton-base: #21262d;\\n --skeleton-highlight: #30363d;\\n --error-text-color: #ff7b72;\\n --error-background: #490202;\\n --error-border: #f85149;\\n --button-background: #21262d;\\n --button-text-color: #c9d1d9;\\n --button-border: #30363d;\\n --button-hover-background: #30363d;\\n --font-family-base:\\n -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif, 'Apple Color Emoji',\\n 'Segoe UI Emoji';\\n --font-family-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;\\n\\n display: block;\\n min-width: 0;\\n font-family: var(--font-family-base);\\n}\\n\\narticle {\\n border: 1px solid var(--border-color);\\n border-radius: 6px;\\n overflow: hidden;\\n background-color: var(--code-background);\\n}\\n\\nheader {\\n background-color: var(--header-background);\\n padding: 8px 16px;\\n border-bottom: 1px solid var(--border-color);\\n color: var(--header-text-color);\\n font-weight: 600;\\n font-size: 14px;\\n}\\n\\n.code-wrapper {\\n width: 100%;\\n margin: 0;\\n overflow-x: auto;\\n background-color: var(--code-background);\\n}\\n\\n.code-table {\\n display: flex;\\n flex-direction: column;\\n width: 100%;\\n min-width: 0;\\n}\\n\\n.code-row {\\n display: flex;\\n}\\n\\n.line-number {\\n flex-shrink: 0;\\n text-align: right;\\n padding-right: 6px;\\n padding-left: 8px;\\n color: var(--line-number-color);\\n user-select: none;\\n border-right: 1px solid var(--border-color);\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 20px;\\n min-width: 40px;\\n}\\n\\n.code-cell {\\n flex: 1;\\n min-width: 0;\\n padding-left: 2px;\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 20px;\\n white-space: pre;\\n}\\n\\n.error {\\n padding: 16px;\\n color: var(--error-text-color);\\n background-color: var(--error-background);\\n border: 1px solid var(--error-border);\\n border-radius: 6px;\\n font-family: var(--font-family-mono);\\n font-size: 12px;\\n line-height: 1.5;\\n margin: 16px;\\n}\\n\\n.retry-button {\\n margin-top: 12px;\\n padding: 8px 16px;\\n background: var(--button-background);\\n color: var(--button-text-color);\\n border: 1px solid var(--button-border);\\n border-radius: 6px;\\n cursor: pointer;\\n font-size: 14px;\\n font-family: var(--font-family-base);\\n font-weight: 500;\\n transition: background-color 0.2s ease;\\n}\\n\\n.retry-button:hover {\\n background-color: var(--button-hover-background);\\n}\\n\\n/* Skeleton loader styles */\\n.skeleton-line {\\n height: 12px;\\n background: linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-highlight) 50%, var(--skeleton-base) 75%);\\n background-size: 200% 100%;\\n animation: loading 1.5s ease-in-out infinite;\\n border-radius: 4px;\\n margin: 4px 0;\\n}\\n\\n@keyframes loading {\\n 0% {\\n background-position: 200% 0;\\n }\\n 100% {\\n background-position: -200% 0;\\n }\\n}\\n\\n.skeleton-loading .line-number {\\n opacity: 0.3;\\n}\\n\"", "export default \"nav[role='tablist'] {\\n display: flex;\\n background-color: var(--tabs-background);\\n border-bottom: 1px solid var(--border-color);\\n overflow-x: auto;\\n}\\n\\nnav > button {\\n padding: 8px 16px;\\n background: none;\\n border: none;\\n border-bottom: 2px solid transparent;\\n color: var(--tab-color);\\n font-size: 14px;\\n font-family: var(--font-family-base);\\n cursor: pointer;\\n white-space: nowrap;\\n transition:\\n color 0.1s ease,\\n border-color 0.1s ease;\\n}\\n\\nnav > button:hover {\\n color: var(--header-text-color);\\n border-bottom-color: var(--tab-hover-border);\\n}\\n\\nnav > button[aria-selected='true'] {\\n color: var(--header-text-color);\\n border-bottom-color: #fd8c73;\\n font-weight: 600;\\n}\\n\\nsection[role='tabpanel'] {\\n display: block;\\n}\\n\\nsection[role='tabpanel'][aria-hidden='true'] {\\n display: none;\\n}\\n\"", "import baseLightCss from './base-light.css';\nimport baseDarkCss from './base-dark.css';\nimport tabCss from './tab.css';\nimport type { ResolvedTheme } from '../types';\n\n/**\n * Manages constructable stylesheets for the component.\n * Implements caching to share stylesheet instances across all component instances.\n */\nexport class StylesheetManager {\n private static baseStylesLight: CSSStyleSheet | null = null;\n private static baseStylesDark: CSSStyleSheet | null = null;\n private static tabStyles: CSSStyleSheet | null = null;\n\n /**\n * Gets the base stylesheet for the specified theme (light or dark).\n * Lazy-loads and caches the stylesheet on first access.\n */\n static getBaseStyleSheet(theme: ResolvedTheme): CSSStyleSheet {\n if (theme === 'dark') {\n if (!this.baseStylesDark) {\n this.baseStylesDark = new CSSStyleSheet();\n this.baseStylesDark.replaceSync(baseDarkCss);\n }\n return this.baseStylesDark;\n } else {\n if (!this.baseStylesLight) {\n this.baseStylesLight = new CSSStyleSheet();\n this.baseStylesLight.replaceSync(baseLightCss);\n }\n return this.baseStylesLight;\n }\n }\n\n /**\n * Gets the tab stylesheet.\n * Lazy-loads and caches the stylesheet on first access.\n */\n static getTabStyleSheet(): CSSStyleSheet {\n if (!this.tabStyles) {\n this.tabStyles = new CSSStyleSheet();\n this.tabStyles.replaceSync(tabCss);\n }\n return this.tabStyles;\n }\n\n /**\n * Gets the highlight.js theme URL for the specified theme.\n */\n static getHighlightJSThemeUrl(theme: ResolvedTheme): string {\n const themeFile = theme === 'dark' ? 'github-dark.min.css' : 'github.min.css';\n return `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/${themeFile}`;\n }\n}\n", "/**\n * Escapes HTML special characters to prevent XSS\n */\nexport function escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Generates HTML for error display\n */\nexport function getErrorContentHtml(errorMessage: string, showRetry = false): string {\n const retryButton = showRetry ? `<button class=\"retry-button\">Retry</button>` : '';\n return `<div class=\"error\">${escapeHtml(errorMessage)}${retryButton}</div>`;\n}\n\n/**\n * Generates HTML for skeleton loading state\n */\nexport function getSkeletonContentHtml(): string {\n // Generate skeleton lines that look like code\n const skeletonLines = Array.from({ length: 20 }, (_, i) => {\n // Random width between 60-90%\n // Using Math.random() is acceptable here for non-cryptographic purposes\n const width = 60 + Math.random() * 30; // NOSONAR\n return `\n <div class=\"code-row\">\n <div class=\"line-number\">${i + 1}</div>\n <div class=\"skeleton-line\" style=\"width: ${width}%\"></div>\n </div>\n `;\n }).join('');\n\n return `\n <div class=\"code-wrapper skeleton-loading\">\n <div class=\"code-table\">\n ${skeletonLines}\n </div>\n </div>\n `;\n}\n\n/**\n * Generates HTML for code display (without syntax highlighting)\n */\nexport function getCodeContentHtml(code: string | null): string {\n // Safety check for null/undefined code\n if (!code) {\n return getErrorContentHtml('No code content available');\n }\n\n const lines = code.split('\\n');\n\n // Remove only the last empty line if it exists (from final newline)\n if (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n return `\n <div class=\"code-wrapper hljs\">\n <div class=\"code-table\">\n ${lines\n .map(\n (line, index) => `\n <div class=\"code-row\">\n <div class=\"line-number\">${index + 1}</div>\n <div class=\"code-cell\">${escapeHtml(line) || ' '}</div>\n </div>\n `\n )\n .join('')}\n </div>\n </div>\n `;\n}\n", "import { escapeHtml } from './html-generators';\n\n/**\n * Generates the outer HTML structure for a single file display.\n *\n * @param filename - The filename to show in the header\n * @param content - The inner content HTML (skeleton, code, or error)\n * @param themeStylesheetUrl - URL for the highlight.js theme CSS\n * @returns Complete HTML string for single file layout\n */\nexport function generateSingleFileTemplate(filename: string, content: string, themeStylesheetUrl: string): string {\n return `<link rel=\"stylesheet\" href=\"${themeStylesheetUrl}\">\n<article>\n <header>${escapeHtml(filename)}</header>\n ${content}\n</article>`;\n}\n\n/**\n * Generates the article content for a single file (header + content).\n * Used when updating only the article innards, not the full template.\n *\n * @param filename - The filename to show in the header\n * @param content - The inner content HTML\n * @returns HTML string for article content\n */\nexport function generateArticleContent(filename: string, content: string): string {\n return `<header>${escapeHtml(filename)}</header>\n ${content}`;\n}\n\n/**\n * Generates the outer HTML structure for tabbed file display.\n *\n * @param tabsHtml - Pre-generated HTML for tab buttons\n * @param activeIndex - Index of the currently active tab\n * @param panelContent - Content HTML for the active panel\n * @param themeStylesheetUrl - URL for the highlight.js theme CSS\n * @returns Complete HTML string for tabbed layout\n */\nexport function generateTabbedTemplate(\n tabsHtml: string,\n activeIndex: number,\n panelContent: string,\n themeStylesheetUrl: string\n): string {\n return `<link rel=\"stylesheet\" href=\"${themeStylesheetUrl}\">\n<article>\n <nav role=\"tablist\" aria-label=\"Code files\">${tabsHtml}</nav>\n <section role=\"tabpanel\"\n id=\"panel-${activeIndex}\"\n aria-labelledby=\"tab-${activeIndex}\"\n data-index=\"${activeIndex}\">\n ${panelContent}\n </section>\n</article>`;\n}\n", "/**\n * Gets syntax-highlighted lines from code using highlight.js\n */\nexport function getHighlightedLines(code: string | null): string[] {\n // Safety check for null/undefined code\n if (!code) {\n return [''];\n }\n\n const lines = code.split('\\n');\n\n // Remove only the last empty line if it exists (from final newline)\n if (lines.length > 0 && lines[lines.length - 1] === '') {\n lines.pop();\n }\n\n const fullCode = lines.join('\\n');\n const result = window.hljs!.highlightAuto(fullCode);\n const highlightedLines = result.value.split('\\n');\n\n // Ensure highlightedLines matches lines length\n while (highlightedLines.length > lines.length) {\n highlightedLines.pop();\n }\n\n return highlightedLines;\n}\n\n/**\n * Applies syntax highlighting to code cells within a container\n */\nexport function applySyntaxHighlighting(container: Element | ShadowRoot, code: string): void {\n const codeCells = container.querySelectorAll('.code-cell');\n\n const applyHighlighting = (): void => {\n const highlightedLines = getHighlightedLines(code);\n codeCells.forEach((cell, index) => {\n (cell as HTMLElement).innerHTML = highlightedLines[index] || ' ';\n });\n };\n\n // Use requestIdleCallback to avoid blocking the main thread\n if ('requestIdleCallback' in window) {\n requestIdleCallback(applyHighlighting);\n } else {\n applyHighlighting();\n }\n}\n", "import type { Theme, ResolvedTheme } from '../types';\n\n/**\n * Resolves a theme attribute to an actual theme value.\n * Handles 'auto' theme by detecting system preference.\n *\n * @param themeAttr - The theme attribute value ('light', 'dark', or 'auto')\n * @returns The resolved theme ('light' or 'dark')\n */\nexport function resolveTheme(themeAttr: Theme): ResolvedTheme {\n if (themeAttr === 'dark') {\n return 'dark';\n }\n\n if (themeAttr === 'light') {\n return 'light';\n }\n\n // auto - detect system preference\n const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n return prefersDark ? 'dark' : 'light';\n}\n\n/**\n * Gets the theme attribute from an element, defaulting to 'auto'.\n *\n * @param element - The HTML element to get the theme from\n * @returns The theme value\n */\nexport function getThemeAttribute(element: HTMLElement): Theme {\n const attr = element.getAttribute('theme');\n if (attr === 'dark' || attr === 'light') {\n return attr;\n }\n return 'auto';\n}\n\n/**\n * Creates a theme change handler that clears cached theme and triggers re-render.\n *\n * @param onThemeChange - Callback to invoke when theme changes\n * @returns Object with setup and cleanup functions for theme media query listener\n */\nexport function createThemeListener(onThemeChange: () => void): {\n setup: () => MediaQueryList | null;\n cleanup: (mediaQuery: MediaQueryList | null) => void;\n handler: () => void;\n} {\n const handler = onThemeChange;\n\n return {\n setup: () => {\n if (window.matchMedia) {\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n mediaQuery.addEventListener('change', handler);\n return mediaQuery;\n }\n return null;\n },\n cleanup: (mediaQuery: MediaQueryList | null) => {\n if (mediaQuery) {\n mediaQuery.removeEventListener('change', handler);\n }\n },\n handler,\n };\n}\n", "/**\n * Manages tab state for the component\n */\nexport class TabState {\n private activeTabIndex = 0;\n private renderedTabs = new Set<number>();\n private tabsFullyRendered = false;\n\n /**\n * Gets the currently active tab index\n */\n getActiveTabIndex(): number {\n return this.activeTabIndex;\n }\n\n /**\n * Sets the active tab index\n */\n setActiveTabIndex(index: number): void {\n this.activeTabIndex = index;\n }\n\n /**\n * Checks if tabs structure is fully rendered with event listeners\n */\n areTabsFullyRendered(): boolean {\n return this.tabsFullyRendered;\n }\n\n /**\n * Marks tabs as fully rendered\n */\n setTabsFullyRendered(value: boolean): void {\n this.tabsFullyRendered = value;\n }\n\n /**\n * Checks if a tab has been rendered\n */\n isTabRendered(index: number): boolean {\n return this.renderedTabs.has(index);\n }\n\n /**\n * Marks a tab as rendered\n */\n markTabAsRendered(index: number): void {\n this.renderedTabs.add(index);\n }\n\n /**\n * Clears all rendered tabs\n */\n clearRenderedTabs(): void {\n this.renderedTabs.clear();\n }\n\n /**\n * Gets the total number of tabs\n */\n getTabCount(files: unknown[]): number {\n return files.length;\n }\n\n /**\n * Resets tab state (useful when re-parsing files)\n */\n reset(): void {\n this.renderedTabs.clear();\n this.tabsFullyRendered = false;\n // Note: activeTabIndex is NOT reset here - that's handled by the caller\n }\n}\n", "import type { FileMetadata } from '../types';\nimport { escapeHtml } from '../rendering/html-generators';\n\n/**\n * Generates HTML for tab buttons\n */\nexport function generateTabsHtml(files: FileMetadata[], activeTabIndex: number): string {\n return files\n .map(\n (file, index) => `\n <button role=\"tab\"\n id=\"tab-${index}\"\n aria-selected=\"${index === activeTabIndex ? 'true' : 'false'}\"\n aria-controls=\"panel-${index}\"\n tabindex=\"${index === activeTabIndex ? '0' : '-1'}\"\n data-index=\"${index}\">\n ${escapeHtml(file.filename)}\n </button>\n `\n )\n .join('');\n}\n\n/**\n * Updates tab button states (aria attributes) when switching tabs\n */\nexport function updateTabButtonStates(shadowRoot: ShadowRoot, newActiveIndex: number): void {\n const tabs = shadowRoot.querySelectorAll('nav > button');\n tabs.forEach((tab) => {\n const tabIndex = parseInt((tab as HTMLElement).dataset.index || '0');\n const isSelected = tabIndex === newActiveIndex;\n tab.setAttribute('aria-selected', isSelected ? 'true' : 'false');\n tab.setAttribute('tabindex', isSelected ? '0' : '-1');\n });\n}\n\n/**\n * Updates tab panel visibility states when switching tabs\n */\nexport function updateTabPanelStates(shadowRoot: ShadowRoot, activeIndex: number): void {\n const allPanels = shadowRoot.querySelectorAll('section[role=\"tabpanel\"]');\n allPanels.forEach((panel) => {\n const panelIndex = parseInt((panel as HTMLElement).dataset.index || '0');\n panel.setAttribute('aria-hidden', panelIndex !== activeIndex ? 'true' : 'false');\n });\n}\n\n/**\n * Creates a new tab panel element for lazy loading\n */\nexport function createTabPanel(index: number, skeletonHtml: string): HTMLElement {\n const contentElement = document.createElement('section');\n contentElement.setAttribute('role', 'tabpanel');\n contentElement.setAttribute('id', `panel-${index}`);\n contentElement.setAttribute('aria-labelledby', `tab-${index}`);\n contentElement.dataset.index = String(index);\n contentElement.innerHTML = skeletonHtml;\n return contentElement;\n}\n\n/**\n * Handles keyboard navigation for tabs (Arrow keys, Home, End)\n * Returns the new index if navigation should occur, null otherwise\n */\nexport function handleTabKeyNavigation(key: string, currentIndex: number, maxIndex: number): number | null {\n switch (key) {\n case 'ArrowLeft':\n return currentIndex > 0 ? currentIndex - 1 : maxIndex;\n case 'ArrowRight':\n return currentIndex < maxIndex ? currentIndex + 1 : 0;\n case 'Home':\n return 0;\n case 'End':\n return maxIndex;\n default:\n return null;\n }\n}\n", "import type { FileMetadata, ResolvedTheme } from './types';\nimport { parseFileAttribute } from './parsers/file-parser';\nimport { isValidGitHubUrl, parseGitHubUrl, extractFilenameFromUrl } from './parsers/url-parser';\nimport { ensureFileLoaded } from './fetching/code-fetcher';\nimport { loadHighlightJS } from './fetching/highlightjs-loader';\nimport { StylesheetManager } from './styles/stylesheet-manager';\nimport {\n escapeHtml,\n getErrorContentHtml,\n getSkeletonContentHtml,\n getCodeContentHtml,\n} from './rendering/html-generators';\nimport {\n generateSingleFileTemplate,\n generateArticleContent,\n generateTabbedTemplate,\n} from './rendering/template-generators';\nimport { applySyntaxHighlighting } from './rendering/syntax-highlighter';\nimport { resolveTheme, getThemeAttribute } from './theme/theme-resolver';\nimport { TabState } from './tabs/tab-state';\nimport {\n generateTabsHtml,\n updateTabButtonStates,\n updateTabPanelStates,\n createTabPanel,\n handleTabKeyNavigation,\n} from './tabs/tab-controller';\n\n/**\n * GitHub Code Web Component\n * Displays GitHub file URLs with syntax highlighting\n */\nexport class GitHubCode extends HTMLElement {\n // Private fields\n #files: FileMetadata[] = [];\n #tabState = new TabState();\n #resolvedTheme: ResolvedTheme | null = null;\n #themeMediaQuery: MediaQueryList | null = null;\n #themeChangeHandler: (() => void) | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n /**\n * Safely gets a file by index, throwing if out of bounds.\n * This provides type-safe array access with noUncheckedIndexedAccess.\n */\n #getFile(index: number): FileMetadata {\n const file = this.#files[index];\n if (!file) {\n throw new Error(`File at index ${index} not found`);\n }\n return file;\n }\n\n static get observedAttributes(): string[] {\n return ['file'];\n }\n\n connectedCallback(): void {\n // Set up theme change listener for reactive theme updates\n this.#setupThemeListener();\n\n // Initial render (will parse files and display)\n void this.#render();\n }\n\n disconnectedCallback(): void {\n // Clean up theme change listener to prevent memory leaks\n if (this.#themeMediaQuery && this.#themeChangeHandler) {\n this.#themeMediaQuery.removeEventListener('change', this.#themeChangeHandler);\n }\n }\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Skip initial attribute set (oldValue is null) - connectedCallback handles initial render\n if (name === 'file' && oldValue !== null && oldValue !== newValue) {\n // Store current active tab index before re-parsing files\n const previousIndex = this.#tabState.getActiveTabIndex();\n\n // Re-parse files (clear renderedTabs since structure will be rebuilt)\n this.#tabState.reset();\n\n // Re-render with new file URLs\n void this.#render();\n\n // Try to preserve tab index if it still exists\n if (previousIndex < this.#files.length) {\n this.#tabState.setActiveTabIndex(previousIndex);\n } else {\n // Reset to first tab if previous index no longer exists\n this.#tabState.setActiveTabIndex(0);\n }\n }\n }\n\n // Private methods - theming\n #setupThemeListener(): void {\n if (window.matchMedia) {\n this.#themeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n this.#themeChangeHandler = () => {\n this.#resolvedTheme = null; // Clear cached theme\n void this.#render(); // Re-render with new theme\n };\n this.#themeMediaQuery.addEventListener('change', this.#themeChangeHandler);\n }\n }\n\n #getResolvedTheme(): ResolvedTheme {\n if (this.#resolvedTheme) {\n return this.#resolvedTheme;\n }\n\n this.#resolvedTheme = resolveTheme(getThemeAttribute(this));\n return this.#resolvedTheme;\n }\n\n // Apply constructable stylesheets to shadow root (CSP-compliant, no 'unsafe-inline' needed)\n #applyStyleSheets(includeTabStyles = false): void {\n const theme = this.#getResolvedTheme();\n const baseSheet = StylesheetManager.getBaseStyleSheet(theme);\n\n if (includeTabStyles) {\n const tabSheet = StylesheetManager.getTabStyleSheet();\n this.shadowRoot!.adoptedStyleSheets = [baseSheet, tabSheet];\n } else {\n this.shadowRoot!.adoptedStyleSheets = [baseSheet];\n }\n }\n\n // Private methods - rendering\n async #render(): Promise<void> {\n const fileAttr = this.getAttribute('file');\n\n if (!fileAttr) {\n this.#showError('Error: \"file\" attribute is required. Please provide a GitHub file URL.');\n return;\n }\n\n const fileUrls = parseFileAttribute(fileAttr);\n\n if (fileUrls.length === 0) {\n this.#showError('Error: \"file\" attribute is required. Please provide a GitHub file URL.');\n return;\n }\n\n // Validate all URLs\n const invalidUrl = fileUrls.find((url) => !isValidGitHubUrl(url));\n if (invalidUrl) {\n // Sanitize URL before using in error message to prevent XSS\n const sanitizedUrl = escapeHtml(invalidUrl);\n this.#showError(\n `Error: Invalid GitHub URL format: ${sanitizedUrl}. Expected format: https://github.com/{owner}/{repo}/blob/{commit}/{path}`\n );\n return;\n }\n\n // Parse URLs to get file metadata first (don't fetch content yet - lazy load on demand)\n this.#files = fileUrls.map((url) => {\n try {\n const { rawUrl, filename } = parseGitHubUrl(url);\n return {\n filename,\n rawUrl,\n url,\n code: null,\n error: null,\n loaded: false,\n };\n } catch (error) {\n const filename = extractFilenameFromUrl(url);\n return {\n filename,\n rawUrl: url,\n url,\n code: null,\n error: error instanceof Error ? error.message : String(error),\n loaded: true, // Parse error - no point retrying\n };\n }\n });\n\n // Render skeleton UI immediately (tabs or single file structure)\n if (fileUrls.length === 1) {\n // Single file: show header + skeleton\n const file = this.#getFile(0);\n if (file.error) {\n this.#showError(`Error loading code: ${file.error}`);\n return;\n }\n // Show skeleton immediately\n this.shadowRoot!.innerHTML = generateSingleFileTemplate(\n file.filename,\n getSkeletonContentHtml(),\n StylesheetManager.getHighlightJSThemeUrl(this.#getResolvedTheme())\n );\n this.#applyStyleSheets(false);\n } else {\n // Multiple files: show tabs + skeleton immediately\n this.#renderTabsSkeletonStructure();\n }\n\n // Load highlight.js in background (UI already visible)\n try {\n await loadHighlightJS();\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.#showError(`Error loading highlight.js: ${errorMsg}`);\n return;\n }\n\n // Now load actual content\n if (fileUrls.length === 1) {\n await this.#displayCode(0);\n } else {\n await this.#displayWithTabs();\n }\n }\n\n async #displayCode(index: number): Promise<void> {\n const file = this.#getFile(index);\n const themeUrl = StylesheetManager.getHighlightJSThemeUrl(this.#getResolvedTheme());\n\n // Render skeleton immediately\n this.shadowRoot!.innerHTML = generateSingleFileTemplate(file.filename, getSkeletonContentHtml(), themeUrl);\n this.#applyStyleSheets(false);\n\n // Load content in background and update when ready\n await ensureFileLoaded(file, false);\n\n const contentArea = this.shadowRoot!.querySelector('article');\n if (file.error) {\n contentArea!.innerHTML = generateArticleContent(file.filename, getErrorContentHtml(file.error, true));\n // Add retry button event listener\n const retryButton = contentArea!.querySelector('.retry-button');\n if (retryButton) {\n retryButton.addEventListener('click', () => {\n void (async () => {\n // Show skeleton during retry\n contentArea!.innerHTML = generateArticleContent(file.filename, getSkeletonContentHtml());\n await ensureFileLoaded(file, true);\n await this.#displayCode(index);\n })();\n });\n }\n } else {\n contentArea!.innerHTML = generateArticleContent(file.filename, getCodeContentHtml(file.code));\n if (window.hljs) {\n applySyntaxHighlighting(this.shadowRoot!, file.code!);\n }\n }\n }\n\n async #displayWithTabs(): Promise<void> {\n if (!this.#tabState.areTabsFullyRendered()) {\n // First full render - build entire structure with event listeners\n await this.#renderTabsStructure();\n this.#tabState.setTabsFullyRendered(true);\n } else {\n // Tab switching - update only what's needed\n this.#switchToTab(this.#tabState.getActiveTabIndex());\n }\n }\n\n #renderTabsSkeletonStructure(): void {\n const activeIndex = this.#tabState.getActiveTabIndex();\n const tabsHtml = generateTabsHtml(this.#files, activeIndex);\n const themeUrl = StylesheetManager.getHighlightJSThemeUrl(this.#getResolvedTheme());\n\n this.shadowRoot!.innerHTML = generateTabbedTemplate(tabsHtml, activeIndex, getSkeletonContentHtml(), themeUrl);\n this.#applyStyleSheets(true);\n }\n\n async #renderTabsStructure(): Promise<void> {\n const activeIndex = this.#tabState.getActiveTabIndex();\n const tabsHtml = generateTabsHtml(this.#files, activeIndex);\n const themeUrl = StylesheetManager.getHighlightJSThemeUrl(this.#getResolvedTheme());\n\n // Render tabs and skeleton immediately\n this.shadowRoot!.innerHTML = generateTabbedTemplate(tabsHtml, activeIndex, getSkeletonContentHtml(), themeUrl);\n this.#applyStyleSheets(true);\n\n // Use event delegation on parent nav element to prevent race conditions\n const nav = this.shadowRoot!.querySelector('nav[role=\"tablist\"]');\n nav!.addEventListener('click', (e) => {\n const tab = (e.target as Element).closest('button[role=\"tab\"]');\n if (tab) {\n const newIndex = parseInt((tab as HTMLElement).dataset.index || '0');\n if (newIndex !== this.#tabState.getActiveTabIndex()) {\n this.#tabState.setActiveTabIndex(newIndex);\n void this.#displayWithTabs();\n }\n }\n });\n\n // Add keyboard navigation for accessibility\n nav!.addEventListener('keydown', (e) => {\n const keyboardEvent = e as KeyboardEvent;\n const tab = (keyboardEvent.target as Element).closest('button[role=\"tab\"]');\n if (!tab) {\n return;\n }\n\n const currentIndex = this.#tabState.getActiveTabIndex();\n const maxIndex = this.#files.length - 1;\n\n const newIndex = handleTabKeyNavigation(keyboardEvent.key, currentIndex, maxIndex);\n\n if (newIndex !== null) {\n e.preventDefault();\n this.#tabState.setActiveTabIndex(newIndex);\n void this.#displayWithTabs().then(() => {\n // Focus the newly selected tab\n const newTab = this.shadowRoot!.querySelector(`button[data-index=\"${newIndex}\"]`);\n if (newTab) {\n (newTab as HTMLElement).focus();\n }\n });\n }\n });\n\n // Mark this tab as rendered\n this.#tabState.markTabAsRendered(activeIndex);\n\n // Load active file content in background and update when ready\n const contentElement = this.shadowRoot!.querySelector('section[role=\"tabpanel\"]');\n await this.#loadAndRenderTabContent(activeIndex, contentElement as HTMLElement).catch((error: unknown) => {\n console.error('Failed to load tab content:', error);\n (contentElement as HTMLElement).innerHTML = getErrorContentHtml(\n `Failed to load content: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n\n #switchToTab(newIndex: number): void {\n // Update tab buttons - use aria-selected and tabindex for accessibility\n updateTabButtonStates(this.shadowRoot!, newIndex);\n\n // Check if content for this tab already exists\n let contentElement = this.shadowRoot!.querySelector(`section[role=\"tabpanel\"][data-index=\"${newIndex}\"]`);\n\n if (!contentElement) {\n // Create skeleton immediately (lazy load pattern)\n contentElement = createTabPanel(newIndex, getSkeletonContentHtml());\n\n // Append skeleton to DOM immediately (user sees it right away)\n this.shadowRoot!.querySelector('article')!.appendChild(contentElement);\n this.#tabState.markTabAsRendered(newIndex);\n\n // Load content asynchronously and update when ready\n void this.#loadAndRenderTabContent(newIndex, contentElement as HTMLElement).catch((error: unknown) => {\n console.error('Failed to load tab content:', error);\n (contentElement as HTMLElement).innerHTML = getErrorContentHtml(\n `Failed to load content: ${error instanceof Error ? error.message : String(error)}`\n );\n });\n }\n\n // Update panels - use aria-hidden instead of .hidden class\n updateTabPanelStates(this.shadowRoot!, newIndex);\n }\n\n async #loadAndRenderTabContent(index: number, contentElement: HTMLElement): Promise<void> {\n // Load file content\n const file = this.#getFile(index);\n await ensureFileLoaded(file, false);\n\n // Replace skeleton with actual content\n if (file.error || !file.code) {\n const errorMsg = file.error || 'Failed to load content: No content available';\n contentElement.innerHTML = getErrorContentHtml(errorMsg, true);\n\n // Add retry button event listener\n const retryButton = contentElement.querySelector('.retry-button');\n if (retryButton) {\n retryButton.addEventListener('click', () => {\n void (async () => {\n // Show skeleton during retry\n contentElement.innerHTML = getSkeletonContentHtml();\n await ensureFileLoaded(file, true);\n await this.#loadAndRenderTabContent(index, contentElement);\n })();\n });\n }\n } else {\n contentElement.innerHTML = getCodeContentHtml(file.code);\n // Apply syntax highlighting\n if (window.hljs) {\n applySyntaxHighlighting(contentElement, file.code);\n }\n }\n }\n\n #showError(message: string): void {\n this.shadowRoot!.innerHTML = `\n <div class=\"error\">${escapeHtml(message)}</div>\n `;\n this.#applyStyleSheets(false);\n }\n}\n", "import { GitHubCode } from './github-code';\n\n// Register the custom element\ncustomElements.define('github-code', GitHubCode);\n\n// Export for potential programmatic usage\nexport { GitHubCode };\n"],
5
+ "mappings": "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",
6
+ "names": ["parseFileAttribute", "fileAttr", "url", "GITHUB_BLOB_PATTERN", "isValidGitHubUrl", "url", "parseGitHubUrl", "match", "owner", "repo", "commit", "path", "rawUrl", "filename", "extractFilenameFromUrl", "fetchCode", "url", "response", "error", "ensureFileLoaded", "file", "isRetry", "code", "highlightJSLoadingPromise", "loadHighlightJS", "resolve", "reject", "script", "errorMsg", "base_light_default", "base_dark_default", "tab_default", "StylesheetManager", "theme", "base_dark_default", "base_light_default", "tab_default", "escapeHtml", "text", "div", "getErrorContentHtml", "errorMessage", "showRetry", "retryButton", "getSkeletonContentHtml", "_", "i", "width", "getCodeContentHtml", "code", "lines", "line", "index", "generateSingleFileTemplate", "filename", "content", "themeStylesheetUrl", "escapeHtml", "generateArticleContent", "generateTabbedTemplate", "tabsHtml", "activeIndex", "panelContent", "getHighlightedLines", "code", "lines", "fullCode", "highlightedLines", "applySyntaxHighlighting", "container", "codeCells", "applyHighlighting", "cell", "index", "resolveTheme", "themeAttr", "getThemeAttribute", "element", "attr", "TabState", "index", "value", "files", "generateTabsHtml", "files", "activeTabIndex", "file", "index", "escapeHtml", "updateTabButtonStates", "shadowRoot", "newActiveIndex", "tab", "isSelected", "updateTabPanelStates", "activeIndex", "panel", "panelIndex", "createTabPanel", "skeletonHtml", "contentElement", "handleTabKeyNavigation", "key", "currentIndex", "maxIndex", "GitHubCode", "#files", "#tabState", "TabState", "#resolvedTheme", "#themeMediaQuery", "#themeChangeHandler", "#getFile", "index", "file", "#setupThemeListener", "#render", "name", "oldValue", "newValue", "previousIndex", "#getResolvedTheme", "resolveTheme", "getThemeAttribute", "#applyStyleSheets", "includeTabStyles", "theme", "baseSheet", "StylesheetManager", "tabSheet", "fileAttr", "#showError", "fileUrls", "parseFileAttribute", "invalidUrl", "url", "isValidGitHubUrl", "sanitizedUrl", "escapeHtml", "rawUrl", "filename", "parseGitHubUrl", "error", "extractFilenameFromUrl", "generateSingleFileTemplate", "getSkeletonContentHtml", "#renderTabsSkeletonStructure", "loadHighlightJS", "errorMsg", "#displayCode", "#displayWithTabs", "themeUrl", "ensureFileLoaded", "contentArea", "generateArticleContent", "getErrorContentHtml", "retryButton", "getCodeContentHtml", "applySyntaxHighlighting", "#switchToTab", "#renderTabsStructure", "activeIndex", "tabsHtml", "generateTabsHtml", "generateTabbedTemplate", "nav", "e", "tab", "newIndex", "keyboardEvent", "currentIndex", "maxIndex", "handleTabKeyNavigation", "newTab", "contentElement", "#loadAndRenderTabContent", "updateTabButtonStates", "createTabPanel", "updateTabPanelStates", "message", "GitHubCode"]
7
+ }
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "github-code",
3
+ "version": "0.1.0",
4
+ "description": "Custom element for embedding GitHub source files with syntax highlighting",
5
+ "type": "module",
6
+ "main": "dist/github-code.min.js",
7
+ "module": "dist/github-code.min.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/github-code.min.js",
11
+ "default": "./dist/github-code.min.js"
12
+ },
13
+ "./dist/*": "./dist/*"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "node esbuild.config.mjs",
20
+ "dev": "node esbuild.config.mjs --watch",
21
+ "type-check": "tsc --noEmit",
22
+ "lint": "eslint src",
23
+ "lint:fix": "eslint src --fix",
24
+ "format": "prettier --write \"src/**/*.ts\"",
25
+ "format:check": "prettier --check \"src/**/*.ts\"",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "test:ui": "vitest --ui",
29
+ "test:coverage": "vitest run --coverage",
30
+ "pretest:e2e": "npm run build",
31
+ "test:e2e": "playwright test",
32
+ "test:e2e:ui": "playwright test --ui",
33
+ "test:e2e:debug": "playwright test --debug",
34
+ "test:e2e:headed": "playwright test --headed",
35
+ "test:all": "npm run test:coverage && npm run test:e2e",
36
+ "test:ci": "npm run lint && npm run type-check && npm run build && npm run test:all"
37
+ },
38
+ "keywords": [
39
+ "web-component",
40
+ "github",
41
+ "code",
42
+ "syntax-highlighting"
43
+ ],
44
+ "author": "",
45
+ "license": "MIT",
46
+ "devDependencies": {
47
+ "@eslint/js": "^9.39.2",
48
+ "@playwright/test": "^1.57.0",
49
+ "@semantic-release/changelog": "^6.0.3",
50
+ "@semantic-release/git": "^10.0.1",
51
+ "@types/node": "^25.0.2",
52
+ "@vitest/coverage-v8": "^4.0.15",
53
+ "@vitest/ui": "^4.0.15",
54
+ "esbuild": "0.27.1",
55
+ "eslint": "^9.39.2",
56
+ "eslint-config-prettier": "^10.1.8",
57
+ "happy-dom": "^20.0.11",
58
+ "prettier": "^3.7.4",
59
+ "semantic-release": "^24.2.9",
60
+ "typescript": "5.9.3",
61
+ "typescript-eslint": "^8.50.0",
62
+ "vitest": "^4.0.15"
63
+ },
64
+ "engines": {
65
+ "node": ">=24.12.0"
66
+ }
67
+ }