viainti-chart 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -138,8 +138,20 @@ function BsArrowRepeat (props) {
138
138
  return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8"},"child":[]}]})(props);
139
139
  }function BsDiagram3 (props) {
140
140
  return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"fillRule":"evenodd","d":"M6 3.5A1.5 1.5 0 0 1 7.5 2h1A1.5 1.5 0 0 1 10 3.5v1A1.5 1.5 0 0 1 8.5 6v1H14a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0V8h-5v.5a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 2 7h5.5V6A1.5 1.5 0 0 1 6 4.5zM8.5 5a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5zM0 11.5A1.5 1.5 0 0 1 1.5 10h1A1.5 1.5 0 0 1 4 11.5v1A1.5 1.5 0 0 1 2.5 14h-1A1.5 1.5 0 0 1 0 12.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm4.5.5A1.5 1.5 0 0 1 7.5 10h1a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 8.5 14h-1A1.5 1.5 0 0 1 6 12.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm4.5.5a1.5 1.5 0 0 1 1.5-1.5h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z"},"child":[]}]})(props);
141
+ }function BsEmojiAngry (props) {
142
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M4.285 12.433a.5.5 0 0 0 .683-.183A3.5 3.5 0 0 1 8 10.5c1.295 0 2.426.703 3.032 1.75a.5.5 0 0 0 .866-.5A4.5 4.5 0 0 0 8 9.5a4.5 4.5 0 0 0-3.898 2.25.5.5 0 0 0 .183.683m6.991-8.38a.5.5 0 1 1 .448.894l-1.009.504c.176.27.285.64.285 1.049 0 .828-.448 1.5-1 1.5s-1-.672-1-1.5c0-.247.04-.48.11-.686a.502.502 0 0 1 .166-.761zm-6.552 0a.5.5 0 0 0-.448.894l1.009.504A1.94 1.94 0 0 0 5 6.5C5 7.328 5.448 8 6 8s1-.672 1-1.5c0-.247-.04-.48-.11-.686a.502.502 0 0 0-.166-.761z"},"child":[]}]})(props);
143
+ }function BsEmojiDizzy (props) {
144
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M9.146 5.146a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708.708l-.647.646.647.646a.5.5 0 0 1-.708.708l-.646-.647-.646.647a.5.5 0 1 1-.708-.708l.647-.646-.647-.646a.5.5 0 0 1 0-.708m-5 0a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 1 1 .708.708l-.647.646.647.646a.5.5 0 1 1-.708.708L5.5 7.207l-.646.647a.5.5 0 1 1-.708-.708l.647-.646-.647-.646a.5.5 0 0 1 0-.708M10 11a2 2 0 1 1-4 0 2 2 0 0 1 4 0"},"child":[]}]})(props);
145
+ }function BsEmojiHeartEyes (props) {
146
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M11.315 10.014a.5.5 0 0 1 .548.736A4.5 4.5 0 0 1 7.965 13a4.5 4.5 0 0 1-3.898-2.25.5.5 0 0 1 .548-.736h.005l.017.005.067.015.252.055c.215.046.515.108.857.169.693.124 1.522.242 2.152.242s1.46-.118 2.152-.242a27 27 0 0 0 1.109-.224l.067-.015.017-.004.005-.002zM4.756 4.566c.763-1.424 4.02-.12.952 3.434-4.496-1.596-2.35-4.298-.952-3.434m6.488 0c1.398-.864 3.544 1.838-.952 3.434-3.067-3.554.19-4.858.952-3.434"},"child":[]}]})(props);
147
+ }function BsEmojiLaughing (props) {
148
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M12.331 9.5a1 1 0 0 1 0 1A5 5 0 0 1 8 13a5 5 0 0 1-4.33-2.5A1 1 0 0 1 4.535 9h6.93a1 1 0 0 1 .866.5M7 6.5c0 .828-.448 0-1 0s-1 .828-1 0S5.448 5 6 5s1 .672 1 1.5m4 0c0 .828-.448 0-1 0s-1 .828-1 0S9.448 5 10 5s1 .672 1 1.5"},"child":[]}]})(props);
149
+ }function BsEmojiNeutral (props) {
150
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M4 10.5a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 0-1h-7a.5.5 0 0 0-.5.5m3-4C7 5.672 6.552 5 6 5s-1 .672-1 1.5S5.448 8 6 8s1-.672 1-1.5m4 0c0-.828-.448-1.5-1-1.5s-1 .672-1 1.5S9.448 8 10 8s1-.672 1-1.5"},"child":[]}]})(props);
141
151
  }function BsEmojiSmile (props) {
142
152
  return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"},"child":[]},{"tag":"path","attr":{"d":"M4.285 9.567a.5.5 0 0 1 .683.183A3.5 3.5 0 0 0 8 11.5a3.5 3.5 0 0 0 3.032-1.75.5.5 0 1 1 .866.5A4.5 4.5 0 0 1 8 12.5a4.5 4.5 0 0 1-3.898-2.25.5.5 0 0 1 .183-.683M7 6.5C7 7.328 6.552 8 6 8s-1-.672-1-1.5S5.448 5 6 5s1 .672 1 1.5m4 0c0 .828-.448 1.5-1 1.5s-1-.672-1-1.5S9.448 5 10 5s1 .672 1 1.5"},"child":[]}]})(props);
153
+ }function BsEmojiSunglasses (props) {
154
+ return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M4.968 9.75a.5.5 0 1 0-.866.5A4.5 4.5 0 0 0 8 12.5a4.5 4.5 0 0 0 3.898-2.25.5.5 0 1 0-.866-.5A3.5 3.5 0 0 1 8 11.5a3.5 3.5 0 0 1-3.032-1.75M7 5.116V5a1 1 0 0 0-1-1H3.28a1 1 0 0 0-.97 1.243l.311 1.242A2 2 0 0 0 4.561 8H5a2 2 0 0 0 1.994-1.839A3 3 0 0 1 8 6c.393 0 .74.064 1.006.161A2 2 0 0 0 11 8h.438a2 2 0 0 0 1.94-1.515l.311-1.242A1 1 0 0 0 12.72 4H10a1 1 0 0 0-1 1v.116A4.2 4.2 0 0 0 8 5c-.35 0-.69.04-1 .116"},"child":[]},{"tag":"path","attr":{"d":"M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0m-1 0A7 7 0 1 0 1 8a7 7 0 0 0 14 0"},"child":[]}]})(props);
143
155
  }function BsEyeSlash (props) {
144
156
  return GenIcon({"attr":{"fill":"currentColor","viewBox":"0 0 16 16"},"child":[{"tag":"path","attr":{"d":"M13.359 11.238C15.06 9.72 16 8 16 8s-3-5.5-8-5.5a7 7 0 0 0-2.79.588l.77.771A6 6 0 0 1 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13 13 0 0 1 14.828 8q-.086.13-.195.288c-.335.48-.83 1.12-1.465 1.755q-.247.248-.517.486z"},"child":[]},{"tag":"path","attr":{"d":"M11.297 9.176a3.5 3.5 0 0 0-4.474-4.474l.823.823a2.5 2.5 0 0 1 2.829 2.829zm-2.943 1.299.822.822a3.5 3.5 0 0 1-4.474-4.474l.823.823a2.5 2.5 0 0 0 2.829 2.829"},"child":[]},{"tag":"path","attr":{"d":"M3.35 5.47q-.27.24-.518.487A13 13 0 0 0 1.172 8l.195.288c.335.48.83 1.12 1.465 1.755C4.121 11.332 5.881 12.5 8 12.5c.716 0 1.39-.133 2.02-.36l.77.772A7 7 0 0 1 8 13.5C3 13.5 0 8 0 8s.939-1.721 2.641-3.238l.708.709zm10.296 8.884-12-12 .708-.708 12 12z"},"child":[]}]})(props);
145
157
  }function BsGear (props) {
@@ -841,7 +853,7 @@ function createStore(creator) {
841
853
  const useChartStore = createStore((set) => ({
842
854
  data: [],
843
855
  visibleData: [],
844
- timeframe: '1h',
856
+ timeframe: '12h',
845
857
  zoomLevel: 1,
846
858
  panOffset: 0,
847
859
  indicators: [],
@@ -920,7 +932,6 @@ const useChartStore = createStore((set) => ({
920
932
  toggleDrawingsHidden: () => set((state) => ({ drawingsHidden: !state.drawingsHidden }))
921
933
  }));
922
934
 
923
- const EMOJIS = ['⭐', '🔥', '🚀', '✅', '❌', '💎', '🐂', '🐻', '📈', '📉'];
924
935
  const cursorOptions = [
925
936
  { type: 'cross', icon: React.createElement(BsCursor, null), label: 'Cross' },
926
937
  { type: 'dot', icon: React.createElement("div", { style: { width: '6px', height: '6px', borderRadius: '50%', background: 'currentColor' } }), label: 'Dot' },
@@ -960,6 +971,15 @@ const measurementOptions = [
960
971
  { tool: 'fibonacci', icon: React.createElement(BsGraphUp, null), label: 'Fibonacci' },
961
972
  { tool: 'ruler', icon: React.createElement(BsGraphDown, null), label: 'Regla' }
962
973
  ];
974
+ const emojiOptions = [
975
+ { label: 'Smile', icon: React.createElement(BsEmojiSmile, null), value: '🙂' },
976
+ { label: 'Laugh', icon: React.createElement(BsEmojiLaughing, null), value: '😂' },
977
+ { label: 'Heart Eyes', icon: React.createElement(BsEmojiHeartEyes, null), value: '😍' },
978
+ { label: 'Sunglasses', icon: React.createElement(BsEmojiSunglasses, null), value: '😎' },
979
+ { label: 'Dizzy', icon: React.createElement(BsEmojiDizzy, null), value: '😵' },
980
+ { label: 'Neutral', icon: React.createElement(BsEmojiNeutral, null), value: '😐' },
981
+ { label: 'Angry', icon: React.createElement(BsEmojiAngry, null), value: '😠' }
982
+ ];
963
983
  const popoverStyle = {
964
984
  position: 'absolute',
965
985
  left: '64px',
@@ -978,6 +998,15 @@ const DrawingToolbar = () => {
978
998
  const { activeTool, cursorType, strokeColor, strokeWidth, setActiveTool, setCursorType, setStrokeColor, setStrokeWidth, clearDrawings, setSelectedEmoji, setIsDrawing } = useChartStore();
979
999
  const [openMenu, setOpenMenu] = useState(null);
980
1000
  const [showEmojiPicker, setShowEmojiPicker] = useState(false);
1001
+ const [isCompact, setIsCompact] = useState(false);
1002
+ useEffect(() => {
1003
+ const handleResize = () => {
1004
+ setIsCompact(window.innerWidth < 768);
1005
+ };
1006
+ handleResize();
1007
+ window.addEventListener('resize', handleResize);
1008
+ return () => window.removeEventListener('resize', handleResize);
1009
+ }, []);
981
1010
  const LINE_COLORS = ['#8ab4ff', '#f472b6', '#34d399', '#facc15', '#f87171', '#e2e8f0'];
982
1011
  const WIDTH_OPTIONS = [1, 1.5, 2.5, 3.5];
983
1012
  const handleSelectTool = (tool) => {
@@ -1073,14 +1102,27 @@ const DrawingToolbar = () => {
1073
1102
  title: 'Limpiar todo'
1074
1103
  }
1075
1104
  ];
1076
- return (React.createElement("div", { style: { position: 'relative', height: '100%' } },
1105
+ const filteredButtons = isCompact
1106
+ ? buttonConfig.filter(btn => !['pitchforks', 'measurements'].includes(btn.key))
1107
+ : buttonConfig;
1108
+ const buildPopoverStyle = (custom) => ({
1109
+ ...popoverStyle,
1110
+ ...(isCompact
1111
+ ? { left: '0px', top: 'calc(100% + 12px)', transform: 'none', width: 'min(360px, 92vw)' }
1112
+ : {}),
1113
+ ...custom
1114
+ });
1115
+ return (React.createElement("div", { style: { position: 'relative', height: '100%', width: '100%' } },
1077
1116
  React.createElement("div", { style: {
1078
- height: '100%',
1117
+ height: isCompact ? 'auto' : '100%',
1118
+ width: '100%',
1079
1119
  display: 'flex',
1080
- flexDirection: 'column',
1120
+ flexDirection: isCompact ? 'row' : 'column',
1081
1121
  alignItems: 'center',
1082
- gap: '12px'
1083
- } }, buttonConfig.map((btn) => (React.createElement("div", { key: btn.key, style: { position: 'relative' } },
1122
+ gap: '12px',
1123
+ overflowX: isCompact ? 'auto' : 'visible',
1124
+ padding: isCompact ? '12px 6px' : 0
1125
+ } }, filteredButtons.map((btn) => (React.createElement("div", { key: btn.key, style: { position: 'relative' } },
1084
1126
  React.createElement("button", { onClick: btn.onClick, title: btn.title, "aria-label": btn.title, style: {
1085
1127
  width: '46px',
1086
1128
  height: '46px',
@@ -1095,7 +1137,7 @@ const DrawingToolbar = () => {
1095
1137
  fontSize: '18px',
1096
1138
  transition: 'all 0.2s'
1097
1139
  } }, btn.icon),
1098
- btn.key === 'cursor' && openMenu === 'cursor' && (React.createElement("div", { style: { ...popoverStyle, gridTemplateColumns: 'repeat(2, 1fr)', width: '200px' } }, cursorOptions.map(option => (React.createElement("button", { key: option.type, onClick: () => handleCursorSelect(option.type), style: {
1140
+ btn.key === 'cursor' && openMenu === 'cursor' && (React.createElement("div", { style: buildPopoverStyle({ gridTemplateColumns: 'repeat(2, 1fr)', width: isCompact ? 'min(320px, 92vw)' : '200px' }) }, cursorOptions.map(option => (React.createElement("button", { key: option.type, onClick: () => handleCursorSelect(option.type), style: {
1099
1141
  width: '84px',
1100
1142
  height: '60px',
1101
1143
  borderRadius: '12px',
@@ -1111,7 +1153,7 @@ const DrawingToolbar = () => {
1111
1153
  }, title: option.label },
1112
1154
  React.createElement("span", { style: { fontSize: '18px', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, option.icon),
1113
1155
  React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase' } }, option.label)))))),
1114
- btn.key === 'lines' && openMenu === 'lines' && (React.createElement("div", { style: { ...popoverStyle, width: '280px' } },
1156
+ btn.key === 'lines' && openMenu === 'lines' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(360px, 92vw)' : '280px' }) },
1115
1157
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Lines"),
1116
1158
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '8px' } }, lineOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1117
1159
  borderRadius: '12px',
@@ -1151,7 +1193,7 @@ const DrawingToolbar = () => {
1151
1193
  } },
1152
1194
  width,
1153
1195
  "px"))))))),
1154
- btn.key === 'shapes' && openMenu === 'shapes' && (React.createElement("div", { style: { ...popoverStyle, width: '200px' } },
1196
+ btn.key === 'shapes' && openMenu === 'shapes' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '200px' }) },
1155
1197
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Shapes"),
1156
1198
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '8px' } }, shapeOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1157
1199
  borderRadius: '12px',
@@ -1168,7 +1210,7 @@ const DrawingToolbar = () => {
1168
1210
  }, title: option.label },
1169
1211
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1170
1212
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em' } }, option.label))))))),
1171
- btn.key === 'channels' && openMenu === 'channels' && (React.createElement("div", { style: { ...popoverStyle, width: '220px' } },
1213
+ btn.key === 'channels' && openMenu === 'channels' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(340px, 92vw)' : '220px' }) },
1172
1214
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Channels"),
1173
1215
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '8px' } }, channelOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1174
1216
  borderRadius: '12px',
@@ -1185,7 +1227,7 @@ const DrawingToolbar = () => {
1185
1227
  }, title: option.label },
1186
1228
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1187
1229
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em' } }, option.label))))))),
1188
- btn.key === 'pitchforks' && openMenu === 'pitchforks' && (React.createElement("div", { style: { ...popoverStyle, width: '200px' } },
1230
+ btn.key === 'pitchforks' && openMenu === 'pitchforks' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '200px' }) },
1189
1231
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Pitchforks"),
1190
1232
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '8px' } }, pitchforkOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1191
1233
  borderRadius: '12px',
@@ -1202,7 +1244,7 @@ const DrawingToolbar = () => {
1202
1244
  }, title: option.label },
1203
1245
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1204
1246
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em', textAlign: 'center' } }, option.label))))))),
1205
- btn.key === 'measurements' && openMenu === 'measurements' && (React.createElement("div", { style: { ...popoverStyle, gridTemplateColumns: 'repeat(2, 1fr)', width: '160px' } }, measurementOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1247
+ btn.key === 'measurements' && openMenu === 'measurements' && (React.createElement("div", { style: buildPopoverStyle({ gridTemplateColumns: 'repeat(2, 1fr)', width: isCompact ? 'min(280px, 80vw)' : '160px' }) }, measurementOptions.map(option => (React.createElement("button", { key: option.tool, onClick: () => handleSelectTool(option.tool), style: {
1206
1248
  width: '64px',
1207
1249
  height: '54px',
1208
1250
  borderRadius: '12px',
@@ -1219,41 +1261,24 @@ const DrawingToolbar = () => {
1219
1261
  }, title: option.label },
1220
1262
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1221
1263
  React.createElement("span", null, option.label)))))),
1222
- btn.key === 'emoji' && showEmojiPicker && (React.createElement("div", { style: {
1223
- position: 'absolute',
1224
- left: '64px',
1225
- top: '50%',
1226
- transform: 'translateY(-50%)',
1227
- zIndex: 1100,
1228
- background: '#020617',
1229
- borderRadius: '14px',
1230
- overflow: 'hidden',
1231
- boxShadow: '0 18px 40px rgba(0,0,0,0.65)',
1232
- padding: '10px',
1233
- border: '1px solid #1f2937'
1234
- } },
1235
- React.createElement("div", { style: {
1236
- display: 'grid',
1237
- gridTemplateColumns: 'repeat(3, 1fr)',
1238
- gap: '6px'
1239
- } }, EMOJIS.map((emoji) => (React.createElement("button", { key: emoji, onClick: () => {
1240
- setSelectedEmoji(emoji);
1264
+ btn.key === 'emoji' && showEmojiPicker && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '220px' }) },
1265
+ React.createElement("p", { style: { margin: '0 0 6px', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Emojis"),
1266
+ React.createElement("div", { style: { display: 'grid', gridTemplateColumns: isCompact ? 'repeat(4, minmax(0, 1fr))' : 'repeat(3, minmax(0, 1fr))', gap: '8px' } }, emojiOptions.map(option => (React.createElement("button", { key: option.label, onClick: () => {
1267
+ setSelectedEmoji(option.value);
1241
1268
  setActiveTool('icon');
1242
1269
  setShowEmojiPicker(false);
1243
1270
  }, style: {
1244
- width: '40px',
1245
- height: '40px',
1246
- background: '#111827',
1271
+ height: '48px',
1272
+ borderRadius: '12px',
1247
1273
  border: '1px solid #1f2937',
1248
- borderRadius: '10px',
1249
- cursor: 'pointer',
1274
+ background: 'rgba(2,6,23,0.65)',
1275
+ color: '#f8fafc',
1250
1276
  display: 'flex',
1251
1277
  alignItems: 'center',
1252
1278
  justifyContent: 'center',
1253
- fontSize: '18px',
1254
- color: '#f8fafc'
1255
- } },
1256
- React.createElement("span", null, emoji)))))))))))));
1279
+ fontSize: '22px',
1280
+ cursor: 'pointer'
1281
+ }, title: option.label, "aria-label": option.label }, option.icon))))))))))));
1257
1282
  };
1258
1283
 
1259
1284
  const drawRoundedRect = (ctx, x, y, width, height, radius = 8) => {
@@ -1990,23 +2015,23 @@ const LINE_TOOL_TYPES = [
1990
2015
  const LINE_TOOL_SET = new Set(LINE_TOOL_TYPES);
1991
2016
  const THEME_PRESETS = {
1992
2017
  dark: {
1993
- pageBg: '#050910',
1994
- heroFrom: '#111827',
1995
- heroTo: '#0b1120',
1996
- panelBg: 'rgba(15,23,42,0.9)',
1997
- panelBorder: '#1f2937',
1998
- cardBg: 'rgba(15,23,42,0.85)',
1999
- cardBorder: '#111827',
2018
+ pageBg: '#000000',
2019
+ heroFrom: '#050505',
2020
+ heroTo: '#000000',
2021
+ panelBg: 'rgba(0,0,0,0.92)',
2022
+ panelBorder: '#0f0f0f',
2023
+ cardBg: 'rgba(0,0,0,0.88)',
2024
+ cardBorder: '#050505',
2000
2025
  textPrimary: '#f8fafc',
2001
- textSecondary: '#94a3b8',
2026
+ textSecondary: '#9ca3af',
2002
2027
  accent: '#2563eb',
2003
- accentSoft: 'rgba(37,99,235,0.2)',
2004
- railBg: 'rgba(2,6,23,0.85)',
2005
- railBorder: '#111827',
2006
- plotBg: '#020617',
2007
- plotBorder: '#111827',
2008
- scaleBg: 'rgba(15,23,42,0.9)',
2009
- overlayBg: 'rgba(5,7,15,0.85)'
2028
+ accentSoft: 'rgba(37,99,235,0.25)',
2029
+ railBg: 'rgba(0,0,0,0.9)',
2030
+ railBorder: '#080808',
2031
+ plotBg: '#010101',
2032
+ plotBorder: '#0d0d0d',
2033
+ scaleBg: 'rgba(0,0,0,0.92)',
2034
+ overlayBg: 'rgba(0,0,0,0.85)'
2010
2035
  },
2011
2036
  blue: {
2012
2037
  pageBg: '#060e1f',
@@ -2058,6 +2083,10 @@ const CUSTOM_THEME_FIELDS = [
2058
2083
  const MIN_CANDLE_PX = 4;
2059
2084
  const MAX_CANDLE_PX = 28;
2060
2085
  const BUFFER_FRACTION = 0.18;
2086
+ const LEFT_OFFSET_BARS = 2;
2087
+ const RIGHT_OFFSET_BARS = 18;
2088
+ const BAR_BODY_RATIO = 0.78;
2089
+ const GRID_DIVISIONS = 10;
2061
2090
  const INERTIA_DURATION_MS = 900;
2062
2091
  const ZOOM_MIN = 0.2;
2063
2092
  const ZOOM_MAX = 6;
@@ -2065,6 +2094,42 @@ const easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
2065
2094
  const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
2066
2095
  const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2067
2096
  const alignStroke = (value) => Math.round(value) + 0.5;
2097
+ const determinePriceFormat = (reference) => {
2098
+ const value = Math.abs(reference);
2099
+ if (!Number.isFinite(value) || value === 0)
2100
+ return { min: 2, max: 6 };
2101
+ if (value < 0.0001)
2102
+ return { min: 6, max: 8 };
2103
+ if (value < 0.001)
2104
+ return { min: 5, max: 7 };
2105
+ if (value < 0.01)
2106
+ return { min: 4, max: 6 };
2107
+ if (value < 0.1)
2108
+ return { min: 4, max: 5 };
2109
+ if (value < 1)
2110
+ return { min: 3, max: 4 };
2111
+ if (value < 100)
2112
+ return { min: 2, max: 3 };
2113
+ return { min: 2, max: 2 };
2114
+ };
2115
+ const getEffectiveBarCount = (count) => Math.max(count + LEFT_OFFSET_BARS + RIGHT_OFFSET_BARS, 1);
2116
+ const getCandleStep = (width, count) => (width <= 0 ? 0 : width / getEffectiveBarCount(count));
2117
+ const getCandleCenter = (index, width, count) => {
2118
+ const step = getCandleStep(width, count);
2119
+ return (index + LEFT_OFFSET_BARS + 0.5) * step;
2120
+ };
2121
+ const clampPixelToChart = (pixel, width) => Math.max(0, Math.min(width, pixel));
2122
+ const pixelToCandleIndex = (pixel, width, count) => {
2123
+ const step = getCandleStep(width, count);
2124
+ if (step === 0)
2125
+ return 0;
2126
+ return pixel / step - LEFT_OFFSET_BARS - 0.5;
2127
+ };
2128
+ const clampCandleIndex = (value, length) => {
2129
+ if (length <= 0)
2130
+ return 0;
2131
+ return clamp(Math.round(value), 0, Math.max(length - 1, 0));
2132
+ };
2068
2133
  const createParallelPoints = (start, end, offset) => {
2069
2134
  const dx = end.x - start.x;
2070
2135
  const dy = end.y - start.y;
@@ -2154,7 +2219,7 @@ const computeVisibleWindow = (dataset, chartWidth, timeframe, zoomLevel, panOffs
2154
2219
  }
2155
2220
  };
2156
2221
  };
2157
- const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2222
+ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange, showStats = true, showHeaderStats = true, showDrawingToolbar = true }) => {
2158
2223
  const chartRef = useRef(null);
2159
2224
  const volumeRef = useRef(null);
2160
2225
  const gridRef = useRef(null);
@@ -2293,7 +2358,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2293
2358
  if (!chartWidth)
2294
2359
  return;
2295
2360
  const candles = getVisibleCandles();
2296
- const pxPerCandle = chartWidth / Math.max(candles.length || 1, 1);
2361
+ const pxPerCandle = chartWidth / Math.max(getEffectiveBarCount(candles.length || 1), 1);
2297
2362
  if (!isFinite(pxPerCandle) || pxPerCandle === 0)
2298
2363
  return;
2299
2364
  const deltaOffset = deltaPx / Math.max(pxPerCandle, 1e-3);
@@ -2417,6 +2482,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2417
2482
  const [priceLabels, setPriceLabels] = useState([]);
2418
2483
  const [timeLabels, setTimeLabels] = useState([]);
2419
2484
  const [clickedPrice, setClickedPrice] = useState(null);
2485
+ const [crosshairMeta, setCrosshairMeta] = useState(null);
2420
2486
  const [showConfigPanel, setShowConfigPanel] = useState(false);
2421
2487
  const [language, setLanguage] = useState('en');
2422
2488
  const [colorScheme, setColorScheme] = useState('green');
@@ -2436,16 +2502,18 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2436
2502
  const iconBaseColor = '#f8fafc';
2437
2503
  const leftRailBaseBg = themePreset === 'light' ? '#0f172a' : activeTheme.panelBg;
2438
2504
  const strings = useMemo(() => UI_TEXT[language] ?? UI_TEXT.en, [language]);
2505
+ const compactHeader = !showHeaderStats;
2439
2506
  const [isFullscreen, setIsFullscreen] = useState(false);
2440
2507
  const [selectedCandleIndex, setSelectedCandleIndex] = useState(null);
2441
2508
  const [seriesType, setSeriesType] = useState('candles');
2442
2509
  const [showSeriesMenu, setShowSeriesMenu] = useState(false);
2443
2510
  const [isSeriesLoading, setIsSeriesLoading] = useState(false);
2444
- const [showQuickTips, setShowQuickTips] = useState(true);
2511
+ const [showQuickTips, setShowQuickTips] = useState(showHeaderStats);
2445
2512
  const [toastMessage, setToastMessage] = useState(null);
2446
2513
  const [showNoteModal, setShowNoteModal] = useState(false);
2447
2514
  const [pendingNotePoint, setPendingNotePoint] = useState(null);
2448
2515
  const [noteDraft, setNoteDraft] = useState('');
2516
+ const [priceFormat, setPriceFormat] = useState({ min: 2, max: 4 });
2449
2517
  const [isMobile, setIsMobile] = useState(false);
2450
2518
  useEffect(() => {
2451
2519
  if (!plotAreaRef.current)
@@ -2468,7 +2536,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2468
2536
  return () => window.clearTimeout(id);
2469
2537
  }, [seriesType, visibleData.length]);
2470
2538
  const locale = language === 'es' ? 'es-ES' : 'en-US';
2471
- const numberFormatter = useMemo(() => new Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }), [locale]);
2539
+ const numberFormatter = useMemo(() => new Intl.NumberFormat(locale, { minimumFractionDigits: priceFormat.min, maximumFractionDigits: priceFormat.max }), [locale, priceFormat]);
2472
2540
  const shortNumberFormatter = useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }), [locale]);
2473
2541
  const timeFormatter = useMemo(() => new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }), [locale]);
