1159c0787d5fce5d239d61a1d5702df217fb8514
slskd's browse response is flat: every folder is a full backslash-delimited path. Rendered as-is, a filter for "pogues" gave 26 rows that all began with the same 31 characters, and the real hierarchy — which is the only way to tell an artist folder from an album folder — was invisible. The shape is now derived once, at ingest, in the sidecar: buildTree() links each path to its parent, synthesizes any ancestor slskd omitted (measured: exactly one missing across ~30k folders on two real peers, but a single gap would strand a whole subtree), and rolls subtree file counts and sizes up bottom-up. A parent's own files are usually just cover art, so the number worth showing on a collapsed row is the subtree's. Storing the shape rather than recomputing it is what lets the UI open one level at a time. Levels are still paged, because fan-out is brutal — the widest folder measured has 1,181 children. Filtering keeps the tree instead of falling back to a list: the search route returns matches plus every ancestor, and the UI renders that skeleton pre-expanded, so you see where a hit lives. Matching runs against the whole path, so a matched folder implies its descendants match too and a matched subtree arrives complete. The match cap is reported in the payload and shown in the UI rather than passed off as the whole answer. The two existing snapshots were backfilled by scripts/rebuild-soulseek-tree.ts, which runs the same buildTree + finishSoulseekBrowse the ingest path runs — no second implementation to drift, and no peer contact needed. Kept for the next time the tree shape changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Description
No description provided
42 MiB
Languages
TypeScript
90.9%
Shell
4.7%
JavaScript
4.1%
CSS
0.2%
HTML
0.1%