import SwiftUI import ProxyCore struct ComposeEditorView: View { let requestId: Int64 @State private var method = "GET" @State private var url = "" @State private var headersText = "" @State private var queryText = "" @State private var bodyText = "" @State private var selectedTab: EditorTab = .headers @State private var isSending = false @State private var responseStatus: Int? @State private var responseBody: String? private let composeRepo = ComposeRepository() enum EditorTab: String, CaseIterable { case headers = "Headers" case query = "Query" case body = "Body" } var body: some View { VStack(spacing: 0) { // Method + URL HStack(spacing: 12) { Menu { ForEach(ProxyConstants.httpMethods, id: \.self) { m in Button(m) { method = m } } } label: { HStack(spacing: 4) { Text(method) .font(.subheadline.weight(.semibold)) .foregroundStyle(.green) Image(systemName: "chevron.down") .font(.caption2) .foregroundStyle(.secondary) } .padding(.horizontal, 10) .padding(.vertical, 6) .background(Color(.systemGray6), in: RoundedRectangle(cornerRadius: 8)) } TextField("Tap to edit URL", text: $url) .textFieldStyle(.roundedBorder) .font(.subheadline) .textInputAutocapitalization(.never) .autocorrectionDisabled() } .padding() // Tabs Picker("Tab", selection: $selectedTab) { ForEach(EditorTab.allCases, id: \.self) { tab in Text(tab.rawValue).tag(tab) } } .pickerStyle(.segmented) .padding(.horizontal) // Tab Content ScrollView { VStack(alignment: .leading, spacing: 12) { switch selectedTab { case .headers: headerEditor case .query: queryEditor case .body: bodyEditor } } .padding() } // Response if let responseBody { Divider() ScrollView { Text(responseBody) .font(.system(.caption, design: .monospaced)) .textSelection(.enabled) .frame(maxWidth: .infinity, alignment: .leading) .padding() } .frame(maxHeight: 200) .background(Color(.systemGray6)) } } .navigationTitle("New Request") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button { Task { await sendRequest() } } label: { Text("Send") .font(.headline) } .buttonStyle(.borderedProminent) .disabled(url.isEmpty || isSending) } } } private var headerEditor: some View { VStack(alignment: .leading, spacing: 8) { if headersText.isEmpty { EmptyStateView( icon: "list.bullet.rectangle", title: "No Headers", subtitle: "Tap 'Edit Headers' to add headers." ) } Button("Edit Headers") { // TODO: Open header editor sheet } .frame(maxWidth: .infinity) .buttonStyle(.borderedProminent) } } private var queryEditor: some View { VStack(alignment: .leading, spacing: 8) { TextEditor(text: $queryText) .font(.system(.caption, design: .monospaced)) .frame(minHeight: 100) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color(.systemGray4)) ) Text("Format: key=value, one per line") .font(.caption2) .foregroundStyle(.secondary) } } private var bodyEditor: some View { VStack(alignment: .leading, spacing: 8) { TextEditor(text: $bodyText) .font(.system(.caption, design: .monospaced)) .frame(minHeight: 200) .overlay( RoundedRectangle(cornerRadius: 8) .stroke(Color(.systemGray4)) ) } } private func sendRequest() async { guard let requestURL = URL(string: url) else { return } isSending = true defer { isSending = false } var request = URLRequest(url: requestURL) request.httpMethod = method if !bodyText.isEmpty { request.httpBody = bodyText.data(using: .utf8) } do { let (data, response) = try await URLSession.shared.data(for: request) if let httpResponse = response as? HTTPURLResponse { responseStatus = httpResponse.statusCode } if let string = String(data: data, encoding: .utf8) { responseBody = string } else { responseBody = "\(data.count) bytes (binary)" } } catch { responseBody = "Error: \(error.localizedDescription)" } } }