Refine macOS app visual hierarchy

This commit is contained in:
diyaa
2026-09-16 00:18:32 +02:00
parent c8242a844a
commit f9af5c21d8
5 changed files with 167 additions and 60 deletions
@@ -13,48 +13,45 @@ struct ProjectBrowserView: View {
var body: some View {
NavigationSplitView {
List(selection: $selectedProjectID) {
Section("Projects") {
ForEach(viewModel.projects) { project in
ProjectListRow(project: project) {
projectPendingDeletion = project
VStack(spacing: 0) {
ProjectLibraryHeader(
projectCount: viewModel.projects.count,
isCreatingProject: viewModel.isCreatingProject
) {
Task {
if let projectID = await viewModel.createProject() {
selectedProjectID = projectID
}
.tag(project.id)
.contextMenu {
Button(role: .destructive) {
projectPendingDeletion = project
} label: {
Label("Delete Project", systemImage: "trash")
}
}
List(selection: $selectedProjectID) {
Section("Projects") {
ForEach(viewModel.projects) { project in
ProjectListRow(project: project) {
projectPendingDeletion = project
}
.tag(project.id)
.contextMenu {
Button(role: .destructive) {
projectPendingDeletion = project
} label: {
Label("Delete Project", systemImage: "trash")
}
}
}
}
}
}
.navigationTitle("AI Music Studio")
.overlay {
if viewModel.isLoading {
ProgressView()
} else if viewModel.projects.isEmpty {
ContentUnavailableView(
"No Projects",
systemImage: "music.note.list",
description: Text("Create a song project to begin.")
)
}
}
.toolbar {
ToolbarItem {
Button {
Task {
if let projectID = await viewModel.createProject() {
selectedProjectID = projectID
}
}
} label: {
Label("New Project", systemImage: "plus")
}
.keyboardShortcut("n", modifiers: .command)
.disabled(viewModel.isCreatingProject)
}
.overlay {
if viewModel.isLoading {
ProgressView()
} else if viewModel.projects.isEmpty {
ContentUnavailableView(
"No Projects",
systemImage: "music.note.list",
description: Text("Create a song project to begin.")
)
}
}
}
} detail: {
@@ -96,6 +93,7 @@ struct ProjectBrowserView: View {
} message: {
Text("This permanently removes the project and its saved versions from this Mac.")
}
.navigationSplitViewStyle(.balanced)
}
private var deletionAlertBinding: Binding<Bool> {
@@ -124,12 +122,57 @@ struct ProjectBrowserView: View {
}
}
private struct ProjectLibraryHeader: View {
let projectCount: Int
let isCreatingProject: Bool
let createAction: () -> Void
var body: some View {
HStack(spacing: 12) {
Image(systemName: "music.note.list")
.font(.title3.weight(.semibold))
.foregroundStyle(.tint)
.frame(width: 34, height: 34)
.background(.tint.opacity(0.12), in: RoundedRectangle(cornerRadius: 8))
VStack(alignment: .leading, spacing: 2) {
Text("AI Music Studio")
.font(.headline)
Text("\(projectCount) saved \(projectCount == 1 ? "project" : "projects")")
.font(.caption)
.foregroundStyle(.secondary)
}
Spacer(minLength: 8)
Button(action: createAction) {
Image(systemName: "plus")
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
.keyboardShortcut("n", modifiers: .command)
.disabled(isCreatingProject)
.help("New project")
.accessibilityLabel("New project")
}
.padding(.horizontal, 14)
.padding(.vertical, 12)
.background(.bar)
}
}
private struct ProjectListRow: View {
let project: SongProjectListItem
let deleteAction: () -> Void
var body: some View {
HStack(alignment: .top, spacing: 8) {
Image(systemName: project.idea.isEmpty ? "music.note" : "music.note.house")
.font(.body.weight(.semibold))
.foregroundStyle(project.idea.isEmpty ? Color.secondary : Color.accentColor)
.frame(width: 32, height: 32)
.background(.quaternary, in: RoundedRectangle(cornerRadius: 8))
VStack(alignment: .leading, spacing: 4) {
Text(project.title)
.font(.headline)