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.cjs CHANGED
@@ -140,8 +140,20 @@ function BsArrowRepeat (props) {
140
140
  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);
141
141
  }function BsDiagram3 (props) {
142
142
  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);
143
+ }function BsEmojiAngry (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":"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);
145
+ }function BsEmojiDizzy (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":"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);
147
+ }function BsEmojiHeartEyes (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":"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);
149
+ }function BsEmojiLaughing (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":"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);
151
+ }function BsEmojiNeutral (props) {
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 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);
143
153
  }function BsEmojiSmile (props) {
144
154
  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);
155
+ }function BsEmojiSunglasses (props) {
156
+ 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);
145
157
  }function BsEyeSlash (props) {
146
158
  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);
147
159
  }function BsGear (props) {
@@ -843,7 +855,7 @@ function createStore(creator) {
843
855
  const useChartStore = createStore((set) => ({
844
856
  data: [],
845
857
  visibleData: [],
846
- timeframe: '1h',
858
+ timeframe: '12h',
847
859
  zoomLevel: 1,
848
860
  panOffset: 0,
849
861
  indicators: [],
@@ -922,7 +934,6 @@ const useChartStore = createStore((set) => ({
922
934
  toggleDrawingsHidden: () => set((state) => ({ drawingsHidden: !state.drawingsHidden }))
923
935
  }));
924
936
 
925
- const EMOJIS = ['⭐', '🔥', '🚀', '✅', '❌', '💎', '🐂', '🐻', '📈', '📉'];
926
937
  const cursorOptions = [
927
938
  { type: 'cross', icon: React.createElement(BsCursor, null), label: 'Cross' },
928
939
  { type: 'dot', icon: React.createElement("div", { style: { width: '6px', height: '6px', borderRadius: '50%', background: 'currentColor' } }), label: 'Dot' },
@@ -962,6 +973,15 @@ const measurementOptions = [
962
973
  { tool: 'fibonacci', icon: React.createElement(BsGraphUp, null), label: 'Fibonacci' },
963
974
  { tool: 'ruler', icon: React.createElement(BsGraphDown, null), label: 'Regla' }
964
975
  ];
976
+ const emojiOptions = [
977
+ { label: 'Smile', icon: React.createElement(BsEmojiSmile, null), value: '🙂' },
978
+ { label: 'Laugh', icon: React.createElement(BsEmojiLaughing, null), value: '😂' },
979
+ { label: 'Heart Eyes', icon: React.createElement(BsEmojiHeartEyes, null), value: '😍' },
980
+ { label: 'Sunglasses', icon: React.createElement(BsEmojiSunglasses, null), value: '😎' },
981
+ { label: 'Dizzy', icon: React.createElement(BsEmojiDizzy, null), value: '😵' },
982
+ { label: 'Neutral', icon: React.createElement(BsEmojiNeutral, null), value: '😐' },
983
+ { label: 'Angry', icon: React.createElement(BsEmojiAngry, null), value: '😠' }
984
+ ];
965
985
  const popoverStyle = {
966
986
  position: 'absolute',
967
987
  left: '64px',
@@ -980,6 +1000,15 @@ const DrawingToolbar = () => {
980
1000
  const { activeTool, cursorType, strokeColor, strokeWidth, setActiveTool, setCursorType, setStrokeColor, setStrokeWidth, clearDrawings, setSelectedEmoji, setIsDrawing } = useChartStore();
981
1001
  const [openMenu, setOpenMenu] = React.useState(null);
982
1002
  const [showEmojiPicker, setShowEmojiPicker] = React.useState(false);
1003
+ const [isCompact, setIsCompact] = React.useState(false);
1004
+ React.useEffect(() => {
1005
+ const handleResize = () => {
1006
+ setIsCompact(window.innerWidth < 768);
1007
+ };
1008
+ handleResize();
1009
+ window.addEventListener('resize', handleResize);
1010
+ return () => window.removeEventListener('resize', handleResize);
1011
+ }, []);
983
1012
  const LINE_COLORS = ['#8ab4ff', '#f472b6', '#34d399', '#facc15', '#f87171', '#e2e8f0'];
984
1013
  const WIDTH_OPTIONS = [1, 1.5, 2.5, 3.5];
985
1014
  const handleSelectTool = (tool) => {
@@ -1075,14 +1104,27 @@ const DrawingToolbar = () => {
1075
1104
  title: 'Limpiar todo'
1076
1105
  }
1077
1106
  ];
1078
- return (React.createElement("div", { style: { position: 'relative', height: '100%' } },
1107
+ const filteredButtons = isCompact
1108
+ ? buttonConfig.filter(btn => !['pitchforks', 'measurements'].includes(btn.key))
1109
+ : buttonConfig;
1110
+ const buildPopoverStyle = (custom) => ({
1111
+ ...popoverStyle,
1112
+ ...(isCompact
1113
+ ? { left: '0px', top: 'calc(100% + 12px)', transform: 'none', width: 'min(360px, 92vw)' }
1114
+ : {}),
1115
+ ...custom
1116
+ });
1117
+ return (React.createElement("div", { style: { position: 'relative', height: '100%', width: '100%' } },
1079
1118
  React.createElement("div", { style: {
1080
- height: '100%',
1119
+ height: isCompact ? 'auto' : '100%',
1120
+ width: '100%',
1081
1121
  display: 'flex',
1082
- flexDirection: 'column',
1122
+ flexDirection: isCompact ? 'row' : 'column',
1083
1123
  alignItems: 'center',
1084
- gap: '12px'
1085
- } }, buttonConfig.map((btn) => (React.createElement("div", { key: btn.key, style: { position: 'relative' } },
1124
+ gap: '12px',
1125
+ overflowX: isCompact ? 'auto' : 'visible',
1126
+ padding: isCompact ? '12px 6px' : 0
1127
+ } }, filteredButtons.map((btn) => (React.createElement("div", { key: btn.key, style: { position: 'relative' } },
1086
1128
  React.createElement("button", { onClick: btn.onClick, title: btn.title, "aria-label": btn.title, style: {
1087
1129
  width: '46px',
1088
1130
  height: '46px',
@@ -1097,7 +1139,7 @@ const DrawingToolbar = () => {
1097
1139
  fontSize: '18px',
1098
1140
  transition: 'all 0.2s'
1099
1141
  } }, btn.icon),
1100
- 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: {
1142
+ 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: {
1101
1143
  width: '84px',
1102
1144
  height: '60px',
1103
1145
  borderRadius: '12px',
@@ -1113,7 +1155,7 @@ const DrawingToolbar = () => {
1113
1155
  }, title: option.label },
1114
1156
  React.createElement("span", { style: { fontSize: '18px', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, option.icon),
1115
1157
  React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase' } }, option.label)))))),
1116
- btn.key === 'lines' && openMenu === 'lines' && (React.createElement("div", { style: { ...popoverStyle, width: '280px' } },
1158
+ btn.key === 'lines' && openMenu === 'lines' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(360px, 92vw)' : '280px' }) },
1117
1159
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Lines"),
1118
1160
  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: {
1119
1161
  borderRadius: '12px',
@@ -1153,7 +1195,7 @@ const DrawingToolbar = () => {
1153
1195
  } },
1154
1196
  width,
1155
1197
  "px"))))))),
1156
- btn.key === 'shapes' && openMenu === 'shapes' && (React.createElement("div", { style: { ...popoverStyle, width: '200px' } },
1198
+ btn.key === 'shapes' && openMenu === 'shapes' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '200px' }) },
1157
1199
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Shapes"),
1158
1200
  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: {
1159
1201
  borderRadius: '12px',
@@ -1170,7 +1212,7 @@ const DrawingToolbar = () => {
1170
1212
  }, title: option.label },
1171
1213
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1172
1214
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em' } }, option.label))))))),
1173
- btn.key === 'channels' && openMenu === 'channels' && (React.createElement("div", { style: { ...popoverStyle, width: '220px' } },
1215
+ btn.key === 'channels' && openMenu === 'channels' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(340px, 92vw)' : '220px' }) },
1174
1216
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Channels"),
1175
1217
  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: {
1176
1218
  borderRadius: '12px',
@@ -1187,7 +1229,7 @@ const DrawingToolbar = () => {
1187
1229
  }, title: option.label },
1188
1230
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1189
1231
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em' } }, option.label))))))),
1190
- btn.key === 'pitchforks' && openMenu === 'pitchforks' && (React.createElement("div", { style: { ...popoverStyle, width: '200px' } },
1232
+ btn.key === 'pitchforks' && openMenu === 'pitchforks' && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '200px' }) },
1191
1233
  React.createElement("p", { style: { margin: 0, fontSize: '11px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Pitchforks"),
1192
1234
  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: {
1193
1235
  borderRadius: '12px',
@@ -1204,7 +1246,7 @@ const DrawingToolbar = () => {
1204
1246
  }, title: option.label },
1205
1247
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1206
1248
  React.createElement("span", { style: { fontSize: '10px', textTransform: 'uppercase', letterSpacing: '0.08em', textAlign: 'center' } }, option.label))))))),
1207
- 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: {
1249
+ 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: {
1208
1250
  width: '64px',
1209
1251
  height: '54px',
1210
1252
  borderRadius: '12px',
@@ -1221,41 +1263,24 @@ const DrawingToolbar = () => {
1221
1263
  }, title: option.label },
1222
1264
  React.createElement("span", { style: { fontSize: '16px' } }, option.icon),
1223
1265
  React.createElement("span", null, option.label)))))),
1224
- btn.key === 'emoji' && showEmojiPicker && (React.createElement("div", { style: {
1225
- position: 'absolute',
1226
- left: '64px',
1227
- top: '50%',
1228
- transform: 'translateY(-50%)',
1229
- zIndex: 1100,
1230
- background: '#020617',
1231
- borderRadius: '14px',
1232
- overflow: 'hidden',
1233
- boxShadow: '0 18px 40px rgba(0,0,0,0.65)',
1234
- padding: '10px',
1235
- border: '1px solid #1f2937'
1236
- } },
1237
- React.createElement("div", { style: {
1238
- display: 'grid',
1239
- gridTemplateColumns: 'repeat(3, 1fr)',
1240
- gap: '6px'
1241
- } }, EMOJIS.map((emoji) => (React.createElement("button", { key: emoji, onClick: () => {
1242
- setSelectedEmoji(emoji);
1266
+ btn.key === 'emoji' && showEmojiPicker && (React.createElement("div", { style: buildPopoverStyle({ width: isCompact ? 'min(320px, 92vw)' : '220px' }) },
1267
+ React.createElement("p", { style: { margin: '0 0 6px', fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, "Emojis"),
1268
+ 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: () => {
1269
+ setSelectedEmoji(option.value);
1243
1270
  setActiveTool('icon');
1244
1271
  setShowEmojiPicker(false);
1245
1272
  }, style: {
1246
- width: '40px',
1247
- height: '40px',
1248
- background: '#111827',
1273
+ height: '48px',
1274
+ borderRadius: '12px',
1249
1275
  border: '1px solid #1f2937',
1250
- borderRadius: '10px',
1251
- cursor: 'pointer',
1276
+ background: 'rgba(2,6,23,0.65)',
1277
+ color: '#f8fafc',
1252
1278
  display: 'flex',
1253
1279
  alignItems: 'center',
1254
1280
  justifyContent: 'center',
1255
- fontSize: '18px',
1256
- color: '#f8fafc'
1257
- } },
1258
- React.createElement("span", null, emoji)))))))))))));
1281
+ fontSize: '22px',
1282
+ cursor: 'pointer'
1283
+ }, title: option.label, "aria-label": option.label }, option.icon))))))))))));
1259
1284
  };
