From 852074e61c068d4837f8c9e3a995795cde344642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Sat, 25 Jul 2026 21:26:44 +0200 Subject: [PATCH] ldap and toc fix --- .env.example | 3 ++- Cargo.lock | 2 +- Cargo.toml | 2 +- static/css/styles.css | 5 +++++ static/home.html | 4 ++-- static/js/public.js | 21 ++++++++++++++++++++- 6 files changed, 31 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index 6a38ad2..3208d7d 100644 --- a/.env.example +++ b/.env.example @@ -101,4 +101,5 @@ AUTHORIZATION_TYPE=local # LDAP_USER_FILTER=(uid={username}) # LDAP_USERNAME_ATTRIBUTE=uid # email auth: LDAP_EMAIL_ATTRIBUTE=mail - +#for both +#LDAP_USER_FILTER=(|(uid={username})(mail={username})) \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a0e8b86..f9eba46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "rustpad" -version = "0.0.30" +version = "0.0.31" dependencies = [ "argon2", "aws-config", diff --git a/Cargo.toml b/Cargo.toml index 91ef205..ad1db99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpad" -version = "0.0.30" +version = "0.0.31" edition = "2024" rust-version = "1.94" description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL" diff --git a/static/css/styles.css b/static/css/styles.css index 3a08dbf..55e0068 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -4139,3 +4139,8 @@ dialog::backdrop { max-width: 150px; transform: none; } + +/* Keep public TOC targets visible below the sticky page header. */ +.public-content :is(h1, h2, h3, h4, h5, h6)[id] { + scroll-margin-top: 88px; +} diff --git a/static/home.html b/static/home.html index 041bcb4..91e0ba9 100644 --- a/static/home.html +++ b/static/home.html @@ -97,8 +97,8 @@
- +
diff --git a/static/js/public.js b/static/js/public.js index 0191bff..28c55b9 100644 --- a/static/js/public.js +++ b/static/js/public.js @@ -16,7 +16,26 @@ function lockPublicContent(allowTaskUpdates) { content.querySelectorAll('.preview-editable').forEach(node => node.classList.remove('preview-editable')); content.querySelectorAll('.task-checkbox').forEach(box => { box.disabled = !allowTaskUpdates; box.title = allowTaskUpdates ? 'Update this task' : 'Task updates are disabled by the owner'; }); } -async function initialize() { try { const page = await api(`/api/public/${encodeURIComponent(token)}`); 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`; content.innerHTML = renderMarkdown(page.content); alignPreviewLineNumbers(content); lockPublicContent(page.allow_task_updates); await Promise.all([renderMermaid(), renderCodeHighlight()]); } catch (error) { content.innerHTML = `

${String(error.message)}

`; } } +function scrollToPublicAnchor(hash, behavior = "auto") { + const rawId = String(hash || "").replace(/^#/, ""); + if (!rawId) return false; + let id; + try { id = decodeURIComponent(rawId); } catch { id = rawId; } + const target = document.getElementById(id); + if (!target || !content.contains(target)) return false; + target.scrollIntoView({ behavior, block: "start" }); + return true; +} +async function initialize() { try { const page = await api(`/api/public/${encodeURIComponent(token)}`); 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`; content.innerHTML = renderMarkdown(page.content); alignPreviewLineNumbers(content); lockPublicContent(page.allow_task_updates); await Promise.all([renderMermaid(), renderCodeHighlight()]); requestAnimationFrame(() => scrollToPublicAnchor(location.hash)); } catch (error) { content.innerHTML = `

${String(error.message)}

`; } } +content.addEventListener("click", event => { + const link = event.target.closest('.markdown-toc a[href^="#"]'); + if (!link) return; + const hash = link.getAttribute("href"); + if (!scrollToPublicAnchor(hash, "smooth")) return; + event.preventDefault(); + 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", 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); }); document.querySelector("#copy-public-link").addEventListener("click", async () => { try { await copyText(location.href); toast("Link copied"); } catch (error) { toast(error.message); } });