Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: support user-supplied literal headers#24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 4 commits intomainfromethan/literal-header-settings
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 122 additions & 35 deletionsCoder Desktop/Coder Desktop.xcodeproj/project.pbxproj
View file
Open in desktop

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.

10 changes: 1 addition & 9 deletionsCoder Desktop/Coder Desktop/About.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,15 +32,7 @@ enum About {

@MainActor
static func open() {
#if compiler(>=5.9) && canImport(AppKit)
if #available(macOS 14, *) {
NSApp.activate()
} else {
NSApp.activate(ignoringOtherApps: true)
}
#else
NSApp.activate(ignoringOtherApps: true)
#endif
appActivate()
NSApp.orderFrontStandardAboutPanel(options: [
.credits: credits,
])
Expand Down
15 changes: 14 additions & 1 deletionCoder Desktop/Coder Desktop/Coder_DesktopApp.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,11 @@ struct DesktopApp: App {
LoginForm<PreviewSession>().environmentObject(appDelegate.session)
}
.windowResizability(.contentSize)
SwiftUI.Settings { SettingsView<PreviewVPN>()
.environmentObject(appDelegate.vpn)
.environmentObject(appDelegate.settings)
}
.windowResizability(.contentSize)
}
}

Expand All@@ -22,10 +27,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
private var menuBarExtra: FluidMenuBarExtra?
let vpn: PreviewVPN
let session: PreviewSession
let settings: Settings

override init() {
// TODO: Replace with realimplementations
// TODO: Replace with realimplementation
vpn = PreviewVPN()
settings = Settings()
session = PreviewSession()
}

Expand All@@ -34,6 +41,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
VPNMenu<PreviewVPN, PreviewSession>().frame(width: 256)
.environmentObject(self.vpn)
.environmentObject(self.session)
.environmentObject(self.settings)
}
}

Expand All@@ -49,3 +57,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
false
}
}

