|
1 | | -// Copy to LLM functionality |
2 | | -// From: https://github.com/leonardocustodio/mkdocs-copy-to-llm/blob/main/mkdocs_copy_to_llm/assets/js/copy-to-llm.js |
| 1 | +/* Copy to LLM functionality |
| 2 | + Modified from: https://github.com/leonardocustodio/mkdocs-copy-to-llm/blob/main/mkdocs_copy_to_llm/assets/js/copy-to-llm.js |
3 | 3 |
|
4 | | -/* |
5 | | - This script adds per-page LLM functionality UI controller that: |
| 4 | + This script adds per-page LLM functionality UI controller that: |
6 | 5 | - Renders a split button next to the main heading that copies, downloads, or opens Markdown in ChatGPT or Claude. |
7 | | - - Emits analytics via GA (TODO: needs wired up to work) |
8 | | - */ |
| 6 | + - Emits analytics via GA (TODO: needs wired up to work). |
| 7 | +*/ |
9 | 8 |
|
10 | 9 | (function () { |
11 | 10 | 'use strict'; |
12 | | -/* protects script from crashing when it's evaluated in env w/o a browser |
13 | | - (build, etc.) no window = no DOM = script quietly exits */ |
| 11 | + /* Protects script from crashing when evaluated in env w/o a browser |
| 12 | + (build, etc.). No window = no DOM = script quietly exits. */ |
14 | 13 | if (typeof window === 'undefined') { |
15 | 14 | return; |
16 | 15 | } |
17 | | -// All config data lives in `llms_config.json` file. |
| 16 | + // All config data lives in `llms_config.json` file. |
18 | 17 | const CONFIG_URL = '/scripts/llms_config.json'; |
19 | 18 |
|
20 | 19 | const state = { |
21 | 20 | // When loadConfig() fetches /scripts/llms_config.json, the parsed JSON lands here. |
22 | 21 | config: null, |
23 | | - /* If multiple callers hit ready() before the first fetch resolves, they all share this promise instead of firing duplicate network requests. */ |
| 22 | + /* If multiple callers hit ready() before the first fetch resolves, they all share this promise instead of firing duplicate network requests. */ |
24 | 23 | configPromise: null, |
25 | 24 | // Derived once from window.location.origin, trimmed of trailing slashes. |
26 | 25 | siteBase: window.location ? window.location.origin.replace(/\/+$/, '') : '', |
27 | | - /** After the config loads, computeRemoteBase(config) may set this to a raw GitHub URL (pulling repository.org, repository.repo, etc.). When present, it’s the highest-priority candidate in getSlugCandidates() for finding Markdown artifacts.*/ |
28 | | - remoteBase: '' |
| 26 | + /* After the config loads, computeRemoteBase(config) may set this to a raw GitHub URL (pulling repository.org, repository.repo, etc.). When present, it’s the highest-priority candidate in getSlugCandidates() for finding Markdown artifacts.*/ |
| 27 | + remoteBase: '', |
29 | 28 | }; |
30 | 29 |
|
31 | 30 | // Called each time a URL is built from a file path/slug. |
|
58 | 57 |
|
59 | 58 | return path || '/'; |
60 | 59 | } |
61 | | - // Called by getPageSlug() after normalizePathname() to build and return the slug |
| 60 | + // Called by getPageSlug() after normalizePathname() to build and return the slug. |
62 | 61 | function buildSlugFromPath(pathname) { |
63 | 62 | if (!pathname || pathname === '/') { |
64 | 63 | return 'index'; |
|
111 | 110 | const outputs = config?.outputs || {}; |
112 | 111 | const files = outputs.files || {}; |
113 | 112 |
|
114 | | - if (repository.host === 'github' && repository.org && repository.repo && repository.default_branch) { |
| 113 | + if ( |
| 114 | + repository.host === 'github' && |
| 115 | + repository.org && |
| 116 | + repository.repo && |
| 117 | + repository.default_branch |
| 118 | + ) { |
115 | 119 | const pagesDir = stripSlashes(files.pages_dir || 'pages'); |
116 | | - const fallbackArtifacts = joinUrl(stripSlashes(outputs.public_root || 'ai'), pagesDir); |
117 | | - const artifactsPath = stripSlashes(repository.ai_artifacts_path || fallbackArtifacts); |
118 | | - return joinUrl(`https://raw.githubusercontent.com/${repository.org}/${repository.repo}/${repository.default_branch}`, artifactsPath); |
| 120 | + const fallbackArtifacts = joinUrl( |
| 121 | + stripSlashes(outputs.public_root || 'ai'), |
| 122 | + pagesDir |
| 123 | + ); |
| 124 | + const artifactsPath = stripSlashes( |
| 125 | + repository.ai_artifacts_path || fallbackArtifacts |
| 126 | + ); |
| 127 | + return joinUrl( |
| 128 | + `https://raw.githubusercontent.com/${repository.org}/${repository.repo}/${repository.default_branch}`, |
| 129 | + artifactsPath |
| 130 | + ); |
119 | 131 | } |
120 | 132 |
|
121 | 133 | return ''; |
|
132 | 144 | return state.configPromise; |
133 | 145 | } |
134 | 146 |
|
135 | | - const configUrl = CONFIG_URL; |
136 | | - |
137 | | - state.configPromise = fetch(configUrl, { credentials: 'omit' }) |
| 147 | + state.configPromise = fetch(CONFIG_URL, { credentials: 'omit' }) |
138 | 148 | .then((response) => { |
139 | 149 | if (!response.ok) { |
140 | 150 | throw new Error(`Failed to load config (${response.status})`); |
|
164 | 174 | return loadConfig(); |
165 | 175 | } |
166 | 176 |
|
167 | | - // Trigger config preload without blocking UI |
| 177 | + // Trigger config preload without blocking UI. |
168 | 178 | ready(); |
169 | 179 |
|
170 | 180 | // Compute the local site-relative path for Markdown artifacts (`/ai/pages/...`). |
|
201 | 211 | if (localBase) { |
202 | 212 | candidates.push(joinUrl(localBase, `${normalizedSlug}.md`)); |
203 | 213 | if (state.siteBase) { |
204 | | - candidates.push(joinUrl(state.siteBase, joinUrl(localBase, `${normalizedSlug}.md`))); |
| 214 | + candidates.push( |
| 215 | + joinUrl(state.siteBase, joinUrl(localBase, `${normalizedSlug}.md`)) |
| 216 | + ); |
205 | 217 | } |
206 | 218 | } |
207 | 219 |
|
|
292 | 304 | } |
293 | 305 |
|
294 | 306 | function trackCopyEvent(eventType, contentLength) { |
295 | | - sendAnalytics( |
296 | | - 'copy_to_llm', |
297 | | - { |
298 | | - event_label: eventType, |
299 | | - value: contentLength, |
300 | | - } |
301 | | - ); |
| 307 | + sendAnalytics('copy_to_llm', { |
| 308 | + event_label: eventType, |
| 309 | + value: contentLength, |
| 310 | + }); |
302 | 311 | } |
303 | 312 |
|
304 | 313 | // Lightweight GA event wrapper for button clicks (download/open/chat etc.). |
305 | 314 | function trackButtonClick(eventType) { |
306 | | - sendAnalytics( |
307 | | - 'copy_to_llm_click', |
308 | | - { |
309 | | - event_label: eventType, |
310 | | - } |
311 | | - ); |
| 315 | + sendAnalytics('copy_to_llm_click', { |
| 316 | + event_label: eventType, |
| 317 | + }); |
312 | 318 | } |
313 | 319 |
|
314 | 320 | // ---------- Page helpers ---------- |
315 | | - |
316 | | - // If fetching Markdown fails, we fall back to scraping the rendered HTML content. |
317 | | - // '.md-content__inner .md-typeset' is the default class for <article> elements |
| 321 | + |
| 322 | + /* If fetching Markdown fails, we fall back to scraping the rendered HTML content: '.md-content__inner .md-typeset' is the default class for <article> elements. */ |
318 | 323 | function getFallbackPageContent() { |
319 | 324 | const articleContent = document.querySelector( |
320 | 325 | '.md-content__inner .md-typeset' |
|
647 | 652 | return; |
648 | 653 | } |
649 | 654 |
|
650 | | - await ready(); |
| 655 | + await ready(); |
651 | 656 | const action = item.dataset.action; |
652 | 657 | const slug = getPageSlug(); |
653 | 658 |
|
|
0 commit comments