github-code 0.2.0 → 0.2.1

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.
package/README.md CHANGED
@@ -11,6 +11,8 @@ A web component for displaying GitHub files with syntax highlighting.
11
11
 
12
12
  See the live demo: **[github-code Demo](https://destan.github.io/github-code/)**
13
13
 
14
+ ![Demo page SS](./docs/images/demo.png)
15
+
14
16
  ## Installation
15
17
 
16
18
  Include the script in your HTML:
@@ -48,10 +50,11 @@ Comma-separate URLs to display files as tabs:
48
50
 
49
51
  ## Attributes
50
52
 
51
- | Attribute | Default | Description |
52
- |-----------|---------|-------------|
53
- | `file` | _(required)_ | GitHub blob URL(s). Comma-separated for multiple files. |
54
- | `theme` | `auto` | `light`, `dark`, or `auto` (follows system preference) |
53
+ | Attribute | Default | Description |
54
+ |-----------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
55
+ | `file` | _(required)_ | GitHub blob URL(s). Comma-separated for multiple files. |
56
+ | `theme` | `auto` | `light`, `dark`, or `auto` (follows system preference) |
57
+ | `highlightjs-url` | `<empty>` | <ul><li>If omitted, uses global `window.hljs` if available, otherwise uses jsDelivr CDN</li><li>Custom highlight.js URL. Omit to use global `window.hljs` if available.</li></ul> |
55
58
 
56
59
  ## URL Format
57
60
 
@@ -61,6 +64,10 @@ URLs must be GitHub blob URLs:
61
64
  https://github.com/{owner}/{repo}/blob/{branch|commit}/{path}
62
65
  ```
63
66
 
67
+ Use the `permalink` of a file:
68
+
69
+ ![permalink in Github](./docs/images/github-copy-permalink.png)
70
+
64
71
  ## Features
65
72
 
66
73
  - Syntax highlighting via highlight.js (auto-detected language)
@@ -69,13 +76,49 @@ https://github.com/{owner}/{repo}/blob/{branch|commit}/{path}
69
76
  - CSP-compliant (no inline styles)
70
77
  - Shadow DOM encapsulation
71
78
 
79
+ ## Advanced Usage
80
+
81
+ ### Custom highlight.js
82
+
83
+ By default, highlight.js is loaded from jsDelivr CDN. You can provide your own:
84
+
85
+ ```html
86
+ <github-code
87
+ file="https://github.com/owner/repo/blob/main/file.ts"
88
+ highlightjs-url="https://your-cdn.com/highlight.min.js">
89
+ </github-code>
90
+ ```
91
+
92
+ If highlight.js is already loaded globally (`window.hljs`), the component will use it automatically.
93
+
94
+ ### Debugging
95
+
96
+ Access component info via the static property:
97
+
98
+ ```javascript
99
+ console.log(GitHubCode.info);
100
+ // { version: "0.2.0", highlightjsUrl: "...", highlightjsSource: "cdn-default" }
101
+ ```
102
+
103
+ ## Troubleshooting
104
+
105
+ ### Content Security Policy (CSP)
106
+
107
+ If using the default CDN and you have a strict CSP, add:
108
+
109
+ ```
110
+ script-src https://cdn.jsdelivr.net;
111
+ ```
112
+
113
+ Or use `highlightjs-url` to load from an allowed source.
114
+
72
115
  ## Development
73
116
 
74
117
  ```bash
75
118
  npm install
76
- npm run build # Build dist files
77
119
  npm run dev # Watch mode
78
120
  npm run test:ci # Full CI check (lint + typecheck + build + tests)
121
+ npm run build # Build dist files
79
122
  ```
80
123
 
81
124
  ## License
@@ -140,7 +140,7 @@ async function loadHighlightJS(customUrl) {
140
140
  // package.json
141
141
  var package_default = {
142
142
  name: "github-code",
143
- version: "0.1.0",
143
+ version: "0.2.0",
144
144
  description: "Custom element for embedding GitHub source files with syntax highlighting",
145
145
  type: "module",
146
146
  main: "dist/github-code.min.js",
@@ -568,7 +568,7 @@ var GitHubCode = class extends HTMLElement {
568
568
  }
569
569
  } else if (name === "theme") {
570
570
  this.#resolvedTheme = null;
571
- void this.#render();
571
+ this.#updateThemeOnly();
572
572
  }
573
573
  }
574
574
  // Private methods - theming
@@ -577,11 +577,21 @@ var GitHubCode = class extends HTMLElement {
577
577
  this.#themeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
578
578
  this.#themeChangeHandler = () => {
579
579
  this.#resolvedTheme = null;
580
- void this.#render();
580
+ this.#updateThemeOnly();
581
581
  };
582
582
  this.#themeMediaQuery.addEventListener("change", this.#themeChangeHandler);
583
583
  }
584
584
  }
585
+ #updateThemeOnly() {
586
+ const theme = this.#getResolvedTheme();
587
+ const themeUrl = StylesheetManager.getHighlightJSThemeUrl(theme);
588
+ const link = this.shadowRoot?.querySelector('link[rel="stylesheet"]');
589
+ if (link) {
590
+ link.setAttribute("href", themeUrl);
591
+ }
592
+ const hasTabs = this.#files.length > 1;
593
+ this.#applyStyleSheets(hasTabs);
594
+ }
585
595
  #getResolvedTheme() {
586
596
  if (this.#resolvedTheme) {
587
597
  return this.#resolvedTheme;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/parsers/file-parser.ts", "../src/parsers/url-parser.ts", "../src/fetching/code-fetcher.ts", "../src/fetching/highlightjs-loader.ts", "../package.json", "../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 * Track which URL was loaded for introspection\n */\nlet loadedHighlightJSUrl: string | null = null;\n\n/**\n * Track the source of the loaded highlight.js library\n */\nlet highlightJSSource: 'user-provided' | 'cdn-default' | 'global' = 'global';\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 * Get the URL that was loaded (or will be loaded)\n * Returns \"auto\" if using default behavior or global hljs\n */\nexport function getLoadedHighlightJSUrl(): string {\n return loadedHighlightJSUrl || 'auto';\n}\n\n/**\n * Get the source of the highlight.js library\n */\nexport function getHighlightJSSource(): 'user-provided' | 'cdn-default' | 'global' {\n return highlightJSSource;\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 * @param customUrl - Optional custom URL to load highlight.js from\n */\nexport async function loadHighlightJS(customUrl?: string): Promise<void> {\n // If already loaded globally, track and return\n if (window.hljs) {\n if (!loadedHighlightJSUrl) {\n highlightJSSource = 'global';\n loadedHighlightJSUrl = 'auto';\n }\n return;\n }\n\n // If currently loading, check for URL conflicts and return the existing promise\n if (highlightJSLoadingPromise) {\n if (customUrl && loadedHighlightJSUrl && customUrl !== loadedHighlightJSUrl) {\n console.warn(\n `[github-code] Different highlight.js URLs detected. ` +\n `Already loading from \"${loadedHighlightJSUrl}\", ` +\n `but this instance requested \"${customUrl}\". ` +\n `The first URL will be used for all instances.`\n );\n }\n return highlightJSLoadingPromise;\n }\n\n // Determine which URL to use\n const DEFAULT_URL = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js';\n const urlToLoad = customUrl || DEFAULT_URL;\n\n // Track state\n loadedHighlightJSUrl = urlToLoad;\n highlightJSSource = customUrl ? 'user-provided' : 'cdn-default';\n\n // Create new loading promise\n highlightJSLoadingPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = urlToLoad;\n\n // Only add integrity for default CDN URL\n if (!customUrl) {\n script.integrity = 'sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU';\n script.crossOrigin = 'anonymous';\n }\n\n script.onload = () => {\n highlightJSLoadingPromise = null;\n\n // Validate that window.hljs was defined\n if (!window.hljs) {\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n reject(\n new Error(\n `The script at \"${urlToLoad}\" loaded successfully but did not define window.hljs. ` +\n `Please ensure this URL points to a valid highlight.js library.`\n )\n );\n return;\n }\n\n resolve();\n };\n\n script.onerror = () => {\n highlightJSLoadingPromise = null;\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n\n const errorMsg =\n `Failed to load highlight.js library from: ${urlToLoad}\\n` +\n (customUrl\n ? 'Please check that the URL is correct and accessible.'\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\n document.head.appendChild(script);\n });\n\n return highlightJSLoadingPromise;\n}\n", "{\n \"name\": \"github-code\",\n \"version\": \"0.1.0\",\n \"description\": \"Custom element for embedding GitHub source files with syntax highlighting\",\n \"type\": \"module\",\n \"main\": \"dist/github-code.min.js\",\n \"module\": \"dist/github-code.min.js\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/github-code.min.js\",\n \"default\": \"./dist/github-code.min.js\"\n },\n \"./dist/*\": \"./dist/*\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"node esbuild.config.mjs\",\n \"dev\": \"node esbuild.config.mjs --watch\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"pretest:e2e\": \"npm run build\",\n \"test:e2e\": \"playwright test\",\n \"test:e2e:ui\": \"playwright test --ui\",\n \"test:e2e:debug\": \"playwright test --debug\",\n \"test:e2e:headed\": \"playwright test --headed\",\n \"test:all\": \"npm run test:coverage && npm run test:e2e\",\n \"test:ci\": \"npm run lint && npm run type-check && npm run build && npm run test:all\"\n },\n \"keywords\": [\n \"web-component\",\n \"github\",\n \"code\",\n \"syntax-highlighting\"\n ],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/destan/github-code.git\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@playwright/test\": \"^1.57.0\",\n \"@semantic-release/changelog\": \"^6.0.3\",\n \"@semantic-release/git\": \"^10.0.1\",\n \"@types/node\": \"^25.0.2\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"@vitest/ui\": \"^4.0.15\",\n \"esbuild\": \"0.27.1\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"happy-dom\": \"^20.0.11\",\n \"prettier\": \"^3.7.4\",\n \"semantic-release\": \"^24.2.9\",\n \"typescript\": \"5.9.3\",\n \"typescript-eslint\": \"^8.50.0\",\n \"vitest\": \"^4.0.15\"\n },\n \"engines\": {\n \"node\": \">=24.12.0\"\n }\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, GitHubCodeInfo } from './types';\nimport { parseFileAttribute } from './parsers/file-parser';\nimport { isValidGitHubUrl, parseGitHubUrl, extractFilenameFromUrl } from './parsers/url-parser';\nimport { ensureFileLoaded } from './fetching/code-fetcher';\nimport { loadHighlightJS, getLoadedHighlightJSUrl, getHighlightJSSource } from './fetching/highlightjs-loader';\nimport packageJson from '../package.json';\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', 'theme'];\n }\n\n /**\n * Runtime information about the component and loaded libraries\n */\n static get info(): GitHubCodeInfo {\n return {\n version: packageJson.version,\n highlightjsUrl: getLoadedHighlightJSUrl(),\n highlightjsSource: getHighlightJSSource(),\n };\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 (oldValue === null || oldValue === newValue) {\n return;\n }\n\n if (name === 'file') {\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 } else if (name === 'theme') {\n this.#resolvedTheme = null; // Clear cached theme\n void this.#render();\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 // Read custom URL attribute (treat \"auto\" as default)\n const customUrlAttr = this.getAttribute('highlightjs-url');\n const customUrl = customUrlAttr && customUrlAttr !== 'auto' ? customUrlAttr : undefined;\n\n await loadHighlightJS(customUrl);\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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",
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 * Track which URL was loaded for introspection\n */\nlet loadedHighlightJSUrl: string | null = null;\n\n/**\n * Track the source of the loaded highlight.js library\n */\nlet highlightJSSource: 'user-provided' | 'cdn-default' | 'global' = 'global';\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 * Get the URL that was loaded (or will be loaded)\n * Returns \"auto\" if using default behavior or global hljs\n */\nexport function getLoadedHighlightJSUrl(): string {\n return loadedHighlightJSUrl || 'auto';\n}\n\n/**\n * Get the source of the highlight.js library\n */\nexport function getHighlightJSSource(): 'user-provided' | 'cdn-default' | 'global' {\n return highlightJSSource;\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 * @param customUrl - Optional custom URL to load highlight.js from\n */\nexport async function loadHighlightJS(customUrl?: string): Promise<void> {\n // If already loaded globally, track and return\n if (window.hljs) {\n if (!loadedHighlightJSUrl) {\n highlightJSSource = 'global';\n loadedHighlightJSUrl = 'auto';\n }\n return;\n }\n\n // If currently loading, check for URL conflicts and return the existing promise\n if (highlightJSLoadingPromise) {\n if (customUrl && loadedHighlightJSUrl && customUrl !== loadedHighlightJSUrl) {\n console.warn(\n `[github-code] Different highlight.js URLs detected. ` +\n `Already loading from \"${loadedHighlightJSUrl}\", ` +\n `but this instance requested \"${customUrl}\". ` +\n `The first URL will be used for all instances.`\n );\n }\n return highlightJSLoadingPromise;\n }\n\n // Determine which URL to use\n const DEFAULT_URL = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js';\n const urlToLoad = customUrl || DEFAULT_URL;\n\n // Track state\n loadedHighlightJSUrl = urlToLoad;\n highlightJSSource = customUrl ? 'user-provided' : 'cdn-default';\n\n // Create new loading promise\n highlightJSLoadingPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = urlToLoad;\n\n // Only add integrity for default CDN URL\n if (!customUrl) {\n script.integrity = 'sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU';\n script.crossOrigin = 'anonymous';\n }\n\n script.onload = () => {\n highlightJSLoadingPromise = null;\n\n // Validate that window.hljs was defined\n if (!window.hljs) {\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n reject(\n new Error(\n `The script at \"${urlToLoad}\" loaded successfully but did not define window.hljs. ` +\n `Please ensure this URL points to a valid highlight.js library.`\n )\n );\n return;\n }\n\n resolve();\n };\n\n script.onerror = () => {\n highlightJSLoadingPromise = null;\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n\n const errorMsg =\n `Failed to load highlight.js library from: ${urlToLoad}\\n` +\n (customUrl\n ? 'Please check that the URL is correct and accessible.'\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\n document.head.appendChild(script);\n });\n\n return highlightJSLoadingPromise;\n}\n", "{\n \"name\": \"github-code\",\n \"version\": \"0.2.0\",\n \"description\": \"Custom element for embedding GitHub source files with syntax highlighting\",\n \"type\": \"module\",\n \"main\": \"dist/github-code.min.js\",\n \"module\": \"dist/github-code.min.js\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/github-code.min.js\",\n \"default\": \"./dist/github-code.min.js\"\n },\n \"./dist/*\": \"./dist/*\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"node esbuild.config.mjs\",\n \"dev\": \"node esbuild.config.mjs --watch\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"pretest:e2e\": \"npm run build\",\n \"test:e2e\": \"playwright test\",\n \"test:e2e:ui\": \"playwright test --ui\",\n \"test:e2e:debug\": \"playwright test --debug\",\n \"test:e2e:headed\": \"playwright test --headed\",\n \"test:all\": \"npm run test:coverage && npm run test:e2e\",\n \"test:ci\": \"npm run lint && npm run type-check && npm run build && npm run test:all\"\n },\n \"keywords\": [\n \"web-component\",\n \"github\",\n \"code\",\n \"syntax-highlighting\"\n ],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/destan/github-code.git\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@playwright/test\": \"^1.57.0\",\n \"@semantic-release/changelog\": \"^6.0.3\",\n \"@semantic-release/git\": \"^10.0.1\",\n \"@types/node\": \"^25.0.2\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"@vitest/ui\": \"^4.0.15\",\n \"esbuild\": \"0.27.1\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"happy-dom\": \"^20.0.11\",\n \"prettier\": \"^3.7.4\",\n \"semantic-release\": \"^24.2.9\",\n \"typescript\": \"5.9.3\",\n \"typescript-eslint\": \"^8.50.0\",\n \"vitest\": \"^4.0.15\"\n },\n \"engines\": {\n \"node\": \">=24.12.0\"\n }\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, GitHubCodeInfo } from './types';\nimport { parseFileAttribute } from './parsers/file-parser';\nimport { isValidGitHubUrl, parseGitHubUrl, extractFilenameFromUrl } from './parsers/url-parser';\nimport { ensureFileLoaded } from './fetching/code-fetcher';\nimport { loadHighlightJS, getLoadedHighlightJSUrl, getHighlightJSSource } from './fetching/highlightjs-loader';\nimport packageJson from '../package.json';\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', 'theme'];\n }\n\n /**\n * Runtime information about the component and loaded libraries\n */\n static get info(): GitHubCodeInfo {\n return {\n version: packageJson.version,\n highlightjsUrl: getLoadedHighlightJSUrl(),\n highlightjsSource: getHighlightJSSource(),\n };\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 (oldValue === null || oldValue === newValue) {\n return;\n }\n\n if (name === 'file') {\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 } else if (name === 'theme') {\n this.#resolvedTheme = null; // Clear cached theme\n this.#updateThemeOnly(); // Just update styles, preserve loaded content\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 this.#updateThemeOnly(); // Update styles without re-rendering\n };\n this.#themeMediaQuery.addEventListener('change', this.#themeChangeHandler);\n }\n }\n\n #updateThemeOnly(): void {\n // Update the highlight.js theme stylesheet link\n const theme = this.#getResolvedTheme();\n const themeUrl = StylesheetManager.getHighlightJSThemeUrl(theme);\n\n const link = this.shadowRoot?.querySelector('link[rel=\"stylesheet\"]');\n if (link) {\n link.setAttribute('href', themeUrl);\n }\n\n // Re-apply constructable stylesheets with new theme\n const hasTabs = this.#files.length > 1;\n this.#applyStyleSheets(hasTabs);\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 // Read custom URL attribute (treat \"auto\" as default)\n const customUrlAttr = this.getAttribute('highlightjs-url');\n const customUrl = customUrlAttr && customUrlAttr !== 'auto' ? customUrlAttr : undefined;\n\n await loadHighlightJS(customUrl);\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
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
- function M(n){return n.split(",").map(e=>e.trim()).filter(e=>e.length>0)}var O=/^https:\/\/github\.com\/[^\/]+\/[^\/]+\/blob\/.+/;function R(n){return O.test(n)}function E(n){let e=/^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.+)$/.exec(n);if(!e)throw new Error("Failed to parse GitHub URL");let t=e[1],r=e[2],o=e[3],i=e[4];if(!t||!r||!o||!i)throw new Error("Failed to parse GitHub URL");let a=`https://raw.githubusercontent.com/${t}/${r}/${o}/${i}`,h=i.split("/").pop()||"unknown";return{rawUrl:a,filename:h}}function C(n){try{return/\/([^\/]+)$/.exec(n)?.[1]??"unknown"}catch{return"unknown"}}async function _(n){try{let e=await fetch(n);if(!e.ok)throw new Error(`Failed to fetch code (HTTP ${e.status}). Please check if the URL is accessible.`);return await e.text()}catch(e){throw e instanceof TypeError&&e.message.includes("fetch")?new Error(`Failed to fetch code from ${n}. This is likely a CORS (Cross-Origin Resource Sharing) error. The server needs to allow requests from this origin. GitHub's raw.githubusercontent.com should work without CORS issues.`):e}}async function g(n,e=!1){if(!(n.loaded&&!e)){e&&(n.loaded=!1,n.error=null);try{let t=await _(n.rawUrl);n.code=t,n.error=null,n.loaded=!0}catch(t){n.code=null,n.error=t instanceof Error?t.message:String(t),n.loaded=!0}}}var u=null,l=null,p="global";function A(){return l||"auto"}function $(){return p}async function F(n){if(window.hljs){l||(p="global",l="auto");return}if(u)return n&&l&&n!==l&&console.warn(`[github-code] Different highlight.js URLs detected. Already loading from "${l}", but this instance requested "${n}". The first URL will be used for all instances.`),u;let t=n||"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js";return l=t,p=n?"user-provided":"cdn-default",u=new Promise((r,o)=>{let i=document.createElement("script");i.src=t,n||(i.integrity="sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU",i.crossOrigin="anonymous"),i.onload=()=>{if(u=null,!window.hljs){l=null,p="global",o(new Error(`The script at "${t}" loaded successfully but did not define window.hljs. Please ensure this URL points to a valid highlight.js library.`));return}r()},i.onerror=()=>{u=null,l=null,p="global";let a=`Failed to load highlight.js library from: ${t}
1
+ function R(n){return n.split(",").map(e=>e.trim()).filter(e=>e.length>0)}var O=/^https:\/\/github\.com\/[^\/]+\/[^\/]+\/blob\/.+/;function M(n){return O.test(n)}function E(n){let e=/^https:\/\/github\.com\/([^\/]+)\/([^\/]+)\/blob\/([^\/]+)\/(.+)$/.exec(n);if(!e)throw new Error("Failed to parse GitHub URL");let t=e[1],r=e[2],o=e[3],i=e[4];if(!t||!r||!o||!i)throw new Error("Failed to parse GitHub URL");let a=`https://raw.githubusercontent.com/${t}/${r}/${o}/${i}`,h=i.split("/").pop()||"unknown";return{rawUrl:a,filename:h}}function A(n){try{return/\/([^\/]+)$/.exec(n)?.[1]??"unknown"}catch{return"unknown"}}async function _(n){try{let e=await fetch(n);if(!e.ok)throw new Error(`Failed to fetch code (HTTP ${e.status}). Please check if the URL is accessible.`);return await e.text()}catch(e){throw e instanceof TypeError&&e.message.includes("fetch")?new Error(`Failed to fetch code from ${n}. This is likely a CORS (Cross-Origin Resource Sharing) error. The server needs to allow requests from this origin. GitHub's raw.githubusercontent.com should work without CORS issues.`):e}}async function g(n,e=!1){if(!(n.loaded&&!e)){e&&(n.loaded=!1,n.error=null);try{let t=await _(n.rawUrl);n.code=t,n.error=null,n.loaded=!0}catch(t){n.code=null,n.error=t instanceof Error?t.message:String(t),n.loaded=!0}}}var u=null,d=null,p="global";function C(){return d||"auto"}function $(){return p}async function F(n){if(window.hljs){d||(p="global",d="auto");return}if(u)return n&&d&&n!==d&&console.warn(`[github-code] Different highlight.js URLs detected. Already loading from "${d}", but this instance requested "${n}". The first URL will be used for all instances.`),u;let t=n||"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js";return d=t,p=n?"user-provided":"cdn-default",u=new Promise((r,o)=>{let i=document.createElement("script");i.src=t,n||(i.integrity="sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU",i.crossOrigin="anonymous"),i.onload=()=>{if(u=null,!window.hljs){d=null,p="global",o(new Error(`The script at "${t}" loaded successfully but did not define window.hljs. Please ensure this URL points to a valid highlight.js library.`));return}r()},i.onerror=()=>{u=null,d=null,p="global";let a=`Failed to load highlight.js library from: ${t}
2
2
  `+(n?"Please check that the URL is correct and accessible.":`If you have a Content Security Policy (CSP), ensure it allows:
3
3
  script-src https://cdnjs.cloudflare.com
4
- style-src https://cdnjs.cloudflare.com`);o(new Error(a))},document.head.appendChild(i)}),u}var U={name:"github-code",version:"0.1.0",description:"Custom element for embedding GitHub source files with syntax highlighting",type:"module",main:"dist/github-code.min.js",module:"dist/github-code.min.js",exports:{".":{import:"./dist/github-code.min.js",default:"./dist/github-code.min.js"},"./dist/*":"./dist/*"},files:["dist"],scripts:{build:"node esbuild.config.mjs",dev:"node esbuild.config.mjs --watch","type-check":"tsc --noEmit",lint:"eslint src","lint:fix":"eslint src --fix",format:'prettier --write "src/**/*.ts"',"format:check":'prettier --check "src/**/*.ts"',test:"vitest run","test:watch":"vitest","test:ui":"vitest --ui","test:coverage":"vitest run --coverage","pretest:e2e":"npm run build","test:e2e":"playwright test","test:e2e:ui":"playwright test --ui","test:e2e:debug":"playwright test --debug","test:e2e:headed":"playwright test --headed","test:all":"npm run test:coverage && npm run test:e2e","test:ci":"npm run lint && npm run type-check && npm run build && npm run test:all"},keywords:["web-component","github","code","syntax-highlighting"],author:"",license:"MIT",repository:{type:"git",url:"git+https://github.com/destan/github-code.git"},devDependencies:{"@eslint/js":"^9.39.2","@playwright/test":"^1.57.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/node":"^25.0.2","@vitest/coverage-v8":"^4.0.15","@vitest/ui":"^4.0.15",esbuild:"0.27.1",eslint:"^9.39.2","eslint-config-prettier":"^10.1.8","happy-dom":"^20.0.11",prettier:"^3.7.4","semantic-release":"^24.2.9",typescript:"5.9.3","typescript-eslint":"^8.50.0",vitest:"^4.0.15"},engines:{node:">=24.12.0"}};var j=`:host {
4
+ style-src https://cdnjs.cloudflare.com`);o(new Error(a))},document.head.appendChild(i)}),u}var U={name:"github-code",version:"0.2.0",description:"Custom element for embedding GitHub source files with syntax highlighting",type:"module",main:"dist/github-code.min.js",module:"dist/github-code.min.js",exports:{".":{import:"./dist/github-code.min.js",default:"./dist/github-code.min.js"},"./dist/*":"./dist/*"},files:["dist"],scripts:{build:"node esbuild.config.mjs",dev:"node esbuild.config.mjs --watch","type-check":"tsc --noEmit",lint:"eslint src","lint:fix":"eslint src --fix",format:'prettier --write "src/**/*.ts"',"format:check":'prettier --check "src/**/*.ts"',test:"vitest run","test:watch":"vitest","test:ui":"vitest --ui","test:coverage":"vitest run --coverage","pretest:e2e":"npm run build","test:e2e":"playwright test","test:e2e:ui":"playwright test --ui","test:e2e:debug":"playwright test --debug","test:e2e:headed":"playwright test --headed","test:all":"npm run test:coverage && npm run test:e2e","test:ci":"npm run lint && npm run type-check && npm run build && npm run test:all"},keywords:["web-component","github","code","syntax-highlighting"],author:"",license:"MIT",repository:{type:"git",url:"git+https://github.com/destan/github-code.git"},devDependencies:{"@eslint/js":"^9.39.2","@playwright/test":"^1.57.0","@semantic-release/changelog":"^6.0.3","@semantic-release/git":"^10.0.1","@types/node":"^25.0.2","@vitest/coverage-v8":"^4.0.15","@vitest/ui":"^4.0.15",esbuild:"0.27.1",eslint:"^9.39.2","eslint-config-prettier":"^10.1.8","happy-dom":"^20.0.11",prettier:"^3.7.4","semantic-release":"^24.2.9",typescript:"5.9.3","typescript-eslint":"^8.50.0",vitest:"^4.0.15"},engines:{node:">=24.12.0"}};var j=`:host {
5
5
  --border-color: #d0d7de;
6
6
  --header-background: #f6f8fa;
7
7
  --header-text-color: #24292f;
@@ -317,7 +317,7 @@ section[role='tabpanel'] {
317
317
  section[role='tabpanel'][aria-hidden='true'] {
318
318
  display: none;
319
319
  }
320
- `;var d=class{static baseStylesLight=null;static baseStylesDark=null;static tabStyles=null;static getBaseStyleSheet(e){return e==="dark"?(this.baseStylesDark||(this.baseStylesDark=new CSSStyleSheet,this.baseStylesDark.replaceSync(I)),this.baseStylesDark):(this.baseStylesLight||(this.baseStylesLight=new CSSStyleSheet,this.baseStylesLight.replaceSync(j)),this.baseStylesLight)}static getTabStyleSheet(){return this.tabStyles||(this.tabStyles=new CSSStyleSheet,this.tabStyles.replaceSync(P)),this.tabStyles}static getHighlightJSThemeUrl(e){return`https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/${e==="dark"?"github-dark.min.css":"github.min.css"}`}};function s(n){let e=document.createElement("div");return e.textContent=n,e.innerHTML}function b(n,e=!1){let t=e?'<button class="retry-button">Retry</button>':"";return`<div class="error">${s(n)}${t}</div>`}function c(){return`
320
+ `;var s=class{static baseStylesLight=null;static baseStylesDark=null;static tabStyles=null;static getBaseStyleSheet(e){return e==="dark"?(this.baseStylesDark||(this.baseStylesDark=new CSSStyleSheet,this.baseStylesDark.replaceSync(I)),this.baseStylesDark):(this.baseStylesLight||(this.baseStylesLight=new CSSStyleSheet,this.baseStylesLight.replaceSync(j)),this.baseStylesLight)}static getTabStyleSheet(){return this.tabStyles||(this.tabStyles=new CSSStyleSheet,this.tabStyles.replaceSync(P)),this.tabStyles}static getHighlightJSThemeUrl(e){return`https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/${e==="dark"?"github-dark.min.css":"github.min.css"}`}};function l(n){let e=document.createElement("div");return e.textContent=n,e.innerHTML}function b(n,e=!1){let t=e?'<button class="retry-button">Retry</button>':"";return`<div class="error">${l(n)}${t}</div>`}function c(){return`
321
321
  <div class="code-wrapper skeleton-loading">
322
322
  <div class="code-table">
323
323
  ${Array.from({length:20},(e,t)=>{let r=60+Math.random()*30;return`
@@ -335,16 +335,16 @@ section[role='tabpanel'][aria-hidden='true'] {
335
335
  ${e.map((t,r)=>`
336
336
  <div class="code-row">
337
337
  <div class="line-number">${r+1}</div>
338
- <div class="code-cell">${s(t)||" "}</div>
338
+ <div class="code-cell">${l(t)||" "}</div>
339
339
  </div>
340
340
  `).join("")}
341
341
  </div>
342
342
  </div>
343
343
  `}function S(n,e,t){return`<link rel="stylesheet" href="${t}">
344
344
  <article>
345
- <header>${s(n)}</header>
345
+ <header>${l(n)}</header>
346
346
  ${e}
347
- </article>`}function f(n,e){return`<header>${s(n)}</header>
347
+ </article>`}function m(n,e){return`<header>${l(n)}</header>
348
348
  ${e}`}function k(n,e,t,r){return`<link rel="stylesheet" href="${r}">
349
349
  <article>
350
350
  <nav role="tablist" aria-label="Code files">${n}</nav>
@@ -357,16 +357,16 @@ section[role='tabpanel'][aria-hidden='true'] {
357
357
  </article>`}function K(n){if(!n)return[""];let e=n.split(`
358
358
  `);e.length>0&&e[e.length-1]===""&&e.pop();let t=e.join(`
359
359
  `),o=window.hljs.highlightAuto(t).value.split(`
360
- `);for(;o.length>e.length;)o.pop();return o}function T(n,e){let t=n.querySelectorAll(".code-cell"),r=()=>{let o=K(e);t.forEach((i,a)=>{i.innerHTML=o[a]||" "})};"requestIdleCallback"in window?requestIdleCallback(r):r()}function G(n){return n==="dark"?"dark":n==="light"?"light":window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function q(n){let e=n.getAttribute("theme");return e==="dark"||e==="light"?e:"auto"}var m=class{activeTabIndex=0;renderedTabs=new Set;tabsFullyRendered=!1;getActiveTabIndex(){return this.activeTabIndex}setActiveTabIndex(e){this.activeTabIndex=e}areTabsFullyRendered(){return this.tabsFullyRendered}setTabsFullyRendered(e){this.tabsFullyRendered=e}isTabRendered(e){return this.renderedTabs.has(e)}markTabAsRendered(e){this.renderedTabs.add(e)}clearRenderedTabs(){this.renderedTabs.clear()}getTabCount(e){return e.length}reset(){this.renderedTabs.clear(),this.tabsFullyRendered=!1}};function H(n,e){return n.map((t,r)=>`
360
+ `);for(;o.length>e.length;)o.pop();return o}function T(n,e){let t=n.querySelectorAll(".code-cell"),r=()=>{let o=K(e);t.forEach((i,a)=>{i.innerHTML=o[a]||" "})};"requestIdleCallback"in window?requestIdleCallback(r):r()}function q(n){return n==="dark"?"dark":n==="light"?"light":window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function G(n){let e=n.getAttribute("theme");return e==="dark"||e==="light"?e:"auto"}var f=class{activeTabIndex=0;renderedTabs=new Set;tabsFullyRendered=!1;getActiveTabIndex(){return this.activeTabIndex}setActiveTabIndex(e){this.activeTabIndex=e}areTabsFullyRendered(){return this.tabsFullyRendered}setTabsFullyRendered(e){this.tabsFullyRendered=e}isTabRendered(e){return this.renderedTabs.has(e)}markTabAsRendered(e){this.renderedTabs.add(e)}clearRenderedTabs(){this.renderedTabs.clear()}getTabCount(e){return e.length}reset(){this.renderedTabs.clear(),this.tabsFullyRendered=!1}};function H(n,e){return n.map((t,r)=>`
361
361
  <button role="tab"
362
362
  id="tab-${r}"
363
363
  aria-selected="${r===e?"true":"false"}"
364
364
  aria-controls="panel-${r}"
365
365
  tabindex="${r===e?"0":"-1"}"
366
366
  data-index="${r}">
367
- ${s(t.filename)}
367
+ ${l(t.filename)}
368
368
  </button>
369
- `).join("")}function J(n,e){n.querySelectorAll("nav > button").forEach(r=>{let i=parseInt(r.dataset.index||"0")===e;r.setAttribute("aria-selected",i?"true":"false"),r.setAttribute("tabindex",i?"0":"-1")})}function z(n,e){n.querySelectorAll('section[role="tabpanel"]').forEach(r=>{let o=parseInt(r.dataset.index||"0");r.setAttribute("aria-hidden",o!==e?"true":"false")})}function B(n,e){let t=document.createElement("section");return t.setAttribute("role","tabpanel"),t.setAttribute("id",`panel-${n}`),t.setAttribute("aria-labelledby",`tab-${n}`),t.dataset.index=String(n),t.innerHTML=e,t}function D(n,e,t){switch(n){case"ArrowLeft":return e>0?e-1:t;case"ArrowRight":return e<t?e+1:0;case"Home":return 0;case"End":return t;default:return null}}var v=class extends HTMLElement{#t=[];#e=new m;#n=null;#a=null;#s=null;constructor(){super(),this.attachShadow({mode:"open"})}#d(e){let t=this.#t[e];if(!t)throw new Error(`File at index ${e} not found`);return t}static get observedAttributes(){return["file","theme"]}static get info(){return{version:U.version,highlightjsUrl:A(),highlightjsSource:$()}}connectedCallback(){this.#b(),this.#l()}disconnectedCallback(){this.#a&&this.#s&&this.#a.removeEventListener("change",this.#s)}attributeChangedCallback(e,t,r){if(!(t===null||t===r))if(e==="file"){let o=this.#e.getActiveTabIndex();this.#e.reset(),this.#l(),o<this.#t.length?this.#e.setActiveTabIndex(o):this.#e.setActiveTabIndex(0)}else e==="theme"&&(this.#n=null,this.#l())}#b(){window.matchMedia&&(this.#a=window.matchMedia("(prefers-color-scheme: dark)"),this.#s=()=>{this.#n=null,this.#l()},this.#a.addEventListener("change",this.#s))}#r(){return this.#n?this.#n:(this.#n=G(q(this)),this.#n)}#o(e=!1){let t=this.#r(),r=d.getBaseStyleSheet(t);if(e){let o=d.getTabStyleSheet();this.shadowRoot.adoptedStyleSheets=[r,o]}else this.shadowRoot.adoptedStyleSheets=[r]}async#l(){let e=this.getAttribute("file");if(!e){this.#i('Error: "file" attribute is required. Please provide a GitHub file URL.');return}let t=M(e);if(t.length===0){this.#i('Error: "file" attribute is required. Please provide a GitHub file URL.');return}let r=t.find(o=>!R(o));if(r){let o=s(r);this.#i(`Error: Invalid GitHub URL format: ${o}. Expected format: https://github.com/{owner}/{repo}/blob/{commit}/{path}`);return}if(this.#t=t.map(o=>{try{let{rawUrl:i,filename:a}=E(o);return{filename:a,rawUrl:i,url:o,code:null,error:null,loaded:!1}}catch(i){return{filename:C(o),rawUrl:o,url:o,code:null,error:i instanceof Error?i.message:String(i),loaded:!0}}}),t.length===1){let o=this.#d(0);if(o.error){this.#i(`Error loading code: ${o.error}`);return}this.shadowRoot.innerHTML=S(o.filename,c(),d.getHighlightJSThemeUrl(this.#r())),this.#o(!1)}else this.#g();try{let o=this.getAttribute("highlightjs-url");await F(o&&o!=="auto"?o:void 0)}catch(o){let i=o instanceof Error?o.message:String(o);this.#i(`Error loading highlight.js: ${i}`);return}t.length===1?await this.#u(0):await this.#c()}async#u(e){let t=this.#d(e),r=d.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=S(t.filename,c(),r),this.#o(!1),await g(t,!1);let o=this.shadowRoot.querySelector("article");if(t.error){o.innerHTML=f(t.filename,b(t.error,!0));let i=o.querySelector(".retry-button");i&&i.addEventListener("click",()=>{(async()=>(o.innerHTML=f(t.filename,c()),await g(t,!0),await this.#u(e)))()})}else o.innerHTML=f(t.filename,w(t.code)),window.hljs&&T(this.shadowRoot,t.code)}async#c(){this.#e.areTabsFullyRendered()?this.#f(this.#e.getActiveTabIndex()):(await this.#p(),this.#e.setTabsFullyRendered(!0))}#g(){let e=this.#e.getActiveTabIndex(),t=H(this.#t,e),r=d.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=k(t,e,c(),r),this.#o(!0)}async#p(){let e=this.#e.getActiveTabIndex(),t=H(this.#t,e),r=d.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=k(t,e,c(),r),this.#o(!0);let o=this.shadowRoot.querySelector('nav[role="tablist"]');o.addEventListener("click",a=>{let h=a.target.closest('button[role="tab"]');if(h){let y=parseInt(h.dataset.index||"0");y!==this.#e.getActiveTabIndex()&&(this.#e.setActiveTabIndex(y),this.#c())}}),o.addEventListener("keydown",a=>{let h=a;if(!h.target.closest('button[role="tab"]'))return;let N=this.#e.getActiveTabIndex(),Q=this.#t.length-1,x=D(h.key,N,Q);x!==null&&(a.preventDefault(),this.#e.setActiveTabIndex(x),this.#c().then(()=>{let L=this.shadowRoot.querySelector(`button[data-index="${x}"]`);L&&L.focus()}))}),this.#e.markTabAsRendered(e);let i=this.shadowRoot.querySelector('section[role="tabpanel"]');await this.#h(e,i).catch(a=>{console.error("Failed to load tab content:",a),i.innerHTML=b(`Failed to load content: ${a instanceof Error?a.message:String(a)}`)})}#f(e){J(this.shadowRoot,e);let t=this.shadowRoot.querySelector(`section[role="tabpanel"][data-index="${e}"]`);t||(t=B(e,c()),this.shadowRoot.querySelector("article").appendChild(t),this.#e.markTabAsRendered(e),this.#h(e,t).catch(r=>{console.error("Failed to load tab content:",r),t.innerHTML=b(`Failed to load content: ${r instanceof Error?r.message:String(r)}`)})),z(this.shadowRoot,e)}async#h(e,t){let r=this.#d(e);if(await g(r,!1),r.error||!r.code){let o=r.error||"Failed to load content: No content available";t.innerHTML=b(o,!0);let i=t.querySelector(".retry-button");i&&i.addEventListener("click",()=>{(async()=>(t.innerHTML=c(),await g(r,!0),await this.#h(e,t)))()})}else t.innerHTML=w(r.code),window.hljs&&T(t,r.code)}#i(e){this.shadowRoot.innerHTML=`
370
- <div class="error">${s(e)}</div>
369
+ `).join("")}function J(n,e){n.querySelectorAll("nav > button").forEach(r=>{let i=parseInt(r.dataset.index||"0")===e;r.setAttribute("aria-selected",i?"true":"false"),r.setAttribute("tabindex",i?"0":"-1")})}function z(n,e){n.querySelectorAll('section[role="tabpanel"]').forEach(r=>{let o=parseInt(r.dataset.index||"0");r.setAttribute("aria-hidden",o!==e?"true":"false")})}function B(n,e){let t=document.createElement("section");return t.setAttribute("role","tabpanel"),t.setAttribute("id",`panel-${n}`),t.setAttribute("aria-labelledby",`tab-${n}`),t.dataset.index=String(n),t.innerHTML=e,t}function D(n,e,t){switch(n){case"ArrowLeft":return e>0?e-1:t;case"ArrowRight":return e<t?e+1:0;case"Home":return 0;case"End":return t;default:return null}}var v=class extends HTMLElement{#t=[];#e=new f;#n=null;#a=null;#s=null;constructor(){super(),this.attachShadow({mode:"open"})}#l(e){let t=this.#t[e];if(!t)throw new Error(`File at index ${e} not found`);return t}static get observedAttributes(){return["file","theme"]}static get info(){return{version:U.version,highlightjsUrl:C(),highlightjsSource:$()}}connectedCallback(){this.#g(),this.#u()}disconnectedCallback(){this.#a&&this.#s&&this.#a.removeEventListener("change",this.#s)}attributeChangedCallback(e,t,r){if(!(t===null||t===r))if(e==="file"){let o=this.#e.getActiveTabIndex();this.#e.reset(),this.#u(),o<this.#t.length?this.#e.setActiveTabIndex(o):this.#e.setActiveTabIndex(0)}else e==="theme"&&(this.#n=null,this.#h())}#g(){window.matchMedia&&(this.#a=window.matchMedia("(prefers-color-scheme: dark)"),this.#s=()=>{this.#n=null,this.#h()},this.#a.addEventListener("change",this.#s))}#h(){let e=this.#r(),t=s.getHighlightJSThemeUrl(e),r=this.shadowRoot?.querySelector('link[rel="stylesheet"]');r&&r.setAttribute("href",t);let o=this.#t.length>1;this.#o(o)}#r(){return this.#n?this.#n:(this.#n=q(G(this)),this.#n)}#o(e=!1){let t=this.#r(),r=s.getBaseStyleSheet(t);if(e){let o=s.getTabStyleSheet();this.shadowRoot.adoptedStyleSheets=[r,o]}else this.shadowRoot.adoptedStyleSheets=[r]}async#u(){let e=this.getAttribute("file");if(!e){this.#i('Error: "file" attribute is required. Please provide a GitHub file URL.');return}let t=R(e);if(t.length===0){this.#i('Error: "file" attribute is required. Please provide a GitHub file URL.');return}let r=t.find(o=>!M(o));if(r){let o=l(r);this.#i(`Error: Invalid GitHub URL format: ${o}. Expected format: https://github.com/{owner}/{repo}/blob/{commit}/{path}`);return}if(this.#t=t.map(o=>{try{let{rawUrl:i,filename:a}=E(o);return{filename:a,rawUrl:i,url:o,code:null,error:null,loaded:!1}}catch(i){return{filename:A(o),rawUrl:o,url:o,code:null,error:i instanceof Error?i.message:String(i),loaded:!0}}}),t.length===1){let o=this.#l(0);if(o.error){this.#i(`Error loading code: ${o.error}`);return}this.shadowRoot.innerHTML=S(o.filename,c(),s.getHighlightJSThemeUrl(this.#r())),this.#o(!1)}else this.#p();try{let o=this.getAttribute("highlightjs-url");await F(o&&o!=="auto"?o:void 0)}catch(o){let i=o instanceof Error?o.message:String(o);this.#i(`Error loading highlight.js: ${i}`);return}t.length===1?await this.#b(0):await this.#d()}async#b(e){let t=this.#l(e),r=s.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=S(t.filename,c(),r),this.#o(!1),await g(t,!1);let o=this.shadowRoot.querySelector("article");if(t.error){o.innerHTML=m(t.filename,b(t.error,!0));let i=o.querySelector(".retry-button");i&&i.addEventListener("click",()=>{(async()=>(o.innerHTML=m(t.filename,c()),await g(t,!0),await this.#b(e)))()})}else o.innerHTML=m(t.filename,w(t.code)),window.hljs&&T(this.shadowRoot,t.code)}async#d(){this.#e.areTabsFullyRendered()?this.#f(this.#e.getActiveTabIndex()):(await this.#m(),this.#e.setTabsFullyRendered(!0))}#p(){let e=this.#e.getActiveTabIndex(),t=H(this.#t,e),r=s.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=k(t,e,c(),r),this.#o(!0)}async#m(){let e=this.#e.getActiveTabIndex(),t=H(this.#t,e),r=s.getHighlightJSThemeUrl(this.#r());this.shadowRoot.innerHTML=k(t,e,c(),r),this.#o(!0);let o=this.shadowRoot.querySelector('nav[role="tablist"]');o.addEventListener("click",a=>{let h=a.target.closest('button[role="tab"]');if(h){let y=parseInt(h.dataset.index||"0");y!==this.#e.getActiveTabIndex()&&(this.#e.setActiveTabIndex(y),this.#d())}}),o.addEventListener("keydown",a=>{let h=a;if(!h.target.closest('button[role="tab"]'))return;let N=this.#e.getActiveTabIndex(),Q=this.#t.length-1,x=D(h.key,N,Q);x!==null&&(a.preventDefault(),this.#e.setActiveTabIndex(x),this.#d().then(()=>{let L=this.shadowRoot.querySelector(`button[data-index="${x}"]`);L&&L.focus()}))}),this.#e.markTabAsRendered(e);let i=this.shadowRoot.querySelector('section[role="tabpanel"]');await this.#c(e,i).catch(a=>{console.error("Failed to load tab content:",a),i.innerHTML=b(`Failed to load content: ${a instanceof Error?a.message:String(a)}`)})}#f(e){J(this.shadowRoot,e);let t=this.shadowRoot.querySelector(`section[role="tabpanel"][data-index="${e}"]`);t||(t=B(e,c()),this.shadowRoot.querySelector("article").appendChild(t),this.#e.markTabAsRendered(e),this.#c(e,t).catch(r=>{console.error("Failed to load tab content:",r),t.innerHTML=b(`Failed to load content: ${r instanceof Error?r.message:String(r)}`)})),z(this.shadowRoot,e)}async#c(e,t){let r=this.#l(e);if(await g(r,!1),r.error||!r.code){let o=r.error||"Failed to load content: No content available";t.innerHTML=b(o,!0);let i=t.querySelector(".retry-button");i&&i.addEventListener("click",()=>{(async()=>(t.innerHTML=c(),await g(r,!0),await this.#c(e,t)))()})}else t.innerHTML=w(r.code),window.hljs&&T(t,r.code)}#i(e){this.shadowRoot.innerHTML=`
370
+ <div class="error">${l(e)}</div>
371
371
  `,this.#o(!1)}};customElements.define("github-code",v);export{v as GitHubCode};
372
372
  //# sourceMappingURL=github-code.min.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/parsers/file-parser.ts", "../src/parsers/url-parser.ts", "../src/fetching/code-fetcher.ts", "../src/fetching/highlightjs-loader.ts", "../package.json", "../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 * Track which URL was loaded for introspection\n */\nlet loadedHighlightJSUrl: string | null = null;\n\n/**\n * Track the source of the loaded highlight.js library\n */\nlet highlightJSSource: 'user-provided' | 'cdn-default' | 'global' = 'global';\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 * Get the URL that was loaded (or will be loaded)\n * Returns \"auto\" if using default behavior or global hljs\n */\nexport function getLoadedHighlightJSUrl(): string {\n return loadedHighlightJSUrl || 'auto';\n}\n\n/**\n * Get the source of the highlight.js library\n */\nexport function getHighlightJSSource(): 'user-provided' | 'cdn-default' | 'global' {\n return highlightJSSource;\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 * @param customUrl - Optional custom URL to load highlight.js from\n */\nexport async function loadHighlightJS(customUrl?: string): Promise<void> {\n // If already loaded globally, track and return\n if (window.hljs) {\n if (!loadedHighlightJSUrl) {\n highlightJSSource = 'global';\n loadedHighlightJSUrl = 'auto';\n }\n return;\n }\n\n // If currently loading, check for URL conflicts and return the existing promise\n if (highlightJSLoadingPromise) {\n if (customUrl && loadedHighlightJSUrl && customUrl !== loadedHighlightJSUrl) {\n console.warn(\n `[github-code] Different highlight.js URLs detected. ` +\n `Already loading from \"${loadedHighlightJSUrl}\", ` +\n `but this instance requested \"${customUrl}\". ` +\n `The first URL will be used for all instances.`\n );\n }\n return highlightJSLoadingPromise;\n }\n\n // Determine which URL to use\n const DEFAULT_URL = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js';\n const urlToLoad = customUrl || DEFAULT_URL;\n\n // Track state\n loadedHighlightJSUrl = urlToLoad;\n highlightJSSource = customUrl ? 'user-provided' : 'cdn-default';\n\n // Create new loading promise\n highlightJSLoadingPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = urlToLoad;\n\n // Only add integrity for default CDN URL\n if (!customUrl) {\n script.integrity = 'sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU';\n script.crossOrigin = 'anonymous';\n }\n\n script.onload = () => {\n highlightJSLoadingPromise = null;\n\n // Validate that window.hljs was defined\n if (!window.hljs) {\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n reject(\n new Error(\n `The script at \"${urlToLoad}\" loaded successfully but did not define window.hljs. ` +\n `Please ensure this URL points to a valid highlight.js library.`\n )\n );\n return;\n }\n\n resolve();\n };\n\n script.onerror = () => {\n highlightJSLoadingPromise = null;\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n\n const errorMsg =\n `Failed to load highlight.js library from: ${urlToLoad}\\n` +\n (customUrl\n ? 'Please check that the URL is correct and accessible.'\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\n document.head.appendChild(script);\n });\n\n return highlightJSLoadingPromise;\n}\n", "{\n \"name\": \"github-code\",\n \"version\": \"0.1.0\",\n \"description\": \"Custom element for embedding GitHub source files with syntax highlighting\",\n \"type\": \"module\",\n \"main\": \"dist/github-code.min.js\",\n \"module\": \"dist/github-code.min.js\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/github-code.min.js\",\n \"default\": \"./dist/github-code.min.js\"\n },\n \"./dist/*\": \"./dist/*\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"node esbuild.config.mjs\",\n \"dev\": \"node esbuild.config.mjs --watch\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"pretest:e2e\": \"npm run build\",\n \"test:e2e\": \"playwright test\",\n \"test:e2e:ui\": \"playwright test --ui\",\n \"test:e2e:debug\": \"playwright test --debug\",\n \"test:e2e:headed\": \"playwright test --headed\",\n \"test:all\": \"npm run test:coverage && npm run test:e2e\",\n \"test:ci\": \"npm run lint && npm run type-check && npm run build && npm run test:all\"\n },\n \"keywords\": [\n \"web-component\",\n \"github\",\n \"code\",\n \"syntax-highlighting\"\n ],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/destan/github-code.git\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@playwright/test\": \"^1.57.0\",\n \"@semantic-release/changelog\": \"^6.0.3\",\n \"@semantic-release/git\": \"^10.0.1\",\n \"@types/node\": \"^25.0.2\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"@vitest/ui\": \"^4.0.15\",\n \"esbuild\": \"0.27.1\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"happy-dom\": \"^20.0.11\",\n \"prettier\": \"^3.7.4\",\n \"semantic-release\": \"^24.2.9\",\n \"typescript\": \"5.9.3\",\n \"typescript-eslint\": \"^8.50.0\",\n \"vitest\": \"^4.0.15\"\n },\n \"engines\": {\n \"node\": \">=24.12.0\"\n }\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, GitHubCodeInfo } from './types';\nimport { parseFileAttribute } from './parsers/file-parser';\nimport { isValidGitHubUrl, parseGitHubUrl, extractFilenameFromUrl } from './parsers/url-parser';\nimport { ensureFileLoaded } from './fetching/code-fetcher';\nimport { loadHighlightJS, getLoadedHighlightJSUrl, getHighlightJSSource } from './fetching/highlightjs-loader';\nimport packageJson from '../package.json';\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', 'theme'];\n }\n\n /**\n * Runtime information about the component and loaded libraries\n */\n static get info(): GitHubCodeInfo {\n return {\n version: packageJson.version,\n highlightjsUrl: getLoadedHighlightJSUrl(),\n highlightjsSource: getHighlightJSSource(),\n };\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 (oldValue === null || oldValue === newValue) {\n return;\n }\n\n if (name === 'file') {\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 } else if (name === 'theme') {\n this.#resolvedTheme = null; // Clear cached theme\n void this.#render();\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 // Read custom URL attribute (treat \"auto\" as default)\n const customUrlAttr = this.getAttribute('highlightjs-url');\n const customUrl = customUrlAttr && customUrlAttr !== 'auto' ? customUrlAttr : undefined;\n\n await loadHighlightJS(customUrl);\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", "loadedHighlightJSUrl", "highlightJSSource", "getLoadedHighlightJSUrl", "getHighlightJSSource", "loadHighlightJS", "customUrl", "urlToLoad", "resolve", "reject", "script", "errorMsg", "package_default", "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", "package_default", "getLoadedHighlightJSUrl", "getHighlightJSSource", "#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", "customUrlAttr", "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"]
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 * Track which URL was loaded for introspection\n */\nlet loadedHighlightJSUrl: string | null = null;\n\n/**\n * Track the source of the loaded highlight.js library\n */\nlet highlightJSSource: 'user-provided' | 'cdn-default' | 'global' = 'global';\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 * Get the URL that was loaded (or will be loaded)\n * Returns \"auto\" if using default behavior or global hljs\n */\nexport function getLoadedHighlightJSUrl(): string {\n return loadedHighlightJSUrl || 'auto';\n}\n\n/**\n * Get the source of the highlight.js library\n */\nexport function getHighlightJSSource(): 'user-provided' | 'cdn-default' | 'global' {\n return highlightJSSource;\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 * @param customUrl - Optional custom URL to load highlight.js from\n */\nexport async function loadHighlightJS(customUrl?: string): Promise<void> {\n // If already loaded globally, track and return\n if (window.hljs) {\n if (!loadedHighlightJSUrl) {\n highlightJSSource = 'global';\n loadedHighlightJSUrl = 'auto';\n }\n return;\n }\n\n // If currently loading, check for URL conflicts and return the existing promise\n if (highlightJSLoadingPromise) {\n if (customUrl && loadedHighlightJSUrl && customUrl !== loadedHighlightJSUrl) {\n console.warn(\n `[github-code] Different highlight.js URLs detected. ` +\n `Already loading from \"${loadedHighlightJSUrl}\", ` +\n `but this instance requested \"${customUrl}\". ` +\n `The first URL will be used for all instances.`\n );\n }\n return highlightJSLoadingPromise;\n }\n\n // Determine which URL to use\n const DEFAULT_URL = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js';\n const urlToLoad = customUrl || DEFAULT_URL;\n\n // Track state\n loadedHighlightJSUrl = urlToLoad;\n highlightJSSource = customUrl ? 'user-provided' : 'cdn-default';\n\n // Create new loading promise\n highlightJSLoadingPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = urlToLoad;\n\n // Only add integrity for default CDN URL\n if (!customUrl) {\n script.integrity = 'sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU';\n script.crossOrigin = 'anonymous';\n }\n\n script.onload = () => {\n highlightJSLoadingPromise = null;\n\n // Validate that window.hljs was defined\n if (!window.hljs) {\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n reject(\n new Error(\n `The script at \"${urlToLoad}\" loaded successfully but did not define window.hljs. ` +\n `Please ensure this URL points to a valid highlight.js library.`\n )\n );\n return;\n }\n\n resolve();\n };\n\n script.onerror = () => {\n highlightJSLoadingPromise = null;\n loadedHighlightJSUrl = null;\n highlightJSSource = 'global';\n\n const errorMsg =\n `Failed to load highlight.js library from: ${urlToLoad}\\n` +\n (customUrl\n ? 'Please check that the URL is correct and accessible.'\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\n document.head.appendChild(script);\n });\n\n return highlightJSLoadingPromise;\n}\n", "{\n \"name\": \"github-code\",\n \"version\": \"0.2.0\",\n \"description\": \"Custom element for embedding GitHub source files with syntax highlighting\",\n \"type\": \"module\",\n \"main\": \"dist/github-code.min.js\",\n \"module\": \"dist/github-code.min.js\",\n \"exports\": {\n \".\": {\n \"import\": \"./dist/github-code.min.js\",\n \"default\": \"./dist/github-code.min.js\"\n },\n \"./dist/*\": \"./dist/*\"\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"node esbuild.config.mjs\",\n \"dev\": \"node esbuild.config.mjs --watch\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"lint:fix\": \"eslint src --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\"\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"pretest:e2e\": \"npm run build\",\n \"test:e2e\": \"playwright test\",\n \"test:e2e:ui\": \"playwright test --ui\",\n \"test:e2e:debug\": \"playwright test --debug\",\n \"test:e2e:headed\": \"playwright test --headed\",\n \"test:all\": \"npm run test:coverage && npm run test:e2e\",\n \"test:ci\": \"npm run lint && npm run type-check && npm run build && npm run test:all\"\n },\n \"keywords\": [\n \"web-component\",\n \"github\",\n \"code\",\n \"syntax-highlighting\"\n ],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/destan/github-code.git\"\n },\n \"devDependencies\": {\n \"@eslint/js\": \"^9.39.2\",\n \"@playwright/test\": \"^1.57.0\",\n \"@semantic-release/changelog\": \"^6.0.3\",\n \"@semantic-release/git\": \"^10.0.1\",\n \"@types/node\": \"^25.0.2\",\n \"@vitest/coverage-v8\": \"^4.0.15\",\n \"@vitest/ui\": \"^4.0.15\",\n \"esbuild\": \"0.27.1\",\n \"eslint\": \"^9.39.2\",\n \"eslint-config-prettier\": \"^10.1.8\",\n \"happy-dom\": \"^20.0.11\",\n \"prettier\": \"^3.7.4\",\n \"semantic-release\": \"^24.2.9\",\n \"typescript\": \"5.9.3\",\n \"typescript-eslint\": \"^8.50.0\",\n \"vitest\": \"^4.0.15\"\n },\n \"engines\": {\n \"node\": \">=24.12.0\"\n }\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, GitHubCodeInfo } from './types';\nimport { parseFileAttribute } from './parsers/file-parser';\nimport { isValidGitHubUrl, parseGitHubUrl, extractFilenameFromUrl } from './parsers/url-parser';\nimport { ensureFileLoaded } from './fetching/code-fetcher';\nimport { loadHighlightJS, getLoadedHighlightJSUrl, getHighlightJSSource } from './fetching/highlightjs-loader';\nimport packageJson from '../package.json';\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', 'theme'];\n }\n\n /**\n * Runtime information about the component and loaded libraries\n */\n static get info(): GitHubCodeInfo {\n return {\n version: packageJson.version,\n highlightjsUrl: getLoadedHighlightJSUrl(),\n highlightjsSource: getHighlightJSSource(),\n };\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 (oldValue === null || oldValue === newValue) {\n return;\n }\n\n if (name === 'file') {\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 } else if (name === 'theme') {\n this.#resolvedTheme = null; // Clear cached theme\n this.#updateThemeOnly(); // Just update styles, preserve loaded content\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 this.#updateThemeOnly(); // Update styles without re-rendering\n };\n this.#themeMediaQuery.addEventListener('change', this.#themeChangeHandler);\n }\n }\n\n #updateThemeOnly(): void {\n // Update the highlight.js theme stylesheet link\n const theme = this.#getResolvedTheme();\n const themeUrl = StylesheetManager.getHighlightJSThemeUrl(theme);\n\n const link = this.shadowRoot?.querySelector('link[rel=\"stylesheet\"]');\n if (link) {\n link.setAttribute('href', themeUrl);\n }\n\n // Re-apply constructable stylesheets with new theme\n const hasTabs = this.#files.length > 1;\n this.#applyStyleSheets(hasTabs);\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 // Read custom URL attribute (treat \"auto\" as default)\n const customUrlAttr = this.getAttribute('highlightjs-url');\n const customUrl = customUrlAttr && customUrlAttr !== 'auto' ? customUrlAttr : undefined;\n\n await loadHighlightJS(customUrl);\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", "loadedHighlightJSUrl", "highlightJSSource", "getLoadedHighlightJSUrl", "getHighlightJSSource", "loadHighlightJS", "customUrl", "urlToLoad", "resolve", "reject", "script", "errorMsg", "package_default", "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", "package_default", "getLoadedHighlightJSUrl", "getHighlightJSSource", "#setupThemeListener", "#render", "name", "oldValue", "newValue", "previousIndex", "#updateThemeOnly", "theme", "#getResolvedTheme", "themeUrl", "StylesheetManager", "link", "hasTabs", "#applyStyleSheets", "resolveTheme", "getThemeAttribute", "includeTabStyles", "baseSheet", "tabSheet", "fileAttr", "#showError", "fileUrls", "parseFileAttribute", "invalidUrl", "url", "isValidGitHubUrl", "sanitizedUrl", "escapeHtml", "rawUrl", "filename", "parseGitHubUrl", "error", "extractFilenameFromUrl", "generateSingleFileTemplate", "getSkeletonContentHtml", "#renderTabsSkeletonStructure", "customUrlAttr", "loadHighlightJS", "errorMsg", "#displayCode", "#displayWithTabs", "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
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-code",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Custom element for embedding GitHub source files with syntax highlighting",
5
5
  "type": "module",
6
6
  "main": "dist/github-code.min.js",