@luxalgo/vela 0.8.1 → 0.8.3
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 +1 -1
- package/dist/{DataProvider-DwFIoV4r.d.ts → DataProvider-oH78tYXL.d.ts} +1 -1
- package/dist/{DataProvider-BrlxntPN.d.cts → DataProvider-wbtJ3-Sa.d.cts} +1 -1
- package/dist/{chunk-DBDXKZUF.js → chunk-2IPXYM4B.js} +306 -177
- package/dist/{chunk-NELQJCGK.js → chunk-G7B7ZCBF.js} +167 -10
- package/dist/{chunk-ATGTCWHJ.js → chunk-HMHA733L.js} +473 -196
- package/dist/{chunk-EZ5FWVLA.js → chunk-KWI3YDBW.js} +343 -17
- package/dist/{chunk-LMBYEGUR.js → chunk-TO3GIK7Z.js} +71 -6
- package/dist/{chunk-BZQM2XO7.js → chunk-VHGACEHO.js} +62 -1
- package/dist/{contributions-tjNExo1o.d.ts → contributions-BhOBRenr.d.cts} +79 -12
- package/dist/{contributions-CtlptxR8.d.cts → contributions-HGqnNb_P.d.ts} +79 -12
- package/dist/index.cjs +733 -113
- package/dist/index.d.cts +34 -14
- package/dist/index.d.ts +34 -14
- package/dist/index.js +5 -5
- package/dist/{options-BnEPUuVV.d.ts → options-lMUwmRwh.d.cts} +69 -4
- package/dist/{options-BnEPUuVV.d.cts → options-lMUwmRwh.d.ts} +69 -4
- package/dist/{plugin-e2WUEtiI.d.ts → plugin-Cr2Pj8q8.d.ts} +4 -4
- package/dist/{plugin-BWxJRKzS.d.cts → plugin-Do5J5xuF.d.cts} +4 -4
- package/dist/plugin.cjs +38 -6
- package/dist/plugin.d.cts +5 -5
- package/dist/plugin.d.ts +5 -5
- package/dist/plugin.js +3 -3
- 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/{side-panel-HF0IAzwf.d.cts → side-panel-_2UK1gSm.d.cts} +58 -3
- package/dist/{side-panel-HF0IAzwf.d.ts → side-panel-_2UK1gSm.d.ts} +58 -3
- package/dist/{statusline-model-DFY7tuHj.d.cts → statusline-model-B-2xGlB_.d.cts} +3 -3
- package/dist/{statusline-model-zKXtGIjj.d.ts → statusline-model-CY1wzxHg.d.ts} +3 -3
- package/dist/ui.cjs +298 -13
- package/dist/ui.d.cts +132 -74
- package/dist/ui.d.ts +132 -74
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +734 -114
- package/dist/vela.global.min.js +86 -52
- package/dist/widget.cjs +1227 -211
- package/dist/widget.d.cts +36 -12
- package/dist/widget.d.ts +36 -12
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +1226 -211
- package/dist/workspace.d.cts +47 -20
- package/dist/workspace.d.ts +47 -20
- package/dist/workspace.js +6 -6
- package/package.json +2 -2
- /package/dist/{chunk-JU4CWEBF.js → chunk-W5YPYZ2P.js} +0 -0
package/dist/vela.global.js
CHANGED
|
@@ -214,7 +214,7 @@ var Vela = (function (exports) {
|
|
|
214
214
|
this.controller.applyProps(this.id, values);
|
|
215
215
|
}
|
|
216
216
|
updateCode(source) {
|
|
217
|
-
this.controller.updateCode(this.id, source);
|
|
217
|
+
return this.controller.updateCode(this.id, source);
|
|
218
218
|
}
|
|
219
219
|
setVisible(visible) {
|
|
220
220
|
this.controller.setVisible(this.id, visible);
|
|
@@ -617,6 +617,7 @@ var Vela = (function (exports) {
|
|
|
617
617
|
registerIcon("plus", S('<path d="M8 2.8v10.4M2.8 8h10.4"/>'));
|
|
618
618
|
registerIcon("minus", S('<path d="M2.8 8h10.4"/>'));
|
|
619
619
|
registerIcon("maximize", S('<path d="M2.5 6V3a.5.5 0 0 1 .5-.5h3M10 2.5h3a.5.5 0 0 1 .5.5v3M13.5 10v3a.5.5 0 0 1-.5.5h-3M6 13.5H3a.5.5 0 0 1-.5-.5v-3"/>'));
|
|
620
|
+
registerIcon("restore", S('<path d="M6 2.5v3a.5.5 0 0 1-.5.5h-3M13.5 6h-3a.5.5 0 0 1-.5-.5v-3M10 13.5v-3a.5.5 0 0 1 .5-.5h3M2.5 10h3a.5.5 0 0 1 .5.5v3"/>'));
|
|
620
621
|
registerIcon("restore", S('<path d="M6.2 2.5v3.7H2.5M9.8 13.5V9.8h3.7M13.5 6.2H9.8V2.5M2.5 9.8h3.7v3.7"/>'));
|
|
621
622
|
registerIcon("star", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>'));
|
|
622
623
|
registerIcon("star-filled", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>', 'fill="currentColor"'));
|
|
@@ -3275,6 +3276,10 @@ var Vela = (function (exports) {
|
|
|
3275
3276
|
|
|
3276
3277
|
// src/core/drawings/types/FibLevels.ts
|
|
3277
3278
|
var FibLevels = class extends FibRatios {
|
|
3279
|
+
/** Price of a level for the two anchor prices; subclasses override to change which anchor ratio 0 sits on. */
|
|
3280
|
+
levelPrice(ratio, p1, p2) {
|
|
3281
|
+
return p1 + ratio * (p2 - p1);
|
|
3282
|
+
}
|
|
3278
3283
|
/** Per-level pixel line + price for the ENABLED levels, spanning the anchors' time range. */
|
|
3279
3284
|
levelLines(proj) {
|
|
3280
3285
|
const a = this.anchors[0];
|
|
@@ -3284,11 +3289,10 @@ var Vela = (function (exports) {
|
|
|
3284
3289
|
const xb = proj.xOf(b.time);
|
|
3285
3290
|
const x1 = Math.min(xa, xb);
|
|
3286
3291
|
const x2 = Math.max(xa, xb);
|
|
3287
|
-
const delta = b.price - a.price;
|
|
3288
3292
|
const out = [];
|
|
3289
3293
|
for (const lv of this.levels) {
|
|
3290
3294
|
if (!lv.enabled) continue;
|
|
3291
|
-
const price =
|
|
3295
|
+
const price = this.levelPrice(lv.ratio, a.price, b.price);
|
|
3292
3296
|
const y = proj.yOf(price, this.paneId);
|
|
3293
3297
|
if (y == null) continue;
|
|
3294
3298
|
out.push({ ratio: lv.ratio, color: lv.color, label: lv.label, price, x1, x2, y });
|
|
@@ -3328,8 +3332,7 @@ var Vela = (function (exports) {
|
|
|
3328
3332
|
const a = this.anchors[0];
|
|
3329
3333
|
const b = this.anchors[1];
|
|
3330
3334
|
if (!a || !b) return null;
|
|
3331
|
-
const
|
|
3332
|
-
const prices = this.levels.filter((l) => l.enabled).map((l) => a.price + l.ratio * delta);
|
|
3335
|
+
const prices = this.levels.filter((l) => l.enabled).map((l) => this.levelPrice(l.ratio, a.price, b.price));
|
|
3333
3336
|
if (prices.length === 0) return null;
|
|
3334
3337
|
return { min: Math.min(...prices), max: Math.max(...prices) };
|
|
3335
3338
|
}
|
|
@@ -3415,13 +3418,24 @@ var Vela = (function (exports) {
|
|
|
3415
3418
|
// src/core/drawings/types/FibRetracement.ts
|
|
3416
3419
|
var LEVELS = fibLevels([0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]);
|
|
3417
3420
|
var FibRetracement = class extends FibLevels {
|
|
3418
|
-
constructor() {
|
|
3419
|
-
super(
|
|
3421
|
+
constructor(init) {
|
|
3422
|
+
super(init);
|
|
3420
3423
|
this.type = "fibretracement";
|
|
3424
|
+
if (this.reverse === void 0) this.reverse = false;
|
|
3421
3425
|
}
|
|
3422
3426
|
defaultLevels() {
|
|
3423
3427
|
return LEVELS;
|
|
3424
3428
|
}
|
|
3429
|
+
levelPrice(ratio, p1, p2) {
|
|
3430
|
+
return this.reverse ? p1 + ratio * (p2 - p1) : p2 + ratio * (p1 - p2);
|
|
3431
|
+
}
|
|
3432
|
+
writeProps() {
|
|
3433
|
+
return { ...super.writeProps(), reverse: this.reverse };
|
|
3434
|
+
}
|
|
3435
|
+
readProps(props) {
|
|
3436
|
+
super.readProps(props);
|
|
3437
|
+
this.reverse = typeof props.reverse === "boolean" ? props.reverse : true;
|
|
3438
|
+
}
|
|
3425
3439
|
};
|
|
3426
3440
|
|
|
3427
3441
|
// src/core/drawings/types/FibExtension.ts
|
|
@@ -7850,6 +7864,7 @@ var Vela = (function (exports) {
|
|
|
7850
7864
|
this.activeEngineStyle = null;
|
|
7851
7865
|
/** The chart's current price style (tracked from the renderer's change events + initial read). */
|
|
7852
7866
|
this.priceStyle = "candles";
|
|
7867
|
+
this.priceStyleWillChangeUnsub = null;
|
|
7853
7868
|
this.unresolvedUnsub = null;
|
|
7854
7869
|
/** Latest chart visible range (left/right bar times), fed to viewport-dependent scripts. */
|
|
7855
7870
|
this.visibleRange = null;
|
|
@@ -7948,6 +7963,7 @@ var Vela = (function (exports) {
|
|
|
7948
7963
|
this.renderer.onThemeSelect?.((name) => this.setTheme(resolveTheme(name)));
|
|
7949
7964
|
this.viewportUnsub = this.renderer.onViewportChange((range) => this.onViewportChange(range));
|
|
7950
7965
|
this.renderer.onPriceStyleChange?.((style) => this.syncPriceStyle(style));
|
|
7966
|
+
this.priceStyleWillChangeUnsub = this.renderer.onPriceStyleWillChange?.((from, to) => this.events.emit("priceStyle:change", { from, to })) ?? null;
|
|
7951
7967
|
const initialStyle = this.renderer.readFeature("priceStyle");
|
|
7952
7968
|
if (typeof initialStyle === "string") this.priceStyle = initialStyle;
|
|
7953
7969
|
this.barTransform = barTransformFor(initialStyle);
|
|
@@ -9234,20 +9250,26 @@ var Vela = (function (exports) {
|
|
|
9234
9250
|
/**
|
|
9235
9251
|
* IndicatorController: replace a SCRIPT indicator's source in place (see
|
|
9236
9252
|
* {@link IndicatorHandle.updateCode}). Prepare-first: the running session keeps
|
|
9237
|
-
* computing until the new source has compiled
|
|
9238
|
-
*
|
|
9253
|
+
* computing until the new source has compiled; then the new code runs on TRIAL until
|
|
9254
|
+
* its first model lands, and a failure before that puts the previous code back.
|
|
9255
|
+
* Natives have no script — warn and leave them alone.
|
|
9239
9256
|
*/
|
|
9240
9257
|
updateCode(id, source) {
|
|
9241
9258
|
const record = this.registry.get(id);
|
|
9242
9259
|
const handle = this.handles.get(id);
|
|
9243
|
-
if (!record || !handle) return;
|
|
9260
|
+
if (!record || !handle) return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator is no longer on the chart.`) });
|
|
9244
9261
|
if (record.native) {
|
|
9245
9262
|
console.warn(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`);
|
|
9246
|
-
return;
|
|
9263
|
+
return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`) });
|
|
9247
9264
|
}
|
|
9248
|
-
|
|
9249
|
-
record.
|
|
9250
|
-
|
|
9265
|
+
const busy = record.pendingSource !== void 0 || record.codeTrial !== void 0;
|
|
9266
|
+
if (!busy && source === record.source) return Promise.resolve({ ok: true, error: null });
|
|
9267
|
+
return new Promise((resolve) => {
|
|
9268
|
+
(record.codeWaiters ?? (record.codeWaiters = [])).push(resolve);
|
|
9269
|
+
if (record.pendingSource === source || record.pendingSource === void 0 && record.codeTrial && record.source === source) return;
|
|
9270
|
+
record.pendingSource = source;
|
|
9271
|
+
void this.swapSource(id, source, handle);
|
|
9272
|
+
});
|
|
9251
9273
|
}
|
|
9252
9274
|
async swapSource(id, source, handle) {
|
|
9253
9275
|
const engine = this.registry.get(id)?.engine ?? this.engineFor(this.registry.get(id)?.options?.language);
|
|
@@ -9258,26 +9280,41 @@ var Vela = (function (exports) {
|
|
|
9258
9280
|
const record2 = this.registry.get(id);
|
|
9259
9281
|
if (record2?.pendingSource === source) {
|
|
9260
9282
|
record2.pendingSource = void 0;
|
|
9261
|
-
this.
|
|
9283
|
+
this.rejectCode(id, record2, handle, err);
|
|
9262
9284
|
}
|
|
9263
9285
|
return;
|
|
9264
9286
|
}
|
|
9265
9287
|
const record = this.registry.get(id);
|
|
9266
9288
|
if (!record || record.pendingSource !== source) return;
|
|
9267
9289
|
record.pendingSource = void 0;
|
|
9290
|
+
const previous = record.codeTrial?.previous ?? {
|
|
9291
|
+
source: record.source,
|
|
9292
|
+
engine: record.engine,
|
|
9293
|
+
prepared: record.prepared,
|
|
9294
|
+
inputValues: record.inputValues,
|
|
9295
|
+
propValues: record.propValues,
|
|
9296
|
+
inputs: [...handle.inputs],
|
|
9297
|
+
props: [...handle.props]
|
|
9298
|
+
};
|
|
9299
|
+
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues, record.prepared?.inputs);
|
|
9300
|
+
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues, record.prepared?.props);
|
|
9268
9301
|
record.source = source;
|
|
9269
9302
|
record.engine = engine;
|
|
9270
9303
|
record.prepared = prepared;
|
|
9271
9304
|
handle.setSource(source);
|
|
9272
|
-
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues);
|
|
9273
|
-
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues);
|
|
9274
9305
|
handle.setSchema(prepared.inputs);
|
|
9275
9306
|
handle.setPropsSchema(prepared.props ?? []);
|
|
9276
9307
|
record.session?.stop();
|
|
9277
9308
|
record.session = void 0;
|
|
9278
9309
|
record.pendingStructural = true;
|
|
9279
9310
|
record.pendingCause = "code";
|
|
9280
|
-
if (record.hidden)
|
|
9311
|
+
if (record.hidden) {
|
|
9312
|
+
record.codeTrial = void 0;
|
|
9313
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9314
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9315
|
+
return;
|
|
9316
|
+
}
|
|
9317
|
+
record.codeTrial = { previous };
|
|
9281
9318
|
if (record.renderHandle) {
|
|
9282
9319
|
this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9283
9320
|
this.setLoading(record, true);
|
|
@@ -9285,6 +9322,46 @@ var Vela = (function (exports) {
|
|
|
9285
9322
|
this.mountLoadingPlaceholder(id, record);
|
|
9286
9323
|
}
|
|
9287
9324
|
this.executeIndicator(id, handle);
|
|
9325
|
+
if (this.bars.length === 0 && record.codeTrial) {
|
|
9326
|
+
record.codeTrial = void 0;
|
|
9327
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
/** Resolve every `updateCode` caller waiting on this record's chain of edits. */
|
|
9331
|
+
settleCode(record, result) {
|
|
9332
|
+
const waiters = record.codeWaiters;
|
|
9333
|
+
record.codeWaiters = void 0;
|
|
9334
|
+
if (waiters) for (const resolve of waiters) resolve(result);
|
|
9335
|
+
}
|
|
9336
|
+
/**
|
|
9337
|
+
* An update failed — at prepare, or on trial before its first model. A trial puts the
|
|
9338
|
+
* previous code back (source, engine, prepared script, values, schema) and restarts it;
|
|
9339
|
+
* its visuals were never dropped, so the chart keeps painting throughout. Then the
|
|
9340
|
+
* failure reports the usual way and every waiting caller learns it.
|
|
9341
|
+
*/
|
|
9342
|
+
rejectCode(id, record, handle, err) {
|
|
9343
|
+
const trial = record.codeTrial;
|
|
9344
|
+
record.codeTrial = void 0;
|
|
9345
|
+
this.fail(id, handle, err);
|
|
9346
|
+
if (trial) {
|
|
9347
|
+
const p = trial.previous;
|
|
9348
|
+
record.session?.stop();
|
|
9349
|
+
record.session = void 0;
|
|
9350
|
+
record.source = p.source;
|
|
9351
|
+
record.engine = p.engine;
|
|
9352
|
+
record.prepared = p.prepared;
|
|
9353
|
+
record.inputValues = p.inputValues;
|
|
9354
|
+
record.propValues = p.propValues;
|
|
9355
|
+
handle.setSource(p.source);
|
|
9356
|
+
handle.setSchema(p.inputs);
|
|
9357
|
+
handle.setPropsSchema(p.props);
|
|
9358
|
+
record.pendingStructural = true;
|
|
9359
|
+
record.pendingCause = void 0;
|
|
9360
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9361
|
+
if (!record.hidden) this.executeIndicator(id, handle);
|
|
9362
|
+
}
|
|
9363
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
9364
|
+
this.settleCode(record, { ok: false, error });
|
|
9288
9365
|
}
|
|
9289
9366
|
/** IndicatorController: tear down an indicator and (if now empty) its pane. */
|
|
9290
9367
|
/** Live handles of every indicator on the chart (script + native), insertion order. */
|
|
@@ -9299,6 +9376,7 @@ var Vela = (function (exports) {
|
|
|
9299
9376
|
removeIndicator(id) {
|
|
9300
9377
|
const record = this.registry.remove(id);
|
|
9301
9378
|
if (record?.native?.type === "volume") this.volumeOptedOut = true;
|
|
9379
|
+
if (record) this.settleCode(record, { ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator was removed.`) });
|
|
9302
9380
|
record?.session?.stop();
|
|
9303
9381
|
if (record?.native?.started) record.native.instance.stop();
|
|
9304
9382
|
this.handles.delete(id);
|
|
@@ -9326,6 +9404,10 @@ var Vela = (function (exports) {
|
|
|
9326
9404
|
if (!visible) {
|
|
9327
9405
|
record.session?.stop();
|
|
9328
9406
|
record.session = void 0;
|
|
9407
|
+
if (record.codeTrial) {
|
|
9408
|
+
record.codeTrial = void 0;
|
|
9409
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9410
|
+
}
|
|
9329
9411
|
if (record.native?.started) record.native.instance.suspend();
|
|
9330
9412
|
if (record.renderHandle) this.renderer.setIndicatorVisible?.(record.renderHandle, false);
|
|
9331
9413
|
else if (record.native) this.mountHiddenNativeRow(id, record);
|
|
@@ -9412,7 +9494,10 @@ var Vela = (function (exports) {
|
|
|
9412
9494
|
if (this.viewportTimer != null) clearTimeout(this.viewportTimer);
|
|
9413
9495
|
this.viewportUnsub?.();
|
|
9414
9496
|
this.paneActionUnsub?.();
|
|
9497
|
+
this.priceStyleWillChangeUnsub?.();
|
|
9498
|
+
this.priceStyleWillChangeUnsub = null;
|
|
9415
9499
|
for (const record of this.registry.all()) {
|
|
9500
|
+
this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
|
|
9416
9501
|
record.session?.stop();
|
|
9417
9502
|
if (record.native?.started) record.native.instance.stop();
|
|
9418
9503
|
}
|
|
@@ -9481,7 +9566,8 @@ var Vela = (function (exports) {
|
|
|
9481
9566
|
const record = this.registry.get(id);
|
|
9482
9567
|
if (!record || !record.engine || !record.prepared || record.hidden) return;
|
|
9483
9568
|
const mode = this.config.live && !record.prepared.reactsToViewport && record.engine.capabilities.streaming ? "live" : "static";
|
|
9484
|
-
|
|
9569
|
+
const prepared = record.prepared;
|
|
9570
|
+
const session = record.engine.execute(
|
|
9485
9571
|
{
|
|
9486
9572
|
prepared: record.prepared,
|
|
9487
9573
|
market: this.market(),
|
|
@@ -9502,6 +9588,10 @@ var Vela = (function (exports) {
|
|
|
9502
9588
|
const cause = record.pendingCause ?? "history";
|
|
9503
9589
|
if (!this.applyModel(id, model)) return;
|
|
9504
9590
|
record.pendingCause = void 0;
|
|
9591
|
+
if (record.codeTrial) {
|
|
9592
|
+
record.codeTrial = void 0;
|
|
9593
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9594
|
+
}
|
|
9505
9595
|
this.emitContextChanged(id);
|
|
9506
9596
|
this.emitScriptRun(id, cause, first2);
|
|
9507
9597
|
},
|
|
@@ -9512,9 +9602,17 @@ var Vela = (function (exports) {
|
|
|
9512
9602
|
handle.emit("alert", { id: a.id, message: a.message, title: a.title, time: a.time });
|
|
9513
9603
|
},
|
|
9514
9604
|
onWarning: (w) => this.events.emit("warning", w),
|
|
9515
|
-
onError: (err) =>
|
|
9605
|
+
onError: (err) => {
|
|
9606
|
+
if (record.codeTrial) this.rejectCode(id, record, handle, err);
|
|
9607
|
+
else this.fail(id, handle, err);
|
|
9608
|
+
}
|
|
9516
9609
|
}
|
|
9517
9610
|
);
|
|
9611
|
+
if (record.prepared !== prepared) {
|
|
9612
|
+
session.stop();
|
|
9613
|
+
return;
|
|
9614
|
+
}
|
|
9615
|
+
record.session = session;
|
|
9518
9616
|
this.emitContextChanged(id);
|
|
9519
9617
|
}
|
|
9520
9618
|
/**
|
|
@@ -10015,7 +10113,7 @@ var Vela = (function (exports) {
|
|
|
10015
10113
|
}
|
|
10016
10114
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
10017
10115
|
}
|
|
10018
|
-
function valuesOnSchema(schema, previous) {
|
|
10116
|
+
function valuesOnSchema(schema, previous, oldSchema = []) {
|
|
10019
10117
|
const out = {};
|
|
10020
10118
|
const declared = /* @__PURE__ */ new Set();
|
|
10021
10119
|
for (const s of schema) {
|
|
@@ -10023,9 +10121,22 @@ var Vela = (function (exports) {
|
|
|
10023
10121
|
declared.add(s.key);
|
|
10024
10122
|
declared.add(s.title);
|
|
10025
10123
|
}
|
|
10026
|
-
|
|
10124
|
+
const oldDefaults = /* @__PURE__ */ new Map();
|
|
10125
|
+
for (const s of oldSchema) {
|
|
10126
|
+
oldDefaults.set(s.key, s.defval);
|
|
10127
|
+
if (!oldDefaults.has(s.title)) oldDefaults.set(s.title, s.defval);
|
|
10128
|
+
}
|
|
10129
|
+
for (const [k, v] of Object.entries(previous)) {
|
|
10130
|
+
if (!declared.has(k)) continue;
|
|
10131
|
+
if (oldDefaults.has(k) && sameInputValue(oldDefaults.get(k), v)) continue;
|
|
10132
|
+
out[k] = v;
|
|
10133
|
+
}
|
|
10027
10134
|
return out;
|
|
10028
10135
|
}
|
|
10136
|
+
function sameInputValue(a, b) {
|
|
10137
|
+
if (a === b) return true;
|
|
10138
|
+
return typeof a === "object" && a !== null && typeof b === "object" && b !== null && JSON.stringify(a) === JSON.stringify(b);
|
|
10139
|
+
}
|
|
10029
10140
|
function blankedModel(model) {
|
|
10030
10141
|
return {
|
|
10031
10142
|
...model,
|
|
@@ -13587,6 +13698,66 @@ ${STATIC_DECLS}
|
|
|
13587
13698
|
return span;
|
|
13588
13699
|
}
|
|
13589
13700
|
|
|
13701
|
+
// src/ui/surface-events.ts
|
|
13702
|
+
var SURFACE_OPEN_EVENT = "vela:surface-open";
|
|
13703
|
+
var SURFACE_CLOSE_EVENT = "vela:surface-close";
|
|
13704
|
+
function announceSurface(el, open2, kind, trigger = null) {
|
|
13705
|
+
const View = el.ownerDocument.defaultView;
|
|
13706
|
+
if (!View) return;
|
|
13707
|
+
el.dispatchEvent(
|
|
13708
|
+
new View.CustomEvent(open2 ? SURFACE_OPEN_EVENT : SURFACE_CLOSE_EVENT, { bubbles: true, composed: true, detail: { kind, trigger } })
|
|
13709
|
+
);
|
|
13710
|
+
}
|
|
13711
|
+
|
|
13712
|
+
// src/ui/surface-exit.ts
|
|
13713
|
+
var CLOSING_ATTR = "data-closing";
|
|
13714
|
+
var SURFACE_EXIT_CAP_MS = 1e3;
|
|
13715
|
+
function animationsOf(els) {
|
|
13716
|
+
return els.flatMap((el) => el.getAnimations({ subtree: true }));
|
|
13717
|
+
}
|
|
13718
|
+
function ends(a) {
|
|
13719
|
+
return Number.isFinite(a.effect?.getComputedTiming().endTime ?? Number.POSITIVE_INFINITY);
|
|
13720
|
+
}
|
|
13721
|
+
function holdForExit(els, done, opts = {}) {
|
|
13722
|
+
const list = Array.isArray(els) ? els : [els];
|
|
13723
|
+
if (list.length === 0 || !list.every((el) => typeof el.getAnimations === "function")) {
|
|
13724
|
+
done();
|
|
13725
|
+
return null;
|
|
13726
|
+
}
|
|
13727
|
+
const before = new Set(animationsOf(list));
|
|
13728
|
+
for (const el of list) el.setAttribute(CLOSING_ATTR, "");
|
|
13729
|
+
const exit = animationsOf(list).filter((a) => !before.has(a) && ends(a));
|
|
13730
|
+
if (exit.length === 0) {
|
|
13731
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
13732
|
+
done();
|
|
13733
|
+
return null;
|
|
13734
|
+
}
|
|
13735
|
+
const frozen = [...list, ...opts.inert ?? []];
|
|
13736
|
+
const wasInert = frozen.map((el) => el.inert);
|
|
13737
|
+
for (const el of frozen) el.inert = true;
|
|
13738
|
+
let settled = false;
|
|
13739
|
+
const settle = (complete) => {
|
|
13740
|
+
if (settled) return;
|
|
13741
|
+
settled = true;
|
|
13742
|
+
clearTimeout(cap);
|
|
13743
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
13744
|
+
frozen.forEach((el, i) => {
|
|
13745
|
+
el.inert = wasInert[i];
|
|
13746
|
+
});
|
|
13747
|
+
if (complete) done();
|
|
13748
|
+
};
|
|
13749
|
+
const cap = setTimeout(() => settle(true), SURFACE_EXIT_CAP_MS);
|
|
13750
|
+
let pending = exit.length;
|
|
13751
|
+
const one = () => {
|
|
13752
|
+
if (--pending === 0) settle(true);
|
|
13753
|
+
};
|
|
13754
|
+
for (const a of exit) a.finished.then(one, one);
|
|
13755
|
+
return {
|
|
13756
|
+
cancel: () => settle(false),
|
|
13757
|
+
finish: () => settle(true)
|
|
13758
|
+
};
|
|
13759
|
+
}
|
|
13760
|
+
|
|
13590
13761
|
// node_modules/@zag-js/anatomy/dist/create-anatomy.mjs
|
|
13591
13762
|
var createAnatomy = (name, parts3 = []) => ({
|
|
13592
13763
|
parts: (...values) => {
|
|
@@ -17999,6 +18170,35 @@ ${STATIC_DECLS}
|
|
|
17999
18170
|
color: var(--vela-fg-bright);
|
|
18000
18171
|
}
|
|
18001
18172
|
.vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
|
|
18173
|
+
/* Badge mode (iconBadges): the row icon sits in a rounded tile and the row grows to fit
|
|
18174
|
+
it. An icon-less row of the same level keeps an invisible slot, so labels align. */
|
|
18175
|
+
.vela-menu[data-badges] > .vela-menu-item { padding: 5px 10px 5px 6px; }
|
|
18176
|
+
.vela-menu-item .vela-menu-badge {
|
|
18177
|
+
width: 24px;
|
|
18178
|
+
height: 24px;
|
|
18179
|
+
padding: 3px;
|
|
18180
|
+
flex: none;
|
|
18181
|
+
box-sizing: border-box;
|
|
18182
|
+
display: inline-flex;
|
|
18183
|
+
align-items: center;
|
|
18184
|
+
justify-content: center;
|
|
18185
|
+
border-radius: var(--vela-radius-md);
|
|
18186
|
+
background: var(--vela-hover);
|
|
18187
|
+
border: 1px solid var(--vela-border);
|
|
18188
|
+
color: var(--vela-fg-muted);
|
|
18189
|
+
transition: background var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease), color var(--vela-dur-fast) var(--vela-ease);
|
|
18190
|
+
}
|
|
18191
|
+
.vela-menu-item .vela-menu-badge:empty { background: transparent; border-color: transparent; }
|
|
18192
|
+
.vela-menu-item .vela-menu-badge .vela-icon { color: inherit; }
|
|
18193
|
+
.vela-menu-item[data-highlighted] .vela-menu-badge:not(:empty) { color: var(--vela-fg-bright); }
|
|
18194
|
+
/* The selected tile inverts: a bright fill under the surface-colored glyph. After the
|
|
18195
|
+
hover rule, so a selected row keeps its tile while highlighted. */
|
|
18196
|
+
.vela-menu-item[data-checked] .vela-menu-badge:not(:empty),
|
|
18197
|
+
.vela-menu-item[data-checkmark] .vela-menu-badge:not(:empty) {
|
|
18198
|
+
background: color-mix(in srgb, var(--vela-fg-bright) 83%, var(--vela-surface));
|
|
18199
|
+
border-color: color-mix(in srgb, var(--vela-fg-bright) 40%, transparent);
|
|
18200
|
+
color: var(--vela-surface);
|
|
18201
|
+
}
|
|
18002
18202
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
18003
18203
|
same control language as the settings dialog's toggles. */
|
|
18004
18204
|
.vela-menu-switch {
|
|
@@ -18065,11 +18265,16 @@ ${STATIC_DECLS}
|
|
|
18065
18265
|
/** Trigger rect captured when this level opened — the list stays put if the
|
|
18066
18266
|
* trigger then moves (favorite chips shifting the caret). */
|
|
18067
18267
|
this.pinnedAnchor = null;
|
|
18268
|
+
/** The open state last announced — see {@link announce}. */
|
|
18269
|
+
this.shown = false;
|
|
18270
|
+
/** The list's exit animation after a close; a reopen cancels it. */
|
|
18271
|
+
this.exit = null;
|
|
18068
18272
|
this.doc = doc;
|
|
18069
18273
|
this.host = opts.host;
|
|
18070
18274
|
this.onSelect = opts.onSelect;
|
|
18071
18275
|
this.onFavorite = opts.onFavorite;
|
|
18072
18276
|
this.checkmarks = opts.checkmarks === true;
|
|
18277
|
+
this.iconBadges = opts.iconBadges === true;
|
|
18073
18278
|
this.positioner = doc.createElement("div");
|
|
18074
18279
|
this.positioner.className = "vela-ui-layer";
|
|
18075
18280
|
this.list = doc.createElement("ul");
|
|
@@ -18078,6 +18283,7 @@ ${STATIC_DECLS}
|
|
|
18078
18283
|
this.positioner.appendChild(this.list);
|
|
18079
18284
|
this.host.appendChild(this.positioner);
|
|
18080
18285
|
const trigger = opts.trigger;
|
|
18286
|
+
this.opener = trigger ?? opts.opener ?? null;
|
|
18081
18287
|
this.ctrl = menuController({
|
|
18082
18288
|
items: [],
|
|
18083
18289
|
id: opts.id,
|
|
@@ -18099,15 +18305,37 @@ ${STATIC_DECLS}
|
|
|
18099
18305
|
if (opts.triggerId && trigger) trigger.id = opts.triggerId;
|
|
18100
18306
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
18101
18307
|
const api = this.ctrl.connect(service);
|
|
18308
|
+
const closing = this.shown && !api.open;
|
|
18309
|
+
if (closing) this.announce(false);
|
|
18310
|
+
if (api.open && this.exit) {
|
|
18311
|
+
this.exit.cancel();
|
|
18312
|
+
this.exit = null;
|
|
18313
|
+
}
|
|
18102
18314
|
if (trigger) spreadProps(trigger, api.getTriggerProps(), this.mid);
|
|
18103
18315
|
spreadProps(this.positioner, api.getPositionerProps(), this.mid);
|
|
18104
18316
|
spreadProps(this.list, api.getContentProps(), this.mid);
|
|
18105
18317
|
this.project(api);
|
|
18318
|
+
if (closing) this.holdExit();
|
|
18319
|
+
if (api.open && !this.shown) this.announce(true);
|
|
18320
|
+
});
|
|
18321
|
+
}
|
|
18322
|
+
/** Keep the just-closed list up through its exit animation. `spreadProps` re-applies
|
|
18323
|
+
* `hidden` only when the machine's value for it changes, so the list stays shown until
|
|
18324
|
+
* the exit ends — or until a reopen, which projects `hidden: false` itself. */
|
|
18325
|
+
holdExit() {
|
|
18326
|
+
this.list.hidden = false;
|
|
18327
|
+
this.exit = holdForExit(this.list, () => {
|
|
18328
|
+
this.exit = null;
|
|
18329
|
+
if (!this.shown) this.list.hidden = true;
|
|
18106
18330
|
});
|
|
18107
18331
|
}
|
|
18108
18332
|
get api() {
|
|
18109
18333
|
return this.ctrl.connect(this.handle.service);
|
|
18110
18334
|
}
|
|
18335
|
+
announce(open2) {
|
|
18336
|
+
this.shown = open2;
|
|
18337
|
+
announceSurface(this.list, open2, "menu", this.opener);
|
|
18338
|
+
}
|
|
18111
18339
|
setItems(items) {
|
|
18112
18340
|
this.items = items;
|
|
18113
18341
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
@@ -18124,7 +18352,9 @@ ${STATIC_DECLS}
|
|
|
18124
18352
|
destroy() {
|
|
18125
18353
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
18126
18354
|
this.subs.clear();
|
|
18355
|
+
this.exit?.finish();
|
|
18127
18356
|
this.handle.stop();
|
|
18357
|
+
if (this.shown) this.announce(false);
|
|
18128
18358
|
this.positioner.remove();
|
|
18129
18359
|
}
|
|
18130
18360
|
byId(id) {
|
|
@@ -18149,6 +18379,10 @@ ${STATIC_DECLS}
|
|
|
18149
18379
|
const doc = this.doc;
|
|
18150
18380
|
this.list.replaceChildren();
|
|
18151
18381
|
const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
|
|
18382
|
+
const iconic = this.items.some((i) => !!i.icon);
|
|
18383
|
+
const badged = this.iconBadges && iconic;
|
|
18384
|
+
if (badged) this.list.dataset.badges = "1";
|
|
18385
|
+
else delete this.list.dataset.badges;
|
|
18152
18386
|
for (const item of this.items) {
|
|
18153
18387
|
if (item.separatorBefore) {
|
|
18154
18388
|
const sep = doc.createElement("li");
|
|
@@ -18174,7 +18408,16 @@ ${STATIC_DECLS}
|
|
|
18174
18408
|
} else if (!branch && !item.toggle && item.checked) {
|
|
18175
18409
|
li.dataset.checked = "1";
|
|
18176
18410
|
}
|
|
18177
|
-
if (
|
|
18411
|
+
if (badged) {
|
|
18412
|
+
const badge = doc.createElement("span");
|
|
18413
|
+
badge.className = "vela-menu-badge";
|
|
18414
|
+
if (item.icon) badge.appendChild(iconEl(item.icon, doc));
|
|
18415
|
+
li.appendChild(badge);
|
|
18416
|
+
} else if (item.icon) {
|
|
18417
|
+
li.appendChild(iconEl(item.icon, doc));
|
|
18418
|
+
} else if (iconic) {
|
|
18419
|
+
li.appendChild(iconEl("", doc));
|
|
18420
|
+
}
|
|
18178
18421
|
const label = doc.createElement("span");
|
|
18179
18422
|
label.className = "vela-menu-label";
|
|
18180
18423
|
label.textContent = item.label;
|
|
@@ -18218,9 +18461,11 @@ ${STATIC_DECLS}
|
|
|
18218
18461
|
const sub2 = new _Surface(doc, {
|
|
18219
18462
|
host: this.host,
|
|
18220
18463
|
placement: "right-start",
|
|
18464
|
+
opener: li,
|
|
18221
18465
|
onSelect: this.onSelect,
|
|
18222
18466
|
onFavorite: this.onFavorite,
|
|
18223
18467
|
checkmarks: this.checkmarks,
|
|
18468
|
+
iconBadges: this.iconBadges,
|
|
18224
18469
|
id: `${this.mid}--${item.id}`
|
|
18225
18470
|
});
|
|
18226
18471
|
sub2.setItems(item.submenu ?? []);
|
|
@@ -18246,7 +18491,8 @@ ${STATIC_DECLS}
|
|
|
18246
18491
|
id: opts.id,
|
|
18247
18492
|
minWidth: opts.minWidth,
|
|
18248
18493
|
onFavorite: opts.onFavorite,
|
|
18249
|
-
checkmarks: opts.checkmarks
|
|
18494
|
+
checkmarks: opts.checkmarks,
|
|
18495
|
+
iconBadges: opts.iconBadges
|
|
18250
18496
|
});
|
|
18251
18497
|
this.root.setItems(opts.items);
|
|
18252
18498
|
}
|
|
@@ -18350,6 +18596,7 @@ ${STATIC_DECLS}
|
|
|
18350
18596
|
|
|
18351
18597
|
// src/ui/components/popover/view.ts
|
|
18352
18598
|
var open = null;
|
|
18599
|
+
var leaving = /* @__PURE__ */ new Set();
|
|
18353
18600
|
function closeOpenPopovers() {
|
|
18354
18601
|
open?.hide();
|
|
18355
18602
|
}
|
|
@@ -18377,6 +18624,8 @@ ${STATIC_DECLS}
|
|
|
18377
18624
|
this.shown = false;
|
|
18378
18625
|
/** The pending removal of a fading-out shell; a show() that reuses the shell cancels it. */
|
|
18379
18626
|
this.leaveTimer = null;
|
|
18627
|
+
/** The shell's exit animation before it leaves the DOM; a show() that reuses the shell cancels it. */
|
|
18628
|
+
this.exit = null;
|
|
18380
18629
|
const doc = opts.trigger.ownerDocument;
|
|
18381
18630
|
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
18382
18631
|
this.trigger = opts.trigger;
|
|
@@ -18413,6 +18662,10 @@ ${STATIC_DECLS}
|
|
|
18413
18662
|
clearTimeout(this.leaveTimer);
|
|
18414
18663
|
this.leaveTimer = null;
|
|
18415
18664
|
}
|
|
18665
|
+
this.exit?.cancel();
|
|
18666
|
+
this.exit = null;
|
|
18667
|
+
leaving.delete(this);
|
|
18668
|
+
for (const p of [...leaving]) if (p.trigger === this.trigger) p.exit?.finish();
|
|
18416
18669
|
ensureUIHost(this.el, this.inheritsTokens ? void 0 : this.theme);
|
|
18417
18670
|
if (this.fadeMs > 0) {
|
|
18418
18671
|
this.el.style.transition = `opacity ${this.fadeMs}ms ease`;
|
|
@@ -18437,16 +18690,21 @@ ${STATIC_DECLS}
|
|
|
18437
18690
|
this.hide();
|
|
18438
18691
|
};
|
|
18439
18692
|
const onReflow = () => this.place();
|
|
18440
|
-
setTimeout(() =>
|
|
18693
|
+
setTimeout(() => {
|
|
18694
|
+
if (this.onOutside === onOutside) document.addEventListener("pointerdown", onOutside, true);
|
|
18695
|
+
}, 0);
|
|
18441
18696
|
document.addEventListener("keydown", onKey, true);
|
|
18442
18697
|
window.addEventListener("resize", onReflow, true);
|
|
18443
18698
|
document.addEventListener("scroll", onReflow, true);
|
|
18444
18699
|
this.onOutside = onOutside;
|
|
18445
18700
|
this.onKey = onKey;
|
|
18446
18701
|
this.onReflow = onReflow;
|
|
18702
|
+
announceSurface(this.el, true, "popover", this.trigger);
|
|
18447
18703
|
}
|
|
18448
18704
|
hide() {
|
|
18449
18705
|
if (!this.shown) return;
|
|
18706
|
+
this.shown = false;
|
|
18707
|
+
announceSurface(this.el, false, "popover", this.trigger);
|
|
18450
18708
|
if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
|
|
18451
18709
|
if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
|
|
18452
18710
|
if (this.onReflow) {
|
|
@@ -18464,9 +18722,13 @@ ${STATIC_DECLS}
|
|
|
18464
18722
|
this.el.remove();
|
|
18465
18723
|
}, this.fadeMs);
|
|
18466
18724
|
} else {
|
|
18467
|
-
this.el
|
|
18725
|
+
this.exit = holdForExit(this.el, () => {
|
|
18726
|
+
this.exit = null;
|
|
18727
|
+
leaving.delete(this);
|
|
18728
|
+
this.el.remove();
|
|
18729
|
+
});
|
|
18730
|
+
if (this.exit) leaving.add(this);
|
|
18468
18731
|
}
|
|
18469
|
-
this.shown = false;
|
|
18470
18732
|
if (open === this) open = null;
|
|
18471
18733
|
this.ctrl.onClose?.();
|
|
18472
18734
|
}
|
|
@@ -18477,6 +18739,7 @@ ${STATIC_DECLS}
|
|
|
18477
18739
|
}
|
|
18478
18740
|
destroy() {
|
|
18479
18741
|
this.hide();
|
|
18742
|
+
this.exit?.finish();
|
|
18480
18743
|
}
|
|
18481
18744
|
reposition() {
|
|
18482
18745
|
if (this.shown) this.place();
|
|
@@ -20356,6 +20619,11 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20356
20619
|
// src/ui/components/dialog/view.ts
|
|
20357
20620
|
var Dialog = class {
|
|
20358
20621
|
constructor(opts = {}) {
|
|
20622
|
+
this.opener = null;
|
|
20623
|
+
this.wasOpen = false;
|
|
20624
|
+
/** The panel and scrim's exit animation after a close; a reopen cancels it. */
|
|
20625
|
+
this.exit = null;
|
|
20626
|
+
this.destroyed = false;
|
|
20359
20627
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
20360
20628
|
injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
|
|
20361
20629
|
const host = opts.host ?? doc.body;
|
|
@@ -20452,19 +20720,73 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20452
20720
|
const mid = String(this.ctrl.props.id);
|
|
20453
20721
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
20454
20722
|
const api = this.ctrl.connect(service);
|
|
20723
|
+
const closing = this.wasOpen && !api.open;
|
|
20724
|
+
if (closing) this.announce(false);
|
|
20455
20725
|
spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
20456
20726
|
spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
20457
20727
|
spreadProps(this.panel, api.getContentProps(), mid);
|
|
20458
20728
|
spreadProps(title, api.getTitleProps(), mid);
|
|
20459
20729
|
spreadProps(close, api.getCloseTriggerProps(), mid);
|
|
20460
|
-
|
|
20461
|
-
|
|
20462
|
-
|
|
20730
|
+
if (api.open) {
|
|
20731
|
+
this.exit?.cancel();
|
|
20732
|
+
this.exit = null;
|
|
20733
|
+
this.setShown(true);
|
|
20734
|
+
} else if (closing) {
|
|
20735
|
+
this.holdExit();
|
|
20736
|
+
} else if (!this.exit) {
|
|
20737
|
+
this.setShown(false);
|
|
20738
|
+
}
|
|
20739
|
+
if (api.open && !this.wasOpen) this.announce(true);
|
|
20740
|
+
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
20741
|
+
this.wasOpen = api.open;
|
|
20742
|
+
});
|
|
20743
|
+
}
|
|
20744
|
+
setShown(shown) {
|
|
20745
|
+
this.backdrop.style.display = shown ? "" : "none";
|
|
20746
|
+
this.positioner.style.display = shown ? "" : "none";
|
|
20747
|
+
}
|
|
20748
|
+
/** Keep the just-closed dialog up through its exit animation, then hide it — or
|
|
20749
|
+
* remove it, when it was destroyed meanwhile. `spreadProps` re-applies the close
|
|
20750
|
+
* props' `hidden` only when its value changes, so lifting it keeps the panel and
|
|
20751
|
+
* scrim up until the exit ends. The positioner spans the viewport: inert, it lets
|
|
20752
|
+
* clicks through to the page while the panel animates out. */
|
|
20753
|
+
holdExit() {
|
|
20754
|
+
const held = [this.panel, this.backdrop];
|
|
20755
|
+
for (const el of held) el.hidden = false;
|
|
20756
|
+
this.exit = holdForExit(
|
|
20757
|
+
held,
|
|
20758
|
+
() => {
|
|
20759
|
+
this.exit = null;
|
|
20760
|
+
if (this.destroyed) {
|
|
20761
|
+
this.detach();
|
|
20762
|
+
} else if (!this.open) {
|
|
20763
|
+
for (const el of held) el.hidden = true;
|
|
20764
|
+
this.setShown(false);
|
|
20765
|
+
}
|
|
20766
|
+
},
|
|
20767
|
+
{ inert: [this.positioner] }
|
|
20768
|
+
);
|
|
20769
|
+
}
|
|
20770
|
+
detach() {
|
|
20771
|
+
this.backdrop.remove();
|
|
20772
|
+
this.positioner.remove();
|
|
20773
|
+
}
|
|
20774
|
+
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
20775
|
+
* teardown that outruns the machine's close notification). */
|
|
20776
|
+
restoreOpener() {
|
|
20777
|
+
const opener = this.opener;
|
|
20778
|
+
this.opener = null;
|
|
20779
|
+
if (opener?.isConnected) opener.focus({ preventScroll: true });
|
|
20780
|
+
}
|
|
20781
|
+
announce(open2) {
|
|
20782
|
+
const opener = this.opener === this.panel.ownerDocument.body ? null : this.opener;
|
|
20783
|
+
announceSurface(this.panel, open2, "dialog", opener);
|
|
20463
20784
|
}
|
|
20464
20785
|
get open() {
|
|
20465
20786
|
return this.ctrl.connect(this.handle.service).open;
|
|
20466
20787
|
}
|
|
20467
20788
|
show() {
|
|
20789
|
+
this.opener = this.panel.ownerDocument.activeElement;
|
|
20468
20790
|
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
20469
20791
|
}
|
|
20470
20792
|
hide() {
|
|
@@ -20477,10 +20799,16 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
20477
20799
|
return el?.closest(".vela-popover, .vela-menu") != null;
|
|
20478
20800
|
}
|
|
20479
20801
|
destroy() {
|
|
20802
|
+
if (this.wasOpen && !this.open) this.handle.flush();
|
|
20480
20803
|
if (this.open) this.hide();
|
|
20481
20804
|
this.handle.stop();
|
|
20482
|
-
this.
|
|
20483
|
-
this.
|
|
20805
|
+
this.destroyed = true;
|
|
20806
|
+
if (this.wasOpen) {
|
|
20807
|
+
this.wasOpen = false;
|
|
20808
|
+
this.announce(false);
|
|
20809
|
+
}
|
|
20810
|
+
this.restoreOpener();
|
|
20811
|
+
if (!this.exit) this.detach();
|
|
20484
20812
|
}
|
|
20485
20813
|
};
|
|
20486
20814
|
|
|
@@ -22932,7 +23260,10 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
22932
23260
|
var LEGEND_CTL_PX = 18;
|
|
22933
23261
|
var LEGEND_ROW_PAD_Y = 2;
|
|
22934
23262
|
var LEGEND_ROW_PAD_X = 6;
|
|
23263
|
+
var LEGEND_LEFT_PX = 10;
|
|
23264
|
+
var LEGEND_SCALE_GAP_PX = 6;
|
|
22935
23265
|
var LEGEND_TITLE_VALUES_GAP_PX = 6;
|
|
23266
|
+
var LEGEND_VALUES_GAP_PX = 5;
|
|
22936
23267
|
var LEGEND_TITLE_STATUS_GAP_PX = 8;
|
|
22937
23268
|
var LEGEND_ACTION_GAP_PX = 6;
|
|
22938
23269
|
var LEGEND_CTL_CSS = `cursor:pointer;display:inline-flex;align-items:center;justify-content:center;border:none;line-height:0;padding:0;width:${LEGEND_CTL_PX}px;height:${LEGEND_CTL_PX}px;border-radius:3px;box-sizing:border-box;flex:none;`;
|
|
@@ -23218,14 +23549,15 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23218
23549
|
row.valuesEl.replaceChildren();
|
|
23219
23550
|
return;
|
|
23220
23551
|
}
|
|
23221
|
-
row.valuesEl.style.display = "
|
|
23552
|
+
row.valuesEl.style.display = "block";
|
|
23222
23553
|
row.valuesEl.replaceChildren();
|
|
23223
|
-
|
|
23554
|
+
row.plotValues.forEach((v, i) => {
|
|
23224
23555
|
const span = document.createElement("span");
|
|
23225
23556
|
span.textContent = v.value;
|
|
23226
23557
|
span.style.color = v.color;
|
|
23558
|
+
if (i > 0) span.style.marginLeft = `${LEGEND_VALUES_GAP_PX}px`;
|
|
23227
23559
|
row.valuesEl.appendChild(span);
|
|
23228
|
-
}
|
|
23560
|
+
});
|
|
23229
23561
|
}
|
|
23230
23562
|
// ── legend move/merge (menu + drag) ─────────────────────────────────────
|
|
23231
23563
|
/** Open the "Move to" menu for a row, anchored under its move button. */
|
|
@@ -23326,7 +23658,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23326
23658
|
if (!lg) {
|
|
23327
23659
|
lg = document.createElement("div");
|
|
23328
23660
|
lg.dataset.velaPane = paneId;
|
|
23329
|
-
lg.style.cssText =
|
|
23661
|
+
lg.style.cssText = `position:absolute;left:${LEGEND_LEFT_PX}px;z-index:5;display:flex;flex-direction:column;align-items:flex-start;gap:0;pointer-events:none;font:12px -apple-system,Segoe UI,sans-serif;`;
|
|
23330
23662
|
applyChromeTokens(lg, this.theme);
|
|
23331
23663
|
this.positionLegend(lg, paneId);
|
|
23332
23664
|
this.container.appendChild(lg);
|
|
@@ -23338,6 +23670,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23338
23670
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
23339
23671
|
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
23340
23672
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
23673
|
+
lg.style.maxWidth = `calc(100% - ${(bounds.rightAxis ?? 0) + LEGEND_LEFT_PX + LEGEND_SCALE_GAP_PX}px)`;
|
|
23341
23674
|
const collapsed = this.paneCollapse.has(paneId);
|
|
23342
23675
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
23343
23676
|
for (const row of this.rows.values()) {
|
|
@@ -23429,7 +23762,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23429
23762
|
}
|
|
23430
23763
|
ensureDialogStyles();
|
|
23431
23764
|
const el = document.createElement("div");
|
|
23432
|
-
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
23765
|
+
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;max-width:calc(100% + ${LEGEND_ROW_PAD_X}px);min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
23433
23766
|
el.addEventListener("mouseleave", () => {
|
|
23434
23767
|
if (this.selectedId !== id) this.setRowHighlighted(id, false);
|
|
23435
23768
|
});
|
|
@@ -23457,7 +23790,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23457
23790
|
const statusEl = document.createElement("span");
|
|
23458
23791
|
statusEl.style.cssText = "display:none;box-sizing:border-box;flex:none;";
|
|
23459
23792
|
const titleWrap = document.createElement("span");
|
|
23460
|
-
titleWrap.style.cssText = "white-space:nowrap;";
|
|
23793
|
+
titleWrap.style.cssText = "white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;";
|
|
23461
23794
|
titleWrap.addEventListener("mouseenter", () => this.setRowHighlighted(id, true));
|
|
23462
23795
|
const titleEl = document.createElement("span");
|
|
23463
23796
|
titleEl.textContent = title;
|
|
@@ -23475,7 +23808,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
23475
23808
|
el.appendChild(calloutsEl);
|
|
23476
23809
|
el.appendChild(statusEl);
|
|
23477
23810
|
const valuesEl = document.createElement("span");
|
|
23478
|
-
valuesEl.style.cssText = `display:none;
|
|
23811
|
+
valuesEl.style.cssText = `display:none;flex:1 1 0%;min-width:0;overflow:hidden;text-overflow:ellipsis;margin-left:${LEGEND_TITLE_VALUES_GAP_PX}px;white-space:nowrap;font-variant-numeric:tabular-nums;`;
|
|
23479
23812
|
el.appendChild(valuesEl);
|
|
23480
23813
|
const controlsEl = document.createElement("span");
|
|
23481
23814
|
controlsEl.style.cssText = `display:none;align-items:center;gap:${LEGEND_ACTION_GAP_PX}px;flex:none;margin-left:${LEGEND_ACTION_GAP_PX}px;`;
|
|
@@ -24648,15 +24981,23 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
|
24648
24981
|
this.seg(x + w, y + h, x, y + h, lineWidth, c);
|
|
24649
24982
|
this.seg(x, y + h, x, y, lineWidth, c);
|
|
24650
24983
|
}
|
|
24651
|
-
/**
|
|
24984
|
+
/**
|
|
24985
|
+
* Filled circle (triangle fan), feathered like a line: the rim is padded by {@link AA_PAD} and
|
|
24986
|
+
* edgeDist runs from 0 at the center to the padded radius at the rim, with edgeHalf = r. The
|
|
24987
|
+
* distance grows linearly along each spoke, so the shader's line feather gives a round soft edge.
|
|
24988
|
+
*/
|
|
24652
24989
|
circle(cx, cy, r, c, segments = 16) {
|
|
24653
|
-
|
|
24990
|
+
const ext = r + AA_PAD;
|
|
24991
|
+
let px = cx + ext;
|
|
24654
24992
|
let py = cy;
|
|
24993
|
+
this.ensure(segments * 3);
|
|
24655
24994
|
for (let i = 1; i <= segments; i += 1) {
|
|
24656
24995
|
const a = i / segments * Math.PI * 2;
|
|
24657
|
-
const nx = cx + Math.cos(a) *
|
|
24658
|
-
const ny = cy + Math.sin(a) *
|
|
24659
|
-
this.
|
|
24996
|
+
const nx = cx + Math.cos(a) * ext;
|
|
24997
|
+
const ny = cy + Math.sin(a) * ext;
|
|
24998
|
+
this.v(cx, cy, c, 0, r);
|
|
24999
|
+
this.v(px, py, c, ext, r);
|
|
25000
|
+
this.v(nx, ny, c, ext, r);
|
|
24660
25001
|
px = nx;
|
|
24661
25002
|
py = ny;
|
|
24662
25003
|
}
|
|
@@ -24837,7 +25178,7 @@ void main() {
|
|
|
24837
25178
|
}
|
|
24838
25179
|
mount(canvas) {
|
|
24839
25180
|
this.canvas = canvas;
|
|
24840
|
-
this.gl = canvas.getContext("webgl2", { alpha: true, antialias:
|
|
25181
|
+
this.gl = canvas.getContext("webgl2", { alpha: true, antialias: false, depth: false, stencil: false, premultipliedAlpha: true });
|
|
24841
25182
|
if (!this.gl) return;
|
|
24842
25183
|
if (!this.initGL()) {
|
|
24843
25184
|
this.gl = null;
|
|
@@ -26062,6 +26403,11 @@ void main() {
|
|
|
26062
26403
|
/** When true (the default), the separators between stacked panes are draggable to
|
|
26063
26404
|
* resize the panes above/below. When false, a press there is a normal data-area pan. */
|
|
26064
26405
|
this.paneResize = true;
|
|
26406
|
+
/** When true (the default), the user drawings take part in pointer input: they claim a
|
|
26407
|
+
* press over a drawing or with a tool armed, and see hover moves. When false, every press
|
|
26408
|
+
* is a plain pan/click and hover leaves them alone; a drawing gesture already under way
|
|
26409
|
+
* still finishes. */
|
|
26410
|
+
this.drawings = true;
|
|
26065
26411
|
this.el = null;
|
|
26066
26412
|
this.dragging = false;
|
|
26067
26413
|
this.moved = false;
|
|
@@ -26125,18 +26471,18 @@ void main() {
|
|
|
26125
26471
|
this.onModifier = (e) => {
|
|
26126
26472
|
if (e.repeat || e.key !== "Shift" && e.key !== "Control" && e.key !== "Meta") return;
|
|
26127
26473
|
if (Number.isNaN(this.cursorX)) return;
|
|
26128
|
-
if (this.dragging
|
|
26474
|
+
if (this.dragging ? this.region !== "drawing" : !this.drawings) return;
|
|
26129
26475
|
this.deps.drawingsPointerMove?.(this.cursorX, this.cursorY, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26130
26476
|
};
|
|
26131
26477
|
this.onDown = (e) => {
|
|
26132
26478
|
if (e.button === 1) {
|
|
26133
26479
|
const { x: x2, y: y2 } = this.local(e);
|
|
26134
|
-
this.middleDeleted = this.deps.drawingsDeleteAt?.(x2, y2) ?? false;
|
|
26480
|
+
this.middleDeleted = this.drawings && (this.deps.drawingsDeleteAt?.(x2, y2) ?? false);
|
|
26135
26481
|
if (this.middleDeleted) e.preventDefault();
|
|
26136
26482
|
return;
|
|
26137
26483
|
}
|
|
26138
26484
|
if (e.button === 2) {
|
|
26139
|
-
this.rightCancelled = this.deps.drawingsCancelPlacement?.() ?? false;
|
|
26485
|
+
this.rightCancelled = this.drawings && (this.deps.drawingsCancelPlacement?.() ?? false);
|
|
26140
26486
|
if (this.rightCancelled) e.preventDefault();
|
|
26141
26487
|
return;
|
|
26142
26488
|
}
|
|
@@ -26158,20 +26504,20 @@ void main() {
|
|
|
26158
26504
|
this.startY = y;
|
|
26159
26505
|
this.prevPressDrawing = this.lastPressDrawing;
|
|
26160
26506
|
this.lastPressDrawing = false;
|
|
26161
|
-
if (this.deps.drawingsClaim?.(x, y)) {
|
|
26507
|
+
if (this.drawings && this.deps.drawingsClaim?.(x, y)) {
|
|
26162
26508
|
this.region = "drawing";
|
|
26163
26509
|
this.lastPressDrawing = true;
|
|
26164
26510
|
this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26165
26511
|
this.capture(e.pointerId);
|
|
26166
26512
|
return;
|
|
26167
26513
|
}
|
|
26168
|
-
if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
26514
|
+
if (this.drawings && e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
26169
26515
|
this.region = "drawing";
|
|
26170
26516
|
this.lastPressDrawing = true;
|
|
26171
26517
|
this.capture(e.pointerId);
|
|
26172
26518
|
return;
|
|
26173
26519
|
}
|
|
26174
|
-
if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
26520
|
+
if (this.drawings && (e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
26175
26521
|
this.region = "drawing";
|
|
26176
26522
|
this.lastPressDrawing = true;
|
|
26177
26523
|
this.capture(e.pointerId);
|
|
@@ -26248,7 +26594,7 @@ void main() {
|
|
|
26248
26594
|
const drawCursor = this.deps.drawingsCursor?.(x, y);
|
|
26249
26595
|
const r = this.regionAt(x, y);
|
|
26250
26596
|
this.el.style.cursor = drawCursor ?? (r === "price" ? "ns-resize" : r === "time" ? "ew-resize" : r === "separator" ? "row-resize" : "");
|
|
26251
|
-
this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26597
|
+
if (this.drawings) this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
26252
26598
|
}
|
|
26253
26599
|
if (e.pointerType !== "touch") this.deps.onPointerMove(x, y);
|
|
26254
26600
|
};
|
|
@@ -26289,7 +26635,7 @@ void main() {
|
|
|
26289
26635
|
this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
|
|
26290
26636
|
} else if (tapRelease && this.region === "data") {
|
|
26291
26637
|
this.deps.onClick(x, y);
|
|
26292
|
-
} else if (this.dragging && this.region === "data") {
|
|
26638
|
+
} else if (this.dragging && this.region === "data" && wasTouch) {
|
|
26293
26639
|
const stale = e.timeStamp - this.lastT > FLING_STALE_MS;
|
|
26294
26640
|
if (!stale && Math.abs(this.vx) > FLING_MIN_SPEED) {
|
|
26295
26641
|
const pitch = this.deps.getCoords().pxPerBar();
|
|
@@ -26490,7 +26836,7 @@ void main() {
|
|
|
26490
26836
|
}
|
|
26491
26837
|
/** Shared double-click / double-tap routing, by the region under the point. */
|
|
26492
26838
|
doubleActivate(x, y) {
|
|
26493
|
-
if (this.deps.drawingsDblClick?.(x, y)) return;
|
|
26839
|
+
if (this.drawings && this.deps.drawingsDblClick?.(x, y)) return;
|
|
26494
26840
|
const region = this.regionAt(x, y);
|
|
26495
26841
|
if (region === "price") this.deps.resetPriceScale(x, y);
|
|
26496
26842
|
else if (region === "separator") this.deps.resetPaneSize(y);
|
|
@@ -28504,10 +28850,13 @@ void main() {
|
|
|
28504
28850
|
const norm = raw / mag;
|
|
28505
28851
|
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
28506
28852
|
const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);
|
|
28507
|
-
const out = [];
|
|
28508
28853
|
const start = Math.ceil(min2 / step) * step;
|
|
28509
|
-
|
|
28510
|
-
|
|
28854
|
+
if (!(step > 0) || start + step === start) return [];
|
|
28855
|
+
const count = Math.floor((max2 - start) / step + 1e-6) + 1;
|
|
28856
|
+
if (!(count > 0) || count > 1e3) return [];
|
|
28857
|
+
const out = [];
|
|
28858
|
+
for (let i = 0; i < count; i += 1) {
|
|
28859
|
+
out.push(Number((start + i * step).toFixed(decimals)));
|
|
28511
28860
|
}
|
|
28512
28861
|
return out;
|
|
28513
28862
|
}
|
|
@@ -28705,6 +29054,100 @@ void main() {
|
|
|
28705
29054
|
return out;
|
|
28706
29055
|
}
|
|
28707
29056
|
|
|
29057
|
+
// src/renderers/native/chrome/priceAxisTicks.ts
|
|
29058
|
+
var PriceAxisTickSource = class {
|
|
29059
|
+
constructor() {
|
|
29060
|
+
this.fn = null;
|
|
29061
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
29062
|
+
this.warned = false;
|
|
29063
|
+
}
|
|
29064
|
+
/** The host function, or null for the built-in ticks. */
|
|
29065
|
+
get hook() {
|
|
29066
|
+
return this.fn;
|
|
29067
|
+
}
|
|
29068
|
+
/** Set (or clear) the host function. Drops every memoized result, so setting the same
|
|
29069
|
+
* function again recomputes — how a host refreshes ticks that depend on its own state. */
|
|
29070
|
+
setHook(fn) {
|
|
29071
|
+
this.fn = fn;
|
|
29072
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
29073
|
+
}
|
|
29074
|
+
/** The pane's master-scale ticks for this frame. An unscaled pane (`axisFormat: 'none'`)
|
|
29075
|
+
* gets none, and the host is never asked for it. */
|
|
29076
|
+
ticksFor(scene, pane, coords) {
|
|
29077
|
+
const format = pane.axisFormat;
|
|
29078
|
+
if (format === "none") return [];
|
|
29079
|
+
const pct = percentScaleFor(scene, pane);
|
|
29080
|
+
const height = pane.bounds.height;
|
|
29081
|
+
const fn = this.fn;
|
|
29082
|
+
if (!fn || !(height > 0)) return paneAxisTicks(pane.scale, height, pct, scene.priceMintick, format);
|
|
29083
|
+
const s = pane.scale;
|
|
29084
|
+
const m = this.memo.get(pane);
|
|
29085
|
+
const fontSize = scene.style.fontSize;
|
|
29086
|
+
if (m && m.kind === pane.kind && m.min === s.min && m.max === s.max && m.log === !!s.log && m.invert === !!s.invert && m.height === height && m.baseline === pct?.baseline && m.indexed === !!pct?.indexed && m.mintick === scene.priceMintick && m.format === format && m.fontSize === fontSize) {
|
|
29087
|
+
return m.ticks;
|
|
29088
|
+
}
|
|
29089
|
+
const defaults2 = paneAxisTicks(s, height, pct, scene.priceMintick, format);
|
|
29090
|
+
const ticks = this.callHook(fn, scene, pane, coords, defaults2, pct?.baseline, pct ? pct.indexed ? "indexed" : "percent" : "price");
|
|
29091
|
+
this.memo.set(pane, {
|
|
29092
|
+
kind: pane.kind,
|
|
29093
|
+
min: s.min,
|
|
29094
|
+
max: s.max,
|
|
29095
|
+
log: !!s.log,
|
|
29096
|
+
invert: !!s.invert,
|
|
29097
|
+
height,
|
|
29098
|
+
baseline: pct?.baseline,
|
|
29099
|
+
indexed: !!pct?.indexed,
|
|
29100
|
+
mintick: scene.priceMintick,
|
|
29101
|
+
format,
|
|
29102
|
+
fontSize,
|
|
29103
|
+
ticks
|
|
29104
|
+
});
|
|
29105
|
+
return ticks;
|
|
29106
|
+
}
|
|
29107
|
+
callHook(fn, scene, pane, coords, defaults2, baseline, mode) {
|
|
29108
|
+
const scale = { min: pane.scale.min, max: pane.scale.max, log: !!pane.scale.log, invert: !!pane.scale.invert };
|
|
29109
|
+
const bounds = { top: 0, height: pane.bounds.height };
|
|
29110
|
+
const ctx = {
|
|
29111
|
+
pane: { id: pane.id, kind: pane.kind },
|
|
29112
|
+
min: Math.min(scale.min, scale.max),
|
|
29113
|
+
max: Math.max(scale.min, scale.max),
|
|
29114
|
+
log: !!scale.log,
|
|
29115
|
+
height: bounds.height,
|
|
29116
|
+
mode,
|
|
29117
|
+
baseline,
|
|
29118
|
+
mintick: scene.priceMintick,
|
|
29119
|
+
fontSize: scene.style.fontSize,
|
|
29120
|
+
priceToY: (price) => coords.priceToY(price, scale, bounds),
|
|
29121
|
+
defaults: defaults2
|
|
29122
|
+
};
|
|
29123
|
+
let out;
|
|
29124
|
+
try {
|
|
29125
|
+
out = fn(ctx);
|
|
29126
|
+
} catch (err) {
|
|
29127
|
+
this.warnOnce("threw", err);
|
|
29128
|
+
return defaults2;
|
|
29129
|
+
}
|
|
29130
|
+
if (out == null) return defaults2;
|
|
29131
|
+
if (!Array.isArray(out)) {
|
|
29132
|
+
this.warnOnce("returned a non-array", out);
|
|
29133
|
+
return defaults2;
|
|
29134
|
+
}
|
|
29135
|
+
const ticks = [];
|
|
29136
|
+
for (const t of out) {
|
|
29137
|
+
if (!t || typeof t !== "object") continue;
|
|
29138
|
+
const { price, label, major } = t;
|
|
29139
|
+
if (typeof price !== "number" || !Number.isFinite(price) || typeof label !== "string") continue;
|
|
29140
|
+
ticks.push(typeof major === "boolean" ? { price, label, major } : { price, label });
|
|
29141
|
+
}
|
|
29142
|
+
return ticks;
|
|
29143
|
+
}
|
|
29144
|
+
warnOnce(what, detail) {
|
|
29145
|
+
if (this.warned) return;
|
|
29146
|
+
this.warned = true;
|
|
29147
|
+
console.warn(`[vela] the priceAxisTicks function ${what} \u2014 the default ticks are used instead.`, detail);
|
|
29148
|
+
}
|
|
29149
|
+
};
|
|
29150
|
+
|
|
28708
29151
|
// src/renderers/native/chrome/countdown.ts
|
|
28709
29152
|
function countdownText(barOpen, barMs, now2) {
|
|
28710
29153
|
if (!(barMs > 0)) return null;
|
|
@@ -29057,8 +29500,11 @@ void main() {
|
|
|
29057
29500
|
}
|
|
29058
29501
|
|
|
29059
29502
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
29503
|
+
var MINOR_LABEL_ALPHA = 0.5;
|
|
29060
29504
|
var ChromeRenderer = class {
|
|
29061
|
-
|
|
29505
|
+
/** `axisTicks` is shared with the backdrop layer, so price labels and gridlines agree. */
|
|
29506
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
29507
|
+
this.axisTicks = axisTicks;
|
|
29062
29508
|
this.canvas = null;
|
|
29063
29509
|
this.ctx = null;
|
|
29064
29510
|
// The color for axis tick labels — the host-passed surface text, set each frame in render().
|
|
@@ -29163,7 +29609,8 @@ void main() {
|
|
|
29163
29609
|
this.drawTimeAxis(ctx, scene, coords, theme, dataW, dataH, fullH);
|
|
29164
29610
|
this.drawMarkLane(ctx, scene, coords, theme, dataW, dataH);
|
|
29165
29611
|
}
|
|
29166
|
-
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge
|
|
29612
|
+
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge,
|
|
29613
|
+
* but clipped to the data area so a glyph on the edge bar never paints over the price scale. */
|
|
29167
29614
|
drawMarkLane(ctx, scene, coords, theme, dataW, dataH) {
|
|
29168
29615
|
if (!scene.marks.visible || scene.timelineMarks.length === 0) {
|
|
29169
29616
|
this.markLayout = { glyphs: [], stacks: /* @__PURE__ */ new Map() };
|
|
@@ -29181,6 +29628,10 @@ void main() {
|
|
|
29181
29628
|
expanded: scene.marksExpandedStack
|
|
29182
29629
|
});
|
|
29183
29630
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
29631
|
+
ctx.save();
|
|
29632
|
+
ctx.beginPath();
|
|
29633
|
+
ctx.rect(0, 0, dataW, dataH);
|
|
29634
|
+
ctx.clip();
|
|
29184
29635
|
paintMarkLane(ctx, this.markLayout, {
|
|
29185
29636
|
axisY: dataH,
|
|
29186
29637
|
background: theme.background,
|
|
@@ -29194,6 +29645,7 @@ void main() {
|
|
|
29194
29645
|
flashKey: scene.marksFlash && scene.marksFlash.until > nowMs ? scene.marksFlash.key : null,
|
|
29195
29646
|
nowMs
|
|
29196
29647
|
});
|
|
29648
|
+
ctx.restore();
|
|
29197
29649
|
}
|
|
29198
29650
|
barTimes(scene) {
|
|
29199
29651
|
if (this.barTimesSrc !== scene.bars || this.barTimesCache.length !== scene.bars.length) {
|
|
@@ -29250,13 +29702,19 @@ void main() {
|
|
|
29250
29702
|
if (!scene.showAxisLabels) return;
|
|
29251
29703
|
ctx.fillStyle = this.axisTextColor;
|
|
29252
29704
|
ctx.textAlign = "left";
|
|
29705
|
+
const font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
29706
|
+
const majorFont = `600 ${font}`;
|
|
29707
|
+
const alpha = ctx.globalAlpha;
|
|
29253
29708
|
for (const pane of panes) {
|
|
29254
29709
|
if (pane.collapsed) continue;
|
|
29255
|
-
const
|
|
29256
|
-
for (const t of paneAxisTicks(pane.scale, pane.bounds.height, pct, scene.priceMintick, pane.axisFormat)) {
|
|
29710
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
29257
29711
|
const y = coords.priceToY(t.price, pane.scale, pane.bounds);
|
|
29258
29712
|
if (y < pane.bounds.top + 6 || y > pane.bounds.top + pane.bounds.height - 4) continue;
|
|
29713
|
+
if (t.major === true) ctx.font = majorFont;
|
|
29714
|
+
else if (t.major === false) ctx.globalAlpha = alpha * MINOR_LABEL_ALPHA;
|
|
29259
29715
|
ctx.fillText(t.label, dataW + 6, y);
|
|
29716
|
+
if (t.major === true) ctx.font = font;
|
|
29717
|
+
else if (t.major === false) ctx.globalAlpha = alpha;
|
|
29260
29718
|
}
|
|
29261
29719
|
if (pane.axisBands) {
|
|
29262
29720
|
for (const b of pane.axisBands) {
|
|
@@ -29498,15 +29956,23 @@ void main() {
|
|
|
29498
29956
|
constructor() {
|
|
29499
29957
|
this.canvas = null;
|
|
29500
29958
|
this.ctx = null;
|
|
29959
|
+
this.shade = null;
|
|
29501
29960
|
}
|
|
29502
29961
|
mount(canvas) {
|
|
29503
29962
|
this.canvas = canvas;
|
|
29504
29963
|
this.ctx = canvas.getContext("2d");
|
|
29505
29964
|
}
|
|
29965
|
+
/** The plot area the last frame veiled for `crosshairOverride.shadeRight` — from the
|
|
29966
|
+
* picked bar's right edge to the price scale, full plot height (the union when both the
|
|
29967
|
+
* local and a synced crosshair veil) — or null when no veil was painted. */
|
|
29968
|
+
get shadeRightArea() {
|
|
29969
|
+
return this.shade;
|
|
29970
|
+
}
|
|
29506
29971
|
/** Clear the cursor canvas and (re)draw the crosshair lines + axis chips. The optional
|
|
29507
29972
|
* `separatorHoverY` highlights the draggable pane separator under the cursor;
|
|
29508
29973
|
* `external` is a SYNCED ghost crosshair (another chart's pointer, pixel-resolved). */
|
|
29509
29974
|
render(scene, coords, theme, separatorHoverY = null, external = null) {
|
|
29975
|
+
this.shade = null;
|
|
29510
29976
|
const ctx = this.ctx;
|
|
29511
29977
|
const canvas = this.canvas;
|
|
29512
29978
|
if (!ctx || !canvas) return;
|
|
@@ -29529,12 +29995,7 @@ void main() {
|
|
|
29529
29995
|
const x = crisp(coords.logicalToX(logical), ov?.width ?? cs.width);
|
|
29530
29996
|
if (vertical && ov?.shadeRight) {
|
|
29531
29997
|
const from = Math.max(0, Math.round(coords.logicalToX(logical + 0.5)));
|
|
29532
|
-
if (from < dataW)
|
|
29533
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
29534
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
29535
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
29536
|
-
ctx.globalAlpha = 1;
|
|
29537
|
-
}
|
|
29998
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
29538
29999
|
}
|
|
29539
30000
|
ctx.strokeStyle = ov?.color ?? cs.color ?? theme.textColor;
|
|
29540
30001
|
ctx.lineWidth = ov?.width ?? cs.width;
|
|
@@ -29570,6 +30031,7 @@ void main() {
|
|
|
29570
30031
|
destroy() {
|
|
29571
30032
|
this.canvas = null;
|
|
29572
30033
|
this.ctx = null;
|
|
30034
|
+
this.shade = null;
|
|
29573
30035
|
}
|
|
29574
30036
|
/** The synced ghost: a dimmed vertical line at the bar the renderer resolved as
|
|
29575
30037
|
* CONTAINING the foreign time (+ horizontal line when a comparable price came
|
|
@@ -29589,12 +30051,7 @@ void main() {
|
|
|
29589
30051
|
if (ov?.vertical === false) return;
|
|
29590
30052
|
if (ov?.shadeRight) {
|
|
29591
30053
|
const from = Math.max(0, Math.round(coords.logicalToX(Math.round(coords.xToLogical(ext.x)) + 0.5)));
|
|
29592
|
-
if (from < dataW)
|
|
29593
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
29594
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
29595
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
29596
|
-
ctx.globalAlpha = 1;
|
|
29597
|
-
}
|
|
30054
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
29598
30055
|
}
|
|
29599
30056
|
if (ext.line === false || x < 0 || x > dataW) return;
|
|
29600
30057
|
ctx.font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
@@ -29630,6 +30087,15 @@ void main() {
|
|
|
29630
30087
|
this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
|
|
29631
30088
|
ctx.globalAlpha = 1;
|
|
29632
30089
|
}
|
|
30090
|
+
/** Paint the `shadeRight` veil from `from` to the price scale and record the veiled area. */
|
|
30091
|
+
paintShade(ctx, shade, from, dataW, dataH) {
|
|
30092
|
+
ctx.fillStyle = shade.color;
|
|
30093
|
+
ctx.globalAlpha = shade.opacity ?? 1;
|
|
30094
|
+
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
30095
|
+
ctx.globalAlpha = 1;
|
|
30096
|
+
const x = this.shade ? Math.min(this.shade.x, from) : from;
|
|
30097
|
+
this.shade = { x, width: dataW - x, height: dataH };
|
|
30098
|
+
}
|
|
29633
30099
|
/** A soft band + a brighter crisp center line over the hovered separator, so it reads as
|
|
29634
30100
|
* a draggable handle (the cursor is already `row-resize`). Spans the full width (data +
|
|
29635
30101
|
* scale gutter) to match the separator itself. Theme-derived (textColor). */
|
|
@@ -32745,6 +33211,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
32745
33211
|
activeDragId() {
|
|
32746
33212
|
return this.state.kind === "pressed" && this.state.moved ? this.state.id : null;
|
|
32747
33213
|
}
|
|
33214
|
+
/** The store drawings a live drag is moving — the pressed one and its riders — or none
|
|
33215
|
+
* before the drag passes the slop. A Ctrl-drag moves copies, so its sources stay put. */
|
|
33216
|
+
movingIds() {
|
|
33217
|
+
if (this.state.kind !== "pressed" || !this.state.moved || this.state.clones) return /* @__PURE__ */ new Set();
|
|
33218
|
+
return /* @__PURE__ */ new Set([this.state.id, ...this.state.riders.map((r) => r.id)]);
|
|
33219
|
+
}
|
|
32748
33220
|
/** The drawing under a press that has not been released yet (drag or click undecided), or null. */
|
|
32749
33221
|
pressedId() {
|
|
32750
33222
|
return this.state.kind === "pressed" ? this.state.id : null;
|
|
@@ -33119,15 +33591,16 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33119
33591
|
}
|
|
33120
33592
|
open(drawing, actions, kind) {
|
|
33121
33593
|
this.close();
|
|
33122
|
-
const
|
|
33594
|
+
const live = actions.resolve() ?? drawing;
|
|
33595
|
+
const snapshot2 = clonePlain(live.serialize());
|
|
33123
33596
|
const grid = fieldGrid({ variant: "inputs" });
|
|
33124
33597
|
grid.style.padding = "16px 20px";
|
|
33125
33598
|
grid.style.overflowY = "auto";
|
|
33126
33599
|
grid.style.overflowX = "hidden";
|
|
33127
33600
|
grid.style.flex = "1 1 auto";
|
|
33128
|
-
if (kind === "position" &&
|
|
33129
|
-
else if (kind === "frvp" &&
|
|
33130
|
-
else if (kind === "levels") this.buildLevels(grid,
|
|
33601
|
+
if (kind === "position" && live instanceof PositionTool) this.buildPosition(grid, live, actions);
|
|
33602
|
+
else if (kind === "frvp" && live instanceof FixedRangeVolumeProfile) this.buildFrvp(grid, live, actions);
|
|
33603
|
+
else if (kind === "levels") this.buildLevels(grid, live, actions);
|
|
33131
33604
|
else return;
|
|
33132
33605
|
const ui = new Dialog({
|
|
33133
33606
|
host: this.host,
|
|
@@ -33403,6 +33876,17 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33403
33876
|
}
|
|
33404
33877
|
}));
|
|
33405
33878
|
}
|
|
33879
|
+
if (drawing instanceof FibRetracement) {
|
|
33880
|
+
const fib = drawing;
|
|
33881
|
+
grid.appendChild(fieldRow({
|
|
33882
|
+
label: "Reverse",
|
|
33883
|
+
bool: true,
|
|
33884
|
+
toggle: {
|
|
33885
|
+
checked: fib.reverse,
|
|
33886
|
+
onChange: (v) => actions.patch({ reverse: v })
|
|
33887
|
+
}
|
|
33888
|
+
}));
|
|
33889
|
+
}
|
|
33406
33890
|
levels.forEach((lv, i) => {
|
|
33407
33891
|
const row = document.createElement("div");
|
|
33408
33892
|
row.className = "vela-field-span";
|
|
@@ -33763,7 +34247,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
33763
34247
|
this.reposition();
|
|
33764
34248
|
return;
|
|
33765
34249
|
}
|
|
33766
|
-
const text = drawing.text;
|
|
34250
|
+
const text = (actions.resolve() ?? drawing).text;
|
|
33767
34251
|
const panel = document.createElement("div");
|
|
33768
34252
|
panel.style.cssText = `padding:6px;border-top:1px solid var(--vela-border);display:flex;flex-direction:column;gap:4px;`;
|
|
33769
34253
|
const ta = new TextArea({
|
|
@@ -34359,6 +34843,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34359
34843
|
// src/renderers/native/drawings/DrawingToolbar.ts
|
|
34360
34844
|
var TOOLBAR_WIDTH = 44;
|
|
34361
34845
|
var TOOLBAR_COLLAPSED_WIDTH = 16;
|
|
34846
|
+
var FLYOUT_GAP = 3;
|
|
34362
34847
|
var DrawingToolbar = class {
|
|
34363
34848
|
constructor(host, theme, onArm, onMagnet = () => {
|
|
34364
34849
|
}, onMeasure = () => {
|
|
@@ -34388,6 +34873,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34388
34873
|
// group id (or MAGNET_ID) whose flyout is open
|
|
34389
34874
|
this.flyoutCell = null;
|
|
34390
34875
|
// the cell the open flyout is anchored to
|
|
34876
|
+
/** Closed flyouts still playing their exit animation, by owner id — reopening the same
|
|
34877
|
+
* owner takes its element back instead of stacking a second one on it. */
|
|
34878
|
+
this.exitingFlyouts = /* @__PURE__ */ new Map();
|
|
34391
34879
|
this.groupCells = /* @__PURE__ */ new Map();
|
|
34392
34880
|
// the composite cell (hover/active bg + flyout anchor)
|
|
34393
34881
|
this.groupIcons = /* @__PURE__ */ new Map();
|
|
@@ -34496,6 +34984,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34496
34984
|
}
|
|
34497
34985
|
destroy() {
|
|
34498
34986
|
this.closeFlyout();
|
|
34987
|
+
for (const { exit } of [...this.exitingFlyouts.values()]) exit.finish();
|
|
34499
34988
|
for (const dispose of this.tipDisposers.splice(0)) dispose();
|
|
34500
34989
|
this.root.remove();
|
|
34501
34990
|
}
|
|
@@ -34723,16 +35212,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34723
35212
|
* outside-dismiss. Callers fill it with items. */
|
|
34724
35213
|
beginFlyout(ownerId, cell) {
|
|
34725
35214
|
const t = this.theme;
|
|
34726
|
-
const
|
|
35215
|
+
const back = this.exitingFlyouts.get(ownerId);
|
|
35216
|
+
if (back) {
|
|
35217
|
+
this.exitingFlyouts.delete(ownerId);
|
|
35218
|
+
back.exit.cancel();
|
|
35219
|
+
back.fly.replaceChildren();
|
|
35220
|
+
}
|
|
35221
|
+
const fly = back?.fly ?? document.createElement("div");
|
|
34727
35222
|
fly.className = "vela-dtb-flyout";
|
|
34728
|
-
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:
|
|
35223
|
+
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:var(--vela-radius-md);background:var(--vela-surface);border:1px solid ${this.borderOverride ?? "var(--vela-border-strong)"};box-shadow:var(--vela-shadow);pointer-events:auto;overflow-y:auto;overscroll-behavior:contain;`;
|
|
34729
35224
|
applyChromeTokens(fly, t);
|
|
34730
35225
|
this.host.appendChild(fly);
|
|
34731
35226
|
const r = cell.getBoundingClientRect();
|
|
34732
35227
|
const rootR = this.root.getBoundingClientRect();
|
|
34733
35228
|
const hostR = this.host.getBoundingClientRect();
|
|
34734
35229
|
const top = r.top - hostR.top;
|
|
34735
|
-
fly.style.left = `${rootR.right - hostR.left
|
|
35230
|
+
fly.style.left = `${rootR.right - hostR.left + FLYOUT_GAP}px`;
|
|
34736
35231
|
fly.style.top = `${top}px`;
|
|
34737
35232
|
fly.style.maxHeight = `${Math.max(120, hostR.height - top - 8)}px`;
|
|
34738
35233
|
this.flyout = fly;
|
|
@@ -34748,6 +35243,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34748
35243
|
if (!cell) return;
|
|
34749
35244
|
const fly = this.beginFlyout(group.id, cell);
|
|
34750
35245
|
const sections = group.sections ?? [{ label: "", tools: group.tools }];
|
|
35246
|
+
const badge = sections.some((s) => s.tools.some((tool) => !!tool.icon));
|
|
34751
35247
|
for (let si = 0; si < sections.length; si++) {
|
|
34752
35248
|
const section = sections[si];
|
|
34753
35249
|
if (section.label) {
|
|
@@ -34758,6 +35254,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34758
35254
|
fly.appendChild(
|
|
34759
35255
|
this.makeFlyoutItem({
|
|
34760
35256
|
icon: tool.icon,
|
|
35257
|
+
badge,
|
|
34761
35258
|
label: tool.label,
|
|
34762
35259
|
selected: tool.type === this.active,
|
|
34763
35260
|
shortcut: this.shortcuts.get(tool.type),
|
|
@@ -34770,6 +35267,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34770
35267
|
);
|
|
34771
35268
|
}
|
|
34772
35269
|
}
|
|
35270
|
+
announceSurface(fly, true, "menu", cell);
|
|
34773
35271
|
}
|
|
34774
35272
|
openMagnetFlyout() {
|
|
34775
35273
|
const cell = this.magnetCell;
|
|
@@ -34793,6 +35291,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34793
35291
|
})
|
|
34794
35292
|
);
|
|
34795
35293
|
}
|
|
35294
|
+
announceSurface(fly, true, "menu", cell);
|
|
34796
35295
|
}
|
|
34797
35296
|
flyoutHeader(text) {
|
|
34798
35297
|
const t = this.theme;
|
|
@@ -34806,21 +35305,24 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34806
35305
|
div.style.cssText = `height:1px;margin:4px 8px;background:${this.borderColor};`;
|
|
34807
35306
|
return div;
|
|
34808
35307
|
}
|
|
34809
|
-
/** A flyout row, left to right: optional leading icon, label, a check when it's the
|
|
34810
|
-
* entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
34811
|
-
* Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
35308
|
+
/** A flyout row, left to right: optional leading icon badge, label, a check when it's the
|
|
35309
|
+
* selected entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
35310
|
+
* rows). Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
34812
35311
|
makeFlyoutItem(opts) {
|
|
34813
35312
|
const t = this.theme;
|
|
34814
35313
|
const item = document.createElement("button");
|
|
34815
35314
|
item.type = "button";
|
|
34816
35315
|
item.className = "vela-dtb-item";
|
|
34817
35316
|
item.setAttribute("aria-label", opts.label);
|
|
34818
|
-
|
|
34819
|
-
|
|
34820
|
-
|
|
34821
|
-
|
|
34822
|
-
|
|
34823
|
-
|
|
35317
|
+
const badged = opts.badge === true || !!opts.icon;
|
|
35318
|
+
const box = badged ? "gap:10px;padding:4px 10px 4px 6px;" : "gap:8px;padding:5px 10px 5px 8px;";
|
|
35319
|
+
item.style.cssText = `display:flex;align-items:center;${box}cursor:pointer;color:${t.textColor};border-radius:var(--vela-radius-sm);font:13px ${t.fontFamily};white-space:nowrap;min-width:148px;`;
|
|
35320
|
+
if (opts.selected) item.dataset.selected = "1";
|
|
35321
|
+
if (badged) {
|
|
35322
|
+
const badge = document.createElement("span");
|
|
35323
|
+
badge.className = "vela-dtb-badge";
|
|
35324
|
+
if (opts.icon) badge.innerHTML = opts.icon;
|
|
35325
|
+
item.appendChild(badge);
|
|
34824
35326
|
}
|
|
34825
35327
|
const label = document.createElement("span");
|
|
34826
35328
|
label.textContent = opts.label;
|
|
@@ -34861,9 +35363,16 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34861
35363
|
}
|
|
34862
35364
|
closeFlyout() {
|
|
34863
35365
|
this.starEls.clear();
|
|
34864
|
-
|
|
34865
|
-
|
|
35366
|
+
const fly = this.flyout;
|
|
35367
|
+
if (fly) {
|
|
34866
35368
|
this.flyout = null;
|
|
35369
|
+
announceSurface(fly, false, "menu", this.flyoutCell);
|
|
35370
|
+
const owner = this.flyoutOwnerId ?? "";
|
|
35371
|
+
const exit = holdForExit(fly, () => {
|
|
35372
|
+
if (this.exitingFlyouts.get(owner)?.fly === fly) this.exitingFlyouts.delete(owner);
|
|
35373
|
+
fly.remove();
|
|
35374
|
+
});
|
|
35375
|
+
if (exit) this.exitingFlyouts.set(owner, { fly, exit });
|
|
34867
35376
|
this.flyoutCell?.classList.remove("vela-open");
|
|
34868
35377
|
this.flyoutCell = null;
|
|
34869
35378
|
this.flyoutOwnerId = null;
|
|
@@ -34947,6 +35456,11 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
34947
35456
|
.vela-dtb[data-collapsed='1'] .vela-dtb-collapse .vela-dtb-hit{width:14px;}
|
|
34948
35457
|
.vela-dtb-item{background:transparent;border:none;transition:background var(--vela-dur-fast) ease;}
|
|
34949
35458
|
.vela-dtb-item:hover{background:var(--vela-hover-strong);}
|
|
35459
|
+
.vela-dtb-badge{width:24px;height:24px;padding:3px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;border-radius:var(--vela-radius-md);background:var(--vela-hover);border:1px solid var(--vela-border);color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,border-color var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
|
|
35460
|
+
.vela-dtb-badge:empty{background:transparent;border-color:transparent;}
|
|
35461
|
+
.vela-dtb-badge svg{width:100%;height:100%;}
|
|
35462
|
+
.vela-dtb-item:hover .vela-dtb-badge:not(:empty){background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
35463
|
+
.vela-dtb-item[data-selected='1'] .vela-dtb-badge:not(:empty){background:color-mix(in srgb,var(--vela-fg-bright) 83%,var(--vela-surface));border-color:color-mix(in srgb,var(--vela-fg-bright) 40%,transparent);color:var(--vela-surface);}
|
|
34950
35464
|
.vela-dtb-star{width:26px;height:22px;margin:-3px -5px -3px 0;padding:3px 5px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;opacity:0;color:inherit;border-radius:var(--vela-radius-sm);transition:opacity .1s ease,color .1s ease,background .1s ease;}
|
|
34951
35465
|
.vela-dtb-star svg{width:16px;height:16px;}
|
|
34952
35466
|
.vela-dtb-item:hover .vela-dtb-star{opacity:.55;}
|
|
@@ -35188,6 +35702,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35188
35702
|
this.drawings = [];
|
|
35189
35703
|
/** Ids painted on an interleave layer this frame — the top canvas paints only their handles. */
|
|
35190
35704
|
this.sliced = /* @__PURE__ */ new Set();
|
|
35705
|
+
/** Interleaved drawings mid-drag: painted on the top canvas, left out of their slice. */
|
|
35706
|
+
this.lifted = /* @__PURE__ */ new Set();
|
|
35191
35707
|
/** Cached slice canvases, keyed `paneId|beforeZ`, reused across frames to avoid churn. */
|
|
35192
35708
|
this.sliceCache = /* @__PURE__ */ new Map();
|
|
35193
35709
|
/** Series boundaries per pane as of the last `prepareSlices` — lets a repaint between data
|
|
@@ -35275,7 +35791,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35275
35791
|
selectedIds: () => this.selectedIds,
|
|
35276
35792
|
emit: (i) => this.emit(i),
|
|
35277
35793
|
changed: () => {
|
|
35278
|
-
this.
|
|
35794
|
+
this.syncLift();
|
|
35279
35795
|
this.render();
|
|
35280
35796
|
},
|
|
35281
35797
|
openSettings: (id, x, y) => this.openSettingsById(id, x, y),
|
|
@@ -35564,6 +36080,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35564
36080
|
this.render();
|
|
35565
36081
|
}
|
|
35566
36082
|
}
|
|
36083
|
+
/** Drop the hover (its handles) — the pointer no longer reaches the drawings. */
|
|
36084
|
+
clearHover() {
|
|
36085
|
+
if (this.hoveredId == null) return;
|
|
36086
|
+
this.hoveredId = null;
|
|
36087
|
+
this.render();
|
|
36088
|
+
}
|
|
35567
36089
|
pointerUp(x, y, snap = "off") {
|
|
35568
36090
|
if (this.eraserMode) {
|
|
35569
36091
|
this.erasing = false;
|
|
@@ -35901,6 +36423,20 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35901
36423
|
/** A drawing that paints inside the series stack changed (content, not hover): its pixels
|
|
35902
36424
|
* live in the backend composite, so this layer alone can't show the change — ask for a
|
|
35903
36425
|
* data frame, which re-runs `prepareSlices` before the backend composites. */
|
|
36426
|
+
/** A drawing being dragged leaves its interleave slice for the top canvas, so each pointer
|
|
36427
|
+
* move repaints one 2D layer instead of re-rasterizing and re-uploading a plot-sized slice
|
|
36428
|
+
* texture through the data frame. The slices rebuild once as the drag starts and once as it
|
|
36429
|
+
* ends (release, cancel), when the lifted set changes. */
|
|
36430
|
+
syncLift() {
|
|
36431
|
+
const moving = this.interaction.movingIds();
|
|
36432
|
+
let same = moving.size === this.lifted.size;
|
|
36433
|
+
if (same) {
|
|
36434
|
+
for (const id of moving) if (!this.lifted.has(id)) same = false;
|
|
36435
|
+
}
|
|
36436
|
+
if (same) return;
|
|
36437
|
+
this.lifted = new Set(moving);
|
|
36438
|
+
this.invalidateSlices();
|
|
36439
|
+
}
|
|
35904
36440
|
invalidateSlices() {
|
|
35905
36441
|
if (this.sliced.size > 0 || this.drawings.some((d) => this.isInterleaved(d))) this.deps.requestDataPaint();
|
|
35906
36442
|
}
|
|
@@ -35919,7 +36455,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35919
36455
|
const theme = this.deps.theme();
|
|
35920
36456
|
const buckets = /* @__PURE__ */ new Map();
|
|
35921
36457
|
for (const d of this.drawings) {
|
|
35922
|
-
if (!d.visible) continue;
|
|
36458
|
+
if (!d.visible || this.lifted.has(d.id)) continue;
|
|
35923
36459
|
const beforeZ = sliceKeyFor(d.zIndex, this.lastBounds.get(d.paneId) ?? []);
|
|
35924
36460
|
if (beforeZ === null) continue;
|
|
35925
36461
|
const key = `${d.paneId}|${beforeZ}`;
|
|
@@ -35971,8 +36507,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
35971
36507
|
mutedLabel: edited instanceof TextLabel ? edited.id : null
|
|
35972
36508
|
};
|
|
35973
36509
|
this.painter.seriesLook = this.deps.seriesLook();
|
|
35974
|
-
|
|
35975
|
-
this.painter.
|
|
36510
|
+
const onTop = (d) => !this.isInterleaved(d) || this.lifted.has(d.id);
|
|
36511
|
+
this.painter.paintAll(ctx, this.drawings.filter(onTop), proj, this.deps.theme(), targets);
|
|
36512
|
+
this.painter.paintHighlights(ctx, this.drawings.filter((d) => !onTop(d)), proj, handleIdsFor(targets));
|
|
35976
36513
|
const clones = this.interaction.dragClones();
|
|
35977
36514
|
if (clones) this.painter.paintAll(ctx, clones, proj, this.deps.theme(), { selected: new Set(clones.map((c) => c.id)) });
|
|
35978
36515
|
this.layoutTextEditor();
|
|
@@ -36539,9 +37076,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
36539
37076
|
consider(drawings.max);
|
|
36540
37077
|
}
|
|
36541
37078
|
if (min2 === Infinity || max2 === -Infinity) return { min: 0, max: 1 };
|
|
36542
|
-
if (min2
|
|
36543
|
-
const
|
|
36544
|
-
|
|
37079
|
+
if (max2 - min2 <= Math.max(Math.abs(min2), Math.abs(max2)) * 1e-12) {
|
|
37080
|
+
const mid = (min2 + max2) / 2;
|
|
37081
|
+
const pad = Math.abs(mid) * 0.1 || 1;
|
|
37082
|
+
return { min: mid - pad, max: mid + pad, log: log && mid - pad > 0 };
|
|
36545
37083
|
}
|
|
36546
37084
|
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
36547
37085
|
const above = margins.top / 100 / content;
|
|
@@ -37045,7 +37583,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37045
37583
|
return Math.ceil(logical) - 0.5;
|
|
37046
37584
|
}
|
|
37047
37585
|
var BackdropRenderer = class {
|
|
37048
|
-
|
|
37586
|
+
/** `axisTicks` is shared with the chrome layer, so gridlines and price labels agree. */
|
|
37587
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
37588
|
+
this.axisTicks = axisTicks;
|
|
37049
37589
|
this.canvas = null;
|
|
37050
37590
|
this.ctx = null;
|
|
37051
37591
|
}
|
|
@@ -37122,9 +37662,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37122
37662
|
if (scene.showGrid && gridHorz.visible && !pane.collapsed) {
|
|
37123
37663
|
ctx.globalAlpha = gridAlpha;
|
|
37124
37664
|
ctx.strokeStyle = horzColor;
|
|
37125
|
-
const pct = percentScaleFor(scene, pane);
|
|
37126
37665
|
ctx.beginPath();
|
|
37127
|
-
for (const t of
|
|
37666
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
37128
37667
|
const y = Math.round(coords.priceToY(t.price, pane.scale, pane.bounds)) + 0.5;
|
|
37129
37668
|
if (y < pane.bounds.top || y > pane.bounds.top + pane.bounds.height) continue;
|
|
37130
37669
|
ctx.moveTo(0, y);
|
|
@@ -37740,8 +38279,16 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37740
38279
|
this.toolbarGutter = 0;
|
|
37741
38280
|
// px reserved on the left for the docked drawings toolbar (0 when hidden)
|
|
37742
38281
|
this.mountContainer = null;
|
|
38282
|
+
/** The host-styleable twin of the `shadeRight` veil (`.vela-shade-right`), directly under
|
|
38283
|
+
* the cursor canvas; hidden whenever the crosshair layer paints no veil. */
|
|
38284
|
+
this.shadeRightEl = null;
|
|
38285
|
+
/** The veil geometry last written to {@link shadeRightEl} ('' = hidden) — the DOM is
|
|
38286
|
+
* touched only when it changes, never on a plain pointer move. */
|
|
38287
|
+
this.shadeRightSig = "";
|
|
37743
38288
|
this.userDrawings = null;
|
|
37744
|
-
|
|
38289
|
+
/** One price-axis tick source for the gridlines (backdrop) and the labels (chrome). */
|
|
38290
|
+
this.priceAxisTicks = new PriceAxisTickSource();
|
|
38291
|
+
this.backdropRenderer = new BackdropRenderer(this.priceAxisTicks);
|
|
37745
38292
|
this.volumeRenderer = new VolumeRenderer();
|
|
37746
38293
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
37747
38294
|
/**
|
|
@@ -37774,7 +38321,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37774
38321
|
this.backendMode = "auto";
|
|
37775
38322
|
this.glowAmount = 0;
|
|
37776
38323
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
37777
|
-
this.chrome = new ChromeRenderer();
|
|
38324
|
+
this.chrome = new ChromeRenderer(this.priceAxisTicks);
|
|
37778
38325
|
/** Prepaints each indicator's Pine drawings into interleave slices at the model's z. */
|
|
37779
38326
|
this.indicatorSlices = new IndicatorDrawingSlices();
|
|
37780
38327
|
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
@@ -37891,6 +38438,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37891
38438
|
// pane just below it
|
|
37892
38439
|
this.resizeSplitStart = null;
|
|
37893
38440
|
// the two panes' shared span when the drag began
|
|
38441
|
+
/** The user drawings take part in pointer input (the runtime `drawingsInteractive` feature). */
|
|
38442
|
+
this.drawingsInteractive = true;
|
|
37894
38443
|
this.hoverSeparatorY = null;
|
|
37895
38444
|
// pixel y of the separator under the cursor (drives its hover highlight)
|
|
37896
38445
|
// ── pane management (merge / reorder / collapse / maximize) ──
|
|
@@ -37946,8 +38495,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37946
38495
|
this.toggleVisibleCbs = /* @__PURE__ */ new Set();
|
|
37947
38496
|
this.moveIndicatorCbs = /* @__PURE__ */ new Set();
|
|
37948
38497
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
38498
|
+
this.priceStyleWillChangeCbs = /* @__PURE__ */ new Set();
|
|
37949
38499
|
this.name = "native";
|
|
37950
|
-
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride"];
|
|
38500
|
+
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride", "drawingsInteractive", "priceAxisTicks"];
|
|
37951
38501
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
37952
38502
|
* so moving onto the button doesn't count as leaving). */
|
|
37953
38503
|
this.onScrollProximityMove = (e) => {
|
|
@@ -37991,6 +38541,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
37991
38541
|
this.scene.priceStyle = opts.priceStyle;
|
|
37992
38542
|
this.scene.basePainting = basePaintingOf(opts.priceStyle);
|
|
37993
38543
|
this.scene.candleOverride = candleOverrideFor(opts.priceStyle, this.scene.style.chartTypes);
|
|
38544
|
+
if (typeof opts.priceAxisTicks === "function") this.priceAxisTicks.setHook(opts.priceAxisTicks);
|
|
37994
38545
|
}
|
|
37995
38546
|
this.theme = this.deriveTheme(DARK_THEME);
|
|
37996
38547
|
}
|
|
@@ -38129,6 +38680,15 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38129
38680
|
case "crosshairOverride":
|
|
38130
38681
|
this.scene.crosshairOverride = sanitizeCrosshairOverride(value);
|
|
38131
38682
|
break;
|
|
38683
|
+
case "drawingsInteractive":
|
|
38684
|
+
this.drawingsInteractive = value !== false;
|
|
38685
|
+
if (this.input) this.input.drawings = this.drawingsInteractive;
|
|
38686
|
+
if (!this.drawingsInteractive) this.userDrawings?.clearHover();
|
|
38687
|
+
return;
|
|
38688
|
+
// affects the next gesture only — clearHover repaints what it drops
|
|
38689
|
+
case "priceAxisTicks":
|
|
38690
|
+
this.priceAxisTicks.setHook(typeof value === "function" ? value : null);
|
|
38691
|
+
break;
|
|
38132
38692
|
case "keyboard":
|
|
38133
38693
|
this.setKeyboardEnabled(Boolean(value));
|
|
38134
38694
|
return;
|
|
@@ -38244,6 +38804,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38244
38804
|
return { visible: this.scene.marks.visible, groups: { ...this.scene.marks.groups } };
|
|
38245
38805
|
case "crosshairOverride":
|
|
38246
38806
|
return this.scene.crosshairOverride ? { ...this.scene.crosshairOverride } : null;
|
|
38807
|
+
case "drawingsInteractive":
|
|
38808
|
+
return this.drawingsInteractive;
|
|
38809
|
+
case "priceAxisTicks":
|
|
38810
|
+
return this.priceAxisTicks.hook;
|
|
38247
38811
|
case "keyboard":
|
|
38248
38812
|
return this.keyboardEnabled;
|
|
38249
38813
|
case "historyChords":
|
|
@@ -38917,6 +39481,12 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38917
39481
|
Object.assign(this.drawingsCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
38918
39482
|
this.cursorCanvas = document.createElement("canvas");
|
|
38919
39483
|
Object.assign(this.cursorCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
39484
|
+
this.shadeRightEl = document.createElement("div");
|
|
39485
|
+
this.shadeRightEl.className = "vela-shade-right";
|
|
39486
|
+
this.shadeRightEl.setAttribute("aria-hidden", "true");
|
|
39487
|
+
this.shadeRightEl.hidden = true;
|
|
39488
|
+
Object.assign(this.shadeRightEl.style, { position: "absolute", top: "0", left: "0", width: "0", height: "0", pointerEvents: "none", display: "none" });
|
|
39489
|
+
this.shadeRightSig = "";
|
|
38920
39490
|
this.overlayRoot = document.createElement("div");
|
|
38921
39491
|
Object.assign(this.overlayRoot.style, { position: "absolute", inset: "0", pointerEvents: "none" });
|
|
38922
39492
|
this.plot = document.createElement("div");
|
|
@@ -38924,7 +39494,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38924
39494
|
this.extLayers = rendererLayers().map((def) => ({ def, instance: def.create(), canvas: this.createLayerCanvas(), channel: def.id, owner: null }));
|
|
38925
39495
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
38926
39496
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
38927
|
-
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
39497
|
+
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.shadeRightEl, this.cursorCanvas, this.overlayRoot);
|
|
38928
39498
|
this.layerOrderSig = "";
|
|
38929
39499
|
this.wrapper.appendChild(this.plot);
|
|
38930
39500
|
this.factoryConfig = this.getConfig();
|
|
@@ -38982,13 +39552,14 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
38982
39552
|
drawingsPointerDown: (x, y, snap, shift4, mod) => this.userDrawings?.pointerDown(x, y, snap, shift4, mod),
|
|
38983
39553
|
drawingsPointerMove: (x, y, snap, shift4, mod) => this.userDrawings?.pointerMove(x, y, snap, shift4, mod),
|
|
38984
39554
|
drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
|
|
38985
|
-
drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
39555
|
+
drawingsCursor: (x, y) => (this.drawingsInteractive ? this.userDrawings?.cursorAt(x, y) : null) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
38986
39556
|
drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
|
|
38987
39557
|
drawingsClearTransient: () => this.userDrawings?.clearTransient()
|
|
38988
39558
|
});
|
|
38989
39559
|
this.input.rightEdgeZoom = this.zoomAnchorMode === "right";
|
|
38990
39560
|
this.input.axisDrag = this.axisDragEnabled;
|
|
38991
39561
|
this.input.paneResize = this.paneResizeEnabled;
|
|
39562
|
+
this.input.drawings = this.drawingsInteractive;
|
|
38992
39563
|
this.input.attach(this.dataCanvas);
|
|
38993
39564
|
if (this.settingsEnabled) this.setSettingsEnabled(true);
|
|
38994
39565
|
this.syncCountdownTimer();
|
|
@@ -39297,6 +39868,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39297
39868
|
this.backend.destroy();
|
|
39298
39869
|
this.chrome.destroy();
|
|
39299
39870
|
this.crosshairLayer.destroy();
|
|
39871
|
+
this.shadeRightEl?.remove();
|
|
39872
|
+
this.shadeRightEl = null;
|
|
39300
39873
|
this.attributionEl?.remove();
|
|
39301
39874
|
this.attributionEl = null;
|
|
39302
39875
|
this.mountContainer?.style.removeProperty("--vela-toolbar-gutter");
|
|
@@ -39643,14 +40216,21 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39643
40216
|
this.priceStyleCbs.add(cb);
|
|
39644
40217
|
return () => this.priceStyleCbs.delete(cb);
|
|
39645
40218
|
}
|
|
40219
|
+
onPriceStyleWillChange(cb) {
|
|
40220
|
+
this.priceStyleWillChangeCbs.add(cb);
|
|
40221
|
+
return () => this.priceStyleWillChangeCbs.delete(cb);
|
|
40222
|
+
}
|
|
39646
40223
|
/**
|
|
39647
40224
|
* THE single write path for the base price style at runtime (feature set / settings dialog /
|
|
39648
|
-
* config template — the constructor seeds the field directly, pre-listeners).
|
|
39649
|
-
*
|
|
39650
|
-
*
|
|
40225
|
+
* config template — the constructor seeds the field directly, pre-listeners). Announces the
|
|
40226
|
+
* switch while the scene still holds the old style (a listener may paint/capture that frame),
|
|
40227
|
+
* then updates the scene, eases any reveal layer toward the new style's target, and notifies
|
|
40228
|
+
* the core — which owns the DATA side of styles that need one (a chart type's SeriesDataEngine).
|
|
39651
40229
|
*/
|
|
39652
40230
|
setPriceStyle(style) {
|
|
39653
40231
|
if (style === this.scene.priceStyle) return;
|
|
40232
|
+
const from = this.scene.priceStyle;
|
|
40233
|
+
for (const cb of this.priceStyleWillChangeCbs) cb(from, style);
|
|
39654
40234
|
this.scene.priceStyle = style;
|
|
39655
40235
|
this.scene.basePainting = basePaintingOf(style);
|
|
39656
40236
|
this.scene.candleOverride = candleOverrideFor(style, this.scene.style.chartTypes);
|
|
@@ -39880,7 +40460,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
39880
40460
|
if (this.easeLiveBar(dtMs)) active = true;
|
|
39881
40461
|
this.skeletonClockMs += dtMs;
|
|
39882
40462
|
this.paintData();
|
|
39883
|
-
this.
|
|
40463
|
+
this.renderCrosshairLayer();
|
|
39884
40464
|
this.updateLegendValues();
|
|
39885
40465
|
this.emitViewportChange();
|
|
39886
40466
|
return active;
|
|
@@ -40455,10 +41035,31 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
40455
41035
|
this.chrome.render(this.scene, this.coords, this.theme, this.axisSurface());
|
|
40456
41036
|
this.trackMarkPopover();
|
|
40457
41037
|
}
|
|
40458
|
-
this.
|
|
41038
|
+
this.renderCrosshairLayer();
|
|
40459
41039
|
if (!repaintsData(level) && this.paintedData) this.repaintCursorLayers();
|
|
40460
41040
|
this.updateLegendValues();
|
|
40461
41041
|
}
|
|
41042
|
+
/** Repaint the L2 crosshair canvas and keep the `.vela-shade-right` element on the veil it painted. */
|
|
41043
|
+
renderCrosshairLayer() {
|
|
41044
|
+
this.crosshairLayer.render(this.scene, this.coords, this.theme, this.hoverSeparatorY, this.externalCrossPx());
|
|
41045
|
+
this.syncShadeRight();
|
|
41046
|
+
}
|
|
41047
|
+
/** Position (or hide) the `.vela-shade-right` element over the veil of the last crosshair frame. */
|
|
41048
|
+
syncShadeRight() {
|
|
41049
|
+
const el = this.shadeRightEl;
|
|
41050
|
+
if (!el) return;
|
|
41051
|
+
const area = this.crosshairLayer.shadeRightArea;
|
|
41052
|
+
const sig = area ? `${area.x},${area.width},${area.height}` : "";
|
|
41053
|
+
if (sig === this.shadeRightSig) return;
|
|
41054
|
+
this.shadeRightSig = sig;
|
|
41055
|
+
if (!area) {
|
|
41056
|
+
el.hidden = true;
|
|
41057
|
+
el.style.display = "none";
|
|
41058
|
+
return;
|
|
41059
|
+
}
|
|
41060
|
+
Object.assign(el.style, { left: `${area.x}px`, width: `${area.width}px`, height: `${area.height}px`, display: "" });
|
|
41061
|
+
el.hidden = false;
|
|
41062
|
+
}
|
|
40462
41063
|
/** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
|
|
40463
41064
|
repaintCursorLayers() {
|
|
40464
41065
|
const pane = this.scene.panes.get(PRICE_PANE_ID);
|
|
@@ -41252,7 +41853,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
41252
41853
|
if (this.animator.active) {
|
|
41253
41854
|
this.computeScales();
|
|
41254
41855
|
this.paintData();
|
|
41255
|
-
this.
|
|
41856
|
+
this.renderCrosshairLayer();
|
|
41256
41857
|
} else {
|
|
41257
41858
|
this.scheduler.flushNow(4 /* Full */);
|
|
41258
41859
|
}
|
|
@@ -45296,7 +45897,8 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
45296
45897
|
glow: options.glow ?? 0,
|
|
45297
45898
|
upColor: options.upColor ?? BULLISH,
|
|
45298
45899
|
downColor: options.downColor ?? BEARISH,
|
|
45299
|
-
priceStyle: options.priceStyle ?? "candles"
|
|
45900
|
+
priceStyle: options.priceStyle ?? "candles",
|
|
45901
|
+
priceAxisTicks: options.priceAxis?.ticks ?? null
|
|
45300
45902
|
};
|
|
45301
45903
|
const RendererClass = options.renderer ?? NativeRenderer;
|
|
45302
45904
|
if (typeof RendererClass !== "function") {
|
|
@@ -45782,6 +46384,23 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
45782
46384
|
return { ...Object.fromEntries(defaultEngines), ...overrides };
|
|
45783
46385
|
}
|
|
45784
46386
|
|
|
46387
|
+
// src/widget/context-menu-model.ts
|
|
46388
|
+
var CONTEXT_MENU_BUILTIN_ORDER = {
|
|
46389
|
+
body: { "reset-view": -30, "remove-drawings": -20, "remove-indicators": -10, settings: 1e3 },
|
|
46390
|
+
"price-axis": {
|
|
46391
|
+
auto: -80,
|
|
46392
|
+
invert: -70,
|
|
46393
|
+
"scale:regular": -60,
|
|
46394
|
+
"scale:percent": -50,
|
|
46395
|
+
"scale:indexed": -40,
|
|
46396
|
+
"scale:log": -30,
|
|
46397
|
+
labels: -20,
|
|
46398
|
+
levels: -10,
|
|
46399
|
+
settings: 1e3
|
|
46400
|
+
},
|
|
46401
|
+
"time-axis": { timezone: -10, settings: 1e3 }
|
|
46402
|
+
};
|
|
46403
|
+
|
|
45785
46404
|
// src/widget/side-panel.ts
|
|
45786
46405
|
var DEFAULT_PANEL_WIDTH = 280;
|
|
45787
46406
|
var DEFAULT_PANEL_MIN_WIDTH = 200;
|
|
@@ -47031,6 +47650,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
|
|
|
47031
47650
|
exports.BinanceProvider = BinanceProvider;
|
|
47032
47651
|
exports.CATEGORICAL = CATEGORICAL;
|
|
47033
47652
|
exports.CHIP_PLATE = CHIP_PLATE;
|
|
47653
|
+
exports.CONTEXT_MENU_BUILTIN_ORDER = CONTEXT_MENU_BUILTIN_ORDER;
|
|
47034
47654
|
exports.CROSSHAIR = CROSSHAIR;
|
|
47035
47655
|
exports.CachingDataFeed = CachingDataFeed;
|
|
47036
47656
|
exports.CoinbaseProvider = CoinbaseProvider;
|