1260
1285
 
1261
1286
  const drawRoundedRect = (ctx, x, y, width, height, radius = 8) => {
@@ -1992,23 +2017,23 @@ const LINE_TOOL_TYPES = [
1992
2017
  const LINE_TOOL_SET = new Set(LINE_TOOL_TYPES);
1993
2018
  const THEME_PRESETS = {
1994
2019
  dark: {
1995
- pageBg: '#050910',
1996
- heroFrom: '#111827',
1997
- heroTo: '#0b1120',
1998
- panelBg: 'rgba(15,23,42,0.9)',
1999
- panelBorder: '#1f2937',
2000
- cardBg: 'rgba(15,23,42,0.85)',
2001
- cardBorder: '#111827',
2020
+ pageBg: '#000000',
2021
+ heroFrom: '#050505',
2022
+ heroTo: '#000000',
2023
+ panelBg: 'rgba(0,0,0,0.92)',
2024
+ panelBorder: '#0f0f0f',
2025
+ cardBg: 'rgba(0,0,0,0.88)',
2026
+ cardBorder: '#050505',
2002
2027
  textPrimary: '#f8fafc',
2003
- textSecondary: '#94a3b8',
2028
+ textSecondary: '#9ca3af',
2004
2029
  accent: '#2563eb',
2005
- accentSoft: 'rgba(37,99,235,0.2)',
2006
- railBg: 'rgba(2,6,23,0.85)',
2007
- railBorder: '#111827',
2008
- plotBg: '#020617',
2009
- plotBorder: '#111827',
2010
- scaleBg: 'rgba(15,23,42,0.9)',
2011
- overlayBg: 'rgba(5,7,15,0.85)'
2030
+ accentSoft: 'rgba(37,99,235,0.25)',
2031
+ railBg: 'rgba(0,0,0,0.9)',
2032
+ railBorder: '#080808',
2033
+ plotBg: '#010101',
2034
+ plotBorder: '#0d0d0d',
2035
+ scaleBg: 'rgba(0,0,0,0.92)',
2036
+ overlayBg: 'rgba(0,0,0,0.85)'
2012
2037
  },
2013
2038
  blue: {
2014
2039
  pageBg: '#060e1f',
@@ -2060,6 +2085,10 @@ const CUSTOM_THEME_FIELDS = [
2060
2085
  const MIN_CANDLE_PX = 4;
2061
2086
  const MAX_CANDLE_PX = 28;
2062
2087
  const BUFFER_FRACTION = 0.18;
2088
+ const LEFT_OFFSET_BARS = 2;
2089
+ const RIGHT_OFFSET_BARS = 18;
2090
+ const BAR_BODY_RATIO = 0.78;
2091
+ const GRID_DIVISIONS = 10;
2063
2092
  const INERTIA_DURATION_MS = 900;
2064
2093
  const ZOOM_MIN = 0.2;
2065
2094
  const ZOOM_MAX = 6;
@@ -2067,6 +2096,42 @@ const easeOutQuad = (t) => 1 - (1 - t) * (1 - t);
2067
2096
  const easeInOutCubic = (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
2068
2097
  const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
2069
2098
  const alignStroke = (value) => Math.round(value) + 0.5;
2099
+ const determinePriceFormat = (reference) => {
2100
+ const value = Math.abs(reference);
2101
+ if (!Number.isFinite(value) || value === 0)
2102
+ return { min: 2, max: 6 };
2103
+ if (value < 0.0001)
2104
+ return { min: 6, max: 8 };
2105
+ if (value < 0.001)
2106
+ return { min: 5, max: 7 };
2107
+ if (value < 0.01)
2108
+ return { min: 4, max: 6 };
2109
+ if (value < 0.1)
2110
+ return { min: 4, max: 5 };
2111
+ if (value < 1)
2112
+ return { min: 3, max: 4 };
2113
+ if (value < 100)
2114
+ return { min: 2, max: 3 };
2115
+ return { min: 2, max: 2 };
2116
+ };
2117
+ const getEffectiveBarCount = (count) => Math.max(count + LEFT_OFFSET_BARS + RIGHT_OFFSET_BARS, 1);
2118
+ const getCandleStep = (width, count) => (width <= 0 ? 0 : width / getEffectiveBarCount(count));
2119
+ const getCandleCenter = (index, width, count) => {
2120
+ const step = getCandleStep(width, count);
2121
+ return (index + LEFT_OFFSET_BARS + 0.5) * step;
2122
+ };
2123
+ const clampPixelToChart = (pixel, width) => Math.max(0, Math.min(width, pixel));
2124
+ const pixelToCandleIndex = (pixel, width, count) => {
2125
+ const step = getCandleStep(width, count);
2126
+ if (step === 0)
2127
+ return 0;
2128
+ return pixel / step - LEFT_OFFSET_BARS - 0.5;
2129
+ };
2130
+ const clampCandleIndex = (value, length) => {
2131
+ if (length <= 0)
2132
+ return 0;
2133
+ return clamp(Math.round(value), 0, Math.max(length - 1, 0));
2134
+ };
2070
2135
  const createParallelPoints = (start, end, offset) => {
2071
2136
  const dx = end.x - start.x;
2072
2137
  const dy = end.y - start.y;
@@ -2156,7 +2221,7 @@ const computeVisibleWindow = (dataset, chartWidth, timeframe, zoomLevel, panOffs
2156
2221
  }
2157
2222
  };
2158
2223
  };
2159
- const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2224
+ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange, showStats = true, showHeaderStats = true, showDrawingToolbar = true }) => {
2160
2225
  const chartRef = React.useRef(null);
2161
2226
  const volumeRef = React.useRef(null);
2162
2227
  const gridRef = React.useRef(null);
@@ -2295,7 +2360,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2295
2360
  if (!chartWidth)
2296
2361
  return;
2297
2362
  const candles = getVisibleCandles();
2298
- const pxPerCandle = chartWidth / Math.max(candles.length || 1, 1);
2363
+ const pxPerCandle = chartWidth / Math.max(getEffectiveBarCount(candles.length || 1), 1);
2299
2364
  if (!isFinite(pxPerCandle) || pxPerCandle === 0)
2300
2365
  return;
2301
2366
  const deltaOffset = deltaPx / Math.max(pxPerCandle, 1e-3);
@@ -2419,6 +2484,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2419
2484
  const [priceLabels, setPriceLabels] = React.useState([]);
2420
2485
  const [timeLabels, setTimeLabels] = React.useState([]);
2421
2486
  const [clickedPrice, setClickedPrice] = React.useState(null);
2487
+ const [crosshairMeta, setCrosshairMeta] = React.useState(null);
2422
2488
  const [showConfigPanel, setShowConfigPanel] = React.useState(false);
2423
2489
  const [language, setLanguage] = React.useState('en');
2424
2490
  const [colorScheme, setColorScheme] = React.useState('green');
@@ -2438,16 +2504,18 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2438
2504
  const iconBaseColor = '#f8fafc';
2439
2505
  const leftRailBaseBg = themePreset === 'light' ? '#0f172a' : activeTheme.panelBg;
2440
2506
  const strings = React.useMemo(() => UI_TEXT[language] ?? UI_TEXT.en, [language]);
2507
+ const compactHeader = !showHeaderStats;
2441
2508
  const [isFullscreen, setIsFullscreen] = React.useState(false);
2442
2509
  const [selectedCandleIndex, setSelectedCandleIndex] = React.useState(null);
2443
2510
  const [seriesType, setSeriesType] = React.useState('candles');
2444
2511
  const [showSeriesMenu, setShowSeriesMenu] = React.useState(false);
2445
2512
  const [isSeriesLoading, setIsSeriesLoading] = React.useState(false);
2446
- const [showQuickTips, setShowQuickTips] = React.useState(true);
2513
+ const [showQuickTips, setShowQuickTips] = React.useState(showHeaderStats);
2447
2514
  const [toastMessage, setToastMessage] = React.useState(null);
2448
2515
  const [showNoteModal, setShowNoteModal] = React.useState(false);
2449
2516
  const [pendingNotePoint, setPendingNotePoint] = React.useState(null);
2450
2517
  const [noteDraft, setNoteDraft] = React.useState('');
2518
+ const [priceFormat, setPriceFormat] = React.useState({ min: 2, max: 4 });
2451
2519
  const [isMobile, setIsMobile] = React.useState(false);
2452
2520
  React.useEffect(() => {
2453
2521
  if (!plotAreaRef.current)
@@ -2470,7 +2538,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2470
2538
  return () => window.clearTimeout(id);
2471
2539
  }, [seriesType, visibleData.length]);
2472
2540
  const locale = language === 'es' ? 'es-ES' : 'en-US';
2473
- const numberFormatter = React.useMemo(() => new Intl.NumberFormat(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 }), [locale]);
2541
+ const numberFormatter = React.useMemo(() => new Intl.NumberFormat(locale, { minimumFractionDigits: priceFormat.min, maximumFractionDigits: priceFormat.max }), [locale, priceFormat]);
2474
2542
  const shortNumberFormatter = React.useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }), [locale]);
