diff --git a/index.html b/index.html index bd4e4e001..b854a54f1 100644 --- a/index.html +++ b/index.html @@ -244,83 +244,174 @@

diff --git a/js/configurator_main.js b/js/configurator_main.js index bf6164be5..e80214f4a 100644 --- a/js/configurator_main.js +++ b/js/configurator_main.js @@ -269,7 +269,87 @@ $(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'); + + // Update aria-expanded for accessibility + header.attr('aria-expanded', header.hasClass('active')); + + // Update the expand/collapse all button state + updateToggleAllButton(); + }); + + // Keyboard accessibility for accordion headers + $('.group-header').on('keydown', function(e) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + $(this).trigger('click'); + } + }); + + // 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').attr('aria-expanded', 'false'); + $('.nav-group .group-items').removeClass('expanded'); + $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active').attr('aria-expanded', 'true'); + $('#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').attr('aria-expanded', 'true'); + $('.nav-group .group-items').addClass('expanded'); + store.set('expand_all_groups', true); + } + + updateToggleAllButton(); + }); + + // 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').attr('aria-expanded', 'true'); + $('.nav-group .group-items').addClass('expanded'); + } else { + // Expand first group only + $('#tabs ul.mode-connected .nav-group:first-child .group-header').addClass('active').attr('aria-expanded', 'true'); + $('#tabs ul.mode-connected .nav-group:first-child .group-items').addClass('expanded'); + } + + // Update button state on initialization + updateToggleAllButton(); + // options $('#options').on('click', function() { 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 diff --git a/locale/en/messages.json b/locale/en/messages.json index c16540af6..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" }, @@ -119,6 +125,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..430a25103 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -680,11 +680,129 @@ 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: transparent; + padding: 0; + min-height: 1px; + border-top: 1px solid rgba(55, 168, 219, 0.3); +} + +#tabs .group-title { + font-family: 'open_sanssemibold', Arial, serif; + 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, opacity 0.2s; + color: #808080; +} + +#tabs .group-header.active .chevron { + display: none; +} + +#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; +} + +/* 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; } +/* 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"); 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 @@ - +