export class SeriesMarkersPrimitive { constructor(markers) { this._markers = markers || []; this._paneViews = [new MarkersPaneView(this)]; } setMarkers(markers) { this._markers = markers; if (this._requestUpdate) { this._requestUpdate(); } } attached(param) { this._chart = param.chart; this._series = param.series; this._requestUpdate = param.requestUpdate; this._requestUpdate(); } detached() { this._chart = undefined; this._series = undefined; this._requestUpdate = undefined; } updateAllViews() {} paneViews() { return this._paneViews; } } class MarkersPaneView { constructor(source) { this._source = source; } renderer() { return new MarkersRenderer(this._source); } } class MarkersRenderer { constructor(source) { this._source = source; } draw(target) { if (!this._source._chart || !this._source._series) return; // Lightweight Charts v5 wraps context const ctx = target.context; const series = this._source._series; const chart = this._source._chart; const markers = this._source._markers; ctx.save(); // Ensure markers are sorted by time (usually already done) for (const marker of markers) { const timeCoordinate = chart.timeScale().timeToCoordinate(marker.time); if (timeCoordinate === null) continue; // To position above or below bar, we need the candle data or we use the marker.value if provided // For true aboveBar/belowBar without candle data, we might just use series.priceToCoordinate on marker.value let price = marker.value; // Fallbacks if no value provided (which our calculator does provide) if (!price) continue; const priceCoordinate = series.priceToCoordinate(price); if (priceCoordinate === null) continue; const x = timeCoordinate; const size = 5; const margin = 12; // Gap between price and marker const isAbove = marker.position === 'aboveBar'; const y = isAbove ? priceCoordinate - margin : priceCoordinate + margin; ctx.fillStyle = marker.color || '#26a69a'; ctx.beginPath(); if (marker.shape === 'arrowUp' || (!marker.shape && !isAbove)) { ctx.moveTo(x, y - size); ctx.lineTo(x - size, y + size); ctx.lineTo(x + size, y + size); } else if (marker.shape === 'arrowDown' || (!marker.shape && isAbove)) { ctx.moveTo(x, y + size); ctx.lineTo(x - size, y - size); ctx.lineTo(x + size, y - size); } else if (marker.shape === 'circle') { ctx.arc(x, y, size, 0, Math.PI * 2); } else if (marker.shape === 'square') { ctx.rect(x - size, y - size, size * 2, size * 2); } else if (marker.shape === 'custom' && marker.text) { ctx.font = '12px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(marker.text, x, y); continue; } else { // Default triangle if (isAbove) { ctx.moveTo(x, y + size); ctx.lineTo(x - size, y - size); ctx.lineTo(x + size, y - size); } else { ctx.moveTo(x, y - size); ctx.lineTo(x - size, y + size); ctx.lineTo(x + size, y + size); } } ctx.fill(); } ctx.restore(); } }