Artifact a2f2ea4750a6eb64923d3b8ed9e129464c10429623c0e05723f32e0fc7315b1d:
- File
docs/site/layouts/index.html
— part of check-in
[e2a2f28861]
at
2026-04-26 07:52:11
on branch trunk
— docs site + landing + Cloudflare auto-deploy (#6)
* design: docs site + landing page + Cloudflare auto-deploy
Mirror dagnats's docs/landing infrastructure for libfossil: a single-page landing at site/index.html (forked CSS/HTML, content rewritten), a Hugo + Hextra docs site at docs/site/, gomarkdoc-generated SDK reference under docs/site/content/docs/reference/sdk/, llms.txt artifacts, and a wrangler.jsonc that lets Cloudflare's git integration auto-deploy on push to main. Existing docs/*.md files move under docs/site/content/docs/.
* design: correct landing-page path to Hugo layout override
dagnats's working landing page lives at docs/site/layouts/index.html (a Hugo home-page layout override), not at site/index.html. The top-level site/index.html in dagnats is an older draft that the build pipeline ignores. Match the working setup.
* design: align visual system + section list with live dagnats landing
The earlier description came from dagnats's stale top-level site/index.html (JetBrains Mono + Instrument Serif italic + ember/teal accents, 8 sections). The live Hugo layout that actually serves at / uses Geist Mono only, prefers-color-scheme dual-mode (warm cream / warm dark), a single ember accent, and 7 sections (Install, What it is, Why you might want it, How it fits together, Compared to neighbors, What it is not, Get going). Update the spec to match what we are actually mirroring.
* design: drop SDK regen from CF build to match dagnats exactly
Earlier draft had CF install Go + gomarkdoc and re-run docs-gen-sdk on every deploy. dagnats does not do that; it relies on the pre-commit hook to keep committed SDK pages in sync, and ships only Hugo + llms-gen + hugo --minify in the CF build. Match that: smaller, faster CF build, no Go-runtime assumption about Workers Builds, and a true 1:1 mirror.
* plan: docs site + landing + CF auto-deploy implementation steps
11 tasks covering Hugo+Hextra bootstrap, content moves, gomarkdoc SDK generation with pre-commit drift check, llms.txt artifacts, Makefile targets, landing page fork from dagnats's live Hugo home layout, wrangler.jsonc for CF Workers Static Assets, README update, and PR creation.
* docs(site): bootstrap Hugo + Hextra skeleton
* docs(site): move existing docs into Hugo content tree
* docs(site): fix links + H1s broken by docs-tree move
- Drop .md extensions on Hugo cross-doc links - Switch source-code references to absolute github.com/danmestas/libfossil URLs (relative ../ paths broke when files moved 3 dirs deeper) - Align H1 of architecture.md and migration-from-fossil.md with their frontmatter titles
* docs(site): add quickstart, examples, and SDK section stubs
* docs(site): tighten quickstart snippets + reference index
- Make the Sync example self-contained (open + sync, full main, real signatures) - Bridge from Clone to Open with a one-line prose link - Add a sentence to reference/_index.md so it does not render blank - Use the project's canonical "pure-Go, CGo-free" phrasing
* docs(site): add docs-gen-sdk Makefile target + commit generated SDK pages
* docs(site): pin gomarkdoc version + replace empty driver stubs with prose
- Add GOMARKDOC_VERSION Makefile variable so contributors don't drift on output format when a new gomarkdoc ships - Drop the two driver packages (modernc, ncruces) from gomarkdoc and hand-write _index.md pages explaining the register-on-import contract; the stubs were byte-stable but rendered as visibly broken documentation
* docs(site): add gen-llms-txt.sh + docs-gen-llms target
* docs(site): switch llms-full frontmatter strip from sed to awk
The sed range-negation pattern '/^---$/,/^---$/!p' silently re-opens a suppression range on any bare '---' in body content, eating Markdown horizontal rules and embedded YAML in fenced code blocks. The awk pattern strips the frontmatter only when it begins on line 1, leaving body content intact regardless of how many '---' lines it contains.
* hooks: pre-commit fails when SDK pages drift from code
* hooks: pre-commit re-installs gomarkdoc if it's the wrong version
The drift check's existing gate just looks for gomarkdoc on PATH. A contributor with an older gomarkdoc installed locally would have the gate pass and then run regeneration with their stale binary, producing either a silent false-negative or a confusing OUT-OF-DATE error with no version hint. Compare the installed version against GOMARKDOC_VERSION from Makefile and re-install on mismatch. Best-effort; offline contributors can still commit.
* docs(site): add docs-serve, docs-build, docs aggregate targets
* docs(site): make docs aggregate parallel-safe via explicit deps
The docs aggregate previously listed gen-sdk, gen-llms, and build as side-by-side prereqs. Make doesn't guarantee left-to-right ordering under -j, and gen-llms reads what gen-sdk writes, while build reads both. Encode the chain explicitly so 'make -j docs' is correct. Side benefit: 'make docs-build' standalone now produces a complete deployable site, matching contributor expectation.
* site(landing): fork dagnats home layout; rebrand chrome
* site(landing): rewrite §01–§07 content for libfossil
* site(landing): use .ascii class for diagram + fix §03 phrasing
* deploy: add wrangler.jsonc for CF Workers Static Assets
* docs: point README at the new docs site (user: [email protected], size: 21233) [annotate] [blame] [check-ins using]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="libfossil — pure-Go library and CLI for Fossil SCM repositories. Zero CGo, static binaries, WASM-ready."> <meta name="color-scheme" content="light dark"> <title>libfossil — pure-Go Fossil SCM library</title> <link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;700&display=swap" rel="stylesheet"> <style> :root { --paper: #f5f1e6; --paper-2:#ece7d8; --ink: #14110b; --mute: #5e5b53; --rule: #14110b; --ember: #c63a0f; --measure: 76ch; --gap: clamp(2.25rem, 5vw, 3.5rem); --mono: "Geist Mono", ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Consolas, "Courier New", monospace; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; -webkit-text-size-adjust: 100%; } body { background: var(--paper); color: var(--ink); font: 400 clamp(15px, 1.05vw, 17px)/1.7 var(--mono); font-feature-settings: "tnum" 1, "calt" 1, "ss01" 1; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; } .skip { position: absolute; left: -9999px; } .skip:focus { position: fixed; top: 0.75rem; left: 0.75rem; background: var(--ember); color: var(--paper); padding: 0.5rem 0.75rem; z-index: 10; text-decoration: none; } header.topbar { border-bottom: 1px solid var(--rule); padding: clamp(0.625rem, 2.5vw, 0.75rem) clamp(1rem, 4vw, 1.5rem); display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap; font-size: 0.78125rem; letter-spacing: 0.04em; } .topbar a { color: inherit; text-decoration: none; border-bottom: 1px solid currentColor; } .topbar a:hover { color: var(--ember); } .topbar .name { font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; } .topbar .meta { color: var(--mute); display: flex; gap: 1.25rem; flex-wrap: wrap; } .topbar .meta span { display: inline-flex; gap: 0.4rem; } .topbar .meta b { color: var(--ink); font-weight: 500; } main { max-width: 88ch; margin: 0 auto; padding: clamp(2rem, 4vw, 3rem) clamp(1rem, 4vw, 1.5rem) clamp(3rem, 6vw, 4.5rem); overflow-wrap: anywhere; } .title { display: grid; grid-template-columns: 1fr; gap: 1.25rem; padding-bottom: clamp(2rem, 4vw, 2.75rem); margin-bottom: clamp(2rem, 4vw, 3rem); border-bottom: 1px solid var(--rule); } .title h1 { font-size: clamp(2.75rem, 13vw, 6.5rem); font-weight: 700; letter-spacing: -0.05em; line-height: 0.92; } .title .lead { font-size: clamp(1rem, 1.2vw, 1.0625rem); max-width: var(--measure); color: var(--ink); line-height: 1.6; } .spec { width: 100%; border-collapse: collapse; font-size: 0.78125rem; margin-top: 0.5rem; letter-spacing: 0.02em; } .spec td { padding: 0.35rem 0; border-top: 1px dashed var(--rule); vertical-align: top; } .spec td:first-child { width: 12rem; color: var(--mute); text-transform: uppercase; letter-spacing: 0.14em; font-size: 0.6875rem; padding-top: 0.5rem; } .spec tr:first-child td { border-top: 1px solid var(--rule); } .spec tr:last-child td { border-bottom: 1px solid var(--rule); } section { margin-top: var(--gap); } section > .num { display: block; font-size: 0.6875rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--mute); margin-bottom: 0.4rem; } h2 { font-size: clamp(1.125rem, 1.6vw, 1.3125rem); font-weight: 700; letter-spacing: -0.01em; margin-bottom: 1.25rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--rule); display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; flex-wrap: wrap; } h2 .anchor { font-size: 0.6875rem; font-weight: 400; color: var(--mute); letter-spacing: 0.14em; text-transform: uppercase; } p { max-width: var(--measure); margin-bottom: 1rem; } p:last-child { margin-bottom: 0; } strong { font-weight: 700; } a { color: var(--ink); text-decoration: none; border-bottom: 1px solid var(--ink); } a:hover { color: var(--ember); border-bottom-color: var(--ember); } code { font: inherit; font-size: 0.94em; background: var(--paper-2); padding: 0.05em 0.35em; } ol, ul { padding-left: 0; max-width: var(--measure); list-style: none; } ol li, ul li { margin-bottom: 0.875rem; padding-left: 2.75rem; position: relative; line-height: 1.65; } ol { counter-reset: item; } ol li { counter-increment: item; } ol li::before { content: counter(item, decimal-leading-zero); position: absolute; left: 0; top: 0.05em; font-weight: 700; font-size: 0.78125rem; letter-spacing: 0.04em; color: var(--ember); width: 2rem; } ul li::before { content: "—"; position: absolute; left: 0.5rem; color: var(--mute); } .code-block { margin-bottom: 1rem; border: 1px solid var(--rule); background: var(--paper-2); } .code-bar { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; padding: 0.35rem 0.5rem 0.35rem 0.85rem; border-bottom: 1px solid var(--rule); background: var(--paper); font-size: 0.625rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--mute); } pre { padding: 0.875rem 1.25rem; font-size: 0.85rem; line-height: 1.65; overflow-x: auto; white-space: pre; font-family: var(--mono); color: var(--ink); } pre.ascii { background: transparent; border: 1px solid var(--rule); padding: clamp(0.75rem, 2.5vw, 1.25rem); font-size: clamp(0.7rem, 1.7vw, 0.8125rem); line-height: 1.45; color: var(--ink); margin-bottom: 1rem; overflow-x: auto; } .diagram { display: block; width: 100%; min-width: 30rem; height: auto; margin-bottom: 0.5rem; font-family: var(--mono); color: var(--ink); } .diagram .rule { stroke: var(--rule); stroke-width: 1; opacity: 0.5; } .diagram .box { fill: none; stroke: var(--rule); stroke-width: 1; } .diagram .head { font-size: 11px; letter-spacing: 1.4px; text-transform: uppercase; fill: var(--mute); } .diagram .item { font-size: 13px; fill: var(--ink); } .diagram .stack { font-size: 12px; fill: var(--mute); font-style: italic; } .diagram .flow line { stroke: var(--ember); stroke-width: 1.5; fill: none; } .diagram .arrowhead { fill: var(--ember); } .diagram .flow-label { font-size: 10px; fill: var(--ember); letter-spacing: 0.6px; text-transform: uppercase; } .copy-btn { background: transparent; color: var(--ink); border: 1px solid var(--ink); font: inherit; font-size: 0.625rem; letter-spacing: 0.1em; text-transform: uppercase; padding: 0.15rem 0.55rem; cursor: pointer; flex-shrink: 0; } .copy-btn:hover, .copy-btn:focus-visible { background: var(--ink); color: var(--paper); outline: none; } .copy-btn[data-state="copied"] { color: var(--ember); border-color: var(--ember); background: var(--paper); } .table-scroll { margin: 0 calc(-1 * clamp(0rem, 2vw, 0.5rem)); overflow-x: auto; -webkit-overflow-scrolling: touch; } table.cmp { width: 100%; min-width: 30rem; border-collapse: collapse; font-size: 0.875rem; margin-bottom: 0.75rem; } table.cmp th, table.cmp td { text-align: left; padding: 0.625rem 0.875rem; vertical-align: top; border-bottom: 1px dashed var(--rule); } table.cmp thead th { border-bottom: 1px solid var(--rule); border-top: 1px solid var(--rule); font-size: 0.6875rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.14em; background: var(--paper-2); } table.cmp td:first-child, table.cmp th:first-child { padding-left: 0.875rem; width: 30%; font-weight: 500; color: var(--mute); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.75rem; } table.cmp td:last-child, table.cmp th:last-child { padding-right: 0.875rem; } table.cmp .col-self { color: var(--ember); font-weight: 700; } table.cmp tbody tr:last-child td { border-bottom: 1px solid var(--rule); } .fineprint { font-size: 0.75rem; color: var(--mute); margin-top: 0.5rem; letter-spacing: 0.02em; } footer { border-top: 1px solid var(--rule); margin-top: var(--gap); padding: 1.5rem 0; font-size: 0.78125rem; color: var(--mute); display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; letter-spacing: 0.04em; } footer a { color: inherit; border-bottom-color: var(--mute); } @media (prefers-color-scheme: dark) { :root { --paper: #15140f; --paper-2:#1f1d17; --ink: #ece8dc; --mute: #908b80; --rule: #ece8dc; --ember: #ff5a26; } } @media print { body { background: white; color: black; font-size: 11pt; line-height: 1.5; } .code-block, pre { background: white; color: black; border: 1px solid black; } a { color: black; border-bottom: 0; text-decoration: underline; } a::after { content: " (" attr(href) ")"; font-size: 0.85em; color: #555; } .copy-btn, .skip { display: none; } section { break-inside: avoid; } } @media (max-width: 540px) { header.topbar { flex-direction: column; align-items: flex-start; gap: 0.5rem; } .topbar .meta { gap: 0.75rem; row-gap: 0.35rem; } .spec td:first-child { width: 8rem; font-size: 0.625rem; } .spec td { padding: 0.4rem 0; } table.cmp { font-size: 0.6875rem; min-width: 0; table-layout: auto; } table.cmp th, table.cmp td { padding: 0.45rem 0.4rem; } table.cmp tbody td { white-space: normal; word-break: normal; overflow-wrap: break-word; } table.cmp thead th { font-size: 0.5rem; letter-spacing: 0.06em; padding: 0.5rem 0.3rem; white-space: nowrap; } table.cmp td:first-child, table.cmp th:first-child { padding-left: 0.55rem; width: 28%; font-size: 0.5625rem; letter-spacing: 0.04em; } table.cmp td:last-child, table.cmp th:last-child { padding-right: 0.55rem; } ol li, ul li { padding-left: 2.25rem; } ol li::before { width: 1.75rem; } footer { flex-direction: column; gap: 0.5rem; align-items: flex-start; } .copy-btn { font-size: 0.5625rem; padding: 0.1rem 0.4rem; } pre { font-size: 0.78rem; padding: 0.75rem 0.9rem; } .code-bar { padding: 0.3rem 0.4rem 0.3rem 0.7rem; } } @media (max-width: 380px) { .topbar .name { letter-spacing: 0.12em; } .spec td:first-child { width: 7rem; } table.cmp { font-size: 0.625rem; } table.cmp th, table.cmp td { padding: 0.4rem 0.25rem; } table.cmp thead th { font-size: 0.4375rem; padding: 0.45rem 0.2rem; } table.cmp td:first-child, table.cmp th:first-child { font-size: 0.5rem; padding-left: 0.4rem; width: 26%; } } </style> </head> <body> <a href="#main" class="skip">skip to content</a> <header class="topbar"> <div class="name">libfossil</div> <div class="meta"> <span>v <b>0.4.1</b></span> <span>license <b>MIT</b></span> <span><a href="https://github.com/danmestas/libfossil">github</a></span> <span><a href="/docs/">docs</a></span> </div> </header> <main id="main"> <section class="title"> <h1>libfossil</h1> <p class="lead">A pure-Go library for Fossil SCM repositories. Zero CGo. Static binaries. WASM-ready.</p> <table class="spec"> <tbody> <tr><td>Project</td> <td>libfossil — pure-Go Fossil SCM library</td></tr> <tr><td>Language</td> <td>Go (module <code>github.com/danmestas/libfossil</code>)</td></tr> <tr><td>Runtime deps</td> <td>none · pluggable SQLite drivers (modernc · ncruces)</td></tr> <tr><td>Binaries</td> <td>1 (libfossil CLI)</td></tr> <tr><td>License</td> <td>MIT</td></tr> <tr><td>Status</td> <td>v0.4.1 · alpha · breaking changes possible</td></tr> </tbody> </table> </section> <section> <span class="num">§01</span> <h2>Install <span class="anchor">install</span></h2> <div class="code-block"> <div class="code-bar"> <span>shell</span> <button type="button" class="copy-btn" data-copy="install" aria-label="Copy install command">copy</button> </div> <pre id="install">$ go install github.com/danmestas/libfossil/cmd/libfossil@latest $ libfossil --help</pre> </div> <p>That is the entire install. The binary is pure-Go, statically linked, and CGo-free — drop it on any Linux/macOS/Windows host with no runtime dependency.</p> </section> <section> <span class="num">§02</span> <h2>What it is <span class="anchor">overview</span></h2> <p>libfossil is a pure-Go reader and writer for <a href="https://fossil-scm.org">Fossil SCM</a> repositories — the SQLite-backed <code>.fossil</code> files used by Fossil. It exposes a Go API for opening repos, reading the timeline, committing, and syncing over HTTP, plus a <code>libfossil</code> CLI driving the same code paths.</p> <p>No CGo means static binaries that cross-compile cleanly. A <code>GOOS=wasip1</code> build runs in browsers and edge runtimes. Two SQLite drivers (<code>modernc</code>, <code>ncruces</code>) are swappable depending on whether you want pure-Go ergonomics or a smaller WASM footprint.</p> <ul> <li><strong>One library, three pluggable axes.</strong> Swap the SQLite driver (<code>modernc</code>/<code>ncruces</code>), the sync transport (HTTP/custom), or the observer (OTel/custom) without touching application code.</li> <li><strong>Static everywhere.</strong> Static binaries cross-compile cleanly. WASM works.</li> <li><strong>Embeddable.</strong> Drop <code>libfossil.Open</code> into any Go service that needs to read <code>.fossil</code> repos — build pipelines, mirror servers, code-search indexers.</li> </ul> </section> <section> <span class="num">§03</span> <h2>Why you might want it <span class="anchor">why</span></h2> <p>Three reasons to reach for libfossil instead of shelling out to the upstream <code>fossil</code> binary or using CGo bindings.</p> <p><strong>You're embedding Fossil into a Go service.</strong> Build pipelines, code-search indexers, mirror servers, and AI-agent tools that read repository history all benefit from a library API instead of subprocess parsing. No exec calls, no version drift between hosts, observable through Go's standard <code>context.Context</code> and OpenTelemetry hooks.</p> <p><strong>You're shipping to constrained targets.</strong> Static binaries cross-compile in seconds; CGo bindings don't. WASM compilation works out of the box. The cost is a slightly slower SQLite (modernc is pure Go); the upside is the binary runs on any target without a build farm.</p> <p><strong>You want one library, not five subprocesses.</strong> Calling <code>fossil</code> over <code>os/exec</code> means re-parsing CLI text on every request. libfossil exposes typed Go values directly.</p> </section> <section> <span class="num">§04</span> <h2>How it fits together <span class="anchor">architecture</span></h2> <div class="table-scroll"> <pre class="ascii"> ┌─────────────┐ ┌─────────────────┐ │ CLI │ │ Library API │ └──────┬──────┘ └────────┬────────┘ │ │ └────────┬───────────┘ ▼ ┌─────────────┐ │ Repo │ ◀───── you call this └──────┬──────┘ │ ┌───────────────────┼───────────────────┐ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ DB Driver │ │ Transport │ │ Observer │ │ modernc / │ │ (HTTP) │ │ (OTel) │ │ ncruces │ └────────────┘ └────────────┘ └─────┬──────┘ ▼ ┌────────────┐ │ .fossil │ (SQLite) └────────────┘ </pre> </div> <p class="fineprint">Three pluggable axes off <code>Repo</code>. The DB driver decides how SQLite runs (pure Go vs. Wasm-friendly). The transport decides how sync talks to a remote (HTTP today; custom transports for unusual networks). Observers hook into sync, checkout, and commit events for telemetry and auditing.</p> </section> <section> <span class="num">§05</span> <h2>Compared to neighbors <span class="anchor">comparison</span></h2> <div class="table-scroll"> <table class="cmp"> <thead> <tr> <th></th> <th class="col-self">libfossil</th> <th>shell-out to fossil</th> <th>CGo libfossil bindings</th> </tr> </thead> <tbody> <tr><td>Runtime deps</td> <td class="col-self">none</td> <td><code>fossil</code> binary in PATH</td> <td>C toolchain + libsqlite3</td></tr> <tr><td>Static binary</td> <td class="col-self">yes</td> <td>host fossil only</td> <td>host SQLite only</td></tr> <tr><td>WASM target</td> <td class="col-self">yes (wasip1)</td> <td>no</td> <td>no</td></tr> <tr><td>Observability</td> <td class="col-self">context + OTel</td> <td>parse stderr</td> <td>none built-in</td></tr> <tr><td>Embeddable in Go</td> <td class="col-self">yes</td> <td>subprocess only</td> <td>yes (with CGo)</td></tr> <tr><td>API stability</td> <td class="col-self">alpha; v0.x</td> <td>stable</td> <td>tracks C upstream</td></tr> </tbody> </table> </div> <p class="fineprint">Comparison reflects libfossil's current scope. The upstream Fossil binary remains the reference implementation; libfossil targets the embedding-into-Go-services niche.</p> </section> <section> <span class="num">§06</span> <h2>What it is not <span class="anchor">limits</span></h2> <p><strong>No GUI.</strong> Fossil ships an embedded web UI; libfossil does not. If you need it, run the upstream <code>fossil</code> binary alongside.</p> <p><strong>No hosting (yet).</strong> libfossil reads and writes <code>.fossil</code> files; it does not run an HTTP server that serves them to other Fossil clients. Mirroring works as a sync target, not a host.</p> <p><strong>No magic schema migrations.</strong> libfossil tracks the upstream Fossil schema. Breaking format changes upstream require a libfossil update before you can open the new format.</p> </section> <section> <span class="num">§07</span> <h2>Get going <span class="anchor">start</span></h2> <div class="code-block"> <div class="code-bar"> <span>shell</span> <button type="button" class="copy-btn" data-copy="quickstart" aria-label="Copy quickstart commands">copy</button> </div> <pre id="quickstart">$ go install github.com/danmestas/libfossil/cmd/libfossil@latest $ libfossil --help # CLI commands $ libfossil --version</pre> </div> <p>Walkthrough: <a href="/docs/quickstart/">/docs/quickstart</a> · Architecture: <a href="/docs/architecture/">/docs/architecture</a> · SDK reference: <a href="/docs/reference/sdk/">/docs/reference/sdk</a> · Source: <a href="https://github.com/danmestas/libfossil">github.com/danmestas/libfossil</a>.</p> </section> </main> <footer> <div>libfossil · MIT · v0.4.1 · 2026 · <a href="https://github.com/danmestas/libfossil">github.com/danmestas/libfossil</a></div> </footer> <script> (function () { var btns = document.querySelectorAll(".copy-btn"); btns.forEach(function (b) { b.addEventListener("click", function () { var t = document.getElementById(b.dataset.copy); if (!t) return; var s = t.textContent.replace(/^\s*\$\s?/gm, "").replace(/\s+#.*$/gm, "").trim(); var done = function () { b.dataset.state = "copied"; b.textContent = "copied"; setTimeout(function () { b.dataset.state = ""; b.textContent = "copy"; }, 1500); }; if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(s).then(done).catch(function () { b.textContent = "select+copy"; }); } else { var ta = document.createElement("textarea"); ta.value = s; ta.style.position = "fixed"; ta.style.left = "-9999px"; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); done(); } catch (e) { b.textContent = "select+copy"; } document.body.removeChild(ta); } }); }); })(); </script> </body> </html>