@@ -11,11 +11,14 @@ public struct MenuView: View {
1111 @Environment ( \. openWindow) private var openWindow
1212
1313 private static let sliderWidth : CGFloat = 270
14+ public static let MenuDisclosureSectionPaddingFix : CGFloat = - 14.0
1415
1516 @Binding private var isMenuPresented : Bool
1617
1718 @State private var isLoading = true
1819 @State private var profile : ExtensionProfile ?
20+
21+ @EnvironmentObject private var commandClient : CommandClient
1922
2023 public init ( isMenuPresented: Binding < Bool > ) {
2124 _isMenuPresented = isMenuPresented
@@ -41,6 +44,9 @@ public struct MenuView: View {
4144 if let profile {
4245 ProfilePicker ( profile)
4346 }
47+ if ( commandClient. clashModeList. count > 0 ) {
48+ ClashOutboundPicker ( )
49+ }
4450 Divider ( )
4551 MenuCommand {
4652 NSApp . setActivationPolicy ( . regular)
@@ -118,6 +124,7 @@ public struct MenuView: View {
118124 @State private var selectedProfileID : Int64 = 0
119125 @State private var reasserting = false
120126 @State private var alert : Alert ?
127+ @State private var isExpanded = false
121128
122129 private var selectedProfileIDLocal : Binding < Int64 > {
123130 $selectedProfileID. withSetter { newValue in
@@ -140,14 +147,22 @@ public struct MenuView: View {
140147 if profileList. isEmpty {
141148 Text ( " Empty profiles " )
142149 } else {
143- MenuSection ( " Profile " )
144- Picker ( " " , selection: selectedProfileIDLocal) {
145- ForEach ( profileList, id: \. id) { profile in
146- Text ( profile. name)
150+ Divider ( )
151+ MenuDisclosureSection (
152+ profileList. firstIndex ( where: { $0. id == selectedProfileIDLocal. wrappedValue } ) . map { " Profile: \( profileList [ $0] . name) " } ?? " Profile " ,
153+ divider: false ,
154+ isExpanded: $isExpanded
155+ ) {
156+ Picker ( " " , selection: selectedProfileIDLocal) {
157+ ForEach ( profileList, id: \. id) { profile in
158+ Text ( profile. name) . frame ( maxWidth: . infinity, alignment: . leading)
159+ }
147160 }
161+ . pickerStyle ( . inline)
162+ . disabled ( !profile. status. isSwitchable || reasserting)
148163 }
149- . pickerStyle ( . inline )
150- . disabled ( !profile . status . isSwitchable || reasserting )
164+ . padding ( . leading , MenuView . MenuDisclosureSectionPaddingFix )
165+ . padding ( . trailing , MenuView . MenuDisclosureSectionPaddingFix )
151166 }
152167 }
153168 }
@@ -204,4 +219,52 @@ public struct MenuView: View {
204219 try LibboxNewStandaloneCommandClient ( ) ? . serviceReload ( )
205220 }
206221 }
222+
223+ private struct ClashOutboundPicker : View {
224+ @EnvironmentObject private var commandClient : CommandClient
225+ @State private var isclashModeExpanded = false
226+ @State private var alert : Alert ?
227+
228+ @State private var wifiSelection : MenuEntry . ID ? = " Wi-Fi Art Thou Romeo "
229+
230+ private var clashLists : [ MenuEntry ] {
231+ commandClient. clashModeList. map { stringValue in
232+ MenuEntry ( name: stringValue, systemImage: " " )
233+ }
234+ }
235+
236+ var body : some View {
237+ viewBuilder {
238+ Divider ( )
239+ MenuDisclosureSection ( " Outbound: " + commandClient. clashMode, divider: false , isExpanded: $isclashModeExpanded) {
240+ MenuScrollView ( maxHeight: 135 ) {
241+ ForEach ( commandClient. clashModeList, id: \. self) { it in
242+ MenuCommand {
243+ commandClient. clashMode = it;
244+ } label: {
245+ if it == commandClient. clashMode {
246+ Image ( systemName: " play.fill " )
247+ }
248+ Text ( it)
249+ }
250+ . padding ( . leading, MenuView . MenuDisclosureSectionPaddingFix)
251+ . padding ( . trailing, MenuView . MenuDisclosureSectionPaddingFix)
252+ }
253+ }
254+ }
255+ . padding ( . leading, MenuView . MenuDisclosureSectionPaddingFix)
256+ . padding ( . trailing, MenuView . MenuDisclosureSectionPaddingFix)
257+ }
258+ . alertBinding ( $alert)
259+ }
260+ private nonisolated func setClashMode( _ newMode: String ) async {
261+ do {
262+ try LibboxNewStandaloneCommandClient ( ) !. setClashMode ( newMode)
263+ } catch {
264+ await MainActor . run {
265+ alert = Alert ( error)
266+ }
267+ }
268+ }
269+ }
207270}
0 commit comments