From eebe691da1b249d7407f3c2c33d4d52394140d8b Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:05:34 +0530 Subject: [PATCH 01/28] Define Core AI Lab product design principles --- PRODUCT.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 PRODUCT.md diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..e0ab78e --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,37 @@ +# Product + +## Register + +product + +## Users + +Apple-platform developers, machine-learning engineers, and framework researchers who need to understand and validate Core AI assets on iPhone, iPad, and Mac. They work through technical, multi-step workflows and need the interface to preserve context while they compare recipes, prepare assets, run models, inspect evidence, and package results. + +## Product Purpose + +Core AI Lab is a native workbench for discovering, converting, inspecting, running, benchmarking, and packaging models with Apple's `CoreAI.framework`. It succeeds when a developer can move from an unfamiliar asset or recipe to a reproducible, evidence-backed result without mistaking a preference, cached artifact, or conversion plan for measured runtime truth. + +## Brand Personality + +Precise, native, and quietly technical. The app should feel like a trustworthy Apple developer instrument: capable without theatrics, information-rich without becoming dense, and candid about prerequisites, progress, and failure. + +## Anti-references + +- Consumer AI chat apps that reduce every workflow to a prompt box. +- Generic SaaS dashboards built from interchangeable card grids and decorative metrics. +- Sci-fi control panels with neon gradients, ornamental glass, or unexplained status lights. +- Marketing surfaces that hide provenance, prerequisites, or limitations behind optimistic copy. +- Bespoke controls that replace familiar Apple platform behavior without improving the task. + +## Design Principles + +1. **Orient before acting.** Every workspace makes its purpose, current state, prerequisites, and next meaningful action clear. +2. **Progressively disclose complexity.** Lead with the common path, then reveal technical detail where it becomes relevant. +3. **Evidence earns emphasis.** Give measured results, provenance, and validation findings stronger hierarchy than decoration or unverified capability claims. +4. **Use platform fluency.** Prefer familiar navigation, controls, keyboard behavior, and system feedback so the tool disappears into the work. +5. **Keep state honest.** Loading, unsupported, cancelled, empty, warning, and failure states remain distinct and actionable. + +## Accessibility & Inclusion + +Follow Apple's accessibility guidance and target WCAG 2.2 AA contrast for custom treatments. Support Dynamic Type, VoiceOver, keyboard and pointer navigation, Reduce Motion, Increase Contrast, and Differentiate Without Color. Status and selection must never rely on color alone, touch targets on iOS must be at least 44 by 44 points, and technical language must remain understandable without hiding exact evidence from expert users. From b5397e8fe41fed9cce802c8fd5216be92e1a1584 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:09:38 +0530 Subject: [PATCH 02/28] Reorganize the Core AI Lab workspace shell --- CoreAILab/ContentView.swift | 44 ++++++++++++++++--- CoreAILab/CoreAILabApp.swift | 3 ++ CoreAILab/CoreAILabSection.swift | 31 ++++++++++++- .../DeviceLab/CoreAIDeviceLabView.swift | 14 ++++-- 4 files changed, 79 insertions(+), 13 deletions(-) diff --git a/CoreAILab/ContentView.swift b/CoreAILab/ContentView.swift index 849202f..a0489a4 100644 --- a/CoreAILab/ContentView.swift +++ b/CoreAILab/ContentView.swift @@ -2,29 +2,54 @@ import SwiftData import SwiftUI struct ContentView: View { - @State private var selection: CoreAILabSection? = .projects + @SceneStorage("CoreAILab.selectedSection") + private var selection: CoreAILabSection? var body: some View { NavigationSplitView { List(selection: $selection) { - Section("Workspaces") { - ForEach(CoreAILabSection.workspaces) { section in + Section("Library") { + ForEach(CoreAILabSection.library) { section in NavigationLink(value: section) { Label(section.title, systemImage: section.systemImage) } + .help(section.summary) + .accessibilityHint(section.summary) } } - Section("Tools") { - ForEach(CoreAILabSection.tools) { section in + Section("Build") { + ForEach(CoreAILabSection.build) { section in NavigationLink(value: section) { Label(section.title, systemImage: section.systemImage) } + .help(section.summary) + .accessibilityHint(section.summary) + } + } + + Section("Run") { + ForEach(CoreAILabSection.run) { section in + NavigationLink(value: section) { + Label(section.title, systemImage: section.systemImage) + } + .help(section.summary) + .accessibilityHint(section.summary) + } + } + + Section("Validate") { + ForEach(CoreAILabSection.validate) { section in + NavigationLink(value: section) { + Label(section.title, systemImage: section.systemImage) + } + .help(section.summary) + .accessibilityHint(section.summary) } } } - .navigationTitle("Core AI Lab") - .navigationSplitViewColumnWidth(min: 190, ideal: 220, max: 280) + .listStyle(.sidebar) + .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 280) } detail: { switch selection ?? .projects { case .projects: @@ -54,6 +79,11 @@ struct ContentView: View { } } .navigationSplitViewStyle(.prominentDetail) + .formStyle(.grouped) + .tint(.blue) +#if os(macOS) + .frame(minWidth: 1_000, minHeight: 680) +#endif } } diff --git a/CoreAILab/CoreAILabApp.swift b/CoreAILab/CoreAILabApp.swift index 12deea5..b7ceac5 100644 --- a/CoreAILab/CoreAILabApp.swift +++ b/CoreAILab/CoreAILabApp.swift @@ -19,5 +19,8 @@ struct CoreAILabApp: App { ContentView() } .modelContainer(modelContainer) +#if os(macOS) + .defaultSize(width: 1_280, height: 820) +#endif } } diff --git a/CoreAILab/CoreAILabSection.swift b/CoreAILab/CoreAILabSection.swift index 3cc9742..7b5bba9 100644 --- a/CoreAILab/CoreAILabSection.swift +++ b/CoreAILab/CoreAILabSection.swift @@ -12,8 +12,10 @@ enum CoreAILabSection: String, CaseIterable, Hashable, Identifiable { case runtime case deviceLab - static let tools: [Self] = [.assetInspector, .runtime, .deviceLab] - static let workspaces = allCases.filter { !tools.contains($0) } + static let library: [Self] = [.projects, .appleModels, .recipes] + static let build: [Self] = [.conversion, .recipeStudio] + static let run: [Self] = [.chatterbox, .diarization, .runtime] + static let validate: [Self] = [.assetInspector, .deviceLab] var id: Self { self } @@ -66,4 +68,29 @@ enum CoreAILabSection: String, CaseIterable, Hashable, Identifiable { "iphone.gen3" } } + + var summary: String { + switch self { + case .projects: + "Organize imported artifacts, provenance, runs, and evidence." + case .appleModels: + "Browse Apple's pinned Core AI export recipes." + case .recipes: + "Review curated recipes and inspect imported bundles." + case .conversion: + "Export a Core AI model from an Apple recipe." + case .recipeStudio: + "Author and validate recipe and pipeline contracts." + case .chatterbox: + "Generate speech with the bundled Core AI pipeline." + case .diarization: + "Build an anonymous speaker timeline from local media." + case .assetInspector: + "Inspect functions, compute types, and specialization caches." + case .runtime: + "Run task adapters and record evidence-backed timing." + case .deviceLab: + "Plan iPhone delivery and import physical-device evidence." + } + } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift index a0e7fc4..6d0d547 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift @@ -8,10 +8,15 @@ struct CoreAIDeviceLabView: View { var body: some View { Form { Section { - Text( - "Author an iPhone target, plan its asset delivery, and import evidence from the physical runner. Preferences remain separate from measured execution placement." - ) - .foregroundStyle(.secondary) + LabeledContent { + Text( + "Author an iPhone target, plan asset delivery, and import evidence from the physical runner. Preferences remain separate from measured execution placement." + ) + .foregroundStyle(.secondary) + } label: { + Label("Physical Device Planning", systemImage: "iphone.gen3") + .font(.headline) + } } CoreAIDeviceTargetAuthoringView(workspace: workspace) @@ -22,6 +27,7 @@ struct CoreAIDeviceLabView: View { isImportingEvidence: $isImportingEvidence ) } + .formStyle(.grouped) .navigationTitle("Device Lab") .fileImporter( isPresented: $isImportingEvidence, From 6406b16b112b521922ba4c66549a679ff6f98ac4 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:11:14 +0530 Subject: [PATCH 03/28] Refine the model and recipe libraries --- .../AppleModelCatalogSourceView.swift | 27 ++++-- .../AppleModels/AppleModelLibraryView.swift | 7 +- .../Features/AppleModels/AppleModelRow.swift | 14 +++- .../Projects/CoreAIProjectLibraryView.swift | 14 +++- .../Projects/CoreAIProjectRowView.swift | 41 +++++++--- .../CoreAIImportedRecipeBundleView.swift | 17 ++-- .../CoreAIRecipeCatalogEntryView.swift | 82 ++++++++++++++----- .../Recipes/CoreAIRecipeCatalogView.swift | 19 +++-- 8 files changed, 167 insertions(+), 54 deletions(-) diff --git a/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift b/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift index 189cb68..ad4b3cf 100644 --- a/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift @@ -13,12 +13,29 @@ struct AppleModelCatalogSourceView: View { Text("A pinned snapshot of Apple's model registry. Entries are export recipes, not bundled weights.") .foregroundStyle(.secondary) - HStack(spacing: 16) { - Label("\(modelCount) presets", systemImage: "list.bullet") - Text(sourceRevision.prefix(8)) - .font(.callout.monospaced()) - .foregroundStyle(.secondary) + ViewThatFits(in: .horizontal) { + HStack { + Label("\(modelCount) recipes", systemImage: "list.bullet") + Label { + Text(sourceRevision.prefix(8)) + .monospaced() + } icon: { + Image(systemName: "point.topleft.down.to.point.bottomright.curvepath") + } + } + + VStack(alignment: .leading) { + Label("\(modelCount) recipes", systemImage: "list.bullet") + Label { + Text(sourceRevision.prefix(8)) + .monospaced() + } icon: { + Image(systemName: "point.topleft.down.to.point.bottomright.curvepath") + } + } } + .font(.callout) + .foregroundStyle(.secondary) if let sourceRepositoryURL { Link("Open apple/coreai-models", destination: sourceRepositoryURL) diff --git a/CoreAILab/Features/AppleModels/AppleModelLibraryView.swift b/CoreAILab/Features/AppleModels/AppleModelLibraryView.swift index 8b0d9f6..46c2e19 100644 --- a/CoreAILab/Features/AppleModels/AppleModelLibraryView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelLibraryView.swift @@ -28,12 +28,17 @@ struct AppleModelLibraryView: View { } ForEach(groups) { group in - Section(group.category.rawValue) { + Section { ForEach(group.models) { entry in NavigationLink(value: entry) { AppleModelRow(model: entry) } } + } header: { + Label( + group.category.rawValue, + systemImage: group.category.systemImage + ) } } } diff --git a/CoreAILab/Features/AppleModels/AppleModelRow.swift b/CoreAILab/Features/AppleModels/AppleModelRow.swift index 29938a8..c458b07 100644 --- a/CoreAILab/Features/AppleModels/AppleModelRow.swift +++ b/CoreAILab/Features/AppleModels/AppleModelRow.swift @@ -11,7 +11,10 @@ struct AppleModelRow: View { Spacer() - Text(model.supportedPlatforms.map(\.rawValue).joined(separator: " · ")) + Label( + model.supportedPlatforms.map(\.rawValue).joined(separator: " · "), + systemImage: platformSystemImage + ) .font(.callout) .foregroundStyle(.secondary) } @@ -19,15 +22,24 @@ struct AppleModelRow: View { Text(model.huggingFaceID) .font(.callout.monospaced()) .foregroundStyle(.secondary) + .lineLimit(1) + .truncationMode(.middle) Label(model.runtimeSupport.title, systemImage: runtimeSystemImage) .font(.callout) .foregroundStyle(.secondary) } .padding(.vertical, 4) + .accessibilityElement(children: .combine) } private var runtimeSystemImage: String { model.isRunnableInLab ? "play.circle.fill" : "shippingbox" } + + private var platformSystemImage: String { + model.supportedPlatforms.count > 1 + ? "desktopcomputer.and.iphone" + : model.supportedPlatforms.first == .iOS ? "iphone" : "desktopcomputer" + } } diff --git a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift index 9c2b76b..5711ba3 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift @@ -8,16 +8,21 @@ struct CoreAIProjectLibraryView: View { @State private var controller = CoreAIProjectLibraryController() @State private var path: [CoreAIProjectRoute] = [] @State private var isCreatingProject = false + @State private var searchText = "" var body: some View { + let visibleProjects = searchText.isEmpty + ? projects + : projects.filter { $0.name.localizedStandardContains(searchText) } + NavigationStack(path: $path) { Group { if projects.isEmpty { ContentUnavailableView { - Label("Create a Lab Project", systemImage: "folder.badge.plus") + Label("Create Your First Project", systemImage: "folder.badge.plus") } description: { Text( - "Projects keep imported models and resource bundles available across launches with checksummed, deduplicated storage." + "Keep models, resource bundles, provenance, runs, and evidence together in checksummed storage." ) } actions: { Button( @@ -27,8 +32,10 @@ struct CoreAIProjectLibraryView: View { ) .buttonStyle(.borderedProminent) } + } else if visibleProjects.isEmpty { + ContentUnavailableView.search } else { - List(projects) { project in + List(visibleProjects) { project in NavigationLink(value: CoreAIProjectRoute.project(project.id)) { CoreAIProjectRowView(project: project) } @@ -36,6 +43,7 @@ struct CoreAIProjectLibraryView: View { } } .navigationTitle("Projects") + .searchable(text: $searchText, prompt: "Search projects") .toolbar { ToolbarItem(placement: .primaryAction) { Button( diff --git a/CoreAILab/Features/Projects/CoreAIProjectRowView.swift b/CoreAILab/Features/Projects/CoreAIProjectRowView.swift index b8d90b3..49d7918 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectRowView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectRowView.swift @@ -4,22 +4,39 @@ struct CoreAIProjectRowView: View { let project: LabProject var body: some View { - HStack { - Label(project.name, systemImage: "folder") + Label { + VStack(alignment: .leading) { + Text(project.name) + .font(.headline) - Spacer() + HStack { + Label { + Text("^[\(project.artifactLinks.count) artifact](inflect: true)") + } icon: { + Image(systemName: "shippingbox") + } - VStack(alignment: .trailing) { - Text(project.artifactLinks.count, format: .number) - .monospacedDigit() - Text( - project.storedByteCount, - format: .byteCount(style: .file) - ) + Label { + Text(project.storedByteCount, format: .byteCount(style: .file)) + } icon: { + Image(systemName: "internaldrive") + } + + Label { + Text(project.updatedAt, format: .relative(presentation: .named)) + } icon: { + Image(systemName: "clock") + } + } + .font(.callout) + .foregroundStyle(.secondary) } - .font(.callout) - .foregroundStyle(.secondary) + } icon: { + Image(systemName: "folder.fill") + .font(.title2) + .foregroundStyle(.tint) } + .labelStyle(.titleAndIcon) .accessibilityElement(children: .combine) .accessibilityLabel( "\(project.name), \(project.artifactLinks.count) artifacts, \(project.storedByteCount.formatted(.byteCount(style: .file)))" diff --git a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift index 9c098fe..f21602b 100644 --- a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift +++ b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift @@ -10,15 +10,18 @@ struct CoreAIImportedRecipeBundleView: View { var body: some View { if let summary { - VStack(alignment: .leading, spacing: 8) { - Text(summary.manifest.displayName) + VStack(alignment: .leading) { + Label(summary.manifest.displayName, systemImage: "shippingbox.fill") .font(.headline) LabeledContent("Trust", value: summary.trustState.displayName) LabeledContent( "Bundle SHA-256", - value: String(summary.manifestSHA256.prefix(12)) + value: summary.manifestSHA256 ) .font(.callout.monospaced()) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) LabeledContent( "Code references", value: "\(summary.manifest.codeReferences.count)" @@ -26,10 +29,12 @@ struct CoreAIImportedRecipeBundleView: View { ForEach(summary.manifest.codeReferences) { reference in VStack(alignment: .leading, spacing: 2) { Text(reference.id) - .font(.callout.weight(.medium)) + .font(.callout) + .bold() Text("\(reference.language.rawValue) · \(reference.relativePath) · \(reference.entryPoint)") - .font(.caption.monospaced()) + .font(.footnote.monospaced()) .foregroundStyle(.secondary) + .textSelection(.enabled) } } } @@ -54,7 +59,7 @@ struct CoreAIImportedRecipeBundleView: View { } } else if summary != nil { Text(statusMessage) - .font(.caption) + .font(.footnote) .foregroundStyle(.secondary) } } diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift index 96490d9..e22e841 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift @@ -4,36 +4,76 @@ struct CoreAIRecipeCatalogEntryView: View { let entry: CoreAIRecipeCatalogEntry var body: some View { - VStack(alignment: .leading, spacing: 8) { - Text(entry.displayName) + VStack(alignment: .leading) { + Label(entry.displayName, systemImage: "waveform.badge.microphone") .font(.headline) Text(entry.summary) .foregroundStyle(.secondary) - LabeledContent("Trust", value: entry.trustState.displayName) - LabeledContent( - "Verification", - value: entry.verificationState.displayName - ) - LabeledContent( - "Recipe SHA-256", - value: entry.recipeManifestSHA256 - ) - .font(.callout.monospaced()) - .textSelection(.enabled) + + Divider() + + LabeledContent("Trust") { + Label(entry.trustState.displayName, systemImage: trustSystemImage) + } + LabeledContent("Verification") { + Label( + entry.verificationState.displayName, + systemImage: verificationSystemImage + ) + } + LabeledContent("Recipe SHA-256") { + Text(entry.recipeManifestSHA256) + .font(.callout.monospaced()) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } + Text(entry.verificationNotes) - .font(.caption) + .font(.footnote) .foregroundStyle(.secondary) if let evidenceReference = entry.evidenceReference { - Text("Evidence: \(evidenceReference)") - .font(.caption.monospaced()) - .foregroundStyle(.secondary) + LabeledContent("Evidence") { + Text(evidenceReference) + .font(.callout.monospaced()) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } } if let evidenceSHA256 = entry.evidenceSHA256 { - LabeledContent("Evidence SHA-256", value: evidenceSHA256) - .font(.caption.monospaced()) - .textSelection(.enabled) + LabeledContent("Evidence SHA-256") { + Text(evidenceSHA256) + .font(.callout.monospaced()) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } } } - .padding(.vertical, 4) + } + + private var trustSystemImage: String { + switch entry.trustState { + case .bundledCurated: + "checkmark.seal.fill" + case .publisherReviewed: + "person.badge.shield.checkmark" + case .importedUntrusted: + "exclamationmark.shield" + } + } + + private var verificationSystemImage: String { + switch entry.verificationState { + case .notVerified: + "questionmark.diamond" + case .schemaValidated: + "doc.badge.checkmark" + case .fixturesValidated: + "checkmark.rectangle.stack" + case .hardwareValidated: + "checkmark.circle.fill" + } } } diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift index cdaae67..a84fa39 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift @@ -10,13 +10,17 @@ struct CoreAIRecipeCatalogView: View { let entries = model.entries NavigationStack { - List { + Form { Section { - Text("Trust describes where a recipe came from. Verification describes which checks have evidence. Neither state grants imported code permission to run.") - .foregroundStyle(.secondary) + VStack(alignment: .leading) { + Label("Trust & Verification", systemImage: "checkmark.shield") + .font(.headline) + Text("Trust describes a recipe's source. Verification names the checks backed by evidence. Neither state grants imported code permission to run.") + .foregroundStyle(.secondary) + } } - Section("Curated recipes") { + Section { if let catalogError = model.catalogError { ContentUnavailableView( "Catalog Unavailable", @@ -33,9 +37,11 @@ struct CoreAIRecipeCatalogView: View { CoreAIRecipeCatalogEntryView(entry: entry) } } + } header: { + Label("Curated Recipes", systemImage: "checkmark.seal") } - Section("Imported bundle") { + Section { CoreAIImportedRecipeBundleView( summary: model.importedSummary, codeApprovalState: model.codeApprovalState, @@ -44,8 +50,11 @@ struct CoreAIRecipeCatalogView: View { onApprove: approveReferencedCode, onRevoke: revokeReferencedCode ) + } header: { + Label("Imported Bundle", systemImage: "shippingbox.and.arrow.backward") } } + .formStyle(.grouped) .navigationTitle("Recipes") .toolbar { ToolbarItem(placement: .primaryAction) { From ecad8ba2a913fa5bd07da1c4f2f30babff318341 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:14:25 +0530 Subject: [PATCH 04/28] Clarify conversion and recipe authoring flows --- .../Conversion/CoreAIConversionLogView.swift | 14 +++--- .../CoreAIConversionSetupView.swift | 16 +++++-- .../CoreAIConversionWorkspaceView.swift | 1 - .../CoreAIRecipeSourceEditorView.swift | 14 ++++-- .../CoreAIRecipeStudioPanel.swift | 25 ++++++++++ .../CoreAIRecipeStudioPanelLink.swift | 2 + .../RecipeStudio/CoreAIRecipeStudioView.swift | 46 +++++++++++++++++-- 7 files changed, 99 insertions(+), 19 deletions(-) diff --git a/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift b/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift index 9eee8fe..10994b6 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift @@ -9,12 +9,14 @@ struct CoreAIConversionLogView: View { ScrollView { LazyVStack(alignment: .leading, spacing: 3) { if entries.isEmpty { - Label( - "Converter output will appear here", - systemImage: "text.alignleft" + ContentUnavailableView( + "No Converter Output", + systemImage: "text.alignleft", + description: Text( + "Start a conversion to stream the original process output here." + ) ) - .foregroundStyle(.secondary) - .padding() + .frame(maxWidth: .infinity, minHeight: 200) } else { ForEach(entries) { entry in Text(entry.message) @@ -27,7 +29,7 @@ struct CoreAIConversionLogView: View { .padding() .textSelection(.enabled) } - .background(.black.opacity(0.04)) + .background(.secondary.opacity(0.08)) .onChange(of: entries.count) { guard let lastID = entries.last?.id else { return } proxy.scrollTo(lastID, anchor: .bottom) diff --git a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift index 69bc4fa..c62a42e 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift @@ -9,7 +9,7 @@ struct CoreAIConversionSetupView: View { var body: some View { Form { - Section("Recipe") { + Section { Picker("Model", selection: $workspace.selectedModelID) { ForEach(workspace.groups) { group in Section(group.category.rawValue) { @@ -33,10 +33,12 @@ struct CoreAIConversionSetupView: View { } } } + } header: { + Label("Recipe", systemImage: "shippingbox") } .disabled(configurationIsLocked) - Section("Workspace") { + Section { CoreAIConversionPathRow( title: "Apple repository", url: workspace.repositoryURL, @@ -60,20 +62,24 @@ struct CoreAIConversionSetupView: View { actionTitle: "Choose uv Executable", action: chooseUVExecutable ) + } header: { + Label("Workspace", systemImage: "folder") } .disabled(configurationIsLocked) - Section("Options") { + Section { Toggle( "Overwrite matching artifacts", isOn: $workspace.overwriteExistingArtifacts ) Text("Source weights remain in the upstream cache. Core AI Lab does not redistribute or relicense them.") .foregroundStyle(.secondary) + } header: { + Label("Options", systemImage: "switch.2") } .disabled(configurationIsLocked) - Section("Environment") { + Section { if let report = workspace.environmentReport { ForEach(report.checks) { check in CoreAIConversionEnvironmentCheckView(check: check) @@ -89,6 +95,8 @@ struct CoreAIConversionSetupView: View { action: checkEnvironment ) .disabled(workspace.phase.isBusy) + } header: { + Label("Environment", systemImage: "checkmark.shield") } Section { diff --git a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift index 1b08182..7f35fd8 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift @@ -65,7 +65,6 @@ struct CoreAIConversionWorkspaceView: View { handleUVSelection(result) } .alert("Conversion Error", isPresented: $workspace.isShowingError) { - Button("OK", role: .cancel) {} } message: { Text(workspace.errorMessage ?? "The conversion could not be completed.") } diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift index 533b611..7c3fdf0 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift @@ -5,10 +5,12 @@ struct CoreAIRecipeSourceEditorView: View { var body: some View { Form { - Section("Recipe") { + Section { TextField("Display name", text: $workspace.recipe.displayName) TextField("Recipe ID", text: $workspace.recipe.id) .coreAIRecipeIdentifierInput() + } header: { + Label("Recipe", systemImage: "doc.text") } Section { @@ -22,12 +24,12 @@ struct CoreAIRecipeSourceEditorView: View { TextField("Pinned revision", text: $workspace.recipe.source.revision) .coreAIRecipeIdentifierInput() } header: { - Text("PyTorch Source") + Label("PyTorch Source", systemImage: "shippingbox") } footer: { Text("A blank revision is allowed while drafting, but a reproducible conversion should pin one before execution.") } - Section("Module") { + Section { TextField("Python module path", text: $workspace.recipe.module.modulePath) .coreAIRecipeIdentifierInput() TextField("Module type", text: $workspace.recipe.module.typeName) @@ -36,10 +38,14 @@ struct CoreAIRecipeSourceEditorView: View { .coreAIRecipeIdentifierInput() TextField("Checkpoint path", text: $workspace.recipe.module.checkpointPath) .coreAIRecipeIdentifierInput() + } header: { + Label("Module", systemImage: "cube") } - Section("Validation") { + Section { CoreAIRecipeValidationIssuesView(issues: workspace.validationIssues) + } header: { + Label("Validation", systemImage: "checkmark.shield") } } .formStyle(.grouped) diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanel.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanel.swift index 9013e51..1a3de36 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanel.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanel.swift @@ -63,6 +63,31 @@ enum CoreAIRecipeStudioPanel: String, CaseIterable, Hashable, Identifiable { "point.3.connected.trianglepath.dotted" } } + + var summary: String { + switch self { + case .source: + "Define the recipe identity, source revision, and Python module." + case .exampleInputs: + "Describe deterministic inputs for conversion and validation." + case .dynamicDimensions: + "Name the dimensions that may vary at runtime." + case .state: + "Describe mutable tensors carried between function calls." + case .externalization: + "Choose resources that live outside the compiled model asset." + case .functions: + "Define callable entry points and their typed inputs and outputs." + case .diagnostics: + "Review source operators the Core AI converter cannot lower." + case .rewrites: + "Inspect the built-in catalog of supported graph rewrites." + case .generatedArtifacts: + "Review generated stubs without executing authored code." + case .pipeline: + "Connect typed assets into a validated pipeline contract." + } + } } extension View { diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanelLink.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanelLink.swift index 4b9d928..97043a9 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanelLink.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioPanelLink.swift @@ -7,5 +7,7 @@ struct CoreAIRecipeStudioPanelLink: View { NavigationLink(value: panel) { Label(panel.title, systemImage: panel.systemImage) } + .help(panel.summary) + .accessibilityHint(panel.summary) } } diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift index 0ec0ab4..9ada284 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift @@ -2,7 +2,8 @@ import SwiftUI struct CoreAIRecipeStudioView: View { @State private var workspace: CoreAIRecipeStudioWorkspaceModel - @State private var selection: CoreAIRecipeStudioPanel? = .source + @SceneStorage("CoreAILab.recipeStudio.selectedPanel") + private var selection: CoreAIRecipeStudioPanel? init(recipe: CoreAIRecipeAuthoringManifest = .starter) { _workspace = State(initialValue: CoreAIRecipeStudioWorkspaceModel(recipe: recipe)) @@ -11,6 +12,19 @@ struct CoreAIRecipeStudioView: View { var body: some View { NavigationSplitView { List(selection: $selection) { + Section { + VStack(alignment: .leading) { + Text(workspace.recipe.displayName) + .font(.headline) + .lineLimit(2) + + Label(validationTitle, systemImage: validationSystemImage) + .font(.callout) + .foregroundStyle(validationStyle) + } + .accessibilityElement(children: .combine) + } + Section("Authoring") { CoreAIRecipeStudioPanelLink(panel: .source) CoreAIRecipeStudioPanelLink(panel: .exampleInputs) @@ -31,12 +45,13 @@ struct CoreAIRecipeStudioView: View { } Section("Draft Status") { - LabeledContent("Validation issues") { - Text(workspace.validationIssues.count, format: .number) - } + Label(validationTitle, systemImage: validationSystemImage) + .foregroundStyle(validationStyle) } } + .listStyle(.sidebar) .navigationTitle("Recipe Studio") + .navigationSplitViewColumnWidth(min: 190, ideal: 220, max: 260) } detail: { switch selection ?? .source { case .source: @@ -63,6 +78,29 @@ struct CoreAIRecipeStudioView: View { } .navigationSplitViewStyle(.balanced) } + + private var validationTitle: String { + let count = workspace.validationIssues.count + if count == 0 { + return "Structurally valid" + } else if count == 1 { + return "1 validation issue" + } else { + return "\(count) validation issues" + } + } + + private var validationSystemImage: String { + workspace.validationIssues.isEmpty + ? "checkmark.circle.fill" + : "exclamationmark.triangle.fill" + } + + private var validationStyle: AnyShapeStyle { + workspace.validationIssues.isEmpty + ? AnyShapeStyle(.green) + : AnyShapeStyle(.orange) + } } #Preview { From 00fcc5651e464a027fec2b96167730a037df8e73 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:18:16 +0530 Subject: [PATCH 05/28] Polish local model run experiences --- .../ChatterboxGenerationSection.swift | 25 +++-- .../Chatterbox/ChatterboxHeroView.swift | 2 +- .../Chatterbox/ChatterboxModelSection.swift | 4 +- .../ChatterboxPipelineSection.swift | 10 +- .../Chatterbox/ChatterboxPromptSection.swift | 6 +- .../SpeakerDiarizationAnalysisSection.swift | 94 ++++++++----------- .../SpeakerDiarizationImportControls.swift | 7 +- .../SpeakerDiarizationImportSection.swift | 14 ++- .../SpeakerDiarizationStatusSection.swift | 16 +++- .../SpeakerDiarizationTurnRow.swift | 9 +- .../SpeakerDiarizationWorkspaceView.swift | 66 ++++++------- .../CoreAIRuntimeExperienceRow.swift | 17 +++- .../CoreAIRuntimeExperienceSectionView.swift | 23 ++++- .../CoreAIRuntimeRecentRunsView.swift | 4 +- .../CoreAIRuntimeRecordingControlsView.swift | 31 ++++-- .../SpeakerDiarizationLabTests.swift | 12 --- 16 files changed, 199 insertions(+), 141 deletions(-) diff --git a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift index c3e0262..5acf8fa 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift @@ -11,16 +11,25 @@ struct ChatterboxGenerationSection: View { var body: some View { Section { - Button( - "Generate speech", - systemImage: "play.circle.fill", - action: generateAction - ) + Button(action: generateAction) { + Label { + Text(isWorking ? "Generating Speech…" : "Generate Speech") + } icon: { + if isWorking { + ProgressView() + .controlSize(.small) + } else { + Image(systemName: "play.circle.fill") + } + } + } .buttonStyle(.borderedProminent) .disabled(!canGenerate) if isWorking { - ProgressView(statusMessage) + Text(statusMessage) + .foregroundStyle(.secondary) + .accessibilityAddTraits(.updatesFrequently) } if let result { @@ -55,9 +64,11 @@ struct ChatterboxGenerationSection: View { item: result.audioURL, preview: SharePreview("Chatterbox Core AI audio") ) { - Label("Share generated audio", systemImage: "square.and.arrow.up") + Label("Share Generated Audio", systemImage: "square.and.arrow.up") } } + } header: { + Label("Generate & Playback", systemImage: "speaker.wave.3") } footer: { Text("The first launch specializes the bundled graphs. Later runs reuse Core AI's persistent cache.") } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxHeroView.swift b/CoreAILab/Features/Chatterbox/ChatterboxHeroView.swift index 66b1f88..8383033 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxHeroView.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxHeroView.swift @@ -20,7 +20,7 @@ struct ChatterboxHeroView: View { .foregroundStyle(.secondary) Label(targetDescription, systemImage: "laptopcomputer") - .font(.caption) + .font(.callout) .foregroundStyle(.secondary) } .padding(.vertical, 6) diff --git a/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift index 5e4eab3..554fd2a 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift @@ -4,7 +4,7 @@ struct ChatterboxModelSection: View { let state: ChatterboxModelState var body: some View { - Section("Bundled Core AI Model") { + Section { Label(state.title, systemImage: state.systemImage) Text(state.detail) @@ -21,6 +21,8 @@ struct ChatterboxModelSection: View { LabeledContent("Author", value: inspection.author) } } + } header: { + Label("Bundled Core AI Model", systemImage: "shippingbox") } } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift index d46aa74..4b09443 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift @@ -11,7 +11,7 @@ struct ChatterboxPipelineSection: View { } var body: some View { - Section("Native Pipeline") { + Section { ForEach(inspection?.assets ?? []) { asset in HStack(alignment: .firstTextBaseline) { Image(systemName: isReady(asset.stage) @@ -23,14 +23,14 @@ struct ChatterboxPipelineSection: View { VStack(alignment: .leading, spacing: 2) { Text(asset.displayName) Text(asset.detail) - .font(.caption) + .font(.footnote) .foregroundStyle(.secondary) } Spacer() Text(asset.formattedSize) - .font(.caption.monospacedDigit()) + .font(.callout.monospacedDigit()) .foregroundStyle(.secondary) } .accessibilityElement(children: .combine) @@ -50,8 +50,10 @@ struct ChatterboxPipelineSection: View { } Text(detail) - .font(.caption) + .font(.footnote) .foregroundStyle(.secondary) + } header: { + Label("Native Pipeline", systemImage: "point.3.connected.trianglepath.dotted") } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift index d899095..34ac7c3 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift @@ -4,13 +4,15 @@ struct ChatterboxPromptSection: View { @Binding var prompt: String var body: some View { - Section("Speech") { + Section { TextField("What should Chatterbox say?", text: $prompt, axis: .vertical) .lineLimit(4...) Text("Expressive tags such as [laugh], [chuckle], [sigh], and [gasp] stay in the text. One generation supports about 10 seconds of speech.") - .font(.caption) + .font(.footnote) .foregroundStyle(.secondary) + } header: { + Label("Speech", systemImage: "text.quote") } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationAnalysisSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationAnalysisSection.swift index 448a50a..4992f35 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationAnalysisSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationAnalysisSection.swift @@ -1,73 +1,57 @@ import SwiftUI -enum SpeakerDiarizationAnalysisLayout: Equatable { - case stacked - case sideBySide - - static let minimumSideBySideWidth: CGFloat = 880 - - init(contentWidth: CGFloat) { - self = contentWidth >= Self.minimumSideBySideWidth ? .sideBySide : .stacked - } -} - struct SpeakerDiarizationAnalysisSection: View { private static let maximumContentWidth: CGFloat = 1_120 private static let horizontalMargin: CGFloat = 64 - let availableWidth: CGFloat let waveform: SpeakerDiarizationWaveform? let result: SpeakerDiarizationResult? let playheadTime: Double let activeTurnID: Int? - private var contentWidth: CGFloat { - min( - max(availableWidth - Self.horizontalMargin, 0), - Self.maximumContentWidth - ) - } - - private var layout: SpeakerDiarizationAnalysisLayout { - SpeakerDiarizationAnalysisLayout(contentWidth: contentWidth) - } - var body: some View { Section { - Group { - if layout == .sideBySide { - HStack(alignment: .top, spacing: 20) { - SpeakerDiarizationTimelineView( - waveform: waveform, - result: result, - playheadTime: playheadTime - ) - .frame(maxWidth: .infinity, alignment: .topLeading) - Divider() - SpeakerDiarizationResultsView( - result: result, - activeTurnID: activeTurnID - ) - .frame(maxWidth: .infinity, alignment: .topLeading) - } - } else { - VStack(alignment: .leading, spacing: 20) { - SpeakerDiarizationTimelineView( - waveform: waveform, - result: result, - playheadTime: playheadTime - ) - .frame(maxWidth: .infinity, alignment: .topLeading) - Divider() - SpeakerDiarizationResultsView( - result: result, - activeTurnID: activeTurnID - ) - .frame(maxWidth: .infinity, alignment: .topLeading) - } + ViewThatFits(in: .horizontal) { + HStack(alignment: .top) { + SpeakerDiarizationTimelineView( + waveform: waveform, + result: result, + playheadTime: playheadTime + ) + .frame(minWidth: 360, maxWidth: .infinity, alignment: .topLeading) + + Divider() + + SpeakerDiarizationResultsView( + result: result, + activeTurnID: activeTurnID + ) + .frame(minWidth: 360, maxWidth: .infinity, alignment: .topLeading) } + + VStack(alignment: .leading) { + SpeakerDiarizationTimelineView( + waveform: waveform, + result: result, + playheadTime: playheadTime + ) + .frame(maxWidth: .infinity, alignment: .topLeading) + + Divider() + + SpeakerDiarizationResultsView( + result: result, + activeTurnID: activeTurnID + ) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + } + .containerRelativeFrame(.horizontal) { length, _ in + min( + max(length - Self.horizontalMargin, 0), + Self.maximumContentWidth + ) } - .frame(width: contentWidth, alignment: .topLeading) } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift index f8f10ce..dd7541a 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift @@ -3,7 +3,8 @@ import SwiftUI struct SpeakerDiarizationImportControls: View { let layout: ControlsLayout let canRunDiarization: Bool - let isBusy: Bool + let canImportModel: Bool + let canImportMedia: Bool let importModelAction: () -> Void let importMediaAction: () -> Void let runAction: () -> Void @@ -11,9 +12,9 @@ struct SpeakerDiarizationImportControls: View { var body: some View { layout { Button("Choose CAM++", systemImage: "shippingbox", action: importModelAction) - .disabled(isBusy) + .disabled(!canImportModel) Button("Choose Audio or Video", systemImage: "waveform", action: importMediaAction) - .disabled(isBusy) + .disabled(!canImportMedia) Button("Run Diarization", systemImage: "person.2.wave.2", action: runAction) .buttonStyle(.borderedProminent) .disabled(!canRunDiarization) diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift index 993d037..78591a3 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift @@ -2,18 +2,20 @@ import SwiftUI struct SpeakerDiarizationImportSection: View { let canRunDiarization: Bool - let isBusy: Bool + let canImportModel: Bool + let canImportMedia: Bool let importModelAction: () -> Void let importMediaAction: () -> Void let runAction: () -> Void var body: some View { - Section("Inputs") { + Section { ViewThatFits(in: .horizontal) { SpeakerDiarizationImportControls( layout: HStackLayout(), canRunDiarization: canRunDiarization, - isBusy: isBusy, + canImportModel: canImportModel, + canImportMedia: canImportMedia, importModelAction: importModelAction, importMediaAction: importMediaAction, runAction: runAction @@ -21,7 +23,8 @@ struct SpeakerDiarizationImportSection: View { SpeakerDiarizationImportControls( layout: VStackLayout(alignment: .leading), canRunDiarization: canRunDiarization, - isBusy: isBusy, + canImportModel: canImportModel, + canImportMedia: canImportMedia, importModelAction: importModelAction, importMediaAction: importMediaAction, runAction: runAction @@ -29,7 +32,10 @@ struct SpeakerDiarizationImportSection: View { } Text("The bundled Apache-2.0 CAM++ model runs through Core AI after 16 kHz decode, energy segmentation, and six-second feature preparation; cosine clustering produces anonymous speaker turns.") + .font(.footnote) .foregroundStyle(.secondary) + } header: { + Label("Inputs", systemImage: "waveform.and.mic") } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift index 987ef1b..7fd077b 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift @@ -31,8 +31,20 @@ struct SpeakerDiarizationStatusSection: View { ) } } - Label(statusMessage, systemImage: isBusy ? "hourglass" : "waveform.badge.mic") - .foregroundStyle(isBusy ? .primary : .secondary) + HStack { + if isBusy { + ProgressView() + .controlSize(.small) + .accessibilityHidden(true) + } else { + Image(systemName: "waveform.badge.mic") + .accessibilityHidden(true) + } + Text(statusMessage) + .foregroundStyle(isBusy ? .primary : .secondary) + } + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.updatesFrequently) } header: { Label("Speaker Diarization Lab", systemImage: "person.wave.2") } footer: { diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift index 0ee3482..d4eff49 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift @@ -20,11 +20,10 @@ struct SpeakerDiarizationTurnRow: View { systemImage: isActive ? "speaker.wave.3.fill" : "person.wave.2" ) if isActive { - Text("Now") - .font(.caption.weight(.semibold)) - .padding(.horizontal, 8) - .padding(.vertical, 3) - .background(.orange.opacity(0.18), in: .capsule) + Label("Now", systemImage: "play.fill") + .font(.footnote) + .bold() + .foregroundStyle(.orange) } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift index 6940e12..c2d92db 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift @@ -11,43 +11,43 @@ struct SpeakerDiarizationWorkspaceView: View { let activeTurn = workspace.result?.turn(at: watcher.currentTime) NavigationStack { - GeometryReader { geometry in - Form { - SpeakerDiarizationStatusSection( - modelInfo: workspace.modelInfo, - summary: workspace.mediaSummary, - statusMessage: workspace.statusMessage, - isBusy: workspace.isBusy - ) + Form { + SpeakerDiarizationStatusSection( + modelInfo: workspace.modelInfo, + summary: workspace.mediaSummary, + statusMessage: workspace.statusMessage, + isBusy: workspace.isBusy + ) - SpeakerDiarizationImportSection( - canRunDiarization: workspace.canRunDiarization, - isBusy: workspace.isBusy, - importModelAction: importModel, - importMediaAction: importMedia, - runAction: workspace.startDiarization - ) + SpeakerDiarizationImportSection( + canRunDiarization: workspace.canRunDiarization, + canImportModel: !workspace.isLoadingModel + && !workspace.isRunningDiarization, + canImportMedia: !workspace.isAnalyzingMedia + && !workspace.isRunningDiarization, + importModelAction: importModel, + importMediaAction: importMedia, + runAction: workspace.startDiarization + ) - SpeakerDiarizationWatcherSection( - summary: workspace.mediaSummary, - player: watcher.player, - currentTime: watcher.currentTime, - activeTurn: activeTurn, - isPlaying: watcher.isPlaying, - togglePlayback: watcher.togglePlayback, - restart: watcher.restart - ) + SpeakerDiarizationWatcherSection( + summary: workspace.mediaSummary, + player: watcher.player, + currentTime: watcher.currentTime, + activeTurn: activeTurn, + isPlaying: watcher.isPlaying, + togglePlayback: watcher.togglePlayback, + restart: watcher.restart + ) - SpeakerDiarizationAnalysisSection( - availableWidth: geometry.size.width, - waveform: workspace.waveform, - result: workspace.result, - playheadTime: watcher.currentTime, - activeTurnID: activeTurn?.id - ) - } - .formStyle(.grouped) + SpeakerDiarizationAnalysisSection( + waveform: workspace.waveform, + result: workspace.result, + playheadTime: watcher.currentTime, + activeTurnID: activeTurn?.id + ) } + .formStyle(.grouped) .navigationTitle("Diarization") .task { await workspace.prepareBundledModel() diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift index 4564745..05f600c 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift @@ -13,24 +13,35 @@ struct CoreAIRuntimeExperienceRow: View { Text(mapping.experience.summary) .foregroundStyle(.secondary) + .lineLimit(2) - Text(capabilitySummary) + Label(capabilitySummary, systemImage: "checklist") .font(.callout) .foregroundStyle(.secondary) + .lineLimit(2) - Label(platformSummary, systemImage: "desktopcomputer.and.iphone") + Label(platformSummary, systemImage: platformSystemImage) .font(.callout) .foregroundStyle(.secondary) } + .padding(.vertical, 4) .accessibilityElement(children: .combine) .accessibilityHint("Opens the local runtime experience") } private var capabilitySummary: String { - mapping.experience.capabilities.map(\.title).joined(separator: ", ") + mapping.experience.capabilities.map(\.title).joined(separator: " · ") } private var platformSummary: String { mapping.experience.platforms.map(\.rawValue).joined(separator: " · ") } + + private var platformSystemImage: String { + mapping.experience.platforms.count > 1 + ? "desktopcomputer.and.iphone" + : mapping.experience.platforms.first == .iOS + ? "iphone" + : "desktopcomputer" + } } diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceSectionView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceSectionView.swift index e436e21..ce471d5 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceSectionView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceSectionView.swift @@ -5,7 +5,7 @@ struct CoreAIRuntimeExperienceSectionView: View { let mappings: [CoreAIRecipeExperienceMapping] var body: some View { - Section(workload.title) { + Section { ForEach(mappings) { mapping in NavigationLink( value: CoreAIRuntimeExperienceRoute( @@ -15,6 +15,27 @@ struct CoreAIRuntimeExperienceSectionView: View { CoreAIRuntimeExperienceRow(mapping: mapping) } } + } header: { + Label(workload.title, systemImage: workloadSystemImage) + } + } + + private var workloadSystemImage: String { + switch workload { + case .audioTranscription: + "waveform" + case .embedding: + "point.3.connected.trianglepath.dotted" + case .genericFunction: + "function" + case .imageGeneration: + "photo" + case .objectDetection: + "viewfinder" + case .segmentation: + "square.3.layers.3d" + case .textGeneration: + "text.bubble" } } } diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecentRunsView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecentRunsView.swift index cb9d628..816e8f3 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecentRunsView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecentRunsView.swift @@ -5,7 +5,7 @@ struct CoreAIRuntimeRecentRunsView: View { var body: some View { if !coordinator.history.isEmpty { - Section("Recent Runtime Runs") { + Section { ForEach(coordinator.history.prefix(8)) { run in VStack(alignment: .leading) { Label( @@ -26,6 +26,8 @@ struct CoreAIRuntimeRecentRunsView: View { } .accessibilityElement(children: .combine) } + } header: { + Label("Recent Runtime Runs", systemImage: "clock.arrow.trianglehead.counterclockwise.rotate.90") } } } diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecordingControlsView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecordingControlsView.swift index b9d79af..1192f19 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecordingControlsView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeRecordingControlsView.swift @@ -6,7 +6,7 @@ struct CoreAIRuntimeRecordingControlsView: View { @Bindable var coordinator: CoreAIRunLifecycleCoordinator var body: some View { - Section("Run Recording") { + Section { Picker("Record in project", selection: $selectedProjectID) { Text("Off") .tag(nil as UUID?) @@ -29,17 +29,32 @@ struct CoreAIRuntimeRecordingControlsView: View { ForEach(coordinator.comparisonOptions) { identity in Text(identity.displayName) .tag(identity as CoreAIRuntimeComparisonIdentity?) - } + } } - Text("Attempts remain cold until one run succeeds for the imported model in this Runtime Studio session; later runs with the same experience and model identity are warm.") - .foregroundStyle(.secondary) + DisclosureGroup { + VStack(alignment: .leading) { + Label("Cold and Warm Timing", systemImage: "thermometer.variable") + .bold() + Text("Attempts remain cold until one run succeeds for the imported model in this Runtime Studio session. Later runs with the same experience and model identity are warm.") - Text("A comparison identity records the intended comparator only; this slice does not claim that outputs were compared.") - .foregroundStyle(.secondary) + Divider() + + Label("Comparison Identity", systemImage: "arrow.left.arrow.right") + .bold() + Text("A comparison identity records the intended comparator only. It does not claim that outputs were compared.") - Text("A registry recipe is an import intent. Runtime Studio checks the imported model family, but records unverified_intent and does not link a project recipe revision without artifact-bound provenance proof.") + Divider() + + Label("Recipe Provenance", systemImage: "checkmark.seal") + .bold() + Text("A registry recipe is an import intent. Runtime Studio records unverified_intent until artifact-bound provenance proves the project recipe revision.") + } + .font(.footnote) .foregroundStyle(.secondary) + } label: { + Label("How Run Evidence Works", systemImage: "info.circle") + } if let persistenceMessage = coordinator.persistenceMessage { Label(persistenceMessage, systemImage: "exclamationmark.triangle") @@ -53,6 +68,8 @@ struct CoreAIRuntimeRecordingControlsView: View { action: coordinator.retryPendingPersistence ) } + } header: { + Label("Run Recording", systemImage: "record.circle") } } } diff --git a/CoreAILabTests/SpeakerDiarizationLabTests.swift b/CoreAILabTests/SpeakerDiarizationLabTests.swift index 0754461..b856949 100644 --- a/CoreAILabTests/SpeakerDiarizationLabTests.swift +++ b/CoreAILabTests/SpeakerDiarizationLabTests.swift @@ -66,18 +66,6 @@ struct SpeakerDiarizationLabTests { #expect(info.scalarTypeName == "float16") } - @Test - func analysisLayoutUsesAContentDrivenBreakpoint() { - let breakpoint = SpeakerDiarizationAnalysisLayout.minimumSideBySideWidth - - #expect( - SpeakerDiarizationAnalysisLayout(contentWidth: breakpoint - 1) == .stacked - ) - #expect( - SpeakerDiarizationAnalysisLayout(contentWidth: breakpoint) == .sideBySide - ) - } - @Test func energySegmentationFindsSeparatedSpeechAndRejectsSilence() { let sampleRate = 1_000 From 44b699a0cde6d6f2c120ec3baf530a1821b0ee9c Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:20:54 +0530 Subject: [PATCH 06/28] Refine inspection and device evidence views --- .../CoreAIAssetInspectorView.swift | 1 + .../CoreAIAssetReportView.swift | 36 ++++++++++++++---- .../CoreAISpecializationControlsView.swift | 23 +++++++++-- .../CoreAIDeviceDiagnosticsView.swift | 12 +++--- .../DeviceLab/CoreAIDeviceEvidenceView.swift | 38 +++++++++++++++---- .../DeviceLab/CoreAIDeviceLabView.swift | 7 ++-- .../CoreAIDeviceStoragePlanView.swift | 4 +- .../CoreAIDeviceTargetAuthoringView.swift | 4 +- 8 files changed, 95 insertions(+), 30 deletions(-) diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift index 9eb218f..2d5c171 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift @@ -51,6 +51,7 @@ struct CoreAIAssetInspectorView: View { ToolbarItem(placement: .primaryAction) { Button("Open Model", systemImage: "folder", action: openModelPicker) .disabled(workspace.phase.isBusy) + .keyboardShortcut("o", modifiers: .command) } } .fileImporter( diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift index 092345f..2c4a98d 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift @@ -6,19 +6,28 @@ struct CoreAIAssetReportView: View { let allowsCacheRemoval: Bool var body: some View { - List { - Section("Asset") { + Form { + Section { LabeledContent("Name", value: report.url.lastPathComponent) - LabeledContent("Valid Core AI asset", value: report.isValid ? "Yes" : "No") + LabeledContent("Core AI asset") { + Label( + report.isValid ? "Valid" : "Invalid", + systemImage: report.isValid + ? "checkmark.circle.fill" + : "xmark.circle.fill" + ) + } LabeledContent("Author", value: valueOrFallback(report.author)) LabeledContent("License", value: valueOrFallback(report.license)) if !report.description.isEmpty { Text(report.description) .foregroundStyle(.secondary) } + } header: { + Label("Asset", systemImage: "shippingbox") } - Section("Functions") { + Section { if report.functions.isEmpty { Text("No functions were declared in the asset summary.") .foregroundStyle(.secondary) @@ -43,9 +52,11 @@ struct CoreAIAssetReportView: View { } } } + } header: { + Label("Functions", systemImage: "function") } - Section("Compute Types") { + Section { if report.computeTypes.isEmpty { Text("No compute types were reported.") .foregroundStyle(.secondary) @@ -54,9 +65,11 @@ struct CoreAIAssetReportView: View { Text(computeType) } } + } header: { + Label("Compute Types", systemImage: "cpu") } - Section("Storage Types") { + Section { if report.storageTypes.isEmpty { Text("No storage statistics were reported.") .foregroundStyle(.secondary) @@ -69,9 +82,11 @@ struct CoreAIAssetReportView: View { ) } } + } header: { + Label("Storage Types", systemImage: "internaldrive") } - Section("Operation Distribution") { + Section { if report.operationDistribution.isEmpty { Text("No operation statistics were reported.") .foregroundStyle(.secondary) @@ -84,12 +99,16 @@ struct CoreAIAssetReportView: View { ) } } + } header: { + Label("Operation Distribution", systemImage: "chart.bar.xaxis") } - Section("Source") { + Section { Text(report.url.path) .font(.callout.monospaced()) .textSelection(.enabled) + } header: { + Label("Source", systemImage: "folder") } CoreAISpecializationControlsView( @@ -97,6 +116,7 @@ struct CoreAIAssetReportView: View { allowsCacheRemoval: allowsCacheRemoval ) } + .formStyle(.grouped) } private func valueOrFallback(_ value: String) -> String { diff --git a/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift b/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift index f2e302f..4b4dbce 100644 --- a/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift @@ -6,7 +6,7 @@ struct CoreAISpecializationControlsView: View { var allowsCacheRemoval = true var body: some View { - Section("Specialization & Cache") { + Section { Picker("Compute profile", selection: $workspace.selectedProfile) { ForEach(CoreAISpecializationProfile.allCases) { profile in Text(profile.title) @@ -96,8 +96,8 @@ struct CoreAISpecializationControlsView: View { .disabled(workspace.phase.isBusy || isInteractionDisabled) if workspace.phase.isBusy { - Label("Core AI operation in progress", systemImage: "hourglass") - .foregroundStyle(.secondary) + ProgressView(operationTitle) + .accessibilityAddTraits(.updatesFrequently) } Text("Core AI exposes hit/miss and deletion for known assets, but not cache paths, entry sizes, or a complete inventory.") @@ -111,6 +111,23 @@ struct CoreAISpecializationControlsView: View { .font(.subheadline) .foregroundStyle(.secondary) } + } header: { + Label("Specialization & Cache", systemImage: "cpu") + } + } + + private var operationTitle: String { + switch workspace.phase { + case .inspecting: + "Inspecting model…" + case .checkingCache: + "Checking specialization cache…" + case .specializing: + "Specializing model…" + case .removingCache: + "Deleting cached specialization…" + case .idle, .ready: + "Updating Core AI state…" } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceDiagnosticsView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceDiagnosticsView.swift index e742f3d..5fa6ad9 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceDiagnosticsView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceDiagnosticsView.swift @@ -4,16 +4,16 @@ struct CoreAIDeviceDiagnosticsView: View { let diagnostics: [CoreAIDeviceDiagnostic] var body: some View { - Section("Authoring and Compatibility") { + Section { ForEach(diagnostics) { diagnostic in - LabeledContent { - Text(diagnostic.detail) - .foregroundStyle(.secondary) - } label: { + VStack(alignment: .leading) { Label( diagnostic.title, systemImage: diagnostic.severity.systemImage ) + Text(diagnostic.detail) + .font(.footnote) + .foregroundStyle(.secondary) } .accessibilityElement(children: .ignore) .accessibilityLabel( @@ -21,6 +21,8 @@ struct CoreAIDeviceDiagnosticsView: View { ) .accessibilityValue(diagnostic.detail) } + } header: { + Label("Compatibility Checks", systemImage: "checkmark.shield") } } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift index ae941ce..59076e9 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift @@ -5,7 +5,7 @@ struct CoreAIDeviceEvidenceView: View { @Binding var isImportingEvidence: Bool var body: some View { - Section("Physical Evidence") { + Section { Button( "Import Runner Evidence", systemImage: "square.and.arrow.down", @@ -19,7 +19,7 @@ struct CoreAIDeviceEvidenceView: View { if let error = workspace.importErrorMessage { Label(error, systemImage: "xmark.octagon") - .foregroundStyle(.secondary) + .foregroundStyle(.red) } if let evidence = workspace.importedEvidence { @@ -31,17 +31,33 @@ struct CoreAIDeviceEvidenceView: View { : evidence.device.modelIdentifier ) LabeledContent("iOS", value: evidence.device.operatingSystemVersion) - LabeledContent("Artifact", value: evidence.artifact.identifier) - LabeledContent("Configuration", value: evidence.configuration.identifier) + LabeledContent("Artifact") { + Text(evidence.artifact.identifier) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } + LabeledContent("Configuration") { + Text(evidence.configuration.identifier) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } LabeledContent( "Specialization", - value: evidence.specialization.status.rawValue + value: displayName(evidence.specialization.status.rawValue) + ) + LabeledContent( + "Inference", + value: displayName(evidence.inference.status.rawValue) + ) + LabeledContent( + "Energy", + value: displayName(evidence.energy.availability.rawValue) ) - LabeledContent("Inference", value: evidence.inference.status.rawValue) - LabeledContent("Energy", value: evidence.energy.availability.rawValue) LabeledContent( "Execution placement", - value: evidence.placement.availability.rawValue + value: displayName(evidence.placement.availability.rawValue) ) Text( "Artifact and configuration SHA-256 identities are retained in the imported JSON." @@ -57,10 +73,16 @@ struct CoreAIDeviceEvidenceView: View { ) ) } + } header: { + Label("Physical Evidence", systemImage: "doc.text.magnifyingglass") } } private func beginImport() { isImportingEvidence = true } + + private func displayName(_ rawValue: String) -> String { + rawValue.replacing("_", with: " ").capitalized + } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift index 6d0d547..792dfdb 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift @@ -8,14 +8,13 @@ struct CoreAIDeviceLabView: View { var body: some View { Form { Section { - LabeledContent { + VStack(alignment: .leading) { + Label("Physical Device Planning", systemImage: "iphone.gen3") + .font(.headline) Text( "Author an iPhone target, plan asset delivery, and import evidence from the physical runner. Preferences remain separate from measured execution placement." ) .foregroundStyle(.secondary) - } label: { - Label("Physical Device Planning", systemImage: "iphone.gen3") - .font(.headline) } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift index adf94d4..26feb05 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift @@ -4,7 +4,7 @@ struct CoreAIDeviceStoragePlanView: View { @Bindable var workspace: CoreAIDeviceLabWorkspaceModel var body: some View { - Section("Asset Delivery") { + Section { Picker("Model delivery", selection: $workspace.modelDeliveryMode) { ForEach(CoreAIAssetDeliveryMode.allCases, id: \.self) { mode in Text(mode.title).tag(mode) @@ -77,6 +77,8 @@ struct CoreAIDeviceStoragePlanView: View { } } } + } header: { + Label("Asset Delivery", systemImage: "shippingbox") } } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift index 1e8d2bb..d2d652f 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift @@ -4,7 +4,7 @@ struct CoreAIDeviceTargetAuthoringView: View { @Bindable var workspace: CoreAIDeviceLabWorkspaceModel var body: some View { - Section("iPhone Target") { + Section { Picker("Compute preference", selection: $workspace.preferredComputeUnit) { ForEach(CoreAIComputeUnitPreference.allCases, id: \.self) { preference in Text(workspace.computeUnitTitle(preference)) @@ -57,6 +57,8 @@ struct CoreAIDeviceTargetAuthoringView: View { ) .font(.subheadline) .foregroundStyle(.secondary) + } header: { + Label("iPhone Target", systemImage: "iphone.gen3") } } } From f25e3de40f88874ce948b0d018d5ec51f3029ccd Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:22:12 +0530 Subject: [PATCH 07/28] Polish Apple model task workspaces --- .../AppleModels/AppleModelDetailView.swift | 23 +++++++++---- .../AppleAudioTranscriptionResultView.swift | 4 ++- .../Audio/AppleAudioWorkspaceView.swift | 26 +++++++++------ .../Diffusion/AppleDiffusionResultView.swift | 4 ++- .../AppleDiffusionWorkspaceView.swift | 26 +++++++++------ .../Language/AppleLanguageResponseView.swift | 4 ++- .../Language/AppleLanguageWorkspaceView.swift | 24 +++++++++----- .../AppleObjectDetectionHeaderView.swift | 11 +++---- .../AppleObjectDetectionPreviewView.swift | 8 ++++- .../AppleObjectDetectionWorkspaceView.swift | 33 ++++++++++++------- .../AppleSegmentationPreviewView.swift | 4 ++- .../AppleSegmentationQueryControlsView.swift | 7 +++- .../AppleSegmentationWorkspaceView.swift | 27 ++++++++------- 13 files changed, 131 insertions(+), 70 deletions(-) diff --git a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift index f3525fc..198cd53 100644 --- a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift @@ -7,7 +7,12 @@ struct AppleModelDetailView: View { var body: some View { Form { Section { - LabeledContent("Model", value: model.huggingFaceID) + LabeledContent("Model") { + Text(model.huggingFaceID) + .lineLimit(1) + .truncationMode(.middle) + .textSelection(.enabled) + } LabeledContent( "Platforms", value: model.supportedPlatforms.map(\.rawValue).joined(separator: ", ") @@ -25,8 +30,8 @@ struct AppleModelDetailView: View { Label(model.category.rawValue, systemImage: model.category.systemImage) } - Section("Export with Apple's recipe") { - Text("Clone apple/coreai-models, run this command from its root, then import the resulting .aimodel or resource folder into the Lab.") + Section { + Text("Clone Apple's coreai-models repository, run this command from its root, then import the exported model or resource folder.") .foregroundStyle(.secondary) Text(model.labRecommendedExportCommand) @@ -41,9 +46,11 @@ struct AppleModelDetailView: View { "Convert This Recipe", value: AppleModelLibraryRoute.conversion(modelID: model.id) ) + } header: { + Label("Export Recipe", systemImage: "terminal") } - Section("Runtime integration") { + Section { Label(model.runtimeSupport.title, systemImage: "shippingbox") Text(model.runtimeSupport.detail) .foregroundStyle(.secondary) @@ -53,7 +60,7 @@ struct AppleModelDetailView: View { } if model.isRunnableInLab { - Text("The Lab includes this runtime adapter, not converted model weights. Export the model locally under its upstream license, then import the result.") + Text("Core AI Lab includes the runtime adapter, not model weights. Export the model locally under its upstream license, then import the result.") .foregroundStyle(.secondary) } @@ -99,9 +106,11 @@ struct AppleModelDetailView: View { value: AppleModelLibraryRoute.audio(audioExample) ) } + } header: { + Label("Runtime Integration", systemImage: "play.rectangle") } - Section("Provenance") { + Section { LabeledContent("Registry revision") { Text(sourceRevision) .font(.callout.monospaced()) @@ -109,6 +118,8 @@ struct AppleModelDetailView: View { } Text("The export recipe and Swift utilities use Apple's BSD-3-Clause repository. Downloaded model weights retain their original authors' licenses and are not redistributed by Core AI Lab.") .foregroundStyle(.secondary) + } header: { + Label("Provenance", systemImage: "checkmark.seal") } } .formStyle(.grouped) diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift index f79ec9e..766d7ac 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift @@ -4,7 +4,7 @@ struct AppleAudioTranscriptionResultView: View { let result: AppleAudioTranscriptionResult? var body: some View { - Section("Transcript") { + Section { if let result { if result.transcript.isEmpty { ContentUnavailableView( @@ -32,6 +32,8 @@ struct AppleAudioTranscriptionResultView: View { description: Text("Choose a short speech recording and transcribe it locally.") ) } + } header: { + Label("Transcript", systemImage: "captions.bubble") } } } diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift index 9d99056..d37d1d4 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift @@ -32,11 +32,13 @@ struct AppleAudioWorkspaceView: View { LabeledContent("Input", value: "1 × \(info.sampleCount) \(info.scalarTypeName)") LabeledContent("Sample rate", value: "\(Int(info.sampleRate).formatted()) Hz mono") } - Label( - workspace.statusMessage, - systemImage: workspace.isBusy ? "hourglass" : "waveform" - ) - .foregroundStyle(workspace.isBusy ? .primary : .secondary) + if workspace.isBusy { + ProgressView(workspace.statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(workspace.statusMessage, systemImage: "waveform") + .foregroundStyle(.secondary) + } } header: { Label(workspace.example.title, systemImage: "waveform.badge.mic") } @@ -46,9 +48,9 @@ struct AppleAudioWorkspaceView: View { context: workspace.runContext ) - Section("Inputs") { + Section { HStack { - Button("Import Wav2Vec2", systemImage: "shippingbox", action: importModel) + Button("Import Wav2Vec2 Model", systemImage: "shippingbox", action: importModel) Button("Choose Audio", systemImage: "waveform", action: importAudio) Button("Transcribe", systemImage: "captions.bubble", action: workspace.startTranscription) .buttonStyle(.borderedProminent) @@ -65,12 +67,16 @@ struct AppleAudioWorkspaceView: View { Text("The static Apple recipe accepts at most five seconds. Audio is decoded, downmixed, and resampled to 16 kHz mono before inference.") .foregroundStyle(.secondary) + } header: { + Label("Model & Audio", systemImage: "waveform.badge.mic") } - Section("Apple Export Command") { + Section { Text(workspace.example.exportCommand) .font(.body.monospaced()) .textSelection(.enabled) + } header: { + Label("Apple Export Command", systemImage: "terminal") } AppleAudioTranscriptionResultView(result: workspace.result) @@ -89,9 +95,9 @@ struct AppleAudioWorkspaceView: View { ) { result in handleAudioImport(result) } - .alert("Audio Transcription Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Transcribe Audio", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the model and audio files, then try again.") } .task(id: initialModelURL) { if let initialModelURL { diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift index 3fd18a0..6d076ae 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift @@ -4,7 +4,7 @@ struct AppleDiffusionResultView: View { let result: AppleDiffusionResult? var body: some View { - Section("Generated Image") { + Section { if let result { Image(decorative: result.image, scale: 1) .resizable() @@ -22,6 +22,8 @@ struct AppleDiffusionResultView: View { description: Text("Import a diffusion bundle, enter a prompt, and generate locally.") ) } + } header: { + Label("Generated Image", systemImage: "photo") } } } diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift index 75386c5..48a8f06 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift @@ -30,11 +30,13 @@ struct AppleDiffusionWorkspaceView: View { LabeledContent("Pipeline", value: info.pipelineName) LabeledContent("Output", value: "\(info.width) × \(info.height)") } - Label( - workspace.statusMessage, - systemImage: workspace.isBusy ? "hourglass" : "wand.and.sparkles" - ) - .foregroundStyle(workspace.isBusy ? .primary : .secondary) + if workspace.isBusy { + ProgressView(workspace.statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(workspace.statusMessage, systemImage: "wand.and.sparkles") + .foregroundStyle(.secondary) + } } header: { Label(workspace.example.title, systemImage: "wand.and.sparkles") } @@ -44,17 +46,19 @@ struct AppleDiffusionWorkspaceView: View { context: workspace.runContext ) - Section("Pipeline Bundle") { + Section { Button( "Import Diffusion Bundle", systemImage: "shippingbox", action: importPipeline ) - Text("Import the entire folder produced by `coreai.diffusion.export`. The Lab reads its metadata and selects Apple's Stable Diffusion, SD3, or FLUX.2 runtime automatically.") + Text("Choose the folder produced by `coreai.diffusion.export`. Core AI Lab reads its metadata and selects Apple's Stable Diffusion, SD3, or FLUX.2 runtime.") .foregroundStyle(.secondary) + } header: { + Label("Pipeline Bundle", systemImage: "shippingbox") } - Section("Prompt") { + Section { TextField("Describe an image", text: $workspace.prompt, axis: .vertical) .lineLimit(3...8) .disabled(!workspace.canEditGenerationInputs) @@ -90,6 +94,8 @@ struct AppleDiffusionWorkspaceView: View { ) } } + } header: { + Label("Prompt", systemImage: "text.bubble") } AppleDiffusionResultView(result: workspace.result) @@ -102,9 +108,9 @@ struct AppleDiffusionWorkspaceView: View { ) { result in handlePipelineImport(result) } - .alert("Diffusion Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Generate the Image", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the pipeline bundle and prompt, then try again.") } .task(id: initialModelURL) { if let initialModelURL { diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift index a76611b..bbbeeb1 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift @@ -4,7 +4,7 @@ struct AppleLanguageResponseView: View { let response: String var body: some View { - Section("Response") { + Section { if response.isEmpty { ContentUnavailableView( "No Response Yet", @@ -15,6 +15,8 @@ struct AppleLanguageResponseView: View { Text(response) .textSelection(.enabled) } + } header: { + Label("Response", systemImage: "text.bubble") } } } diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift index 53ecd49..c3a86fb 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift @@ -26,11 +26,13 @@ struct AppleLanguageWorkspaceView: View { Form { Section { LabeledContent("Model", value: workspace.modelName ?? "Not loaded") - Label( - workspace.statusMessage, - systemImage: workspace.isBusy ? "hourglass" : "text.bubble" - ) - .foregroundStyle(workspace.isBusy ? .primary : .secondary) + if workspace.isBusy { + ProgressView(workspace.statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(workspace.statusMessage, systemImage: "text.bubble") + .foregroundStyle(.secondary) + } } header: { Label(workspace.example.title, systemImage: "text.bubble.fill") } @@ -40,7 +42,7 @@ struct AppleLanguageWorkspaceView: View { context: workspace.runContext ) - Section("Model Bundle") { + Section { HStack { Button("Import Qwen Bundle", systemImage: "shippingbox", action: importModel) Button("New Session", systemImage: "arrow.counterclockwise", action: resetSession) @@ -57,9 +59,11 @@ struct AppleLanguageWorkspaceView: View { .font(.body.monospaced()) .textSelection(.enabled) } + } header: { + Label("Model Bundle", systemImage: "shippingbox") } - Section("Prompt") { + Section { TextField("Ask Qwen", text: $workspace.prompt, axis: .vertical) .lineLimit(3...8) .disabled(!workspace.canEditGenerationInputs) @@ -79,6 +83,8 @@ struct AppleLanguageWorkspaceView: View { Button("Cancel", systemImage: "stop.fill", role: .destructive, action: workspace.cancelGeneration) } } + } header: { + Label("Prompt", systemImage: "text.bubble") } AppleLanguageResponseView(response: workspace.response) @@ -91,9 +97,9 @@ struct AppleLanguageWorkspaceView: View { ) { result in handleModelImport(result) } - .alert("Language Model Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Generate a Response", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the model bundle and prompt, then try again.") } .task(id: initialModelURL) { if let initialModelURL { diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift index b96b093..03657f9 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift @@ -17,12 +17,11 @@ struct AppleObjectDetectionHeaderView: View { LabeledContent("Model", value: modelName ?? "Not imported") LabeledContent("Image", value: imageName ?? "Not selected") - HStack(spacing: 8) { - if isBusy { - ProgressView() - .controlSize(.small) - } - Text(statusMessage) + if isBusy { + ProgressView(statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(statusMessage, systemImage: "viewfinder") .foregroundStyle(.secondary) } } diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift index ab134be..0c2dee6 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift @@ -17,7 +17,13 @@ struct AppleObjectDetectionPreviewView: View { } .accessibilityLabel("Object detection source image") - if !detections.isEmpty { + if detections.isEmpty { + ContentUnavailableView( + "No Detections Yet", + systemImage: "viewfinder", + description: Text("Run detection to identify objects in this image.") + ) + } else { Table(detections) { TableColumn("Object", value: \.label) TableColumn("Confidence") { detection in diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift index 48952e9..525b16f 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift @@ -19,15 +19,17 @@ struct AppleObjectDetectionWorkspaceView: View { } var body: some View { - ScrollView { - VStack(alignment: .leading, spacing: 24) { + Form { + Section { AppleObjectDetectionHeaderView( modelName: workspace.modelName, imageName: workspace.imageName, statusMessage: workspace.statusMessage, isBusy: workspace.isLoadingModel || workspace.isRunning ) + } + Section { HStack(spacing: 12) { Button("Import YOLOS Model", systemImage: "shippingbox", action: importModel) Button("Choose Image", systemImage: "photo", action: importImage) @@ -36,18 +38,24 @@ struct AppleObjectDetectionWorkspaceView: View { .disabled(!workspace.canRun) } .disabled(workspace.isBusy) + } header: { + Label("Model & Image", systemImage: "viewfinder") + } - CoreAIRuntimeLifecycleView( - coordinator: workspace.runCoordinator, - context: workspace.runContext - ) + CoreAIRuntimeLifecycleView( + coordinator: workspace.runCoordinator, + context: workspace.runContext + ) - Text("Export command") - .font(.headline) + Section { Text("uv run models/yolo/export.py --model hustvl/yolos-tiny --dtype float16") .font(.body.monospaced()) .textSelection(.enabled) + } header: { + Label("Apple Export Command", systemImage: "terminal") + } + Section { if let sourceImage = workspace.sourceImage { AppleObjectDetectionPreviewView( image: sourceImage, @@ -60,10 +68,11 @@ struct AppleObjectDetectionWorkspaceView: View { description: Text("The result will show Apple's COCO labels, confidence, and bounding boxes.") ) } + } header: { + Label("Result", systemImage: "viewfinder") } - .frame(maxWidth: 1_200, alignment: .leading) - .padding(32) } + .formStyle(.grouped) .navigationTitle("Object Detection") .fileImporter( isPresented: $isImportingModel, @@ -77,9 +86,9 @@ struct AppleObjectDetectionWorkspaceView: View { ) { result in handleImageImport(result) } - .alert("Object Detection Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Detect Objects", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the model and image, then try again.") } } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift index 7bf13fc..20a31f5 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift @@ -5,7 +5,7 @@ struct AppleSegmentationPreviewView: View { let result: AppleSegmentationResult? var body: some View { - Section("Result") { + Section { if let image { Image( image, @@ -30,6 +30,8 @@ struct AppleSegmentationPreviewView: View { description: Text("Choose an image to preview segmentation results.") ) } + } header: { + Label("Result", systemImage: "square.stack.3d.up") } } } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift index 532de40..eb38b1a 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift @@ -4,7 +4,7 @@ struct AppleSegmentationQueryControlsView: View { @Bindable var workspace: AppleSegmentationWorkspaceModel var body: some View { - Section(workspace.example.usesTextPrompt ? "Text Prompt" : "Point Prompt") { + Section { if workspace.example.usesTextPrompt { TextField( "Object to segment", @@ -36,6 +36,11 @@ struct AppleSegmentationQueryControlsView: View { description: Text("Point controls appear after an image is loaded.") ) } + } header: { + Label( + workspace.example.usesTextPrompt ? "Text Prompt" : "Point Prompt", + systemImage: workspace.example.usesTextPrompt ? "text.cursor" : "scope" + ) } .disabled(workspace.isBusy) } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift index 8657fb8..c21ac76 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift @@ -28,11 +28,13 @@ struct AppleSegmentationWorkspaceView: View { Section { LabeledContent("Model", value: workspace.modelName ?? "Not loaded") LabeledContent("Image", value: workspace.imageName ?? "Not loaded") - Label( - workspace.statusMessage, - systemImage: statusSystemImage - ) - .foregroundStyle(workspace.isBusy ? .primary : .secondary) + if workspace.isBusy { + ProgressView(workspace.statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(workspace.statusMessage, systemImage: statusSystemImage) + .foregroundStyle(.secondary) + } } header: { Label(workspace.example.title, systemImage: "square.stack.3d.up") } @@ -42,7 +44,7 @@ struct AppleSegmentationWorkspaceView: View { context: workspace.runContext ) - Section("Run Apple's Export") { + Section { HStack { Button("Import Model Bundle", systemImage: "shippingbox", action: importModel) Button("Choose Image", systemImage: "photo", action: importImage) @@ -51,10 +53,16 @@ struct AppleSegmentationWorkspaceView: View { .disabled(!workspace.canRun) } .disabled(workspace.isBusy) + } header: { + Label("Model & Image", systemImage: "square.stack.3d.up") + } + Section { Text(workspace.example.exportCommand) .font(.body.monospaced()) .textSelection(.enabled) + } header: { + Label("Apple Export Command", systemImage: "terminal") } AppleSegmentationQueryControlsView(workspace: workspace) @@ -77,9 +85,9 @@ struct AppleSegmentationWorkspaceView: View { ) { result in handleImageImport(result) } - .alert("Segmentation Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Segment the Image", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the model bundle and image, then try again.") } .task(id: initialModelURL) { if let initialModelURL { @@ -93,9 +101,6 @@ struct AppleSegmentationWorkspaceView: View { } private var statusSystemImage: String { - if workspace.isBusy { - return "hourglass" - } if workspace.isShowingError { return "exclamationmark.triangle" } From 6ed5b738383aec97da4914877b0c5f8415f0d54e Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:23:10 +0530 Subject: [PATCH 08/28] Clarify the function workbench hierarchy --- .../CoreAIFunctionBenchmarkActionsView.swift | 1 + .../CoreAIFunctionBenchmarkControlsView.swift | 11 ++++-- .../CoreAIFunctionBenchmarkResultsView.swift | 4 +- .../CoreAIFunctionContractView.swift | 4 +- .../CoreAIFunctionInputsView.swift | 4 +- .../CoreAIFunctionResultsView.swift | 4 +- .../CoreAIFunctionWorkbenchView.swift | 38 +++++++++++++------ .../CoreAIIntegrationExportSection.swift | 4 +- 8 files changed, 50 insertions(+), 20 deletions(-) diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift index f3fe52f..3c63e7e 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift @@ -23,6 +23,7 @@ struct CoreAIFunctionBenchmarkActionsView: View { Button( "Stop After Current Inference", systemImage: "stop.fill", + role: .cancel, action: workspace.stopBenchmarkAfterCurrentInference ) } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift index 7a23904..13ffcee 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift @@ -31,8 +31,13 @@ struct CoreAIFunctionBenchmarkControlsView: View { } if let message = workspace.benchmarkStatusMessage { - Label(message, systemImage: "info.circle") - .foregroundStyle(.secondary) + if workspace.phase == .benchmarking { + ProgressView(message) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(message, systemImage: "info.circle") + .foregroundStyle(.secondary) + } } if CoreAIBuildConfiguration.current == .debug { @@ -43,7 +48,7 @@ struct CoreAIFunctionBenchmarkControlsView: View { .foregroundStyle(.orange) } } header: { - Text("Benchmark") + Label("Benchmark", systemImage: "gauge.with.dots.needle.67percent") } footer: { Text( "Warmups are excluded. Measured runs reuse one function and one deterministic input set, execute sequentially, and remain visible individually. Stopping takes effect between Core AI inference calls." diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkResultsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkResultsView.swift index d57d937..859f97a 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkResultsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkResultsView.swift @@ -5,13 +5,15 @@ struct CoreAIFunctionBenchmarkResultsView: View { let exportEvidence: (CoreAIFunctionBenchmarkReport) -> Void var body: some View { - Section("Benchmark History") { + Section { ForEach(reports) { report in CoreAIFunctionBenchmarkReportView( report: report, exportEvidence: exportEvidence ) } + } header: { + Label("Benchmark History", systemImage: "clock.arrow.circlepath") } } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionContractView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionContractView.swift index b1c80d3..e02bac8 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionContractView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionContractView.swift @@ -4,7 +4,7 @@ struct CoreAIFunctionContractView: View { @Bindable var workspace: CoreAIFunctionWorkbenchWorkspaceModel var body: some View { - Section("Function") { + Section { Picker("Entry point", selection: $workspace.selectedFunctionName) { ForEach(workspace.contracts) { contract in Text(contract.name) @@ -23,6 +23,8 @@ struct CoreAIFunctionContractView: View { .foregroundStyle(.secondary) } } + } header: { + Label("Function", systemImage: "function") } } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift index 28f3af9..eab5a3d 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift @@ -5,7 +5,7 @@ struct CoreAIFunctionInputsView: View { let isDisabled: Bool var body: some View { - Section("Generated Inputs") { + Section { if drafts.isEmpty { Text("This function has no generated tensor inputs.") .foregroundStyle(.secondary) @@ -17,6 +17,8 @@ struct CoreAIFunctionInputsView: View { ) } } + } header: { + Label("Generated Inputs", systemImage: "slider.horizontal.3") } } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionResultsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionResultsView.swift index 06d2863..1a0b4b4 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionResultsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionResultsView.swift @@ -4,7 +4,7 @@ struct CoreAIFunctionResultsView: View { let result: CoreAIFunctionRunResult var body: some View { - Section("Latest Run") { + Section { LabeledContent("Function", value: result.functionName) LabeledContent("Inference time") { Text(result.duration.formatted(.time(pattern: .minuteSecond))) @@ -12,6 +12,8 @@ struct CoreAIFunctionResultsView: View { ForEach(result.outputs) { output in CoreAIFunctionOutputSummaryView(output: output) } + } header: { + Label("Latest Run", systemImage: "checkmark.circle") } } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift index 015e8d5..7b00c09 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift @@ -37,8 +37,8 @@ struct CoreAIFunctionWorkbenchView: View { Group { if let report = workspace.assetWorkspace.report { - List { - Section("Asset") { + Form { + Section { LabeledContent("Name", value: report.url.lastPathComponent) LabeledContent( "Device", @@ -47,7 +47,11 @@ struct CoreAIFunctionWorkbenchView: View { Text(report.url.path) .font(.callout.monospaced()) .foregroundStyle(.secondary) + .lineLimit(1) + .truncationMode(.middle) .textSelection(.enabled) + } header: { + Label("Asset", systemImage: "shippingbox") } CoreAIRuntimeLifecycleView( @@ -63,7 +67,7 @@ struct CoreAIFunctionWorkbenchView: View { ) if workspace.assetWorkspace.specializationResult == nil { - Section("Function Workbench") { + Section { ContentUnavailableView( "Specialize the Asset", systemImage: "cpu", @@ -71,9 +75,11 @@ struct CoreAIFunctionWorkbenchView: View { "Choose a compute profile above, then specialize or load its cached model to inspect runtime contracts." ) ) + } header: { + Label("Function Workbench", systemImage: "function") } } else if workspace.phase == .preparingContracts { - Section("Function Workbench") { + Section { ContentUnavailableView { Label("Reading Function Contracts", systemImage: "list.bullet.rectangle") } description: { @@ -81,14 +87,16 @@ struct CoreAIFunctionWorkbenchView: View { } actions: { ProgressView() } + } header: { + Label("Function Workbench", systemImage: "function") } } else if workspace.contracts.isEmpty { - Section("Function Workbench") { + Section { ContentUnavailableView { Label( workspace.contractLoadFailureMessage == nil ? "No Functions" - : "Unable to Load Functions", + : "Couldn't Read Functions", systemImage: workspace.contractLoadFailureMessage == nil ? "function" : "exclamationmark.triangle" @@ -107,6 +115,8 @@ struct CoreAIFunctionWorkbenchView: View { ) } } + } header: { + Label("Function Workbench", systemImage: "function") } } else { CoreAIFunctionContractView(workspace: workspace) @@ -124,10 +134,14 @@ struct CoreAIFunctionWorkbenchView: View { .buttonStyle(.borderedProminent) .disabled(!workspace.canRun) - if workspace.phase.isBusy { - Label("Core AI operation in progress", systemImage: "hourglass") - .foregroundStyle(.secondary) + if workspace.phase == .running { + ProgressView( + "Running \(workspace.selectedFunctionName ?? "function")…" + ) + .accessibilityAddTraits(.updatesFrequently) } + } header: { + Label("Run", systemImage: "play.fill") } footer: { Text( "Generated inputs are synthetic contract probes, not semantically correct task data. Core AI inference itself cannot be canceled once started." @@ -153,6 +167,7 @@ struct CoreAIFunctionWorkbenchView: View { ) } } + .formStyle(.grouped) } else if workspace.phase == .loadingAsset || workspace.assetWorkspace.isInspecting { ContentUnavailableView { @@ -184,6 +199,7 @@ struct CoreAIFunctionWorkbenchView: View { || workspace.assetWorkspace.phase.isBusy || workspace.isExportingIntegration ) + .keyboardShortcut("o", modifiers: .command) } } .fileImporter( @@ -207,11 +223,11 @@ struct CoreAIFunctionWorkbenchView: View { handleBenchmarkEvidenceExport(result) } .alert( - "Function Workbench Error", + "Couldn't Complete the Core AI Operation", isPresented: $assetWorkspace.isShowingError ) { } message: { - Text(assetWorkspace.errorMessage ?? "The Core AI operation failed.") + Text(assetWorkspace.errorMessage ?? "Check the model and configuration, then try again.") } .task(id: initialURL) { do { diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift index 5c012b5..698c101 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift @@ -37,10 +37,10 @@ struct CoreAIIntegrationExportSection: View { .foregroundStyle(.secondary) } } header: { - Text("Integration Export") + Label("Integration Export", systemImage: "shippingbox.and.arrow.backward") } footer: { Text( - "Exports a standalone Swift package with the original asset, checksums, notices, typed metadata, generated invocation code, and an offline verifier. The optional AOT script is never run automatically. Stateful and image-input functions remain manifest-only." + "Creates a standalone Swift package with the original asset, checksums, notices, typed metadata, generated invocation code, and an offline verifier. The optional AOT script never runs automatically. Stateful and image-input functions remain manifest-only." ) } } From e2d0880c17e2487d7893f480a72d21aa05c9d489 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:24:17 +0530 Subject: [PATCH 09/28] Polish project management flows --- .../CoreAIArtifactProjectPickerView.swift | 4 +-- .../Projects/CoreAINewProjectView.swift | 17 ++++++--- .../CoreAIProjectArtifactDetailView.swift | 32 ++++++++++------- .../Projects/CoreAIProjectDetailView.swift | 26 ++++++++------ .../Projects/CoreAIProjectLibraryView.swift | 4 +-- .../CoreAISourceProvenanceEditorView.swift | 35 +++++++++++-------- 6 files changed, 71 insertions(+), 47 deletions(-) diff --git a/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift b/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift index ea4ad44..ffdc964 100644 --- a/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift +++ b/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift @@ -57,9 +57,9 @@ struct CoreAIArtifactProjectPickerView: View { storeArtifact(in: project) } } - .alert("Artifact Could Not Be Stored", isPresented: $controller.isShowingError) { + .alert("Couldn't Store the Artifact", isPresented: $controller.isShowingError) { } message: { - Text(controller.errorMessage ?? "Core AI Lab could not store the artifact.") + Text(controller.errorMessage ?? "Choose another project or try again.") } } diff --git a/CoreAILab/Features/Projects/CoreAINewProjectView.swift b/CoreAILab/Features/Projects/CoreAINewProjectView.swift index e456a7e..f2ef34e 100644 --- a/CoreAILab/Features/Projects/CoreAINewProjectView.swift +++ b/CoreAILab/Features/Projects/CoreAINewProjectView.swift @@ -14,8 +14,14 @@ struct CoreAINewProjectView: View { NavigationStack { Form { - TextField("Project Name", text: $name) - .textContentType(.name) + Section { + TextField("Project Name", text: $name) + .textContentType(.name) + } header: { + Label("Project", systemImage: "folder") + } footer: { + Text("Projects keep related assets, provenance, runs, and evidence together.") + } } .formStyle(.grouped) .navigationTitle("New Project") @@ -27,13 +33,14 @@ struct CoreAINewProjectView: View { ToolbarItem(placement: .confirmationAction) { Button("Create", action: createProject) .disabled(name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty) + .keyboardShortcut(.defaultAction) } } } - .frame(minWidth: 360, minHeight: 180) - .alert("Project Could Not Be Created", isPresented: $controller.isShowingError) { + .frame(minWidth: 380, minHeight: 220) + .alert("Couldn't Create the Project", isPresented: $controller.isShowingError) { } message: { - Text(controller.errorMessage ?? "Core AI Lab could not create the project.") + Text(controller.errorMessage ?? "Choose a different name and try again.") } } diff --git a/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift b/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift index 7b2d982..02bcb78 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift @@ -18,7 +18,7 @@ struct CoreAIProjectArtifactDetailView: View { Form { if let artifact = link.artifact { - Section("Artifact") { + Section { LabeledContent("Name", value: link.displayName) LabeledContent( "Kind", @@ -31,9 +31,11 @@ struct CoreAIProjectArtifactDetailView: View { LabeledContent("Imported") { Text(artifact.importedAt, format: .dateTime.day().month().year().hour().minute()) } + } header: { + Label("Artifact", systemImage: "shippingbox") } - Section("Integrity") { + Section { LabeledContent("SHA-256") { Text(artifact.sha256Digest) .font(.callout.monospaced()) @@ -42,8 +44,12 @@ struct CoreAIProjectArtifactDetailView: View { LabeledContent("Store path") { Text(artifact.storageRelativePath) .font(.callout.monospaced()) + .lineLimit(1) + .truncationMode(.middle) .textSelection(.enabled) } + } header: { + Label("Integrity", systemImage: "checkmark.seal") } if artifact.resourceSnapshotData != nil { @@ -74,15 +80,15 @@ struct CoreAIProjectArtifactDetailView: View { } if artifact.kind == .modelAsset { - Section("Open With") { - NavigationLink( - "Asset Inspector", - value: CoreAIProjectRoute.inspect(link.id) - ) - NavigationLink( - "Function Workbench", - value: CoreAIProjectRoute.workbench(link.id) - ) + Section { + NavigationLink(value: CoreAIProjectRoute.inspect(link.id)) { + Label("Asset Inspector", systemImage: "doc.text.magnifyingglass") + } + NavigationLink(value: CoreAIProjectRoute.workbench(link.id)) { + Label("Function Workbench", systemImage: "function") + } + } header: { + Label("Open With", systemImage: "arrow.up.forward.app") } CoreAIProjectSpecializationCacheView( @@ -149,9 +155,9 @@ struct CoreAIProjectArtifactDetailView: View { "Project cache records are removed. Core AI deletes configurations only when another project does not still reference them." ) } - .alert("Artifact Operation Failed", isPresented: $controller.isShowingError) { + .alert("Couldn't Update the Artifact", isPresented: $controller.isShowingError) { } message: { - Text(controller.errorMessage ?? "The artifact operation failed.") + Text(controller.errorMessage ?? "Check the stored artifact and try again.") } .sheet(isPresented: $isShowingProvenanceEditor) { CoreAISourceProvenanceEditorView( diff --git a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift index 1ec0083..ccd6347 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift @@ -16,8 +16,8 @@ struct CoreAIProjectDetailView: View { var body: some View { @Bindable var controller = controller - List { - Section("Overview") { + Form { + Section { LabeledContent("Artifacts", value: project.artifactLinks.count.formatted()) LabeledContent("Stored size") { Text(project.storedByteCount, format: .byteCount(style: .file)) @@ -28,9 +28,11 @@ struct CoreAIProjectDetailView: View { LabeledContent("Last opened") { Text(project.lastOpenedAt, format: .relative(presentation: .named)) } + } header: { + Label("Overview", systemImage: "folder") } - Section("Artifacts") { + Section { if project.artifactLinks.isEmpty { ContentUnavailableView { Label("No Stored Artifacts", systemImage: "shippingbox") @@ -54,14 +56,14 @@ struct CoreAIProjectDetailView: View { } if controller.activeProjectID == project.id { - Label( - controller.activeOperation?.title ?? "Updating project…", - systemImage: controller.activeOperation?.systemImage ?? "hourglass" - ) - .foregroundStyle(.secondary) + ProgressView(controller.activeOperation?.title ?? "Updating project…") + .accessibilityAddTraits(.updatesFrequently) } + } header: { + Label("Artifacts", systemImage: "shippingbox") } } + .formStyle(.grouped) .navigationTitle(project.name) .toolbar { ToolbarItemGroup(placement: .primaryAction) { @@ -106,9 +108,9 @@ struct CoreAIProjectDetailView: View { "Project metadata is deleted. Stored artifacts are reclaimed only when no other project references the same SHA-256 content." ) } - .alert("Project Operation Failed", isPresented: $controller.isShowingError) { + .alert("Couldn't Update the Project", isPresented: $controller.isShowingError) { } message: { - Text(controller.errorMessage ?? "The project operation failed.") + Text(controller.errorMessage ?? "Check the project and try again.") } .task(id: project.id) { do { @@ -138,7 +140,9 @@ struct CoreAIProjectDetailView: View { } } case .failure(let error): - controller.present(error) + if (error as? CocoaError)?.code != .userCancelled { + controller.present(error) + } } } diff --git a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift index 5711ba3..ec7cf35 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift @@ -66,9 +66,9 @@ struct CoreAIProjectLibraryView: View { path.append(.project(project.id)) } } - .alert("Project Operation Failed", isPresented: $controller.isShowingError) { + .alert("Couldn't Update the Project Library", isPresented: $controller.isShowingError) { } message: { - Text(controller.errorMessage ?? "The project operation failed.") + Text(controller.errorMessage ?? "Check project storage and try again.") } } diff --git a/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift b/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift index a0170d4..0fa155f 100644 --- a/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift +++ b/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift @@ -32,36 +32,43 @@ struct CoreAISourceProvenanceEditorView: View { var body: some View { NavigationStack { Form { - Picker("Source type", selection: $kind) { - ForEach(CoreAISourceProvenanceKind.allCases) { kind in - Text(kind.title).tag(kind) + Section { + Picker("Source type", selection: $kind) { + ForEach(CoreAISourceProvenanceKind.allCases) { kind in + Text(kind.title).tag(kind) + } } - } - TextField("Source location", text: $sourceLocation, axis: .vertical) - .lineLimit(2...5) - TextField("Provider", text: $providerName) - TextField("License", text: $licenseName) - TextField("Notes", text: $notes, axis: .vertical) - .lineLimit(3...8) + TextField("Source location", text: $sourceLocation, axis: .vertical) + .lineLimit(2...5) + TextField("Provider", text: $providerName) + TextField("License", text: $licenseName) + TextField("Notes", text: $notes, axis: .vertical) + .lineLimit(3...8) + } header: { + Label("Source", systemImage: "link") + } footer: { + Text("Record enough information to trace this artifact to its original source and license.") + } } .formStyle(.grouped) .navigationTitle("Source Provenance") .toolbar { ToolbarItem(placement: .cancellationAction) { - Button("Cancel", action: dismiss.callAsFunction) + Button("Cancel", role: .cancel, action: dismiss.callAsFunction) } ToolbarItem(placement: .confirmationAction) { Button("Save", action: save) .disabled(kind != .unknown && sourceLocation.trimmed.isEmpty) + .keyboardShortcut(.defaultAction) } } - .alert("Unable to Save Provenance", isPresented: $isShowingError) { - Button("OK") { + .alert("Couldn't Save Provenance", isPresented: $isShowingError) { + Button("Dismiss", role: .cancel) { errorMessage = nil } } message: { - Text(errorMessage ?? "The source provenance could not be saved.") + Text(errorMessage ?? "Check the source details and try again.") } } } From 1e7938c25544bee47442ccbc01eda6ac6a9a63e9 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:26:15 +0530 Subject: [PATCH 10/28] Refine product voice and recovery copy --- .../AppleModels/AppleModelDetailView.swift | 4 +-- .../AppleObjectDetectionHeaderView.swift | 2 +- .../CoreAIAssetInspectorView.swift | 8 +++-- .../Chatterbox/ChatterboxPresentedError.swift | 1 + .../Chatterbox/ChatterboxWorkspaceModel.swift | 36 +++++++++++++------ .../Chatterbox/ChatterboxWorkspaceView.swift | 13 +++---- .../CoreAIConversionWorkspaceView.swift | 14 +++++--- .../SpeakerDiarizationStatusSection.swift | 23 +++++------- .../SpeakerDiarizationWorkspaceModel.swift | 2 +- .../SpeakerDiarizationWorkspaceView.swift | 12 +++++-- .../Recipes/CoreAIRecipeCatalogView.swift | 6 ++-- .../CoreAIRecipeCatalogWorkspaceModel.swift | 2 +- .../CoreAIRuntimeExperienceRoute.swift | 2 +- .../CoreAIRuntimeStudioView.swift | 9 ++--- ...reAIRuntimeUnsupportedExperienceView.swift | 2 +- 15 files changed, 82 insertions(+), 54 deletions(-) diff --git a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift index 198cd53..b523e91 100644 --- a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift @@ -73,7 +73,7 @@ struct AppleModelDetailView: View { if let segmentationExample = model.segmentationExample { if segmentationExample == .sam3 { - Text("SAM 3 requires accepting Meta's gated Hugging Face license and authenticating with the `hf` command-line tool before export. Credentials stay outside the Lab.") + Text("SAM 3 requires accepting Meta's gated Hugging Face license and authenticating with the `hf` command-line tool before export. Core AI Lab never reads or stores those credentials.") .foregroundStyle(.secondary) } NavigationLink( @@ -91,7 +91,7 @@ struct AppleModelDetailView: View { if let diffusionExample = model.diffusionExample { if diffusionExample == .stableDiffusion35 { - Text("Stable Diffusion 3.5 weights require accepting Stability AI's gated Hugging Face terms and authenticating with the `hf` command-line tool before export. Credentials stay outside the Lab.") + Text("Stable Diffusion 3.5 weights require accepting Stability AI's gated Hugging Face terms and authenticating with the `hf` command-line tool before export. Core AI Lab never reads or stores those credentials.") .foregroundStyle(.secondary) } NavigationLink( diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift index 03657f9..22abefa 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift @@ -11,7 +11,7 @@ struct AppleObjectDetectionHeaderView: View { Label("YOLOS Tiny", systemImage: "viewfinder") .font(.title2.bold()) - Text("The first runnable Apple gallery model uses Apple's export recipe and CoreAIObjectDetection Swift package.") + Text("Uses Apple's YOLOS export recipe and the CoreAIObjectDetection Swift package.") .foregroundStyle(.secondary) LabeledContent("Model", value: modelName ?? "Not imported") diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift index 2d5c171..799dc51 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift @@ -60,9 +60,9 @@ struct CoreAIAssetInspectorView: View { ) { result in handleModelImport(result) } - .alert("Core AI Operation Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Inspect the Model", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The Core AI operation failed.") + Text(workspace.errorMessage ?? "Check the model asset and try again.") } .task(id: initialURL) { do { @@ -94,7 +94,9 @@ struct CoreAIAssetInspectorView: View { await workspace.inspect(url: url) } case .failure(let error): - workspace.presentImportError(error) + if (error as? CocoaError)?.code != .userCancelled { + workspace.presentImportError(error) + } } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPresentedError.swift b/CoreAILab/Features/Chatterbox/ChatterboxPresentedError.swift index 0e02110..77326de 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPresentedError.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPresentedError.swift @@ -2,5 +2,6 @@ import Foundation struct ChatterboxPresentedError: Identifiable { let id = UUID() + let title: String let message: String } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift index 6922270..d85a051 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift @@ -5,12 +5,12 @@ import Observation @MainActor @Observable final class ChatterboxWorkspaceModel { - var prompt = "Oh, that's hilarious! [chuckle] This voice is running entirely on your Mac with Core AI." + var prompt = "Oh, that's hilarious! [chuckle] This voice was generated locally with Core AI." var modelState = ChatterboxModelState.notLoaded var generatedResult: ChatterboxGenerationResult? var isWorking = false var isPlaying = false - var statusMessage = "Preparing Core AI" + var statusMessage = "Preparing Core AI…" var presentedError: ChatterboxPresentedError? private(set) var recipeManifest: CoreAIRecipeManifest? @@ -34,6 +34,15 @@ final class ChatterboxWorkspaceModel { return inspection } + var isShowingError: Bool { + get { presentedError != nil } + set { + if !newValue { + presentedError = nil + } + } + } + var canGenerate: Bool { guard let inspection else { return false @@ -55,13 +64,15 @@ final class ChatterboxWorkspaceModel { let manifest = try await engine.bundledRecipeManifest() recipeManifest = manifest let targetName = manifest.defaultTarget?.displayName ?? "selected target" - statusMessage = "Specializing \(manifest.pipeline.stages.count) models for \(targetName)" - modelState = .ready(try await engine.prepareBundledModels()) + statusMessage = "Specializing \(manifest.pipeline.stages.count) models for \(targetName)…" + let inspection = try await engine.prepareBundledModels() + modelState = .ready(inspection) + statusMessage = "Ready to generate speech." } catch { recipeManifest = nil modelState = .failed(error.localizedDescription) - statusMessage = "Core AI preparation failed" - present(error) + statusMessage = "Core AI preparation failed." + present(error, title: "Couldn't Prepare Chatterbox") } isWorking = false } @@ -83,7 +94,7 @@ final class ChatterboxWorkspaceModel { private func synthesize() async { stopPlayback() isWorking = true - statusMessage = "Generating speech entirely with Core AI" + statusMessage = "Generating speech with Core AI…" generatedResult = nil do { @@ -91,9 +102,11 @@ final class ChatterboxWorkspaceModel { ChatterboxGenerationRequest(text: prompt) ) generatedResult = result + statusMessage = "Generated \(result.audioDuration.formatted(.number.precision(.fractionLength(1)))) seconds of speech in \(result.elapsedTime.formatted(.number.precision(.fractionLength(1)))) seconds." play(result) } catch { - present(error) + statusMessage = "Speech generation failed." + present(error, title: "Couldn't Generate Speech") } isWorking = false } @@ -117,7 +130,9 @@ final class ChatterboxWorkspaceModel { self?.isPlaying = false } } catch { - present(error) + isPlaying = false + statusMessage = "Speech is ready, but playback couldn't start." + present(error, title: "Couldn't Play Speech") } } @@ -129,8 +144,9 @@ final class ChatterboxWorkspaceModel { isPlaying = false } - private func present(_ error: Error) { + private func present(_ error: Error, title: String) { presentedError = ChatterboxPresentedError( + title: title, message: error.localizedDescription ) } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift index 7d7adf4..18ca032 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift @@ -31,12 +31,13 @@ struct ChatterboxWorkspaceView: View { .task { await model.prepare() } - .alert(item: $model.presentedError) { presentedError in - Alert( - title: Text("Chatterbox Core AI"), - message: Text(presentedError.message), - dismissButton: .default(Text("OK")) - ) + .alert( + model.presentedError?.title ?? "Couldn't Complete the Request", + isPresented: $model.isShowingError + ) { + Button("Dismiss", role: .cancel) {} + } message: { + Text(model.presentedError?.message ?? "Try again.") } } } diff --git a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift index 7f35fd8..1244fc6 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift @@ -64,9 +64,9 @@ struct CoreAIConversionWorkspaceView: View { ) { result in handleUVSelection(result) } - .alert("Conversion Error", isPresented: $workspace.isShowingError) { + .alert("Couldn't Complete the Conversion", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The conversion could not be completed.") + Text(workspace.errorMessage ?? "Review the environment checks and try again.") } .sheet(item: $artifactToStore) { artifact in CoreAIArtifactProjectPickerView(artifactURL: artifact.url) @@ -99,7 +99,7 @@ struct CoreAIConversionWorkspaceView: View { await workspace.refreshEnvironment() } case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -111,7 +111,7 @@ struct CoreAIConversionWorkspaceView: View { await workspace.refreshEnvironment() } case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -123,6 +123,12 @@ struct CoreAIConversionWorkspaceView: View { await workspace.refreshEnvironment() } case .failure(let error): + presentSelectionError(error) + } + } + + private func presentSelectionError(_ error: any Error) { + if (error as? CocoaError)?.code != .userCancelled { workspace.presentImportError(error) } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift index 7fd077b..bd12018 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift @@ -31,24 +31,17 @@ struct SpeakerDiarizationStatusSection: View { ) } } - HStack { - if isBusy { - ProgressView() - .controlSize(.small) - .accessibilityHidden(true) - } else { - Image(systemName: "waveform.badge.mic") - .accessibilityHidden(true) - } - Text(statusMessage) - .foregroundStyle(isBusy ? .primary : .secondary) + if isBusy { + ProgressView(statusMessage) + .accessibilityAddTraits(.updatesFrequently) + } else { + Label(statusMessage, systemImage: "waveform.badge.mic") + .foregroundStyle(.secondary) } - .accessibilityElement(children: .combine) - .accessibilityAddTraits(.updatesFrequently) } header: { - Label("Speaker Diarization Lab", systemImage: "person.wave.2") + Label("Speaker Diarization", systemImage: "person.wave.2") } footer: { - Text("The bundled CAM++ asset is Apache-2.0. This experimental batch engine assigns anonymous labels, not real identities, and energy segmentation does not detect overlapping speakers.") + Text("The bundled CAM++ asset is Apache-2.0. This experimental batch engine uses anonymous labels—not identities—and does not detect overlapping speakers.") } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift index 286a90d..57bd22e 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift @@ -73,7 +73,7 @@ final class SpeakerDiarizationWorkspaceModel { ? "CAM++ is ready. Choose media or run diarization." : "Media and CAM++ are ready for batch diarization." } catch is CancellationError { - statusMessage = "Model import cancelled." + statusMessage = "Model import canceled." } catch { present(error) } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift index c2d92db..719fffa 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift @@ -71,9 +71,9 @@ struct SpeakerDiarizationWorkspaceView: View { ) { result in handleMediaImport(result) } - .alert("Diarization Lab Failed", isPresented: $workspace.isShowingError) { + .alert("Couldn't Separate the Speakers", isPresented: $workspace.isShowingError) { } message: { - Text(workspace.errorMessage ?? "The request could not be completed.") + Text(workspace.errorMessage ?? "Check the model and media file, then try again.") } } } @@ -91,7 +91,7 @@ struct SpeakerDiarizationWorkspaceView: View { case .success(let url): workspace.selectMedia(url) case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -102,6 +102,12 @@ struct SpeakerDiarizationWorkspaceView: View { await workspace.loadModel(from: url) } case .failure(let error): + presentSelectionError(error) + } + } + + private func presentSelectionError(_ error: any Error) { + if (error as? CocoaError)?.code != .userCancelled { workspace.presentImportError(error) } } diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift index a84fa39..8e769be 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift @@ -74,7 +74,7 @@ struct CoreAIRecipeCatalogView: View { handleImportResult(result) } .alert( - "Recipe Bundle Import Failed", + "Couldn't Import the Recipe Bundle", isPresented: $model.isShowingError, presenting: model.errorMessage ) { _ in @@ -94,7 +94,9 @@ struct CoreAIRecipeCatalogView: View { guard let url = urls.first else { return } Task { await model.importBundle(at: url) } case .failure(let error): - model.presentImportError(error) + if (error as? CocoaError)?.code != .userCancelled { + model.presentImportError(error) + } } } diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogWorkspaceModel.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogWorkspaceModel.swift index 7c02a9b..930a2fa 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogWorkspaceModel.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogWorkspaceModel.swift @@ -80,7 +80,7 @@ final class CoreAIRecipeCatalogWorkspaceModel { } catch is CancellationError { guard activeImportID == importID else { return } phase = .idle - statusMessage = "Import cancelled." + statusMessage = "Import canceled." } catch { guard activeImportID == importID else { return } phase = .idle diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRoute.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRoute.swift index 44044f0..9bb9064 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRoute.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRoute.swift @@ -4,6 +4,6 @@ struct CoreAIRuntimeExperienceRoute: Hashable { let experienceID: String var unavailableDescription: String { - "The experience “\(experienceID)” is not available in the current runtime registry or on this platform." + "The experience “\(experienceID)” is no longer in the runtime registry or isn't available on this platform." } } diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeStudioView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeStudioView.swift index cf0caa3..b4fffac 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeStudioView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeStudioView.swift @@ -15,12 +15,12 @@ struct CoreAIRuntimeStudioView: View { Group { if let loadError = model.loadError { ContentUnavailableView( - "Runtime Registry Unavailable", + "Couldn't Load Runtime Experiences", systemImage: "exclamationmark.triangle", description: Text(loadError) ) } else if model.registry == nil { - ProgressView("Loading Runtime Studio…") + ProgressView("Loading runtime experiences…") } else if model.filteredMappings.isEmpty { ContentUnavailableView.search } else { @@ -39,6 +39,7 @@ struct CoreAIRuntimeStudioView: View { ) } } + .listStyle(.inset) } } .navigationTitle("Runtime Studio") @@ -66,11 +67,11 @@ struct CoreAIRuntimeStudioView: View { ) } else { ContentUnavailableView( - "Experience Unavailable", + "Experience Not Found", systemImage: "questionmark.folder", description: Text(route.unavailableDescription) ) - .navigationTitle("Experience Unavailable") + .navigationTitle("Experience Not Found") } } .task { diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift index fcb8c0c..2305c45 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift @@ -5,7 +5,7 @@ struct CoreAIRuntimeUnsupportedExperienceView: View { var body: some View { ContentUnavailableView( - "Experience Adapter Unavailable", + "Adapter Not Available in This Build", systemImage: "exclamationmark.triangle", description: Text( "The recipe maps \(mapping.experience.modelIdentifier) to \(mapping.experience.adapter.rawValue), but this build cannot resolve that model preset." From 98bfa4d796ac05aa68405f0cfae266d8233dc7c0 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:27:57 +0530 Subject: [PATCH 11/28] Document the native design system --- .impeccable/design.json | 132 +++++++++++++++++++++++++++++++ DESIGN.md | 168 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 .impeccable/design.json create mode 100644 DESIGN.md diff --git a/.impeccable/design.json b/.impeccable/design.json new file mode 100644 index 0000000..795230a --- /dev/null +++ b/.impeccable/design.json @@ -0,0 +1,132 @@ +{ + "schemaVersion": 2, + "generatedAt": "2026-06-24T04:56:51Z", + "title": "Design System: Core AI Lab", + "extensions": { + "colorMeta": { + "system-blue": { + "role": "primary", + "displayName": "System Blue", + "tonalRamp": ["#EAF3FF", "#8FC2FF", "#007AFF", "#0057B8", "#003A7A"] + }, + "system-green": { + "role": "success", + "displayName": "System Green", + "tonalRamp": ["#EAF9EE", "#91E3A5", "#34C759", "#248A3D", "#176128"] + }, + "system-orange": { + "role": "warning", + "displayName": "System Orange", + "tonalRamp": ["#FFF4E5", "#FFD08A", "#FF9500", "#B56900", "#7A4700"] + }, + "system-red": { + "role": "destructive", + "displayName": "System Red", + "tonalRamp": ["#FFECEC", "#FF9C99", "#FF3B30", "#C5221F", "#7F1715"] + }, + "on-accent": { + "role": "on-primary", + "displayName": "On Accent" + } + }, + "typographyMeta": { + "title": { + "displayName": "System Title", + "purpose": "Navigation and workspace identity; use SwiftUI semantic title styles at runtime." + }, + "body": { + "displayName": "System Body", + "purpose": "Controls, instructions, results, and user-authored content." + }, + "technical": { + "displayName": "Technical Mono", + "purpose": "Commands, hashes, paths, identifiers, shapes, and numeric evidence." + } + }, + "shadows": [], + "motion": [ + { + "name": "system-state-change", + "value": "platform-default", + "purpose": "Use SwiftUI's restrained state transitions and honor Reduce Motion." + } + ], + "breakpoints": [] + }, + "components": [ + { + "name": "Primary Action", + "kind": "button", + "refersTo": "button-primary", + "description": "The single prominent action in a workflow, mirroring SwiftUI borderedProminent styling.", + "html": "", + "css": ".ds-primary-action { appearance: none; border: 0; border-radius: 8px; padding: 8px 14px; background: #007AFF; color: #FFFFFF; font: 600 14px/1.25 -apple-system, BlinkMacSystemFont, sans-serif; transition: filter 140ms ease, transform 140ms ease; } .ds-primary-action:hover { filter: brightness(1.06); } .ds-primary-action:active { transform: scale(0.98); } .ds-primary-action:focus-visible { outline: 3px solid rgba(0,122,255,0.35); outline-offset: 2px; }" + }, + { + "name": "Secondary Action", + "kind": "button", + "description": "A supporting action with standard system prominence.", + "html": "", + "css": ".ds-secondary-action { appearance: none; border: 1px solid rgba(60,60,67,0.24); border-radius: 8px; padding: 7px 13px; background: rgba(118,118,128,0.12); color: #1D1D1F; font: 500 14px/1.25 -apple-system, BlinkMacSystemFont, sans-serif; transition: background 140ms ease, transform 140ms ease; } .ds-secondary-action:hover { background: rgba(118,118,128,0.18); } .ds-secondary-action:active { transform: scale(0.98); } .ds-secondary-action:focus-visible { outline: 3px solid rgba(0,122,255,0.35); outline-offset: 2px; }" + }, + { + "name": "Search Field", + "kind": "input", + "description": "Native-feeling search for libraries and registries.", + "html": "", + "css": ".ds-search { display: inline-flex; align-items: center; gap: 7px; width: 280px; border-radius: 8px; padding: 7px 10px; background: rgba(118,118,128,0.12); color: rgba(60,60,67,0.65); font: 400 14px/1.25 -apple-system, BlinkMacSystemFont, sans-serif; } .ds-search input { min-width: 0; flex: 1; border: 0; outline: 0; background: transparent; color: #1D1D1F; font: inherit; } .ds-search:focus-within { box-shadow: 0 0 0 3px rgba(0,122,255,0.28); }" + }, + { + "name": "Sidebar Destination", + "kind": "nav", + "description": "A concise system-symbol destination row with a selected state.", + "html": "", + "css": ".ds-sidebar { display: grid; gap: 2px; width: 240px; font: 500 14px/1.25 -apple-system, BlinkMacSystemFont, sans-serif; } .ds-sidebar-row { display: flex; align-items: center; gap: 8px; border: 0; border-radius: 7px; padding: 7px 9px; background: transparent; color: #1D1D1F; text-align: left; } .ds-sidebar-row:hover { background: rgba(118,118,128,0.10); } .ds-sidebar-row.ds-selected { background: #007AFF; color: #FFFFFF; } .ds-sidebar-row:focus-visible { outline: 3px solid rgba(0,122,255,0.35); outline-offset: 1px; }" + }, + { + "name": "Evidence Section", + "kind": "custom", + "description": "A grouped, flat evidence surface using labels and selectable technical values.", + "html": "