2474
2542
  const latencyMs = useMemo(() => Math.round(20 + Math.random() * 15), []);
@@ -2530,9 +2598,14 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2530
2598
  const priceRange = Math.max(1e-6, maxPrice - minPrice);
2531
2599
  const maxVolume = Math.max(bounds.maxVolume, 1);
2532
2600
  priceWindowRef.current = { min: minPrice, max: maxPrice };
2601
+ const referenceMagnitude = Math.min(Math.abs(minPrice), Math.abs(maxPrice)) || Math.max(Math.abs(minPrice), Math.abs(maxPrice));
2602
+ const suggestedFormat = determinePriceFormat(referenceMagnitude);
2603
+ setPriceFormat((prev) => (prev.min === suggestedFormat.min && prev.max === suggestedFormat.max ? prev : suggestedFormat));
2533
2604
  const chartWidth = Math.max(1, cssWidth - priceScaleWidth);
2534
2605
  const chartHeight = Math.max(1, cssHeight - timeScaleHeight - volumeHeight);
2535
- const candleWidth = chartWidth / Math.max(candles.length, 1);
2606
+ const candleStep = getCandleStep(chartWidth, candles.length);
2607
+ const candleWidth = Math.max(1, candleStep * BAR_BODY_RATIO);
2608
+ const centerX = (index) => getCandleCenter(index, chartWidth, candles.length);
2536
2609
  // Generate price labels
