diff --git a/backend/database/sql/sqlcgen/playlists.sql.go b/backend/database/sql/sqlcgen/playlists.sql.go index 45f5d34..0e1380c 100644 --- a/backend/database/sql/sqlcgen/playlists.sql.go +++ b/backend/database/sql/sqlcgen/playlists.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: playlists.sql package sqlcgen diff --git a/backend/database/sql/sqlcgen/queue.sql.go b/backend/database/sql/sqlcgen/queue.sql.go index ff4bb0e..e718b34 100644 --- a/backend/database/sql/sqlcgen/queue.sql.go +++ b/backend/database/sql/sqlcgen/queue.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: queue.sql package sqlcgen diff --git a/backend/queue/queue.go b/backend/queue/queue.go index 0ca5af8..58e996a 100644 --- a/backend/queue/queue.go +++ b/backend/queue/queue.go @@ -38,8 +38,8 @@ type TrackLoader interface { CurrentPositionSeconds() (int, error) } -// Track represents a track in the queue with its metadata. -type Track struct { +// QueueTrack represents a track in the queue with its metadata. +type QueueTrack struct { ID int64 `json:"id"` AudioFileID int64 `json:"audioFileId"` FilePath string `json:"filePath"` @@ -48,13 +48,13 @@ type Track struct { Artist string `json:"artist"` } -// State is the full state emitted to the frontend. -type State struct { - Tracks []Track `json:"tracks"` - CurrentIndex int `json:"currentIndex"` - ShuffleMode bool `json:"shuffleMode"` - RepeatMode RepeatMode `json:"repeatMode"` - SourcePlaylistID int64 `json:"sourcePlaylistId"` +// QueueState is the full state emitted to the frontend. +type QueueState struct { + Tracks []QueueTrack `json:"tracks"` + CurrentIndex int `json:"currentIndex"` + ShuffleMode bool `json:"shuffleMode"` + RepeatMode RepeatMode `json:"repeatMode"` + SourcePlaylistID int64 `json:"sourcePlaylistId"` } // Queue manages an ordered list of tracks for playback. @@ -65,7 +65,7 @@ type Queue struct { player TrackLoader mu sync.Mutex - tracks []Track + tracks []QueueTrack currentIndex int shuffleMode bool repeatMode RepeatMode @@ -331,7 +331,7 @@ func (q *Queue) SetQueue(filePaths []string, startIndex int) { defer q.mu.Unlock() // Look up audio file IDs and metadata for all paths. - tracks := make([]Track, 0, len(filePaths)) + tracks := make([]QueueTrack, 0, len(filePaths)) for i, fp := range filePaths { af, err := q.db.Queries.GetAudioFileByPath(q.db.Ctx, fp) @@ -341,7 +341,7 @@ func (q *Queue) SetQueue(filePaths []string, startIndex int) { continue } - track := Track{ + track := QueueTrack{ AudioFileID: af.ID, FilePath: fp, Position: int64(i), @@ -395,7 +395,7 @@ func (q *Queue) AddTrack(filePath string) { wasEmpty := len(q.tracks) == 0 - track := Track{ + track := QueueTrack{ AudioFileID: af.ID, FilePath: filePath, Position: int64(len(q.tracks)), @@ -449,7 +449,7 @@ func (q *Queue) AddTracks(filePaths []string) { continue } - track := Track{ + track := QueueTrack{ AudioFileID: af.ID, FilePath: fp, Position: int64(len(q.tracks)), @@ -490,8 +490,7 @@ func (q *Queue) InsertNextTracks(filePaths []string) { } wasEmpty := len(q.tracks) == 0 - - var newTracks []Track + var newTracks []QueueTrack for _, fp := range filePaths { af, err := q.db.Queries.GetAudioFileByPath(q.db.Ctx, fp) @@ -501,7 +500,7 @@ func (q *Queue) InsertNextTracks(filePaths []string) { continue } - track := Track{ + track := QueueTrack{ AudioFileID: af.ID, FilePath: fp, } @@ -520,7 +519,7 @@ func (q *Queue) InsertNextTracks(filePaths []string) { } // Insert the block into the slice at insertPos. - tail := make([]Track, len(q.tracks[insertPos:])) + tail := make([]QueueTrack, len(q.tracks[insertPos:])) copy(tail, q.tracks[insertPos:]) q.tracks = append(q.tracks[:insertPos], newTracks...) q.tracks = append(q.tracks, tail...) @@ -559,7 +558,7 @@ func (q *Queue) InsertNext(filePath string) { insertPos = len(q.tracks) } - track := Track{ + track := QueueTrack{ AudioFileID: af.ID, FilePath: filePath, Position: int64(insertPos), @@ -573,7 +572,7 @@ func (q *Queue) InsertNext(filePath string) { } // Insert into slice. - q.tracks = append(q.tracks, Track{}) + q.tracks = append(q.tracks, QueueTrack{}) copy(q.tracks[insertPos+1:], q.tracks[insertPos:]) q.tracks[insertPos] = track @@ -711,14 +710,14 @@ func (q *Queue) CycleRepeat() { } // GetState returns the current queue state for the frontend. -func (q *Queue) GetState() State { +func (q *Queue) GetState() QueueState { q.mu.Lock() defer q.mu.Unlock() - tracks := make([]Track, len(q.tracks)) + tracks := make([]QueueTrack, len(q.tracks)) copy(tracks, q.tracks) - return State{ + return QueueState{ Tracks: tracks, CurrentIndex: q.currentIndex, ShuffleMode: q.shuffleMode, @@ -791,10 +790,10 @@ func (q *Queue) RestoreState() { return } - q.tracks = make([]Track, 0, len(rows)) + q.tracks = make([]QueueTrack, 0, len(rows)) for _, row := range rows { - q.tracks = append(q.tracks, Track{ + q.tracks = append(q.tracks, QueueTrack{ ID: row.ID, AudioFileID: row.AudioFileID, FilePath: row.FilePath, @@ -955,25 +954,13 @@ func (q *Queue) playCurrentTrack() { } if q.currentIndex < 0 || q.currentIndex >= len(q.tracks) { - q.logger.Warn( - "Current index out of range", - "index", - q.currentIndex, - "trackCount", - len(q.tracks), - ) + q.logger.Warn("Current index out of range", "index", q.currentIndex, "trackCount", len(q.tracks)) return } track := q.tracks[q.currentIndex] - q.logger.Info( - "Playing track from queue", - "filePath", - track.FilePath, - "position", - q.currentIndex, - ) + q.logger.Info("Playing track from queue", "filePath", track.FilePath, "position", q.currentIndex) err := q.player.LoadFile(track.FilePath) if err != nil { @@ -993,8 +980,8 @@ func (q *Queue) playCurrentTrack() { // onQueueExhausted is called when there are no more tracks to play. // This is the extension point for a future fallback playlist feature. func (q *Queue) onQueueExhausted() { - // Future: load fallback playlist here. q.logger.Info("Queue exhausted, stopping playback") + // Future: load fallback playlist here. } // reindexPositions updates the Position field of all tracks to match slice index. @@ -1060,7 +1047,7 @@ func (q *Queue) emitQueueChanged() { return } - state := State{ + state := QueueState{ Tracks: q.tracks, CurrentIndex: q.currentIndex, ShuffleMode: q.shuffleMode, @@ -1070,7 +1057,7 @@ func (q *Queue) emitQueueChanged() { // Ensure tracks is never nil in JSON. if state.Tracks == nil { - state.Tracks = []Track{} + state.Tracks = []QueueTrack{} } runtime.EventsEmit(q.ctx, events.QueueChanged, state) diff --git a/frontend/index.css b/frontend/index.css index 5105bbb..55e11e1 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -112,5 +112,9 @@ body div.sidebar { grid-area: main-panel; padding: 0.25em; background-color: #212529; - overflow: auto; + overflow: hidden; +} + +.main-panel > * { + height: 100%; } diff --git a/frontend/index.ts b/frontend/index.ts index 472358a..ee38a47 100644 --- a/frontend/index.ts +++ b/frontend/index.ts @@ -10,10 +10,6 @@ import { setBasePath } from '@awesome.me/webawesome/dist/webawesome.js'; setBasePath('/dist/webawesome'); -// Scroll state management per view -const scrollPositions = new Map(); -let currentView = 'tracks'; - // Navigation event listener for view switching document.addEventListener('navigate', (e: Event) => { const { view } = (e as CustomEvent).detail; @@ -21,9 +17,6 @@ document.addEventListener('navigate', (e: Event) => { if (!mainContent) return; - // Save scroll position for current view before switching - scrollPositions.set(currentView, mainContent.scrollTop); - switch (view) { case 'albums': mainContent.innerHTML = ''; @@ -36,12 +29,6 @@ document.addEventListener('navigate', (e: Event) => {

