mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-13 20:09:55 +00:00
111 lines
4.3 KiB
Swift
111 lines
4.3 KiB
Swift
import HapiClient
|
|
import HapiProtocol
|
|
import HapiUI
|
|
import SwiftUI
|
|
|
|
/// The process page also hosts permissions. Reuse the same question form there,
|
|
/// without an inspector button that would reopen the current process.
|
|
struct PendingPermissionFooter: View {
|
|
let tool: ChatToolCall
|
|
let requestId: String
|
|
let interactions: ChatInteractor
|
|
|
|
var body: some View {
|
|
if isQuestionDetailsTool(tool.name) {
|
|
if interactions.permissionOverrides[requestId] == .alreadyHandled {
|
|
AlreadyHandledLine()
|
|
} else {
|
|
QuestionAnswerFormView(
|
|
tool: tool, requestId: requestId, enabled: true,
|
|
submitting: interactions.permissionOverrides[requestId] == .resolving,
|
|
openDetails: nil,
|
|
submit: { interactions.resolvePermission(requestId: requestId, action: $0) }
|
|
)
|
|
}
|
|
} else {
|
|
PermissionActionsRow(tool: tool, requestId: requestId, interactions: interactions)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Ordinary approvals retain their existing permission-specific controls.
|
|
struct PermissionActionsRow: View {
|
|
let tool: ChatToolCall
|
|
let requestId: String
|
|
let interactions: ChatInteractor
|
|
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
|
|
|
|
var body: some View {
|
|
let override = interactions.permissionOverrides[requestId]
|
|
if override == .alreadyHandled {
|
|
AlreadyHandledLine()
|
|
} else {
|
|
let resolving = override == .resolving
|
|
let codex = isCodexPermissionUX(flavor: interactions.flavor, toolName: tool.name)
|
|
let canAllowForSession = !codex && !PermissionGates.hideAllowForSession.contains(tool.name)
|
|
let canAllowAllEdits = interactions.flavor == "claude"
|
|
&& PermissionGates.editTools.contains(tool.name)
|
|
|
|
let layout = dynamicTypeSize.isAccessibilitySize
|
|
? AnyLayout(VStackLayout(alignment: .leading, spacing: 8))
|
|
: AnyLayout(HStackLayout(spacing: 8))
|
|
layout {
|
|
Button {
|
|
interactions.resolvePermission(requestId: requestId, action: .allow)
|
|
} label: {
|
|
Text("Allow")
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
.frame(maxWidth: .infinity, minHeight: 44)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.tint(.green)
|
|
|
|
Button {
|
|
interactions.resolvePermission(requestId: requestId, action: codex ? .abort : .deny)
|
|
} label: {
|
|
Text(codex ? String(localized: "Abort") : String(localized: "Deny"))
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
.frame(maxWidth: .infinity, minHeight: 44)
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.tint(.red)
|
|
|
|
if resolving {
|
|
ProgressView()
|
|
.controlSize(.small)
|
|
} else if codex || canAllowForSession || canAllowAllEdits {
|
|
Menu {
|
|
if codex || canAllowForSession {
|
|
Button("Allow for this session") {
|
|
interactions.resolvePermission(requestId: requestId, action: .allowForSession)
|
|
}
|
|
}
|
|
if canAllowAllEdits {
|
|
Button("Allow all edits") {
|
|
interactions.resolvePermission(requestId: requestId, action: .allowAllEdits)
|
|
}
|
|
}
|
|
} label: {
|
|
Image(systemName: "ellipsis")
|
|
.frame(minWidth: 44, minHeight: 44)
|
|
.contentShape(Rectangle())
|
|
}
|
|
}
|
|
}
|
|
.disabled(resolving)
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 6)
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct AlreadyHandledLine: View {
|
|
var body: some View {
|
|
Text("Already handled elsewhere")
|
|
.font(.caption)
|
|
.foregroundStyle(.secondary)
|
|
.padding(.horizontal, 10)
|
|
.padding(.vertical, 6)
|
|
}
|
|
}
|