Add per-page element customization and panel settings polish

Phase 5 of the player panels redesign:
- Per-page element toggles now rendered and editable for every page:
  comments (author avatars), related (thumbnails), chapters (thumbnails),
  queue (previously played section), tracks (video/audio/subtitles/speed
  sections)
- Page bar labels toggle (icons-only bar shows the selected page's name)
- Layout mode schematic diagram in the details panel settings
- NEW badge machinery for future page types (versionAdded on PanelPageType)
- Typed per-page settings accessors on PanelPageConfiguration and VM update
  helpers for each page kind
- Unit tests for per-page settings round-trip and accessor fallbacks

The standalone quality selector sheet is unaffected: section gating defaults
to everything visible and only the embedded tracks page passes preset
settings through.
This commit is contained in:
Arkadiusz Fal
2026-09-22 09:37:10 +02:00
parent 052cb76702
commit c289e7576e
19 changed files with 618 additions and 68 deletions

View File

@@ -11,6 +11,7 @@ struct ChapterRow: View {
let chapter: VideoChapter
let isActive: Bool
let storyboard: Storyboard?
var showThumbnail: Bool = true
let onTap: () -> Void
@State private var thumbnail: PlatformImage?
@@ -19,29 +20,9 @@ struct ChapterRow: View {
Button(action: onTap) {
HStack(spacing: 12) {
// Thumbnail from storyboard
Group {
if let thumbnail {
#if os(macOS)
Image(nsImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
#else
Image(uiImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
#endif
} else {
Rectangle()
.fill(.quaternary)
.overlay {
Image(systemName: "film")
.foregroundStyle(.secondary)
}
}
if showThumbnail {
thumbnailContent
}
.frame(width: 80, height: 45)
.clipShape(RoundedRectangle(cornerRadius: 4))
// Info
VStack(alignment: .leading, spacing: 4) {
Text(chapter.title)
@@ -69,10 +50,37 @@ struct ChapterRow: View {
.buttonStyle(.plain)
.listRowBackground(isActive ? Color.accentColor.opacity(0.1) : nil)
.task {
await loadThumbnail()
if showThumbnail {
await loadThumbnail()
}
}
}
private var thumbnailContent: some View {
Group {
if let thumbnail {
#if os(macOS)
Image(nsImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
#else
Image(uiImage: thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
#endif
} else {
Rectangle()
.fill(.quaternary)
.overlay {
Image(systemName: "film")
.foregroundStyle(.secondary)
}
}
}
.frame(width: 80, height: 45)
.clipShape(RoundedRectangle(cornerRadius: 4))
}
private func loadThumbnail() async {
guard let storyboard else { return }