2537
2610
  const priceLabelsArray = [];
2538
2611
  for (let i = 0; i <= 10; i++) {
@@ -2567,9 +2640,11 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2567
2640
  gridCtx.lineTo(chartWidth, y);
2568
2641
  gridCtx.stroke();
2569
2642
  }
2570
- const timeStep = Math.max(1, Math.floor(candles.length / 10));
2643
+ const timeStep = Math.max(1, Math.floor(Math.max(candles.length, 1) / GRID_DIVISIONS));
2571
2644
  for (let i = 0; i <= candles.length; i += timeStep) {
2572
- const x = alignStroke(i * candleWidth);
2645
+ const x = alignStroke((i + LEFT_OFFSET_BARS) * candleStep);
2646
+ if (x < 0 || x > chartWidth)
2647
+ continue;
2573
2648
  gridCtx.beginPath();
2574
2649
  gridCtx.moveTo(x, 0);
2575
2650
  gridCtx.lineTo(x, chartHeight);
@@ -2595,7 +2670,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2595
2670
  : minPrice;
2596
2671
  chartCtx.beginPath();
2597
2672
  candles.forEach((candle, index) => {
2598
- const x = index * candleWidth + candleWidth / 2;
2673
+ const x = centerX(index);
2599
2674
  const yClose = ((maxPrice - candle.close) / priceRange) * chartHeight;
2600
2675
  if (index === 0) {
2601
2676
  chartCtx.moveTo(x, yClose);
@@ -2603,6 +2678,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2603
2678
  else if (seriesType === 'step') {
2604
2679
  const prev = candles[index - 1];
2605
2680
  if (prev) {
2681
+ centerX(index - 1);
2606
2682
  const prevY = ((maxPrice - prev.close) / priceRange) * chartHeight;
2607
2683
  chartCtx.lineTo(x, prevY);
2608
2684
  chartCtx.lineTo(x, yClose);
@@ -2618,7 +2694,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2618
2694
  chartCtx.stroke();
2619
2695
  if (seriesType === 'line-markers') {
2620
2696
  candles.forEach((candle, index) => {
2621
- const x = index * candleWidth + candleWidth / 2;
2697
+ const x = centerX(index);
2622
2698
  const yClose = ((maxPrice - candle.close) / priceRange) * chartHeight;
2623
2699
  chartCtx.beginPath();
2624
2700
  chartCtx.arc(x, yClose, 2.5, 0, 2 * Math.PI);
@@ -2627,8 +2703,8 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2627
2703
  });
2628
2704
  }
2629
2705
  if (seriesType === 'area' || seriesType === 'hlc-area' || seriesType === 'baseline') {
2630
- chartCtx.lineTo((candles.length - 1) * candleWidth + candleWidth / 2, ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2631
- chartCtx.lineTo(0 * candleWidth + candleWidth / 2, ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2706
+ chartCtx.lineTo(centerX(candles.length - 1), ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2707
+ chartCtx.lineTo(centerX(0), ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2632
2708
  chartCtx.closePath();
2633
2709
  chartCtx.fillStyle =
2634
2710
  seriesType === 'baseline' ? 'rgba(41,98,255,0.25)' : 'rgba(41,98,255,0.15)';
@@ -2638,7 +2714,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2638
2714
  else {
2639
2715
  // ----- CANDLE / BAR-BASED SERIES -----
2640
2716
  candles.forEach((candle, index) => {
2641
- const xCenter = index * candleWidth + candleWidth / 2;
2717
+ const xCenter = centerX(index);
2642
2718
  const strokeX = alignStroke(xCenter);
2643
2719
  const yHigh = ((maxPrice - candle.high) / priceRange) * chartHeight;
2644
2720
  const yLow = ((maxPrice - candle.low) / priceRange) * chartHeight;
@@ -2723,12 +2799,12 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2723
2799
  if (volumeCtx) {
2724
2800
  volumeCtx.clearRect(0, 0, chartWidth, volumeHeight);
2725
2801
  candles.forEach((candle, index) => {
2726
- const x = index * candleWidth;
2802
+ const xLeft = centerX(index) - candleWidth / 2;
2727
2803
  const barHeight = ((candle.volume || 0) / maxVolume) * volumeHeight;
2728
2804
  const y = volumeHeight - barHeight;
2729
2805
  const isBullish = candle.close > candle.open;
2730
2806
  volumeCtx.fillStyle = isBullish ? bullishColor : bearishColor;
2731
- volumeCtx.fillRect(x + 1, y, candleWidth - 2, barHeight);
2807
+ volumeCtx.fillRect(xLeft + 1, y, Math.max(1, candleWidth - 2), barHeight);
2732
2808
  });
2733
2809
  }
2734
2810
  // Draw indicators
@@ -2742,7 +2818,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2742
2818
  indicatorCtx.beginPath();
2743
2819
  indicator.data.forEach((value, index) => {
2744
2820
  if (value !== null && value !== undefined) {
2745
- const x = index * candleWidth + candleWidth / 2;
2821
+ const x = centerX(index);
2746
2822
  const y = ((maxPrice - value) / priceRange) * chartHeight;
2747
2823
  if (index === 0) {
2748
2824
  indicatorCtx.moveTo(x, y);
@@ -2757,11 +2833,11 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2757
2833
  else if (indicator.type === 'histogram') {
2758
2834
  indicator.data.forEach((value, index) => {
2759
2835
  if (value !== null && value !== undefined) {
2760
- const x = index * candleWidth;
2836
+ const xLeft = centerX(index) - candleWidth / 2;
2761
2837
  const barHeight = Math.abs(value) * chartHeight * 0.1; // Scale histogram
2762
2838
  const y = value >= 0 ? chartHeight / 2 - barHeight : chartHeight / 2;
2763
2839
  indicatorCtx.fillStyle = value >= 0 ? '#089981' : '#f23645';
2764
- indicatorCtx.fillRect(x + 2, y, candleWidth - 4, barHeight);
2840
+ indicatorCtx.fillRect(xLeft + 2, y, Math.max(1, candleWidth - 4), barHeight);
2765
2841
  }
2766
2842
  });
2767
2843
  }
@@ -2791,32 +2867,46 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2791
2867
  }
2792
2868
  }, [chartWidth, chartHeight, overlayHeight]);
2793
2869
  const handlePointerMove = useCallback((e) => {
2794
- if (interactionsLocked || !visibleData.length)
2870
+ if (interactionsLocked || !visibleData.length) {
2871
+ setCrosshairMeta(null);
2795
2872
  return;
2873
+ }
2796
2874
  const rect = e.currentTarget.getBoundingClientRect();
2797
2875
  const x = e.clientX - rect.left;
2798
2876
  const y = e.clientY - rect.top;
2799
2877
  setClickedPrice(null);
2800
2878
  if (cursorType !== 'cross') {
2879
+ setCrosshairMeta(null);
2801
2880
  return;
2802
2881
  }
2803
2882
  if (!isDraggingRef.current) {
2804
2883
  const { min, max } = priceWindowRef.current;
2805
- const snappedX = coordsRef.current.snapToCandle(x, chartWidth, visibleData.length);
2806
- const snappedY = coordsRef.current.snapToPrice(y, chartHeight, min, max);
2807
- mousePosRef.current = {
2808
- x: coordsRef.current.timeToPixel(snappedX, chartWidth, visibleData.length),
2809
- y: coordsRef.current.priceToPixel(snappedY, chartHeight, min, max)
2810
- };
2884
+ const rawIndex = pixelToCandleIndex(x, chartWidth, visibleData.length);
2885
+ const hoveredIndex = clampCandleIndex(rawIndex, visibleData.length);
2886
+ const hoveredCandle = visibleData[hoveredIndex];
2887
+ const snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, min, max);
2888
+ const yPixel = coordsRef.current.priceToPixel(snappedPrice, chartHeight, min, max);
2889
+ const xPixel = magnetEnabled
2890
+ ? getCandleCenter(hoveredIndex, chartWidth, visibleData.length)
2891
+ : clampPixelToChart(x, chartWidth);
2892
+ mousePosRef.current = { x: xPixel, y: yPixel };
2893
+ setCrosshairMeta({
2894
+ price: snappedPrice,
2895
+ timestamp: hoveredCandle?.timestamp ?? null,
2896
+ x: xPixel,
2897
+ y: yPixel,
2898
+ candle: hoveredCandle ?? null
2899
+ });
2811
2900
  showCrosshairRef.current = true;
2812
2901
  requestAnimationFrame(drawCrosshair);
2813
2902
  }
2814
- }, [visibleData, cursorType, chartWidth, chartHeight, drawCrosshair, interactionsLocked]);
2903
+ }, [visibleData, cursorType, chartWidth, chartHeight, drawCrosshair, interactionsLocked, magnetEnabled]);
2815
2904
  const handleMouseLeave = useCallback(() => {
2816
2905
  showCrosshairRef.current = false;
2817
2906
  isDraggingRef.current = false;
2818
2907
  dragSampleRef.current.velocity = 0;
2819
2908
  dragSampleRef.current.lastTs = 0;
2909
+ setCrosshairMeta(null);
2820
2910
  requestAnimationFrame(() => {
2821
2911
  const overlayCtx = overlayRef.current?.getContext('2d');
2822
2912
  if (overlayCtx) {
@@ -2860,15 +2950,21 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2860
2950
  ? { minPrice: priceWindowRef.current.min, maxPrice: priceWindowRef.current.max }
2861
2951
  : getDataBounds(targetData);
2862
2952
  const dataLength = Math.max(targetData.length, 1);
2863
- if (magnetEnabled && visibleData.length) {
2864
- const snappedIndex = coordsRef.current.snapToCandle(x, chartWidth, dataLength);
2865
- const snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice, 0.5);
2866
- x = coordsRef.current.timeToPixel(snappedIndex, chartWidth, dataLength);
2867
- y = coordsRef.current.priceToPixel(snappedPrice, chartHeight, bounds.minPrice, bounds.maxPrice);
2868
- }
2869
- const price = coordsRef.current.pixelToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice);
2870
- const time = coordsRef.current.pixelToTime(x, chartWidth, dataLength);
2871
- return { x, y, price, time };
2953
+ let snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice, 0.5);
2954
+ let snappedY = y;
2955
+ if (magnetEnabled && targetData.length) {
2956
+ const snappedIndex = clampCandleIndex(pixelToCandleIndex(x, chartWidth, dataLength), dataLength);
2957
+ x = getCandleCenter(snappedIndex, chartWidth, dataLength);
2958
+ snappedY = coordsRef.current.priceToPixel(snappedPrice, chartHeight, bounds.minPrice, bounds.maxPrice);
2959
+ y = snappedY;
2960
+ }
2961
+ else {
2962
+ x = clampPixelToChart(x, chartWidth);
2963
+ }
2964
+ const price = coordsRef.current.pixelToPrice(snappedY, chartHeight, bounds.minPrice, bounds.maxPrice);
2965
+ const hoveredIndex = clampCandleIndex(pixelToCandleIndex(x, chartWidth, dataLength), dataLength);
2966
+ const time = targetData[hoveredIndex]?.timestamp ?? hoveredIndex;
2967
+ return { x, y: snappedY, price, time };
2872
2968
  }, [visibleData, storeData, magnetEnabled, chartWidth, chartHeight, interactionsLocked]);
2873
2969
  const tryEraseDrawing = useCallback((x, y) => {
2874
2970
  for (let i = drawings.length - 1; i >= 0; i--) {
@@ -3079,7 +3175,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3079
3175
  }
3080
3176
  if (visibleData.length) {
3081
3177
  const { min, max } = priceWindowRef.current;
3082
- const snappedIndex = coordsRef.current.snapToCandle(rawX, chartWidth, visibleData.length);
3178
+ const snappedIndex = clampCandleIndex(pixelToCandleIndex(rawX, chartWidth, visibleData.length), visibleData.length);
3083
3179
  setSelectedCandleIndex(snappedIndex);
3084
3180
  const price = coordsRef.current.pixelToPrice(rawY, chartHeight, min, max);
3085
3181
  setClickedPrice({ x: rawX, y: rawY, price });
@@ -3222,6 +3318,87 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3222
3318
  { value: 'columns', label: 'Columnas', icon: seriesIconMap.columns },
3223
3319
  { value: 'high-low', label: 'High/Low', icon: seriesIconMap['high-low'] }
3224
3320
  ];
3321
+ const headerButtons = (React.createElement(React.Fragment, null,
3322
+ React.createElement("button", { onClick: () => setShowSeriesMenu(!showSeriesMenu), style: {
3323
+ width: '38px',
3324
+ height: '38px',
3325
+ borderRadius: '999px',
3326
+ border: `1px solid ${showSeriesMenu ? activeTheme.accent : iconBaseBg}`,
3327
+ background: showSeriesMenu ? activeTheme.accent : iconBaseBg,
3328
+ color: iconBaseColor,
3329
+ display: 'flex',
3330
+ alignItems: 'center',
3331
+ justifyContent: 'center',
3332
+ cursor: 'pointer',
3333
+ transition: 'all 0.2s'
3334
+ }, title: strings.buttons.series, "aria-label": strings.buttons.series }, seriesIconMap[seriesType] ?? React.createElement(BsGraphUp, null)),
3335
+ React.createElement("button", { onClick: toggleIndicatorsPanel, style: {
3336
+ background: showIndicatorsPanel ? '#2563eb' : 'rgba(15,23,42,0.6)',
3337
+ color: '#e2e8f0',
3338
+ border: '1px solid #1f2937',
3339
+ borderRadius: '999px',
3340
+ padding: '8px 14px',
3341
+ fontSize: '12px',
3342
+ fontWeight: 600,
3343
+ cursor: 'pointer',
3344
+ transition: 'all 0.2s'
3345
+ } }, strings.buttons.indicators),
3346
+ React.createElement("button", { onClick: () => setShowConfigPanel(!showConfigPanel), style: {
3347
+ width: '38px',
3348
+ height: '38px',
3349
+ borderRadius: '999px',
3350
+ border: `1px solid ${showConfigPanel ? activeTheme.accent : iconBaseBg}`,
3351
+ background: showConfigPanel ? activeTheme.accent : iconBaseBg,
3352
+ color: iconBaseColor,
3353
+ display: 'flex',
3354
+ alignItems: 'center',
3355
+ justifyContent: 'center',
3356
+ cursor: 'pointer',
3357
+ transition: 'all 0.2s'
3358
+ }, title: strings.buttons.config, "aria-label": strings.buttons.config },
3359
+ React.createElement(BsGear, null)),
3360
+ React.createElement("button", { onClick: () => setIsFullscreen(prev => !prev), style: {
3361
+ width: '38px',
3362
+ height: '38px',
3363
+ borderRadius: '999px',
3364
+ border: `1px solid ${isFullscreen ? activeTheme.accent : iconBaseBg}`,
3365
+ background: isFullscreen ? activeTheme.accent : iconBaseBg,
3366
+ color: iconBaseColor,
3367
+ display: 'flex',
3368
+ alignItems: 'center',
3369
+ justifyContent: 'center',
3370
+ cursor: 'pointer',
3371
+ transition: 'all 0.2s'
3372
+ }, title: isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter, "aria-label": isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter },
3373
+ React.createElement(BsArrowsFullscreen, null)),
3374
+ React.createElement("button", { onClick: takeScreenshot, style: {
3375
+ width: '38px',
3376
+ height: '38px',
3377
+ borderRadius: '999px',
3378
+ border: `1px solid ${iconBaseBg}`,
3379
+ background: iconBaseBg,
3380
+ color: iconBaseColor,
3381
+ display: 'flex',
3382
+ alignItems: 'center',
3383
+ justifyContent: 'center',
3384
+ cursor: 'pointer',
3385
+ transition: 'all 0.2s'
3386
+ }, title: strings.buttons.screenshot, "aria-label": strings.buttons.screenshot },
3387
+ React.createElement(BsCamera, null)),
3388
+ showHeaderStats && (React.createElement("button", { onClick: () => setShowQuickTips(prev => !prev), style: {
3389
+ width: '38px',
3390
+ height: '38px',
3391
+ borderRadius: '999px',
3392
+ border: `1px solid ${showQuickTips ? activeTheme.accent : iconBaseBg}`,
3393
+ background: showQuickTips ? activeTheme.accent : iconBaseBg,
3394
+ color: iconBaseColor,
3395
+ display: 'flex',
3396
+ alignItems: 'center',
3397
+ justifyContent: 'center',
3398
+ cursor: 'pointer',
3399
+ transition: 'all 0.2s'
3400
+ }, title: strings.buttons.help, "aria-label": strings.buttons.help },
3401
+ React.createElement(BsQuestionCircle, null)))));
3225
3402
  const cursorCss = isDraggingRef.current
3226
3403
  ? 'grabbing'
3227
3404
  : cursorType === 'cross'
@@ -3250,13 +3427,12 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3250
3427
  background: `linear-gradient(120deg, ${activeTheme.heroFrom} 0%, ${activeTheme.heroTo} 100%)`,
3251
3428
  borderBottom: `1px solid ${activeTheme.panelBorder}`,
3252
3429
  display: 'flex',
3253
- flexWrap: 'wrap',
3430
+ flexDirection: 'column',
3254
3431
  gap: isMobile ? '12px' : '16px',
3255
- alignItems: 'center',
3256
3432
  padding: isMobile ? '12px 16px' : '16px 20px',
3257
3433
  boxShadow: elevatedShadow
3258
3434
  }, initial: { opacity: 0, y: -15 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3 } },
3259
- React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '12px', alignItems: 'center', minWidth: 0 } },
3435
+ showHeaderStats && (React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '12px', alignItems: 'center', minWidth: 0 } },
3260
3436
  React.createElement("div", { style: { background: activeTheme.cardBg, border: `1px solid ${activeTheme.cardBorder}`, borderRadius: '14px', padding: '10px 16px', minWidth: '160px', color: activeTheme.textPrimary } },
3261
3437
  React.createElement("div", { style: { fontSize: '11px', textTransform: 'uppercase', letterSpacing: '0.08em', color: activeTheme.textSecondary, marginBottom: '4px' } }, strings.symbolLabel),
3262
3438
  React.createElement("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: activeTheme.textPrimary, fontWeight: 600, gap: '10px' } },
@@ -3288,150 +3464,118 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3288
3464
  { label: 'V', value: referenceCandle.volume ?? 0 }
3289
3465
  ].map(item => (React.createElement("div", { key: item.label, style: { display: 'flex', flexDirection: 'column', minWidth: '50px' } },
3290
3466
  React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase' } }, item.label),
3291
- React.createElement("span", { style: { color: '#e2e8f0', fontWeight: 600 } }, item.value.toFixed(2)))))))),
3292
- React.createElement("div", { style: {
3467
+ React.createElement("span", { style: { color: '#e2e8f0', fontWeight: 600 } }, item.value.toFixed(2))))))))),
3468
+ compactHeader ? (React.createElement("div", { style: {
3469
+ width: '100%',
3293
3470
  display: 'flex',
3471
+ flexWrap: isMobile ? 'wrap' : 'nowrap',
3294
3472
  alignItems: 'center',
3295
- gap: '12px',
3296
- padding: '10px 14px',
3297
- background: activeTheme.panelBg,
3298
- borderRadius: '16px',
3299
- border: `1px solid ${activeTheme.panelBorder}`,
3300
- overflowX: 'auto'
3473
+ gap: isMobile ? '12px' : '16px'
3301
3474
  } },
3302
- React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' } },
3303
- React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3304
- React.createElement("span", { style: { fontSize: '11px', color: '#f8fafc', fontWeight: 600, padding: '2px 8px', borderRadius: '999px', background: 'rgba(37,99,235,0.25)', border: '1px solid rgba(37,99,235,0.6)' } }, timeframe)),
3305
- React.createElement("div", { style: { display: 'flex', gap: '6px', flex: 1, minWidth: 0, overflowX: 'auto', paddingBottom: '4px' } }, ['1m', '3m', '5m', '15m', '30m', '1h', '4h'].map(tf => (React.createElement("button", { key: tf, onClick: () => handleTimeframeChange(tf), style: {
3306
- background: timeframe === tf ? activeTheme.accent : activeTheme.panelBg,
3307
- color: timeframe === tf ? '#f8fafc' : activeTheme.textSecondary,
3308
- border: `1px solid ${timeframe === tf ? activeTheme.accent : activeTheme.panelBorder}`,
3309
- borderRadius: '10px',
3310
- padding: '6px 12px',
3311
- fontSize: '12px',
3312
- fontWeight: 600,
3313
- cursor: 'pointer',
3314
- transition: 'all 0.2s',
3315
- flex: '0 0 auto',
3316
- whiteSpace: 'nowrap'
3317
- } }, tf)))),
3318
- React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px', color: '#cbd5f5', whiteSpace: 'nowrap', flexShrink: 0 } },
3319
- React.createElement(BsClockHistory, null),
3320
- React.createElement("span", null,
3321
- strings.axis.time,
3322
- ": ",
3323
- currentTimeLabel)),
3324
- React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3325
- background: 'rgba(15,23,42,0.8)',
3326
- color: '#e2e8f0',
3327
- border: '1px solid #1f2937',
3328
- padding: '10px 36px 10px 12px',
3329
- borderRadius: '12px',
3330
- cursor: 'pointer',
3331
- fontSize: '12px',
3332
- fontWeight: 600,
3333
- appearance: 'none',
3334
- backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,
3335
- backgroundPosition: 'right 10px center',
3336
- backgroundRepeat: 'no-repeat',
3337
- backgroundSize: '14px',
3338
- minWidth: '120px',
3339
- flexShrink: 0
3340
- } },
3341
- React.createElement("option", { value: "1m" }, "1m"),
3342
- React.createElement("option", { value: "3m" }, "3m"),
3343
- React.createElement("option", { value: "5m" }, "5m"),
3344
- React.createElement("option", { value: "15m" }, "15m"),
3345
- React.createElement("option", { value: "30m" }, "30m"),
3346
- React.createElement("option", { value: "1h" }, "1h"),
3347
- React.createElement("option", { value: "4h" }, "4h"),
3348
- React.createElement("option", { value: "12h" }, "12h"),
3349
- React.createElement("option", { value: "1D" }, "1D"),
3350
- React.createElement("option", { value: "3D" }, "3D"),
3351
- React.createElement("option", { value: "1W" }, "1W"),
3352
- React.createElement("option", { value: "1M" }, "1M"))),
3353
- React.createElement("div", { style: { marginLeft: 'auto', display: 'flex', flexWrap: 'wrap', gap: '8px', alignItems: 'center' } },
3354
- React.createElement("button", { onClick: () => setShowSeriesMenu(!showSeriesMenu), style: {
3355
- width: '38px',
3356
- height: '38px',
3357
- borderRadius: '999px',
3358
- border: `1px solid ${showSeriesMenu ? activeTheme.accent : iconBaseBg}`,
3359
- background: showSeriesMenu ? activeTheme.accent : iconBaseBg,
3360
- color: iconBaseColor,
3475
+ React.createElement("div", { style: {
3361
3476
  display: 'flex',
3362
3477
  alignItems: 'center',
3363
- justifyContent: 'center',
3364
- cursor: 'pointer',
3365
- transition: 'all 0.2s'
3366
- }, title: strings.buttons.series, "aria-label": strings.buttons.series }, seriesIconMap[seriesType] ?? React.createElement(BsGraphUp, null)),
3367
- React.createElement("button", { onClick: toggleIndicatorsPanel, style: {
3368
- background: showIndicatorsPanel ? '#2563eb' : 'rgba(15,23,42,0.6)',
3369
- color: '#e2e8f0',
3370
- border: '1px solid #1f2937',
3371
- borderRadius: '999px',
3478
+ gap: '10px',
3372
3479
  padding: '8px 14px',
3373
- fontSize: '12px',
3374
- fontWeight: 600,
3375
- cursor: 'pointer',
3376
- transition: 'all 0.2s'
3377
- } }, strings.buttons.indicators),
3378
- React.createElement("button", { onClick: () => setShowConfigPanel(!showConfigPanel), style: {
3379
- width: '38px',
3380
- height: '38px',
3381
- borderRadius: '999px',
3382
- border: `1px solid ${showConfigPanel ? activeTheme.accent : iconBaseBg}`,
3383
- background: showConfigPanel ? activeTheme.accent : iconBaseBg,
3384
- color: iconBaseColor,
3385
- display: 'flex',
3386
- alignItems: 'center',
3387
- justifyContent: 'center',
3388
- cursor: 'pointer',
3389
- transition: 'all 0.2s'
3390
- }, title: strings.buttons.config, "aria-label": strings.buttons.config },
3391
- React.createElement(BsGear, null)),
3392
- React.createElement("button", { onClick: () => setIsFullscreen(prev => !prev), style: {
3393
- width: '38px',
3394
- height: '38px',
3395
- borderRadius: '999px',
3396
- border: `1px solid ${isFullscreen ? activeTheme.accent : iconBaseBg}`,
3397
- background: isFullscreen ? activeTheme.accent : iconBaseBg,
3398
- color: iconBaseColor,
3399
- display: 'flex',
3400
- alignItems: 'center',
3401
- justifyContent: 'center',
3402
- cursor: 'pointer',
3403
- transition: 'all 0.2s'
3404
- }, title: isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter, "aria-label": isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter },
3405
- React.createElement(BsArrowsFullscreen, null)),
3406
- React.createElement("button", { onClick: takeScreenshot, style: {
3407
- width: '38px',
3408
- height: '38px',
3409
- borderRadius: '999px',
3410
- border: `1px solid ${iconBaseBg}`,
3411
- background: iconBaseBg,
3412
- color: iconBaseColor,
3413
- display: 'flex',
3414
- alignItems: 'center',
3415
- justifyContent: 'center',
3416
- cursor: 'pointer',
3417
- transition: 'all 0.2s'
3418
- }, title: strings.buttons.screenshot, "aria-label": strings.buttons.screenshot },
3419
- React.createElement(BsCamera, null)),
3420
- React.createElement("button", { onClick: () => setShowQuickTips(prev => !prev), style: {
3421
- width: '38px',
3422
- height: '38px',
3423
- borderRadius: '999px',
3424
- border: `1px solid ${showQuickTips ? activeTheme.accent : iconBaseBg}`,
3425
- background: showQuickTips ? activeTheme.accent : iconBaseBg,
3426
- color: iconBaseColor,
3480
+ background: activeTheme.panelBg,
3481
+ borderRadius: '16px',
3482
+ border: `1px solid ${activeTheme.panelBorder}`,
3483
+ flex: 1,
3484
+ minWidth: isMobile ? '100%' : '320px'
3485
+ } },
3486
+ React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3487
+ React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3488
+ background: 'rgba(15,23,42,0.8)',
3489
+ color: '#e2e8f0',
3490
+ border: '1px solid #1f2937',
3491
+ padding: '8px 32px 8px 12px',
3492
+ borderRadius: '12px',
3493
+ cursor: 'pointer',
3494
+ fontSize: '12px',
3495
+ fontWeight: 600,
3496
+ appearance: 'none',
3497
+ backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,
3498
+ backgroundPosition: 'right 10px center',
3499
+ backgroundRepeat: 'no-repeat',
3500
+ backgroundSize: '14px',
3501
+ minWidth: '140px'
3502
+ } },
3503
+ React.createElement("option", { value: "1m" }, "1m"),
3504
+ React.createElement("option", { value: "3m" }, "3m"),
3505
+ React.createElement("option", { value: "5m" }, "5m"),
3506
+ React.createElement("option", { value: "15m" }, "15m"),
3507
+ React.createElement("option", { value: "30m" }, "30m"),
3508
+ React.createElement("option", { value: "1h" }, "1h"),
3509
+ React.createElement("option", { value: "4h" }, "4h"),
3510
+ React.createElement("option", { value: "12h" }, "12h"),
3511
+ React.createElement("option", { value: "1D" }, "1D"),
3512
+ React.createElement("option", { value: "3D" }, "3D"),
3513
+ React.createElement("option", { value: "1W" }, "1W"),
3514
+ React.createElement("option", { value: "1M" }, "1M"))),
3515
+ React.createElement("div", { style: { marginLeft: isMobile ? 0 : 'auto', display: 'flex', flexWrap: isMobile ? 'wrap' : 'nowrap', gap: '8px', alignItems: 'center' } }, headerButtons))) : (React.createElement(React.Fragment, null,
3516
+ React.createElement("div", { style: {
3427
3517
  display: 'flex',
3428
3518
  alignItems: 'center',
3429
- justifyContent: 'center',
3430
- cursor: 'pointer',
3431
- transition: 'all 0.2s'
3432
- }, title: strings.buttons.help, "aria-label": strings.buttons.help },
3433
- React.createElement(BsQuestionCircle, null)))),
3434
- derivedStats && (React.createElement("div", { style: { padding: '16px 20px 0', background: activeTheme.panelBg, borderBottom: `1px solid ${activeTheme.panelBorder}` } },
3519
+ gap: '12px',
3520
+ padding: '10px 14px',
3521
+ background: activeTheme.panelBg,
3522
+ borderRadius: '16px',
3523
+ border: `1px solid ${activeTheme.panelBorder}`,
3524
+ overflowX: 'auto'
3525
+ } },
3526
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' } },
3527
+ React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3528
+ React.createElement("span", { style: { fontSize: '11px', color: '#f8fafc', fontWeight: 600, padding: '2px 8px', borderRadius: '999px', background: 'rgba(37,99,235,0.25)', border: '1px solid rgba(37,99,235,0.6)' } }, timeframe)),
3529
+ React.createElement("div", { style: { display: 'flex', gap: '6px', flex: 1, minWidth: 0, overflowX: 'auto', paddingBottom: '4px' } }, ['1m', '3m', '5m', '15m', '30m', '1h', '4h'].map(tf => (React.createElement("button", { key: tf, onClick: () => handleTimeframeChange(tf), style: {
3530
+ background: timeframe === tf ? activeTheme.accent : activeTheme.panelBg,
3531
+ color: timeframe === tf ? '#f8fafc' : activeTheme.textSecondary,
3532
+ border: `1px solid ${timeframe === tf ? activeTheme.accent : activeTheme.panelBorder}`,
3533
+ borderRadius: '10px',
3534
+ padding: '6px 12px',
3535
+ fontSize: '12px',
3536
+ fontWeight: 600,
3537
+ cursor: 'pointer',
3538
+ transition: 'all 0.2s',
3539
+ flex: '0 0 auto',
3540
+ whiteSpace: 'nowrap'
3541
+ } }, tf)))),
3542
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px', color: '#cbd5f5', whiteSpace: 'nowrap', flexShrink: 0 } },
3543
+ React.createElement(BsClockHistory, null),
3544
+ React.createElement("span", null,
3545
+ strings.axis.time,
3546
+ ": ",
3547
+ currentTimeLabel)),
3548
+ React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3549
+ background: 'rgba(15,23,42,0.8)',
3550
+ color: '#e2e8f0',
3551
+ border: '1px solid #1f2937',
3552
+ padding: '10px 36px 10px 12px',
3553
+ borderRadius: '12px',
3554
+ cursor: 'pointer',
3555
+ fontSize: '12px',
3556
+ fontWeight: 600,
3557
+ appearance: 'none',
3558
+ backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%2394a3b8' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,
3559
+ backgroundPosition: 'right 10px center',
3560
+ backgroundRepeat: 'no-repeat',
3561
+ backgroundSize: '14px',
3562
+ minWidth: '120px',
3563
+ flexShrink: 0
3564
+ } },
3565
+ React.createElement("option", { value: "1m" }, "1m"),
3566
+ React.createElement("option", { value: "3m" }, "3m"),
3567
+ React.createElement("option", { value: "5m" }, "5m"),
3568
+ React.createElement("option", { value: "15m" }, "15m"),
3569
+ React.createElement("option", { value: "30m" }, "30m"),
3570
+ React.createElement("option", { value: "1h" }, "1h"),
3571
+ React.createElement("option", { value: "4h" }, "4h"),
3572
+ React.createElement("option", { value: "12h" }, "12h"),
3573
+ React.createElement("option", { value: "1D" }, "1D"),
3574
+ React.createElement("option", { value: "3D" }, "3D"),
3575
+ React.createElement("option", { value: "1W" }, "1W"),
3576
+ React.createElement("option", { value: "1M" }, "1M"))),
3577
+ React.createElement("div", { style: { marginLeft: 'auto', display: 'flex', flexWrap: 'wrap', gap: '8px', alignItems: 'center' } }, headerButtons)))),
3578
+ showStats && derivedStats && (React.createElement("div", { style: { padding: '16px 20px 0', background: activeTheme.panelBg, borderBottom: `1px solid ${activeTheme.panelBorder}` } },
3435
3579
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: '12px' } },
3436
3580
  React.createElement("div", { style: { ...metricCardStyle, background: activeTheme.cardBg, border: `1px solid ${activeTheme.cardBorder}` } },
3437
3581
  React.createElement("span", { style: { ...metricLabelStyle, color: activeTheme.textSecondary } }, "Trading range"),
@@ -3459,7 +3603,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3459
3603
  "Latency ~",
3460
3604
  latencyMs,
3461
3605
  "ms"))))),
