feat(sidebar): multi-level playlist folders with tree and navigation views (#2017)

Group playlists into folders using a configurable separator (default '/').
Three view modes:
- Single: first-level grouping only (original behavior)
- Tree: full recursive nesting with connecting lines (configurable indent and line color)
- Navigation: drill-down view with stacked breadcrumb chain

Folders are sorted before playlists at every level. New settings render as
indented sub-options under the master 'Enable folders' toggle.
This commit is contained in:
Shawn
2026-05-13 20:07:45 -04:00
committed by GitHub
parent ffe59b2c78
commit 27a62a2a02
10 changed files with 1005 additions and 29 deletions
+27
View File
@@ -175,6 +175,8 @@ const SideQueueLayoutSchema = z.enum(['horizontal', 'vertical']);
const SidebarPanelTypeSchema = z.enum(['queue', 'lyrics', 'visualizer']);
const SidebarPlaylistFolderViewSchema = z.enum(['single', 'tree', 'navigation']);
const CollectionSchema = z.object({
filterQueryString: z.string(),
id: z.string(),
@@ -500,6 +502,11 @@ export const GeneralSettingsSchema = z.object({
sidebarCollapseShared: z.boolean(),
sidebarItems: z.array(SidebarItemTypeSchema),
sidebarPanelOrder: z.array(SidebarPanelTypeSchema),
sidebarPlaylistFolders: z.boolean(),
sidebarPlaylistFolderSeparator: z.string().min(1),
sidebarPlaylistFolderTreeIndent: z.number().int().min(0).max(64),
sidebarPlaylistFolderTreeLineColor: z.string(),
sidebarPlaylistFolderView: SidebarPlaylistFolderViewSchema,
sidebarPlaylistList: z.boolean(),
sidebarPlaylistListFilterRegex: z.string(),
sidebarPlaylistSorting: z.boolean(),
@@ -1169,6 +1176,11 @@ const initialState: SettingsState = {
sidebarCollapseShared: false,
sidebarItems,
sidebarPanelOrder: ['queue', 'lyrics', 'visualizer'],
sidebarPlaylistFolders: true,
sidebarPlaylistFolderSeparator: '/',
sidebarPlaylistFolderTreeIndent: 16,
sidebarPlaylistFolderTreeLineColor: '',
sidebarPlaylistFolderView: 'single',
sidebarPlaylistList: true,
sidebarPlaylistListFilterRegex: '',
sidebarPlaylistSorting: false,
@@ -2552,6 +2564,21 @@ export const useCollections = () => {
);
};
export const useSidebarPlaylistFolders = () =>
useSettingsStore((state) => state.general.sidebarPlaylistFolders, shallow);
export const useSidebarPlaylistFolderSeparator = () =>
useSettingsStore((state) => state.general.sidebarPlaylistFolderSeparator, shallow);
export const useSidebarPlaylistFolderView = () =>
useSettingsStore((state) => state.general.sidebarPlaylistFolderView, shallow);
export const useSidebarPlaylistFolderTreeIndent = () =>
useSettingsStore((state) => state.general.sidebarPlaylistFolderTreeIndent, shallow);
export const useSidebarPlaylistFolderTreeLineColor = () =>
useSettingsStore((state) => state.general.sidebarPlaylistFolderTreeLineColor, shallow);
export const useSidebarPlaylistList = () =>
useSettingsStore((state) => state.general.sidebarPlaylistList, shallow);