Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cea139b

Browse files
committedJan 17, 2025
feat: support user-supplied literal headers
1 parent f0465a3 commit cea139b

File tree

14 files changed

+357
-41
lines changed

14 files changed

+357
-41
lines changed
 

‎Coder Desktop/Coder Desktop.xcodeproj/project.pbxproj

Lines changed: 106 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
/* Begin PBXBuildFile section */
1010
961679332CFF117300B2B6DF /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 961679322CFF117300B2B6DF /* NetworkExtension.framework */; };
1111
9616793D2CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension in Embed System Extensions */ = {isa = PBXBuildFile; fileRef = 961679302CFF117300B2B6DF /* com.coder.Coder-Desktop.VPN.systemextension */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
12+
AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = AA2C690E2D34F6920059AFAF /* LaunchAtLogin */; };
1213
AA3B3DA92D2D23860099996A /* VPNLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA3B3DA12D2D23860099996A /* VPNLib.framework */; };
1314
AA3B3DBF2D2D23AB0099996A /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */; };
1415
AA3B3DC12D2D23AB0099996A /* SwiftProtobufPluginLibrary in Frameworks */ = {isa = PBXBuildFile; productRef = AA3B3DC02D2D23AB0099996A /* SwiftProtobufPluginLibrary */; };
@@ -229,6 +230,7 @@
229230
files = (
230231
AA3B40A42D2FC8560099996A /* CoderSDK.framework in Frameworks */,
231232
AA8BC4CF2D00A4B700E1ABAA /* KeychainAccess in Frameworks */,
233+
AA2C690F2D34F6920059AFAF /* LaunchAtLogin in Frameworks */,
232234
AA8BC33F2D0061F200E1ABAA /* FluidMenuBarExtra in Frameworks */,
233235
);
234236
runOnlyForDeploymentPostprocessing = 0;
@@ -368,7 +370,7 @@
368370
buildRules = (
369371
);
370372
dependencies = (
371-
AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */,
373+
AA2C698C2D354A800059AFAF /* PBXTargetDependency */,
372374
9616793C2CFF117300B2B6DF /* PBXTargetDependency */,
373375
AA3B40A32D2FC8560099996A /* PBXTargetDependency */,
374376
);
@@ -379,6 +381,7 @@
379381
packageProductDependencies = (
380382
AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */,
381383
AA8BC4CE2D00A4B700E1ABAA /* KeychainAccess */,
384+
AA2C690E2D34F6920059AFAF /* LaunchAtLogin */,
382385
);
383386
productName = "Coder Desktop";
384387
productReference = 961678FC2CFF100D00B2B6DF /* Coder Desktop.app */;
@@ -395,6 +398,7 @@
395398
buildRules = (
396399
);
397400
dependencies = (
401+
AA2C698E2D354A840059AFAF /* PBXTargetDependency */,
398402
961679112CFF100E00B2B6DF /* PBXTargetDependency */,
399403
AA3B40BA2D2FDA5C0099996A /* PBXTargetDependency */,
400404
);
@@ -421,6 +425,7 @@
421425
buildRules = (
422426
);
423427
dependencies = (
428+
AA2C69902D354A880059AFAF /* PBXTargetDependency */,
424429
9616791B2CFF100E00B2B6DF /* PBXTargetDependency */,
425430
);
426431
fileSystemSynchronizedGroups = (
@@ -445,6 +450,7 @@
445450
buildRules = (
446451
);
447452
dependencies = (
453+
AA2C69922D354A8B0059AFAF /* PBXTargetDependency */,
448454
AA3B3DD02D2D249F0099996A /* PBXTargetDependency */,
449455
);
450456
fileSystemSynchronizedGroups = (
@@ -469,6 +475,7 @@
469475
buildRules = (
470476
);
471477
dependencies = (
478+
AA2C69942D354A8E0059AFAF /* PBXTargetDependency */,
472479
AA3B40C32D2FE7760099996A /* PBXTargetDependency */,
473480
);
474481
fileSystemSynchronizedGroups = (
@@ -494,6 +501,7 @@
494501
buildRules = (
495502
);
496503
dependencies = (
504+
AA2C69962D354A910059AFAF /* PBXTargetDependency */,
497505
AA3B3DAB2D2D23860099996A /* PBXTargetDependency */,
498506
AA3B3DAD2D2D23860099996A /* PBXTargetDependency */,
499507
);
@@ -520,6 +528,7 @@
520528
buildRules = (
521529
);
522530
dependencies = (
531+
AA2C69982D354A940059AFAF /* PBXTargetDependency */,
523532
);
524533
fileSystemSynchronizedGroups = (
525534
AA3B40922D2FC8560099996A /* CoderSDK */,
@@ -542,6 +551,7 @@
542551
buildRules = (
543552
);
544553
dependencies = (
554+
AA2C699A2D354A970059AFAF /* PBXTargetDependency */,
545555
AA3B409B2D2FC8560099996A /* PBXTargetDependency */,
546556
AA3B409D2D2FC8560099996A /* PBXTargetDependency */,
547557
);
@@ -607,11 +617,12 @@
607617
minimizedProjectReferenceProxies = 1;
608618
packageReferences = (
609619
AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */,
610-
AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
611620
AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */,
612621
AA8BC4CD2D00A4B700E1ABAA /* XCRemoteSwiftPackageReference "KeychainAccess" */,
613622
961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */,
614623
AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */,
624+
AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */,
625+
AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */,
615626
);
616627
preferredProjectObjectVersion = 77;
617628
productRefGroup = 961678FD2CFF100D00B2B6DF /* Products */;
@@ -764,6 +775,38 @@
764775
target = 9616792F2CFF117300B2B6DF /* VPN */;
765776
targetProxy = 9616793B2CFF117300B2B6DF /* PBXContainerItemProxy */;
766777
};
778+
AA2C698C2D354A800059AFAF /* PBXTargetDependency */ = {
779+
isa = PBXTargetDependency;
780+
productRef = AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */;
781+
};
782+
AA2C698E2D354A840059AFAF /* PBXTargetDependency */ = {
783+
isa = PBXTargetDependency;
784+
productRef = AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */;
785+
};
786+
AA2C69902D354A880059AFAF /* PBXTargetDependency */ = {
787+
isa = PBXTargetDependency;
788+
productRef = AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */;
789+
};
790+
AA2C69922D354A8B0059AFAF /* PBXTargetDependency */ = {
791+
isa = PBXTargetDependency;
792+
productRef = AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */;
793+
};
794+
AA2C69942D354A8E0059AFAF /* PBXTargetDependency */ = {
795+
isa = PBXTargetDependency;
796+
productRef = AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */;
797+
};
798+
AA2C69962D354A910059AFAF /* PBXTargetDependency */ = {
799+
isa = PBXTargetDependency;
800+
productRef = AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */;
801+
};
802+
AA2C69982D354A940059AFAF /* PBXTargetDependency */ = {
803+
isa = PBXTargetDependency;
804+
productRef = AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */;
805+
};
806+
AA2C699A2D354A970059AFAF /* PBXTargetDependency */ = {
807+
isa = PBXTargetDependency;
808+
productRef = AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */;
809+
};
767810
AA3B3DAB2D2D23860099996A /* PBXTargetDependency */ = {
768811
isa = PBXTargetDependency;
769812
target = AA3B3DA02D2D23860099996A /* VPNLib */;
@@ -804,10 +847,6 @@
804847
target = AA3B40902D2FC8560099996A /* CoderSDK */;
805848
targetProxy = AA3B40C22D2FE7760099996A /* PBXContainerItemProxy */;
806849
};
807-
AA8BC33C2D0060E700E1ABAA /* PBXTargetDependency */ = {
808-
isa = PBXTargetDependency;
809-
productRef = AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */;
810-
};
811850
/* End PBXTargetDependency section */
812851

813852
/* Begin XCBuildConfiguration section */
@@ -1446,6 +1485,22 @@
14461485
version = 1.28.2;
14471486
};
14481487
};
1488+
AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */ = {
1489+
isa = XCRemoteSwiftPackageReference;
1490+
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-modern";
1491+
requirement = {
1492+
kind = exactVersion;
1493+
version = 1.1.0;
1494+
};
1495+
};
1496+
AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = {
1497+
isa = XCRemoteSwiftPackageReference;
1498+
repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins";
1499+
requirement = {
1500+
kind = upToNextMajorVersion;
1501+
minimumVersion = 0.58.0;
1502+
};
1503+
};
14491504
AA3B3E8A2D2E0FE10099996A /* XCRemoteSwiftPackageReference "Mocker" */ = {
14501505
isa = XCRemoteSwiftPackageReference;
14511506
repositoryURL = "https://github.com/WeTransfer/Mocker";
@@ -1462,14 +1517,6 @@
14621517
minimumVersion = 0.10.0;
14631518
};
14641519
};
1465-
AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */ = {
1466-
isa = XCRemoteSwiftPackageReference;
1467-
repositoryURL = "https://github.com/SimplyDanny/SwiftLintPlugins";
1468-
requirement = {
1469-
kind = upToNextMajorVersion;
1470-
minimumVersion = 0.57.1;
1471-
};
1472-
};
14731520
AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */ = {
14741521
isa = XCRemoteSwiftPackageReference;
14751522
repositoryURL = "https://github.com/lfroms/fluid-menu-bar-extra";
@@ -1489,6 +1536,51 @@
14891536
/* End XCRemoteSwiftPackageReference section */
14901537

14911538
/* Begin XCSwiftPackageProductDependency section */
1539+
AA2C690E2D34F6920059AFAF /* LaunchAtLogin */ = {
1540+
isa = XCSwiftPackageProductDependency;
1541+
package = AA2C690D2D34F6920059AFAF /* XCRemoteSwiftPackageReference "LaunchAtLogin-modern" */;
1542+
productName = LaunchAtLogin;
1543+
};
1544+
AA2C698B2D354A800059AFAF /* SwiftLintBuildToolPlugin */ = {
1545+
isa = XCSwiftPackageProductDependency;
1546+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1547+
productName = "plugin:SwiftLintBuildToolPlugin";
1548+
};
1549+
AA2C698D2D354A840059AFAF /* SwiftLintBuildToolPlugin */ = {
1550+
isa = XCSwiftPackageProductDependency;
1551+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1552+
productName = "plugin:SwiftLintBuildToolPlugin";
1553+
};
1554+
AA2C698F2D354A880059AFAF /* SwiftLintBuildToolPlugin */ = {
1555+
isa = XCSwiftPackageProductDependency;
1556+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1557+
productName = "plugin:SwiftLintBuildToolPlugin";
1558+
};
1559+
AA2C69912D354A8B0059AFAF /* SwiftLintBuildToolPlugin */ = {
1560+
isa = XCSwiftPackageProductDependency;
1561+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1562+
productName = "plugin:SwiftLintBuildToolPlugin";
1563+
};
1564+
AA2C69932D354A8E0059AFAF /* SwiftLintBuildToolPlugin */ = {
1565+
isa = XCSwiftPackageProductDependency;
1566+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1567+
productName = "plugin:SwiftLintBuildToolPlugin";
1568+
};
1569+
AA2C69952D354A910059AFAF /* SwiftLintBuildToolPlugin */ = {
1570+
isa = XCSwiftPackageProductDependency;
1571+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1572+
productName = "plugin:SwiftLintBuildToolPlugin";
1573+
};
1574+
AA2C69972D354A940059AFAF /* SwiftLintBuildToolPlugin */ = {
1575+
isa = XCSwiftPackageProductDependency;
1576+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1577+
productName = "plugin:SwiftLintBuildToolPlugin";
1578+
};
1579+
AA2C69992D354A970059AFAF /* SwiftLintBuildToolPlugin */ = {
1580+
isa = XCSwiftPackageProductDependency;
1581+
package = AA2C698A2D354A600059AFAF /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1582+
productName = "plugin:SwiftLintBuildToolPlugin";
1583+
};
14921584
AA3B3DBE2D2D23AB0099996A /* SwiftProtobuf */ = {
14931585
isa = XCSwiftPackageProductDependency;
14941586
package = 961679512CFF207900B2B6DF /* XCRemoteSwiftPackageReference "swift-protobuf" */;
@@ -1519,11 +1611,6 @@
15191611
package = AA8BC3372D00609700E1ABAA /* XCRemoteSwiftPackageReference "ViewInspector" */;
15201612
productName = ViewInspector;
15211613
};
1522-
AA8BC33B2D0060E700E1ABAA /* SwiftLintBuildToolPlugin */ = {
1523-
isa = XCSwiftPackageProductDependency;
1524-
package = AA8BC33A2D0060C500E1ABAA /* XCRemoteSwiftPackageReference "SwiftLintPlugins" */;
1525-
productName = "plugin:SwiftLintBuildToolPlugin";
1526-
};
15271614
AA8BC33E2D0061F200E1ABAA /* FluidMenuBarExtra */ = {
15281615
isa = XCSwiftPackageProductDependency;
15291616
package = AA8BC33D2D0061F200E1ABAA /* XCRemoteSwiftPackageReference "fluid-menu-bar-extra" */;

‎Coder Desktop/Coder Desktop.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 12 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Coder Desktop/Coder Desktop/About.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,7 @@ enum About {
3232

3333
@MainActor
3434
static func open() {
35-
#if compiler(>=5.9) && canImport(AppKit)
36-
if #available(macOS 14, *) {
37-
NSApp.activate()
38-
} else {
39-
NSApp.activate(ignoringOtherApps: true)
40-
}
41-
#else
42-
NSApp.activate(ignoringOtherApps: true)
43-
#endif
35+
appActivate()
4436
NSApp.orderFrontStandardAboutPanel(options: [
4537
.credits: credits,
4638
])

‎Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import FluidMenuBarExtra
2+
import SwiftData
23
import SwiftUI
34

45
@main
@@ -14,6 +15,7 @@ struct DesktopApp: App {
1415
LoginForm<PreviewSession>().environmentObject(appDelegate.session)
1516
}
1617
.windowResizability(.contentSize)
18+
Settings { SettingsView() }.modelContainer(appDelegate.modelContainer)
1719
}
1820
}
1921

@@ -22,11 +24,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2224
private var menuBarExtra: FluidMenuBarExtra?
2325
let vpn: PreviewVPN
2426
let session: PreviewSession
27+
let modelContainer: ModelContainer
2528

2629
override init() {
2730
// TODO: Replace with real implementations
2831
vpn = PreviewVPN()
2932
session = PreviewSession()
33+
modelContainer = try! ModelContainer( // swiftlint:disable:this force_try
34+
for: LiteralHeader.self,
35+
configurations: ModelConfiguration(
36+
url: .applicationSupportDirectory
37+
.appending(component: Bundle.main.bundleIdentifier!)
38+
.appending(component: "Store.sqlite")
39+
)
40+
)
3041
}
3142

3243
func applicationDidFinishLaunching(_: Notification) {
@@ -41,3 +52,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
4152
false
4253
}
4354
}
55+
56+
@MainActor
57+
func appActivate() {
58+
#if compiler(>=5.9) && canImport(AppKit)
59+
if #available(macOS 14, *) {
60+
NSApp.activate()
61+
} else {
62+
NSApp.activate(ignoringOtherApps: true)
63+
}
64+
#else
65+
NSApp.activate(ignoringOtherApps: true)
66+
#endif
67+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import CoderSDK
2+
import Foundation
3+
import SwiftData
4+
5+
@Model
6+
final class LiteralHeader: Hashable, Identifiable, Equatable {
7+
var header: String
8+
var value: String
9+
init(header: String, value: String) {
10+
self.header = header
11+
self.value = value
12+
}
13+
}
14+
15+
extension LiteralHeader {
16+
func toSDKHeader() -> HTTPHeader {
17+
return .init(header: header, value: value)
18+
}
19+
}

