{
+ const params = section.params || []
+ if (!params.some(p => p.key === "ConditionalExperimental")) return section
+ const byKey = new Map(params.map(p => [p.key, p]))
+ function owner(p) {
+ const seen = new Set()
+ while (p?.parent_key && !seen.has(p.parent_key)) {
+ if (p.parent_key === "ConditionalExperimental") return "conditional_experimental"
+ if (p.parent_key === "ConditionalChill") return "conditional_chill"
+ seen.add(p.parent_key)
+ p = byKey.get(p.parent_key)
+ }
+ return null
+ }
+ return { ...section, params: params.flatMap(p => {
+ if (p.key === "ConditionalChill" || p.key === "ExperimentalMode") return []
+ if (p.key === "ConditionalExperimental") return [{
+ key: LONGITUDINAL_MODE_KEY, label: "Longitudinal control mode", data_type: "string",
+ ui_type: "dropdown", settings_tier: "simple", options: LONGITUDINAL_MODES, is_parent_toggle: true,
+ description: "Chill: conventional speed control. Experimental: model-controlled gas and brakes. Conditional Experimental: Chill, switching to Experimental under your chosen conditions. Conditional Chill: Experimental, switching to Chill for simple cruising.",
+ }]
+ const mode = owner(p)
+ return [{ ...p, ...(mode ? { longitudinal_mode: mode, settings_tier: "simple" } : {}),
+ ...(["ConditionalExperimental", "ConditionalChill"].includes(p.parent_key) ? { parent_key: LONGITUDINAL_MODE_KEY } : {}) }]
+ }) }
+ })
+}
+
+export function validLongitudinalSnapshot(data) {
+ return !!data && LONGITUDINAL_MODES.some(mode => mode.value === data.mode) &&
+ typeof data.locked === "boolean" && typeof data.reason === "string" &&
+ typeof data.experimental_confirmed === "boolean" &&
+ ["ExperimentalMode", "ConditionalExperimental", "ConditionalChill"].every(key => typeof data.values?.[key] === "boolean")
+}
diff --git a/starpilot/system/the_galaxy/assets/mobile/css/material.css b/starpilot/system/the_galaxy/assets/mobile/css/material.css
index 488f3b09e2..9b987df931 100644
--- a/starpilot/system/the_galaxy/assets/mobile/css/material.css
+++ b/starpilot/system/the_galaxy/assets/mobile/css/material.css
@@ -800,6 +800,14 @@ body.is-scrolling .gx-tile {
.gx-row--favorites { align-items: stretch; flex-direction: column; }
.gx-row--favorites .gx-row__info { flex: none; }
.gx-row--stack { align-items: stretch; flex-direction: column; }
+/* Keep the native keyboard/touch picker, but wrap its verified label instead
+ of clipping Conditional Experimental at enlarged phone UI scales. */
+.gx-mode-select { position: relative; width: 100%; }
+.gx-mode-select__label { display: flex; align-items: center; justify-content: space-between; gap: 12px; white-space: normal; }
+.gx-mode-select__label span { min-width: 0; overflow-wrap: anywhere; }
+.gx-mode-select__label i { flex-shrink: 0; }
+.gx-mode-select select { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
+.gx-mode-select:focus-within { outline: 2px solid var(--primary); outline-offset: 3px; border-radius: 16px; }
.gx-row--stack .gx-row__info { flex: none; }
.gx-row--stack .gx-field,
.gx-row--stack .gx-slider-row { width: 100%; }
diff --git a/starpilot/system/the_galaxy/assets/mobile/js/components/GalaxyToggleCard.js b/starpilot/system/the_galaxy/assets/mobile/js/components/GalaxyToggleCard.js
index e99304857d..396e845b03 100644
--- a/starpilot/system/the_galaxy/assets/mobile/js/components/GalaxyToggleCard.js
+++ b/starpilot/system/the_galaxy/assets/mobile/js/components/GalaxyToggleCard.js
@@ -14,6 +14,7 @@ export const GalaxyToggleCard = {
value: { default: undefined },
values: { type: Object, default: () => ({}) },
locked: { type: Boolean, default: false },
+ lockMessage: { type: String, default: "This setting can only be changed while parked." },
manageable: { type: Boolean, default: false },
manageOpen: { type: Boolean, default: false },
},
@@ -72,6 +73,7 @@ export const GalaxyToggleCard = {
labelOf(el) { return el?.options?.[el.selectedIndex]?.textContent || "" },
rollback(prev) { this.$emit("change", { key: this.param.key, value: prev }) },
async commit(nextValue) {
+ if (this.locked || this.updating) return
const prev = this.value
const label = this.lastLabel || ""
this.$emit("change", { key: this.param.key, value: nextValue })
@@ -179,7 +181,7 @@ export const GalaxyToggleCard = {
Advanced
{{ displayParam.description }}
-
Locked: This setting can only be changed while parked.
+
Locked: {{ lockMessage }}