tpmjs/apps/omega-mac/OmegaMac/Views/Sidebar/SidebarView.swift
Thomas Davis 1cd44b4e97 feat: add Omega Mac native macOS SwiftUI chat app
Native macOS counterpart to the web Omega agent, connecting directly
to the OpenAI API and TPMJS tool registry (1M+ AI-ready tools).

- SwiftUI app targeting macOS 15+ with dark theme
- Full agentic loop: auto-discover tools via BM25, stream OpenAI
  responses, execute tools via remote sandbox, loop up to 10x
- SwiftData persistence for conversations, messages, tool runs
- Keychain storage for API keys and environment variables
- SSE streaming via URLSession.bytes with custom parser
- Actor-based services (OpenAIService, TPMJSRegistryService)
- NavigationSplitView layout with sidebar + chat detail
- MarkdownUI for rendering assistant responses
- Settings: API key, model picker, env vars, custom system prompt
- Keyboard shortcuts: Cmd+N new chat, Cmd+, settings, Enter send

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 21:29:39 +10:00

52 lines
1.6 KiB
Swift

import SwiftData
import SwiftUI
struct SidebarView: View {
@Binding var selectedConversation: Conversation?
let onNewConversation: () -> Void
@Query(sort: \Conversation.updatedAt, order: .reverse)
private var conversations: [Conversation]
@Environment(\.modelContext) private var modelContext
var body: some View {
List(selection: $selectedConversation) {
ForEach(conversations) { conversation in
ConversationRow(conversation: conversation)
.tag(conversation)
.contextMenu {
Button("Delete", role: .destructive) {
deleteConversation(conversation)
}
}
}
}
.listStyle(.sidebar)
.toolbar {
ToolbarItem(placement: .automatic) {
Button(action: onNewConversation) {
Image(systemName: "plus")
}
.help("New Conversation (Cmd+N)")
}
}
.overlay {
if conversations.isEmpty {
ContentUnavailableView {
Label("No Conversations", systemImage: "bubble.left.and.bubble.right")
} description: {
Text("Press Cmd+N to start a new conversation")
}
}
}
}
private func deleteConversation(_ conversation: Conversation) {
if selectedConversation == conversation {
selectedConversation = nil
}
modelContext.delete(conversation)
try? modelContext.save()
}
}