@luxalgo/vela 0.6.7 → 0.6.9
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/{DataProvider-BsQM2WNH.d.cts → DataProvider-8Z95Q-RJ.d.cts} +1 -1
- package/dist/{DataProvider-BSLlBpB9.d.ts → DataProvider-DExJrfut.d.ts} +1 -1
- package/dist/{chunk-2R7BANEM.js → chunk-62SVGONC.js} +2 -2
- package/dist/{chunk-6WFKTAT4.js → chunk-6WDDVMBJ.js} +548 -147
- package/dist/{chunk-L3I2CCYO.js → chunk-EQCHJZOT.js} +49 -2
- package/dist/{chunk-OICPLNU7.js → chunk-FCVIG7JG.js} +188 -2
- package/dist/{chunk-WVP4XV5A.js → chunk-STHSKXOR.js} +407 -159
- package/dist/{chunk-PN5KWFZ4.js → chunk-WFSZBX3R.js} +2 -2
- package/dist/{contributions-Z12BrWCy.d.ts → contributions-C1U2Krwg.d.cts} +112 -7
- package/dist/{contributions-Hv4_cOJo.d.cts → contributions-D7PVZO2i.d.ts} +112 -7
- package/dist/index.cjs +755 -152
- package/dist/index.d.cts +12 -7
- package/dist/index.d.ts +12 -7
- package/dist/index.js +4 -4
- package/dist/{options-BaVTMXaO.d.ts → options-FM0peknS.d.cts} +84 -7
- package/dist/{options-BaVTMXaO.d.cts → options-FM0peknS.d.ts} +84 -7
- package/dist/{plugin-9koBjc5H.d.ts → plugin-7bkF32Rk.d.ts} +3 -3
- package/dist/{plugin-BXhDHTYn.d.cts → plugin-DfVqBz9p.d.cts} +3 -3
- package/dist/plugin.cjs +18 -2
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/{bottombar-Br4rzx_R.d.ts → statusline-DOPiT6I6.d.cts} +91 -6
- package/dist/{bottombar-Div5zibZ.d.cts → statusline-zdF4eZLr.d.ts} +91 -6
- package/dist/ui.cjs +201 -4
- package/dist/ui.d.cts +89 -2
- package/dist/ui.d.ts +89 -2
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +755 -152
- package/dist/vela.global.min.js +93 -44
- package/dist/widget.cjs +1144 -271
- package/dist/widget.d.cts +12 -85
- package/dist/widget.d.ts +12 -85
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +1144 -271
- package/dist/workspace.d.cts +65 -11
- package/dist/workspace.d.ts +65 -11
- package/dist/workspace.js +6 -6
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS } from './chunk-
|
|
2
|
-
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-
|
|
3
|
-
import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, BEARISH, BULLISH, isDarkColor, SERIES_LINE, SLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-
|
|
1
|
+
import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS } from './chunk-EQCHJZOT.js';
|
|
2
|
+
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, CalloutBubble, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-FCVIG7JG.js';
|
|
3
|
+
import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, BEARISH, BULLISH, isDarkColor, SERIES_LINE, SLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-WFSZBX3R.js';
|
|
4
4
|
|
|
5
5
|
// src/core/events/EventBus.ts
|
|
6
6
|
var TypedEventBus = class {
|
|
@@ -428,6 +428,15 @@ var RendererControl = class {
|
|
|
428
428
|
setLegendActions(provider) {
|
|
429
429
|
this.renderer.setLegendActions?.(provider);
|
|
430
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Wire the legend rows' HOST-CONTRIBUTED callout bubbles (the shells route the
|
|
433
|
+
* plugin registry through this; see `registerLegendCallout`). Silent on a renderer
|
|
434
|
+
* without the seam — contributed callouts simply never show there, same graceful
|
|
435
|
+
* degradation as {@link setLegendActions}.
|
|
436
|
+
*/
|
|
437
|
+
setLegendCallouts(provider) {
|
|
438
|
+
this.renderer.setLegendCallouts?.(provider);
|
|
439
|
+
}
|
|
431
440
|
/**
|
|
432
441
|
* Replace the indicator legend's fold toggle with a host action (or restore it with
|
|
433
442
|
* `null`) — multi-chart shells point the chip at their indicator overview instead of
|
|
@@ -2898,6 +2907,10 @@ function isLineLikeSeries(spec) {
|
|
|
2898
2907
|
}
|
|
2899
2908
|
|
|
2900
2909
|
// src/renderers/shared/IndicatorInputsDialog.ts
|
|
2910
|
+
var PROPS_TAB = "Properties";
|
|
2911
|
+
function bagOf(row, decl) {
|
|
2912
|
+
return decl.prop ? row.propValues : row.values;
|
|
2913
|
+
}
|
|
2901
2914
|
var SOURCES = ["close", "open", "high", "low", "hl2", "hlc3", "ohlc4", "volume"];
|
|
2902
2915
|
var IndicatorInputsDialog = class {
|
|
2903
2916
|
constructor(host) {
|
|
@@ -2908,6 +2921,7 @@ var IndicatorInputsDialog = class {
|
|
|
2908
2921
|
this.openId = null;
|
|
2909
2922
|
this.row = null;
|
|
2910
2923
|
this.snapshot = null;
|
|
2924
|
+
this.propSnapshot = null;
|
|
2911
2925
|
this.dialogTips = [];
|
|
2912
2926
|
/** Re-applies every `when` gate against current values; set while the dialog is open. */
|
|
2913
2927
|
this.refreshVisibility = null;
|
|
@@ -2930,10 +2944,11 @@ var IndicatorInputsDialog = class {
|
|
|
2930
2944
|
}
|
|
2931
2945
|
open(row) {
|
|
2932
2946
|
this.close();
|
|
2933
|
-
if (row.inputs.length === 0) return;
|
|
2947
|
+
if (row.inputs.length === 0 && row.props.length === 0) return;
|
|
2934
2948
|
this.row = row;
|
|
2935
2949
|
this.openId = row.id;
|
|
2936
2950
|
this.snapshot = { ...row.values };
|
|
2951
|
+
this.propSnapshot = { ...row.propValues };
|
|
2937
2952
|
ensureDialogStyles();
|
|
2938
2953
|
const t = this.host.theme();
|
|
2939
2954
|
const border = "var(--vela-border)";
|
|
@@ -2941,7 +2956,7 @@ var IndicatorInputsDialog = class {
|
|
|
2941
2956
|
const host = this.host.dialogHost() ?? this.host.container;
|
|
2942
2957
|
const ui = new Dialog({
|
|
2943
2958
|
host,
|
|
2944
|
-
title: row.
|
|
2959
|
+
title: row.title,
|
|
2945
2960
|
// Non-modal: live-edit dialog — a modal machine locks pointer events on the
|
|
2946
2961
|
// whole body, killing the chart and the body-portaled popovers.
|
|
2947
2962
|
modal: false,
|
|
@@ -2953,12 +2968,16 @@ var IndicatorInputsDialog = class {
|
|
|
2953
2968
|
closeOnEscape: false,
|
|
2954
2969
|
footer: (foot) => {
|
|
2955
2970
|
foot.append(
|
|
2971
|
+
this.resetAction(),
|
|
2956
2972
|
this.dialogButton("Cancel", false, () => this.revertAndClose()),
|
|
2957
2973
|
this.dialogButton("Ok", true, () => this.close())
|
|
2958
2974
|
);
|
|
2959
2975
|
},
|
|
2976
|
+
// Stale-guard: destroying a dialog fires its machine's onOpenChange(false)
|
|
2977
|
+
// asynchronously — after a reset rebuild that notification must not close
|
|
2978
|
+
// the replacement dialog.
|
|
2960
2979
|
onOpenChange: (open) => {
|
|
2961
|
-
if (!open) this.close();
|
|
2980
|
+
if (!open && this.uiDialog === ui) this.close();
|
|
2962
2981
|
}
|
|
2963
2982
|
});
|
|
2964
2983
|
applyChromeTokens(ui.panel, t);
|
|
@@ -2969,6 +2988,16 @@ var IndicatorInputsDialog = class {
|
|
|
2969
2988
|
text: () => "Close"
|
|
2970
2989
|
}));
|
|
2971
2990
|
const tabDefs = tabInputs(row.inputs);
|
|
2991
|
+
if (row.props.length > 0) {
|
|
2992
|
+
const propDecls = row.props.map((p) => {
|
|
2993
|
+
const d = { ...p, prop: true };
|
|
2994
|
+
delete d.tab;
|
|
2995
|
+
return d;
|
|
2996
|
+
});
|
|
2997
|
+
const existing = tabDefs.find((t2) => t2.name === PROPS_TAB);
|
|
2998
|
+
if (existing) existing.inputs.push(...propDecls);
|
|
2999
|
+
else tabDefs.push({ name: PROPS_TAB, inputs: propDecls });
|
|
3000
|
+
}
|
|
2972
3001
|
const tabs = document.createElement("div");
|
|
2973
3002
|
tabs.style.cssText = `display:flex;gap:12px;padding:0 20px;border-bottom:1px solid ${border};flex:0 0 auto;`;
|
|
2974
3003
|
const tabEls = [];
|
|
@@ -3071,9 +3100,10 @@ var IndicatorInputsDialog = class {
|
|
|
3071
3100
|
this.openId = null;
|
|
3072
3101
|
this.row = null;
|
|
3073
3102
|
this.snapshot = null;
|
|
3103
|
+
this.propSnapshot = null;
|
|
3074
3104
|
ui?.destroy();
|
|
3075
3105
|
}
|
|
3076
|
-
/** Restore every input to its open-time value (re-running the indicator), then close. */
|
|
3106
|
+
/** Restore every input and prop to its open-time value (re-running the indicator), then close. */
|
|
3077
3107
|
revertAndClose() {
|
|
3078
3108
|
const row = this.row;
|
|
3079
3109
|
const snap = this.snapshot;
|
|
@@ -3087,12 +3117,55 @@ var IndicatorInputsDialog = class {
|
|
|
3087
3117
|
}
|
|
3088
3118
|
}
|
|
3089
3119
|
}
|
|
3120
|
+
const propSnap = this.propSnapshot;
|
|
3121
|
+
if (row && propSnap) {
|
|
3122
|
+
for (const p of row.props) {
|
|
3123
|
+
const snapped = propSnap[p.key];
|
|
3124
|
+
const before = snapped !== void 0 ? snapped : p.defval;
|
|
3125
|
+
if (row.propValues[p.key] !== before) {
|
|
3126
|
+
row.propValues[p.key] = before;
|
|
3127
|
+
this.host.onChange?.({ indicatorId: row.id, key: p.key, value: before, kind: "prop" });
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3090
3131
|
this.close();
|
|
3091
3132
|
}
|
|
3133
|
+
/** The footer's reset button — same chip as Cancel, pinned to the LEFT edge
|
|
3134
|
+
* (`margin-right:auto` against the footer's flex-end keeps Cancel/Ok right). */
|
|
3135
|
+
resetAction() {
|
|
3136
|
+
const b = this.dialogButton("Reset defaults", false, () => this.resetToDefaults());
|
|
3137
|
+
b.style.marginRight = "auto";
|
|
3138
|
+
return b;
|
|
3139
|
+
}
|
|
3140
|
+
/** Restore every input to its declared default (re-running the indicator), then
|
|
3141
|
+
* re-open the form so each control re-reads the restored values — the same
|
|
3142
|
+
* rebuild-after-reset move as the chart-settings dialog. The open-time snapshot
|
|
3143
|
+
* survives the rebuild, so Cancel after a reset still reverts the whole session. */
|
|
3144
|
+
resetToDefaults() {
|
|
3145
|
+
const row = this.row;
|
|
3146
|
+
if (!row) return;
|
|
3147
|
+
const snap = this.snapshot;
|
|
3148
|
+
const propSnap = this.propSnapshot;
|
|
3149
|
+
for (const inp of row.inputs) {
|
|
3150
|
+
if (row.values[inp.key] !== inp.defval) {
|
|
3151
|
+
row.values[inp.key] = inp.defval;
|
|
3152
|
+
this.host.onChange?.({ indicatorId: row.id, key: inp.key, value: inp.defval });
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
for (const p of row.props) {
|
|
3156
|
+
if (row.propValues[p.key] !== p.defval) {
|
|
3157
|
+
row.propValues[p.key] = p.defval;
|
|
3158
|
+
this.host.onChange?.({ indicatorId: row.id, key: p.key, value: p.defval, kind: "prop" });
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
this.open(row);
|
|
3162
|
+
if (snap) this.snapshot = snap;
|
|
3163
|
+
if (propSnap) this.propSnapshot = propSnap;
|
|
3164
|
+
}
|
|
3092
3165
|
/** Write one edit through: store it, notify the host, and re-apply the `when` gates. */
|
|
3093
|
-
commit(row,
|
|
3094
|
-
row.
|
|
3095
|
-
this.host.onChange?.({ indicatorId: row.id, key, value });
|
|
3166
|
+
commit(row, decl, value) {
|
|
3167
|
+
bagOf(row, decl)[decl.key] = value;
|
|
3168
|
+
this.host.onChange?.({ indicatorId: row.id, key: decl.key, value, ...decl.prop ? { kind: "prop" } : {} });
|
|
3096
3169
|
this.refreshVisibility?.();
|
|
3097
3170
|
}
|
|
3098
3171
|
/** One settings row (or several `inline=` inputs) placed into a section's grid. */
|
|
@@ -3119,7 +3192,7 @@ var IndicatorInputsDialog = class {
|
|
|
3119
3192
|
const id = idOf(lead);
|
|
3120
3193
|
const info = lead.tooltip ? this.infoButton(lead.tooltip) : void 0;
|
|
3121
3194
|
if (lead.type === "bool") {
|
|
3122
|
-
const current = Boolean(row
|
|
3195
|
+
const current = Boolean(bagOf(row, lead)[lead.key] ?? lead.defval);
|
|
3123
3196
|
return append(fieldRow({
|
|
3124
3197
|
label: nameOf(lead),
|
|
3125
3198
|
id,
|
|
@@ -3128,7 +3201,7 @@ var IndicatorInputsDialog = class {
|
|
|
3128
3201
|
toggle: {
|
|
3129
3202
|
id,
|
|
3130
3203
|
checked: current,
|
|
3131
|
-
onChange: (v) => this.commit(row, lead
|
|
3204
|
+
onChange: (v) => this.commit(row, lead, v)
|
|
3132
3205
|
}
|
|
3133
3206
|
}));
|
|
3134
3207
|
}
|
|
@@ -3153,8 +3226,8 @@ var IndicatorInputsDialog = class {
|
|
|
3153
3226
|
}
|
|
3154
3227
|
/** Build the typed control for one input, committing edits live via `onChange`. */
|
|
3155
3228
|
buildControl(row, inp, id) {
|
|
3156
|
-
const current = row
|
|
3157
|
-
const emit = (value) => this.commit(row, inp
|
|
3229
|
+
const current = bagOf(row, inp)[inp.key] ?? inp.defval;
|
|
3230
|
+
const emit = (value) => this.commit(row, inp, value);
|
|
3158
3231
|
if (inp.type === "bool") {
|
|
3159
3232
|
return buildFieldControl({ kind: "switch", id, checked: Boolean(current), onChange: (v) => emit(v) }).el;
|
|
3160
3233
|
}
|
|
@@ -3165,7 +3238,7 @@ var IndicatorInputsDialog = class {
|
|
|
3165
3238
|
kind: "color",
|
|
3166
3239
|
id,
|
|
3167
3240
|
theme: this.host.theme(),
|
|
3168
|
-
get: () => String(row
|
|
3241
|
+
get: () => String(bagOf(row, inp)[inp.key] ?? inp.defval),
|
|
3169
3242
|
onChange: (v) => emit(v)
|
|
3170
3243
|
}).el;
|
|
3171
3244
|
}
|
|
@@ -3728,6 +3801,7 @@ function timeParts(ts) {
|
|
|
3728
3801
|
}
|
|
3729
3802
|
|
|
3730
3803
|
// src/renderers/shared/InputsUI.ts
|
|
3804
|
+
var LEGEND_AT_TOP_ATTR = "data-vela-pane-at-top";
|
|
3731
3805
|
var STATUS_KEYFRAMES = "@keyframes vela-ind-pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.3;transform:scale(.6)}}";
|
|
3732
3806
|
var LEGEND_ICON_PX2 = 16;
|
|
3733
3807
|
var LEGEND_CTL_PX = 18;
|
|
@@ -3766,10 +3840,14 @@ var InputsUI = class {
|
|
|
3766
3840
|
this.moveApi = null;
|
|
3767
3841
|
/** Host-contributed legend actions, resolved PER ROW at render time (see setLegendActions). */
|
|
3768
3842
|
this.legendActions = null;
|
|
3843
|
+
/** Host-contributed callout bubbles, resolved PER ROW at render time (see setLegendCallouts). */
|
|
3844
|
+
this.legendCallouts = null;
|
|
3769
3845
|
/** Chrome-tooltip disposers, per row id — a tip open at removal must not outlive its row. */
|
|
3770
3846
|
this.rowTips = /* @__PURE__ */ new Map();
|
|
3771
3847
|
/** Same, for the contributed extras only (rebuilt independently by setLegendActions). */
|
|
3772
3848
|
this.extrasTips = /* @__PURE__ */ new Map();
|
|
3849
|
+
/** Same, for the contributed callouts only (rebuilt independently by setLegendCallouts). */
|
|
3850
|
+
this.calloutTips = /* @__PURE__ */ new Map();
|
|
3773
3851
|
/** Open "Move to" menu (kept so it can be torn down). */
|
|
3774
3852
|
this.moveMenu = null;
|
|
3775
3853
|
this.moveTargets = [];
|
|
@@ -3927,6 +4005,42 @@ var InputsUI = class {
|
|
|
3927
4005
|
extrasEl.appendChild(btn2);
|
|
3928
4006
|
}
|
|
3929
4007
|
}
|
|
4008
|
+
/**
|
|
4009
|
+
* Wire the host-contributed callout bubbles. Same contract as
|
|
4010
|
+
* {@link setLegendActions}: re-calling replaces the provider and re-projects the
|
|
4011
|
+
* rows already on screen.
|
|
4012
|
+
*/
|
|
4013
|
+
setLegendCallouts(provider) {
|
|
4014
|
+
this.legendCallouts = provider;
|
|
4015
|
+
for (const row of this.rows.values()) this.renderCallouts(row);
|
|
4016
|
+
}
|
|
4017
|
+
/** (Re)build one row's callout bubbles — tinted icon circles beside the title whose
|
|
4018
|
+
* click (when the view carries content) deploys a panel of text and actions. */
|
|
4019
|
+
renderCallouts(row) {
|
|
4020
|
+
this.disposeTips(this.calloutTips, row.id);
|
|
4021
|
+
for (const bubble of row.callouts) bubble.destroy();
|
|
4022
|
+
row.callouts = [];
|
|
4023
|
+
row.calloutsEl.replaceChildren();
|
|
4024
|
+
const views = this.legendCallouts?.(row.id) ?? [];
|
|
4025
|
+
row.calloutsEl.style.display = views.length > 0 ? "inline-flex" : "none";
|
|
4026
|
+
for (const view of views) {
|
|
4027
|
+
const bubble = new CalloutBubble({
|
|
4028
|
+
icon: view.icon,
|
|
4029
|
+
background: view.background,
|
|
4030
|
+
...view.color !== void 0 ? { color: view.color } : {},
|
|
4031
|
+
label: view.tooltip,
|
|
4032
|
+
...view.content !== void 0 ? { panel: view.content } : {},
|
|
4033
|
+
// The panel portals into the plot host and self-tokens: it must work on
|
|
4034
|
+
// a bare chart, where no `.vela-ui` token ancestor exists.
|
|
4035
|
+
host: this.container,
|
|
4036
|
+
theme: () => this.theme
|
|
4037
|
+
});
|
|
4038
|
+
bubble.el.dataset.legendCallout = view.id;
|
|
4039
|
+
this.tip(this.calloutTips, row.id, bubble.el, view.tooltip);
|
|
4040
|
+
row.callouts.push(bubble);
|
|
4041
|
+
row.calloutsEl.appendChild(bubble.el);
|
|
4042
|
+
}
|
|
4043
|
+
}
|
|
3930
4044
|
/** Reposition the per-pane legend containers after a layout change. */
|
|
3931
4045
|
reposition() {
|
|
3932
4046
|
for (const [paneId, lg] of this.legends) this.positionLegend(lg, paneId);
|
|
@@ -4094,6 +4208,7 @@ var InputsUI = class {
|
|
|
4094
4208
|
}
|
|
4095
4209
|
positionLegend(lg, paneId) {
|
|
4096
4210
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
4211
|
+
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
4097
4212
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
4098
4213
|
const collapsed = this.paneCollapse.has(paneId);
|
|
4099
4214
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
@@ -4169,13 +4284,13 @@ var InputsUI = class {
|
|
|
4169
4284
|
}
|
|
4170
4285
|
/** Create or update an indicator's legend row (in the legend for its pane). */
|
|
4171
4286
|
upsert(id, title, inputs, values, paneId = "price", opts = {}) {
|
|
4172
|
-
const settingsTitle = opts.settingsTitle ?? title;
|
|
4173
4287
|
const existing = this.rows.get(id);
|
|
4174
4288
|
if (existing) {
|
|
4175
4289
|
existing.title = title;
|
|
4176
|
-
existing.settingsTitle = settingsTitle;
|
|
4177
4290
|
existing.inputs = inputs;
|
|
4178
4291
|
existing.values = { ...values };
|
|
4292
|
+
existing.props = opts.props ?? [];
|
|
4293
|
+
existing.propValues = { ...opts.propValues ?? {} };
|
|
4179
4294
|
existing.titleEl.textContent = title;
|
|
4180
4295
|
if (existing.paneId !== paneId) {
|
|
4181
4296
|
existing.paneId = paneId;
|
|
@@ -4227,6 +4342,9 @@ var InputsUI = class {
|
|
|
4227
4342
|
titleWrap.appendChild(beta);
|
|
4228
4343
|
}
|
|
4229
4344
|
el.appendChild(titleWrap);
|
|
4345
|
+
const calloutsEl = document.createElement("span");
|
|
4346
|
+
calloutsEl.style.cssText = `display:none;align-items:center;gap:4px;flex:none;margin-left:${LEGEND_TITLE_STATUS_GAP_PX}px;`;
|
|
4347
|
+
el.appendChild(calloutsEl);
|
|
4230
4348
|
el.appendChild(statusEl);
|
|
4231
4349
|
const valuesEl = document.createElement("span");
|
|
4232
4350
|
valuesEl.style.cssText = `display:none;align-items:center;gap:5px;margin-left:${LEGEND_TITLE_VALUES_GAP_PX}px;white-space:nowrap;font-variant-numeric:tabular-nums;`;
|
|
@@ -4243,13 +4361,13 @@ var InputsUI = class {
|
|
|
4243
4361
|
eye.className = "vela-ind-ctl";
|
|
4244
4362
|
eye.style.cssText = LEGEND_CTL_CSS;
|
|
4245
4363
|
eye.addEventListener("click", () => {
|
|
4246
|
-
const
|
|
4247
|
-
this.onToggleVisible?.(id, Boolean(
|
|
4364
|
+
const row2 = this.rows.get(id);
|
|
4365
|
+
this.onToggleVisible?.(id, Boolean(row2?.hidden));
|
|
4248
4366
|
});
|
|
4249
4367
|
controlsEl.appendChild(eye);
|
|
4250
4368
|
eyeEl = eye;
|
|
4251
4369
|
}
|
|
4252
|
-
if (inputs.length > 0) {
|
|
4370
|
+
if (inputs.length > 0 || (opts.props ?? []).length > 0) {
|
|
4253
4371
|
const gear = document.createElement("button");
|
|
4254
4372
|
gear.type = "button";
|
|
4255
4373
|
gear.setAttribute("aria-label", "Settings");
|
|
@@ -4289,7 +4407,9 @@ var InputsUI = class {
|
|
|
4289
4407
|
controlsEl.appendChild(close);
|
|
4290
4408
|
el.appendChild(controlsEl);
|
|
4291
4409
|
this.attach(this.legendFor(paneId), el, !!opts.native);
|
|
4292
|
-
|
|
4410
|
+
const row = { id, title, inputs, values: { ...values }, props: opts.props ?? [], propValues: { ...opts.propValues ?? {} }, el, titleEl, statusEl, valuesEl, plotValues: [], plotValuesKey: "", showValues: null, highlighted: false, paneId, hidden: false, eyeEl, controlsEl, extrasEl, calloutsEl, callouts: [], native: !!opts.native };
|
|
4411
|
+
this.rows.set(id, row);
|
|
4412
|
+
this.renderCallouts(row);
|
|
4293
4413
|
this.syncFoldToggle();
|
|
4294
4414
|
}
|
|
4295
4415
|
/** Place a row in its pane's legend — native rows PREPEND (pinned to the top), Pine rows append. */
|
|
@@ -4298,9 +4418,11 @@ var InputsUI = class {
|
|
|
4298
4418
|
else container.appendChild(el);
|
|
4299
4419
|
}
|
|
4300
4420
|
/** Reflect programmatic input changes (so a re-opened dialog shows current values). */
|
|
4301
|
-
setValues(id, values) {
|
|
4421
|
+
setValues(id, values, props) {
|
|
4302
4422
|
const row = this.rows.get(id);
|
|
4303
|
-
if (row)
|
|
4423
|
+
if (!row) return;
|
|
4424
|
+
row.values = { ...row.values, ...values };
|
|
4425
|
+
if (props) row.propValues = { ...row.propValues, ...props };
|
|
4304
4426
|
}
|
|
4305
4427
|
/**
|
|
4306
4428
|
* Reflect an indicator's live status in its legend row: `'loading'` shows three pulsing
|
|
@@ -4382,8 +4504,13 @@ var InputsUI = class {
|
|
|
4382
4504
|
syncRowActions(row) {
|
|
4383
4505
|
const open = row.highlighted;
|
|
4384
4506
|
row.controlsEl.style.display = open || row.hidden ? "inline-flex" : "none";
|
|
4385
|
-
if (open)
|
|
4386
|
-
|
|
4507
|
+
if (open) {
|
|
4508
|
+
row.el.appendChild(row.statusEl);
|
|
4509
|
+
row.el.appendChild(row.calloutsEl);
|
|
4510
|
+
} else {
|
|
4511
|
+
row.el.insertBefore(row.statusEl, row.valuesEl);
|
|
4512
|
+
row.el.insertBefore(row.calloutsEl, row.statusEl);
|
|
4513
|
+
}
|
|
4387
4514
|
for (const child of Array.from(row.controlsEl.children)) {
|
|
4388
4515
|
if (!(child instanceof HTMLElement) || child === row.eyeEl) continue;
|
|
4389
4516
|
if (child === row.extrasEl) {
|
|
@@ -4401,10 +4528,12 @@ var InputsUI = class {
|
|
|
4401
4528
|
}
|
|
4402
4529
|
remove(id) {
|
|
4403
4530
|
const row = this.rows.get(id);
|
|
4531
|
+
for (const bubble of row?.callouts ?? []) bubble.destroy();
|
|
4404
4532
|
row?.el.remove();
|
|
4405
4533
|
this.rows.delete(id);
|
|
4406
4534
|
this.disposeTips(this.rowTips, id);
|
|
4407
4535
|
this.disposeTips(this.extrasTips, id);
|
|
4536
|
+
this.disposeTips(this.calloutTips, id);
|
|
4408
4537
|
if (this.selectedId === id) this.selectedId = null;
|
|
4409
4538
|
if (this.inputsDialog.openId === id) this.inputsDialog.close();
|
|
4410
4539
|
this.syncFoldToggle();
|
|
@@ -4424,6 +4553,8 @@ var InputsUI = class {
|
|
|
4424
4553
|
this.rowMenu = null;
|
|
4425
4554
|
for (const id of [...this.rowTips.keys()]) this.disposeTips(this.rowTips, id);
|
|
4426
4555
|
for (const id of [...this.extrasTips.keys()]) this.disposeTips(this.extrasTips, id);
|
|
4556
|
+
for (const id of [...this.calloutTips.keys()]) this.disposeTips(this.calloutTips, id);
|
|
4557
|
+
for (const row of this.rows.values()) for (const bubble of row.callouts) bubble.destroy();
|
|
4427
4558
|
for (const lg of this.legends.values()) lg.remove();
|
|
4428
4559
|
this.legends.clear();
|
|
4429
4560
|
this.rows.clear();
|
|
@@ -4778,6 +4909,32 @@ var SIZE_PX = {
|
|
|
4778
4909
|
large: 16,
|
|
4779
4910
|
huge: 20
|
|
4780
4911
|
};
|
|
4912
|
+
function fontPxOf(size) {
|
|
4913
|
+
if (typeof size === "number") return size > 0 ? size : SIZE_PX.auto;
|
|
4914
|
+
return SIZE_PX[size] ?? SIZE_PX.auto;
|
|
4915
|
+
}
|
|
4916
|
+
function tableHasContent(t) {
|
|
4917
|
+
return t.cells.some((row) => row?.some((c) => c != null && !c.merged));
|
|
4918
|
+
}
|
|
4919
|
+
function mergeRenderPlan(t) {
|
|
4920
|
+
const span = /* @__PURE__ */ new Map();
|
|
4921
|
+
const omit = /* @__PURE__ */ new Set();
|
|
4922
|
+
for (const m of t.merges) {
|
|
4923
|
+
span.set(`${m.startRow}:${m.startCol}`, { cs: m.endCol - m.startCol + 1, rs: m.endRow - m.startRow + 1 });
|
|
4924
|
+
for (let r = m.startRow; r <= m.endRow; r += 1) {
|
|
4925
|
+
for (let c = m.startCol; c <= m.endCol; c += 1) {
|
|
4926
|
+
if (r !== m.startRow || c !== m.startCol) omit.add(`${r}:${c}`);
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
for (let r = 0; r < t.rows; r += 1) {
|
|
4931
|
+
for (let c = 0; c < t.columns; c += 1) {
|
|
4932
|
+
if (t.cells[r]?.[c]?.merged && !span.has(`${r}:${c}`)) omit.add(`${r}:${c}`);
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
for (const key of span.keys()) omit.delete(key);
|
|
4936
|
+
return { span, omit };
|
|
4937
|
+
}
|
|
4781
4938
|
var TableOverlay = class {
|
|
4782
4939
|
constructor(container, theme, paneBounds) {
|
|
4783
4940
|
this.container = container;
|
|
@@ -4798,7 +4955,9 @@ var TableOverlay = class {
|
|
|
4798
4955
|
update(tables) {
|
|
4799
4956
|
this.lastTables = tables;
|
|
4800
4957
|
this.root.replaceChildren();
|
|
4801
|
-
for (const t of tables)
|
|
4958
|
+
for (const t of tables) {
|
|
4959
|
+
if (tableHasContent(t)) this.root.appendChild(this.renderTable(t));
|
|
4960
|
+
}
|
|
4802
4961
|
}
|
|
4803
4962
|
/** Re-render at the current pane geometry — after layout settles or on resize. */
|
|
4804
4963
|
reposition() {
|
|
@@ -4813,39 +4972,36 @@ var TableOverlay = class {
|
|
|
4813
4972
|
this.root.remove();
|
|
4814
4973
|
}
|
|
4815
4974
|
renderTable(t) {
|
|
4975
|
+
const b = this.paneBounds(t.paneId);
|
|
4816
4976
|
const wrap = document.createElement("div");
|
|
4817
4977
|
wrap.style.position = "absolute";
|
|
4818
|
-
|
|
4978
|
+
if (t.frameColor && t.frameWidth > 0) wrap.style.border = `${t.frameWidth}px solid ${t.frameColor}`;
|
|
4979
|
+
this.anchor(wrap, t.position, b);
|
|
4819
4980
|
const table = document.createElement("table");
|
|
4820
4981
|
Object.assign(table.style, {
|
|
4821
4982
|
borderCollapse: "collapse",
|
|
4822
4983
|
background: t.bgColor ?? "transparent",
|
|
4823
4984
|
fontFamily: this.theme.fontFamily || "sans-serif",
|
|
4824
|
-
// Frame as an inset shadow (not a `border`) so it stays independent of the
|
|
4825
|
-
// collapsed cell borders even when frame_width != border_width.
|
|
4826
|
-
boxShadow: t.frameColor && t.frameWidth > 0 ? `inset 0 0 0 ${t.frameWidth}px ${t.frameColor}` : "none",
|
|
4827
4985
|
border: "none",
|
|
4828
4986
|
tableLayout: "auto",
|
|
4829
4987
|
// Re-enable pointer events on the table only (root is none) so cell tooltips work.
|
|
4830
4988
|
pointerEvents: "auto"
|
|
4831
4989
|
});
|
|
4832
|
-
const span
|
|
4833
|
-
const
|
|
4834
|
-
|
|
4835
|
-
span.set(`${m.startRow}:${m.startCol}`, { cs: m.endCol - m.startCol + 1, rs: m.endRow - m.startRow + 1 });
|
|
4836
|
-
for (let r = m.startRow; r <= m.endRow; r += 1) {
|
|
4837
|
-
for (let c = m.startCol; c <= m.endCol; c += 1) {
|
|
4838
|
-
if (r !== m.startRow || c !== m.startCol) skip.add(`${r}:${c}`);
|
|
4839
|
-
}
|
|
4840
|
-
}
|
|
4841
|
-
}
|
|
4990
|
+
const { span, omit } = mergeRenderPlan(t);
|
|
4991
|
+
const plotW = Math.max(0, (this.root.clientWidth || this.container.clientWidth) - b.rightAxis);
|
|
4992
|
+
const paneH = b.height;
|
|
4842
4993
|
const cellBorder = t.borderColor && t.borderWidth > 0 ? `${t.borderWidth}px solid ${t.borderColor}` : "none";
|
|
4843
4994
|
for (let r = 0; r < t.rows; r += 1) {
|
|
4844
4995
|
const tr = document.createElement("tr");
|
|
4845
4996
|
for (let c = 0; c < t.columns; c += 1) {
|
|
4997
|
+
if (omit.has(`${r}:${c}`)) continue;
|
|
4846
4998
|
const cell = t.cells[r]?.[c] ?? null;
|
|
4847
|
-
if (skip.has(`${r}:${c}`) || cell?.merged) continue;
|
|
4848
4999
|
const td = document.createElement("td");
|
|
5000
|
+
if (cell === null) {
|
|
5001
|
+
Object.assign(td.style, { padding: "0", border: "none" });
|
|
5002
|
+
tr.appendChild(td);
|
|
5003
|
+
continue;
|
|
5004
|
+
}
|
|
4849
5005
|
const sp = span.get(`${r}:${c}`);
|
|
4850
5006
|
if (sp) {
|
|
4851
5007
|
if (sp.cs > 1) td.colSpan = sp.cs;
|
|
@@ -4854,18 +5010,22 @@ var TableOverlay = class {
|
|
|
4854
5010
|
Object.assign(td.style, {
|
|
4855
5011
|
border: cellBorder,
|
|
4856
5012
|
padding: "2px 6px",
|
|
4857
|
-
background: cell
|
|
4858
|
-
color: cell
|
|
4859
|
-
textAlign: cell
|
|
4860
|
-
verticalAlign: cell
|
|
4861
|
-
fontSize: `${
|
|
4862
|
-
fontFamily: cell
|
|
4863
|
-
fontWeight: cell
|
|
4864
|
-
fontStyle: cell
|
|
4865
|
-
|
|
5013
|
+
background: cell.bgColor ?? "transparent",
|
|
5014
|
+
color: cell.textColor ?? this.theme.textColor,
|
|
5015
|
+
textAlign: cell.hAlign,
|
|
5016
|
+
verticalAlign: cell.vAlign === "top" ? "top" : cell.vAlign === "bottom" ? "bottom" : "middle",
|
|
5017
|
+
fontSize: `${fontPxOf(cell.textSize)}px`,
|
|
5018
|
+
fontFamily: cell.fontFamily === "monospace" ? "monospace" : "inherit",
|
|
5019
|
+
fontWeight: cell.bold ? "bold" : "normal",
|
|
5020
|
+
fontStyle: cell.italic ? "italic" : "normal",
|
|
5021
|
+
// Pine cell text never wraps; `\n` still breaks lines. Wrapping
|
|
5022
|
+
// used to collapse unicode sparklines and ━━━ dividers.
|
|
5023
|
+
whiteSpace: "pre"
|
|
4866
5024
|
});
|
|
4867
|
-
if (cell
|
|
4868
|
-
td.
|
|
5025
|
+
if (cell.width) td.style.width = `${cell.width / 100 * plotW}px`;
|
|
5026
|
+
if (cell.height) td.style.height = `${cell.height / 100 * paneH}px`;
|
|
5027
|
+
if (cell.tooltip) td.title = cell.tooltip;
|
|
5028
|
+
td.textContent = cell.text ?? "";
|
|
4869
5029
|
tr.appendChild(td);
|
|
4870
5030
|
}
|
|
4871
5031
|
table.appendChild(tr);
|
|
@@ -5750,8 +5910,14 @@ var WebGL2Backend = class {
|
|
|
5750
5910
|
return sc === pane.scale ? pane : { ...pane, scale: sc };
|
|
5751
5911
|
};
|
|
5752
5912
|
b.alpha = this.modelAlpha;
|
|
5753
|
-
for (const m of models) for (const bgSpan of m.backgrounds) this.emitBackground(b, bgSpan, pane, coords);
|
|
5754
|
-
for (const m of models) for (const f of m.fills) this.emitFill(b, m, f, effPane(m), coords, i0, i1, scene.offsetOf(m.id));
|
|
5913
|
+
for (const m of models) for (const bgSpan of m.backgrounds) if (bgSpan.overlay !== true) this.emitBackground(b, bgSpan, pane, coords);
|
|
5914
|
+
for (const m of models) for (const f of m.fills) if (f.overlay !== true) this.emitFill(b, m, f, effPane(m), coords, i0, i1, scene.offsetOf(m.id));
|
|
5915
|
+
if (isPrice) {
|
|
5916
|
+
for (const m of scene.indicators.values()) {
|
|
5917
|
+
for (const bgSpan of m.backgrounds) if (bgSpan.overlay === true) this.emitBackground(b, bgSpan, pane, coords);
|
|
5918
|
+
for (const f of m.fills) if (f.overlay === true) this.emitFill(b, m, f, pane, coords, i0, i1, scene.offsetOf(m.id));
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5755
5921
|
const drawCandles = isPrice && !scene.candlesHidden;
|
|
5756
5922
|
let candleDrawn = false;
|
|
5757
5923
|
for (const m of models) {
|
|
@@ -5765,7 +5931,7 @@ var WebGL2Backend = class {
|
|
|
5765
5931
|
b.alpha = this.modelAlpha;
|
|
5766
5932
|
const off = scene.offsetOf(m.id);
|
|
5767
5933
|
const mp = effPane(m);
|
|
5768
|
-
for (const s of m.series) this.emitSeries(b, s, mp, coords, i0, i1, theme, off);
|
|
5934
|
+
for (const s of m.series) if (s.overlay !== true) this.emitSeries(b, s, mp, coords, i0, i1, theme, off);
|
|
5769
5935
|
}
|
|
5770
5936
|
if (drawCandles && !candleDrawn) {
|
|
5771
5937
|
drawSlicesUpTo(scene.candleZ);
|
|
@@ -5774,6 +5940,12 @@ var WebGL2Backend = class {
|
|
|
5774
5940
|
}
|
|
5775
5941
|
drawSlicesUpTo(Infinity);
|
|
5776
5942
|
b.alpha = this.modelAlpha;
|
|
5943
|
+
if (isPrice) {
|
|
5944
|
+
for (const m of scene.indicators.values()) {
|
|
5945
|
+
const off = scene.offsetOf(m.id);
|
|
5946
|
+
for (const s of m.series) if (s.overlay === true) this.emitSeries(b, s, pane, coords, i0, i1, theme, off);
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5777
5949
|
for (const m of models) {
|
|
5778
5950
|
const mp = effPane(m);
|
|
5779
5951
|
for (const pl of m.priceLines) this.emitHline(b, pl, mp, coords, dataW, theme);
|
|
@@ -5820,7 +5992,7 @@ var WebGL2Backend = class {
|
|
|
5820
5992
|
for (const pane of scene.orderedPanes()) {
|
|
5821
5993
|
const b = this.glowBatch;
|
|
5822
5994
|
b.reset();
|
|
5823
|
-
this.emitGlowSources(b, scene
|
|
5995
|
+
this.emitGlowSources(b, scene, pane, coords, i0, i1);
|
|
5824
5996
|
if (b.vertexCount === 0) continue;
|
|
5825
5997
|
const topH = Math.round(pane.bounds.top * dpr * 0.5);
|
|
5826
5998
|
const botH = Math.round((pane.bounds.top + pane.bounds.height) * dpr * 0.5);
|
|
@@ -5889,15 +6061,23 @@ var WebGL2Backend = class {
|
|
|
5889
6061
|
gl.bindVertexArray(null);
|
|
5890
6062
|
return true;
|
|
5891
6063
|
}
|
|
5892
|
-
/** The "neon" elements that glow: line/area/step lines + point markers (not candles/fills/bars).
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
6064
|
+
/** The "neon" elements that glow: line/area/step lines + point markers (not candles/fills/bars).
|
|
6065
|
+
* Routing mirrors the main pass: own series per pane, force_overlay series on the price pane. */
|
|
6066
|
+
emitGlowSources(b, scene, pane, coords, i0, i1) {
|
|
6067
|
+
const emitOne = (s, off) => {
|
|
6068
|
+
if (!isLineLikeSeries(s) || s.visible === false) return;
|
|
6069
|
+
if (s.kind === "histogram" || s.kind === "columns") return;
|
|
6070
|
+
if (s.kind === "circles" || s.kind === "cross") this.emitPointMarkers(b, s, pane, coords, i0, i1, off);
|
|
6071
|
+
else this.emitPolyline(b, s, pane, coords, i0, i1, s.kind === "step", off);
|
|
6072
|
+
};
|
|
6073
|
+
for (const m of scene.indicatorsForPane(pane.id)) {
|
|
6074
|
+
const off = scene.offsetOf(m.id);
|
|
6075
|
+
for (const s of m.series) if (s.overlay !== true) emitOne(s, off);
|
|
6076
|
+
}
|
|
6077
|
+
if (pane.kind === "price") {
|
|
6078
|
+
for (const m of scene.indicators.values()) {
|
|
6079
|
+
const off = scene.offsetOf(m.id);
|
|
6080
|
+
for (const s of m.series) if (s.overlay === true) emitOne(s, off);
|
|
5901
6081
|
}
|
|
5902
6082
|
}
|
|
5903
6083
|
}
|
|
@@ -6180,9 +6360,9 @@ var WebGL2Backend = class {
|
|
|
6180
6360
|
b.alpha = this.candleBodyAlpha;
|
|
6181
6361
|
b.rect(g.bodyX, bodyTop, g.bodyW, bodyH, c);
|
|
6182
6362
|
}
|
|
6183
|
-
if (cs.borderVisible ||
|
|
6363
|
+
if (cs.borderVisible || fading && cs.bodyVisible) {
|
|
6184
6364
|
b.alpha = this.candleStructureAlpha;
|
|
6185
|
-
const bord = cs.borderVisible
|
|
6365
|
+
const bord = cs.borderVisible ? parseColor((isUp ? cs.borderUpColor : cs.borderDownColor) ?? bodyColorStr) : c;
|
|
6186
6366
|
const bw = Math.max(0, g.bodyW - 1);
|
|
6187
6367
|
const bh = Math.max(0, bodyH - 1);
|
|
6188
6368
|
b.rectStroke(g.bodyX + 0.5, bodyTop + 0.5, bw, bh, 1, bord);
|
|
@@ -7734,8 +7914,14 @@ var Canvas2dBackend = class {
|
|
|
7734
7914
|
return sc === pane.scale ? pane : { ...pane, scale: sc };
|
|
7735
7915
|
};
|
|
7736
7916
|
ctx.globalAlpha = this.modelAlpha;
|
|
7737
|
-
for (const m of models) for (const bg of m.backgrounds) this.drawBackground(ctx, bg, pane, coords);
|
|
7738
|
-
for (const m of models) for (const f of m.fills) this.drawFill(ctx, m, f, effPane(m), coords, i0, i1, scene.offsetOf(m.id));
|
|
7917
|
+
for (const m of models) for (const bg of m.backgrounds) if (bg.overlay !== true) this.drawBackground(ctx, bg, pane, coords);
|
|
7918
|
+
for (const m of models) for (const f of m.fills) if (f.overlay !== true) this.drawFill(ctx, m, f, effPane(m), coords, i0, i1, scene.offsetOf(m.id));
|
|
7919
|
+
if (isPrice) {
|
|
7920
|
+
for (const m of scene.indicators.values()) {
|
|
7921
|
+
for (const bg of m.backgrounds) if (bg.overlay === true) this.drawBackground(ctx, bg, pane, coords);
|
|
7922
|
+
for (const f of m.fills) if (f.overlay === true) this.drawFill(ctx, m, f, pane, coords, i0, i1, scene.offsetOf(m.id));
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7739
7925
|
const slices = scene.drawingSlices.get(pane.id) ?? [];
|
|
7740
7926
|
let si = 0;
|
|
7741
7927
|
const drawSlicesUpTo = (z) => {
|
|
@@ -7757,7 +7943,7 @@ var Canvas2dBackend = class {
|
|
|
7757
7943
|
ctx.globalAlpha = this.modelAlpha;
|
|
7758
7944
|
const off = scene.offsetOf(m.id);
|
|
7759
7945
|
const mp = effPane(m);
|
|
7760
|
-
for (const s of m.series) this.drawSeries(ctx, s, mp, coords, i0, i1, theme, off);
|
|
7946
|
+
for (const s of m.series) if (s.overlay !== true) this.drawSeries(ctx, s, mp, coords, i0, i1, theme, off);
|
|
7761
7947
|
}
|
|
7762
7948
|
if (drawCandles && !candleDrawn) {
|
|
7763
7949
|
drawSlicesUpTo(scene.candleZ);
|
|
@@ -7765,6 +7951,13 @@ var Canvas2dBackend = class {
|
|
|
7765
7951
|
this.drawPriceSeries(ctx, scene, i0, i1, coords, pane, theme, barColorMap, dataW);
|
|
7766
7952
|
}
|
|
7767
7953
|
drawSlicesUpTo(Infinity);
|
|
7954
|
+
if (isPrice) {
|
|
7955
|
+
ctx.globalAlpha = this.modelAlpha;
|
|
7956
|
+
for (const m of scene.indicators.values()) {
|
|
7957
|
+
const off = scene.offsetOf(m.id);
|
|
7958
|
+
for (const s of m.series) if (s.overlay === true) this.drawSeries(ctx, s, pane, coords, i0, i1, theme, off);
|
|
7959
|
+
}
|
|
7960
|
+
}
|
|
7768
7961
|
ctx.globalAlpha = this.modelAlpha;
|
|
7769
7962
|
for (const m of models) {
|
|
7770
7963
|
const mp = effPane(m);
|
|
@@ -8036,9 +8229,9 @@ var Canvas2dBackend = class {
|
|
|
8036
8229
|
ctx.fillStyle = color;
|
|
8037
8230
|
ctx.fillRect(g.bodyX, top, g.bodyW, bodyH);
|
|
8038
8231
|
}
|
|
8039
|
-
if (cs.borderVisible ||
|
|
8232
|
+
if (cs.borderVisible || fading && cs.bodyVisible) {
|
|
8040
8233
|
ctx.globalAlpha = this.candleStructureAlpha;
|
|
8041
|
-
ctx.strokeStyle = cs.borderVisible
|
|
8234
|
+
ctx.strokeStyle = cs.borderVisible ? (up ? cs.borderUpColor : cs.borderDownColor) ?? color : color;
|
|
8042
8235
|
ctx.lineWidth = 1;
|
|
8043
8236
|
const bw = Math.max(0, g.bodyW - 1);
|
|
8044
8237
|
const bh = Math.max(0, bodyH - 1);
|
|
@@ -8470,6 +8663,8 @@ var DrawingSceneRenderer = class {
|
|
|
8470
8663
|
this.set = set;
|
|
8471
8664
|
/** measureText width cache, keyed by `${font} ${text}`, persists across frames. */
|
|
8472
8665
|
this.widthCache = /* @__PURE__ */ new Map();
|
|
8666
|
+
/** Tooltip hit-rects captured while drawing the CURRENT set (rebuilt per render). */
|
|
8667
|
+
this.tipRegions = [];
|
|
8473
8668
|
/**
|
|
8474
8669
|
* Index offset of the CURRENT set's model: its `xloc:'bar_index'` coordinates count
|
|
8475
8670
|
* from the model's anchor bar, so they shift by this to land on chart logical indices.
|
|
@@ -8490,8 +8685,13 @@ var DrawingSceneRenderer = class {
|
|
|
8490
8685
|
const s = this.set;
|
|
8491
8686
|
return !s.lines.length && !s.boxes.length && !s.labels.length && !s.polylines.length && !s.linefills.length;
|
|
8492
8687
|
}
|
|
8688
|
+
/** Tooltip hit-rects of the labels drawn by the LAST `render` call (same coords as `ctx`). */
|
|
8689
|
+
labelTipRegions() {
|
|
8690
|
+
return this.tipRegions;
|
|
8691
|
+
}
|
|
8493
8692
|
/** Draw the whole set into `ctx` using the supplied coordinate closures. */
|
|
8494
8693
|
render(ctx, W, H, xOf, yOf) {
|
|
8694
|
+
this.tipRegions = [];
|
|
8495
8695
|
if (this.isEmpty()) return;
|
|
8496
8696
|
ctx.save();
|
|
8497
8697
|
this.drawLinefills(ctx, W, H, xOf, yOf);
|
|
@@ -8839,13 +9039,35 @@ var DrawingSceneRenderer = class {
|
|
|
8839
9039
|
if (this.isPointShape(lb.style)) {
|
|
8840
9040
|
if (!lb.noFill) this.drawLabelShape(ctx, lb.style, px, py, fontPx, color);
|
|
8841
9041
|
if (lb.text) this.drawLabelText(ctx, lb, px, py + fontPx, fontPx);
|
|
8842
|
-
|
|
8843
|
-
|
|
9042
|
+
if (lb.tooltip) {
|
|
9043
|
+
const r = Math.max(4, fontPx * 0.6) + 3;
|
|
9044
|
+
this.tipRegions.push({ left: px - r, top: py - r, right: px + r, bottom: py + r, text: lb.tooltip });
|
|
9045
|
+
}
|
|
9046
|
+
} else if (lb.style === "none" || lb.style === "text_outline") {
|
|
9047
|
+
if (lb.text) {
|
|
9048
|
+
this.drawLabelText(ctx, lb, px, py, fontPx, lb.style === "text_outline");
|
|
9049
|
+
if (lb.tooltip) this.tipRegions.push(this.textRegion(ctx, lb, px, py, fontPx, lb.tooltip));
|
|
9050
|
+
}
|
|
8844
9051
|
} else {
|
|
8845
|
-
this.drawBubble(ctx, lb, px, py, fontPx, color);
|
|
9052
|
+
const r = this.drawBubble(ctx, lb, px, py, fontPx, color);
|
|
9053
|
+
if (lb.tooltip) this.tipRegions.push({ left: r.x, top: r.y, right: r.x + r.w, bottom: r.y + r.h, text: lb.tooltip });
|
|
8846
9054
|
}
|
|
8847
9055
|
}
|
|
8848
9056
|
}
|
|
9057
|
+
/** Canvas font for a label's text — family + Pine `text_formatting` (bold/italic). */
|
|
9058
|
+
labelFont(lb, fontPx) {
|
|
9059
|
+
const family = lb.fontFamily === "monospace" ? "monospace" : this.deps.theme.fontFamily || "sans-serif";
|
|
9060
|
+
return `${lb.italic ? "italic " : ""}${lb.bold ? "bold " : ""}${fontPx}px ${family}`;
|
|
9061
|
+
}
|
|
9062
|
+
/** Hover rect of a text-only label (style none/text_outline/noFill), centered like drawLabelText. */
|
|
9063
|
+
textRegion(ctx, lb, cx, cy, fontPx, text) {
|
|
9064
|
+
const font = this.labelFont(lb, fontPx);
|
|
9065
|
+
const lines = (lb.text ?? "").split("\n");
|
|
9066
|
+
const w = Math.max(1, ...lines.map((l) => this.measure(ctx, font, l)));
|
|
9067
|
+
const h = fontPx * 1.25 * lines.length;
|
|
9068
|
+
const left = lb.textAlign === "left" ? cx : lb.textAlign === "right" ? cx - w : cx - w / 2;
|
|
9069
|
+
return { left, top: cy - h / 2, right: left + w, bottom: cy + h / 2, text };
|
|
9070
|
+
}
|
|
8849
9071
|
isPointShape(style) {
|
|
8850
9072
|
switch (style) {
|
|
8851
9073
|
case "circle":
|
|
@@ -8864,8 +9086,7 @@ var DrawingSceneRenderer = class {
|
|
|
8864
9086
|
}
|
|
8865
9087
|
}
|
|
8866
9088
|
drawLabelText(ctx, lb, cx, cy, fontPx, outline = false) {
|
|
8867
|
-
|
|
8868
|
-
ctx.font = `${fontPx}px ${family}`;
|
|
9089
|
+
ctx.font = this.labelFont(lb, fontPx);
|
|
8869
9090
|
ctx.textAlign = lb.textAlign === "left" ? "left" : lb.textAlign === "right" ? "right" : "center";
|
|
8870
9091
|
ctx.textBaseline = "middle";
|
|
8871
9092
|
const lines = lb.text.split("\n");
|
|
@@ -8945,9 +9166,9 @@ var DrawingSceneRenderer = class {
|
|
|
8945
9166
|
ctx.fill();
|
|
8946
9167
|
}
|
|
8947
9168
|
}
|
|
9169
|
+
/** Draw a bubble label; returns the bubble body rect (for tooltip hit-testing). */
|
|
8948
9170
|
drawBubble(ctx, lb, px, py, fontPx, color) {
|
|
8949
|
-
|
|
8950
|
-
ctx.font = `${fontPx}px ${family}`;
|
|
9171
|
+
ctx.font = this.labelFont(lb, fontPx);
|
|
8951
9172
|
const lines = (lb.text ?? "").split("\n");
|
|
8952
9173
|
const padX = 6;
|
|
8953
9174
|
const padY = 4;
|
|
@@ -9005,38 +9226,51 @@ var DrawingSceneRenderer = class {
|
|
|
9005
9226
|
by = py - h - ptr;
|
|
9006
9227
|
pointer = "down";
|
|
9007
9228
|
}
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
if (pointer !== "none") {
|
|
9012
|
-
ctx.beginPath();
|
|
9013
|
-
if (pointer === "down") {
|
|
9014
|
-
ctx.moveTo(px - ptr, by + h);
|
|
9015
|
-
ctx.lineTo(px + ptr, by + h);
|
|
9016
|
-
ctx.lineTo(px, py);
|
|
9017
|
-
} else if (pointer === "up") {
|
|
9018
|
-
ctx.moveTo(px - ptr, by);
|
|
9019
|
-
ctx.lineTo(px + ptr, by);
|
|
9020
|
-
ctx.lineTo(px, py);
|
|
9021
|
-
} else if (pointer === "left") {
|
|
9022
|
-
ctx.moveTo(bx, py - ptr);
|
|
9023
|
-
ctx.lineTo(bx, py + ptr);
|
|
9024
|
-
ctx.lineTo(px, py);
|
|
9025
|
-
} else {
|
|
9026
|
-
ctx.moveTo(bx + w, py - ptr);
|
|
9027
|
-
ctx.lineTo(bx + w, py + ptr);
|
|
9028
|
-
ctx.lineTo(px, py);
|
|
9029
|
-
}
|
|
9030
|
-
ctx.closePath();
|
|
9229
|
+
if (!lb.noFill) {
|
|
9230
|
+
ctx.fillStyle = color;
|
|
9231
|
+
this.roundRect(ctx, bx, by, w, h, 4);
|
|
9031
9232
|
ctx.fill();
|
|
9233
|
+
if (pointer !== "none") {
|
|
9234
|
+
ctx.beginPath();
|
|
9235
|
+
if (pointer === "down") {
|
|
9236
|
+
ctx.moveTo(px - ptr, by + h);
|
|
9237
|
+
ctx.lineTo(px + ptr, by + h);
|
|
9238
|
+
ctx.lineTo(px, py);
|
|
9239
|
+
} else if (pointer === "up") {
|
|
9240
|
+
ctx.moveTo(px - ptr, by);
|
|
9241
|
+
ctx.lineTo(px + ptr, by);
|
|
9242
|
+
ctx.lineTo(px, py);
|
|
9243
|
+
} else if (pointer === "left") {
|
|
9244
|
+
ctx.moveTo(bx, py - ptr);
|
|
9245
|
+
ctx.lineTo(bx, py + ptr);
|
|
9246
|
+
ctx.lineTo(px, py);
|
|
9247
|
+
} else {
|
|
9248
|
+
ctx.moveTo(bx + w, py - ptr);
|
|
9249
|
+
ctx.lineTo(bx + w, py + ptr);
|
|
9250
|
+
ctx.lineTo(px, py);
|
|
9251
|
+
}
|
|
9252
|
+
ctx.closePath();
|
|
9253
|
+
ctx.fill();
|
|
9254
|
+
}
|
|
9032
9255
|
}
|
|
9033
9256
|
if (lb.text) {
|
|
9034
|
-
ctx.fillStyle = lb.textColor ?? contrastColor(color);
|
|
9035
|
-
ctx.textAlign = "center";
|
|
9257
|
+
ctx.fillStyle = lb.textColor ?? (lb.noFill ? this.deps.theme.textColor : contrastColor(color));
|
|
9036
9258
|
ctx.textBaseline = "middle";
|
|
9259
|
+
let tx;
|
|
9260
|
+
if (lb.textAlign === "left") {
|
|
9261
|
+
ctx.textAlign = "left";
|
|
9262
|
+
tx = bx + padX;
|
|
9263
|
+
} else if (lb.textAlign === "right") {
|
|
9264
|
+
ctx.textAlign = "right";
|
|
9265
|
+
tx = bx + w - padX;
|
|
9266
|
+
} else {
|
|
9267
|
+
ctx.textAlign = "center";
|
|
9268
|
+
tx = bx + w / 2;
|
|
9269
|
+
}
|
|
9037
9270
|
const startY = by + padY + lineH / 2;
|
|
9038
|
-
for (let i = 0; i < lines.length; i += 1) ctx.fillText(lines[i],
|
|
9271
|
+
for (let i = 0; i < lines.length; i += 1) ctx.fillText(lines[i], tx, startY + i * lineH);
|
|
9039
9272
|
}
|
|
9273
|
+
return { x: bx, y: by, w, h };
|
|
9040
9274
|
}
|
|
9041
9275
|
roundRect(ctx, x, y, w, h, r) {
|
|
9042
9276
|
const rr = Math.min(r, w / 2, h / 2);
|
|
@@ -9274,6 +9508,8 @@ var ChromeRenderer = class {
|
|
|
9274
9508
|
this.axisTextColor = DARK_THEME.textColor;
|
|
9275
9509
|
// Shared Pine-drawing renderer (line/box/label/polyline/linefill); widthCache persists.
|
|
9276
9510
|
this.drawScene = new DrawingSceneRenderer({ timeToLogical: () => 0, barAt: () => null, theme: {} });
|
|
9511
|
+
// Tooltip hit-rects of every label drawn this frame, in plot coords (rebuilt per render).
|
|
9512
|
+
this.labelTips = [];
|
|
9277
9513
|
}
|
|
9278
9514
|
mount(canvas) {
|
|
9279
9515
|
this.canvas = canvas;
|
|
@@ -9315,6 +9551,7 @@ var ChromeRenderer = class {
|
|
|
9315
9551
|
const dataW = coords.width;
|
|
9316
9552
|
const dataH = coords.height;
|
|
9317
9553
|
this.axisTextColor = surface?.textColor ?? theme.textColor;
|
|
9554
|
+
this.labelTips = [];
|
|
9318
9555
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
9319
9556
|
ctx.clearRect(0, 0, fullW, fullH);
|
|
9320
9557
|
if (surface && (fullW > dataW || fullH > dataH)) {
|
|
@@ -9424,6 +9661,17 @@ var ChromeRenderer = class {
|
|
|
9424
9661
|
(price) => coords.priceToY(price, pane.scale, pane.bounds) - pane.bounds.top
|
|
9425
9662
|
);
|
|
9426
9663
|
ctx.restore();
|
|
9664
|
+
for (const r of this.drawScene.labelTipRegions()) {
|
|
9665
|
+
this.labelTips.push({ ...r, top: r.top + pane.bounds.top, bottom: r.bottom + pane.bounds.top });
|
|
9666
|
+
}
|
|
9667
|
+
}
|
|
9668
|
+
/** Tooltip of the topmost label under a plot-space point, or null. Fed by the last render. */
|
|
9669
|
+
labelTooltipAt(x, y) {
|
|
9670
|
+
for (let i = this.labelTips.length - 1; i >= 0; i -= 1) {
|
|
9671
|
+
const r = this.labelTips[i];
|
|
9672
|
+
if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) return r.text;
|
|
9673
|
+
}
|
|
9674
|
+
return null;
|
|
9427
9675
|
}
|
|
9428
9676
|
// ── axes ──
|
|
9429
9677
|
drawPriceAxes(ctx, scene, coords, theme, dataW, panes) {
|
|
@@ -9640,6 +9888,68 @@ function formatCountdown(ms) {
|
|
|
9640
9888
|
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
|
|
9641
9889
|
}
|
|
9642
9890
|
|
|
9891
|
+
// src/renderers/native/chrome/LabelTooltip.ts
|
|
9892
|
+
var HOVER_DELAY_MS = 350;
|
|
9893
|
+
var LabelTooltip = class {
|
|
9894
|
+
constructor(plot, deps) {
|
|
9895
|
+
this.plot = plot;
|
|
9896
|
+
this.deps = deps;
|
|
9897
|
+
this.tip = null;
|
|
9898
|
+
this.timer = null;
|
|
9899
|
+
/** Text of the currently open OR armed tip — dedupes moves inside one label. */
|
|
9900
|
+
this.current = null;
|
|
9901
|
+
this.onMove = (e) => {
|
|
9902
|
+
if (e.pointerType !== "mouse") return;
|
|
9903
|
+
const rect = this.plot.getBoundingClientRect();
|
|
9904
|
+
const x = e.clientX - rect.left;
|
|
9905
|
+
const y = e.clientY - rect.top;
|
|
9906
|
+
const text = this.deps.lookup(x, y);
|
|
9907
|
+
if (!text) {
|
|
9908
|
+
this.clear();
|
|
9909
|
+
return;
|
|
9910
|
+
}
|
|
9911
|
+
if (text === this.current) return;
|
|
9912
|
+
this.clear();
|
|
9913
|
+
this.current = text;
|
|
9914
|
+
this.timer = window.setTimeout(() => this.show(text, x, y), HOVER_DELAY_MS);
|
|
9915
|
+
};
|
|
9916
|
+
this.onLeave = () => {
|
|
9917
|
+
this.clear();
|
|
9918
|
+
};
|
|
9919
|
+
plot.addEventListener("pointermove", this.onMove);
|
|
9920
|
+
plot.addEventListener("pointerleave", this.onLeave);
|
|
9921
|
+
plot.addEventListener("pointerdown", this.onLeave);
|
|
9922
|
+
}
|
|
9923
|
+
destroy() {
|
|
9924
|
+
this.plot.removeEventListener("pointermove", this.onMove);
|
|
9925
|
+
this.plot.removeEventListener("pointerleave", this.onLeave);
|
|
9926
|
+
this.plot.removeEventListener("pointerdown", this.onLeave);
|
|
9927
|
+
this.clear();
|
|
9928
|
+
}
|
|
9929
|
+
show(text, x, y) {
|
|
9930
|
+
const doc = this.plot.ownerDocument;
|
|
9931
|
+
const tip = doc.createElement("div");
|
|
9932
|
+
tip.textContent = text;
|
|
9933
|
+
tip.style.cssText = "position:absolute;z-index:var(--vela-z-tooltip);pointer-events:none;background:var(--vela-bg);border:1px solid var(--vela-border);color:var(--vela-fg);border-radius:var(--vela-radius-md);padding:4px 9px;box-shadow:var(--vela-shadow);font:var(--vela-font-size-md) var(--vela-font);max-width:260px;";
|
|
9934
|
+
applyChromeTokens(tip, this.deps.theme());
|
|
9935
|
+
this.plot.appendChild(tip);
|
|
9936
|
+
const pw = this.plot.clientWidth;
|
|
9937
|
+
const ph = this.plot.clientHeight;
|
|
9938
|
+
tip.style.left = `${Math.max(0, Math.min(x + 12, pw - tip.offsetWidth - 4))}px`;
|
|
9939
|
+
tip.style.top = `${Math.max(0, Math.min(y + 16, ph - tip.offsetHeight - 4))}px`;
|
|
9940
|
+
this.tip = tip;
|
|
9941
|
+
}
|
|
9942
|
+
clear() {
|
|
9943
|
+
if (this.timer !== null) {
|
|
9944
|
+
clearTimeout(this.timer);
|
|
9945
|
+
this.timer = null;
|
|
9946
|
+
}
|
|
9947
|
+
this.tip?.remove();
|
|
9948
|
+
this.tip = null;
|
|
9949
|
+
this.current = null;
|
|
9950
|
+
}
|
|
9951
|
+
};
|
|
9952
|
+
|
|
9643
9953
|
// src/renderers/native/chrome/CrosshairRenderer.ts
|
|
9644
9954
|
var CrosshairRenderer = class {
|
|
9645
9955
|
constructor() {
|
|
@@ -14717,19 +15027,8 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
14717
15027
|
for (const model of models) {
|
|
14718
15028
|
const off = offsetOf(model.id);
|
|
14719
15029
|
for (const s of model.series) {
|
|
14720
|
-
if (s.
|
|
14721
|
-
|
|
14722
|
-
const b = s.bars[i - off];
|
|
14723
|
-
if (b) {
|
|
14724
|
-
consider(b.high);
|
|
14725
|
-
consider(b.low);
|
|
14726
|
-
}
|
|
14727
|
-
}
|
|
14728
|
-
} else if (isLineLikeSeries(s)) {
|
|
14729
|
-
for (let i = i0; i <= i1; i += 1) consider(s.points[i - off]?.value);
|
|
14730
|
-
if (s.kind === "histogram" || s.kind === "columns") consider(s.style?.base ?? 0);
|
|
14731
|
-
else if (s.style?.base != null) consider(s.style.base);
|
|
14732
|
-
}
|
|
15030
|
+
if (s.overlay === true) continue;
|
|
15031
|
+
considerSeries(s, i0, i1, off, consider);
|
|
14733
15032
|
}
|
|
14734
15033
|
for (const pl of model.priceLines) consider(pl.price);
|
|
14735
15034
|
}
|
|
@@ -14751,6 +15050,36 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
14751
15050
|
const span = max - min;
|
|
14752
15051
|
return { min: min - span * MARGIN_BOTTOM, max: max + span * MARGIN_TOP };
|
|
14753
15052
|
}
|
|
15053
|
+
function considerSeries(s, i0, i1, off, consider) {
|
|
15054
|
+
if (s.kind === "candle" || s.kind === "bar") {
|
|
15055
|
+
for (let i = i0; i <= i1; i += 1) {
|
|
15056
|
+
const b = s.bars[i - off];
|
|
15057
|
+
if (b) {
|
|
15058
|
+
consider(b.high);
|
|
15059
|
+
consider(b.low);
|
|
15060
|
+
}
|
|
15061
|
+
}
|
|
15062
|
+
} else if (isLineLikeSeries(s)) {
|
|
15063
|
+
for (let i = i0; i <= i1; i += 1) consider(s.points[i - off]?.value);
|
|
15064
|
+
if (s.kind === "histogram" || s.kind === "columns") consider(s.style?.base ?? 0);
|
|
15065
|
+
else if (s.style?.base != null) consider(s.style.base);
|
|
15066
|
+
}
|
|
15067
|
+
}
|
|
15068
|
+
function overlaySeriesRange(models, i0, i1, offsetOf = () => 0) {
|
|
15069
|
+
let min = Infinity;
|
|
15070
|
+
let max = -Infinity;
|
|
15071
|
+
const consider = (v) => {
|
|
15072
|
+
if (v != null && Number.isFinite(v)) {
|
|
15073
|
+
if (v < min) min = v;
|
|
15074
|
+
if (v > max) max = v;
|
|
15075
|
+
}
|
|
15076
|
+
};
|
|
15077
|
+
for (const model of models) {
|
|
15078
|
+
const off = offsetOf(model.id);
|
|
15079
|
+
for (const s of model.series) if (s.overlay === true) considerSeries(s, i0, i1, off, consider);
|
|
15080
|
+
}
|
|
15081
|
+
return min === Infinity ? null : { min, max };
|
|
15082
|
+
}
|
|
14754
15083
|
function expandScaleByPixels(scale, heightPx, abovePx, belowPx) {
|
|
14755
15084
|
if (abovePx <= 0 && belowPx <= 0) return scale;
|
|
14756
15085
|
const content = heightPx - abovePx - belowPx;
|
|
@@ -14805,6 +15134,11 @@ function resizeSplit(split, dyTotal, minPx = MIN_PANE_PX) {
|
|
|
14805
15134
|
}
|
|
14806
15135
|
|
|
14807
15136
|
// src/renderers/native/backdrop/BackdropRenderer.ts
|
|
15137
|
+
function clipHighlightRect(x1, x2, left, right) {
|
|
15138
|
+
const x = Math.max(left, x1);
|
|
15139
|
+
const end = Math.min(right, x2);
|
|
15140
|
+
return end > x ? { x, width: end - x } : null;
|
|
15141
|
+
}
|
|
14808
15142
|
var BackdropRenderer = class {
|
|
14809
15143
|
constructor() {
|
|
14810
15144
|
this.canvas = null;
|
|
@@ -14837,17 +15171,22 @@ var BackdropRenderer = class {
|
|
|
14837
15171
|
/** Renderer-owned session highlight bands: full-height (all panes), behind the grid.
|
|
14838
15172
|
* Session-zone washes (pre/post-market) paint first, host highlights on top. */
|
|
14839
15173
|
drawHighlights(ctx, scene, coords) {
|
|
14840
|
-
const
|
|
14841
|
-
if (
|
|
15174
|
+
const sessions = scene.sessionHighlightBands();
|
|
15175
|
+
if (sessions.length > 0) {
|
|
15176
|
+
const left = Math.max(0, coords.logicalToX(-0.5));
|
|
15177
|
+
const right = Math.min(coords.width, coords.logicalToX(coords.barCount - 0.5));
|
|
15178
|
+
this.drawHighlightSet(ctx, sessions, coords, left, right);
|
|
15179
|
+
}
|
|
15180
|
+
this.drawHighlightSet(ctx, scene.highlights, coords, 0, coords.width);
|
|
15181
|
+
}
|
|
15182
|
+
drawHighlightSet(ctx, bands, coords, left, right) {
|
|
14842
15183
|
for (const band of bands) {
|
|
14843
15184
|
const x1 = coords.timeToX(band.from);
|
|
14844
15185
|
const x2 = coords.timeToX(band.to);
|
|
14845
|
-
|
|
14846
|
-
|
|
14847
|
-
const cw = Math.min(coords.width, x2) - cx;
|
|
14848
|
-
if (cw <= 0) continue;
|
|
15186
|
+
const rect = clipHighlightRect(x1, x2, left, right);
|
|
15187
|
+
if (!rect) continue;
|
|
14849
15188
|
ctx.fillStyle = band.color;
|
|
14850
|
-
ctx.fillRect(
|
|
15189
|
+
ctx.fillRect(rect.x, 0, rect.width, coords.height);
|
|
14851
15190
|
}
|
|
14852
15191
|
}
|
|
14853
15192
|
// ── grid ── vert/horz gate on `scene.showGrid` AND their own per-axis visibility
|
|
@@ -15497,6 +15836,8 @@ var NativeRenderer = class {
|
|
|
15497
15836
|
this.glowAmount = 0;
|
|
15498
15837
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
15499
15838
|
this.chrome = new ChromeRenderer();
|
|
15839
|
+
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
15840
|
+
this.labelTooltip = null;
|
|
15500
15841
|
this.crosshairLayer = new CrosshairRenderer();
|
|
15501
15842
|
/** 1 Hz repaint pump so the price-axis countdown-to-bar-close ticks; null when off. */
|
|
15502
15843
|
this.countdownTimer = null;
|
|
@@ -15507,6 +15848,8 @@ var NativeRenderer = class {
|
|
|
15507
15848
|
this.indicatorValuesOn = true;
|
|
15508
15849
|
/** Host-contributed legend actions — held here so a rebuild of the legend re-wires them. */
|
|
15509
15850
|
this.legendActionsProvider = null;
|
|
15851
|
+
/** Host-contributed legend callouts — held here so a rebuild of the legend re-wires them. */
|
|
15852
|
+
this.legendCalloutsProvider = null;
|
|
15510
15853
|
/** Host override of the legend's fold toggle — held here so a remount re-applies it. */
|
|
15511
15854
|
this.legendOverviewAction = null;
|
|
15512
15855
|
// ── keyboard navigation / accessibility (item 11) ──
|
|
@@ -16615,6 +16958,10 @@ var NativeRenderer = class {
|
|
|
16615
16958
|
this.plot.appendChild(this.scrollButton);
|
|
16616
16959
|
this.plot.addEventListener("pointermove", this.onScrollProximityMove);
|
|
16617
16960
|
this.plot.addEventListener("pointerleave", this.onScrollProximityLeave);
|
|
16961
|
+
this.labelTooltip = new LabelTooltip(this.plot, {
|
|
16962
|
+
theme: () => this.chromeTheme(),
|
|
16963
|
+
lookup: (x, y) => this.chrome.labelTooltipAt(x, y)
|
|
16964
|
+
});
|
|
16618
16965
|
this.userDrawings = new UserDrawingController(this.wrapper, this.plot, this.drawingsCanvas, {
|
|
16619
16966
|
projector: () => this.drawingProjector(),
|
|
16620
16967
|
dpr: () => this.coords.dpr,
|
|
@@ -16654,9 +17001,10 @@ var NativeRenderer = class {
|
|
|
16654
17001
|
this.inputsUI.setDialogHost(this.dialogHost);
|
|
16655
17002
|
this.inputsUI.setSymbolPicker(this.symbolPicker);
|
|
16656
17003
|
this.inputsUI.setLegendActions(this.legendActionsProvider);
|
|
17004
|
+
this.inputsUI.setLegendCallouts(this.legendCalloutsProvider);
|
|
16657
17005
|
this.inputsUI.setLegendOverviewAction(this.legendOverviewAction);
|
|
16658
17006
|
this.inputsUI.setOnChange((c) => {
|
|
16659
|
-
for (const cb of this.inputChangeCbs) cb({ indicatorId: c.indicatorId, key: c.key, value: c.value });
|
|
17007
|
+
for (const cb of this.inputChangeCbs) cb({ indicatorId: c.indicatorId, key: c.key, value: c.value, ...c.kind ? { kind: c.kind } : {} });
|
|
16660
17008
|
});
|
|
16661
17009
|
this.inputsUI.setOnRemove((id) => {
|
|
16662
17010
|
for (const cb of this.removeIndicatorCbs) cb(id);
|
|
@@ -16846,6 +17194,8 @@ var NativeRenderer = class {
|
|
|
16846
17194
|
this.settingsButton = null;
|
|
16847
17195
|
this.plot?.removeEventListener("pointermove", this.onScrollProximityMove);
|
|
16848
17196
|
this.plot?.removeEventListener("pointerleave", this.onScrollProximityLeave);
|
|
17197
|
+
this.labelTooltip?.destroy();
|
|
17198
|
+
this.labelTooltip = null;
|
|
16849
17199
|
this.scrollButton?.remove();
|
|
16850
17200
|
this.scrollButton = null;
|
|
16851
17201
|
for (const l of this.extLayers) l.instance.destroy?.();
|
|
@@ -17086,7 +17436,7 @@ var NativeRenderer = class {
|
|
|
17086
17436
|
else this.scene.assignIndicatorZ(model.id);
|
|
17087
17437
|
this.inputsUI.upsert(model.id, model.shorttitle ?? model.title, model.inputs, model.inputValues, model.paneId, {
|
|
17088
17438
|
native: !!model.native,
|
|
17089
|
-
...model.
|
|
17439
|
+
...model.props ? { props: model.props, propValues: model.propValues ?? {} } : {}
|
|
17090
17440
|
});
|
|
17091
17441
|
this.syncTables(model);
|
|
17092
17442
|
if (model.native?.type === "volume") {
|
|
@@ -17137,8 +17487,8 @@ var NativeRenderer = class {
|
|
|
17137
17487
|
this.paneControls?.refresh();
|
|
17138
17488
|
this.scheduler.invalidate(4 /* Full */);
|
|
17139
17489
|
}
|
|
17140
|
-
setIndicatorInputs(handle, values) {
|
|
17141
|
-
this.inputsUI.setValues(handle.id, values);
|
|
17490
|
+
setIndicatorInputs(handle, values, props) {
|
|
17491
|
+
this.inputsUI.setValues(handle.id, values, props);
|
|
17142
17492
|
}
|
|
17143
17493
|
setSymbolPicker(picker) {
|
|
17144
17494
|
this.symbolPicker = picker;
|
|
@@ -17148,6 +17498,10 @@ var NativeRenderer = class {
|
|
|
17148
17498
|
this.legendActionsProvider = provider;
|
|
17149
17499
|
this.inputsUI?.setLegendActions(provider);
|
|
17150
17500
|
}
|
|
17501
|
+
setLegendCallouts(provider) {
|
|
17502
|
+
this.legendCalloutsProvider = provider;
|
|
17503
|
+
this.inputsUI?.setLegendCallouts(provider);
|
|
17504
|
+
}
|
|
17151
17505
|
setLegendOverviewAction(action) {
|
|
17152
17506
|
this.legendOverviewAction = action;
|
|
17153
17507
|
this.inputsUI?.setLegendOverviewAction(action);
|
|
@@ -18128,7 +18482,11 @@ var NativeRenderer = class {
|
|
|
18128
18482
|
}
|
|
18129
18483
|
const models = this.scene.indicatorsForPane(pane.id);
|
|
18130
18484
|
const masterModels = models.filter((m) => m.ownScale !== true);
|
|
18131
|
-
|
|
18485
|
+
let dr = this.chrome.paneDrawingsRange(masterModels, this.scene, pane === pricePane, vr);
|
|
18486
|
+
if (pane === pricePane) {
|
|
18487
|
+
const or = overlaySeriesRange(this.scene.indicators.values(), i0, i1, (id) => this.scene.offsetOf(id));
|
|
18488
|
+
if (or) dr = dr ? { min: Math.min(dr.min, or.min), max: Math.max(dr.max, or.max) } : or;
|
|
18489
|
+
}
|
|
18132
18490
|
const includeCandles = pane.kind === "price" && !this.scene.candlesHidden;
|
|
18133
18491
|
pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
18134
18492
|
pane.percentBaseline = pane.kind === "price" ? this.bars[i0]?.close ?? 0 : this.firstVisibleValue(masterModels, i0);
|
|
@@ -18402,6 +18760,7 @@ var NativeRenderer = class {
|
|
|
18402
18760
|
for (const m of models) {
|
|
18403
18761
|
const off = this.scene.offsetOf(m.id);
|
|
18404
18762
|
for (const s of m.series) {
|
|
18763
|
+
if (s.overlay === true) continue;
|
|
18405
18764
|
if (isLineLikeSeries(s)) {
|
|
18406
18765
|
const v = s.points[i0 - off]?.value;
|
|
18407
18766
|
if (v != null && Number.isFinite(v)) return v;
|
|
@@ -18782,6 +19141,7 @@ var IndicatorHandleImpl = class {
|
|
|
18782
19141
|
constructor(id, title, controller, source) {
|
|
18783
19142
|
this.controller = controller;
|
|
18784
19143
|
this.schema = [];
|
|
19144
|
+
this.propsSchema = [];
|
|
18785
19145
|
this.visibleState = true;
|
|
18786
19146
|
this.bus = new TypedEventBus();
|
|
18787
19147
|
this.id = id;
|
|
@@ -18791,6 +19151,9 @@ var IndicatorHandleImpl = class {
|
|
|
18791
19151
|
get inputs() {
|
|
18792
19152
|
return this.schema;
|
|
18793
19153
|
}
|
|
19154
|
+
get props() {
|
|
19155
|
+
return this.propsSchema;
|
|
19156
|
+
}
|
|
18794
19157
|
get visible() {
|
|
18795
19158
|
return this.visibleState;
|
|
18796
19159
|
}
|
|
@@ -18800,6 +19163,12 @@ var IndicatorHandleImpl = class {
|
|
|
18800
19163
|
setInputs(values) {
|
|
18801
19164
|
this.controller.applyInputs(this.id, values);
|
|
18802
19165
|
}
|
|
19166
|
+
setProp(key, value) {
|
|
19167
|
+
this.controller.applyProps(this.id, { [key]: value });
|
|
19168
|
+
}
|
|
19169
|
+
setProps(values) {
|
|
19170
|
+
this.controller.applyProps(this.id, values);
|
|
19171
|
+
}
|
|
18803
19172
|
setVisible(visible) {
|
|
18804
19173
|
this.controller.setVisible(this.id, visible);
|
|
18805
19174
|
}
|
|
@@ -18819,6 +19188,9 @@ var IndicatorHandleImpl = class {
|
|
|
18819
19188
|
setSchema(schema) {
|
|
18820
19189
|
this.schema = schema;
|
|
18821
19190
|
}
|
|
19191
|
+
setPropsSchema(schema) {
|
|
19192
|
+
this.propsSchema = schema;
|
|
19193
|
+
}
|
|
18822
19194
|
/** Sync the public `visible` getter when the orchestrator changes visibility (API or legend eye). */
|
|
18823
19195
|
setVisibleState(visible) {
|
|
18824
19196
|
this.visibleState = visible;
|
|
@@ -18832,6 +19204,8 @@ var IndicatorHandleImpl = class {
|
|
|
18832
19204
|
function summarizeModel(model) {
|
|
18833
19205
|
const series = {};
|
|
18834
19206
|
for (const s of model.series) series[s.kind] = (series[s.kind] ?? 0) + 1;
|
|
19207
|
+
const countOverlay = (items) => items?.filter((i) => i.overlay === true).length ?? 0;
|
|
19208
|
+
const forcedOverlay = countOverlay(model.series) + countOverlay(model.fills) + countOverlay(model.backgrounds) + countOverlay(model.lines) + countOverlay(model.boxes) + countOverlay(model.labels) + countOverlay(model.polylines) + countOverlay(model.linefills) + countOverlay(model.tables);
|
|
18835
19209
|
return {
|
|
18836
19210
|
id: model.id,
|
|
18837
19211
|
title: model.title,
|
|
@@ -18851,7 +19225,8 @@ function summarizeModel(model) {
|
|
|
18851
19225
|
tables: model.tables?.length ?? 0,
|
|
18852
19226
|
barColors: model.barColors?.length ?? 0,
|
|
18853
19227
|
trades: model.trades?.length ?? 0,
|
|
18854
|
-
inputs: model.inputs.length
|
|
19228
|
+
inputs: model.inputs.length,
|
|
19229
|
+
forcedOverlay
|
|
18855
19230
|
};
|
|
18856
19231
|
}
|
|
18857
19232
|
function inspectModels(models) {
|
|
@@ -18872,7 +19247,8 @@ function inspectModels(models) {
|
|
|
18872
19247
|
linefills: sum((s) => s.linefills),
|
|
18873
19248
|
tables: sum((s) => s.tables),
|
|
18874
19249
|
barColors: sum((s) => s.barColors),
|
|
18875
|
-
trades: sum((s) => s.trades)
|
|
19250
|
+
trades: sum((s) => s.trades),
|
|
19251
|
+
forcedOverlay: sum((s) => s.forcedOverlay)
|
|
18876
19252
|
}
|
|
18877
19253
|
};
|
|
18878
19254
|
}
|
|
@@ -19016,7 +19392,9 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19016
19392
|
for (const engine of engines) this.registerEngine(engine.language, engine);
|
|
19017
19393
|
this.defaultLanguage = config.defaultLanguage;
|
|
19018
19394
|
this.renderer.mount(container, config.theme);
|
|
19019
|
-
this.renderer.onInputChange(
|
|
19395
|
+
this.renderer.onInputChange(
|
|
19396
|
+
(e) => e.kind === "prop" ? this.applyProps(e.indicatorId, { [e.key]: e.value }) : this.applyInputs(e.indicatorId, { [e.key]: e.value })
|
|
19397
|
+
);
|
|
19020
19398
|
this.renderer.onRemoveIndicator((id) => this.removeIndicator(id));
|
|
19021
19399
|
this.renderer.onToggleIndicatorVisible?.((id, visible) => this.setVisible(id, visible));
|
|
19022
19400
|
this.paneActionUnsub = this.renderer.onPaneAction?.((a) => this.handlePaneAction(a)) ?? null;
|
|
@@ -19696,7 +20074,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19696
20074
|
const title = options.title ?? "Indicator";
|
|
19697
20075
|
const handle = new IndicatorHandleImpl(id, title, this, source);
|
|
19698
20076
|
this.handles.set(id, handle);
|
|
19699
|
-
this.registry.add({ id, title, source, options, inputValues: { ...options.inputs ?? {} } });
|
|
20077
|
+
this.registry.add({ id, title, source, options, inputValues: { ...options.inputs ?? {} }, propValues: { ...options.props ?? {} } });
|
|
19700
20078
|
void this.startIndicator(id, source, options, handle);
|
|
19701
20079
|
return handle;
|
|
19702
20080
|
}
|
|
@@ -19719,7 +20097,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19719
20097
|
return handle;
|
|
19720
20098
|
}
|
|
19721
20099
|
const inputValues = { ...descriptor.defaultInputs(), ...options.inputs ?? {} };
|
|
19722
|
-
this.registry.add({ id, title, source: type, inputValues, native: { type, instance: descriptor.create(), descriptor } });
|
|
20100
|
+
this.registry.add({ id, title, source: type, inputValues, propValues: {}, native: { type, instance: descriptor.create(), descriptor } });
|
|
19723
20101
|
handle.setSchema(descriptor.inputsSchema());
|
|
19724
20102
|
void this.startNativeIndicator(id, handle);
|
|
19725
20103
|
return handle;
|
|
@@ -19772,6 +20150,19 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19772
20150
|
if (record.session) record.session.update(record.inputValues);
|
|
19773
20151
|
else if (record.native && !record.hidden) record.native.instance.setInputs(record.inputValues);
|
|
19774
20152
|
}
|
|
20153
|
+
/** IndicatorController: re-run an indicator with merged declaration-prop overrides.
|
|
20154
|
+
* Same lifecycle as {@link applyInputs} — a prop change replays the whole script.
|
|
20155
|
+
* Script indicators only: natives have no declaration props. */
|
|
20156
|
+
applyProps(id, values) {
|
|
20157
|
+
const record = this.registry.get(id);
|
|
20158
|
+
if (!record?.session) return;
|
|
20159
|
+
record.propValues = { ...record.propValues, ...values };
|
|
20160
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
20161
|
+
record.pendingStructural = true;
|
|
20162
|
+
if (!record.hidden) this.setLoading(record, true);
|
|
20163
|
+
record.pendingCause = "inputs";
|
|
20164
|
+
record.session.update(record.inputValues, record.propValues);
|
|
20165
|
+
}
|
|
19775
20166
|
/** IndicatorController: tear down an indicator and (if now empty) its pane. */
|
|
19776
20167
|
/** Live handles of every indicator on the chart (script + native), insertion order. */
|
|
19777
20168
|
listIndicators() {
|
|
@@ -19920,6 +20311,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19920
20311
|
for (const input of prepared.inputs) defaults[input.key] = input.defval;
|
|
19921
20312
|
record.inputValues = { ...defaults, ...record.inputValues };
|
|
19922
20313
|
handle.setSchema(prepared.inputs);
|
|
20314
|
+
const propDefaults = {};
|
|
20315
|
+
for (const prop of prepared.props ?? []) propDefaults[prop.key] = prop.defval;
|
|
20316
|
+
record.propValues = { ...propDefaults, ...record.propValues };
|
|
20317
|
+
handle.setPropsSchema(prepared.props ?? []);
|
|
19923
20318
|
this.mountLoadingPlaceholder(id, record);
|
|
19924
20319
|
this.executeIndicator(id, handle);
|
|
19925
20320
|
} catch (err) {
|
|
@@ -19943,6 +20338,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
19943
20338
|
getBars: () => this.bars,
|
|
19944
20339
|
fetchSeries: (sym, tf, range) => this.fetchSeries(sym, tf, range),
|
|
19945
20340
|
inputs: record.inputValues,
|
|
20341
|
+
props: record.propValues,
|
|
19946
20342
|
visibleRange: this.currentVisibleRange(),
|
|
19947
20343
|
mode,
|
|
19948
20344
|
// Mid-backfill session starts (add / re-show / price-style re-execute) let the
|
|
@@ -20036,6 +20432,9 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
20036
20432
|
const meta = record.prepared.meta;
|
|
20037
20433
|
const model = {
|
|
20038
20434
|
id,
|
|
20435
|
+
// Deliberately the FULL title, never a shorttitle: while the script loads the
|
|
20436
|
+
// legend identifies it by its full name; the compact shorttitle arrives with
|
|
20437
|
+
// the first computed model and takes over from there.
|
|
20039
20438
|
title: record.options?.title ?? meta.title,
|
|
20040
20439
|
overlay: meta.overlay,
|
|
20041
20440
|
paneHint: meta.overlay ? "price" : "new",
|
|
@@ -20044,7 +20443,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
20044
20443
|
backgrounds: [],
|
|
20045
20444
|
priceLines: [],
|
|
20046
20445
|
inputs: record.prepared.inputs,
|
|
20047
|
-
inputValues: record.inputValues
|
|
20446
|
+
inputValues: record.inputValues,
|
|
20447
|
+
...record.prepared.props ? { props: record.prepared.props, propValues: record.propValues } : {}
|
|
20048
20448
|
};
|
|
20049
20449
|
const paneId = this.routePane(id, model, record.options ?? {});
|
|
20050
20450
|
this.placeModel(model, id, paneId);
|
|
@@ -20277,7 +20677,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
20277
20677
|
record.model = model;
|
|
20278
20678
|
if (record.pendingStructural) {
|
|
20279
20679
|
record.renderHandle = this.renderer.mountIndicator(model);
|
|
20280
|
-
this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues);
|
|
20680
|
+
this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
20281
20681
|
record.pendingStructural = false;
|
|
20282
20682
|
} else {
|
|
20283
20683
|
this.renderer.updateIndicator(record.renderHandle, modelToValuePatch(model));
|
|
@@ -20304,16 +20704,17 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
20304
20704
|
placeModel(model, id, paneId) {
|
|
20305
20705
|
model.id = id;
|
|
20306
20706
|
model.paneId = paneId;
|
|
20307
|
-
|
|
20308
|
-
for (const
|
|
20309
|
-
for (const
|
|
20707
|
+
const paneOf = (item) => item.overlay === true ? "price" : paneId;
|
|
20708
|
+
for (const series of model.series) series.paneId = paneOf(series);
|
|
20709
|
+
for (const fill of model.fills) fill.paneId = paneOf(fill);
|
|
20710
|
+
for (const bg of model.backgrounds) bg.paneId = paneOf(bg);
|
|
20310
20711
|
for (const line of model.priceLines) line.paneId = paneId;
|
|
20311
|
-
if (model.lines) for (const ln of model.lines) ln.paneId =
|
|
20312
|
-
if (model.boxes) for (const bx of model.boxes) bx.paneId =
|
|
20313
|
-
if (model.labels) for (const lb of model.labels) lb.paneId =
|
|
20314
|
-
if (model.polylines) for (const pl of model.polylines) pl.paneId =
|
|
20315
|
-
if (model.linefills) for (const lf of model.linefills) lf.paneId =
|
|
20316
|
-
if (model.tables) for (const tb of model.tables) tb.paneId =
|
|
20712
|
+
if (model.lines) for (const ln of model.lines) ln.paneId = paneOf(ln);
|
|
20713
|
+
if (model.boxes) for (const bx of model.boxes) bx.paneId = paneOf(bx);
|
|
20714
|
+
if (model.labels) for (const lb of model.labels) lb.paneId = paneOf(lb);
|
|
20715
|
+
if (model.polylines) for (const pl of model.polylines) pl.paneId = paneOf(pl);
|
|
20716
|
+
if (model.linefills) for (const lf of model.linefills) lf.paneId = paneOf(lf);
|
|
20717
|
+
if (model.tables) for (const tb of model.tables) tb.paneId = paneOf(tb);
|
|
20317
20718
|
}
|
|
20318
20719
|
emitContextChanged(id) {
|
|
20319
20720
|
if (!this.registry.get(id)?.session?.getContext) return;
|
|
@@ -20973,4 +21374,4 @@ function resolveElement(container) {
|
|
|
20973
21374
|
return element;
|
|
20974
21375
|
}
|
|
20975
21376
|
|
|
20976
|
-
export { BUILTIN_PRICE_STYLES, BarStore, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LIGHT_THEME, MultiProviderFeed, NativeRenderer, RendererControl, TIMEZONES, TypedEventBus, Vela, applyAttributionMarkTheme, buildToolbar, createAttributionMark, createCustomMark, defaultToolbar, normalizeTimezone, parseSymbol, priceStyleIds, registerBuiltinChartTypes, resolveTheme, sharedBarStore, timeframeToMs, tzButtonLabel, tzMenuLabel, tzOffset };
|
|
21377
|
+
export { BUILTIN_PRICE_STYLES, BarStore, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LEGEND_AT_TOP_ATTR, LIGHT_THEME, MultiProviderFeed, NativeRenderer, RendererControl, TIMEZONES, TypedEventBus, Vela, applyAttributionMarkTheme, buildToolbar, createAttributionMark, createCustomMark, defaultToolbar, normalizeTimezone, parseSymbol, priceStyleIds, registerBuiltinChartTypes, resolveTheme, sharedBarStore, timeframeToMs, tzButtonLabel, tzMenuLabel, tzOffset };
|