(node, parentEl)
| 294 | // --- Sidebar tree --------------------------------------------- |
| 295 | |
| 296 | function renderFolderNode(node, parentEl) { |
| 297 | const entries = Object.entries(node).filter(([k]) => k !== "__files"); |
| 298 | const files = node.__files || []; |
| 299 | |
| 300 | for (const [folderName, folderNode] of entries) { |
| 301 | const folderDiv = document.createElement("div"); |
| 302 | folderDiv.className = "folder"; |
| 303 | |
| 304 | const nameEl = document.createElement("div"); |
| 305 | nameEl.className = "folder-name"; |
| 306 | nameEl.textContent = folderName; |
| 307 | folderDiv.appendChild(nameEl); |
| 308 | |
| 309 | const childrenEl = document.createElement("div"); |
| 310 | childrenEl.className = "folder-children"; |
| 311 | folderDiv.appendChild(childrenEl); |
| 312 | |
| 313 | // Start collapsed |
| 314 | childrenEl.style.display = "none"; |
| 315 | |
| 316 | nameEl.addEventListener("click", () => { |
| 317 | const isHidden = childrenEl.style.display === "none"; |
| 318 | childrenEl.style.display = isHidden ? "block" : "none"; |
| 319 | nameEl.classList.toggle("open", isHidden); |
| 320 | }); |
| 321 | |
| 322 | renderFolderNode(folderNode.__children, childrenEl); |
| 323 | parentEl.appendChild(folderDiv); |
| 324 | } |
| 325 | |
| 326 | for (const note of files) { |
| 327 | const link = document.createElement("div"); |
| 328 | link.className = "note-link"; |
| 329 | link.textContent = note.title; |
| 330 | link.dataset.noteId = note.id; |
| 331 | link.addEventListener("click", () => { |
| 332 | loadNote(note); |
| 333 | // Auto-clear search results when a note is clicked from tree |
| 334 | searchResultsEl.innerHTML = ""; |
| 335 | searchInput.value = ""; |
| 336 | }); |
| 337 | parentEl.appendChild(link); |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | function renderTree() { |
| 342 | treeEl.innerHTML = ""; |
no test coverage detected