Explorar el Código

Merge branch 'dev' into feat/dev-eversense

Deniz Cengiz hace 13 horas
padre
commit
3e9f4fe361
Se han modificado 29 ficheros con 281 adiciones y 31 borrados
  1. 3 3
      Config.xcconfig
  2. 10 6
      Gemfile.lock
  3. 8 0
      Trio.xcodeproj/project.pbxproj
  4. 1 1
      Trio.xcworkspace/xcshareddata/swiftpm/Package.resolved
  5. 1 0
      Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  6. 1 0
      Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  7. 1 0
      Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift
  8. 1 0
      Trio/Sources/Modules/CGMSettings/View/CGMRootView.swift
  9. 1 0
      Trio/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift
  10. 2 1
      Trio/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift
  11. 1 0
      Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift
  12. 1 0
      Trio/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift
  13. 2 0
      Trio/Sources/Modules/Home/View/HomeRootView.swift
  14. 1 0
      Trio/Sources/Modules/LiveActivitySettings/View/LiveActivitySettingsRootView.swift
  15. 1 0
      Trio/Sources/Modules/MealSettings/View/MealSettingsRootView.swift
  16. 1 0
      Trio/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift
  17. 1 0
      Trio/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift
  18. 1 1
      Trio/Sources/Modules/Onboarding/View/TherapySettingEditorView.swift
  19. 1 0
      Trio/Sources/Modules/RemoteControlConfig/View/RemoteControlConfig.swift
  20. 1 0
      Trio/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift
  21. 66 2
      Trio/Sources/Modules/Settings/SettingItems.swift
  22. 19 8
      Trio/Sources/Modules/Settings/View/SettingsRootView.swift
  23. 1 0
      Trio/Sources/Modules/ShortcutsConfig/View/ShortcutsConfigView.swift
  24. 1 0
      Trio/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift
  25. 9 8
      Trio/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift
  26. 1 0
      Trio/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift
  27. 2 1
      Trio/Sources/Views/SettingInputSection.swift
  28. 73 0
      Trio/Sources/Views/SettingsSearchHighlight.swift
  29. 69 0
      TrioTests/SettingsSearchTests.swift

+ 3 - 3
Config.xcconfig

@@ -18,12 +18,12 @@ BUNDLE_IDENTIFIER = org.nightscout.$(DEVELOPMENT_TEAM).trio
 TRIO_APP_GROUP_ID = group.org.nightscout.$(DEVELOPMENT_TEAM).trio.trio-app-group
 
 // The developers set the version numbers, please leave them alone
-APP_VERSION = 0.7.0
-APP_DEV_VERSION = 0.7.0.22
+APP_VERSION = 0.8.0
+APP_DEV_VERSION = 0.8.0
 APP_BUILD_NUMBER = 1
 COPYRIGHT_NOTICE =
 
 // Optional overrides - these can be used to insert your TEAMID into the DEVELOPER_TEAM field
 #include? "../../ConfigOverride.xcconfig"
 #include? "../ConfigOverride.xcconfig"
-#include? "ConfigOverride.xcconfig"
+#include? "ConfigOverride.xcconfig"

+ 10 - 6
Gemfile.lock

@@ -43,16 +43,17 @@ GEM
     dotenv (2.8.1)
     emoji_regex (3.2.3)
     excon (0.112.0)
-    faraday (1.8.0)
+    faraday (1.10.5)
       faraday-em_http (~> 1.0)
       faraday-em_synchrony (~> 1.0)
       faraday-excon (~> 1.1)
-      faraday-httpclient (~> 1.0.1)
+      faraday-httpclient (~> 1.0)
+      faraday-multipart (~> 1.0)
       faraday-net_http (~> 1.0)
-      faraday-net_http_persistent (~> 1.1)
+      faraday-net_http_persistent (~> 1.0)
       faraday-patron (~> 1.0)
       faraday-rack (~> 1.0)
-      multipart-post (>= 1.2, < 3)
+      faraday-retry (~> 1.0)
       ruby2_keywords (>= 0.0.4)
     faraday-cookie_jar (0.0.8)
       faraday (>= 0.8.0)
@@ -61,10 +62,13 @@ GEM
     faraday-em_synchrony (1.0.1)
     faraday-excon (1.1.0)
     faraday-httpclient (1.0.1)
+    faraday-multipart (1.2.0)
+      multipart-post (~> 2.0)
     faraday-net_http (1.0.2)
     faraday-net_http_persistent (1.2.0)
     faraday-patron (1.0.0)
     faraday-rack (1.0.0)
