@forgecharts/sdk 1.5.82 → 1.5.83

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.
Files changed (53) hide show
  1. package/dist/api/EventBus.d.ts +1 -1
  2. package/dist/api/{TChart.d.ts → ForgeCharts.d.ts} +38 -4
  3. package/dist/api/ForgeCharts.d.ts.map +1 -0
  4. package/dist/api/createChart.d.ts +4 -4
  5. package/dist/api/createChart.d.ts.map +1 -1
  6. package/dist/core/Chart.d.ts +5 -5
  7. package/dist/core/Chart.d.ts.map +1 -1
  8. package/dist/core/Series.d.ts +14 -0
  9. package/dist/core/Series.d.ts.map +1 -1
  10. package/dist/core/__tests__/seriesCutoff.test.d.ts +2 -0
  11. package/dist/core/__tests__/seriesCutoff.test.d.ts.map +1 -0
  12. package/dist/datafeed/DatafeedConnector.d.ts +1 -1
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +432 -27
  16. package/dist/index.js.map +1 -1
  17. package/dist/internal.d.ts +1 -1
  18. package/dist/internal.js +432 -27
  19. package/dist/internal.js.map +1 -1
  20. package/dist/licensing/packageFeatures.d.ts.map +1 -1
  21. package/dist/node.d.ts +1 -1
  22. package/dist/node.d.ts.map +1 -1
  23. package/dist/pixi/PixiCandlestickRenderer.d.ts +4 -0
  24. package/dist/pixi/PixiCandlestickRenderer.d.ts.map +1 -1
  25. package/dist/pixi/PixiChart.d.ts +12 -2
  26. package/dist/pixi/PixiChart.d.ts.map +1 -1
  27. package/dist/pixi/PixiHistogramRenderer.d.ts +2 -0
  28. package/dist/pixi/PixiHistogramRenderer.d.ts.map +1 -1
  29. package/dist/pixi/PixiLineRenderer.d.ts +2 -0
  30. package/dist/pixi/PixiLineRenderer.d.ts.map +1 -1
  31. package/dist/react/canvas/ChartCanvas.d.ts +7 -2
  32. package/dist/react/canvas/ChartCanvas.d.ts.map +1 -1
  33. package/dist/react/hooks/useUserPackage.d.ts.map +1 -1
  34. package/dist/react/index.js +719 -63
  35. package/dist/react/index.js.map +1 -1
  36. package/dist/react/internal.js +726 -83
  37. package/dist/react/internal.js.map +1 -1
  38. package/dist/react/shell/AlertsDrawer.d.ts +11 -0
  39. package/dist/react/shell/AlertsDrawer.d.ts.map +1 -0
  40. package/dist/react/shell/ManagedAppShell.d.ts +1 -1
  41. package/dist/react/shell/ManagedAppShell.d.ts.map +1 -1
  42. package/dist/react/workspace/ChartWorkspace.d.ts +10 -1
  43. package/dist/react/workspace/ChartWorkspace.d.ts.map +1 -1
  44. package/dist/react/workspace/LayoutMenu.d.ts +3 -1
  45. package/dist/react/workspace/LayoutMenu.d.ts.map +1 -1
  46. package/dist/react/workspace/toolbars/TopToolbar.d.ts +7 -1
  47. package/dist/react/workspace/toolbars/TopToolbar.d.ts.map +1 -1
  48. package/dist/types/ChartBackend.d.ts +15 -4
  49. package/dist/types/ChartBackend.d.ts.map +1 -1
  50. package/dist/types/ISeries.d.ts +5 -0
  51. package/dist/types/ISeries.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/api/TChart.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { TextStyle, Application, Container, Graphics, Text, FillGradient, CanvasTextMetrics } from 'pixi.js';
1
+ import { TextStyle, Application, Graphics, Container, Text, FillGradient, CanvasTextMetrics } from 'pixi.js';
2
2
  import { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback, createContext, useMemo, useContext } from 'react';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import ReactDOM, { createPortal } from 'react-dom';
