diff --git a/starpilot/system/the_pond/assets/components/tools/troubleshoot.css b/starpilot/system/the_pond/assets/components/tools/troubleshoot.css index 0bdb14c1..9cbf5cc1 100644 --- a/starpilot/system/the_pond/assets/components/tools/troubleshoot.css +++ b/starpilot/system/the_pond/assets/components/tools/troubleshoot.css @@ -23,6 +23,22 @@ margin-bottom: 10px; } +.troubleshootToggle { + display: inline-flex; + align-items: center; + gap: 8px; + background: rgba(255, 255, 255, 0.05); + border: 1px solid var(--sidebar-border-color); + border-radius: var(--border-radius-md); + padding: 10px 14px; + font-size: 0.95rem; + font-weight: 600; +} + +.troubleshootToggle input { + margin: 0; +} + .troubleshootButton { background: linear-gradient(135deg, #7a62b8, #8b6cc5); color: #fff; @@ -103,7 +119,17 @@ grid-template-columns: 2fr 1fr 1fr 1.3fr; } +.troubleshootItemRowChanged { + background: linear-gradient(90deg, rgba(217, 90, 123, 0.12), rgba(122, 98, 184, 0.05)); + border-left: 3px solid #d95a7b; + padding-left: 12px; +} + .troubleshootItemLabel { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; font-weight: 600; } @@ -114,6 +140,24 @@ word-break: break-word; } +.troubleshootItemValueChanged { + color: #ffd1dd; + font-weight: 700; +} + +.troubleshootChangedBadge { + display: inline-flex; + align-items: center; + border-radius: 999px; + background: rgba(217, 90, 123, 0.2); + color: #ffd7e2; + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.02em; + padding: 3px 8px; + text-transform: uppercase; +} + @media (max-width: 720px) { .troubleshootHeaderRow, .troubleshootItemRow { diff --git a/starpilot/system/the_pond/assets/components/tools/troubleshoot.js b/starpilot/system/the_pond/assets/components/tools/troubleshoot.js index 76e05e64..5769805b 100644 --- a/starpilot/system/the_pond/assets/components/tools/troubleshoot.js +++ b/starpilot/system/the_pond/assets/components/tools/troubleshoot.js @@ -8,6 +8,7 @@ const state = reactive({ isOnroad: false, refreshing: false, busySection: "", + showNonDefaultOnly: false, }) let initialized = false @@ -29,21 +30,66 @@ function formatLearnedValue(value) { return text ? formatValue(value) : "" } +function valuesMatch(left, right) { + if (left === right) return true + if ((left === null || left === undefined) && (right === null || right === undefined)) return true + + if (typeof left === "number" && typeof right === "number") { + return Math.abs(left - right) < 1e-9 + } + + const leftText = String(left ?? "").trim() + const rightText = String(right ?? "").trim() + if (!leftText && !rightText) return true + + const leftNumber = Number(leftText) + const rightNumber = Number(rightText) + if (leftText !== "" && rightText !== "" && Number.isFinite(leftNumber) && Number.isFinite(rightNumber)) { + return Math.abs(leftNumber - rightNumber) < 1e-9 + } + + return leftText === rightText +} + +function isNonDefaultItem(item) { + return !valuesMatch(item?.value, item?.defaultValue) +} + +function getVisibleSectionItems(section) { + const items = Array.isArray(section?.items) ? section.items : [] + if (!state.showNonDefaultOnly) return items + return items.filter(isNonDefaultItem) +} + +function getVisibleSections() { + const sections = Array.isArray(state.sections) ? state.sections : [] + if (!state.showNonDefaultOnly) return sections + return sections.filter((section) => getVisibleSectionItems(section).length > 0) +} + +function countNonDefaultItems() { + return (Array.isArray(state.sections) ? state.sections : []).reduce((count, section) => { + const items = Array.isArray(section?.items) ? section.items : [] + return count + items.filter(isNonDefaultItem).length + }, 0) +} + function buildReportText() { const lines = [] lines.push("StarPilot Troubleshoot Report") lines.push(`Generated: ${new Date().toISOString()}`) lines.push(`Onroad: ${state.isOnroad ? "Yes" : "No"}`) + lines.push(`Only non-default values: ${state.showNonDefaultOnly ? "Yes" : "No"}`) lines.push("") lines.push("Snapshot") for (const item of state.snapshot) { lines.push(`- ${item.label}: ${formatValue(item.value)}`) } - for (const section of state.sections) { + for (const section of getVisibleSections()) { lines.push("") lines.push(section.title) - for (const item of section.items || []) { + for (const item of getVisibleSectionItems(section)) { const learnedSuffix = formatLearnedValue(item.learnedValue) ? `, learned: ${formatLearnedValue(item.learnedValue)}` : "" @@ -140,12 +186,15 @@ function initialize() { } function itemRows(section) { - const items = Array.isArray(section?.items) ? section.items : [] + const items = getVisibleSectionItems(section) const rowClassName = section?.hasLearnedColumn ? "troubleshootItemRow troubleshootItemRowLearned" : "troubleshootItemRow" return items.map((item) => html` -
-
${item.label}
-
${formatValue(item.value)}
+
+
+ ${item.label} + ${isNonDefaultItem(item) ? html`Changed` : ""} +
+
${formatValue(item.value)}
${formatValue(item.defaultValue)}
${section?.hasLearnedColumn ? html`
${formatLearnedValue(item.learnedValue)}
` : ""}
@@ -172,9 +221,17 @@ export function Troubleshoot() { +
${() => state.error ? html`

Error: ${state.error}

` : ""}

Onroad: ${state.isOnroad ? "Yes" : "No"}

+

Changed Settings: ${() => countNonDefaultItems()}

@@ -191,7 +248,7 @@ export function Troubleshoot() { `)}
- ${() => state.sections.map((section) => html` + ${() => getVisibleSections().map((section) => html`

${section.title}

@@ -212,6 +269,12 @@ export function Troubleshoot() { ${itemRows(section)}
`)} + + ${() => state.showNonDefaultOnly && countNonDefaultItems() === 0 ? html` +
+ No settings are currently different from their defaults. +
+ ` : ""} ` : ""}
`