react-klinecharts-ui 0.6.0 → 1.0.0

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
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkFSHI2ZDB_cjs = require('./chunk-FSHI2ZDB.cjs');
3
+ var chunkLGYYJ2GP_cjs = require('./chunk-LGYYJ2GP.cjs');
4
4
  var react = require('react');
5
- var reactKlinecharts = require('react-klinecharts');
5
+ var klinecharts = require('klinecharts');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
8
  var KlinechartsUIStateContext = react.createContext(null);
@@ -64,6 +64,25 @@ function reducer(state, action) {
64
64
  return { ...state, indicatorVisibility: action.visibility };
65
65
  case "SET_ALERTS":
66
66
  return { ...state, alerts: action.alerts };
67
+ case "ADD_ALERT":
68
+ return { ...state, alerts: [...state.alerts, action.alert] };
69
+ case "REMOVE_ALERT":
70
+ return {
71
+ ...state,
72
+ alerts: state.alerts.filter((a) => a.id !== action.id)
73
+ };
74
+ case "CLEAR_ALERTS":
75
+ return { ...state, alerts: [] };
76
+ case "MARK_ALERT_TRIGGERED": {
77
+ if (action.ids.length === 0) return state;
78
+ const triggered = new Set(action.ids);
79
+ return {
80
+ ...state,
81
+ alerts: state.alerts.map(
82
+ (a) => triggered.has(a.id) ? { ...a, triggered: true } : a
83
+ )
84
+ };
85
+ }
67
86
  case "SET_MEASURE":
68
87
  return { ...state, measure: { ...state.measure, ...action.measure } };
69
88
  case "SET_REPLAY":
@@ -153,7 +172,6 @@ function KlinechartsUIProvider({
153
172
  const replayIndexRef = react.useRef(0);
154
173
  const alertPrevCloseRef = react.useRef(null);
155
174
  const stateRef = react.useRef(state);
156
- stateRef.current = state;
157
175
  const callbacksRef = react.useRef({
158
176
  onStateChange,
159
177
  onSymbolChange,
@@ -163,15 +181,18 @@ function KlinechartsUIProvider({
163
181
  onMainIndicatorsChange,
164
182
  onSubIndicatorsChange
165
183
  });
166
- callbacksRef.current = {
167
- onStateChange,
168
- onSymbolChange,
169
- onPeriodChange,
170
- onThemeChange,
171
- onTimezoneChange,
172
- onMainIndicatorsChange,
173
- onSubIndicatorsChange
174
- };
184
+ react.useEffect(() => {
185
+ stateRef.current = state;
186
+ callbacksRef.current = {
187
+ onStateChange,
188
+ onSymbolChange,
189
+ onPeriodChange,
190
+ onThemeChange,
191
+ onTimezoneChange,
192
+ onMainIndicatorsChange,
193
+ onSubIndicatorsChange
194
+ };
195
+ });
175
196
  const enhancedDispatch = react.useCallback((action) => {
176
197
  const prevState = stateRef.current;
177
198
  const newState = reducer(prevState, action);
@@ -201,11 +222,17 @@ function KlinechartsUIProvider({
201
222
  }
202
223
  }, []);
203
224
  const extraOverlaysRef = react.useRef(extraOverlays);
225
+ const datafeedRef = react.useRef(datafeed);
226
+ const onSettingsChangeRef = react.useRef(onSettingsChange);
227
+ react.useEffect(() => {
228
+ datafeedRef.current = datafeed;
229
+ onSettingsChangeRef.current = onSettingsChange;
230
+ });
204
231
  react.useEffect(() => {
205
232
  if (shouldRegister) {
206
- chunkFSHI2ZDB_cjs.registerExtensions();
233
+ chunkLGYYJ2GP_cjs.registerExtensions();
207
234
  }
208
- extraOverlaysRef.current?.forEach((overlay) => reactKlinecharts.registerOverlay(overlay));
235
+ extraOverlaysRef.current?.forEach((overlay) => klinecharts.registerOverlay(overlay));
209
236
  }, [shouldRegister]);
210
237
  react.useEffect(() => {
211
238
  if (state.chart && state.styles) {
@@ -225,26 +252,29 @@ function KlinechartsUIProvider({
225
252
  alertPrevCloseRef.current = currentClose;
226
253
  if (prevClose === null) return;
227
254
  const alerts = stateRef.current.alerts;
228
- let changed = false;
229
- const next = alerts.map((alert) => {
230
- if (alert.triggered) return alert;
255
+ const triggeredIds = [];
256
+ for (const alert of alerts) {
257
+ if (alert.triggered) continue;
231
258
  const crossedUp = prevClose < alert.price && currentClose >= alert.price;
232
259
  const crossedDown = prevClose > alert.price && currentClose <= alert.price;
233
260
  const shouldTrigger = alert.condition === "crossing_up" ? crossedUp : alert.condition === "crossing_down" ? crossedDown : crossedUp || crossedDown;
234
261
  if (shouldTrigger) {
235
- changed = true;
236
- const triggered = { ...alert, triggered: true };
237
- alertTriggeredListenerRef.current?.(triggered);
238
- return triggered;
262
+ triggeredIds.push(alert.id);
263
+ alertTriggeredListenerRef.current?.({ ...alert, triggered: true });
239
264
  }
240
- return alert;
241
- });
242
- if (changed) {
243
- enhancedDispatch({ type: "SET_ALERTS", alerts: next });
265
+ }
266
+ if (triggeredIds.length > 0) {
267
+ enhancedDispatch({
268
+ type: "MARK_ALERT_TRIGGERED",
269
+ ids: triggeredIds
270
+ });
244
271
  }
245
272
  }, 1e3);
246
273
  return () => clearInterval(interval);
247
274
  }, [state.chart, hasAlerts, enhancedDispatch]);
275
+ react.useEffect(() => {
276
+ alertPrevCloseRef.current = null;
277
+ }, [state.symbol, state.period]);
248
278
  react.useEffect(() => {
249
279
  return () => {
250
280
  if (replayIntervalRef.current !== null) {
@@ -256,8 +286,8 @@ function KlinechartsUIProvider({
256
286
  const dispatchValue = react.useMemo(
257
287
  () => ({
258
288
  dispatch: enhancedDispatch,
259
- datafeed,
260
- onSettingsChange,
289
+ datafeed: datafeedRef.current,
290
+ onSettingsChange: onSettingsChangeRef.current,
261
291
  fullscreenContainerRef,
262
292
  undoRedoListenerRef,
263
293
  alertTriggeredListenerRef,
@@ -265,7 +295,8 @@ function KlinechartsUIProvider({
265
295
  replaySavedDataRef,
266
296
  replayIndexRef
267
297
  }),
268
- [enhancedDispatch, datafeed, onSettingsChange]
298
+ // eslint-disable-next-line react-hooks/exhaustive-deps
299
+ [enhancedDispatch]
269
300
  );
270
301
  return /* @__PURE__ */ jsxRuntime.jsx(KlinechartsUIStateContext.Provider, { value: state, children: /* @__PURE__ */ jsxRuntime.jsx(KlinechartsUIDispatchContext.Provider, { value: dispatchValue, children }) });
271
302
  }
@@ -1223,15 +1254,17 @@ function useDrawingTools() {
1223
1254
  const [isVisible, setIsVisible] = react.useState(true);
1224
1255
  const [autoRetrigger, setAutoRetrigger] = react.useState(true);
1225
1256
  const activeToolRef = react.useRef(activeTool);
1226
- activeToolRef.current = activeTool;
1227
1257
  const autoRetriggerRef = react.useRef(autoRetrigger);
1228
- autoRetriggerRef.current = autoRetrigger;
1229
1258
  const isLockedRef = react.useRef(isLocked);
1230
- isLockedRef.current = isLocked;
1231
1259
  const isVisibleRef = react.useRef(isVisible);
1232
- isVisibleRef.current = isVisible;
1233
1260
  const magnetModeRef = react.useRef(magnetMode);
1234
- magnetModeRef.current = magnetMode;
1261
+ react.useEffect(() => {
1262
+ activeToolRef.current = activeTool;
1263
+ autoRetriggerRef.current = autoRetrigger;
1264
+ isLockedRef.current = isLocked;
1265
+ isVisibleRef.current = isVisible;
1266
+ magnetModeRef.current = magnetMode;
1267
+ });
1235
1268
  const categories = react.useMemo(
1236
1269
  () => DRAWING_CATEGORIES.map((cat) => ({
1237
1270
  key: cat.key,
@@ -1242,6 +1275,8 @@ function useDrawingTools() {
1242
1275
  })),
1243
1276
  []
1244
1277
  );
1278
+ const createOverlayForToolRef = react.useRef(() => {
1279
+ });
1245
1280
  const createOverlayForTool = react.useCallback(
1246
1281
  (name) => {
1247
1282
  const mode = magnetModeRef.current === "strong" ? "strong_magnet" : magnetModeRef.current === "weak" ? "weak_magnet" : "normal";
@@ -1267,7 +1302,7 @@ function useDrawingTools() {
1267
1302
  });
1268
1303
  if (autoRetriggerRef.current && activeToolRef.current === name) {
1269
1304
  requestAnimationFrame(() => {
1270
- createOverlayForTool(name);
1305
+ createOverlayForToolRef.current(name);
1271
1306
  });
1272
1307
  }
1273
1308
  }
@@ -1275,6 +1310,9 @@ function useDrawingTools() {
1275
1310
  },
1276
1311
  [state.chart, undoRedoListenerRef]
1277
1312
  );
1313
+ react.useEffect(() => {
1314
+ createOverlayForToolRef.current = createOverlayForTool;
1315
+ });
1278
1316
  const selectTool = react.useCallback(
1279
1317
  (name) => {
1280
1318
  setActiveTool(name);
@@ -1681,10 +1719,10 @@ function useScreenshot() {
1681
1719
  function useHotkeys() {
1682
1720
  const { state } = useKlinechartsUI();
1683
1721
  const registerHotkey = react.useCallback((template) => {
1684
- reactKlinecharts.registerHotkey(template);
1722
+ klinecharts.registerHotkey(template);
1685
1723
  }, []);
1686
1724
  const getHotkey = react.useCallback(
1687
- (name) => reactKlinecharts.getHotkey(name) ?? null,
1725
+ (name) => klinecharts.getHotkey(name) ?? null,
1688
1726
  []
1689
1727
  );
1690
1728
  const setHotkeysEnabled = react.useCallback(
@@ -1702,7 +1740,7 @@ function useHotkeys() {
1702
1740
  return {
1703
1741
  registerHotkey,
1704
1742
  getHotkey,
1705
- supportedHotkeys: reactKlinecharts.getSupportedHotkeys(),
1743
+ supportedHotkeys: klinecharts.getSupportedHotkeys(),
1706
1744
  setHotkeysEnabled,
1707
1745
  getHotkeysConfig
1708
1746
  };
@@ -1731,22 +1769,28 @@ function useFullscreen() {
1731
1769
  const enter = react.useCallback(() => {
1732
1770
  const el = fullscreenContainerRef.current;
1733
1771
  if (!el) return;
1772
+ let result;
1734
1773
  if (el.requestFullscreen) {
1735
- el.requestFullscreen();
1774
+ result = el.requestFullscreen();
1736
1775
  } else if ("webkitRequestFullscreen" in el) {
1737
1776
  el.webkitRequestFullscreen();
1738
1777
  } else if ("msRequestFullscreen" in el) {
1739
1778
  el.msRequestFullscreen();
1740
1779
  }
1780
+ result?.catch(() => {
1781
+ });
1741
1782
  }, [fullscreenContainerRef]);
1742
1783
  const exit = react.useCallback(() => {
1784
+ let result;
1743
1785
  if (document.exitFullscreen) {
1744
- document.exitFullscreen();
1786
+ result = document.exitFullscreen();
1745
1787
  } else if ("webkitExitFullscreen" in document) {
1746
1788
  document.webkitExitFullscreen();
1747
1789
  } else if ("msExitFullscreen" in document) {
1748
1790
  document.msExitFullscreen();
1749
1791
  }
1792
+ result?.catch(() => {
1793
+ });
1750
1794
  }, []);
1751
1795
  const toggle = react.useCallback(() => {
1752
1796
  if (isFullscreen) {
@@ -1777,6 +1821,7 @@ function useFullscreen() {
1777
1821
  function useOrderLines() {
1778
1822
  const { state } = useKlinechartsUI();
1779
1823
  const callbacksRef = react.useRef(/* @__PURE__ */ new Map());
1824
+ const ownedIdsRef = react.useRef(/* @__PURE__ */ new Set());
1780
1825
  const createOrderLine = react.useCallback(
1781
1826
  (options) => {
1782
1827
  if (!state.chart) return null;
@@ -1807,6 +1852,7 @@ function useOrderLines() {
1807
1852
  }
1808
1853
  }
1809
1854
  });
1855
+ ownedIdsRef.current.add(id);
1810
1856
  return id;
1811
1857
  },
1812
1858
  [state.chart]
@@ -1838,12 +1884,28 @@ function useOrderLines() {
1838
1884
  (id) => {
1839
1885
  state.chart?.removeOverlay({ id });
1840
1886
  callbacksRef.current.delete(id);
1887
+ ownedIdsRef.current.delete(id);
1841
1888
  },
1842
1889
  [state.chart]
1843
1890
  );
1844
1891
  const removeAllOrderLines = react.useCallback(() => {
1845
1892
  state.chart?.removeOverlay({ name: "orderLine" });
1846
1893
  callbacksRef.current.clear();
1894
+ ownedIdsRef.current.clear();
1895
+ }, [state.chart]);
1896
+ react.useEffect(() => {
1897
+ const chart = state.chart;
1898
+ const owned = ownedIdsRef.current;
1899
+ return () => {
1900
+ owned.forEach((id) => {
1901
+ try {
1902
+ chart?.removeOverlay({ id });
1903
+ } catch {
1904
+ }
1905
+ });
1906
+ owned.clear();
1907
+ callbacksRef.current.clear();
1908
+ };
1847
1909
  }, [state.chart]);
1848
1910
  return {
1849
1911
  createOrderLine,
@@ -1861,6 +1923,14 @@ function useUndoRedo() {
1861
1923
  const isProcessingRef = react.useRef(false);
1862
1924
  const canUndo = undoStack.length > 0;
1863
1925
  const canRedo = redoStack.length > 0;
1926
+ const undoStackRef = react.useRef([]);
1927
+ const redoStackRef = react.useRef([]);
1928
+ react.useEffect(() => {
1929
+ undoStackRef.current = undoStack;
1930
+ }, [undoStack]);
1931
+ react.useEffect(() => {
1932
+ redoStackRef.current = redoStack;
1933
+ }, [redoStack]);
1864
1934
  const pushAction = react.useCallback((action) => {
1865
1935
  if (isProcessingRef.current) return;
1866
1936
  setUndoStack((prev) => [...prev, action]);
@@ -1877,10 +1947,11 @@ function useUndoRedo() {
1877
1947
  setRedoStack([]);
1878
1948
  }, []);
1879
1949
  const undo = react.useCallback(() => {
1880
- if (undoStack.length === 0 || !state.chart || isProcessingRef.current)
1950
+ const stack = undoStackRef.current;
1951
+ if (stack.length === 0 || !state.chart || isProcessingRef.current)
1881
1952
  return;
1882
1953
  isProcessingRef.current = true;
1883
- const action = undoStack[undoStack.length - 1];
1954
+ const action = stack[stack.length - 1];
1884
1955
  setUndoStack((prev) => prev.slice(0, -1));
1885
1956
  try {
1886
1957
  switch (action.type) {
@@ -2001,12 +2072,13 @@ function useUndoRedo() {
2001
2072
  } finally {
2002
2073
  isProcessingRef.current = false;
2003
2074
  }
2004
- }, [undoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
2075
+ }, [state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
2005
2076
  const redo = react.useCallback(() => {
2006
- if (redoStack.length === 0 || !state.chart || isProcessingRef.current)
2077
+ const stack = redoStackRef.current;
2078
+ if (stack.length === 0 || !state.chart || isProcessingRef.current)
2007
2079
  return;
2008
2080
  isProcessingRef.current = true;
2009
- const action = redoStack[redoStack.length - 1];
2081
+ const action = stack[stack.length - 1];
2010
2082
  setRedoStack((prev) => prev.slice(0, -1));
2011
2083
  try {
2012
2084
  switch (action.type) {
@@ -2131,7 +2203,7 @@ function useUndoRedo() {
2131
2203
  } finally {
2132
2204
  isProcessingRef.current = false;
2133
2205
  }
2134
- }, [redoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
2206
+ }, [state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
2135
2207
  react.useEffect(() => {
2136
2208
  const handleKeyDown = (e) => {
2137
2209
  const isCtrlOrMeta = e.ctrlKey || e.metaKey;
@@ -2169,6 +2241,7 @@ function generateId() {
2169
2241
  );
2170
2242
  }
2171
2243
  function getLayoutIds() {
2244
+ if (typeof localStorage === "undefined") return [];
2172
2245
  try {
2173
2246
  const raw = localStorage.getItem(INDEX_KEY);
2174
2247
  return raw ? JSON.parse(raw) : [];
@@ -2177,6 +2250,7 @@ function getLayoutIds() {
2177
2250
  }
2178
2251
  }
2179
2252
  function getEntry(id) {
2253
+ if (typeof localStorage === "undefined") return null;
2180
2254
  try {
2181
2255
  const raw = localStorage.getItem(STORAGE_KEY_PREFIX + id);
2182
2256
  return raw ? JSON.parse(raw) : null;
@@ -2186,35 +2260,31 @@ function getEntry(id) {
2186
2260
  }
2187
2261
  function useLayoutManager() {
2188
2262
  const { state, dispatch } = useKlinechartsUI();
2189
- const [layouts, setLayouts] = react.useState([]);
2263
+ const [layouts, setLayouts] = react.useState(
2264
+ () => getLayoutIds().map((id) => getEntry(id)).filter((e) => e !== null)
2265
+ );
2190
2266
  const [autoSaveEnabled, setAutoSaveEnabled] = react.useState(false);
2191
2267
  const autoSaveTimerRef = react.useRef(null);
2192
2268
  const autoSaveIdRef = react.useRef(null);
2193
2269
  const refreshLayouts = react.useCallback(() => {
2194
- const entries = getLayoutIds().map((id) => getEntry(id)).filter((e) => e !== null);
2195
- setLayouts(entries);
2270
+ setLayouts(
2271
+ getLayoutIds().map((id) => getEntry(id)).filter((e) => e !== null)
2272
+ );
2196
2273
  }, []);
2197
- react.useEffect(() => {
2198
- refreshLayouts();
2199
- }, [refreshLayouts]);
2200
2274
  const serializeState = react.useCallback(() => {
2201
2275
  const chart = state.chart;
2202
2276
  if (!chart) return null;
2203
2277
  const indicators2 = [];
2204
- const indicatorMap = chart.getIndicatorByPaneId?.();
2205
- if (indicatorMap) {
2206
- indicatorMap.forEach((paneIndicators, paneId) => {
2207
- paneIndicators.forEach((indicator) => {
2208
- const yAxisId = state.indicatorAxes[indicator.id];
2209
- indicators2.push({
2210
- paneId,
2211
- name: indicator.name,
2212
- calcParams: indicator.calcParams,
2213
- visible: indicator.visible,
2214
- ...indicator.styles ? { styles: indicator.styles } : {},
2215
- ...yAxisId ? { yAxisId } : {}
2216
- });
2217
- });
2278
+ const allIndicators = chart.getIndicators();
2279
+ for (const indicator of allIndicators) {
2280
+ const yAxisId = state.indicatorAxes[indicator.id];
2281
+ indicators2.push({
2282
+ paneId: indicator.paneId,
2283
+ name: indicator.name,
2284
+ calcParams: indicator.calcParams,
2285
+ visible: indicator.visible,
2286
+ ...indicator.styles ? { styles: indicator.styles } : {},
2287
+ ...yAxisId ? { yAxisId } : {}
2218
2288
  });
2219
2289
  }
2220
2290
  const drawings = [];
@@ -2278,13 +2348,8 @@ function useLayoutManager() {
2278
2348
  if (chartState.version !== STATE_VERSION) return false;
2279
2349
  const chart = state.chart;
2280
2350
  chart.removeOverlay();
2281
- const indicatorMap = chart.getIndicatorByPaneId?.();
2282
- if (indicatorMap) {
2283
- indicatorMap.forEach((_paneIndicators, paneId) => {
2284
- _paneIndicators.forEach((_, indicatorName) => {
2285
- chart.removeIndicator({ paneId, name: indicatorName });
2286
- });
2287
- });
2351
+ for (const indicator of chart.getIndicators()) {
2352
+ chart.removeIndicator({ id: indicator.id });
2288
2353
  }
2289
2354
  const newMainIndicators = [];
2290
2355
  const newSubIndicators = {};
@@ -2302,7 +2367,12 @@ function useLayoutManager() {
2302
2367
  visible: ind.visible
2303
2368
  },
2304
2369
  {
2305
- isStack: ind.paneId !== "candle_pane",
2370
+ // Main indicators stack OVER the candle series on the candle
2371
+ // pane (isStack: true), exactly as useIndicators does on the
2372
+ // add path. The previous `ind.paneId !== "candle_pane"` inverted
2373
+ // this, so loading a layout with main indicators replaced the
2374
+ // candles instead of overlaying them.
2375
+ isStack: isMain,
2306
2376
  pane: { id: ind.paneId },
2307
2377
  ...ind.yAxisId ? { yAxis: { id: ind.yAxisId } } : {}
2308
2378
  }
@@ -2502,8 +2572,10 @@ function useScriptEditor() {
2502
2572
  const [status, setStatus] = react.useState("");
2503
2573
  const [isRunning, setIsRunning] = react.useState(false);
2504
2574
  const activeIdRef = react.useRef(null);
2575
+ const [activeName, setActiveName] = react.useState(null);
2505
2576
  const activeNameRef = react.useRef(null);
2506
- const hasActiveScript = activeNameRef.current !== null;
2577
+ activeNameRef.current = activeName;
2578
+ const hasActiveScript = activeName !== null;
2507
2579
  const runScript = react.useCallback(() => {
2508
2580
  const chart = state.chart;
2509
2581
  if (!chart) {
@@ -2524,31 +2596,38 @@ function useScriptEditor() {
2524
2596
  `"use strict";
2525
2597
  ${code}`
2526
2598
  ];
2527
- const fn = new Function(...allArgs);
2599
+ const compiledFn = new Function(...allArgs);
2528
2600
  const shadowValues = SHADOW_KEYS.map(() => void 0);
2529
- const result = fn(...shadowValues, chunkFSHI2ZDB_cjs.TA_default, dataList, parsedParams);
2530
- if (!Array.isArray(result)) {
2601
+ const sampleResult = compiledFn(
2602
+ ...shadowValues,
2603
+ chunkLGYYJ2GP_cjs.TA_default,
2604
+ dataList,
2605
+ parsedParams
2606
+ );
2607
+ if (!Array.isArray(sampleResult)) {
2531
2608
  throw new Error("Script must return an Array.");
2532
2609
  }
2533
- const sample = result.find((v) => v !== null && v !== void 0);
2610
+ const sample = sampleResult.find(
2611
+ (v) => v !== null && v !== void 0
2612
+ );
2534
2613
  const seriesKeys = sample ? Object.keys(sample) : ["value"];
2535
2614
  if (seriesKeys.length === 0) {
2536
2615
  throw new Error("Returned objects have no keys.");
2537
2616
  }
2538
2617
  scriptCounter++;
2539
- const indicatorName = `_custom_script_${scriptCounter}`;
2618
+ const indicatorName = `_custom_script_active`;
2540
2619
  const figures = seriesKeys.map((key, i) => ({
2541
2620
  key,
2542
2621
  title: `${key}: `,
2543
2622
  type: "line",
2544
2623
  styles: () => ({ color: SERIES_COLORS[i % SERIES_COLORS.length] })
2545
2624
  }));
2546
- reactKlinecharts.registerIndicator({
2625
+ klinecharts.registerIndicator({
2547
2626
  name: indicatorName,
2548
2627
  shortName: (scriptName.trim() || `Script #${scriptCounter}`) + (placement === "main" ? " (Main)" : " (Sub)"),
2549
2628
  calcParams: parsedParams,
2550
2629
  figures,
2551
- calc: () => result,
2630
+ calc: (liveDataList) => compiledFn(...shadowValues, chunkLGYYJ2GP_cjs.TA_default, liveDataList, parsedParams),
2552
2631
  extendData: {
2553
2632
  isCustomScript: true,
2554
2633
  code,
@@ -2574,7 +2653,7 @@ ${code}`
2574
2653
  paneId = chart.createIndicator({ name: indicatorName });
2575
2654
  }
2576
2655
  activeIdRef.current = typeof paneId === "string" ? paneId : null;
2577
- activeNameRef.current = indicatorName;
2656
+ setActiveName(indicatorName);
2578
2657
  const title = scriptName.trim() || `Script #${scriptCounter}`;
2579
2658
  setStatus(
2580
2659
  `${title} applied to ${placement === "main" ? "Main Chart" : "Sub Pane"} \u2014 ${seriesKeys.length} series: ${seriesKeys.join(", ")}`
@@ -2596,7 +2675,7 @@ ${code}`
2596
2675
  } catch {
2597
2676
  }
2598
2677
  activeIdRef.current = null;
2599
- activeNameRef.current = null;
2678
+ setActiveName(null);
2600
2679
  setStatus("Indicator removed.");
2601
2680
  }
2602
2681
  }, [state.chart]);
@@ -2728,9 +2807,9 @@ function useAlerts() {
2728
2807
  triggered: false,
2729
2808
  extendData: resolvedExtendData
2730
2809
  };
2731
- dispatch({ type: "SET_ALERTS", alerts: [...state.alerts, alert] });
2810
+ dispatch({ type: "ADD_ALERT", alert });
2732
2811
  if (state.chart) {
2733
- chunkFSHI2ZDB_cjs.ensureAlertLineRegistered();
2812
+ chunkLGYYJ2GP_cjs.ensureAlertLineRegistered();
2734
2813
  state.chart.createOverlay({
2735
2814
  name: "alertLine",
2736
2815
  id,
@@ -2742,24 +2821,19 @@ function useAlerts() {
2742
2821
  }
2743
2822
  return id;
2744
2823
  },
2745
- [state.chart, state.alerts, dispatch]
2824
+ [state.chart, dispatch]
2746
2825
  );
2747
2826
  const removeAlert = react.useCallback(
2748
2827
  (id) => {
2749
- dispatch({
2750
- type: "SET_ALERTS",
2751
- alerts: state.alerts.filter((a) => a.id !== id)
2752
- });
2828
+ dispatch({ type: "REMOVE_ALERT", id });
2753
2829
  state.chart?.removeOverlay({ id });
2754
2830
  },
2755
- [state.chart, state.alerts, dispatch]
2831
+ [state.chart, dispatch]
2756
2832
  );
2757
2833
  const clearAlerts = react.useCallback(() => {
2758
- for (const alert of state.alerts) {
2759
- state.chart?.removeOverlay({ id: alert.id });
2760
- }
2761
- dispatch({ type: "SET_ALERTS", alerts: [] });
2762
- }, [state.chart, state.alerts, dispatch]);
2834
+ state.chart?.removeOverlay({ groupId: "price_alerts" });
2835
+ dispatch({ type: "CLEAR_ALERTS" });
2836
+ }, [state.chart, dispatch]);
2763
2837
  const onAlertTriggered = react.useCallback(
2764
2838
  (callback) => {
2765
2839
  alertTriggeredListenerRef.current = callback;
@@ -2943,6 +3017,7 @@ function useReplay() {
2943
3017
  );
2944
3018
  const startReplay = react.useCallback(() => {
2945
3019
  if (!state.chart) return;
3020
+ if (isReplaying) return;
2946
3021
  const dataList = state.chart.getDataList();
2947
3022
  if (!dataList || dataList.length === 0) return;
2948
3023
  replaySavedDataRef.current = [...dataList];
@@ -2958,7 +3033,12 @@ function useReplay() {
2958
3033
  });
2959
3034
  state.chart.clearData?.();
2960
3035
  startInterval(speed);
2961
- }, [state.chart, speed, startInterval, replaySavedDataRef, replayIndexRef, dispatch]);
3036
+ }, [state.chart, state.symbol, state.period, speed, startInterval, isReplaying, replaySavedDataRef, replayIndexRef, dispatch]);
3037
+ react.useEffect(() => {
3038
+ if (isReplaying) {
3039
+ stopReplay();
3040
+ }
3041
+ }, [state.symbol, state.period]);
2962
3042
  const stopReplay = react.useCallback(() => {
2963
3043
  if (!state.chart) return;
2964
3044
  clearInterval_();
@@ -3054,24 +3134,29 @@ var DEFAULT_COLORS = [
3054
3134
  "#ff5722",
3055
3135
  "#8bc34a"
3056
3136
  ];
3057
- var colorIndex = 0;
3058
- var compareCounter = 0;
3059
3137
  function useCompare() {
3060
3138
  const { state, datafeed } = useKlinechartsUI();
3061
3139
  const [symbols, setSymbols] = react.useState([]);
3062
3140
  const indicatorsRef = react.useRef(/* @__PURE__ */ new Map());
3141
+ const instanceSalt = react.useId().replace(/[^a-zA-Z0-9]/g, "");
3063
3142
  const addSymbol = react.useCallback(
3064
3143
  async (ticker, color) => {
3065
3144
  if (!state.chart || !datafeed) return;
3066
3145
  if (indicatorsRef.current.has(ticker)) return;
3067
- const assignedColor = color ?? DEFAULT_COLORS[colorIndex++ % DEFAULT_COLORS.length];
3146
+ const assignedColor = color ?? DEFAULT_COLORS[symbols.length % DEFAULT_COLORS.length];
3068
3147
  const mainDataList = state.chart.getDataList();
3069
3148
  if (!mainDataList || mainDataList.length === 0) return;
3070
3149
  const from = mainDataList[0].timestamp;
3071
3150
  const to = mainDataList[mainDataList.length - 1].timestamp;
3151
+ const mainSymbol = state.symbol;
3152
+ const symbol = {
3153
+ ticker,
3154
+ pricePrecision: mainSymbol?.pricePrecision ?? 2,
3155
+ volumePrecision: mainSymbol?.volumePrecision ?? 8
3156
+ };
3072
3157
  const compareData = await datafeed.getHistoryKLineData(
3073
- { ticker },
3074
- { ...state.period, label: "" },
3158
+ symbol,
3159
+ state.period,
3075
3160
  from,
3076
3161
  to
3077
3162
  );
@@ -3090,14 +3175,8 @@ function useCompare() {
3090
3175
  }
3091
3176
  if (basePrice === null || basePrice === 0) return;
3092
3177
  const bp = basePrice;
3093
- const normalizedData = mainDataList.map((mainBar) => {
3094
- const close = compareMap.get(mainBar.timestamp);
3095
- return {
3096
- pct: close != null ? (close - bp) / bp * 100 : NaN
3097
- };
3098
- });
3099
- const indicatorName = `__cmp_${++compareCounter}_${ticker}`;
3100
- reactKlinecharts.registerIndicator({
3178
+ const indicatorName = `__cmp_${instanceSalt}_${ticker}`;
3179
+ klinecharts.registerIndicator({
3101
3180
  name: indicatorName,
3102
3181
  shortName: ticker,
3103
3182
  figures: [
@@ -3108,7 +3187,16 @@ function useCompare() {
3108
3187
  styles: () => ({ color: assignedColor })
3109
3188
  }
3110
3189
  ],
3111
- calc: () => normalizedData
3190
+ calc: (dataList) => {
3191
+ let lastPct = null;
3192
+ return dataList.map((bar) => {
3193
+ const close = compareMap.get(bar.timestamp);
3194
+ if (close != null) {
3195
+ lastPct = (close - bp) / bp * 100;
3196
+ }
3197
+ return { pct: lastPct };
3198
+ });
3199
+ }
3112
3200
  });
3113
3201
  const paneId = state.chart.createIndicator(
3114
3202
  { name: indicatorName },
@@ -3126,7 +3214,7 @@ function useCompare() {
3126
3214
  ];
3127
3215
  });
3128
3216
  },
3129
- [state.chart, state.period, datafeed]
3217
+ [state.chart, state.symbol, state.period, datafeed, symbols.length, instanceSalt]
3130
3218
  );
3131
3219
  const removeSymbol = react.useCallback(
3132
3220
  (ticker) => {
@@ -3336,13 +3424,11 @@ function useAnnotations() {
3336
3424
  [state.chart]
3337
3425
  );
3338
3426
  const clearAnnotations = react.useCallback(() => {
3339
- setAnnotations((prev) => {
3340
- for (const annotation of prev) {
3341
- state.chart?.removeOverlay({ id: annotation.id });
3342
- }
3343
- return [];
3344
- });
3345
- }, [state.chart]);
3427
+ for (const annotation of annotations) {
3428
+ state.chart?.removeOverlay({ id: annotation.id });
3429
+ }
3430
+ setAnnotations([]);
3431
+ }, [state.chart, annotations]);
3346
3432
  react.useEffect(() => {
3347
3433
  return () => {
3348
3434
  state.chart?.removeOverlay({ groupId: "annotations" });
@@ -3423,179 +3509,179 @@ function createDataLoader(datafeed, dispatch) {
3423
3509
 
3424
3510
  Object.defineProperty(exports, "TA", {
3425
3511
  enumerable: true,
3426
- get: function () { return chunkFSHI2ZDB_cjs.TA_default; }
3512
+ get: function () { return chunkLGYYJ2GP_cjs.TA_default; }
3427
3513
  });
3428
3514
  Object.defineProperty(exports, "abcd", {
3429
3515
  enumerable: true,
3430
- get: function () { return chunkFSHI2ZDB_cjs.abcd_default; }
3516
+ get: function () { return chunkLGYYJ2GP_cjs.abcd_default; }
3431
3517
  });
3432
3518
  Object.defineProperty(exports, "alertLine", {
3433
3519
  enumerable: true,
3434
- get: function () { return chunkFSHI2ZDB_cjs.alertLine_default; }
3520
+ get: function () { return chunkLGYYJ2GP_cjs.alertLine_default; }
3435
3521
  });
3436
3522
  Object.defineProperty(exports, "anyWaves", {
3437
3523
  enumerable: true,
3438
- get: function () { return chunkFSHI2ZDB_cjs.anyWaves_default; }
3524
+ get: function () { return chunkLGYYJ2GP_cjs.anyWaves_default; }
3439
3525
  });
3440
3526
  Object.defineProperty(exports, "arrow", {
3441
3527
  enumerable: true,
3442
- get: function () { return chunkFSHI2ZDB_cjs.arrow_default; }
3528
+ get: function () { return chunkLGYYJ2GP_cjs.arrow_default; }
3443
3529
  });
3444
3530
  Object.defineProperty(exports, "bollTv", {
3445
3531
  enumerable: true,
3446
- get: function () { return chunkFSHI2ZDB_cjs.bollTv_default; }
3532
+ get: function () { return chunkLGYYJ2GP_cjs.bollTv_default; }
3447
3533
  });
3448
3534
  Object.defineProperty(exports, "brush", {
3449
3535
  enumerable: true,
3450
- get: function () { return chunkFSHI2ZDB_cjs.brush_default; }
3536
+ get: function () { return chunkLGYYJ2GP_cjs.brush_default; }
3451
3537
  });
3452
3538
  Object.defineProperty(exports, "cci", {
3453
3539
  enumerable: true,
3454
- get: function () { return chunkFSHI2ZDB_cjs.cci_default; }
3540
+ get: function () { return chunkLGYYJ2GP_cjs.cci_default; }
3455
3541
  });
3456
3542
  Object.defineProperty(exports, "circle", {
3457
3543
  enumerable: true,
3458
- get: function () { return chunkFSHI2ZDB_cjs.circle_default; }
3544
+ get: function () { return chunkLGYYJ2GP_cjs.circle_default; }
3459
3545
  });
3460
3546
  Object.defineProperty(exports, "depthOverlay", {
3461
3547
  enumerable: true,
3462
- get: function () { return chunkFSHI2ZDB_cjs.depthOverlay_default; }
3548
+ get: function () { return chunkLGYYJ2GP_cjs.depthOverlay_default; }
3463
3549
  });
3464
3550
  Object.defineProperty(exports, "eightWaves", {
3465
3551
  enumerable: true,
3466
- get: function () { return chunkFSHI2ZDB_cjs.eightWaves_default; }
3552
+ get: function () { return chunkLGYYJ2GP_cjs.eightWaves_default; }
3467
3553
  });
3468
3554
  Object.defineProperty(exports, "elliottWave", {
3469
3555
  enumerable: true,
3470
- get: function () { return chunkFSHI2ZDB_cjs.elliottWave_default; }
3556
+ get: function () { return chunkLGYYJ2GP_cjs.elliottWave_default; }
3471
3557
  });
3472
3558
  Object.defineProperty(exports, "fibRetracement", {
3473
3559
  enumerable: true,
3474
- get: function () { return chunkFSHI2ZDB_cjs.fibRetracement_default; }
3560
+ get: function () { return chunkLGYYJ2GP_cjs.fibRetracement_default; }
3475
3561
  });
3476
3562
  Object.defineProperty(exports, "fibonacciCircle", {
3477
3563
  enumerable: true,
3478
- get: function () { return chunkFSHI2ZDB_cjs.fibonacciCircle_default; }
3564
+ get: function () { return chunkLGYYJ2GP_cjs.fibonacciCircle_default; }
3479
3565
  });
3480
3566
  Object.defineProperty(exports, "fibonacciExtension", {
3481
3567
  enumerable: true,
3482
- get: function () { return chunkFSHI2ZDB_cjs.fibonacciExtension_default; }
3568
+ get: function () { return chunkLGYYJ2GP_cjs.fibonacciExtension_default; }
3483
3569
  });
3484
3570
  Object.defineProperty(exports, "fibonacciSegment", {
3485
3571
  enumerable: true,
3486
- get: function () { return chunkFSHI2ZDB_cjs.fibonacciSegment_default; }
3572
+ get: function () { return chunkLGYYJ2GP_cjs.fibonacciSegment_default; }
3487
3573
  });
3488
3574
  Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
3489
3575
  enumerable: true,
3490
- get: function () { return chunkFSHI2ZDB_cjs.fibonacciSpeedResistanceFan_default; }
3576
+ get: function () { return chunkLGYYJ2GP_cjs.fibonacciSpeedResistanceFan_default; }
3491
3577
  });
3492
3578
  Object.defineProperty(exports, "fibonacciSpiral", {
3493
3579
  enumerable: true,
3494
- get: function () { return chunkFSHI2ZDB_cjs.fibonacciSpiral_default; }
3580
+ get: function () { return chunkLGYYJ2GP_cjs.fibonacciSpiral_default; }
3495
3581
  });
3496
3582
  Object.defineProperty(exports, "fiveWaves", {
3497
3583
  enumerable: true,
3498
- get: function () { return chunkFSHI2ZDB_cjs.fiveWaves_default; }
3584
+ get: function () { return chunkLGYYJ2GP_cjs.fiveWaves_default; }
3499
3585
  });
3500
3586
  Object.defineProperty(exports, "gannBox", {
3501
3587
  enumerable: true,
3502
- get: function () { return chunkFSHI2ZDB_cjs.gannBox_default; }
3588
+ get: function () { return chunkLGYYJ2GP_cjs.gannBox_default; }
3503
3589
  });
3504
3590
  Object.defineProperty(exports, "gannFan", {
3505
3591
  enumerable: true,
3506
- get: function () { return chunkFSHI2ZDB_cjs.gannFan_default; }
3592
+ get: function () { return chunkLGYYJ2GP_cjs.gannFan_default; }
3507
3593
  });
3508
3594
  Object.defineProperty(exports, "hma", {
3509
3595
  enumerable: true,
3510
- get: function () { return chunkFSHI2ZDB_cjs.hma_default; }
3596
+ get: function () { return chunkLGYYJ2GP_cjs.hma_default; }
3511
3597
  });
3512
3598
  Object.defineProperty(exports, "ichimoku", {
3513
3599
  enumerable: true,
3514
- get: function () { return chunkFSHI2ZDB_cjs.ichimoku_default; }
3600
+ get: function () { return chunkLGYYJ2GP_cjs.ichimoku_default; }
3515
3601
  });
3516
3602
  Object.defineProperty(exports, "indicators", {
3517
3603
  enumerable: true,
3518
- get: function () { return chunkFSHI2ZDB_cjs.indicators; }
3604
+ get: function () { return chunkLGYYJ2GP_cjs.indicators; }
3519
3605
  });
3520
3606
  Object.defineProperty(exports, "longPosition", {
3521
3607
  enumerable: true,
3522
- get: function () { return chunkFSHI2ZDB_cjs.longPosition_default; }
3608
+ get: function () { return chunkLGYYJ2GP_cjs.longPosition_default; }
3523
3609
  });
3524
3610
  Object.defineProperty(exports, "maRibbon", {
3525
3611
  enumerable: true,
3526
- get: function () { return chunkFSHI2ZDB_cjs.maRibbon_default; }
3612
+ get: function () { return chunkLGYYJ2GP_cjs.maRibbon_default; }
3527
3613
  });
3528
3614
  Object.defineProperty(exports, "macdTv", {
3529
3615
  enumerable: true,
3530
- get: function () { return chunkFSHI2ZDB_cjs.macdTv_default; }
3616
+ get: function () { return chunkLGYYJ2GP_cjs.macdTv_default; }
3531
3617
  });
3532
3618
  Object.defineProperty(exports, "measure", {
3533
3619
  enumerable: true,
3534
- get: function () { return chunkFSHI2ZDB_cjs.measure_default; }
3620
+ get: function () { return chunkLGYYJ2GP_cjs.measure_default; }
3535
3621
  });
3536
3622
  Object.defineProperty(exports, "orderLine", {
3537
3623
  enumerable: true,
3538
- get: function () { return chunkFSHI2ZDB_cjs.orderLine_default; }
3624
+ get: function () { return chunkLGYYJ2GP_cjs.orderLine_default; }
3539
3625
  });
3540
3626
  Object.defineProperty(exports, "overlays", {
3541
3627
  enumerable: true,
3542
- get: function () { return chunkFSHI2ZDB_cjs.overlays; }
3628
+ get: function () { return chunkLGYYJ2GP_cjs.overlays; }
3543
3629
  });
3544
3630
  Object.defineProperty(exports, "parallelChannel", {
3545
3631
  enumerable: true,
3546
- get: function () { return chunkFSHI2ZDB_cjs.parallelChannel_default; }
3632
+ get: function () { return chunkLGYYJ2GP_cjs.parallelChannel_default; }
3547
3633
  });
3548
3634
  Object.defineProperty(exports, "parallelogram", {
3549
3635
  enumerable: true,
3550
- get: function () { return chunkFSHI2ZDB_cjs.parallelogram_default; }
3636
+ get: function () { return chunkLGYYJ2GP_cjs.parallelogram_default; }
3551
3637
  });
3552
3638
  Object.defineProperty(exports, "pivotPoints", {
3553
3639
  enumerable: true,
3554
- get: function () { return chunkFSHI2ZDB_cjs.pivotPoints_default; }
3640
+ get: function () { return chunkLGYYJ2GP_cjs.pivotPoints_default; }
3555
3641
  });
3556
3642
  Object.defineProperty(exports, "ray", {
3557
3643
  enumerable: true,
3558
- get: function () { return chunkFSHI2ZDB_cjs.ray_default; }
3644
+ get: function () { return chunkLGYYJ2GP_cjs.ray_default; }
3559
3645
  });
3560
3646
  Object.defineProperty(exports, "rect", {
3561
3647
  enumerable: true,
3562
- get: function () { return chunkFSHI2ZDB_cjs.rect_default; }
3648
+ get: function () { return chunkLGYYJ2GP_cjs.rect_default; }
3563
3649
  });
3564
3650
  Object.defineProperty(exports, "registerExtensions", {
3565
3651
  enumerable: true,
3566
- get: function () { return chunkFSHI2ZDB_cjs.registerExtensions; }
3652
+ get: function () { return chunkLGYYJ2GP_cjs.registerExtensions; }
3567
3653
  });
3568
3654
  Object.defineProperty(exports, "rsiTv", {
3569
3655
  enumerable: true,
3570
- get: function () { return chunkFSHI2ZDB_cjs.rsiTv_default; }
3656
+ get: function () { return chunkLGYYJ2GP_cjs.rsiTv_default; }
3571
3657
  });
3572
3658
  Object.defineProperty(exports, "shortPosition", {
3573
3659
  enumerable: true,
3574
- get: function () { return chunkFSHI2ZDB_cjs.shortPosition_default; }
3660
+ get: function () { return chunkLGYYJ2GP_cjs.shortPosition_default; }
3575
3661
  });
3576
3662
  Object.defineProperty(exports, "stochastic", {
3577
3663
  enumerable: true,
3578
- get: function () { return chunkFSHI2ZDB_cjs.stochastic_default; }
3664
+ get: function () { return chunkLGYYJ2GP_cjs.stochastic_default; }
3579
3665
  });
3580
3666
  Object.defineProperty(exports, "superTrend", {
3581
3667
  enumerable: true,
3582
- get: function () { return chunkFSHI2ZDB_cjs.superTrend_default; }
3668
+ get: function () { return chunkLGYYJ2GP_cjs.superTrend_default; }
3583
3669
  });
3584
3670
  Object.defineProperty(exports, "threeWaves", {
3585
3671
  enumerable: true,
3586
- get: function () { return chunkFSHI2ZDB_cjs.threeWaves_default; }
3672
+ get: function () { return chunkLGYYJ2GP_cjs.threeWaves_default; }
3587
3673
  });
3588
3674
  Object.defineProperty(exports, "triangle", {
3589
3675
  enumerable: true,
3590
- get: function () { return chunkFSHI2ZDB_cjs.triangle_default; }
3676
+ get: function () { return chunkLGYYJ2GP_cjs.triangle_default; }
3591
3677
  });
3592
3678
  Object.defineProperty(exports, "vwap", {
3593
3679
  enumerable: true,
3594
- get: function () { return chunkFSHI2ZDB_cjs.vwap_default; }
3680
+ get: function () { return chunkLGYYJ2GP_cjs.vwap_default; }
3595
3681
  });
3596
3682
  Object.defineProperty(exports, "xabcd", {
3597
3683
  enumerable: true,
3598
- get: function () { return chunkFSHI2ZDB_cjs.xabcd_default; }
3684
+ get: function () { return chunkLGYYJ2GP_cjs.xabcd_default; }
3599
3685
  });
3600
3686
  exports.CANDLE_TYPES = CANDLE_TYPES;
3601
3687
  exports.COMPARE_RULES = COMPARE_RULES;