diff --git a/backend/main.py b/backend/main.py
index 09cb54c..bbc0637 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -2816,51 +2816,6 @@ async def api_share_revoke(share_id: str, current_user=Depends(require_auth)):
raise HTTPException(404, "Share not found")
return {"status": "revoked"}
-
-@app.get("/s/{token}/pdf")
-async def public_share_pdf(token: str):
- """Download shared document as PDF — no authentication required."""
- share = get_share_by_token(token)
- if not share:
- raise HTTPException(404, "Share not found or expired")
- vault_data = get_vault_data(share["vault"])
- if not vault_data:
- raise HTTPException(404, "Vault not found")
- vault_root = Path(vault_data["path"])
- file_path = _resolve_safe_path(vault_root, share["path"])
- if not file_path.exists():
- raise HTTPException(404, "File not found")
- try:
- raw = file_path.read_text(encoding="utf-8", errors="replace")
- except Exception:
- raise HTTPException(500, "Cannot read file")
- record_access(token)
- raw = redact_file_content(raw, str(file_path))
- post = parse_markdown_file(raw)
- html = _render_markdown(post.content, share["vault"], file_path)
- title = post.metadata.get("title", file_path.stem)
-
- # Build a full HTML page for PDF rendering
- full_html = f"""
{title}
-{title}
{html}"""
-
- filename = f"{title}.pdf"
- return HTMLResponse(
- content=full_html,
- media_type="text/html",
- headers={
- "Content-Disposition": f'attachment; filename="{filename}"',
- "X-PDF-Download": "true",
- },
- )
-
-
@app.get("/s/{token}/pdf")
async def public_share_pdf_download(token: str):
"""Download shared document as real PDF via WeasyPrint."""
diff --git a/frontend/app.js b/frontend/app.js
index 70b3601..2d02076 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -3242,12 +3242,6 @@
const sourceBtn = el("button", { class: "btn-action", title: "Voir la source" }, [icon("code", 14), document.createTextNode("Source")]);
- const downloadBtn = el("button", { class: "btn-action", title: "Télécharger" }, [icon("download", 14), document.createTextNode("Télécharger")]);
- downloadBtn.addEventListener("click", () => {
- const dlUrl = `/api/file/${encodeURIComponent(data.vault)}/download?path=${encodeURIComponent(data.path)}`;
- window.open(dlUrl, "_blank");
- });
-
// MD download button
const mdBtn = el("button", { class: "btn-action", title: "Télécharger en .md" }, [icon("file-text", 14), document.createTextNode(".md")]);
mdBtn.addEventListener("click", () => {
@@ -3359,7 +3353,7 @@
// Assemble
area.innerHTML = "";
area.appendChild(breadcrumb);
- area.appendChild(el("div", { class: "file-header" }, [el("div", { class: "file-title" }, [document.createTextNode(data.title)]), tagsDiv, el("div", { class: "file-actions" }, [copyBtn, sourceBtn, downloadBtn, mdBtn, pdfBtn, editBtn, openNewWindowBtn, tocBtn, shareBtn, bookmarkBtn])]));
+ area.appendChild(el("div", { class: "file-header" }, [el("div", { class: "file-title" }, [document.createTextNode(data.title)]), tagsDiv, el("div", { class: "file-actions" }, [copyBtn, sourceBtn, mdBtn, pdfBtn, editBtn, openNewWindowBtn, tocBtn, shareBtn, bookmarkBtn])]));
if (fmSection) area.appendChild(fmSection);
area.appendChild(mdDiv);
area.appendChild(rawDiv);
diff --git a/frontend/popout.html b/frontend/popout.html
index 8cdbf45..d23651b 100644
--- a/frontend/popout.html
+++ b/frontend/popout.html
@@ -751,8 +751,8 @@ const RightSidebarManager = {
const dlBtn = document.createElement("button");
dlBtn.className = "btn-action";
- dlBtn.title = "Télécharger";
- dlBtn.innerHTML = ' Télécharger';
+ dlBtn.title = "Télécharger en .md";
+ dlBtn.innerHTML = ' .md';
dlBtn.addEventListener("click", () => {
const a = document.createElement("a");
a.href = `/api/file/${encodeURIComponent(vault)}/download?path=${encodeURIComponent(path)}`;
@@ -763,6 +763,15 @@ const RightSidebarManager = {
});
actionsDiv.appendChild(dlBtn);
+ const pdfBtn = document.createElement("button");
+ pdfBtn.className = "btn-action";
+ pdfBtn.title = "Télécharger en PDF";
+ pdfBtn.innerHTML = ' PDF';
+ pdfBtn.addEventListener("click", () => {
+ window.open(`/api/file/${encodeURIComponent(vault)}/pdf?path=${encodeURIComponent(path)}`, "_blank");
+ });
+ actionsDiv.appendChild(pdfBtn);
+
const editBtn = document.createElement("button");
editBtn.className = "btn-action";
editBtn.title = "Éditer";
@@ -779,6 +788,48 @@ const RightSidebarManager = {
tocBtn.innerHTML = ' TOC';
tocBtn.addEventListener("click", () => { RightSidebarManager.toggle(); });
actionsDiv.appendChild(tocBtn);
+
+ // Share button
+ const shareBtn = document.createElement("button");
+ shareBtn.className = "btn-action";
+ shareBtn.title = "Partager ce document";
+ shareBtn.innerHTML = ' Partager';
+ shareBtn.addEventListener("click", async () => {
+ try {
+ const res = await fetch(`/api/share/${encodeURIComponent(vault)}`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ path }),
+ credentials: "include",
+ });
+ if (!res.ok) throw new Error("Erreur");
+ const share = await res.json();
+ const url = window.location.origin + share.url;
+ try { await navigator.clipboard.writeText(url); } catch { /* fallback */ }
+ alert("Lien copié : " + url);
+ } catch (err) { alert("Erreur: " + err.message); }
+ });
+ actionsDiv.appendChild(shareBtn);
+
+ // Bookmark button
+ const bookmarkBtn = document.createElement("button");
+ bookmarkBtn.className = "btn-action";
+ bookmarkBtn.title = "Ajouter/Retirer des bookmarks";
+ bookmarkBtn.innerHTML = ' Bookmark';
+ bookmarkBtn.addEventListener("click", async () => {
+ try {
+ const res = await fetch("/api/bookmarks/toggle", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ vault, path, title: data.title }),
+ credentials: "include",
+ });
+ if (!res.ok) throw new Error("Erreur");
+ const bm = await res.json();
+ bookmarkBtn.classList.toggle("active", bm.bookmarked);
+ } catch (err) { alert("Erreur: " + err.message); }
+ });
+ actionsDiv.appendChild(bookmarkBtn);
header.appendChild(actionsDiv);
area.appendChild(header);