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>
69 lines
2 KiB
Swift
69 lines
2 KiB
Swift
import SwiftUI
|
|
|
|
struct MessageBubble: View {
|
|
let message: Message
|
|
|
|
var body: some View {
|
|
switch message.role {
|
|
case .user:
|
|
userBubble
|
|
case .assistant:
|
|
assistantBubble
|
|
case .tool:
|
|
toolResultsBubble
|
|
case .system:
|
|
EmptyView()
|
|
}
|
|
}
|
|
|
|
private var userBubble: some View {
|
|
HStack(alignment: .top) {
|
|
Spacer(minLength: 60)
|
|
Text(message.content)
|
|
.font(.body)
|
|
.foregroundStyle(.white)
|
|
.padding(12)
|
|
.background(.accent)
|
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
|
.textSelection(.enabled)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
|
|
private var assistantBubble: some View {
|
|
HStack(alignment: .top) {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
if !message.content.isEmpty {
|
|
MarkdownView(content: message.content)
|
|
}
|
|
|
|
// Token usage
|
|
if let input = message.inputTokens, let output = message.outputTokens {
|
|
Text("In: \(input) | Out: \(output)")
|
|
.font(.system(size: 10, design: .monospaced))
|
|
.foregroundStyle(.tertiary)
|
|
.padding(.top, 4)
|
|
}
|
|
}
|
|
.padding(12)
|
|
.background(.quaternary.opacity(0.5))
|
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
|
.textSelection(.enabled)
|
|
|
|
Spacer(minLength: 60)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
|
|
private var toolResultsBubble: some View {
|
|
HStack(alignment: .top) {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
ForEach(message.toolCalls) { tc in
|
|
JSONToolResultView(toolCallData: tc)
|
|
}
|
|
}
|
|
Spacer(minLength: 60)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|