3462
- React.createElement(AnimatePresence, null, showQuickTips && (React.createElement(motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, style: {
3606
+ React.createElement(AnimatePresence, null, showHeaderStats && showQuickTips && (React.createElement(motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, style: {
3463
3607
  position: (isFullscreen ? 'fixed' : 'absolute'),
3464
3608
  top: isFullscreen ? 80 : 140,
3465
3609
  left: isFullscreen ? 80 : 140,
@@ -3557,7 +3701,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3557
3701
  minHeight: 0,
3558
3702
  alignItems: 'stretch'
3559
3703
  } },
3560
- React.createElement("div", { style: { width: isMobile ? '60px' : '88px', flexShrink: 0, height: '100%' } },
3704
+ showDrawingToolbar && (React.createElement("div", { style: { width: isMobile ? '60px' : '88px', flexShrink: 0, height: '100%' } },
3561
3705
  React.createElement("div", { style: {
3562
3706
  height: '100%',
3563
3707
  borderRadius: '24px',
@@ -3568,7 +3712,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3568
3712
  alignItems: 'center',
3569
3713
  padding: isMobile ? '12px 0' : '16px 0'
3570
3714
  } },
3571
- React.createElement(DrawingToolbar, null))),
3715
+ React.createElement(DrawingToolbar, null)))),
3572
3716
  React.createElement("div", { ref: plotAreaRef, style: {
3573
3717
  flex: 1,
3574
3718
  position: 'relative',
@@ -3609,6 +3753,65 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3609
3753
  React.createElement("div", { style: { position: 'absolute', bottom: 0, left: 0, right: priceScaleWidth, height: `${timeScaleHeight}px`, background: activeTheme.scaleBg, borderTop: `1px solid ${activeTheme.plotBorder}`, fontSize: '11px', color: activeTheme.textSecondary, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: '4px', padding: '4px 10px' } },
3610
3754
  React.createElement("span", { style: { textTransform: 'uppercase', fontSize: '10px', letterSpacing: '0.08em' } }, strings.axis.time),
3611
3755
  React.createElement("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', color: activeTheme.textPrimary } }, timeLabels.map((label, i) => (React.createElement("div", { key: i }, label))))),
3756
+ crosshairMeta?.candle && (React.createElement("div", { style: {
3757
+ position: 'absolute',
3758
+ top: 16,
3759
+ left: 16,
3760
+ padding: '10px 14px',
3761
+ borderRadius: '14px',
3762
+ background: activeTheme.overlayBg,
3763
+ border: `1px solid ${activeTheme.panelBorder}`,
3764
+ boxShadow: '0 12px 30px rgba(0,0,0,0.45)',
3765
+ color: activeTheme.textPrimary,
3766
+ fontSize: '11px',
3767
+ letterSpacing: '0.04em',
3768
+ display: 'flex',
3769
+ gap: '14px',
3770
+ pointerEvents: 'none',
3771
+ zIndex: 60
3772
+ } },
3773
+ ([
3774
+ { label: 'O', value: crosshairMeta.candle.open },
3775
+ { label: 'H', value: crosshairMeta.candle.high },
3776
+ { label: 'L', value: crosshairMeta.candle.low },
3777
+ { label: 'C', value: crosshairMeta.candle.close }
3778
+ ]).map((item) => (React.createElement("div", { key: item.label, style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
3779
+ React.createElement("span", { style: { color: activeTheme.textSecondary } }, item.label),
3780
+ React.createElement("span", { style: { fontWeight: 600 } }, numberFormatter.format(item.value))))),
3781
+ React.createElement("div", { style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
3782
+ React.createElement("span", { style: { color: activeTheme.textSecondary } }, "V"),
3783
+ React.createElement("span", { style: { fontWeight: 600 } }, shortNumberFormatter.format(crosshairMeta.candle.volume ?? 0))))),
3784
+ crosshairMeta && (React.createElement(React.Fragment, null,
3785
+ React.createElement("div", { style: {
3786
+ position: 'absolute',
3787
+ right: `${priceScaleWidth}px`,
3788
+ top: `${Math.min(Math.max(crosshairMeta.y, 12), overlayHeight - 12)}px`,
3789
+ transform: 'translate(0, -50%)',
3790
+ background: activeTheme.scaleBg,
3791
+ border: `1px solid ${activeTheme.panelBorder}`,
3792
+ borderRadius: '6px 0 0 6px',
3793
+ padding: '2px 8px',
3794
+ fontSize: '11px',
3795
+ fontWeight: 600,
3796
+ color: activeTheme.textPrimary,
3797
+ pointerEvents: 'none',
3798
+ boxShadow: '0 8px 20px rgba(0,0,0,0.4)'
3799
+ } }, numberFormatter.format(crosshairMeta.price)),
3800
+ crosshairMeta.timestamp && (React.createElement("div", { style: {
3801
+ position: 'absolute',
3802
+ left: `${Math.min(Math.max(crosshairMeta.x, 40), chartWidth - 40)}px`,
3803
+ bottom: `${timeScaleHeight}px`,
3804
+ transform: 'translate(-50%, 50%)',
3805
+ background: activeTheme.scaleBg,
3806
+ border: `1px solid ${activeTheme.panelBorder}`,
3807
+ borderRadius: '6px',
3808
+ padding: '2px 8px',
3809
+ fontSize: '10px',
3810
+ letterSpacing: '0.05em',
3811
+ color: activeTheme.textPrimary,
3812
+ pointerEvents: 'none',
3813
+ boxShadow: '0 8px 20px rgba(0,0,0,0.35)'
3814
+ } }, timeFormatter.format(new Date(crosshairMeta.timestamp)))))),
3612
3815
  clickedPrice && (React.createElement("div", { style: {
3613
3816
  position: 'absolute',
3614
3817
  background: '#1e222d',
@@ -3791,7 +3994,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3791
3994
  React.createElement("span", null, field.label),
3792
3995
  React.createElement("input", { type: "color", value: customTheme[field.key], onChange: (event) => handleCustomThemeChange(field.key, event.target.value), style: { width: '100%', height: '34px', border: 'none', cursor: 'pointer', borderRadius: '8px', background: 'transparent' } })))))),
3793
3996
  React.createElement("div", { style: { fontSize: '12px', color: activeTheme.textSecondary } }, strings.config.soon))),
3794
- React.createElement("div", { style: { padding: isMobile ? '8px 16px' : '10px 20px', background: activeTheme.panelBg, borderTop: `1px solid ${activeTheme.panelBorder}`, display: 'flex', flexWrap: 'wrap', gap: isMobile ? '12px' : '18px', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: activeTheme.textSecondary } },
3997
+ showHeaderStats && (React.createElement("div", { style: { padding: isMobile ? '8px 16px' : '10px 20px', background: activeTheme.panelBg, borderTop: `1px solid ${activeTheme.panelBorder}`, display: 'flex', flexWrap: 'wrap', gap: isMobile ? '12px' : '18px', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: activeTheme.textSecondary } },
3795
3998
  React.createElement("span", null,
3796
3999
  "Latency ",
3797
4000
  latencyMs,
@@ -3800,7 +4003,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3800
4003
  "Session ",
3801
4004
  derivedStats?.session ?? 'Global'),
3802
4005
  React.createElement("span", null, "Feed Binance Composite"),
3803
- React.createElement("span", null, "Security AES-256"))));
4006
+ React.createElement("span", null, "Security AES-256")))));
3804
4007
  };
3805
4008
  var TradingViewChart_default = memo(TradingViewChart);
3806
4009