// // PersonalDataApprovalSheet.swift // oAI // // Approval UI for AI-requested Calendar/Reminders write actions // // SPDX-License-Identifier: AGPL-3.0-or-later // Copyright (C) 2026 Rune Olsen // // This file is part of oAI. // // oAI is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // oAI is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General // Public License for more details. // // You should have received a copy of the GNU Affero General Public // License along with oAI. If not, see . import SwiftUI struct PersonalDataApprovalSheet: View { let pending: MCPService.PendingPersonalDataAction let onApprove: (_ forSession: Bool) -> Void let onDeny: () -> Void var body: some View { VStack(alignment: .leading, spacing: 20) { // Header HStack(spacing: 12) { Image(systemName: "calendar.badge.exclamationmark") .font(.title2) .foregroundStyle(.orange) VStack(alignment: .leading, spacing: 2) { Text("Allow This Action?") .font(.system(size: 17, weight: .semibold)) Text("The AI wants to make a change to your calendar or reminders") .font(.system(size: 13)) .foregroundStyle(.secondary) } Spacer() } // Action description VStack(alignment: .leading, spacing: 6) { Text("ACTION") .font(.system(size: 11, weight: .medium)) .foregroundStyle(.secondary) Text(pending.summary) .font(.system(size: 13)) .foregroundStyle(.primary) .frame(maxWidth: .infinity, alignment: .leading) .textSelection(.enabled) .padding(12) .background(Color.secondary.opacity(0.08)) .clipShape(RoundedRectangle(cornerRadius: 8)) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color.secondary.opacity(0.2), lineWidth: 1) ) } // Buttons HStack(spacing: 8) { Button("Deny") { onDeny() } .buttonStyle(.bordered) .tint(.red) .keyboardShortcut(.escape, modifiers: []) Spacer() Button("Allow Once") { onApprove(false) } .buttonStyle(.bordered) .tint(.orange) Button("Allow for Session") { onApprove(true) } .buttonStyle(.borderedProminent) .tint(.orange) .keyboardShortcut(.return, modifiers: []) } } .padding(24) .frame(width: 480) } }