diff --git a/ClashX Dashboard.xcodeproj/project.pbxproj b/ClashX Dashboard.xcodeproj/project.pbxproj index ae9b686..5435c95 100644 --- a/ClashX Dashboard.xcodeproj/project.pbxproj +++ b/ClashX Dashboard.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 01CD0A9229E93ABB00F4C17E /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 01CD0A9129E93ABB00F4C17E /* DifferenceKit */; }; 01DCEFB12A150E8B00DBBDB3 /* RuleProvidersRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DCEFB02A150E8B00DBBDB3 /* RuleProvidersRowView.swift */; }; 01DCEFB32A150FB300DBBDB3 /* ProxyProvidersRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01DCEFB22A150FB300DBBDB3 /* ProxyProvidersRowView.swift */; }; + 01F5E3F42A1E53F4008F3DEB /* ConfigItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */; }; 01F885CF29DFD8DF008241EB /* CollectionsTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F885CE29DFD8DF008241EB /* CollectionsTableView.swift */; }; 01F885D129E03F20008241EB /* CollectionTableCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01F885D029E03F20008241EB /* CollectionTableCellView.xib */; }; 01F885D329E04E21008241EB /* ProxyGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01F885D229E04E21008241EB /* ProxyGroupView.swift */; }; @@ -115,6 +116,7 @@ 01A3EF032A120103003038B5 /* DBProxyStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DBProxyStorage.swift; sourceTree = ""; }; 01DCEFB02A150E8B00DBBDB3 /* RuleProvidersRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuleProvidersRowView.swift; sourceTree = ""; }; 01DCEFB22A150FB300DBBDB3 /* ProxyProvidersRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyProvidersRowView.swift; sourceTree = ""; }; + 01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigItemView.swift; sourceTree = ""; }; 01F885CE29DFD8DF008241EB /* CollectionsTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionsTableView.swift; sourceTree = ""; }; 01F885D029E03F20008241EB /* CollectionTableCellView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CollectionTableCellView.xib; sourceTree = ""; }; 01F885D229E04E21008241EB /* ProxyGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyGroupView.swift; sourceTree = ""; }; @@ -356,6 +358,7 @@ isa = PBXGroup; children = ( 0192317D29DD5E0100539EDD /* ConfigView.swift */, + 01F5E3F32A1E53F4008F3DEB /* ConfigItemView.swift */, ); path = Config; sourceTree = ""; @@ -480,6 +483,7 @@ 01F885D529E053DE008241EB /* ProxyItemView.swift in Sources */, 0192B5D029DE5151002CDBF3 /* ClashRuleProvider.swift in Sources */, 01DCEFB12A150E8B00DBBDB3 /* RuleProvidersRowView.swift in Sources */, + 01F5E3F42A1E53F4008F3DEB /* ConfigItemView.swift in Sources */, 0192317129DD566000539EDD /* SidebarView.swift in Sources */, 0192B5D129DE5151002CDBF3 /* ClashRule.swift in Sources */, 0192317C29DD5DF200539EDD /* ConnectionsView.swift in Sources */, diff --git a/ClashX Dashboard/Views/ContentTabs/Config/ConfigItemView.swift b/ClashX Dashboard/Views/ContentTabs/Config/ConfigItemView.swift new file mode 100644 index 0000000..0fd86c5 --- /dev/null +++ b/ClashX Dashboard/Views/ContentTabs/Config/ConfigItemView.swift @@ -0,0 +1,36 @@ +// +// ConfigItemView.swift +// ClashX Dashboard +// +// + +import SwiftUI + +struct ConfigItemView: View { + + @State var name: String + var content: () -> Content + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + HStack { + Text(name) + .font(.subheadline) + .foregroundColor(.secondary) + Spacer() + } + HStack(content: content) + } + .padding(EdgeInsets(top: 10, leading: 13, bottom: 10, trailing: 13)) + .background(Color(nsColor: .textBackgroundColor)) + .cornerRadius(10) + } +} + +struct ConfigItemView_Previews: PreviewProvider { + static var previews: some View { + ConfigItemView(name: "test") { + Text("label") + } + } +} diff --git a/ClashX Dashboard/Views/ContentTabs/Config/ConfigView.swift b/ClashX Dashboard/Views/ContentTabs/Config/ConfigView.swift index 7c81ed7..2b8860c 100644 --- a/ClashX Dashboard/Views/ContentTabs/Config/ConfigView.swift +++ b/ClashX Dashboard/Views/ContentTabs/Config/ConfigView.swift @@ -12,117 +12,239 @@ struct ConfigView: View { @State var socks5Port: Int = 0 @State var mixedPort: Int = 0 @State var redirPort: Int = 0 - @State var mode: String = "Rule" - @State var logLevel: String = "Debug" + @State var mode: ClashProxyMode = .direct + @State var logLevel: ClashLogLevel = .unknow @State var allowLAN: Bool = false @State var sniffer: Bool = false + @State var ipv6: Bool = false @State var enableTUNDevice: Bool = false @State var tunIPStack: String = "System" @State var deviceName: String = "utun9" @State var interfaceName: String = "en0" - @State var disableAll = true + @State private var configInited = false + + private let toggleStyle = SwitchToggleStyle() var body: some View { ScrollView { - LazyVGrid(columns: [ - GridItem(.flexible()), - GridItem(.flexible()) - ]) { - VStack(alignment: .leading) { - Text("Http Port") - TextField("0", value: $httpPort, formatter: NumberFormatter()) - } - - VStack(alignment: .leading) { - Text("Socks5 Port") - TextField("0", value: $socks5Port, formatter: NumberFormatter()) - } - - VStack(alignment: .leading) { - Text("Mixed Port") - TextField("0", value: $mixedPort, formatter: NumberFormatter()) - } - - VStack(alignment: .leading) { - Text("Redir Port") - TextField("0", value: $redirPort, formatter: NumberFormatter()) - } - - VStack(alignment: .leading) { - Text("Mode") - Picker("", selection: $mode) { - ForEach(["Direct", "Rule", "Script", "Global"], id: \.self) { - Text($0) - } - } - .pickerStyle(.menu) - } - - VStack(alignment: .leading) { - Text("Log Level") - Picker("", selection: $logLevel) { - ForEach(["Silent", "Error", "Warning", "Info", "Debug"], id: \.self) { - Text($0) - } - } - .pickerStyle(.menu) - } - Toggle("Allow LAN", isOn: $allowLAN) - Toggle("Sniffer", isOn: $sniffer) - } - .padding() + modeView + + content1 + .padding() Divider() .padding() - LazyVGrid(columns: [ - GridItem(.flexible()), - GridItem(.flexible()) - ]) { - Toggle("Enable TUN Device", isOn: $enableTUNDevice) - - VStack(alignment: .leading) { - Text("TUN IP Stack") - Picker("", selection: $tunIPStack) { - ForEach(["gVisor", "System", "LWIP"], id: \.self) { - Text($0) - } - } - .pickerStyle(.menu) - } - - VStack(alignment: .leading) { - Text("Device Name") - TextField("utun9", text: $deviceName) - } - - VStack(alignment: .leading) { - Text("Interface Name") - TextField("en0", text: $interfaceName) - } - - } - .padding() + tunView + .padding() + + Divider() + .padding() + + content2 + .padding() } - .disabled(disableAll) + .disabled(!configInited) .onAppear { + configInited = false ApiRequest.requestConfig { config in httpPort = config.port socks5Port = config.socksPort mixedPort = config.mixedPort redirPort = config.redirPort - mode = config.mode.rawValue.capitalized - logLevel = config.logLevel.rawValue.capitalized + mode = config.mode + logLevel = config.logLevel allowLAN = config.allowLan sniffer = config.sniffing + ipv6 = config.ipv6 enableTUNDevice = config.tun.enable tunIPStack = config.tun.stack deviceName = config.tun.device interfaceName = config.interfaceName + + configInited = true + } + } + .onDisappear { + configInited = false + } + } + + + var modeView: some View { + Picker("", selection: $mode) { + ForEach([ + ClashProxyMode.direct, + .rule, + .global + ], id: \.self) { + Text($0.name).tag($0) + } + } + .onChange(of: mode) { newValue in + guard configInited else { return } + ApiRequest.updateOutBoundMode(mode: newValue) + } + .padding() + .controlSize(.large) + .labelsHidden() + .pickerStyle(.segmented) + } + + var content1: some View { + LazyVGrid(columns: [ + GridItem(.flexible()), + GridItem(.flexible()) + ], alignment: .leading) { + + ConfigItemView(name: "Http Port") { + Text(String(httpPort)) + .font(.system(size: 17)) + } + + ConfigItemView(name: "Socks5 Port") { + Text(String(socks5Port)) + .font(.system(size: 17)) + } + + ConfigItemView(name: "Mixed Port") { + Text(String(mixedPort)) + .font(.system(size: 17)) + } + + ConfigItemView(name: "Redir Port") { + Text(String(redirPort)) + .font(.system(size: 17)) + } + + ConfigItemView(name: "Log Level") { + Text(logLevel.rawValue.capitalized) + .font(.system(size: 17)) + +// Picker("", selection: $logLevel) { +// ForEach([ +// ClashLogLevel.silent, +// .error, +// .warning, +// .info, +// .debug, +// .unknow +// ], id: \.self) { +// Text($0.rawValue.capitalized).tag($0) +// } +// } +// .disabled(true) +// .pickerStyle(.menu) + } + + ConfigItemView(name: "ipv6") { + Toggle("", isOn: $ipv6) + .toggleStyle(toggleStyle) + .disabled(true) + } + } + } + + var tunView: some View { + LazyVGrid(columns: [ + GridItem(.flexible()), + GridItem(.flexible()) + ], alignment: .leading) { + + + ConfigItemView(name: "Enable TUN Device") { + Toggle("", isOn: $enableTUNDevice) + .toggleStyle(toggleStyle) + } + + + ConfigItemView(name: "TUN IP Stack") { +// Picker("", selection: $tunIPStack) { +// ForEach(["gVisor", "System", "LWIP"], id: \.self) { +// Text($0) +// } +// } +// .pickerStyle(.menu) + + Text(tunIPStack) + .font(.system(size: 17)) + } + + + ConfigItemView(name: "Device Name") { + Text(deviceName) + .font(.system(size: 17)) + } + + + ConfigItemView(name: "Interface Name") { + Text(interfaceName) + .font(.system(size: 17)) + } + + } + } + + var content2: some View { + LazyVGrid(columns: [ + GridItem(.flexible()), + GridItem(.flexible()) + ], alignment: .leading) { + + ConfigItemView(name: "Allow LAN") { + Toggle("", isOn: $allowLAN) + .toggleStyle(toggleStyle) + .onChange(of: allowLAN) { newValue in + guard configInited else { return } + ApiRequest.updateAllowLan(allow: newValue) { + ApiRequest.requestConfig { config in + allowLAN = config.allowLan + } + } + } + } + + ConfigItemView(name: "Sniffer") { + Toggle("", isOn: $sniffer) + .toggleStyle(toggleStyle) + .onChange(of: sniffer) { newValue in + guard configInited else { return } + ApiRequest.updateSniffing(enable: newValue) { + ApiRequest.requestConfig { config in + sniffer = config.sniffing + } + } + } + } + + /* + ConfigItemView(name: "Reload") { + Button { + AppDelegate.shared.updateConfig() + } label: { + Text("Reload config file") + } + } + */ + + ConfigItemView(name: "GEO Databases") { + Button { + ApiRequest.updateGEO() + } label: { + Text("Update GEO Databases") + } + } + + ConfigItemView(name: "FakeIP") { + Button { + ApiRequest.flushFakeipCache() + } label: { + Text("Flush fake-iP data") + } } } }