@luxalgo/vela 0.6.10 → 0.6.12
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/README.md +110 -46
- package/dist/{DataProvider-CWmp31dA.d.ts → DataProvider-DhzstpQb.d.ts} +1 -1
- package/dist/{DataProvider-l_eLMly_.d.cts → DataProvider-DlMtrwqM.d.cts} +1 -1
- package/dist/{chunk-4KZVZ7QQ.js → chunk-F4M24ANM.js} +130 -1
- package/dist/{chunk-NMTQXNT4.js → chunk-IZV3CW5N.js} +245 -43
- package/dist/{chunk-FFOP37FQ.js → chunk-KV7FDWSL.js} +748 -70
- package/dist/{contributions-BCz6Dr6a.d.ts → contributions-37nni40G.d.ts} +20 -4
- package/dist/{contributions-D9vTzm5p.d.cts → contributions-lPojhTxI.d.cts} +20 -4
- package/dist/index.cjs +866 -58
- package/dist/index.d.cts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +3 -3
- package/dist/{options-DSqHsQyN.d.cts → options-CYS5Wmlx.d.cts} +78 -2
- package/dist/{options-DSqHsQyN.d.ts → options-CYS5Wmlx.d.ts} +78 -2
- package/dist/{plugin-Bt8hLR8Z.d.cts → plugin-Cwikpz1m.d.cts} +10 -3
- package/dist/{plugin-CH7pfMrE.d.ts → plugin-DHyaoMjW.d.ts} +10 -3
- package/dist/plugin.cjs +119 -0
- 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/{statusline-5K7y4Ya2.d.ts → statusline-model-CiJm-riV.d.cts} +17 -87
- package/dist/{statusline-TYLQmoeh.d.cts → statusline-model-D-q_OOx9.d.ts} +17 -87
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/vela.global.js +866 -58
- package/dist/vela.global.min.js +52 -52
- package/dist/widget.cjs +1109 -100
- package/dist/widget.d.cts +118 -7
- package/dist/widget.d.ts +118 -7
- package/dist/widget.js +4 -4
- package/dist/workspace.cjs +1109 -100
- package/dist/workspace.d.cts +20 -11
- package/dist/workspace.d.ts +20 -11
- package/dist/workspace.js +3 -3
- package/package.json +1 -1
- /package/dist/{chunk-AOGZBKUE.js → chunk-H26BEHF4.js} +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-
|
|
2
|
-
import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType } from './chunk-
|
|
1
|
+
import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-KV7FDWSL.js';
|
|
2
|
+
import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType, inputDeltas } from './chunk-F4M24ANM.js';
|
|
3
3
|
import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-G77Y7LK2.js';
|
|
4
4
|
import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-MHY7MVXH.js';
|
|
5
|
-
import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-KG4YT3TI.js';
|
|
5
|
+
import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-KG4YT3TI.js';
|
|
6
6
|
import { baseOf } from './chunk-W4EJWLEO.js';
|
|
7
7
|
|
|
8
8
|
// src/widget/timeframe.ts
|
|
@@ -3757,11 +3757,12 @@ async function resolveIndicators(config, fetchImpl = fetch) {
|
|
|
3757
3757
|
}
|
|
3758
3758
|
return out;
|
|
3759
3759
|
}
|
|
3760
|
+
var ledgerEntryName = (e) => typeof e === "string" ? e : e.name;
|
|
3760
3761
|
function indicatorLedger(i) {
|
|
3761
3762
|
const natives = [...i.present];
|
|
3762
3763
|
if (i.volumePending && !natives.includes("volume")) natives.push("volume");
|
|
3763
3764
|
return {
|
|
3764
|
-
manifest: i.manifestSettled ? [...i.
|
|
3765
|
+
manifest: i.manifestSettled ? [...i.instanceEntries] : [...i.pendingManifest ?? i.instanceEntries],
|
|
3765
3766
|
natives
|
|
3766
3767
|
};
|
|
3767
3768
|
}
|
|
@@ -3843,7 +3844,17 @@ function sanitizeCell(raw) {
|
|
|
3843
3844
|
if (c.drawings != null && typeof c.drawings === "object") out.drawings = c.drawings;
|
|
3844
3845
|
const ind = c.indicators;
|
|
3845
3846
|
if (ind != null && typeof ind === "object") {
|
|
3846
|
-
const manifest = Array.isArray(ind.manifest) ? ind.manifest.
|
|
3847
|
+
const manifest = Array.isArray(ind.manifest) ? ind.manifest.flatMap((n) => {
|
|
3848
|
+
if (typeof n === "string") return [n];
|
|
3849
|
+
if (n != null && typeof n === "object" && typeof n.name === "string") {
|
|
3850
|
+
const e = n;
|
|
3851
|
+
const bag = (v) => v != null && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
3852
|
+
const inputs = bag(e.inputs);
|
|
3853
|
+
const props = bag(e.props);
|
|
3854
|
+
return [inputs || props ? { name: e.name, ...inputs ? { inputs } : {}, ...props ? { props } : {} } : e.name];
|
|
3855
|
+
}
|
|
3856
|
+
return [];
|
|
3857
|
+
}) : [];
|
|
3847
3858
|
const natives = Array.isArray(ind.natives) ? ind.natives.filter((n) => typeof n === "string") : [];
|
|
3848
3859
|
out.indicators = { manifest, natives };
|
|
3849
3860
|
}
|
|
@@ -3977,6 +3988,21 @@ function fmtChange(open, close) {
|
|
|
3977
3988
|
return `${sign}${fmtPrice(diff, decimalsFor(close))} (${sign}${pct.toFixed(2)}%)`;
|
|
3978
3989
|
}
|
|
3979
3990
|
|
|
3991
|
+
// src/widget/statusline-model.ts
|
|
3992
|
+
function segmentVisibility(parts, chartHidden = false) {
|
|
3993
|
+
return { avatar: parts.logo, symbol: parts.name, meta: parts.name, market: parts.market, ohlc: parts.ohlc && !chartHidden, change: parts.change && !chartHidden, eye: chartHidden };
|
|
3994
|
+
}
|
|
3995
|
+
function statuslineMenuItems(parts, chartVisible) {
|
|
3996
|
+
return [
|
|
3997
|
+
{ id: "part:logo", label: "Symbol logo", checked: parts.logo },
|
|
3998
|
+
{ id: "part:name", label: "Symbol name", checked: parts.name },
|
|
3999
|
+
{ id: "part:market", label: "Market status", checked: parts.market },
|
|
4000
|
+
{ id: "part:ohlc", label: "OHLC values", checked: parts.ohlc },
|
|
4001
|
+
{ id: "part:change", label: "Bar change values", checked: parts.change },
|
|
4002
|
+
{ id: "chart", label: chartVisible ? "Hide chart" : "Show chart", separatorBefore: true }
|
|
4003
|
+
];
|
|
4004
|
+
}
|
|
4005
|
+
|
|
3980
4006
|
// src/widget/statusline.ts
|
|
3981
4007
|
var STYLE_ID10 = "vela-widget-statusline";
|
|
3982
4008
|
var CSS10 = `
|
|
@@ -4006,7 +4032,13 @@ var CSS10 = `
|
|
|
4006
4032
|
padding: 2px 7px;
|
|
4007
4033
|
margin-left: -7px;
|
|
4008
4034
|
}
|
|
4009
|
-
|
|
4035
|
+
/* Hovering opens the chip the same way a legend row opens: solid chart background
|
|
4036
|
+
* plus the same inset neutral outline the indicator rows wear (InputsUI's
|
|
4037
|
+
* setRowHighlighted) \u2014 the two columns read as one family. */
|
|
4038
|
+
.vela-statusline:hover { background: var(--vela-bg); box-shadow: inset 0 0 0 1px var(--vela-border); }
|
|
4039
|
+
/* Chart hidden (the price series' eye \u2014 renderer 'candleVisible'): the line dims to
|
|
4040
|
+
* the same 0.5 wash a hidden indicator's legend row wears. */
|
|
4041
|
+
.vela-statusline.vela-sl-chart-hidden { opacity: 0.5; }
|
|
4010
4042
|
.vela-statusline .vela-sl-avatar {
|
|
4011
4043
|
width: 18px;
|
|
4012
4044
|
height: 18px;
|
|
@@ -4031,6 +4063,24 @@ var CSS10 = `
|
|
|
4031
4063
|
* these are the pre-ink fallbacks only. */
|
|
4032
4064
|
.vela-statusline .vela-sl-change[data-dir='up'] { color: var(--vela-up); }
|
|
4033
4065
|
.vela-statusline .vela-sl-change[data-dir='down'] { color: var(--vela-down); }
|
|
4066
|
+
/* The show-chart eye \u2014 out only while the chart is hidden (syncParts drives display),
|
|
4067
|
+
* replacing the value readout it took away. Same footprint as a legend action button. */
|
|
4068
|
+
.vela-statusline .vela-sl-eye {
|
|
4069
|
+
align-self: center;
|
|
4070
|
+
align-items: center;
|
|
4071
|
+
justify-content: center;
|
|
4072
|
+
width: 18px;
|
|
4073
|
+
height: 18px;
|
|
4074
|
+
padding: 0;
|
|
4075
|
+
border: none;
|
|
4076
|
+
border-radius: 3px;
|
|
4077
|
+
background: none;
|
|
4078
|
+
color: var(--vela-fg-muted);
|
|
4079
|
+
cursor: pointer;
|
|
4080
|
+
line-height: 0;
|
|
4081
|
+
flex: none;
|
|
4082
|
+
}
|
|
4083
|
+
.vela-statusline .vela-sl-eye:hover { color: var(--vela-fg); background: color-mix(in srgb, var(--vela-fg) 12%, transparent); }
|
|
4034
4084
|
/* Stack the TOP pane's legend below the status line (lower study panes stay put).
|
|
4035
4085
|
* The renderer marks whichever legend sits at the plot's top edge \u2014 the price pane
|
|
4036
4086
|
* normally, or a maximized study pane filling the plot \u2014 so the legend never merges
|
|
@@ -4140,7 +4190,12 @@ var Statusline = class {
|
|
|
4140
4190
|
constructor(host, symbol, iconFor) {
|
|
4141
4191
|
this.host = host;
|
|
4142
4192
|
this.iconFor = iconFor;
|
|
4143
|
-
this.parts = { name: true, market: true, ohlc: true, change: true };
|
|
4193
|
+
this.parts = { logo: true, name: true, market: true, ohlc: true, change: true };
|
|
4194
|
+
/** The right-click action menu — present once a host wires it via {@link attachMenu}. */
|
|
4195
|
+
this.menu = null;
|
|
4196
|
+
this.menuHooks = null;
|
|
4197
|
+
/** Mirror of the renderer's `candleVisible` — see {@link setChartHidden}. */
|
|
4198
|
+
this.chartHidden = false;
|
|
4144
4199
|
this.lastBar = null;
|
|
4145
4200
|
this.hoverBar = null;
|
|
4146
4201
|
this.unsubs = [];
|
|
@@ -4156,6 +4211,15 @@ var Statusline = class {
|
|
|
4156
4211
|
/** Fit mode (multi-chart cells): one row, overflowing segments hidden — see {@link setFitMode}. */
|
|
4157
4212
|
this.fitMode = false;
|
|
4158
4213
|
this.fitRO = null;
|
|
4214
|
+
this.onContextMenu = (e) => {
|
|
4215
|
+
if (!this.menu || !this.menuHooks) return;
|
|
4216
|
+
e.preventDefault();
|
|
4217
|
+
e.stopPropagation();
|
|
4218
|
+
const chartVisible = this.menuHooks.chartVisible();
|
|
4219
|
+
this.setChartHidden(!chartVisible);
|
|
4220
|
+
this.menu.setItems(statuslineMenuItems(this.parts, chartVisible));
|
|
4221
|
+
this.menu.openAt(e.clientX, e.clientY);
|
|
4222
|
+
};
|
|
4159
4223
|
const doc = host.ownerDocument;
|
|
4160
4224
|
injectStyles(STYLE_ID10, CSS10, doc);
|
|
4161
4225
|
host.classList.add("vela-has-statusline");
|
|
@@ -4182,9 +4246,21 @@ var Statusline = class {
|
|
|
4182
4246
|
this.ohlcEl.className = "vela-sl-ohlc";
|
|
4183
4247
|
this.changeEl = doc.createElement("span");
|
|
4184
4248
|
this.changeEl.className = "vela-sl-change";
|
|
4185
|
-
this.
|
|
4249
|
+
this.eyeEl = doc.createElement("button");
|
|
4250
|
+
this.eyeEl.type = "button";
|
|
4251
|
+
this.eyeEl.className = "vela-sl-eye";
|
|
4252
|
+
this.eyeEl.innerHTML = iconAt("eye-off", 14);
|
|
4253
|
+
this.eyeEl.setAttribute("aria-label", "Show chart");
|
|
4254
|
+
this.eyeEl.style.display = "none";
|
|
4255
|
+
this.eyeEl.addEventListener("click", (e) => {
|
|
4256
|
+
e.stopPropagation();
|
|
4257
|
+
this.menuHooks?.setChartVisible(true);
|
|
4258
|
+
this.setChartHidden(false);
|
|
4259
|
+
});
|
|
4260
|
+
this.el.append(this.avatarEl, this.symbolEl, this.metaEl, this.marketEl, this.ohlcEl, this.changeEl, this.eyeEl);
|
|
4186
4261
|
host.appendChild(this.el);
|
|
4187
4262
|
this.marketTip = new Tooltip(this.marketEl, { content: MARKET_LABELS.open, placement: "bottom" });
|
|
4263
|
+
this.eyeTip = new Tooltip(this.eyeEl, { content: "Show chart", placement: "bottom" });
|
|
4188
4264
|
this.setMarketStatus("open");
|
|
4189
4265
|
this.render();
|
|
4190
4266
|
}
|
|
@@ -4194,6 +4270,7 @@ var Statusline = class {
|
|
|
4194
4270
|
const fresh = tickerIconEl(this.el.ownerDocument, baseOfTicker(ticker), ticker, "vela-sl-avatar", this.iconFor?.(symbol));
|
|
4195
4271
|
this.avatarEl.replaceWith(fresh);
|
|
4196
4272
|
this.avatarEl = fresh;
|
|
4273
|
+
this.syncParts();
|
|
4197
4274
|
this.fit();
|
|
4198
4275
|
}
|
|
4199
4276
|
/**
|
|
@@ -4221,20 +4298,25 @@ var Statusline = class {
|
|
|
4221
4298
|
}
|
|
4222
4299
|
/** Project the parts config onto the segments (the baseline fit() prunes from). */
|
|
4223
4300
|
syncParts() {
|
|
4224
|
-
|
|
4225
|
-
this.
|
|
4226
|
-
this.
|
|
4227
|
-
this.
|
|
4301
|
+
const seg = segmentVisibility(this.parts, this.chartHidden);
|
|
4302
|
+
this.avatarEl.style.display = seg.avatar ? "" : "none";
|
|
4303
|
+
this.symbolEl.style.display = seg.symbol ? "" : "none";
|
|
4304
|
+
this.metaEl.style.display = seg.meta ? "" : "none";
|
|
4305
|
+
this.marketEl.style.display = seg.market ? "" : "none";
|
|
4306
|
+
this.ohlcEl.style.display = seg.ohlc ? "" : "none";
|
|
4307
|
+
this.changeEl.style.display = seg.change ? "" : "none";
|
|
4308
|
+
this.eyeEl.style.display = seg.eye ? "inline-flex" : "none";
|
|
4228
4309
|
}
|
|
4229
4310
|
/** Hide overflowing segments until the row fits its max-width (fit mode only). */
|
|
4230
4311
|
fit() {
|
|
4231
4312
|
if (!this.fitMode) return;
|
|
4232
4313
|
this.syncParts();
|
|
4314
|
+
const seg = segmentVisibility(this.parts, this.chartHidden);
|
|
4233
4315
|
const order = [
|
|
4234
|
-
[this.ohlcEl,
|
|
4235
|
-
[this.changeEl,
|
|
4236
|
-
[this.metaEl,
|
|
4237
|
-
[this.marketEl,
|
|
4316
|
+
[this.ohlcEl, seg.ohlc],
|
|
4317
|
+
[this.changeEl, seg.change],
|
|
4318
|
+
[this.metaEl, seg.meta],
|
|
4319
|
+
[this.marketEl, seg.market]
|
|
4238
4320
|
];
|
|
4239
4321
|
for (const [el, shown] of order) {
|
|
4240
4322
|
if (this.el.scrollWidth <= this.el.clientWidth) break;
|
|
@@ -4255,7 +4337,6 @@ var Statusline = class {
|
|
|
4255
4337
|
this.readout = readout;
|
|
4256
4338
|
this.render();
|
|
4257
4339
|
}
|
|
4258
|
-
/** Show/hide one part (the settings dialog's Status line tab drives these). */
|
|
4259
4340
|
/** The "· BINANCE · 1h" segment after the symbol — venue first, then resolution. */
|
|
4260
4341
|
setMeta(timeframe, provider) {
|
|
4261
4342
|
this.metaEl.textContent = `${provider ? `\xB7 ${provider.toUpperCase()} ` : ""}\xB7 ${timeframeLabel(timeframe)}`;
|
|
@@ -4274,6 +4355,9 @@ var Statusline = class {
|
|
|
4274
4355
|
});
|
|
4275
4356
|
this.marketTip.setContent(MARKET_LABELS[status]);
|
|
4276
4357
|
}
|
|
4358
|
+
/** Show/hide one part — the settings dialog's Status line tab and the right-click
|
|
4359
|
+
* menu both drive these. 'name' owns the venue/timeframe meta too (see
|
|
4360
|
+
* {@link segmentVisibility}). */
|
|
4277
4361
|
setPartVisible(part, visible) {
|
|
4278
4362
|
this.parts[part] = visible;
|
|
4279
4363
|
this.syncParts();
|
|
@@ -4282,6 +4366,47 @@ var Statusline = class {
|
|
|
4282
4366
|
partVisible(part) {
|
|
4283
4367
|
return this.parts[part];
|
|
4284
4368
|
}
|
|
4369
|
+
/** Mirror the chart's (price series') visibility: dim the whole line like a hidden
|
|
4370
|
+
* indicator's legend row, drop the value readout (OHLC + bar change — values of a
|
|
4371
|
+
* series that isn't painted), and put the show-chart eye out in its place. The
|
|
4372
|
+
* parts config is untouched, so showing the chart restores the readout exactly as
|
|
4373
|
+
* configured. Idempotent; {@link render} re-syncs it from the live renderer, so
|
|
4374
|
+
* toggles made elsewhere (the object tree's eye) converge too. */
|
|
4375
|
+
setChartHidden(hidden) {
|
|
4376
|
+
if (hidden === this.chartHidden) return;
|
|
4377
|
+
this.chartHidden = hidden;
|
|
4378
|
+
this.el.classList.toggle("vela-sl-chart-hidden", hidden);
|
|
4379
|
+
this.syncParts();
|
|
4380
|
+
this.fit();
|
|
4381
|
+
}
|
|
4382
|
+
/** Wire the right-click action menu: one checkable toggle per part plus hide/show
|
|
4383
|
+
* for the chart itself. The menu is built once; later calls just swap the hooks. */
|
|
4384
|
+
attachMenu(hooks) {
|
|
4385
|
+
this.menuHooks = hooks;
|
|
4386
|
+
if (this.menu) return;
|
|
4387
|
+
this.menu = new Menu({
|
|
4388
|
+
host: this.host,
|
|
4389
|
+
items: [],
|
|
4390
|
+
placement: "bottom-start",
|
|
4391
|
+
// Pointer-anchored action menu — checked state reads as a leading ✓ (the
|
|
4392
|
+
// same shape as the chart's own context menu).
|
|
4393
|
+
checkmarks: true,
|
|
4394
|
+
onSelect: (id) => this.runMenuItem(id)
|
|
4395
|
+
});
|
|
4396
|
+
this.el.addEventListener("contextmenu", this.onContextMenu);
|
|
4397
|
+
}
|
|
4398
|
+
runMenuItem(id) {
|
|
4399
|
+
const hooks = this.menuHooks;
|
|
4400
|
+
if (!hooks) return;
|
|
4401
|
+
if (id.startsWith("part:")) {
|
|
4402
|
+
const part = id.slice("part:".length);
|
|
4403
|
+
hooks.setPart(part, !this.parts[part]);
|
|
4404
|
+
} else if (id === "chart") {
|
|
4405
|
+
const next = !hooks.chartVisible();
|
|
4406
|
+
hooks.setChartVisible(next);
|
|
4407
|
+
this.setChartHidden(!next);
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4285
4410
|
/** (Re)bind to a chart instance — called after every widget rebuild. */
|
|
4286
4411
|
onChart(chart) {
|
|
4287
4412
|
this.detach();
|
|
@@ -4303,7 +4428,11 @@ var Statusline = class {
|
|
|
4303
4428
|
this.detach();
|
|
4304
4429
|
this.fitRO?.disconnect();
|
|
4305
4430
|
this.fitRO = null;
|
|
4431
|
+
this.el.removeEventListener("contextmenu", this.onContextMenu);
|
|
4432
|
+
this.menu?.destroy();
|
|
4433
|
+
this.menu = null;
|
|
4306
4434
|
this.marketTip.destroy();
|
|
4435
|
+
this.eyeTip.destroy();
|
|
4307
4436
|
this.marketBubble.destroy();
|
|
4308
4437
|
this.host.classList.remove("vela-has-statusline", "vela-sl-fit-host");
|
|
4309
4438
|
this.el.remove();
|
|
@@ -4313,6 +4442,7 @@ var Statusline = class {
|
|
|
4313
4442
|
this.unsubs = [];
|
|
4314
4443
|
}
|
|
4315
4444
|
render() {
|
|
4445
|
+
if (this.menuHooks) this.setChartHidden(!this.menuHooks.chartVisible());
|
|
4316
4446
|
const bar = this.hoverBar ?? this.lastBar;
|
|
4317
4447
|
if (!bar) {
|
|
4318
4448
|
this.ohlcEl.replaceChildren();
|
|
@@ -4969,6 +5099,7 @@ var TIME_AXIS_H2 = 22;
|
|
|
4969
5099
|
var CONTROLS_BOTTOM_PX = TIME_AXIS_H2 + 12;
|
|
4970
5100
|
var CLUSTER_H = 24;
|
|
4971
5101
|
var CLUSTER_PILL = "rgba(0,0,0,0.65)";
|
|
5102
|
+
var CLUSTER_LEFT_CSS = "calc((100% + var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px)) / 2)";
|
|
4972
5103
|
var STYLE_ID12 = "vela-cell-controls";
|
|
4973
5104
|
var CSS12 = `
|
|
4974
5105
|
.vela-cc-btn{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:var(--vela-radius-sm);background:transparent;line-height:0;font-size:12px;color:var(--vela-fg-muted);cursor:pointer;}
|
|
@@ -4978,8 +5109,11 @@ var CSS12 = `
|
|
|
4978
5109
|
.vela-cc-grip{cursor:grab;touch-action:none;}
|
|
4979
5110
|
.vela-cc-grip:active{cursor:grabbing;}
|
|
4980
5111
|
`;
|
|
4981
|
-
function
|
|
4982
|
-
|
|
5112
|
+
function plotCenterX(width, toolbarGutter = 0, scaleGutter = 0) {
|
|
5113
|
+
return (width + toolbarGutter - scaleGutter) / 2;
|
|
5114
|
+
}
|
|
5115
|
+
function nearBottomCenter(x, y, width, height, proximityPx = CELL_CONTROLS_PROXIMITY_PX, gutters = {}) {
|
|
5116
|
+
const cx = plotCenterX(width, gutters.toolbar ?? 0, gutters.scale ?? 0);
|
|
4983
5117
|
const cy = height - CONTROLS_BOTTOM_PX - CLUSTER_H / 2;
|
|
4984
5118
|
return Math.hypot(x - cx, y - cy) <= proximityPx;
|
|
4985
5119
|
}
|
|
@@ -4998,7 +5132,7 @@ var CellControls = class {
|
|
|
4998
5132
|
if (this.suspended) return;
|
|
4999
5133
|
if (this.dragging) return;
|
|
5000
5134
|
const rect = this.host.getBoundingClientRect();
|
|
5001
|
-
this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height));
|
|
5135
|
+
this.setNear(nearBottomCenter(e.clientX - rect.left, e.clientY - rect.top, rect.width, rect.height, CELL_CONTROLS_PROXIMITY_PX, this.hostGutters()));
|
|
5002
5136
|
};
|
|
5003
5137
|
this.onHostLeave = () => {
|
|
5004
5138
|
if (this.dragging) return;
|
|
@@ -5009,7 +5143,7 @@ var CellControls = class {
|
|
|
5009
5143
|
this.root = host.ownerDocument.createElement("div");
|
|
5010
5144
|
Object.assign(this.root.style, {
|
|
5011
5145
|
position: "absolute",
|
|
5012
|
-
left:
|
|
5146
|
+
left: CLUSTER_LEFT_CSS,
|
|
5013
5147
|
bottom: `${CONTROLS_BOTTOM_PX}px`,
|
|
5014
5148
|
transform: "translateX(-50%)",
|
|
5015
5149
|
zIndex: "6",
|
|
@@ -5109,6 +5243,16 @@ var CellControls = class {
|
|
|
5109
5243
|
this.suspended = on;
|
|
5110
5244
|
if (on) this.setNear(false);
|
|
5111
5245
|
}
|
|
5246
|
+
/** Live renderer gutters on the cell host (0 when unpublished — a test stub). */
|
|
5247
|
+
hostGutters() {
|
|
5248
|
+
const view = this.host.ownerDocument.defaultView;
|
|
5249
|
+
if (!view) return { toolbar: 0, scale: 0 };
|
|
5250
|
+
const cs = view.getComputedStyle(this.host);
|
|
5251
|
+
return {
|
|
5252
|
+
toolbar: Number.parseFloat(cs.getPropertyValue("--vela-toolbar-gutter")) || 0,
|
|
5253
|
+
scale: Number.parseFloat(cs.getPropertyValue("--vela-scale-gutter")) || 0
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5112
5256
|
setNear(near) {
|
|
5113
5257
|
if (near === this.near) return;
|
|
5114
5258
|
this.near = near;
|
|
@@ -5223,6 +5367,12 @@ function cellDrawings(opt) {
|
|
|
5223
5367
|
if (opt === true || opt == null) return { toolbar: false };
|
|
5224
5368
|
return { ...opt, toolbar: false };
|
|
5225
5369
|
}
|
|
5370
|
+
function instanceDeltas(handle) {
|
|
5371
|
+
if (!handle) return void 0;
|
|
5372
|
+
const inputs = inputDeltas(handle.inputs, handle.inputValues());
|
|
5373
|
+
const props = inputDeltas(handle.props, handle.propValues());
|
|
5374
|
+
return inputs || props ? { ...inputs ? { inputs } : {}, ...props ? { props } : {} } : void 0;
|
|
5375
|
+
}
|
|
5226
5376
|
var ChartCell = class {
|
|
5227
5377
|
constructor(id, gridHost, seed, deps) {
|
|
5228
5378
|
this.id = id;
|
|
@@ -5273,7 +5423,14 @@ var ChartCell = class {
|
|
|
5273
5423
|
this.destroyed = false;
|
|
5274
5424
|
this.appTheme = deps.theme;
|
|
5275
5425
|
const symbol = prefixedSymbol(seed);
|
|
5276
|
-
this.state = {
|
|
5426
|
+
this.state = {
|
|
5427
|
+
symbol,
|
|
5428
|
+
provider: parseSymbol(symbol ?? "").provider ?? void 0,
|
|
5429
|
+
timeframe: seed.timeframe,
|
|
5430
|
+
priceStyle: seed.priceStyle,
|
|
5431
|
+
bars: seed.bars,
|
|
5432
|
+
session: normalizeSession(seed.session)
|
|
5433
|
+
};
|
|
5277
5434
|
const doc = gridHost.ownerDocument;
|
|
5278
5435
|
this.host = doc.createElement("div");
|
|
5279
5436
|
this.host.className = "vela-cell";
|
|
@@ -5361,6 +5518,11 @@ var ChartCell = class {
|
|
|
5361
5518
|
this.statusline = deps.statusline ? new Statusline(this.host, symbol ?? "", (sym) => this.inner?.data.symbolIcon(sym)) : null;
|
|
5362
5519
|
this.statusline?.setMeta(seed.timeframe ?? "60", this.state.provider ?? "");
|
|
5363
5520
|
this.statusline?.onChart(this.inner);
|
|
5521
|
+
this.statusline?.attachMenu({
|
|
5522
|
+
setPart: (part, visible) => this.setStatuslinePart(part, visible),
|
|
5523
|
+
chartVisible: () => this.inner?.renderer.get("candleVisible") !== false,
|
|
5524
|
+
setChartVisible: (visible) => this.inner?.renderer.set("candleVisible", visible)
|
|
5525
|
+
});
|
|
5364
5526
|
this.marketStatus = this.statusline ? new MarketStatusTracker((s) => this.statusline?.setMarketStatus(s)) : null;
|
|
5365
5527
|
void this.inner.data.ready().then(() => {
|
|
5366
5528
|
if (this.inner && this.state.symbol) {
|
|
@@ -5398,6 +5560,7 @@ var ChartCell = class {
|
|
|
5398
5560
|
this.syncPresentNatives();
|
|
5399
5561
|
this.refreshNativeCatalog();
|
|
5400
5562
|
});
|
|
5563
|
+
this.inner.on("indicator:inputs", () => this.deps.onStateDirty());
|
|
5401
5564
|
this.inner.on("indicator:removed", ({ id: id2 }) => {
|
|
5402
5565
|
if (this.destroyed) return;
|
|
5403
5566
|
const idx = this.instances.findIndex((it) => it.handle?.id === id2);
|
|
@@ -5406,7 +5569,7 @@ var ChartCell = class {
|
|
|
5406
5569
|
this.instances.splice(idx, 1);
|
|
5407
5570
|
this.history.push({
|
|
5408
5571
|
undo: () => {
|
|
5409
|
-
snapshot.handle = this.addToChart(snapshot.entry);
|
|
5572
|
+
snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
|
|
5410
5573
|
this.instances.push(snapshot);
|
|
5411
5574
|
this.deps.onIndicatorsChanged(this.id);
|
|
5412
5575
|
},
|
|
@@ -5567,7 +5730,7 @@ var ChartCell = class {
|
|
|
5567
5730
|
kind: "select",
|
|
5568
5731
|
label: "Bars to fetch",
|
|
5569
5732
|
id: "bars",
|
|
5570
|
-
options: ["500", "1000", "2000", "5000", "10000", "20000"],
|
|
5733
|
+
options: ["500", "1000", "2000", "5000", "10000", "20000", "50000", "60000", "80000", "100000"],
|
|
5571
5734
|
get: () => String(this.state.bars ?? 1e3),
|
|
5572
5735
|
set: (v) => {
|
|
5573
5736
|
this.state.bars = Number(v);
|
|
@@ -5599,10 +5762,34 @@ var ChartCell = class {
|
|
|
5599
5762
|
id: "status-line",
|
|
5600
5763
|
rows: [
|
|
5601
5764
|
{ kind: "heading", label: "Status line", id: "parts" },
|
|
5602
|
-
{
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5765
|
+
{
|
|
5766
|
+
kind: "toggle",
|
|
5767
|
+
label: "Symbol name",
|
|
5768
|
+
id: "name",
|
|
5769
|
+
get: () => sl.partVisible("name"),
|
|
5770
|
+
set: (v) => this.setStatuslinePart("name", v)
|
|
5771
|
+
},
|
|
5772
|
+
{
|
|
5773
|
+
kind: "toggle",
|
|
5774
|
+
label: "Market status",
|
|
5775
|
+
id: "market",
|
|
5776
|
+
get: () => sl.partVisible("market"),
|
|
5777
|
+
set: (v) => this.setStatuslinePart("market", v)
|
|
5778
|
+
},
|
|
5779
|
+
{
|
|
5780
|
+
kind: "toggle",
|
|
5781
|
+
label: "OHLC values",
|
|
5782
|
+
id: "ohlc",
|
|
5783
|
+
get: () => sl.partVisible("ohlc"),
|
|
5784
|
+
set: (v) => this.setStatuslinePart("ohlc", v)
|
|
5785
|
+
},
|
|
5786
|
+
{
|
|
5787
|
+
kind: "toggle",
|
|
5788
|
+
label: "Bar change values",
|
|
5789
|
+
id: "change",
|
|
5790
|
+
get: () => sl.partVisible("change"),
|
|
5791
|
+
set: (v) => this.setStatuslinePart("change", v)
|
|
5792
|
+
},
|
|
5606
5793
|
{ kind: "heading", label: "Indicators", id: "indicators" },
|
|
5607
5794
|
{
|
|
5608
5795
|
kind: "toggle",
|
|
@@ -5655,7 +5842,7 @@ var ChartCell = class {
|
|
|
5655
5842
|
statusPrefs() {
|
|
5656
5843
|
const sl = this.statusline;
|
|
5657
5844
|
return {
|
|
5658
|
-
parts: sl ? { name: sl.partVisible("name"), market: sl.partVisible("market"), ohlc: sl.partVisible("ohlc"), change: sl.partVisible("change") } : null,
|
|
5845
|
+
parts: sl ? { logo: sl.partVisible("logo"), name: sl.partVisible("name"), market: sl.partVisible("market"), ohlc: sl.partVisible("ohlc"), change: sl.partVisible("change") } : null,
|
|
5659
5846
|
indicatorTitles: this.indicatorTitlesOn,
|
|
5660
5847
|
indicatorValues: this.indicatorValuesOn
|
|
5661
5848
|
};
|
|
@@ -5795,9 +5982,9 @@ var ChartCell = class {
|
|
|
5795
5982
|
this.manifest = list;
|
|
5796
5983
|
if (this.pendingManifestNames) {
|
|
5797
5984
|
if (list.length === 0) return;
|
|
5798
|
-
for (const
|
|
5799
|
-
const entry = list.find((e) => e.name ===
|
|
5800
|
-
if (entry) this.addManifestInstance(entry, { record: false });
|
|
5985
|
+
for (const led of this.pendingManifestNames) {
|
|
5986
|
+
const entry = list.find((e) => e.name === ledgerEntryName(led));
|
|
5987
|
+
if (entry) this.addManifestInstance(entry, { record: false, ...typeof led === "object" ? { inputs: led.inputs, props: led.props } : {} });
|
|
5801
5988
|
}
|
|
5802
5989
|
this.pendingManifestNames = null;
|
|
5803
5990
|
return;
|
|
@@ -5827,9 +6014,10 @@ var ChartCell = class {
|
|
|
5827
6014
|
}
|
|
5828
6015
|
for (const it of [...this.instances]) this.dropInstance(it);
|
|
5829
6016
|
if (this.manifest.length > 0) {
|
|
5830
|
-
for (const
|
|
5831
|
-
const entry = this.manifest.find((e) => e.name ===
|
|
5832
|
-
if (entry)
|
|
6017
|
+
for (const item of led.manifest) {
|
|
6018
|
+
const entry = this.manifest.find((e) => e.name === ledgerEntryName(item));
|
|
6019
|
+
if (entry)
|
|
6020
|
+
this.addManifestInstance(entry, { record: false, ...typeof item === "object" ? { inputs: item.inputs, props: item.props } : {} });
|
|
5833
6021
|
}
|
|
5834
6022
|
this.pendingManifestNames = null;
|
|
5835
6023
|
} else if (!this.deps.manifestSettled()) {
|
|
@@ -5878,12 +6066,16 @@ var ChartCell = class {
|
|
|
5878
6066
|
* a persistence handler's `restore` runs silently, a user-driven call records.
|
|
5879
6067
|
*/
|
|
5880
6068
|
addExternalIndicator(entry) {
|
|
5881
|
-
this.addManifestInstance(
|
|
6069
|
+
this.addManifestInstance(
|
|
6070
|
+
{ ...entry, enabled: true },
|
|
6071
|
+
{ external: true, ...entry.inputs ? { inputs: entry.inputs } : {}, ...entry.props ? { props: entry.props } : {} }
|
|
6072
|
+
);
|
|
5882
6073
|
}
|
|
5883
6074
|
/** Add ONE instance of a manifest entry (repeatable — duplicates are legitimate). */
|
|
5884
6075
|
addManifestInstance(entry, opts = {}) {
|
|
5885
6076
|
if (this.destroyed) return;
|
|
5886
|
-
const
|
|
6077
|
+
const values = opts.inputs || opts.props ? { inputs: opts.inputs, props: opts.props } : void 0;
|
|
6078
|
+
const it = { entry, handle: this.addToChart(entry, values), ...opts.external ? { external: true } : {}, ...values ? { values } : {} };
|
|
5887
6079
|
this.instances.push(it);
|
|
5888
6080
|
this.deps.onIndicatorsChanged(this.id);
|
|
5889
6081
|
if (opts.record === false) return;
|
|
@@ -5891,7 +6083,7 @@ var ChartCell = class {
|
|
|
5891
6083
|
this.history.push({
|
|
5892
6084
|
undo: () => this.dropInstance(snapshot),
|
|
5893
6085
|
redo: () => {
|
|
5894
|
-
snapshot.handle = this.addToChart(snapshot.entry);
|
|
6086
|
+
snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
|
|
5895
6087
|
this.instances.push(snapshot);
|
|
5896
6088
|
this.deps.onIndicatorsChanged(this.id);
|
|
5897
6089
|
}
|
|
@@ -5904,7 +6096,7 @@ var ChartCell = class {
|
|
|
5904
6096
|
const snapshot = it;
|
|
5905
6097
|
this.history.push({
|
|
5906
6098
|
undo: () => {
|
|
5907
|
-
snapshot.handle = this.addToChart(snapshot.entry);
|
|
6099
|
+
snapshot.handle = this.addToChart(snapshot.entry, snapshot.values);
|
|
5908
6100
|
this.instances.push(snapshot);
|
|
5909
6101
|
this.deps.onIndicatorsChanged(this.id);
|
|
5910
6102
|
},
|
|
@@ -5914,6 +6106,9 @@ var ChartCell = class {
|
|
|
5914
6106
|
dropInstance(it) {
|
|
5915
6107
|
const idx = this.instances.indexOf(it);
|
|
5916
6108
|
if (idx >= 0) this.instances.splice(idx, 1);
|
|
6109
|
+
const captured = instanceDeltas(it.handle);
|
|
6110
|
+
if (captured) it.values = captured;
|
|
6111
|
+
else delete it.values;
|
|
5917
6112
|
try {
|
|
5918
6113
|
it.handle?.remove();
|
|
5919
6114
|
} catch {
|
|
@@ -5956,9 +6151,13 @@ var ChartCell = class {
|
|
|
5956
6151
|
this.deps.onIndicatorsChanged(this.id);
|
|
5957
6152
|
});
|
|
5958
6153
|
}
|
|
5959
|
-
addToChart(entry) {
|
|
6154
|
+
addToChart(entry, values) {
|
|
5960
6155
|
try {
|
|
5961
|
-
return this.inner?.addIndicator(entry.script,
|
|
6156
|
+
return this.inner?.addIndicator(entry.script, {
|
|
6157
|
+
...entry.language !== void 0 ? { language: entry.language } : {},
|
|
6158
|
+
...values?.inputs ? { inputs: values.inputs } : {},
|
|
6159
|
+
...values?.props ? { props: values.props } : {}
|
|
6160
|
+
}) ?? null;
|
|
5962
6161
|
} catch (err) {
|
|
5963
6162
|
console.warn(`[vela] indicator "${entry.name}" failed to add:`, err);
|
|
5964
6163
|
return null;
|
|
@@ -6072,7 +6271,10 @@ var ChartCell = class {
|
|
|
6072
6271
|
// manifest — their plugin persists them via the `ext` seam instead.
|
|
6073
6272
|
indicators: indicatorLedger({
|
|
6074
6273
|
present: this.inner ? this.inner.presentNativeIndicators() : [],
|
|
6075
|
-
|
|
6274
|
+
instanceEntries: this.instances.filter((it) => !it.external).map((it) => {
|
|
6275
|
+
const d = it.handle ? instanceDeltas(it.handle) : it.values;
|
|
6276
|
+
return d ? { name: it.entry.name, ...d } : it.entry.name;
|
|
6277
|
+
}),
|
|
6076
6278
|
pendingManifest: this.pendingManifestNames,
|
|
6077
6279
|
manifestSettled: this.deps.manifestSettled(),
|
|
6078
6280
|
volumePending: this.volumeMayBePending && this.volumeIntent
|
|
@@ -7618,7 +7820,7 @@ function buildContext(host) {
|
|
|
7618
7820
|
}
|
|
7619
7821
|
|
|
7620
7822
|
// src/workspace/VelaWorkspace.ts
|
|
7621
|
-
var DEFAULT_TIMEFRAMES = ["1", "5", "15", "60", "240", "D", "W"];
|
|
7823
|
+
var DEFAULT_TIMEFRAMES = ["1", "5", "15", "30", "60", "240", "D", "W", "M"];
|
|
7622
7824
|
var GAP_PX = 2;
|
|
7623
7825
|
var POOL_CAP = 16;
|
|
7624
7826
|
var TIME_AXIS_H3 = 22;
|