()
| 1232 | } |
| 1233 | |
| 1234 | function render() { |
| 1235 | const list = document.getElementById("catalogList"); |
| 1236 | const strip = document.getElementById("catalogStrip"); |
| 1237 | const catalog = document.getElementById("catalogPanel"); |
| 1238 | const searchInput = document.getElementById("catalogSearch"); |
| 1239 | if (!list) return; |
| 1240 | |
| 1241 | list.innerHTML = ""; |
| 1242 | if (strip) strip.innerHTML = ""; |
| 1243 | |
| 1244 | const trash = getTrashFolder(); |
| 1245 | const trashIds = new Set(trash?.items || []); |
| 1246 | const isTrashView = catalogView === "trash"; |
| 1247 | const isFavoritesView = catalogView === "favorites"; |
| 1248 | const isLibraryView = !isTrashView && !isFavoritesView; |
| 1249 | |
| 1250 | catalog?.classList.toggle("trash-view", isTrashView); |
| 1251 | catalog?.classList.toggle("favorites-view", isFavoritesView); |
| 1252 | |
| 1253 | document.querySelector(".rail-library")?.classList.toggle("active", isLibraryView); |
| 1254 | document.querySelector(".rail-library")?.setAttribute("aria-pressed", String(isLibraryView)); |
| 1255 | document.querySelector(".rail-favorites")?.classList.toggle("active", isFavoritesView); |
| 1256 | document.querySelector(".rail-favorites")?.setAttribute("aria-pressed", String(isFavoritesView)); |
| 1257 | document.querySelector(".rail-trash")?.classList.toggle("active", isTrashView); |
| 1258 | document.querySelector(".rail-trash")?.setAttribute("aria-pressed", String(isTrashView)); |
| 1259 | |
| 1260 | if (searchInput) { |
| 1261 | searchInput.placeholder = isTrashView ? "Search trash…" : isFavoritesView ? "Search favorites…" : "Search library…"; |
| 1262 | } |
| 1263 | |
| 1264 | const nonTrash = folders.filter((f) => f.id !== TRASH_ID && !f.parentId); |
| 1265 | |
| 1266 | // ── Trash view ── |
| 1267 | if (isTrashView) { |
| 1268 | const visibleTrashItems = (trash?.items || []).filter((id) => trackMatchesSearch(tracks[id])); |
| 1269 | if (!trash?.items.length) { |
| 1270 | list.innerHTML = '<span class="folder-empty trash-empty">Trash is empty</span>'; |
| 1271 | } else if (visibleTrashItems.length === 0) { |
| 1272 | list.innerHTML = '<span class="folder-empty trash-empty">No deleted tracks match your search</span>'; |
| 1273 | } else { |
| 1274 | for (const id of visibleTrashItems) { |
| 1275 | const item = renderTrackItem(id, { inTrash: true }); |
| 1276 | if (item) list.appendChild(item); |
| 1277 | } |
| 1278 | } |
| 1279 | return; |
| 1280 | } |
| 1281 | |
| 1282 | // ── Favorites view ── |
| 1283 | if (isFavoritesView) { |
| 1284 | const favIds = Object.entries(tracks) |
| 1285 | .filter(([id, t]) => !trashIds.has(id) && t.favorite && trackMatchesSearch(t)) |
| 1286 | .sort(([, a], [, b]) => (b.createdAt ?? 0) - (a.createdAt ?? 0)) |
| 1287 | .map(([id]) => id); |
| 1288 | if (!favIds.length) { |
| 1289 | list.innerHTML = `<span class="folder-empty trash-empty">${catalogSearchQuery ? "No favorites match your search" : "No favorites yet — click ♥ on a track to save it"}</span>`; |
| 1290 | } else { |
| 1291 | for (const id of favIds) { |
no test coverage detected