Add dark theme support and bookmark status to share view

- Implement dark/light theme toggle with persistent preference via
  localStorage
- Add a sticky toolbar with theme toggle, Markdown export, and PDF
  export buttons
- Update bookmark button to reflect current state with visual feedback
- Introduce CSS custom properties for theming and responsive layout
  improvements
This commit is contained in:
2026-05-26 20:19:58 -04:00
parent d4896a5df1
commit 9752b18529
3 changed files with 64 additions and 7 deletions
+41 -5
View File
@@ -2812,12 +2812,48 @@ async def public_share_view(token: str):
post = parse_markdown_file(raw)
html = _render_markdown(post.content, share["vault"], file_path)
title = post.metadata.get("title", file_path.stem)
return HTMLResponse(f"""<!DOCTYPE html><html lang="fr"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
return HTMLResponse(f"""<!DOCTYPE html><html lang="fr" data-theme="dark"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>{title} — ObsiGate Share</title>
<style>body{{font-family:system-ui,sans-serif;max-width:800px;margin:0 auto;padding:20px;line-height:1.6;color:#1a1a2e;background:#f8f9fa}}
pre{{background:#eee;padding:12px;border-radius:6px;overflow-x:auto}}code{{font-size:0.9em}}a{{color:#6366f1}}.wikilink{{color:#6366f1;cursor:default}}
img{{max-width:100%}}.share-banner{{background:#6366f1;color:#fff;padding:8px 16px;border-radius:6px;margin-bottom:20px;font-size:0.85rem}}</style></head>
<body><div class="share-banner">📄 Document partagé via ObsiGate</div><h1>{title}</h1>{html}</body></html>""")
<style>
:root {{ --bg:#1a1a2e; --bg-card:#16213e; --text:#e0e0e0; --text-muted:#888; --accent:#6366f1; --border:#2a2a4a; }}
[data-theme="light"] {{ --bg:#f8f9fa; --bg-card:#fff; --text:#1a1a2e; --text-muted:#666; --accent:#4f46e5; --border:#ddd; }}
*{{box-sizing:border-box;margin:0;padding:0}}
body{{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.7;min-height:100vh}}
.toolbar{{position:sticky;top:0;z-index:10;background:var(--bg-card);border-bottom:1px solid var(--border);padding:8px 16px;display:flex;align-items:center;gap:8px;flex-wrap:wrap}}
.toolbar-title{{font-weight:600;font-size:0.9rem;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}
.toolbar-btn{{padding:6px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);cursor:pointer;font-size:0.8rem;display:flex;align-items:center;gap:4px;transition:all .15s}}
.toolbar-btn:hover{{background:var(--accent);color:#fff;border-color:var(--accent)}}
.share-banner{{background:var(--accent);color:#fff;padding:6px 16px;font-size:0.8rem;text-align:center}}
.content{{max-width:820px;margin:0 auto;padding:24px 20px 60px}}
.content h1{{font-size:1.8rem;margin-bottom:16px;border-bottom:2px solid var(--border);padding-bottom:8px}}
.content h2{{font-size:1.4rem;margin:24px 0 12px}}
.content h3{{font-size:1.15rem;margin:20px 0 8px}}
.content p{{margin:8px 0}}
.content pre{{background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:12px 16px;overflow-x:auto;font-size:0.85rem}}
.content code{{font-size:0.9em;background:var(--bg-card);padding:1px 4px;border-radius:3px}}
.content pre code{{background:none;padding:0}}
.content a{{color:var(--accent)}}.content img{{max-width:100%;border-radius:6px}}
.content blockquote{{border-left:3px solid var(--accent);padding-left:16px;color:var(--text-muted);margin:12px 0}}
.content table{{border-collapse:collapse;width:100%;margin:12px 0}}
.content th,.content td{{border:1px solid var(--border);padding:8px 12px;text-align:left}}
.content th{{background:var(--bg-card)}}
@media(max-width:600px){{.content{{padding:16px 12px 40px}}.toolbar{{gap:4px}}.toolbar-btn{{padding:4px 8px;font-size:0.7rem}}}}
</style></head>
<body>
<div class="share-banner">📄 Document partagé via ObsiGate</div>
<div class="toolbar">
<span class="toolbar-title">{title}</span>
<button class="toolbar-btn" onclick="toggleTheme()" title="Thème clair/sombre">🌓</button>
<button class="toolbar-btn" onclick="exportMD()" title="Exporter en Markdown">📥 .md</button>
<button class="toolbar-btn" onclick="exportPDF()" title="Exporter en PDF">📄 PDF</button>
</div>
<div class="content" id="content">{html}</div>
<script>
function toggleTheme(){{var t=document.documentElement;t.dataset.theme=t.dataset.theme==="light"?"dark":"light";localStorage.setItem("obsigate-share-theme",t.dataset.theme)}}
(function(){{var s=localStorage.getItem("obsigate-share-theme");if(s)document.documentElement.dataset.theme=s}})();
function exportMD(){{var t=document.getElementById("content").innerText;var b=new Blob([t],{{type:"text/markdown"}});var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download="{title}.md";a.click()}}
function exportPDF(){{window.print()}}
</script></body></html>""")
# ---------------------------------------------------------------------------
+14 -2
View File
@@ -3283,11 +3283,23 @@
})();
shareBtn.addEventListener("click", () => openShareDialog(data.vault, data.path));
// Bookmark button
const bookmarkBtn = el("button", { class: "btn-action", title: "Ajouter/Retirer des bookmarks" }, [icon("bookmark-plus", 14), document.createTextNode("Bookmark")]);
// Bookmark button — check if already bookmarked
const bookmarkBtn = el("button", { class: "btn-action btn-bookmark", title: "Ajouter/Retirer des bookmarks" }, [icon("bookmark-plus", 14), document.createTextNode("Bookmark")]);
// Check bookmark status and color the button
(async () => {
try {
const bms = await api("/api/bookmarks");
if (Array.isArray(bms) && bms.some(b => b.vault === data.vault && b.path === data.path)) {
bookmarkBtn.classList.add("active");
bookmarkBtn.title = "Retirer des bookmarks";
}
} catch (e) { /* ignore */ }
})();
bookmarkBtn.addEventListener("click", async () => {
try {
const res = await api("/api/bookmarks/toggle", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ vault: data.vault, path: data.path, title: data.title }) });
bookmarkBtn.classList.toggle("active", res.bookmarked);
bookmarkBtn.title = res.bookmarked ? "Retirer des bookmarks" : "Ajouter aux bookmarks";
showToast(res.bookmarked ? "Ajouté aux bookmarks" : "Retiré des bookmarks", "success");
if (typeof DashboardBookmarkWidget !== "undefined") DashboardBookmarkWidget.load();
} catch (err) { showToast("Erreur: " + err.message, "error"); }
+9
View File
@@ -5830,6 +5830,15 @@ body.popup-mode .content-area {
.search-replace-btn.destructive { border-color: var(--text-error); color: var(--text-error); }
/* ── Active search result highlight ── */
.btn-bookmark.active {
color: #059669;
border-color: #059669;
background: rgba(5, 150, 105, 0.08);
}
.btn-bookmark.active:hover {
background: rgba(5, 150, 105, 0.15);
}
.btn-share.shared {
color: #059669;
border-color: #059669;