您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
从 pkg.go.dev 提取 Go 语言包的文档内容(包括 API 定义和代码示例),并将其转换为结构化的 LLM.txt 文本格式,方便在本地使用大型语言模型 (LLM) 进行参考和分析。
- // ==UserScript==
- // @name Pkg.go.dev to LLM.txt Formatter
- // @name:zh-CN Pkg.go.dev 文档转 LLM.txt 格式化工具
- // @namespace https://x.com/janxin
- // @version 0.1.1
- // @description Extracts Go package documentation (API definitions, examples) from pkg.go.dev and converts it into a structured LLM.txt format, suitable for local AI/LLM reference and analysis.
- // @description:zh-CN 从 pkg.go.dev 提取 Go 语言包的文档内容(包括 API 定义和代码示例),并将其转换为结构化的 LLM.txt 文本格式,方便在本地使用大型语言模型 (LLM) 进行参考和分析。
- // @author hellowor
- // @match https://pkg.go.dev/*
- // @icon https://pkg.go.dev/favicon.ico
- // @grant GM_addStyle
- // @grant GM_setClipboard
- // @homepageURL https://x.com/janxin
- // @supportURL https://x.com/janxin
- // @license MIT
- // ==/UserScript==
- (function() {
- 'use strict';
- GM_addStyle(`
- .llm-download-button {
- position: fixed;
- bottom: 20px;
- right: 20px;
- z-index: 9999;
- padding: 10px 15px;
- background-color: #007d9c;
- color: white;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- font-size: 14px;
- box-shadow: 0 2px 5px rgba(0,0,0,0.2);
- }
- .llm-download-button:hover {
- background-color: #005f79;
- }
- `);
- function getCleanText(element) {
- return element ? element.textContent.trim() : '';
- }
- function getCodeFromPre(preElement) {
- if (!preElement) return '';
- // Check if there's a span inside the pre, which often holds the actual code lines
- const spanInsidePre = preElement.querySelector('span');
- if (spanInsidePre) {
- let code = '';
- spanInsidePre.childNodes.forEach(node => {
- if (node.nodeType === Node.TEXT_NODE) {
- code += node.textContent;
- } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'BR') {
- code += '\n';
- } else if (node.nodeType === Node.ELEMENT_NODE) {
- code += node.textContent;
- }
- });
- const trimmedCodeFromSpan = code.trim();
- if (trimmedCodeFromSpan) {
- return trimmedCodeFromSpan;
- }
- }
- // Fallback if no span or span processing yielded empty string
- return preElement.textContent; // textContent decodes HTML entities
- }
- function getDirectSiblingParagraphs(element) {
- if (!element) {
- return '';
- }
- let description = [];
- let sibling = element.nextElementSibling;
- while (sibling && (sibling.tagName === 'P' || (sibling.tagName === 'UL' && !sibling.closest('.Documentation-exampleDetails')))) {
- if (sibling.tagName === 'P') {
- description.push(getCleanText(sibling));
- } else if (sibling.tagName === 'UL') {
- let listItems = [];
- sibling.querySelectorAll('li').forEach(li => listItems.push('- ' + getCleanText(li)));
- if (listItems.length > 0) {
- description.push(listItems.join('\n'));
- }
- }
- sibling = sibling.nextElementSibling;
- }
- return description.join('\n\n');
- }
- function extractExample(detailElement, level = 3) {
- const summaryEl = detailElement.querySelector('.Documentation-exampleDetailsHeader');
- const exampleBody = detailElement.querySelector('.Documentation-exampleDetailsBody');
- let codeContent = '';
- if (exampleBody) {
- const textareaEl = exampleBody.querySelector('textarea.Documentation-exampleCode.code');
- if (textareaEl) {
- codeContent = textareaEl.value;
- // console.log(`[extractExample] Found textarea for "${getCleanText(summaryEl)}". Value length: ${codeContent.length}. Starts with: ${codeContent.substring(0, 70).replace(/\n/g, '\\n')}`);
- }
- // If textarea not found or its value is empty, try <pre>
- if (!codeContent.trim()) {
- const preEl = exampleBody.querySelector('pre.Documentation-exampleCode');
- if (preEl) {
- codeContent = getCodeFromPre(preEl);
- // console.log(`[extractExample] Found pre for "${getCleanText(summaryEl)}". Content length: ${codeContent.length}. Starts with: ${codeContent.substring(0, 70).replace(/\n/g, '\\n')}`);
- }
- }
- if (!codeContent.trim() && !textareaEl && !exampleBody.querySelector('pre.Documentation-exampleCode')) {
- // console.warn(`[extractExample] No code element (textarea or pre) found for example: "${getCleanText(summaryEl)}" in body:`, exampleBody.innerHTML.substring(0,200));
- }
- } else {
- // console.warn(`[extractExample] No exampleBody found for example: "${getCleanText(summaryEl)}"`);
- }
- const outputLabelEl = detailElement.querySelector('.Documentation-exampleOutputLabel');
- const outputEl = exampleBody ? exampleBody.querySelector('span.Documentation-exampleOutput, pre.Documentation-exampleOutput') : null;
- let exampleText = "";
- const title = getCleanText(summaryEl).replace(/ ¶$/, '');
- if (title) {
- exampleText += `${'#'.repeat(level)} Example: ${title}\n\n`;
- } else {
- exampleText += `${'#'.repeat(level)} Example\n\n`;
- }
- const trimmedCode = codeContent.trim();
- if (trimmedCode) {
- exampleText += "```go\n" + trimmedCode + "\n```\n\n";
- } else {
- // console.warn(`[extractExample] Code content is effectively empty for: "${title}"`);
- }
- if (outputLabelEl && outputEl) {
- let outputContent = "";
- if (outputEl.tagName === 'PRE') {
- outputContent = getCodeFromPre(outputEl);
- } else {
- const preInsideSpan = outputEl.querySelector('pre');
- if (preInsideSpan) {
- outputContent = getCodeFromPre(preInsideSpan);
- } else {
- outputContent = getCleanText(outputEl);
- }
- }
- const trimmedOutput = outputContent.trim();
- if (trimmedOutput) {
- exampleText += `Output:\n\`\`\`\n${trimmedOutput}\n\`\`\`\n\n`;
- }
- }
- return exampleText;
- }
- function extractNameFromSignatureOrHeader(headerEl, sigPre, entityType = "Unknown") {
- if (headerEl) {
- const nameAnchor = headerEl.querySelector('a:not(.Documentation-idLink):not(.Documentation-source)');
- if (nameAnchor && getCleanText(nameAnchor)) {
- return getCleanText(nameAnchor);
- }
- }
- if (sigPre) {
- const sigText = getCodeFromPre(sigPre).trim(); // Ensure sigText is trimmed before regex
- let match;
- switch (entityType) {
- case "Function":
- case "Constructor":
- match = sigText.match(/^func\s+([A-Z_][A-Za-z0-9_]*)\s*\(/);
- if (match && match[1]) return match[1];
- break;
- case "Method":
- match = sigText.match(/^func\s*\([\s\S]*?\)\s*([A-Z_][A-Za-z0-9_]*)\s*\(/);
- if (match && match[1]) return match[1];
- break;
- case "Type":
- match = sigText.match(/^type\s+([A-Z_][A-Za-z0-9_]*)/);
- if (match && match[1]) return match[1];
- break;
- }
- }
- if (headerEl) {
- let headerText = getCleanText(headerEl).replace(/ ¶$/, '');
- headerText = headerText.replace(/^func\s+/, '').replace(/^type\s+/, '');
- const firstWord = headerText.split(/\s|\(/)[0];
- if (firstWord) return firstWord;
- }
- return `Unknown${entityType}`;
- }
- function processDocumentationSection() {
- let output = [];
- const docContainer = document.querySelector('.Documentation.js-documentation .Documentation-content.js-docContent');
- if (!docContainer) {
- console.warn("Main documentation content (.Documentation-content.js-docContent) not found.");
- return '';
- }
- const overviewSection = docContainer.querySelector('section.Documentation-overview');
- if (overviewSection) {
- const overviewHeader = overviewSection.querySelector('h3#pkg-overview');
- if (overviewHeader) {
- const packageDescription = getDirectSiblingParagraphs(overviewHeader);
- if (packageDescription) {
- output.push("## Package Overview\n\n" + packageDescription + "\n\n");
- }
- }
- const overviewExamples = overviewSection.querySelectorAll('details.Documentation-exampleDetails');
- if (overviewExamples.length > 0) {
- let examplesInSectionFound = false;
- overviewExamples.forEach(ex => {
- const exampleContent = extractExample(ex, 3);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) { // Check if more than just title
- if (!examplesInSectionFound) {
- output.push("## Package Examples (from Overview)\n");
- examplesInSectionFound = true;
- }
- output.push(exampleContent);
- }
- });
- }
- }
- const examplesSectionHeader = docContainer.querySelector('h4#pkg-examples');
- if (examplesSectionHeader) {
- const examplesList = examplesSectionHeader.parentElement.querySelector('ul.Documentation-examplesList');
- if (examplesList) {
- let examplesInSectionFound = false;
- examplesList.querySelectorAll('li a.js-exampleHref').forEach(exLink => {
- const exampleId = exLink.getAttribute('href').substring(1);
- const exampleDetail = docContainer.querySelector(`details#${exampleId}.Documentation-exampleDetails`);
- if (exampleDetail) {
- const exampleContent = extractExample(exampleDetail, 3);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) {
- if (!examplesInSectionFound) {
- output.push("## Examples (Listed)\n");
- examplesInSectionFound = true;
- }
- output.push(exampleContent);
- }
- }
- });
- }
- }
- const constHeader = docContainer.querySelector('#pkg-constants');
- if (constHeader) {
- const constSection = constHeader.closest('h3').nextElementSibling;
- if (constSection && constSection.classList.contains('Documentation-constants')) {
- const declarations = constSection.querySelectorAll('div.Documentation-declaration');
- if (declarations.length > 0) {
- output.push("## Constants\n");
- declarations.forEach(decl => {
- const sigPre = decl.querySelector('pre');
- if (sigPre) output.push("```go\n" + getCodeFromPre(sigPre).trim() + "\n```\n");
- const desc = getDirectSiblingParagraphs(decl);
- if (desc) output.push(desc + "\n");
- output.push("---\n");
- });
- }
- }
- }
- const varHeader = docContainer.querySelector('#pkg-variables');
- if (varHeader) {
- const varSection = varHeader.closest('h3').nextElementSibling;
- if (varSection && varSection.classList.contains('Documentation-variables')) {
- const declarations = varSection.querySelectorAll('div.Documentation-declaration');
- if (declarations.length > 0) {
- output.push("## Variables\n");
- declarations.forEach(decl => {
- const sigPre = decl.querySelector('pre');
- if (sigPre) output.push("```go\n" + getCodeFromPre(sigPre).trim() + "\n```\n");
- const desc = getDirectSiblingParagraphs(decl);
- if (desc) output.push(desc + "\n");
- output.push("---\n");
- });
- }
- }
- }
- const funcHeader = docContainer.querySelector('#pkg-functions');
- if (funcHeader) {
- const funcSection = funcHeader.closest('h3').nextElementSibling;
- if (funcSection && funcSection.classList.contains('Documentation-functions')) {
- const functions = funcSection.querySelectorAll('div.Documentation-function');
- if (functions.length > 0) {
- output.push("## Functions\n");
- functions.forEach(fnDiv => {
- const fnHeaderEl = fnDiv.querySelector('h4.Documentation-functionHeader');
- const declarationDiv = fnDiv.querySelector('div.Documentation-declaration');
- const sigPre = declarationDiv ? declarationDiv.querySelector('pre') : null;
- const funcName = extractNameFromSignatureOrHeader(fnHeaderEl, sigPre, "Function");
- output.push(`### Function: ${funcName}\n`);
- if (sigPre) {
- output.push("```go\n" + getCodeFromPre(sigPre).trim() + "\n```\n");
- }
- const desc = declarationDiv ? getDirectSiblingParagraphs(declarationDiv) : (fnHeaderEl ? getDirectSiblingParagraphs(fnHeaderEl) : '');
- if (desc) output.push(desc + "\n");
- fnDiv.querySelectorAll('details.Documentation-exampleDetails').forEach(ex => {
- const exampleContent = extractExample(ex, 4);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) {
- output.push(exampleContent);
- }
- });
- output.push("---\n");
- });
- }
- }
- }
- const typeHeader = docContainer.querySelector('#pkg-types');
- if (typeHeader) {
- const typeSection = typeHeader.closest('h3').nextElementSibling;
- if (typeSection && typeSection.classList.contains('Documentation-types')) {
- const types = typeSection.querySelectorAll('div.Documentation-type');
- if (types.length > 0) {
- output.push("## Types\n");
- types.forEach(typeDiv => {
- const typeHeaderEl = typeDiv.querySelector('h4.Documentation-typeHeader');
- const typeDeclarationDiv = typeDiv.querySelector('div.Documentation-declaration');
- const sigPre = typeDeclarationDiv ? typeDeclarationDiv.querySelector('pre') : null;
- const typeName = extractNameFromSignatureOrHeader(typeHeaderEl, sigPre, "Type");
- output.push(`### Type: ${typeName}\n`);
- if (sigPre) {
- output.push("```go\n" + getCodeFromPre(sigPre).trim() + "\n```\n");
- }
- const desc = typeDeclarationDiv ? getDirectSiblingParagraphs(typeDeclarationDiv) : (typeHeaderEl ? getDirectSiblingParagraphs(typeHeaderEl) : '');
- if (desc) output.push(desc + "\n");
- typeDiv.querySelectorAll(':scope > details.Documentation-exampleDetails').forEach(ex => {
- const exampleContent = extractExample(ex, 4);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) {
- output.push(exampleContent);
- }
- });
- typeDiv.querySelectorAll('div.Documentation-typeFunc').forEach(assocFnDiv => {
- const assocFnHeaderEl = assocFnDiv.querySelector('h4.Documentation-functionHeader');
- const assocFnDeclarationDiv = assocFnDiv.querySelector('div.Documentation-declaration');
- const assocSigPre = assocFnDeclarationDiv ? assocFnDeclarationDiv.querySelector('pre') : null;
- const constructorName = extractNameFromSignatureOrHeader(assocFnHeaderEl, assocSigPre, "Constructor");
- output.push(`#### Constructor: ${constructorName}\n`);
- if (assocSigPre) {
- output.push("```go\n" + getCodeFromPre(assocSigPre).trim() + "\n```\n");
- }
- const assocDesc = assocFnDeclarationDiv ? getDirectSiblingParagraphs(assocFnDeclarationDiv) : (assocFnHeaderEl ? getDirectSiblingParagraphs(assocFnHeaderEl) : '');
- if (assocDesc) output.push(assocDesc + "\n");
- assocFnDiv.querySelectorAll('details.Documentation-exampleDetails').forEach(ex => {
- const exampleContent = extractExample(ex, 5);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) {
- output.push(exampleContent);
- }
- });
- output.push("---\n");
- });
- typeDiv.querySelectorAll('div.Documentation-typeMethod').forEach(assocMethodDiv => {
- const assocMethodHeaderEl = assocMethodDiv.querySelector('h4.Documentation-functionHeader');
- const assocMethodDeclarationDiv = assocMethodDiv.querySelector('div.Documentation-declaration');
- const assocSigPre = assocMethodDeclarationDiv ? assocMethodDeclarationDiv.querySelector('pre') : null;
- const methodName = extractNameFromSignatureOrHeader(assocMethodHeaderEl, assocSigPre, "Method");
- output.push(`#### Method: ${methodName}\n`);
- if (assocSigPre) {
- output.push("```go\n" + getCodeFromPre(assocSigPre).trim() + "\n```\n");
- }
- const assocDesc = assocMethodDeclarationDiv ? getDirectSiblingParagraphs(assocMethodDeclarationDiv) : (assocMethodHeaderEl ? getDirectSiblingParagraphs(assocMethodHeaderEl) : '');
- if (assocDesc) output.push(assocDesc + "\n");
- assocMethodDiv.querySelectorAll('details.Documentation-exampleDetails').forEach(ex => {
- const exampleContent = extractExample(ex, 5);
- if (exampleContent.split('\n').filter(l => l.trim() !== '').length > 2) {
- output.push(exampleContent);
- }
- });
- output.push("---\n");
- });
- output.push("===\n");
- });
- }
- }
- }
- let cleanedOutput = output.join('\n')
- .replace(/(\n---\n)+(\s*(\n---|\n===|$))/g, '\n---\n$2') // Consolidate multiple --- unless followed by ===
- .replace(/(\n===\n)+/g, '\n===\n') // Consolidate multiple ===
- .replace(/\n{3,}/g, '\n\n'); // Max 2 blank lines
- return cleanedOutput.trim();
- }
- function getPackageNameForFilename() {
- let path = window.location.pathname;
- path = path.split('@')[0];
- if (path.startsWith('/')) {
- path = path.substring(1);
- }
- return path.replace(/\/$/, '').replace(/\//g, '_');
- }
- function download(filename, text) {
- const element = document.createElement('a');
- element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
- element.setAttribute('download', filename);
- element.style.display = 'none';
- document.body.appendChild(element);
- element.click();
- document.body.removeChild(element);
- }
- function initializeScraper() {
- // console.log("Starting extraction...");
- const data = processDocumentationSection();
- if (data.trim()) {
- const packageName = getPackageNameForFilename();
- const filename = packageName ? `${packageName}_llm.txt` : 'llm.txt';
- download(filename, data);
- // console.log(`${filename} download initiated.`);
- // GM_setClipboard(data); // For debugging
- // alert('Data extracted and download initiated!');
- } else {
- // console.warn("No documentation section found or no actual data was extracted.");
- alert("Could not find documentation section or no actual data was extracted. Check console for details (if any logs were enabled).");
- }
- }
- const downloadButton = document.createElement('button');
- downloadButton.textContent = 'Download llm.txt';
- downloadButton.className = 'llm-download-button';
- downloadButton.addEventListener('click', () => {
- // console.log("Button clicked, scheduling scraper with 1s delay.");
- setTimeout(initializeScraper, 1000); // 1 second delay
- });
- // Fallback: if the page is very simple and loads fast, or if the button is added very late
- // For very dynamic pages, a MutationObserver on document.body or a specific container might be more robust
- // but setTimeout is simpler for now.
- if (document.readyState === "complete") {
- // If page is already loaded, maybe CodeMirror is also ready?
- // This is less likely to be the case for why it's not working.
- // The click handler with setTimeout is more reliable.
- }
- document.body.appendChild(downloadButton);
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址