@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.
- package/dist/api/EventBus.d.ts +1 -1
- package/dist/api/{TChart.d.ts → ForgeCharts.d.ts} +38 -4
- package/dist/api/ForgeCharts.d.ts.map +1 -0
- package/dist/api/createChart.d.ts +4 -4
- package/dist/api/createChart.d.ts.map +1 -1
- package/dist/core/Chart.d.ts +5 -5
- package/dist/core/Chart.d.ts.map +1 -1
- package/dist/core/Series.d.ts +14 -0
- package/dist/core/Series.d.ts.map +1 -1
- package/dist/core/__tests__/seriesCutoff.test.d.ts +2 -0
- package/dist/core/__tests__/seriesCutoff.test.d.ts.map +1 -0
- package/dist/datafeed/DatafeedConnector.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +432 -27
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +432 -27
- package/dist/internal.js.map +1 -1
- package/dist/licensing/packageFeatures.d.ts.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/pixi/PixiCandlestickRenderer.d.ts +4 -0
- package/dist/pixi/PixiCandlestickRenderer.d.ts.map +1 -1
- package/dist/pixi/PixiChart.d.ts +12 -2
- package/dist/pixi/PixiChart.d.ts.map +1 -1
- package/dist/pixi/PixiHistogramRenderer.d.ts +2 -0
- package/dist/pixi/PixiHistogramRenderer.d.ts.map +1 -1
- package/dist/pixi/PixiLineRenderer.d.ts +2 -0
- package/dist/pixi/PixiLineRenderer.d.ts.map +1 -1
- package/dist/react/canvas/ChartCanvas.d.ts +7 -2
- package/dist/react/canvas/ChartCanvas.d.ts.map +1 -1
- package/dist/react/hooks/useUserPackage.d.ts.map +1 -1
- package/dist/react/index.js +719 -63
- package/dist/react/index.js.map +1 -1
- package/dist/react/internal.js +726 -83
- package/dist/react/internal.js.map +1 -1
- package/dist/react/shell/AlertsDrawer.d.ts +11 -0
- package/dist/react/shell/AlertsDrawer.d.ts.map +1 -0
- package/dist/react/shell/ManagedAppShell.d.ts +1 -1
- package/dist/react/shell/ManagedAppShell.d.ts.map +1 -1
- package/dist/react/workspace/ChartWorkspace.d.ts +10 -1
- package/dist/react/workspace/ChartWorkspace.d.ts.map +1 -1
- package/dist/react/workspace/LayoutMenu.d.ts +3 -1
- package/dist/react/workspace/LayoutMenu.d.ts.map +1 -1
- package/dist/react/workspace/toolbars/TopToolbar.d.ts +7 -1
- package/dist/react/workspace/toolbars/TopToolbar.d.ts.map +1 -1
- package/dist/types/ChartBackend.d.ts +15 -4
- package/dist/types/ChartBackend.d.ts.map +1 -1
- package/dist/types/ISeries.d.ts +5 -0
- package/dist/types/ISeries.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/api/TChart.d.ts.map +0 -1
package/dist/react/internal.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React11, { createContext, forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback, useMemo, useContext, useReducer } from 'react';
|
|
2
|
-
import { TextStyle, Application,
|
|
2
|
+
import { TextStyle, Application, Graphics, Container, Text, FillGradient, CanvasTextMetrics } from 'pixi.js';
|
|
3
3
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import ReactDOM, { createPortal } from 'react-dom';
|
|
5
5
|
|
|
@@ -1410,6 +1410,7 @@ var Series = class _Series {
|
|
|
1410
1410
|
// ─── ISeries implementation ──────────────────────────────────────────────────
|
|
1411
1411
|
setData(data) {
|
|
1412
1412
|
this._data = [...data].sort((a, b) => a.time - b.time);
|
|
1413
|
+
this._cutoffView = null;
|
|
1413
1414
|
this.onDataChanged?.();
|
|
1414
1415
|
}
|
|
1415
1416
|
/**
|
|
@@ -1449,11 +1450,47 @@ var Series = class _Series {
|
|
|
1449
1450
|
this._data = this._data.slice(this._data.length - 1e5);
|
|
1450
1451
|
}
|
|
1451
1452
|
}
|
|
1453
|
+
this._cutoffView = null;
|
|
1452
1454
|
this.onDataChanged?.();
|
|
1453
1455
|
}
|
|
1454
|
-
|
|
1456
|
+
/**
|
|
1457
|
+
* Bar-replay display cutoff. When set, data() returns only bars at or
|
|
1458
|
+
* before this time while the FULL tape keeps flowing underneath — the
|
|
1459
|
+
* datafeed keeps writing live bars via setData/update, and everything
|
|
1460
|
+
* downstream (renderers, indicators, crosshair, last-price) naturally sees
|
|
1461
|
+
* the truncated past. Replay is not a mode the pipeline knows about; it is
|
|
1462
|
+
* this clamp plus a controller that moves it.
|
|
1463
|
+
*/
|
|
1464
|
+
_cutoffTime = null;
|
|
1465
|
+
_cutoffView = null;
|
|
1466
|
+
setCutoffTime(time) {
|
|
1467
|
+
this._cutoffTime = time;
|
|
1468
|
+
this._cutoffView = null;
|
|
1469
|
+
this.onDataChanged?.();
|
|
1470
|
+
}
|
|
1471
|
+
cutoffTime() {
|
|
1472
|
+
return this._cutoffTime;
|
|
1473
|
+
}
|
|
1474
|
+
/** The complete tape, ignoring any replay cutoff. Datafeed-side reads only. */
|
|
1475
|
+
fullData() {
|
|
1455
1476
|
return this._data;
|
|
1456
1477
|
}
|
|
1478
|
+
data() {
|
|
1479
|
+
if (this._cutoffTime === null) return this._data;
|
|
1480
|
+
const len = this._data.length;
|
|
1481
|
+
const cached = this._cutoffView;
|
|
1482
|
+
if (cached && cached.len === len && cached.cutoff === this._cutoffTime) return cached.view;
|
|
1483
|
+
let lo = 0;
|
|
1484
|
+
let hi = len;
|
|
1485
|
+
while (lo < hi) {
|
|
1486
|
+
const mid = lo + hi >>> 1;
|
|
1487
|
+
if (this._data[mid].time <= this._cutoffTime) lo = mid + 1;
|
|
1488
|
+
else hi = mid;
|
|
1489
|
+
}
|
|
1490
|
+
const view = this._data.slice(0, lo);
|
|
1491
|
+
this._cutoffView = { len, cutoff: this._cutoffTime, view };
|
|
1492
|
+
return view;
|
|
1493
|
+
}
|
|
1457
1494
|
applyOptions(options) {
|
|
1458
1495
|
this._options = { ...this._options, ...options };
|
|
1459
1496
|
}
|
|
@@ -4730,14 +4767,14 @@ var Chart = class {
|
|
|
4730
4767
|
_crosshairEnabled = true;
|
|
4731
4768
|
_magnetMode = "snap-xy";
|
|
4732
4769
|
// ── Trading overlay ───────────────────────────────────────────────────────────
|
|
4733
|
-
/** Current set of order lines supplied by
|
|
4770
|
+
/** Current set of order lines supplied by ForgeCharts for canvas rendering. */
|
|
4734
4771
|
_tradingOrders = [];
|
|
4735
4772
|
/** Called whenever a drawing is committed (created or updated). */
|
|
4736
4773
|
onDrawingCommitted;
|
|
4737
4774
|
/** Fired when an EXISTING drawing is modified via drag (handle or body move). */
|
|
4738
4775
|
onDrawingEdited;
|
|
4739
4776
|
_drawingEditDirty = false;
|
|
4740
|
-
/** Ctrl+Z / Ctrl+Y — drawing history is owned by
|
|
4777
|
+
/** Ctrl+Z / Ctrl+Y — drawing history is owned by ForgeCharts. */
|
|
4741
4778
|
onUndoRequest;
|
|
4742
4779
|
onRedoRequest;
|
|
4743
4780
|
/** Called whenever a drawing is deleted. */
|
|
@@ -4753,7 +4790,7 @@ var Chart = class {
|
|
|
4753
4790
|
/** Called when the user drags a TP/SL pill off an entry line to create a bracket leg. */
|
|
4754
4791
|
onTpSlCreate;
|
|
4755
4792
|
/**
|
|
4756
|
-
* Called after every pan or zoom interaction so external code (e.g.
|
|
4793
|
+
* Called after every pan or zoom interaction so external code (e.g. ForgeCharts)
|
|
4757
4794
|
* can check whether the visible time range now extends past the oldest
|
|
4758
4795
|
* loaded bar and trigger a lazy history fetch.
|
|
4759
4796
|
*/
|
|
@@ -5256,11 +5293,11 @@ var Chart = class {
|
|
|
5256
5293
|
}
|
|
5257
5294
|
ctx.restore();
|
|
5258
5295
|
}
|
|
5259
|
-
/** Provides the overlay renderer with the latest order list from
|
|
5296
|
+
/** Provides the overlay renderer with the latest order list from ForgeCharts. */
|
|
5260
5297
|
setTradingOrders(orders) {
|
|
5261
5298
|
this._tradingOrders = orders;
|
|
5262
5299
|
}
|
|
5263
|
-
/** Provides the overlay renderer with the latest position list from
|
|
5300
|
+
/** Provides the overlay renderer with the latest position list from ForgeCharts. */
|
|
5264
5301
|
setTradingPositions(_positions) {
|
|
5265
5302
|
}
|
|
5266
5303
|
/** Sets contract switch markers for continuous futures roll dates. */
|
|
@@ -6077,9 +6114,17 @@ var PixiCandlestickRenderer = class {
|
|
|
6077
6114
|
this._gfx = new Graphics();
|
|
6078
6115
|
layer.addChild(this._gfx);
|
|
6079
6116
|
}
|
|
6117
|
+
/** Clear this renderer's graphics — used when another style takes over. */
|
|
6118
|
+
clear() {
|
|
6119
|
+
this._gfx.clear();
|
|
6120
|
+
}
|
|
6080
6121
|
draw(bars, transform, opts) {
|
|
6081
6122
|
const gfx = this._gfx;
|
|
6082
6123
|
gfx.clear();
|
|
6124
|
+
if (opts?.type === "bar") {
|
|
6125
|
+
this._drawBars(bars, transform, opts);
|
|
6126
|
+
return;
|
|
6127
|
+
}
|
|
6083
6128
|
if (bars.length === 0) return;
|
|
6084
6129
|
const { plotWidth: pw, plotHeight: ph } = transform;
|
|
6085
6130
|
const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
|
|
@@ -6210,6 +6255,36 @@ var PixiCandlestickRenderer = class {
|
|
|
6210
6255
|
/**
|
|
6211
6256
|
* Converts OHLCV bars to Heikin-Ashi values before drawing.
|
|
6212
6257
|
*/
|
|
6258
|
+
/** Classic OHLC bars: high-low spine, open tick left, close tick right. */
|
|
6259
|
+
_drawBars(bars, transform, opts) {
|
|
6260
|
+
const gfx = this._gfx;
|
|
6261
|
+
if (bars.length === 0) return;
|
|
6262
|
+
const upColor = hexToNum(opts && "upColor" in opts ? opts.upColor : void 0, UP_COLOR);
|
|
6263
|
+
const downColor = hexToNum(opts && "downColor" in opts ? opts.downColor : void 0, DOWN_COLOR);
|
|
6264
|
+
const { plotWidth: pw } = transform;
|
|
6265
|
+
let barPitch = pw / Math.max(1, bars.length);
|
|
6266
|
+
if (bars.length >= 2) {
|
|
6267
|
+
const dx = Math.abs(transform.timeToX(bars[1].time) - transform.timeToX(bars[0].time));
|
|
6268
|
+
if (dx > 0) barPitch = dx;
|
|
6269
|
+
}
|
|
6270
|
+
const tick = Math.max(1.5, Math.min(barPitch * 0.35, 6));
|
|
6271
|
+
const lineW = barPitch < 3 ? 1 : 1.5;
|
|
6272
|
+
for (const bar of bars) {
|
|
6273
|
+
const x = transform.timeToX(bar.time);
|
|
6274
|
+
if (x < -barPitch || x > pw + barPitch) continue;
|
|
6275
|
+
const yHigh = transform.priceToY(bar.high);
|
|
6276
|
+
const yLow = transform.priceToY(bar.low);
|
|
6277
|
+
const yOpen = transform.priceToY(bar.open);
|
|
6278
|
+
const yClose = transform.priceToY(bar.close);
|
|
6279
|
+
const color = bar.close >= bar.open ? upColor : downColor;
|
|
6280
|
+
gfx.moveTo(x, yHigh).lineTo(x, yLow);
|
|
6281
|
+
if (barPitch >= 3) {
|
|
6282
|
+
gfx.moveTo(x - tick, yOpen).lineTo(x, yOpen);
|
|
6283
|
+
gfx.moveTo(x, yClose).lineTo(x + tick, yClose);
|
|
6284
|
+
}
|
|
6285
|
+
gfx.stroke({ color, width: lineW });
|
|
6286
|
+
}
|
|
6287
|
+
}
|
|
6213
6288
|
drawHeikinAshi(bars, transform, opts) {
|
|
6214
6289
|
const first = bars[0];
|
|
6215
6290
|
const last = bars[bars.length - 1];
|
|
@@ -7532,6 +7607,10 @@ var PixiLineRenderer = class {
|
|
|
7532
7607
|
this._gradientCache = null;
|
|
7533
7608
|
this._gfx.destroy(true);
|
|
7534
7609
|
}
|
|
7610
|
+
/** Clear this renderer's graphics — used when another style takes over. */
|
|
7611
|
+
clear() {
|
|
7612
|
+
this._gfx.clear();
|
|
7613
|
+
}
|
|
7535
7614
|
draw(bars, transform, options) {
|
|
7536
7615
|
const gfx = this._gfx;
|
|
7537
7616
|
gfx.clear();
|
|
@@ -7626,6 +7705,10 @@ var PixiHistogramRenderer = class {
|
|
|
7626
7705
|
destroy() {
|
|
7627
7706
|
this._gfx.destroy(true);
|
|
7628
7707
|
}
|
|
7708
|
+
/** Clear this renderer's graphics — used when another style takes over. */
|
|
7709
|
+
clear() {
|
|
7710
|
+
this._gfx.clear();
|
|
7711
|
+
}
|
|
7629
7712
|
draw(bars, transform) {
|
|
7630
7713
|
const gfx = this._gfx;
|
|
7631
7714
|
gfx.clear();
|
|
@@ -8605,7 +8688,7 @@ var PixiChart = class {
|
|
|
8605
8688
|
/** Fired when an EXISTING drawing is modified via drag (handle or body move). */
|
|
8606
8689
|
onDrawingEdited;
|
|
8607
8690
|
_drawingEditDirty = false;
|
|
8608
|
-
/** Ctrl+Z / Ctrl+Y — drawing history is owned by
|
|
8691
|
+
/** Ctrl+Z / Ctrl+Y — drawing history is owned by ForgeCharts. */
|
|
8609
8692
|
onUndoRequest;
|
|
8610
8693
|
onRedoRequest;
|
|
8611
8694
|
/** Called whenever a drawing is deleted. */
|
|
@@ -8620,7 +8703,7 @@ var PixiChart = class {
|
|
|
8620
8703
|
onDrawingContextMenu;
|
|
8621
8704
|
/** Called when the user double-clicks a drawing. */
|
|
8622
8705
|
onDrawingDoubleClick;
|
|
8623
|
-
/** Called after every pan/zoom so
|
|
8706
|
+
/** Called after every pan/zoom so ForgeCharts can trigger lazy history loading. */
|
|
8624
8707
|
onViewportChanged;
|
|
8625
8708
|
constructor(container, options = {}) {
|
|
8626
8709
|
this._container = container;
|
|
@@ -8727,6 +8810,12 @@ var PixiChart = class {
|
|
|
8727
8810
|
this.onViewportChanged?.();
|
|
8728
8811
|
},
|
|
8729
8812
|
onCrosshairMove: (x, y) => {
|
|
8813
|
+
if (this._replaySelect) {
|
|
8814
|
+
const t = this._cursorBucketTime(x);
|
|
8815
|
+
this._replayHoverTime = t;
|
|
8816
|
+
this._replaySelect.onHover(t);
|
|
8817
|
+
this._drawReplayDim(x);
|
|
8818
|
+
}
|
|
8730
8819
|
if (!this._crosshairEnabled) return;
|
|
8731
8820
|
if (this._magnetMode === "off") {
|
|
8732
8821
|
this._crosshairRenderer.update(x, y, this._cursorBucketTime(x));
|
|
@@ -9016,7 +9105,7 @@ var PixiChart = class {
|
|
|
9016
9105
|
}).catch(() => {
|
|
9017
9106
|
});
|
|
9018
9107
|
}
|
|
9019
|
-
// ─── Drawing objects (used by PixiChart directly;
|
|
9108
|
+
// ─── Drawing objects (used by PixiChart directly; ForgeCharts uses DrawingManager) ─
|
|
9020
9109
|
/** @internal Used by PixiChart when DrawingManager pushes an update. */
|
|
9021
9110
|
setDrawings(drawings) {
|
|
9022
9111
|
this._drawings = [...drawings];
|
|
@@ -9025,6 +9114,47 @@ var PixiChart = class {
|
|
|
9025
9114
|
// ─── Rendering ───────────────────────────────────────────────────────────────
|
|
9026
9115
|
/** Styles whose renderer has already logged a failure this session. */
|
|
9027
9116
|
_renderErrorLogged = /* @__PURE__ */ new Set();
|
|
9117
|
+
// ── Bar-replay anchor selection ─────────────────────────────────────────
|
|
9118
|
+
_replaySelect = null;
|
|
9119
|
+
_replayHoverTime = null;
|
|
9120
|
+
_replayDimGfx = null;
|
|
9121
|
+
_replayClickHandler = null;
|
|
9122
|
+
setReplaySelect(cb) {
|
|
9123
|
+
this._replaySelect = cb;
|
|
9124
|
+
this._replayHoverTime = null;
|
|
9125
|
+
if (cb) {
|
|
9126
|
+
this._replayClickHandler = () => {
|
|
9127
|
+
if (this._replayHoverTime !== null) this._replaySelect?.onCommit(this._replayHoverTime);
|
|
9128
|
+
};
|
|
9129
|
+
this._app.canvas.addEventListener("click", this._replayClickHandler);
|
|
9130
|
+
this._app.canvas.style.cursor = "crosshair";
|
|
9131
|
+
} else {
|
|
9132
|
+
if (this._replayClickHandler) {
|
|
9133
|
+
this._app.canvas.removeEventListener("click", this._replayClickHandler);
|
|
9134
|
+
this._replayClickHandler = null;
|
|
9135
|
+
}
|
|
9136
|
+
this._app.canvas.style.cursor = "";
|
|
9137
|
+
if (this._replayDimGfx) {
|
|
9138
|
+
this._replayDimGfx.clear();
|
|
9139
|
+
this._replayDimGfx.destroy();
|
|
9140
|
+
this._replayDimGfx = null;
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
/** Translucent veil over the bars a replay-anchor click would remove. */
|
|
9145
|
+
_drawReplayDim(x) {
|
|
9146
|
+
if (!this._replayDimGfx) {
|
|
9147
|
+
this._replayDimGfx = new Graphics();
|
|
9148
|
+
this._layers.get(5 /* Interaction */).addChildAt(this._replayDimGfx, 0);
|
|
9149
|
+
}
|
|
9150
|
+
const gfx = this._replayDimGfx;
|
|
9151
|
+
const { plotWidth, plotHeight } = this._transform;
|
|
9152
|
+
gfx.clear();
|
|
9153
|
+
if (x >= 0 && x < plotWidth) {
|
|
9154
|
+
gfx.rect(x, 0, plotWidth - x, plotHeight).fill({ color: 8421520, alpha: 0.35 });
|
|
9155
|
+
}
|
|
9156
|
+
this._layers.dirty.mark(5 /* Interaction */);
|
|
9157
|
+
}
|
|
9028
9158
|
_renderFrame() {
|
|
9029
9159
|
if (!this._ready || this._destroyed) return;
|
|
9030
9160
|
const t = this._transform;
|
|
@@ -9034,6 +9164,9 @@ var PixiChart = class {
|
|
|
9034
9164
|
if (dirty.isDirty(1 /* PriceSeries */)) {
|
|
9035
9165
|
this._autoFitPriceRange();
|
|
9036
9166
|
dirty.mark(0 /* Background */);
|
|
9167
|
+
this._candleRenderer.clear();
|
|
9168
|
+
this._lineRenderer.clear();
|
|
9169
|
+
this._histogramRenderer.clear();
|
|
9037
9170
|
for (const series of this._series) {
|
|
9038
9171
|
const opts = series.options();
|
|
9039
9172
|
if (opts.visible === false) continue;
|
|
@@ -11596,6 +11729,8 @@ var resolver = () => ChartRuntimeResolver.getInstance();
|
|
|
11596
11729
|
var canUseChartTypes = () => resolver().canUseChartTypes();
|
|
11597
11730
|
var canUseExtendedDrawings = () => resolver().canUseExtendedDrawings();
|
|
11598
11731
|
var canUseMultiPane = () => resolver().canUseMultiPane();
|
|
11732
|
+
var canUseBarReplay = () => resolver().canUseBarReplay();
|
|
11733
|
+
var canRenderOverlays = () => resolver().canRenderOverlays();
|
|
11599
11734
|
var canUseDraggableOrders = () => resolver().canUseDraggableOrders();
|
|
11600
11735
|
var canUseBracketOrders = () => resolver().canUseBracketOrders();
|
|
11601
11736
|
var canUsePositionsOverlay = () => resolver().canUsePositionsOverlay();
|
|
@@ -11912,8 +12047,8 @@ var ManagedTradingController = class {
|
|
|
11912
12047
|
}
|
|
11913
12048
|
};
|
|
11914
12049
|
|
|
11915
|
-
// src/api/
|
|
11916
|
-
var
|
|
12050
|
+
// src/api/ForgeCharts.ts
|
|
12051
|
+
var ForgeCharts = class _ForgeCharts {
|
|
11917
12052
|
_core;
|
|
11918
12053
|
_bus;
|
|
11919
12054
|
_indicators;
|
|
@@ -12304,7 +12439,7 @@ var TChart = class _TChart {
|
|
|
12304
12439
|
"[ForgeCharts] ForgeScript indicators are not enabled on the active license. Contact your license provider to enable the forgeScript feature."
|
|
12305
12440
|
);
|
|
12306
12441
|
}
|
|
12307
|
-
let finalConfig =
|
|
12442
|
+
let finalConfig = _ForgeCharts.OVERLAY_ONLY.has(config.type) && config.overlay !== true ? { ...config, overlay: true } : config;
|
|
12308
12443
|
if (finalConfig.overlay !== true && !canUseMultiPane()) {
|
|
12309
12444
|
console.warn("[ForgeCharts] multiPane is not enabled on the active license \u2014 indicator added as a price-pane overlay instead.");
|
|
12310
12445
|
finalConfig = { ...finalConfig, overlay: true };
|
|
@@ -12482,7 +12617,7 @@ var TChart = class _TChart {
|
|
|
12482
12617
|
*/
|
|
12483
12618
|
startDrawingTool(type) {
|
|
12484
12619
|
this._assertAlive();
|
|
12485
|
-
if (
|
|
12620
|
+
if (_ForgeCharts.EXTENDED_DRAWING_TOOLS.has(type) && !canUseExtendedDrawings()) {
|
|
12486
12621
|
console.warn(`[ForgeCharts] Drawing tool '${type}' requires the extendedDrawings feature, which is not enabled on the active license.`);
|
|
12487
12622
|
return;
|
|
12488
12623
|
}
|
|
@@ -12520,6 +12655,14 @@ var TChart = class _TChart {
|
|
|
12520
12655
|
/** Set contract switch roll markers for continuous futures. */
|
|
12521
12656
|
setContractSwitches(switches) {
|
|
12522
12657
|
this._assertAlive();
|
|
12658
|
+
if (!canRenderOverlays()) {
|
|
12659
|
+
if (switches.length > 0) {
|
|
12660
|
+
console.warn("[ForgeCharts] Overlay rendering (overlays) is not enabled on the active plan \u2014 contract switch markers suppressed.");
|
|
12661
|
+
}
|
|
12662
|
+
this._core.setContractSwitches([]);
|
|
12663
|
+
this._core.markDirty();
|
|
12664
|
+
return;
|
|
12665
|
+
}
|
|
12523
12666
|
this._core.setContractSwitches(switches);
|
|
12524
12667
|
this._core.markDirty();
|
|
12525
12668
|
}
|
|
@@ -12563,7 +12706,7 @@ var TChart = class _TChart {
|
|
|
12563
12706
|
const snap = this._drawings.all().map((d) => ({ ...d, points: [...d.points] }));
|
|
12564
12707
|
this._drawHistory.splice(this._drawHistIdx + 1);
|
|
12565
12708
|
this._drawHistory.push(snap);
|
|
12566
|
-
if (this._drawHistory.length >
|
|
12709
|
+
if (this._drawHistory.length > _ForgeCharts._HIST_MAX) this._drawHistory.shift();
|
|
12567
12710
|
this._drawHistIdx = this._drawHistory.length - 1;
|
|
12568
12711
|
}
|
|
12569
12712
|
/** Steps drawing state one edit back (Ctrl+Z). */
|
|
@@ -12928,6 +13071,158 @@ var TChart = class _TChart {
|
|
|
12928
13071
|
}
|
|
12929
13072
|
return this._unmanagedIngestion;
|
|
12930
13073
|
}
|
|
13074
|
+
// ─── Bar replay ───────────────────────────────────────────────────────────────
|
|
13075
|
+
//
|
|
13076
|
+
// Replay is a display cutoff on the primary series (Series.setCutoffTime)
|
|
13077
|
+
// plus this controller moving it. The full tape keeps flowing underneath —
|
|
13078
|
+
// live bars land in fullData() and stay hidden until stepped over or the
|
|
13079
|
+
// replay exits — so no datafeed, indicator, or renderer knows replay
|
|
13080
|
+
// exists. Selection (the hover shadow of what a click would remove) is a
|
|
13081
|
+
// backend surface only Pixi implements; ForgeCharts refuses replay elsewhere.
|
|
13082
|
+
/** Playback speeds, in bars per second. */
|
|
13083
|
+
static REPLAY_SPEEDS = [0.5, 1, 2, 5, 10];
|
|
13084
|
+
_replay = {
|
|
13085
|
+
selecting: false,
|
|
13086
|
+
active: false,
|
|
13087
|
+
playing: false,
|
|
13088
|
+
speedIdx: 1,
|
|
13089
|
+
timer: null
|
|
13090
|
+
};
|
|
13091
|
+
_emitReplayState() {
|
|
13092
|
+
this._bus.emit("replayStateChanged", {
|
|
13093
|
+
selecting: this._replay.selecting,
|
|
13094
|
+
active: this._replay.active,
|
|
13095
|
+
playing: this._replay.playing,
|
|
13096
|
+
speed: _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx],
|
|
13097
|
+
cutoffTime: this._series.cutoffTime()
|
|
13098
|
+
});
|
|
13099
|
+
}
|
|
13100
|
+
/** Current replay state — the control strip's render source. */
|
|
13101
|
+
replayState() {
|
|
13102
|
+
return {
|
|
13103
|
+
selecting: this._replay.selecting,
|
|
13104
|
+
active: this._replay.active,
|
|
13105
|
+
playing: this._replay.playing,
|
|
13106
|
+
speed: _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx],
|
|
13107
|
+
cutoffTime: this._series.cutoffTime()
|
|
13108
|
+
};
|
|
13109
|
+
}
|
|
13110
|
+
/**
|
|
13111
|
+
* Enter replay-anchor selection: the cursor shadows everything a click
|
|
13112
|
+
* would remove; clicking commits that bucket as the replay head. Returns
|
|
13113
|
+
* false (with a warning) when the plan lacks bar_replay or the backend
|
|
13114
|
+
* has no selection surface.
|
|
13115
|
+
*/
|
|
13116
|
+
replayEnterSelection() {
|
|
13117
|
+
this._assertAlive();
|
|
13118
|
+
if (!canUseBarReplay()) {
|
|
13119
|
+
console.warn("[ForgeCharts] Bar replay (bar_replay) is not enabled on the active plan.");
|
|
13120
|
+
return false;
|
|
13121
|
+
}
|
|
13122
|
+
if (!this._core.setReplaySelect) {
|
|
13123
|
+
console.warn("[ForgeCharts] Bar replay requires the Pixi render backend.");
|
|
13124
|
+
return false;
|
|
13125
|
+
}
|
|
13126
|
+
this._replayStopTimer();
|
|
13127
|
+
this._replay.selecting = true;
|
|
13128
|
+
this._replay.playing = false;
|
|
13129
|
+
this._core.setReplaySelect({
|
|
13130
|
+
onHover: () => {
|
|
13131
|
+
},
|
|
13132
|
+
onCommit: (time) => this._replayCommit(time)
|
|
13133
|
+
});
|
|
13134
|
+
this._emitReplayState();
|
|
13135
|
+
return true;
|
|
13136
|
+
}
|
|
13137
|
+
/** Leave selection without committing (existing replay, if any, survives). */
|
|
13138
|
+
replayCancelSelection() {
|
|
13139
|
+
this._assertAlive();
|
|
13140
|
+
if (!this._replay.selecting) return;
|
|
13141
|
+
this._replay.selecting = false;
|
|
13142
|
+
this._core.setReplaySelect?.(null);
|
|
13143
|
+
this._emitReplayState();
|
|
13144
|
+
}
|
|
13145
|
+
_replayCommit(time) {
|
|
13146
|
+
this._replay.selecting = false;
|
|
13147
|
+
this._core.setReplaySelect?.(null);
|
|
13148
|
+
this._series.setCutoffTime(time);
|
|
13149
|
+
this._replay.active = true;
|
|
13150
|
+
this._replay.playing = false;
|
|
13151
|
+
this._core.markDirty();
|
|
13152
|
+
this._emitReplayState();
|
|
13153
|
+
}
|
|
13154
|
+
replayPlay() {
|
|
13155
|
+
this._assertAlive();
|
|
13156
|
+
if (!this._replay.active || this._replay.playing) return;
|
|
13157
|
+
this._replay.playing = true;
|
|
13158
|
+
this._replayStartTimer();
|
|
13159
|
+
this._emitReplayState();
|
|
13160
|
+
}
|
|
13161
|
+
replayPause() {
|
|
13162
|
+
this._assertAlive();
|
|
13163
|
+
if (!this._replay.playing) return;
|
|
13164
|
+
this._replay.playing = false;
|
|
13165
|
+
this._replayStopTimer();
|
|
13166
|
+
this._emitReplayState();
|
|
13167
|
+
}
|
|
13168
|
+
/** Advance the replay head n bars. Pauses at the end of the tape. */
|
|
13169
|
+
replayStepForward(n = 1) {
|
|
13170
|
+
this._assertAlive();
|
|
13171
|
+
if (!this._replay.active) return;
|
|
13172
|
+
const full = this._series.fullData();
|
|
13173
|
+
const shown = this._series.data().length;
|
|
13174
|
+
const nextIdx = Math.min(shown + n, full.length) - 1;
|
|
13175
|
+
if (nextIdx < shown) {
|
|
13176
|
+
this.replayPause();
|
|
13177
|
+
return;
|
|
13178
|
+
}
|
|
13179
|
+
this._series.setCutoffTime(full[nextIdx].time);
|
|
13180
|
+
this._core.markDirty();
|
|
13181
|
+
if (nextIdx >= full.length - 1) this.replayPause();
|
|
13182
|
+
this._emitReplayState();
|
|
13183
|
+
}
|
|
13184
|
+
/** Rewind the replay head n bars back in time (default 10). */
|
|
13185
|
+
replayRewind(n = 10) {
|
|
13186
|
+
this._assertAlive();
|
|
13187
|
+
if (!this._replay.active) return;
|
|
13188
|
+
const full = this._series.fullData();
|
|
13189
|
+
const shown = this._series.data().length;
|
|
13190
|
+
const idx = Math.max(shown - 1 - n, 0);
|
|
13191
|
+
if (full.length === 0) return;
|
|
13192
|
+
this._series.setCutoffTime(full[idx].time);
|
|
13193
|
+
this._core.markDirty();
|
|
13194
|
+
this._emitReplayState();
|
|
13195
|
+
}
|
|
13196
|
+
/** Cycle to the next playback speed; restarts the timer when playing. */
|
|
13197
|
+
replayCycleSpeed() {
|
|
13198
|
+
this._assertAlive();
|
|
13199
|
+
this._replay.speedIdx = (this._replay.speedIdx + 1) % _ForgeCharts.REPLAY_SPEEDS.length;
|
|
13200
|
+
if (this._replay.playing) this._replayStartTimer();
|
|
13201
|
+
this._emitReplayState();
|
|
13202
|
+
return _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
|
|
13203
|
+
}
|
|
13204
|
+
/** Exit replay entirely: the full tape (live head included) returns. */
|
|
13205
|
+
replayExit() {
|
|
13206
|
+
this._assertAlive();
|
|
13207
|
+
this._replayStopTimer();
|
|
13208
|
+
this._replay.selecting = false;
|
|
13209
|
+
this._replay.active = false;
|
|
13210
|
+
this._replay.playing = false;
|
|
13211
|
+
this._core.setReplaySelect?.(null);
|
|
13212
|
+
this._series.setCutoffTime(null);
|
|
13213
|
+
this._core.markDirty();
|
|
13214
|
+
this._core.scrollToEnd();
|
|
13215
|
+
this._emitReplayState();
|
|
13216
|
+
}
|
|
13217
|
+
_replayStartTimer() {
|
|
13218
|
+
this._replayStopTimer();
|
|
13219
|
+
const speed = _ForgeCharts.REPLAY_SPEEDS[this._replay.speedIdx];
|
|
13220
|
+
this._replay.timer = setInterval(() => this.replayStepForward(1), Math.max(1e3 / speed, 16));
|
|
13221
|
+
}
|
|
13222
|
+
_replayStopTimer() {
|
|
13223
|
+
if (this._replay.timer) clearInterval(this._replay.timer);
|
|
13224
|
+
this._replay.timer = null;
|
|
13225
|
+
}
|
|
12931
13226
|
// ─── Datafeed ─────────────────────────────────────────────────────────────────
|
|
12932
13227
|
_buildConnector(datafeed) {
|
|
12933
13228
|
return new DatafeedConnector(datafeed, this._series, {
|
|
@@ -18369,7 +18664,7 @@ var DEMONSTRATION_STROKE = "rgba(255, 200, 50, 0.7)";
|
|
|
18369
18664
|
var DEMONSTRATION_COLOR = "var(--crosshair-overlay, rgba(255,255,255,0.75))";
|
|
18370
18665
|
|
|
18371
18666
|
// src/version.ts
|
|
18372
|
-
var FORGECHARTS_VERSION = "1.5.
|
|
18667
|
+
var FORGECHARTS_VERSION = "1.5.83" ;
|
|
18373
18668
|
function applyRuntimeConfig(caps, config) {
|
|
18374
18669
|
if (!config) return caps;
|
|
18375
18670
|
const orderEntry = config.enableOrderEntry === false ? false : caps.orderEntry;
|
|
@@ -18471,10 +18766,7 @@ function useHostLicense(apiUrl, getAuthToken) {
|
|
|
18471
18766
|
function useUserPackage(apiUrl, getAuthToken) {
|
|
18472
18767
|
useEffect(() => {
|
|
18473
18768
|
const resolver3 = ChartRuntimeResolver.getInstance();
|
|
18474
|
-
if (!apiUrl || !getAuthToken)
|
|
18475
|
-
resolver3.setUserPackageFeatures(null);
|
|
18476
|
-
return;
|
|
18477
|
-
}
|
|
18769
|
+
if (!apiUrl || !getAuthToken) return;
|
|
18478
18770
|
let cancelled = false;
|
|
18479
18771
|
void (async () => {
|
|
18480
18772
|
try {
|
|
@@ -23333,6 +23625,14 @@ var ChartCanvas = forwardRef(
|
|
|
23333
23625
|
},
|
|
23334
23626
|
addIndicator: (config) => chartRef.current?.addIndicator(config) ?? null,
|
|
23335
23627
|
setSeriesType: (type) => chartRef.current?.setSeriesType(type),
|
|
23628
|
+
toggleReplay: () => {
|
|
23629
|
+
const chart = chartRef.current;
|
|
23630
|
+
if (!chart) return;
|
|
23631
|
+
const st = chart.replayState();
|
|
23632
|
+
if (st.selecting) chart.replayCancelSelection();
|
|
23633
|
+
else if (st.active) chart.replayExit();
|
|
23634
|
+
else chart.replayEnterSelection();
|
|
23635
|
+
},
|
|
23336
23636
|
removeIndicator: (id) => chartRef.current?.removeIndicator(id),
|
|
23337
23637
|
moveIndicatorToOverlay: (id) => chartRef.current?.moveIndicatorToOverlay(id),
|
|
23338
23638
|
moveIndicatorToPane: (id, targetPaneId) => chartRef.current?.moveIndicatorToPane(id, targetPaneId),
|
|
@@ -23380,7 +23680,7 @@ var ChartCanvas = forwardRef(
|
|
|
23380
23680
|
useEffect(() => {
|
|
23381
23681
|
const container = priceRef.current;
|
|
23382
23682
|
if (!container) return;
|
|
23383
|
-
const chart = new
|
|
23683
|
+
const chart = new ForgeCharts({
|
|
23384
23684
|
container,
|
|
23385
23685
|
symbol,
|
|
23386
23686
|
interval: timeframe,
|
|
@@ -23394,6 +23694,9 @@ var ChartCanvas = forwardRef(
|
|
|
23394
23694
|
for (const config of initialIndicators) {
|
|
23395
23695
|
chart.addIndicator(config);
|
|
23396
23696
|
}
|
|
23697
|
+
chart.on("replayStateChanged", (s) => {
|
|
23698
|
+
setReplayState(s.selecting || s.active ? s : null);
|
|
23699
|
+
});
|
|
23397
23700
|
transformRef.current = chart.getTransform();
|
|
23398
23701
|
const syncChartState = () => {
|
|
23399
23702
|
setBars([...chart.getBars()]);
|
|
@@ -24353,6 +24656,7 @@ var ChartCanvas = forwardRef(
|
|
|
24353
24656
|
selectPointerToolRef.current = handleToolSelect;
|
|
24354
24657
|
const initialPointerAppliedRef = useRef(false);
|
|
24355
24658
|
const initialSeriesAppliedRef = useRef(false);
|
|
24659
|
+
const [replayState, setReplayState] = useState(null);
|
|
24356
24660
|
useEffect(() => {
|
|
24357
24661
|
if (!initialSeriesAppliedRef.current && initialSeriesType && chartRef.current) {
|
|
24358
24662
|
initialSeriesAppliedRef.current = true;
|
|
@@ -24361,7 +24665,7 @@ var ChartCanvas = forwardRef(
|
|
|
24361
24665
|
if (initialPointerAppliedRef.current || !initialPointerTool) return;
|
|
24362
24666
|
initialPointerAppliedRef.current = true;
|
|
24363
24667
|
selectPointerToolRef.current(initialPointerTool);
|
|
24364
|
-
}, [initialPointerTool]);
|
|
24668
|
+
}, [initialPointerTool, initialSeriesType]);
|
|
24365
24669
|
const handleMagnetModeChange = useCallback((mode) => {
|
|
24366
24670
|
setMagnetMode(mode);
|
|
24367
24671
|
chartRef.current?.setMagnetMode(mode);
|
|
@@ -24457,6 +24761,107 @@ var ChartCanvas = forwardRef(
|
|
|
24457
24761
|
crosshairXRef.current = null;
|
|
24458
24762
|
},
|
|
24459
24763
|
children: [
|
|
24764
|
+
replayState && (() => {
|
|
24765
|
+
const dark = effectiveTheme !== "light";
|
|
24766
|
+
const bg = dark ? "rgba(24,26,34,0.94)" : "rgba(255,255,255,0.96)";
|
|
24767
|
+
const fg = dark ? "#dfe3ee" : "#20242f";
|
|
24768
|
+
const line = dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
|
|
24769
|
+
const btn = {
|
|
24770
|
+
background: "transparent",
|
|
24771
|
+
border: "none",
|
|
24772
|
+
color: fg,
|
|
24773
|
+
cursor: "pointer",
|
|
24774
|
+
padding: "5px 9px",
|
|
24775
|
+
borderRadius: 6,
|
|
24776
|
+
fontSize: 12,
|
|
24777
|
+
display: "inline-flex",
|
|
24778
|
+
alignItems: "center",
|
|
24779
|
+
gap: 5,
|
|
24780
|
+
lineHeight: 1
|
|
24781
|
+
};
|
|
24782
|
+
const call = (fn) => () => {
|
|
24783
|
+
if (chartRef.current) fn(chartRef.current);
|
|
24784
|
+
};
|
|
24785
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
24786
|
+
position: "absolute",
|
|
24787
|
+
bottom: 46,
|
|
24788
|
+
left: "50%",
|
|
24789
|
+
transform: "translateX(-50%)",
|
|
24790
|
+
zIndex: 8,
|
|
24791
|
+
display: "flex",
|
|
24792
|
+
alignItems: "center",
|
|
24793
|
+
gap: 2,
|
|
24794
|
+
background: bg,
|
|
24795
|
+
color: fg,
|
|
24796
|
+
border: `1px solid ${line}`,
|
|
24797
|
+
borderRadius: 8,
|
|
24798
|
+
padding: "2px 6px",
|
|
24799
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
|
|
24800
|
+
userSelect: "none"
|
|
24801
|
+
}, children: [
|
|
24802
|
+
replayState.selecting ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, padding: "5px 9px" }, children: "Click a bar to start replay from there" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
24803
|
+
/* @__PURE__ */ jsx(
|
|
24804
|
+
"button",
|
|
24805
|
+
{
|
|
24806
|
+
style: btn,
|
|
24807
|
+
title: "Pick a new starting bar",
|
|
24808
|
+
onClick: call((c) => c.replayEnterSelection()),
|
|
24809
|
+
children: "\u2506\u2190 Select bar"
|
|
24810
|
+
}
|
|
24811
|
+
),
|
|
24812
|
+
/* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
|
|
24813
|
+
/* @__PURE__ */ jsx(
|
|
24814
|
+
"button",
|
|
24815
|
+
{
|
|
24816
|
+
style: btn,
|
|
24817
|
+
title: "Rewind 10 bars",
|
|
24818
|
+
onClick: call((c) => c.replayRewind(10)),
|
|
24819
|
+
children: "\u23EA"
|
|
24820
|
+
}
|
|
24821
|
+
),
|
|
24822
|
+
/* @__PURE__ */ jsx(
|
|
24823
|
+
"button",
|
|
24824
|
+
{
|
|
24825
|
+
style: btn,
|
|
24826
|
+
title: replayState.playing ? "Pause" : "Play",
|
|
24827
|
+
onClick: call((c) => replayState.playing ? c.replayPause() : c.replayPlay()),
|
|
24828
|
+
children: replayState.playing ? "\u23F8" : "\u25B6"
|
|
24829
|
+
}
|
|
24830
|
+
),
|
|
24831
|
+
/* @__PURE__ */ jsx(
|
|
24832
|
+
"button",
|
|
24833
|
+
{
|
|
24834
|
+
style: btn,
|
|
24835
|
+
title: "Forward one bar",
|
|
24836
|
+
onClick: call((c) => c.replayStepForward(1)),
|
|
24837
|
+
children: "\u23E9"
|
|
24838
|
+
}
|
|
24839
|
+
),
|
|
24840
|
+
/* @__PURE__ */ jsxs(
|
|
24841
|
+
"button",
|
|
24842
|
+
{
|
|
24843
|
+
style: { ...btn, fontVariantNumeric: "tabular-nums" },
|
|
24844
|
+
title: "Playback speed",
|
|
24845
|
+
onClick: call((c) => c.replayCycleSpeed()),
|
|
24846
|
+
children: [
|
|
24847
|
+
replayState.speed,
|
|
24848
|
+
"x"
|
|
24849
|
+
]
|
|
24850
|
+
}
|
|
24851
|
+
)
|
|
24852
|
+
] }),
|
|
24853
|
+
/* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
|
|
24854
|
+
/* @__PURE__ */ jsx(
|
|
24855
|
+
"button",
|
|
24856
|
+
{
|
|
24857
|
+
style: btn,
|
|
24858
|
+
title: replayState.selecting ? "Cancel selection" : "Exit replay",
|
|
24859
|
+
onClick: call((c) => replayState.selecting ? c.replayCancelSelection() : c.replayExit()),
|
|
24860
|
+
children: "\u2715"
|
|
24861
|
+
}
|
|
24862
|
+
)
|
|
24863
|
+
] });
|
|
24864
|
+
})(),
|
|
24460
24865
|
/* @__PURE__ */ jsx(
|
|
24461
24866
|
"div",
|
|
24462
24867
|
{
|
|
@@ -24544,7 +24949,7 @@ var ChartCanvas = forwardRef(
|
|
|
24544
24949
|
symbol,
|
|
24545
24950
|
timeframe,
|
|
24546
24951
|
bars,
|
|
24547
|
-
theme,
|
|
24952
|
+
theme: effectiveTheme,
|
|
24548
24953
|
indicators,
|
|
24549
24954
|
overlayColors: OVERLAY_COLORS,
|
|
24550
24955
|
onRemove: handleRemoveIndicator,
|
|
@@ -24986,6 +25391,7 @@ function LayoutMenu({
|
|
|
24986
25391
|
currentName,
|
|
24987
25392
|
currentLayoutId,
|
|
24988
25393
|
autoSave,
|
|
25394
|
+
saveState,
|
|
24989
25395
|
onFetchLayouts,
|
|
24990
25396
|
onSave,
|
|
24991
25397
|
onLoad,
|
|
@@ -25127,7 +25533,9 @@ function LayoutMenu({
|
|
|
25127
25533
|
] }),
|
|
25128
25534
|
currentName ?? "Layout",
|
|
25129
25535
|
" \u25BE",
|
|
25130
|
-
|
|
25536
|
+
saveState === "saving" && /* @__PURE__ */ jsx("span", { style: { fontSize: 10.5, opacity: 0.75, fontStyle: "italic" }, children: "Saving\u2026" }),
|
|
25537
|
+
saveState === "saved" && /* @__PURE__ */ jsx("span", { style: { fontSize: 10.5, color: "var(--up)" }, children: "Saved \u2713" }),
|
|
25538
|
+
saveState !== "saving" && saveState !== "saved" && autoSave && isSaved && /* @__PURE__ */ jsx("span", { className: "layout-autosave-dot", title: "Auto-save enabled" })
|
|
25131
25539
|
]
|
|
25132
25540
|
}
|
|
25133
25541
|
),
|
|
@@ -26500,28 +26908,32 @@ function RollRuleDropdown({
|
|
|
26500
26908
|
))
|
|
26501
26909
|
] });
|
|
26502
26910
|
}
|
|
26503
|
-
var
|
|
26504
|
-
{
|
|
26505
|
-
/* @__PURE__ */
|
|
26506
|
-
|
|
26507
|
-
|
|
26508
|
-
|
|
26509
|
-
|
|
26510
|
-
|
|
26511
|
-
|
|
26512
|
-
|
|
26513
|
-
|
|
26514
|
-
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26521
|
-
/* @__PURE__ */ jsx("
|
|
26522
|
-
/* @__PURE__ */
|
|
26523
|
-
|
|
26911
|
+
var SERIES_STYLE_GROUPS = [
|
|
26912
|
+
{ label: "Bars", items: [
|
|
26913
|
+
{ id: "bar", label: "Bars", icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", stroke: "currentColor", strokeWidth: "1.4", fill: "none", children: [
|
|
26914
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "2", x2: "5", y2: "13" }),
|
|
26915
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "5", x2: "5", y2: "5" }),
|
|
26916
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "10", x2: "7", y2: "10" }),
|
|
26917
|
+
/* @__PURE__ */ jsx("line", { x1: "11", y1: "4", x2: "11", y2: "14" }),
|
|
26918
|
+
/* @__PURE__ */ jsx("line", { x1: "9", y1: "7", x2: "11", y2: "7" }),
|
|
26919
|
+
/* @__PURE__ */ jsx("line", { x1: "11", y1: "12", x2: "13", y2: "12" })
|
|
26920
|
+
] }) },
|
|
26921
|
+
{ id: "candlestick", label: "Candles", icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "currentColor", children: [
|
|
26922
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "3", height: "6", rx: "0.5" }),
|
|
26923
|
+
/* @__PURE__ */ jsx("line", { x1: "4.5", y1: "2", x2: "4.5", y2: "14", stroke: "currentColor", strokeWidth: "1" }),
|
|
26924
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "3", width: "3", height: "7", rx: "0.5" }),
|
|
26925
|
+
/* @__PURE__ */ jsx("line", { x1: "11.5", y1: "1", x2: "11.5", y2: "13", stroke: "currentColor", strokeWidth: "1" })
|
|
26926
|
+
] }) }
|
|
26927
|
+
] },
|
|
26928
|
+
{ label: "Lines", items: [
|
|
26929
|
+
{ id: "line", label: "Line", icon: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", children: /* @__PURE__ */ jsx("polyline", { points: "2,12 6,7 10,10 14,4" }) }) },
|
|
26930
|
+
{ id: "area", label: "Area", icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", children: [
|
|
26931
|
+
/* @__PURE__ */ jsx("polyline", { points: "2,12 6,7 10,10 14,4", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
26932
|
+
/* @__PURE__ */ jsx("polygon", { points: "2,12 6,7 10,10 14,4 14,14 2,14", fill: "currentColor", opacity: "0.35" })
|
|
26933
|
+
] }) }
|
|
26934
|
+
] }
|
|
26524
26935
|
];
|
|
26936
|
+
var ALL_SERIES_STYLES = SERIES_STYLE_GROUPS.flatMap((g) => g.items);
|
|
26525
26937
|
function TopToolbar({
|
|
26526
26938
|
symbol,
|
|
26527
26939
|
timeframe,
|
|
@@ -26535,6 +26947,9 @@ function TopToolbar({
|
|
|
26535
26947
|
onFavoritesChange,
|
|
26536
26948
|
seriesType,
|
|
26537
26949
|
onSeriesTypeChange,
|
|
26950
|
+
onToggleReplay,
|
|
26951
|
+
onToggleAlerts,
|
|
26952
|
+
alertsOpen,
|
|
26538
26953
|
onAddIndicator,
|
|
26539
26954
|
maxIndicators,
|
|
26540
26955
|
indicatorsOpen,
|
|
@@ -26572,7 +26987,8 @@ function TopToolbar({
|
|
|
26572
26987
|
showSavedLayouts,
|
|
26573
26988
|
showCustomTimeframes,
|
|
26574
26989
|
showDataExport,
|
|
26575
|
-
showMultiChartLayout
|
|
26990
|
+
showMultiChartLayout,
|
|
26991
|
+
layoutSaveState
|
|
26576
26992
|
}) {
|
|
26577
26993
|
const [tfOpen, setTfOpen] = useState(false);
|
|
26578
26994
|
const [styleOpen, setStyleOpen] = useState(false);
|
|
@@ -26730,27 +27146,60 @@ function TopToolbar({
|
|
|
26730
27146
|
onClick: () => setStyleOpen((o) => !o),
|
|
26731
27147
|
title: "Chart style",
|
|
26732
27148
|
children: [
|
|
26733
|
-
(
|
|
26734
|
-
(
|
|
27149
|
+
(ALL_SERIES_STYLES.find((s) => s.id === seriesType) ?? ALL_SERIES_STYLES[1]).icon,
|
|
27150
|
+
(ALL_SERIES_STYLES.find((s) => s.id === seriesType) ?? ALL_SERIES_STYLES[1]).label
|
|
26735
27151
|
]
|
|
26736
27152
|
}
|
|
26737
27153
|
),
|
|
26738
|
-
styleOpen && /* @__PURE__ */ jsx("div", { className: "tf-dropdown", style: { minWidth:
|
|
26739
|
-
"
|
|
26740
|
-
|
|
26741
|
-
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
27154
|
+
styleOpen && /* @__PURE__ */ jsx("div", { className: "tf-dropdown", style: { minWidth: 165 }, children: SERIES_STYLE_GROUPS.map((group) => /* @__PURE__ */ jsxs("div", { children: [
|
|
27155
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
27156
|
+
padding: "6px 12px 3px",
|
|
27157
|
+
fontSize: 10.5,
|
|
27158
|
+
fontWeight: 600,
|
|
27159
|
+
letterSpacing: 1,
|
|
27160
|
+
textTransform: "uppercase",
|
|
27161
|
+
opacity: 0.55
|
|
27162
|
+
}, children: group.label }),
|
|
27163
|
+
group.items.map((style) => /* @__PURE__ */ jsxs(
|
|
27164
|
+
"button",
|
|
27165
|
+
{
|
|
27166
|
+
className: "tf-dropdown-row",
|
|
27167
|
+
style: style.id === seriesType ? { fontWeight: 600 } : void 0,
|
|
27168
|
+
onClick: () => {
|
|
27169
|
+
onSeriesTypeChange(style.id);
|
|
27170
|
+
setStyleOpen(false);
|
|
27171
|
+
},
|
|
27172
|
+
children: [
|
|
27173
|
+
/* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 8 }, children: [
|
|
27174
|
+
style.icon,
|
|
27175
|
+
style.label
|
|
27176
|
+
] }),
|
|
27177
|
+
style.id === seriesType && /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: { opacity: 0.9 }, children: "\u2713" })
|
|
27178
|
+
]
|
|
26746
27179
|
},
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
|
|
26750
|
-
|
|
26751
|
-
|
|
26752
|
-
|
|
26753
|
-
|
|
27180
|
+
style.id
|
|
27181
|
+
))
|
|
27182
|
+
] }, group.label)) })
|
|
27183
|
+
] })
|
|
27184
|
+
] }),
|
|
27185
|
+
onToggleAlerts !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27186
|
+
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
27187
|
+
/* @__PURE__ */ jsxs("button", { className: `ind-trigger${alertsOpen ? " active" : ""}`, onClick: onToggleAlerts, title: "Price alerts", children: [
|
|
27188
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
27189
|
+
/* @__PURE__ */ jsx("path", { d: "M8 2.5a4 4 0 0 0-4 4v2.5l-1.4 2.3a.5.5 0 0 0 .43.75h9.94a.5.5 0 0 0 .43-.75L12 9V6.5a4 4 0 0 0-4-4z" }),
|
|
27190
|
+
/* @__PURE__ */ jsx("path", { d: "M6.6 12.5a1.5 1.5 0 0 0 2.8 0" })
|
|
27191
|
+
] }),
|
|
27192
|
+
"Alerts"
|
|
27193
|
+
] })
|
|
27194
|
+
] }),
|
|
27195
|
+
onToggleReplay !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27196
|
+
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
27197
|
+
/* @__PURE__ */ jsxs("button", { className: "ind-trigger", onClick: onToggleReplay, title: "Bar replay", children: [
|
|
27198
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
27199
|
+
/* @__PURE__ */ jsx("polygon", { points: "6,4 12,8 6,12", fill: "currentColor", stroke: "none" }),
|
|
27200
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "3", x2: "3", y2: "13" })
|
|
27201
|
+
] }),
|
|
27202
|
+
"Replay"
|
|
26754
27203
|
] })
|
|
26755
27204
|
] }),
|
|
26756
27205
|
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
@@ -26819,6 +27268,7 @@ function TopToolbar({
|
|
|
26819
27268
|
currentName: currentLayoutName,
|
|
26820
27269
|
currentLayoutId,
|
|
26821
27270
|
autoSave,
|
|
27271
|
+
...layoutSaveState !== void 0 ? { saveState: layoutSaveState } : {},
|
|
26822
27272
|
onFetchLayouts,
|
|
26823
27273
|
onSave: onSaveLayout,
|
|
26824
27274
|
onLoad: onLoadLayout,
|
|
@@ -31571,6 +32021,194 @@ function WatchlistDrawer({ onClose, onSelectSymbol, symbolResolver, apiUrl, getA
|
|
|
31571
32021
|
}
|
|
31572
32022
|
);
|
|
31573
32023
|
}
|
|
32024
|
+
function AlertsDrawer({ apiUrl, getAuthToken, symbol, onClose }) {
|
|
32025
|
+
const [alerts, setAlerts] = useState(null);
|
|
32026
|
+
const [error, setError] = useState("");
|
|
32027
|
+
const [busy, setBusy] = useState(false);
|
|
32028
|
+
const [formSymbol, setFormSymbol] = useState(symbol);
|
|
32029
|
+
const [condition, setCondition] = useState("above");
|
|
32030
|
+
const [priceStr, setPriceStr] = useState("");
|
|
32031
|
+
useEffect(() => {
|
|
32032
|
+
setFormSymbol(symbol);
|
|
32033
|
+
}, [symbol]);
|
|
32034
|
+
const call = useCallback(async (path, init) => {
|
|
32035
|
+
const token = getAuthToken ? await getAuthToken() : "";
|
|
32036
|
+
return fetch(`${apiUrl.replace(/\/$/, "")}/api/alerts${path}`, {
|
|
32037
|
+
...init,
|
|
32038
|
+
headers: {
|
|
32039
|
+
"Content-Type": "application/json",
|
|
32040
|
+
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
32041
|
+
...init?.headers ?? {}
|
|
32042
|
+
}
|
|
32043
|
+
});
|
|
32044
|
+
}, [apiUrl, getAuthToken]);
|
|
32045
|
+
const load = useCallback(() => {
|
|
32046
|
+
call("").then(async (r) => {
|
|
32047
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
32048
|
+
setAlerts(await r.json());
|
|
32049
|
+
}).catch((e) => setError(`Failed to load alerts: ${e instanceof Error ? e.message : e}`));
|
|
32050
|
+
}, [call]);
|
|
32051
|
+
useEffect(() => {
|
|
32052
|
+
load();
|
|
32053
|
+
const t = setInterval(load, 15e3);
|
|
32054
|
+
return () => clearInterval(t);
|
|
32055
|
+
}, [load]);
|
|
32056
|
+
async function create() {
|
|
32057
|
+
const price = parseFloat(priceStr);
|
|
32058
|
+
if (!(price > 0) || !formSymbol.trim()) return;
|
|
32059
|
+
setBusy(true);
|
|
32060
|
+
setError("");
|
|
32061
|
+
try {
|
|
32062
|
+
const r = await call("", {
|
|
32063
|
+
method: "POST",
|
|
32064
|
+
body: JSON.stringify({ symbol: formSymbol.trim(), condition, price })
|
|
32065
|
+
});
|
|
32066
|
+
const d = await r.json().catch(() => ({}));
|
|
32067
|
+
if (!r.ok) throw new Error(d.error ?? `HTTP ${r.status}`);
|
|
32068
|
+
setPriceStr("");
|
|
32069
|
+
load();
|
|
32070
|
+
} catch (e) {
|
|
32071
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
32072
|
+
} finally {
|
|
32073
|
+
setBusy(false);
|
|
32074
|
+
}
|
|
32075
|
+
}
|
|
32076
|
+
async function remove(id) {
|
|
32077
|
+
try {
|
|
32078
|
+
await call(`/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
32079
|
+
setAlerts((prev) => prev?.filter((a) => a.id !== id) ?? null);
|
|
32080
|
+
} catch {
|
|
32081
|
+
}
|
|
32082
|
+
}
|
|
32083
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
32084
|
+
width: 280,
|
|
32085
|
+
flexShrink: 0,
|
|
32086
|
+
display: "flex",
|
|
32087
|
+
flexDirection: "column",
|
|
32088
|
+
background: "var(--surface)",
|
|
32089
|
+
border: "1px solid var(--border)",
|
|
32090
|
+
borderRadius: 8,
|
|
32091
|
+
overflow: "hidden",
|
|
32092
|
+
color: "var(--text)"
|
|
32093
|
+
}, children: [
|
|
32094
|
+
/* @__PURE__ */ jsxs("div", { style: {
|
|
32095
|
+
display: "flex",
|
|
32096
|
+
alignItems: "center",
|
|
32097
|
+
justifyContent: "space-between",
|
|
32098
|
+
padding: "10px 14px",
|
|
32099
|
+
borderBottom: "1px solid var(--border)"
|
|
32100
|
+
}, children: [
|
|
32101
|
+
/* @__PURE__ */ jsx("strong", { style: { fontSize: 13 }, children: "Alerts" }),
|
|
32102
|
+
/* @__PURE__ */ jsx(
|
|
32103
|
+
"button",
|
|
32104
|
+
{
|
|
32105
|
+
onClick: onClose,
|
|
32106
|
+
title: "Close",
|
|
32107
|
+
style: { background: "none", border: "none", color: "var(--text-muted)", cursor: "pointer", fontSize: 14 },
|
|
32108
|
+
children: "\u2715"
|
|
32109
|
+
}
|
|
32110
|
+
)
|
|
32111
|
+
] }),
|
|
32112
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "10px 14px", borderBottom: "1px solid var(--border)", display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
32113
|
+
/* @__PURE__ */ jsx(
|
|
32114
|
+
"input",
|
|
32115
|
+
{
|
|
32116
|
+
value: formSymbol,
|
|
32117
|
+
onChange: (e) => setFormSymbol(e.target.value),
|
|
32118
|
+
placeholder: "Symbol",
|
|
32119
|
+
style: { fontSize: 12.5, padding: "5px 8px", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6 }
|
|
32120
|
+
}
|
|
32121
|
+
),
|
|
32122
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
32123
|
+
/* @__PURE__ */ jsxs(
|
|
32124
|
+
"select",
|
|
32125
|
+
{
|
|
32126
|
+
value: condition,
|
|
32127
|
+
onChange: (e) => setCondition(e.target.value),
|
|
32128
|
+
style: { fontSize: 12.5, padding: "5px 6px", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6 },
|
|
32129
|
+
children: [
|
|
32130
|
+
/* @__PURE__ */ jsx("option", { value: "above", children: "Crosses above" }),
|
|
32131
|
+
/* @__PURE__ */ jsx("option", { value: "below", children: "Crosses below" })
|
|
32132
|
+
]
|
|
32133
|
+
}
|
|
32134
|
+
),
|
|
32135
|
+
/* @__PURE__ */ jsx(
|
|
32136
|
+
"input",
|
|
32137
|
+
{
|
|
32138
|
+
value: priceStr,
|
|
32139
|
+
onChange: (e) => setPriceStr(e.target.value.replace(/[^0-9.]/g, "")),
|
|
32140
|
+
placeholder: "Price",
|
|
32141
|
+
inputMode: "decimal",
|
|
32142
|
+
style: { flex: 1, minWidth: 0, fontSize: 12.5, padding: "5px 8px", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6, fontVariantNumeric: "tabular-nums" },
|
|
32143
|
+
onKeyDown: (e) => e.key === "Enter" && !busy && create()
|
|
32144
|
+
}
|
|
32145
|
+
)
|
|
32146
|
+
] }),
|
|
32147
|
+
/* @__PURE__ */ jsx(
|
|
32148
|
+
"button",
|
|
32149
|
+
{
|
|
32150
|
+
onClick: create,
|
|
32151
|
+
disabled: busy || !(parseFloat(priceStr) > 0),
|
|
32152
|
+
style: {
|
|
32153
|
+
fontSize: 12.5,
|
|
32154
|
+
padding: "6px 0",
|
|
32155
|
+
borderRadius: 6,
|
|
32156
|
+
border: "none",
|
|
32157
|
+
cursor: "pointer",
|
|
32158
|
+
background: "var(--primary, #5b5bd6)",
|
|
32159
|
+
color: "#fff",
|
|
32160
|
+
opacity: busy || !(parseFloat(priceStr) > 0) ? 0.55 : 1
|
|
32161
|
+
},
|
|
32162
|
+
children: busy ? "Creating\u2026" : "Create alert"
|
|
32163
|
+
}
|
|
32164
|
+
),
|
|
32165
|
+
error && /* @__PURE__ */ jsx("div", { style: { fontSize: 11.5, color: "var(--down, #e5484d)" }, children: error })
|
|
32166
|
+
] }),
|
|
32167
|
+
/* @__PURE__ */ jsxs("div", { style: { flex: 1, overflowY: "auto" }, children: [
|
|
32168
|
+
alerts === null && !error && /* @__PURE__ */ jsx("div", { style: { padding: 14, fontSize: 12.5, color: "var(--text-muted)" }, children: "Loading\u2026" }),
|
|
32169
|
+
alerts !== null && alerts.length === 0 && /* @__PURE__ */ jsx("div", { style: { padding: 14, fontSize: 12.5, color: "var(--text-muted)" }, children: "No alerts yet. They fire server-side and email you \u2014 the chart doesn't need to stay open." }),
|
|
32170
|
+
alerts?.map((a) => /* @__PURE__ */ jsxs("div", { style: {
|
|
32171
|
+
display: "flex",
|
|
32172
|
+
alignItems: "center",
|
|
32173
|
+
gap: 8,
|
|
32174
|
+
padding: "8px 14px",
|
|
32175
|
+
borderBottom: "1px solid var(--border)",
|
|
32176
|
+
fontSize: 12.5,
|
|
32177
|
+
opacity: a.status === "triggered" ? 0.75 : 1
|
|
32178
|
+
}, children: [
|
|
32179
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
32180
|
+
width: 7,
|
|
32181
|
+
height: 7,
|
|
32182
|
+
borderRadius: "50%",
|
|
32183
|
+
flexShrink: 0,
|
|
32184
|
+
background: a.status === "active" ? "var(--up, #22a06b)" : "var(--text-muted)"
|
|
32185
|
+
}, title: a.status }),
|
|
32186
|
+
/* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
32187
|
+
/* @__PURE__ */ jsxs("div", { style: { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: [
|
|
32188
|
+
/* @__PURE__ */ jsx("strong", { children: a.symbol }),
|
|
32189
|
+
" ",
|
|
32190
|
+
a.condition === "above" ? "\u2265" : "\u2264",
|
|
32191
|
+
" ",
|
|
32192
|
+
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: a.price })
|
|
32193
|
+
] }),
|
|
32194
|
+
a.status === "triggered" && /* @__PURE__ */ jsxs("div", { style: { fontSize: 11, color: "var(--text-muted)" }, children: [
|
|
32195
|
+
"fired at ",
|
|
32196
|
+
a.trigger_price
|
|
32197
|
+
] })
|
|
32198
|
+
] }),
|
|
32199
|
+
/* @__PURE__ */ jsx(
|
|
32200
|
+
"button",
|
|
32201
|
+
{
|
|
32202
|
+
onClick: () => remove(a.id),
|
|
32203
|
+
title: "Remove alert",
|
|
32204
|
+
style: { background: "none", border: "none", color: "var(--text-muted)", cursor: "pointer", fontSize: 13 },
|
|
32205
|
+
children: "\u2715"
|
|
32206
|
+
}
|
|
32207
|
+
)
|
|
32208
|
+
] }, a.id))
|
|
32209
|
+
] })
|
|
32210
|
+
] });
|
|
32211
|
+
}
|
|
31574
32212
|
function ChartWorkspace({
|
|
31575
32213
|
// TabBar
|
|
31576
32214
|
tabs,
|
|
@@ -31621,6 +32259,9 @@ function ChartWorkspace({
|
|
|
31621
32259
|
seriesType,
|
|
31622
32260
|
onSeriesTypeChange,
|
|
31623
32261
|
showSeriesTypes,
|
|
32262
|
+
onToggleReplay,
|
|
32263
|
+
showReplay,
|
|
32264
|
+
layoutSaveState,
|
|
31624
32265
|
extraTfGroups,
|
|
31625
32266
|
// RightToolbar
|
|
31626
32267
|
watchlistOpen,
|
|
@@ -31731,6 +32372,9 @@ function ChartWorkspace({
|
|
|
31731
32372
|
const effShowMultiChartLayout = gate(showMultiChartLayout, capabilities.multiChartLayout);
|
|
31732
32373
|
const effShowMultipleTabs = gate(showMultipleTabs, capabilities.multipleWorkspaces);
|
|
31733
32374
|
const effShowSeriesTypes = gate(showSeriesTypes, capabilities.chartTypes) && onSeriesTypeChange !== void 0;
|
|
32375
|
+
const effShowReplay = gate(showReplay, capabilities.barReplay) && onToggleReplay !== void 0;
|
|
32376
|
+
const effShowAlerts = gate(void 0, capabilities.alerts) && hostApiUrl !== void 0;
|
|
32377
|
+
const [alertsOpen, setAlertsOpen] = React11.useState(false);
|
|
31734
32378
|
const tabItems = tabs.map((t) => ({
|
|
31735
32379
|
id: t.id,
|
|
31736
32380
|
label: t.label,
|
|
@@ -31809,6 +32453,8 @@ function ChartWorkspace({
|
|
|
31809
32453
|
onSymbolChange,
|
|
31810
32454
|
onTimeframeChange,
|
|
31811
32455
|
...effShowSeriesTypes ? { seriesType: seriesType ?? "candlestick", onSeriesTypeChange } : {},
|
|
32456
|
+
...effShowReplay ? { onToggleReplay } : {},
|
|
32457
|
+
...effShowAlerts ? { onToggleAlerts: () => setAlertsOpen((o) => !o), alertsOpen } : {},
|
|
31812
32458
|
onAddCustomTimeframe,
|
|
31813
32459
|
onRemoveCustomTimeframe,
|
|
31814
32460
|
onFavoritesChange: onFavoriteTfsChange,
|
|
@@ -31824,6 +32470,7 @@ function ChartWorkspace({
|
|
|
31824
32470
|
currentLayoutName,
|
|
31825
32471
|
currentLayoutId,
|
|
31826
32472
|
autoSave,
|
|
32473
|
+
...layoutSaveState !== void 0 ? { layoutSaveState } : {},
|
|
31827
32474
|
onFetchLayouts,
|
|
31828
32475
|
onSaveLayout,
|
|
31829
32476
|
onLoadLayout,
|
|
@@ -31855,6 +32502,15 @@ function ChartWorkspace({
|
|
|
31855
32502
|
/* @__PURE__ */ jsx("div", { style: { flex: 1, display: "flex", gap: 4, minHeight: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsxs("div", { style: { position: "relative", flex: 1, display: "flex", gap: 4, minWidth: 0, minHeight: 0 }, children: [
|
|
31856
32503
|
(dataBlocked || isLicensed === false) && /* @__PURE__ */ jsx(LicenseRequiredOverlay, {}),
|
|
31857
32504
|
/* @__PURE__ */ jsx("div", { style: { position: "relative", flex: 1, minWidth: 0, minHeight: 0 }, children: chartSlots }),
|
|
32505
|
+
effShowAlerts && alertsOpen && /* @__PURE__ */ jsx(
|
|
32506
|
+
AlertsDrawer,
|
|
32507
|
+
{
|
|
32508
|
+
apiUrl: hostApiUrl,
|
|
32509
|
+
getAuthToken,
|
|
32510
|
+
symbol: activeSymbol ?? "",
|
|
32511
|
+
onClose: () => setAlertsOpen(false)
|
|
32512
|
+
}
|
|
32513
|
+
),
|
|
31858
32514
|
autoTrading && !onToggleOrderEntry && effOrderEntryOpen && capabilities.orderEntry && tradingBridge && /* @__PURE__ */ jsx(
|
|
31859
32515
|
OrderTicket,
|
|
31860
32516
|
{
|
|
@@ -35258,22 +35914,6 @@ var ManagedAppShell = forwardRef(
|
|
|
35258
35914
|
const maxBars = data?.package?.maxHistoricalBars ?? null;
|
|
35259
35915
|
const df = datafeed;
|
|
35260
35916
|
df.setMaxHistoricalBars?.(maxBars);
|
|
35261
|
-
if (data?.features && typeof data.features === "object") {
|
|
35262
|
-
const lm = LicenseManager.getInstance();
|
|
35263
|
-
const current = lm.getFeatures();
|
|
35264
|
-
const merged = { ...current };
|
|
35265
|
-
for (const [key, val] of Object.entries(data.features)) {
|
|
35266
|
-
if (val === true) {
|
|
35267
|
-
merged[key] = true;
|
|
35268
|
-
}
|
|
35269
|
-
}
|
|
35270
|
-
const payload = lm.getLicense();
|
|
35271
|
-
if (payload) {
|
|
35272
|
-
lm.loadLicense({ ...payload, features: merged });
|
|
35273
|
-
} else {
|
|
35274
|
-
lm.loadLicense({ licenseKey: "", mode: "unmanaged", features: merged });
|
|
35275
|
-
}
|
|
35276
|
-
}
|
|
35277
35917
|
}
|
|
35278
35918
|
}
|
|
35279
35919
|
if (plansRes.ok) {
|
|
@@ -36623,13 +37263,14 @@ var ManagedAppShell = forwardRef(
|
|
|
36623
37263
|
}
|
|
36624
37264
|
),
|
|
36625
37265
|
scriptDrawerOpen && capabilities.forgeScript && (renderScriptDrawer ? renderScriptDrawer({ onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator }) : /* @__PURE__ */ jsx(ScriptDrawer, { onClose: () => setScriptDrawerOpen(false), onAddIndicator: handleAddIndicator, ...apiUrl !== void 0 ? { apiUrl } : {}, ...getAuthToken ? { getAuthToken } : {} })),
|
|
36626
|
-
(orderPanelOpen || connectedSession?.disconnected) && connectedSession && capabilities.renderManagedTradingControls && /* @__PURE__ */ jsx(
|
|
37266
|
+
(orderPanelOpen || connectedSession?.disconnected) && connectedSession && capabilities.renderManagedTradingControls && capabilities.orderEntry && /* @__PURE__ */ jsx(
|
|
36627
37267
|
OrderEntryPanel,
|
|
36628
37268
|
{
|
|
36629
37269
|
sessionId: connectedSession.sessionId,
|
|
36630
37270
|
brokerSlug: connectedSession.brokerSlug,
|
|
36631
37271
|
accounts: connectedSession.accounts,
|
|
36632
37272
|
selectedAccountId,
|
|
37273
|
+
showTrailingStop: capabilities.trailingStops,
|
|
36633
37274
|
defaultSymbol: activeTab.symbol,
|
|
36634
37275
|
apiUrl,
|
|
36635
37276
|
getAuthToken,
|
|
@@ -36817,7 +37458,7 @@ var ManagedAppShell = forwardRef(
|
|
|
36817
37458
|
) : null;
|
|
36818
37459
|
const drawers = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
36819
37460
|
watchlistOpen && capabilities.watchlists && (renderWatchlistDrawer ? renderWatchlistDrawer({ onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange }) : /* @__PURE__ */ jsx(WatchlistDrawer, { onClose: () => setWatchlistOpen(false), onSelectSymbol: handleSymbolChange, symbolResolver })),
|
|
36820
|
-
tradeDrawerOpen && capabilities.renderManagedTradingControls && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(
|
|
37461
|
+
tradeDrawerOpen && capabilities.renderManagedTradingControls && capabilities.orderEntry && (renderTradeDrawer ? renderTradeDrawer({ onClose: () => setTradeDrawerOpen(false) }) : /* @__PURE__ */ jsx(
|
|
36821
37462
|
TradeDrawer,
|
|
36822
37463
|
{
|
|
36823
37464
|
onClose: () => setTradeDrawerOpen(false),
|
|
@@ -36981,6 +37622,8 @@ var ManagedAppShell = forwardRef(
|
|
|
36981
37622
|
onTimeframeChange: handleTimeframeChange,
|
|
36982
37623
|
seriesType,
|
|
36983
37624
|
onSeriesTypeChange: handleSeriesTypeChange,
|
|
37625
|
+
onToggleReplay: () => chartHandleRef.current?.toggleReplay(),
|
|
37626
|
+
...apiUrl !== void 0 ? { hostApiUrl: apiUrl } : {},
|
|
36984
37627
|
onAddCustomTimeframe: handleAddCustomTimeframe,
|
|
36985
37628
|
onRemoveCustomTimeframe: handleRemoveCustomTimeframe,
|
|
36986
37629
|
onFavoriteTfsChange: setFavoriteTfs,
|
|
@@ -37004,7 +37647,7 @@ var ManagedAppShell = forwardRef(
|
|
|
37004
37647
|
onDeleteLayout: handleDeleteLayout,
|
|
37005
37648
|
onOpenLayoutInNewTab: handleOpenLayoutInNewTab,
|
|
37006
37649
|
symbolResolver,
|
|
37007
|
-
showTradeButton: capabilities.renderManagedTradingControls,
|
|
37650
|
+
showTradeButton: capabilities.renderManagedTradingControls && capabilities.orderEntry,
|
|
37008
37651
|
tradeDrawerOpen: tradeDrawerOpen || orderPanelOpen,
|
|
37009
37652
|
onToggleTradeDrawer: () => {
|
|
37010
37653
|
if (orderPanelOpen && !tradeDrawerOpen) {
|
|
@@ -37019,7 +37662,7 @@ var ManagedAppShell = forwardRef(
|
|
|
37019
37662
|
onToggleWatchlist: () => setWatchlistOpen((o) => !o),
|
|
37020
37663
|
orderEntryOpen: orderPanelOpen,
|
|
37021
37664
|
onToggleOrderEntry: handleToggleOrderEntry,
|
|
37022
|
-
showOrderEntry: capabilities.renderManagedTradingControls,
|
|
37665
|
+
showOrderEntry: capabilities.renderManagedTradingControls && capabilities.orderEntry,
|
|
37023
37666
|
showAgent: agentEnabled,
|
|
37024
37667
|
agentOpen: agentPanelOpen,
|
|
37025
37668
|
onToggleAgent: () => setAgentPanelOpen((o) => !o),
|