From ef2eb78e9605b7ff42d4be0b197cc56f17f2fd25 Mon Sep 17 00:00:00 2001 From: sanjaysgk <44039457+sanjaysgk@users.noreply.github.com> Date: Tue, 26 May 2026 11:16:40 +1000 Subject: [PATCH] docs: serve raw /llm.txt and add a copy-page-as-text button on all pages --- docs/javascripts/copy-page.js | 57 +++++++++++++++++++++++++++++++++++ docs/llm.md | 4 +++ docs/stylesheets/extra.css | 29 ++++++++++++++++++ docs_hooks.py | 17 +++++++++++ mkdocs.yml | 9 ++++++ 5 files changed, 116 insertions(+) create mode 100644 docs/javascripts/copy-page.js create mode 100644 docs/stylesheets/extra.css create mode 100644 docs_hooks.py diff --git a/docs/javascripts/copy-page.js b/docs/javascripts/copy-page.js new file mode 100644 index 0000000..09a5df3 --- /dev/null +++ b/docs/javascripts/copy-page.js @@ -0,0 +1,57 @@ +/* Adds a "Copy page as text" button to every docs page. Copies the rendered + text of the main content to the clipboard. Re-runs on Material instant + navigation. */ +(function () { + function addCopyButton() { + var content = document.querySelector(".md-content__inner"); + if (!content || content.querySelector(".copy-page-btn")) return; + + var btn = document.createElement("button"); + btn.type = "button"; + btn.className = "copy-page-btn"; + btn.innerHTML = 'Copy page as text'; + + btn.addEventListener("click", function () { + var clone = content.cloneNode(true); + clone.querySelectorAll(".copy-page-btn").forEach(function (b) { b.remove(); }); + var text = clone.innerText.replace(/\n{3,}/g, "\n\n").trim(); + + function done() { + var label = btn.querySelector(".cpb-label"); + var prev = label.textContent; + label.textContent = "Copied!"; + btn.classList.add("copied"); + setTimeout(function () { + label.textContent = prev; + btn.classList.remove("copied"); + }, 1500); + } + + if (navigator.clipboard && navigator.clipboard.writeText) { + navigator.clipboard.writeText(text).then(done).catch(fallback); + } else { + fallback(); + } + + function fallback() { + var ta = document.createElement("textarea"); + ta.value = text; + ta.style.position = "fixed"; + ta.style.opacity = "0"; + document.body.appendChild(ta); + ta.focus(); + ta.select(); + try { document.execCommand("copy"); done(); } catch (e) { /* ignore */ } + ta.remove(); + } + }); + + content.insertBefore(btn, content.firstChild); + } + + if (typeof window.document$ !== "undefined" && window.document$.subscribe) { + window.document$.subscribe(addCopyButton); // Material instant navigation + } else { + document.addEventListener("DOMContentLoaded", addCopyButton); + } +})(); diff --git a/docs/llm.md b/docs/llm.md index 0ccb70f..6cb919f 100644 --- a/docs/llm.md +++ b/docs/llm.md @@ -4,6 +4,10 @@ > help an end user run **every** MHC-TP use case correctly. It restates the > install, inputs, commands, flags, recipes, outputs, and the matching method so > no other page is required. +> +> **Raw text:** a machine-readable copy of this page is served at +> [`/llm.txt`](../llm.txt). Use the **Copy page as text** button (top right) to +> copy the whole guide to your clipboard. ## What MHC-TP does diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..eea5b63 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,29 @@ +/* "Copy page as text" button */ +.copy-page-btn { + float: right; + margin: 0 0 .5rem .75rem; + display: inline-flex; + align-items: center; + gap: .35rem; + font: inherit; + font-size: .68rem; + font-weight: 700; + letter-spacing: .02em; + padding: .3rem .7rem; + color: var(--md-primary-fg-color); + background: transparent; + border: 1px solid var(--md-primary-fg-color); + border-radius: .4rem; + cursor: pointer; + transition: background .15s, color .15s, border-color .15s; +} +.copy-page-btn::before { content: "\1F4CB"; /* clipboard */ } +.copy-page-btn:hover { + background: var(--md-primary-fg-color); + color: var(--md-primary-bg-color); +} +.copy-page-btn.copied { + border-color: #2e7d32; + color: #2e7d32; + background: transparent; +} diff --git a/docs_hooks.py b/docs_hooks.py new file mode 100644 index 0000000..903d191 --- /dev/null +++ b/docs_hooks.py @@ -0,0 +1,17 @@ +"""MkDocs build hooks. + +Emit a raw plain-text copy of the LLM guide at the site root (``/llm.txt``) so +machine/LLM consumers can fetch the instructions without HTML. The source of +truth is ``docs/llm.md``; this copies it verbatim on every build. +""" + +from __future__ import annotations + +import pathlib + + +def on_post_build(config, **kwargs) -> None: + site = pathlib.Path(config["site_dir"]) + src = pathlib.Path(config["docs_dir"]) / "llm.md" + if src.exists(): + (site / "llm.txt").write_text(src.read_text(encoding="utf-8"), encoding="utf-8") diff --git a/mkdocs.yml b/mkdocs.yml index 60fc1e9..084ee6e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -49,6 +49,15 @@ plugins: separate_signature: true docstring_section_style: list +hooks: + - docs_hooks.py + +extra_css: + - stylesheets/extra.css + +extra_javascript: + - javascripts/copy-page.js + markdown_extensions: - admonition - attr_list