From c75975a5868159e56147cdb9119b5ae1c521e021 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Tue, 20 Jan 2026 23:01:02 -0600 Subject: [PATCH 1/4] Fix JS Programming decompiler incorrect operand validation warnings Problem: The decompiler was eagerly decompiling both operandA and operandB for all action operations, even when operandB was unused. This caused incorrect type-specific validation warnings for operations like SET_PROFILE (operation 42) which only uses operandA. Example: SET_PROFILE with operandB type=6 (PID), value=1700 (garbage data) would trigger: "Invalid PID operand value 1700. Valid range is 0-3." Solution: Added operation-specific operand handling with two categories: - operandAOnlyOperations: Operations that only use operandA (skip operandB decompilation) - noOperandOperations: Operations that use no operands (skip both) Additionally, added version detection warnings for unexpected operands: - Warns when unused operands have non-zero type or value - Helps detect firmware/configurator version mismatches - Shows operation name, type, and value for debugging Example warning: "Unexpected operand B to Set Profile operation (type=6, value=1700). This may indicate a firmware version mismatch." Benefits: - Prevents type-specific validation errors on garbage data in unused operands - Preserves validation for operations that legitimately use PID/other operands - Provides version detection when firmware adds new operand usage --- js/transpiler/transpiler/action_decompiler.js | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/js/transpiler/transpiler/action_decompiler.js b/js/transpiler/transpiler/action_decompiler.js index aa2ebc788..3a51ce3da 100644 --- a/js/transpiler/transpiler/action_decompiler.js +++ b/js/transpiler/transpiler/action_decompiler.js @@ -51,14 +51,60 @@ class ActionDecompiler { return this.handleOverrideThrottle(lc, allConditions); } + // Operations that only use operandA (operandB is unused/ignored) + // These should not decompile operandB to avoid incorrect validation warnings + const operandAOnlyOperations = [ + OPERATION.SET_VTX_POWER_LEVEL, + OPERATION.SET_VTX_BAND, + OPERATION.SET_VTX_CHANNEL, + OPERATION.SET_OSD_LAYOUT, + OPERATION.LOITER_OVERRIDE, + OPERATION.OVERRIDE_MIN_GROUND_SPEED, + OPERATION.SET_HEADING_TARGET, + OPERATION.SET_PROFILE, + OPERATION.SET_GIMBAL_SENSITIVITY + ]; + + // Operations that use no operands (boolean flags only) + const noOperandOperations = [ + OPERATION.OVERRIDE_ARMING_SAFETY, + OPERATION.SWAP_ROLL_YAW, + OPERATION.INVERT_ROLL, + OPERATION.INVERT_PITCH, + OPERATION.INVERT_YAW, + OPERATION.DISABLE_GPS_FIX, + OPERATION.RESET_MAG_CALIBRATION + ]; + // INAV operand pattern (confirmed by logic_condition.c): - // - Most overrides: operandA = value, operandB = 0 + // - Most overrides: operandA = value, operandB = 0 (unused) // - GVAR_INC/DEC: operandA = gvar index, operandB = increment/decrement // - FLIGHT_AXIS: operandA = axis index, operandB = angle/rate // - RC_CHANNEL: operandA = channel, operandB = value // - PORT_SET: operandA = pin, operandB = value - const valueA = this.decompileOperand(lc.operandAType, lc.operandAValue, allConditions); - const valueB = this.decompileOperand(lc.operandBType, lc.operandBValue, allConditions); + + // Warn about unexpected operands (version detection for new firmware features) + if (noOperandOperations.includes(lc.operation)) { + if (lc.operandAType !== 0 || lc.operandAValue !== 0) { + this.addWarning(`Unexpected operand A to ${getOperationName(lc.operation)} operation (type=${lc.operandAType}, value=${lc.operandAValue}). This may indicate a firmware version mismatch.`); + } + if (lc.operandBType !== 0 || lc.operandBValue !== 0) { + this.addWarning(`Unexpected operand B to ${getOperationName(lc.operation)} operation (type=${lc.operandBType}, value=${lc.operandBValue}). This may indicate a firmware version mismatch.`); + } + } else if (operandAOnlyOperations.includes(lc.operation)) { + if (lc.operandBType !== 0 || lc.operandBValue !== 0) { + this.addWarning(`Unexpected operand B to ${getOperationName(lc.operation)} operation (type=${lc.operandBType}, value=${lc.operandBValue}). This may indicate a firmware version mismatch.`); + } + } + + // Only decompile operands that are actually used by the operation + // This prevents incorrect validation warnings (e.g., PID range check on unused operands) + const valueA = noOperandOperations.includes(lc.operation) + ? null + : this.decompileOperand(lc.operandAType, lc.operandAValue, allConditions); + const valueB = (operandAOnlyOperations.includes(lc.operation) || noOperandOperations.includes(lc.operation)) + ? null + : this.decompileOperand(lc.operandBType, lc.operandBValue, allConditions); switch (lc.operation) { // GVAR operations: operandA = index, operandB = value From 1129e306a992a95f332ce4399cd36aed313124cd Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 21 Jan 2026 10:51:55 -0600 Subject: [PATCH 2/4] Feature: Add accordion navigation for improved tab organization Organizes the 24 configuration tabs into 8 collapsible accordion groups for improved navigation and reduced visual clutter. Groups: - Setup & Configuration (5 tabs) - Flight Control (5 tabs) - Tuning (3 tabs) - Navigation & Mission (2 tabs) - Sensors & Peripherals (3 tabs) - Data Logging (2 tabs) - Programming (2 tabs) - Tools (2 tabs) Implementation: - Added accordion group structure to index.html - Added accordion styling to main.css (preserves INAV cyan theme) - Added expand/collapse JavaScript to configurator_main.js - Added English translation keys for navigation groups - First group (Setup & Configuration) expands by default No new dependencies required - uses existing jQuery and CSS transitions. --- index.html | 214 +++++++++++++++++++++++++++------------- js/configurator_main.js | 16 ++- locale/en/messages.json | 24 +++++ src/css/main.css | 73 ++++++++++++++ 4 files changed, 257 insertions(+), 70 deletions(-) diff --git a/index.html b/index.html index bd4e4e001..196f391ff 100644 --- a/index.html +++ b/index.html @@ -244,83 +244,159 @@