+    faraday-retry (1.0.4)
     faraday_middleware (1.2.1)
       faraday (~> 1.0)
     fastimage (2.4.0)
@@ -163,7 +167,7 @@ GEM
       mutex_m
     jmespath (1.6.2)
     json (2.19.4)
-    jwt (2.10.2)
+    jwt (2.10.3)
       base64
     logger (1.7.0)
     mini_magick (4.13.2)
@@ -178,7 +182,7 @@ GEM
     os (1.1.4)
     ostruct (0.6.3)
     plist (3.7.2)
-    public_suffix (7.0.2)
+    public_suffix (7.0.5)
     rake (13.3.1)
     representable (3.2.0)
       declarative (< 0.1.0)

+ 8 - 0
Trio.xcodeproj/project.pbxproj

@@ -349,6 +349,7 @@
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
 		98641AF4F92123DA668AB931 /* CarbRatioEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */; };
+		41740E936552456AAC0EDAC3 /* SettingsSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3919BBB515547118D684CA2 /* SettingsSearchTests.swift */; };
 		A33352ED40476125EBAC6EE0 /* CarbRatioEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */; };
 		AD3D2CD42CD01B9EB8F26522 /* PumpConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */; };
 		B7C465E9472624D8A2BE2A6A /* (null) in Sources */ = {isa = PBXBuildFile; };
@@ -551,6 +552,7 @@
 		DD1745242C55526000211FAC /* SMBSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745232C55526000211FAC /* SMBSettingsStateModel.swift */; };
 		DD1745262C55526F00211FAC /* SMBSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745252C55526F00211FAC /* SMBSettingsRootView.swift */; };
 		DD1745292C55642100211FAC /* SettingInputSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1745282C55642100211FAC /* SettingInputSection.swift */; };
+		AC19EF2C94084B5BA0175D1D /* SettingsSearchHighlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48B83503461B4F8D97B30115 /* SettingsSearchHighlight.swift */; };
 		DD17452B2C556E8100211FAC /* SettingInputHintView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17452A2C556E8100211FAC /* SettingInputHintView.swift */; };
 		DD17452E2C55AE4800211FAC /* TargetBehavoirDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17452D2C55AE4800211FAC /* TargetBehavoirDataFlow.swift */; };
 		DD1745302C55AE5300211FAC /* TargetBehaviorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17452F2C55AE5300211FAC /* TargetBehaviorProvider.swift */; };
@@ -1424,6 +1426,7 @@
 		DD1745232C55526000211FAC /* SMBSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMBSettingsStateModel.swift; sourceTree = "<group>"; };
 		DD1745252C55526F00211FAC /* SMBSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMBSettingsRootView.swift; sourceTree = "<group>"; };
 		DD1745282C55642100211FAC /* SettingInputSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingInputSection.swift; sourceTree = "<group>"; };
+		48B83503461B4F8D97B30115 /* SettingsSearchHighlight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSearchHighlight.swift; sourceTree = "<group>"; };
 		DD17452A2C556E8100211FAC /* SettingInputHintView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingInputHintView.swift; sourceTree = "<group>"; };
 		DD17452D2C55AE4800211FAC /* TargetBehavoirDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetBehavoirDataFlow.swift; sourceTree = "<group>"; };
 		DD17452F2C55AE5300211FAC /* TargetBehaviorProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TargetBehaviorProvider.swift; sourceTree = "<group>"; };
@@ -1614,6 +1617,7 @@
 		E592A3742CEEC038009A472C /* ContactImageProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageProvider.swift; sourceTree = "<group>"; };
 		E592A3752CEEC038009A472C /* ContactImageStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageStateModel.swift; sourceTree = "<group>"; };
 		E625985B47742D498CB1681A /* GlucoseNotificationSettingsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsProvider.swift; sourceTree = "<group>"; };
+		B3919BBB515547118D684CA2 /* SettingsSearchTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SettingsSearchTests.swift; sourceTree = "<group>"; };
 		F816825D28DB441200054060 /* HeartBeatManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeartBeatManager.swift; sourceTree = "<group>"; };
 		F816825F28DB441800054060 /* BluetoothTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothTransmitter.swift; sourceTree = "<group>"; };
 		F90692A9274B7AAE0037068D /* HealthKitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthKitManager.swift; sourceTree = "<group>"; };
@@ -2421,6 +2425,7 @@
 				38DF1785276A73D400B3528F /* TagCloudView.swift */,
 				DD88C8E12C50420800F2D558 /* DefinitionRow.swift */,
 				DD1745282C55642100211FAC /* SettingInputSection.swift */,
