refresh Reset price scale
diff --git a/indicator_panel issue.PNG b/indicator_panel issue.PNG
new file mode 100644
index 0000000..f3ba9c2
Binary files /dev/null and b/indicator_panel issue.PNG differ
diff --git a/js/ui/chart.js b/js/ui/chart.js
index bda3a10..5dc4df5 100644
--- a/js/ui/chart.js
+++ b/js/ui/chart.js
@@ -169,6 +169,7 @@ constructor() {
this.avgPriceSeries = null;
this.dailyMAData = new Map(); // timestamp -> { ma44, ma125, price }
this.currentMouseTime = null;
+ this.drawingItems = []; // Store drawing items for management
// Throttled versions of heavy functions
this.throttledOnVisibleRangeChange = throttle(this.onVisibleRangeChange.bind(this), 150);
@@ -522,6 +523,9 @@ constructor() {
}
}
+ // Drawing Tools
+ this.initDrawingTools();
+
// Initialize state from storage
this.scaleState = {
autoScale: localStorage.getItem('winterfail_scale_auto') !== 'false',
@@ -579,6 +583,341 @@ constructor() {
});
}
+ initDrawingTools() {
+ const btnDrawingTools = document.getElementById('btnDrawingTools');
+ const drawingToolsPopup = document.getElementById('drawingToolsPopup');
+
+ if (btnDrawingTools && drawingToolsPopup) {
+ btnDrawingTools.addEventListener('click', (e) => {
+ e.stopPropagation();
+ drawingToolsPopup.classList.toggle('hidden');
+ });
+
+ document.addEventListener('click', closeDrawingToolsPopup);
+ document.addEventListener('touchstart', closeDrawingToolsPopup, { passive: true });
+
+ function closeDrawingToolsPopup(e) {
+ const isInside = drawingToolsPopup.contains(e.target) || e.target === btnDrawingTools;
+ const isDrawingButton = e.target.closest('#btnDrawingTools');
+
+ if (!isInside && !isDrawingButton) {
+ drawingToolsPopup.classList.add('hidden');
+ }
+ }
+ }
+
+ let isDrawing = false;
+ let drawMode = null;
+ let startPoint = null;
+ let drawLine = null;
+ let drawArrow = null;
+ let drawText = null;
+
+ window.activateDrawingTool = (tool) => {
+ if (tool === 'clear') {
+ this.clearDrawingTools();
+ return;
+ }
+
+ drawMode = tool;
+ isDrawing = true;
+
+ const chart = this.chart;
+ const container = chart.container();
+
+ const onMouseDown = (e) => {
+ e.preventDefault();
+ const rect = container.getBoundingClientRect();
+ const x = e.clientX - rect.left;
+ const y = e.clientY - rect.top;
+
+ if (drawMode === 'trend_line' || drawMode === 'horizontal_line' || drawMode === 'vertical_line') {
+ this.createLine(x, y);
+ } else if (drawMode === 'arrow_up' || drawMode === 'arrow_down') {
+ this.createArrow(drawMode === 'arrow_up' ? 'arrowUp' : 'arrowDown', x, y);
+ } else if (drawMode === 'text') {
+ this.createText(x, y);
+ }
+ };
+
+ const onTouchStart = (e) => {
+ e.preventDefault();
+ const touch = e.touches[0];
+ const rect = container.getBoundingClientRect();
+ const x = touch.clientX - rect.left;
+ const y = touch.clientY - rect.top;
+
+ if (drawMode === 'trend_line' || drawMode === 'horizontal_line' || drawMode === 'vertical_line') {
+ this.createLine(x, y);
+ } else if (drawMode === 'arrow_up' || drawMode === 'arrow_down') {
+ this.createArrow(drawMode === 'arrow_up' ? 'arrowUp' : 'arrowDown', x, y);
+ } else if (drawMode === 'text') {
+ this.createText(x, y);
+ }
+ };
+
+ container.addEventListener('mousedown', onMouseDown);
+ container.addEventListener('touchstart', onTouchStart);
+
+ const closeDrawing = () => {
+ container.removeEventListener('mousedown', onMouseDown);
+ container.removeEventListener('touchstart', onTouchStart);
+ document.removeEventListener('click', closeDrawing);
+ document.removeEventListener('touchstart', closeDrawing);
+ };
+
+ document.addEventListener('click', closeDrawing);
+ document.addEventListener('touchstart', closeDrawing);
+
+ drawingToolsPopup.classList.add('hidden');
+ };
+ }
+
+ createLine(x, y) {
+ const chart = this.chart;
+ const candleSeries = this.candleSeries;
+
+ let lineData = {
+ time: [],
+ value: []
+ };
+
+ const line = chart.addLineSeries({
+ color: '#2962ff',
+ lineWidth: 2,
+ lineStyle: LightweightCharts.LineStyle.Solid,
+ crosshairMarkerVisible: true,
+ crosshairMarkerRadius: 4,
+ baseIndex: 0,
+ });
+
+ let points = [];
+
+ const container = chart.container();
+ let startTime = null;
+ let startPrice = null;
+
+ const onMouseDown = (e) => {
+ e.preventDefault();
+ const rect = container.getBoundingClientRect();
+ const clientX = e.clientX;
+ const clientY = e.clientY;
+
+ const timeCoord = chart.timeScale().coordinateToTime(x);
+ const price = chart.priceScale().coordinateToPrice(y);
+
+ if (startTime === null) {
+ startTime = timeCoord;
+ startPrice = price;
+
+ line.setData([{
+ time: startTime,
+ value: startPrice
+ }]);
+ } else {
+ line.setData([
+ { time: startTime, value: startPrice },
+ { time: timeCoord, value: price }
+ ]);
+ startTime = null;
+ startPrice = null;
+ }
+ };
+
+ const onTouchStart = (e) => {
+ e.preventDefault();
+ const touch = e.touches[0];
+ const rect = container.getBoundingClientRect();
+ const clientX = touch.clientX;
+ const clientY = touch.clientY;
+
+ const timeCoord = chart.timeScale().coordinateToTime(x);
+ const price = chart.priceScale().coordinateToPrice(y);
+
+ if (startTime === null) {
+ startTime = timeCoord;
+ startPrice = price;
+
+ line.setData([{
+ time: startTime,
+ value: startPrice
+ }]);
+ } else {
+ line.setData([
+ { time: startTime, value: startPrice },
+ { time: timeCoord, value: price }
+ ]);
+ startTime = null;
+ startPrice = null;
+ }
+ };
+
+ container.addEventListener('mousedown', onMouseDown);
+ container.addEventListener('touchstart', onTouchStart);
+
+ const cleanup = () => {
+ container.removeEventListener('mousedown', onMouseDown);
+ container.removeEventListener('touchstart', onTouchStart);
+ };
+
+ document.addEventListener('click', cleanup);
+ document.addEventListener('touchstart', cleanup);
+ }
+
+ createArrow(arrowType, x, y) {
+ const chart = this.chart;
+ const container = chart.container();
+
+ const markerPrimitive = new SeriesMarkersPrimitive();
+ this.candleSeries.attachPrimitive(markerPrimitive);
+
+ const rect = container.getBoundingClientRect();
+ const time = chart.timeScale().coordinateToTime(x);
+ const price = chart.priceScale().coordinateToPrice(y);
+
+ const marker = {
+ time: time,
+ position: arrowType === 'arrowUp' ? 'belowBar' : 'aboveBar',
+ color: arrowType === 'arrowUp' ? '#26a69a' : '#ef5350',
+ shape: arrowType === 'arrowUp' ? 'arrowUp' : 'arrowDown',
+ text: ''
+ };
+
+ markerPrimitive.setMarkers([marker]);
+
+ let isDragging = false;
+ let startX = 0;
+ let startY = 0;
+ let startTime = time;
+ let startPrice = price;
+
+ const onMouseDown = (e) => {
+ e.preventDefault();
+ isDragging = true;
+ startX = e.clientX;
+ startY = e.clientY;
+ };
+
+ const onMouseMove = (e) => {
+ if (!isDragging) return;
+
+ const dx = e.clientX - startX;
+ const dy = e.clientY - startY;
+ const rect = container.getBoundingClientRect();
+
+ const timeCoord = chart.timeScale().coordinateToTime(x + dx);
+ const newPrice = chart.priceScale().coordinateToPrice(y + dy);
+
+ const newMarker = {
+ time: timeCoord,
+ position: arrowType === 'arrowUp' ? 'belowBar' : 'aboveBar',
+ color: arrowType === 'arrowUp' ? '#26a69a' : '#ef5350',
+ shape: arrowType === 'arrowUp' ? 'arrowUp' : 'arrowDown',
+ text: ''
+ };
+
+ markerPrimitive.setMarkers([newMarker]);
+ };
+
+ const onMouseUp = () => {
+ isDragging = false;
+ };
+
+ container.addEventListener('mousedown', onMouseDown);
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+
+ const cleanup = () => {
+ container.removeEventListener('mousedown', onMouseDown);
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ document.removeEventListener('click', cleanup);
+ document.removeEventListener('touchstart', cleanup);
+ };
+
+ document.addEventListener('click', cleanup);
+ document.addEventListener('touchstart', cleanup);
+ }
+
+ createText(x, y) {
+ const chart = this.chart;
+ const container = chart.container();
+
+ const text = prompt('Enter text label:', '');
+ if (!text) return;
+
+ const rect = container.getBoundingClientRect();
+ const time = chart.timeScale().coordinateToTime(x);
+ const price = chart.priceScale().coordinateToPrice(y);
+
+ const label = chart.addLabel({
+ text: text,
+ color: '#ffffff',
+ fontSize: 11,
+ fontFamily: 'Inter',
+ fontWeight: 'bold',
+ crosshairMarkerVisible: false,
+ time: time,
+ price: price
+ });
+
+ let isDragging = false;
+ let startX = 0;
+ let startY = 0;
+ let startTime = time;
+ let startPrice = price;
+
+ const onMouseDown = (e) => {
+ e.preventDefault();
+ isDragging = true;
+ startX = e.clientX;
+ startY = e.clientY;
+ };
+
+ const onMouseMove = (e) => {
+ if (!isDragging) return;
+
+ const dx = e.clientX - startX;
+ const dy = e.clientY - startY;
+
+ const newTime = chart.timeScale().coordinateToTime(x + dx);
+ const newPrice = chart.priceScale().coordinateToPrice(y + dy);
+
+ label.applyOptions({
+ time: newTime,
+ price: newPrice
+ });
+ };
+
+ const onMouseUp = () => {
+ isDragging = false;
+ };
+
+ container.addEventListener('mousedown', onMouseDown);
+ document.addEventListener('mousemove', onMouseMove);
+ document.addEventListener('mouseup', onMouseUp);
+
+ const cleanup = () => {
+ container.removeEventListener('mousedown', onMouseDown);
+ document.removeEventListener('mousemove', onMouseMove);
+ document.removeEventListener('mouseup', onMouseUp);
+ document.removeEventListener('click', cleanup);
+ document.removeEventListener('touchstart', cleanup);
+ };
+
+ document.addEventListener('click', cleanup);
+ document.addEventListener('touchstart', cleanup);
+ }
+
+ clearDrawingTools() {
+ const allSeries = this.chart.series();
+ allSeries.forEach(series => {
+ if (series.type !== 'Candlestick' && series.type !== 'Line' && series.type !== 'Area') {
+ this.chart.removeSeries(series);
+ }
+ });
+ }
+
initNavigationControls() {
const chartWrapper = document.getElementById('chartWrapper');
const navLeft = document.getElementById('navLeft');
diff --git a/test.json b/test.json
new file mode 100644
index 0000000..676993b
--- /dev/null
+++ b/test.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://opencode.ai/theme.json",
+ "theme": {
+ "background": "#09141B",
+ "text": "#DEB88D",
+ "textMuted": "#17384C",
+ "border": "#17384C",
+ "borderActive": "#1BBCDD",
+ "primary": "#FCA02F",
+ "secondary": "#027C9B",
+ "accent": "#68D4F1",
+ "error": "#D15123",
+ "warning": "#FDD39F",
+ "success": "#50A3B5",
+ "info": "#1E4950",
+ "backgroundPanel": "#17384C",
+ "backgroundElement": "#1E4950",
+ "diffAdded": "#628D98",
+ "diffRemoved": "#D48678",
+ "diffContext": "#17384C",
+ "diffHunkHeader": "#434B53",
+ "diffHighlightAdded": "#1BBCDD",
+ "diffHighlightRemoved": "#D48678",
+ "diffAddedBg": "#09141B",
+ "diffRemovedBg": "#09141B"
+ }
+}