// 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) } }