swift-apple-intelligence-grpc/Sources/AppleIntelligenceApp/App.swift
Mathias Beaulieu-Duncan 638656e7ca Add vision support, gRPC reflection toggle, and chat improvements
- Add Vision framework integration for image analysis (OCR, classification)
- Add image attachment support in chat UI with drag & drop
- Add recent images sidebar from Downloads/Desktop
- Add copy to clipboard button for assistant responses
- Add gRPC reflection service with toggle in settings
- Create proper .proto file and generate Swift code
- Add server restart when toggling reflection setting
- Fix port number formatting in settings (remove comma grouping)
- Update gRPC dependencies to v2.x

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 16:18:06 -05:00

123 lines
3.5 KiB
Swift

import SwiftUI
@main
struct AppleIntelligenceApp: App {
@State private var settings = AppSettings()
@State private var serverManager: ServerManager?
@State private var chatViewModel = ChatViewModel()
@State private var didAutoStart = false
var body: some Scene {
MenuBarExtra {
if let serverManager {
MenuView(
serverManager: serverManager,
settings: settings
)
}
} label: {
Image(systemName: serverManager?.state.isRunning == true ? "brain.fill" : "brain")
.onAppear {
// Auto-start server on app launch if enabled
if !didAutoStart {
didAutoStart = true
if settings.autoStartServer, let serverManager {
serverManager.start()
}
}
}
}
Window("Chat", id: "chat") {
ChatView(viewModel: chatViewModel)
}
.defaultSize(width: 500, height: 600)
Window("Settings", id: "settings") {
SettingsView(settings: settings, serverManager: serverManager)
}
.windowResizability(.contentSize)
}
init() {
let settings = AppSettings()
_settings = State(initialValue: settings)
_serverManager = State(initialValue: ServerManager(settings: settings))
}
}
struct MenuView: View {
@Bindable var serverManager: ServerManager
@Bindable var settings: AppSettings
@Environment(\.openWindow) private var openWindow
var body: some View {
VStack(alignment: .leading, spacing: 4) {
// Status section
HStack {
Circle()
.fill(statusColor)
.frame(width: 8, height: 8)
Text(serverManager.state.statusText)
.font(.headline)
}
.padding(.bottom, 4)
// Model status
Text("Model: \(modelStatusText)")
.font(.caption)
.foregroundStyle(.secondary)
Divider()
.padding(.vertical, 4)
// Toggle button
Button(serverManager.state.isRunning ? "Stop Server" : "Start Server") {
serverManager.toggle()
}
.keyboardShortcut("s", modifiers: .command)
Divider()
.padding(.vertical, 4)
Button("Open Chat...") {
openWindow(id: "chat")
NSApp.activate(ignoringOtherApps: true)
}
.keyboardShortcut("c", modifiers: .command)
Button("Settings...") {
openWindow(id: "settings")
NSApp.activate(ignoringOtherApps: true)
}
.keyboardShortcut(",", modifiers: .command)
Divider()
.padding(.vertical, 4)
Button("Quit") {
NSApplication.shared.terminate(nil)
}
.keyboardShortcut("q", modifiers: .command)
}
.padding(8)
}
private var statusColor: Color {
switch serverManager.state {
case .stopped:
return .gray
case .starting:
return .yellow
case .running:
return .green
case .error:
return .red
}
}
private var modelStatusText: String {
"Internal"
}
}