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>
This commit is contained in:
firestar5683
2026-08-04 12:28:26 -05:00
parent ab447ef306
commit 0d3330c501
4 changed files with 147 additions and 5 deletions
@@ -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
@@ -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;
@@ -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")}
</button>
`
} else if (isSlider) {
rowControl = html`
<div class="ds-slider-container">
<input
type="range"
class="ds-slider"
min="${numericBounds(p).min}"
max="${numericBounds(p).max}"
step="${numericBounds(p).step}"
aria-label="${p.label}"
disabled="${() => isLocked() || isNumericUpdating(p.key)}"
value="${() => {
const bounds = numericBounds(p)
const preview = state.sliderPreviewValues[p.key]
return formatNumericForInput(preview ?? resolveCurrentNumericValue(p, bounds), stepPrecision(bounds.step, p.precision))
}}"
@input="${(event) => previewSliderParam(p, event.currentTarget.value)}"
@change="${(event) => commitSliderParam(p, event.currentTarget.value)}" />
<div class="ds-slider-scale">
<span>${formatSliderValue(numericBounds(p).min, String(numericBounds(p).step), p.precision, p.key)}</span>
<span>${formatSliderValue(numericBounds(p).max, String(numericBounds(p).step), p.precision, p.key)}</span>
</div>
<button
class="ds-reset-btn"
disabled="${() => {
const bounds = numericBounds(p)
const defaultValue = resolveDefaultNumericValue(p, bounds)
const currentValue = resolveCurrentNumericValue(p, bounds)
const precision = stepPrecision(bounds.step, p.precision)
const epsilon = Math.pow(10, -(precision + 2))
return isLocked() || isNumericUpdating(p.key) || defaultValue === null || Math.abs(defaultValue - currentValue) <= epsilon
}}"
@click="${() => resetNumericParam(p)}">Reset to Default</button>
</div>
`
} else if (isNumeric) {
rowControl = html`
<div class="ds-stepper-container">
@@ -1550,7 +1636,9 @@ function renderSettingRow(p) {
<span class="ds-row-label">${p.label}</span>
${flmParamStatus ? html`<span class="ds-flm-badge">Currently overridden by FLM</span>` : ""}
</div>
${p.description ? html`<div class="ds-row-desc">${p.description}</div>` : ""}
${p.description_steps
? html`<div class="ds-row-desc">${() => getSliderDescription(p, state.sliderPreviewValues[p.key] ?? state.values[p.key])}</div>`
: (p.description ? html`<div class="ds-row-desc">${p.description}</div>` : "")}
${() => {
const reason = lockReason()
return reason ? html`<div class="ds-row-desc"><strong>Locked:</strong> ${reason}</div>` : ""
@@ -1584,7 +1672,7 @@ function renderSettingRow(p) {
</div>
${(isNumeric || isColor) ? html`<span class="ds-row-value" id="ds-display-${p.key}">${() => {
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) : ".."
}}</span>` : ""}
@@ -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",