Files
CxIDE/Views/AgentSidebarView.swift
T
cx-git-agent c118996746 feat: CxIDE v1 — native macOS SwiftUI IDE with agentic AI assistant
- SwiftUI macOS app with C++17 code analysis engine (ObjC++ bridge)
- Agentic AI loop: LLM plans → tool calls → execution → feedback loop
- 15 agent tools: file ops, terminal, git, xcode build, code intel
- 7 persistent terminal tools with background session management
- Chat sidebar with agent step rendering and auto-apply
- NVIDIA NIM API integration (Llama 3.3 70B default)
- OpenAI tool_calls format with prompt-based fallback
- Code editor with syntax highlighting and multi-tab support
- File tree, console view, terminal view
- Git integration and workspace management
2026-04-21 16:05:52 -05:00

166 lines
5.9 KiB
Swift

// AgentSidebarView.swift
// CxIDE Agent sidebar panel with quick actions, tool browser, and status.
import SwiftUI
struct AgentSidebarView: View {
@ObservedObject var viewModel: EditorViewModel
private var agent: AgentService { viewModel.agentService }
var body: some View {
VStack(alignment: .leading, spacing: 0) {
// Header
HStack {
Image(systemName: "cpu")
.foregroundColor(.purple)
Text("CxSwiftAgent")
.font(.system(size: 11, weight: .semibold))
Spacer()
Circle()
.fill(agent.status.color)
.frame(width: 8, height: 8)
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
Divider()
ScrollView {
VStack(alignment: .leading, spacing: 12) {
statusSection
quickActionsSection
toolBrowserSection
}
.padding(10)
}
}
.background(VSC.sidebarBg)
}
// MARK: - Status
private var statusSection: some View {
VStack(alignment: .leading, spacing: 6) {
Label {
Text(agent.status.displayText)
.font(.system(size: 10))
.foregroundColor(agent.status.color)
} icon: {
Image(systemName: agent.status.iconName)
.font(.system(size: 10))
.foregroundColor(agent.status.color)
}
HStack(spacing: 12) {
Label("\(agent.toolCount) tools", systemImage: "wrench.fill")
Label("\(agent.messageCount) msgs", systemImage: "bubble.left.fill")
}
.font(.system(size: 9))
.foregroundColor(.secondary)
if agent.sandboxMode {
Label("Sandbox Mode", systemImage: "lock.shield.fill")
.font(.system(size: 9))
.foregroundColor(.orange)
}
}
.padding(8)
.background(VSC.inputBg)
.cornerRadius(6)
}
// MARK: - Quick Actions
private var quickActionsSection: some View {
VStack(alignment: .leading, spacing: 6) {
Text("QUICK ACTIONS")
.font(.system(size: 9, weight: .bold))
.foregroundColor(.secondary)
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 6) {
quickActionButton("Explain", icon: "text.bubble", action: viewModel.agentExplainCode)
quickActionButton("Review", icon: "eye", action: viewModel.agentReviewCode)
quickActionButton("Fix", icon: "wrench", action: viewModel.agentFixCode)
quickActionButton("Tests", icon: "checkmark.seal", action: viewModel.agentGenerateTests)
quickActionButton("Diagnose", icon: "stethoscope", action: viewModel.agentRunDiagnostics)
quickActionButton("Security", icon: "lock.shield", action: viewModel.agentSecurityAudit)
quickActionButton("Workspace", icon: "folder.badge.gearshape", action: viewModel.agentAnalyzeWorkspace)
quickActionButton("All Tools", icon: "list.bullet", action: viewModel.agentListTools)
}
}
}
private func quickActionButton(_ title: String, icon: String, action: @escaping () -> Void) -> some View {
Button(action: action) {
VStack(spacing: 3) {
Image(systemName: icon)
.font(.system(size: 13))
Text(title)
.font(.system(size: 9))
}
.frame(maxWidth: .infinity)
.padding(.vertical, 8)
.background(Color.purple.opacity(0.15))
.cornerRadius(6)
}
.buttonStyle(.plain)
.disabled(agent.status != .idle)
}
// MARK: - Tool Browser
private var toolBrowserSection: some View {
VStack(alignment: .leading, spacing: 6) {
Text("TOOLS (\(agent.toolCount))")
.font(.system(size: 9, weight: .bold))
.foregroundColor(.secondary)
let grouped = Dictionary(grouping: agent.availableTools) { $0.category }
ForEach(grouped.keys.sorted(), id: \.self) { category in
DisclosureGroup {
ForEach(grouped[category] ?? []) { tool in
toolRow(tool)
}
} label: {
HStack {
Text(category)
.font(.system(size: 10, weight: .medium))
Spacer()
Text("\(grouped[category]?.count ?? 0)")
.font(.system(size: 9))
.foregroundColor(.secondary)
}
}
.font(.system(size: 10))
}
}
}
private func toolRow(_ tool: AgentToolInfo) -> some View {
Button {
viewModel.bottomPanel = .agent
Task { _ = await agent.callTool(name: tool.name) }
} label: {
HStack(spacing: 4) {
Image(systemName: tool.isReadOnly ? "lock.fill" : "play.fill")
.font(.system(size: 7))
.foregroundColor(tool.isReadOnly ? .green : .orange)
VStack(alignment: .leading, spacing: 1) {
Text(tool.name)
.font(.system(size: 9, weight: .medium, design: .monospaced))
Text(tool.description)
.font(.system(size: 8))
.foregroundColor(.secondary)
.lineLimit(1)
}
Spacer()
}
.padding(.vertical, 3)
.padding(.horizontal, 4)
}
.buttonStyle(.plain)
}
}