iscc-sct / docs /javascripts /copypage.js
titusz's picture
Sync to v0.2.2 (github.com/iscc/iscc-sct @ bf39b3a)
8ef3a6b verified
Raw History Blame Contribute Delete
6.94 kB
/**
* "Copy page" split-button dropdown for documentation pages.
*
* Clicking the button copies the page as Markdown. Clicking the chevron
* opens a dropdown with additional actions: view as Markdown and edit on GitHub.
*
* Relies on per-page .md files generated by scripts/gen_llms_full.py.
*/
(function () {
"use strict";
var REPO_URL = "https://github.com/iscc/iscc-sct";
var EDIT_BRANCH = "main";
// Lucide icons (inline SVG)
var ICONS = {
copy:
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>',
fileText:
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>',
pencil:
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/></svg>',
chevronDown:
'<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>',
check:
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
};
/** Derive the docs-relative path from the current page URL. */
function getDocPath() {
var path = window.location.pathname;
if (path.endsWith("/")) path = path.slice(0, -1);
return path ? path.slice(1) + ".md" : "index.md";
}
/** URL to the per-page .md file (generated by gen_llms_full.py). */
function getMarkdownUrl() {
return "/" + getDocPath();
}
/** GitHub edit URL for the current page source. */
function getEditUrl() {
return REPO_URL + "/edit/" + EDIT_BRANCH + "/docs/" + getDocPath();
}
/** Create a dropdown menu item button. */
function createItem(icon, title, desc, onClick) {
var btn = document.createElement("button");
btn.className = "copy-page__item";
btn.type = "button";
btn.innerHTML =
'<span class="copy-page__item-icon">' +
icon +
"</span>" +
'<span class="copy-page__item-text">' +
'<span class="copy-page__item-title">' +
title +
"</span>" +
'<span class="copy-page__item-desc">' +
desc +
"</span>" +
"</span>";
btn.addEventListener("click", function () {
onClick(btn);
});
return btn;
}
/** Copy the page Markdown to clipboard with visual feedback on the action button. */
function copyPage(el, actionBtn) {
el.classList.remove("copy-page--open");
var origHtml = actionBtn.innerHTML;
fetch(getMarkdownUrl())
.then(function (r) {
if (!r.ok) throw new Error(r.status);
return r.text();
})
.then(function (text) {
return navigator.clipboard.writeText(text);
})
.then(function () {
actionBtn.innerHTML = ICONS.check + "<span>Copied!</span>";
setTimeout(function () {
actionBtn.innerHTML = origHtml;
}, 2000);
})
.catch(function (err) {
console.error("Copy page failed:", err);
});
}
function init() {
var article = document.querySelector(".md-content__inner");
if (!article) return;
var h1 = article.querySelector("h1");
if (!h1) return;
// Container
var el = document.createElement("div");
el.className = "copy-page";
// Split button wrapper
var split = document.createElement("div");
split.className = "copy-page__split";
// Left: direct copy action
var actionBtn = document.createElement("button");
actionBtn.className = "copy-page__action";
actionBtn.type = "button";
actionBtn.title = "Copy page as Markdown for LLMs";
actionBtn.innerHTML = ICONS.copy + "<span>Copy page</span>";
actionBtn.addEventListener("click", function () {
copyPage(el, actionBtn);
});
// Right: chevron toggle for dropdown
var toggleBtn = document.createElement("button");
toggleBtn.className = "copy-page__toggle";
toggleBtn.type = "button";
toggleBtn.title = "More actions";
toggleBtn.innerHTML = ICONS.chevronDown;
toggleBtn.addEventListener("click", function () {
el.classList.toggle("copy-page--open");
});
split.appendChild(actionBtn);
split.appendChild(toggleBtn);
// Dropdown menu
var menu = document.createElement("div");
menu.className = "copy-page__menu";
// Item 1: Copy page as Markdown
menu.appendChild(
createItem(
ICONS.copy,
"Copy page",
"Copy page as Markdown for LLMs",
function () {
copyPage(el, actionBtn);
},
),
);
// Item 2: View as Markdown
menu.appendChild(
createItem(
ICONS.fileText,
"View as Markdown",
"View this page as plain text",
function () {
el.classList.remove("copy-page--open");
window.open(getMarkdownUrl(), "_blank");
},
),
);
// Item 2: Edit on GitHub
menu.appendChild(
createItem(
ICONS.pencil,
"Edit on GitHub",
"Edit this page on GitHub",
function () {
el.classList.remove("copy-page--open");
window.open(getEditUrl(), "_blank");
},
),
);
el.appendChild(split);
el.appendChild(menu);
// Clicks inside the widget stay contained
el.addEventListener("click", function (e) {
e.stopPropagation();
});
// Wrap h1 and button in a flex container for alignment
var wrapper = document.createElement("div");
wrapper.className = "copy-page-heading";
h1.parentNode.insertBefore(wrapper, h1);
wrapper.appendChild(h1);
wrapper.appendChild(el);
// Close dropdown on outside click or Escape
document.addEventListener("click", function () {
el.classList.remove("copy-page--open");
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") el.classList.remove("copy-page--open");
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();