From c983ca6621ee6a98cb350d68545cbd266ce9bd21 Mon Sep 17 00:00:00 2001 From: xziino Date: Sun, 24 May 2026 08:12:09 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Planer:=20Gantt-Klick=20zum=20Hinzuf=C3=BCg?= =?UTF-8?q?en=20repariert=20(Pointer-Capture-Bug)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit setPointerCapture() leitet Compatibility Mouse Events (inkl. click) an das capturing Element um – e.target im click-Handler war immer .timeline-scroll, nie das angeklickte .timeline-track. Fix: document.elementFromPoint() für zuverlässigen Hit-Test unabhängig von Pointer Capture. Pan-Threshold zusätzlich von 3px auf 8px erhöht. Co-Authored-By: Claude Sonnet 4.6 --- js/planner.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/js/planner.js b/js/planner.js index 6d580a1..273e078 100644 --- a/js/planner.js +++ b/js/planner.js @@ -1242,7 +1242,7 @@ function initTimeline(planId) { timeline.addEventListener('pointermove', e => { if (!timelinePan || timelinePan.pointerId !== e.pointerId) return; const dx = e.clientX - timelinePan.startX; - if (Math.abs(dx) > 3) { + if (Math.abs(dx) > 8) { timelinePan.moved = true; timelinePan.scroll.classList.add('timeline-scroll--dragging'); timelinePan.scroll.scrollLeft = timelinePan.startScrollLeft - dx; @@ -1307,8 +1307,11 @@ function initTimeline(planId) { return; } - const track = e.target.closest('.timeline-player-row .timeline-track'); - const row = e.target.closest('.timeline-player-row'); + // setPointerCapture() leitet compatibility mouse events (inkl. click) an das + // capturing element um, daher e.target nicht verlässlich — echtes Element per Hit-Test: + const actualTarget = document.elementFromPoint(e.clientX, e.clientY); + const track = actualTarget?.closest('.timeline-player-row .timeline-track'); + const row = actualTarget?.closest('.timeline-player-row'); if (!track || !row) return; const plan = getPlan(planId); if (!plan) return; From be65d0b228581896b8b71deb2513415578649e23 Mon Sep 17 00:00:00 2001 From: xziino Date: Sun, 24 May 2026 08:17:22 +0200 Subject: [PATCH 2/3] Planer: Gantt-Overlap-Check auf vollen Cooldown erweitert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit assignmentWindowMs und candidateWindow nutzen jetzt max(duration, cooldown) statt nur die aktive Dauer – Abilities konnten bisher erneut platziert werden bevor ihr Recast abgelaufen war. Co-Authored-By: Claude Sonnet 4.6 --- js/planner.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/js/planner.js b/js/planner.js index 273e078..5db6c43 100644 --- a/js/planner.js +++ b/js/planner.js @@ -836,7 +836,9 @@ function layoutBossActions(mechanics, duration) { } function assignmentWindowMs(assignment) { - return Math.max(1, assignmentDurationSeconds(assignment)) * 1000; + const durationMs = Math.max(1, assignmentDurationSeconds(assignment)) * 1000; + const cooldownMs = assignmentCooldownSeconds(assignment) * 1000; + return Math.max(durationMs, cooldownMs); } function sameAssignmentRef(mechanic, assignment, ref) { @@ -847,7 +849,9 @@ function sameAssignmentRef(mechanic, assignment, ref) { } function assignmentOverlapsJob(plan, job, ability, timestamp, ignore = null, candidate = null) { - const candidateWindow = Math.max(candidate ? assignmentDurationSeconds(candidate) : 0, 1) * 1000; + const candidateDurationMs = Math.max(candidate ? assignmentDurationSeconds(candidate) : 0, 1) * 1000; + const candidateCooldownMs = candidate ? assignmentCooldownSeconds(candidate) * 1000 : 0; + const candidateWindow = Math.max(candidateDurationMs, candidateCooldownMs); const candidateStart = Math.max(0, timestamp); const candidateEnd = candidateStart + candidateWindow; const ignoredActivation = assignmentEntryForRef(plan, ignore); From 669bcd937bd19db1896fcf0bcde14ee44832a940 Mon Sep 17 00:00:00 2001 From: xziino Date: Sun, 24 May 2026 08:21:51 +0200 Subject: [PATCH 3/3] Planer: Scroll-Position im Gantt nach Re-Render beibehalten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refreshTimeline() speichert scrollLeft vor dem innerHTML-Reset und stellt ihn danach wieder her – die Ansicht springt beim Verschieben von Abilities nicht mehr an den Anfang. Co-Authored-By: Claude Sonnet 4.6 --- js/planner.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/js/planner.js b/js/planner.js index 5db6c43..7febf21 100644 --- a/js/planner.js +++ b/js/planner.js @@ -1038,7 +1038,12 @@ function refreshTimeline(planId) { if (normalizeActivationCopies(plan)) updatePlan(planId, { mechanics: plan.mechanics }); const timeline = document.getElementById('planner-timeline'); const settings = document.getElementById('timeline-settings'); - if (timeline) timeline.innerHTML = renderTimelineHtml(plan); + if (timeline) { + const savedScroll = timeline.querySelector('.timeline-scroll')?.scrollLeft ?? 0; + timeline.innerHTML = renderTimelineHtml(plan); + const newScroll = timeline.querySelector('.timeline-scroll'); + if (newScroll && savedScroll > 0) newScroll.scrollLeft = savedScroll; + } if (settings) settings.innerHTML = renderTimelineSettingsHtml(plan); }