@luxalgo/vela 0.7.4 → 0.7.6
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-RF1axuy1.d.cts → DataProvider-CGa4KHRa.d.cts} +1 -1
- package/dist/{DataProvider-CVpN5sCq.d.ts → DataProvider-CmWUtDVL.d.ts} +1 -1
- package/dist/{chunk-CSFNKXUW.js → chunk-EMB53WNQ.js} +16 -7
- package/dist/{chunk-DUURH3RN.js → chunk-JSVCWYBF.js} +271 -74
- package/dist/{contributions-CspPrMAJ.d.ts → contributions-C7Wsm_S9.d.ts} +13 -3
- package/dist/{contributions-DpZeu7KP.d.cts → contributions-D6p1RB38.d.cts} +13 -3
- package/dist/index.cjs +271 -74
- package/dist/index.d.cts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +1 -1
- package/dist/{options-XVpfr9HV.d.cts → options-D1AJKsn_.d.cts} +18 -0
- package/dist/{options-XVpfr9HV.d.ts → options-D1AJKsn_.d.ts} +18 -0
- package/dist/{plugin-D07HD2GL.d.cts → plugin-CxWfg6vK.d.cts} +3 -3
- package/dist/{plugin-BoLOkblF.d.ts → plugin-SodaQhjQ.d.ts} +3 -3
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- 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-model-Bd4qKuD6.d.cts → statusline-model-gWHrsOy3.d.cts} +3 -3
- package/dist/{statusline-model-CPa_hmhd.d.ts → statusline-model-xBiVsKoE.d.ts} +3 -3
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/vela.global.js +271 -74
- package/dist/vela.global.min.js +53 -49
- package/dist/widget.cjs +286 -80
- package/dist/widget.d.cts +6 -6
- package/dist/widget.d.ts +6 -6
- package/dist/widget.js +3 -3
- package/dist/workspace.cjs +286 -80
- package/dist/workspace.d.cts +10 -5
- package/dist/workspace.d.ts +10 -5
- package/dist/workspace.js +2 -2
- package/package.json +1 -1
package/dist/vela.global.js
CHANGED
|
@@ -105,10 +105,18 @@ var Vela = (function (exports) {
|
|
|
105
105
|
this.records = /* @__PURE__ */ new Map();
|
|
106
106
|
this.counter = 0;
|
|
107
107
|
}
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Mint a unique per-instance id. Host-supplied ids share the namespace, so a minted
|
|
110
|
+
* id skips anything already recorded — and anything `taken` reports live elsewhere
|
|
111
|
+
* (a handle the orchestrator holds outside the records, e.g. a fail-soft native).
|
|
112
|
+
*/
|
|
113
|
+
nextId(prefix = "ind", taken = () => false) {
|
|
114
|
+
let id;
|
|
115
|
+
do {
|
|
116
|
+
this.counter += 1;
|
|
117
|
+
id = `${prefix}-${this.counter}`;
|
|
118
|
+
} while (this.records.has(id) || taken(id));
|
|
119
|
+
return id;
|
|
112
120
|
}
|
|
113
121
|
add(record) {
|
|
114
122
|
this.records.set(record.id, record);
|
|
@@ -7116,6 +7124,42 @@ var Vela = (function (exports) {
|
|
|
7116
7124
|
}
|
|
7117
7125
|
};
|
|
7118
7126
|
|
|
7127
|
+
// src/core/marks/visibility.ts
|
|
7128
|
+
function markGroupOwnVisible(choices, groupId, groups) {
|
|
7129
|
+
const chosen = choices[groupId];
|
|
7130
|
+
if (typeof chosen === "boolean") return chosen;
|
|
7131
|
+
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
7132
|
+
}
|
|
7133
|
+
function markGroupVisible(choices, groupId, groups) {
|
|
7134
|
+
if (groupId === void 0) return true;
|
|
7135
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7136
|
+
let id = groupId;
|
|
7137
|
+
while (id !== void 0 && !seen.has(id)) {
|
|
7138
|
+
seen.add(id);
|
|
7139
|
+
if (!markGroupOwnVisible(choices, id, groups)) return false;
|
|
7140
|
+
const parent = groups.find((g) => g.id === id)?.parent;
|
|
7141
|
+
id = parent !== void 0 && groups.some((g) => g.id === parent) ? parent : void 0;
|
|
7142
|
+
}
|
|
7143
|
+
return true;
|
|
7144
|
+
}
|
|
7145
|
+
function markGroupRows(groups) {
|
|
7146
|
+
const ids = new Set(groups.map((g) => g.id));
|
|
7147
|
+
const out = [];
|
|
7148
|
+
const placed = /* @__PURE__ */ new Set();
|
|
7149
|
+
const place = (group, depth) => {
|
|
7150
|
+
if (placed.has(group.id)) return;
|
|
7151
|
+
placed.add(group.id);
|
|
7152
|
+
out.push({ group, depth });
|
|
7153
|
+
for (const child of groups) if (child.parent === group.id && child.id !== group.id) place(child, depth + 1);
|
|
7154
|
+
};
|
|
7155
|
+
for (const g of groups) {
|
|
7156
|
+
const parentKnown = g.parent !== void 0 && ids.has(g.parent) && g.parent !== g.id;
|
|
7157
|
+
if (!parentKnown) place(g, 0);
|
|
7158
|
+
}
|
|
7159
|
+
for (const g of groups) place(g, 0);
|
|
7160
|
+
return out;
|
|
7161
|
+
}
|
|
7162
|
+
|
|
7119
7163
|
// src/core/marks/MarksController.ts
|
|
7120
7164
|
var MarksController = class {
|
|
7121
7165
|
constructor(renderer, events) {
|
|
@@ -7160,6 +7204,7 @@ var Vela = (function (exports) {
|
|
|
7160
7204
|
defineGroup(group) {
|
|
7161
7205
|
if (!group || typeof group.id !== "string" || group.id.length === 0) throw new Error("[vela] marks.defineGroup: `id` must be a non-empty string");
|
|
7162
7206
|
if (typeof group.label !== "string") throw new Error(`[vela] marks.defineGroup: group "${group.id}" needs a string \`label\``);
|
|
7207
|
+
if (group.parent !== void 0 && (typeof group.parent !== "string" || group.parent.length === 0)) throw new Error(`[vela] marks.defineGroup: group "${group.id}" has a \`parent\` that is not a group id`);
|
|
7163
7208
|
this.groups.set(group.id, { ...group });
|
|
7164
7209
|
this.sync();
|
|
7165
7210
|
}
|
|
@@ -7179,12 +7224,16 @@ var Vela = (function (exports) {
|
|
|
7179
7224
|
}
|
|
7180
7225
|
this.renderer.applyFeature("marks", { groups: { [id]: visible } });
|
|
7181
7226
|
}
|
|
7182
|
-
/**
|
|
7227
|
+
/**
|
|
7228
|
+
* A group's effective visibility: its own switch — the user's (persisted) choice, else
|
|
7229
|
+
* the declared default, else visible — AND every ancestor's, so a child under a
|
|
7230
|
+
* switched-off parent reads hidden whatever its own choice says.
|
|
7231
|
+
*/
|
|
7183
7232
|
isGroupVisible(id) {
|
|
7184
7233
|
const state = this.renderer.readFeature("marks");
|
|
7185
|
-
const
|
|
7186
|
-
if (typeof
|
|
7187
|
-
return this.groups.
|
|
7234
|
+
const groups = {};
|
|
7235
|
+
for (const [gid, v] of Object.entries(state?.groups ?? {})) if (typeof v === "boolean") groups[gid] = v;
|
|
7236
|
+
return markGroupVisible(groups, id, [...this.groups.values()]);
|
|
7188
7237
|
}
|
|
7189
7238
|
destroy() {
|
|
7190
7239
|
for (const unsub of this.subs) unsub();
|
|
@@ -8612,7 +8661,7 @@ var Vela = (function (exports) {
|
|
|
8612
8661
|
this.engines.set(language, engine);
|
|
8613
8662
|
}
|
|
8614
8663
|
addIndicator(source, options = {}) {
|
|
8615
|
-
const id = this.
|
|
8664
|
+
const id = this.claimIndicatorId(options.id);
|
|
8616
8665
|
const title = options.title ?? "Indicator";
|
|
8617
8666
|
const handle = new IndicatorHandleImpl(id, title, this, source);
|
|
8618
8667
|
this.handles.set(id, handle);
|
|
@@ -8634,7 +8683,7 @@ var Vela = (function (exports) {
|
|
|
8634
8683
|
const existing = this.registry.all().find((r) => r.native?.type === type);
|
|
8635
8684
|
if (existing) return this.handles.get(existing.id) ?? new IndicatorHandleImpl(existing.id, existing.title, this, void 0, type);
|
|
8636
8685
|
}
|
|
8637
|
-
const id = this.registry.nextId("native");
|
|
8686
|
+
const id = this.registry.nextId("native", (candidate) => this.handles.has(candidate));
|
|
8638
8687
|
const title = descriptor?.title ?? type;
|
|
8639
8688
|
const handle = new IndicatorHandleImpl(id, title, this, void 0, type);
|
|
8640
8689
|
this.handles.set(id, handle);
|
|
@@ -8912,6 +8961,24 @@ var Vela = (function (exports) {
|
|
|
8912
8961
|
this.events.clear();
|
|
8913
8962
|
}
|
|
8914
8963
|
// ── internals ───────────────────────────────────────────────
|
|
8964
|
+
/**
|
|
8965
|
+
* The id a new script indicator runs under: the host's when supplied, else a minted
|
|
8966
|
+
* one. A host id is opaque but must be a non-empty string, and it must not be live
|
|
8967
|
+
* on this chart — a duplicate is a programming error surfaced synchronously, never
|
|
8968
|
+
* renamed behind the caller's back (the whole point of supplying one is that
|
|
8969
|
+
* `handle.id` equals what was passed). "Live" reads both the records and the handle
|
|
8970
|
+
* map: a fail-soft handle never enters the registry but still owns its id.
|
|
8971
|
+
*/
|
|
8972
|
+
claimIndicatorId(requested) {
|
|
8973
|
+
if (requested === void 0) return this.registry.nextId("ind", (candidate) => this.handles.has(candidate));
|
|
8974
|
+
if (typeof requested !== "string" || requested.length === 0) {
|
|
8975
|
+
throw new TypeError("[vela] addIndicator: `id` must be a non-empty string");
|
|
8976
|
+
}
|
|
8977
|
+
if (this.handles.has(requested) || this.registry.get(requested)) {
|
|
8978
|
+
throw new Error(`[vela] addIndicator: indicator id "${requested}" is already live on this chart`);
|
|
8979
|
+
}
|
|
8980
|
+
return requested;
|
|
8981
|
+
}
|
|
8915
8982
|
async startIndicator(id, source, options, handle) {
|
|
8916
8983
|
try {
|
|
8917
8984
|
await this.readyPromise;
|
|
@@ -23374,6 +23441,17 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23374
23441
|
for (const t of chartTypes()) if (t.id === style) return (t.basePainting ?? "candles") === "candles";
|
|
23375
23442
|
return false;
|
|
23376
23443
|
}
|
|
23444
|
+
var CANDLE_OVERRIDE_KEYS = [
|
|
23445
|
+
"candleUpColor",
|
|
23446
|
+
"candleDownColor",
|
|
23447
|
+
"candleBodyVisible",
|
|
23448
|
+
"candleBorderVisible",
|
|
23449
|
+
"candleBorderUpColor",
|
|
23450
|
+
"candleBorderDownColor",
|
|
23451
|
+
"candleWickVisible",
|
|
23452
|
+
"candleWickUpColor",
|
|
23453
|
+
"candleWickDownColor"
|
|
23454
|
+
];
|
|
23377
23455
|
function candleOverrideFor(style, bags) {
|
|
23378
23456
|
if (!hasOwnCandlePaint(style)) return null;
|
|
23379
23457
|
const bag = bags[style] ?? {};
|
|
@@ -23445,7 +23523,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23445
23523
|
function clampSpacing(v) {
|
|
23446
23524
|
return v < 0.1 ? 0.1 : v > 10 ? 10 : v;
|
|
23447
23525
|
}
|
|
23448
|
-
function factoryResetConfig(factory) {
|
|
23526
|
+
function factoryResetConfig(factory, priceStyle = factory.series.style) {
|
|
23449
23527
|
const bag = {};
|
|
23450
23528
|
for (const t of chartTypes()) {
|
|
23451
23529
|
const section = t.settings;
|
|
@@ -23467,10 +23545,16 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23467
23545
|
if (!section.instances) addRows(section.rows);
|
|
23468
23546
|
bag[t.id] = defaults2;
|
|
23469
23547
|
}
|
|
23548
|
+
for (const t of chartTypes()) {
|
|
23549
|
+
if (!hasOwnCandlePaint(t.id)) continue;
|
|
23550
|
+
const defaults2 = bag[t.id] ?? {};
|
|
23551
|
+
for (const key of CANDLE_OVERRIDE_KEYS) defaults2[key] = null;
|
|
23552
|
+
bag[t.id] = defaults2;
|
|
23553
|
+
}
|
|
23470
23554
|
for (const [typeId, vals] of Object.entries(factory.chartTypes)) {
|
|
23471
23555
|
bag[typeId] = { ...bag[typeId] ?? {}, ...vals };
|
|
23472
23556
|
}
|
|
23473
|
-
return { ...factory, chartTypes: bag };
|
|
23557
|
+
return { ...factory, chartTypes: bag, series: { ...factory.series, style: priceStyle } };
|
|
23474
23558
|
}
|
|
23475
23559
|
function mergeConfig(base, patch) {
|
|
23476
23560
|
const p = asObject(patch);
|
|
@@ -25849,11 +25933,8 @@ void main() {
|
|
|
25849
25933
|
for (const [id, v] of Object.entries(g)) if (typeof v === "boolean") groups[id] = v;
|
|
25850
25934
|
return { visible: typeof p.visible === "boolean" ? p.visible : base.visible, groups };
|
|
25851
25935
|
}
|
|
25852
|
-
function
|
|
25853
|
-
|
|
25854
|
-
const chosen = state.groups[groupId];
|
|
25855
|
-
if (typeof chosen === "boolean") return chosen;
|
|
25856
|
-
return groups.find((g) => g.id === groupId)?.visible !== false;
|
|
25936
|
+
function markGroupVisible2(state, groupId, groups) {
|
|
25937
|
+
return markGroupVisible(state.groups, groupId, groups);
|
|
25857
25938
|
}
|
|
25858
25939
|
|
|
25859
25940
|
// src/renderers/native/core/SceneGraph.ts
|
|
@@ -28029,13 +28110,29 @@ void main() {
|
|
|
28029
28110
|
const img = deps.icons.get(mark.glyph.icon, ink, symbolPx, deps.dpr);
|
|
28030
28111
|
if (img) ctx.drawImage(img, center.x - symbolPx / 2, center.y - symbolPx / 2, symbolPx, symbolPx);
|
|
28031
28112
|
} else if (mark.glyph.letter) {
|
|
28113
|
+
const letter = mark.glyph.letter.slice(0, 2);
|
|
28032
28114
|
ctx.fillStyle = ink;
|
|
28033
|
-
|
|
28034
|
-
ctx.
|
|
28115
|
+
let px = letterFontPx(size3, letter);
|
|
28116
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
28117
|
+
if (letter.length > 1) {
|
|
28118
|
+
px = fitLetterPx(px, ctx.measureText(letter).width, symbolInnerWidth(shape, size3));
|
|
28119
|
+
ctx.font = `600 ${px}px ${deps.fontFamily}`;
|
|
28120
|
+
}
|
|
28121
|
+
ctx.fillText(letter, center.x, center.y + 0.5);
|
|
28035
28122
|
}
|
|
28036
28123
|
}
|
|
28037
28124
|
ctx.restore();
|
|
28038
28125
|
}
|
|
28126
|
+
function letterFontPx(size3, letter) {
|
|
28127
|
+
return Math.round(size3 * (letter.length > 1 ? 0.38 : 0.58));
|
|
28128
|
+
}
|
|
28129
|
+
function symbolInnerWidth(shape, size3) {
|
|
28130
|
+
return (shape === "pin" ? size3 * 0.82 : size3) - 3;
|
|
28131
|
+
}
|
|
28132
|
+
function fitLetterPx(px, width, inner) {
|
|
28133
|
+
if (!(width > inner) || !(width > 0)) return px;
|
|
28134
|
+
return Math.max(4, Math.floor(px * inner / width));
|
|
28135
|
+
}
|
|
28039
28136
|
function traceShape(ctx, shape, x, y, size3) {
|
|
28040
28137
|
const r = size3 / 2;
|
|
28041
28138
|
ctx.beginPath();
|
|
@@ -28237,7 +28334,7 @@ void main() {
|
|
|
28237
28334
|
this.markLayout = layoutMarkLane({
|
|
28238
28335
|
marks: scene.timelineMarks,
|
|
28239
28336
|
groups: scene.markGroups,
|
|
28240
|
-
hidden: (groupId) => !
|
|
28337
|
+
hidden: (groupId) => !markGroupVisible2(scene.marks, groupId, scene.markGroups),
|
|
28241
28338
|
barTimes: this.barTimes(scene),
|
|
28242
28339
|
intervalMs: coords.barInterval,
|
|
28243
28340
|
xOf: (bar) => coords.logicalToX(bar),
|
|
@@ -29004,6 +29101,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29004
29101
|
muted and non-interactive. Applied to each row's children so it survives display:contents;
|
|
29005
29102
|
!important beats the inline opacity on labels. */
|
|
29006
29103
|
.vela-sd-soft>*{opacity:0.4 !important;pointer-events:none !important;}
|
|
29104
|
+
/* A mark group nested under a parent group on the Events tab: indented one step per level
|
|
29105
|
+
(--vela-sd-depth). The indent rides the first child (the switch) as a MARGIN \u2014 padding
|
|
29106
|
+
would push the switch's own tick out of its box. */
|
|
29107
|
+
.vela-sd-nested>*:first-child{margin-left:calc(var(--vela-sd-depth,1)*24px);}
|
|
29007
29108
|
/* \u2500\u2500 mobile presentation (.vela-sd-mobile on the scrim; structural sizes are inline in open()) \u2500\u2500
|
|
29008
29109
|
The tab rail becomes a burger-opened overlay sidebar; the group TOC becomes a sticky
|
|
29009
29110
|
row of horizontally scrollable tabs; the instance strip scrolls instead of wrapping;
|
|
@@ -29052,6 +29153,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29052
29153
|
this.hostSections = [];
|
|
29053
29154
|
/** The timeline-mark groups (defined + named by marks) — one checkbox each on the Events tab. */
|
|
29054
29155
|
this.markGroups = [];
|
|
29156
|
+
/** A group's OWN switch (what its checkbox shows) — a child under an off parent keeps its own state. */
|
|
29157
|
+
this.markGroupOwnVisible = () => true;
|
|
29055
29158
|
this.markGroupVisible = () => true;
|
|
29056
29159
|
/** The Canvas → Theme row: current app theme + where a pick is raised. The row is a
|
|
29057
29160
|
* host callback, NOT a config patch — the app theme stays out of the persisted
|
|
@@ -29069,6 +29172,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29069
29172
|
this.activeSection = null;
|
|
29070
29173
|
/** Mobile chrome: fullscreen card, burger-opened section sidebar, TOC as top tabs. */
|
|
29071
29174
|
this.mobileLayout = false;
|
|
29175
|
+
/** Opens/closes the mobile section sidebar of the CURRENT build (the burger in the
|
|
29176
|
+
* shell header outlives pane rebuilds, so it reaches the rail through here). */
|
|
29177
|
+
this.toggleRail = null;
|
|
29072
29178
|
/** The visibility policy: setting ids hidden by the host (subtree semantics). */
|
|
29073
29179
|
this.hiddenSettings = /* @__PURE__ */ new Set();
|
|
29074
29180
|
if (getComputedStyle(container).position === "static") container.style.position = "relative";
|
|
@@ -29078,7 +29184,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29078
29184
|
this.hostSections = sections;
|
|
29079
29185
|
}
|
|
29080
29186
|
/** The timeline-mark groups and their current visibility — the Events tab's rows on next open. */
|
|
29081
|
-
setMarkGroups(groups, visible) {
|
|
29187
|
+
setMarkGroups(groups, visible, ownVisible = visible) {
|
|
29188
|
+
this.markGroupOwnVisible = ownVisible;
|
|
29082
29189
|
this.markGroups = groups;
|
|
29083
29190
|
this.markGroupVisible = visible;
|
|
29084
29191
|
}
|
|
@@ -29144,7 +29251,6 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29144
29251
|
this.onReset = onReset ?? null;
|
|
29145
29252
|
ensureControlStyles();
|
|
29146
29253
|
const mobile = this.mobileLayout;
|
|
29147
|
-
let toggleRail = null;
|
|
29148
29254
|
let burger;
|
|
29149
29255
|
if (mobile) {
|
|
29150
29256
|
burger = document.createElement("button");
|
|
@@ -29152,8 +29258,83 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29152
29258
|
burger.className = "vela-sd-burger";
|
|
29153
29259
|
burger.innerHTML = iconAt("burger", 16);
|
|
29154
29260
|
burger.title = "Sections";
|
|
29155
|
-
burger.addEventListener("click", () => toggleRail?.());
|
|
29261
|
+
burger.addEventListener("click", () => this.toggleRail?.());
|
|
29156
29262
|
}
|
|
29263
|
+
const ui = new Dialog({
|
|
29264
|
+
host: this.container,
|
|
29265
|
+
title: "Chart settings",
|
|
29266
|
+
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
29267
|
+
// machine locks pointer events on the whole body, killing the chart, the
|
|
29268
|
+
// legend, and the body-portaled popovers (color picker, select lists).
|
|
29269
|
+
modal: false,
|
|
29270
|
+
contained: true,
|
|
29271
|
+
align: "top",
|
|
29272
|
+
draggable: !mobile,
|
|
29273
|
+
flush: true,
|
|
29274
|
+
className: "vela-dialog--settings",
|
|
29275
|
+
headerStart: burger,
|
|
29276
|
+
closeOnBackdrop: true,
|
|
29277
|
+
footer: (foot) => {
|
|
29278
|
+
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
29279
|
+
const resetBtn = document.createElement("button");
|
|
29280
|
+
resetBtn.type = "button";
|
|
29281
|
+
resetBtn.textContent = "Reset defaults";
|
|
29282
|
+
resetBtn.className = "vela-sd-btn";
|
|
29283
|
+
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
29284
|
+
foot.appendChild(resetBtn);
|
|
29285
|
+
},
|
|
29286
|
+
// A close signal may only close ITS OWN dialog: the machine reports the exit
|
|
29287
|
+
// asynchronously, so a close-then-reopen in one tick would otherwise see the
|
|
29288
|
+
// old instance's signal tear down the freshly opened one.
|
|
29289
|
+
onOpenChange: (open2) => {
|
|
29290
|
+
if (!open2 && this.ui === ui) this.close();
|
|
29291
|
+
}
|
|
29292
|
+
});
|
|
29293
|
+
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
29294
|
+
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
29295
|
+
this.root = ui.positioner;
|
|
29296
|
+
this.ui = ui;
|
|
29297
|
+
const panes = this.buildContent(ui, config, section, mobile);
|
|
29298
|
+
ui.show();
|
|
29299
|
+
this.layoutPanes(panes);
|
|
29300
|
+
}
|
|
29301
|
+
/**
|
|
29302
|
+
* Re-seed every control of an OPEN dialog from `config`, in place: the shell stays
|
|
29303
|
+
* (no close/open transition), only the tab rail and panes rebuild, landing back on
|
|
29304
|
+
* the current tab. The reset path — the restored values must show without the
|
|
29305
|
+
* dialog re-entering. No-op while closed.
|
|
29306
|
+
*/
|
|
29307
|
+
refresh(config) {
|
|
29308
|
+
const ui = this.ui;
|
|
29309
|
+
if (!ui) return;
|
|
29310
|
+
closeOpenPopovers();
|
|
29311
|
+
closeWidthPopover();
|
|
29312
|
+
for (const dispose of this.hintTips) dispose();
|
|
29313
|
+
this.hintTips = [];
|
|
29314
|
+
this.config = config;
|
|
29315
|
+
ui.body.replaceChildren();
|
|
29316
|
+
this.layoutPanes(this.buildContent(ui, config, this.activeSection ?? void 0, this.mobileLayout));
|
|
29317
|
+
}
|
|
29318
|
+
/** Structured chart-type panes (instance strip / group TOC) own their layout and
|
|
29319
|
+
* tag their rows hosts instead; each host gets its own field grid. Runs on a SHOWN
|
|
29320
|
+
* dialog — the grids measure their labels. */
|
|
29321
|
+
layoutPanes(panes) {
|
|
29322
|
+
for (const el of panes) {
|
|
29323
|
+
const hosts = [...el.querySelectorAll("[data-sd-rows-host]")];
|
|
29324
|
+
if (hosts.length === 0) {
|
|
29325
|
+
this.layoutSettingsGrids(el);
|
|
29326
|
+
continue;
|
|
29327
|
+
}
|
|
29328
|
+
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29329
|
+
}
|
|
29330
|
+
}
|
|
29331
|
+
/**
|
|
29332
|
+
* Build the tab rail + one pane per section into `ui.body` (the linear `body` of
|
|
29333
|
+
* section markers and rows is split afterwards), select `section` (the active
|
|
29334
|
+
* style's own tab when none is asked for), and return the pane elements for
|
|
29335
|
+
* {@link layoutPanes}.
|
|
29336
|
+
*/
|
|
29337
|
+
buildContent(ui, config, section, mobile) {
|
|
29157
29338
|
const body = document.createElement("div");
|
|
29158
29339
|
body.style.cssText = "display:flex;flex-direction:column;gap:0;";
|
|
29159
29340
|
const sid = (el, id) => {
|
|
@@ -29357,9 +29538,28 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29357
29538
|
if (this.markGroups.length > 0) {
|
|
29358
29539
|
body.append(sid(this.section("Events"), MARKS_SETTINGS_ID));
|
|
29359
29540
|
body.append(sid(this.sectionTitle("Visible events"), MARKS_GROUPS_SETTINGS_ID));
|
|
29360
|
-
|
|
29361
|
-
|
|
29541
|
+
const rowsById = /* @__PURE__ */ new Map();
|
|
29542
|
+
const rows = markGroupRows(this.markGroups);
|
|
29543
|
+
const refreshDimming = () => {
|
|
29544
|
+
for (const { group } of rows) {
|
|
29545
|
+
const el = rowsById.get(group.id);
|
|
29546
|
+
if (!el || group.parent === void 0) continue;
|
|
29547
|
+
el.classList.toggle("vela-sd-soft", !this.markGroupVisible(group.parent));
|
|
29548
|
+
}
|
|
29549
|
+
};
|
|
29550
|
+
for (const { group: g, depth } of rows) {
|
|
29551
|
+
const row = this.boolRow(g.label, this.markGroupOwnVisible(g.id), (v) => {
|
|
29552
|
+
this.emit({ marks: { groups: { [g.id]: v } } });
|
|
29553
|
+
refreshDimming();
|
|
29554
|
+
});
|
|
29555
|
+
if (depth > 0) {
|
|
29556
|
+
row.classList.add("vela-sd-nested");
|
|
29557
|
+
row.style.setProperty("--vela-sd-depth", String(depth));
|
|
29558
|
+
}
|
|
29559
|
+
rowsById.set(g.id, row);
|
|
29560
|
+
body.append(sid(row, markGroupSettingsId(g.id)));
|
|
29362
29561
|
}
|
|
29562
|
+
refreshDimming();
|
|
29363
29563
|
}
|
|
29364
29564
|
renderChartTypeSections("end");
|
|
29365
29565
|
renderHostSections("end");
|
|
@@ -29387,8 +29587,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29387
29587
|
if (mobile) {
|
|
29388
29588
|
railScrim = document.createElement("div");
|
|
29389
29589
|
railScrim.className = "vela-sd-railscrim";
|
|
29390
|
-
railScrim.addEventListener("click", () => toggleRail?.());
|
|
29391
|
-
toggleRail = (open2) => {
|
|
29590
|
+
railScrim.addEventListener("click", () => this.toggleRail?.());
|
|
29591
|
+
this.toggleRail = (open2) => {
|
|
29392
29592
|
const on = open2 ?? !rail.classList.contains("open");
|
|
29393
29593
|
rail.classList.toggle("open", on);
|
|
29394
29594
|
railScrim?.classList.toggle("open", on);
|
|
@@ -29428,35 +29628,6 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29428
29628
|
});
|
|
29429
29629
|
if (hidActive) activate(0);
|
|
29430
29630
|
};
|
|
29431
|
-
const ui = new Dialog({
|
|
29432
|
-
host: this.container,
|
|
29433
|
-
title: "Chart settings",
|
|
29434
|
-
// Non-modal: a live-edit dialog must leave the page interactive — a modal
|
|
29435
|
-
// machine locks pointer events on the whole body, killing the chart, the
|
|
29436
|
-
// legend, and the body-portaled popovers (color picker, select lists).
|
|
29437
|
-
modal: false,
|
|
29438
|
-
contained: true,
|
|
29439
|
-
align: "top",
|
|
29440
|
-
draggable: !mobile,
|
|
29441
|
-
flush: true,
|
|
29442
|
-
className: "vela-dialog--settings",
|
|
29443
|
-
headerStart: burger,
|
|
29444
|
-
closeOnBackdrop: true,
|
|
29445
|
-
footer: (foot) => {
|
|
29446
|
-
foot.style.cssText = `padding:10px 14px;display:flex;align-items:center;justify-content:flex-start;gap:8px;`;
|
|
29447
|
-
const resetBtn = document.createElement("button");
|
|
29448
|
-
resetBtn.type = "button";
|
|
29449
|
-
resetBtn.textContent = "Reset defaults";
|
|
29450
|
-
resetBtn.className = "vela-sd-btn";
|
|
29451
|
-
resetBtn.addEventListener("click", () => this.onReset?.());
|
|
29452
|
-
foot.appendChild(resetBtn);
|
|
29453
|
-
},
|
|
29454
|
-
onOpenChange: (open2) => {
|
|
29455
|
-
if (!open2) this.close();
|
|
29456
|
-
}
|
|
29457
|
-
});
|
|
29458
|
-
if (mobile) ui.positioner.classList.add("vela-sd-mobile");
|
|
29459
|
-
ui.positioner.style.paddingTop = mobile ? "0" : "8vh";
|
|
29460
29631
|
const activate = (idx) => {
|
|
29461
29632
|
panes.forEach((p, i) => {
|
|
29462
29633
|
p.el.style.display = i === idx ? "block" : "none";
|
|
@@ -29465,7 +29636,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29465
29636
|
this.activeSection = panes[idx]?.title ?? null;
|
|
29466
29637
|
if (mobile) {
|
|
29467
29638
|
ui.titleEl.textContent = panes[idx]?.title ?? "Chart settings";
|
|
29468
|
-
toggleRail?.(false);
|
|
29639
|
+
this.toggleRail?.(false);
|
|
29469
29640
|
}
|
|
29470
29641
|
};
|
|
29471
29642
|
panes.forEach((p, i) => {
|
|
@@ -29480,17 +29651,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29480
29651
|
shell.append(rail, paneHost);
|
|
29481
29652
|
if (railScrim) shell.append(railScrim);
|
|
29482
29653
|
ui.body.appendChild(shell);
|
|
29483
|
-
|
|
29484
|
-
this.ui = ui;
|
|
29485
|
-
ui.show();
|
|
29486
|
-
for (const p of panes) {
|
|
29487
|
-
const hosts = [...p.el.querySelectorAll("[data-sd-rows-host]")];
|
|
29488
|
-
if (hosts.length === 0) {
|
|
29489
|
-
this.layoutSettingsGrids(p.el);
|
|
29490
|
-
continue;
|
|
29491
|
-
}
|
|
29492
|
-
for (const h of hosts) this.layoutSettingsGrids(h);
|
|
29493
|
-
}
|
|
29654
|
+
return panes.map((p) => p.el);
|
|
29494
29655
|
}
|
|
29495
29656
|
close() {
|
|
29496
29657
|
closeOpenPopovers();
|
|
@@ -29498,6 +29659,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
29498
29659
|
const ui = this.ui;
|
|
29499
29660
|
this.ui = null;
|
|
29500
29661
|
this.root = null;
|
|
29662
|
+
this.toggleRail = null;
|
|
29501
29663
|
this.tabs = [];
|
|
29502
29664
|
for (const dispose of this.hintTips) dispose();
|
|
29503
29665
|
this.hintTips = [];
|
|
@@ -37597,7 +37759,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37597
37759
|
}
|
|
37598
37760
|
this.settingsDialog.setTheme(this.theme);
|
|
37599
37761
|
this.settingsDialog.setHostSections(this.hostSettingsSections);
|
|
37600
|
-
this.settingsDialog.setMarkGroups(
|
|
37762
|
+
this.settingsDialog.setMarkGroups(
|
|
37763
|
+
this.markGroupsForEventsTab(),
|
|
37764
|
+
(id) => markGroupVisible2(this.scene.marks, id, this.scene.markGroups),
|
|
37765
|
+
(id) => markGroupOwnVisible(this.scene.marks.groups, id, this.scene.markGroups)
|
|
37766
|
+
);
|
|
37601
37767
|
this.settingsDialog.setHiddenSettings(this.hiddenSettings);
|
|
37602
37768
|
this.syncThemeControl();
|
|
37603
37769
|
this.settingsDialog.toggle(
|
|
@@ -37605,13 +37771,24 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37605
37771
|
(patch) => this.applyConfig(patch),
|
|
37606
37772
|
(json) => this.applyConfig(json),
|
|
37607
37773
|
() => {
|
|
37608
|
-
if (this.factoryConfig) this.applyConfig(
|
|
37609
|
-
this.settingsDialog?.
|
|
37610
|
-
this.openSettingsDialog();
|
|
37774
|
+
if (this.factoryConfig) this.applyConfig(this.factoryResetDocument(this.factoryConfig));
|
|
37775
|
+
this.settingsDialog?.refresh(this.getConfig());
|
|
37611
37776
|
},
|
|
37612
37777
|
section
|
|
37613
37778
|
);
|
|
37614
37779
|
}
|
|
37780
|
+
/**
|
|
37781
|
+
* The document "Reset defaults" applies: every setting back to its first-run value,
|
|
37782
|
+
* with two things that are NOT settings held or resolved here — the price style
|
|
37783
|
+
* stays the one the user is looking at, and the timeline-mark groups (an additive
|
|
37784
|
+
* merge, like the type bags) are named back to their host-declared visibility.
|
|
37785
|
+
*/
|
|
37786
|
+
factoryResetDocument(factory) {
|
|
37787
|
+
const doc = factoryResetConfig(factory, this.scene.priceStyle);
|
|
37788
|
+
const groups = { ...doc.marks.groups };
|
|
37789
|
+
for (const g of this.markGroupsInUse()) groups[g.id] = g.visible !== false;
|
|
37790
|
+
return { ...doc, marks: { ...doc.marks, groups } };
|
|
37791
|
+
}
|
|
37615
37792
|
/** Close the in-chart dialogs (indicator settings + chart-settings gear). No-op when none are open. */
|
|
37616
37793
|
closeDialogs() {
|
|
37617
37794
|
this.inputsUI?.closeOpenDialog();
|
|
@@ -38674,6 +38851,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38674
38851
|
markGroupsInUse() {
|
|
38675
38852
|
return effectiveMarkGroups(this.scene.timelineMarks, this.scene.markGroups);
|
|
38676
38853
|
}
|
|
38854
|
+
/**
|
|
38855
|
+
* The groups as the Events tab lists them: nested only under a DEFINED parent. The
|
|
38856
|
+
* painter's visibility chain resolves parents against the defined groups alone, so a
|
|
38857
|
+
* parent that marks merely name must not nest (and dim) a child the painter still shows.
|
|
38858
|
+
*/
|
|
38859
|
+
markGroupsForEventsTab() {
|
|
38860
|
+
const defined = new Set(this.scene.markGroups.map((g) => g.id));
|
|
38861
|
+
return this.markGroupsInUse().map((g) => g.parent !== void 0 && !defined.has(g.parent) ? { ...g, parent: void 0 } : g);
|
|
38862
|
+
}
|
|
38677
38863
|
onViewportChange(cb) {
|
|
38678
38864
|
this.viewportCbs.add(cb);
|
|
38679
38865
|
return () => this.viewportCbs.delete(cb);
|
|
@@ -42843,6 +43029,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
42843
43029
|
}
|
|
42844
43030
|
|
|
42845
43031
|
// src/Vela.ts
|
|
43032
|
+
var asError = (err) => err instanceof Error ? err : new Error(String(err));
|
|
42846
43033
|
var Vela = class {
|
|
42847
43034
|
constructor(container, options = {}, deps = {}) {
|
|
42848
43035
|
registerBuiltinChartTypes();
|
|
@@ -42966,7 +43153,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
42966
43153
|
* fetch from — the same relationship `script:run` has to `context:changed`.
|
|
42967
43154
|
*/
|
|
42968
43155
|
runScript(source, options) {
|
|
42969
|
-
|
|
43156
|
+
let handle;
|
|
43157
|
+
try {
|
|
43158
|
+
handle = this.addIndicator(source, options);
|
|
43159
|
+
} catch (err) {
|
|
43160
|
+
return Promise.resolve({ ok: false, run: null, error: asError(err), onUpdate: () => () => void 0, remove: () => void 0 });
|
|
43161
|
+
}
|
|
42970
43162
|
const updates = /* @__PURE__ */ new Set();
|
|
42971
43163
|
const drop = () => {
|
|
42972
43164
|
try {
|
|
@@ -43010,7 +43202,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
43010
43202
|
});
|
|
43011
43203
|
}
|
|
43012
43204
|
runIndicator(source, options) {
|
|
43013
|
-
|
|
43205
|
+
let handle;
|
|
43206
|
+
try {
|
|
43207
|
+
handle = this.addIndicator(source, options);
|
|
43208
|
+
} catch (err) {
|
|
43209
|
+
return Promise.resolve({ ok: false, handle: null, error: asError(err), context: null });
|
|
43210
|
+
}
|
|
43014
43211
|
return new Promise((resolve) => {
|
|
43015
43212
|
const offReady = handle.on("ready", () => {
|
|
43016
43213
|
offReady();
|