Files
org_roam/assets/scripts/sidebar.js
Zaine a1da976cc6
All checks were successful
Build Roam Site / build (push) Successful in 29s
perms fix
2026-05-06 15:52:11 +01:00

169 lines
5.3 KiB
JavaScript
Executable File

/* =========================================================
SIDEBAR — reads /assets/sidebar-tree.json
Falls back to search-index.json if tree unavailable.
sidebar-tree.json shape:
{
"groups": [
{ "label": "java", "files": [{ "title": "...", "url": "..." }, ...] },
...
{ "label": "uncategorised", "files": [...] }
]
}
========================================================= */
async function buildSidebar() {
const sidebar = document.getElementById("sidebar");
if (!sidebar) return;
const currentPath = location.pathname;
// --- Files label (kept from original) ---
const filesLabel = el("div", { class: "sidebar-section-label" });
filesLabel.textContent = "files";
sidebar.appendChild(filesLabel);
const tree = el("div", { id: "file-tree" });
sidebar.appendChild(tree);
// --- Try sidebar-tree.json first ---
let groups = null;
try {
const res = await fetch("/assets/sidebar-tree.json");
if (res.ok) {
const data = await res.json();
if (Array.isArray(data.groups) && data.groups.length) {
groups = data.groups;
}
}
} catch (_) { /* fall through */ }
// --- Fallback: build a single flat group from search-index.json ---
if (!groups) {
groups = await buildGroupsFromSearchIndex();
}
if (!groups || !groups.length) {
const empty = el("div", { class: "tree-folder" });
empty.style.cssText = "opacity:.4;font-size:.7rem;padding:8px 14px;";
empty.textContent = "no notes found";
tree.appendChild(empty);
return;
}
// --- Render each group as a collapsible folder ---
groups.forEach(group => {
const { label, files } = group;
if (!files || !files.length) return;
// Folder row
const folderEl = el("div", {
class: "tree-folder",
"data-folder": label,
});
const icon = el("span", { class: "tree-icon" });
icon.textContent = "▾";
const labelSpan = el("span", { class: "tree-folder-label" });
labelSpan.textContent = label;
const countBadge = el("span", { class: "tree-folder-count" });
countBadge.textContent = files.length;
folderEl.append(icon, labelSpan, countBadge);
// Children container
const children = el("div", { class: "tree-children" });
files.forEach(f => {
const pathname = normalisePathname(f.url);
children.appendChild(makeFileItem({ title: f.title, pathname }, currentPath));
});
// Collapse / expand
folderEl.addEventListener("click", () => {
const isOpen = children.style.display !== "none";
children.style.display = isOpen ? "none" : "";
icon.textContent = isOpen ? "▸" : "▾";
folderEl.classList.toggle("collapsed", isOpen);
});
tree.appendChild(folderEl);
tree.appendChild(children);
});
// --- Pinned section (unchanged from original) ---
try {
const res = await fetch("/assets/search-index.json");
if (res.ok) {
const raw = await res.json();
const all = Array.isArray(raw) ? raw : (raw.entries || raw.notes || []);
const pinned = all
.filter(e => /\/(inbox|daily[-_]?log|journal|home|index|moc)\.html$/i
.test(e.url || e.path || e.pathname || ""))
.map(e => ({
title: e.title || filenameToTitle(e.url || e.path || e.pathname),
pathname: normalisePathname(e.url || e.path || e.pathname),
}));
if (pinned.length) {
const divider = el("div", { class: "sidebar-divider" });
const pinnedLabel = el("div", { class: "sidebar-section-label" });
pinnedLabel.textContent = "pinned";
const pinnedWrap = el("div", { id: "sidebar-pinned" });
pinned.forEach(f => pinnedWrap.appendChild(makeFileItem(f, currentPath)));
sidebar.append(divider, pinnedLabel, pinnedWrap);
}
}
} catch (_) { /* pinned section is optional */ }
}
/* Fallback: turns search-index.json into a single "all notes" group */
async function buildGroupsFromSearchIndex() {
try {
const res = await fetch("/assets/search-index.json");
if (!res.ok) return null;
const raw = await res.json();
const all = Array.isArray(raw) ? raw : (raw.entries || raw.notes || []);
const files = all
.filter(e => e && (e.url || e.path || e.pathname))
.map(e => ({
title: e.title || filenameToTitle(e.url || e.path || e.pathname),
url: e.url || e.path || e.pathname,
}))
.sort((a, b) => a.title.localeCompare(b.title));
return files.length ? [{ label: "all notes", files }] : null;
} catch (_) {
return null;
}
}
/* =========================================================
CSS to paste into style.css (tree-folder-count badge
+ collapsed state)
=========================================================
Add this block anywhere after the existing .tree-folder rules:
.tree-folder-count {
margin-left: auto;
font-size: 0.65rem;
font-family: var(--font-mono);
color: var(--fg-faint);
background: var(--chip-bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 0 5px;
flex-shrink: 0;
line-height: 1.6;
}
.tree-folder.collapsed {
opacity: 0.75;
}
.tree-folder-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
}
========================================================= */