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 <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-08 08:35:56 +08:00
parent bae78d8368
commit 5af0cbf286
2 changed files with 32 additions and 34 deletions
+31 -33
View File
@@ -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) {
+1 -1
View File
@@ -349,7 +349,7 @@
<div id="toast"></div>
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
<script src="/assets/chart.js?v=20260604-upnl-contracts"></script>
<script src="/assets/chart.js?v=20260608-auto-viewport"></script>
<script src="/assets/archive.js?v=20260607-hub-archive-v6"></script>
<script src="/assets/ai_review_render.js?v=2"></script>
<script src="/assets/app.js?v=20260607-hub-archive-v1"></script>