diff --git a/ui_server.py b/ui_server.py index c861468..ea4f561 100644 --- a/ui_server.py +++ b/ui_server.py @@ -1287,6 +1287,8 @@ HTML = """ .config-scale-control input { width: 74px; height: 16px; margin: 0; accent-color: var(--ui-accent); cursor: pointer; } .config-scale-control input:disabled { opacity: .45; cursor: default; } .config-scale-control output { min-width: 34px; color: var(--ui-text); font-variant-numeric: tabular-nums; text-align: right; } + .config-scale-mode { min-height: 26px !important; padding: 0 8px !important; white-space: nowrap; } + .config-scale-mode[aria-pressed="true"] { color: #fff !important; background: var(--ui-accent-bg) !important; border-color: var(--ui-accent) !important; box-shadow: 0 0 0 2px color-mix(in srgb, var(--ui-accent) 16%, transparent); } .config-layout-state { min-width: 0; color: var(--ui-muted); font-size: 10px; } .config-workspace { display: grid; grid-template-columns: minmax(0, 1fr); min-width: 0; min-height: 0; } .config-workspace.has-structure { grid-template-columns: minmax(190px, 260px) minmax(0, 1fr); align-items: start; } @@ -1603,6 +1605,7 @@ HTML = """ + Ctrl/Cmd + клик — выбрать несколько · перетаскивайте группу @@ -1829,6 +1832,7 @@ HTML = """ + Ctrl/Cmd + клик — выбрать несколько · перетаскивайте группу @@ -2093,6 +2097,7 @@ HTML = """ let deletedPanelOpen = false; let configZIndex = 1; let configContentScale = 1; + let configScaleWheelEnabled = false; let configBlockObserver = null; let selectedConfigBlock = null; let selectedConfigBlocks = new Set(); @@ -2138,6 +2143,14 @@ HTML = """ updateConfigBlockScale(block); saveConfigLayout(); } + function setConfigScaleMode(enabled) { + configScaleWheelEnabled = !!enabled && !!selectedConfigBlock; + const button = $('configScaleMode'); + if (!button) return; + button.disabled = !selectedConfigBlock; + button.setAttribute('aria-pressed', String(configScaleWheelEnabled)); + button.textContent = configScaleWheelEnabled ? 'масштаб колесом: вкл' : 'масштаб колесом: выкл'; + } function selectConfigBlock(block) { selectedConfigBlocks = new Set(block && !configBlockState(block) ? [block] : []); updateConfigSelection(block); @@ -2152,6 +2165,7 @@ HTML = """ if (!selectedConfigBlock) { if (control) control.disabled = true; if (remove) remove.disabled = true; + setConfigScaleMode(false); return; } const entry = configLayout[selectedConfigBlock.dataset.section] || {}; @@ -2161,6 +2175,7 @@ HTML = """ setText('configContentScaleValue', `${value}%`); if (control) control.disabled = selectedConfigBlocks.size !== 1; if (remove) remove.disabled = !selectedConfigBlocks.size; + setConfigScaleMode(configScaleWheelEnabled); updateConfigBlockScale(selectedConfigBlock); if (selectedConfigBlocks.size > 1) setText('configLayoutState', `выбрано ${selectedConfigBlocks.size} блока · перетаскивайте группу`); } @@ -2637,6 +2652,14 @@ HTML = """ block.style.cursor = edge ? `${edge}-resize` : ''; } }); + block.addEventListener('wheel', event => { + if (!configScaleWheelEnabled || configBlockState(block) || !event.deltaY) return; + event.preventDefault(); + event.stopPropagation(); + if (selectedConfigBlock !== block) selectConfigBlock(block); + const current = Number(configLayout[block.dataset.section]?.scale) || 100; + applyConfigContentScale(current + (event.deltaY < 0 ? 5 : -5)); + }, {passive: false}); block.addEventListener('pointerleave', () => { if (!block.classList.contains('is-resizing')) block.style.cursor = ''; }); block.addEventListener('pointerdown', event => { if (event.defaultPrevented || block.classList.contains('is-resizing')) return; @@ -2756,6 +2779,7 @@ HTML = """ selectConfigBlock(configBlocks.find(block => !configBlockState(block)) || null); $('configResetLayout')?.addEventListener('click', resetConfigLayout); $('configDeleteSelected')?.addEventListener('click', deleteSelectedConfigBlocks); + $('configScaleMode')?.addEventListener('click', () => setConfigScaleMode(!configScaleWheelEnabled)); $('configRestoreToggle')?.addEventListener('click', () => { restorePanelOpen = !restorePanelOpen; renderConfigRestorePanel(); @@ -2811,6 +2835,7 @@ HTML = """ let selectedNetworkBlocks = new Set(); let networkStructureOpen = false; let networkContentScale = 1; + let networkScaleWheelEnabled = false; function readNetworkLayout() { try { const saved = JSON.parse(localStorage.getItem('fpv-network-layout-v1') || '{}'); @@ -2857,6 +2882,7 @@ HTML = """ if (!networkSelectedBlock) { if (control) control.disabled = true; if (remove) remove.disabled = true; + setNetworkScaleMode(false); return; } const value = Math.max(50, Math.min(200, Math.round(Number(networkLayout[networkSelectedBlock.dataset.section]?.scale) || 100))); @@ -2865,6 +2891,7 @@ HTML = """ setText('networkContentScaleValue', `${value}%`); if (control) control.disabled = selectedNetworkBlocks.size !== 1; if (remove) remove.disabled = !selectedNetworkBlocks.size; + setNetworkScaleMode(networkScaleWheelEnabled); updateNetworkBlockScale(networkSelectedBlock); if (selectedNetworkBlocks.size > 1) setText('networkLayoutState', `выбрано ${selectedNetworkBlocks.size} блока · перетаскивайте группу`); } @@ -2887,6 +2914,14 @@ HTML = """ updateNetworkBlockScale(block); saveNetworkLayout(); } + function setNetworkScaleMode(enabled) { + networkScaleWheelEnabled = !!enabled && !!networkSelectedBlock; + const button = $('networkScaleMode'); + if (!button) return; + button.disabled = !networkSelectedBlock; + button.setAttribute('aria-pressed', String(networkScaleWheelEnabled)); + button.textContent = networkScaleWheelEnabled ? 'масштаб колесом: вкл' : 'масштаб колесом: выкл'; + } function captureNetworkLayout(block) { const key = block?.dataset.section; if (!key) return; @@ -3261,6 +3296,14 @@ HTML = """ block.style.cursor = edge ? `${edge}-resize` : ''; } }); + block.addEventListener('wheel', event => { + if (!networkScaleWheelEnabled || networkState(block) || !event.deltaY) return; + event.preventDefault(); + event.stopPropagation(); + if (networkSelectedBlock !== block) selectNetworkBlock(block); + const current = Number(networkLayout[block.dataset.section]?.scale) || 100; + applyNetworkContentScale(current + (event.deltaY < 0 ? 5 : -5)); + }, {passive: false}); block.addEventListener('pointerdown', event => { if (event.defaultPrevented || block.classList.contains('is-resizing')) return; startNetworkDrag(block, event); @@ -3314,6 +3357,7 @@ HTML = """ selectNetworkBlock(networkBlocks.find(block => !networkState(block)) || null); $('networkResetLayout')?.addEventListener('click', resetNetworkLayout); $('networkDeleteSelected')?.addEventListener('click', deleteSelectedNetworkBlocks); + $('networkScaleMode')?.addEventListener('click', () => setNetworkScaleMode(!networkScaleWheelEnabled)); $('networkStructureToggle')?.addEventListener('click', () => { networkStructureOpen = !networkStructureOpen; renderNetworkStructure();