@@ -1383,6 +1383,7 @@ var Series = class _Series {
1383
1383
  // ─── ISeries implementation ──────────────────────────────────────────────────
1384
1384
  setData(data) {
1385
1385
  this._data = [...data].sort((a, b) => a.time - b.time);
1386
+ this._cutoffView = null;
1386
1387
  this.onDataChanged?.();
1387
1388
  }
1388
1389
  /**
@@ -1422,11 +1423,47 @@ var Series = class _Series {
1422
1423
  this._data = this._data.slice(this._data.length - 1e5);
1423
1424
  }
1424
1425
  }
1426
+ this._cutoffView = null;
1425
1427
  this.onDataChanged?.();
1426
1428
  }
1427
- data() {
1429
+ /**
1430
+ * Bar-replay display cutoff. When set, data() returns only bars at or
1431
+ * before this time while the FULL tape keeps flowing underneath — the
1432
+ * datafeed keeps writing live bars via setData/update, and everything
1433
+ * downstream (renderers, indicators, crosshair, last-price) naturally sees
1434
+ * the truncated past. Replay is not a mode the pipeline knows about; it is
1435
+ * this clamp plus a controller that moves it.
1436
+ */
1437
+ _cutoffTime = null;
1438
+ _cutoffView = null;
1439
+ setCutoffTime(time) {
1440
+ this._cutoffTime = time;
1441
+ this._cutoffView = null;
1442
+ this.onDataChanged?.();
1443
+ }
1444
+ cutoffTime() {
1445
+ return this._cutoffTime;
1446
+ }
1447
+ /** The complete tape, ignoring any replay cutoff. Datafeed-side reads only. */
1448
+ fullData() {
1428
1449
  return this._data;
1429
1450
  }
1451
+ data() {
1452
+ if (this._cutoffTime === null) return this._data;
1453
+ const len = this._data.length;
1454
+ const cached = this._cutoffView;
1455
+ if (cached && cached.len === len && cached.cutoff === this._cutoffTime) return cached.view;
1456
+ let lo = 0;
1457
+ let hi = len;
1458
+ while (lo < hi) {
1459
+ const mid = lo + hi >>> 1;
1460
+ if (this._data[mid].time <= this._cutoffTime) lo = mid + 1;
1461
+ else hi = mid;
1462
+ }
1463
+ const view = this._data.slice(0, lo);
1464
+ this._cutoffView = { len, cutoff: this._cutoffTime, view };
1465
+ return view;
1466
+ }
1430
1467
  applyOptions(options) {
1431
1468
  this._options = { ...this._options, ...options };
1432
1469
  }
@@ -4757,14 +4794,14 @@ var Chart = class {
4757
4794
  _crosshairEnabled = true;
4758
4795
  _magnetMode = "snap-xy";
4759
4796
  // ── Trading overlay ───────────────────────────────────────────────────────────
4760
- /** Current set of order lines supplied by TChart for canvas rendering. */
4797
+ /** Current set of order lines supplied by ForgeCharts for canvas rendering. */
4761
4798
  _tradingOrders = [];
4762
4799
  /** Called whenever a drawing is committed (created or updated). */
4763
4800
  onDrawingCommitted;
4764
4801
  /** Fired when an EXISTING drawing is modified via drag (handle or body move). */
4765
4802
  onDrawingEdited;
4766
4803
  _drawingEditDirty = false;
4767
- /** Ctrl+Z / Ctrl+Y — drawing history is owned by TChart. */
4804
+ /** Ctrl+Z / Ctrl+Y — drawing history is owned by ForgeCharts. */
4768
4805
  onUndoRequest;
4769
4806
  onRedoRequest;
4770
4807
  /** Called whenever a drawing is deleted. */
@@ -4780,7 +4817,7 @@ var Chart = class {
4780
4817
  /** Called when the user drags a TP/SL pill off an entry line to create a bracket leg. */
4781
4818
  onTpSlCreate;
4782
4819
  /**
4783
- * Called after every pan or zoom interaction so external code (e.g. TChart)
4820
+ * Called after every pan or zoom interaction so external code (e.g. ForgeCharts)
4784
4821
  * can check whether the visible time range now extends past the oldest
4785
4822
  * loaded bar and trigger a lazy history fetch.
4786
4823
  */
@@ -5283,11 +5320,11 @@ var Chart = class {
5283
5320
  }
5284
5321
  ctx.restore();
5285
5322
  }
5286
- /** Provides the overlay renderer with the latest order list from TChart. */
5323
+ /** Provides the overlay renderer with the latest order list from ForgeCharts. */
5287
5324
  setTradingOrders(orders) {
5288
5325
  this._tradingOrders = orders;
5289
5326
  }
5290
- /** Provides the overlay renderer with the latest position list from TChart. */
5327
+ /** Provides the overlay renderer with the latest position list from ForgeCharts. */
5291
5328
  setTradingPositions(_positions) {
5292
5329
  }
5293
5330
  /** Sets contract switch markers for continuous futures roll dates. */
@@ -6104,9 +6141,17 @@ var PixiCandlestickRenderer = class {
6104
6141
  this._gfx = new Graphics();
6105
6142
  layer.addChild(this._gfx);
6106
6143
  }
6144
+ /** Clear this renderer's graphics — used when another style takes over. */
6145
+ clear() {
6146
+ this._gfx.clear();
6147
+ }
6107
6148
  draw(bars, transform, opts) {
6108
6149
  const gfx = this._gfx;
6109
6150
  gfx.clear();
6151
+ if (opts?.type === "bar") {
6152
+ this._drawBars(bars, transform, opts);
6153
+ return;
6154
+ }
6110
6155
  if (bars.length === 0) return;
6111
6156
  const { plotWidth: pw, plotHeight: ph } = transform;
6112
6157
  const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
@@ -6237,6 +6282,36 @@ var PixiCandlestickRenderer = class {
6237
6282
  /**
6238
6283
  * Converts OHLCV bars to Heikin-Ashi values before drawing.
6239
6284
  */
6285
+ /** Classic OHLC bars: high-low spine, open tick left, close tick right. */
6286
+ _drawBars(bars, transform, opts) {
6287
+ const gfx = this._gfx;
6288
+ if (bars.length === 0) return;
6289
+ const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
6290
+ const downColor = hexToNum(opts && "downColor" in opts ? opts.downColor : void 0, DOWN_COLOR);
6291
+ const { plotWidth: pw } = transform;
6292
+ let barPitch = pw / Math.max(1, bars.length);
6293
+ if (bars.length >= 2) {
6294
+ const dx = Math.abs(transform.timeToX(bars[1].time) - transform.timeToX(bars[0].time));
6295
+ if (dx > 0) barPitch = dx;
6296
+ }
6297
+ const tick = Math.max(1.5, Math.min(barPitch * 0.35, 6));
6298
+ const lineW = barPitch < 3 ? 1 : 1.5;
6299
+ for (const bar of bars) {
6300
+ const x = transform.timeToX(bar.time);
6301
+ if (x < -barPitch || x > pw + barPitch) continue;
6302
+ const yHigh = transform.priceToY(bar.high);
6303
+ const yLow = transform.priceToY(bar.low);
6304
+ const yOpen = transform.priceToY(bar.open);
6305
+ const yClose = transform.priceToY(bar.close);
6306
+ const color = bar.close >= bar.open ? upColor : downColor;
6307
+ gfx.moveTo(x, yHigh).lineTo(x, yLow);
6308
+ if (barPitch >= 3) {
6309
+ gfx.moveTo(x - tick, yOpen).lineTo(x, yOpen);
6310
+ gfx.moveTo(x, yClose).lineTo(x + tick, yClose);
6311
+ }
6312
+ gfx.stroke({ color, width: lineW });
6313
+ }
6314
+ }
6240
6315
  drawHeikinAshi(bars, transform, opts) {
6241
6316
  const first = bars[0];
6242
6317
  const last = bars[bars.length - 1];
@@ -7559,6 +7634,10 @@ var PixiLineRenderer = class {
7559
7634
  this._gradientCache = null;
7560
7635
  this._gfx.destroy(true);
7561
7636
  }
7637
+ /** Clear this renderer's graphics — used when another style takes over. */
7638
+ clear() {
7639
+ this._gfx.clear();
7640
+ }
7562
7641
  draw(bars, transform, options) {
7563
7642
  const gfx = this._gfx;
7564
7643
  gfx.clear();
@@ -7653,6 +7732,10 @@ var PixiHistogramRenderer = class {
7653
7732
  destroy() {
7654
7733
  this._gfx.destroy(true);
7655
7734
  }
7735
+ /** Clear this renderer's graphics — used when another style takes over. */
7736
+ clear() {
7737
+ this._gfx.clear();
7738
+ }
7656
7739
  draw(bars, transform) {
7657
7740
  const gfx = this._gfx;
7658
7741
  gfx.clear();
@@ -8632,7 +8715,7 @@ var PixiChart = class {
8632
8715
  /** Fired when an EXISTING drawing is modified via drag (handle or body move). */
8633
8716
  onDrawingEdited;
8634
8717
  _drawingEditDirty = false;
8635
- /** Ctrl+Z / Ctrl+Y — drawing history is owned by TChart. */
8718
+ /** Ctrl+Z / Ctrl+Y — drawing history is owned by ForgeCharts. */
8636
8719
  onUndoRequest;
8637
8720
  onRedoRequest;
8638
8721
  /** Called whenever a drawing is deleted. */
@@ -8647,7 +8730,7 @@ var PixiChart = class {
8647
8730
  onDrawingContextMenu;
8648
8731
  /** Called when the user double-clicks a drawing. */
8649
8732
  onDrawingDoubleClick;
8650
- /** Called after every pan/zoom so TChart can trigger lazy history loading. */
8733
+ /** Called after every pan/zoom so ForgeCharts can trigger lazy history loading. */
8651
8734
  onViewportChanged;
8652
8735
  constructor(container, options = {}) {
8653
8736
  this._container = container;
@@ -8754,6 +8837,12 @@ var PixiChart = class {
8754
8837
  this.onViewportChanged?.();
8755
8838
  },
8756
8839
  onCrosshairMove: (x, y) => {
8840
+ if (this._replaySelect) {
8841
+ const t = this._cursorBucketTime(x);
8842
+ this._replayHoverTime = t;
8843
+ this._replaySelect.onHover(t);
8844
+ this._drawReplayDim(x);
8845
+ }
8757
8846
  if (!this._crosshairEnabled) return;
8758
8847
  if (this._magnetMode === "off") {
8759
8848
  this._crosshairRenderer.update(x, y, this._cursorBucketTime(x));
@@ -9043,7 +9132,7 @@ var PixiChart = class {
9043
9132
  }).catch(() => {
9044
9133
  });
9045
9134
  }
9046
- // ─── Drawing objects (used by PixiChart directly; TChart uses DrawingManager) ─
9135
+ // ─── Drawing objects (used by PixiChart directly; ForgeCharts uses DrawingManager) ─
9047
9136
  /** @internal Used by PixiChart when DrawingManager pushes an update. */
9048
9137
  setDrawings(drawings) {
9049
9138
  this._drawings = [...drawings];
@@ -9052,6 +9141,47 @@ var PixiChart = class {
9052
9141
  // ─── Rendering ───────────────────────────────────────────────────────────────
9053
9142
  /** Styles whose renderer has already logged a failure this session. */
9054
9143
  _renderErrorLogged = /* @__PURE__ */ new Set();
9144
+ // ── Bar-replay anchor selection ─────────────────────────────────────────
9145
+ _replaySelect = null;
9146
+ _replayHoverTime = null;
9147
+ _replayDimGfx = null;
9148
+ _replayClickHandler = null;
9149
+ setReplaySelect(cb) {
9150
+ this._replaySelect = cb;
9151
+ this._replayHoverTime = null;
9152
+ if (cb) {
9153
+ this._replayClickHandler = () => {
9154
+ if (this._replayHoverTime !== null) this._replaySelect?.onCommit(this._replayHoverTime);
9155
+ };
9156
+ this._app.canvas.addEventListener("click", this._replayClickHandler);
9157
+ this._app.canvas.style.cursor = "crosshair";
9158
+ } else {
9159
+ if (this._replayClickHandler) {
9160
+ this._app.canvas.removeEventListener("click", this._replayClickHandler);
9161
+ this._replayClickHandler = null;
9162
+ }
9163
+ this._app.canvas.style.cursor = "";
9164
+ if (this._replayDimGfx) {
9165
+ this._replayDimGfx.clear();
9166
+ this._replayDimGfx.destroy();
9167
+ this._replayDimGfx = null;
9168
+ }
9169
+ }
9170
+ }
9171
+ /** Translucent veil over the bars a replay-anchor click would remove. */
9172
+ _drawReplayDim(x) {
9173
+ if (!this._replayDimGfx) {
9174
+ this._replayDimGfx = new Graphics();
9175
+ this._layers.get(5 /* Interaction */).addChildAt(this._replayDimGfx, 0);
9176
+ }
9177
+ const gfx = this._replayDimGfx;
9178
+ const { plotWidth, plotHeight } = this._transform;
9179
+ gfx.clear();
9180
+ if (x >= 0 && x < plotWidth) {
9181
+ gfx.rect(x, 0, plotWidth - x, plotHeight).fill({ color: 8421520, alpha: 0.35 });
9182
+ }
9183
+ this._layers.dirty.mark(5 /* Interaction */);
9184
+ }
9055
9185
  _renderFrame() {
9056
9186
  if (!this._ready || this._destroyed) return;
9057
9187
  const t = this._transform;
@@ -9061,6 +9191,9 @@ var PixiChart = class {
9061
9191
  if (dirty.isDirty(1 /* PriceSeries */)) {
9062
9192
  this._autoFitPriceRange();
9063
9193
  dirty.mark(0 /* Background */);
9194
+ this._candleRenderer.clear();
9195
+ this._lineRenderer.clear();
9196
+ this._histogramRenderer.clear();
9064
9197
  for (const series of this._series) {
9065
9198
  const opts = series.options();
9066
9199
  if (opts.visible === false) continue;
@@ -11197,8 +11330,10 @@ var KIT_FEATURE_TO_CAPABILITY = {
11197
11330
  multi_chart_layout: "multiChartLayout"
11198
11331
  };
11199
11332
  var UNMAPPED_KIT_FEATURES = [
11200
- "delayed_data",
11201
- "ai_script_conversion"
11333
+ "delayed_data"
11334
+ // 'ai_script_conversion' (deleted in migration 028) and 'agent' (the
11335
+ // ai_agent duplicate, deleted in migration 052) used to sit here; a key
11336
+ // that no longer exists in the catalog needs no unmapped entry.
11202
11337
  ];
11203
11338
  var PACKAGE_GATED_CAPABILITIES = new Set(
11204
11339
  Object.values(KIT_FEATURE_TO_CAPABILITY)
@@ -12008,8 +12143,8 @@ var ManagedTradingController = class {
12008
12143
  }
12009
12144
  };
12010
12145
 
12011
- // src/api/TChart.ts
12012
- var TChart = class _TChart {
12146
+ // src/api/ForgeCharts.ts
12147
+ var ForgeCharts = class _ForgeCharts {
12013
12148
  _core;
12014
12149
  _bus;
12015
12150
  _indicators;
@@ -12400,7 +12535,7 @@ var TChart = class _TChart {
12400
12535
  "[ForgeCharts] ForgeScript indicators are not enabled on the active license. Contact your license provider to enable the forgeScript feature."
12401
12536
  );
12402
12537
  }
12403
- let finalConfig = _TChart.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
12538
+ let finalConfig = _ForgeCharts.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
12404
12539
  if (finalConfig.overlay !== true && !canUseMultiPane()) {
12405
12540
  console.warn("[ForgeCharts] multiPane is not enabled on the active license \u2014 indicator added as a price-pane overlay instead.");
12406
12541
  finalConfig = { ...finalConfig, overlay: true };
@@ -12578,7 +12713,7 @@ var TChart = class _TChart {
12578
12713
  */
12579
12714
  startDrawingTool(type) {
12580
12715
  this._assertAlive();
12581
- if (_TChart.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
12716
+ if (_ForgeCharts.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
12582
12717
  console.warn(`[ForgeCharts] Drawing tool '${type}' requires the extendedDrawings feature, which is not enabled on the active license.`);
12583
12718
  return;
12584
12719
  }
@@ -12616,6 +12751,14 @@ var TChart = class _TChart {
12616
12751
  /** Set contract switch roll markers for continuous futures. */
12617
12752
  setContractSwitches(switches) {
12618
12753
  this._assertAlive();
12754
+ if (!canRenderOverlays()) {
12755
+ if (switches.length > 0) {
12756
+ console.warn("[ForgeCharts] Overlay rendering (overlays) is not enabled on the active plan \u2014 contract switch markers suppressed.");
12757
+ }
12758
+ this._core.setContractSwitches([]);
12759
+ this._core.markDirty();
12760
+ return;
12761
+ }
12619
12762
  this._core.setContractSwitches(switches);
12620
12763
  this._core.markDirty();
12621
12764
  }
@@ -12659,7 +12802,7 @@ var TChart = class _TChart {
12659
12802
  const snap = this._drawings.all().map((d) => ({ ...d, points: [...d.points] }));
12660
12803
  this._drawHistory.splice(this._drawHistIdx + 1);
12661
12804
  this._drawHistory.push(snap);
12662
- if (this._drawHistory.length > _TChart._HIST_MAX) this._drawHistory.shift();
12805
+ if (this._drawHistory.length > _ForgeCharts._HIST_MAX) this._drawHistory.shift();
12663
12806
  this._drawHistIdx = this._drawHistory.length - 1;
12664
12807
  }
12665
12808
  /** Steps drawing state one edit back (Ctrl+Z). */
@@ -13024,6 +13167,158 @@ var TChart = class _TChart {
13024
13167
  }
13025
13168
  return this._unmanagedIngestion;
13026
13169
  }
13170
+ // ─── Bar replay ───────────────────────────────────────────────────────────────
13171
+ //
13172
+ // Replay is a display cutoff on the primary series (Series.setCutoffTime)
13173
+ // plus this controller moving it. The full tape keeps flowing underneath —
13174
+ // live bars land in fullData() and stay hidden until stepped over or the
13175
+ // replay exits — so no datafeed, indicator, or renderer knows replay
13176
+ // exists. Selection (the hover shadow of what a click would remove) is a
13177
+ // backend surface only Pixi implements; ForgeCharts refuses replay elsewhere.
13178
+ /** Playback speeds, in bars per second. */
13179
+ static REPLAY_SPEEDS = [0.5, 1, 2, 5, 10];
13180
+ _replay = {
13181
+ selecting: false,
13182
+ active: false,
13183
+ playing: false,
13184
+ speedIdx: 1,
13185
+ timer: null
13186
+ };
13187
+ _emitReplayState() {
13188
+ this._bus.emit("replayStateChanged", {
13189
+ selecting: this._replay.selecting,
13190
+ active: this._replay.active,
13191
+ playing: this._replay.playing,
13192
+ speed: _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx],
13193
+ cutoffTime: this._series.cutoffTime()
13194
+ });
13195
+ }
13196
+ /** Current replay state — the control strip's render source. */
13197
+ replayState() {
13198
+ return {
13199
+ selecting: this._replay.selecting,
13200
+ active: this._replay.active,
13201
+ playing: this._replay.playing,
13202
+ speed: _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx],
13203
+ cutoffTime: this._series.cutoffTime()
13204
+ };
13205
+ }
13206
+ /**
13207
+ * Enter replay-anchor selection: the cursor shadows everything a click
13208
+ * would remove; clicking commits that bucket as the replay head. Returns
13209
+ * false (with a warning) when the plan lacks bar_replay or the backend
13210
+ * has no selection surface.
13211
+ */
13212
+ replayEnterSelection() {
13213
+ this._assertAlive();
13214
+ if (!canUseBarReplay()) {
13215
+ console.warn("[ForgeCharts] Bar replay (bar_replay) is not enabled on the active plan.");
13216
+ return false;
13217
+ }
13218
+ if (!this._core.setReplaySelect) {
13219
+ console.warn("[ForgeCharts] Bar replay requires the Pixi render backend.");
13220
+ return false;
13221
+ }
13222
+ this._replayStopTimer();
13223
+ this._replay.selecting = true;
13224
+ this._replay.playing = false;
13225
+ this._core.setReplaySelect({
13226
+ onHover: () => {
13227
+ },
13228
+ onCommit: (time) => this._replayCommit(time)
13229
+ });
13230
+ this._emitReplayState();
13231
+ return true;
13232
+ }
13233
+ /** Leave selection without committing (existing replay, if any, survives). */
13234
+ replayCancelSelection() {
13235
+ this._assertAlive();
13236
+ if (!this._replay.selecting) return;
13237
+ this._replay.selecting = false;
13238
+ this._core.setReplaySelect?.(null);
13239
+ this._emitReplayState();
13240
+ }
13241
+ _replayCommit(time) {
13242
+ this._replay.selecting = false;
13243
+ this._core.setReplaySelect?.(null);
13244
+ this._series.setCutoffTime(time);
13245
+ this._replay.active = true;
13246
+ this._replay.playing = false;
13247
+ this._core.markDirty();
13248
+ this._emitReplayState();
13249
+ }
13250
+ replayPlay() {
13251
+ this._assertAlive();
13252
+ if (!this._replay.active || this._replay.playing) return;
13253
+ this._replay.playing = true;
13254
+ this._replayStartTimer();
13255
+ this._emitReplayState();
13256
+ }
13257
+ replayPause() {
13258
+ this._assertAlive();
13259
+ if (!this._replay.playing) return;
13260
+ this._replay.playing = false;
13261
+ this._replayStopTimer();
13262
+ this._emitReplayState();
13263
+ }
13264
+ /** Advance the replay head n bars. Pauses at the end of the tape. */
13265
+ replayStepForward(n = 1) {
13266
+ this._assertAlive();
13267
+ if (!this._replay.active) return;
13268
+ const full = this._series.fullData();
13269
+ const shown = this._series.data().length;
13270
+ const nextIdx = Math.min(shown + n, full.length) - 1;
13271
+ if (nextIdx < shown) {
13272
+ this.replayPause();
13273
+ return;
13274
+ }
13275
+ this._series.setCutoffTime(full[nextIdx].time);
13276
+ this._core.markDirty();
13277
+ if (nextIdx >= full.length - 1) this.replayPause();
13278
+ this._emitReplayState();
13279
+ }
13280
+ /** Rewind the replay head n bars back in time (default 10). */
13281
+ replayRewind(n = 10) {
13282
+ this._assertAlive();
13283
+ if (!this._replay.active) return;
13284
+ const full = this._series.fullData();
13285
+ const shown = this._series.data().length;
13286
+ const idx = Math.max(shown - 1 - n, 0);
13287
+ if (full.length === 0) return;
13288
+ this._series.setCutoffTime(full[idx].time);
13289
+ this._core.markDirty();
13290
+ this._emitReplayState();
13291
+ }
13292
+ /** Cycle to the next playback speed; restarts the timer when playing. */
13293
+ replayCycleSpeed() {
13294
+ this._assertAlive();
13295
+ this._replay.speedIdx = (this._replay.speedIdx + 1) % _ForgeCharts.REPLAY_SPEEDS.length;
13296
+ if (this._replay.playing) this._replayStartTimer();
13297
+ this._emitReplayState();
13298
+ return _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
13299
+ }
13300
+ /** Exit replay entirely: the full tape (live head included) returns. */
13301
+ replayExit() {
13302
+ this._assertAlive();
13303
+ this._replayStopTimer();
13304
+ this._replay.selecting = false;
13305
+ this._replay.active = false;
13306
+ this._replay.playing = false;
13307
+ this._core.setReplaySelect?.(null);
13308
+ this._series.setCutoffTime(null);
13309
+ this._core.markDirty();
13310
+ this._core.scrollToEnd();
13311
+ this._emitReplayState();
13312
+ }
13313
+ _replayStartTimer() {
13314
+ this._replayStopTimer();
13315
+ const speed = _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
13316
+ this._replay.timer = setInterval(() => this.replayStepForward(1), Math.max(1e3 / speed, 16));
13317
+ }
13318
+ _replayStopTimer() {
13319
+ if (this._replay.timer) clearInterval(this._replay.timer);
13320
+ this._replay.timer = null;
13321
+ }
13027
13322
  // ─── Datafeed ─────────────────────────────────────────────────────────────────
13028
13323
  _buildConnector(datafeed) {
13029
13324
  return new DatafeedConnector(datafeed, this._series, {
@@ -13080,7 +13375,7 @@ function createChart(config) {
13080
13375
  if (!config.container) {
13081
13376
  throw new Error("[ForgeCharts] createChart() requires a valid container element.");
13082
13377
  }
13083
- return new TChart(config);
13378
+ return new ForgeCharts(config);
13084
13379
  }
13085
13380
 
13086
13381
  // src/api/ReferenceAPI.ts
@@ -18635,10 +18930,7 @@ function useHostLicense(apiUrl, getAuthToken) {
18635
18930
  function useUserPackage(apiUrl, getAuthToken) {
18636
18931
  useEffect(() => {
18637
18932
  const resolver3 = ChartRuntimeResolver.getInstance();
18638
- if (!apiUrl || !getAuthToken) {
18639
- resolver3.setUserPackageFeatures(null);
18640
- return;
18641
- }
18933
+ if (!apiUrl || !getAuthToken) return;
18642
18934
  let cancelled = false;
18643
18935
  void (async () => {
18644
18936
  try {
@@ -23353,6 +23645,14 @@ var ChartCanvas = forwardRef(
23353
23645
  },
23354
23646
  addIndicator: (config) => chartRef.current?.addIndicator(config) ?? null,
23355
23647
  setSeriesType: (type) => chartRef.current?.setSeriesType(type),
23648
+ toggleReplay: () => {
23649
+ const chart = chartRef.current;
23650
+ if (!chart) return;
23651
+ const st = chart.replayState();
23652
+ if (st.selecting) chart.replayCancelSelection();
23653
+ else if (st.active) chart.replayExit();
23654
+ else chart.replayEnterSelection();
23655
+ },
23356
23656
  removeIndicator: (id) => chartRef.current?.removeIndicator(id),
23357
23657
  moveIndicatorToOverlay: (id) => chartRef.current?.moveIndicatorToOverlay(id),
23358
23658
  moveIndicatorToPane: (id, targetPaneId) => chartRef.current?.moveIndicatorToPane(id, targetPaneId),
@@ -23400,7 +23700,7 @@ var ChartCanvas = forwardRef(
23400
23700
  useEffect(() => {
23401
23701
  const container = priceRef.current;
23402
23702
  if (!container) return;
23403
- const chart = new TChart({
23703
+ const chart = new ForgeCharts({
23404
23704
  container,
23405
23705
  symbol,
23406
23706
  interval: timeframe,
@@ -23414,6 +23714,9 @@ var ChartCanvas = forwardRef(
23414
23714
  for (const config of initialIndicators) {
23415
23715
  chart.addIndicator(config);
23416
23716
  }
23717
+ chart.on("replayStateChanged", (s) => {
23718
+ setReplayState(s.selecting || s.active ? s : null);
23719
+ });
23417
23720
  transformRef.current = chart.getTransform();
23418
23721
  const syncChartState = () => {
23419
23722
  setBars([...chart.getBars()]);
@@ -24373,6 +24676,7 @@ var ChartCanvas = forwardRef(
24373
24676
  selectPointerToolRef.current = handleToolSelect;
24374
24677
  const initialPointerAppliedRef = useRef(false);
24375
24678
  const initialSeriesAppliedRef = useRef(false);
24679
+ const [replayState, setReplayState] = useState(null);
24376
24680
  useEffect(() => {
24377
24681
  if (!initialSeriesAppliedRef.current && initialSeriesType && chartRef.current) {
24378
24682
  initialSeriesAppliedRef.current = true;
@@ -24381,7 +24685,7 @@ var ChartCanvas = forwardRef(
24381
24685
  if (initialPointerAppliedRef.current || !initialPointerTool) return;
24382
24686
  initialPointerAppliedRef.current = true;
24383
24687
  selectPointerToolRef.current(initialPointerTool);
24384
- }, [initialPointerTool]);
24688
+ }, [initialPointerTool, initialSeriesType]);
24385
24689
  const handleMagnetModeChange = useCallback((mode) => {
24386
24690
  setMagnetMode(mode);
24387
24691
  chartRef.current?.setMagnetMode(mode);
@@ -24477,6 +24781,107 @@ var ChartCanvas = forwardRef(
24477
24781
  crosshairXRef.current = null;
24478
24782
  },
24479
24783
  children: [
24784
+ replayState && (() => {
24785
+ const dark = effectiveTheme !== "light";
24786
+ const bg = dark ? "rgba(24,26,34,0.94)" : "rgba(255,255,255,0.96)";
24787
+ const fg = dark ? "#dfe3ee" : "#20242f";
24788
+ const line = dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
24789
+ const btn = {
24790
+ background: "transparent",
24791
+ border: "none",
24792
+ color: fg,
24793
+ cursor: "pointer",
24794
+ padding: "5px 9px",
24795
+ borderRadius: 6,
24796
+ fontSize: 12,
24797
+ display: "inline-flex",
24798
+ alignItems: "center",
24799
+ gap: 5,
24800
+ lineHeight: 1
24801
+ };
24802
+ const call = (fn) => () => {
24803
+ if (chartRef.current) fn(chartRef.current);
24804
+ };
24805
+ return /* @__PURE__ */ jsxs("div", { style: {
24806
+ position: "absolute",
24807
+ bottom: 46,
24808
+ left: "50%",
24809
+ transform: "translateX(-50%)",
24810
+ zIndex: 8,
24811
+ display: "flex",
24812
+ alignItems: "center",
24813
+ gap: 2,
24814
+ background: bg,
24815
+ color: fg,
24816
+ border: `1px solid ${line}`,
24817
+ borderRadius: 8,
24818
+ padding: "2px 6px",
24819
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
24820
+ userSelect: "none"
24821
+ }, children: [
24822
+ replayState.selecting ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, padding: "5px 9px" }, children: "Click a bar to start replay from there" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
24823
+ /* @__PURE__ */ jsx(
24824
+ "button",
24825
+ {
24826
+ style: btn,
24827
+ title: "Pick a new starting bar",
24828
+ onClick: call((c) => c.replayEnterSelection()),
24829
+ children: "\u2506\u2190 Select bar"
24830
+ }
24831
+ ),
24832
+ /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24833
+ /* @__PURE__ */ jsx(
24834
+ "button",
24835
+ {
24836
+ style: btn,
24837
+ title: "Rewind 10 bars",
24838
+ onClick: call((c) => c.replayRewind(10)),
24839
+ children: "\u23EA"
24840
+ }
24841
+ ),
24842
+ /* @__PURE__ */ jsx(
24843
+ "button",
24844
+ {
24845
+ style: btn,
24846
+ title: replayState.playing ? "Pause" : "Play",
24847
+ onClick: call((c) => replayState.playing ? c.replayPause() : c.replayPlay()),
24848
+ children: replayState.playing ? "\u23F8" : "\u25B6"
24849
+ }
24850
+ ),
24851
+ /* @__PURE__ */ jsx(
24852
+ "button",
24853
+ {
24854
+ style: btn,
24855
+ title: "Forward one bar",
24856
+ onClick: call((c) => c.replayStepForward(1)),
24857
+ children: "\u23E9"
24858
+ }
24859
+ ),
24860
+ /* @__PURE__ */ jsxs(
24861
+ "button",
24862
+ {
24863
+ style: { ...btn, fontVariantNumeric: "tabular-nums" },
24864
+ title: "Playback speed",
24865
+ onClick: call((c) => c.replayCycleSpeed()),
24866
+ children: [
24867
+ replayState.speed,
24868
+ "x"
24869
+ ]
24870
+ }
24871
+ )
24872
+ ] }),
24873
+ /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24874
+ /* @__PURE__ */ jsx(
24875
+ "button",
24876
+ {
24877
+ style: btn,
24878
+ title: replayState.selecting ? "Cancel selection" : "Exit replay",
24879
+ onClick: call((c) => replayState.selecting ? c.replayCancelSelection() : c.replayExit()),
24880
+ children: "\u2715"
24881
+ }
24882
+ )
24883
+ ] });
24884
+ })(),
24480
24885
  /* @__PURE__ */ jsx(
24481
24886
  "div",
24482
24887
  {
@@ -24564,7 +24969,7 @@ var ChartCanvas = forwardRef(
24564
24969
  symbol,
24565
24970
  timeframe,
24566
24971
  bars,
24567
- theme,
24972
+ theme: effectiveTheme,
24568
24973
  indicators,
24569
24974
  overlayColors: OVERLAY_COLORS,
24570
24975
  onRemove: handleRemoveIndicator,
@@ -25620,7 +26025,7 @@ var demonstrationTool = {
25620
26025
  };
25621
26026
 
25622
26027
  // src/version.ts
25623
- var FORGECHARTS_VERSION = "1.5.82" ;
26028
+ var FORGECHARTS_VERSION = "1.5.83" ;
25624
26029
 
25625
26030
  // src/compatibility.ts
25626
26031
  var MIN_KIT_API = 1;
@@ -25677,6 +26082,6 @@ function checkKitCompatibility(kit) {
25677
26082
  return { ...base, level: "ok", message: `SDK ${sdkVersion} matches the Kit.` };
25678
26083
  }
25679
26084
 
25680
- export { CROSSHAIR_LABEL, CROSSHAIR_STYLE, CURRENT_SCHEMA_VERSION, CURSOR_LABEL, CURSOR_STYLE, CandleEngine, ChartRuntimeResolver, CoordTransform, DEFAULT_SYNC_OPTIONS, DEMONSTRATION_COLOR, DEMONSTRATION_FILL, DEMONSTRATION_LABEL, DEMONSTRATION_RADIUS, DEMONSTRATION_STROKE, DEMONSTRATION_STYLE, DOT_COLOR, DOT_LABEL, DOT_RADIUS, DOT_STYLE, DatafeedConnector, ForgeScriptIndicator, ForgeScriptRuntime, Series2 as ForgeScriptSeries, GridChartContainer, GridSyncProvider, IndicatorDAG, KIT_FEATURE_TO_CAPABILITY, LayoutTemplateSelector, LicenseManager, MAX_KIT_API, MIN_KIT_API, ManagedTradingController, PACKAGE_GATED_CAPABILITIES, PRODUCT_TIERS, PaneManager, ReferenceAPI, TChart, TradingOverlayStore, UNMAPPED_KIT_FEATURES, UnmanagedIngestion, assertCanPlaceOrders, assertCanUseBrackets, assertCanUseDraggableOrders, assertCanUseManagedTrading, assertManagedMode, canPlaceBrackets, canPlaceOrders, canRenderBracketControls, canRenderBuySellButtons, canRenderCandles, canRenderDrawings, canRenderExternalOverlayOnlyMode, canRenderFills, canRenderIndicators, canRenderManagedTradingControls, canRenderOrderEntry, canRenderOrderModificationControls, canRenderOrderTicket, canRenderOverlays, canRenderPositions, canUseAiAgent, canUseAlerts, canUseBarReplay, canUseBracketOrders, canUseChartTypes, canUseCustomIndicators, canUseCustomTimeframes, canUseDataExport, canUseDraggable, canUseDraggableOrders, canUseDrawingTools, canUseExtendedDrawings, canUseExternalIngestion, canUseForgeScript, canUseHistoricalData, canUseIndicators, canUseManagedTrading, canUseManagedTradingHook, canUseMultiChartLayout, canUseMultiPane, canUseMultipleProviders, canUseMultipleWorkspaces, canUseOrderEntry, canUsePositionsOverlay, canUseRealtimeData, canUseSavedLayouts, canUseTrailingStops, canUseWatchlists, checkKitCompatibility, clearTelemetryListeners, computeEMA, computeEMAFromSeries, computeMACD, computeRSI, computeSMA, computeSMAFromSeries, computeVolume, computeWMAFromSeries, createChart, crosshairTool, cursorTool, demonstrationTool, detectLanguage, detectLanguageWithFallback, dotTool, evaluateSaveGate, exchangeNow, extractField, getBucketStart2 as getBucketStart, getCapabilities, getDefaultCapabilities, getDefaultStyle, getGridTemplate, getGroupedTemplates, getMissingBarCount, getStyleSlots, hasFeature, initServerClock, isManagedCapable, isManagedMode, isUnmanagedMode, isValidCombination, lineStyleToDash, mergeBars, migrateLegacyPayload, onConversionEvent, packageAllows, resolveStyle, runSandbox, serverClockOffset, serverNow, timeframeToMs2 as timeframeToMs, toCapabilityFlags, useGridSync, validateForgeScript, validateLicensePolicy };
26085
+ export { CROSSHAIR_LABEL, CROSSHAIR_STYLE, CURRENT_SCHEMA_VERSION, CURSOR_LABEL, CURSOR_STYLE, CandleEngine, ChartRuntimeResolver, CoordTransform, DEFAULT_SYNC_OPTIONS, DEMONSTRATION_COLOR, DEMONSTRATION_FILL, DEMONSTRATION_LABEL, DEMONSTRATION_RADIUS, DEMONSTRATION_STROKE, DEMONSTRATION_STYLE, DOT_COLOR, DOT_LABEL, DOT_RADIUS, DOT_STYLE, DatafeedConnector, ForgeCharts, ForgeScriptIndicator, ForgeScriptRuntime, Series2 as ForgeScriptSeries, GridChartContainer, GridSyncProvider, IndicatorDAG, KIT_FEATURE_TO_CAPABILITY, LayoutTemplateSelector, LicenseManager, MAX_KIT_API, MIN_KIT_API, ManagedTradingController, PACKAGE_GATED_CAPABILITIES, PRODUCT_TIERS, PaneManager, ReferenceAPI, ForgeCharts as TChart, TradingOverlayStore, UNMAPPED_KIT_FEATURES, UnmanagedIngestion, assertCanPlaceOrders, assertCanUseBrackets, assertCanUseDraggableOrders, assertCanUseManagedTrading, assertManagedMode, canPlaceBrackets, canPlaceOrders, canRenderBracketControls, canRenderBuySellButtons, canRenderCandles, canRenderDrawings, canRenderExternalOverlayOnlyMode, canRenderFills, canRenderIndicators, canRenderManagedTradingControls, canRenderOrderEntry, canRenderOrderModificationControls, canRenderOrderTicket, canRenderOverlays, canRenderPositions, canUseAiAgent, canUseAlerts, canUseBarReplay, canUseBracketOrders, canUseChartTypes, canUseCustomIndicators, canUseCustomTimeframes, canUseDataExport, canUseDraggable, canUseDraggableOrders, canUseDrawingTools, canUseExtendedDrawings, canUseExternalIngestion, canUseForgeScript, canUseHistoricalData, canUseIndicators, canUseManagedTrading, canUseManagedTradingHook, canUseMultiChartLayout, canUseMultiPane, canUseMultipleProviders, canUseMultipleWorkspaces, canUseOrderEntry, canUsePositionsOverlay, canUseRealtimeData, canUseSavedLayouts, canUseTrailingStops, canUseWatchlists, checkKitCompatibility, clearTelemetryListeners, computeEMA, computeEMAFromSeries, computeMACD, computeRSI, computeSMA, computeSMAFromSeries, computeVolume, computeWMAFromSeries, createChart, crosshairTool, cursorTool, demonstrationTool, detectLanguage, detectLanguageWithFallback, dotTool, evaluateSaveGate, exchangeNow, extractField, getBucketStart2 as getBucketStart, getCapabilities, getDefaultCapabilities, getDefaultStyle, getGridTemplate, getGroupedTemplates, getMissingBarCount, getStyleSlots, hasFeature, initServerClock, isManagedCapable, isManagedMode, isUnmanagedMode, isValidCombination, lineStyleToDash, mergeBars, migrateLegacyPayload, onConversionEvent, packageAllows, resolveStyle, runSandbox, serverClockOffset, serverNow, timeframeToMs2 as timeframeToMs, toCapabilityFlags, useGridSync, validateForgeScript, validateLicensePolicy };
25681
26086
  //# sourceMappingURL=index.js.map
25682
26087
  //# sourceMappingURL=index.js.map