diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index a824e2a..18a3ca4 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -35,6 +35,10 @@ export default function App() {
// WebSocket Data States (Phase 5)
const [domData, setDomData] = useState(null);
const [bigTrades, setBigTrades] = useState([]);
+
+ // Chart toolbar
+ const [showChartSettings, setShowChartSettings] = useState(false);
+ const [centerTrigger, setCenterTrigger] = useState(0);
// Toasts
const [toasts, setToasts] = useState([]);
@@ -263,6 +267,115 @@ export default function App() {
)}
+ {/* Chart Toolbar */}
+
+ {/* Align to current price */}
+
+
+
+
+ {/* Settings gear */}
+
+
+
+ {/* Floating Settings Panel */}
+ {showChartSettings && (
+
+
+ Configurações
+
+
+
+ {/* View Mode */}
+
+
MODO DE EXIBIÇÃO
+
+
+
+
+
+
+ {/* Price Step */}
+
+
PASSO DO PREÇO (ticks)
+
+
+ setStepMultiplier(Math.max(1, Number(e.target.value) || 1))}
+ className="flex-1 bg-transparent text-center text-sm font-bold text-white outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+ />
+
+
+
+
+ {/* Close Mode */}
+
+ MODO DE FECHAMENTO
+
+
+
+ {/* Delta Max */}
+ {closeMode === 'delta' && (
+
+
DELTA MÁX
+
+
+ { setDeltaMax(Math.max(100, Number(e.target.value) || 100)); setConfigDirty(true); }}
+ className="flex-1 bg-transparent text-center text-sm font-bold text-white outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+ />
+
+
+
+ )}
+
+ {/* Apply */}
+
+
+ )}
+
@@ -310,8 +424,14 @@ export default function App() {
PRICE STEP
-
-
{(stepMultiplier * dynamicTickSize).toFixed(2)} pts
+
+
setStepMultiplier(Math.max(1, Number(e.target.value) || 1))}
+ className="px-1 py-1 text-sm font-bold text-white min-w-[52px] text-center bg-transparent outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
+ />
diff --git a/frontend/src/FootprintCanvas.jsx b/frontend/src/FootprintCanvas.jsx
index 5ec39ea..0dcc267 100644
--- a/frontend/src/FootprintCanvas.jsx
+++ b/frontend/src/FootprintCanvas.jsx
@@ -1,6 +1,6 @@
import React, { useRef, useEffect, useState } from 'react';
-export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultiplier = 1, viewMode = 'bidask', imbalanceRatio = 300, onStepChange, bid = 0, ask = 0, domData = null, bigTrades = [] }) {
+export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultiplier = 1, viewMode = 'bidask', imbalanceRatio = 300, onStepChange, bid = 0, ask = 0, domData = null, bigTrades = [], centerTrigger = 0 }) {
const canvasRef = useRef(null);
// Navigation & Scale State
@@ -28,6 +28,13 @@ export default function FootprintCanvas({ clusters, tickSize = 1.0, stepMultipli
const rowHeight = 26 * zoomV; // height of each price cell (vertical only)
const axisWidth = 70; // width of the vertical price axis on the right
+ // Center on current price when triggered from toolbar
+ useEffect(() => {
+ if (centerTrigger > 0) {
+ setScrollOffset(prev => ({ ...prev, y: 0 }));
+ }
+ }, [centerTrigger]);
+
// Auto-scroll to keep latest cluster visible whenever clusters or horizontal zoom changes
const lastClusterCount = useRef(0);
useEffect(() => {