version 1.0.0

This commit is contained in:
diyaa
2026-05-24 20:53:42 +02:00
commit 4b8701e55b
6577 changed files with 25019 additions and 0 deletions
@@ -0,0 +1,47 @@
import AppKit
import Foundation
@MainActor
final class FolderAccessService {
private let bookmarkKey = "velody.selected-folder.bookmark"
func chooseFolder() -> URL? {
let panel = NSOpenPanel()
panel.canChooseDirectories = true
panel.canChooseFiles = false
panel.allowsMultipleSelection = false
panel.prompt = "Choose Folder"
guard panel.runModal() == .OK, let url = panel.url else {
return nil
}
do {
let bookmark = try url.bookmarkData(
options: [.withSecurityScope],
includingResourceValuesForKeys: nil,
relativeTo: nil
)
UserDefaults.standard.set(bookmark, forKey: bookmarkKey)
return url
} catch {
return url
}
}
func storedFolderURL() -> URL? {
guard
let bookmark = UserDefaults.standard.data(forKey: bookmarkKey)
else {
return nil
}
var isStale = false
return try? URL(
resolvingBookmarkData: bookmark,
options: [.withSecurityScope],
relativeTo: nil,
bookmarkDataIsStale: &isStale
)
}
}
@@ -0,0 +1,56 @@
import SwiftUI
import VelodyDomain
struct MacLibraryView: View {
@State private var viewModel = MacLibraryViewModel()
var body: some View {
NavigationSplitView {
List(viewModel.tracks) { track in
VStack(alignment: .leading, spacing: 4) {
Text(track.title)
.font(.headline)
Text(track.artist)
.foregroundStyle(.secondary)
if let album = track.album {
Text(album)
.font(.caption)
.foregroundStyle(.secondary)
}
}
.padding(.vertical, 4)
}
.navigationTitle("Velody")
} detail: {
VStack(alignment: .leading, spacing: 16) {
Text("Private Library Foundation")
.font(.largeTitle)
Text("Selected folder")
.font(.headline)
Text(viewModel.selectedFolderPath)
.textSelection(.enabled)
Text("Sync status")
.font(.headline)
Text(viewModel.syncStatus)
.foregroundStyle(.secondary)
HStack {
Button("Choose Watched Folder") {
viewModel.chooseFolder()
}
Button("Refresh Placeholder Sync") {
Task {
await viewModel.refreshSyncStatus()
}
}
}
Spacer()
}
.padding(24)
}
.task {
await viewModel.loadIfNeeded()
}
}
}
@@ -0,0 +1,59 @@
import Foundation
import Observation
import VelodyDomain
import VelodyNetworking
import VelodyPersistence
import VelodySync
import VelodyUtilities
@MainActor
@Observable
final class MacLibraryViewModel {
var tracks: [LibraryTrack] = []
var selectedFolderPath = "No folder selected"
var syncStatus = "Sync not started"
private let folderAccessService = FolderAccessService()
private let keychainService = MemoryKeychainService()
private let store = InMemoryLocalLibraryStore()
private let syncCoordinator: PlaceholderSyncCoordinator
private var hasLoaded = false
init() {
let environment = ServerEnvironment(
baseURL: URL(string: "http://localhost:3000")!,
appVersion: "0.1.0"
)
let apiClient = StubVelodyAPIClient(environment: environment)
self.syncCoordinator = PlaceholderSyncCoordinator(
apiClient: apiClient,
store: store
)
if let url = folderAccessService.storedFolderURL() {
selectedFolderPath = url.path
}
}
func loadIfNeeded() async {
guard !hasLoaded else { return }
hasLoaded = true
await refreshSyncStatus()
await keychainService.save("placeholder-bootstrap-token", forKey: "bootstrap-token")
}
func refreshSyncStatus() async {
do {
let result = try await syncCoordinator.performInitialSync()
tracks = result.tracks
syncStatus = result.statusMessage
} catch {
syncStatus = "Sync placeholder failed: \(error.localizedDescription)"
}
}
func chooseFolder() {
if let url = folderAccessService.chooseFolder() {
selectedFolderPath = url.path
}
}
}
@@ -0,0 +1,10 @@
import SwiftUI
@main
struct VelodyMacApp: App {
var body: some Scene {
WindowGroup {
MacLibraryView()
}
}
}