@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/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React11, { forwardRef, useRef, useState, useEffect, useImperativeHandle, useCallback, createContext, 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, {
|
|
@@ -18146,7 +18441,7 @@ var DEMONSTRATION_STROKE = "rgba(255, 200, 50, 0.7)";
|
|
|
18146
18441
|
var DEMONSTRATION_COLOR = "var(--crosshair-overlay, rgba(255,255,255,0.75))";
|
|
18147
18442
|
|
|
18148
18443
|
// src/version.ts
|
|
18149
|
-
var FORGECHARTS_VERSION = "1.5.
|
|
18444
|
+
var FORGECHARTS_VERSION = "1.5.83" ;
|
|
18150
18445
|
function applyRuntimeConfig(caps, config) {
|
|
18151
18446
|
if (!config) return caps;
|
|
18152
18447
|
const orderEntry = config.enableOrderEntry === false ? false : caps.orderEntry;
|
|
@@ -18248,10 +18543,7 @@ function useHostLicense(apiUrl, getAuthToken) {
|
|
|
18248
18543
|
function useUserPackage(apiUrl, getAuthToken) {
|
|
18249
18544
|
useEffect(() => {
|
|
18250
18545
|
const resolver3 = ChartRuntimeResolver.getInstance();
|
|
18251
|
-
if (!apiUrl || !getAuthToken)
|
|
18252
|
-
resolver3.setUserPackageFeatures(null);
|
|
18253
|
-
return;
|
|
18254
|
-
}
|
|
18546
|
+
if (!apiUrl || !getAuthToken) return;
|
|
18255
18547
|
let cancelled = false;
|
|
18256
18548
|
void (async () => {
|
|
18257
18549
|
try {
|
|
@@ -23110,6 +23402,14 @@ var ChartCanvas = forwardRef(
|
|
|
23110
23402
|
},
|
|
23111
23403
|
addIndicator: (config) => chartRef.current?.addIndicator(config) ?? null,
|
|
23112
23404
|
setSeriesType: (type) => chartRef.current?.setSeriesType(type),
|
|
23405
|
+
toggleReplay: () => {
|
|
23406
|
+
const chart = chartRef.current;
|
|
23407
|
+
if (!chart) return;
|
|
23408
|
+
const st = chart.replayState();
|
|
23409
|
+
if (st.selecting) chart.replayCancelSelection();
|
|
23410
|
+
else if (st.active) chart.replayExit();
|
|
23411
|
+
else chart.replayEnterSelection();
|
|
23412
|
+
},
|
|
23113
23413
|
removeIndicator: (id) => chartRef.current?.removeIndicator(id),
|
|
23114
23414
|
moveIndicatorToOverlay: (id) => chartRef.current?.moveIndicatorToOverlay(id),
|
|
23115
23415
|
moveIndicatorToPane: (id, targetPaneId) => chartRef.current?.moveIndicatorToPane(id, targetPaneId),
|
|
@@ -23157,7 +23457,7 @@ var ChartCanvas = forwardRef(
|
|
|
23157
23457
|
useEffect(() => {
|
|
23158
23458
|
const container = priceRef.current;
|
|
23159
23459
|
if (!container) return;
|
|
23160
|
-
const chart = new
|
|
23460
|
+
const chart = new ForgeCharts({
|
|
23161
23461
|
container,
|
|
23162
23462
|
symbol,
|
|
23163
23463
|
interval: timeframe,
|
|
@@ -23171,6 +23471,9 @@ var ChartCanvas = forwardRef(
|
|
|
23171
23471
|
for (const config of initialIndicators) {
|
|
23172
23472
|
chart.addIndicator(config);
|
|
23173
23473
|
}
|
|
23474
|
+
chart.on("replayStateChanged", (s) => {
|
|
23475
|
+
setReplayState(s.selecting || s.active ? s : null);
|
|
23476
|
+
});
|
|
23174
23477
|
transformRef.current = chart.getTransform();
|
|
23175
23478
|
const syncChartState = () => {
|
|
23176
23479
|
setBars([...chart.getBars()]);
|
|
@@ -24130,6 +24433,7 @@ var ChartCanvas = forwardRef(
|
|
|
24130
24433
|
selectPointerToolRef.current = handleToolSelect;
|
|
24131
24434
|
const initialPointerAppliedRef = useRef(false);
|
|
24132
24435
|
const initialSeriesAppliedRef = useRef(false);
|
|
24436
|
+
const [replayState, setReplayState] = useState(null);
|
|
24133
24437
|
useEffect(() => {
|
|
24134
24438
|
if (!initialSeriesAppliedRef.current && initialSeriesType && chartRef.current) {
|
|
24135
24439
|
initialSeriesAppliedRef.current = true;
|
|
@@ -24138,7 +24442,7 @@ var ChartCanvas = forwardRef(
|
|
|
24138
24442
|
if (initialPointerAppliedRef.current || !initialPointerTool) return;
|
|
24139
24443
|
initialPointerAppliedRef.current = true;
|
|
24140
24444
|
selectPointerToolRef.current(initialPointerTool);
|
|
24141
|
-
}, [initialPointerTool]);
|
|
24445
|
+
}, [initialPointerTool, initialSeriesType]);
|
|
24142
24446
|
const handleMagnetModeChange = useCallback((mode) => {
|
|
24143
24447
|
setMagnetMode(mode);
|
|
24144
24448
|
chartRef.current?.setMagnetMode(mode);
|
|
@@ -24234,6 +24538,107 @@ var ChartCanvas = forwardRef(
|
|
|
24234
24538
|
crosshairXRef.current = null;
|
|
24235
24539
|
},
|
|
24236
24540
|
children: [
|
|
24541
|
+
replayState && (() => {
|
|
24542
|
+
const dark = effectiveTheme !== "light";
|
|
24543
|
+
const bg = dark ? "rgba(24,26,34,0.94)" : "rgba(255,255,255,0.96)";
|
|
24544
|
+
const fg = dark ? "#dfe3ee" : "#20242f";
|
|
24545
|
+
const line = dark ? "rgba(255,255,255,0.12)" : "rgba(0,0,0,0.12)";
|
|
24546
|
+
const btn = {
|
|
24547
|
+
background: "transparent",
|
|
24548
|
+
border: "none",
|
|
24549
|
+
color: fg,
|
|
24550
|
+
cursor: "pointer",
|
|
24551
|
+
padding: "5px 9px",
|
|
24552
|
+
borderRadius: 6,
|
|
24553
|
+
fontSize: 12,
|
|
24554
|
+
display: "inline-flex",
|
|
24555
|
+
alignItems: "center",
|
|
24556
|
+
gap: 5,
|
|
24557
|
+
lineHeight: 1
|
|
24558
|
+
};
|
|
24559
|
+
const call = (fn) => () => {
|
|
24560
|
+
if (chartRef.current) fn(chartRef.current);
|
|
24561
|
+
};
|
|
24562
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
24563
|
+
position: "absolute",
|
|
24564
|
+
bottom: 46,
|
|
24565
|
+
left: "50%",
|
|
24566
|
+
transform: "translateX(-50%)",
|
|
24567
|
+
zIndex: 8,
|
|
24568
|
+
display: "flex",
|
|
24569
|
+
alignItems: "center",
|
|
24570
|
+
gap: 2,
|
|
24571
|
+
background: bg,
|
|
24572
|
+
color: fg,
|
|
24573
|
+
border: `1px solid ${line}`,
|
|
24574
|
+
borderRadius: 8,
|
|
24575
|
+
padding: "2px 6px",
|
|
24576
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
|
|
24577
|
+
userSelect: "none"
|
|
24578
|
+
}, children: [
|
|
24579
|
+
replayState.selecting ? /* @__PURE__ */ jsx("span", { style: { fontSize: 12, padding: "5px 9px" }, children: "Click a bar to start replay from there" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
24580
|
+
/* @__PURE__ */ jsx(
|
|
24581
|
+
"button",
|
|
24582
|
+
{
|
|
24583
|
+
style: btn,
|
|
24584
|
+
title: "Pick a new starting bar",
|
|
24585
|
+
onClick: call((c) => c.replayEnterSelection()),
|
|
24586
|
+
children: "\u2506\u2190 Select bar"
|
|
24587
|
+
}
|
|
24588
|
+
),
|
|
24589
|
+
/* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
|
|
24590
|
+
/* @__PURE__ */ jsx(
|
|
24591
|
+
"button",
|
|
24592
|
+
{
|
|
24593
|
+
style: btn,
|
|
24594
|
+
title: "Rewind 10 bars",
|
|
24595
|
+
onClick: call((c) => c.replayRewind(10)),
|
|
24596
|
+
children: "\u23EA"
|
|
24597
|
+
}
|
|
24598
|
+
),
|
|
24599
|
+
/* @__PURE__ */ jsx(
|
|
24600
|
+
"button",
|
|
24601
|
+
{
|
|
24602
|
+
style: btn,
|
|
24603
|
+
title: replayState.playing ? "Pause" : "Play",
|
|
24604
|
+
onClick: call((c) => replayState.playing ? c.replayPause() : c.replayPlay()),
|
|
24605
|
+
children: replayState.playing ? "\u23F8" : "\u25B6"
|
|
24606
|
+
}
|
|
24607
|
+
),
|
|
24608
|
+
/* @__PURE__ */ jsx(
|
|
24609
|
+
"button",
|
|
24610
|
+
{
|
|
24611
|
+
style: btn,
|
|
24612
|
+
title: "Forward one bar",
|
|
24613
|
+
onClick: call((c) => c.replayStepForward(1)),
|
|
24614
|
+
children: "\u23E9"
|
|
24615
|
+
}
|
|
24616
|
+
),
|
|
24617
|
+
/* @__PURE__ */ jsxs(
|
|
24618
|
+
"button",
|
|
24619
|
+
{
|
|
24620
|
+
style: { ...btn, fontVariantNumeric: "tabular-nums" },
|
|
24621
|
+
title: "Playback speed",
|
|
24622
|
+
onClick: call((c) => c.replayCycleSpeed()),
|
|
24623
|
+
children: [
|
|
24624
|
+
replayState.speed,
|
|
24625
|
+
"x"
|
|
24626
|
+
]
|
|
24627
|
+
}
|
|
24628
|
+
)
|
|
24629
|
+
] }),
|
|
24630
|
+
/* @__PURE__ */ jsx("span", { style: { width: 1, alignSelf: "stretch", background: line, margin: "4px 2px" } }),
|
|
24631
|
+
/* @__PURE__ */ jsx(
|
|
24632
|
+
"button",
|
|
24633
|
+
{
|
|
24634
|
+
style: btn,
|
|
24635
|
+
title: replayState.selecting ? "Cancel selection" : "Exit replay",
|
|
24636
|
+
onClick: call((c) => replayState.selecting ? c.replayCancelSelection() : c.replayExit()),
|
|
24637
|
+
children: "\u2715"
|
|
24638
|
+
}
|
|
24639
|
+
)
|
|
24640
|
+
] });
|
|
24641
|
+
})(),
|
|
24237
24642
|
/* @__PURE__ */ jsx(
|
|
24238
24643
|
"div",
|
|
24239
24644
|
{
|
|
@@ -24321,7 +24726,7 @@ var ChartCanvas = forwardRef(
|
|
|
24321
24726
|
symbol,
|
|
24322
24727
|
timeframe,
|
|
24323
24728
|
bars,
|
|
24324
|
-
theme,
|
|
24729
|
+
theme: effectiveTheme,
|
|
24325
24730
|
indicators,
|
|
24326
24731
|
overlayColors: OVERLAY_COLORS,
|
|
24327
24732
|
onRemove: handleRemoveIndicator,
|
|
@@ -24762,6 +25167,7 @@ function LayoutMenu({
|
|
|
24762
25167
|
currentName,
|
|
24763
25168
|
currentLayoutId,
|
|
24764
25169
|
autoSave,
|
|
25170
|
+
saveState,
|
|
24765
25171
|
onFetchLayouts,
|
|
24766
25172
|
onSave,
|
|
24767
25173
|
onLoad,
|
|
@@ -24903,7 +25309,9 @@ function LayoutMenu({
|
|
|
24903
25309
|
] }),
|
|
24904
25310
|
currentName ?? "Layout",
|
|
24905
25311
|
" \u25BE",
|
|
24906
|
-
|
|
25312
|
+
saveState === "saving" && /* @__PURE__ */ jsx("span", { style: { fontSize: 10.5, opacity: 0.75, fontStyle: "italic" }, children: "Saving\u2026" }),
|
|
25313
|
+
saveState === "saved" && /* @__PURE__ */ jsx("span", { style: { fontSize: 10.5, color: "var(--up)" }, children: "Saved \u2713" }),
|
|
25314
|
+
saveState !== "saving" && saveState !== "saved" && autoSave && isSaved && /* @__PURE__ */ jsx("span", { className: "layout-autosave-dot", title: "Auto-save enabled" })
|
|
24907
25315
|
]
|
|
24908
25316
|
}
|
|
24909
25317
|
),
|
|
@@ -26275,28 +26683,32 @@ function RollRuleDropdown({
|
|
|
26275
26683
|
))
|
|
26276
26684
|
] });
|
|
26277
26685
|
}
|
|
26278
|
-
var
|
|
26279
|
-
{
|
|
26280
|
-
/* @__PURE__ */
|
|
26281
|
-
|
|
26282
|
-
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26288
|
-
|
|
26289
|
-
|
|
26290
|
-
|
|
26291
|
-
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
/* @__PURE__ */ jsx("
|
|
26297
|
-
/* @__PURE__ */
|
|
26298
|
-
|
|
26686
|
+
var SERIES_STYLE_GROUPS = [
|
|
26687
|
+
{ label: "Bars", items: [
|
|
26688
|
+
{ 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: [
|
|
26689
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "2", x2: "5", y2: "13" }),
|
|
26690
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "5", x2: "5", y2: "5" }),
|
|
26691
|
+
/* @__PURE__ */ jsx("line", { x1: "5", y1: "10", x2: "7", y2: "10" }),
|
|
26692
|
+
/* @__PURE__ */ jsx("line", { x1: "11", y1: "4", x2: "11", y2: "14" }),
|
|
26693
|
+
/* @__PURE__ */ jsx("line", { x1: "9", y1: "7", x2: "11", y2: "7" }),
|
|
26694
|
+
/* @__PURE__ */ jsx("line", { x1: "11", y1: "12", x2: "13", y2: "12" })
|
|
26695
|
+
] }) },
|
|
26696
|
+
{ id: "candlestick", label: "Candles", icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "currentColor", children: [
|
|
26697
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "3", height: "6", rx: "0.5" }),
|
|
26698
|
+
/* @__PURE__ */ jsx("line", { x1: "4.5", y1: "2", x2: "4.5", y2: "14", stroke: "currentColor", strokeWidth: "1" }),
|
|
26699
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "3", width: "3", height: "7", rx: "0.5" }),
|
|
26700
|
+
/* @__PURE__ */ jsx("line", { x1: "11.5", y1: "1", x2: "11.5", y2: "13", stroke: "currentColor", strokeWidth: "1" })
|
|
26701
|
+
] }) }
|
|
26702
|
+
] },
|
|
26703
|
+
{ label: "Lines", items: [
|
|
26704
|
+
{ 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" }) }) },
|
|
26705
|
+
{ id: "area", label: "Area", icon: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", children: [
|
|
26706
|
+
/* @__PURE__ */ jsx("polyline", { points: "2,12 6,7 10,10 14,4", fill: "none", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
26707
|
+
/* @__PURE__ */ jsx("polygon", { points: "2,12 6,7 10,10 14,4 14,14 2,14", fill: "currentColor", opacity: "0.35" })
|
|
26708
|
+
] }) }
|
|
26709
|
+
] }
|
|
26299
26710
|
];
|
|
26711
|
+
var ALL_SERIES_STYLES = SERIES_STYLE_GROUPS.flatMap((g) => g.items);
|
|
26300
26712
|
function TopToolbar({
|
|
26301
26713
|
symbol,
|
|
26302
26714
|
timeframe,
|
|
@@ -26310,6 +26722,9 @@ function TopToolbar({
|
|
|
26310
26722
|
onFavoritesChange,
|
|
26311
26723
|
seriesType,
|
|
26312
26724
|
onSeriesTypeChange,
|
|
26725
|
+
onToggleReplay,
|
|
26726
|
+
onToggleAlerts,
|
|
26727
|
+
alertsOpen,
|
|
26313
26728
|
onAddIndicator,
|
|
26314
26729
|
maxIndicators,
|
|
26315
26730
|
indicatorsOpen,
|
|
@@ -26347,7 +26762,8 @@ function TopToolbar({
|
|
|
26347
26762
|
showSavedLayouts,
|
|
26348
26763
|
showCustomTimeframes,
|
|
26349
26764
|
showDataExport,
|
|
26350
|
-
showMultiChartLayout
|
|
26765
|
+
showMultiChartLayout,
|
|
26766
|
+
layoutSaveState
|
|
26351
26767
|
}) {
|
|
26352
26768
|
const [tfOpen, setTfOpen] = useState(false);
|
|
26353
26769
|
const [styleOpen, setStyleOpen] = useState(false);
|
|
@@ -26505,27 +26921,60 @@ function TopToolbar({
|
|
|
26505
26921
|
onClick: () => setStyleOpen((o) => !o),
|
|
26506
26922
|
title: "Chart style",
|
|
26507
26923
|
children: [
|
|
26508
|
-
(
|
|
26509
|
-
(
|
|
26924
|
+
(ALL_SERIES_STYLES.find((s) => s.id === seriesType) ?? ALL_SERIES_STYLES[1]).icon,
|
|
26925
|
+
(ALL_SERIES_STYLES.find((s) => s.id === seriesType) ?? ALL_SERIES_STYLES[1]).label
|
|
26510
26926
|
]
|
|
26511
26927
|
}
|
|
26512
26928
|
),
|
|
26513
|
-
styleOpen && /* @__PURE__ */ jsx("div", { className: "tf-dropdown", style: { minWidth:
|
|
26514
|
-
"
|
|
26515
|
-
|
|
26516
|
-
|
|
26517
|
-
|
|
26518
|
-
|
|
26519
|
-
|
|
26520
|
-
|
|
26929
|
+
styleOpen && /* @__PURE__ */ jsx("div", { className: "tf-dropdown", style: { minWidth: 165 }, children: SERIES_STYLE_GROUPS.map((group) => /* @__PURE__ */ jsxs("div", { children: [
|
|
26930
|
+
/* @__PURE__ */ jsx("div", { style: {
|
|
26931
|
+
padding: "6px 12px 3px",
|
|
26932
|
+
fontSize: 10.5,
|
|
26933
|
+
fontWeight: 600,
|
|
26934
|
+
letterSpacing: 1,
|
|
26935
|
+
textTransform: "uppercase",
|
|
26936
|
+
opacity: 0.55
|
|
26937
|
+
}, children: group.label }),
|
|
26938
|
+
group.items.map((style) => /* @__PURE__ */ jsxs(
|
|
26939
|
+
"button",
|
|
26940
|
+
{
|
|
26941
|
+
className: "tf-dropdown-row",
|
|
26942
|
+
style: style.id === seriesType ? { fontWeight: 600 } : void 0,
|
|
26943
|
+
onClick: () => {
|
|
26944
|
+
onSeriesTypeChange(style.id);
|
|
26945
|
+
setStyleOpen(false);
|
|
26946
|
+
},
|
|
26947
|
+
children: [
|
|
26948
|
+
/* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 8 }, children: [
|
|
26949
|
+
style.icon,
|
|
26950
|
+
style.label
|
|
26951
|
+
] }),
|
|
26952
|
+
style.id === seriesType && /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: { opacity: 0.9 }, children: "\u2713" })
|
|
26953
|
+
]
|
|
26521
26954
|
},
|
|
26522
|
-
|
|
26523
|
-
|
|
26524
|
-
|
|
26525
|
-
|
|
26526
|
-
|
|
26527
|
-
|
|
26528
|
-
|
|
26955
|
+
style.id
|
|
26956
|
+
))
|
|
26957
|
+
] }, group.label)) })
|
|
26958
|
+
] })
|
|
26959
|
+
] }),
|
|
26960
|
+
onToggleAlerts !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
26961
|
+
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
26962
|
+
/* @__PURE__ */ jsxs("button", { className: `ind-trigger${alertsOpen ? " active" : ""}`, onClick: onToggleAlerts, title: "Price alerts", children: [
|
|
26963
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
26964
|
+
/* @__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" }),
|
|
26965
|
+
/* @__PURE__ */ jsx("path", { d: "M6.6 12.5a1.5 1.5 0 0 0 2.8 0" })
|
|
26966
|
+
] }),
|
|
26967
|
+
"Alerts"
|
|
26968
|
+
] })
|
|
26969
|
+
] }),
|
|
26970
|
+
onToggleReplay !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
26971
|
+
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
26972
|
+
/* @__PURE__ */ jsxs("button", { className: "ind-trigger", onClick: onToggleReplay, title: "Bar replay", children: [
|
|
26973
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
26974
|
+
/* @__PURE__ */ jsx("polygon", { points: "6,4 12,8 6,12", fill: "currentColor", stroke: "none" }),
|
|
26975
|
+
/* @__PURE__ */ jsx("line", { x1: "3", y1: "3", x2: "3", y2: "13" })
|
|
26976
|
+
] }),
|
|
26977
|
+
"Replay"
|
|
26529
26978
|
] })
|
|
26530
26979
|
] }),
|
|
26531
26980
|
/* @__PURE__ */ jsx("div", { className: "toolbar-sep" }),
|
|
@@ -26594,6 +27043,7 @@ function TopToolbar({
|
|
|
26594
27043
|
currentName: currentLayoutName,
|
|
26595
27044
|
currentLayoutId,
|
|
26596
27045
|
autoSave,
|
|
27046
|
+
...layoutSaveState !== void 0 ? { saveState: layoutSaveState } : {},
|
|
26597
27047
|
onFetchLayouts,
|
|
26598
27048
|
onSave: onSaveLayout,
|
|
26599
27049
|
onLoad: onLoadLayout,
|
|
@@ -31346,6 +31796,194 @@ function WatchlistDrawer({ onClose, onSelectSymbol, symbolResolver, apiUrl, getA
|
|
|
31346
31796
|
}
|
|
31347
31797
|
);
|
|
31348
31798
|
}
|
|
31799
|
+
function AlertsDrawer({ apiUrl, getAuthToken, symbol, onClose }) {
|
|
31800
|
+
const [alerts, setAlerts] = useState(null);
|
|
31801
|
+
const [error, setError] = useState("");
|
|
31802
|
+
const [busy, setBusy] = useState(false);
|
|
31803
|
+
const [formSymbol, setFormSymbol] = useState(symbol);
|
|
31804
|
+
const [condition, setCondition] = useState("above");
|
|
31805
|
+
const [priceStr, setPriceStr] = useState("");
|
|
31806
|
+
useEffect(() => {
|
|
31807
|
+
setFormSymbol(symbol);
|
|
31808
|
+
}, [symbol]);
|
|
31809
|
+
const call = useCallback(async (path, init) => {
|
|
31810
|
+
const token = getAuthToken ? await getAuthToken() : "";
|
|
31811
|
+
return fetch(`${apiUrl.replace(/\/$/, "")}/api/alerts${path}`, {
|
|
31812
|
+
...init,
|
|
31813
|
+
headers: {
|
|
31814
|
+
"Content-Type": "application/json",
|
|
31815
|
+
...token ? { Authorization: `Bearer ${token}` } : {},
|
|
31816
|
+
...init?.headers ?? {}
|
|
31817
|
+
}
|
|
31818
|
+
});
|
|
31819
|
+
}, [apiUrl, getAuthToken]);
|
|
31820
|
+
const load = useCallback(() => {
|
|
31821
|
+
call("").then(async (r) => {
|
|
31822
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
31823
|
+
setAlerts(await r.json());
|
|
31824
|
+
}).catch((e) => setError(`Failed to load alerts: ${e instanceof Error ? e.message : e}`));
|
|
31825
|
+
}, [call]);
|
|
31826
|
+
useEffect(() => {
|
|
31827
|
+
load();
|
|
31828
|
+
const t = setInterval(load, 15e3);
|
|
31829
|
+
return () => clearInterval(t);
|
|
31830
|
+
}, [load]);
|
|
31831
|
+
async function create() {
|
|
31832
|
+
const price = parseFloat(priceStr);
|
|
31833
|
+
if (!(price > 0) || !formSymbol.trim()) return;
|
|
31834
|
+
setBusy(true);
|
|
31835
|
+
setError("");
|
|
31836
|
+
try {
|
|
31837
|
+
const r = await call("", {
|
|
31838
|
+
method: "POST",
|
|
31839
|
+
body: JSON.stringify({ symbol: formSymbol.trim(), condition, price })
|
|
31840
|
+
});
|
|
31841
|
+
const d = await r.json().catch(() => ({}));
|
|
31842
|
+
if (!r.ok) throw new Error(d.error ?? `HTTP ${r.status}`);
|
|
31843
|
+
setPriceStr("");
|
|
31844
|
+
load();
|
|
31845
|
+
} catch (e) {
|
|
31846
|
+
setError(e instanceof Error ? e.message : String(e));
|
|
31847
|
+
} finally {
|
|
31848
|
+
setBusy(false);
|
|
31849
|
+
}
|
|
31850
|
+
}
|
|
31851
|
+
async function remove(id) {
|
|
31852
|
+
try {
|
|
31853
|
+
await call(`/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
31854
|
+
setAlerts((prev) => prev?.filter((a) => a.id !== id) ?? null);
|
|
31855
|
+
} catch {
|
|
31856
|
+
}
|
|
31857
|
+
}
|
|
31858
|
+
return /* @__PURE__ */ jsxs("div", { style: {
|
|
31859
|
+
width: 280,
|
|
31860
|
+
flexShrink: 0,
|
|
31861
|
+
display: "flex",
|
|
31862
|
+
flexDirection: "column",
|
|
31863
|
+
background: "var(--surface)",
|
|
31864
|
+
border: "1px solid var(--border)",
|
|
31865
|
+
borderRadius: 8,
|
|
31866
|
+
overflow: "hidden",
|
|
31867
|
+
color: "var(--text)"
|
|
31868
|
+
}, children: [
|
|
31869
|
+
/* @__PURE__ */ jsxs("div", { style: {
|
|
31870
|
+
display: "flex",
|
|
31871
|
+
alignItems: "center",
|
|
31872
|
+
justifyContent: "space-between",
|
|
31873
|
+
padding: "10px 14px",
|
|
31874
|
+
borderBottom: "1px solid var(--border)"
|
|
31875
|
+
}, children: [
|
|
31876
|
+
/* @__PURE__ */ jsx("strong", { style: { fontSize: 13 }, children: "Alerts" }),
|
|
31877
|
+
/* @__PURE__ */ jsx(
|
|
31878
|
+
"button",
|
|
31879
|
+
{
|
|
31880
|
+
onClick: onClose,
|
|
31881
|
+
title: "Close",
|
|
31882
|
+
style: { background: "none", border: "none", color: "var(--text-muted)", cursor: "pointer", fontSize: 14 },
|
|
31883
|
+
children: "\u2715"
|
|
31884
|
+
}
|
|
31885
|
+
)
|
|
31886
|
+
] }),
|
|
31887
|
+
/* @__PURE__ */ jsxs("div", { style: { padding: "10px 14px", borderBottom: "1px solid var(--border)", display: "flex", flexDirection: "column", gap: 8 }, children: [
|
|
31888
|
+
/* @__PURE__ */ jsx(
|
|
31889
|
+
"input",
|
|
31890
|
+
{
|
|
31891
|
+
value: formSymbol,
|
|
31892
|
+
onChange: (e) => setFormSymbol(e.target.value),
|
|
31893
|
+
placeholder: "Symbol",
|
|
31894
|
+
style: { fontSize: 12.5, padding: "5px 8px", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6 }
|
|
31895
|
+
}
|
|
31896
|
+
),
|
|
31897
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
31898
|
+
/* @__PURE__ */ jsxs(
|
|
31899
|
+
"select",
|
|
31900
|
+
{
|
|
31901
|
+
value: condition,
|
|
31902
|
+
onChange: (e) => setCondition(e.target.value),
|
|
31903
|
+
style: { fontSize: 12.5, padding: "5px 6px", background: "var(--surface-2)", color: "var(--text)", border: "1px solid var(--border)", borderRadius: 6 },
|
|
31904
|
+
children: [
|
|
31905
|
+
/* @__PURE__ */ jsx("option", { value: "above", children: "Crosses above" }),
|
|
31906
|
+
/* @__PURE__ */ jsx("option", { value: "below", children: "Crosses below" })
|
|
31907
|
+
]
|
|
31908
|
+
}
|
|
31909
|
+
),
|
|
31910
|
+
/* @__PURE__ */ jsx(
|
|
31911
|
+
"input",
|
|
31912
|
+
{
|
|
31913
|
+
value: priceStr,
|
|
31914
|
+
onChange: (e) => setPriceStr(e.target.value.replace(/[^0-9.]/g, "")),
|
|
31915
|
+
placeholder: "Price",
|
|
31916
|
+
inputMode: "decimal",
|
|
31917
|
+
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" },
|
|
31918
|
+
onKeyDown: (e) => e.key === "Enter" && !busy && create()
|
|
31919
|
+
}
|
|
31920
|
+
)
|
|
31921
|
+
] }),
|
|
31922
|
+
/* @__PURE__ */ jsx(
|
|
31923
|
+
"button",
|
|
31924
|
+
{
|
|
31925
|
+
onClick: create,
|
|
31926
|
+
disabled: busy || !(parseFloat(priceStr) > 0),
|
|
31927
|
+
style: {
|
|
31928
|
+
fontSize: 12.5,
|
|
31929
|
+
padding: "6px 0",
|
|
31930
|
+
borderRadius: 6,
|
|
31931
|
+
border: "none",
|
|
31932
|
+
cursor: "pointer",
|
|
31933
|
+
background: "var(--primary, #5b5bd6)",
|
|
31934
|
+
color: "#fff",
|
|
31935
|
+
opacity: busy || !(parseFloat(priceStr) > 0) ? 0.55 : 1
|
|
31936
|
+
},
|
|
31937
|
+
children: busy ? "Creating\u2026" : "Create alert"
|
|
31938
|
+
}
|
|
31939
|
+
),
|
|
31940
|
+
error && /* @__PURE__ */ jsx("div", { style: { fontSize: 11.5, color: "var(--down, #e5484d)" }, children: error })
|
|
31941
|
+
] }),
|
|
31942
|
+
/* @__PURE__ */ jsxs("div", { style: { flex: 1, overflowY: "auto" }, children: [
|
|
31943
|
+
alerts === null && !error && /* @__PURE__ */ jsx("div", { style: { padding: 14, fontSize: 12.5, color: "var(--text-muted)" }, children: "Loading\u2026" }),
|
|
31944
|
+
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." }),
|
|
31945
|
+
alerts?.map((a) => /* @__PURE__ */ jsxs("div", { style: {
|
|
31946
|
+
display: "flex",
|
|
31947
|
+
alignItems: "center",
|
|
31948
|
+
gap: 8,
|
|
31949
|
+
padding: "8px 14px",
|
|
31950
|
+
borderBottom: "1px solid var(--border)",
|
|
31951
|
+
fontSize: 12.5,
|
|
31952
|
+
opacity: a.status === "triggered" ? 0.75 : 1
|
|
31953
|
+
}, children: [
|
|
31954
|
+
/* @__PURE__ */ jsx("span", { style: {
|
|
31955
|
+
width: 7,
|
|
31956
|
+
height: 7,
|
|
31957
|
+
borderRadius: "50%",
|
|
31958
|
+
flexShrink: 0,
|
|
31959
|
+
background: a.status === "active" ? "var(--up, #22a06b)" : "var(--text-muted)"
|
|
31960
|
+
}, title: a.status }),
|
|
31961
|
+
/* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
31962
|
+
/* @__PURE__ */ jsxs("div", { style: { whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: [
|
|
31963
|
+
/* @__PURE__ */ jsx("strong", { children: a.symbol }),
|
|
31964
|
+
" ",
|
|
31965
|
+
a.condition === "above" ? "\u2265" : "\u2264",
|
|
31966
|
+
" ",
|
|
31967
|
+
/* @__PURE__ */ jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: a.price })
|
|
31968
|
+
] }),
|
|
31969
|
+
a.status === "triggered" && /* @__PURE__ */ jsxs("div", { style: { fontSize: 11, color: "var(--text-muted)" }, children: [
|
|
31970
|
+
"fired at ",
|
|
31971
|
+
a.trigger_price
|
|
31972
|
+
] })
|
|
31973
|
+
] }),
|
|
31974
|
+
/* @__PURE__ */ jsx(
|
|
31975
|
+
"button",
|
|
31976
|
+
{
|
|
31977
|
+
onClick: () => remove(a.id),
|
|
31978
|
+
title: "Remove alert",
|
|
31979
|
+
style: { background: "none", border: "none", color: "var(--text-muted)", cursor: "pointer", fontSize: 13 },
|
|
31980
|
+
children: "\u2715"
|
|
31981
|
+
}
|
|
31982
|
+
)
|
|
31983
|
+
] }, a.id))
|
|
31984
|
+
] })
|
|
31985
|
+
] });
|
|
31986
|
+
}
|
|
31349
31987
|
function ChartWorkspace({
|
|
31350
31988
|
// TabBar
|
|
31351
31989
|
tabs,
|
|
@@ -31396,6 +32034,9 @@ function ChartWorkspace({
|
|
|
31396
32034
|
seriesType,
|
|
31397
32035
|
onSeriesTypeChange,
|
|
31398
32036
|
showSeriesTypes,
|
|
32037
|
+
onToggleReplay,
|
|
32038
|
+
showReplay,
|
|
32039
|
+
layoutSaveState,
|
|
31399
32040
|
extraTfGroups,
|
|
31400
32041
|
// RightToolbar
|
|
31401
32042
|
watchlistOpen,
|
|
@@ -31506,6 +32147,9 @@ function ChartWorkspace({
|
|
|
31506
32147
|
const effShowMultiChartLayout = gate(showMultiChartLayout, capabilities.multiChartLayout);
|
|
31507
32148
|
const effShowMultipleTabs = gate(showMultipleTabs, capabilities.multipleWorkspaces);
|
|
31508
32149
|
const effShowSeriesTypes = gate(showSeriesTypes, capabilities.chartTypes) && onSeriesTypeChange !== void 0;
|
|
32150
|
+
const effShowReplay = gate(showReplay, capabilities.barReplay) && onToggleReplay !== void 0;
|
|
32151
|
+
const effShowAlerts = gate(void 0, capabilities.alerts) && hostApiUrl !== void 0;
|
|
32152
|
+
const [alertsOpen, setAlertsOpen] = React11.useState(false);
|
|
31509
32153
|
const tabItems = tabs.map((t) => ({
|
|
31510
32154
|
id: t.id,
|
|
31511
32155
|
label: t.label,
|
|
@@ -31584,6 +32228,8 @@ function ChartWorkspace({
|
|
|
31584
32228
|
onSymbolChange,
|
|
31585
32229
|
onTimeframeChange,
|
|
31586
32230
|
...effShowSeriesTypes ? { seriesType: seriesType ?? "candlestick", onSeriesTypeChange } : {},
|
|
32231
|
+
...effShowReplay ? { onToggleReplay } : {},
|
|
32232
|
+
...effShowAlerts ? { onToggleAlerts: () => setAlertsOpen((o) => !o), alertsOpen } : {},
|
|
31587
32233
|
onAddCustomTimeframe,
|
|
31588
32234
|
onRemoveCustomTimeframe,
|
|
31589
32235
|
onFavoritesChange: onFavoriteTfsChange,
|
|
@@ -31599,6 +32245,7 @@ function ChartWorkspace({
|
|
|
31599
32245
|
currentLayoutName,
|
|
31600
32246
|
currentLayoutId,
|
|
31601
32247
|
autoSave,
|
|
32248
|
+
...layoutSaveState !== void 0 ? { layoutSaveState } : {},
|
|
31602
32249
|
onFetchLayouts,
|
|
31603
32250
|
onSaveLayout,
|
|
31604
32251
|
onLoadLayout,
|
|
@@ -31630,6 +32277,15 @@ function ChartWorkspace({
|
|
|
31630
32277
|
/* @__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: [
|
|
31631
32278
|
(dataBlocked || isLicensed === false) && /* @__PURE__ */ jsx(LicenseRequiredOverlay, {}),
|
|
31632
32279
|
/* @__PURE__ */ jsx("div", { style: { position: "relative", flex: 1, minWidth: 0, minHeight: 0 }, children: chartSlots }),
|
|
32280
|
+
effShowAlerts && alertsOpen && /* @__PURE__ */ jsx(
|
|
32281
|
+
AlertsDrawer,
|
|
32282
|
+
{
|
|
32283
|
+
apiUrl: hostApiUrl,
|
|
32284
|
+
getAuthToken,
|
|
32285
|
+
symbol: activeSymbol ?? "",
|
|
32286
|
+
onClose: () => setAlertsOpen(false)
|
|
32287
|
+
}
|
|
32288
|
+
),
|
|
31633
32289
|
autoTrading && !onToggleOrderEntry && effOrderEntryOpen && capabilities.orderEntry && tradingBridge && /* @__PURE__ */ jsx(
|
|
31634
32290
|
OrderTicket,
|
|
31635
32291
|
{
|