From 3e1bfc982a91a224f7f805197f33e033085dba0a Mon Sep 17 00:00:00 2001 From: AlexsandrSnytkin Date: Fri, 18 Sep 2026 20:24:27 +0700 Subject: [PATCH] fix(ui): restore canvas wheel scrolling --- ui_server.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/ui_server.py b/ui_server.py index 322eefc..90129ac 100644 --- a/ui_server.py +++ b/ui_server.py @@ -1312,7 +1312,7 @@ HTML = """ .config-deleted-list { display: grid; gap: 5px; padding-top: 4px; border-top: 1px solid var(--ui-border); } .config-deleted-list[hidden] { display: none !important; } .config-restore-panel[hidden], .config-trash[hidden] { display: none !important; } - .config-canvas { position: relative; display: block; min-height: 760px; padding: 8px 6px 18px; overflow: auto; overscroll-behavior: contain; background: radial-gradient(circle at 12% 8%, color-mix(in srgb, var(--ui-accent) 7%, transparent), transparent 28%), linear-gradient(135deg, color-mix(in srgb, var(--ui-bg) 74%, var(--ui-panel)), var(--ui-bg)); } + .config-canvas { position: relative; display: block; min-height: 760px; padding: 8px 6px 18px; overflow: auto; overscroll-behavior: auto; background: radial-gradient(circle at 12% 8%, color-mix(in srgb, var(--ui-accent) 7%, transparent), transparent 28%), linear-gradient(135deg, color-mix(in srgb, var(--ui-bg) 74%, var(--ui-panel)), var(--ui-bg)); } .config-canvas::before { content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none; opacity: .34; background-image: linear-gradient(color-mix(in srgb, var(--ui-text) 5%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--ui-text) 5%, transparent) 1px, transparent 1px); background-size: 28px 28px; mask-image: linear-gradient(to bottom, black, transparent 86%); } .config-canvas > .config-block { position: absolute; z-index: 1; box-sizing: border-box; container: config-block / inline-size; margin: 0; min-width: 240px; min-height: 110px; border: 1px solid var(--ui-border) !important; border-radius: 14px; background: color-mix(in srgb, var(--ui-panel) 94%, var(--ui-accent)) !important; box-shadow: 0 8px 22px color-mix(in srgb, var(--ui-bg) 22%, transparent); overflow: auto; transition: border-color .2s ease, box-shadow .2s ease, opacity .2s ease, transform .2s ease; } body[data-card-style="flat"] .config-canvas > .config-block { box-shadow: none; border-radius: 8px; } @@ -1485,7 +1485,7 @@ HTML = """ .network-workspace.has-structure > .network-restore-panel { grid-column: 1; border-right: 1px solid var(--ui-border); border-bottom: 0; } .network-workspace.has-structure > .network-canvas { grid-column: 2; } .network-restore-panel { min-height: 0; max-height: calc(100vh - 128px); } - .network-canvas { position: relative; display: block; min-height: 720px; padding: 14px; overflow: auto; background: radial-gradient(circle at 10% 0%, color-mix(in srgb, var(--ui-accent) 7%, transparent), transparent 32%), color-mix(in srgb, var(--ui-bg) 74%, var(--ui-panel)); } + .network-canvas { position: relative; display: block; min-height: 720px; padding: 14px; overflow: auto; overscroll-behavior: auto; background: radial-gradient(circle at 10% 0%, color-mix(in srgb, var(--ui-accent) 7%, transparent), transparent 32%), color-mix(in srgb, var(--ui-bg) 74%, var(--ui-panel)); } .network-block { --block-scale: 1; position: absolute; min-width: 240px; min-height: 110px; margin: 0; padding: 14px; border: 1px solid var(--ui-border) !important; border-radius: 14px !important; background: color-mix(in srgb, var(--ui-panel) 94%, var(--ui-accent)) !important; box-shadow: 0 8px 22px color-mix(in srgb, var(--ui-bg) 20%, transparent); overflow: auto; transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease; cursor: grab; touch-action: none; } .network-block:hover { border-color: color-mix(in srgb, var(--ui-accent) 48%, var(--ui-border)) !important; transform: translateY(-2px); } .network-block.is-selected { border-color: color-mix(in srgb, var(--ui-accent) 70%, var(--ui-border)) !important; box-shadow: 0 0 0 2px color-mix(in srgb, var(--ui-accent) 12%, transparent), 0 8px 22px color-mix(in srgb, var(--ui-bg) 20%, transparent); } @@ -2661,6 +2661,7 @@ HTML = """ function initConfigCanvas() { configCanvas = $('controlForm'); if (!configCanvas) return; + enableCanvasWheel(configCanvas, '.config-block'); configLayout = readConfigLayout(); configBlocks = Array.from(configCanvas.children).filter(node => node.matches('details[data-section]')); Object.entries(configLayout).forEach(([key, entry]) => { @@ -2714,6 +2715,20 @@ HTML = """ selectConfigBlock(selectedConfigBlock); $('configContentScale')?.addEventListener('input', event => applyConfigContentScale(event.target.value)); } + function enableCanvasWheel(canvas, blockSelector) { + canvas.addEventListener('wheel', event => { + if (event.defaultPrevented || event.target instanceof Element && event.target.closest(blockSelector)) return; + const maxLeft = Math.max(0, canvas.scrollWidth - canvas.clientWidth); + const maxTop = Math.max(0, canvas.scrollHeight - canvas.clientHeight); + if (!maxLeft && !maxTop) return; + const left = Math.max(0, Math.min(maxLeft, canvas.scrollLeft + event.deltaX)); + const top = Math.max(0, Math.min(maxTop, canvas.scrollTop + event.deltaY)); + if (left === canvas.scrollLeft && top === canvas.scrollTop) return; + canvas.scrollLeft = left; + canvas.scrollTop = top; + event.preventDefault(); + }, {passive: false}); + } let networkCanvas = null; let networkBlocks = []; let networkLayout = {}; @@ -3137,6 +3152,7 @@ HTML = """ function initNetworkCanvas() { networkCanvas = $('networkCanvas'); if (!networkCanvas) return; + enableCanvasWheel(networkCanvas, '.network-block'); networkLayout = readNetworkLayout(); networkBlocks = Array.from(networkCanvas.children).filter(node => node.classList.contains('network-block')); Object.entries(networkLayout).forEach(([key, entry]) => {