@forgecharts/sdk 1.5.69 → 1.5.71

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.
@@ -3,7 +3,32 @@ import { TextStyle, Application, Container, Graphics, Text, FillGradient, Canvas
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import ReactDOM, { createPortal } from 'react-dom';
5
5
 
6
- // src/react/canvas/ChartCanvas.tsx
6
+ // src/react/persistence/kitPreferencesApi.ts
7
+ function createKitPreferencesApi(cfg) {
8
+ const base = cfg.apiUrl.replace(/\/$/, "");
9
+ const authed = async () => {
10
+ const token = await cfg.getAuthToken();
11
+ return {
12
+ "Content-Type": "application/json",
13
+ ...token ? { Authorization: `Bearer ${token}` } : {}
14
+ };
15
+ };
16
+ return {
17
+ async get() {
18
+ const res = await fetch(`${base}/api/preferences`, { headers: await authed() });
19
+ if (!res.ok) throw new Error(`preferences load failed (${res.status})`);
20
+ return await res.json();
21
+ },
22
+ async put(data) {
23
+ const res = await fetch(`${base}/api/preferences`, {
24
+ method: "PUT",
25
+ headers: await authed(),
26
+ body: JSON.stringify(data)
27
+ });
28
+ if (!res.ok) throw new Error(`preferences save failed (${res.status})`);
29
+ }
30
+ };
31
+ }
7
32
 
8
33
  // ../shared/src/time/timeframeRegistry.ts
9
34
  function isCalendarTimeframe(key) {
@@ -102,6 +127,36 @@ function isGLBXDailyBreak(nowMs = Date.now()) {
102
127
  return weekday === "Mon" || weekday === "Tue" || weekday === "Wed" || weekday === "Thu";
103
128
  }
104
129
 
130
+ // src/core/cursorTime.ts
131
+ function cursorBucketTime(bars, transform, interval, x) {
132
+ const raw = transform.xToTime(x);
133
+ const tfSec = parseTfSeconds(interval) ?? 0;
134
+ const floored = tfSec > 0 ? Math.floor(raw / tfSec) * tfSec : raw;
135
+ if (!bars || bars.length === 0) return floored;
136
+ let lo = 0, hi = bars.length - 1;
137
+ while (lo < hi) {
138
+ const mid = lo + hi >>> 1;
139
+ if (bars[mid].time < raw) lo = mid + 1;
140
+ else hi = mid;
141
+ }
142
+ let nearest = lo;
143
+ let best = Math.abs(transform.timeToX(bars[lo].time) - x);
144
+ for (const i of [lo - 1, lo + 1]) {
145
+ if (i < 0 || i >= bars.length) continue;
146
+ const d = Math.abs(transform.timeToX(bars[i].time) - x);
147
+ if (d < best) {
148
+ best = d;
149
+ nearest = i;
150
+ }
151
+ }
152
+ const first = bars[0].time;
153
+ const last = bars[bars.length - 1].time;
154
+ const halfCol = bars.length > 1 ? Math.abs(transform.timeToX(bars[1].time) - transform.timeToX(first)) / 2 : Infinity;
155
+ const edgeX = raw > last ? transform.timeToX(last) : raw < first ? transform.timeToX(first) : null;
156
+ if (edgeX !== null && Math.abs(x - edgeX) > halfCol) return floored;
157
+ return bars[nearest].time;
158
+ }
159
+
105
160
  // src/core/CanvasLayer.ts
106
161
  var CanvasLayer = class {
107
162
  canvas;
@@ -4740,10 +4795,7 @@ var Chart = class {
4740
4795
  onCrosshairMove: (x, y) => {
4741
4796
  if (!this._crosshairEnabled) return;
4742
4797
  if (this._magnetMode === "off") {
4743
- const tfSec = _tfToSeconds(this._interval);
4744
- const rawTime = this._transform.xToTime(x);
4745
- const flooredTime = Math.floor(rawTime / tfSec) * tfSec;
4746
- this._crosshair.update(x, y, flooredTime);
4798
+ this._crosshair.update(x, y, this._cursorBucketTime(x));
4747
4799
  } else {
4748
4800
  const snap = this.snapXToBar(x);
4749
4801
  const sy = this._magnetMode === "snap-xy" ? snap.closeY ?? y : y;
@@ -4784,7 +4836,8 @@ var Chart = class {
4784
4836
  },
4785
4837
  onDrawPointerMove: (x, y) => {
4786
4838
  this._drawingCursorPt = {
4787
- time: this._transform.xToTime(x),
4839
+ // Bucket time, not raw: the label and the stored anchor agree.
4840
+ time: this._cursorBucketTime(x),
4788
4841
  price: this._transform.yToPrice(y)
4789
4842
  };
4790
4843
  this._dirty = true;
@@ -5057,9 +5110,19 @@ var Chart = class {
5057
5110
  closeY: this._transform.priceToY(bar.close)
5058
5111
  };
5059
5112
  }
5060
- const tfSec = _tfToSeconds(this._interval);
5061
- const rawTime = this._transform.xToTime(x);
5062
- return { x, time: Math.floor(rawTime / tfSec) * tfSec };
5113
+ return { x, time: this._cursorBucketTime(x) };
5114
+ }
5115
+ /** Bucket time for a cursor x see core/cursorTime.ts for the rule. */
5116
+ _cursorBucketTime(x) {
5117
+ let bars = null;
5118
+ for (const s of this._series) {
5119
+ const data = s.data();
5120
+ if (data.length > 0) {
5121
+ bars = data;
5122
+ break;
5123
+ }
5124
+ }
5125
+ return cursorBucketTime(bars, this._transform, this._interval, x);
5063
5126
  }
5064
5127
  _computeViewport() {
5065
5128
  return {
@@ -5536,7 +5599,7 @@ var Chart = class {
5536
5599
  const MAGNET_PX = 15;
5537
5600
  const t = this._transform;
5538
5601
  const price = t.yToPrice(y);
5539
- const time = t.xToTime(x);
5602
+ const time = this._cursorBucketTime(x);
5540
5603
  if (this._magnetMode === "off") return { time, price };
5541
5604
  let bestBar = null;
5542
5605
  let bestDist = SNAP_PX;
@@ -6235,7 +6298,16 @@ var PixiCrosshairRenderer = class {
6235
6298
  interval = "1h";
6236
6299
  /** IANA timezone for the time label. Falls back to UTC. */
6237
6300
  timezone = "UTC";
6238
- update(x, y) {
6301
+ /**
6302
+ * The time the chart computed for this cursor position — nearest bar, else
6303
+ * floored to the timeframe grid. This renderer must never derive its own:
6304
+ * it once did (raw xToTime), and every pointer style's time label showed
6305
+ * interpolated minute noise (10:02 on an hourly chart) on any timeframe
6306
+ * wider than the interpolation error.
6307
+ */
6308
+ _snapTime = null;
6309
+ update(x, y, snapTime) {
6310
+ this._snapTime = snapTime ?? null;
6239
6311
  this._x = x;
6240
6312
  this._y = y;
6241
6313
  }
@@ -6277,7 +6349,7 @@ var PixiCrosshairRenderer = class {
6277
6349
  this._priceLabel.text = priceText;
6278
6350
  this._priceLabel.x = pw + 4;
6279
6351
  this._priceLabel.y = y - this._priceLabel.height / 2;
6280
- const time = transform.xToTime(x);
6352
+ const time = this._snapTime ?? transform.xToTime(x);
6281
6353
  const timeText = this._formatTime(time);
6282
6354
  const tlw = DAILY_INTERVALS.has(this.interval) ? 120 : 170;
6283
6355
  const tlh = tsh - 4;
@@ -8674,11 +8746,11 @@ var PixiChart = class {
8674
8746
  onCrosshairMove: (x, y) => {
8675
8747
  if (!this._crosshairEnabled) return;
8676
8748
  if (this._magnetMode === "off") {
8677
- this._crosshairRenderer.update(x, y);
8749
+ this._crosshairRenderer.update(x, y, this._cursorBucketTime(x));
8678
8750
  } else {
8679
8751
  const snap = this.snapXToBar(x);
8680
8752
  const sy = this._magnetMode === "snap-xy" ? snap.closeY ?? y : y;
8681
- this._crosshairRenderer.update(snap.x, sy);
8753
+ this._crosshairRenderer.update(snap.x, sy, snap.time);
8682
8754
  }
8683
8755
  const ah = this._interaction.axisHover;
8684
8756
  if (ah !== this._lastAxisHover) {
@@ -8724,7 +8796,9 @@ var PixiChart = class {
8724
8796
  },
8725
8797
  onDrawPointerMove: (x, y) => {
8726
8798
  this._drawingCursorPt = {
8727
- time: this._transform.xToTime(x),
8799
+ // Bucket time, not raw: what the crosshair label shows is what a
8800
+ // drawing placed here will store.
8801
+ time: this._cursorBucketTime(x),
8728
8802
  price: this._transform.yToPrice(y)
8729
8803
  };
8730
8804
  this._layers.dirty.mark(3 /* Drawing */);
@@ -9291,6 +9365,18 @@ var PixiChart = class {
9291
9365
  setNativeCursorHidden(hidden) {
9292
9366
  this._interaction.setHideCursor(hidden);
9293
9367
  }
9368
+ /** Bucket time for a cursor x — see core/cursorTime.ts for the rule. */
9369
+ _cursorBucketTime(x) {
9370
+ let bars = null;
9371
+ for (const s of this._series) {
9372
+ const data = s.data();
9373
+ if (data.length > 0) {
9374
+ bars = data;
9375
+ break;
9376
+ }
9377
+ }
9378
+ return cursorBucketTime(bars, this._transform, this._interval, x);
9379
+ }
9294
9380
  snapXToBar(x) {
9295
9381
  const SNAP_PX = 20;
9296
9382
  let bars = null;
@@ -9334,7 +9420,7 @@ var PixiChart = class {
9334
9420
  closeY: t.priceToY(bar.close)
9335
9421
  };
9336
9422
  }
9337
- return { x, time: t.xToTime(x) };
9423
+ return { x, time: this._cursorBucketTime(x) };
9338
9424
  }
9339
9425
  setIndicatorsVisible(v) {
9340
9426
  this._indicatorsVisible = v;
@@ -18281,7 +18367,7 @@ var DEMONSTRATION_STROKE = "rgba(255, 200, 50, 0.7)";
18281
18367
  var DEMONSTRATION_COLOR = "var(--crosshair-overlay, rgba(255,255,255,0.75))";
18282
18368
 
18283
18369
  // src/version.ts
18284
- var FORGECHARTS_VERSION = "1.5.69" ;
18370
+ var FORGECHARTS_VERSION = "1.5.71" ;
18285
18371
  function applyRuntimeConfig(caps, config) {
18286
18372
  if (!config) return caps;
18287
18373
  const orderEntry = config.enableOrderEntry === false ? false : caps.orderEntry;
@@ -23116,6 +23202,7 @@ var ChartCanvas = forwardRef(
23116
23202
  providerKey,
23117
23203
  initialActiveTool,
23118
23204
  onPointerToolChange,
23205
+ initialPointerTool,
23119
23206
  initialMagnetMode,
23120
23207
  onMagnetModeChange,
23121
23208
  drawingFavorites: drawingFavoritesProp,
@@ -24252,6 +24339,12 @@ var ChartCanvas = forwardRef(
24252
24339
  const handleToolSelectRef = useRef(handleToolSelect);
24253
24340
  handleToolSelectRef.current = handleToolSelect;
24254
24341
  selectPointerToolRef.current = handleToolSelect;
24342
+ const initialPointerAppliedRef = useRef(false);
24343
+ useEffect(() => {
24344
+ if (initialPointerAppliedRef.current || !initialPointerTool) return;
24345
+ initialPointerAppliedRef.current = true;
24346
+ selectPointerToolRef.current(initialPointerTool);
24347
+ }, [initialPointerTool]);
24255
24348
  const handleMagnetModeChange = useCallback((mode) => {
24256
24349
  setMagnetMode(mode);
24257
24350
  chartRef.current?.setMagnetMode(mode);
@@ -37836,6 +37929,6 @@ function IndicatorPane({
37836
37929
  ] });
37837
37930
  }
37838
37931
 
37839
- export { AgentFAB, AgentProvider, AssistantPanel, BottomToolbar, ChartCanvas, ChartContextMenu, ChartSettingsDialog, ChartWorkspace, CommandDispatcher, DEFAULT_FAVORITES, FloatingPanel, IndicatorLabel, IndicatorPane, IndicatorsDialog, LayoutMenu, LeftToolbar, LicenseRequiredOverlay, ManagedAppShell, MultiPaneChart, OrderTicket, PointerOverlay, RightToolbar, SymbolSearchDialog, TabBar, TopToolbar, VoiceInput, createTradingBridgeLogger, useAgent, useChartCapabilities, useHostLicense, useLicenseDataBlocked, useUserPackage };
37932
+ export { AgentFAB, AgentProvider, AssistantPanel, BottomToolbar, ChartCanvas, ChartContextMenu, ChartSettingsDialog, ChartWorkspace, CommandDispatcher, DEFAULT_FAVORITES, FloatingPanel, IndicatorLabel, IndicatorPane, IndicatorsDialog, LayoutMenu, LeftToolbar, LicenseRequiredOverlay, ManagedAppShell, MultiPaneChart, OrderTicket, PointerOverlay, RightToolbar, SymbolSearchDialog, TabBar, TopToolbar, VoiceInput, createKitPreferencesApi, createTradingBridgeLogger, useAgent, useChartCapabilities, useHostLicense, useLicenseDataBlocked, useUserPackage };
37840
37933
  //# sourceMappingURL=internal.js.map
37841
37934
  //# sourceMappingURL=internal.js.map