diff --git a/js/configurator_main.js b/js/configurator_main.js index bf6164be5..b81fd597e 100644 --- a/js/configurator_main.js +++ b/js/configurator_main.js @@ -269,7 +269,21 @@ $(function() { $('#tabs ul.mode-disconnected li a:first').trigger( "click" ); - + // Accordion Navigation Groups + $('.group-header').on('click', function(e) { + e.stopPropagation(); // Prevent triggering tab click + const header = $(this); + const items = header.next('.group-items'); + + // Toggle this group + header.toggleClass('active'); + items.toggleClass('expanded'); + }); + + // Initialize: expand first group by default + $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active'); + $('#tabs ul.mode-connected .nav-group:first-child .group-items').addClass('expanded'); + // options $('#options').on('click', function() { diff --git a/locale/en/messages.json b/locale/en/messages.json index c16540af6..d638d600c 100644 --- a/locale/en/messages.json +++ b/locale/en/messages.json @@ -119,6 +119,30 @@ "tabHelp": { "message": "Documentation & Support" }, + "navGroupSetup": { + "message": "Setup & Configuration" + }, + "navGroupFlight": { + "message": "Flight Control" + }, + "navGroupTuning": { + "message": "Tuning" + }, + "navGroupNavigation": { + "message": "Navigation & Mission" + }, + "navGroupSensors": { + "message": "Sensors & Peripherals" + }, + "navGroupLogging": { + "message": "Data Logging" + }, + "navGroupProgramming": { + "message": "Programming" + }, + "navGroupTools": { + "message": "Tools" + }, "tabSetup": { "message": "Status" }, diff --git a/src/css/main.css b/src/css/main.css index 43dab6ed9..c123dbcd3 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -680,11 +680,84 @@ input[type="number"]::-webkit-inner-spin-button { background-color: #37a8db; } +/* Accordion Navigation Groups */ +#tabs .nav-group { + border-bottom: 1px solid rgba(0, 0, 0, 0.30); +} + +#tabs .group-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px 12px; + cursor: pointer; + background-color: #2d2d2d; + transition: background-color 0.2s; + user-select: none; + border-top: solid 1px rgba(255, 255, 255, 0.05); +} + +#tabs .group-header:hover { + background-color: #353535; +} + +#tabs .group-header.active { + background-color: #3a3a3a; +} + +#tabs .group-title { + font-family: 'open_sanssemibold', Arial, serif; + font-size: 12px; + font-weight: 600; + color: #b0b0b0; +} + +#tabs .chevron { + font-size: 10px; + transition: transform 0.2s; + color: #808080; +} + +#tabs .group-header.active .chevron { + transform: rotate(90deg); +} + +#tabs .group-items { + max-height: 0; + overflow: hidden; + transition: max-height 0.3s ease; + background-color: #252525; + list-style: none; + padding: 0; + margin: 0; +} + +#tabs .group-items.expanded { + max-height: 500px; +} + +#tabs .group-items li { + border-bottom: 1px solid rgba(0, 0, 0, 0.20); +} + +#tabs .group-items li:last-child { + border-bottom: 0; +} + +#tabs .group-items li a { + padding-left: 40px; +} + .tabicon { background: no-repeat 13px 7px; background-size: 15px; } +/* Adjust icon position for accordion tabs */ +#tabs .group-items .tabicon { + background-position: 20px 7px; +} + /* Tab-Icons */ .ic_setup { background-image: url("./../../images/icons/cf_icon_setup_grey.svg"); From e2ff657b568b09d56599e6f979c581d308b13ea2 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 21 Jan 2026 17:09:15 -0600 Subject: [PATCH 3/4] Add expand/collapse all toggle for accordion navigation Adds an icon-only toggle button at the bottom of the navigation menu to expand or collapse all accordion groups. Features: - SVG icon button that swaps between expand (double chevron down) and collapse (double chevron up) states - Clicking expands all 8 navigation groups or collapses to first group only - Preference persists across configurator restarts via electron-store - When all groups expanded, headers become minimal cyan divider lines to maximize vertical space - Icon-only design saves space - all 24 tabs fit without scrolling - Smooth CSS transitions for icon swap and header state changes Implementation: - Toggle button in index.html with both expand/collapse SVG icons - JavaScript in configurator_main.js handles toggle logic and persistence - CSS in main.css for compact headers when expanded and icon styling - Translation keys in locale/en/messages.json for accessibility Addresses need for quick access to all tabs while maintaining visual organization of accordion groups. --- index.html | 15 ++++++++++ js/configurator_main.js | 61 +++++++++++++++++++++++++++++++++++++++-- locale/en/messages.json | 6 ++++ src/css/main.css | 51 ++++++++++++++++++++++++++++++++-- tabs/options.html | 2 +- 5 files changed, 128 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index 196f391ff..92dfd4217 100644 --- a/index.html +++ b/index.html @@ -397,6 +397,21 @@

+ + +
diff --git a/js/configurator_main.js b/js/configurator_main.js index b81fd597e..106ec357f 100644 --- a/js/configurator_main.js +++ b/js/configurator_main.js @@ -278,11 +278,66 @@ $(function() { // Toggle this group header.toggleClass('active'); items.toggleClass('expanded'); + + // Update the expand/collapse all button state + updateToggleAllButton(); + }); + + // Function to update toggle all button state + function updateToggleAllButton() { + const allExpanded = $('.nav-group .group-header.active').length === $('.nav-group .group-header').length; + const $expandIcon = $('#toggleAllGroups .expand-icon'); + const $collapseIcon = $('#toggleAllGroups .collapse-icon'); + const $toggleText = $('#toggleAllGroups .toggle-text'); + + if (allExpanded) { + $expandIcon.hide(); + $collapseIcon.show(); + $toggleText.attr('data-i18n', 'navCollapseAll'); + $toggleText.text(i18n.getMessage('navCollapseAll')); + } else { + $expandIcon.show(); + $collapseIcon.hide(); + $toggleText.attr('data-i18n', 'navExpandAll'); + $toggleText.text(i18n.getMessage('navExpandAll')); + } + } + + // Expand/Collapse All Toggle + $('#toggleAllGroups').on('click', function(e) { + e.preventDefault(); + const allExpanded = $('.nav-group .group-header.active').length === $('.nav-group .group-header').length; + + if (allExpanded) { + // Collapse all except first + $('.nav-group .group-header').removeClass('active'); + $('.nav-group .group-items').removeClass('expanded'); + $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active'); + $('#tabs ul.mode-connected .nav-group:first-child .group-items').addClass('expanded'); + store.set('expand_all_groups', false); + } else { + // Expand all + $('.nav-group .group-header').addClass('active'); + $('.nav-group .group-items').addClass('expanded'); + store.set('expand_all_groups', true); + } + + updateToggleAllButton(); }); - // Initialize: expand first group by default - $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active'); - $('#tabs ul.mode-connected .nav-group:first-child .group-items').addClass('expanded'); + // Initialize: apply saved expand all preference or expand first group by default + if (store.get('expand_all_groups', false)) { + // Expand all groups + $('.nav-group .group-header').addClass('active'); + $('.nav-group .group-items').addClass('expanded'); + } else { + // Expand first group only + $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active'); + $('#tabs ul.mode-connected .nav-group:first-child .group-items').addClass('expanded'); + } + + // Update button state on initialization + updateToggleAllButton(); // options diff --git a/locale/en/messages.json b/locale/en/messages.json index d638d600c..e9261dd26 100644 --- a/locale/en/messages.json +++ b/locale/en/messages.json @@ -47,6 +47,12 @@ "options_cliAutocomplete": { "message": "Advanced CLI AutoComplete" }, + "navExpandAll": { + "message": "Expand All" + }, + "navCollapseAll": { + "message": "Collapse All" + }, "options_unit_type": { "message": "Set how the units render on the configurator only" }, diff --git a/src/css/main.css b/src/css/main.css index c123dbcd3..430a25103 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -702,7 +702,10 @@ input[type="number"]::-webkit-inner-spin-button { } #tabs .group-header.active { - background-color: #3a3a3a; + background-color: transparent; + padding: 0; + min-height: 1px; + border-top: 1px solid rgba(55, 168, 219, 0.3); } #tabs .group-title { @@ -710,16 +713,21 @@ input[type="number"]::-webkit-inner-spin-button { font-size: 12px; font-weight: 600; color: #b0b0b0; + transition: opacity 0.2s; +} + +#tabs .group-header.active .group-title { + display: none; } #tabs .chevron { font-size: 10px; - transition: transform 0.2s; + transition: transform 0.2s, opacity 0.2s; color: #808080; } #tabs .group-header.active .chevron { - transform: rotate(90deg); + display: none; } #tabs .group-items { @@ -748,6 +756,43 @@ input[type="number"]::-webkit-inner-spin-button { padding-left: 40px; } +/* Expand/Collapse All Toggle */ +#tabs .nav-toggle-all { + margin-top: 4px; + padding: 0; + border-top: 1px solid rgba(255, 255, 255, 0.1); +} + +#tabs .nav-toggle-all a { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 6px 12px; + color: #37a8db; + font-family: 'open_sanssemibold', Arial, serif; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.5px; + background-color: rgba(55, 168, 219, 0.05); + transition: background-color 0.2s, color 0.2s; +} + +#tabs .nav-toggle-all a:hover { + background-color: rgba(55, 168, 219, 0.15); + color: #4db8eb; +} + +#tabs .nav-toggle-all svg { + width: 20px; + height: 20px; + flex-shrink: 0; +} + +#tabs .nav-toggle-all .toggle-text { + display: none; +} + .tabicon { background: no-repeat 13px 7px; background-size: 15px; diff --git a/tabs/options.html b/tabs/options.html index 38aecc647..33f36d61a 100644 --- a/tabs/options.html +++ b/tabs/options.html @@ -29,7 +29,7 @@ - +
From f4667c84f5a1f245d561245006d324e47dc239b9 Mon Sep 17 00:00:00 2001 From: Ray Morris Date: Wed, 21 Jan 2026 17:43:50 -0600 Subject: [PATCH 4/4] Improve accordion navigation accessibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds WCAG-compliant accessibility features to accordion navigation: HTML changes: - Added role="button" to all group headers for screen reader compatibility - Added tabindex="0" to make headers keyboard-navigable - Added aria-expanded="false" initial state to indicate collapsed groups JavaScript changes: - Updates aria-expanded dynamically when groups expand/collapse - Added keyboard event handler for Enter and Space keys - Ensures aria-expanded is set correctly on initialization - Updates aria-expanded when using expand/collapse all button - Fixed localization attribute on JavaScript Programming tab (i18n → data-i18n) Benefits: - Screen readers announce group headers as interactive buttons - Keyboard users can Tab to headers and activate with Enter/Space - Assistive technology correctly reports expanded/collapsed state - Meets WCAG 2.1 Level AA compliance for keyboard navigation Addresses qodo-merge code review suggestions for accessibility. --- index.html | 18 +++++++++--------- js/configurator_main.js | 21 ++++++++++++++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index 92dfd4217..b854a54f1 100644 --- a/index.html +++ b/index.html @@ -246,7 +246,7 @@

  • - > +