+				48B83503461B4F8D97B30115 /* SettingsSearchHighlight.swift */,
 				DD17452A2C556E8100211FAC /* SettingInputHintView.swift */,
 			);
 			path = Views;
@@ -2757,6 +2762,7 @@
 				38FCF3F825E902C20078B0D1 /* FileStorageTests.swift */,
 				3B997DCE2DC00A3A006B6BB2 /* JSONImporterTests.swift */,
 				CE1F6DD82BADF4620064EB8D /* PluginManagerTests.swift */,
+				B3919BBB515547118D684CA2 /* SettingsSearchTests.swift */,
 				BD8FC0532D66186000B95AED /* TestError.swift */,
 				BD8FC0702D661B0000B95AED /* TidepoolTherapySettingsTests.swift */,
 			);
@@ -4463,6 +4469,7 @@
 				BDF34F832C10C5B600D51995 /* DataManager.swift in Sources */,
 				38B4F3C625E5017E00E76A18 /* NotificationCenter.swift in Sources */,
 				19D466A729AA2C22004D5F33 /* MealSettingsStateModel.swift in Sources */,
+				AC19EF2C94084B5BA0175D1D /* SettingsSearchHighlight.swift in Sources */,
 				DD17452B2C556E8100211FAC /* SettingInputHintView.swift in Sources */,
 				38E44528274E401C00EC9A94 /* Protected.swift in Sources */,
 				DD3F1F8B2D9E08B600DCE7B3 /* NightscoutLoginStepView.swift in Sources */,
@@ -4968,6 +4975,7 @@
 				38FCF3F925E902C20078B0D1 /* FileStorageTests.swift in Sources */,
 				BD8FC0602D6619DB00B95AED /* CarbsStorageTests.swift in Sources */,
 				BD8FC05E2D6618CE00B95AED /* BolusCalculatorTests.swift in Sources */,
+				41740E936552456AAC0EDAC3 /* SettingsSearchTests.swift in Sources */,
 				B015AFE52E500000000D7351 /* BolusSafetyValidatorTests.swift in Sources */,
 				BD8FC0712D661B0000B95AED /* TidepoolTherapySettingsTests.swift in Sources */,
 			);

