From d62d7dd9fc4bbc3022b98db16de7c3cf637e324a Mon Sep 17 00:00:00 2001 From: firestar5683 <168790843+firestar5683@users.noreply.github.com> Date: Tue, 3 Mar 2026 22:29:10 -0600 Subject: [PATCH] Moar Toggles --- .../components/tools/device_settings.js | 82 +- .../tools/device_settings_layout.json | 1458 ++++++++++++++++- tools/StarPilot/generate_pond_layout.py | 170 +- 3 files changed, 1664 insertions(+), 46 deletions(-) diff --git a/frogpilot/system/the_pond/assets/components/tools/device_settings.js b/frogpilot/system/the_pond/assets/components/tools/device_settings.js index f04abf2a8..6d1068647 100644 --- a/frogpilot/system/the_pond/assets/components/tools/device_settings.js +++ b/frogpilot/system/the_pond/assets/components/tools/device_settings.js @@ -6,6 +6,7 @@ import { html, reactive } from "https://esm.sh/@arrow-js/core" const state = reactive({ layout: [], allKeys: [], + paramMetaByKey: {}, values: {}, loadingLayout: true, loadingValues: true, @@ -34,12 +35,15 @@ async function fetchLayoutAndParams() { // Extract flatter key map const keys = [] + const paramMetaByKey = {} for (const section of layoutData) { for (const p of section.params) { keys.push(p.key) + paramMetaByKey[p.key] = p } } state.allKeys = keys + state.paramMetaByKey = paramMetaByKey } catch (e) { console.error("Failed to fetch UI layout:", e) } @@ -58,6 +62,17 @@ async function fetchLayoutAndParams() { } function syncInputs() { + const selectValue = (value) => (value === null || value === undefined ? "" : String(value)) + const applySelectOptions = (el, options) => { + el.innerHTML = "" + for (const opt of options) { + const o = document.createElement("option") + o.value = String(opt.value) + o.textContent = opt.label + el.appendChild(o) + } + } + for (const key of state.allKeys) { const el = document.getElementById(`ds-${key}`) if (el) { @@ -65,20 +80,19 @@ function syncInputs() { el.checked = !!state.values[key] } else if (el.tagName === "SELECT") { const endpoint = el.getAttribute("data-endpoint") + const inlineOptions = state.paramMetaByKey[key]?.options if (endpoint && !el.dataset.hydrated) { el.dataset.hydrated = "1" fetch(endpoint).then(r => r.json()).then(options => { - el.innerHTML = "" - for (const opt of options) { - const o = document.createElement("option") - o.value = opt.value - o.textContent = opt.label - el.appendChild(o) - } - el.value = state.values[key] || "" + applySelectOptions(el, options) + el.value = selectValue(state.values[key]) }).catch(() => { el.innerHTML = '' }) + } else if (Array.isArray(inlineOptions) && inlineOptions.length > 0 && !el.dataset.hydrated) { + el.dataset.hydrated = "1" + applySelectOptions(el, inlineOptions) + el.value = selectValue(state.values[key]) } else { - el.value = state.values[key] || "" + el.value = selectValue(state.values[key]) } } else { el.value = state.values[key] @@ -119,6 +133,41 @@ function formatSliderValue(val, stepStr, precisionInt, key) { return Number(v.toFixed(dec)).toString() } +function numericBounds(param) { + const defaultBounds = { + min: param.min !== undefined ? param.min : (param.data_type === "float" ? 0.0 : 0), + max: param.max !== undefined ? param.max : (param.data_type === "float" ? 100.0 : 100), + step: param.step !== undefined ? param.step : (param.data_type === "float" ? 0.01 : 1), + } + + const toFinite = (value) => { + const n = Number(value) + return Number.isFinite(n) ? n : null + } + + if (param.key === "ScreenBrightness") { + return { min: 1, max: 101, step: 1 } + } + if (param.key === "ScreenBrightnessOnroad") { + return { min: 0, max: 101, step: 1 } + } + + if (param.key === "SteerKP") { + const base = toFinite(state.values.SteerKPStock) || toFinite(state.values.SteerKP) || 0.6 + return { min: +(base * 0.5).toFixed(2), max: +(base * 1.5).toFixed(2), step: 0.01 } + } + if (param.key === "SteerLatAccel") { + const base = toFinite(state.values.SteerLatAccelStock) || toFinite(state.values.SteerLatAccel) || 2.0 + return { min: +(base * 0.5).toFixed(2), max: +(base * 1.25).toFixed(2), step: 0.01 } + } + if (param.key === "SteerRatio") { + const base = toFinite(state.values.SteerRatioStock) || toFinite(state.values.SteerRatio) || 15.0 + return { min: +(base * 0.25).toFixed(2), max: +(base * 1.5).toFixed(2), step: 0.01 } + } + + return defaultBounds +} + async function updateParam(key, elType) { const current = state.values[key] @@ -161,7 +210,7 @@ function revertInput(key, current, elType) { const el = document.getElementById(`ds-${key}`) if (el) { if (elType === "checkbox") el.checked = !!current - else if (elType === "dropdown") el.value = current || "" + else if (elType === "dropdown") el.value = (current === null || current === undefined ? "" : String(current)) else { el.value = current const displayEl = document.getElementById(`ds-display-${key}`) @@ -255,7 +304,7 @@ export function DeviceSettings() { if (!state.expanded[p.parent_key]) return "" } - const isNumeric = p.ui_type === "numeric" || p.data_type === "float" || p.data_type === "int" + const isNumeric = p.ui_type === "numeric" const isChild = p.parent_key ? "ds-child-modifier" : "" return html` @@ -276,18 +325,23 @@ export function DeviceSettings() { ${isNumeric ? html`
+ ${(() => { + const bounds = numericBounds(p) + return html` + ` + })()}
` : p.ui_type === "dropdown" ? html`