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:
@@ -2026,12 +2026,6 @@
|
|||||||
return true;
|
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() {
|
function markChartRangeUserAdjusted() {
|
||||||
chartRangeUserLocked = true;
|
chartRangeUserLocked = true;
|
||||||
if (chartRangeLockTimer) clearTimeout(chartRangeLockTimer);
|
if (chartRangeLockTimer) clearTimeout(chartRangeLockTimer);
|
||||||
@@ -2041,14 +2035,37 @@
|
|||||||
}, 30000);
|
}, 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) {
|
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;
|
suppressRangeUserLock = true;
|
||||||
chart.timeScale().setVisibleLogicalRange(range);
|
chart.timeScale().setVisibleLogicalRange(clamped);
|
||||||
suppressRangeUserLock = false;
|
suppressRangeUserLock = false;
|
||||||
return true;
|
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) {
|
function shouldLoadOlderOnRange(range) {
|
||||||
if (!range || !lastCandles.length) return false;
|
if (!range || !lastCandles.length) return false;
|
||||||
const n = lastCandles.length;
|
const n = lastCandles.length;
|
||||||
@@ -2229,11 +2246,9 @@
|
|||||||
if (!lastViewKey || vKey !== lastViewKey) return;
|
if (!lastViewKey || vKey !== lastViewKey) return;
|
||||||
const myToken = loadToken;
|
const myToken = loadToken;
|
||||||
const epochAtStart = chartViewEpoch;
|
const epochAtStart = chartViewEpoch;
|
||||||
const candleCountBefore = lastCandles.length;
|
const autoFollow = priceAutoScale;
|
||||||
let savedRange = null;
|
let savedRange = null;
|
||||||
if (chart) savedRange = chart.timeScale().getVisibleLogicalRange();
|
if (chart) savedRange = chart.timeScale().getVisibleLogicalRange();
|
||||||
const wasViewingTail =
|
|
||||||
!savedRange || isViewingChartTail(savedRange, candleCountBefore);
|
|
||||||
try {
|
try {
|
||||||
const data = await fetchChartChunk({
|
const data = await fetchChartChunk({
|
||||||
exchange_key: exKey,
|
exchange_key: exKey,
|
||||||
@@ -2255,37 +2270,20 @@
|
|||||||
applyChartPriceFormat();
|
applyChartPriceFormat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const autoFollow = priceAutoScale;
|
|
||||||
const shouldPreserve =
|
|
||||||
savedRange &&
|
|
||||||
isVisibleRangeValidForCandles(savedRange, candleCountBefore) &&
|
|
||||||
(!autoFollow || chartRangeUserLocked || !wasViewingTail);
|
|
||||||
applyCandlesToChart(
|
applyCandlesToChart(
|
||||||
mergeCandles(lastCandles, alignCandlesToTick(data.candles), { prepend: false }),
|
mergeCandles(lastCandles, alignCandlesToTick(data.candles), { prepend: false }),
|
||||||
0,
|
0,
|
||||||
{
|
{
|
||||||
preserveRange: !!shouldPreserve,
|
preserveRange: false,
|
||||||
skipAutoScale: !autoFollow,
|
skipAutoScale: !autoFollow,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (epochAtStart !== chartViewEpoch) return;
|
if (epochAtStart !== chartViewEpoch) return;
|
||||||
const n = lastCandles.length;
|
const n = lastCandles.length;
|
||||||
const minorTailUpdate = Math.abs(n - candleCountBefore) <= CHART_TAIL_REFRESH_LIMIT + 5;
|
if (autoFollow) {
|
||||||
if (!autoFollow) {
|
applyDefaultVisibleRange();
|
||||||
if (savedRange && isVisibleRangeValidForCandles(savedRange, n)) {
|
} else if (savedRange) {
|
||||||
restoreVisibleLogicalRange(savedRange, n);
|
applyPreservedVisibleRange(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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
scheduleRangeUiUpdate();
|
scheduleRangeUiUpdate();
|
||||||
if (posContext) {
|
if (posContext) {
|
||||||
|
|||||||
@@ -349,7 +349,7 @@
|
|||||||
|
|
||||||
<div id="toast"></div>
|
<div id="toast"></div>
|
||||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
<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/archive.js?v=20260607-hub-archive-v6"></script>
|
||||||
<script src="/assets/ai_review_render.js?v=2"></script>
|
<script src="/assets/ai_review_render.js?v=2"></script>
|
||||||
<script src="/assets/app.js?v=20260607-hub-archive-v1"></script>
|
<script src="/assets/app.js?v=20260607-hub-archive-v1"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user