+ 1 - 1
Trio.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -1,5 +1,5 @@
 {
-  "originHash" : "598841ae6fe892058ca678f5672f34299df2d62843330367c207648003263ccd",
+  "originHash" : "1e72c1cdf8ea5ec9fe527ebfab01ea55fca9e8651fe3252338fd3d4ea2cb327a",
   "pins" : [
     {
       "identity" : "abseil-cpp-binary",

+ 1 - 0
Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -406,6 +406,7 @@ extension AlgorithmAdvancedSettings {
             .onAppear(perform: configureView)
             .navigationTitle("Additionals")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
             .onDisappear {
                 state.saveIfChanged()
             }

+ 1 - 0
Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -230,6 +230,7 @@ extension AutosensSettings {
             .onAppear(perform: configureView)
             .navigationTitle("Autosens")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift

@@ -200,6 +200,7 @@ extension BolusCalculatorConfig {
             .onAppear(perform: configureView)
             .navigationBarTitle("Bolus Calculator")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/CGMSettings/View/CGMRootView.swift

@@ -150,6 +150,7 @@ extension CGMSettings {
                 .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
                 .onAppear(perform: configureView)
                 .navigationTitle("CGM")
+                .settingsHighlightScroll()
                 .navigationBarTitleDisplayMode(.automatic)
                 .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
                 .sheet(isPresented: $state.shouldDisplayCGMSetupSheet) {

+ 1 - 0
Trio/Sources/Modules/CalendarEventSettings/View/CalendarEventSettingsRootView.swift

@@ -147,6 +147,7 @@ extension CalendarEventSettings {
             .onAppear(perform: configureView)
             .navigationTitle("Calendar Events")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 2 - 1
Trio/Sources/Modules/DynamicSettings/View/DynamicSettingsRootView.swift

@@ -165,7 +165,7 @@ extension DynamicSettings {
                             }.padding(.top)
                         }.padding(.bottom)
                     }
-                ).listRowBackground(Color.chart)
+                ).settingsSearchTarget(label: String(localized: "Dynamic ISF"))
 
                 if state.dynamicSensitivityType != .disabled {
                     if state.dynamicSensitivityType == .logarithmic {
@@ -283,6 +283,7 @@ extension DynamicSettings {
             .onAppear(perform: configureView)
             .navigationBarTitle("Dynamic Settings")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/GeneralSettings/View/UnitsLimitsSettingsRootView.swift

@@ -227,6 +227,7 @@ extension UnitsLimitsSettings {
             .onDisappear {
                 state.saveIfChanged()
             }
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/GlucoseNotificationSettings/View/GlucoseNotificationSettingsRootView.swift

@@ -264,6 +264,7 @@ extension GlucoseNotificationSettings {
             .onAppear(perform: configureView)
             .navigationBarTitle("Trio Notifications")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
 
         var lowAndHighGlucoseAlertSection: some View {

+ 2 - 0
Trio/Sources/Modules/Home/View/HomeRootView.swift

@@ -22,6 +22,7 @@ extension Home {
         @State var state = StateModel()
 
         @State var settingsPath = NavigationPath()
+        @State var settingsSearchHighlight = SettingsSearchHighlight()
         @State var isStatusPopupPresented = false
         @State var showCancelAlert = false
         @State var showCancelConfirmDialog = false
@@ -1097,6 +1098,7 @@ extension Home {
 
                     NavigationStack(path: self.$settingsPath) {
                         Settings.RootView(resolver: resolver) }
+                        .environment(settingsSearchHighlight)
                         .tabItem { Label(
                             "Settings",
                             systemImage: "gear"

+ 1 - 0
Trio/Sources/Modules/LiveActivitySettings/View/LiveActivitySettingsRootView.swift

@@ -233,6 +233,7 @@ extension LiveActivitySettings {
             .onAppear(perform: configureView)
             .navigationTitle("Live Activity")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/MealSettings/View/MealSettingsRootView.swift

@@ -371,6 +371,7 @@ extension MealSettings {
             .onAppear(perform: configureView)
             .navigationBarTitle("Meal Settings")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -52,6 +52,7 @@ struct NightscoutFetchView: View {
         }
         .navigationTitle("Fetch")
         .navigationBarTitleDisplayMode(.automatic)
+        .settingsHighlightScroll()
         .scrollContentBackground(.hidden)
         .background(appState.trioBackgroundColor(for: colorScheme))
     }

+ 1 - 0
Trio/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

@@ -81,6 +81,7 @@ struct NightscoutUploadView: View {
         }
         .navigationTitle("Upload")
         .navigationBarTitleDisplayMode(.automatic)
+        .settingsHighlightScroll()
         .scrollContentBackground(.hidden)
         .background(appState.trioBackgroundColor(for: colorScheme))
     }

+ 1 - 1
Trio/Sources/Modules/Onboarding/View/TherapySettingEditorView.swift

@@ -7,7 +7,7 @@ struct TherapySettingEditorView: View {
     var valueOptions: [Decimal]
     var validateOnDelete: (() -> Void)?
     var onItemAdded: (() -> Void)?
-    
+
     private let basalFormatter: NumberFormatter = {
         let numberFormatter = NumberFormatter()
         numberFormatter.maximumFractionDigits = 3

+ 1 - 0
Trio/Sources/Modules/RemoteControlConfig/View/RemoteControlConfig.swift

@@ -101,6 +101,7 @@ extension RemoteControlConfig {
             .onAppear(perform: configureView)
             .navigationTitle("Remote Control")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/SMBSettings/View/SMBSettingsRootView.swift

@@ -370,6 +370,7 @@ extension SMBSettings {
             .onAppear(perform: configureView)
             .navigationTitle("SMB Settings")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 66 - 2
Trio/Sources/Modules/Settings/SettingItems.swift

@@ -8,16 +8,22 @@ struct SettingItem: Identifiable {
     let view: Screen
     let searchContents: [String]?
     let path: [String]?
+    /// Maps a `searchContents` string to the exact label used in `SettingInputSection`
+    /// when the two differ (e.g. `"Max IOB"` → `"Maximum Insulin on Board (IOB)"`).
+    /// Entries whose searchContents string already matches the label don't need an entry here.
+    let scrollTargetLabels: [String: String]?
 
     init(
         title: String,
         view: Screen,
         searchContents: [String]? = nil,
+        scrollTargetLabels: [String: String]? = nil,
         path: [String]? = nil
     ) {
         self.title = title
         self.view = view
         self.searchContents = searchContents
+        self.scrollTargetLabels = scrollTargetLabels
         self.path = path
     }
 }
@@ -26,6 +32,11 @@ struct FilteredSettingItem: Identifiable {
     let id = UUID()
     let settingItem: SettingItem
     let matchedContent: String
+    /// The label string used as the scroll/highlight target in the destination view.
+    /// Falls back to `matchedContent` when no explicit mapping exists.
+    var scrollLabel: String {
+        settingItem.scrollTargetLabels?[matchedContent] ?? matchedContent
+    }
 }
 
 enum SettingItems {
@@ -89,6 +100,12 @@ enum SettingItems {
                 "Minimum Safety Threshold",
                 "Delivery Limits"
             ],
+            scrollTargetLabels: [
+                "Max IOB": "Maximum Insulin on Board (IOB)",
+                "Max Bolus": "Maximum Bolus",
+                "Max Basal": "Maximum Basal Rate",
+                "Max COB": "Maximum Carbs on Board (COB)"
+            ],
             path: ["Therapy Settings", "Units and Limits"]
         ),
         SettingItem(title: "Basal Rates", view: .basalProfileEditor, path: ["Therapy Settings"]),
@@ -122,6 +139,12 @@ enum SettingItems {
                 "Max UAM SMB Basal Minutes",
                 "Max Allowed Glucose Rise for SMB"
             ],
+            scrollTargetLabels: [
+                "Enable SMB With Temporary Target": "Enable SMB With Temptarget",
+                "Allow SMB With High Temporary Target": "Allow SMB With High Temptarget",
+                "Max UAM SMB Basal Minutes": "Max UAM Basal Minutes",
+                "High Glucose Target": "Enable SMB With High Glucose"
+            ],
             path: ["Algorithm", "Super Micro Bolus (SMB)"]
         ),
         SettingItem(
@@ -148,6 +171,10 @@ enum SettingItems {
                 "Resistance Lowers Target",
                 "Half Basal Exercise Target"
             ],
+            scrollTargetLabels: [
+                "High Temptarget Raises Sensitivity": "High Temp Target Raises Sensitivity",
+                "Low Temptarget Lowers Sensitivity": "Low Temp Target Lowers Sensitivity"
+            ],
             path: ["Algorithm", "Target Behavior"]
         ),
         SettingItem(
@@ -168,6 +195,11 @@ enum SettingItems {
                 "Remaining Carbs Cap",
                 "Noisy CGM Target Multiplier"
             ],
+            scrollTargetLabels: [
+                "Min 5m Carbimpact": "Min 5m Carb Impact",
+                "Remaining Carbs Fraction": "Remaining Carbs Percentage",
+                "Noisy CGM Target Multiplier": "Noisy CGM Target Increase"
+            ],
             path: ["Algorithm", "Additionals"]
         )
     ]
@@ -185,6 +217,12 @@ enum SettingItems {
                 "Super Bolus Factor",
                 "Very Low Glucose Warning"
             ],
+            scrollTargetLabels: [
+                "Enable Reduced Bolus Factor": "Enable Reduced Bolus Option",
+                "Reduced Bolus Factor": "Enable Reduced Bolus Option",
+                "Enable Super Bolus": "Enable Super Bolus Option",
+                "Super Bolus Factor": "Enable Super Bolus Option"
+            ],
             path: ["Features", "Bolus Calculator"]
         ),
         SettingItem(
@@ -201,6 +239,14 @@ enum SettingItems {
                 "Fat and Protein Percentage",
                 "FPU"
             ],
+            scrollTargetLabels: [
+                "Max Fat": "Enable Fat and Protein Entries",
+                "Max Protein": "Enable Fat and Protein Entries",
+                "Fat and Protein Delay": "Enable Fat and Protein Entries",
+                "Spread Interval": "Enable Fat and Protein Entries",
+                "Fat and Protein Percentage": "Enable Fat and Protein Entries",
+                "FPU": "Enable Fat and Protein Entries"
+            ],
             path: ["Features", "Meal Settings"]
         ),
         SettingItem(
@@ -224,7 +270,6 @@ enum SettingItems {
                 "Show Low and High Thresholds",
                 "Low Threshold",
                 "High Threshold",
-                "X-Axis Interval Step",
                 "eA1c/GMI Display Unit",
                 "Show Carbs Required Badge",
                 "Carbs Required Threshold",
@@ -232,17 +277,32 @@ enum SettingItems {
                 "Bolus Display Threshold",
                 "Cone",
                 "Lines",
+                "Appearance",
                 "Dark Mode",
                 "Light Mode",
-                "Appearance",
                 "Dark Scheme",
                 "Light Scheme",
                 "Glucose Color Scheme",
                 "Time in Range Type",
                 "Time in Tight Range (TITR)",
                 "Time in Normoglycemia (TING)",
+                "X-Axis Interval Step",
                 "Require Adjustments Confirmation"
             ],
+            scrollTargetLabels: [
+                "Show Y-Axis Grid Lines": "Show X-Axis Grid Lines",
+                "High Threshold": "Low Threshold",
+                "Cone": "Forecast Display Type",
+                "Lines": "Forecast Display Type",
+                "Dark Mode": "Appearance",
+                "Light Mode": "Appearance",
+                "Time in Tight Range (TITR)": "Time in Range Type",
+                "Time in Normoglycemia (TING)": "Time in Range Type",
+                "Dark Scheme": "Appearance",
+                "Light Scheme": "Appearance",
+                "X-Axis Interval Step": "Show X-Axis Grid Lines",
+                "Carbs Required Threshold": "Show Carbs Required Badge"
+            ],
             path: ["Features", "User Interface"]
         ),
         SettingItem(
@@ -275,6 +335,10 @@ enum SettingItems {
                 "Low Glucose Alarm Limit",
                 "High Glucose Alarm Limit"
             ],
+            scrollTargetLabels: [
+                "Low Glucose Alarm Limit": "Glucose Notifications",
+                "High Glucose Alarm Limit": "Glucose Notifications"
+            ],
             path: ["Notifications", "Trio Notifications"] // Glucose
         ),
         SettingItem(

+ 19 - 8
Trio/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -38,6 +38,7 @@ extension Settings {
         @Environment(\.colorScheme) var colorScheme
         @EnvironmentObject var appIcons: Icons
         @Environment(AppState.self) var appState
+        @Environment(SettingsSearchHighlight.self) var searchHighlight
 
         private var filteredItems: [FilteredSettingItem] {
             SettingItems.filteredItems(searchText: searchText)
@@ -284,15 +285,19 @@ extension Settings {
                         content: {
                             if filteredItems.isNotEmpty {
                                 ForEach(filteredItems) { filteredItem in
-                                    VStack(alignment: .leading) {
-                                        Text(filteredItem.matchedContent.localized).bold()
-                                        if let path = filteredItem.settingItem.path {
-                                            Text(path.map(\.localized).joined(separator: " > "))
-                                                .font(.caption)
-                                                .foregroundColor(.secondary)
+                                    NavigationLink(value: SearchResultTarget(
+                                        screen: filteredItem.settingItem.view,
+                                        scrollLabel: filteredItem.scrollLabel.localized
+                                    )) {
+                                        VStack(alignment: .leading) {
+                                            Text(filteredItem.matchedContent.localized).bold()
+                                            if let path = filteredItem.settingItem.path {
+                                                Text(path.map(\.localized).joined(separator: " > "))
+                                                    .font(.caption)
+                                                    .foregroundColor(.secondary)
+                                            }
                                         }
-
-                                    }.navigationLink(to: filteredItem.settingItem.view, from: self)
+                                    }
                                 }
                             } else {
                                 Text("No settings matching your search query")
@@ -339,6 +344,12 @@ extension Settings {
                 }
             }
             .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
+            .navigationDestination(for: SearchResultTarget.self) { target in
+                state.view(for: target.screen)
+                    .onAppear {
+                        searchHighlight.highlightedSetting = target.scrollLabel
+                    }
+            }
             .screenNavigation(self)
             .onAppear {
                 Task { @MainActor in

+ 1 - 0
Trio/Sources/Modules/ShortcutsConfig/View/ShortcutsConfigView.swift

@@ -80,6 +80,7 @@ extension ShortcutsConfig {
             .onAppear(perform: configureView)
             .navigationTitle("Shortcuts")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/TargetBehavoir/View/TargetBehavoirRootView.swift

@@ -200,6 +200,7 @@ extension TargetBehavoir {
             }
             .navigationTitle("Target Behavior")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
 
         private var effectiveLowTTLowersSensBinding: Binding<Bool> {

+ 9 - 8
Trio/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift

@@ -96,7 +96,7 @@ extension UserInterfaceSettings {
                             }.padding(.top)
                         }.padding(.bottom)
                     }
-                ).listRowBackground(Color.chart)
+                ).settingsSearchTarget(label: String(localized: "Appearance"))
 
                 Section {
                     VStack {
@@ -154,7 +154,7 @@ extension UserInterfaceSettings {
                             ).buttonStyle(BorderlessButtonStyle())
                         }.padding(.top)
                     }.padding(.bottom)
-                }.listRowBackground(Color.chart)
+                }.settingsSearchTarget(label: String(localized: "Glucose Color Scheme"))
 
                 Section(
                     header: Text("Home View Settings"),
@@ -189,7 +189,7 @@ extension UserInterfaceSettings {
                             }.padding(.top)
                         }.padding(.vertical)
                     }
-                ).listRowBackground(Color.chart)
+                ).settingsSearchTarget(label: String(localized: "Show X-Axis Grid Lines"))
 
                 SettingInputSection(
                     decimalValue: $decimalPlaceholder,
@@ -319,7 +319,7 @@ extension UserInterfaceSettings {
                                 ).buttonStyle(BorderlessButtonStyle())
                             }.padding(.top)
                         }.padding(.bottom)
-                    }.listRowBackground(Color.chart)
+                    }.settingsSearchTarget(label: String(localized: "Low Threshold"))
                 }
 
                 Section {
@@ -374,7 +374,7 @@ extension UserInterfaceSettings {
                             ).buttonStyle(BorderlessButtonStyle())
                         }.padding(.top)
                     }.padding(.bottom)
-                }.listRowBackground(Color.chart)
+                }.settingsSearchTarget(label: String(localized: "Forecast Display Type"))
 
                 Section {
                     VStack {
@@ -416,7 +416,7 @@ extension UserInterfaceSettings {
                             ).buttonStyle(BorderlessButtonStyle())
                         }.padding(.top)
                     }.padding(.bottom)
-                }.listRowBackground(Color.chart)
+                }.settingsSearchTarget(label: String(localized: "Bolus Display Threshold"))
 
                 Section(
                     header: Text("Trio Statistics"),
@@ -459,7 +459,7 @@ extension UserInterfaceSettings {
                             }.padding(.top)
                         }.padding(.bottom)
                     }
-                ).listRowBackground(Color.chart)
+                ).settingsSearchTarget(label: String(localized: "eA1c/GMI Display Unit"))
 
                 Section {
                     VStack(alignment: .leading) {
@@ -538,7 +538,7 @@ extension UserInterfaceSettings {
                             ).buttonStyle(BorderlessButtonStyle())
                         }.padding(.top)
                     }.padding(.bottom)
-                }.listRowBackground(Color.chart)
+                }.settingsSearchTarget(label: String(localized: "Time in Range Type"))
 
                 SettingInputSection(
                     decimalValue: $state.carbsRequiredThreshold,
@@ -600,6 +600,7 @@ extension UserInterfaceSettings {
             .onAppear(perform: configureView)
             .navigationBarTitle("User Interface")
             .navigationBarTitleDisplayMode(.automatic)
+            .settingsHighlightScroll()
         }
     }
 }

+ 1 - 0
Trio/Sources/Modules/WatchConfig/View/WatchConfigAppleWatchView.swift

@@ -68,6 +68,7 @@ struct WatchConfigAppleWatchView: BaseView {
         }
         .navigationTitle("Apple Watch")
         .navigationBarTitleDisplayMode(.automatic)
+        .settingsHighlightScroll()
         .scrollContentBackground(.hidden)
         .background(appState.trioBackgroundColor(for: colorScheme))
     }

+ 2 - 1
Trio/Sources/Views/SettingInputSection.swift

@@ -83,7 +83,8 @@ struct SettingInputSection<VerboseHint: View>: View {
             },
             header: { headerText.map(Text.init) },
             footer: { footerText.map(Text.init) }
-        ).listRowBackground(Color.chart)
+        )
+        .settingsSearchTarget(label: label)
     }
 
     // Helper function to retrieve PickerSetting based on key

