From 5af0cbf286b3a30d8a90500934109f3f4c4b618a Mon Sep 17 00:00:00 2001 From: dekun Date: Mon, 8 Jun 2026 08:35:56 +0800 Subject: [PATCH] Fix chart auto toggle to clearly split follow vs manual zoom. On tail refresh, auto-on always snaps to latest candles while auto-off preserves the saved viewport with clamped range restore. Co-authored-by: Cursor --- manual_trading_hub/static/chart.js | 64 ++++++++++++++-------------- manual_trading_hub/static/index.html | 2 +- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/manual_trading_hub/static/chart.js b/manual_trading_hub/static/chart.js index 5af58be..e25c13e 100644 --- a/manual_trading_hub/static/chart.js +++ b/manual_trading_hub/static/chart.js @@ -2026,12 +2026,6 @@ return true; } - function isViewingChartTail(range, candleCount) { - if (!range || candleCount <= 0) return true; - const maxTo = candleCount - 1 + RIGHT_OFFSET_BARS; - return range.to >= maxTo - 24; - } - function markChartRangeUserAdjusted() { chartRangeUserLocked = true; if (chartRangeLockTimer) clearTimeout(chartRangeLockTimer); @@ -2041,14 +2035,37 @@ }, 30000); } + function clampVisibleLogicalRange(range, candleCount) { + if (!range || candleCount <= 0) return null; + const maxTo = candleCount - 1 + RIGHT_OFFSET_BARS; + const from = Math.max(-2, Math.min(range.from, candleCount - 1)); + const to = Math.max(0, Math.min(range.to, maxTo + 8)); + if (to <= from) return null; + return { from: from, to: to }; + } + function restoreVisibleLogicalRange(range, candleCount) { - if (!chart || !range || !isVisibleRangeValidForCandles(range, candleCount)) return false; + const clamped = clampVisibleLogicalRange(range, candleCount); + if (!chart || !clamped || !isVisibleRangeValidForCandles(clamped, candleCount)) return false; suppressRangeUserLock = true; - chart.timeScale().setVisibleLogicalRange(range); + chart.timeScale().setVisibleLogicalRange(clamped); suppressRangeUserLock = false; return true; } + function applyPreservedVisibleRange(range, candleCount) { + if (!chart || !range || !candleCount) return; + function applyOnce() { + if (!chart || !lastCandles.length) return; + applyChartRightGap(); + restoreVisibleLogicalRange(range, lastCandles.length); + updateVisibleRangeMarkers(); + } + applyOnce(); + requestAnimationFrame(applyOnce); + setTimeout(applyOnce, 0); + } + function shouldLoadOlderOnRange(range) { if (!range || !lastCandles.length) return false; const n = lastCandles.length; @@ -2229,11 +2246,9 @@ if (!lastViewKey || vKey !== lastViewKey) return; const myToken = loadToken; const epochAtStart = chartViewEpoch; - const candleCountBefore = lastCandles.length; + const autoFollow = priceAutoScale; let savedRange = null; if (chart) savedRange = chart.timeScale().getVisibleLogicalRange(); - const wasViewingTail = - !savedRange || isViewingChartTail(savedRange, candleCountBefore); try { const data = await fetchChartChunk({ exchange_key: exKey, @@ -2255,37 +2270,20 @@ applyChartPriceFormat(); } } - const autoFollow = priceAutoScale; - const shouldPreserve = - savedRange && - isVisibleRangeValidForCandles(savedRange, candleCountBefore) && - (!autoFollow || chartRangeUserLocked || !wasViewingTail); applyCandlesToChart( mergeCandles(lastCandles, alignCandlesToTick(data.candles), { prepend: false }), 0, { - preserveRange: !!shouldPreserve, + preserveRange: false, skipAutoScale: !autoFollow, } ); if (epochAtStart !== chartViewEpoch) return; const n = lastCandles.length; - const minorTailUpdate = Math.abs(n - candleCountBefore) <= CHART_TAIL_REFRESH_LIMIT + 5; - if (!autoFollow) { - if (savedRange && isVisibleRangeValidForCandles(savedRange, n)) { - restoreVisibleLogicalRange(savedRange, n); - } - } else if (wasViewingTail && !chartRangeUserLocked) { - restoreVisibleLogicalRange(tailVisibleLogicalRange(n), n); - } else if (shouldPreserve && minorTailUpdate) { - if (!restoreVisibleLogicalRange(savedRange, n) && !chartRangeUserLocked) { - restoreVisibleLogicalRange(tailVisibleLogicalRange(n), n); - } - } else if (!restoreVisibleLogicalRange(savedRange, n) && !chartRangeUserLocked) { - const curRange = chart && chart.timeScale().getVisibleLogicalRange(); - if (!curRange || !isVisibleRangeValidForCandles(curRange, n)) { - restoreVisibleLogicalRange(tailVisibleLogicalRange(n), n); - } + if (autoFollow) { + applyDefaultVisibleRange(); + } else if (savedRange) { + applyPreservedVisibleRange(savedRange, n); } scheduleRangeUiUpdate(); if (posContext) { diff --git a/manual_trading_hub/static/index.html b/manual_trading_hub/static/index.html index e28a80b..7cf6efd 100644 --- a/manual_trading_hub/static/index.html +++ b/manual_trading_hub/static/index.html @@ -349,7 +349,7 @@
- +