@MainActor
func appActivate() {
NSApp.activate()
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
import CoderSDK
import Foundation
import KeychainAccess
import NetworkExtension
import SwiftUI

protocol Session: ObservableObject {
var hasSession: Bool { get }
Expand DownExpand Up@@ -89,3 +91,47 @@ class SecureSession: ObservableObject, Session {
static let sessionToken = "sessionToken"
}
}

class Settings: ObservableObject {
private let store: UserDefaults
@AppStorage(Keys.useLiteralHeaders) var useLiteralHeaders = false

@Published var literalHeaders: [LiteralHeader] {
didSet {
try? store.set(JSONEncoder().encode(literalHeaders), forKey: Keys.literalHeaders)
}
}

init(store: UserDefaults = .standard) {
self.store = store
_literalHeaders = Published(
initialValue: store.data(
forKey: Keys.literalHeaders
).flatMap { try? JSONDecoder().decode([LiteralHeader].self, from: $0) } ?? []
)
}

enum Keys {
static let useLiteralHeaders = "UseLiteralHeaders"
static let literalHeaders = "LiteralHeaders"
}
}

struct LiteralHeader: Hashable, Identifiable, Equatable, Codable {
var header: String
var value: String
var id: String {
"\(header):\(value)"
}

init(header: String, value: String) {
self.header = header
self.value = value
}
}

extension LiteralHeader {
func toSDKHeader() -> HTTPHeader {
return .init(header: header, value: value)
}
}
3 changes: 2 additions & 1 deletionCoder Desktop/Coder Desktop/Views/LoginForm.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ import SwiftUI

struct LoginForm<S: Session>: View {
@EnvironmentObject var session: S
@EnvironmentObject var settings: Settings
@Environment(\.dismiss) private var dismiss

@State private var baseAccessURL: String = ""
Expand DownExpand Up@@ -68,7 +69,7 @@ struct LoginForm<S: Session>: View {
}
loading = true
defer { loading = false }
let client = Client(url: url, token: sessionToken)
let client = Client(url: url, token: sessionToken, headers: settings.literalHeaders.map { $0.toSDKHeader() })
do {
_ = try await client.user("me")
} catch {
Expand Down
16 changes: 16 additions & 0 deletionsCoder Desktop/Coder Desktop/Views/Settings/GeneralTab.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
import LaunchAtLogin
import SwiftUI

struct GeneralTab: View {
var body: some View {
Form {
Section {
LaunchAtLogin.Toggle("Launch at Login")
}
}.formStyle(.grouped)
}
}

#Preview {
GeneralTab()
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
import SwiftUI

struct LiteralHeaderModal: View {
var existingHeader: LiteralHeader?

@EnvironmentObject var settings: Settings
@Environment(\.dismiss) private var dismiss

@State private var header: String = ""
@State private var value: String = ""

var body: some View {
VStack(spacing: 0) {
Form {
Section {
TextField("Header", text: $header)
TextField("Value", text: $value)
}
}.formStyle(.grouped).scrollDisabled(true).padding(.horizontal)
Divider()
HStack {
Spacer()
Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction)
Button(existingHeader == nil ? "Add" : "Save", action: submit)
.keyboardShortcut(.defaultAction)
}.padding(20)
}.onAppear {
if let existingHeader {
self.header = existingHeader.header
self.value = existingHeader.value
}
}
}

func submit() {
defer { dismiss() }
if let existingHeader {
settings.literalHeaders.removeAll { $0 == existingHeader }
}
let newHeader = LiteralHeader(header: header, value: value)
if !settings.literalHeaders.contains(newHeader) {
settings.literalHeaders.append(newHeader)
}
}
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
import SwiftUI

struct LiteralHeadersSection<VPN: VPNService>: View {
@EnvironmentObject var vpn: VPN
@EnvironmentObject var settings: Settings

@State private var selectedHeader: LiteralHeader.ID?
@State private var editingHeader: LiteralHeader?
@State private var addingNewHeader = false

let inspection = Inspection<Self>()

var body: some View {
Section {
Toggle(isOn: settings.$useLiteralHeaders) {
Text("HTTP Headers")
Text("When enabled, these headers will be included on all outgoing HTTP requests.")
if vpn.state != .disabled { Text("Cannot be modified while Coder VPN is enabled.") }
}
.controlSize(.large)

Table(settings.literalHeaders, selection: $selectedHeader) {
TableColumn("Header", value: \.header)
TableColumn("Value", value: \.value)
}.opacity(settings.useLiteralHeaders ? 1 : 0.5)
.frame(minWidth: 400, minHeight: 200)
.padding(.bottom, 25)
.overlay(alignment: .bottom) {
VStack(alignment: .leading, spacing: 0) {
Divider()
HStack(spacing: 0) {
Button {
addingNewHeader = true
} label: {
Image(systemName: "plus")
.frame(width: 24, height: 24)
}
Divider()
Button {
settings.literalHeaders.removeAll { $0.id == selectedHeader }
selectedHeader = nil
} label: {
Image(systemName: "minus")
.frame(width: 24, height: 24)
}.disabled(selectedHeader == nil)
}
.buttonStyle(.borderless)
}
.background(.primary.opacity(0.04))
.fixedSize(horizontal: false, vertical: true)
}
.background(.primary.opacity(0.04))
.contextMenu(forSelectionType: LiteralHeader.ID.self, menu: { _ in },
primaryAction: { selectedHeaders in
if let firstHeader = selectedHeaders.first {
editingHeader = settings.literalHeaders.first(where: { $0.id == firstHeader })
}
})
.disabled(!settings.useLiteralHeaders)
}
.sheet(isPresented: $addingNewHeader) {
LiteralHeaderModal()
}
.sheet(item: $editingHeader) { header in
LiteralHeaderModal(existingHeader: header)
}.onTapGesture {
selectedHeader = nil
}.disabled(vpn.state != .disabled)
.onReceive(inspection.notice) { self.inspection.visit(self, $0) } // ViewInspector
}
}
14 changes: 14 additions & 0 deletionsCoder Desktop/Coder Desktop/Views/Settings/NetworkTab.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
import SwiftUI

struct NetworkTab<VPN: VPNService>: View {
var body: some View {
Form {
LiteralHeadersSection<VPN>()
}
.formStyle(.grouped)
}
}

#Preview {
NetworkTab<PreviewVPN>()
}
26 changes: 26 additions & 0 deletionsCoder Desktop/Coder Desktop/Views/Settings/Settings.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
import SwiftUI

struct SettingsView<VPN: VPNService>: View {
@AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general

var body: some View {
TabView(selection: $selection) {
GeneralTab()
.tabItem {
Label("General", systemImage: "gearshape")
}.tag(SettingsTab.general)
NetworkTab<VPN>()
.tabItem {
Label("Network", systemImage: "dot.radiowaves.left.and.right")
}.tag(SettingsTab.network)
}.frame(width: 600)
.frame(maxHeight: 500)
.scrollContentBackground(.hidden)
.fixedSize()
}
}

enum SettingsTab: Int {
case general
case network
}
1 change: 1 addition & 0 deletionsCoder Desktop/Coder Desktop/Views/Util.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import Combine
import SwiftUI

// This is required for inspecting stateful views
final class Inspection<V> {
Expand Down
9 changes: 9 additions & 0 deletionsCoder Desktop/Coder Desktop/Views/VPNMenu.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,6 +3,7 @@ import SwiftUI
struct VPNMenu<VPN: VPNService, S: Session>: View {
@EnvironmentObject var vpn: VPN
@EnvironmentObject var session: S
@Environment(\.openSettings) private var openSettings

let inspection = Inspection<Self>()

Expand All@@ -21,6 +22,8 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
)) {
Text("CoderVPN")
.frame(maxWidth: .infinity, alignment: .leading)
.font(.body.bold())
.foregroundColor(.primary)
}.toggleStyle(.switch)
.disabled(vpnDisabled)
}
Expand DownExpand Up@@ -50,6 +53,12 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
TrayDivider()
}
AuthButton<VPN, S>()
Button {
openSettings()
appActivate()
} label: {
ButtonRowView { Text("Settings") }
}.buttonStyle(.plain)
Button {
About.open()
} label: {
Expand Down
10 changes: 1 addition & 9 deletionsCoder Desktop/Coder Desktop/Windows.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,15 +8,7 @@ enum Windows: String {
extension OpenWindowAction {
// Type-safe wrapper for opening windows that also focuses the new window
func callAsFunction(id: Windows) {
#if compiler(>=5.9) && canImport(AppKit)
if #available(macOS 14, *) {
NSApp.activate()
} else {
NSApp.activate(ignoringOtherApps: true)
}
#else
NSApp.activate(ignoringOtherApps: true)
#endif
appActivate()
callAsFunction(id: id.rawValue)
// The arranging behaviour is flakey without this
NSApp.arrangeInFront(nil)
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp