feat(web): storage admin, search and catalog pages (M2)
ci / ts / ts (packages + web) (push) Successful in 1m56s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m59s

- Storage admin: local folders with a folder picker and NFS sources,
  connection test, scan and full scan with live progress, enable,
  disable, delete
- path pattern editor with reorder, examples and a preview table that
  shows which pattern matched and what came from tags; compile errors
  land under the broken pattern
- Library page with counters, scans and unreadable files; search
  reindex for admins
- search box in the top bar: combobox with keyboard navigation, 150 ms
  debounce, cancelled stale requests, "/" and Ctrl+K, layout fix hint
- search page with tabs and paging, artist and album pages, album
  cards and track lists; unavailable tracks are dimmed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-20 07:01:52 +03:00
1 parent c1160cccd3
commit a685b1b7ca
27 files changed
+2505 -9

No files matched your search

+164
View File
@@ -0,0 +1,164 @@
.cover {
position: relative;
display: inline-grid;
place-items: center;
flex: none;
overflow: hidden;
border-radius: 8px;
color: rgba(255, 255, 255, 0.85);
}
.cover img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.round {
border-radius: 50%;
}
.link {
color: inherit;
text-decoration: none;
}
.link:hover {
text-decoration: underline;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
gap: 20px;
}
.card,
.artistChip {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
padding: 10px;
margin: -10px;
border-radius: var(--radius-lg);
color: inherit;
text-decoration: none;
}
.card:hover,
.artistChip:hover {
background: var(--hover);
}
.artistChip {
align-items: center;
text-align: center;
width: 128px;
}
.cardCover {
aspect-ratio: 1;
border-radius: 12px;
margin-bottom: 4px;
}
.cardTitle {
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.cardMeta {
font-size: 13px;
color: var(--text3);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
.tracks {
display: flex;
flex-direction: column;
}
.disc {
margin: 14px 12px 6px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text3);
}
.track {
display: grid;
grid-template-columns: 40px minmax(0, 1fr) auto;
align-items: center;
gap: 14px;
min-height: 56px;
padding: 6px 12px;
border-radius: 10px;
}
.track:has(.trackAlbum) {
grid-template-columns: 40px minmax(0, 2fr) minmax(0, 1fr) auto;
}
.track:hover {
background: var(--hover);
}
.unavailable {
opacity: 0.45;
}
.num {
display: grid;
place-items: center;
color: var(--text3);
font-variant-numeric: tabular-nums;
}
.trackMain {
display: flex;
flex-direction: column;
min-width: 0;
}
.trackTitle {
font-weight: 500;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.trackArtists,
.trackAlbum {
font-size: 13px;
color: var(--text2);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.duration {
font-size: 13px;
color: var(--text3);
font-variant-numeric: tabular-nums;
}
@media (max-width: 767px) {
.track:has(.trackAlbum) {
grid-template-columns: 40px minmax(0, 1fr) auto;
}
.trackAlbum {
display: none;
}
}