tpmjs/apps/omega-mac/OmegaMac/Views/Chat/ChatInputBar.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

54 lines
1.8 KiB
Swift

import SwiftUI
struct ChatInputBar: View {
@Binding var text: String
let isStreaming: Bool
let onSend: () -> Void
var body: some View {
VStack(spacing: 4) {
Divider()
HStack(alignment: .bottom, spacing: 8) {
TextEditor(text: $text)
.font(.body)
.scrollContentBackground(.hidden)
.padding(8)
.background(.quaternary.opacity(0.3))
.clipShape(RoundedRectangle(cornerRadius: 10))
.frame(minHeight: 40, maxHeight: 160)
.fixedSize(horizontal: false, vertical: true)
.onKeyPress(.return, phases: .down) { keyPress in
if keyPress.modifiers.contains(.shift) {
return .ignored // Let shift+enter add newline
}
if canSend {
onSend()
return .handled
}
return .ignored
}
Button(action: onSend) {
Image(systemName: "arrow.up.circle.fill")
.font(.title2)
.foregroundStyle(canSend ? .accent : .tertiary)
}
.buttonStyle(.borderless)
.disabled(!canSend)
}
.padding(.horizontal, 16)
.padding(.vertical, 8)
Text("Enter to send, Shift+Enter for new line")
.font(.caption2)
.foregroundStyle(.tertiary)
.padding(.bottom, 4)
}
.background(.background)
}
private var canSend: Bool {
!isStreaming && !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
}
}