Skip to content

Commit 586b790

Browse files
committed
fix UI diff scrolling bug
1 parent c729379 commit 586b790

3 files changed

Lines changed: 30 additions & 6 deletions

File tree

ui/main.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,14 @@ button.busy .spinner-border {
302302
fill: rgba(58, 140, 154, 0.2);
303303
stroke: rgba(58, 140, 154, 0.5);
304304
}
305+
306+
// Let clicks pass through AceDiff's gutter SVG (connectors) so that
307+
// the overview minimap (.diff-overview) underneath remains clickable.
308+
// AceDiff recreates its gutter SVG asynchronously on scroll/resize,
309+
// which can push it on top of the minimap in the DOM.
310+
.acediff__gutter > svg:not(.diff-overview) {
311+
pointer-events: none;
312+
}
305313
}
306314

307315
/* History mode styles */

ui/modules/things/thingsDiff.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,17 @@ export function ThingsDiff(targetTab) {
187187
fetchAndDiff();
188188
viewDirty = false;
189189
}
190-
// Resize editors to fit the container
190+
// Resize editors to fit the container and re-render the overview
191+
// after AceDiff's async event handlers (scroll sync, resize) settle.
191192
if (diffInstance) {
192193
const editors = diffInstance.getEditors();
193194
editors.left.resize();
194195
editors.right.resize();
196+
setTimeout(() => {
197+
if (diffInstance) {
198+
renderChangeOverview(diffInstance.diffs);
199+
}
200+
}, 50);
195201
}
196202
syncTabs(tabLink);
197203
}
@@ -420,6 +426,13 @@ export function ThingsDiff(targetTab) {
420426

421427
const svgNS = 'http://www.w3.org/2000/svg';
422428

429+
// Ensure AceDiff's connector SVGs don't block minimap clicks.
430+
// AceDiff recreates its gutter SVG asynchronously (on scroll, resize),
431+
// so this must run every time the overview is (re-)rendered.
432+
gutterEl.querySelectorAll(':scope > svg:not(.diff-overview)').forEach(
433+
(svg: SVGElement) => { svg.style.pointerEvents = 'none'; }
434+
);
435+
423436
// Reuse existing SVG or create a new one
424437
if (!overviewSvg) {
425438
overviewSvg = document.createElementNS(svgNS, 'svg');
@@ -459,11 +472,11 @@ export function ThingsDiff(targetTab) {
459472

460473
rect.addEventListener('click', (e) => {
461474
e.stopPropagation();
462-
// Scroll both editors without animation. Animation caused a
463-
// bounce: each animated frame triggered AceDiff's proportional
464-
// scroll sync on the opposite editor, creating oscillation.
475+
// Only scroll the left editor — AceDiff's locked proportional
476+
// scroll sync will position the right editor automatically.
477+
// Scrolling both editors caused a race: AceDiff's throttled
478+
// (16ms) sync handlers would override one or both positions.
465479
editors.left.scrollToLine(diff.leftStartLine, true, false, () => {});
466-
editors.right.scrollToLine(diff.rightStartLine, true, false, () => {});
467480
});
468481
rect.addEventListener('mouseenter', () => {
469482
rect.style.fill = 'rgba(58, 140, 154, 0.9)';

ui/modules/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import * as ace from 'ace-builds/src-noconflict/ace';
1717
import { Modal, Toast } from 'bootstrap';
1818
import DOMPurify from 'dompurify';
1919

20+
// Disable Ace worker loading globally — the JSON worker would try to fetch
21+
// from a CDN, which fails in restricted environments. Workers are only used
22+
// for live syntax validation, which is not needed in our read-only editors.
23+
ace.config.setDefaultValue('session', 'useWorker', false);
2024

2125
const dom = {
2226
modalBodyConfirm: null,
@@ -370,7 +374,6 @@ export function confirm(message: string, action: string, callback) {
370374
export function createAceEditor(domId: string, sessionMode, readOnly = false, wrap = false) {
371375
const result = ace.edit(domId);
372376
result.setOption('wrap', wrap);
373-
result.setOption('useWorker', false);
374377
result.session.setMode(sessionMode);
375378
if (readOnly) {
376379
result.setReadOnly(true);

0 commit comments

Comments
 (0)