2475
2543
  const timeFormatter = React.useMemo(() => new Intl.DateTimeFormat(locale, { hour: '2-digit', minute: '2-digit' }), [locale]);
2476
2544
  const latencyMs = React.useMemo(() => Math.round(20 + Math.random() * 15), []);
@@ -2532,9 +2600,14 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2532
2600
  const priceRange = Math.max(1e-6, maxPrice - minPrice);
2533
2601
  const maxVolume = Math.max(bounds.maxVolume, 1);
2534
2602
  priceWindowRef.current = { min: minPrice, max: maxPrice };
2603
+ const referenceMagnitude = Math.min(Math.abs(minPrice), Math.abs(maxPrice)) || Math.max(Math.abs(minPrice), Math.abs(maxPrice));
2604
+ const suggestedFormat = determinePriceFormat(referenceMagnitude);
2605
+ setPriceFormat((prev) => (prev.min === suggestedFormat.min && prev.max === suggestedFormat.max ? prev : suggestedFormat));
2535
2606
  const chartWidth = Math.max(1, cssWidth - priceScaleWidth);
2536
2607
  const chartHeight = Math.max(1, cssHeight - timeScaleHeight - volumeHeight);
2537
- const candleWidth = chartWidth / Math.max(candles.length, 1);
2608
+ const candleStep = getCandleStep(chartWidth, candles.length);
2609
+ const candleWidth = Math.max(1, candleStep * BAR_BODY_RATIO);
2610
+ const centerX = (index) => getCandleCenter(index, chartWidth, candles.length);
2538
2611
  // Generate price labels
2539
2612
  const priceLabelsArray = [];
2540
2613
  for (let i = 0; i <= 10; i++) {
@@ -2569,9 +2642,11 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2569
2642
  gridCtx.lineTo(chartWidth, y);
2570
2643
  gridCtx.stroke();
2571
2644
  }