‎Coder Desktop/Coder Desktop/Views/LoginForm.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import CoderSDK
2+
import SwiftData
23
import SwiftUI
34

45
struct LoginForm<S: Session>: View {
@@ -11,6 +12,7 @@ struct LoginForm<S: Session>: View {
1112
@State private var currentPage: LoginPage = .serverURL
1213
@State private var loading: Bool = false
1314
@FocusState private var focusedField: LoginField?
15+
@Query private var headers: [LiteralHeader]
1416

1517
let inspection = Inspection<Self>()
1618

@@ -68,7 +70,7 @@ struct LoginForm<S: Session>: View {
6870
}
6971
loading = true
7072
defer { loading = false }
71-
let client = Client(url: url, token: sessionToken)
73+
let client = Client(url: url, token: sessionToken, headers: headers.map { $0.toSDKHeader() })
7274
do {
7375
_ = try await client.user("me")
7476
} catch {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import LaunchAtLogin
2+
import SwiftUI
3+
4+
struct GeneralTab: View {
5+
var body: some View {
6+
Form {
7+
Section {
8+
LaunchAtLogin.Toggle("Launch at Login")
9+
}
10+
}.formStyle(.grouped)
11+
}
12+
}
13+
14+
#Preview {
15+
GeneralTab()
16+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import SwiftData
2+
import SwiftUI
3+
4+
struct LiteralHeaderModal: View {
5+
var existingHeader: LiteralHeader?
6+
@State private var header: String = ""
7+
@State private var value: String = ""
8+
@Environment(\.dismiss) private var dismiss
9+
@Environment(\.modelContext) private var modelContext
10+
11+
var body: some View {
12+
VStack(spacing: 0) {
13+
Form {
14+
Section {
15+
TextField("Header", text: $header)
16+
TextField("Value", text: $value)
17+
}
18+
}.formStyle(.grouped).scrollDisabled(true).padding(.horizontal)
19+
Divider()
20+
HStack {
21+
Spacer()
22+
Button("Cancel", action: { dismiss() }).keyboardShortcut(.cancelAction)
23+
Button(existingHeader == nil ? "Add" : "Save", action: submit)
24+
.keyboardShortcut(.defaultAction)
25+
}.padding(20)
26+
}.onAppear {
27+
if let existingHeader {
28+
self.header = existingHeader.header
29+
self.value = existingHeader.value
30+
}
31+
}
32+
}
33+
34+
func submit() {
35+
if let existingHeader {
36+
existingHeader.header = header
37+
existingHeader.value = value
38+
} else {
39+
modelContext.insert(LiteralHeader(header: header, value: value))
40+
}
41+
dismiss()
42+
}
43+
}
44+
45+
#Preview {
46+
LiteralHeaderModal()
47+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import SwiftData
2+
import SwiftUI
3+
4+
struct LiteralHeadersSection: View {
5+
@AppStorage("UseLiteralHeaders") private var useLiteralHeaders = false
6+
@Environment(\.modelContext) private var modelContext
7+
8+
@State private var selectedHeader: PersistentIdentifier?
9+
@State private var editingHeader: LiteralHeader?
10+
@State private var addingNewHeader = false
11+
@Query private var headers: [LiteralHeader]
12+
13+
var body: some View {
14+
Section {
15+
Toggle(isOn: $useLiteralHeaders) {
16+
Text("HTTP Headers")
17+
Text("When enabled, these headers will be included on all outgoing HTTP requests.")
18+
}
19+
.controlSize(.large)
20+
21+
Table(headers, selection: $selectedHeader) {
22+
TableColumn("Header", value: \.header)
23+
TableColumn("Value", value: \.value)
24+
}.opacity(useLiteralHeaders ? 1 : 0.5)
25+
.frame(minWidth: 400, minHeight: 200)
26+
.padding(.bottom, 25)
27+
.overlay(alignment: .bottom) {
28+
VStack(alignment: .leading, spacing: 0) {
29+
Divider()
30+
HStack(spacing: 0) {
31+
Button {
32+
addingNewHeader = true
33+
} label: {
34+
Image(systemName: "plus")
35+
.frame(width: 24, height: 24)
36+
}
37+
Divider()
38+
Button {
39+
removeSelection()
40+
} label: {
41+
Image(systemName: "minus")
42+
.frame(width: 24, height: 24)
43+
}.disabled(selectedHeader == nil)
44+
}
45+
.buttonStyle(.borderless)
46+
}
47+
.background(.primary.opacity(0.04))
48+
.fixedSize(horizontal: false, vertical: true)
49+
}
50+
.background(.primary.opacity(0.04))
51+
.contextMenu(forSelectionType: PersistentIdentifier.self, menu: { _ in },
52+
primaryAction: { selectedHeaders in
53+
if let firstHeader = selectedHeaders.first {
54+
editingHeader = headers.first(where: { $0.id == firstHeader })
55+
}
56+
})
57+
.disabled(!useLiteralHeaders)
58+
}
59+
.sheet(isPresented: $addingNewHeader) {
60+
LiteralHeaderModal()
61+
}
62+
.sheet(item: $editingHeader) { header in
63+
LiteralHeaderModal(existingHeader: header)
64+
}.onTapGesture {
65+
selectedHeader = nil
66+
}
67+
}
68+
69+
func removeSelection() {
70+
if let selectedHeader, let header = headers.first(where: { $0.id == selectedHeader }) {
71+
modelContext.delete(header)
72+
self.selectedHeader = nil
73+
}
74+
}
75+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import SwiftData
2+
import SwiftUI
3+
4+
struct NetworkTab: View {
5+
var body: some View {
6+
Form {
7+
LiteralHeadersSection()
8+
}
9+
.formStyle(.grouped)
10+
}
11+
}
12+
13+
#Preview {
14+
NetworkTab()
15+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import SwiftUI
2+
3+
struct SettingsView: View {
4+
@AppStorage("SettingsSelectedIndex") private var selection: SettingsTab = .general
5+
6+
var body: some View {
7+
TabView(selection: $selection) {
8+
GeneralTab()
9+
.tabItem {
10+
Label("General", systemImage: "gearshape")
11+
}.tag(SettingsTab.general)
12+
NetworkTab()
13+
.tabItem {
14+
Label("Network", systemImage: "dot.radiowaves.left.and.right")
15+
}.tag(SettingsTab.network)
16+
}.frame(width: 600)
17+
.frame(maxHeight: 500)
18+
.scrollContentBackground(.hidden)
19+
.fixedSize()
20+
}
21+
}
22+
23+
enum SettingsTab: Int {
24+
case general
25+
case network
26+
}

‎Coder Desktop/Coder Desktop/Views/Util.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Combine
2+
import SwiftUI
23

34
// This is required for inspecting stateful views
45
final class Inspection<V> {

‎Coder Desktop/Coder Desktop/Views/VPNMenu.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import SwiftUI
33
struct VPNMenu<VPN: VPNService, S: Session>: View {
44
@EnvironmentObject var vpn: VPN
55
@EnvironmentObject var session: S
6+
@Environment(\.openSettings) private var openSettings
67

78
let inspection = Inspection<Self>()
89

@@ -21,6 +22,8 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
2122
)) {
2223
Text("CoderVPN")
2324
.frame(maxWidth: .infinity, alignment: .leading)
25+
.font(.body.bold())
26+
.foregroundColor(.primary)
2427
}.toggleStyle(.switch)
2528
.disabled(vpnDisabled)
2629
}
@@ -50,6 +53,14 @@ struct VPNMenu<VPN: VPNService, S: Session>: View {
5053
TrayDivider()
5154
}
5255
AuthButton<VPN, S>()
56+
Button {
57+
openSettings()
58+
appActivate()
59+
} label: {
60+
ButtonRowView {
61+
Text("Settings")
62+
}
63+
}.buttonStyle(.plain)
5364
Button {
5465
About.open()
5566
} label: {

‎Coder Desktop/Coder Desktop/Windows.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,7 @@ enum Windows: String {
88
extension OpenWindowAction {
99
// Type-safe wrapper for opening windows that also focuses the new window
1010
func callAsFunction(id: Windows) {
11-
#if compiler(>=5.9) && canImport(AppKit)
12-
if #available(macOS 14, *) {
13-
NSApp.activate()
14-
} else {
15-
NSApp.activate(ignoringOtherApps: true)
16-
}
17-
#else
18-
NSApp.activate(ignoringOtherApps: true)
19-
#endif
11+
appActivate()
2012
callAsFunction(id: id.rawValue)
2113
// The arranging behaviour is flakey without this
2214
NSApp.arrangeInFront(nil)

0 commit comments

Comments
 (0)
Please sign in to comment.