feat(12-02): replace config-page library section with full library management UI

- Add library list with name, path, track count per library
- Add Library button opens folder picker, auto-creates library
- Inline rename with Enter/Escape via overflow menu
- Removal confirmation dialog with real impact counts (tracks, playlists, queue)
- Toast notification with removal summary after library removal
- Add GetAllLibrariesWithTrackCounts + Info type to backend Library struct
- Add Wails binding stubs for AddLibrary, RenameLibrary, RemoveLibrary, GetRemovalImpact
- Add Info, RemovalImpact, RemovalSummary types to models.ts
- Remove old single-directory library config UI (GetLibraryDirectory/SetLibraryDirectory)
This commit is contained in:
2026-03-12 19:50:59 -04:00
parent 27c773b3d0
commit ffc5d9639c
5 changed files with 575 additions and 59 deletions
+10
View File
@@ -3,6 +3,8 @@
import {library} from '../models';
import {context} from '../models';
export function AddLibrary(arg1:string):Promise<library.Info>;
export function CancelAllScans():Promise<void>;
export function CancelCurrentScan():Promise<void>;
@@ -13,6 +15,8 @@ export function FullRescan():Promise<library.ScanMetrics>;
export function GetAlbumTracks(arg1:number):Promise<Array<library.Track>>;
export function GetAllLibrariesWithTrackCounts():Promise<Array<library.Info>>;
export function GetAlbumsByArtist(arg1:number):Promise<Array<library.Album>>;
export function GetAllAlbums():Promise<Array<library.Album>>;
@@ -23,6 +27,8 @@ export function GetAllGenresWithCounts():Promise<Array<library.GenreWithCount>>;
export function GetAllTracks():Promise<Array<library.Track>>;
export function GetRemovalImpact(arg1:number):Promise<library.RemovalImpact>;
export function GetScanQueueLength():Promise<number>;
export function GetTracksByGenre(arg1:string):Promise<Array<library.Track>>;
@@ -33,6 +39,10 @@ export function IsScanPaused():Promise<boolean>;
export function PauseScan():Promise<void>;
export function RemoveLibrary(arg1:number):Promise<library.RemovalSummary>;
export function RenameLibrary(arg1:number, arg2:string):Promise<void>;
export function QueuedLibraryNames():Promise<Array<string>>;
export function ResumeScan():Promise<void>;
+20
View File
@@ -2,6 +2,10 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function AddLibrary(arg1) {
return window['go']['library']['Library']['AddLibrary'](arg1);
}
export function CancelAllScans() {
return window['go']['library']['Library']['CancelAllScans']();
}
@@ -22,6 +26,10 @@ export function GetAlbumTracks(arg1) {
return window['go']['library']['Library']['GetAlbumTracks'](arg1);
}
export function GetAllLibrariesWithTrackCounts() {
return window['go']['library']['Library']['GetAllLibrariesWithTrackCounts']();
}
export function GetAlbumsByArtist(arg1) {
return window['go']['library']['Library']['GetAlbumsByArtist'](arg1);
}
@@ -42,6 +50,10 @@ export function GetAllTracks() {
return window['go']['library']['Library']['GetAllTracks']();
}
export function GetRemovalImpact(arg1) {
return window['go']['library']['Library']['GetRemovalImpact'](arg1);
}
export function GetScanQueueLength() {
return window['go']['library']['Library']['GetScanQueueLength']();
}
@@ -62,6 +74,14 @@ export function PauseScan() {
return window['go']['library']['Library']['PauseScan']();
}
export function RemoveLibrary(arg1) {
return window['go']['library']['Library']['RemoveLibrary'](arg1);
}
export function RenameLibrary(arg1, arg2) {
return window['go']['library']['Library']['RenameLibrary'](arg1, arg2);
}
export function QueuedLibraryNames() {
return window['go']['library']['Library']['QueuedLibraryNames']();
}
+56
View File
@@ -40,6 +40,62 @@ export namespace library {
this.Name = source["Name"];
}
}
export class Info {
id: number;
name: string;
path: string;
trackCount: number;
static createFrom(source: any = {}) {
return new Info(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
this.path = source["path"];
this.trackCount = source["trackCount"];
}
}
export class RemovalImpact {
trackCount: number;
playlistsAffected: number;
queueItemCount: number;
static createFrom(source: any = {}) {
return new RemovalImpact(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.trackCount = source["trackCount"];
this.playlistsAffected = source["playlistsAffected"];
this.queueItemCount = source["queueItemCount"];
}
}
export class RemovalSummary {
tracksDeleted: number;
artistsRemoved: number;
albumsRemoved: number;
genresRemoved: number;
playlistsAffected: number;
queueItemsRemoved: number;
static createFrom(source: any = {}) {
return new RemovalSummary(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.tracksDeleted = source["tracksDeleted"];
this.artistsRemoved = source["artistsRemoved"];
this.albumsRemoved = source["albumsRemoved"];
this.genresRemoved = source["genresRemoved"];
this.playlistsAffected = source["playlistsAffected"];
this.queueItemsRemoved = source["queueItemsRemoved"];
}
}
export class GenreWithCount {
Name: string;
TrackCount: number;