@@ -54,12 +54,16 @@ struct LiquidTabsRootView: View {
5454 // Controls whether the hidden UITextField should grab keyboard focus.
5555 @State private var hackShowKeyboard : Bool = false
5656
57- // Native selection type
57+ // Native selection type: dynamic tabs + search
5858 enum TabSelection : Hashable {
59- case first, second, third, fourth, search
59+ case content( Int ) // index into store.tabs
60+ case search
6061 }
6162
62- @State private var selectedTab : TabSelection = . first
63+ @State private var selectedTab : TabSelection = . content( 0 )
64+
65+ // (optional) cap number of main tabs if you like
66+ private let maxMainTabs = 6
6367
6468 // MARK: - Re-Tap Logic
6569
@@ -93,44 +97,45 @@ struct LiquidTabsRootView: View {
9397
9498 // MARK: - Tab Helpers
9599
96- private var firstTab : LiquidTab ? { store. tabs. first }
97- private var secondTab : LiquidTab ? { store. tabs. count > 1 ? store. tabs [ 1 ] : nil }
98- private var thirdTab : LiquidTab ? { store. tabs. count > 2 ? store. tabs [ 2 ] : nil }
99- private var fourthTab : LiquidTab ? { store. tabs. count > 3 ? store. tabs [ 3 ] : nil }
100+ private var firstTab : LiquidTab ? {
101+ store. tabs. first
102+ }
100103
104+ /// Get tab id for a selection
101105 private func tabId( for selection: TabSelection ) -> String ? {
102106 switch selection {
103- case . first : return firstTab ? . id
104- case . second : return secondTab ? . id
105- case . third : return thirdTab ? . id
106- case . fourth : return fourthTab ? . id
107- case . search : return " search "
107+ case . content ( let index ) :
108+ guard index >= 0 && index < store . tabs . count else { return nil }
109+ return store . tabs [ index ] . id
110+ case . search :
111+ return " search "
108112 }
109113 }
110114
111-
115+ /// Map a tab id back to TabSelection
112116 private func selection( forId id: String ) -> TabSelection ? {
113- if id == firstTab? . id { return . first }
114- if id == secondTab? . id { return . second }
115- if id == thirdTab? . id { return . third }
116- if id == fourthTab? . id { return . fourth }
117- if id == " search " { return . search }
117+ if id == " search " {
118+ return . search
119+ }
120+
121+ if let index = store. tabs. firstIndex ( where: { $0. id == id } ) {
122+ return . content( index)
123+ }
124+
118125 return nil
119126 }
120127
121-
128+ /// Compute initial selection based on store.selectedId or available tabs
122129 private func initialSelection( ) -> TabSelection {
123- if let id = store. selectedId {
124- if id == firstTab? . id { return . first }
125- if id == secondTab? . id { return . second }
126- if id == thirdTab? . id { return . third }
127- if id == fourthTab? . id { return . fourth }
128- if id == " search " { return . search }
130+ if let id = store. selectedId,
131+ let sel = selection ( forId: id) {
132+ return sel
133+ }
134+
135+ if !store. tabs. isEmpty {
136+ return . content( 0 )
129137 }
130- if firstTab != nil { return . first }
131- if secondTab != nil { return . second }
132- if thirdTab != nil { return . third }
133- if fourthTab != nil { return . fourth }
138+
134139 return . search
135140 }
136141
@@ -149,36 +154,14 @@ struct LiquidTabsRootView: View {
149154 // Main TabView using the PROXY BINDING
150155 TabView ( selection: tabSelectionProxy) {
151156
152- // ---- Tab 1 ----
153- if let tab = firstTab {
154- Tab ( tab. title, systemImage: tab. systemImage, value: TabSelection . first) {
155- NativeNavHost ( navController: navController)
156- . ignoresSafeArea ( )
157- . background ( Color . logseqBackground)
158- }
159- }
160-
161- // ---- Tab 2 ----
162- if let tab = secondTab {
163- Tab ( tab. title, systemImage: tab. systemImage, value: TabSelection . second) {
164- NativeNavHost ( navController: navController)
165- . ignoresSafeArea ( )
166- . background ( Color . logseqBackground)
167- }
168- }
169-
170- // ---- Tab 3 ----
171- if let tab = thirdTab {
172- Tab ( tab. title, systemImage: tab. systemImage, value: TabSelection . third) {
173- NativeNavHost ( navController: navController)
174- . ignoresSafeArea ( )
175- . background ( Color . logseqBackground)
176- }
177- }
178-
179- // ---- Tab 4 ----
180- if let tab = fourthTab {
181- Tab ( tab. title, systemImage: tab. systemImage, value: TabSelection . fourth) {
157+ // ---- Dynamic main tabs, using Tab(...) API ----
158+ ForEach ( Array ( store. tabs. prefix ( maxMainTabs) . enumerated ( ) ) ,
159+ id: \. element. id) { index, tab in
160+ Tab (
161+ tab. title,
162+ systemImage: tab. systemImage,
163+ value: TabSelection . content ( index)
164+ ) {
182165 NativeNavHost ( navController: navController)
183166 . ignoresSafeArea ( )
184167 . background ( Color . logseqBackground)
@@ -191,7 +174,7 @@ struct LiquidTabsRootView: View {
191174 navController: navController,
192175 isSearchFocused: $isSearchFocused,
193176 selectedTab: $selectedTab,
194- firstTabId: firstTab ? . id,
177+ firstTabId: store . tabs . first ? . id,
195178 store: store
196179 )
197180 . ignoresSafeArea ( )
@@ -216,9 +199,32 @@ struct LiquidTabsRootView: View {
216199 . onAppear {
217200 let initial = initialSelection ( )
218201 selectedTab = initial
219- if initial == . search {
202+ if case . search = initial {
220203 isSearchPresented = true
221204 }
205+
206+ let appearance = UITabBarAppearance ( )
207+ appearance. configureWithOpaqueBackground ( )
208+
209+ // Background
210+ appearance. backgroundColor = UIColor . logseqBackground
211+
212+ // Selected text color
213+ appearance. stackedLayoutAppearance. selected. titleTextAttributes = [
214+ . foregroundColor: UIColor . label
215+ ]
216+
217+ // Unselected text color (70%)
218+ let dimmed = UIColor . label. withAlphaComponent ( 0.7 )
219+ appearance. stackedLayoutAppearance. normal. titleTextAttributes = [
220+ . foregroundColor: dimmed
221+ ]
222+
223+ // Apply the appearance
224+ let tabBar = UITabBar . appearance ( )
225+ tabBar. tintColor = . label
226+ tabBar. standardAppearance = appearance
227+ tabBar. scrollEdgeAppearance = appearance
222228 }
223229 // Handle STANDARD tab selection changes
224230 . onChange ( of: selectedTab) { newValue in
@@ -227,9 +233,10 @@ struct LiquidTabsRootView: View {
227233 LiquidTabsPlugin . shared? . notifyTabSelected ( id: id)
228234 }
229235
230- if newValue == . search {
236+ switch newValue {
237+ case . search:
231238 isSearchPresented = true
232- } else {
239+ case . content :
233240 hackShowKeyboard = false
234241 isSearchFocused = false
235242 isSearchPresented = false
@@ -257,7 +264,6 @@ struct LiquidTabsRootView: View {
257264 }
258265
259266 // If it's already selected, treat it as a no-op for programmatic changes
260- // (if you want programmatic re-tap behavior, you could call handleRetap here)
261267 if newSelection == selectedTab {
262268 return
263269 }
@@ -333,12 +339,12 @@ private struct SearchTabHost: View {
333339 if searching {
334340 wasSearching = true
335341 } else if wasSearching,
336- selectedTab . wrappedValue == . search ,
342+ case . search = selectedTab . wrappedValue ,
337343 let firstId = firstTabId {
338344
339- // Cancel logic - Programmatic switch
345+ // Cancel logic - Programmatic switch back to first content tab
340346 wasSearching = false
341- selectedTab. wrappedValue = . first
347+ selectedTab. wrappedValue = . content ( 0 )
342348 store. selectedId = firstId
343349 LiquidTabsPlugin . shared? . notifyTabSelected ( id: firstId)
344350 }
0 commit comments