From 0d3330c5011694e0ab1d3bbf47e7e9cfc0ac49bc Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Tue, 4 Aug 2026 12:28:26 -0500 Subject: [PATCH] ui(galaxy): add guided E2E override slider Add a 0.05-step range control with plain-language guidance for lane-centering E2E authority. Descriptions group the same behavior across five practical levels, from rigid centering to immediate hazard response. Co-authored-by: jc01rho <4989674+jc01rho@users.noreply.github.com> --- .../tests/test_lane_centering_galaxy.py | 2 + .../components/tools/device_settings.css | 29 ++++++ .../components/tools/device_settings.js | 94 ++++++++++++++++++- .../tools/device_settings_layout.json | 27 +++++- 4 files changed, 147 insertions(+), 5 deletions(-) diff --git a/starpilot/common/tests/test_lane_centering_galaxy.py b/starpilot/common/tests/test_lane_centering_galaxy.py index ffd1e78f4..aa48692ec 100644 --- a/starpilot/common/tests/test_lane_centering_galaxy.py +++ b/starpilot/common/tests/test_lane_centering_galaxy.py @@ -48,3 +48,5 @@ def test_lane_centering_galaxy_controls(): assert e2e_authority["min"] == 0.0 assert e2e_authority["max"] == 1.0 assert e2e_authority["step"] == 0.05 + assert e2e_authority["control"] == "slider" + assert len(e2e_authority["description_steps"]) == 5 diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings.css b/starpilot/system/the_galaxy/assets/components/tools/device_settings.css index b65d368ac..5e66425e2 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings.css +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings.css @@ -515,6 +515,35 @@ opacity: var(--disabled-opacity); } +.ds-slider-container { + width: 100%; +} + +@media (min-width: 768px) { + .ds-slider-container { + max-width: 420px; + } +} + +.ds-slider { + accent-color: var(--main-fg); + cursor: pointer; + width: 100%; +} + +.ds-slider:disabled { + cursor: not-allowed; + opacity: var(--disabled-opacity); +} + +.ds-slider-scale { + color: var(--text-muted); + display: flex; + font-size: var(--font-size-xs); + justify-content: space-between; + margin-top: 0.1rem; +} + /* ――― Favorite Slots ――― */ .ds-favorites-panel { display: grid; diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js index 09916e757..07100bc57 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings.js +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings.js @@ -73,6 +73,7 @@ const state = reactive({ fetched: false, activeSectionSlug: "", numericUpdating: {}, + sliderPreviewValues: {}, actionUpdating: {}, favoriteLoading: false, favoriteSaving: false, @@ -840,6 +841,16 @@ function getParamDisplayLabel(key) { return state.paramMetaByKey[key]?.label || key } +function getSliderDescription(param, value) { + const steps = Array.isArray(param.description_steps) ? param.description_steps : [] + if (!steps.length) return param.description || "" + + const numericValue = Number(value) + if (!Number.isFinite(numericValue)) return param.description || "" + const selected = steps.find(step => numericValue <= Number(step.max)) || steps[steps.length - 1] + return selected.description || param.description || "" +} + function confirmPandaFirmwareToggle(key, enabled) { if (!PANDA_FIRMWARE_TOGGLE_KEYS.has(key)) return true @@ -870,7 +881,12 @@ function syncNumericDisplay(param, rawValue) { async function updateNumericParam(param, numericValue, options = {}) { const key = param.key - const current = state.values[key] + const current = options.previousValue !== undefined ? options.previousValue : state.values[key] + if (Object.prototype.hasOwnProperty.call(state.sliderPreviewValues, key)) { + const nextPreviewValues = { ...state.sliderPreviewValues } + delete nextPreviewValues[key] + state.sliderPreviewValues = nextPreviewValues + } const successMessage = options.successMessage state.numericUpdating = { ...state.numericUpdating, [key]: true } state.values = { ...state.values, [key]: numericValue } @@ -905,6 +921,40 @@ async function updateNumericParam(param, numericValue, options = {}) { } } +function previewSliderParam(param, rawValue) { + if (isNumericUpdating(param.key)) return + + const bounds = numericBounds(param) + const precision = stepPrecision(bounds.step, param.precision) + const snapped = snapNumericToBoundsAndStep(rawValue, bounds, precision) + if (snapped === null) return + + state.sliderPreviewValues = { ...state.sliderPreviewValues, [param.key]: snapped } + syncNumericDisplay(param, snapped) +} + +function commitSliderParam(param, rawValue) { + if (isNumericUpdating(param.key)) return + + const bounds = numericBounds(param) + const precision = stepPrecision(bounds.step, param.precision) + const next = snapNumericToBoundsAndStep(rawValue, bounds, precision) + if (next === null) return + + const current = resolveCurrentNumericValue(param, bounds) + const previewValues = { ...state.sliderPreviewValues } + delete previewValues[param.key] + state.sliderPreviewValues = previewValues + + const epsilon = Math.pow(10, -(precision + 2)) + if (Math.abs(next - current) <= epsilon) { + syncNumericDisplay(param, current) + return + } + + updateNumericParam(param, next, { previousValue: current }) +} + function stepNumericParam(param, direction) { const bounds = numericBounds(param) const min = Number(bounds.min) @@ -1408,6 +1458,7 @@ function renderSettingRow(p) { } const isNumeric = p.ui_type === "numeric" + const isSlider = isNumeric && p.control === "slider" const isColor = p.ui_type === "color" const isAction = p.ui_type === "action" const isGroup = isGroupParam(p) @@ -1427,6 +1478,41 @@ function renderSettingRow(p) { ${() => state.actionUpdating[p.key] ? "Resetting..." : (p.action_label || "Run")} ` + } else if (isSlider) { + rowControl = html` +
+ +
+ ${formatSliderValue(numericBounds(p).min, String(numericBounds(p).step), p.precision, p.key)} + ${formatSliderValue(numericBounds(p).max, String(numericBounds(p).step), p.precision, p.key)} +
+ +
+ ` } else if (isNumeric) { rowControl = html`
@@ -1550,7 +1636,9 @@ function renderSettingRow(p) { ${p.label} ${flmParamStatus ? html`Currently overridden by FLM` : ""}
- ${p.description ? html`
${p.description}
` : ""} + ${p.description_steps + ? html`
${() => getSliderDescription(p, state.sliderPreviewValues[p.key] ?? state.values[p.key])}
` + : (p.description ? html`
${p.description}
` : "")} ${() => { const reason = lockReason() return reason ? html`
Locked: ${reason}
` : "" @@ -1584,7 +1672,7 @@ function renderSettingRow(p) { ${(isNumeric || isColor) ? html`${() => { if (isColor) return formatColorDisplayValue(p) - const currentValue = state.values[p.key] + const currentValue = state.sliderPreviewValues[p.key] ?? state.values[p.key] const bounds = numericBounds(p) return currentValue !== undefined ? formatSliderValue(currentValue, String(bounds.step), p.precision, p.key) : ".." }}` : ""} diff --git a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json index fad579e7b..b279e88b1 100644 --- a/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json +++ b/starpilot/system/the_galaxy/assets/components/tools/device_settings_layout.json @@ -4108,15 +4108,38 @@ { "key": "LaneCenteringE2EAuthority", "label": "E2E Override Strength", - "description": "How strongly a confident end-to-end path can override lane centering when it deliberately departs the lane target. 1.0 gives the model full authority; 0.0 disables break-in.", + "description": "Choose how strongly the vision model may override lane centering when it sees a hazard.", "data_type": "float", "ui_type": "numeric", + "control": "slider", "min": 0.0, "max": 1.0, "step": 0.05, "precision": 2, "parent_key": "LaneCentering", - "settings_tier": "advanced" + "settings_tier": "advanced", + "description_steps": [ + { + "max": 0.2, + "description": "Vehicle rigidly tries to center itself rather than avoiding hazards, potholes, or road debris." + }, + { + "max": 0.4, + "description": "Vehicle tries to center itself and slowly adjusts away from hazards, but may react too slowly." + }, + { + "max": 0.6, + "description": "Vehicle provides additional space around hazards like cyclists and vehicles crossing into the lane." + }, + { + "max": 0.8, + "description": "Vehicle smooths out hazard avoidance, making less rapid adjustments." + }, + { + "max": 1.0, + "description": "Vehicle reacts to hazards recognized by the vision model as quickly as possible." + } + ] }, { "key": "HondaLateralPidKpScale",