Skip to content

Commit eb7f197

Browse files
committed
removes unneeded checks and processing to reduce console noise and confusing errors.
1 parent c891daa commit eb7f197

File tree

1 file changed

+52
-238
lines changed

1 file changed

+52
-238
lines changed

js/copy-to-llm.js

Lines changed: 52 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,6 @@
1313
if (typeof window === 'undefined') {
1414
return;
1515
}
16-
// All config data lives in `llms_config.json` file.
17-
const CONFIG_URL = '/scripts/llms_config.json';
18-
19-
const state = {
20-
// When loadConfig() fetches /scripts/llms_config.json, the parsed JSON lands here.
21-
config: null,
22-
/* If multiple callers hit ready() before the first fetch resolves, they all share this promise instead of firing duplicate network requests. */
23-
configPromise: null,
24-
// Derived once from window.location.origin, trimmed of trailing slashes.
25-
siteBase: window.location ? window.location.origin.replace(/\/+$/, '') : '',
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: '',
28-
};
29-
30-
// Called each time a URL is built from a file path/slug.
31-
function joinUrl(base, path) {
32-
const trimmedBase = (base || '').replace(/\/+$/, '');
33-
const trimmedPath = (path || '').replace(/^\/+/, '');
34-
if (!trimmedBase) {
35-
return trimmedPath ? `/${trimmedPath}` : '/';
36-
}
37-
return trimmedPath ? `${trimmedBase}/${trimmedPath}` : trimmedBase;
38-
}
39-
40-
// Removes slashes as part of slug and URL building.
41-
function stripSlashes(value) {
42-
return (value || '').replace(/^\/+|\/+$/g, '');
43-
}
44-
4516
// Called by getPageSlug() to decode slightly different permutations of a path.
4617
function normalizePathname(pathname) {
4718
let path = decodeURIComponent(pathname || '/');
@@ -104,182 +75,54 @@
10475
return buildSlugFromPath(normalized);
10576
}
10677

