Moar Toggles

This commit is contained in:
firestar5683
2026-03-03 22:29:10 -06:00
parent c84acea993
commit d62d7dd9fc
3 changed files with 1664 additions and 46 deletions
@@ -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 = '<option value="">Error loading</option>' })
} 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`
<div class="ds-slider-container">
${(() => {
const bounds = numericBounds(p)
return html`
<input
type="range"
class="ds-slider"
id="ds-${p.key}"
min="${p.min !== undefined ? p.min : (p.data_type === 'float' ? 0.0 : 0)}"
max="${p.max !== undefined ? p.max : (p.data_type === 'float' ? 100.0 : 100)}"
step="${p.step !== undefined ? p.step : (p.data_type === 'float' ? 0.01 : 1)}"
min="${bounds.min}"
max="${bounds.max}"
step="${bounds.step}"
data-precision="${p.precision !== undefined ? p.precision : ''}"
value="${state.values[p.key] !== undefined ? state.values[p.key] : ''}"
@input="${(e) => handleSliderInput(e, p.key)}"
@change="${() => updateParam(p.key, 'numeric')}"
/>
`
})()}
</div>
` : p.ui_type === "dropdown" ? html`
<select class="ds-select" id="ds-${p.key}"
File diff suppressed because it is too large Load Diff