@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/internal.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. */
@@ -6114,9 +6151,17 @@ var PixiCandlestickRenderer = class {
6114
6151
  this._gfx = new Graphics();
6115
6152
  layer.addChild(this._gfx);
6116
6153
  }
6154
+ /** Clear this renderer's graphics — used when another style takes over. */
6155
+ clear() {
6156
+ this._gfx.clear();
6157
+ }
6117
6158
  draw(bars, transform, opts) {
6118
6159
  const gfx = this._gfx;
6119
6160
  gfx.clear();
6161
+ if (opts?.type === "bar") {
6162
+ this._drawBars(bars, transform, opts);
6163
+ return;
6164
+ }
6120
6165
  if (bars.length === 0) return;
6121
6166
  const { plotWidth: pw, plotHeight: ph } = transform;
6122
6167
  const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
@@ -6247,6 +6292,36 @@ var PixiCandlestickRenderer = class {
6247
6292
  /**
6248
6293
  * Converts OHLCV bars to Heikin-Ashi values before drawing.
6249
6294
  */
6295
+ /** Classic OHLC bars: high-low spine, open tick left, close tick right. */
6296
+ _drawBars(bars, transform, opts) {
6297
+ const gfx = this._gfx;
6298
+ if (bars.length === 0) return;
6299
+ const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
6300
+ const downColor = hexToNum(opts && "downColor" in opts ? opts.downColor : void 0, DOWN_COLOR);
6301
+ const { plotWidth: pw } = transform;
6302
+ let barPitch = pw / Math.max(1, bars.length);
6303
+ if (bars.length >= 2) {
6304
+ const dx = Math.abs(transform.timeToX(bars[1].time) - transform.timeToX(bars[0].time));
6305
+ if (dx > 0) barPitch = dx;
6306
+ }
6307
+ const tick = Math.max(1.5, Math.min(barPitch * 0.35, 6));
6308
+ const lineW = barPitch < 3 ? 1 : 1.5;
6309
+ for (const bar of bars) {
6310
+ const x = transform.timeToX(bar.time);
6311
+ if (x < -barPitch || x > pw + barPitch) continue;
6312
+ const yHigh = transform.priceToY(bar.high);
6313
+ const yLow = transform.priceToY(bar.low);
6314
+ const yOpen = transform.priceToY(bar.open);
6315
+ const yClose = transform.priceToY(bar.close);
6316
+ const color = bar.close >= bar.open ? upColor : downColor;
6317
+ gfx.moveTo(x, yHigh).lineTo(x, yLow);
6318
+ if (barPitch >= 3) {
6319
+ gfx.moveTo(x - tick, yOpen).lineTo(x, yOpen);
6320
+ gfx.moveTo(x, yClose).lineTo(x + tick, yClose);
6321
+ }
6322
+ gfx.stroke({ color, width: lineW });
6323
+ }
6324
+ }
6250
6325
  drawHeikinAshi(bars, transform, opts) {
6251
6326
  const first = bars[0];
6252
6327
  const last = bars[bars.length - 1];
@@ -7569,6 +7644,10 @@ var PixiLineRenderer = class {
7569
7644
  this._gradientCache = null;
7570
7645
  this._gfx.destroy(true);
7571
7646
  }
7647
+ /** Clear this renderer's graphics — used when another style takes over. */
7648
+ clear() {
7649
+ this._gfx.clear();
7650
+ }
7572
7651
  draw(bars, transform, options) {
7573
7652
  const gfx = this._gfx;
7574
7653
  gfx.clear();
@@ -7663,6 +7742,10 @@ var PixiHistogramRenderer = class {
7663
7742
  destroy() {
7664
7743
  this._gfx.destroy(true);
7665
7744
  }
7745
+ /** Clear this renderer's graphics — used when another style takes over. */
7746
+ clear() {
7747
+ this._gfx.clear();
7748
+ }
7666
7749
  draw(bars, transform) {
7667
7750
  const gfx = this._gfx;
7668
7751
  gfx.clear();
@@ -8642,7 +8725,7 @@ var PixiChart = class {
8642
8725
  /** Fired when an EXISTING drawing is modified via drag (handle or body move). */
8643
8726
  onDrawingEdited;
8644
8727
  _drawingEditDirty = false;
8645
- /** Ctrl+Z / Ctrl+Y — drawing history is owned by TChart. */
8728
+ /** Ctrl+Z / Ctrl+Y — drawing history is owned by ForgeCharts. */
8646
8729
  onUndoRequest;
8647
8730
  onRedoRequest;
8648
8731
  /** Called whenever a drawing is deleted. */
@@ -8657,7 +8740,7 @@ var PixiChart = class {
8657
8740
  onDrawingContextMenu;
8658
8741
  /** Called when the user double-clicks a drawing. */
8659
8742
  onDrawingDoubleClick;
8660
- /** Called after every pan/zoom so TChart can trigger lazy history loading. */
8743
+ /** Called after every pan/zoom so ForgeCharts can trigger lazy history loading. */
8661
8744
  onViewportChanged;
8662
8745
  constructor(container, options = {}) {
8663
8746
  this._container = container;
@@ -8764,6 +8847,12 @@ var PixiChart = class {
8764
8847
  this.onViewportChanged?.();
8765
8848
  },
8766
8849
  onCrosshairMove: (x, y) => {
8850
+ if (this._replaySelect) {
8851
+ const t = this._cursorBucketTime(x);
8852
+ this._replayHoverTime = t;
8853
+ this._replaySelect.onHover(t);
8854
+ this._drawReplayDim(x);
8855
+ }
8767
8856
  if (!this._crosshairEnabled) return;
8768
8857
  if (this._magnetMode === "off") {
8769
8858
  this._crosshairRenderer.update(x, y, this._cursorBucketTime(x));
@@ -9053,7 +9142,7 @@ var PixiChart = class {
9053
9142
  }).catch(() => {
9054
9143
  });
9055
9144
  }
9056
- // ─── Drawing objects (used by PixiChart directly; TChart uses DrawingManager) ─
9145
+ // ─── Drawing objects (used by PixiChart directly; ForgeCharts uses DrawingManager) ─
9057
9146
  /** @internal Used by PixiChart when DrawingManager pushes an update. */
9058
9147
  setDrawings(drawings) {
9059
9148
  this._drawings = [...drawings];
@@ -9062,6 +9151,47 @@ var PixiChart = class {
9062
9151
  // ─── Rendering ───────────────────────────────────────────────────────────────
9063
9152
  /** Styles whose renderer has already logged a failure this session. */
9064
9153
  _renderErrorLogged = /* @__PURE__ */ new Set();
9154
+ // ── Bar-replay anchor selection ─────────────────────────────────────────
9155
+ _replaySelect = null;
9156
+ _replayHoverTime = null;
9157
+ _replayDimGfx = null;
9158
+ _replayClickHandler = null;
9159
+ setReplaySelect(cb) {
9160
+ this._replaySelect = cb;
9161
+ this._replayHoverTime = null;
9162
+ if (cb) {
9163
+ this._replayClickHandler = () => {
9164
+ if (this._replayHoverTime !== null) this._replaySelect?.onCommit(this._replayHoverTime);
9165
+ };
9166
+ this._app.canvas.addEventListener("click", this._replayClickHandler);
9167
+ this._app.canvas.style.cursor = "crosshair";
9168
+ } else {
9169
+ if (this._replayClickHandler) {
9170
+ this._app.canvas.removeEventListener("click", this._replayClickHandler);
9171
+ this._replayClickHandler = null;
9172
+ }
9173
+ this._app.canvas.style.cursor = "";
9174
+ if (this._replayDimGfx) {
9175
+ this._replayDimGfx.clear();
9176
+ this._replayDimGfx.destroy();
9177
+ this._replayDimGfx = null;
9178
+ }
9179
+ }
9180
+ }
9181
+ /** Translucent veil over the bars a replay-anchor click would remove. */
9182
+ _drawReplayDim(x) {
9183
+ if (!this._replayDimGfx) {
9184
+ this._replayDimGfx = new Graphics();
9185
+ this._layers.get(5 /* Interaction */).addChildAt(this._replayDimGfx, 0);
9186
+ }
9187
+ const gfx = this._replayDimGfx;
9188
+ const { plotWidth, plotHeight } = this._transform;
9189
+ gfx.clear();
9190
+ if (x >= 0 && x < plotWidth) {
9191
+ gfx.rect(x, 0, plotWidth - x, plotHeight).fill({ color: 8421520, alpha: 0.35 });
9192
+ }
9193
+ this._layers.dirty.mark(5 /* Interaction */);
9194
+ }
9065
9195
  _renderFrame() {
9066
9196
  if (!this._ready || this._destroyed) return;
9067
9197
  const t = this._transform;
@@ -9071,6 +9201,9 @@ var PixiChart = class {
9071
9201
  if (dirty.isDirty(1 /* PriceSeries */)) {
9072
9202
  this._autoFitPriceRange();
9073
9203
  dirty.mark(0 /* Background */);
9204
+ this._candleRenderer.clear();
9205
+ this._lineRenderer.clear();
9206
+ this._histogramRenderer.clear();
9074
9207
  for (const series of this._series) {
9075
9208
  const opts = series.options();
9076
9209
  if (opts.visible === false) continue;
@@ -11207,8 +11340,10 @@ var KIT_FEATURE_TO_CAPABILITY = {
11207
11340
  multi_chart_layout: "multiChartLayout"
11208
11341
  };
11209
11342
  var UNMAPPED_KIT_FEATURES = [
11210
- "delayed_data",
11211
- "ai_script_conversion"
11343
+ "delayed_data"
11344
+ // 'ai_script_conversion' (deleted in migration 028) and 'agent' (the
11345
+ // ai_agent duplicate, deleted in migration 052) used to sit here; a key
11346
+ // that no longer exists in the catalog needs no unmapped entry.
11212
11347
  ];
11213
11348
  var PACKAGE_GATED_CAPABILITIES = new Set(
11214
11349
  Object.values(KIT_FEATURE_TO_CAPABILITY)
@@ -12018,8 +12153,8 @@ var ManagedTradingController = class {
12018
12153
  }
12019
12154
  };
12020
12155
 
12021
- // src/api/TChart.ts
12022
- var TChart = class _TChart {
12156
+ // src/api/ForgeCharts.ts
12157
+ var ForgeCharts = class _ForgeCharts {
12023
12158
  _core;
12024
12159
  _bus;
12025
12160
  _indicators;
@@ -12410,7 +12545,7 @@ var TChart = class _TChart {
12410
12545
  "[ForgeCharts] ForgeScript indicators are not enabled on the active license. Contact your license provider to enable the forgeScript feature."
12411
12546
  );
12412
12547
  }
12413
- let finalConfig = _TChart.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
12548
+ let finalConfig = _ForgeCharts.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
12414
12549
  if (finalConfig.overlay !== true && !canUseMultiPane()) {
12415
12550
  console.warn("[ForgeCharts] multiPane is not enabled on the active license \u2014 indicator added as a price-pane overlay instead.");
12416
12551
  finalConfig = { ...finalConfig, overlay: true };
@@ -12588,7 +12723,7 @@ var TChart = class _TChart {
12588
12723
  */
12589
12724
  startDrawingTool(type) {
12590
12725
  this._assertAlive();
12591
- if (_TChart.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
12726
+ if (_ForgeCharts.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
12592
12727
  console.warn(`[ForgeCharts] Drawing tool '${type}' requires the extendedDrawings feature, which is not enabled on the active license.`);
12593
12728
  return;
12594
12729
  }
@@ -12626,6 +12761,14 @@ var TChart = class _TChart {
12626
12761
  /** Set contract switch roll markers for continuous futures. */
12627
12762
  setContractSwitches(switches) {
12628
12763
  this._assertAlive();
12764
+ if (!canRenderOverlays()) {
12765
+ if (switches.length > 0) {
12766
+ console.warn("[ForgeCharts] Overlay rendering (overlays) is not enabled on the active plan \u2014 contract switch markers suppressed.");
12767
+ }
12768
+ this._core.setContractSwitches([]);
12769
+ this._core.markDirty();
12770
+ return;
12771
+ }
12629
12772
  this._core.setContractSwitches(switches);
12630
12773
  this._core.markDirty();
12631
12774
  }
@@ -12669,7 +12812,7 @@ var TChart = class _TChart {
12669
12812
  const snap = this._drawings.all().map((d) => ({ ...d, points: [...d.points] }));
12670
12813
  this._drawHistory.splice(this._drawHistIdx + 1);
12671
12814
  this._drawHistory.push(snap);
12672
- if (this._drawHistory.length > _TChart._HIST_MAX) this._drawHistory.shift();
12815
+ if (this._drawHistory.length > _ForgeCharts._HIST_MAX) this._drawHistory.shift();
12673
12816
  this._drawHistIdx = this._drawHistory.length - 1;
12674
12817
  }
12675
12818
  /** Steps drawing state one edit back (Ctrl+Z). */
@@ -13034,6 +13177,158 @@ var TChart = class _TChart {
13034
13177
  }
13035
13178
  return this._unmanagedIngestion;
13036
13179
  }
13180
+ // ─── Bar replay ───────────────────────────────────────────────────────────────
13181
+ //
13182
+ // Replay is a display cutoff on the primary series (Series.setCutoffTime)
13183
+ // plus this controller moving it. The full tape keeps flowing underneath —
13184
+ // live bars land in fullData() and stay hidden until stepped over or the
13185
+ // replay exits — so no datafeed, indicator, or renderer knows replay
13186
+ // exists. Selection (the hover shadow of what a click would remove) is a
13187
+ // backend surface only Pixi implements; ForgeCharts refuses replay elsewhere.
13188
+ /** Playback speeds, in bars per second. */
13189
+ static REPLAY_SPEEDS = [0.5, 1, 2, 5, 10];
13190
+ _replay = {
13191
+ selecting: false,
13192
+ active: false,
13193
+ playing: false,
13194
+ speedIdx: 1,
13195
+ timer: null
13196
+ };
13197
+ _emitReplayState() {
13198
+ this._bus.emit("replayStateChanged", {
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
+ /** Current replay state — the control strip's render source. */
13207
+ replayState() {
13208
+ return {
13209
+ selecting: this._replay.selecting,
13210
+ active: this._replay.active,
13211
+ playing: this._replay.playing,
13212
+ speed: _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx],
13213
+ cutoffTime: this._series.cutoffTime()
13214
+ };
13215
+ }
13216
+ /**
13217
+ * Enter replay-anchor selection: the cursor shadows everything a click
13218
+ * would remove; clicking commits that bucket as the replay head. Returns
13219
+ * false (with a warning) when the plan lacks bar_replay or the backend
13220
+ * has no selection surface.
13221
+ */
13222
+ replayEnterSelection() {
13223
+ this._assertAlive();
13224
+ if (!canUseBarReplay()) {
13225
+ console.warn("[ForgeCharts] Bar replay (bar_replay) is not enabled on the active plan.");
13226
+ return false;
13227
+ }
13228
+ if (!this._core.setReplaySelect) {
13229
+ console.warn("[ForgeCharts] Bar replay requires the Pixi render backend.");
13230
+ return false;
13231
+ }
13232
+ this._replayStopTimer();
13233
+ this._replay.selecting = true;
13234
+ this._replay.playing = false;
13235
+ this._core.setReplaySelect({
13236
+ onHover: () => {
13237
+ },
13238
+ onCommit: (time) => this._replayCommit(time)
13239
+ });
13240
+ this._emitReplayState();
13241
+ return true;
13242
+ }
13243
+ /** Leave selection without committing (existing replay, if any, survives). */
13244
+ replayCancelSelection() {
13245
+ this._assertAlive();
13246
+ if (!this._replay.selecting) return;
13247
+ this._replay.selecting = false;
13248
+ this._core.setReplaySelect?.(null);
13249
+ this._emitReplayState();
13250
+ }
13251
+ _replayCommit(time) {
13252
+ this._replay.selecting = false;
13253
+ this._core.setReplaySelect?.(null);
13254
+ this._series.setCutoffTime(time);
13255
+ this._replay.active = true;
13256
+ this._replay.playing = false;
13257
+ this._core.markDirty();
13258
+ this._emitReplayState();
13259
+ }
13260
+ replayPlay() {
13261
+ this._assertAlive();
13262
+ if (!this._replay.active || this._replay.playing) return;
13263
+ this._replay.playing = true;
13264
+ this._replayStartTimer();
13265
+ this._emitReplayState();
13266
+ }
13267
+ replayPause() {
13268
+ this._assertAlive();
13269
+ if (!this._replay.playing) return;
13270
+ this._replay.playing = false;
13271
+ this._replayStopTimer();
13272
+ this._emitReplayState();
13273
+ }
13274
+ /** Advance the replay head n bars. Pauses at the end of the tape. */
13275
+ replayStepForward(n = 1) {
13276
+ this._assertAlive();
13277
+ if (!this._replay.active) return;
13278
+ const full = this._series.fullData();
13279
+ const shown = this._series.data().length;
13280
+ const nextIdx = Math.min(shown + n, full.length) - 1;
13281
+ if (nextIdx < shown) {
13282
+ this.replayPause();
13283
+ return;
13284
+ }
13285
+ this._series.setCutoffTime(full[nextIdx].time);
13286
+ this._core.markDirty();
13287
+ if (nextIdx >= full.length - 1) this.replayPause();
13288
+ this._emitReplayState();
13289
+ }
13290
+ /** Rewind the replay head n bars back in time (default 10). */
13291
+ replayRewind(n = 10) {
13292
+ this._assertAlive();
13293
+ if (!this._replay.active) return;
13294
+ const full = this._series.fullData();
13295
+ const shown = this._series.data().length;
13296
+ const idx = Math.max(shown - 1 - n, 0);
13297
+ if (full.length === 0) return;
13298
+ this._series.setCutoffTime(full[idx].time);
13299
+ this._core.markDirty();
13300
+ this._emitReplayState();
13301
+ }
13302
+ /** Cycle to the next playback speed; restarts the timer when playing. */
13303
+ replayCycleSpeed() {
13304
+ this._assertAlive();
13305
+ this._replay.speedIdx = (this._replay.speedIdx + 1) % _ForgeCharts.REPLAY_SPEEDS.length;
13306
+ if (this._replay.playing) this._replayStartTimer();
13307
+ this._emitReplayState();
13308
+ return _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
13309
+ }
13310
+ /** Exit replay entirely: the full tape (live head included) returns. */
13311
+ replayExit() {
13312
+ this._assertAlive();
13313
+ this._replayStopTimer();
13314
+ this._replay.selecting = false;
13315
+ this._replay.active = false;
13316
+ this._replay.playing = false;
13317
+ this._core.setReplaySelect?.(null);
13318
+ this._series.setCutoffTime(null);
13319
+ this._core.markDirty();
13320
+ this._core.scrollToEnd();
13321
+ this._emitReplayState();
13322
+ }
13323
+ _replayStartTimer() {
13324
+ this._replayStopTimer();
13325
+ const speed = _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
13326
+ this._replay.timer = setInterval(() => this.replayStepForward(1), Math.max(1e3 / speed, 16));
13327
+ }
13328
+ _replayStopTimer() {
13329
+ if (this._replay.timer) clearInterval(this._replay.timer);
13330
+ this._replay.timer = null;
13331
+ }
13037
13332
  // ─── Datafeed ─────────────────────────────────────────────────────────────────
13038
13333
  _buildConnector(datafeed) {
13039
13334
  return new DatafeedConnector(datafeed, this._series, {
@@ -13090,7 +13385,7 @@ function createChart(config) {
13090
13385
  if (!config.container) {
13091
13386
  throw new Error("[ForgeCharts] createChart() requires a valid container element.");
13092
13387
  }
13093
- return new TChart(config);
13388
+ return new ForgeCharts(config);
13094
13389
  }
13095
13390
 
13096
13391
  // src/api/ReferenceAPI.ts
@@ -18645,10 +18940,7 @@ function useHostLicense(apiUrl, getAuthToken) {
18645
18940
  function useUserPackage(apiUrl, getAuthToken) {
18646
18941
  useEffect(() => {
18647
18942
  const resolver3 = ChartRuntimeResolver.getInstance();
18648
- if (!apiUrl || !getAuthToken) {
18649
- resolver3.setUserPackageFeatures(null);
18650
- return;
18651
- }
18943
+ if (!apiUrl || !getAuthToken) return;
18652
18944
  let cancelled = false;
18653
18945
  void (async () => {
18654
18946
  try {
@@ -23363,6 +23655,14 @@ var ChartCanvas = forwardRef(
23363
23655
  },
23364
23656
  addIndicator: (config) => chartRef.current?.addIndicator(config) ?? null,
23365
23657
  setSeriesType: (type) => chartRef.current?.setSeriesType(type),
23658
+ toggleReplay: () => {
23659
+ const chart = chartRef.current;
23660
+ if (!chart) return;
23661
+ const st = chart.replayState();
23662
+ if (st.selecting) chart.replayCancelSelection();
23663
+ else if (st.active) chart.replayExit();
23664
+ else chart.replayEnterSelection();
23665
+ },
23366
23666
  removeIndicator: (id) => chartRef.current?.removeIndicator(id),
23367
23667
  moveIndicatorToOverlay: (id) => chartRef.current?.moveIndicatorToOverlay(id),
23368
23668
  moveIndicatorToPane: (id, targetPaneId) => chartRef.current?.moveIndicatorToPane(id, targetPaneId),
@@ -23410,7 +23710,7 @@ var ChartCanvas = forwardRef(
23410
23710
  useEffect(() => {
23411
23711
  const container = priceRef.current;
23412
23712
  if (!container) return;
23413
- const chart = new TChart({
23713
+ const chart = new ForgeCharts({
23414
23714
  container,
23415
23715
  symbol,
23416
23716
  interval: timeframe,
@@ -23424,6 +23724,9 @@ var ChartCanvas = forwardRef(
23424
23724
  for (const config of initialIndicators) {
23425
23725
  chart.addIndicator(config);
23426
23726
  }
23727
+ chart.on("replayStateChanged", (s) => {
23728
+ setReplayState(s.selecting || s.active ? s : null);
23729
+ });
23427
23730
  transformRef.current = chart.getTransform();
23428
23731
  const syncChartState = () => {
23429
23732
  setBars([...chart.getBars()]);
@@ -24383,6 +24686,7 @@ var ChartCanvas = forwardRef(
24383
24686
  selectPointerToolRef.current = handleToolSelect;
24384
24687
  const initialPointerAppliedRef = useRef(false);
24385
24688
  const initialSeriesAppliedRef = useRef(false);
24689
+ const [replayState, setReplayState] = useState(null);
24386
24690
  useEffect(() => {
24387
24691
  if (!initialSeriesAppliedRef.current && initialSeriesType && chartRef.current) {
24388
24692
  initialSeriesAppliedRef.current = true;
@@ -24391,7 +24695,7 @@ var ChartCanvas = forwardRef(
24391
24695
  if (initialPointerAppliedRef.current || !initialPointerTool) return;
24392
24696
  initialPointerAppliedRef.current = true;
24393
24697
  selectPointerToolRef.current(initialPointerTool);
24394
- }, [initialPointerTool]);
24698
+ }, [initialPointerTool, initialSeriesType]);
24395
24699
  const handleMagnetModeChange = useCallback((mode) => {
24396
24700
  setMagnetMode(mode);
24397
24701
  chartRef.current?.setMagnetMode(mode);
@@ -24487,6 +24791,107 @@ var ChartCanvas = forwardRef(
24487
24791
  crosshairXRef.current = null;
24488
24792
  },
24489
24793
  children: [
24794
+ replayState && (() => {
24795
+ const dark = effectiveTheme !== "light";
24796
+ const bg = dark ? "rgba(24,26,34,0.94)" : "rgba(255,255,255,0.96)";
24797
+ const fg = dark ? "#dfe3ee" : "#20242f";
24798
+ const line = dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
24799
+ const btn = {
24800
+ background: "transparent",
24801
+ border: "none",
24802
+ color: fg,
24803
+ cursor: "pointer",
24804
+ padding: "5px 9px",
24805
+ borderRadius: 6,
24806
+ fontSize: 12,
24807
+ display: "inline-flex",
24808
+ alignItems: "center",
24809
+ gap: 5,
24810
+ lineHeight: 1
24811
+ };
24812
+ const call = (fn) => () => {
24813
+ if (chartRef.current) fn(chartRef.current);
24814
+ };
24815
+ return /* @__PURE__ */ jsxs("div", { style: {
24816
+ position: "absolute",
24817
+ bottom: 46,
24818
+ left: "50%",
24819
+ transform: "translateX(-50%)",
24820
+ zIndex: 8,
24821
+ display: "flex",
24822
+ alignItems: "center",
24823
+ gap: 2,
24824
+ background: bg,
24825
+ color: fg,
24826
+ border: `1px solid ${line}`,
24827
+ borderRadius: 8,
24828
+ padding: "2px 6px",
24829
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
24830
+ userSelect: "none"
24831
+ }, children: [
24832
+ replayState.selecting ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, padding: "5px 9px" }, children: "Click a bar to start replay from there" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
24833
+ /* @__PURE__ */ jsx(
24834
+ "button",
24835
+ {
24836
+ style: btn,
24837
+ title: "Pick a new starting bar",
24838
+ onClick: call((c) => c.replayEnterSelection()),
24839
+ children: "\u2506\u2190 Select bar"
24840
+ }
24841
+ ),
24842
+ /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24843
+ /* @__PURE__ */ jsx(
24844
+ "button",
24845
+ {
24846
+ style: btn,
24847
+ title: "Rewind 10 bars",
24848
+ onClick: call((c) => c.replayRewind(10)),
24849
+ children: "\u23EA"
24850
+ }
24851
+ ),
24852
+ /* @__PURE__ */ jsx(
24853
+ "button",
24854
+ {
24855
+ style: btn,
24856
+ title: replayState.playing ? "Pause" : "Play",
24857
+ onClick: call((c) => replayState.playing ? c.replayPause() : c.replayPlay()),
24858
+ children: replayState.playing ? "\u23F8" : "\u25B6"
24859
+ }
24860
+ ),
24861
+ /* @__PURE__ */ jsx(
24862
+ "button",
24863
+ {
24864
+ style: btn,
24865
+ title: "Forward one bar",
24866
+ onClick: call((c) => c.replayStepForward(1)),
24867
+ children: "\u23E9"
24868
+ }
24869
+ ),
24870
+ /* @__PURE__ */ jsxs(
24871
+ "button",
24872
+ {
24873
+ style: { ...btn, fontVariantNumeric: "tabular-nums" },
24874
+ title: "Playback speed",
24875
+ onClick: call((c) => c.replayCycleSpeed()),
24876
+ children: [
24877
+ replayState.speed,
24878
+ "x"
24879
+ ]
24880
+ }
24881
+ )
24882
+ ] }),
24883
+ /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24884
+ /* @__PURE__ */ jsx(
24885
+ "button",
24886
+ {
24887
+ style: btn,
24888
+ title: replayState.selecting ? "Cancel selection" : "Exit replay",
24889
+ onClick: call((c) => replayState.selecting ? c.replayCancelSelection() : c.replayExit()),
24890
+ children: "\u2715"
24891
+ }
24892
+ )
24893
+ ] });
24894
+ })(),
24490
24895
  /* @__PURE__ */ jsx(
24491
24896
  "div",
24492
24897
  {
@@ -24574,7 +24979,7 @@ var ChartCanvas = forwardRef(
24574
24979
  symbol,
24575
24980
  timeframe,
24576
24981
  bars,
24577
- theme,
24982
+ theme: effectiveTheme,
24578
24983
  indicators,
24579
24984
  overlayColors: OVERLAY_COLORS,
24580
24985
  onRemove: handleRemoveIndicator,
@@ -25630,7 +26035,7 @@ var demonstrationTool = {
25630
26035
  };
25631
26036
 
25632
26037
  // src/version.ts
25633
- var FORGECHARTS_VERSION = "1.5.82" ;
26038
+ var FORGECHARTS_VERSION = "1.5.83" ;
25634
26039
 
25635
26040
  // src/compatibility.ts
25636
26041
  var MIN_KIT_API = 1;
@@ -25687,6 +26092,6 @@ function checkKitCompatibility(kit) {
25687
26092
  return { ...base, level: "ok", message: `SDK ${sdkVersion} matches the Kit.` };
25688
26093
  }
25689
26094
 
25690
- export { CROSSHAIR_LABEL, CROSSHAIR_STYLE, CURRENT_SCHEMA_VERSION, CURSOR_LABEL, CURSOR_STYLE, CandleEngine, Chart, ChartRuntimeResolver, CoordTransform, Crosshair, DEFAULT_SYNC_OPTIONS, DEMONSTRATION_COLOR, DEMONSTRATION_FILL, DEMONSTRATION_LABEL, DEMONSTRATION_RADIUS, DEMONSTRATION_STROKE, DEMONSTRATION_STYLE, DOT_COLOR, DOT_LABEL, DOT_RADIUS, DOT_STYLE, DatafeedConnector, DirtyFlags, ForgeScriptIndicator, ForgeScriptRuntime, Series2 as ForgeScriptSeries, GridChartContainer, GridSyncProvider, IndicatorDAG, InteractionManager, KIT_FEATURE_TO_CAPABILITY, LayerName, LayoutTemplateSelector, LicenseManager, MAX_KIT_API, MIN_KIT_API, ManagedTradingController, PACKAGE_GATED_CAPABILITIES, PRODUCT_TIERS, PaneManager, PixiBenchmark, PixiChart, PixiLayerManager, PriceScale, ReferenceAPI, Series, TChart, TimeScale, 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 };
26095
+ export { CROSSHAIR_LABEL, CROSSHAIR_STYLE, CURRENT_SCHEMA_VERSION, CURSOR_LABEL, CURSOR_STYLE, CandleEngine, Chart, ChartRuntimeResolver, CoordTransform, Crosshair, DEFAULT_SYNC_OPTIONS, DEMONSTRATION_COLOR, DEMONSTRATION_FILL, DEMONSTRATION_LABEL, DEMONSTRATION_RADIUS, DEMONSTRATION_STROKE, DEMONSTRATION_STYLE, DOT_COLOR, DOT_LABEL, DOT_RADIUS, DOT_STYLE, DatafeedConnector, DirtyFlags, ForgeCharts, ForgeScriptIndicator, ForgeScriptRuntime, Series2 as ForgeScriptSeries, GridChartContainer, GridSyncProvider, IndicatorDAG, InteractionManager, KIT_FEATURE_TO_CAPABILITY, LayerName, LayoutTemplateSelector, LicenseManager, MAX_KIT_API, MIN_KIT_API, ManagedTradingController, PACKAGE_GATED_CAPABILITIES, PRODUCT_TIERS, PaneManager, PixiBenchmark, PixiChart, PixiLayerManager, PriceScale, ReferenceAPI, Series, ForgeCharts as TChart, TimeScale, 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 };
25691
26096
  //# sourceMappingURL=internal.js.map
25692
26097
  //# sourceMappingURL=internal.js.map