Shared CocotteCockpitKit (views/model/LiveCockpitAPI) + iOS TabView shell (Drops/Assets/Fleet/Activity/Insights) + macOS shell. XcodeGen project.yml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
129 lines
4.5 KiB
Swift
129 lines
4.5 KiB
Swift
import SwiftUI
|
|
import CocotteCockpitKit
|
|
|
|
// Stream 2 — the iOS cockpit. TabView shell (navigation lives here, not the Kit):
|
|
// Drops · Assets · Fleet · Activity · Insights, reusing the shared model + views.
|
|
|
|
@main
|
|
struct CocotteCockpitiOSApp: App {
|
|
var body: some Scene {
|
|
WindowGroup { RootView() }
|
|
}
|
|
}
|
|
|
|
struct RootView: View {
|
|
@State private var model = CockpitModel()
|
|
@State private var theme: Theme = .dark
|
|
@State private var selection: Int = {
|
|
let args = ProcessInfo.processInfo.arguments
|
|
if let i = args.firstIndex(of: "--tab"), i + 1 < args.count, let n = Int(args[i + 1]) { return n }
|
|
return 0
|
|
}()
|
|
|
|
private var tokens: Tokens { Tokens.make(theme, .regular) }
|
|
|
|
var body: some View {
|
|
TabView(selection: $selection) {
|
|
DropsTab(model: model, theme: $theme)
|
|
.tabItem { Label("Drops", systemImage: "tray.full") }.tag(0)
|
|
AssetsTab(model: model)
|
|
.tabItem { Label("Assets", systemImage: "photo.on.rectangle") }.tag(1)
|
|
FleetTab(model: model)
|
|
.tabItem { Label("Fleet", systemImage: "person.3") }.tag(2)
|
|
ActivityTab(model: model)
|
|
.tabItem { Label("Activity", systemImage: "bolt.horizontal") }.tag(3)
|
|
InsightsTab(model: model)
|
|
.tabItem { Label("Insights", systemImage: "chart.bar") }.tag(4)
|
|
}
|
|
.environment(\.tokens, tokens)
|
|
.preferredColorScheme(theme == .dark ? .dark : .light)
|
|
.tint(tokens.accent)
|
|
}
|
|
}
|
|
|
|
private struct DropsTab: View {
|
|
var model: CockpitModel
|
|
@Binding var theme: Theme
|
|
@State private var path: [UUID] = []
|
|
@State private var composing = ProcessInfo.processInfo.arguments.contains("--composer")
|
|
|
|
var body: some View {
|
|
NavigationStack(path: $path) {
|
|
ContentDropsView(model: model, onSelectDrop: { path.append($0.id) })
|
|
.navigationTitle("CocotteAI · social")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarLeading) {
|
|
Button { theme = (theme == .dark ? .light : .dark) } label: {
|
|
Image(systemName: "circle.lefthalf.filled")
|
|
}
|
|
}
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button { composing = true } label: { Image(systemName: "plus") }
|
|
}
|
|
}
|
|
.navigationDestination(for: UUID.self) { id in
|
|
if let drop = model.drop(id) {
|
|
DropDetailView(drop: drop)
|
|
.navigationTitle(drop.displayTitle)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
.sheet(isPresented: $composing) {
|
|
ComposerView(model: model, onClose: { composing = false })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct AssetsTab: View {
|
|
var model: CockpitModel
|
|
var body: some View {
|
|
NavigationStack {
|
|
AssetLibraryView(model: model)
|
|
.navigationTitle("Assets")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct FleetTab: View {
|
|
var model: CockpitModel
|
|
@State private var path: [UUID] = []
|
|
var body: some View {
|
|
NavigationStack(path: $path) {
|
|
FleetListView(model: model, onSelect: { path.append($0.id) })
|
|
.navigationTitle("Fleet · content")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.navigationDestination(for: UUID.self) { id in
|
|
if let s = model.specialist(id) {
|
|
SpecialistDetailView(specialist: s)
|
|
.navigationTitle(s.displayName)
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct ActivityTab: View {
|
|
var model: CockpitModel
|
|
var body: some View {
|
|
NavigationStack {
|
|
ActivityView(model: model)
|
|
.navigationTitle("Activity")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct InsightsTab: View {
|
|
var model: CockpitModel
|
|
var body: some View {
|
|
NavigationStack {
|
|
AnalyticsView(model: model)
|
|
.navigationTitle("Insights")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
}
|
|
}
|
|
}
|