@forgecharts/sdk 1.5.83 → 1.5.84

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/internal.js CHANGED
@@ -9416,6 +9416,16 @@ var PixiChart = class {
9416
9416
  }
9417
9417
  this._markViewportDirty();
9418
9418
  }
9419
+ /**
9420
+ * Pans the viewport so the bar at `time` sits at the horizontal center.
9421
+ * Used by bar replay: the anchor candle the trader picked becomes the
9422
+ * chart's center, with the (empty, dimmed-away) future to its right.
9423
+ */
9424
+ centerOnTime(time) {
9425
+ const px = this._transform.timeToX(time);
9426
+ this._transform.pan(-(px - this._transform.plotWidth / 2), 0);
9427
+ this._markViewportDirty();
9428
+ }
9419
9429
  /** Pans the viewport so the latest bar sits near the right edge. */
9420
9430
  _scrollToLatestBar() {
9421
9431
  let latestTime = -Infinity;
@@ -13186,7 +13196,7 @@ var ForgeCharts = class _ForgeCharts {
13186
13196
  // exists. Selection (the hover shadow of what a click would remove) is a
13187
13197
  // backend surface only Pixi implements; ForgeCharts refuses replay elsewhere.
13188
13198
  /** Playback speeds, in bars per second. */
13189
- static REPLAY_SPEEDS = [0.5, 1, 2, 5, 10];
13199
+ static REPLAY_SPEEDS = [0.5, 1, 2, 5, 10, 15, 20];
13190
13200
  _replay = {
13191
13201
  selecting: false,
13192
13202
  active: false,
@@ -13254,6 +13264,7 @@ var ForgeCharts = class _ForgeCharts {
13254
13264
  this._series.setCutoffTime(time);
13255
13265
  this._replay.active = true;
13256
13266
  this._replay.playing = false;
13267
+ this._core.centerOnTime?.(time);
13257
13268
  this._core.markDirty();
13258
13269
  this._emitReplayState();
13259
13270
  }
@@ -13299,6 +13310,33 @@ var ForgeCharts = class _ForgeCharts {
13299
13310
  this._core.markDirty();
13300
13311
  this._emitReplayState();
13301
13312
  }
13313
+ /** Set an exact playback speed (nearest catalogued value). */
13314
+ replaySetSpeed(speed) {
13315
+ this._assertAlive();
13316
+ let best = 0;
13317
+ for (let i = 0; i < _ForgeCharts.REPLAY_SPEEDS.length; i++) {
13318
+ if (Math.abs(_ForgeCharts.REPLAY_SPEEDS[i] - speed) < Math.abs(_ForgeCharts.REPLAY_SPEEDS[best] - speed)) best = i;
13319
+ }
13320
+ this._replay.speedIdx = best;
13321
+ if (this._replay.playing) this._replayStartTimer();
13322
+ this._emitReplayState();
13323
+ }
13324
+ /**
13325
+ * Restore a persisted replay session (browser refresh mid-replay). Applies
13326
+ * the same gate as entering selection; the cutoff simply lands where it
13327
+ * was, paused, with the anchor centered. A cutoff at or beyond the tape's
13328
+ * head means the replay had effectively finished — nothing is restored.
13329
+ */
13330
+ replayRestore(cutoffTime, speed) {
13331
+ this._assertAlive();
13332
+ if (!canUseBarReplay()) return false;
13333
+ const full = this._series.fullData();
13334
+ const last = full[full.length - 1];
13335
+ if (!last || cutoffTime >= last.time) return false;
13336
+ if (speed !== void 0) this.replaySetSpeed(speed);
13337
+ this._replayCommit(cutoffTime);
13338
+ return true;
13339
+ }
13302
13340
  /** Cycle to the next playback speed; restarts the timer when playing. */
13303
13341
  replayCycleSpeed() {
13304
13342
  this._assertAlive();
@@ -23536,6 +23574,8 @@ var ChartCanvas = forwardRef(
23536
23574
  onPointerToolChange,
23537
23575
  initialPointerTool,
23538
23576
  initialSeriesType,
23577
+ initialReplay,
23578
+ onReplayStateChange,
23539
23579
  initialMagnetMode,
23540
23580
  onMagnetModeChange,
23541
23581
  drawingFavorites: drawingFavoritesProp,
@@ -23726,6 +23766,7 @@ var ChartCanvas = forwardRef(
23726
23766
  }
23727
23767
  chart.on("replayStateChanged", (s) => {
23728
23768
  setReplayState(s.selecting || s.active ? s : null);
23769
+ onReplayStateChangeRef.current?.(s);
23729
23770
  });
23730
23771
  transformRef.current = chart.getTransform();
23731
23772
  const syncChartState = () => {
@@ -24687,6 +24728,36 @@ var ChartCanvas = forwardRef(
24687
24728
  const initialPointerAppliedRef = useRef(false);
24688
24729
  const initialSeriesAppliedRef = useRef(false);
24689
24730
  const [replayState, setReplayState] = useState(null);
24731
+ const [replayBarOffset, setReplayBarOffset] = useState(0);
24732
+ const replayDragRef = useRef(null);
24733
+ const onReplayStateChangeRef = useRef(onReplayStateChange);
24734
+ useEffect(() => {
24735
+ onReplayStateChangeRef.current = onReplayStateChange;
24736
+ }, [onReplayStateChange]);
24737
+ const initialReplayAppliedRef = useRef(false);
24738
+ useEffect(() => {
24739
+ if (initialReplayAppliedRef.current || !initialReplay || bars.length === 0) return;
24740
+ initialReplayAppliedRef.current = true;
24741
+ chartRef.current?.replayRestore(initialReplay.cutoffTime, initialReplay.speed);
24742
+ }, [initialReplay, bars]);
24743
+ useEffect(() => {
24744
+ const move = (e) => {
24745
+ const d = replayDragRef.current;
24746
+ if (!d) return;
24747
+ const host = outerRef.current;
24748
+ const max = host ? Math.max(0, host.clientHeight - 80) : 400;
24749
+ setReplayBarOffset(Math.min(max, Math.max(0, d.startOffset + (d.startY - e.clientY))));
24750
+ };
24751
+ const up = () => {
24752
+ replayDragRef.current = null;
24753
+ };
24754
+ window.addEventListener("pointermove", move);
24755
+ window.addEventListener("pointerup", up);
24756
+ return () => {
24757
+ window.removeEventListener("pointermove", move);
24758
+ window.removeEventListener("pointerup", up);
24759
+ };
24760
+ }, []);
24690
24761
  useEffect(() => {
24691
24762
  if (!initialSeriesAppliedRef.current && initialSeriesType && chartRef.current) {
24692
24763
  initialSeriesAppliedRef.current = true;
@@ -24793,15 +24864,15 @@ var ChartCanvas = forwardRef(
24793
24864
  children: [
24794
24865
  replayState && (() => {
24795
24866
  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)";
24867
+ const bg = dark ? "#000000" : "#ffffff";
24868
+ const fg = dark ? "#ffffff" : "#000000";
24869
+ const line = dark ? "rgba(255,255,255,0.22)" : "rgba(0,0,0,0.22)";
24799
24870
  const btn = {
24800
24871
  background: "transparent",
24801
24872
  border: "none",
24802
24873
  color: fg,
24803
24874
  cursor: "pointer",
24804
- padding: "5px 9px",
24875
+ padding: "6px 10px",
24805
24876
  borderRadius: 6,
24806
24877
  fontSize: 12,
24807
24878
  display: "inline-flex",
@@ -24809,27 +24880,44 @@ var ChartCanvas = forwardRef(
24809
24880
  gap: 5,
24810
24881
  lineHeight: 1
24811
24882
  };
24883
+ const sep = /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "6px 4px" } });
24812
24884
  const call = (fn) => () => {
24813
24885
  if (chartRef.current) fn(chartRef.current);
24814
24886
  };
24815
24887
  return /* @__PURE__ */ jsxs("div", { style: {
24816
24888
  position: "absolute",
24817
- bottom: 46,
24818
- left: "50%",
24819
- transform: "translateX(-50%)",
24889
+ left: 0,
24890
+ right: 0,
24891
+ bottom: replayBarOffset,
24820
24892
  zIndex: 8,
24821
24893
  display: "flex",
24822
24894
  alignItems: "center",
24823
24895
  gap: 2,
24896
+ height: 38,
24897
+ padding: "0 8px",
24898
+ boxSizing: "border-box",
24824
24899
  background: bg,
24825
24900
  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)",
24901
+ borderTop: `1px solid ${line}`,
24902
+ borderBottom: `1px solid ${line}`,
24830
24903
  userSelect: "none"
24831
24904
  }, 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: [
24905
+ /* @__PURE__ */ jsx(
24906
+ "span",
24907
+ {
24908
+ title: "Drag to move",
24909
+ onPointerDown: (e) => {
24910
+ e.preventDefault();
24911
+ replayDragRef.current = { startY: e.clientY, startOffset: replayBarOffset };
24912
+ },
24913
+ style: { cursor: "ns-resize", padding: "6px 6px", color: fg, opacity: 0.55, fontSize: 12, letterSpacing: 1 },
24914
+ children: "\u2059\u2059"
24915
+ }
24916
+ ),
24917
+ sep,
24918
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, fontWeight: 600, letterSpacing: 0.3, padding: "0 6px" }, children: "Replay" }),
24919
+ sep,
24920
+ replayState.selecting ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, padding: "0 8px", opacity: 0.85 }, children: "Click a bar to start replay from there" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
24833
24921
  /* @__PURE__ */ jsx(
24834
24922
  "button",
24835
24923
  {
@@ -24839,7 +24927,7 @@ var ChartCanvas = forwardRef(
24839
24927
  children: "\u2506\u2190 Select bar"
24840
24928
  }
24841
24929
  ),
24842
- /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24930
+ sep,
24843
24931
  /* @__PURE__ */ jsx(
24844
24932
  "button",
24845
24933
  {
@@ -24870,8 +24958,8 @@ var ChartCanvas = forwardRef(
24870
24958
  /* @__PURE__ */ jsxs(
24871
24959
  "button",
24872
24960
  {
24873
- style: { ...btn, fontVariantNumeric: "tabular-nums" },
24874
- title: "Playback speed",
24961
+ style: { ...btn, fontVariantNumeric: "tabular-nums", minWidth: 44, justifyContent: "center" },
24962
+ title: "Playback speed (0.5\u201320 bars per second)",
24875
24963
  onClick: call((c) => c.replayCycleSpeed()),
24876
24964
  children: [
24877
24965
  replayState.speed,
@@ -24880,7 +24968,7 @@ var ChartCanvas = forwardRef(
24880
24968
  }
24881
24969
  )
24882
24970
  ] }),
24883
- /* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
24971
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
24884
24972
  /* @__PURE__ */ jsx(
24885
24973
  "button",
24886
24974
  {
@@ -26035,7 +26123,7 @@ var demonstrationTool = {
26035
26123
  };
26036
26124
 
26037
26125
  // src/version.ts
26038
- var FORGECHARTS_VERSION = "1.5.83" ;
26126
+ var FORGECHARTS_VERSION = "1.5.84" ;
26039
26127
 
26040
26128
  // src/compatibility.ts
26041
26129
  var MIN_KIT_API = 1;