107-
// Uses config.repository + outputs metadata to compute a raw GitHub base URL.
108-
function computeRemoteBase(config) {
109-
const repository = config?.repository || {};
110-
const outputs = config?.outputs || {};
111-
const files = outputs.files || {};
112-
113-
if (
114-
repository.host === 'github' &&
115-
repository.org &&
116-
repository.repo &&
117-
repository.default_branch
118-
) {
119-
const pagesDir = stripSlashes(files.pages_dir || 'pages');
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-
);
131-
}
132-
133-
return '';
134-
}
135-
136-
// Fetch `llms_config.json` once and cache both the promise and the parsed object.
137-
function loadConfig() {
138-
if (state.configPromise) {
139-
return state.configPromise;
140-
}
141-
142-
if (typeof fetch !== 'function') {
143-
state.configPromise = Promise.resolve(null);
144-
return state.configPromise;
145-
}
146-
147-
state.configPromise = fetch(CONFIG_URL, { credentials: 'omit' })
148-
.then((response) => {
149-
if (!response.ok) {
150-
throw new Error(`Failed to load config (${response.status})`);
151-
}
152-
return response.json();
153-
})
154-
.then((config) => {
155-
state.config = config;
156-
state.remoteBase = computeRemoteBase(config);
157-
return state.config;
158-
})
159-
.catch((error) => {
160-
console.warn('Unable to load llms_config.json', error);
161-
state.config = null;
162-
state.remoteBase = '';
163-
return null;
164-
});
165-
166-
return state.configPromise;
167-
}
168-
169-
// Public entry point to ensure config is loaded before performing network operations.
170-
async function ready() {
171-
if (state.config || state.configPromise) {
172-
return state.configPromise || state.config;
173-
}
174-
return loadConfig();
175-
}
176-
177-
// Trigger config preload without blocking UI.
178-
ready();
179-
180-
// Compute the local site-relative path for Markdown artifacts (`/ai/pages/...`).
181-
function getLocalPagesBase() {
182-
const config = state.config;
183-
const outputs = config?.outputs || {};
184-
const files = outputs.files || {};
185-
const publicRoot = `/${stripSlashes(outputs.public_root || 'ai')}`;
186-
const pagesDir = stripSlashes(files.pages_dir || 'pages');
187-
return joinUrl(publicRoot, pagesDir);
188-
}
189-
190-
// Preserve ordering while removing duplicates created by overlapping base URLs.
191-
function dedupe(list) {
192-
const seen = [];
193-
list.forEach((item) => {
194-
if (item && !seen.includes(item)) {
195-
seen.push(item);
196-
}
197-
});
198-
return seen;
199-
}
200-
201-
// Build a prioritized list of URLs where a slug's Markdown could exist.
202-
function getSlugCandidates(slug) {
78+
function getMarkdownUrl(slug) {
20379
const normalizedSlug = (slug || 'index').toString().replace(/\.md$/i, '');
204-
const candidates = [];
205-
206-
if (state.remoteBase) {
207-
candidates.push(joinUrl(state.remoteBase, `${normalizedSlug}.md`));
208-
}
209-
210-
const localBase = getLocalPagesBase();
211-
if (localBase) {
212-
candidates.push(joinUrl(localBase, `${normalizedSlug}.md`));
213-
if (state.siteBase) {
214-
candidates.push(
215-
joinUrl(state.siteBase, joinUrl(localBase, `${normalizedSlug}.md`))
216-
);
217-
}
218-
}
219-
220-
candidates.push(joinUrl('', `ai/pages/${normalizedSlug}.md`));
221-
222-
return dedupe(candidates);
80+
const host = window.location ? window.location.host : '';
81+
const protocol = window.location ? window.location.protocol : 'https:';
82+
return `${protocol}//${host}/ai/pages/${normalizedSlug}.md`;
22383
}
22484

225-
// Simple fetch wrapper that tolerates 404s and returns `null` instead of throwing.
226-
async function fetchText(url) {
85+
const NO_MARKDOWN_MESSAGE = 'No Markdown file available.';
86+
87+
async function fetchMarkdown(slug) {
88+
const url = getMarkdownUrl(slug);
22789
try {
22890
const response = await fetch(url, { credentials: 'omit' });
22991
if (!response.ok) {
23092
if (response.status === 404) {
231-
return null;
93+
return { text: null, url, status: 404 };
23294
}
23395
throw new Error(`HTTP ${response.status}`);
23496
}
235-
return await response.text();
97+
const text = await response.text();
98+
return { text, url, status: 200 };
23699
} catch (error) {
237-
console.error('Failed to fetch text', url, error);
238-
return null;
239-
}
240-
}
241-
242-
// Walk the candidate list until a Markdown file returns successfully.
243-
async function fetchSlugContent(slug) {
244-
await ready();
245-
const candidates = getSlugCandidates(slug);
246-
for (const url of candidates) {
247-
const text = await fetchText(url);
248-
if (text) {
249-
return { text, url };
250-
}
100+
console.warn('Copy to LLM: unable to fetch markdown file', url, error);
101+
return { text: null, url, status: 'error' };
251102
}
252-
return null;
253103
}
254104

255-
// Same candidate iteration as `fetchSlugContent`, but pipes the first successful response into a download.
256-
async function downloadSlug(slug, filename) {
257-
await ready();
258-
const candidates = getSlugCandidates(slug);
259-
for (const url of candidates) {
260-
try {
261-
const response = await fetch(url, { credentials: 'omit' });
262-
if (!response.ok) {
263-
if (response.status === 404) {
264-
continue;
265-
}
266-
throw new Error(`HTTP ${response.status}`);
267-
}
268-
const blob = await response.blob();
269-
const objectUrl = URL.createObjectURL(blob);
270-
const link = document.createElement('a');
271-
link.href = objectUrl;
272-
link.download = filename;
273-
document.body.appendChild(link);
274-
link.click();
275-
URL.revokeObjectURL(objectUrl);
276-
link.remove();
277-
return true;
278-
} catch (error) {
279-
console.error('Download failed, trying next candidate', url, error);
105+
async function downloadMarkdown(slug, filename) {
106+
const url = getMarkdownUrl(slug);
107+
try {
108+
const response = await fetch(url, { credentials: 'omit' });
109+
if (!response.ok) {
110+
return response.status === 404 ? { success: false, status: 404 } : { success: false, status: response.status };
280111
}
112+
const blob = await response.blob();
113+
const objectUrl = URL.createObjectURL(blob);
114+
const link = document.createElement('a');
115+
link.href = objectUrl;
116+
link.download = filename;
117+
document.body.appendChild(link);
118+
link.click();
119+
URL.revokeObjectURL(objectUrl);
120+
link.remove();
121+
return { success: true };
122+
} catch (error) {
123+
console.warn('Copy to LLM: download failed', url, error);
124+
return { success: false, status: 'error' };
281125
}
282-
return false;
283126
}
284127

285128
// ---------- Analytics helpers ----------
@@ -319,14 +162,6 @@
319162

320163
// ---------- Page helpers ----------
321164

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. */
323-
function getFallbackPageContent() {
324-
const articleContent = document.querySelector(
325-
'.md-content__inner .md-typeset'
326-
);
327-
return articleContent ? articleContent.innerText.trim() : '';
328-
}
329-
330165
// ---------- Clipboard helpers ----------
331166
async function copyToClipboard(text, button, eventType) {
332167
let copied = false;
@@ -575,35 +410,19 @@
575410
let copySucceeded = false;
576411
const slug = getPageSlug();
577412

578-
try {
579-
const result = await fetchSlugContent(slug);
580-
if (result && result.text) {
581-
copySucceeded = await copyToClipboard(
582-
result.text,
583-
copyButton,
584-
'markdown_content'
585-
);
586-
}
587-
} catch (error) {
588-
console.error('Copy to LLM: failed to copy markdown content', error);
589-
}
590-
591-
if (!copySucceeded) {
592-
const fallback = getFallbackPageContent();
593-
if (fallback) {
594-
try {
595-
copySucceeded = await copyToClipboard(
596-
fallback,
597-
copyButton,
598-
'page_content'
599-
);
600-
} catch (fallbackError) {
601-
console.error('Copy to LLM: fallback copy failed', fallbackError);
602-
}
413+
const { text, status } = await fetchMarkdown(slug);
414+
if (text) {
415+
copySucceeded = await copyToClipboard(
416+
text,
417+
copyButton,
418+
'markdown_content'
419+
);
420+
if (!copySucceeded) {
421+
showCopyError(copyButton);
603422
}
604-
}
605-
606-
if (!copySucceeded) {
423+
} else if (status === 404) {
424+
showToast(NO_MARKDOWN_MESSAGE);
425+
} else {
607426
showCopyError(copyButton);
608427
}
609428

@@ -652,28 +471,26 @@
652471
return;
653472
}
654473

655-
await ready();
656474
const action = item.dataset.action;
657475
const slug = getPageSlug();
658476

659477
// Each dropdown option maps to one of the shared helpers or a new-tab prompt.
660478
switch (action) {
661479
case 'download-markdown': {
662480
trackButtonClick('download_page_markdown');
663-
const success = await downloadSlug(slug, `${slug}.md`);
664-
if (!success) {
665-
showCopyError(item);
666-
} else {
481+
const result = await downloadMarkdown(slug, `${slug}.md`);
482+
if (result.success) {
667483
showCopySuccess(item);
484+
} else if (result.status === 404) {
485+
showToast(NO_MARKDOWN_MESSAGE);
486+
} else {
487+
showCopyError(item);
668488
}
669489
break;
670490
}
671491
case 'open-chatgpt': {
672492
trackButtonClick('open_chatgpt');
673-
const candidates = getSlugCandidates(slug);
674-
const mdUrl = candidates.length
675-
? candidates[0]
676-
: window.location.href;
493+
const mdUrl = getMarkdownUrl(slug);
677494
const prompt = `Read ${mdUrl} so I can ask questions about it.`;
678495
const chatGPTUrl = `https://chatgpt.com/?hints=search&q=${encodeURIComponent(
679496
prompt
@@ -683,10 +500,7 @@
683500
}
684501
case 'open-claude': {
685502
trackButtonClick('open_claude');
686-
const candidates = getSlugCandidates(slug);
687-
const mdUrl = candidates.length
688-
? candidates[0]
689-
: window.location.href;
503+
const mdUrl = getMarkdownUrl(slug);
690504
const prompt = `Read ${mdUrl} so I can ask questions about it.`;
691505
const claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(
692506
prompt

0 commit comments

Comments
 (0)