Coming soon: ${view}

`; } - - // Restore scroll position for new view - mainContent.scrollTop = scrollPositions.get(view) ?? 0; - - // Update current view tracker - currentView = view; }); // Queue panel toggle diff --git a/frontend/package.json b/frontend/package.json index 8e91d70..45d8ad2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,6 +7,7 @@ "dependencies": { "@awesome.me/webawesome": "^3.2.1", "@lit-labs/signals": "^0.2.0", + "@lit-labs/virtualizer": "^2.1.1", "htmx.org": "2.0.8", "lit": "^3.2.1" }, diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index a3c53d2..c04dddc 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -0abc2bb78bacb130d41b3ed39483ae9f \ No newline at end of file +02c7eb24a50fc8301be7488868be5860 \ No newline at end of file diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a3d69fa..745f9f3 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,20 +10,23 @@ importers: dependencies: '@awesome.me/webawesome': specifier: ^3.2.1 - version: 3.2.1(@floating-ui/utils@0.2.10)(@types/react@19.2.14) + version: 3.2.1(@floating-ui/utils@0.2.9)(@types/react@19.1.0) '@lit-labs/signals': specifier: ^0.2.0 version: 0.2.0 + '@lit-labs/virtualizer': + specifier: ^2.1.1 + version: 2.1.1 htmx.org: specifier: 2.0.8 version: 2.0.8 lit: specifier: ^3.2.1 - version: 3.3.2 + version: 3.2.1 devDependencies: stylelint-config-standard: specifier: ^40.0.0 - version: 40.0.0(stylelint@17.3.0(typescript@5.2.2)) + version: 40.0.0(stylelint@16.19.1(typescript@5.2.2)) ts-lit-plugin: specifier: ^2.0.2 version: 2.0.2 @@ -43,67 +46,48 @@ packages: resolution: {integrity: sha512-D/cZlPC97Ni0K5IgE+l7UKDBlthRCWIyz42voxpZrl/the+A3dAJtUKG6sr1GDDp6v2JEh4iFsyFDpZH8kuVOQ==} engines: {node: '>=14.17.0'} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} engines: {node: '>=6.9.0'} - '@cacheable/memory@2.0.7': - resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} - - '@cacheable/utils@2.3.4': - resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} - - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} - engines: {node: '>=20.19.0'} + '@csstools/css-parser-algorithms@3.0.4': + resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==} + engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-tokenizer': ^3.0.3 - '@csstools/css-parser-algorithms@4.0.0': - resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} - engines: {node: '>=20.19.0'} + '@csstools/css-tokenizer@3.0.3': + resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@4.0.2': + resolution: {integrity: sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==} + engines: {node: '>=18'} peerDependencies: - '@csstools/css-tokenizer': ^4.0.0 + '@csstools/css-parser-algorithms': ^3.0.4 + '@csstools/css-tokenizer': ^3.0.3 - '@csstools/css-syntax-patches-for-csstree@1.0.27': - resolution: {integrity: sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==} - - '@csstools/css-tokenizer@4.0.0': - resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} - engines: {node: '>=20.19.0'} - - '@csstools/media-query-list-parser@5.0.0': - resolution: {integrity: sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg==} - engines: {node: '>=20.19.0'} + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} peerDependencies: - '@csstools/css-parser-algorithms': ^4.0.0 - '@csstools/css-tokenizer': ^4.0.0 - - '@csstools/selector-resolve-nested@4.0.0': - resolution: {integrity: sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA==} - engines: {node: '>=20.19.0'} - peerDependencies: - postcss-selector-parser: ^7.1.1 - - '@csstools/selector-specificity@6.0.0': - resolution: {integrity: sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA==} - engines: {node: '>=20.19.0'} - peerDependencies: - postcss-selector-parser: ^7.1.1 + postcss-selector-parser: ^7.0.0 '@ctrl/tinycolor@4.1.0': resolution: {integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==} engines: {node: '>=14'} + '@dual-bundle/import-meta-resolve@4.1.0': + resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==} + '@emmetio/extract-abbreviation@0.1.6': resolution: {integrity: sha512-Ce3xE2JvTSEbASFbRbA1gAIcMcZWdS2yUYRaQbeM0nbOzaZrUYfa3ePtcriYRZOZmr+CkKA+zbjhvTpIOAYVcw==} @@ -263,37 +247,34 @@ packages: cpu: [x64] os: [win32] - '@floating-ui/core@1.7.4': - resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} + '@floating-ui/core@1.6.9': + resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} - '@floating-ui/dom@1.7.5': - resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} + '@floating-ui/dom@1.6.13': + resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} - '@floating-ui/utils@0.2.10': - resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@keyv/bigmap@1.3.1': - resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} - engines: {node: '>= 18'} - peerDependencies: - keyv: ^5.6.0 - - '@keyv/serialize@1.1.1': - resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@keyv/serialize@1.0.3': + resolution: {integrity: sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g==} '@lit-labs/signals@0.2.0': resolution: {integrity: sha512-68plyIbciumbwKaiilhLNyhz4Vg6/+nJwDufG2xxWA9r/fUw58jxLHCAlKs+q1CE5Lmh3cZ3ShyYKnOCebEpVA==} - '@lit-labs/ssr-dom-shim@1.5.1': - resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} + '@lit-labs/ssr-dom-shim@1.3.0': + resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} + + '@lit-labs/virtualizer@2.1.1': + resolution: {integrity: sha512-JWxMwnlouLdwpw8spLTuax53WMnSP3xt0dCyxAS7GJr5Otda9MGgR/ghAdfwhSY75TmjbE1T2TqChwoGCw3ggw==} '@lit/react@1.0.8': resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==} peerDependencies: '@types/react': 17 || 18 || 19 - '@lit/reactive-element@2.1.2': - resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} + '@lit/reactive-element@2.0.4': + resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -341,79 +322,66 @@ packages: resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.57.1': resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.57.1': resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.57.1': resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.57.1': resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.57.1': resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.57.1': resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.57.1': resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.57.1': resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.57.1': resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.57.1': resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.57.1': resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.57.1': resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.57.1': resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} @@ -451,15 +419,11 @@ packages: '@shoelace-style/localize@3.2.1': resolution: {integrity: sha512-r4C9C/5kSfMBIr0D9imvpRdCNXtUNgyYThc4YlS6K5Hchv1UyxNQ9mxwj+BTRH2i1Neits260sR3OjKMnplsFA==} - '@sindresorhus/merge-streams@4.0.0': - resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} - engines: {node: '>=18'} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.1.0': + resolution: {integrity: sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==} '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -474,10 +438,6 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -493,13 +453,19 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} - balanced-match@3.0.1: - resolution: {integrity: sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==} - engines: {node: '>= 16'} + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -509,8 +475,11 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - cacheable@2.3.2: - resolution: {integrity: sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + cacheable@1.9.0: + resolution: {integrity: sha512-8D5htMCxPDUULux9gFzv30f04Xo3wCnik0oOxKoRTPIBoqA7HtOcJ87uBhQTs3jCfZZTrUBGsYIZOgE0ZRgMAg==} callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} @@ -558,9 +527,9 @@ packages: typescript: optional: true - css-functions-list@3.3.3: - resolution: {integrity: sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==} - engines: {node: '>=12'} + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} css-tree@3.1.0: resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} @@ -571,11 +540,11 @@ packages: engines: {node: '>=4'} hasBin: true - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -587,6 +556,10 @@ packages: resolution: {integrity: sha512-qTBmfQoXvhKO75D/05C8m+fteQmn4U46FWYiLhXtZQInzitXLWY0EQ/2oKnpAz9g2lQWW8jYcLcT+hPJGT+kig==} engines: {node: '>=10.13'} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -594,8 +567,8 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} @@ -617,15 +590,15 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -636,15 +609,15 @@ packages: picomatch: optional: true - file-entry-cache@11.1.2: - resolution: {integrity: sha512-N2WFfK12gmrK1c1GXOqiAJ1tc5YE+R53zvQ+t5P8S5XhnmKYVB5eZEiLNZKDSmoG8wqqbF9EXYBBW/nef19log==} + file-entry-cache@10.1.0: + resolution: {integrity: sha512-Et/ex6smi3wOOB+n5mek+Grf7P2AxZR5ueqRUvAAn4qkyatXi3cUC1cuQXVkX0VlzBVsN4BkWJFmY/fYiRTdww==} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - flat-cache@6.1.20: - resolution: {integrity: sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==} + flat-cache@6.1.9: + resolution: {integrity: sha512-DUqiKkTlAfhtl7g78IuwqYM+YqvT+as0mY+EVk6mfimy19U79pJCzDZQsnqk3Ou/T6hFXWLGbwbADzD/c8Tydg==} flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} @@ -658,10 +631,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} - engines: {node: '>=18'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -674,9 +643,9 @@ packages: resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} engines: {node: '>=6'} - globby@16.1.0: - resolution: {integrity: sha512-+A4Hq7m7Ze592k9gZRy4gJ27DrXRNnC1vPjxTt1qQxEY8RxagBkBxivkCwg7FxSTG0iLLEMaUx13oOr0R2/qcQ==} - engines: {node: '>=20'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} globjoin@0.1.4: resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} @@ -685,35 +654,35 @@ packages: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - has-flag@5.0.1: - resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} - engines: {node: '>=12'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} - hashery@1.4.0: - resolution: {integrity: sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==} - engines: {node: '>=20'} + hookified@1.9.0: + resolution: {integrity: sha512-2yEEGqphImtKIe1NXWEhu6yD3hlFR4Mxk4Mtp3XEyScpSt4pQ4ymmXA1zzxZpj99QkFK+nN0nzjeb2+RUi/6CQ==} - hookified@1.15.1: - resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} - - html-tags@5.1.0: - resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} - engines: {node: '>=20.10'} + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} htmx.org@2.0.8: resolution: {integrity: sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-meta-resolve@4.2.0: - resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -744,10 +713,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-inside@4.0.0: - resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} - engines: {node: '>=12'} - is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -758,8 +723,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true json-parse-even-better-errors@2.3.1: @@ -771,15 +736,15 @@ packages: jsonc-parser@1.0.3: resolution: {integrity: sha512-hk/69oAeaIzchq/v3lS50PXuzn5O2ynldopMC+SWBql7J2WtdptfB9dy8Y7+Og5rPkTCpn83zTiO8FMcqlXJ/g==} - keyv@5.6.0: - resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + keyv@5.3.3: + resolution: {integrity: sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==} kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - known-css-properties@0.37.0: - resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + known-css-properties@0.36.0: + resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} @@ -792,14 +757,14 @@ packages: resolution: {integrity: sha512-XiAjnwVipNrKav7r3CSEZpWt+mwYxrhPRVC7h8knDmn/HWTzzWJvPe+mwBcL2brn4xhItAMzZhFC8tzzqHKmiQ==} hasBin: true - lit-element@4.2.2: - resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} + lit-element@4.1.1: + resolution: {integrity: sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==} - lit-html@3.3.2: - resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} + lit-html@3.2.1: + resolution: {integrity: sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==} - lit@3.3.2: - resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} + lit@3.2.1: + resolution: {integrity: sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==} lodash.deburr@4.1.0: resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} @@ -807,15 +772,15 @@ packages: lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - mathml-tag-names@4.0.0: - resolution: {integrity: sha512-aa6AU2Pcx0VP/XWnh8IGL0SYSgQHDT6Ucror2j2mXeFAlN3ahaNs8EZtG1YiticMkSLj3Gt6VPFfZogt7G5iFQ==} + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - meow@14.0.0: - resolution: {integrity: sha512-JhC3R1f6dbspVtmF3vKjAWz1EVIvwFrGGPLSdU6rK79xBwHWTuHoLnRX/t1/zHS1Ch1Y2UtIrih7DAHuH9JFJA==} - engines: {node: '>=20'} + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} @@ -857,6 +822,10 @@ packages: parse5@5.1.0: resolution: {integrity: sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -868,27 +837,30 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + postcss-safe-parser@7.0.1: resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} engines: {node: '>=18.0'} peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@7.1.1: - resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - qified@0.6.0: - resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} - engines: {node: '>=20'} - qr-creator@1.0.0: resolution: {integrity: sha512-C0cqfbS1P5hfqN4NhsYsUXePlk9BO+a45bAQ3xLYjBL3bOIFzoVEjs79Fado9u9BPBD3buHi3+vY+C8tHh4qMQ==} @@ -899,6 +871,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -911,6 +886,10 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -930,9 +909,9 @@ packages: signal-polyfill@0.2.2: resolution: {integrity: sha512-p63Y4Er5/eMQ9RHg0M0Y64NlsQKpiu6MDdhBXpyywRuWiPywhJTpKJ1iB5K2hJEbFZ0BnDS7ZkJ+0AfTuL37Rg==} - slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} @@ -946,18 +925,10 @@ packages: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string-width@8.1.1: - resolution: {integrity: sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw==} - engines: {node: '>=20'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - stylelint-config-recommended@18.0.0: resolution: {integrity: sha512-mxgT2XY6YZ3HWWe3Di8umG6aBmWmHTblTgu/f10rqFXnyWxjKWwNdjSWkgkwCtxIKnqjSJzvFmPT5yabVIRxZg==} engines: {node: '>=20.19.0'} @@ -970,22 +941,22 @@ packages: peerDependencies: stylelint: ^17.0.0 - stylelint@17.3.0: - resolution: {integrity: sha512-1POV91lcEMhj6SLVaOeA0KlS9yattS+qq+cyWqP/nYzWco7K5jznpGH1ExngvPlTM9QF1Kjd2bmuzJu9TH2OcA==} - engines: {node: '>=20.19.0'} + stylelint@16.19.1: + resolution: {integrity: sha512-C1SlPZNMKl+d/C867ZdCRthrS+6KuZ3AoGW113RZCOL0M8xOGpgx7G70wq7lFvqvm4dcfdGFVLB/mNaLFChRKw==} + engines: {node: '>=18.12.0'} hasBin: true - supports-color@10.2.2: - resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} - engines: {node: '>=18'} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} - supports-hyperlinks@4.4.0: - resolution: {integrity: sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==} - engines: {node: '>=20'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} svg-tags@1.0.0: resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} @@ -1008,6 +979,9 @@ packages: ts-simple-type@2.0.0-next.0: resolution: {integrity: sha512-A+hLX83gS+yH6DtzNAhzZbPfU+D9D8lHlTSd7GeoMRBjOt3GRylDqLTYbdmjA4biWvq2xSfpqfIDj2l0OA/BVg==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + typescript-lit-html-plugin@0.9.0: resolution: {integrity: sha512-Ux2I1sPpt2akNbRZiBAND9oA8XNE2BuVmDwsb7rZshJ9T8/Na2rICE5Tnuj9dPHdFUATdOGjVEagn1/v8T4gCQ==} @@ -1023,10 +997,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - unicorn-magic@0.4.0: - resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} - engines: {node: '>=20'} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -1122,9 +1092,9 @@ packages: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - write-file-atomic@7.0.0: - resolution: {integrity: sha512-YnlPC6JqnZl6aO4uRc+dx5PHguiR9S6WeoLtpxNT9wIG+BDya7ZNE1q7KOjVgaA73hKhKLpVPgJ5QA9THQ5BRg==} - engines: {node: ^20.17.0 || >=22.9.0} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -1140,71 +1110,52 @@ packages: snapshots: - '@awesome.me/webawesome@3.2.1(@floating-ui/utils@0.2.10)(@types/react@19.2.14)': + '@awesome.me/webawesome@3.2.1(@floating-ui/utils@0.2.9)(@types/react@19.1.0)': dependencies: '@ctrl/tinycolor': 4.1.0 - '@floating-ui/dom': 1.7.5 - '@lit/react': 1.0.8(@types/react@19.2.14) + '@floating-ui/dom': 1.6.13 + '@lit/react': 1.0.8(@types/react@19.1.0) '@shoelace-style/animations': 1.2.0 '@shoelace-style/localize': 3.2.1 - composed-offset-position: 0.0.6(@floating-ui/utils@0.2.10) - lit: 3.3.2 + composed-offset-position: 0.0.6(@floating-ui/utils@0.2.9) + lit: 3.2.1 nanoid: 5.1.6 qr-creator: 1.0.0 transitivePeerDependencies: - '@floating-ui/utils' - '@types/react' - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.27.1': {} - '@babel/runtime@7.28.6': {} - - '@cacheable/memory@2.0.7': + '@babel/runtime@7.27.0': dependencies: - '@cacheable/utils': 2.3.4 - '@keyv/bigmap': 1.3.1(keyv@5.6.0) - hookified: 1.15.1 - keyv: 5.6.0 + regenerator-runtime: 0.14.1 - '@cacheable/utils@2.3.4': + '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)': dependencies: - hashery: 1.4.0 - keyv: 5.6.0 + '@csstools/css-tokenizer': 3.0.3 - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-tokenizer@3.0.3': {} + + '@csstools/media-query-list-parser@4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)': dependencies: - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 - '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': dependencies: - '@csstools/css-tokenizer': 4.0.0 - - '@csstools/css-syntax-patches-for-csstree@1.0.27': {} - - '@csstools/css-tokenizer@4.0.0': {} - - '@csstools/media-query-list-parser@5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': - dependencies: - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-tokenizer': 4.0.0 - - '@csstools/selector-resolve-nested@4.0.0(postcss-selector-parser@7.1.1)': - dependencies: - postcss-selector-parser: 7.1.1 - - '@csstools/selector-specificity@6.0.0(postcss-selector-parser@7.1.1)': - dependencies: - postcss-selector-parser: 7.1.1 + postcss-selector-parser: 7.1.0 '@ctrl/tinycolor@4.1.0': {} + '@dual-bundle/import-meta-resolve@4.1.0': {} + '@emmetio/extract-abbreviation@0.1.6': {} '@esbuild/aix-ppc64@0.27.3': @@ -1285,39 +1236,40 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@floating-ui/core@1.7.4': + '@floating-ui/core@1.6.9': dependencies: - '@floating-ui/utils': 0.2.10 + '@floating-ui/utils': 0.2.9 - '@floating-ui/dom@1.7.5': + '@floating-ui/dom@1.6.13': dependencies: - '@floating-ui/core': 1.7.4 - '@floating-ui/utils': 0.2.10 + '@floating-ui/core': 1.6.9 + '@floating-ui/utils': 0.2.9 - '@floating-ui/utils@0.2.10': {} + '@floating-ui/utils@0.2.9': {} - '@keyv/bigmap@1.3.1(keyv@5.6.0)': + '@keyv/serialize@1.0.3': dependencies: - hashery: 1.4.0 - hookified: 1.15.1 - keyv: 5.6.0 - - '@keyv/serialize@1.1.1': {} + buffer: 6.0.3 '@lit-labs/signals@0.2.0': dependencies: - lit: 3.3.2 + lit: 3.2.1 signal-polyfill: 0.2.2 - '@lit-labs/ssr-dom-shim@1.5.1': {} + '@lit-labs/ssr-dom-shim@1.3.0': {} - '@lit/react@1.0.8(@types/react@19.2.14)': + '@lit-labs/virtualizer@2.1.1': dependencies: - '@types/react': 19.2.14 + lit: 3.2.1 + tslib: 2.8.1 - '@lit/reactive-element@2.1.2': + '@lit/react@1.0.8(@types/react@19.1.0)': dependencies: - '@lit-labs/ssr-dom-shim': 1.5.1 + '@types/react': 19.1.0 + + '@lit/reactive-element@2.0.4': + dependencies: + '@lit-labs/ssr-dom-shim': 1.3.0 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -1329,7 +1281,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 + fastq: 1.19.1 '@rollup/rollup-android-arm-eabi@4.57.1': optional: true @@ -1410,13 +1362,11 @@ snapshots: '@shoelace-style/localize@3.2.1': {} - '@sindresorhus/merge-streams@4.0.0': {} - '@types/estree@1.0.8': {} - '@types/react@19.2.14': + '@types/react@19.1.0': dependencies: - csstype: 3.2.3 + csstype: 3.1.3 '@types/trusted-types@2.0.7': {} @@ -1425,14 +1375,12 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 + fast-uri: 3.0.6 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -1448,9 +1396,13 @@ snapshots: argparse@2.0.1: {} + array-union@2.1.0: {} + astral-regex@2.0.0: {} - balanced-match@3.0.1: {} + balanced-match@2.0.0: {} + + base64-js@1.5.1: {} binary-extensions@2.3.0: {} @@ -1458,13 +1410,15 @@ snapshots: dependencies: fill-range: 7.1.1 - cacheable@2.3.2: + buffer@6.0.3: dependencies: - '@cacheable/memory': 2.0.7 - '@cacheable/utils': 2.3.4 - hookified: 1.15.1 - keyv: 5.6.0 - qified: 0.6.0 + base64-js: 1.5.1 + ieee754: 1.2.1 + + cacheable@1.9.0: + dependencies: + hookified: 1.9.0 + keyv: 5.3.3 callsites@3.1.0: {} @@ -1506,20 +1460,20 @@ snapshots: colord@2.9.3: {} - composed-offset-position@0.0.6(@floating-ui/utils@0.2.10): + composed-offset-position@0.0.6(@floating-ui/utils@0.2.9): dependencies: - '@floating-ui/utils': 0.2.10 + '@floating-ui/utils': 0.2.9 cosmiconfig@9.0.0(typescript@5.2.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.1.0 parse-json: 5.2.0 optionalDependencies: typescript: 5.2.2 - css-functions-list@3.3.3: {} + css-functions-list@3.2.3: {} css-tree@3.1.0: dependencies: @@ -1528,23 +1482,27 @@ snapshots: cssesc@3.0.0: {} - csstype@3.2.3: {} + csstype@3.1.3: {} - debug@4.4.3: + debug@4.4.0: dependencies: ms: 2.1.3 didyoumean2@4.1.0: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.27.0 leven: 3.1.0 lodash.deburr: 4.1.0 + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + emoji-regex@8.0.0: {} env-paths@2.2.1: {} - error-ex@1.3.4: + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -1591,11 +1549,11 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-uri@3.1.0: {} + fast-uri@3.0.6: {} fastest-levenshtein@1.0.16: {} - fastq@1.20.1: + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -1603,19 +1561,19 @@ snapshots: optionalDependencies: picomatch: 4.0.3 - file-entry-cache@11.1.2: + file-entry-cache@10.1.0: dependencies: - flat-cache: 6.1.20 + flat-cache: 6.1.9 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - flat-cache@6.1.20: + flat-cache@6.1.9: dependencies: - cacheable: 2.3.2 + cacheable: 1.9.0 flatted: 3.3.3 - hookified: 1.15.1 + hookified: 1.9.0 flatted@3.3.3: {} @@ -1624,8 +1582,6 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.4.0: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -1640,40 +1596,38 @@ snapshots: kind-of: 6.0.3 which: 1.3.1 - globby@16.1.0: + globby@11.1.0: dependencies: - '@sindresorhus/merge-streams': 4.0.0 + array-union: 2.1.0 + dir-glob: 3.0.1 fast-glob: 3.3.3 - ignore: 7.0.5 - is-path-inside: 4.0.0 - slash: 5.1.0 - unicorn-magic: 0.4.0 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 globjoin@0.1.4: {} has-flag@3.0.0: {} - has-flag@5.0.1: {} + has-flag@4.0.0: {} - hashery@1.4.0: - dependencies: - hookified: 1.15.1 + hookified@1.9.0: {} - hookified@1.15.1: {} - - html-tags@5.1.0: {} + html-tags@3.3.1: {} htmx.org@2.0.8: {} - ignore@7.0.5: {} + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.4: {} import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - import-meta-resolve@4.2.0: {} - imurmurhash@0.1.4: {} ini@1.3.8: {} @@ -1694,15 +1648,13 @@ snapshots: is-number@7.0.0: {} - is-path-inside@4.0.0: {} - is-plain-object@5.0.0: {} isexe@2.0.0: {} js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -1712,13 +1664,13 @@ snapshots: jsonc-parser@1.0.3: {} - keyv@5.6.0: + keyv@5.3.3: dependencies: - '@keyv/serialize': 1.1.1 + '@keyv/serialize': 1.0.3 kind-of@6.0.3: {} - known-css-properties@0.37.0: {} + known-css-properties@0.36.0: {} leven@3.1.0: {} @@ -1736,31 +1688,31 @@ snapshots: vscode-html-languageservice: 3.1.0 web-component-analyzer: 2.0.0 - lit-element@4.2.2: + lit-element@4.1.1: dependencies: - '@lit-labs/ssr-dom-shim': 1.5.1 - '@lit/reactive-element': 2.1.2 - lit-html: 3.3.2 + '@lit-labs/ssr-dom-shim': 1.3.0 + '@lit/reactive-element': 2.0.4 + lit-html: 3.2.1 - lit-html@3.3.2: + lit-html@3.2.1: dependencies: '@types/trusted-types': 2.0.7 - lit@3.3.2: + lit@3.2.1: dependencies: - '@lit/reactive-element': 2.1.2 - lit-element: 4.2.2 - lit-html: 3.3.2 + '@lit/reactive-element': 2.0.4 + lit-element: 4.1.1 + lit-html: 3.2.1 lodash.deburr@4.1.0: {} lodash.truncate@4.4.2: {} - mathml-tag-names@4.0.0: {} + mathml-tag-names@2.1.3: {} mdn-data@2.12.2: {} - meow@14.0.0: {} + meow@13.2.0: {} merge2@1.4.1: {} @@ -1785,39 +1737,45 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 parse5@5.1.0: {} + path-type@4.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.6): - dependencies: - postcss: 8.5.6 + postcss-resolve-nested-selector@0.1.6: {} - postcss-selector-parser@7.1.1: + postcss-safe-parser@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-selector-parser@7.1.0: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.5.6: + postcss@8.5.3: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - qified@0.6.0: + postcss@8.5.6: dependencies: - hookified: 1.15.1 + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 qr-creator@1.0.0: {} @@ -1827,12 +1785,16 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} resolve-from@4.0.0: {} + resolve-from@5.0.0: {} + reusify@1.1.0: {} rollup@4.57.1: @@ -1874,7 +1836,7 @@ snapshots: signal-polyfill@0.2.2: {} - slash@5.1.0: {} + slash@3.0.0: {} slice-ansi@4.0.0: dependencies: @@ -1890,83 +1852,75 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string-width@8.1.1: - dependencies: - get-east-asian-width: 1.4.0 - strip-ansi: 7.1.2 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: + stylelint-config-recommended@18.0.0(stylelint@16.19.1(typescript@5.2.2)): dependencies: - ansi-regex: 6.2.2 + stylelint: 16.19.1(typescript@5.2.2) - stylelint-config-recommended@18.0.0(stylelint@17.3.0(typescript@5.2.2)): + stylelint-config-standard@40.0.0(stylelint@16.19.1(typescript@5.2.2)): dependencies: - stylelint: 17.3.0(typescript@5.2.2) + stylelint: 16.19.1(typescript@5.2.2) + stylelint-config-recommended: 18.0.0(stylelint@16.19.1(typescript@5.2.2)) - stylelint-config-standard@40.0.0(stylelint@17.3.0(typescript@5.2.2)): + stylelint@16.19.1(typescript@5.2.2): dependencies: - stylelint: 17.3.0(typescript@5.2.2) - stylelint-config-recommended: 18.0.0(stylelint@17.3.0(typescript@5.2.2)) - - stylelint@17.3.0(typescript@5.2.2): - dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-syntax-patches-for-csstree': 1.0.27 - '@csstools/css-tokenizer': 4.0.0 - '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) - '@csstools/selector-specificity': 6.0.0(postcss-selector-parser@7.1.1) - balanced-match: 3.0.1 + '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3) + '@csstools/css-tokenizer': 3.0.3 + '@csstools/media-query-list-parser': 4.0.2(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + '@dual-bundle/import-meta-resolve': 4.1.0 + balanced-match: 2.0.0 colord: 2.9.3 cosmiconfig: 9.0.0(typescript@5.2.2) - css-functions-list: 3.3.3 + css-functions-list: 3.2.3 css-tree: 3.1.0 - debug: 4.4.3 + debug: 4.4.0 fast-glob: 3.3.3 fastest-levenshtein: 1.0.16 - file-entry-cache: 11.1.2 + file-entry-cache: 10.1.0 global-modules: 2.0.0 - globby: 16.1.0 + globby: 11.1.0 globjoin: 0.1.4 - html-tags: 5.1.0 - ignore: 7.0.5 - import-meta-resolve: 4.2.0 + html-tags: 3.3.1 + ignore: 7.0.4 imurmurhash: 0.1.4 is-plain-object: 5.0.0 - known-css-properties: 0.37.0 - mathml-tag-names: 4.0.0 - meow: 14.0.0 + known-css-properties: 0.36.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 micromatch: 4.0.8 normalize-path: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.6 - postcss-safe-parser: 7.0.1(postcss@8.5.6) - postcss-selector-parser: 7.1.1 + postcss: 8.5.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.5.3) + postcss-selector-parser: 7.1.0 postcss-value-parser: 4.2.0 - string-width: 8.1.1 - supports-hyperlinks: 4.4.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.2.0 svg-tags: 1.0.0 table: 6.9.0 - write-file-atomic: 7.0.0 + write-file-atomic: 5.0.1 transitivePeerDependencies: - supports-color - typescript - supports-color@10.2.2: {} - supports-color@5.5.0: dependencies: has-flag: 3.0.0 - supports-hyperlinks@4.4.0: + supports-color@7.2.0: dependencies: - has-flag: 5.0.1 - supports-color: 10.2.2 + has-flag: 4.0.0 + + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 svg-tags@1.0.0: {} @@ -1994,6 +1948,8 @@ snapshots: ts-simple-type@2.0.0-next.0: {} + tslib@2.8.1: {} + typescript-lit-html-plugin@0.9.0: dependencies: typescript-styled-plugin: 0.13.0 @@ -2012,8 +1968,6 @@ snapshots: typescript@5.2.2: {} - unicorn-magic@0.4.0: {} - util-deprecate@1.0.2: {} vite-plugin-static-copy@3.2.0(vite@7.3.1): @@ -2095,7 +2049,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - write-file-atomic@7.0.0: + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 signal-exit: 4.1.0 diff --git a/frontend/src/components/cover-grid/cover-grid.ts b/frontend/src/components/cover-grid/cover-grid.ts index b3ac40c..304511c 100644 --- a/frontend/src/components/cover-grid/cover-grid.ts +++ b/frontend/src/components/cover-grid/cover-grid.ts @@ -4,6 +4,8 @@ import { EventsEmit } from '@runtime/runtime'; import { GetAllAlbums, GetAlbumTracks } from '@go/library/Library'; import { library } from '@go/models'; import { QueueController } from '@store/controllers/queue-controller'; +import '@lit-labs/virtualizer'; +import { grid } from '@lit-labs/virtualizer/layouts/grid.js'; import '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; @@ -16,14 +18,14 @@ export class CoverGrid extends LitElement { static override styles = css` :host { - display: block; + display: flex; + flex-direction: column; + overflow: hidden; } - .grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); - gap: 16px; - padding: 16px; + lit-virtualizer { + flex: 1; + overflow-y: auto; } .album-card { @@ -33,6 +35,7 @@ export class CoverGrid extends LitElement { border-radius: 8px; padding: 8px; transition: background-color 0.2s ease; + box-sizing: border-box; } .album-card:hover { @@ -260,6 +263,61 @@ export class CoverGrid extends LitElement { } } + private renderAlbumCard = (album: library.Album): unknown => { + return html` +
this.onAlbumClick(album)} + @keydown=${(e: KeyboardEvent) => this.onAlbumKeydown(e, album)} + @contextmenu=${(e: MouseEvent) => this.onAlbumContextMenu(e, album)} + > +
+ ${album.CoverArtPath + ? html`${album.Name} cover` + : html`
+ ${this.getAlbumInitial(album.Name)} +
`} +
+
+
${album.Name}
+
+ ${album.ArtistName}${album.Year ? ` - ${album.Year}` : ''} +
+
+
+ `; + }; + + private getAlbumInitial(name: string): string { + return name.charAt(0).toUpperCase(); + } + + private onAlbumClick(album: library.Album) { + EventsEmit('AlbumSelected', album); + this.dispatchEvent( + new CustomEvent('album-selected', { + detail: album, + bubbles: true, + composed: true, + }) + ); + } + + private onAlbumKeydown(e: KeyboardEvent, album: library.Album) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + this.onAlbumClick(album); + } + } + override render() { if (this.loading) { return html`
Loading albums...
`; @@ -275,9 +333,16 @@ export class CoverGrid extends LitElement { } return html` -
- ${this.albums.map(album => this.renderAlbumCard(album))} -
+ `; } - - private renderAlbumCard(album: library.Album) { - return html` -
this.onAlbumClick(album)} - @keydown=${(e: KeyboardEvent) => this.onAlbumKeydown(e, album)} - @contextmenu=${(e: MouseEvent) => this.onAlbumContextMenu(e, album)} - > -
- ${album.CoverArtPath - ? html`${album.Name} cover` - : html`
- ${this.getAlbumInitial(album.Name)} -
`} -
-
-
${album.Name}
-
- ${album.ArtistName}${album.Year ? ` - ${album.Year}` : ''} -
-
-
- `; - } - - private getAlbumInitial(name: string): string { - return name.charAt(0).toUpperCase(); - } - - private onAlbumClick(album: library.Album) { - EventsEmit('AlbumSelected', album); - this.dispatchEvent( - new CustomEvent('album-selected', { - detail: album, - bubbles: true, - composed: true, - }) - ); - } - - private onAlbumKeydown(e: KeyboardEvent, album: library.Album) { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - this.onAlbumClick(album); - } - } } declare global { diff --git a/frontend/src/components/track-list/track-list.ts b/frontend/src/components/track-list/track-list.ts index 5f1ca8e..162ba9b 100644 --- a/frontend/src/components/track-list/track-list.ts +++ b/frontend/src/components/track-list/track-list.ts @@ -6,6 +6,8 @@ import { customElement, state, query } from 'lit/decorators.js'; import { formatMilliseconds } from '@utils/time'; import { PlayerController } from '@store/controllers/player-controller'; import { QueueController } from '@store/controllers/queue-controller'; +import '@lit-labs/virtualizer'; +import { flow } from '@lit-labs/virtualizer/layouts/flow.js'; import '@awesome.me/webawesome/dist/components/popup/popup.js'; import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js'; import '@awesome.me/webawesome/dist/components/icon/icon.js'; @@ -30,42 +32,48 @@ export class TrackList extends LitElement { private closeHandler = () => this.closeContextMenu(); static override styles = css` - table { - width: 100%; - border-collapse: collapse; + :host { + display: flex; + flex-direction: column; + overflow: hidden; } - th { + .header-row { + display: grid; + grid-template-columns: 1fr 1fr 80px; padding: 8px; - text-align: left; font-weight: bold; color: #fff; - } - - thead tr { border-bottom: 1px solid #666; + flex-shrink: 0; } - tbody tr { + lit-virtualizer { + flex: 1; + overflow-y: auto; + } + + .track-row { + display: grid; + grid-template-columns: 1fr 1fr 80px; + padding: 8px; border-bottom: 1px solid #333; + align-items: center; + width: 100%; } - tbody tr:hover { + .track-row:hover { background-color: rgba(255, 255, 255, 0.05); } - tbody tr.active { + .track-row.active { background-color: rgba(255, 212, 59, 0.1); } - tbody tr.active .track-name-button { + .track-row.active .track-name-button { color: #ffd43b; } - td { - padding: 8px; - } - .track-name-button { background: none; border: none; @@ -74,12 +82,23 @@ export class TrackList extends LitElement { padding: 0; cursor: pointer; width: 100%; + font: inherit; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .track-name-button:hover { text-decoration: underline; } + .artist-name, + .track-length { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + #context-menu { z-index: 200; } @@ -209,45 +228,45 @@ export class TrackList extends LitElement { return currentTrack.filePath === track.FilePath; } + private renderTrackRow = (track: library.Track): unknown => { + const active = this.isActiveTrack(track); + + return html` +
this.onTrackContextMenu(e, track)} + > +
+ +
+
${track.ArtistName}
+
${formatMilliseconds(track.TrackLength)}
+
+ `; + }; + override render() { return html` -
- ${this.tracks.length === 0 - ? html`

Loading tracks...

` - : html` - - - - - - - - - - ${this.tracks.map( - (track) => html` - - this.onTrackContextMenu(e, track)} - > - - - - - ` - )} - -
Track NameArtistTrack Length
- - ${track.ArtistName}${formatMilliseconds(track.TrackLength)}
- `} -
+ ${this.tracks.length === 0 + ? html`

Loading tracks...

` + : html` +
+ Track Name + Artist + Track Length +
+ + `}