Merge master into main
This commit is contained in:
@@ -1,8 +1,50 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RootView: View {
|
struct RootView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ProjectBrowserView()
|
ProjectBrowserView()
|
||||||
|
=======
|
||||||
|
import MusicAssistantCore
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct RootView: View {
|
||||||
|
private let appName = "AI Music Studio"
|
||||||
|
private let boundarySummary = ProductBoundarySummary.initial
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationSplitView {
|
||||||
|
List {
|
||||||
|
Label("Projects", systemImage: "music.note.list")
|
||||||
|
Label("Settings", systemImage: "gearshape")
|
||||||
|
}
|
||||||
|
.navigationTitle(appName)
|
||||||
|
} detail: {
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
Text("New Song Project")
|
||||||
|
.font(.largeTitle.weight(.semibold))
|
||||||
|
|
||||||
|
Text(boundarySummary.description)
|
||||||
|
.font(.body)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.frame(maxWidth: 560, alignment: .leading)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
Text("Foundation status")
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
Label("Presentation, Domain, Services, Integrations, and Persistence boundaries are separated.", systemImage: "checkmark.circle")
|
||||||
|
Label("External providers are represented by protocols only.", systemImage: "lock.shield")
|
||||||
|
Label("Secrets are expected from local environment configuration.", systemImage: "key")
|
||||||
|
}
|
||||||
|
.labelStyle(.titleAndIcon)
|
||||||
|
}
|
||||||
|
.padding(32)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
}
|
||||||
|
>>>>>>> origin/master
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Foundation
|
|||||||
|
|
||||||
public struct AppConfiguration: Equatable, Sendable {
|
public struct AppConfiguration: Equatable, Sendable {
|
||||||
public let openAIAPIKey: String?
|
public let openAIAPIKey: String?
|
||||||
|
<<<<<<< HEAD
|
||||||
public let privateApplicationRules: PrivateApplicationRules?
|
public let privateApplicationRules: PrivateApplicationRules?
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
@@ -10,12 +11,21 @@ public struct AppConfiguration: Equatable, Sendable {
|
|||||||
) {
|
) {
|
||||||
self.openAIAPIKey = openAIAPIKey
|
self.openAIAPIKey = openAIAPIKey
|
||||||
self.privateApplicationRules = privateApplicationRules
|
self.privateApplicationRules = privateApplicationRules
|
||||||
|
=======
|
||||||
|
|
||||||
|
public init(openAIAPIKey: String?) {
|
||||||
|
self.openAIAPIKey = openAIAPIKey
|
||||||
|
>>>>>>> origin/master
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func development(environment: [String: String] = ProcessInfo.processInfo.environment) -> AppConfiguration {
|
public static func development(environment: [String: String] = ProcessInfo.processInfo.environment) -> AppConfiguration {
|
||||||
AppConfiguration(
|
AppConfiguration(
|
||||||
|
<<<<<<< HEAD
|
||||||
openAIAPIKey: environment["MUSIC_ASSISTANT_OPENAI_API_KEY"],
|
openAIAPIKey: environment["MUSIC_ASSISTANT_OPENAI_API_KEY"],
|
||||||
privateApplicationRules: PrivateApplicationRules(content: environment["MUSIC_ASSISTANT_PRIVATE_AI_RULES"])
|
privateApplicationRules: PrivateApplicationRules(content: environment["MUSIC_ASSISTANT_PRIVATE_AI_RULES"])
|
||||||
|
=======
|
||||||
|
openAIAPIKey: environment["MUSIC_ASSISTANT_OPENAI_API_KEY"]
|
||||||
|
>>>>>>> origin/master
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ public extension SongProject {
|
|||||||
versions.first { $0.id == versionID }
|
versions.first { $0.id == versionID }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
mutating func restoreVersion(id versionID: String, restoredAt: Date = Date()) -> Bool {
|
mutating func restoreVersion(id versionID: String, restoredAt: Date = Date()) -> Bool {
|
||||||
guard let version = version(id: versionID) else { return false }
|
guard let version = version(id: versionID) else { return false }
|
||||||
restoreSnapshot(version.snapshot, updatedAt: restoredAt)
|
restoreSnapshot(version.snapshot, updatedAt: restoredAt)
|
||||||
@@ -373,6 +374,8 @@ public extension SongProject {
|
|||||||
self.updatedAt = updatedAt
|
self.updatedAt = updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> origin/master
|
||||||
mutating func appendSection(_ section: SongSection) {
|
mutating func appendSection(_ section: SongSection) {
|
||||||
var orderedSection = section
|
var orderedSection = section
|
||||||
orderedSection.order = nextSectionOrder
|
orderedSection.order = nextSectionOrder
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
<<<<<<< HEAD
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public protocol AIService: Sendable {
|
public protocol AIService: Sendable {
|
||||||
@@ -166,5 +167,24 @@ public struct SongProjectUpdateResult: Equatable, Sendable {
|
|||||||
self.changedScopes = changedScopes
|
self.changedScopes = changedScopes
|
||||||
self.notes = notes
|
self.notes = notes
|
||||||
self.followUpQuestions = followUpQuestions
|
self.followUpQuestions = followUpQuestions
|
||||||
|
=======
|
||||||
|
public protocol AIService: Sendable {
|
||||||
|
func planSongProject(from request: SongPlanningRequest) async throws -> SongPlanningDraft
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SongPlanningRequest: Equatable, Sendable {
|
||||||
|
public let userIntent: String
|
||||||
|
|
||||||
|
public init(userIntent: String) {
|
||||||
|
self.userIntent = userIntent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SongPlanningDraft: Equatable, Sendable {
|
||||||
|
public let summary: String
|
||||||
|
|
||||||
|
public init(summary: String) {
|
||||||
|
self.summary = summary
|
||||||
|
>>>>>>> origin/master
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,28 @@ import XCTest
|
|||||||
final class AppConfigurationTests: XCTestCase {
|
final class AppConfigurationTests: XCTestCase {
|
||||||
func testDevelopmentConfigurationReadsKnownEnvironmentKeys() {
|
func testDevelopmentConfigurationReadsKnownEnvironmentKeys() {
|
||||||
let configuration = AppConfiguration.development(environment: [
|
let configuration = AppConfiguration.development(environment: [
|
||||||
|
<<<<<<< HEAD
|
||||||
"MUSIC_ASSISTANT_OPENAI_API_KEY": "openai-test-key",
|
"MUSIC_ASSISTANT_OPENAI_API_KEY": "openai-test-key",
|
||||||
"MUSIC_ASSISTANT_PRIVATE_AI_RULES": " private product rules "
|
"MUSIC_ASSISTANT_PRIVATE_AI_RULES": " private product rules "
|
||||||
])
|
])
|
||||||
|
|
||||||
XCTAssertEqual(configuration.openAIAPIKey, "openai-test-key")
|
XCTAssertEqual(configuration.openAIAPIKey, "openai-test-key")
|
||||||
XCTAssertEqual(configuration.privateApplicationRules?.content, "private product rules")
|
XCTAssertEqual(configuration.privateApplicationRules?.content, "private product rules")
|
||||||
|
=======
|
||||||
|
"MUSIC_ASSISTANT_OPENAI_API_KEY": "openai-test-key"
|
||||||
|
])
|
||||||
|
|
||||||
|
XCTAssertEqual(configuration.openAIAPIKey, "openai-test-key")
|
||||||
|
>>>>>>> origin/master
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDevelopmentConfigurationAllowsMissingSecrets() {
|
func testDevelopmentConfigurationAllowsMissingSecrets() {
|
||||||
let configuration = AppConfiguration.development(environment: [:])
|
let configuration = AppConfiguration.development(environment: [:])
|
||||||
|
|
||||||
XCTAssertNil(configuration.openAIAPIKey)
|
XCTAssertNil(configuration.openAIAPIKey)
|
||||||
|
<<<<<<< HEAD
|
||||||
XCTAssertNil(configuration.privateApplicationRules)
|
XCTAssertNil(configuration.privateApplicationRules)
|
||||||
|
=======
|
||||||
|
>>>>>>> origin/master
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ final class SongProjectModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(project.genres.map(\.id), ["folk", "electronic"])
|
XCTAssertEqual(project.genres.map(\.id), ["folk", "electronic"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
func testRestoresPreviousVersionWithoutDestroyingLaterVersions() {
|
func testRestoresPreviousVersionWithoutDestroyingLaterVersions() {
|
||||||
let createdAt = Date(timeIntervalSince1970: 100)
|
let createdAt = Date(timeIntervalSince1970: 100)
|
||||||
let restoredAt = Date(timeIntervalSince1970: 900)
|
let restoredAt = Date(timeIntervalSince1970: 900)
|
||||||
@@ -177,6 +178,8 @@ final class SongProjectModelTests: XCTestCase {
|
|||||||
XCTAssertEqual(project, restoredProject)
|
XCTAssertEqual(project, restoredProject)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> origin/master
|
||||||
func testGenreStylesAreModeledAsMultiSelect() {
|
func testGenreStylesAreModeledAsMultiSelect() {
|
||||||
let arabicPop = GenreStyle(id: "arabic-pop", name: "Arabic Pop")
|
let arabicPop = GenreStyle(id: "arabic-pop", name: "Arabic Pop")
|
||||||
let synthwave = GenreStyle(id: "synthwave", name: "Synthwave")
|
let synthwave = GenreStyle(id: "synthwave", name: "Synthwave")
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ requirement is missing and blocks implementation, record it in
|
|||||||
- [x] Create new/open/save song projects locally.
|
- [x] Create new/open/save song projects locally.
|
||||||
- [x] Implement project list.
|
- [x] Implement project list.
|
||||||
- [x] Implement immutable or snapshot-based version history.
|
- [x] Implement immutable or snapshot-based version history.
|
||||||
|
<<<<<<< HEAD
|
||||||
- [x] Restore a previous version without destroying later versions.
|
- [x] Restore a previous version without destroying later versions.
|
||||||
- [x] Add autosave strategy that does not create excessive versions.
|
- [x] Add autosave strategy that does not create excessive versions.
|
||||||
|
|
||||||
@@ -49,14 +50,37 @@ requirement is missing and blocks implementation, record it in
|
|||||||
timestamp-based placement.
|
timestamp-based placement.
|
||||||
- [x] Add Manual/Auto toggles for supported controls.
|
- [x] Add Manual/Auto toggles for supported controls.
|
||||||
- [x] Build final review screen where every generated field can be
|
- [x] Build final review screen where every generated field can be
|
||||||
|
=======
|
||||||
|
- [ ] Restore a previous version without destroying later versions.
|
||||||
|
- [ ] Add autosave strategy that does not create excessive versions.
|
||||||
|
|
||||||
|
## Phase 3 --- Core macOS UI
|
||||||
|
|
||||||
|
- [ ] Build project browser.
|
||||||
|
- [ ] Build natural chat workspace as the primary entry point.
|
||||||
|
- [ ] Build editable project inspector for duration, genres,
|
||||||
|
instruments, vocals and language.
|
||||||
|
- [ ] Build song structure editor.
|
||||||
|
- [ ] Build arrangement editor supporting section-based and
|
||||||
|
timestamp-based placement.
|
||||||
|
- [ ] Add Manual/Auto toggles for supported controls.
|
||||||
|
- [ ] Build final review screen where every generated field can be
|
||||||
|
>>>>>>> origin/master
|
||||||
edited.
|
edited.
|
||||||
|
|
||||||
## Phase 4 --- OpenAI Integration
|
## Phase 4 --- OpenAI Integration
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
- [x] Create provider-independent `AIService` interface.
|
- [x] Create provider-independent `AIService` interface.
|
||||||
- [x] Implement OpenAI client.
|
- [x] Implement OpenAI client.
|
||||||
- [x] Define private application-rule injection mechanism.
|
- [x] Define private application-rule injection mechanism.
|
||||||
- [x] Define structured AI response schema for SongProject updates.
|
- [x] Define structured AI response schema for SongProject updates.
|
||||||
|
=======
|
||||||
|
- [ ] Create provider-independent `AIService` interface.
|
||||||
|
- [ ] Implement OpenAI client.
|
||||||
|
- [ ] Define private application-rule injection mechanism.
|
||||||
|
- [ ] Define structured AI response schema for SongProject updates.
|
||||||
|
>>>>>>> origin/master
|
||||||
- [ ] Implement idea → complete project generation.
|
- [ ] Implement idea → complete project generation.
|
||||||
- [ ] Implement existing lyrics → correction/improvement flow.
|
- [ ] Implement existing lyrics → correction/improvement flow.
|
||||||
- [ ] Implement Auto mode for structure, arrangement, BPM/key/maqam
|
- [ ] Implement Auto mode for structure, arrangement, BPM/key/maqam
|
||||||
|
|||||||
Reference in New Issue
Block a user