+ 73 - 0
Trio/Sources/Views/SettingsSearchHighlight.swift

@@ -0,0 +1,73 @@
+import SwiftUI
+
+@MainActor @Observable final class SettingsSearchHighlight {
+    var highlightedSetting: String?
+}
+
+/// Wraps a Screen value with the scroll-target label for search-result navigation.
+struct SearchResultTarget: Hashable {
+    let screen: Screen
+    let scrollLabel: String
+}
+
+private struct SettingsHighlightScrollModifier: ViewModifier {
+    @Environment(SettingsSearchHighlight.self) private var searchHighlight
+
+    func body(content: Content) -> some View {
+        ScrollViewReader { proxy in
+            content
+                .task(id: searchHighlight.highlightedSetting) {
+                    guard let target = searchHighlight.highlightedSetting else { return }
+                    try? await Task.sleep(for: .milliseconds(500))
+                    guard !Task.isCancelled else { return }
+                    withAnimation { proxy.scrollTo(target, anchor: .center) }
+                }
+        }
+    }
+}
+
+private struct SettingsSearchHighlightAnimationModifier: ViewModifier {
+    let label: String
+    @Environment(SettingsSearchHighlight.self) private var searchHighlight
+    @State private var highlightOpacity: Double = 0.0
+
+    func body(content: Content) -> some View {
+        content
+            .listRowBackground(
+                Color.chart.overlay(Color.accentColor.opacity(highlightOpacity))
+                    .animation(.easeOut(duration: 1.2), value: highlightOpacity)
+            )
+            .onAppear {
+                guard searchHighlight.highlightedSetting == label else { return }
+                startHighlightAnimation()
+            }
+            .onChange(of: searchHighlight.highlightedSetting) { _, newValue in
+                guard newValue == label else { return }
+                startHighlightAnimation()
+            }
+    }
+
+    private func startHighlightAnimation() {
+        Task { @MainActor in
+            try? await Task.sleep(for: .milliseconds(500))
+            highlightOpacity = 0.6
+            try? await Task.sleep(for: .milliseconds(800))
+            searchHighlight.highlightedSetting = nil
+            highlightOpacity = 0.0
+        }
+    }
+}
+
+extension View {
+    /// Enables scroll-to-highlight on a settings screen. Add once per destination view.
+    func settingsHighlightScroll() -> some View {
+        modifier(SettingsHighlightScrollModifier())
+    }
+
+    /// Marks a section as a scroll-to and highlight target for settings search.
+    /// Combines `.id(label)` with a highlight flash animation in a single call.
+    func settingsSearchTarget(label: String) -> some View {
+        id(label)
+            .modifier(SettingsSearchHighlightAnimationModifier(label: label))
+    }
+}