2572
- const timeStep = Math.max(1, Math.floor(candles.length / 10));
2645
+ const timeStep = Math.max(1, Math.floor(Math.max(candles.length, 1) / GRID_DIVISIONS));
2573
2646
  for (let i = 0; i <= candles.length; i += timeStep) {
2574
- const x = alignStroke(i * candleWidth);
2647
+ const x = alignStroke((i + LEFT_OFFSET_BARS) * candleStep);
2648
+ if (x < 0 || x > chartWidth)
2649
+ continue;
2575
2650
  gridCtx.beginPath();
2576
2651
  gridCtx.moveTo(x, 0);
2577
2652
  gridCtx.lineTo(x, chartHeight);
@@ -2597,7 +2672,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2597
2672
  : minPrice;
2598
2673
  chartCtx.beginPath();
2599
2674
  candles.forEach((candle, index) => {
2600
- const x = index * candleWidth + candleWidth / 2;
2675
+ const x = centerX(index);
2601
2676
  const yClose = ((maxPrice - candle.close) / priceRange) * chartHeight;
2602
2677
  if (index === 0) {
2603
2678
  chartCtx.moveTo(x, yClose);
@@ -2605,6 +2680,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2605
2680
  else if (seriesType === 'step') {
2606
2681
  const prev = candles[index - 1];
2607
2682
  if (prev) {
2683
+ centerX(index - 1);
2608
2684
  const prevY = ((maxPrice - prev.close) / priceRange) * chartHeight;
2609
2685
  chartCtx.lineTo(x, prevY);
2610
2686
  chartCtx.lineTo(x, yClose);
@@ -2620,7 +2696,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2620
2696
  chartCtx.stroke();
2621
2697
  if (seriesType === 'line-markers') {
2622
2698
  candles.forEach((candle, index) => {
2623
- const x = index * candleWidth + candleWidth / 2;
2699
+ const x = centerX(index);
2624
2700
  const yClose = ((maxPrice - candle.close) / priceRange) * chartHeight;
2625
2701
  chartCtx.beginPath();
2626
2702
  chartCtx.arc(x, yClose, 2.5, 0, 2 * Math.PI);
@@ -2629,8 +2705,8 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2629
2705
  });
2630
2706
  }
2631
2707
  if (seriesType === 'area' || seriesType === 'hlc-area' || seriesType === 'baseline') {
2632
- chartCtx.lineTo((candles.length - 1) * candleWidth + candleWidth / 2, ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2633
- chartCtx.lineTo(0 * candleWidth + candleWidth / 2, ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2708
+ chartCtx.lineTo(centerX(candles.length - 1), ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2709
+ chartCtx.lineTo(centerX(0), ((maxPrice - baselinePrice) / priceRange) * chartHeight);
2634
2710
  chartCtx.closePath();
2635
2711
  chartCtx.fillStyle =
2636
2712
  seriesType === 'baseline' ? 'rgba(41,98,255,0.25)' : 'rgba(41,98,255,0.15)';
@@ -2640,7 +2716,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2640
2716
  else {
2641
2717
  // ----- CANDLE / BAR-BASED SERIES -----
2642
2718
  candles.forEach((candle, index) => {
2643
- const xCenter = index * candleWidth + candleWidth / 2;
2719
+ const xCenter = centerX(index);
2644
2720
  const strokeX = alignStroke(xCenter);
2645
2721
  const yHigh = ((maxPrice - candle.high) / priceRange) * chartHeight;
2646
2722
  const yLow = ((maxPrice - candle.low) / priceRange) * chartHeight;
@@ -2725,12 +2801,12 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2725
2801
  if (volumeCtx) {
2726
2802
  volumeCtx.clearRect(0, 0, chartWidth, volumeHeight);
2727
2803
  candles.forEach((candle, index) => {
2728
- const x = index * candleWidth;
2804
+ const xLeft = centerX(index) - candleWidth / 2;
2729
2805
  const barHeight = ((candle.volume || 0) / maxVolume) * volumeHeight;
2730
2806
  const y = volumeHeight - barHeight;
2731
2807
  const isBullish = candle.close > candle.open;
2732
2808
  volumeCtx.fillStyle = isBullish ? bullishColor : bearishColor;
2733
- volumeCtx.fillRect(x + 1, y, candleWidth - 2, barHeight);
2809
+ volumeCtx.fillRect(xLeft + 1, y, Math.max(1, candleWidth - 2), barHeight);
2734
2810
  });
2735
2811
  }
2736
2812
  // Draw indicators
@@ -2744,7 +2820,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2744
2820
  indicatorCtx.beginPath();
2745
2821
  indicator.data.forEach((value, index) => {
2746
2822
  if (value !== null && value !== undefined) {
2747
- const x = index * candleWidth + candleWidth / 2;
2823
+ const x = centerX(index);
2748
2824
  const y = ((maxPrice - value) / priceRange) * chartHeight;
2749
2825
  if (index === 0) {
2750
2826
  indicatorCtx.moveTo(x, y);
@@ -2759,11 +2835,11 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2759
2835
  else if (indicator.type === 'histogram') {
2760
2836
  indicator.data.forEach((value, index) => {
2761
2837
  if (value !== null && value !== undefined) {
2762
- const x = index * candleWidth;
2838
+ const xLeft = centerX(index) - candleWidth / 2;
2763
2839
  const barHeight = Math.abs(value) * chartHeight * 0.1; // Scale histogram
2764
2840
  const y = value >= 0 ? chartHeight / 2 - barHeight : chartHeight / 2;
2765
2841
  indicatorCtx.fillStyle = value >= 0 ? '#089981' : '#f23645';
2766
- indicatorCtx.fillRect(x + 2, y, candleWidth - 4, barHeight);
2842
+ indicatorCtx.fillRect(xLeft + 2, y, Math.max(1, candleWidth - 4), barHeight);
2767
2843
  }
2768
2844
  });
2769
2845
  }
@@ -2793,32 +2869,46 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2793
2869
  }
2794
2870
  }, [chartWidth, chartHeight, overlayHeight]);
2795
2871
  const handlePointerMove = React.useCallback((e) => {
2796
- if (interactionsLocked || !visibleData.length)
2872
+ if (interactionsLocked || !visibleData.length) {
2873
+ setCrosshairMeta(null);
2797
2874
  return;
2875
+ }
2798
2876
  const rect = e.currentTarget.getBoundingClientRect();
2799
2877
  const x = e.clientX - rect.left;
2800
2878
  const y = e.clientY - rect.top;
2801
2879
  setClickedPrice(null);
2802
2880
  if (cursorType !== 'cross') {
2881
+ setCrosshairMeta(null);
2803
2882
  return;
2804
2883
  }
2805
2884
  if (!isDraggingRef.current) {
2806
2885
  const { min, max } = priceWindowRef.current;
2807
- const snappedX = coordsRef.current.snapToCandle(x, chartWidth, visibleData.length);
2808
- const snappedY = coordsRef.current.snapToPrice(y, chartHeight, min, max);
2809
- mousePosRef.current = {
2810
- x: coordsRef.current.timeToPixel(snappedX, chartWidth, visibleData.length),
2811
- y: coordsRef.current.priceToPixel(snappedY, chartHeight, min, max)
2812
- };
2886
+ const rawIndex = pixelToCandleIndex(x, chartWidth, visibleData.length);
2887
+ const hoveredIndex = clampCandleIndex(rawIndex, visibleData.length);
2888
+ const hoveredCandle = visibleData[hoveredIndex];
2889
+ const snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, min, max);
2890
+ const yPixel = coordsRef.current.priceToPixel(snappedPrice, chartHeight, min, max);
2891
+ const xPixel = magnetEnabled
2892
+ ? getCandleCenter(hoveredIndex, chartWidth, visibleData.length)
2893
+ : clampPixelToChart(x, chartWidth);
2894
+ mousePosRef.current = { x: xPixel, y: yPixel };
2895
+ setCrosshairMeta({
2896
+ price: snappedPrice,
2897
+ timestamp: hoveredCandle?.timestamp ?? null,
2898
+ x: xPixel,
2899
+ y: yPixel,
2900
+ candle: hoveredCandle ?? null
2901
+ });
2813
2902
  showCrosshairRef.current = true;
2814
2903
  requestAnimationFrame(drawCrosshair);
2815
2904
  }
2816
- }, [visibleData, cursorType, chartWidth, chartHeight, drawCrosshair, interactionsLocked]);
2905
+ }, [visibleData, cursorType, chartWidth, chartHeight, drawCrosshair, interactionsLocked, magnetEnabled]);
2817
2906
  const handleMouseLeave = React.useCallback(() => {
2818
2907
  showCrosshairRef.current = false;
2819
2908
  isDraggingRef.current = false;
2820
2909
  dragSampleRef.current.velocity = 0;
2821
2910
  dragSampleRef.current.lastTs = 0;
2911
+ setCrosshairMeta(null);
2822
2912
  requestAnimationFrame(() => {
2823
2913
  const overlayCtx = overlayRef.current?.getContext('2d');
2824
2914
  if (overlayCtx) {
@@ -2862,15 +2952,21 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
2862
2952
  ? { minPrice: priceWindowRef.current.min, maxPrice: priceWindowRef.current.max }
2863
2953
  : getDataBounds(targetData);
2864
2954
  const dataLength = Math.max(targetData.length, 1);
2865
- if (magnetEnabled && visibleData.length) {
2866
- const snappedIndex = coordsRef.current.snapToCandle(x, chartWidth, dataLength);
2867
- const snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice, 0.5);
2868
- x = coordsRef.current.timeToPixel(snappedIndex, chartWidth, dataLength);
2869
- y = coordsRef.current.priceToPixel(snappedPrice, chartHeight, bounds.minPrice, bounds.maxPrice);
2870
- }
2871
- const price = coordsRef.current.pixelToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice);
2872
- const time = coordsRef.current.pixelToTime(x, chartWidth, dataLength);
2873
- return { x, y, price, time };
2955
+ let snappedPrice = coordsRef.current.snapToPrice(y, chartHeight, bounds.minPrice, bounds.maxPrice, 0.5);
2956
+ let snappedY = y;
2957
+ if (magnetEnabled && targetData.length) {
2958
+ const snappedIndex = clampCandleIndex(pixelToCandleIndex(x, chartWidth, dataLength), dataLength);
2959
+ x = getCandleCenter(snappedIndex, chartWidth, dataLength);
2960
+ snappedY = coordsRef.current.priceToPixel(snappedPrice, chartHeight, bounds.minPrice, bounds.maxPrice);
2961
+ y = snappedY;
2962
+ }
2963
+ else {
2964
+ x = clampPixelToChart(x, chartWidth);
2965
+ }
2966
+ const price = coordsRef.current.pixelToPrice(snappedY, chartHeight, bounds.minPrice, bounds.maxPrice);
2967
+ const hoveredIndex = clampCandleIndex(pixelToCandleIndex(x, chartWidth, dataLength), dataLength);
2968
+ const time = targetData[hoveredIndex]?.timestamp ?? hoveredIndex;
2969
+ return { x, y: snappedY, price, time };
2874
2970
  }, [visibleData, storeData, magnetEnabled, chartWidth, chartHeight, interactionsLocked]);
2875
2971
  const tryEraseDrawing = React.useCallback((x, y) => {
2876
2972
  for (let i = drawings.length - 1; i >= 0; i--) {
@@ -3081,7 +3177,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3081
3177
  }
3082
3178
  if (visibleData.length) {
3083
3179
  const { min, max } = priceWindowRef.current;
3084
- const snappedIndex = coordsRef.current.snapToCandle(rawX, chartWidth, visibleData.length);
3180
+ const snappedIndex = clampCandleIndex(pixelToCandleIndex(rawX, chartWidth, visibleData.length), visibleData.length);
3085
3181
  setSelectedCandleIndex(snappedIndex);
3086
3182
  const price = coordsRef.current.pixelToPrice(rawY, chartHeight, min, max);
3087
3183
  setClickedPrice({ x: rawX, y: rawY, price });
@@ -3224,6 +3320,87 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3224
3320
  { value: 'columns', label: 'Columnas', icon: seriesIconMap.columns },
3225
3321
  { value: 'high-low', label: 'High/Low', icon: seriesIconMap['high-low'] }
3226
3322
  ];
3323
+ const headerButtons = (React.createElement(React.Fragment, null,
3324
+ React.createElement("button", { onClick: () => setShowSeriesMenu(!showSeriesMenu), style: {
3325
+ width: '38px',
3326
+ height: '38px',
3327
+ borderRadius: '999px',
3328
+ border: `1px solid ${showSeriesMenu ? activeTheme.accent : iconBaseBg}`,
3329
+ background: showSeriesMenu ? activeTheme.accent : iconBaseBg,
3330
+ color: iconBaseColor,
3331
+ display: 'flex',
3332
+ alignItems: 'center',
3333
+ justifyContent: 'center',
3334
+ cursor: 'pointer',
3335
+ transition: 'all 0.2s'
3336
+ }, title: strings.buttons.series, "aria-label": strings.buttons.series }, seriesIconMap[seriesType] ?? React.createElement(BsGraphUp, null)),
3337
+ React.createElement("button", { onClick: toggleIndicatorsPanel, style: {
3338
+ background: showIndicatorsPanel ? '#2563eb' : 'rgba(15,23,42,0.6)',
3339
+ color: '#e2e8f0',
3340
+ border: '1px solid #1f2937',
3341
+ borderRadius: '999px',
3342
+ padding: '8px 14px',
3343
+ fontSize: '12px',
3344
+ fontWeight: 600,
3345
+ cursor: 'pointer',
3346
+ transition: 'all 0.2s'
3347
+ } }, strings.buttons.indicators),
3348
+ React.createElement("button", { onClick: () => setShowConfigPanel(!showConfigPanel), style: {
3349
+ width: '38px',
3350
+ height: '38px',
3351
+ borderRadius: '999px',
3352
+ border: `1px solid ${showConfigPanel ? activeTheme.accent : iconBaseBg}`,
3353
+ background: showConfigPanel ? activeTheme.accent : iconBaseBg,
3354
+ color: iconBaseColor,
3355
+ display: 'flex',
3356
+ alignItems: 'center',
3357
+ justifyContent: 'center',
3358
+ cursor: 'pointer',
3359
+ transition: 'all 0.2s'
3360
+ }, title: strings.buttons.config, "aria-label": strings.buttons.config },
3361
+ React.createElement(BsGear, null)),
3362
+ React.createElement("button", { onClick: () => setIsFullscreen(prev => !prev), style: {
3363
+ width: '38px',
3364
+ height: '38px',
3365
+ borderRadius: '999px',
3366
+ border: `1px solid ${isFullscreen ? activeTheme.accent : iconBaseBg}`,
3367
+ background: isFullscreen ? activeTheme.accent : iconBaseBg,
3368
+ color: iconBaseColor,
3369
+ display: 'flex',
3370
+ alignItems: 'center',
3371
+ justifyContent: 'center',
3372
+ cursor: 'pointer',
3373
+ transition: 'all 0.2s'
3374
+ }, title: isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter, "aria-label": isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter },
3375
+ React.createElement(BsArrowsFullscreen, null)),
3376
+ React.createElement("button", { onClick: takeScreenshot, style: {
3377
+ width: '38px',
3378
+ height: '38px',
3379
+ borderRadius: '999px',
3380
+ border: `1px solid ${iconBaseBg}`,
3381
+ background: iconBaseBg,
3382
+ color: iconBaseColor,
3383
+ display: 'flex',
3384
+ alignItems: 'center',
3385
+ justifyContent: 'center',
3386
+ cursor: 'pointer',
3387
+ transition: 'all 0.2s'
3388
+ }, title: strings.buttons.screenshot, "aria-label": strings.buttons.screenshot },
3389
+ React.createElement(BsCamera, null)),
3390
+ showHeaderStats && (React.createElement("button", { onClick: () => setShowQuickTips(prev => !prev), style: {
3391
+ width: '38px',
3392
+ height: '38px',
3393
+ borderRadius: '999px',
3394
+ border: `1px solid ${showQuickTips ? activeTheme.accent : iconBaseBg}`,
3395
+ background: showQuickTips ? activeTheme.accent : iconBaseBg,
3396
+ color: iconBaseColor,
3397
+ display: 'flex',
3398
+ alignItems: 'center',
3399
+ justifyContent: 'center',
3400
+ cursor: 'pointer',
3401
+ transition: 'all 0.2s'
3402
+ }, title: strings.buttons.help, "aria-label": strings.buttons.help },
3403
+ React.createElement(BsQuestionCircle, null)))));
3227
3404
  const cursorCss = isDraggingRef.current
3228
3405
  ? 'grabbing'
3229
3406
  : cursorType === 'cross'
@@ -3252,13 +3429,12 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3252
3429
  background: `linear-gradient(120deg, ${activeTheme.heroFrom} 0%, ${activeTheme.heroTo} 100%)`,
3253
3430
  borderBottom: `1px solid ${activeTheme.panelBorder}`,
3254
3431
  display: 'flex',
3255
- flexWrap: 'wrap',
3432
+ flexDirection: 'column',
3256
3433
  gap: isMobile ? '12px' : '16px',
3257
- alignItems: 'center',
3258
3434
  padding: isMobile ? '12px 16px' : '16px 20px',
3259
3435
  boxShadow: elevatedShadow
3260
3436
  }, initial: { opacity: 0, y: -15 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.3 } },
3261
- React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '12px', alignItems: 'center', minWidth: 0 } },
3437
+ showHeaderStats && (React.createElement("div", { style: { display: 'flex', flexWrap: 'wrap', gap: '12px', alignItems: 'center', minWidth: 0 } },
3262
3438
  React.createElement("div", { style: { background: activeTheme.cardBg, border: `1px solid ${activeTheme.cardBorder}`, borderRadius: '14px', padding: '10px 16px', minWidth: '160px', color: activeTheme.textPrimary } },
3263
3439
  React.createElement("div", { style: { fontSize: '11px', textTransform: 'uppercase', letterSpacing: '0.08em', color: activeTheme.textSecondary, marginBottom: '4px' } }, strings.symbolLabel),
3264
3440
  React.createElement("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: activeTheme.textPrimary, fontWeight: 600, gap: '10px' } },
@@ -3290,150 +3466,118 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3290
3466
  { label: 'V', value: referenceCandle.volume ?? 0 }
3291
3467
  ].map(item => (React.createElement("div", { key: item.label, style: { display: 'flex', flexDirection: 'column', minWidth: '50px' } },
3292
3468
  React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase' } }, item.label),
3293
- React.createElement("span", { style: { color: '#e2e8f0', fontWeight: 600 } }, item.value.toFixed(2)))))))),
3294
- React.createElement("div", { style: {
3469
+ React.createElement("span", { style: { color: '#e2e8f0', fontWeight: 600 } }, item.value.toFixed(2))))))))),
3470
+ compactHeader ? (React.createElement("div", { style: {
3471
+ width: '100%',
3295
3472
  display: 'flex',
3473
+ flexWrap: isMobile ? 'wrap' : 'nowrap',
3296
3474
  alignItems: 'center',
3297
- gap: '12px',
3298
- padding: '10px 14px',
3299
- background: activeTheme.panelBg,
3300
- borderRadius: '16px',
3301
- border: `1px solid ${activeTheme.panelBorder}`,
3302
- overflowX: 'auto'
3475
+ gap: isMobile ? '12px' : '16px'
3303
3476
  } },
3304
- React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' } },
3305
- React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3306
- 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)),
3307
- 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: {
3308
- background: timeframe === tf ? activeTheme.accent : activeTheme.panelBg,
3309
- color: timeframe === tf ? '#f8fafc' : activeTheme.textSecondary,
3310
- border: `1px solid ${timeframe === tf ? activeTheme.accent : activeTheme.panelBorder}`,
3311
- borderRadius: '10px',
3312
- padding: '6px 12px',
3313
- fontSize: '12px',
3314
- fontWeight: 600,
3315
- cursor: 'pointer',
3316
- transition: 'all 0.2s',
3317
- flex: '0 0 auto',
3318
- whiteSpace: 'nowrap'
3319
- } }, tf)))),
3320
- React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px', color: '#cbd5f5', whiteSpace: 'nowrap', flexShrink: 0 } },
3321
- React.createElement(BsClockHistory, null),
3322
- React.createElement("span", null,
3323
- strings.axis.time,
3324
- ": ",
3325
- currentTimeLabel)),
3326
- React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3327
- background: 'rgba(15,23,42,0.8)',
3328
- color: '#e2e8f0',
3329
- border: '1px solid #1f2937',
3330
- padding: '10px 36px 10px 12px',
3331
- borderRadius: '12px',
3332
- cursor: 'pointer',
3333
- fontSize: '12px',
3334
- fontWeight: 600,
3335
- appearance: 'none',
3336
- 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")`,
3337
- backgroundPosition: 'right 10px center',
3338
- backgroundRepeat: 'no-repeat',
3339
- backgroundSize: '14px',
3340
- minWidth: '120px',
3341
- flexShrink: 0
3342
- } },
3343
- React.createElement("option", { value: "1m" }, "1m"),
3344
- React.createElement("option", { value: "3m" }, "3m"),
3345
- React.createElement("option", { value: "5m" }, "5m"),
3346
- React.createElement("option", { value: "15m" }, "15m"),
3347
- React.createElement("option", { value: "30m" }, "30m"),
3348
- React.createElement("option", { value: "1h" }, "1h"),
3349
- React.createElement("option", { value: "4h" }, "4h"),
3350
- React.createElement("option", { value: "12h" }, "12h"),
3351
- React.createElement("option", { value: "1D" }, "1D"),
3352
- React.createElement("option", { value: "3D" }, "3D"),
3353
- React.createElement("option", { value: "1W" }, "1W"),
3354
- React.createElement("option", { value: "1M" }, "1M"))),
3355
- React.createElement("div", { style: { marginLeft: 'auto', display: 'flex', flexWrap: 'wrap', gap: '8px', alignItems: 'center' } },
3356
- React.createElement("button", { onClick: () => setShowSeriesMenu(!showSeriesMenu), style: {
3357
- width: '38px',
3358
- height: '38px',
3359
- borderRadius: '999px',
3360
- border: `1px solid ${showSeriesMenu ? activeTheme.accent : iconBaseBg}`,
3361
- background: showSeriesMenu ? activeTheme.accent : iconBaseBg,
3362
- color: iconBaseColor,
3477
+ React.createElement("div", { style: {
3363
3478
  display: 'flex',
3364
3479
  alignItems: 'center',
3365
- justifyContent: 'center',
3366
- cursor: 'pointer',
3367
- transition: 'all 0.2s'
3368
- }, title: strings.buttons.series, "aria-label": strings.buttons.series }, seriesIconMap[seriesType] ?? React.createElement(BsGraphUp, null)),
3369
- React.createElement("button", { onClick: toggleIndicatorsPanel, style: {
3370
- background: showIndicatorsPanel ? '#2563eb' : 'rgba(15,23,42,0.6)',
3371
- color: '#e2e8f0',
3372
- border: '1px solid #1f2937',
3373
- borderRadius: '999px',
3480
+ gap: '10px',
3374
3481
  padding: '8px 14px',
3375
- fontSize: '12px',
3376
- fontWeight: 600,
3377
- cursor: 'pointer',
3378
- transition: 'all 0.2s'
3379
- } }, strings.buttons.indicators),
3380
- React.createElement("button", { onClick: () => setShowConfigPanel(!showConfigPanel), style: {
3381
- width: '38px',
3382
- height: '38px',
3383
- borderRadius: '999px',
3384
- border: `1px solid ${showConfigPanel ? activeTheme.accent : iconBaseBg}`,
3385
- background: showConfigPanel ? activeTheme.accent : iconBaseBg,
3386
- color: iconBaseColor,
3387
- display: 'flex',
3388
- alignItems: 'center',
3389
- justifyContent: 'center',
3390
- cursor: 'pointer',
3391
- transition: 'all 0.2s'
3392
- }, title: strings.buttons.config, "aria-label": strings.buttons.config },
3393
- React.createElement(BsGear, null)),
3394
- React.createElement("button", { onClick: () => setIsFullscreen(prev => !prev), style: {
3395
- width: '38px',
3396
- height: '38px',
3397
- borderRadius: '999px',
3398
- border: `1px solid ${isFullscreen ? activeTheme.accent : iconBaseBg}`,
3399
- background: isFullscreen ? activeTheme.accent : iconBaseBg,
3400
- color: iconBaseColor,
3401
- display: 'flex',
3402
- alignItems: 'center',
3403
- justifyContent: 'center',
3404
- cursor: 'pointer',
3405
- transition: 'all 0.2s'
3406
- }, title: isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter, "aria-label": isFullscreen ? strings.buttons.fullscreenExit : strings.buttons.fullscreenEnter },
3407
- React.createElement(BsArrowsFullscreen, null)),
3408
- React.createElement("button", { onClick: takeScreenshot, style: {
3409
- width: '38px',
3410
- height: '38px',
3411
- borderRadius: '999px',
3412
- border: `1px solid ${iconBaseBg}`,
3413
- background: iconBaseBg,
3414
- color: iconBaseColor,
3415
- display: 'flex',
3416
- alignItems: 'center',
3417
- justifyContent: 'center',
3418
- cursor: 'pointer',
3419
- transition: 'all 0.2s'
3420
- }, title: strings.buttons.screenshot, "aria-label": strings.buttons.screenshot },
3421
- React.createElement(BsCamera, null)),
3422
- React.createElement("button", { onClick: () => setShowQuickTips(prev => !prev), style: {
3423
- width: '38px',
3424
- height: '38px',
3425
- borderRadius: '999px',
3426
- border: `1px solid ${showQuickTips ? activeTheme.accent : iconBaseBg}`,
3427
- background: showQuickTips ? activeTheme.accent : iconBaseBg,
3428
- color: iconBaseColor,
3482
+ background: activeTheme.panelBg,
3483
+ borderRadius: '16px',
3484
+ border: `1px solid ${activeTheme.panelBorder}`,
3485
+ flex: 1,
3486
+ minWidth: isMobile ? '100%' : '320px'
3487
+ } },
3488
+ React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3489
+ React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3490
+ background: 'rgba(15,23,42,0.8)',
3491
+ color: '#e2e8f0',
3492
+ border: '1px solid #1f2937',
3493
+ padding: '8px 32px 8px 12px',
3494
+ borderRadius: '12px',
3495
+ cursor: 'pointer',
3496
+ fontSize: '12px',
3497
+ fontWeight: 600,
3498
+ appearance: 'none',
3499
+ 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")`,
3500
+ backgroundPosition: 'right 10px center',
3501
+ backgroundRepeat: 'no-repeat',
3502
+ backgroundSize: '14px',
3503
+ minWidth: '140px'
3504
+ } },
3505
+ React.createElement("option", { value: "1m" }, "1m"),
3506
+ React.createElement("option", { value: "3m" }, "3m"),
3507
+ React.createElement("option", { value: "5m" }, "5m"),
3508
+ React.createElement("option", { value: "15m" }, "15m"),
3509
+ React.createElement("option", { value: "30m" }, "30m"),
3510
+ React.createElement("option", { value: "1h" }, "1h"),
3511
+ React.createElement("option", { value: "4h" }, "4h"),
3512
+ React.createElement("option", { value: "12h" }, "12h"),
3513
+ React.createElement("option", { value: "1D" }, "1D"),
3514
+ React.createElement("option", { value: "3D" }, "3D"),
3515
+ React.createElement("option", { value: "1W" }, "1W"),
3516
+ React.createElement("option", { value: "1M" }, "1M"))),
3517
+ React.createElement("div", { style: { marginLeft: isMobile ? 0 : 'auto', display: 'flex', flexWrap: isMobile ? 'wrap' : 'nowrap', gap: '8px', alignItems: 'center' } }, headerButtons))) : (React.createElement(React.Fragment, null,
3518
+ React.createElement("div", { style: {
3429
3519
  display: 'flex',
3430
3520
  alignItems: 'center',
3431
- justifyContent: 'center',
3432
- cursor: 'pointer',
3433
- transition: 'all 0.2s'
3434
- }, title: strings.buttons.help, "aria-label": strings.buttons.help },
3435
- React.createElement(BsQuestionCircle, null)))),
3436
- derivedStats && (React.createElement("div", { style: { padding: '16px 20px 0', background: activeTheme.panelBg, borderBottom: `1px solid ${activeTheme.panelBorder}` } },
3521
+ gap: '12px',
3522
+ padding: '10px 14px',
3523
+ background: activeTheme.panelBg,
3524
+ borderRadius: '16px',
3525
+ border: `1px solid ${activeTheme.panelBorder}`,
3526
+ overflowX: 'auto'
3527
+ } },
3528
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', whiteSpace: 'nowrap' } },
3529
+ React.createElement("span", { style: { fontSize: '10px', letterSpacing: '0.08em', textTransform: 'uppercase', color: '#94a3b8' } }, strings.timeframeTitle),
3530
+ 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)),
3531
+ 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: {
3532
+ background: timeframe === tf ? activeTheme.accent : activeTheme.panelBg,
3533
+ color: timeframe === tf ? '#f8fafc' : activeTheme.textSecondary,
3534
+ border: `1px solid ${timeframe === tf ? activeTheme.accent : activeTheme.panelBorder}`,
3535
+ borderRadius: '10px',
3536
+ padding: '6px 12px',
3537
+ fontSize: '12px',
3538
+ fontWeight: 600,
3539
+ cursor: 'pointer',
3540
+ transition: 'all 0.2s',
3541
+ flex: '0 0 auto',
3542
+ whiteSpace: 'nowrap'
3543
+ } }, tf)))),
3544
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px', color: '#cbd5f5', whiteSpace: 'nowrap', flexShrink: 0 } },
3545
+ React.createElement(BsClockHistory, null),
3546
+ React.createElement("span", null,
3547
+ strings.axis.time,
3548
+ ": ",
3549
+ currentTimeLabel)),
3550
+ React.createElement("select", { value: timeframe, onChange: (e) => handleTimeframeChange(e.target.value), style: {
3551
+ background: 'rgba(15,23,42,0.8)',
3552
+ color: '#e2e8f0',
3553
+ border: '1px solid #1f2937',
3554
+ padding: '10px 36px 10px 12px',
3555
+ borderRadius: '12px',
3556
+ cursor: 'pointer',
3557
+ fontSize: '12px',
3558
+ fontWeight: 600,
3559
+ appearance: 'none',
3560
+ 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")`,
3561
+ backgroundPosition: 'right 10px center',
3562
+ backgroundRepeat: 'no-repeat',
3563
+ backgroundSize: '14px',
3564
+ minWidth: '120px',
3565
+ flexShrink: 0
3566
+ } },
3567
+ React.createElement("option", { value: "1m" }, "1m"),
3568
+ React.createElement("option", { value: "3m" }, "3m"),
3569
+ React.createElement("option", { value: "5m" }, "5m"),
3570
+ React.createElement("option", { value: "15m" }, "15m"),
3571
+ React.createElement("option", { value: "30m" }, "30m"),
3572
+ React.createElement("option", { value: "1h" }, "1h"),
3573
+ React.createElement("option", { value: "4h" }, "4h"),
3574
+ React.createElement("option", { value: "12h" }, "12h"),
3575
+ React.createElement("option", { value: "1D" }, "1D"),
3576
+ React.createElement("option", { value: "3D" }, "3D"),
3577
+ React.createElement("option", { value: "1W" }, "1W"),
3578
+ React.createElement("option", { value: "1M" }, "1M"))),
3579
+ React.createElement("div", { style: { marginLeft: 'auto', display: 'flex', flexWrap: 'wrap', gap: '8px', alignItems: 'center' } }, headerButtons)))),
3580
+ showStats && derivedStats && (React.createElement("div", { style: { padding: '16px 20px 0', background: activeTheme.panelBg, borderBottom: `1px solid ${activeTheme.panelBorder}` } },
3437
3581
  React.createElement("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))', gap: '12px' } },
3438
3582
  React.createElement("div", { style: { ...metricCardStyle, background: activeTheme.cardBg, border: `1px solid ${activeTheme.cardBorder}` } },
3439
3583
  React.createElement("span", { style: { ...metricLabelStyle, color: activeTheme.textSecondary } }, "Trading range"),
@@ -3461,7 +3605,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3461
3605
  "Latency ~",
3462
3606
  latencyMs,
3463
3607
  "ms"))))),
