BundledBrowseView.swift (2248B)
1 import SwiftUI 2 3 struct BundledBrowseView: View { 4 let onSelected: (String) -> Void 5 6 private var bundles: [PuzzleCatalog.PuzzleBundle] { 7 PuzzleCatalog.bundles() 8 } 9 10 var body: some View { 11 List(bundles) { bundle in 12 NavigationLink { 13 PuzzleListView(puzzles: bundle.puzzles, onSelected: onSelected) 14 .navigationTitle(bundle.name) 15 .navigationBarTitleDisplayMode(.inline) 16 } label: { 17 VStack(alignment: .leading, spacing: 2) { 18 Text(bundle.name) 19 Text("^[\(bundle.puzzles.count) puzzle](inflect: true)") 20 .font(.subheadline) 21 .foregroundStyle(.secondary) 22 } 23 } 24 } 25 } 26 } 27 28 struct DebugBrowseView: View { 29 let onSelected: (String) -> Void 30 31 private var puzzles: [PuzzleCatalog.Entry] { 32 PuzzleCatalog.debugPuzzles() 33 } 34 35 var body: some View { 36 PuzzleListView(puzzles: puzzles, onSelected: onSelected) 37 } 38 } 39 40 /// A list of individual puzzles, each showing a grid preview alongside its 41 /// title and publisher. 42 private struct PuzzleListView: View { 43 let puzzles: [PuzzleCatalog.Entry] 44 let onSelected: (String) -> Void 45 46 var body: some View { 47 List(puzzles) { entry in 48 Button { 49 if let source = try? entry.loadSource() { 50 onSelected(source) 51 } 52 } label: { 53 HStack(spacing: 12) { 54 GridThumbnailView( 55 width: entry.gridWidth, 56 height: entry.gridHeight, 57 cells: entry.blockCells.map { $0 ? .block : .empty } 58 ) 59 VStack(alignment: .leading, spacing: 2) { 60 Text(entry.title) 61 if let publisher = entry.publisher, !publisher.isEmpty { 62 Text(publisher) 63 .font(.subheadline) 64 .foregroundStyle(.secondary) 65 } 66 } 67 } 68 } 69 .buttonStyle(.plain) 70 } 71 } 72 }