Docs exports: JSON llms.txt llms-full.txt Agent Files

class Shards::Commands::Docs

Overview

Generates project documentation with theming and AI assistant integration.

Wraps crystal docs and post-processes the output to:

Defined in:

commands/docs.cr

Constant Summary

AI_BUTTONS_CSS = ".ai-assistant-bar {\n display: flex;\n gap: 8px;\n align-items: center;\n margin: 15px 0;\n padding: 10px 0;\n border-top: 1px solid var(--h2-border, #E6E6E6);\n flex-wrap: wrap;\n}\n.ai-assistant-bar .ai-label {\n font-size: 13px;\n color: var(--ai-btn-text, #555);\n margin-right: 4px;\n}\n.ai-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 5px 12px;\n border: 1px solid var(--ai-btn-border, #ddd);\n border-radius: 6px;\n background: var(--ai-btn-bg, #f0f0f0);\n color: var(--ai-btn-text, #555);\n text-decoration: none;\n font-size: 13px;\n cursor: pointer;\n transition: background .15s, border-color .15s;\n}\n.ai-btn:hover {\n background: var(--ai-btn-hover-bg, #e0e0e0);\n border-color: var(--ai-btn-border, #ccc);\n}\n.ai-btn:visited {\n color: var(--ai-btn-text, #555);\n}\n.ai-btn svg {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n}\n.ai-btn.claude-btn:hover { border-color: #D97706; }\n.ai-btn.chatgpt-btn:hover { border-color: #10A37F; }\n.ai-btn.gemini-btn:hover { border-color: #4285F4; }\n.ai-btn.md-btn:hover { border-color: var(--accent-secondary, #624288); }\n.docs-resource-bar {\n display: flex;\n gap: 8px;\n align-items: center;\n margin: 0 0 16px;\n flex-wrap: wrap;\n}\n.docs-resource-bar .resource-btn:hover {\n border-color: var(--accent-secondary, #624288);\n}\n.ashard-story {\n margin: 0 0 18px;\n padding: 16px 18px;\n border: 1px solid rgba(98, 66, 136, 0.16);\n border-radius: 14px;\n background:\n linear-gradient(135deg, rgba(238, 231, 248, 0.95), rgba(248, 244, 253, 0.95)),\n radial-gradient(circle at top right, rgba(98, 66, 136, 0.12), transparent 42%);\n box-shadow: 0 18px 40px rgba(71, 38, 110, 0.08);\n}\n.ashard-story h2 {\n margin: 0 0 10px;\n padding: 0;\n border: 0;\n}\n.ashard-story p {\n margin: 0 0 10px;\n}\n.ashard-story p:last-child {\n margin-bottom: 0;\n}\n.ashard-story code {\n font-size: .95em;\n}\n@media (prefers-color-scheme: dark) {\n .ashard-story {\n border-color: rgba(176, 146, 212, 0.22);\n background:\n linear-gradient(135deg, rgba(42, 31, 58, 0.95), rgba(28, 24, 36, 0.96)),\n radial-gradient(circle at top right, rgba(176, 146, 212, 0.15), transparent 42%);\n box-shadow: 0 18px 40px rgba(0, 0, 0, 0.35);\n }\n}"
AI_BUTTONS_JS = "<script>\n(function() {\n function getPageMarkdown() {\n var title = document.querySelector('h1.type-name');\n var content = document.querySelector('.main-content');\n if (!content) return '';\n\n var text = '';\n if (title) text += '# ' + title.textContent.trim() + '\\n\\n';\n\n var sections = content.querySelectorAll('h2, p, pre, dl, .entry-detail');\n sections.forEach(function(el) {\n if (el.tagName === 'H2') text += '## ' + el.textContent.trim() + '\\n\\n';\n else if (el.tagName === 'P') text += el.textContent.trim() + '\\n\\n';\n else if (el.tagName === 'PRE') text += '```\\n' + el.textContent.trim() + '\\n```\\n\\n';\n else if (el.tagName === 'DL') {\n el.querySelectorAll('dt').forEach(function(dt) {\n text += '- `' + dt.textContent.trim() + '`\\n';\n });\n text += '\\n';\n }\n });\n return text;\n }\n\n function buildPrompt(typeName, projectName) {\n return 'I\\'m working with the ' + projectName + ' Crystal library. ' +\n 'Help me understand and use `' + typeName + '`. ' +\n 'Here is the API documentation:\\n\\n';\n }\n\n function getTypeName() {\n var el = document.querySelector('h1.type-name');\n return el ? el.textContent.trim() : document.title;\n }\n\n function getProjectName() {\n var el = document.querySelector('.project-name');\n return el ? el.textContent.trim() : 'this project';\n }\n\n function openInClaude() {\n var prompt = buildPrompt(getTypeName(), getProjectName()) + getPageMarkdown();\n var url = 'https://claude.ai/new?q=' + encodeURIComponent(prompt);\n window.open(url, '_blank');\n }\n\n function openInChatGPT() {\n var prompt = buildPrompt(getTypeName(), getProjectName()) + getPageMarkdown();\n var url = 'https://chatgpt.com/?q=' + encodeURIComponent(prompt);\n window.open(url, '_blank');\n }\n\n function openInGemini() {\n var prompt = buildPrompt(getTypeName(), getProjectName()) + getPageMarkdown();\n var url = 'https://gemini.google.com/app?q=' + encodeURIComponent(prompt);\n window.open(url, '_blank');\n }\n\n function viewMarkdown() {\n var mdPath = window.location.pathname.replace(/\\.html$/, '.md');\n if (mdPath === window.location.pathname) mdPath += '.md';\n window.open(mdPath, '_blank');\n }\n\n document.addEventListener('DOMContentLoaded', function() {\n var h1 = document.querySelector('h1.type-name');\n var mainContent = document.querySelector('.main-content');\n if (!mainContent) return;\n\n var bar = document.createElement('div');\n bar.className = 'ai-assistant-bar';\n bar.innerHTML =\n '<span class=\"ai-label\">Discuss with AI:</span>' +\n '<button class=\"ai-btn claude-btn\" onclick=\"window.__openInClaude()\" title=\"Open in Claude\">' +\n '<svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h-2v-2h2v2zm2.07-7.75l-.9.92C11.45 10.9 11 11.5 11 13H9v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H6c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\" fill=\"currentColor\"/></svg>' +\n 'Claude' +\n '</button>' +\n '<button class=\"ai-btn chatgpt-btn\" onclick=\"window.__openInChatGPT()\" title=\"Open in ChatGPT\">' +\n '<svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\" fill=\"currentColor\"/></svg>' +\n 'ChatGPT' +\n '</button>' +\n '<button class=\"ai-btn gemini-btn\" onclick=\"window.__openInGemini()\" title=\"Open in Gemini\">' +\n '<svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M12 2L2 7v10l10 5 10-5V7L12 2zm0 2.18L19.18 8 12 11.82 4.82 8 12 4.18zM4 9.12l7 3.5V19.5l-7-3.5V9.12zm9 10.38v-6.88l7-3.5v6.88l-7 3.5z\" fill=\"currentColor\"/></svg>' +\n 'Gemini' +\n '</button>' +\n '<button class=\"ai-btn md-btn\" onclick=\"window.__viewMarkdown()\" title=\"View as Markdown\">' +\n '<svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M20.56 18H3.44C2.65 18 2 17.37 2 16.59V7.41C2 6.63 2.65 6 3.44 6h17.12c.79 0 1.44.63 1.44 1.41v9.18c0 .78-.65 1.41-1.44 1.41zM6.81 15.19v-3.68l1.83 2.29 1.83-2.29v3.68h1.83V8.81h-1.83l-1.83 2.29-1.83-2.29H5v6.38h1.81zm10.99-3.19h-1.83V8.81h-1.83V12h-1.84l2.75 3.19L17.8 12z\" fill=\"currentColor\"/></svg>' +\n 'Markdown' +\n '</button>';\n\n if (h1 && h1.nextSibling) {\n mainContent.insertBefore(bar, h1.nextSibling);\n } else {\n mainContent.insertBefore(bar, mainContent.firstChild);\n }\n });\n\n window.__openInClaude = openInClaude;\n window.__openInChatGPT = openInChatGPT;\n window.__openInGemini = openInGemini;\n window.__viewMarkdown = viewMarkdown;\n})();\n</script>"
CSS_VARIABLES = ":root {\n /* Sidebar */\n --sidebar-bg: #2E1052;\n --sidebar-text: #F8F4FD;\n --sidebar-link-hover: #866BA6;\n --sidebar-shadow: rgba(0,0,0,.35);\n --sidebar-input-shadow: rgba(0,0,0,.25);\n --sidebar-input-focus-shadow: rgba(0,0,0,.5);\n --sidebar-focus-outline: #D1B7F1;\n --sidebar-width: 30em;\n\n /* Project header */\n --project-name-color: #f4f4f4;\n\n /* Main content */\n --body-bg: #FFFFFF;\n --body-text: #333;\n --body-font: \"Avenir\", \"Tahoma\", \"Lucida Sans\", \"Lucida Grande\", Verdana, Arial, sans-serif;\n --link-color: #263F6C;\n --link-visited: #112750;\n --heading-color: #444444;\n\n /* Type name banner */\n --type-name-color: #47266E;\n --type-name-bg: #F8F8F8;\n --type-name-border: #EBEBEB;\n\n /* Code and signatures */\n --code-font: Menlo, Monaco, Consolas, 'Courier New', Courier, monospace;\n --code-bg: rgba(40,35,30,0.05);\n --pre-bg: #fdfdfd;\n --pre-border: #eee;\n --pre-text: #333;\n --signature-bg: #f8f8f8;\n --signature-color: #47266E;\n --signature-border: #f0f0f0;\n --signature-hover-bg: #D5CAE3;\n --signature-hover-border: #624288;\n\n /* Accent colors */\n --accent-primary: #47266E;\n --accent-secondary: #624288;\n --accent-highlight: #D5CAE3;\n --kind-color: #866BA6;\n\n /* Inherited methods */\n --inherited-link: #47266E;\n --inherited-link-hover: #6C518B;\n --inherited-tooltip-bg: #D5CAE3;\n\n /* Syntax highlighting */\n --syntax-comment: #969896;\n --syntax-number: #0086b3;\n --syntax-type: #0086b3;\n --syntax-string: #183691;\n --syntax-interpolation: #7f5030;\n --syntax-keyword: #a71d5d;\n --syntax-operator: #a71d5d;\n --syntax-method: #795da3;\n\n /* Borders */\n --h2-border: #E6E6E6;\n --table-border: #eee;\n\n /* Search results */\n --search-current-border: #ddd;\n --search-current-bg: rgba(200,200,200,0.4);\n --search-args-color: #dddddd;\n\n /* Permalink */\n --permalink-color: #624288;\n\n /* AI buttons */\n --ai-btn-bg: #f0f0f0;\n --ai-btn-border: #ddd;\n --ai-btn-text: #555;\n --ai-btn-hover-bg: #e0e0e0;\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --body-bg: #1b1b1b;\n --body-text: white;\n --link-color: #8cb4ff;\n --link-visited: #5f8de3;\n --heading-color: white;\n --type-name-color: white;\n --type-name-bg: #202020;\n --type-name-border: #353535;\n --code-bg: #202020;\n --pre-bg: #202020;\n --pre-border: #353535;\n --pre-text: white;\n --signature-bg: #202020;\n --signature-color: white;\n --signature-border: #353535;\n --signature-hover-bg: #443d4d;\n --signature-hover-border: #b092d4;\n --accent-primary: white;\n --accent-highlight: #443d4d;\n --kind-color: #b092d4;\n --inherited-link: #B290D9;\n --inherited-link-hover: #D4B7F4;\n --inherited-tooltip-bg: #443d4d;\n --syntax-comment: #a1a1a1;\n --syntax-number: #00ade6;\n --syntax-type: #00ade6;\n --syntax-string: #7799ff;\n --syntax-interpolation: #b38668;\n --syntax-keyword: #ff66ae;\n --syntax-operator: #ff66ae;\n --syntax-method: #b9a5d6;\n --h2-border: #353535;\n --table-border: #353535;\n --permalink-color: #b092d4;\n --ai-btn-bg: #2a2a2a;\n --ai-btn-border: #444;\n --ai-btn-text: #ccc;\n --ai-btn-hover-bg: #3a3a3a;\n }\n}"

Instance Method Summary

Instance methods inherited from class Shards::Command

check_crystal_version(packages) check_crystal_version, check_symlink_privilege check_symlink_privilege, handle_resolver_errors(&) handle_resolver_errors, lockfile? lockfile?, lockfile_path : String lockfile_path, locks locks, override override, override_path : String | Nil override_path, path : String path, spec spec, spec_filename spec_filename, spec_path : String spec_path, touch_install_path touch_install_path, write_lockfile(packages) write_lockfile

Constructor methods inherited from class Shards::Command

new(path) new

Class methods inherited from class Shards::Command

run(path, *args, **kwargs) run

Instance Method Detail

def run(args : Array(String)) #

[View source]