new functions and fixes

This commit is contained in:
Mateusz Gruszczyński
2026-08-06 09:43:49 +02:00
parent 7989a873e7
commit ce368cf3fd
8 changed files with 88 additions and 24 deletions
+22 -3
View File
@@ -20,6 +20,8 @@ import { loadHighlight, loadMediaPlayer, loadMermaid } from "@rustpad/vendor-lib
const token = location.pathname.split("/").filter(Boolean)[1];
const content = document.querySelector("#public-content");
const lineNumbersToggle = document.querySelector("#public-line-numbers-toggle");
const fullWidthToggle = document.querySelector("#public-full-width-toggle");
const fullWidthStorageKey = "rustpad:public-full-width";
const passwordDialog = document.querySelector("#public-password-dialog"), passwordForm = document.querySelector("#public-password-form"), passwordInput = document.querySelector("#public-password"), passwordError = document.querySelector("#public-password-error");
let pagePassword = "";
let mermaidRenderVersion = 0;
@@ -57,21 +59,38 @@ function scrollToPublicAnchor(hash, behavior = "auto") {
try { id = decodeURIComponent(rawId); } catch { id = rawId; }
const target = document.getElementById(id);
if (!target || !content.contains(target)) return false;
for (let details = target.closest("details"); details; details = details.parentElement?.closest("details")) details.open = true;
target.scrollIntoView({ behavior, block: "start" });
return true;
}
function setFullWidth(enabled, { persist = true } = {}) {
const active = Boolean(enabled);
document.body.classList.toggle("public-full-width", active);
fullWidthToggle.checked = active;
if (persist) {
try { localStorage.setItem(fullWidthStorageKey, active ? "1" : "0"); } catch { }
}
requestAnimationFrame(() => alignPreviewLineNumbers(content));
}
function restoreFullWidth() {
let enabled = false;
try { enabled = localStorage.getItem(fullWidthStorageKey) === "1"; } catch { }
setFullWidth(enabled, { persist: false });
}
async function initialize() { try { const page = await api(`/api/public/${encodeURIComponent(token)}`, { headers: pageHeaders() }); if (passwordDialog.open) passwordDialog.close(); passwordError.textContent = ""; document.querySelector("#public-title").textContent = page.title; document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")}${page.allow_task_updates ? " · tasks can be updated" : ""}`; document.title = `${page.title} · RustPad`; setMarkdownFiles(page.files || []); content.innerHTML = renderMarkdown(page.content); alignPreviewLineNumbers(content); lockPublicContent(page.allow_task_updates); await Promise.all([renderMermaid(), renderCodeHighlight(), renderMediaPlayers()]); requestAnimationFrame(() => scrollToPublicAnchor(location.hash)); } catch (error) { if (error.status === 401 || error.status === 403) { passwordError.textContent = error.status === 403 ? "Sign in with an authorized account or enter the resource password." : "Enter the correct password."; if (!passwordDialog.open) passwordDialog.showModal(); passwordInput.focus(); return; } content.replaceChildren(); const message = document.createElement("p"); message.className = "error"; message.textContent = String(error.message); content.append(message); } }
content.addEventListener("click", event => {
const link = event.target.closest('.markdown-toc a[href^="#"]');
if (!link) return;
const link = event.target.closest('a[href^="#"]');
if (!link || !content.contains(link)) return;
const hash = link.getAttribute("href");
if (!scrollToPublicAnchor(hash, "smooth")) return;
event.preventDefault();
if (!hash || hash === "#" || !scrollToPublicAnchor(hash, "smooth")) return;
history.replaceState(null, "", `${location.pathname}${location.search}${hash}`);
});
window.addEventListener("hashchange", () => scrollToPublicAnchor(location.hash, "smooth"));
content.addEventListener("change", async event => { const box = event.target.closest(".task-checkbox"); if (!box || box.disabled) return; const previous = !box.checked; box.disabled = true; try { const page = await api(`/api/public/${encodeURIComponent(token)}/tasks`, { method: "POST", headers: pageHeaders(), body: JSON.stringify({ source_line: Number(box.dataset.sourceLine), checked: box.checked }) }); document.querySelector("#public-meta").textContent = `Updated: ${new Date(page.updated_at).toLocaleString("en-US")} · tasks can be updated`; toast("Task saved"); } catch (error) { box.checked = previous; toast(error.message); } finally { box.disabled = false; } });
lineNumbersToggle.addEventListener("change", () => { document.body.classList.toggle("hide-preview-line-numbers", !lineNumbersToggle.checked); });
fullWidthToggle.addEventListener("change", () => setFullWidth(fullWidthToggle.checked));
window.addEventListener("resize", () => requestAnimationFrame(() => alignPreviewLineNumbers(content)));
passwordForm.addEventListener("submit", async event => { event.preventDefault(); pagePassword = passwordInput.value; await initialize(); });
passwordDialog.addEventListener("cancel", event => event.preventDefault());
document.querySelector("#copy-public-link").addEventListener("click", async () => { try { await copyText(location.href); toast("Link copied"); } catch (error) { toast(error.message); } });