3464
- React.createElement(framerMotion.AnimatePresence, null, showQuickTips && (React.createElement(framerMotion.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, style: {
3608
+ React.createElement(framerMotion.AnimatePresence, null, showHeaderStats && showQuickTips && (React.createElement(framerMotion.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -10 }, transition: { duration: 0.2 }, style: {
3465
3609
  position: (isFullscreen ? 'fixed' : 'absolute'),
3466
3610
  top: isFullscreen ? 80 : 140,
3467
3611
  left: isFullscreen ? 80 : 140,
@@ -3559,7 +3703,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3559
3703
  minHeight: 0,
3560
3704
  alignItems: 'stretch'
3561
3705
  } },
3562
- React.createElement("div", { style: { width: isMobile ? '60px' : '88px', flexShrink: 0, height: '100%' } },
3706
+ showDrawingToolbar && (React.createElement("div", { style: { width: isMobile ? '60px' : '88px', flexShrink: 0, height: '100%' } },
3563
3707
  React.createElement("div", { style: {
3564
3708
  height: '100%',
3565
3709
  borderRadius: '24px',
@@ -3570,7 +3714,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3570
3714
  alignItems: 'center',
3571
3715
  padding: isMobile ? '12px 0' : '16px 0'
3572
3716
  } },
3573
- React.createElement(DrawingToolbar, null))),
3717
+ React.createElement(DrawingToolbar, null)))),
3574
3718
  React.createElement("div", { ref: plotAreaRef, style: {
3575
3719
  flex: 1,
3576
3720
  position: 'relative',
@@ -3611,6 +3755,65 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3611
3755
  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' } },
3612
3756
  React.createElement("span", { style: { textTransform: 'uppercase', fontSize: '10px', letterSpacing: '0.08em' } }, strings.axis.time),
3613
3757
  React.createElement("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', color: activeTheme.textPrimary } }, timeLabels.map((label, i) => (React.createElement("div", { key: i }, label))))),
3758
+ crosshairMeta?.candle && (React.createElement("div", { style: {
3759
+ position: 'absolute',
3760
+ top: 16,
3761
+ left: 16,
3762
+ padding: '10px 14px',
3763
+ borderRadius: '14px',
3764
+ background: activeTheme.overlayBg,
3765
+ border: `1px solid ${activeTheme.panelBorder}`,
3766
+ boxShadow: '0 12px 30px rgba(0,0,0,0.45)',
3767
+ color: activeTheme.textPrimary,
3768
+ fontSize: '11px',
3769
+ letterSpacing: '0.04em',
3770
+ display: 'flex',
3771
+ gap: '14px',
3772
+ pointerEvents: 'none',
3773
+ zIndex: 60
3774
+ } },
3775
+ ([
3776
+ { label: 'O', value: crosshairMeta.candle.open },
3777
+ { label: 'H', value: crosshairMeta.candle.high },
3778
+ { label: 'L', value: crosshairMeta.candle.low },
3779
+ { label: 'C', value: crosshairMeta.candle.close }
3780
+ ]).map((item) => (React.createElement("div", { key: item.label, style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
3781
+ React.createElement("span", { style: { color: activeTheme.textSecondary } }, item.label),
3782
+ React.createElement("span", { style: { fontWeight: 600 } }, numberFormatter.format(item.value))))),
3783
+ React.createElement("div", { style: { display: 'flex', flexDirection: 'column', gap: '2px' } },
3784
+ React.createElement("span", { style: { color: activeTheme.textSecondary } }, "V"),
3785
+ React.createElement("span", { style: { fontWeight: 600 } }, shortNumberFormatter.format(crosshairMeta.candle.volume ?? 0))))),
3786
+ crosshairMeta && (React.createElement(React.Fragment, null,
3787
+ React.createElement("div", { style: {
3788
+ position: 'absolute',
3789
+ right: `${priceScaleWidth}px`,
3790
+ top: `${Math.min(Math.max(crosshairMeta.y, 12), overlayHeight - 12)}px`,
3791
+ transform: 'translate(0, -50%)',
3792
+ background: activeTheme.scaleBg,
3793
+ border: `1px solid ${activeTheme.panelBorder}`,
3794
+ borderRadius: '6px 0 0 6px',
3795
+ padding: '2px 8px',
3796
+ fontSize: '11px',
3797
+ fontWeight: 600,
3798
+ color: activeTheme.textPrimary,
3799
+ pointerEvents: 'none',
3800
+ boxShadow: '0 8px 20px rgba(0,0,0,0.4)'
3801
+ } }, numberFormatter.format(crosshairMeta.price)),
3802
+ crosshairMeta.timestamp && (React.createElement("div", { style: {
3803
+ position: 'absolute',
3804
+ left: `${Math.min(Math.max(crosshairMeta.x, 40), chartWidth - 40)}px`,
3805
+ bottom: `${timeScaleHeight}px`,
3806
+ transform: 'translate(-50%, 50%)',
3807
+ background: activeTheme.scaleBg,
3808
+ border: `1px solid ${activeTheme.panelBorder}`,
3809
+ borderRadius: '6px',
3810
+ padding: '2px 8px',
3811
+ fontSize: '10px',
3812
+ letterSpacing: '0.05em',
3813
+ color: activeTheme.textPrimary,
3814
+ pointerEvents: 'none',
3815
+ boxShadow: '0 8px 20px rgba(0,0,0,0.35)'
3816
+ } }, timeFormatter.format(new Date(crosshairMeta.timestamp)))))),
3614
3817
  clickedPrice && (React.createElement("div", { style: {
3615
3818
  position: 'absolute',
3616
3819
  background: '#1e222d',
@@ -3793,7 +3996,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3793
3996
  React.createElement("span", null, field.label),
3794
3997
  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' } })))))),
3795
3998
  React.createElement("div", { style: { fontSize: '12px', color: activeTheme.textSecondary } }, strings.config.soon))),
3796
- 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 } },
3999
+ 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 } },
3797
4000
  React.createElement("span", null,
3798
4001
  "Latency ",
3799
4002
  latencyMs,
@@ -3802,7 +4005,7 @@ const TradingViewChart = ({ data, symbol = 'BTC/USDT', onTimeframeChange }) => {
3802
4005
  "Session ",
3803
4006
  derivedStats?.session ?? 'Global'),
3804
4007
  React.createElement("span", null, "Feed Binance Composite"),
3805
- React.createElement("span", null, "Security AES-256"))));
4008
+ React.createElement("span", null, "Security AES-256")))));
3806
4009
  };
3807
4010
  var TradingViewChart_default = React.memo(TradingViewChart);
3808
4011