+ 69 - 0
TrioTests/SettingsSearchTests.swift

@@ -0,0 +1,69 @@
+import Foundation
+import Testing
+@testable import Trio
+
+@Suite("Settings Search Navigation") struct SettingsSearchTests {
+    @Test("Searching 'Dynamic ISF' finds the Dynamic Settings screen") func searchDynamicISF() {
+        let results = SettingItems.filteredItems(searchText: "Dynamic ISF")
+        #expect(!results.isEmpty)
+        let match = results.first { $0.matchedContent == "Dynamic ISF" }
+        #expect(match != nil)
+        #expect(match?.settingItem.view == .dynamicISF)
+        #expect(match?.scrollLabel == "Dynamic ISF")
+    }
+
+    @Test("All scrollTargetLabels have valid non-empty targets") func scrollTargetLabelsNonEmpty() {
+        for item in SettingItems.allItems {
+            guard let labels = item.scrollTargetLabels else { continue }
+            for (key, value) in labels {
+                #expect(!value.isEmpty)
+                #expect(item.searchContents?.contains(key) == true)
+            }
+        }
+    }
+
+    @Test("Every searchContents entry produces at least one result") func allSearchContentsAreSearchable() {
+        for item in SettingItems.allItems {
+            guard let contents = item.searchContents else { continue }
+            for content in contents {
+                let results = SettingItems.filteredItems(searchText: content)
+                #expect(!results.isEmpty)
+            }
+        }
+    }
+
+    @Test("SearchResultTarget is Hashable and equatable by value") func searchResultTargetHashable() {
+        let a = SearchResultTarget(screen: .dynamicISF, scrollLabel: "Dynamic ISF")
+        let b = SearchResultTarget(screen: .dynamicISF, scrollLabel: "Dynamic ISF")
+        let c = SearchResultTarget(screen: .dynamicISF, scrollLabel: "Adjust Basal")
+        #expect(a == b)
+        #expect(a != c)
+        #expect(a.hashValue == b.hashValue)
+    }
+
+    @Test("SettingsSearchHighlight starts nil and accepts assignments")
+    @MainActor func highlightStateTransitions() {
+        let highlight = SettingsSearchHighlight()
+        #expect(highlight.highlightedSetting == nil)
+
+        highlight.highlightedSetting = "Dynamic ISF"
+        #expect(highlight.highlightedSetting == "Dynamic ISF")
+
+        highlight.highlightedSetting = nil
+        #expect(highlight.highlightedSetting == nil)
+    }
+
+    @Test("SettingsSearchHighlight can be set and cleared in sequence")
+    @MainActor func highlightSequentialUpdates() async {
+        let highlight = SettingsSearchHighlight()
+
+        highlight.highlightedSetting = "First Setting"
+        #expect(highlight.highlightedSetting == "First Setting")
+
+        highlight.highlightedSetting = "Second Setting"
+        #expect(highlight.highlightedSetting == "Second Setting")
+
+        highlight.highlightedSetting = nil
+        #expect(highlight.highlightedSetting == nil)
+    }
+}