Integrity

SHA-256
0f4e…a819
Stored size
428 MB
", + "css": ".ds-evidence { width: 360px; color: #1D1D1F; font: 400 14px/1.35 -apple-system, BlinkMacSystemFont, sans-serif; } .ds-evidence h3 { margin: 0 0 7px; color: rgba(60,60,67,0.72); font-size: 12px; font-weight: 600; text-transform: uppercase; } .ds-evidence dl { margin: 0; overflow: hidden; border: 1px solid rgba(60,60,67,0.16); border-radius: 10px; background: rgba(255,255,255,0.9); } .ds-evidence dl div { display: flex; justify-content: space-between; gap: 20px; padding: 10px 12px; } .ds-evidence dl div + div { border-top: 1px solid rgba(60,60,67,0.12); } .ds-evidence dt { color: rgba(60,60,67,0.72); } .ds-evidence dd { margin: 0; font-family: ui-monospace, SFMono-Regular, monospace; }" + } + ], + "narrative": { + "northStar": "The Native Instrument", + "overview": "Core AI Lab is calm at rest, exact when interrogated, and candid about what it knows. It uses standard platform structure so technical evidence—not ornamental interface chrome—holds attention. Every workspace moves from orientation through preparation and action to observable, verifiable results.", + "keyCharacteristics": [ + "Native and platform-adaptive", + "Evidence-led rather than decorative", + "Progressive disclosure for technical density", + "Explicit prerequisites, progress, cancellation, and failure", + "Keyboard and VoiceOver accessible" + ], + "rules": [ + { + "name": "The One Accent Rule", + "body": "Use one prominent blue action per workflow; supporting actions retain standard prominence.", + "section": "colors" + }, + { + "name": "The Semantic Type Rule", + "body": "Use SwiftUI semantic styles and let Dynamic Type determine runtime size.", + "section": "typography" + }, + { + "name": "The Platform Depth Rule", + "body": "If SwiftUI already communicates the layer, do not add another shadow, blur, stroke, or floating container.", + "section": "elevation" + } + ], + "dos": [ + "Do start each workspace with purpose, current state, and the next meaningful action.", + "Do keep evidence precise, selectable, and visually subordinate to the current task.", + "Do pair status color with text and a familiar symbol." + ], + "donts": [ + "Don't build generic card grids or prompt-first chat surfaces.", + "Don't add ornamental glass, neon gradients, or unexplained status lights.", + "Don't claim execution placement, performance, or cache benefit without evidence." + ] + } +} diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..3f20a36 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,168 @@ +--- +name: Core AI Lab +description: A precise native workbench for evidence-backed Core AI workflows. +colors: + system-blue: "#007AFF" + system-green: "#34C759" + system-orange: "#FF9500" + system-red: "#FF3B30" + on-accent: "#FFFFFF" +typography: + title: + fontFamily: "SF Pro, -apple-system, BlinkMacSystemFont, sans-serif" + fontSize: "1.25rem" + fontWeight: 600 + lineHeight: 1.25 + letterSpacing: "normal" + body: + fontFamily: "SF Pro, -apple-system, BlinkMacSystemFont, sans-serif" + fontSize: "1rem" + fontWeight: 400 + lineHeight: 1.4 + letterSpacing: "normal" + technical: + fontFamily: "SF Mono, ui-monospace, SFMono-Regular, monospace" + fontSize: "0.875rem" + fontWeight: 400 + lineHeight: 1.4 + letterSpacing: "normal" +components: + button-primary: + backgroundColor: "{colors.system-blue}" + textColor: "{colors.on-accent}" + typography: "{typography.body}" + status-success: + textColor: "{colors.system-green}" + typography: "{typography.body}" + status-warning: + textColor: "{colors.system-orange}" + typography: "{typography.body}" + status-error: + textColor: "{colors.system-red}" + typography: "{typography.body}" +--- + +# Design System: Core AI Lab + +## Overview + +**Creative North Star: “The Native Instrument”** + +Core AI Lab should feel like an Apple developer instrument: calm at rest, exact when interrogated, and candid about what it knows. The interface uses standard SwiftUI navigation, forms, lists, tables, toolbars, and status views so platform behavior carries the visual language. Technical density is welcome when it improves comparison or preserves evidence; decoration is not a substitute for hierarchy. + +Each workspace follows a legible sequence: orient, prepare, act, observe, verify. Common actions stay visible while prerequisites, provenance, raw identifiers, and limitations appear where they become relevant. Standard controls inherit the platform's current materials and Liquid Glass behavior automatically; the app does not add ornamental glass effects of its own. + +The system explicitly rejects consumer chat-app framing, generic dashboard card grids, science-fiction control panels, and marketing-first presentation. It should remain recognizably native on iPhone, iPad, and Mac while preserving the rigor expected from a model inspection and validation tool. + +**Key Characteristics:** + +- Native and platform-adaptive +- Evidence-led rather than decorative +- Information-rich with progressive disclosure +- Clear about prerequisites, progress, cancellation, and failure +- Accessible by default, including keyboard and VoiceOver workflows + +## Colors + +Color is semantic and restrained. SwiftUI system backgrounds, labels, separators, fills, and materials are the source of truth for light mode, dark mode, Increase Contrast, and platform variation. + +### Primary + +- **System Blue:** The sole interactive accent for selection, links, focus, and the primary action in a workflow. + +### Status + +- **System Green:** Verified success or a completed, valid state. Pair it with a checkmark and text. +- **System Orange:** Caution, provisional evidence, or a condition that deserves review. Pair it with a warning symbol and explanation. +- **System Red:** Destructive actions and failures that need attention. Never use it as ambient decoration. + +### Neutral + +- Use semantic SwiftUI styles such as primary, secondary, tertiary, background, grouped background, separator, and material. Do not hardcode neutral colors that would break system appearance modes. + +**The One Accent Rule.** A screen gets one prominent blue action. Secondary actions use standard bordered, plain, menu, or navigation treatments. + +**The Redundancy Rule.** Status is always expressed with text and an icon or shape in addition to color. + +## Typography + +**Display Font:** SF Pro through SwiftUI semantic title styles +**Body Font:** SF Pro through SwiftUI semantic body, callout, subheadline, and footnote styles +**Label/Mono Font:** SF Mono through monospaced variants of the nearest semantic style + +**Character:** Familiar, highly legible, and quiet. Typography creates hierarchy without oversized display treatments or gratuitous weight changes. + +### Hierarchy + +- **Title:** Navigation titles and the occasional workspace identity. Prefer the system navigation title before adding an in-content title. +- **Headline:** Section-leading labels and a small number of meaningful summaries. +- **Body:** Primary instructions, results, and user-authored content. Allow Dynamic Type to determine the runtime size. +- **Subheadline / Footnote:** Evidence boundaries, provenance, prerequisites, and supporting detail. +- **Technical:** Commands, hashes, paths, identifiers, tensor shapes, and numeric evidence. Keep them selectable and truncate long identifiers in the middle when horizontal space is limited. + +**The Semantic Type Rule.** Use SwiftUI semantic styles rather than fixed point sizes. Bold is reserved for hierarchy, not routine emphasis. + +## Elevation + +The system is flat by default. Depth comes from platform navigation, grouped form backgrounds, sheets, popovers, menus, and system materials rather than custom shadows. Liquid Glass belongs to the navigation and control layer supplied by SwiftUI; content and evidence surfaces remain solid and readable. A material may back a temporary progress overlay, but it must not become a decorative card treatment. + +**The Platform Depth Rule.** If SwiftUI already communicates the layer, do not add another shadow, blur, stroke, or floating container. + +## Components + +### Navigation + +- Use `NavigationSplitView` for the app shell and group destinations as Library, Build, Run, and Validate. +- Preserve the selected destination, supply a symbol and concise accessibility hint, and let the system manage sidebar selection and toolbar overflow. +- Use a nested sidebar only when a tool has a genuine second-level information architecture, such as Recipe Studio. + +### Grouped Workspaces + +- Use grouped `Form` sections for ordered technical workflows. Every meaningful section has a concise noun label and, where useful, a familiar SF Symbol. +- Lead with current state and prerequisites, then inputs, the primary action, results, and evidence. +- Prefer full-width readable content over grids of small cards. + +### Buttons + +- Use an active verb and a familiar symbol. One action per workflow may use `borderedProminent`; supporting actions remain standard. +- Keep destructive actions in menus, confirmation dialogs, or explicit destructive roles unless immediate visibility is essential. +- While an action runs, show a labeled `ProgressView` that names the current work. Do not leave an active button looking tappable. +- Maintain a minimum 44 by 44 point interactive target on touch platforms and preserve standard keyboard shortcuts on Mac. + +### Inputs and Search + +- Use native fields, pickers, steppers, sliders, file importers, and search. Labels describe the value, while footers explain constraints or consequences. +- Disable an input only when editing it would be invalid during the current state; never use disabled styling as a substitute for an explanation. + +### Empty, Loading, and Failure States + +- Use `ContentUnavailableView` for first-run guidance, empty results, unsupported capabilities, and missing content. +- Loading states name the operation and show progress. Failure titles state what could not be completed; the message gives the exact error or a useful recovery action. +- Treat user-canceled pickers as cancellation, not failure. + +### Evidence and Results + +- Use `LabeledContent`, disclosure groups, lists, and tables for comparable facts. Keep raw commands, hashes, paths, and identifiers monospaced and selectable. +- Distinguish preferences, plans, cache states, and measured evidence in both layout and copy. +- Collapse explanatory detail when the primary task would otherwise be buried, but never hide caveats required to interpret a result honestly. + +## Do's and Don'ts + +### Do: + +- **Do** start every workspace with purpose, current state, and the next meaningful action. +- **Do** rely on standard SwiftUI controls so appearance, Liquid Glass, keyboard behavior, and accessibility adapt with the platform. +- **Do** use one prominent primary action and let toolbars contain only high-value, contextual commands. +- **Do** use Dynamic Type, semantic colors, text-plus-symbol status, and 44-point touch targets. +- **Do** write concise sentence-case labels and task-specific failures such as “Couldn't Import the Recipe Bundle.” +- **Do** keep technical evidence precise, selectable, and visually quieter than the user's current task. + +### Don't: + +- **Don't** imitate consumer AI chat apps that reduce every workflow to a prompt box. +- **Don't** build generic SaaS dashboards from interchangeable card grids and decorative metrics. +- **Don't** use sci-fi control panels with neon gradients, ornamental glass, or unexplained status lights. +- **Don't** create marketing surfaces that hide provenance, prerequisites, or limitations behind optimistic copy. +- **Don't** introduce bespoke controls that replace familiar Apple platform behavior without improving the task. +- **Don't** use color alone, static hourglass symbols, vague “Operation Failed” alerts, or user-visible raw enum values. +- **Don't** claim hardware placement, performance, cache benefit, or model availability without corresponding evidence. From 05885617dad5e0bdb70595c2bff7d1e3a872b96f Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:31:55 +0530 Subject: [PATCH 12/28] Harden responsive and accessible interactions --- .../Audio/AppleAudioWorkspaceView.swift | 47 ++++++++++----- .../AppleDiffusionWorkspaceView.swift | 39 ++++++++----- .../Language/AppleLanguageWorkspaceView.swift | 57 +++++++++++++++---- .../AppleObjectDetectionWorkspaceView.swift | 31 +++++++--- .../AppleSegmentationWorkspaceView.swift | 31 +++++++--- .../ChatterboxGenerationSection.swift | 15 +++-- .../ChatterboxPipelineSection.swift | 2 +- .../Chatterbox/ChatterboxWorkspaceModel.swift | 8 +++ .../Chatterbox/ChatterboxWorkspaceView.swift | 1 + .../DeviceLab/CoreAIDeviceLabView.swift | 4 +- ...CoreAIProjectSpecializationCacheView.swift | 8 ++- .../CoreAIImportedRecipeBundleView.swift | 2 +- ...reAIRuntimeUnsupportedExperienceView.swift | 2 +- .../CoreAIExperienceAdapter.swift | 17 ++++++ 14 files changed, 195 insertions(+), 69 deletions(-) diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift index d37d1d4..2743954 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift @@ -49,20 +49,9 @@ struct AppleAudioWorkspaceView: View { ) Section { - HStack { - Button("Import Wav2Vec2 Model", systemImage: "shippingbox", action: importModel) - Button("Choose Audio", systemImage: "waveform", action: importAudio) - Button("Transcribe", systemImage: "captions.bubble", action: workspace.startTranscription) - .buttonStyle(.borderedProminent) - .disabled(!workspace.canTranscribe) - if workspace.isTranscribing { - Button( - "Cancel", - systemImage: "stop.fill", - role: .destructive, - action: workspace.cancelTranscription - ) - } + ViewThatFits(in: .horizontal) { + inputActions(axis: .horizontal) + inputActions(axis: .vertical) } Text("The static Apple recipe accepts at most five seconds. Audio is decoded, downmixed, and resampled to 16 kHz mono before inference.") @@ -122,7 +111,7 @@ struct AppleAudioWorkspaceView: View { await workspace.loadModel(from: url) } case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -131,6 +120,34 @@ struct AppleAudioWorkspaceView: View { case .success(let url): workspace.selectAudio(url) case .failure(let error): + presentSelectionError(error) + } + } + + private func inputActions(axis: Axis) -> some View { + let layout = axis == .horizontal + ? AnyLayout(HStackLayout()) + : AnyLayout(VStackLayout(alignment: .leading)) + + return layout { + Button("Import Wav2Vec2 Model", systemImage: "shippingbox", action: importModel) + Button("Choose Audio", systemImage: "waveform", action: importAudio) + Button("Transcribe", systemImage: "captions.bubble", action: workspace.startTranscription) + .buttonStyle(.borderedProminent) + .disabled(!workspace.canTranscribe) + if workspace.isTranscribing { + Button( + "Cancel", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelTranscription + ) + } + } + } + + private func presentSelectionError(_ error: any Error) { + if (error as? CocoaError)?.code != .userCancelled { workspace.presentImportError(error) } } diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift index 48a8f06..bba97e6 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift @@ -81,18 +81,9 @@ struct AppleDiffusionWorkspaceView: View { .disabled(!workspace.canEditGenerationInputs) } - HStack { - Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) - .buttonStyle(.borderedProminent) - .disabled(!workspace.canGenerate) - if workspace.isGenerating { - Button( - "Cancel", - systemImage: "stop.fill", - role: .destructive, - action: workspace.cancelGeneration - ) - } + ViewThatFits(in: .horizontal) { + generationActions(axis: .horizontal) + generationActions(axis: .vertical) } } header: { Label("Prompt", systemImage: "text.bubble") @@ -131,7 +122,29 @@ struct AppleDiffusionWorkspaceView: View { await workspace.loadPipeline(from: url) } case .failure(let error): - workspace.presentImportError(error) + if (error as? CocoaError)?.code != .userCancelled { + workspace.presentImportError(error) + } + } + } + + private func generationActions(axis: Axis) -> some View { + let layout = axis == .horizontal + ? AnyLayout(HStackLayout()) + : AnyLayout(VStackLayout(alignment: .leading)) + + return layout { + Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) + .buttonStyle(.borderedProminent) + .disabled(!workspace.canGenerate) + if workspace.isGenerating { + Button( + "Cancel", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelGeneration + ) + } } } } diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift index c3a86fb..60636b3 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift @@ -43,10 +43,9 @@ struct AppleLanguageWorkspaceView: View { ) Section { - HStack { - Button("Import Qwen Bundle", systemImage: "shippingbox", action: importModel) - Button("New Session", systemImage: "arrow.counterclockwise", action: resetSession) - .disabled(workspace.modelName == nil || workspace.isBusy) + ViewThatFits(in: .horizontal) { + modelActions(axis: .horizontal) + modelActions(axis: .vertical) } LabeledContent("macOS export") { @@ -75,13 +74,9 @@ struct AppleLanguageWorkspaceView: View { ) .disabled(!workspace.canEditGenerationInputs) - HStack { - Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) - .buttonStyle(.borderedProminent) - .disabled(!workspace.canGenerate) - if workspace.isGenerating { - Button("Cancel", systemImage: "stop.fill", role: .destructive, action: workspace.cancelGeneration) - } + ViewThatFits(in: .horizontal) { + generationActions(axis: .horizontal) + generationActions(axis: .vertical) } } header: { Label("Prompt", systemImage: "text.bubble") @@ -126,7 +121,45 @@ struct AppleLanguageWorkspaceView: View { await workspace.loadModel(from: url) } case .failure(let error): - workspace.presentImportError(error) + if (error as? CocoaError)?.code != .userCancelled { + workspace.presentImportError(error) + } + } + } + + private func modelActions(axis: Axis) -> some View { + adaptiveLayout(axis: axis) { + Button("Import Qwen Bundle", systemImage: "shippingbox", action: importModel) + Button("New Session", systemImage: "arrow.counterclockwise", action: resetSession) + .disabled(workspace.modelName == nil || workspace.isBusy) + } + } + + private func generationActions(axis: Axis) -> some View { + adaptiveLayout(axis: axis) { + Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) + .buttonStyle(.borderedProminent) + .disabled(!workspace.canGenerate) + if workspace.isGenerating { + Button( + "Cancel", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelGeneration + ) + } + } + } + + private func adaptiveLayout( + axis: Axis, + @ViewBuilder content: () -> Content + ) -> some View { + let layout = axis == .horizontal + ? AnyLayout(HStackLayout()) + : AnyLayout(VStackLayout(alignment: .leading)) + return layout { + content() } } } diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift index 525b16f..bcecf81 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift @@ -30,12 +30,9 @@ struct AppleObjectDetectionWorkspaceView: View { } Section { - HStack(spacing: 12) { - Button("Import YOLOS Model", systemImage: "shippingbox", action: importModel) - Button("Choose Image", systemImage: "photo", action: importImage) - Button("Run Detection", systemImage: "play.fill", action: runDetection) - .buttonStyle(.borderedProminent) - .disabled(!workspace.canRun) + ViewThatFits(in: .horizontal) { + inputActions(axis: .horizontal) + inputActions(axis: .vertical) } .disabled(workspace.isBusy) } header: { @@ -113,7 +110,7 @@ struct AppleObjectDetectionWorkspaceView: View { await workspace.loadModel(from: url) } case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -122,6 +119,26 @@ struct AppleObjectDetectionWorkspaceView: View { case .success(let url): workspace.loadImage(from: url) case .failure(let error): + presentSelectionError(error) + } + } + + private func inputActions(axis: Axis) -> some View { + let layout = axis == .horizontal + ? AnyLayout(HStackLayout(spacing: 12)) + : AnyLayout(VStackLayout(alignment: .leading)) + + return layout { + Button("Import YOLOS Model", systemImage: "shippingbox", action: importModel) + Button("Choose Image", systemImage: "photo", action: importImage) + Button("Run Detection", systemImage: "play.fill", action: runDetection) + .buttonStyle(.borderedProminent) + .disabled(!workspace.canRun) + } + } + + private func presentSelectionError(_ error: any Error) { + if (error as? CocoaError)?.code != .userCancelled { workspace.presentImportError(error) } } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift index c21ac76..5af7b7b 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift @@ -45,12 +45,9 @@ struct AppleSegmentationWorkspaceView: View { ) Section { - HStack { - Button("Import Model Bundle", systemImage: "shippingbox", action: importModel) - Button("Choose Image", systemImage: "photo", action: importImage) - Button("Run Segmentation", systemImage: "play.fill", action: runSegmentation) - .buttonStyle(.borderedProminent) - .disabled(!workspace.canRun) + ViewThatFits(in: .horizontal) { + inputActions(axis: .horizontal) + inputActions(axis: .vertical) } .disabled(workspace.isBusy) } header: { @@ -127,7 +124,7 @@ struct AppleSegmentationWorkspaceView: View { await workspace.loadModel(from: url) } case .failure(let error): - workspace.presentImportError(error) + presentSelectionError(error) } } @@ -136,6 +133,26 @@ struct AppleSegmentationWorkspaceView: View { case .success(let url): workspace.loadImage(from: url) case .failure(let error): + presentSelectionError(error) + } + } + + private func inputActions(axis: Axis) -> some View { + let layout = axis == .horizontal + ? AnyLayout(HStackLayout()) + : AnyLayout(VStackLayout(alignment: .leading)) + + return layout { + Button("Import Model Bundle", systemImage: "shippingbox", action: importModel) + Button("Choose Image", systemImage: "photo", action: importImage) + Button("Run Segmentation", systemImage: "play.fill", action: runSegmentation) + .buttonStyle(.borderedProminent) + .disabled(!workspace.canRun) + } + } + + private func presentSelectionError(_ error: any Error) { + if (error as? CocoaError)?.code != .userCancelled { workspace.presentImportError(error) } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift index 5acf8fa..9a45645 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift @@ -3,6 +3,7 @@ import SwiftUI struct ChatterboxGenerationSection: View { let canGenerate: Bool let isWorking: Bool + let workingActionTitle: String let statusMessage: String let result: ChatterboxGenerationResult? let isPlaying: Bool @@ -13,7 +14,7 @@ struct ChatterboxGenerationSection: View { Section { Button(action: generateAction) { Label { - Text(isWorking ? "Generating Speech…" : "Generate Speech") + Text(isWorking ? workingActionTitle : "Generate Speech") } icon: { if isWorking { ProgressView() @@ -34,20 +35,18 @@ struct ChatterboxGenerationSection: View { if let result { Button( - isPlaying ? "Stop playback" : "Play generated speech", + isPlaying ? "Stop Playback" : "Play Generated Speech", systemImage: isPlaying ? "stop.fill" : "speaker.wave.3.fill", action: playbackAction ) LabeledContent( - "Generation", - value: result.elapsedTime, - format: .number.precision(.fractionLength(2)) + "Generation time", + value: "\(result.elapsedTime.formatted(.number.precision(.fractionLength(2)))) seconds" ) LabeledContent( - "Audio", - value: result.audioDuration, - format: .number.precision(.fractionLength(2)) + "Audio duration", + value: "\(result.audioDuration.formatted(.number.precision(.fractionLength(2)))) seconds" ) LabeledContent( "Real-time factor", diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift index 4b09443..7501752 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift @@ -38,7 +38,7 @@ struct ChatterboxPipelineSection: View { switch state { case .preparing: - ProgressView("Loading the recipe contract") + ProgressView("Loading the recipe contract…") case .failed: Label("Pipeline details are unavailable", systemImage: "xmark.octagon") .foregroundStyle(.red) diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift index d85a051..9d0adee 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceModel.swift @@ -52,6 +52,14 @@ final class ChatterboxWorkspaceModel { && !isWorking } + var workingActionTitle: String { + if case .preparing = modelState { + "Preparing Models…" + } else { + "Generating Speech…" + } + } + func prepare() async { guard !hasPrepared else { return diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift index 18ca032..35f5b6c 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift @@ -19,6 +19,7 @@ struct ChatterboxWorkspaceView: View { ChatterboxGenerationSection( canGenerate: model.canGenerate, isWorking: model.isWorking, + workingActionTitle: model.workingActionTitle, statusMessage: model.statusMessage, result: model.generatedResult, isPlaying: model.isPlaying, diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift index 792dfdb..add70ce 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift @@ -42,7 +42,9 @@ struct CoreAIDeviceLabView: View { guard let url = urls.first else { return } workspace.importEvidence(from: url) case .failure(let error): - workspace.reportImportFailure(error) + if (error as? CocoaError)?.code != .userCancelled { + workspace.reportImportFailure(error) + } } } } diff --git a/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift b/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift index 6ee7c93..3da2566 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift @@ -8,7 +8,7 @@ struct CoreAIProjectSpecializationCacheView: View { let removeAll: () -> Void var body: some View { - Section("Specialization Cache") { + Section { if link.specializationCaches.isEmpty { Text("Specialize this project artifact to register a cache entry.") .foregroundStyle(.secondary) @@ -31,9 +31,11 @@ struct CoreAIProjectSpecializationCacheView: View { } if isUpdatingCache { - Label("Updating Core AI cache…", systemImage: "hourglass") - .foregroundStyle(.secondary) + ProgressView("Updating Core AI cache…") + .accessibilityAddTraits(.updatesFrequently) } + } header: { + Label("Specialization Cache", systemImage: "cpu") } } } diff --git a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift index f21602b..21b8b65 100644 --- a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift +++ b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift @@ -31,7 +31,7 @@ struct CoreAIImportedRecipeBundleView: View { Text(reference.id) .font(.callout) .bold() - Text("\(reference.language.rawValue) · \(reference.relativePath) · \(reference.entryPoint)") + Text("\(reference.language.rawValue.capitalized) · \(reference.relativePath) · \(reference.entryPoint)") .font(.footnote.monospaced()) .foregroundStyle(.secondary) .textSelection(.enabled) diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift index 2305c45..c1fcab4 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeUnsupportedExperienceView.swift @@ -8,7 +8,7 @@ struct CoreAIRuntimeUnsupportedExperienceView: View { "Adapter Not Available in This Build", systemImage: "exclamationmark.triangle", description: Text( - "The recipe maps \(mapping.experience.modelIdentifier) to \(mapping.experience.adapter.rawValue), but this build cannot resolve that model preset." + "The recipe maps \(mapping.experience.modelIdentifier) to \(mapping.experience.adapter.title), but this build cannot resolve that model preset." ) ) .navigationTitle(mapping.experience.title) diff --git a/CoreAILabCore/RuntimeStudio/CoreAIExperienceAdapter.swift b/CoreAILabCore/RuntimeStudio/CoreAIExperienceAdapter.swift index 07bb898..dfc3505 100644 --- a/CoreAILabCore/RuntimeStudio/CoreAIExperienceAdapter.swift +++ b/CoreAILabCore/RuntimeStudio/CoreAIExperienceAdapter.swift @@ -8,6 +8,23 @@ enum CoreAIExperienceAdapter: String, Codable, Hashable, Sendable { case appleSegmentation case genericFunctionWorkbench + var title: String { + switch self { + case .appleAudioTranscription: + "Apple Audio Transcription" + case .appleDiffusion: + "Apple Diffusion" + case .appleLanguage: + "Apple Language" + case .appleObjectDetection: + "Apple Object Detection" + case .appleSegmentation: + "Apple Segmentation" + case .genericFunctionWorkbench: + "Function Workbench" + } + } + func supports(_ workload: CoreAIExperienceWorkload) -> Bool { switch self { case .appleAudioTranscription: From b9f97e0ed4552a2d47202de7a645951b15c80cba Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:34:40 +0530 Subject: [PATCH 13/28] Update the runtime copy contract --- CoreAILabTests/CoreAIExperienceRegistryTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CoreAILabTests/CoreAIExperienceRegistryTests.swift b/CoreAILabTests/CoreAIExperienceRegistryTests.swift index 30d6e7b..4e20995 100644 --- a/CoreAILabTests/CoreAIExperienceRegistryTests.swift +++ b/CoreAILabTests/CoreAIExperienceRegistryTests.swift @@ -158,7 +158,7 @@ struct CoreAIExperienceRegistryTests { ) #expect(route.unavailableDescription.contains("missing-experience")) - #expect(route.unavailableDescription.contains("current runtime registry")) + #expect(route.unavailableDescription.contains("runtime registry")) } private func makeManifest( From 5c0a22424e17a3b78b21b572061a7fb5ad29174f Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:39:21 +0530 Subject: [PATCH 14/28] Support every iPad interface orientation --- CoreAIFrameworkLab.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CoreAIFrameworkLab.xcodeproj/project.pbxproj b/CoreAIFrameworkLab.xcodeproj/project.pbxproj index a2dc50d..7f73409 100644 --- a/CoreAIFrameworkLab.xcodeproj/project.pbxproj +++ b/CoreAIFrameworkLab.xcodeproj/project.pbxproj @@ -3558,6 +3558,8 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -3804,6 +3806,8 @@ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", From b0dbabafdcefa7c142d04e83b298e380ad6e84fa Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 10:39:47 +0530 Subject: [PATCH 15/28] Polish design system documentation --- DESIGN.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/DESIGN.md b/DESIGN.md index 3f20a36..421c135 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -86,9 +86,9 @@ Color is semantic and restrained. SwiftUI system backgrounds, labels, separators ## Typography -**Display Font:** SF Pro through SwiftUI semantic title styles -**Body Font:** SF Pro through SwiftUI semantic body, callout, subheadline, and footnote styles -**Label/Mono Font:** SF Mono through monospaced variants of the nearest semantic style +- **Display Font:** SF Pro through SwiftUI semantic title styles +- **Body Font:** SF Pro through SwiftUI semantic body, callout, subheadline, and footnote styles +- **Label/Mono Font:** SF Mono through monospaced variants of the nearest semantic style **Character:** Familiar, highly legible, and quiet. Typography creates hierarchy without oversized display treatments or gratuitous weight changes. From c8a66a3ea5e4a5f107e3e313f4f817e680e97ea6 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:01:38 +0530 Subject: [PATCH 16/28] Add contextual workspace inspector --- CoreAIFrameworkLab.xcodeproj/project.pbxproj | 6 ++ CoreAILab/ContentView.swift | 27 ++++- CoreAILab/CoreAILabSection.swift | 103 +++++++++++++++++++ CoreAILab/CoreAIWorkspaceInspectorView.swift | 42 ++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 CoreAILab/CoreAIWorkspaceInspectorView.swift diff --git a/CoreAIFrameworkLab.xcodeproj/project.pbxproj b/CoreAIFrameworkLab.xcodeproj/project.pbxproj index 7f73409..1a9f585 100644 --- a/CoreAIFrameworkLab.xcodeproj/project.pbxproj +++ b/CoreAIFrameworkLab.xcodeproj/project.pbxproj @@ -151,6 +151,7 @@ 0C134E67620FC0A3B0E60716 /* CoreAIFunctionRunResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68CCDF520342FD26442812A9 /* CoreAIFunctionRunResult.swift */; }; 0CA626CEC6DCB9DD04BEC842 /* CoreAIConversionLogEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = A02C5555829471BC72BC6DD3 /* CoreAIConversionLogEntry.swift */; }; 0D4A159FDF138A057E6FBD05 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7FB5157DD183D55DA2447D /* ContentView.swift */; }; + 8FCF2AB42FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FCF2AB22FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift */; }; 0E7D6BCC627B4110452820F8 /* CoreAIConversionEvidenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0840FFCF985C8BD6D753BB /* CoreAIConversionEvidenceView.swift */; }; 0EA3F7ACB5DB93864BEB972F /* CoreAITensorScalarSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D016EBAA024026EC05DF16A /* CoreAITensorScalarSupport.swift */; }; 0EE6E3D34C93FB57AF51CA51 /* CoreAIConversionJobStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2B4E458E18BCA4B37A8998A /* CoreAIConversionJobStoreError.swift */; }; @@ -308,6 +309,7 @@ 3E7EA385F765D82D1650E92C /* CoreAIRuntimeMetricEvidence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0877E38482D768EC4F68D71 /* CoreAIRuntimeMetricEvidence.swift */; }; 3EC275FBC73185FC3063A7FE /* SpeakerDiarizationCAMPPlusEmbeddingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4ED73F083BBA3B4FF8751CF /* SpeakerDiarizationCAMPPlusEmbeddingModel.swift */; }; 3F0C34F96B21DE35BE1E9A04 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7FB5157DD183D55DA2447D /* ContentView.swift */; }; + 8FCF2AB32FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FCF2AB22FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift */; }; 3F725CD5200A0B349E7A76CB /* CoreAIConversionProcessEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C64E05BE9C31ED8C4EBE3199 /* CoreAIConversionProcessEvent.swift */; }; 3FBBF65C7E3750853D7CA39C /* CoreAISwiftPackageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18EDE7C13B56DD6321F3DFE0 /* CoreAISwiftPackageGenerator.swift */; }; 3FE119F9A41259FA9F296234 /* CoreAIRuntimeMetricEvidence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0877E38482D768EC4F68D71 /* CoreAIRuntimeMetricEvidence.swift */; }; @@ -1086,6 +1088,7 @@ 0E0BDACAD345692BD0CF2CD0 /* CoreAINECompatibilityCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreAINECompatibilityCheck.swift; sourceTree = ""; }; 0E13A77B8F8BA388C8EA12A9 /* CoreAIProjectRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreAIProjectRoute.swift; sourceTree = ""; }; 0E7FB5157DD183D55DA2447D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 8FCF2AB22FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreAIWorkspaceInspectorView.swift; sourceTree = ""; }; 0EA900D41D26D5440F5B873D /* AppleModelLibraryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleModelLibraryModel.swift; sourceTree = ""; }; 0F3153C86C4BB1C0F56A8514 /* CoreAIRuntimeLifecycleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreAIRuntimeLifecycleView.swift; sourceTree = ""; }; 0F5B304A4D84B06BFE71C5A8 /* CoreAIPipelineCodec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreAIPipelineCodec.swift; sourceTree = ""; }; @@ -2091,6 +2094,7 @@ isa = PBXGroup; children = ( 0E7FB5157DD183D55DA2447D /* ContentView.swift */, + 8FCF2AB22FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift */, 7716B246D5E600F1622AB1D5 /* Core AI Lab.icon */, 21EF809F4325A5DED84D6974 /* CoreAILabApp.swift */, 3865FA28608466F39B8B3251 /* CoreAILabSection.swift */, @@ -2765,6 +2769,7 @@ 27368950A09746EC0B06C643 /* ChatterboxWorkspaceModel.swift in Sources */, 862DF4D00DCD7711215B7B2F /* ChatterboxWorkspaceView.swift in Sources */, 3F0C34F96B21DE35BE1E9A04 /* ContentView.swift in Sources */, + 8FCF2AB32FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift in Sources */, D236381C42FD8790C405E461 /* CoreAIAheadOfTimeCompileScriptGenerator.swift in Sources */, 3CF411188EC9E73089394377 /* CoreAIArtifactDigest.swift in Sources */, 5F9C403770360C83B2236A22 /* CoreAIArtifactDigesting.swift in Sources */, @@ -3210,6 +3215,7 @@ DBDABC2C289C2DFD75A28166 /* ChatterboxWorkspaceModel.swift in Sources */, A282A0775612CEAB4AAD38B9 /* ChatterboxWorkspaceView.swift in Sources */, 0D4A159FDF138A057E6FBD05 /* ContentView.swift in Sources */, + 8FCF2AB42FEBA26500094256 /* CoreAIWorkspaceInspectorView.swift in Sources */, 9969D80D026481CEAD9F51CF /* CoreAIAheadOfTimeCompileScriptGenerator.swift in Sources */, DAA3634A83A4ACCF2D541968 /* CoreAIArtifactDigest.swift in Sources */, 586E37FC732BACF003A4F168 /* CoreAIArtifactDigesting.swift in Sources */, diff --git a/CoreAILab/ContentView.swift b/CoreAILab/ContentView.swift index a0489a4..fc05f12 100644 --- a/CoreAILab/ContentView.swift +++ b/CoreAILab/ContentView.swift @@ -5,7 +5,12 @@ struct ContentView: View { @SceneStorage("CoreAILab.selectedSection") private var selection: CoreAILabSection? + @SceneStorage("CoreAILab.isWorkspaceInspectorPresented") + private var isWorkspaceInspectorPresented = false + var body: some View { + let selectedSection = selection ?? .projects + NavigationSplitView { List(selection: $selection) { Section("Library") { @@ -51,7 +56,7 @@ struct ContentView: View { .listStyle(.sidebar) .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 280) } detail: { - switch selection ?? .projects { + switch selectedSection { case .projects: CoreAIProjectLibraryView() case .appleModels: @@ -81,10 +86,30 @@ struct ContentView: View { .navigationSplitViewStyle(.prominentDetail) .formStyle(.grouped) .tint(.blue) + .inspector(isPresented: $isWorkspaceInspectorPresented) { + CoreAIWorkspaceInspectorView(section: selectedSection) + } + .toolbar { + ToolbarItem(placement: .secondaryAction) { + Button( + "Workspace Inspector", + systemImage: "sidebar.trailing", + action: toggleWorkspaceInspector + ) + .help("Show the selected workspace's workflow and evidence boundary") +#if os(macOS) + .keyboardShortcut("0", modifiers: [.command, .option]) +#endif + } + } #if os(macOS) .frame(minWidth: 1_000, minHeight: 680) #endif } + + private func toggleWorkspaceInspector() { + isWorkspaceInspectorPresented.toggle() + } } #Preview { diff --git a/CoreAILab/CoreAILabSection.swift b/CoreAILab/CoreAILabSection.swift index 7b5bba9..85e4270 100644 --- a/CoreAILab/CoreAILabSection.swift +++ b/CoreAILab/CoreAILabSection.swift @@ -93,4 +93,107 @@ enum CoreAILabSection: String, CaseIterable, Hashable, Identifiable { "Plan iPhone delivery and import physical-device evidence." } } + + var areaTitle: String { + switch self { + case .projects, .appleModels, .recipes: + "Library" + case .conversion, .recipeStudio: + "Build" + case .chatterbox, .diarization, .runtime: + "Run" + case .assetInspector, .deviceLab: + "Validate" + } + } + + var workflowSteps: [String] { + switch self { + case .projects: + [ + "Create or open a project", + "Import checked artifacts", + "Review runs and evidence" + ] + case .appleModels: + [ + "Choose an Apple recipe", + "Review its requirements and provenance", + "Convert it or open its runtime" + ] + case .recipes: + [ + "Choose a curated recipe", + "Review its code and provenance", + "Import the approved bundle" + ] + case .conversion: + [ + "Configure the recipe", + "Validate the local environment", + "Convert and verify the artifacts" + ] + case .recipeStudio: + [ + "Define the source and contracts", + "Resolve unsupported operations", + "Compose and validate the pipeline" + ] + case .chatterbox: + [ + "Prepare the bundled models", + "Write expressive speech", + "Generate and review local audio" + ] + case .diarization: + [ + "Import local media", + "Analyze anonymous speakers", + "Review the speaker timeline" + ] + case .assetInspector: + [ + "Open an .aimodel package", + "Inspect descriptors and compute types", + "Specialize and verify cache state" + ] + case .runtime: + [ + "Choose an experience", + "Provide its assets and inputs", + "Run and record measured evidence" + ] + case .deviceLab: + [ + "Define the physical target", + "Plan asset delivery", + "Import device-run evidence" + ] + } + } + + var evidenceBoundary: String { + switch self { + case .projects: + "Checksummed storage preserves artifacts and provenance. A stored artifact is not a runtime measurement." + case .appleModels: + "Catalog entries describe pinned Apple recipes. Core AI Lab does not bundle the source model weights." + case .recipes: + "A recipe documents a conversion path. Review its code and upstream license before approving an import." + case .conversion: + "The command, process log, checksums, and validation findings are evidence. A planned command is not a completed conversion." + case .recipeStudio: + "Structural validation checks the authored contract. It does not prove that conversion or runtime execution will succeed." + case .chatterbox: + "Generated audio comes from the bundled local pipeline. Cache reuse does not prove a speed or memory improvement." + case .diarization: + "Speaker labels are anonymous clusters inferred from local media, not verified identities." + case .assetInspector: + "Descriptors and cache state come from Core AI. A preferred compute unit does not prove hardware placement." + case .runtime: + "Only completed runs produce measured timing. Setup choices and comparison identities remain contextual metadata." + case .deviceLab: + "Target preferences and storage plans are proposals. Imported runner output is the physical-device evidence." + } + } } diff --git a/CoreAILab/CoreAIWorkspaceInspectorView.swift b/CoreAILab/CoreAIWorkspaceInspectorView.swift new file mode 100644 index 0000000..b8351d6 --- /dev/null +++ b/CoreAILab/CoreAIWorkspaceInspectorView.swift @@ -0,0 +1,42 @@ +import SwiftUI + +struct CoreAIWorkspaceInspectorView: View { + let section: CoreAILabSection + + var body: some View { + Form { + Section { + LabeledContent("Area", value: section.areaTitle) + Text(section.summary) + .foregroundStyle(.secondary) + } header: { + Label(section.title, systemImage: section.systemImage) + } + + Section { + ForEach(section.workflowSteps.indices, id: \.self) { index in + Label( + section.workflowSteps[index], + systemImage: "\(index + 1).circle" + ) + } + } header: { + Label("Workflow", systemImage: "point.3.connected.trianglepath.dotted") + } + + Section { + Text(section.evidenceBoundary) + .foregroundStyle(.secondary) + } header: { + Label("Evidence Boundary", systemImage: "checkmark.seal") + } + } + .formStyle(.grouped) + .navigationTitle("Workspace") + .inspectorColumnWidth(min: 260, ideal: 300, max: 360) + } +} + +#Preview { + CoreAIWorkspaceInspectorView(section: .runtime) +} From e2784de8a195b78697140837c6ba0d83ae8adef4 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:06:05 +0530 Subject: [PATCH 17/28] Keep primary actions in the toolbar --- .../Audio/AppleAudioWorkspaceView.swift | 24 +++++++++++ .../AppleDiffusionWorkspaceView.swift | 22 ++++++++++ .../Language/AppleLanguageWorkspaceView.swift | 22 ++++++++++ .../AppleObjectDetectionWorkspaceView.swift | 11 +++++ .../AppleSegmentationWorkspaceView.swift | 11 +++++ .../ChatterboxGenerationSection.swift | 2 + .../Chatterbox/ChatterboxWorkspaceView.swift | 13 ++++++ .../CoreAIConversionSetupView.swift | 22 ---------- .../CoreAIConversionWorkspaceView.swift | 22 ++++++++++ .../SpeakerDiarizationImportControls.swift | 2 + .../SpeakerDiarizationWorkspaceView.swift | 13 ++++++ .../CoreAIFunctionWorkbenchView.swift | 43 ++++++++++++++----- 12 files changed, 174 insertions(+), 33 deletions(-) diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift index 2743954..81ceb1a 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift @@ -72,6 +72,28 @@ struct AppleAudioWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("Audio Transcription") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + if workspace.isTranscribing { + Button( + "Cancel Transcription", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelTranscription + ) + } else { + Button( + "Transcribe", + systemImage: "captions.bubble", + action: workspace.startTranscription + ) + .disabled(!workspace.canTranscribe) + .help(workspace.statusMessage) + } + } +#endif + } .fileImporter( isPresented: $isImportingModel, allowedContentTypes: [.coreAIModelAsset, .folder] @@ -132,6 +154,7 @@ struct AppleAudioWorkspaceView: View { return layout { Button("Import Wav2Vec2 Model", systemImage: "shippingbox", action: importModel) Button("Choose Audio", systemImage: "waveform", action: importAudio) +#if !os(macOS) Button("Transcribe", systemImage: "captions.bubble", action: workspace.startTranscription) .buttonStyle(.borderedProminent) .disabled(!workspace.canTranscribe) @@ -143,6 +166,7 @@ struct AppleAudioWorkspaceView: View { action: workspace.cancelTranscription ) } +#endif } } diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift index bba97e6..00fa118 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift @@ -81,10 +81,12 @@ struct AppleDiffusionWorkspaceView: View { .disabled(!workspace.canEditGenerationInputs) } +#if !os(macOS) ViewThatFits(in: .horizontal) { generationActions(axis: .horizontal) generationActions(axis: .vertical) } +#endif } header: { Label("Prompt", systemImage: "text.bubble") } @@ -93,6 +95,24 @@ struct AppleDiffusionWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("Diffusion Playground") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + if workspace.isGenerating { + Button( + "Cancel Generation", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelGeneration + ) + } else { + Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) + .disabled(!workspace.canGenerate) + .help(workspace.statusMessage) + } + } +#endif + } .fileImporter( isPresented: $isImportingPipeline, allowedContentTypes: [.folder] @@ -128,6 +148,7 @@ struct AppleDiffusionWorkspaceView: View { } } +#if !os(macOS) private func generationActions(axis: Axis) -> some View { let layout = axis == .horizontal ? AnyLayout(HStackLayout()) @@ -147,4 +168,5 @@ struct AppleDiffusionWorkspaceView: View { } } } +#endif } diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift index 60636b3..6cdaa38 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift @@ -74,10 +74,12 @@ struct AppleLanguageWorkspaceView: View { ) .disabled(!workspace.canEditGenerationInputs) +#if !os(macOS) ViewThatFits(in: .horizontal) { generationActions(axis: .horizontal) generationActions(axis: .vertical) } +#endif } header: { Label("Prompt", systemImage: "text.bubble") } @@ -86,6 +88,24 @@ struct AppleLanguageWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("\(workspace.example.title) Language Model") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + if workspace.isGenerating { + Button( + "Cancel Generation", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelGeneration + ) + } else { + Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) + .disabled(!workspace.canGenerate) + .help(workspace.statusMessage) + } + } +#endif + } .fileImporter( isPresented: $isImportingModel, allowedContentTypes: [.folder] @@ -135,6 +155,7 @@ struct AppleLanguageWorkspaceView: View { } } +#if !os(macOS) private func generationActions(axis: Axis) -> some View { adaptiveLayout(axis: axis) { Button("Generate", systemImage: "play.fill", action: workspace.startGeneration) @@ -150,6 +171,7 @@ struct AppleLanguageWorkspaceView: View { } } } +#endif private func adaptiveLayout( axis: Axis, diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift index bcecf81..7f30f0e 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift @@ -71,6 +71,15 @@ struct AppleObjectDetectionWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("Object Detection") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + Button("Run Detection", systemImage: "play.fill", action: runDetection) + .disabled(!workspace.canRun) + .help(workspace.statusMessage) + } +#endif + } .fileImporter( isPresented: $isImportingModel, allowedContentTypes: [.coreAIModelAsset, .folder] @@ -131,9 +140,11 @@ struct AppleObjectDetectionWorkspaceView: View { return layout { Button("Import YOLOS Model", systemImage: "shippingbox", action: importModel) Button("Choose Image", systemImage: "photo", action: importImage) +#if !os(macOS) Button("Run Detection", systemImage: "play.fill", action: runDetection) .buttonStyle(.borderedProminent) .disabled(!workspace.canRun) +#endif } } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift index 5af7b7b..30cceb7 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift @@ -70,6 +70,15 @@ struct AppleSegmentationWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("\(workspace.example.title) Segmentation") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + Button("Run Segmentation", systemImage: "play.fill", action: runSegmentation) + .disabled(!workspace.canRun) + .help(workspace.statusMessage) + } +#endif + } .fileImporter( isPresented: $isImportingModel, allowedContentTypes: [.folder] @@ -145,9 +154,11 @@ struct AppleSegmentationWorkspaceView: View { return layout { Button("Import Model Bundle", systemImage: "shippingbox", action: importModel) Button("Choose Image", systemImage: "photo", action: importImage) +#if !os(macOS) Button("Run Segmentation", systemImage: "play.fill", action: runSegmentation) .buttonStyle(.borderedProminent) .disabled(!workspace.canRun) +#endif } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift index 9a45645..9f919d2 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift @@ -12,6 +12,7 @@ struct ChatterboxGenerationSection: View { var body: some View { Section { +#if !os(macOS) Button(action: generateAction) { Label { Text(isWorking ? workingActionTitle : "Generate Speech") @@ -26,6 +27,7 @@ struct ChatterboxGenerationSection: View { } .buttonStyle(.borderedProminent) .disabled(!canGenerate) +#endif if isWorking { Text(statusMessage) diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift index 35f5b6c..de90d72 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift @@ -29,6 +29,19 @@ struct ChatterboxWorkspaceView: View { } .formStyle(.grouped) .navigationTitle(model.recipeManifest?.displayName ?? "Text to Speech") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + Button( + "Generate Speech", + systemImage: "play.circle.fill", + action: model.generate + ) + .disabled(!model.canGenerate) + .help(model.statusMessage) + } +#endif + } .task { await model.prepare() } diff --git a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift index c62a42e..43e278b 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift @@ -99,28 +99,6 @@ struct CoreAIConversionSetupView: View { Label("Environment", systemImage: "checkmark.shield") } - Section { - if workspace.canCancelConversion { - Button( - "Cancel Conversion", - systemImage: "stop.fill", - role: .destructive, - action: workspace.cancelConversion - ) - .keyboardShortcut(.cancelAction) - } else { - Button( - "Start Conversion", - systemImage: "play.fill", - action: workspace.startConversion - ) - .buttonStyle(.borderedProminent) - .keyboardShortcut(.return, modifiers: .command) - .disabled(!workspace.canStartConversion) - } - } footer: { - Text("The first run can create a Python environment and download many gigabytes. The evidence pane keeps the original converter output visible.") - } } .formStyle(.grouped) } diff --git a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift index 1244fc6..536c123 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionWorkspaceView.swift @@ -43,6 +43,28 @@ struct CoreAIConversionWorkspaceView: View { } } .navigationTitle("Convert") + .toolbar { + ToolbarItem(placement: .primaryAction) { + if workspace.canCancelConversion { + Button( + "Cancel Conversion", + systemImage: "stop.fill", + role: .cancel, + action: workspace.cancelConversion + ) + .keyboardShortcut(.cancelAction) + } else { + Button( + "Start Conversion", + systemImage: "play.fill", + action: workspace.startConversion + ) + .disabled(!workspace.canStartConversion) + .help(workspace.statusMessage) + .keyboardShortcut(.return, modifiers: .command) + } + } + } .navigationDestination(for: CoreAIConversionArtifact.self) { artifact in CoreAIConversionArtifactDestinationView(artifact: artifact) } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift index dd7541a..1f8c2e9 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationImportControls.swift @@ -15,9 +15,11 @@ struct SpeakerDiarizationImportControls: View { .disabled(!canImportModel) Button("Choose Audio or Video", systemImage: "waveform", action: importMediaAction) .disabled(!canImportMedia) +#if !os(macOS) Button("Run Diarization", systemImage: "person.2.wave.2", action: runAction) .buttonStyle(.borderedProminent) .disabled(!canRunDiarization) +#endif } } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift index 719fffa..7114493 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift @@ -49,6 +49,19 @@ struct SpeakerDiarizationWorkspaceView: View { } .formStyle(.grouped) .navigationTitle("Diarization") + .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + Button( + "Run Diarization", + systemImage: "person.2.wave.2", + action: workspace.startDiarization + ) + .disabled(!workspace.canRunDiarization) + .help(workspace.statusMessage) + } +#endif + } .task { await workspace.prepareBundledModel() } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift index 7b00c09..512cc04 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift @@ -125,6 +125,18 @@ struct CoreAIFunctionWorkbenchView: View { isDisabled: workspace.phase.isBusy ) + if workspace.phase == .running { + Section { + ProgressView( + "Running \(workspace.selectedFunctionName ?? "function")…" + ) + .accessibilityAddTraits(.updatesFrequently) + } header: { + Label("Run", systemImage: "play.fill") + } + } + +#if !os(macOS) Section { Button( "Run Function", @@ -133,20 +145,10 @@ struct CoreAIFunctionWorkbenchView: View { ) .buttonStyle(.borderedProminent) .disabled(!workspace.canRun) - - if workspace.phase == .running { - ProgressView( - "Running \(workspace.selectedFunctionName ?? "function")…" - ) - .accessibilityAddTraits(.updatesFrequently) - } } header: { Label("Run", systemImage: "play.fill") - } footer: { - Text( - "Generated inputs are synthetic contract probes, not semantically correct task data. Core AI inference itself cannot be canceled once started." - ) } +#endif CoreAIFunctionBenchmarkControlsView(workspace: workspace) @@ -192,6 +194,24 @@ struct CoreAIFunctionWorkbenchView: View { } .navigationTitle("Function Workbench") .toolbar { +#if os(macOS) + ToolbarItem(placement: .primaryAction) { + Button("Run Function", systemImage: "play.fill", action: runFunction) + .disabled(!workspace.canRun) + .help( + "Run synthetic contract inputs. Core AI inference cannot be canceled once started." + ) + } + ToolbarItem(placement: .secondaryAction) { + Button("Open Model", systemImage: "folder", action: openModelPicker) + .disabled( + workspace.phase.isBusy + || workspace.assetWorkspace.phase.isBusy + || workspace.isExportingIntegration + ) + .keyboardShortcut("o", modifiers: .command) + } +#else ToolbarItem(placement: .primaryAction) { Button("Open Model", systemImage: "folder", action: openModelPicker) .disabled( @@ -201,6 +221,7 @@ struct CoreAIFunctionWorkbenchView: View { ) .keyboardShortcut("o", modifiers: .command) } +#endif } .fileImporter( isPresented: $isImportingModel, From 54e2c56f710949ebb6d631b9ed10b559e4a84fee Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:16:36 +0530 Subject: [PATCH 18/28] Remove redundant secondary copy --- .../AppleModelCatalogSourceView.swift | 30 +++++---------- .../AppleModels/AppleModelDetailView.swift | 32 ++++++++-------- .../Features/AppleModels/AppleModelRow.swift | 33 +++++------------ .../Audio/AppleAudioWorkspaceView.swift | 6 +-- .../AppleDiffusionWorkspaceView.swift | 9 +---- .../Language/AppleLanguageWorkspaceView.swift | 3 -- .../AppleObjectDetectionHeaderView.swift | 7 +--- .../AppleSegmentationQueryControlsView.swift | 6 +-- .../AppleSegmentationWorkspaceView.swift | 13 ------- .../CoreAISpecializationControlsView.swift | 29 ++++----------- .../ChatterboxGenerationSection.swift | 3 +- .../Chatterbox/ChatterboxModelSection.swift | 5 +-- .../ChatterboxPipelineSection.swift | 25 +------------ .../Chatterbox/ChatterboxPromptSection.swift | 7 ++-- .../Chatterbox/ChatterboxWorkspaceView.swift | 2 - .../CoreAIConversionCommandView.swift | 7 ++-- .../CoreAIConversionSetupView.swift | 6 +-- .../CoreAIConversionStatusView.swift | 5 +-- .../DeviceLab/CoreAIDeviceEvidenceView.swift | 6 +-- .../DeviceLab/CoreAIDeviceLabView.swift | 14 ++----- .../CoreAIDeviceStoragePlanView.swift | 2 +- .../CoreAIDeviceTargetAuthoringView.swift | 6 +-- .../SpeakerDiarizationImportSection.swift | 7 ++-- .../SpeakerDiarizationStatusSection.swift | 8 ++-- .../SpeakerDiarizationTurnRow.swift | 2 +- .../SpeakerDiarizationWatcherSection.swift | 3 +- .../CoreAIFunctionBenchmarkActionsView.swift | 3 ++ .../CoreAIFunctionBenchmarkControlsView.swift | 4 -- .../CoreAIIntegrationExportSection.swift | 7 ++-- .../Projects/CoreAINewProjectView.swift | 3 +- .../CoreAIProjectArtifactRowView.swift | 2 +- .../Projects/CoreAIProjectRowView.swift | 37 ++++--------------- .../CoreAISourceProvenanceEditorView.swift | 3 +- .../CoreAISpecializationCacheRowView.swift | 26 ++++++------- .../CoreAIPipelineStudioView.swift | 6 +-- ...eAIRecipeDynamicDimensionsEditorView.swift | 5 ++- .../CoreAIRecipeRewriteCatalogView.swift | 17 ++++----- .../CoreAIRecipeSourceEditorView.swift | 3 +- .../CoreAIImportedRecipeBundleView.swift | 18 +++------ .../CoreAIRecipeCatalogEntryView.swift | 6 +-- .../Recipes/CoreAIRecipeCatalogView.swift | 12 ++---- .../CoreAIRuntimeExperienceRow.swift | 12 ++---- 42 files changed, 130 insertions(+), 310 deletions(-) diff --git a/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift b/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift index ad4b3cf..344c318 100644 --- a/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelCatalogSourceView.swift @@ -6,26 +6,18 @@ struct AppleModelCatalogSourceView: View { let sourceRepositoryURL: URL? var body: some View { - VStack(alignment: .leading, spacing: 8) { + HStack(alignment: .firstTextBaseline) { Label("Apple Core AI Models", systemImage: "apple.logo") .font(.headline) - Text("A pinned snapshot of Apple's model registry. Entries are export recipes, not bundled weights.") - .foregroundStyle(.secondary) + Spacer() - ViewThatFits(in: .horizontal) { - HStack { - Label("\(modelCount) recipes", systemImage: "list.bullet") - Label { - Text(sourceRevision.prefix(8)) - .monospaced() - } icon: { - Image(systemName: "point.topleft.down.to.point.bottomright.curvepath") - } - } + Label("\(modelCount) recipes", systemImage: "list.bullet") + .font(.callout) + .foregroundStyle(.secondary) - VStack(alignment: .leading) { - Label("\(modelCount) recipes", systemImage: "list.bullet") + if let sourceRepositoryURL { + Link(destination: sourceRepositoryURL) { Label { Text(sourceRevision.prefix(8)) .monospaced() @@ -33,15 +25,11 @@ struct AppleModelCatalogSourceView: View { Image(systemName: "point.topleft.down.to.point.bottomright.curvepath") } } - } - .font(.callout) - .foregroundStyle(.secondary) - - if let sourceRepositoryURL { - Link("Open apple/coreai-models", destination: sourceRepositoryURL) + .font(.callout) } } .padding(.vertical, 8) .accessibilityElement(children: .contain) + .help("Pinned Apple model recipes; model weights are not bundled") } } diff --git a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift index b523e91..d825ae7 100644 --- a/CoreAILab/Features/AppleModels/AppleModelDetailView.swift +++ b/CoreAILab/Features/AppleModels/AppleModelDetailView.swift @@ -31,12 +31,12 @@ struct AppleModelDetailView: View { } Section { - Text("Clone Apple's coreai-models repository, run this command from its root, then import the exported model or resource folder.") - .foregroundStyle(.secondary) - Text(model.labRecommendedExportCommand) .font(.body.monospaced()) .textSelection(.enabled) + .help( + "Run from the root of a local apple/coreai-models checkout, then import the exported asset." + ) if let recipeURL = model.recipeURL(sourceRevision: sourceRevision) { Link("Read the pinned Apple recipe", destination: recipeURL) @@ -52,18 +52,12 @@ struct AppleModelDetailView: View { Section { Label(model.runtimeSupport.title, systemImage: "shippingbox") - Text(model.runtimeSupport.detail) - .foregroundStyle(.secondary) + .help(model.runtimeSupport.detail) if let productName = model.runtimeSupport.productName { LabeledContent("Swift product", value: productName) } - if model.isRunnableInLab { - Text("Core AI Lab includes the runtime adapter, not model weights. Export the model locally under its upstream license, then import the result.") - .foregroundStyle(.secondary) - } - if model.runtimeSupport == .objectDetection, model.isRunnableInLab { NavigationLink( "Open Object Detection Playground", @@ -73,8 +67,11 @@ struct AppleModelDetailView: View { if let segmentationExample = model.segmentationExample { if segmentationExample == .sam3 { - Text("SAM 3 requires accepting Meta's gated Hugging Face license and authenticating with the `hf` command-line tool before export. Core AI Lab never reads or stores those credentials.") - .foregroundStyle(.secondary) + Label("Upstream license required", systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + .help( + "Accept Meta's gated Hugging Face license and authenticate with the hf tool before export. Core AI Lab never reads or stores those credentials." + ) } NavigationLink( segmentationExample.playgroundButtonTitle, @@ -91,8 +88,11 @@ struct AppleModelDetailView: View { if let diffusionExample = model.diffusionExample { if diffusionExample == .stableDiffusion35 { - Text("Stable Diffusion 3.5 weights require accepting Stability AI's gated Hugging Face terms and authenticating with the `hf` command-line tool before export. Core AI Lab never reads or stores those credentials.") - .foregroundStyle(.secondary) + Label("Upstream license required", systemImage: "exclamationmark.triangle") + .foregroundStyle(.orange) + .help( + "Accept Stability AI's gated Hugging Face terms and authenticate with the hf tool before export. Core AI Lab never reads or stores those credentials." + ) } NavigationLink( diffusionExample.playgroundButtonTitle, @@ -116,8 +116,8 @@ struct AppleModelDetailView: View { .font(.callout.monospaced()) .textSelection(.enabled) } - Text("The export recipe and Swift utilities use Apple's BSD-3-Clause repository. Downloaded model weights retain their original authors' licenses and are not redistributed by Core AI Lab.") - .foregroundStyle(.secondary) + LabeledContent("Recipe code", value: "Apple BSD-3-Clause") + LabeledContent("Model weights", value: "Upstream license") } header: { Label("Provenance", systemImage: "checkmark.seal") } diff --git a/CoreAILab/Features/AppleModels/AppleModelRow.swift b/CoreAILab/Features/AppleModels/AppleModelRow.swift index c458b07..269e8ce 100644 --- a/CoreAILab/Features/AppleModels/AppleModelRow.swift +++ b/CoreAILab/Features/AppleModels/AppleModelRow.swift @@ -4,37 +4,22 @@ struct AppleModelRow: View { let model: AppleCoreAIModel var body: some View { - VStack(alignment: .leading, spacing: 6) { - HStack(alignment: .firstTextBaseline) { - Text(model.shortName) - .font(.headline) + HStack(alignment: .firstTextBaseline) { + Text(model.shortName) + .font(.headline) - Spacer() + Spacer() - Label( - model.supportedPlatforms.map(\.rawValue).joined(separator: " · "), - systemImage: platformSystemImage - ) - .font(.callout) - .foregroundStyle(.secondary) - } - - Text(model.huggingFaceID) - .font(.callout.monospaced()) - .foregroundStyle(.secondary) - .lineLimit(1) - .truncationMode(.middle) - - Label(model.runtimeSupport.title, systemImage: runtimeSystemImage) + Label( + model.supportedPlatforms.map(\.rawValue).joined(separator: " · "), + systemImage: platformSystemImage + ) .font(.callout) .foregroundStyle(.secondary) } .padding(.vertical, 4) .accessibilityElement(children: .combine) - } - - private var runtimeSystemImage: String { - model.isRunnableInLab ? "play.circle.fill" : "shippingbox" + .help("\(model.huggingFaceID) · \(model.runtimeSupport.title)") } private var platformSystemImage: String { diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift index 81ceb1a..8054d66 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioWorkspaceView.swift @@ -35,9 +35,6 @@ struct AppleAudioWorkspaceView: View { if workspace.isBusy { ProgressView(workspace.statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(workspace.statusMessage, systemImage: "waveform") - .foregroundStyle(.secondary) } } header: { Label(workspace.example.title, systemImage: "waveform.badge.mic") @@ -54,11 +51,10 @@ struct AppleAudioWorkspaceView: View { inputActions(axis: .vertical) } - Text("The static Apple recipe accepts at most five seconds. Audio is decoded, downmixed, and resampled to 16 kHz mono before inference.") - .foregroundStyle(.secondary) } header: { Label("Model & Audio", systemImage: "waveform.badge.mic") } + .help("Audio is limited to five seconds and prepared as 16 kHz mono before inference.") Section { Text(workspace.example.exportCommand) diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift index 00fa118..305a2dc 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionWorkspaceView.swift @@ -33,9 +33,6 @@ struct AppleDiffusionWorkspaceView: View { if workspace.isBusy { ProgressView(workspace.statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(workspace.statusMessage, systemImage: "wand.and.sparkles") - .foregroundStyle(.secondary) } } header: { Label(workspace.example.title, systemImage: "wand.and.sparkles") @@ -52,8 +49,7 @@ struct AppleDiffusionWorkspaceView: View { systemImage: "shippingbox", action: importPipeline ) - Text("Choose the folder produced by `coreai.diffusion.export`. Core AI Lab reads its metadata and selects Apple's Stable Diffusion, SD3, or FLUX.2 runtime.") - .foregroundStyle(.secondary) + .help("Choose a folder produced by coreai.diffusion.export.") } header: { Label("Pipeline Bundle", systemImage: "shippingbox") } @@ -63,8 +59,7 @@ struct AppleDiffusionWorkspaceView: View { .lineLimit(3...8) .disabled(!workspace.canEditGenerationInputs) if workspace.modelInfo?.supportsNegativePrompt == false { - Text("FLUX.2 does not consume a negative prompt.") - .foregroundStyle(.secondary) + LabeledContent("Negative prompt", value: "Not supported") } else { TextField("Negative prompt", text: $workspace.negativePrompt, axis: .vertical) .lineLimit(2...5) diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift index 6cdaa38..157b149 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageWorkspaceView.swift @@ -29,9 +29,6 @@ struct AppleLanguageWorkspaceView: View { if workspace.isBusy { ProgressView(workspace.statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(workspace.statusMessage, systemImage: "text.bubble") - .foregroundStyle(.secondary) } } header: { Label(workspace.example.title, systemImage: "text.bubble.fill") diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift index 22abefa..a76eb73 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionHeaderView.swift @@ -11,20 +11,15 @@ struct AppleObjectDetectionHeaderView: View { Label("YOLOS Tiny", systemImage: "viewfinder") .font(.title2.bold()) - Text("Uses Apple's YOLOS export recipe and the CoreAIObjectDetection Swift package.") - .foregroundStyle(.secondary) - LabeledContent("Model", value: modelName ?? "Not imported") LabeledContent("Image", value: imageName ?? "Not selected") if isBusy { ProgressView(statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(statusMessage, systemImage: "viewfinder") - .foregroundStyle(.secondary) } } + .help("Uses Apple's YOLOS export recipe and CoreAIObjectDetection runtime.") .accessibilityElement(children: .contain) } } diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift index eb38b1a..baca330 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift @@ -12,8 +12,7 @@ struct AppleSegmentationQueryControlsView: View { axis: .vertical ) .lineLimit(2...4) - Text("SAM 3 uses the tokenizer bundled with Apple's exported resource folder.") - .foregroundStyle(.secondary) + .help("SAM 3 uses the tokenizer bundled with the exported resource folder.") } else if workspace.sourceImage != nil { LabeledContent("Horizontal position") { Text(workspace.pointX, format: .number.precision(.fractionLength(0))) @@ -27,8 +26,7 @@ struct AppleSegmentationQueryControlsView: View { } Slider(value: $workspace.pointY, in: 0...workspace.imageHeight) - Text("Coordinates are measured in pixels from the image's top-left corner.") - .foregroundStyle(.secondary) + .help("Coordinates use pixels from the image's top-left corner.") } else { ContentUnavailableView( "Choose an Image", diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift index 30cceb7..cce38d3 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationWorkspaceView.swift @@ -31,9 +31,6 @@ struct AppleSegmentationWorkspaceView: View { if workspace.isBusy { ProgressView(workspace.statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(workspace.statusMessage, systemImage: statusSystemImage) - .foregroundStyle(.secondary) } } header: { Label(workspace.example.title, systemImage: "square.stack.3d.up") @@ -106,16 +103,6 @@ struct AppleSegmentationWorkspaceView: View { isImportingModel = true } - private var statusSystemImage: String { - if workspace.isShowingError { - return "exclamationmark.triangle" - } - if workspace.modelName != nil, workspace.sourceImage != nil { - return "checkmark.circle" - } - return "info.circle" - } - private func importImage() { isImportingImage = true } diff --git a/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift b/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift index 4b4dbce..aac655e 100644 --- a/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAISpecializationControlsView.swift @@ -18,10 +18,7 @@ struct CoreAISpecializationControlsView: View { .onChange(of: workspace.selectedProfile) { refreshCacheStatus() } - - Text(workspace.selectedProfile.detail) - .font(.subheadline) - .foregroundStyle(.secondary) + .help(workspace.selectedProfile.detail) Toggle( "Expect frequent input reshapes", @@ -31,12 +28,9 @@ struct CoreAISpecializationControlsView: View { .onChange(of: workspace.expectFrequentReshapes) { refreshCacheStatus() } - - Text( - "This Core AI specialization option is part of the cache identity. Measure both settings for dynamic-shape workloads instead of assuming one is faster." + .help( + "This setting is part of the cache identity. Measure both configurations for dynamic-shape workloads." ) - .font(.subheadline) - .foregroundStyle(.secondary) LabeledContent("Selected configuration") { Label( @@ -78,6 +72,11 @@ struct CoreAISpecializationControlsView: View { action: prepareAssetRemoval ) } + .help( + allowsCacheRemoval + ? "Core AI exposes hit, miss, and deletion for this known asset." + : "Remove project-owned cache configurations from the artifact detail screen." + ) .confirmationDialog( workspace.cacheRemovalTitle, isPresented: $workspace.isConfirmingCacheRemoval, @@ -99,18 +98,6 @@ struct CoreAISpecializationControlsView: View { ProgressView(operationTitle) .accessibilityAddTraits(.updatesFrequently) } - - Text("Core AI exposes hit/miss and deletion for known assets, but not cache paths, entry sizes, or a complete inventory.") - .font(.subheadline) - .foregroundStyle(.secondary) - - if !allowsCacheRemoval { - Text( - "Remove project-owned cache configurations from the artifact detail screen so configurations referenced by another project remain available." - ) - .font(.subheadline) - .foregroundStyle(.secondary) - } } header: { Label("Specialization & Cache", systemImage: "cpu") } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift index 9f919d2..979ef06 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxGenerationSection.swift @@ -70,8 +70,7 @@ struct ChatterboxGenerationSection: View { } } header: { Label("Generate & Playback", systemImage: "speaker.wave.3") - } footer: { - Text("The first launch specializes the bundled graphs. Later runs reuse Core AI's persistent cache.") } + .help("The first launch specializes the bundled graphs; later runs may reuse Core AI's cache.") } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift index 554fd2a..3b1e154 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxModelSection.swift @@ -6,10 +6,7 @@ struct ChatterboxModelSection: View { var body: some View { Section { Label(state.title, systemImage: state.systemImage) - - Text(state.detail) - .font(.subheadline) - .foregroundStyle(.secondary) + .help(state.detail) if case .ready(let inspection) = state { LabeledContent("Model bundle", value: inspection.formattedTotalSize) diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift index 7501752..771d58b 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPipelineSection.swift @@ -20,12 +20,8 @@ struct ChatterboxPipelineSection: View { .foregroundStyle(isReady(asset.stage) ? .green : .secondary) .accessibilityHidden(true) - VStack(alignment: .leading, spacing: 2) { - Text(asset.displayName) - Text(asset.detail) - .font(.footnote) - .foregroundStyle(.secondary) - } + Text(asset.displayName) + .help(asset.detail) Spacer() @@ -49,9 +45,6 @@ struct ChatterboxPipelineSection: View { EmptyView() } - Text(detail) - .font(.footnote) - .foregroundStyle(.secondary) } header: { Label("Native Pipeline", systemImage: "point.3.connected.trianglepath.dotted") } @@ -61,18 +54,4 @@ struct ChatterboxPipelineSection: View { inspection?.contractValidation.presentStages.contains(stage) == true } - private var detail: String { - switch state { - case .notLoaded: - "The app has not started validating the bundled recipe." - case .preparing: - "The app is verifying every bundled asset and function before enabling generation." - case .ready(let inspection): - inspection.contractValidation.isComplete - ? "Text tokenization, autoregressive T3 decoding, S3Gen, and waveform synthesis all run locally." - : "The recipe is incomplete; generation remains disabled." - case .failed: - "Model preparation failed, so the pipeline contract could not be verified." - } - } } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift b/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift index 34ac7c3..cfebb0d 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxPromptSection.swift @@ -7,10 +7,9 @@ struct ChatterboxPromptSection: View { Section { TextField("What should Chatterbox say?", text: $prompt, axis: .vertical) .lineLimit(4...) - - Text("Expressive tags such as [laugh], [chuckle], [sigh], and [gasp] stay in the text. One generation supports about 10 seconds of speech.") - .font(.footnote) - .foregroundStyle(.secondary) + .help( + "Expressive tags such as [laugh], [chuckle], [sigh], and [gasp] stay in the text. One generation supports about 10 seconds of speech." + ) } header: { Label("Speech", systemImage: "text.quote") } diff --git a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift index de90d72..dedeeb3 100644 --- a/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift +++ b/CoreAILab/Features/Chatterbox/ChatterboxWorkspaceView.swift @@ -8,8 +8,6 @@ struct ChatterboxWorkspaceView: View { NavigationStack { Form { - ChatterboxHeroView(manifest: model.recipeManifest) - ChatterboxModelSection(state: model.modelState) ChatterboxPromptSection(prompt: $model.prompt) diff --git a/CoreAILab/Features/Conversion/CoreAIConversionCommandView.swift b/CoreAILab/Features/Conversion/CoreAIConversionCommandView.swift index 8d653f1..7238a4c 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionCommandView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionCommandView.swift @@ -16,10 +16,9 @@ struct CoreAIConversionCommandView: View { .padding(.vertical, 4) } .scrollIndicators(.visible) - - Text("Displayed for evidence only. Core AI Lab passes these arguments directly without invoking a shell.") - .font(.callout) - .foregroundStyle(.secondary) + .help( + "Displayed for evidence only. Core AI Lab passes these arguments directly without invoking a shell." + ) } } } diff --git a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift index 43e278b..4ed1357 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionSetupView.swift @@ -72,8 +72,7 @@ struct CoreAIConversionSetupView: View { "Overwrite matching artifacts", isOn: $workspace.overwriteExistingArtifacts ) - Text("Source weights remain in the upstream cache. Core AI Lab does not redistribute or relicense them.") - .foregroundStyle(.secondary) + .help("Source weights remain in the upstream cache and keep their upstream license.") } header: { Label("Options", systemImage: "switch.2") } @@ -84,9 +83,6 @@ struct CoreAIConversionSetupView: View { ForEach(report.checks) { check in CoreAIConversionEnvironmentCheckView(check: check) } - } else { - Text("Run the environment check before converting.") - .foregroundStyle(.secondary) } Button( diff --git a/CoreAILab/Features/Conversion/CoreAIConversionStatusView.swift b/CoreAILab/Features/Conversion/CoreAIConversionStatusView.swift index 46080d5..3349ee3 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionStatusView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionStatusView.swift @@ -22,9 +22,6 @@ struct CoreAIConversionStatusView: View { } } - Text(statusMessage) - .foregroundStyle(.secondary) - HStack(spacing: 16) { if let processIdentifier { Label("PID \(processIdentifier)", systemImage: "terminal") @@ -39,7 +36,9 @@ struct CoreAIConversionStatusView: View { .font(.callout.monospacedDigit()) .foregroundStyle(.secondary) } + .help(statusMessage) .accessibilityElement(children: .contain) + .accessibilityHint(statusMessage) .accessibilityAddTraits(.updatesFrequently) } } diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift index 59076e9..f00a180 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift @@ -59,11 +59,6 @@ struct CoreAIDeviceEvidenceView: View { "Execution placement", value: displayName(evidence.placement.availability.rawValue) ) - Text( - "Artifact and configuration SHA-256 identities are retained in the imported JSON." - ) - .font(.subheadline) - .foregroundStyle(.secondary) } else { ContentUnavailableView( "No Device Evidence", @@ -76,6 +71,7 @@ struct CoreAIDeviceEvidenceView: View { } header: { Label("Physical Evidence", systemImage: "doc.text.magnifyingglass") } + .help("Imported JSON retains artifact and configuration SHA-256 identities.") } private func beginImport() { diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift index add70ce..91a5ee5 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceLabView.swift @@ -7,17 +7,6 @@ struct CoreAIDeviceLabView: View { var body: some View { Form { - Section { - VStack(alignment: .leading) { - Label("Physical Device Planning", systemImage: "iphone.gen3") - .font(.headline) - Text( - "Author an iPhone target, plan asset delivery, and import evidence from the physical runner. Preferences remain separate from measured execution placement." - ) - .foregroundStyle(.secondary) - } - } - CoreAIDeviceTargetAuthoringView(workspace: workspace) CoreAIDeviceStoragePlanView(workspace: workspace) CoreAIDeviceDiagnosticsView(diagnostics: workspace.diagnostics) @@ -27,6 +16,9 @@ struct CoreAIDeviceLabView: View { ) } .formStyle(.grouped) + .help( + "Author an iPhone target, plan asset delivery, and import physical-runner evidence." + ) .navigationTitle("Device Lab") .fileImporter( isPresented: $isImportingEvidence, diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift index 26feb05..c9f0100 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceStoragePlanView.swift @@ -43,7 +43,7 @@ struct CoreAIDeviceStoragePlanView: View { if let error = workspace.storagePlanErrorMessage { Label(error, systemImage: "xmark.octagon") - .foregroundStyle(.secondary) + .foregroundStyle(.red) } else if let plan = workspace.storagePlan { LabeledContent("App download") { Text( diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift index d2d652f..d7ba0f7 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceTargetAuthoringView.swift @@ -11,6 +11,7 @@ struct CoreAIDeviceTargetAuthoringView: View { .tag(preference) } } + .help("A compute preference does not prove execution placement.") Toggle( "Expect frequent reshapes", isOn: $workspace.expectsFrequentReshapes @@ -52,11 +53,6 @@ struct CoreAIDeviceTargetAuthoringView: View { "Leave input widths dynamic", isOn: $workspace.usesDynamicSequenceDimension ) - Text( - "A compute preference shapes specialization options. It is not an execution-placement measurement." - ) - .font(.subheadline) - .foregroundStyle(.secondary) } header: { Label("iPhone Target", systemImage: "iphone.gen3") } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift index 78591a3..ccb54fe 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationImportSection.swift @@ -30,12 +30,11 @@ struct SpeakerDiarizationImportSection: View { runAction: runAction ) } - - Text("The bundled Apache-2.0 CAM++ model runs through Core AI after 16 kHz decode, energy segmentation, and six-second feature preparation; cosine clustering produces anonymous speaker turns.") - .font(.footnote) - .foregroundStyle(.secondary) } header: { Label("Inputs", systemImage: "waveform.and.mic") } + .help( + "Core AI runs the bundled CAM++ model after 16 kHz decode, energy segmentation, and feature preparation." + ) } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift index bd12018..5380065 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationStatusSection.swift @@ -34,14 +34,12 @@ struct SpeakerDiarizationStatusSection: View { if isBusy { ProgressView(statusMessage) .accessibilityAddTraits(.updatesFrequently) - } else { - Label(statusMessage, systemImage: "waveform.badge.mic") - .foregroundStyle(.secondary) } } header: { Label("Speaker Diarization", systemImage: "person.wave.2") - } footer: { - Text("The bundled CAM++ asset is Apache-2.0. This experimental batch engine uses anonymous labels—not identities—and does not detect overlapping speakers.") } + .help( + "\(statusMessage) The Apache-2.0 CAM++ engine uses anonymous labels and does not detect overlapping speakers." + ) } } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift index d4eff49..ba33db8 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationTurnRow.swift @@ -6,7 +6,7 @@ struct SpeakerDiarizationTurnRow: View { var body: some View { LabeledContent { - VStack(alignment: .trailing) { + HStack { Text(timeRange) .monospacedDigit() Text(clusterEvidence) diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift index 3d01993..e3f1c18 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift @@ -40,8 +40,6 @@ struct SpeakerDiarizationWatcherSection: View { Button("Restart", systemImage: "backward.end.fill", action: restart) } - Text("The watcher synchronizes playback with the completed batch timeline. It does not claim streaming inference.") - .foregroundStyle(.secondary) } else { ContentUnavailableView( "No Media to Watch", @@ -50,6 +48,7 @@ struct SpeakerDiarizationWatcherSection: View { ) } } + .help("Playback follows the completed batch timeline; it is not streaming inference.") } private func positionText(for summary: SpeakerDiarizationMediaSummary) -> String { diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift index 3c63e7e..802f5c0 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkActionsView.swift @@ -18,6 +18,9 @@ struct CoreAIFunctionBenchmarkActionsView: View { ) .buttonStyle(.borderedProminent) .disabled(!workspace.canBenchmark) + .help( + "Warmups are excluded. Measured runs reuse one deterministic input set and execute sequentially." + ) if workspace.phase == .benchmarking { Button( diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift index 13ffcee..62c317f 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionBenchmarkControlsView.swift @@ -49,10 +49,6 @@ struct CoreAIFunctionBenchmarkControlsView: View { } } header: { Label("Benchmark", systemImage: "gauge.with.dots.needle.67percent") - } footer: { - Text( - "Warmups are excluded. Measured runs reuse one function and one deterministic input set, execute sequentially, and remain visible individually. Stopping takes effect between Core AI inference calls." - ) } } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift index 698c101..c8ad514 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIIntegrationExportSection.swift @@ -30,6 +30,9 @@ struct CoreAIIntegrationExportSection: View { action: chooseDestination ) .disabled(!workspace.canExportIntegration) + .help( + "Create a Swift package with the asset, checksums, notices, typed metadata, invocation code, and an offline verifier." + ) } if let status = workspace.exportStatusMessage { @@ -38,10 +41,6 @@ struct CoreAIIntegrationExportSection: View { } } header: { Label("Integration Export", systemImage: "shippingbox.and.arrow.backward") - } footer: { - Text( - "Creates a standalone Swift package with the original asset, checksums, notices, typed metadata, generated invocation code, and an offline verifier. The optional AOT script never runs automatically. Stateful and image-input functions remain manifest-only." - ) } } diff --git a/CoreAILab/Features/Projects/CoreAINewProjectView.swift b/CoreAILab/Features/Projects/CoreAINewProjectView.swift index f2ef34e..763b8ff 100644 --- a/CoreAILab/Features/Projects/CoreAINewProjectView.swift +++ b/CoreAILab/Features/Projects/CoreAINewProjectView.swift @@ -17,10 +17,9 @@ struct CoreAINewProjectView: View { Section { TextField("Project Name", text: $name) .textContentType(.name) + .help("Projects keep related assets, provenance, runs, and evidence together.") } header: { Label("Project", systemImage: "folder") - } footer: { - Text("Projects keep related assets, provenance, runs, and evidence together.") } } .formStyle(.grouped) diff --git a/CoreAILab/Features/Projects/CoreAIProjectArtifactRowView.swift b/CoreAILab/Features/Projects/CoreAIProjectArtifactRowView.swift index 5ffa5ee..824b9e5 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectArtifactRowView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectArtifactRowView.swift @@ -13,7 +13,7 @@ struct CoreAIProjectArtifactRowView: View { Spacer() if let artifact = link.artifact { - VStack(alignment: .trailing) { + HStack { Text(artifact.byteCount, format: .byteCount(style: .file)) Text(artifact.shortDigest) .monospaced() diff --git a/CoreAILab/Features/Projects/CoreAIProjectRowView.swift b/CoreAILab/Features/Projects/CoreAIProjectRowView.swift index 49d7918..fdde7c3 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectRowView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectRowView.swift @@ -4,42 +4,21 @@ struct CoreAIProjectRowView: View { let project: LabProject var body: some View { - Label { - VStack(alignment: .leading) { - Text(project.name) - .font(.headline) + HStack(alignment: .firstTextBaseline) { + Label(project.name, systemImage: "folder.fill") + .font(.headline) - HStack { - Label { - Text("^[\(project.artifactLinks.count) artifact](inflect: true)") - } icon: { - Image(systemName: "shippingbox") - } + Spacer() - Label { - Text(project.storedByteCount, format: .byteCount(style: .file)) - } icon: { - Image(systemName: "internaldrive") - } - - Label { - Text(project.updatedAt, format: .relative(presentation: .named)) - } icon: { - Image(systemName: "clock") - } - } - .font(.callout) + Text("^[\(project.artifactLinks.count) artifact](inflect: true)") .foregroundStyle(.secondary) - } - } icon: { - Image(systemName: "folder.fill") - .font(.title2) - .foregroundStyle(.tint) } - .labelStyle(.titleAndIcon) .accessibilityElement(children: .combine) .accessibilityLabel( "\(project.name), \(project.artifactLinks.count) artifacts, \(project.storedByteCount.formatted(.byteCount(style: .file)))" ) + .help( + "\(project.storedByteCount.formatted(.byteCount(style: .file))) · Updated \(project.updatedAt.formatted(.relative(presentation: .named)))" + ) } } diff --git a/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift b/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift index 0fa155f..2f88b11 100644 --- a/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift +++ b/CoreAILab/Features/Projects/CoreAISourceProvenanceEditorView.swift @@ -41,14 +41,13 @@ struct CoreAISourceProvenanceEditorView: View { TextField("Source location", text: $sourceLocation, axis: .vertical) .lineLimit(2...5) + .help("Record enough detail to trace the artifact to its source and license.") TextField("Provider", text: $providerName) TextField("License", text: $licenseName) TextField("Notes", text: $notes, axis: .vertical) .lineLimit(3...8) } header: { Label("Source", systemImage: "link") - } footer: { - Text("Record enough information to trace this artifact to its original source and license.") } } .formStyle(.grouped) diff --git a/CoreAILab/Features/Projects/CoreAISpecializationCacheRowView.swift b/CoreAILab/Features/Projects/CoreAISpecializationCacheRowView.swift index 7ef9065..4deea87 100644 --- a/CoreAILab/Features/Projects/CoreAISpecializationCacheRowView.swift +++ b/CoreAILab/Features/Projects/CoreAISpecializationCacheRowView.swift @@ -7,23 +7,16 @@ struct CoreAISpecializationCacheRowView: View { var body: some View { HStack { - VStack(alignment: .leading) { - Label(record.configurationTitle, systemImage: "cpu") - Text( - record.lastUsedAt, - format: .relative(presentation: .named) - ) - .foregroundStyle(.secondary) - Text( - record.wasLoadedFromCache - ? "Loaded from existing cache" - : "Created by specialization" - ) - .foregroundStyle(.secondary) - } + Label(record.configurationTitle, systemImage: "cpu") Spacer() + Text( + record.lastUsedAt, + format: .relative(presentation: .named) + ) + .foregroundStyle(.secondary) + Button( "Remove \(record.configurationTitle)", systemImage: "trash", @@ -35,5 +28,10 @@ struct CoreAISpecializationCacheRowView: View { .disabled(isDisabled) } .accessibilityElement(children: .contain) + .help( + record.wasLoadedFromCache + ? "Loaded from an existing specialization cache" + : "Created by specialization" + ) } } diff --git a/CoreAILab/Features/RecipeStudio/CoreAIPipelineStudioView.swift b/CoreAILab/Features/RecipeStudio/CoreAIPipelineStudioView.swift index a8b0eef..9bdece5 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIPipelineStudioView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIPipelineStudioView.swift @@ -63,16 +63,14 @@ struct CoreAIPipelineStudioView: View { action: workspace.connectSelectedEndpoints ) .disabled(!workspace.canConnectSelectedEndpoints) + .help("Each destination accepts one compatible source value contract.") } header: { Text("Connect Typed Ports") - } footer: { - Text("Only compatible value contracts can be connected, and each destination accepts one source.") } Section("Edges") { if displayedEdges.isEmpty { - Text("No edges") - .foregroundStyle(.secondary) + ContentUnavailableView("No Edges", systemImage: "arrow.triangle.branch") } ForEach(displayedEdges) { edge in LabeledContent( diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift index 2fb9fdc..627d3af 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift @@ -45,8 +45,9 @@ struct CoreAIRecipeDynamicDimensionsEditorView: View { action: workspace.addDynamicDimension ) .disabled(!workspace.canAddDynamicDimension) - } footer: { - Text("Bounds are authoring constraints, not evidence that every shape specializes or runs on a preferred compute unit.") + .help( + "Bounds are authoring constraints; they do not prove specialization or execution placement." + ) } } .formStyle(.grouped) diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeRewriteCatalogView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeRewriteCatalogView.swift index e15062b..f502745 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeRewriteCatalogView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeRewriteCatalogView.swift @@ -3,20 +3,19 @@ import SwiftUI struct CoreAIRecipeRewriteCatalogView: View { var body: some View { List(CoreAIRecipeRewriteCatalog.builtIn) { rewrite in - VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { Label(rewrite.title, systemImage: "arrow.trianglehead.2.clockwise.rotate.90") .font(.headline) + + Spacer() + Text(rewrite.strategy.title) - .font(.subheadline) - .foregroundStyle(.secondary) - Text(rewrite.summary) - Text(rewrite.operatorNames.joined(separator: ", ")) - .font(.body.monospaced()) - .textSelection(.enabled) - Text(rewrite.evidence) - .font(.footnote) + .font(.callout) .foregroundStyle(.secondary) } + .help( + "\(rewrite.summary) Operators: \(rewrite.operatorNames.joined(separator: ", ")). \(rewrite.evidence)" + ) } .navigationTitle("Rewrite Catalog") } diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift index 7c3fdf0..c35dd25 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeSourceEditorView.swift @@ -23,10 +23,9 @@ struct CoreAIRecipeSourceEditorView: View { .coreAIRecipeIdentifierInput() TextField("Pinned revision", text: $workspace.recipe.source.revision) .coreAIRecipeIdentifierInput() + .help("Pin a revision before executing a reproducible conversion.") } header: { Label("PyTorch Source", systemImage: "shippingbox") - } footer: { - Text("A blank revision is allowed while drafting, but a reproducible conversion should pin one before execution.") } Section { diff --git a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift index 21b8b65..bb76f12 100644 --- a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift +++ b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift @@ -27,15 +27,11 @@ struct CoreAIImportedRecipeBundleView: View { value: "\(summary.manifest.codeReferences.count)" ) ForEach(summary.manifest.codeReferences) { reference in - VStack(alignment: .leading, spacing: 2) { - Text(reference.id) - .font(.callout) - .bold() - Text("\(reference.language.rawValue.capitalized) · \(reference.relativePath) · \(reference.entryPoint)") - .font(.footnote.monospaced()) - .foregroundStyle(.secondary) - .textSelection(.enabled) - } + LabeledContent( + reference.id, + value: "\(reference.language.rawValue.capitalized) · \(reference.entryPoint)" + ) + .help(reference.relativePath) } } @@ -57,10 +53,6 @@ struct CoreAIImportedRecipeBundleView: View { ProgressView() Text(statusMessage) } - } else if summary != nil { - Text(statusMessage) - .font(.footnote) - .foregroundStyle(.secondary) } } diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift index e22e841..3f0a9fe 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogEntryView.swift @@ -7,8 +7,6 @@ struct CoreAIRecipeCatalogEntryView: View { VStack(alignment: .leading) { Label(entry.displayName, systemImage: "waveform.badge.microphone") .font(.headline) - Text(entry.summary) - .foregroundStyle(.secondary) Divider() @@ -29,9 +27,6 @@ struct CoreAIRecipeCatalogEntryView: View { .textSelection(.enabled) } - Text(entry.verificationNotes) - .font(.footnote) - .foregroundStyle(.secondary) if let evidenceReference = entry.evidenceReference { LabeledContent("Evidence") { Text(evidenceReference) @@ -51,6 +46,7 @@ struct CoreAIRecipeCatalogEntryView: View { } } } + .help("\(entry.summary) \(entry.verificationNotes)") } private var trustSystemImage: String { diff --git a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift index 8e769be..cfe13dd 100644 --- a/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift +++ b/CoreAILab/Features/Recipes/CoreAIRecipeCatalogView.swift @@ -11,15 +11,6 @@ struct CoreAIRecipeCatalogView: View { NavigationStack { Form { - Section { - VStack(alignment: .leading) { - Label("Trust & Verification", systemImage: "checkmark.shield") - .font(.headline) - Text("Trust describes a recipe's source. Verification names the checks backed by evidence. Neither state grants imported code permission to run.") - .foregroundStyle(.secondary) - } - } - Section { if let catalogError = model.catalogError { ContentUnavailableView( @@ -40,6 +31,9 @@ struct CoreAIRecipeCatalogView: View { } header: { Label("Curated Recipes", systemImage: "checkmark.seal") } + .help( + "Trust identifies source. Verification names evidence-backed checks; neither grants code permission." + ) Section { CoreAIImportedRecipeBundleView( diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift index 05f600c..132ec0a 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeExperienceRow.swift @@ -4,21 +4,14 @@ struct CoreAIRuntimeExperienceRow: View { let mapping: CoreAIRecipeExperienceMapping var body: some View { - VStack(alignment: .leading) { + HStack(alignment: .firstTextBaseline) { Label( mapping.experience.title, systemImage: mapping.experience.systemImage ) .font(.headline) - Text(mapping.experience.summary) - .foregroundStyle(.secondary) - .lineLimit(2) - - Label(capabilitySummary, systemImage: "checklist") - .font(.callout) - .foregroundStyle(.secondary) - .lineLimit(2) + Spacer() Label(platformSummary, systemImage: platformSystemImage) .font(.callout) @@ -27,6 +20,7 @@ struct CoreAIRuntimeExperienceRow: View { .padding(.vertical, 4) .accessibilityElement(children: .combine) .accessibilityHint("Opens the local runtime experience") + .help("\(mapping.experience.summary) \(capabilitySummary)") } private var capabilitySummary: String { From 21da0c4973b9515171dfb1bedbbf9759cd59c14a Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:16:41 +0530 Subject: [PATCH 19/28] Align the workspace inspector --- CoreAILab/CoreAIWorkspaceInspectorView.swift | 75 +++++++++++++------- 1 file changed, 51 insertions(+), 24 deletions(-) diff --git a/CoreAILab/CoreAIWorkspaceInspectorView.swift b/CoreAILab/CoreAIWorkspaceInspectorView.swift index b8351d6..108403a 100644 --- a/CoreAILab/CoreAIWorkspaceInspectorView.swift +++ b/CoreAILab/CoreAIWorkspaceInspectorView.swift @@ -4,37 +4,64 @@ struct CoreAIWorkspaceInspectorView: View { let section: CoreAILabSection var body: some View { - Form { - Section { - LabeledContent("Area", value: section.areaTitle) - Text(section.summary) - .foregroundStyle(.secondary) - } header: { - Label(section.title, systemImage: section.systemImage) - } - - Section { - ForEach(section.workflowSteps.indices, id: \.self) { index in - Label( - section.workflowSteps[index], - systemImage: "\(index + 1).circle" - ) + ScrollView { + VStack(alignment: .leading, spacing: 18) { + inspectorSection( + "Workflow", + systemImage: "point.3.connected.trianglepath.dotted" + ) { + VStack(alignment: .leading, spacing: 12) { + ForEach(section.workflowSteps.indices, id: \.self) { index in + inspectorLabel( + section.workflowSteps[index], + systemImage: "\(index + 1).circle" + ) + } + } } - } header: { - Label("Workflow", systemImage: "point.3.connected.trianglepath.dotted") - } - Section { - Text(section.evidenceBoundary) - .foregroundStyle(.secondary) - } header: { - Label("Evidence Boundary", systemImage: "checkmark.seal") + Divider() + .padding(.leading, contentInset) + + inspectorSection("Evidence", systemImage: "checkmark.seal") { + Text(section.evidenceBoundary) + .padding(.leading, contentInset) + .fixedSize(horizontal: false, vertical: true) + } } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(16) } - .formStyle(.grouped) .navigationTitle("Workspace") .inspectorColumnWidth(min: 260, ideal: 300, max: 360) } + + private let contentInset: CGFloat = 24 + + private func inspectorSection( + _ title: String, + systemImage: String, + @ViewBuilder content: () -> Content + ) -> some View { + VStack(alignment: .leading, spacing: 12) { + inspectorLabel(title, systemImage: systemImage) + .font(.headline) + .foregroundStyle(.secondary) + content() + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + private func inspectorLabel( + _ title: String, + systemImage: String + ) -> some View { + HStack(alignment: .firstTextBaseline, spacing: 8) { + Image(systemName: systemImage) + .frame(width: 16) + Text(title) + } + } } #Preview { From 6d8e7b1f8a10f46bbda1f09cb831126cfbffc401 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:20:50 +0530 Subject: [PATCH 20/28] Anchor workspace toolbar controls --- CoreAILab/ContentView.swift | 27 ++++++++++++++++++-- CoreAILab/CoreAIWorkspaceInspectorView.swift | 4 +++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CoreAILab/ContentView.swift b/CoreAILab/ContentView.swift index fc05f12..20298f1 100644 --- a/CoreAILab/ContentView.swift +++ b/CoreAILab/ContentView.swift @@ -8,10 +8,12 @@ struct ContentView: View { @SceneStorage("CoreAILab.isWorkspaceInspectorPresented") private var isWorkspaceInspectorPresented = false + @State private var columnVisibility: NavigationSplitViewVisibility = .all + var body: some View { let selectedSection = selection ?? .projects - NavigationSplitView { + NavigationSplitView(columnVisibility: $columnVisibility) { List(selection: $selection) { Section("Library") { ForEach(CoreAILabSection.library) { section in @@ -55,6 +57,9 @@ struct ContentView: View { } .listStyle(.sidebar) .navigationSplitViewColumnWidth(min: 200, ideal: 240, max: 280) +#if os(macOS) + .toolbar(removing: .sidebarToggle) +#endif } detail: { switch selectedSection { case .projects: @@ -90,7 +95,19 @@ struct ContentView: View { CoreAIWorkspaceInspectorView(section: selectedSection) } .toolbar { - ToolbarItem(placement: .secondaryAction) { +#if os(macOS) + ToolbarItem(placement: .navigation) { + Button( + "Toggle Sidebar", + systemImage: "sidebar.leading", + action: toggleSidebar + ) + .help("Show or hide the workspace sidebar") + .keyboardShortcut("s", modifiers: [.command, .control]) + } +#endif + + ToolbarItem(placement: .primaryAction) { Button( "Workspace Inspector", systemImage: "sidebar.trailing", @@ -110,6 +127,12 @@ struct ContentView: View { private func toggleWorkspaceInspector() { isWorkspaceInspectorPresented.toggle() } + + private func toggleSidebar() { + withAnimation { + columnVisibility = columnVisibility == .detailOnly ? .all : .detailOnly + } + } } #Preview { diff --git a/CoreAILab/CoreAIWorkspaceInspectorView.swift b/CoreAILab/CoreAIWorkspaceInspectorView.swift index 108403a..aba110c 100644 --- a/CoreAILab/CoreAIWorkspaceInspectorView.swift +++ b/CoreAILab/CoreAIWorkspaceInspectorView.swift @@ -60,7 +60,11 @@ struct CoreAIWorkspaceInspectorView: View { Image(systemName: systemImage) .frame(width: 16) Text(title) + .lineLimit(nil) + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity, alignment: .leading) } + .frame(maxWidth: .infinity, alignment: .leading) } } From da21e9eac61bad130a39434516520285ea44d30b Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:22:10 +0530 Subject: [PATCH 21/28] Remove duplicate empty-library actions --- .../Projects/CoreAIProjectLibraryView.swift | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift index ec7cf35..5bb8162 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift @@ -32,27 +32,31 @@ struct CoreAIProjectLibraryView: View { ) .buttonStyle(.borderedProminent) } - } else if visibleProjects.isEmpty { - ContentUnavailableView.search } else { - List(visibleProjects) { project in - NavigationLink(value: CoreAIProjectRoute.project(project.id)) { - CoreAIProjectRowView(project: project) + Group { + if visibleProjects.isEmpty { + ContentUnavailableView.search + } else { + List(visibleProjects) { project in + NavigationLink(value: CoreAIProjectRoute.project(project.id)) { + CoreAIProjectRowView(project: project) + } + } + } + } + .searchable(text: $searchText, prompt: "Search projects") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button( + "New Project", + systemImage: "plus", + action: showNewProject + ) } } } } .navigationTitle("Projects") - .searchable(text: $searchText, prompt: "Search projects") - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button( - "New Project", - systemImage: "plus", - action: showNewProject - ) - } - } .navigationDestination(for: CoreAIProjectRoute.self) { route in CoreAIProjectDestinationView( route: route, From c97c4ef4adb5e6d39826f9b07d43c59ea8db4231 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:25:15 +0530 Subject: [PATCH 22/28] Simplify empty and result states --- .../AppleAudioTranscriptionResultView.swift | 3 +- .../Diffusion/AppleDiffusionResultView.swift | 3 +- .../Language/AppleLanguageResponseView.swift | 3 +- .../AppleObjectDetectionPreviewView.swift | 3 +- .../AppleObjectDetectionWorkspaceView.swift | 3 +- .../AppleSegmentationPreviewView.swift | 3 +- .../AppleSegmentationQueryControlsView.swift | 3 +- .../CoreAIAssetInspectorView.swift | 4 --- .../Conversion/CoreAIConversionLogView.swift | 5 +--- .../DeviceLab/CoreAIDeviceEvidenceView.swift | 6 ++-- .../SpeakerDiarizationResultsView.swift | 3 +- .../SpeakerDiarizationTimelineView.swift | 3 +- .../SpeakerDiarizationWatcherSection.swift | 6 ++-- .../CoreAIFunctionWorkbenchView.swift | 30 +++++++------------ .../CoreAIArtifactProjectPickerView.swift | 2 -- .../Projects/CoreAIProjectDetailView.swift | 4 --- .../Projects/CoreAIProjectLibraryView.swift | 4 --- ...eAIRecipeDynamicDimensionsEditorView.swift | 4 +-- .../CoreAIRecipeExampleInputsEditorView.swift | 4 +-- ...oreAIRecipeExternalizationEditorView.swift | 4 +-- ...IRecipeFunctionEntrypointsEditorView.swift | 4 +-- .../CoreAIRecipeGeneratedArtifactsView.swift | 4 +-- .../CoreAIRecipeStateEditorView.swift | 4 +-- ...CoreAIUnsupportedOperationReportView.swift | 4 +-- .../CoreAIImportedRecipeBundleView.swift | 4 +-- .../CoreAIRuntimeLifecycleView.swift | 5 +--- 26 files changed, 41 insertions(+), 84 deletions(-) diff --git a/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift b/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift index 766d7ac..aa1b24b 100644 --- a/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift +++ b/CoreAILab/Features/AppleModels/Audio/AppleAudioTranscriptionResultView.swift @@ -28,8 +28,7 @@ struct AppleAudioTranscriptionResultView: View { } else { ContentUnavailableView( "No Transcript Yet", - systemImage: "captions.bubble", - description: Text("Choose a short speech recording and transcribe it locally.") + systemImage: "captions.bubble" ) } } header: { diff --git a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift index 6d076ae..5334d92 100644 --- a/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift +++ b/CoreAILab/Features/AppleModels/Diffusion/AppleDiffusionResultView.swift @@ -18,8 +18,7 @@ struct AppleDiffusionResultView: View { } else { ContentUnavailableView( "No Image Yet", - systemImage: "photo.badge.plus", - description: Text("Import a diffusion bundle, enter a prompt, and generate locally.") + systemImage: "photo.badge.plus" ) } } header: { diff --git a/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift b/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift index bbbeeb1..28d8d07 100644 --- a/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift +++ b/CoreAILab/Features/AppleModels/Language/AppleLanguageResponseView.swift @@ -8,8 +8,7 @@ struct AppleLanguageResponseView: View { if response.isEmpty { ContentUnavailableView( "No Response Yet", - systemImage: "text.bubble", - description: Text("Import Qwen, enter a prompt, and generate locally.") + systemImage: "text.bubble" ) } else { Text(response) diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift index 0c2dee6..b0ba7d4 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionPreviewView.swift @@ -20,8 +20,7 @@ struct AppleObjectDetectionPreviewView: View { if detections.isEmpty { ContentUnavailableView( "No Detections Yet", - systemImage: "viewfinder", - description: Text("Run detection to identify objects in this image.") + systemImage: "viewfinder" ) } else { Table(detections) { diff --git a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift index 7f30f0e..647ed3f 100644 --- a/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift +++ b/CoreAILab/Features/AppleModels/ObjectDetection/AppleObjectDetectionWorkspaceView.swift @@ -61,8 +61,7 @@ struct AppleObjectDetectionWorkspaceView: View { } else { ContentUnavailableView( "Choose an Image", - systemImage: "photo", - description: Text("The result will show Apple's COCO labels, confidence, and bounding boxes.") + systemImage: "photo" ) } } header: { diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift index 20a31f5..c38b305 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationPreviewView.swift @@ -26,8 +26,7 @@ struct AppleSegmentationPreviewView: View { } else { ContentUnavailableView( "No Image", - systemImage: "photo", - description: Text("Choose an image to preview segmentation results.") + systemImage: "photo" ) } } header: { diff --git a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift index baca330..00832b3 100644 --- a/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift +++ b/CoreAILab/Features/AppleModels/Segmentation/AppleSegmentationQueryControlsView.swift @@ -30,8 +30,7 @@ struct AppleSegmentationQueryControlsView: View { } else { ContentUnavailableView( "Choose an Image", - systemImage: "point.bottomleft.forward.to.point.topright.scurvepath", - description: Text("Point controls appear after an image is loaded.") + systemImage: "point.bottomleft.forward.to.point.topright.scurvepath" ) } } header: { diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift index 799dc51..3d3c182 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift @@ -30,16 +30,12 @@ struct CoreAIAssetInspectorView: View { } else if workspace.isInspecting { ContentUnavailableView { Label("Inspecting Model", systemImage: "doc.text.magnifyingglass") - } description: { - Text("Reading metadata, functions, and compute types from the Core AI asset.") } actions: { ProgressView() } } else { ContentUnavailableView { Label("Inspect a Core AI Model", systemImage: "doc.text.magnifyingglass") - } description: { - Text("Open any exported .aimodel package, including assets produced by Apple's coreai-models recipes.") } actions: { Button("Open Model", systemImage: "folder", action: openModelPicker) .buttonStyle(.borderedProminent) diff --git a/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift b/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift index 10994b6..6c83683 100644 --- a/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift +++ b/CoreAILab/Features/Conversion/CoreAIConversionLogView.swift @@ -11,10 +11,7 @@ struct CoreAIConversionLogView: View { if entries.isEmpty { ContentUnavailableView( "No Converter Output", - systemImage: "text.alignleft", - description: Text( - "Start a conversion to stream the original process output here." - ) + systemImage: "text.alignleft" ) .frame(maxWidth: .infinity, minHeight: 200) } else { diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift index f00a180..af81c72 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift @@ -62,11 +62,9 @@ struct CoreAIDeviceEvidenceView: View { } else { ContentUnavailableView( "No Device Evidence", - systemImage: "iphone.slash", - description: Text( - "Run the physical harness or its dry run with --evidence-json, then import that file." - ) + systemImage: "iphone.slash" ) + .help("Run the physical harness with --evidence-json, then import that file.") } } header: { Label("Physical Evidence", systemImage: "doc.text.magnifyingglass") diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationResultsView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationResultsView.swift index 0bd2506..b6fa1e6 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationResultsView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationResultsView.swift @@ -79,8 +79,7 @@ struct SpeakerDiarizationResultsView: View { } else { ContentUnavailableView( "No Speaker Turns Yet", - systemImage: "person.2.slash", - description: Text("Choose media, then run the bundled CAM++ diarizer.") + systemImage: "person.2.slash" ) .frame(maxWidth: .infinity, minHeight: 180) } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationTimelineView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationTimelineView.swift index 23b8137..c765e5c 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationTimelineView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationTimelineView.swift @@ -45,8 +45,7 @@ struct SpeakerDiarizationTimelineView: View { } else { ContentUnavailableView( "No Media Selected", - systemImage: "waveform", - description: Text("Choose an audio or video file to build the first timeline.") + systemImage: "waveform" ) .frame(maxWidth: .infinity, minHeight: 180) } diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift index e3f1c18..282c6d4 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWatcherSection.swift @@ -21,8 +21,7 @@ struct SpeakerDiarizationWatcherSection: View { } else { ContentUnavailableView( "Audio Watcher", - systemImage: "waveform.circle", - description: Text("Playback still drives the same live playhead and active speaker state.") + systemImage: "waveform.circle" ) } @@ -43,8 +42,7 @@ struct SpeakerDiarizationWatcherSection: View { } else { ContentUnavailableView( "No Media to Watch", - systemImage: "play.rectangle", - description: Text("Import audio or video to enable synchronized playback.") + systemImage: "play.rectangle" ) } } diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift index 512cc04..5a83bf7 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift @@ -70,11 +70,9 @@ struct CoreAIFunctionWorkbenchView: View { Section { ContentUnavailableView( "Specialize the Asset", - systemImage: "cpu", - description: Text( - "Choose a compute profile above, then specialize or load its cached model to inspect runtime contracts." - ) + systemImage: "cpu" ) + .help("Choose a compute profile, then specialize or load its cached model.") } header: { Label("Function Workbench", systemImage: "function") } @@ -82,8 +80,6 @@ struct CoreAIFunctionWorkbenchView: View { Section { ContentUnavailableView { Label("Reading Function Contracts", systemImage: "list.bullet.rectangle") - } description: { - Text("Loading input, state, and output descriptors from the specialized model.") } actions: { ProgressView() } @@ -174,35 +170,23 @@ struct CoreAIFunctionWorkbenchView: View { || workspace.assetWorkspace.isInspecting { ContentUnavailableView { Label("Opening Model", systemImage: "shippingbox") - } description: { - Text("Inspecting the asset before specialization.") } actions: { ProgressView() } } else { ContentUnavailableView { Label("Function Workbench", systemImage: "function") - } description: { - Text( - "Open a Core AI asset to inspect every function and run supported stateless tensor contracts with generated inputs." - ) } actions: { Button("Open Model", systemImage: "folder", action: openModelPicker) .buttonStyle(.borderedProminent) } + .help("Open a Core AI asset to inspect its functions and supported tensor contracts.") } } .navigationTitle("Function Workbench") .toolbar { #if os(macOS) - ToolbarItem(placement: .primaryAction) { - Button("Run Function", systemImage: "play.fill", action: runFunction) - .disabled(!workspace.canRun) - .help( - "Run synthetic contract inputs. Core AI inference cannot be canceled once started." - ) - } - ToolbarItem(placement: .secondaryAction) { + ToolbarItemGroup(placement: .primaryAction) { Button("Open Model", systemImage: "folder", action: openModelPicker) .disabled( workspace.phase.isBusy @@ -210,6 +194,12 @@ struct CoreAIFunctionWorkbenchView: View { || workspace.isExportingIntegration ) .keyboardShortcut("o", modifiers: .command) + + Button("Run Function", systemImage: "play.fill", action: runFunction) + .disabled(!workspace.canRun) + .help( + "Run synthetic contract inputs. Core AI inference cannot be canceled once started." + ) } #else ToolbarItem(placement: .primaryAction) { diff --git a/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift b/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift index ffdc964..c2a68c5 100644 --- a/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift +++ b/CoreAILab/Features/Projects/CoreAIArtifactProjectPickerView.swift @@ -18,8 +18,6 @@ struct CoreAIArtifactProjectPickerView: View { if projects.isEmpty { ContentUnavailableView { Label("No Projects", systemImage: "folder.badge.plus") - } description: { - Text("Create a project before storing this conversion output.") } actions: { Button( "New Project", diff --git a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift index ccd6347..5b95e34 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift @@ -36,10 +36,6 @@ struct CoreAIProjectDetailView: View { if project.artifactLinks.isEmpty { ContentUnavailableView { Label("No Stored Artifacts", systemImage: "shippingbox") - } description: { - Text( - "Import a .aimodel package, an Apple resource folder, or a supporting model file." - ) } actions: { Button( "Import Artifact", diff --git a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift index 5bb8162..63d3d9a 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectLibraryView.swift @@ -20,10 +20,6 @@ struct CoreAIProjectLibraryView: View { if projects.isEmpty { ContentUnavailableView { Label("Create Your First Project", systemImage: "folder.badge.plus") - } description: { - Text( - "Keep models, resource bundles, provenance, runs, and evidence together in checksummed storage." - ) } actions: { Button( "New Project", diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift index 627d3af..3ab6f98 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeDynamicDimensionsEditorView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeDynamicDimensionsEditorView: View { if workspace.recipe.dynamicDimensions.isEmpty { ContentUnavailableView( "No Dynamic Dimensions", - systemImage: "arrow.left.and.right", - description: Text("Static example shapes remain unchanged until a bounded dynamic axis is added.") + systemImage: "arrow.left.and.right" ) + .help("Static example shapes remain unchanged until a bounded dynamic axis is added.") } ForEach($workspace.recipe.dynamicDimensions) { $dimension in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeExampleInputsEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeExampleInputsEditorView.swift index 7f127d3..0f0e980 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeExampleInputsEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeExampleInputsEditorView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeExampleInputsEditorView: View { if workspace.recipe.exampleInputs.isEmpty { ContentUnavailableView( "No Example Inputs", - systemImage: "square.and.pencil", - description: Text("Add the concrete arguments used to export and validate this recipe.") + systemImage: "square.and.pencil" ) + .help("Add the concrete arguments used to export and validate this recipe.") } ForEach($workspace.recipe.exampleInputs) { $input in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeExternalizationEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeExternalizationEditorView.swift index 5b76e87..bda4766 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeExternalizationEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeExternalizationEditorView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeExternalizationEditorView: View { if workspace.recipe.externalizationRules.isEmpty { ContentUnavailableView( "No Externalization Rules", - systemImage: "externaldrive", - description: Text("Weights remain under the converter's default policy until a module rule is added.") + systemImage: "externaldrive" ) + .help("Weights use the converter's default policy until a module rule is added.") } ForEach($workspace.recipe.externalizationRules) { $rule in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeFunctionEntrypointsEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeFunctionEntrypointsEditorView.swift index d3c9e4f..d05045d 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeFunctionEntrypointsEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeFunctionEntrypointsEditorView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeFunctionEntrypointsEditorView: View { if workspace.recipe.functionEntrypoints.isEmpty { ContentUnavailableView( "No Function Entrypoints", - systemImage: "function", - description: Text("Define at least one exported module method and its named contract.") + systemImage: "function" ) + .help("Define at least one exported module method and its named contract.") } ForEach($workspace.recipe.functionEntrypoints) { $function in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeGeneratedArtifactsView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeGeneratedArtifactsView.swift index 25a4c6e..0a6f869 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeGeneratedArtifactsView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeGeneratedArtifactsView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeGeneratedArtifactsView: View { if workspace.generatedArtifacts.isEmpty { ContentUnavailableView( "No Generated Stubs", - systemImage: "doc.badge.gearshape", - description: Text("Generate stubs from an attributed unsupported-operation finding.") + systemImage: "doc.badge.gearshape" ) + .help("Generate stubs from an attributed unsupported-operation finding.") } ForEach(workspace.generatedArtifacts) { artifact in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStateEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStateEditorView.swift index 8072303..db8788c 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStateEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStateEditorView.swift @@ -8,9 +8,9 @@ struct CoreAIRecipeStateEditorView: View { if workspace.recipe.stateBindings.isEmpty { ContentUnavailableView( "No Explicit State", - systemImage: "memorychip", - description: Text("Add state only when the exported functions expose named input and output bindings.") + systemImage: "memorychip" ) + .help("Add state only when exported functions expose named input and output bindings.") } ForEach($workspace.recipe.stateBindings) { $state in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIUnsupportedOperationReportView.swift b/CoreAILab/Features/RecipeStudio/CoreAIUnsupportedOperationReportView.swift index 055b2c7..3c3a5ed 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIUnsupportedOperationReportView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIUnsupportedOperationReportView.swift @@ -8,9 +8,9 @@ struct CoreAIUnsupportedOperationReportView: View { if workspace.recipe.unsupportedOperations.isEmpty { ContentUnavailableView( "No Unsupported Operations Reported", - systemImage: "checkmark.circle", - description: Text("This means the draft has no imported findings; it does not prove that export will succeed.") + systemImage: "checkmark.circle" ) + .help("No imported findings does not prove that export will succeed.") } ForEach(workspace.recipe.unsupportedOperations) { finding in diff --git a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift index bb76f12..4f125b7 100644 --- a/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift +++ b/CoreAILab/Features/Recipes/CoreAIImportedRecipeBundleView.swift @@ -43,9 +43,9 @@ struct CoreAIImportedRecipeBundleView: View { } else { ContentUnavailableView( "No Imported Bundle", - systemImage: "shippingbox", - description: Text(statusMessage) + systemImage: "shippingbox" ) + .help(statusMessage) } if isImporting { diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift index 4b1eb11..a1bf826 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift @@ -34,10 +34,7 @@ struct CoreAIRuntimeLifecycleView: View { } else { ContentUnavailableView( "No Runtime Run Yet", - systemImage: "clock", - description: Text( - "Import the required model and inputs, then run this experience." - ) + systemImage: "clock" ) } } From cf41edceb9d2b8c9d5f0c50f31b175e5985c482d Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:31:25 +0530 Subject: [PATCH 23/28] Reveal workflow evidence progressively --- .../CoreAIAssetInspectorView.swift | 10 ++-- .../CoreAIAssetReportView.swift | 15 ++---- .../SpeakerDiarizationWorkspaceView.swift | 32 +++++++------ .../CoreAIFunctionInputsView.swift | 3 +- .../CoreAIFunctionWorkbenchView.swift | 46 ++++++++++-------- .../CoreAIProjectArtifactDetailView.swift | 2 +- .../Projects/CoreAIProjectDetailView.swift | 14 +++--- ...CoreAIProjectSpecializationCacheView.swift | 3 +- .../CoreAISourceProvenanceSummaryView.swift | 6 +-- .../CoreAIRecipeReferenceListEditorView.swift | 4 -- .../RecipeStudio/CoreAIRecipeStudioView.swift | 13 ++--- .../CoreAIRuntimeLifecycleView.swift | 48 ++++++++----------- 12 files changed, 90 insertions(+), 106 deletions(-) diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift index 3d3c182..ad3a246 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetInspectorView.swift @@ -44,10 +44,12 @@ struct CoreAIAssetInspectorView: View { } .navigationTitle("Asset Inspector") .toolbar { - ToolbarItem(placement: .primaryAction) { - Button("Open Model", systemImage: "folder", action: openModelPicker) - .disabled(workspace.phase.isBusy) - .keyboardShortcut("o", modifiers: .command) + if workspace.report != nil { + ToolbarItem(placement: .primaryAction) { + Button("Open Model", systemImage: "folder", action: openModelPicker) + .disabled(workspace.phase.isBusy) + .keyboardShortcut("o", modifiers: .command) + } } } .fileImporter( diff --git a/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift b/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift index 2c4a98d..e80e80c 100644 --- a/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift +++ b/CoreAILab/Features/AssetInspector/CoreAIAssetReportView.swift @@ -20,8 +20,7 @@ struct CoreAIAssetReportView: View { LabeledContent("Author", value: valueOrFallback(report.author)) LabeledContent("License", value: valueOrFallback(report.license)) if !report.description.isEmpty { - Text(report.description) - .foregroundStyle(.secondary) + LabeledContent("Description", value: report.description) } } header: { Label("Asset", systemImage: "shippingbox") @@ -29,8 +28,7 @@ struct CoreAIAssetReportView: View { Section { if report.functions.isEmpty { - Text("No functions were declared in the asset summary.") - .foregroundStyle(.secondary) + Label("No Functions Declared", systemImage: "minus.circle") } else { ForEach(report.functions) { function in DisclosureGroup { @@ -58,8 +56,7 @@ struct CoreAIAssetReportView: View { Section { if report.computeTypes.isEmpty { - Text("No compute types were reported.") - .foregroundStyle(.secondary) + Label("Not Reported", systemImage: "minus.circle") } else { ForEach(report.computeTypes, id: \.self) { computeType in Text(computeType) @@ -71,8 +68,7 @@ struct CoreAIAssetReportView: View { Section { if report.storageTypes.isEmpty { - Text("No storage statistics were reported.") - .foregroundStyle(.secondary) + Label("Not Reported", systemImage: "minus.circle") } else { ForEach(report.storageTypes) { storageType in LabeledContent( @@ -88,8 +84,7 @@ struct CoreAIAssetReportView: View { Section { if report.operationDistribution.isEmpty { - Text("No operation statistics were reported.") - .foregroundStyle(.secondary) + Label("Not Reported", systemImage: "minus.circle") } else { ForEach(report.operationDistribution) { operation in LabeledContent( diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift index 7114493..138a1f1 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceView.swift @@ -30,22 +30,24 @@ struct SpeakerDiarizationWorkspaceView: View { runAction: workspace.startDiarization ) - SpeakerDiarizationWatcherSection( - summary: workspace.mediaSummary, - player: watcher.player, - currentTime: watcher.currentTime, - activeTurn: activeTurn, - isPlaying: watcher.isPlaying, - togglePlayback: watcher.togglePlayback, - restart: watcher.restart - ) + if workspace.mediaSummary != nil { + SpeakerDiarizationWatcherSection( + summary: workspace.mediaSummary, + player: watcher.player, + currentTime: watcher.currentTime, + activeTurn: activeTurn, + isPlaying: watcher.isPlaying, + togglePlayback: watcher.togglePlayback, + restart: watcher.restart + ) - SpeakerDiarizationAnalysisSection( - waveform: workspace.waveform, - result: workspace.result, - playheadTime: watcher.currentTime, - activeTurnID: activeTurn?.id - ) + SpeakerDiarizationAnalysisSection( + waveform: workspace.waveform, + result: workspace.result, + playheadTime: watcher.currentTime, + activeTurnID: activeTurn?.id + ) + } } .formStyle(.grouped) .navigationTitle("Diarization") diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift index eab5a3d..5dc5487 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionInputsView.swift @@ -7,8 +7,7 @@ struct CoreAIFunctionInputsView: View { var body: some View { Section { if drafts.isEmpty { - Text("This function has no generated tensor inputs.") - .foregroundStyle(.secondary) + Label("No Generated Inputs", systemImage: "minus.circle") } else { ForEach(drafts, id: \.name) { draft in CoreAIFunctionInputDraftView( diff --git a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift index 5a83bf7..3bf7062 100644 --- a/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift +++ b/CoreAILab/Features/FunctionWorkbench/CoreAIFunctionWorkbenchView.swift @@ -186,30 +186,34 @@ struct CoreAIFunctionWorkbenchView: View { .navigationTitle("Function Workbench") .toolbar { #if os(macOS) - ToolbarItemGroup(placement: .primaryAction) { - Button("Open Model", systemImage: "folder", action: openModelPicker) - .disabled( - workspace.phase.isBusy - || workspace.assetWorkspace.phase.isBusy - || workspace.isExportingIntegration - ) - .keyboardShortcut("o", modifiers: .command) + if workspace.assetWorkspace.report != nil { + ToolbarItemGroup(placement: .primaryAction) { + Button("Open Model", systemImage: "folder", action: openModelPicker) + .disabled( + workspace.phase.isBusy + || workspace.assetWorkspace.phase.isBusy + || workspace.isExportingIntegration + ) + .keyboardShortcut("o", modifiers: .command) - Button("Run Function", systemImage: "play.fill", action: runFunction) - .disabled(!workspace.canRun) - .help( - "Run synthetic contract inputs. Core AI inference cannot be canceled once started." - ) + Button("Run Function", systemImage: "play.fill", action: runFunction) + .disabled(!workspace.canRun) + .help( + "Run synthetic contract inputs. Core AI inference cannot be canceled once started." + ) + } } #else - ToolbarItem(placement: .primaryAction) { - Button("Open Model", systemImage: "folder", action: openModelPicker) - .disabled( - workspace.phase.isBusy - || workspace.assetWorkspace.phase.isBusy - || workspace.isExportingIntegration - ) - .keyboardShortcut("o", modifiers: .command) + if workspace.assetWorkspace.report != nil { + ToolbarItem(placement: .primaryAction) { + Button("Open Model", systemImage: "folder", action: openModelPicker) + .disabled( + workspace.phase.isBusy + || workspace.assetWorkspace.phase.isBusy + || workspace.isExportingIntegration + ) + .keyboardShortcut("o", modifiers: .command) + } } #endif } diff --git a/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift b/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift index 02bcb78..fbf91b7 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectArtifactDetailView.swift @@ -75,7 +75,7 @@ struct CoreAIProjectArtifactDetailView: View { "The persisted descriptor snapshot is invalid.", systemImage: "exclamationmark.triangle" ) - .foregroundStyle(.secondary) + .foregroundStyle(.orange) } } diff --git a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift index 5b95e34..fbd5d6b 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectDetailView.swift @@ -63,12 +63,14 @@ struct CoreAIProjectDetailView: View { .navigationTitle(project.name) .toolbar { ToolbarItemGroup(placement: .primaryAction) { - Button( - "Import Artifact", - systemImage: "square.and.arrow.down", - action: showArtifactImporter - ) - .disabled(controller.isPerformingOperation) + if !project.artifactLinks.isEmpty { + Button( + "Import Artifact", + systemImage: "square.and.arrow.down", + action: showArtifactImporter + ) + .disabled(controller.isPerformingOperation) + } Menu("Project Actions", systemImage: "ellipsis.circle") { Button("Rename Project", systemImage: "pencil", action: showRenamePrompt) diff --git a/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift b/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift index 3da2566..4319201 100644 --- a/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift +++ b/CoreAILab/Features/Projects/CoreAIProjectSpecializationCacheView.swift @@ -10,8 +10,7 @@ struct CoreAIProjectSpecializationCacheView: View { var body: some View { Section { if link.specializationCaches.isEmpty { - Text("Specialize this project artifact to register a cache entry.") - .foregroundStyle(.secondary) + Label("No Cached Configurations", systemImage: "minus.circle") } else { ForEach(link.sortedSpecializationCaches) { record in CoreAISpecializationCacheRowView( diff --git a/CoreAILab/Features/Projects/CoreAISourceProvenanceSummaryView.swift b/CoreAILab/Features/Projects/CoreAISourceProvenanceSummaryView.swift index 5ac2f75..2f8029e 100644 --- a/CoreAILab/Features/Projects/CoreAISourceProvenanceSummaryView.swift +++ b/CoreAILab/Features/Projects/CoreAISourceProvenanceSummaryView.swift @@ -24,12 +24,10 @@ struct CoreAISourceProvenanceSummaryView: View { LabeledContent("License", value: provenance.licenseName) } if !provenance.notes.isEmpty { - Text(provenance.notes) - .foregroundStyle(.secondary) + LabeledContent("Notes", value: provenance.notes) } } else { - Text("No source provenance has been recorded.") - .foregroundStyle(.secondary) + Label("No Source Provenance", systemImage: "minus.circle") } Button( "Edit Source Provenance", diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeReferenceListEditorView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeReferenceListEditorView.swift index 8699b1d..51f00ec 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeReferenceListEditorView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeReferenceListEditorView.swift @@ -22,10 +22,6 @@ struct CoreAIRecipeReferenceListEditorView: View { GroupBox(title) { VStack(alignment: .leading) { - if valueIDs.isEmpty { - Text("None") - .foregroundStyle(.secondary) - } ForEach(valueIDs, id: \.self) { valueID in if let value = value(for: valueID) { HStack { diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift index 9ada284..58c9898 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift @@ -13,16 +13,9 @@ struct CoreAIRecipeStudioView: View { NavigationSplitView { List(selection: $selection) { Section { - VStack(alignment: .leading) { - Text(workspace.recipe.displayName) - .font(.headline) - .lineLimit(2) - - Label(validationTitle, systemImage: validationSystemImage) - .font(.callout) - .foregroundStyle(validationStyle) - } - .accessibilityElement(children: .combine) + Text(workspace.recipe.displayName) + .font(.headline) + .lineLimit(2) } Section("Authoring") { diff --git a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift index a1bf826..5e3696f 100644 --- a/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift +++ b/CoreAILab/Features/RuntimeStudio/CoreAIRuntimeLifecycleView.swift @@ -5,37 +5,31 @@ struct CoreAIRuntimeLifecycleView: View { let context: CoreAIRuntimeRunContext var body: some View { - GroupBox("Shared Run Lifecycle") { - if let run = coordinator.latestRun(for: context.experienceID) { - VStack(alignment: .leading) { + if let run = coordinator.latestRun(for: context.experienceID) { + Section { + LabeledContent("Status") { Label(run.state.title, systemImage: run.state.systemImage) - LabeledContent("Timing class", value: run.timingClass.title) - LabeledContent("Model identity", value: run.modelIdentity) - if let durationSeconds = run.durationSeconds { - LabeledContent("Elapsed") { - Text( - durationSeconds, - format: .number.precision(.fractionLength(3)) - ) - Text("seconds") - } - } - if let comparison = run.selectedComparisonIdentity { - LabeledContent( - "Comparison identity", - value: comparison.displayName + } + LabeledContent("Timing class", value: run.timingClass.title) + LabeledContent("Model identity", value: run.modelIdentity) + if let durationSeconds = run.durationSeconds { + LabeledContent("Elapsed") { + Text( + durationSeconds, + format: .number.precision(.fractionLength(3)) ) + Text("seconds") } - Text(run.summary) - .foregroundStyle(.secondary) } - .frame(maxWidth: .infinity, alignment: .leading) - .accessibilityElement(children: .contain) - } else { - ContentUnavailableView( - "No Runtime Run Yet", - systemImage: "clock" - ) + if let comparison = run.selectedComparisonIdentity { + LabeledContent( + "Comparison identity", + value: comparison.displayName + ) + } + LabeledContent("Summary", value: run.summary) + } header: { + Label("Run Record", systemImage: "clock") } } } From 2a91c3489bb41bccade74367b765fdaab81c1f98 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:33:21 +0530 Subject: [PATCH 24/28] Tighten validation status copy --- .../RecipeStudio/CoreAIPipelineValidationIssuesView.swift | 2 +- .../RecipeStudio/CoreAIRecipeValidationIssuesView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CoreAILab/Features/RecipeStudio/CoreAIPipelineValidationIssuesView.swift b/CoreAILab/Features/RecipeStudio/CoreAIPipelineValidationIssuesView.swift index 47572b3..3664485 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIPipelineValidationIssuesView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIPipelineValidationIssuesView.swift @@ -5,7 +5,7 @@ struct CoreAIPipelineValidationIssuesView: View { var body: some View { if issues.isEmpty { - Label("Pipeline contract is valid", systemImage: "checkmark.circle.fill") + Label("Contract Valid", systemImage: "checkmark.circle.fill") .foregroundStyle(.green) } else { ForEach(issues) { issue in diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeValidationIssuesView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeValidationIssuesView.swift index 962ac68..33cad95 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeValidationIssuesView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeValidationIssuesView.swift @@ -5,7 +5,7 @@ struct CoreAIRecipeValidationIssuesView: View { var body: some View { if issues.isEmpty { - Label("This draft is structurally valid", systemImage: "checkmark.circle.fill") + Label("Structurally Valid", systemImage: "checkmark.circle.fill") .foregroundStyle(.green) } else { ForEach(issues) { issue in From 42baa6f786234e7715276f93567f3d455043ffad Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:40:06 +0530 Subject: [PATCH 25/28] Isolate real-model contract tests --- .../CoreAIFunctionBenchmarkTests.swift | 7 +++++- .../CoreAIFunctionWorkbenchTests.swift | 7 +++++- .../CoreAIIntegrationExportTests.swift | 18 +++++++++++--- CoreAILabTests/CoreAITestFixtures.swift | 24 +++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/CoreAILabTests/CoreAIFunctionBenchmarkTests.swift b/CoreAILabTests/CoreAIFunctionBenchmarkTests.swift index 0007ac2..c0bfb95 100644 --- a/CoreAILabTests/CoreAIFunctionBenchmarkTests.swift +++ b/CoreAILabTests/CoreAIFunctionBenchmarkTests.swift @@ -372,7 +372,12 @@ struct CoreAIFunctionBenchmarkTests { @Test func realFixtureRunsWarmupAndMeasuredInference() async throws { let service = CoreAISpecializationService() - let fixtureURL = try CoreAITestFixtures.tensorModelURL() + let fixtureURL = try CoreAITestFixtures.temporaryTensorModelURL() + defer { + try? FileManager.default.removeItem( + at: fixtureURL.deletingLastPathComponent() + ) + } try? await service.removeCachedEntries(at: fixtureURL) do { diff --git a/CoreAILabTests/CoreAIFunctionWorkbenchTests.swift b/CoreAILabTests/CoreAIFunctionWorkbenchTests.swift index c8ed112..3bd5608 100644 --- a/CoreAILabTests/CoreAIFunctionWorkbenchTests.swift +++ b/CoreAILabTests/CoreAIFunctionWorkbenchTests.swift @@ -207,7 +207,12 @@ struct CoreAIFunctionWorkbenchTests { @Test func realCoreAIFixtureRunsFloatAndIntegerFunctions() async throws { let service = CoreAISpecializationService() - let fixtureURL = try CoreAITestFixtures.tensorModelURL() + let fixtureURL = try CoreAITestFixtures.temporaryTensorModelURL() + defer { + try? FileManager.default.removeItem( + at: fixtureURL.deletingLastPathComponent() + ) + } try? await service.removeCachedEntries(at: fixtureURL) do { diff --git a/CoreAILabTests/CoreAIIntegrationExportTests.swift b/CoreAILabTests/CoreAIIntegrationExportTests.swift index bfff896..5c2e914 100644 --- a/CoreAILabTests/CoreAIIntegrationExportTests.swift +++ b/CoreAILabTests/CoreAIIntegrationExportTests.swift @@ -595,6 +595,7 @@ struct CoreAIIntegrationExportTests { func cleanExportVerifierBuildsTheStandalonePackage() async throws { let exportParent = temporaryDirectory() let cleanParent = temporaryDirectory() + let developerDirectory = try activeDeveloperDirectory() defer { try? FileManager.default.removeItem(at: exportParent) try? FileManager.default.removeItem(at: cleanParent) @@ -614,7 +615,7 @@ struct CoreAIIntegrationExportTests { at: URL(filePath: "/usr/bin/python3"), arguments: [cleanPackageURL.appending(path: "verify-export.py").path], environment: [ - "DEVELOPER_DIR": "/Applications/Xcode-beta.app/Contents/Developer", + "DEVELOPER_DIR": developerDirectory, ] ) #expect(output.contains("Core AI integration export verified.")) @@ -682,7 +683,7 @@ struct CoreAIIntegrationExportTests { "--disable-automatic-resolution", ], environment: [ - "DEVELOPER_DIR": "/Applications/Xcode-beta.app/Contents/Developer", + "DEVELOPER_DIR": developerDirectory, "SWIFTPM_DISABLE_PACKAGE_REPOSITORY_CACHE": "1", ] ) @@ -752,7 +753,7 @@ struct CoreAIIntegrationExportTests { "build", ], environment: [ - "DEVELOPER_DIR": "/Applications/Xcode-beta.app/Contents/Developer", + "DEVELOPER_DIR": developerDirectory, "SWIFTPM_DISABLE_PACKAGE_REPOSITORY_CACHE": "1", ], currentDirectoryURL: iOSConsumerURL @@ -850,6 +851,17 @@ struct CoreAIIntegrationExportTests { URL.temporaryDirectory.appending(path: UUID().uuidString, directoryHint: .isDirectory) } + private func activeDeveloperDirectory() throws -> String { + if let developerDirectory = ProcessInfo.processInfo.environment["DEVELOPER_DIR"], + FileManager.default.fileExists(atPath: developerDirectory) { + return developerDirectory + } + return try runExecutable( + at: URL(filePath: "/usr/bin/xcode-select"), + arguments: ["--print-path"] + ).trimmingCharacters(in: .whitespacesAndNewlines) + } + private func packageSnapshot(at rootURL: URL) throws -> [String: Data] { guard let enumerator = FileManager.default.enumerator( at: rootURL, diff --git a/CoreAILabTests/CoreAITestFixtures.swift b/CoreAILabTests/CoreAITestFixtures.swift index edd9358..c55410b 100644 --- a/CoreAILabTests/CoreAITestFixtures.swift +++ b/CoreAILabTests/CoreAITestFixtures.swift @@ -55,6 +55,30 @@ enum CoreAITestFixtures { return modelURL } + static func temporaryTensorModelURL() throws -> URL { + let sourceURL = try tensorModelURL() + let directoryURL = URL.temporaryDirectory.appending( + path: UUID().uuidString, + directoryHint: .isDirectory + ) + let modelURL = directoryURL.appending( + path: sourceURL.lastPathComponent, + directoryHint: .isDirectory + ) + + do { + try FileManager.default.createDirectory( + at: directoryURL, + withIntermediateDirectories: true + ) + try FileManager.default.copyItem(at: sourceURL, to: modelURL) + return modelURL + } catch { + try? FileManager.default.removeItem(at: directoryURL) + throw error + } + } + static func diarizationFeatureURL() throws -> URL { let bundle = Bundle(for: CoreAITestBundleToken.self) guard let url = bundle.url( From a3de574fa804e6ee2593ae3a60faa7e11a54f0ce Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:41:45 +0530 Subject: [PATCH 26/28] Keep restored sidebar selections visible --- CoreAILab/ContentView.swift | 17 +++++++++++++---- .../RecipeStudio/CoreAIRecipeStudioView.swift | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/CoreAILab/ContentView.swift b/CoreAILab/ContentView.swift index 20298f1..1cc4249 100644 --- a/CoreAILab/ContentView.swift +++ b/CoreAILab/ContentView.swift @@ -3,7 +3,7 @@ import SwiftUI struct ContentView: View { @SceneStorage("CoreAILab.selectedSection") - private var selection: CoreAILabSection? + private var selectedSectionRawValue = CoreAILabSection.projects.rawValue @SceneStorage("CoreAILab.isWorkspaceInspectorPresented") private var isWorkspaceInspectorPresented = false @@ -11,10 +11,8 @@ struct ContentView: View { @State private var columnVisibility: NavigationSplitViewVisibility = .all var body: some View { - let selectedSection = selection ?? .projects - NavigationSplitView(columnVisibility: $columnVisibility) { - List(selection: $selection) { + List(selection: selectedSectionBinding) { Section("Library") { ForEach(CoreAILabSection.library) { section in NavigationLink(value: section) { @@ -128,6 +126,17 @@ struct ContentView: View { isWorkspaceInspectorPresented.toggle() } + private var selectedSection: CoreAILabSection { + CoreAILabSection(rawValue: selectedSectionRawValue) ?? .projects + } + + private var selectedSectionBinding: Binding { + Binding( + get: { selectedSection }, + set: { selectedSectionRawValue = ($0 ?? .projects).rawValue } + ) + } + private func toggleSidebar() { withAnimation { columnVisibility = columnVisibility == .detailOnly ? .all : .detailOnly diff --git a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift index 58c9898..f2630d4 100644 --- a/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift +++ b/CoreAILab/Features/RecipeStudio/CoreAIRecipeStudioView.swift @@ -3,7 +3,7 @@ import SwiftUI struct CoreAIRecipeStudioView: View { @State private var workspace: CoreAIRecipeStudioWorkspaceModel @SceneStorage("CoreAILab.recipeStudio.selectedPanel") - private var selection: CoreAIRecipeStudioPanel? + private var selectedPanelRawValue = CoreAIRecipeStudioPanel.source.rawValue init(recipe: CoreAIRecipeAuthoringManifest = .starter) { _workspace = State(initialValue: CoreAIRecipeStudioWorkspaceModel(recipe: recipe)) @@ -11,7 +11,7 @@ struct CoreAIRecipeStudioView: View { var body: some View { NavigationSplitView { - List(selection: $selection) { + List(selection: selectedPanelBinding) { Section { Text(workspace.recipe.displayName) .font(.headline) @@ -46,7 +46,7 @@ struct CoreAIRecipeStudioView: View { .navigationTitle("Recipe Studio") .navigationSplitViewColumnWidth(min: 190, ideal: 220, max: 260) } detail: { - switch selection ?? .source { + switch selectedPanel { case .source: CoreAIRecipeSourceEditorView(workspace: workspace) case .exampleInputs: @@ -72,6 +72,17 @@ struct CoreAIRecipeStudioView: View { .navigationSplitViewStyle(.balanced) } + private var selectedPanel: CoreAIRecipeStudioPanel { + CoreAIRecipeStudioPanel(rawValue: selectedPanelRawValue) ?? .source + } + + private var selectedPanelBinding: Binding { + Binding( + get: { selectedPanel }, + set: { selectedPanelRawValue = ($0 ?? .source).rawValue } + ) + } + private var validationTitle: String { let count = workspace.validationIssues.count if count == 0 { From 1fc8dee539411d66064ee24d3992dd9c465ef15e Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:42:23 +0530 Subject: [PATCH 27/28] Present device evidence statuses clearly --- .../DeviceLab/CoreAIDeviceEvidenceView.swift | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift index af81c72..354ca56 100644 --- a/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift +++ b/CoreAILab/Features/DeviceLab/CoreAIDeviceEvidenceView.swift @@ -45,19 +45,19 @@ struct CoreAIDeviceEvidenceView: View { } LabeledContent( "Specialization", - value: displayName(evidence.specialization.status.rawValue) + value: displayName(evidence.specialization.status) ) LabeledContent( "Inference", - value: displayName(evidence.inference.status.rawValue) + value: displayName(evidence.inference.status) ) LabeledContent( "Energy", - value: displayName(evidence.energy.availability.rawValue) + value: displayName(evidence.energy.availability) ) LabeledContent( "Execution placement", - value: displayName(evidence.placement.availability.rawValue) + value: displayName(evidence.placement.availability) ) } else { ContentUnavailableView( @@ -76,7 +76,23 @@ struct CoreAIDeviceEvidenceView: View { isImportingEvidence = true } - private func displayName(_ rawValue: String) -> String { - rawValue.replacing("_", with: " ").capitalized + private func displayName(_ status: CoreAIDeviceTrialStatus) -> String { + switch status { + case .notRun: + "Not run" + case .succeeded: + "Succeeded" + case .failed: + "Failed" + } + } + + private func displayName(_ availability: CoreAIDeviceMetricAvailability) -> String { + switch availability { + case .unavailable: + "Unavailable" + case .observed: + "Observed" + } } } From fed5a260a8f2f523e4934a8b0293dd495c21eaf9 Mon Sep 17 00:00:00 2001 From: rudrankriyam Date: Wed, 24 Jun 2026 11:44:38 +0530 Subject: [PATCH 28/28] Preserve diarization media during replacement --- .../SpeakerDiarizationWorkspaceModel.swift | 5 +-- .../SpeakerDiarizationLabTests.swift | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift index 57bd22e..95c9cc7 100644 --- a/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift +++ b/CoreAILab/Features/Diarization/SpeakerDiarizationWorkspaceModel.swift @@ -85,10 +85,6 @@ final class SpeakerDiarizationWorkspaceModel { diarizationTask = nil analysisGeneration += 1 let generation = analysisGeneration - mediaURL = nil - mediaSummary = nil - waveform = nil - result = nil isRunningDiarization = false isAnalyzingMedia = true clearError() @@ -137,6 +133,7 @@ final class SpeakerDiarizationWorkspaceModel { mediaURL = url mediaSummary = analysis.summary waveform = analysis.waveform + result = nil clearError() statusMessage = modelInfo == nil ? "Media is ready. Choose a compatible CAM++ model to diarize it." diff --git a/CoreAILabTests/SpeakerDiarizationLabTests.swift b/CoreAILabTests/SpeakerDiarizationLabTests.swift index b856949..a6d8f8c 100644 --- a/CoreAILabTests/SpeakerDiarizationLabTests.swift +++ b/CoreAILabTests/SpeakerDiarizationLabTests.swift @@ -31,6 +31,43 @@ struct SpeakerDiarizationLabTests { workspace.cancelWork() } + @Test + @MainActor + func replacementKeepsCurrentMediaVisibleWhileCandidateIsAnalyzed() async throws { + let currentURL = FileManager.default.temporaryDirectory.appending( + path: "diarization-current-\(UUID().uuidString).wav" + ) + let missingURL = FileManager.default.temporaryDirectory.appending( + path: "diarization-missing-\(UUID().uuidString).wav" + ) + defer { try? FileManager.default.removeItem(at: currentURL) } + try writeSineWave(to: currentURL) + + let workspace = SpeakerDiarizationWorkspaceModel( + engine: SpeakerDiarizationServiceFake() + ) + workspace.selectMedia(currentURL) + while workspace.isAnalyzingMedia { + await Task.yield() + } + let currentSummary = try #require(workspace.mediaSummary) + let currentWaveform = try #require(workspace.waveform) + + workspace.selectMedia(missingURL) + + #expect(workspace.isAnalyzingMedia) + #expect(workspace.mediaURL == currentURL) + #expect(workspace.mediaSummary == currentSummary) + #expect(workspace.waveform == currentWaveform) + + while workspace.isAnalyzingMedia { + await Task.yield() + } + #expect(workspace.mediaURL == currentURL) + #expect(workspace.mediaSummary == currentSummary) + #expect(workspace.waveform == currentWaveform) + } + @Test func bundledCAMPlusHasPinnedLicenseProvenanceAndContract() async throws { let modelURL = try SpeakerDiarizationBundledModel.url()