@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/index.cjs
CHANGED
|
@@ -238,7 +238,7 @@ var IndicatorHandleImpl = class {
|
|
|
238
238
|
this.controller.applyProps(this.id, values);
|
|
239
239
|
}
|
|
240
240
|
updateCode(source) {
|
|
241
|
-
this.controller.updateCode(this.id, source);
|
|
241
|
+
return this.controller.updateCode(this.id, source);
|
|
242
242
|
}
|
|
243
243
|
setVisible(visible) {
|
|
244
244
|
this.controller.setVisible(this.id, visible);
|
|
@@ -641,6 +641,7 @@ registerIcon("expand", S('<rect x="2.2" y="2.2" width="11.6" height="11.6" rx="1
|
|
|
641
641
|
registerIcon("plus", S('<path d="M8 2.8v10.4M2.8 8h10.4"/>'));
|
|
642
642
|
registerIcon("minus", S('<path d="M2.8 8h10.4"/>'));
|
|
643
643
|
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"/>'));
|
|
644
|
+
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"/>'));
|
|
644
645
|
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"/>'));
|
|
645
646
|
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"/>'));
|
|
646
647
|
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"'));
|
|
@@ -3299,6 +3300,10 @@ var FibRatios = class extends Drawing {
|
|
|
3299
3300
|
|
|
3300
3301
|
// src/core/drawings/types/FibLevels.ts
|
|
3301
3302
|
var FibLevels = class extends FibRatios {
|
|
3303
|
+
/** Price of a level for the two anchor prices; subclasses override to change which anchor ratio 0 sits on. */
|
|
3304
|
+
levelPrice(ratio, p1, p2) {
|
|
3305
|
+
return p1 + ratio * (p2 - p1);
|
|
3306
|
+
}
|
|
3302
3307
|
/** Per-level pixel line + price for the ENABLED levels, spanning the anchors' time range. */
|
|
3303
3308
|
levelLines(proj) {
|
|
3304
3309
|
const a = this.anchors[0];
|
|
@@ -3308,11 +3313,10 @@ var FibLevels = class extends FibRatios {
|
|
|
3308
3313
|
const xb = proj.xOf(b.time);
|
|
3309
3314
|
const x1 = Math.min(xa, xb);
|
|
3310
3315
|
const x2 = Math.max(xa, xb);
|
|
3311
|
-
const delta = b.price - a.price;
|
|
3312
3316
|
const out = [];
|
|
3313
3317
|
for (const lv of this.levels) {
|
|
3314
3318
|
if (!lv.enabled) continue;
|
|
3315
|
-
const price =
|
|
3319
|
+
const price = this.levelPrice(lv.ratio, a.price, b.price);
|
|
3316
3320
|
const y = proj.yOf(price, this.paneId);
|
|
3317
3321
|
if (y == null) continue;
|
|
3318
3322
|
out.push({ ratio: lv.ratio, color: lv.color, label: lv.label, price, x1, x2, y });
|
|
@@ -3352,8 +3356,7 @@ var FibLevels = class extends FibRatios {
|
|
|
3352
3356
|
const a = this.anchors[0];
|
|
3353
3357
|
const b = this.anchors[1];
|
|
3354
3358
|
if (!a || !b) return null;
|
|
3355
|
-
const
|
|
3356
|
-
const prices = this.levels.filter((l) => l.enabled).map((l) => a.price + l.ratio * delta);
|
|
3359
|
+
const prices = this.levels.filter((l) => l.enabled).map((l) => this.levelPrice(l.ratio, a.price, b.price));
|
|
3357
3360
|
if (prices.length === 0) return null;
|
|
3358
3361
|
return { min: Math.min(...prices), max: Math.max(...prices) };
|
|
3359
3362
|
}
|
|
@@ -3439,13 +3442,24 @@ function cycleLevels(count, enabledCount = count) {
|
|
|
3439
3442
|
// src/core/drawings/types/FibRetracement.ts
|
|
3440
3443
|
var LEVELS = fibLevels([0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]);
|
|
3441
3444
|
var FibRetracement = class extends FibLevels {
|
|
3442
|
-
constructor() {
|
|
3443
|
-
super(
|
|
3445
|
+
constructor(init) {
|
|
3446
|
+
super(init);
|
|
3444
3447
|
this.type = "fibretracement";
|
|
3448
|
+
if (this.reverse === void 0) this.reverse = false;
|
|
3445
3449
|
}
|
|
3446
3450
|
defaultLevels() {
|
|
3447
3451
|
return LEVELS;
|
|
3448
3452
|
}
|
|
3453
|
+
levelPrice(ratio, p1, p2) {
|
|
3454
|
+
return this.reverse ? p1 + ratio * (p2 - p1) : p2 + ratio * (p1 - p2);
|
|
3455
|
+
}
|
|
3456
|
+
writeProps() {
|
|
3457
|
+
return { ...super.writeProps(), reverse: this.reverse };
|
|
3458
|
+
}
|
|
3459
|
+
readProps(props) {
|
|
3460
|
+
super.readProps(props);
|
|
3461
|
+
this.reverse = typeof props.reverse === "boolean" ? props.reverse : true;
|
|
3462
|
+
}
|
|
3449
3463
|
};
|
|
3450
3464
|
|
|
3451
3465
|
// src/core/drawings/types/FibExtension.ts
|
|
@@ -7874,6 +7888,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
7874
7888
|
this.activeEngineStyle = null;
|
|
7875
7889
|
/** The chart's current price style (tracked from the renderer's change events + initial read). */
|
|
7876
7890
|
this.priceStyle = "candles";
|
|
7891
|
+
this.priceStyleWillChangeUnsub = null;
|
|
7877
7892
|
this.unresolvedUnsub = null;
|
|
7878
7893
|
/** Latest chart visible range (left/right bar times), fed to viewport-dependent scripts. */
|
|
7879
7894
|
this.visibleRange = null;
|
|
@@ -7972,6 +7987,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
7972
7987
|
this.renderer.onThemeSelect?.((name) => this.setTheme(resolveTheme(name)));
|
|
7973
7988
|
this.viewportUnsub = this.renderer.onViewportChange((range) => this.onViewportChange(range));
|
|
7974
7989
|
this.renderer.onPriceStyleChange?.((style) => this.syncPriceStyle(style));
|
|
7990
|
+
this.priceStyleWillChangeUnsub = this.renderer.onPriceStyleWillChange?.((from, to) => this.events.emit("priceStyle:change", { from, to })) ?? null;
|
|
7975
7991
|
const initialStyle = this.renderer.readFeature("priceStyle");
|
|
7976
7992
|
if (typeof initialStyle === "string") this.priceStyle = initialStyle;
|
|
7977
7993
|
this.barTransform = barTransformFor(initialStyle);
|
|
@@ -9258,20 +9274,26 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9258
9274
|
/**
|
|
9259
9275
|
* IndicatorController: replace a SCRIPT indicator's source in place (see
|
|
9260
9276
|
* {@link IndicatorHandle.updateCode}). Prepare-first: the running session keeps
|
|
9261
|
-
* computing until the new source has compiled
|
|
9262
|
-
*
|
|
9277
|
+
* computing until the new source has compiled; then the new code runs on TRIAL until
|
|
9278
|
+
* its first model lands, and a failure before that puts the previous code back.
|
|
9279
|
+
* Natives have no script — warn and leave them alone.
|
|
9263
9280
|
*/
|
|
9264
9281
|
updateCode(id, source) {
|
|
9265
9282
|
const record = this.registry.get(id);
|
|
9266
9283
|
const handle = this.handles.get(id);
|
|
9267
|
-
if (!record || !handle) return;
|
|
9284
|
+
if (!record || !handle) return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator is no longer on the chart.`) });
|
|
9268
9285
|
if (record.native) {
|
|
9269
9286
|
console.warn(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`);
|
|
9270
|
-
return;
|
|
9287
|
+
return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`) });
|
|
9271
9288
|
}
|
|
9272
|
-
|
|
9273
|
-
record.
|
|
9274
|
-
|
|
9289
|
+
const busy = record.pendingSource !== void 0 || record.codeTrial !== void 0;
|
|
9290
|
+
if (!busy && source === record.source) return Promise.resolve({ ok: true, error: null });
|
|
9291
|
+
return new Promise((resolve) => {
|
|
9292
|
+
(record.codeWaiters ?? (record.codeWaiters = [])).push(resolve);
|
|
9293
|
+
if (record.pendingSource === source || record.pendingSource === void 0 && record.codeTrial && record.source === source) return;
|
|
9294
|
+
record.pendingSource = source;
|
|
9295
|
+
void this.swapSource(id, source, handle);
|
|
9296
|
+
});
|
|
9275
9297
|
}
|
|
9276
9298
|
async swapSource(id, source, handle) {
|
|
9277
9299
|
const engine = this.registry.get(id)?.engine ?? this.engineFor(this.registry.get(id)?.options?.language);
|
|
@@ -9282,26 +9304,41 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9282
9304
|
const record2 = this.registry.get(id);
|
|
9283
9305
|
if (record2?.pendingSource === source) {
|
|
9284
9306
|
record2.pendingSource = void 0;
|
|
9285
|
-
this.
|
|
9307
|
+
this.rejectCode(id, record2, handle, err);
|
|
9286
9308
|
}
|
|
9287
9309
|
return;
|
|
9288
9310
|
}
|
|
9289
9311
|
const record = this.registry.get(id);
|
|
9290
9312
|
if (!record || record.pendingSource !== source) return;
|
|
9291
9313
|
record.pendingSource = void 0;
|
|
9314
|
+
const previous = record.codeTrial?.previous ?? {
|
|
9315
|
+
source: record.source,
|
|
9316
|
+
engine: record.engine,
|
|
9317
|
+
prepared: record.prepared,
|
|
9318
|
+
inputValues: record.inputValues,
|
|
9319
|
+
propValues: record.propValues,
|
|
9320
|
+
inputs: [...handle.inputs],
|
|
9321
|
+
props: [...handle.props]
|
|
9322
|
+
};
|
|
9323
|
+
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues, record.prepared?.inputs);
|
|
9324
|
+
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues, record.prepared?.props);
|
|
9292
9325
|
record.source = source;
|
|
9293
9326
|
record.engine = engine;
|
|
9294
9327
|
record.prepared = prepared;
|
|
9295
9328
|
handle.setSource(source);
|
|
9296
|
-
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues);
|
|
9297
|
-
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues);
|
|
9298
9329
|
handle.setSchema(prepared.inputs);
|
|
9299
9330
|
handle.setPropsSchema(prepared.props ?? []);
|
|
9300
9331
|
record.session?.stop();
|
|
9301
9332
|
record.session = void 0;
|
|
9302
9333
|
record.pendingStructural = true;
|
|
9303
9334
|
record.pendingCause = "code";
|
|
9304
|
-
if (record.hidden)
|
|
9335
|
+
if (record.hidden) {
|
|
9336
|
+
record.codeTrial = void 0;
|
|
9337
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9338
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9339
|
+
return;
|
|
9340
|
+
}
|
|
9341
|
+
record.codeTrial = { previous };
|
|
9305
9342
|
if (record.renderHandle) {
|
|
9306
9343
|
this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9307
9344
|
this.setLoading(record, true);
|
|
@@ -9309,6 +9346,46 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9309
9346
|
this.mountLoadingPlaceholder(id, record);
|
|
9310
9347
|
}
|
|
9311
9348
|
this.executeIndicator(id, handle);
|
|
9349
|
+
if (this.bars.length === 0 && record.codeTrial) {
|
|
9350
|
+
record.codeTrial = void 0;
|
|
9351
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9352
|
+
}
|
|
9353
|
+
}
|
|
9354
|
+
/** Resolve every `updateCode` caller waiting on this record's chain of edits. */
|
|
9355
|
+
settleCode(record, result) {
|
|
9356
|
+
const waiters = record.codeWaiters;
|
|
9357
|
+
record.codeWaiters = void 0;
|
|
9358
|
+
if (waiters) for (const resolve of waiters) resolve(result);
|
|
9359
|
+
}
|
|
9360
|
+
/**
|
|
9361
|
+
* An update failed — at prepare, or on trial before its first model. A trial puts the
|
|
9362
|
+
* previous code back (source, engine, prepared script, values, schema) and restarts it;
|
|
9363
|
+
* its visuals were never dropped, so the chart keeps painting throughout. Then the
|
|
9364
|
+
* failure reports the usual way and every waiting caller learns it.
|
|
9365
|
+
*/
|
|
9366
|
+
rejectCode(id, record, handle, err) {
|
|
9367
|
+
const trial = record.codeTrial;
|
|
9368
|
+
record.codeTrial = void 0;
|
|
9369
|
+
this.fail(id, handle, err);
|
|
9370
|
+
if (trial) {
|
|
9371
|
+
const p = trial.previous;
|
|
9372
|
+
record.session?.stop();
|
|
9373
|
+
record.session = void 0;
|
|
9374
|
+
record.source = p.source;
|
|
9375
|
+
record.engine = p.engine;
|
|
9376
|
+
record.prepared = p.prepared;
|
|
9377
|
+
record.inputValues = p.inputValues;
|
|
9378
|
+
record.propValues = p.propValues;
|
|
9379
|
+
handle.setSource(p.source);
|
|
9380
|
+
handle.setSchema(p.inputs);
|
|
9381
|
+
handle.setPropsSchema(p.props);
|
|
9382
|
+
record.pendingStructural = true;
|
|
9383
|
+
record.pendingCause = void 0;
|
|
9384
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
9385
|
+
if (!record.hidden) this.executeIndicator(id, handle);
|
|
9386
|
+
}
|
|
9387
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
9388
|
+
this.settleCode(record, { ok: false, error });
|
|
9312
9389
|
}
|
|
9313
9390
|
/** IndicatorController: tear down an indicator and (if now empty) its pane. */
|
|
9314
9391
|
/** Live handles of every indicator on the chart (script + native), insertion order. */
|
|
@@ -9323,6 +9400,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9323
9400
|
removeIndicator(id) {
|
|
9324
9401
|
const record = this.registry.remove(id);
|
|
9325
9402
|
if (record?.native?.type === "volume") this.volumeOptedOut = true;
|
|
9403
|
+
if (record) this.settleCode(record, { ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator was removed.`) });
|
|
9326
9404
|
record?.session?.stop();
|
|
9327
9405
|
if (record?.native?.started) record.native.instance.stop();
|
|
9328
9406
|
this.handles.delete(id);
|
|
@@ -9350,6 +9428,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9350
9428
|
if (!visible) {
|
|
9351
9429
|
record.session?.stop();
|
|
9352
9430
|
record.session = void 0;
|
|
9431
|
+
if (record.codeTrial) {
|
|
9432
|
+
record.codeTrial = void 0;
|
|
9433
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9434
|
+
}
|
|
9353
9435
|
if (record.native?.started) record.native.instance.suspend();
|
|
9354
9436
|
if (record.renderHandle) this.renderer.setIndicatorVisible?.(record.renderHandle, false);
|
|
9355
9437
|
else if (record.native) this.mountHiddenNativeRow(id, record);
|
|
@@ -9436,7 +9518,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9436
9518
|
if (this.viewportTimer != null) clearTimeout(this.viewportTimer);
|
|
9437
9519
|
this.viewportUnsub?.();
|
|
9438
9520
|
this.paneActionUnsub?.();
|
|
9521
|
+
this.priceStyleWillChangeUnsub?.();
|
|
9522
|
+
this.priceStyleWillChangeUnsub = null;
|
|
9439
9523
|
for (const record of this.registry.all()) {
|
|
9524
|
+
this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
|
|
9440
9525
|
record.session?.stop();
|
|
9441
9526
|
if (record.native?.started) record.native.instance.stop();
|
|
9442
9527
|
}
|
|
@@ -9505,7 +9590,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9505
9590
|
const record = this.registry.get(id);
|
|
9506
9591
|
if (!record || !record.engine || !record.prepared || record.hidden) return;
|
|
9507
9592
|
const mode = this.config.live && !record.prepared.reactsToViewport && record.engine.capabilities.streaming ? "live" : "static";
|
|
9508
|
-
|
|
9593
|
+
const prepared = record.prepared;
|
|
9594
|
+
const session = record.engine.execute(
|
|
9509
9595
|
{
|
|
9510
9596
|
prepared: record.prepared,
|
|
9511
9597
|
market: this.market(),
|
|
@@ -9526,6 +9612,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9526
9612
|
const cause = record.pendingCause ?? "history";
|
|
9527
9613
|
if (!this.applyModel(id, model)) return;
|
|
9528
9614
|
record.pendingCause = void 0;
|
|
9615
|
+
if (record.codeTrial) {
|
|
9616
|
+
record.codeTrial = void 0;
|
|
9617
|
+
this.settleCode(record, { ok: true, error: null });
|
|
9618
|
+
}
|
|
9529
9619
|
this.emitContextChanged(id);
|
|
9530
9620
|
this.emitScriptRun(id, cause, first);
|
|
9531
9621
|
},
|
|
@@ -9536,9 +9626,17 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
9536
9626
|
handle.emit("alert", { id: a.id, message: a.message, title: a.title, time: a.time });
|
|
9537
9627
|
},
|
|
9538
9628
|
onWarning: (w) => this.events.emit("warning", w),
|
|
9539
|
-
onError: (err) =>
|
|
9629
|
+
onError: (err) => {
|
|
9630
|
+
if (record.codeTrial) this.rejectCode(id, record, handle, err);
|
|
9631
|
+
else this.fail(id, handle, err);
|
|
9632
|
+
}
|
|
9540
9633
|
}
|
|
9541
9634
|
);
|
|
9635
|
+
if (record.prepared !== prepared) {
|
|
9636
|
+
session.stop();
|
|
9637
|
+
return;
|
|
9638
|
+
}
|
|
9639
|
+
record.session = session;
|
|
9542
9640
|
this.emitContextChanged(id);
|
|
9543
9641
|
}
|
|
9544
9642
|
/**
|
|
@@ -10039,7 +10137,7 @@ function yieldToPaint() {
|
|
|
10039
10137
|
}
|
|
10040
10138
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
10041
10139
|
}
|
|
10042
|
-
function valuesOnSchema(schema, previous) {
|
|
10140
|
+
function valuesOnSchema(schema, previous, oldSchema = []) {
|
|
10043
10141
|
const out = {};
|
|
10044
10142
|
const declared = /* @__PURE__ */ new Set();
|
|
10045
10143
|
for (const s of schema) {
|
|
@@ -10047,9 +10145,22 @@ function valuesOnSchema(schema, previous) {
|
|
|
10047
10145
|
declared.add(s.key);
|
|
10048
10146
|
declared.add(s.title);
|
|
10049
10147
|
}
|
|
10050
|
-
|
|
10148
|
+
const oldDefaults = /* @__PURE__ */ new Map();
|
|
10149
|
+
for (const s of oldSchema) {
|
|
10150
|
+
oldDefaults.set(s.key, s.defval);
|
|
10151
|
+
if (!oldDefaults.has(s.title)) oldDefaults.set(s.title, s.defval);
|
|
10152
|
+
}
|
|
10153
|
+
for (const [k, v] of Object.entries(previous)) {
|
|
10154
|
+
if (!declared.has(k)) continue;
|
|
10155
|
+
if (oldDefaults.has(k) && sameInputValue(oldDefaults.get(k), v)) continue;
|
|
10156
|
+
out[k] = v;
|
|
10157
|
+
}
|
|
10051
10158
|
return out;
|
|
10052
10159
|
}
|
|
10160
|
+
function sameInputValue(a, b) {
|
|
10161
|
+
if (a === b) return true;
|
|
10162
|
+
return typeof a === "object" && a !== null && typeof b === "object" && b !== null && JSON.stringify(a) === JSON.stringify(b);
|
|
10163
|
+
}
|
|
10053
10164
|
function blankedModel(model) {
|
|
10054
10165
|
return {
|
|
10055
10166
|
...model,
|
|
@@ -11693,6 +11804,66 @@ function iconEl(id, doc = document) {
|
|
|
11693
11804
|
if (svg) span.innerHTML = svg;
|
|
11694
11805
|
return span;
|
|
11695
11806
|
}
|
|
11807
|
+
|
|
11808
|
+
// src/ui/surface-events.ts
|
|
11809
|
+
var SURFACE_OPEN_EVENT = "vela:surface-open";
|
|
11810
|
+
var SURFACE_CLOSE_EVENT = "vela:surface-close";
|
|
11811
|
+
function announceSurface(el, open2, kind, trigger = null) {
|
|
11812
|
+
const View = el.ownerDocument.defaultView;
|
|
11813
|
+
if (!View) return;
|
|
11814
|
+
el.dispatchEvent(
|
|
11815
|
+
new View.CustomEvent(open2 ? SURFACE_OPEN_EVENT : SURFACE_CLOSE_EVENT, { bubbles: true, composed: true, detail: { kind, trigger } })
|
|
11816
|
+
);
|
|
11817
|
+
}
|
|
11818
|
+
|
|
11819
|
+
// src/ui/surface-exit.ts
|
|
11820
|
+
var CLOSING_ATTR = "data-closing";
|
|
11821
|
+
var SURFACE_EXIT_CAP_MS = 1e3;
|
|
11822
|
+
function animationsOf(els) {
|
|
11823
|
+
return els.flatMap((el) => el.getAnimations({ subtree: true }));
|
|
11824
|
+
}
|
|
11825
|
+
function ends(a) {
|
|
11826
|
+
return Number.isFinite(a.effect?.getComputedTiming().endTime ?? Number.POSITIVE_INFINITY);
|
|
11827
|
+
}
|
|
11828
|
+
function holdForExit(els, done, opts = {}) {
|
|
11829
|
+
const list = Array.isArray(els) ? els : [els];
|
|
11830
|
+
if (list.length === 0 || !list.every((el) => typeof el.getAnimations === "function")) {
|
|
11831
|
+
done();
|
|
11832
|
+
return null;
|
|
11833
|
+
}
|
|
11834
|
+
const before = new Set(animationsOf(list));
|
|
11835
|
+
for (const el of list) el.setAttribute(CLOSING_ATTR, "");
|
|
11836
|
+
const exit = animationsOf(list).filter((a) => !before.has(a) && ends(a));
|
|
11837
|
+
if (exit.length === 0) {
|
|
11838
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
11839
|
+
done();
|
|
11840
|
+
return null;
|
|
11841
|
+
}
|
|
11842
|
+
const frozen = [...list, ...opts.inert ?? []];
|
|
11843
|
+
const wasInert = frozen.map((el) => el.inert);
|
|
11844
|
+
for (const el of frozen) el.inert = true;
|
|
11845
|
+
let settled = false;
|
|
11846
|
+
const settle = (complete) => {
|
|
11847
|
+
if (settled) return;
|
|
11848
|
+
settled = true;
|
|
11849
|
+
clearTimeout(cap);
|
|
11850
|
+
for (const el of list) el.removeAttribute(CLOSING_ATTR);
|
|
11851
|
+
frozen.forEach((el, i) => {
|
|
11852
|
+
el.inert = wasInert[i];
|
|
11853
|
+
});
|
|
11854
|
+
if (complete) done();
|
|
11855
|
+
};
|
|
11856
|
+
const cap = setTimeout(() => settle(true), SURFACE_EXIT_CAP_MS);
|
|
11857
|
+
let pending = exit.length;
|
|
11858
|
+
const one = () => {
|
|
11859
|
+
if (--pending === 0) settle(true);
|
|
11860
|
+
};
|
|
11861
|
+
for (const a of exit) a.finished.then(one, one);
|
|
11862
|
+
return {
|
|
11863
|
+
cancel: () => settle(false),
|
|
11864
|
+
finish: () => settle(true)
|
|
11865
|
+
};
|
|
11866
|
+
}
|
|
11696
11867
|
function menuController(opts) {
|
|
11697
11868
|
return {
|
|
11698
11869
|
machine: menu__namespace.machine,
|
|
@@ -11774,6 +11945,35 @@ var MENU_CSS = `
|
|
|
11774
11945
|
color: var(--vela-fg-bright);
|
|
11775
11946
|
}
|
|
11776
11947
|
.vela-menu-item[data-checkmark] { color: var(--vela-fg-bright); }
|
|
11948
|
+
/* Badge mode (iconBadges): the row icon sits in a rounded tile and the row grows to fit
|
|
11949
|
+
it. An icon-less row of the same level keeps an invisible slot, so labels align. */
|
|
11950
|
+
.vela-menu[data-badges] > .vela-menu-item { padding: 5px 10px 5px 6px; }
|
|
11951
|
+
.vela-menu-item .vela-menu-badge {
|
|
11952
|
+
width: 24px;
|
|
11953
|
+
height: 24px;
|
|
11954
|
+
padding: 3px;
|
|
11955
|
+
flex: none;
|
|
11956
|
+
box-sizing: border-box;
|
|
11957
|
+
display: inline-flex;
|
|
11958
|
+
align-items: center;
|
|
11959
|
+
justify-content: center;
|
|
11960
|
+
border-radius: var(--vela-radius-md);
|
|
11961
|
+
background: var(--vela-hover);
|
|
11962
|
+
border: 1px solid var(--vela-border);
|
|
11963
|
+
color: var(--vela-fg-muted);
|
|
11964
|
+
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);
|
|
11965
|
+
}
|
|
11966
|
+
.vela-menu-item .vela-menu-badge:empty { background: transparent; border-color: transparent; }
|
|
11967
|
+
.vela-menu-item .vela-menu-badge .vela-icon { color: inherit; }
|
|
11968
|
+
.vela-menu-item[data-highlighted] .vela-menu-badge:not(:empty) { color: var(--vela-fg-bright); }
|
|
11969
|
+
/* The selected tile inverts: a bright fill under the surface-colored glyph. After the
|
|
11970
|
+
hover rule, so a selected row keeps its tile while highlighted. */
|
|
11971
|
+
.vela-menu-item[data-checked] .vela-menu-badge:not(:empty),
|
|
11972
|
+
.vela-menu-item[data-checkmark] .vela-menu-badge:not(:empty) {
|
|
11973
|
+
background: color-mix(in srgb, var(--vela-fg-bright) 83%, var(--vela-surface));
|
|
11974
|
+
border-color: color-mix(in srgb, var(--vela-fg-bright) 40%, transparent);
|
|
11975
|
+
color: var(--vela-surface);
|
|
11976
|
+
}
|
|
11777
11977
|
/* Switch rows (boolean settings in a dropdown): a right-aligned toggle pill \u2014 the
|
|
11778
11978
|
same control language as the settings dialog's toggles. */
|
|
11779
11979
|
.vela-menu-switch {
|
|
@@ -11838,11 +12038,16 @@ var Surface = class _Surface {
|
|
|
11838
12038
|
/** Trigger rect captured when this level opened — the list stays put if the
|
|
11839
12039
|
* trigger then moves (favorite chips shifting the caret). */
|
|
11840
12040
|
this.pinnedAnchor = null;
|
|
12041
|
+
/** The open state last announced — see {@link announce}. */
|
|
12042
|
+
this.shown = false;
|
|
12043
|
+
/** The list's exit animation after a close; a reopen cancels it. */
|
|
12044
|
+
this.exit = null;
|
|
11841
12045
|
this.doc = doc;
|
|
11842
12046
|
this.host = opts.host;
|
|
11843
12047
|
this.onSelect = opts.onSelect;
|
|
11844
12048
|
this.onFavorite = opts.onFavorite;
|
|
11845
12049
|
this.checkmarks = opts.checkmarks === true;
|
|
12050
|
+
this.iconBadges = opts.iconBadges === true;
|
|
11846
12051
|
this.positioner = doc.createElement("div");
|
|
11847
12052
|
this.positioner.className = "vela-ui-layer";
|
|
11848
12053
|
this.list = doc.createElement("ul");
|
|
@@ -11851,6 +12056,7 @@ var Surface = class _Surface {
|
|
|
11851
12056
|
this.positioner.appendChild(this.list);
|
|
11852
12057
|
this.host.appendChild(this.positioner);
|
|
11853
12058
|
const trigger = opts.trigger;
|
|
12059
|
+
this.opener = trigger ?? opts.opener ?? null;
|
|
11854
12060
|
this.ctrl = menuController({
|
|
11855
12061
|
items: [],
|
|
11856
12062
|
id: opts.id,
|
|
@@ -11872,15 +12078,37 @@ var Surface = class _Surface {
|
|
|
11872
12078
|
if (opts.triggerId && trigger) trigger.id = opts.triggerId;
|
|
11873
12079
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
11874
12080
|
const api = this.ctrl.connect(service);
|
|
12081
|
+
const closing = this.shown && !api.open;
|
|
12082
|
+
if (closing) this.announce(false);
|
|
12083
|
+
if (api.open && this.exit) {
|
|
12084
|
+
this.exit.cancel();
|
|
12085
|
+
this.exit = null;
|
|
12086
|
+
}
|
|
11875
12087
|
if (trigger) vanilla.spreadProps(trigger, api.getTriggerProps(), this.mid);
|
|
11876
12088
|
vanilla.spreadProps(this.positioner, api.getPositionerProps(), this.mid);
|
|
11877
12089
|
vanilla.spreadProps(this.list, api.getContentProps(), this.mid);
|
|
11878
12090
|
this.project(api);
|
|
12091
|
+
if (closing) this.holdExit();
|
|
12092
|
+
if (api.open && !this.shown) this.announce(true);
|
|
12093
|
+
});
|
|
12094
|
+
}
|
|
12095
|
+
/** Keep the just-closed list up through its exit animation. `spreadProps` re-applies
|
|
12096
|
+
* `hidden` only when the machine's value for it changes, so the list stays shown until
|
|
12097
|
+
* the exit ends — or until a reopen, which projects `hidden: false` itself. */
|
|
12098
|
+
holdExit() {
|
|
12099
|
+
this.list.hidden = false;
|
|
12100
|
+
this.exit = holdForExit(this.list, () => {
|
|
12101
|
+
this.exit = null;
|
|
12102
|
+
if (!this.shown) this.list.hidden = true;
|
|
11879
12103
|
});
|
|
11880
12104
|
}
|
|
11881
12105
|
get api() {
|
|
11882
12106
|
return this.ctrl.connect(this.handle.service);
|
|
11883
12107
|
}
|
|
12108
|
+
announce(open2) {
|
|
12109
|
+
this.shown = open2;
|
|
12110
|
+
announceSurface(this.list, open2, "menu", this.opener);
|
|
12111
|
+
}
|
|
11884
12112
|
setItems(items) {
|
|
11885
12113
|
this.items = items;
|
|
11886
12114
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
@@ -11897,7 +12125,9 @@ var Surface = class _Surface {
|
|
|
11897
12125
|
destroy() {
|
|
11898
12126
|
for (const sub2 of this.subs.values()) sub2.destroy();
|
|
11899
12127
|
this.subs.clear();
|
|
12128
|
+
this.exit?.finish();
|
|
11900
12129
|
this.handle.stop();
|
|
12130
|
+
if (this.shown) this.announce(false);
|
|
11901
12131
|
this.positioner.remove();
|
|
11902
12132
|
}
|
|
11903
12133
|
byId(id) {
|
|
@@ -11922,6 +12152,10 @@ var Surface = class _Surface {
|
|
|
11922
12152
|
const doc = this.doc;
|
|
11923
12153
|
this.list.replaceChildren();
|
|
11924
12154
|
const markable = this.checkmarks && this.items.some((i) => !(i.submenu && i.submenu.length > 0) && !i.toggle && i.checked !== void 0);
|
|
12155
|
+
const iconic = this.items.some((i) => !!i.icon);
|
|
12156
|
+
const badged = this.iconBadges && iconic;
|
|
12157
|
+
if (badged) this.list.dataset.badges = "1";
|
|
12158
|
+
else delete this.list.dataset.badges;
|
|
11925
12159
|
for (const item of this.items) {
|
|
11926
12160
|
if (item.separatorBefore) {
|
|
11927
12161
|
const sep = doc.createElement("li");
|
|
@@ -11947,7 +12181,16 @@ var Surface = class _Surface {
|
|
|
11947
12181
|
} else if (!branch && !item.toggle && item.checked) {
|
|
11948
12182
|
li.dataset.checked = "1";
|
|
11949
12183
|
}
|
|
11950
|
-
if (
|
|
12184
|
+
if (badged) {
|
|
12185
|
+
const badge = doc.createElement("span");
|
|
12186
|
+
badge.className = "vela-menu-badge";
|
|
12187
|
+
if (item.icon) badge.appendChild(iconEl(item.icon, doc));
|
|
12188
|
+
li.appendChild(badge);
|
|
12189
|
+
} else if (item.icon) {
|
|
12190
|
+
li.appendChild(iconEl(item.icon, doc));
|
|
12191
|
+
} else if (iconic) {
|
|
12192
|
+
li.appendChild(iconEl("", doc));
|
|
12193
|
+
}
|
|
11951
12194
|
const label = doc.createElement("span");
|
|
11952
12195
|
label.className = "vela-menu-label";
|
|
11953
12196
|
label.textContent = item.label;
|
|
@@ -11991,9 +12234,11 @@ var Surface = class _Surface {
|
|
|
11991
12234
|
const sub2 = new _Surface(doc, {
|
|
11992
12235
|
host: this.host,
|
|
11993
12236
|
placement: "right-start",
|
|
12237
|
+
opener: li,
|
|
11994
12238
|
onSelect: this.onSelect,
|
|
11995
12239
|
onFavorite: this.onFavorite,
|
|
11996
12240
|
checkmarks: this.checkmarks,
|
|
12241
|
+
iconBadges: this.iconBadges,
|
|
11997
12242
|
id: `${this.mid}--${item.id}`
|
|
11998
12243
|
});
|
|
11999
12244
|
sub2.setItems(item.submenu ?? []);
|
|
@@ -12019,7 +12264,8 @@ var Menu = class {
|
|
|
12019
12264
|
id: opts.id,
|
|
12020
12265
|
minWidth: opts.minWidth,
|
|
12021
12266
|
onFavorite: opts.onFavorite,
|
|
12022
|
-
checkmarks: opts.checkmarks
|
|
12267
|
+
checkmarks: opts.checkmarks,
|
|
12268
|
+
iconBadges: opts.iconBadges
|
|
12023
12269
|
});
|
|
12024
12270
|
this.root.setItems(opts.items);
|
|
12025
12271
|
}
|
|
@@ -12123,6 +12369,7 @@ var POPOVER_CSS = `
|
|
|
12123
12369
|
|
|
12124
12370
|
// src/ui/components/popover/view.ts
|
|
12125
12371
|
var open = null;
|
|
12372
|
+
var leaving = /* @__PURE__ */ new Set();
|
|
12126
12373
|
function closeOpenPopovers() {
|
|
12127
12374
|
open?.hide();
|
|
12128
12375
|
}
|
|
@@ -12150,6 +12397,8 @@ var Popover = class {
|
|
|
12150
12397
|
this.shown = false;
|
|
12151
12398
|
/** The pending removal of a fading-out shell; a show() that reuses the shell cancels it. */
|
|
12152
12399
|
this.leaveTimer = null;
|
|
12400
|
+
/** The shell's exit animation before it leaves the DOM; a show() that reuses the shell cancels it. */
|
|
12401
|
+
this.exit = null;
|
|
12153
12402
|
const doc = opts.trigger.ownerDocument;
|
|
12154
12403
|
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
12155
12404
|
this.trigger = opts.trigger;
|
|
@@ -12186,6 +12435,10 @@ var Popover = class {
|
|
|
12186
12435
|
clearTimeout(this.leaveTimer);
|
|
12187
12436
|
this.leaveTimer = null;
|
|
12188
12437
|
}
|
|
12438
|
+
this.exit?.cancel();
|
|
12439
|
+
this.exit = null;
|
|
12440
|
+
leaving.delete(this);
|
|
12441
|
+
for (const p of [...leaving]) if (p.trigger === this.trigger) p.exit?.finish();
|
|
12189
12442
|
ensureUIHost(this.el, this.inheritsTokens ? void 0 : this.theme);
|
|
12190
12443
|
if (this.fadeMs > 0) {
|
|
12191
12444
|
this.el.style.transition = `opacity ${this.fadeMs}ms ease`;
|
|
@@ -12210,16 +12463,21 @@ var Popover = class {
|
|
|
12210
12463
|
this.hide();
|
|
12211
12464
|
};
|
|
12212
12465
|
const onReflow = () => this.place();
|
|
12213
|
-
setTimeout(() =>
|
|
12466
|
+
setTimeout(() => {
|
|
12467
|
+
if (this.onOutside === onOutside) document.addEventListener("pointerdown", onOutside, true);
|
|
12468
|
+
}, 0);
|
|
12214
12469
|
document.addEventListener("keydown", onKey, true);
|
|
12215
12470
|
window.addEventListener("resize", onReflow, true);
|
|
12216
12471
|
document.addEventListener("scroll", onReflow, true);
|
|
12217
12472
|
this.onOutside = onOutside;
|
|
12218
12473
|
this.onKey = onKey;
|
|
12219
12474
|
this.onReflow = onReflow;
|
|
12475
|
+
announceSurface(this.el, true, "popover", this.trigger);
|
|
12220
12476
|
}
|
|
12221
12477
|
hide() {
|
|
12222
12478
|
if (!this.shown) return;
|
|
12479
|
+
this.shown = false;
|
|
12480
|
+
announceSurface(this.el, false, "popover", this.trigger);
|
|
12223
12481
|
if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
|
|
12224
12482
|
if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
|
|
12225
12483
|
if (this.onReflow) {
|
|
@@ -12237,9 +12495,13 @@ var Popover = class {
|
|
|
12237
12495
|
this.el.remove();
|
|
12238
12496
|
}, this.fadeMs);
|
|
12239
12497
|
} else {
|
|
12240
|
-
this.el
|
|
12498
|
+
this.exit = holdForExit(this.el, () => {
|
|
12499
|
+
this.exit = null;
|
|
12500
|
+
leaving.delete(this);
|
|
12501
|
+
this.el.remove();
|
|
12502
|
+
});
|
|
12503
|
+
if (this.exit) leaving.add(this);
|
|
12241
12504
|
}
|
|
12242
|
-
this.shown = false;
|
|
12243
12505
|
if (open === this) open = null;
|
|
12244
12506
|
this.ctrl.onClose?.();
|
|
12245
12507
|
}
|
|
@@ -12250,6 +12512,7 @@ var Popover = class {
|
|
|
12250
12512
|
}
|
|
12251
12513
|
destroy() {
|
|
12252
12514
|
this.hide();
|
|
12515
|
+
this.exit?.finish();
|
|
12253
12516
|
}
|
|
12254
12517
|
reposition() {
|
|
12255
12518
|
if (this.shown) this.place();
|
|
@@ -12940,6 +13203,11 @@ ${overlayScrollbarCss(".vela-dialog *")}
|
|
|
12940
13203
|
`;
|
|
12941
13204
|
var Dialog = class {
|
|
12942
13205
|
constructor(opts = {}) {
|
|
13206
|
+
this.opener = null;
|
|
13207
|
+
this.wasOpen = false;
|
|
13208
|
+
/** The panel and scrim's exit animation after a close; a reopen cancels it. */
|
|
13209
|
+
this.exit = null;
|
|
13210
|
+
this.destroyed = false;
|
|
12943
13211
|
const doc = (opts.host ?? document.body).ownerDocument;
|
|
12944
13212
|
injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
|
|
12945
13213
|
const host = opts.host ?? doc.body;
|
|
@@ -13036,19 +13304,73 @@ var Dialog = class {
|
|
|
13036
13304
|
const mid = String(this.ctrl.props.id);
|
|
13037
13305
|
this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
|
|
13038
13306
|
const api = this.ctrl.connect(service);
|
|
13307
|
+
const closing = this.wasOpen && !api.open;
|
|
13308
|
+
if (closing) this.announce(false);
|
|
13039
13309
|
vanilla.spreadProps(this.backdrop, api.getBackdropProps(), mid);
|
|
13040
13310
|
vanilla.spreadProps(this.positioner, api.getPositionerProps(), mid);
|
|
13041
13311
|
vanilla.spreadProps(this.panel, api.getContentProps(), mid);
|
|
13042
13312
|
vanilla.spreadProps(title, api.getTitleProps(), mid);
|
|
13043
13313
|
vanilla.spreadProps(close, api.getCloseTriggerProps(), mid);
|
|
13044
|
-
|
|
13045
|
-
|
|
13314
|
+
if (api.open) {
|
|
13315
|
+
this.exit?.cancel();
|
|
13316
|
+
this.exit = null;
|
|
13317
|
+
this.setShown(true);
|
|
13318
|
+
} else if (closing) {
|
|
13319
|
+
this.holdExit();
|
|
13320
|
+
} else if (!this.exit) {
|
|
13321
|
+
this.setShown(false);
|
|
13322
|
+
}
|
|
13323
|
+
if (api.open && !this.wasOpen) this.announce(true);
|
|
13324
|
+
if (this.wasOpen && !api.open) this.restoreOpener();
|
|
13325
|
+
this.wasOpen = api.open;
|
|
13046
13326
|
});
|
|
13047
13327
|
}
|
|
13328
|
+
setShown(shown) {
|
|
13329
|
+
this.backdrop.style.display = shown ? "" : "none";
|
|
13330
|
+
this.positioner.style.display = shown ? "" : "none";
|
|
13331
|
+
}
|
|
13332
|
+
/** Keep the just-closed dialog up through its exit animation, then hide it — or
|
|
13333
|
+
* remove it, when it was destroyed meanwhile. `spreadProps` re-applies the close
|
|
13334
|
+
* props' `hidden` only when its value changes, so lifting it keeps the panel and
|
|
13335
|
+
* scrim up until the exit ends. The positioner spans the viewport: inert, it lets
|
|
13336
|
+
* clicks through to the page while the panel animates out. */
|
|
13337
|
+
holdExit() {
|
|
13338
|
+
const held = [this.panel, this.backdrop];
|
|
13339
|
+
for (const el of held) el.hidden = false;
|
|
13340
|
+
this.exit = holdForExit(
|
|
13341
|
+
held,
|
|
13342
|
+
() => {
|
|
13343
|
+
this.exit = null;
|
|
13344
|
+
if (this.destroyed) {
|
|
13345
|
+
this.detach();
|
|
13346
|
+
} else if (!this.open) {
|
|
13347
|
+
for (const el of held) el.hidden = true;
|
|
13348
|
+
this.setShown(false);
|
|
13349
|
+
}
|
|
13350
|
+
},
|
|
13351
|
+
{ inert: [this.positioner] }
|
|
13352
|
+
);
|
|
13353
|
+
}
|
|
13354
|
+
detach() {
|
|
13355
|
+
this.backdrop.remove();
|
|
13356
|
+
this.positioner.remove();
|
|
13357
|
+
}
|
|
13358
|
+
/** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
|
|
13359
|
+
* teardown that outruns the machine's close notification). */
|
|
13360
|
+
restoreOpener() {
|
|
13361
|
+
const opener = this.opener;
|
|
13362
|
+
this.opener = null;
|
|
13363
|
+
if (opener?.isConnected) opener.focus({ preventScroll: true });
|
|
13364
|
+
}
|
|
13365
|
+
announce(open2) {
|
|
13366
|
+
const opener = this.opener === this.panel.ownerDocument.body ? null : this.opener;
|
|
13367
|
+
announceSurface(this.panel, open2, "dialog", opener);
|
|
13368
|
+
}
|
|
13048
13369
|
get open() {
|
|
13049
13370
|
return this.ctrl.connect(this.handle.service).open;
|
|
13050
13371
|
}
|
|
13051
13372
|
show() {
|
|
13373
|
+
this.opener = this.panel.ownerDocument.activeElement;
|
|
13052
13374
|
this.ctrl.connect(this.handle.service).setOpen(true);
|
|
13053
13375
|
}
|
|
13054
13376
|
hide() {
|
|
@@ -13061,10 +13383,16 @@ var Dialog = class {
|
|
|
13061
13383
|
return el?.closest(".vela-popover, .vela-menu") != null;
|
|
13062
13384
|
}
|
|
13063
13385
|
destroy() {
|
|
13386
|
+
if (this.wasOpen && !this.open) this.handle.flush();
|
|
13064
13387
|
if (this.open) this.hide();
|
|
13065
13388
|
this.handle.stop();
|
|
13066
|
-
this.
|
|
13067
|
-
this.
|
|
13389
|
+
this.destroyed = true;
|
|
13390
|
+
if (this.wasOpen) {
|
|
13391
|
+
this.wasOpen = false;
|
|
13392
|
+
this.announce(false);
|
|
13393
|
+
}
|
|
13394
|
+
this.restoreOpener();
|
|
13395
|
+
if (!this.exit) this.detach();
|
|
13068
13396
|
}
|
|
13069
13397
|
};
|
|
13070
13398
|
|
|
@@ -15516,7 +15844,10 @@ var LEGEND_ICON_PX2 = 16;
|
|
|
15516
15844
|
var LEGEND_CTL_PX = 18;
|
|
15517
15845
|
var LEGEND_ROW_PAD_Y = 2;
|
|
15518
15846
|
var LEGEND_ROW_PAD_X = 6;
|
|
15847
|
+
var LEGEND_LEFT_PX = 10;
|
|
15848
|
+
var LEGEND_SCALE_GAP_PX = 6;
|
|
15519
15849
|
var LEGEND_TITLE_VALUES_GAP_PX = 6;
|
|
15850
|
+
var LEGEND_VALUES_GAP_PX = 5;
|
|
15520
15851
|
var LEGEND_TITLE_STATUS_GAP_PX = 8;
|
|
15521
15852
|
var LEGEND_ACTION_GAP_PX = 6;
|
|
15522
15853
|
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;`;
|
|
@@ -15802,14 +16133,15 @@ var InputsUI = class {
|
|
|
15802
16133
|
row.valuesEl.replaceChildren();
|
|
15803
16134
|
return;
|
|
15804
16135
|
}
|
|
15805
|
-
row.valuesEl.style.display = "
|
|
16136
|
+
row.valuesEl.style.display = "block";
|
|
15806
16137
|
row.valuesEl.replaceChildren();
|
|
15807
|
-
|
|
16138
|
+
row.plotValues.forEach((v, i) => {
|
|
15808
16139
|
const span = document.createElement("span");
|
|
15809
16140
|
span.textContent = v.value;
|
|
15810
16141
|
span.style.color = v.color;
|
|
16142
|
+
if (i > 0) span.style.marginLeft = `${LEGEND_VALUES_GAP_PX}px`;
|
|
15811
16143
|
row.valuesEl.appendChild(span);
|
|
15812
|
-
}
|
|
16144
|
+
});
|
|
15813
16145
|
}
|
|
15814
16146
|
// ── legend move/merge (menu + drag) ─────────────────────────────────────
|
|
15815
16147
|
/** Open the "Move to" menu for a row, anchored under its move button. */
|
|
@@ -15910,7 +16242,7 @@ var InputsUI = class {
|
|
|
15910
16242
|
if (!lg) {
|
|
15911
16243
|
lg = document.createElement("div");
|
|
15912
16244
|
lg.dataset.velaPane = paneId;
|
|
15913
|
-
lg.style.cssText =
|
|
16245
|
+
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;`;
|
|
15914
16246
|
applyChromeTokens(lg, this.theme);
|
|
15915
16247
|
this.positionLegend(lg, paneId);
|
|
15916
16248
|
this.container.appendChild(lg);
|
|
@@ -15922,6 +16254,7 @@ var InputsUI = class {
|
|
|
15922
16254
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
15923
16255
|
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
15924
16256
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
16257
|
+
lg.style.maxWidth = `calc(100% - ${(bounds.rightAxis ?? 0) + LEGEND_LEFT_PX + LEGEND_SCALE_GAP_PX}px)`;
|
|
15925
16258
|
const collapsed = this.paneCollapse.has(paneId);
|
|
15926
16259
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
15927
16260
|
for (const row of this.rows.values()) {
|
|
@@ -16013,7 +16346,7 @@ var InputsUI = class {
|
|
|
16013
16346
|
}
|
|
16014
16347
|
ensureDialogStyles();
|
|
16015
16348
|
const el = document.createElement("div");
|
|
16016
|
-
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;`;
|
|
16349
|
+
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;`;
|
|
16017
16350
|
el.addEventListener("mouseleave", () => {
|
|
16018
16351
|
if (this.selectedId !== id) this.setRowHighlighted(id, false);
|
|
16019
16352
|
});
|
|
@@ -16041,7 +16374,7 @@ var InputsUI = class {
|
|
|
16041
16374
|
const statusEl = document.createElement("span");
|
|
16042
16375
|
statusEl.style.cssText = "display:none;box-sizing:border-box;flex:none;";
|
|
16043
16376
|
const titleWrap = document.createElement("span");
|
|
16044
|
-
titleWrap.style.cssText = "white-space:nowrap;";
|
|
16377
|
+
titleWrap.style.cssText = "white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;";
|
|
16045
16378
|
titleWrap.addEventListener("mouseenter", () => this.setRowHighlighted(id, true));
|
|
16046
16379
|
const titleEl = document.createElement("span");
|
|
16047
16380
|
titleEl.textContent = title;
|
|
@@ -16059,7 +16392,7 @@ var InputsUI = class {
|
|
|
16059
16392
|
el.appendChild(calloutsEl);
|
|
16060
16393
|
el.appendChild(statusEl);
|
|
16061
16394
|
const valuesEl = document.createElement("span");
|
|
16062
|
-
valuesEl.style.cssText = `display:none;
|
|
16395
|
+
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;`;
|
|
16063
16396
|
el.appendChild(valuesEl);
|
|
16064
16397
|
const controlsEl = document.createElement("span");
|
|
16065
16398
|
controlsEl.style.cssText = `display:none;align-items:center;gap:${LEGEND_ACTION_GAP_PX}px;flex:none;margin-left:${LEGEND_ACTION_GAP_PX}px;`;
|
|
@@ -17232,15 +17565,23 @@ var Batch = class {
|
|
|
17232
17565
|
this.seg(x + w, y + h, x, y + h, lineWidth, c);
|
|
17233
17566
|
this.seg(x, y + h, x, y, lineWidth, c);
|
|
17234
17567
|
}
|
|
17235
|
-
/**
|
|
17568
|
+
/**
|
|
17569
|
+
* Filled circle (triangle fan), feathered like a line: the rim is padded by {@link AA_PAD} and
|
|
17570
|
+
* edgeDist runs from 0 at the center to the padded radius at the rim, with edgeHalf = r. The
|
|
17571
|
+
* distance grows linearly along each spoke, so the shader's line feather gives a round soft edge.
|
|
17572
|
+
*/
|
|
17236
17573
|
circle(cx, cy, r, c, segments = 16) {
|
|
17237
|
-
|
|
17574
|
+
const ext = r + AA_PAD;
|
|
17575
|
+
let px = cx + ext;
|
|
17238
17576
|
let py = cy;
|
|
17577
|
+
this.ensure(segments * 3);
|
|
17239
17578
|
for (let i = 1; i <= segments; i += 1) {
|
|
17240
17579
|
const a = i / segments * Math.PI * 2;
|
|
17241
|
-
const nx = cx + Math.cos(a) *
|
|
17242
|
-
const ny = cy + Math.sin(a) *
|
|
17243
|
-
this.
|
|
17580
|
+
const nx = cx + Math.cos(a) * ext;
|
|
17581
|
+
const ny = cy + Math.sin(a) * ext;
|
|
17582
|
+
this.v(cx, cy, c, 0, r);
|
|
17583
|
+
this.v(px, py, c, ext, r);
|
|
17584
|
+
this.v(nx, ny, c, ext, r);
|
|
17244
17585
|
px = nx;
|
|
17245
17586
|
py = ny;
|
|
17246
17587
|
}
|
|
@@ -17421,7 +17762,7 @@ var WebGL2Backend = class {
|
|
|
17421
17762
|
}
|
|
17422
17763
|
mount(canvas) {
|
|
17423
17764
|
this.canvas = canvas;
|
|
17424
|
-
this.gl = canvas.getContext("webgl2", { alpha: true, antialias:
|
|
17765
|
+
this.gl = canvas.getContext("webgl2", { alpha: true, antialias: false, depth: false, stencil: false, premultipliedAlpha: true });
|
|
17425
17766
|
if (!this.gl) return;
|
|
17426
17767
|
if (!this.initGL()) {
|
|
17427
17768
|
this.gl = null;
|
|
@@ -18646,6 +18987,11 @@ var InputController = class {
|
|
|
18646
18987
|
/** When true (the default), the separators between stacked panes are draggable to
|
|
18647
18988
|
* resize the panes above/below. When false, a press there is a normal data-area pan. */
|
|
18648
18989
|
this.paneResize = true;
|
|
18990
|
+
/** When true (the default), the user drawings take part in pointer input: they claim a
|
|
18991
|
+
* press over a drawing or with a tool armed, and see hover moves. When false, every press
|
|
18992
|
+
* is a plain pan/click and hover leaves them alone; a drawing gesture already under way
|
|
18993
|
+
* still finishes. */
|
|
18994
|
+
this.drawings = true;
|
|
18649
18995
|
this.el = null;
|
|
18650
18996
|
this.dragging = false;
|
|
18651
18997
|
this.moved = false;
|
|
@@ -18709,18 +19055,18 @@ var InputController = class {
|
|
|
18709
19055
|
this.onModifier = (e) => {
|
|
18710
19056
|
if (e.repeat || e.key !== "Shift" && e.key !== "Control" && e.key !== "Meta") return;
|
|
18711
19057
|
if (Number.isNaN(this.cursorX)) return;
|
|
18712
|
-
if (this.dragging
|
|
19058
|
+
if (this.dragging ? this.region !== "drawing" : !this.drawings) return;
|
|
18713
19059
|
this.deps.drawingsPointerMove?.(this.cursorX, this.cursorY, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
18714
19060
|
};
|
|
18715
19061
|
this.onDown = (e) => {
|
|
18716
19062
|
if (e.button === 1) {
|
|
18717
19063
|
const { x: x2, y: y2 } = this.local(e);
|
|
18718
|
-
this.middleDeleted = this.deps.drawingsDeleteAt?.(x2, y2) ?? false;
|
|
19064
|
+
this.middleDeleted = this.drawings && (this.deps.drawingsDeleteAt?.(x2, y2) ?? false);
|
|
18719
19065
|
if (this.middleDeleted) e.preventDefault();
|
|
18720
19066
|
return;
|
|
18721
19067
|
}
|
|
18722
19068
|
if (e.button === 2) {
|
|
18723
|
-
this.rightCancelled = this.deps.drawingsCancelPlacement?.() ?? false;
|
|
19069
|
+
this.rightCancelled = this.drawings && (this.deps.drawingsCancelPlacement?.() ?? false);
|
|
18724
19070
|
if (this.rightCancelled) e.preventDefault();
|
|
18725
19071
|
return;
|
|
18726
19072
|
}
|
|
@@ -18742,20 +19088,20 @@ var InputController = class {
|
|
|
18742
19088
|
this.startY = y;
|
|
18743
19089
|
this.prevPressDrawing = this.lastPressDrawing;
|
|
18744
19090
|
this.lastPressDrawing = false;
|
|
18745
|
-
if (this.deps.drawingsClaim?.(x, y)) {
|
|
19091
|
+
if (this.drawings && this.deps.drawingsClaim?.(x, y)) {
|
|
18746
19092
|
this.region = "drawing";
|
|
18747
19093
|
this.lastPressDrawing = true;
|
|
18748
19094
|
this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
18749
19095
|
this.capture(e.pointerId);
|
|
18750
19096
|
return;
|
|
18751
19097
|
}
|
|
18752
|
-
if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
19098
|
+
if (this.drawings && e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
18753
19099
|
this.region = "drawing";
|
|
18754
19100
|
this.lastPressDrawing = true;
|
|
18755
19101
|
this.capture(e.pointerId);
|
|
18756
19102
|
return;
|
|
18757
19103
|
}
|
|
18758
|
-
if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
19104
|
+
if (this.drawings && (e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
18759
19105
|
this.region = "drawing";
|
|
18760
19106
|
this.lastPressDrawing = true;
|
|
18761
19107
|
this.capture(e.pointerId);
|
|
@@ -18832,7 +19178,7 @@ var InputController = class {
|
|
|
18832
19178
|
const drawCursor = this.deps.drawingsCursor?.(x, y);
|
|
18833
19179
|
const r = this.regionAt(x, y);
|
|
18834
19180
|
this.el.style.cursor = drawCursor ?? (r === "price" ? "ns-resize" : r === "time" ? "ew-resize" : r === "separator" ? "row-resize" : "");
|
|
18835
|
-
this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
19181
|
+
if (this.drawings) this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
18836
19182
|
}
|
|
18837
19183
|
if (e.pointerType !== "touch") this.deps.onPointerMove(x, y);
|
|
18838
19184
|
};
|
|
@@ -18873,7 +19219,7 @@ var InputController = class {
|
|
|
18873
19219
|
this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
|
|
18874
19220
|
} else if (tapRelease && this.region === "data") {
|
|
18875
19221
|
this.deps.onClick(x, y);
|
|
18876
|
-
} else if (this.dragging && this.region === "data") {
|
|
19222
|
+
} else if (this.dragging && this.region === "data" && wasTouch) {
|
|
18877
19223
|
const stale = e.timeStamp - this.lastT > FLING_STALE_MS;
|
|
18878
19224
|
if (!stale && Math.abs(this.vx) > FLING_MIN_SPEED) {
|
|
18879
19225
|
const pitch = this.deps.getCoords().pxPerBar();
|
|
@@ -19074,7 +19420,7 @@ var InputController = class {
|
|
|
19074
19420
|
}
|
|
19075
19421
|
/** Shared double-click / double-tap routing, by the region under the point. */
|
|
19076
19422
|
doubleActivate(x, y) {
|
|
19077
|
-
if (this.deps.drawingsDblClick?.(x, y)) return;
|
|
19423
|
+
if (this.drawings && this.deps.drawingsDblClick?.(x, y)) return;
|
|
19078
19424
|
const region = this.regionAt(x, y);
|
|
19079
19425
|
if (region === "price") this.deps.resetPriceScale(x, y);
|
|
19080
19426
|
else if (region === "separator") this.deps.resetPaneSize(y);
|
|
@@ -21088,10 +21434,13 @@ function priceTicks(min, max, target = 6) {
|
|
|
21088
21434
|
const norm = raw / mag;
|
|
21089
21435
|
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
21090
21436
|
const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);
|
|
21091
|
-
const out = [];
|
|
21092
21437
|
const start = Math.ceil(min / step) * step;
|
|
21093
|
-
|
|
21094
|
-
|
|
21438
|
+
if (!(step > 0) || start + step === start) return [];
|
|
21439
|
+
const count = Math.floor((max - start) / step + 1e-6) + 1;
|
|
21440
|
+
if (!(count > 0) || count > 1e3) return [];
|
|
21441
|
+
const out = [];
|
|
21442
|
+
for (let i = 0; i < count; i += 1) {
|
|
21443
|
+
out.push(Number((start + i * step).toFixed(decimals)));
|
|
21095
21444
|
}
|
|
21096
21445
|
return out;
|
|
21097
21446
|
}
|
|
@@ -21289,6 +21638,100 @@ function calendarTicks(zFrom, zTo, months, offsetMs) {
|
|
|
21289
21638
|
return out;
|
|
21290
21639
|
}
|
|
21291
21640
|
|
|
21641
|
+
// src/renderers/native/chrome/priceAxisTicks.ts
|
|
21642
|
+
var PriceAxisTickSource = class {
|
|
21643
|
+
constructor() {
|
|
21644
|
+
this.fn = null;
|
|
21645
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
21646
|
+
this.warned = false;
|
|
21647
|
+
}
|
|
21648
|
+
/** The host function, or null for the built-in ticks. */
|
|
21649
|
+
get hook() {
|
|
21650
|
+
return this.fn;
|
|
21651
|
+
}
|
|
21652
|
+
/** Set (or clear) the host function. Drops every memoized result, so setting the same
|
|
21653
|
+
* function again recomputes — how a host refreshes ticks that depend on its own state. */
|
|
21654
|
+
setHook(fn) {
|
|
21655
|
+
this.fn = fn;
|
|
21656
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
21657
|
+
}
|
|
21658
|
+
/** The pane's master-scale ticks for this frame. An unscaled pane (`axisFormat: 'none'`)
|
|
21659
|
+
* gets none, and the host is never asked for it. */
|
|
21660
|
+
ticksFor(scene, pane, coords) {
|
|
21661
|
+
const format = pane.axisFormat;
|
|
21662
|
+
if (format === "none") return [];
|
|
21663
|
+
const pct = percentScaleFor(scene, pane);
|
|
21664
|
+
const height = pane.bounds.height;
|
|
21665
|
+
const fn = this.fn;
|
|
21666
|
+
if (!fn || !(height > 0)) return paneAxisTicks(pane.scale, height, pct, scene.priceMintick, format);
|
|
21667
|
+
const s = pane.scale;
|
|
21668
|
+
const m = this.memo.get(pane);
|
|
21669
|
+
const fontSize = scene.style.fontSize;
|
|
21670
|
+
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) {
|
|
21671
|
+
return m.ticks;
|
|
21672
|
+
}
|
|
21673
|
+
const defaults2 = paneAxisTicks(s, height, pct, scene.priceMintick, format);
|
|
21674
|
+
const ticks = this.callHook(fn, scene, pane, coords, defaults2, pct?.baseline, pct ? pct.indexed ? "indexed" : "percent" : "price");
|
|
21675
|
+
this.memo.set(pane, {
|
|
21676
|
+
kind: pane.kind,
|
|
21677
|
+
min: s.min,
|
|
21678
|
+
max: s.max,
|
|
21679
|
+
log: !!s.log,
|
|
21680
|
+
invert: !!s.invert,
|
|
21681
|
+
height,
|
|
21682
|
+
baseline: pct?.baseline,
|
|
21683
|
+
indexed: !!pct?.indexed,
|
|
21684
|
+
mintick: scene.priceMintick,
|
|
21685
|
+
format,
|
|
21686
|
+
fontSize,
|
|
21687
|
+
ticks
|
|
21688
|
+
});
|
|
21689
|
+
return ticks;
|
|
21690
|
+
}
|
|
21691
|
+
callHook(fn, scene, pane, coords, defaults2, baseline, mode) {
|
|
21692
|
+
const scale = { min: pane.scale.min, max: pane.scale.max, log: !!pane.scale.log, invert: !!pane.scale.invert };
|
|
21693
|
+
const bounds = { top: 0, height: pane.bounds.height };
|
|
21694
|
+
const ctx = {
|
|
21695
|
+
pane: { id: pane.id, kind: pane.kind },
|
|
21696
|
+
min: Math.min(scale.min, scale.max),
|
|
21697
|
+
max: Math.max(scale.min, scale.max),
|
|
21698
|
+
log: !!scale.log,
|
|
21699
|
+
height: bounds.height,
|
|
21700
|
+
mode,
|
|
21701
|
+
baseline,
|
|
21702
|
+
mintick: scene.priceMintick,
|
|
21703
|
+
fontSize: scene.style.fontSize,
|
|
21704
|
+
priceToY: (price) => coords.priceToY(price, scale, bounds),
|
|
21705
|
+
defaults: defaults2
|
|
21706
|
+
};
|
|
21707
|
+
let out;
|
|
21708
|
+
try {
|
|
21709
|
+
out = fn(ctx);
|
|
21710
|
+
} catch (err) {
|
|
21711
|
+
this.warnOnce("threw", err);
|
|
21712
|
+
return defaults2;
|
|
21713
|
+
}
|
|
21714
|
+
if (out == null) return defaults2;
|
|
21715
|
+
if (!Array.isArray(out)) {
|
|
21716
|
+
this.warnOnce("returned a non-array", out);
|
|
21717
|
+
return defaults2;
|
|
21718
|
+
}
|
|
21719
|
+
const ticks = [];
|
|
21720
|
+
for (const t of out) {
|
|
21721
|
+
if (!t || typeof t !== "object") continue;
|
|
21722
|
+
const { price, label, major } = t;
|
|
21723
|
+
if (typeof price !== "number" || !Number.isFinite(price) || typeof label !== "string") continue;
|
|
21724
|
+
ticks.push(typeof major === "boolean" ? { price, label, major } : { price, label });
|
|
21725
|
+
}
|
|
21726
|
+
return ticks;
|
|
21727
|
+
}
|
|
21728
|
+
warnOnce(what, detail) {
|
|
21729
|
+
if (this.warned) return;
|
|
21730
|
+
this.warned = true;
|
|
21731
|
+
console.warn(`[vela] the priceAxisTicks function ${what} \u2014 the default ticks are used instead.`, detail);
|
|
21732
|
+
}
|
|
21733
|
+
};
|
|
21734
|
+
|
|
21292
21735
|
// src/renderers/native/chrome/countdown.ts
|
|
21293
21736
|
function countdownText(barOpen, barMs, now2) {
|
|
21294
21737
|
if (!(barMs > 0)) return null;
|
|
@@ -21641,8 +22084,11 @@ function standaloneSvg(markup, ink, px) {
|
|
|
21641
22084
|
}
|
|
21642
22085
|
|
|
21643
22086
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
22087
|
+
var MINOR_LABEL_ALPHA = 0.5;
|
|
21644
22088
|
var ChromeRenderer = class {
|
|
21645
|
-
|
|
22089
|
+
/** `axisTicks` is shared with the backdrop layer, so price labels and gridlines agree. */
|
|
22090
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
22091
|
+
this.axisTicks = axisTicks;
|
|
21646
22092
|
this.canvas = null;
|
|
21647
22093
|
this.ctx = null;
|
|
21648
22094
|
// The color for axis tick labels — the host-passed surface text, set each frame in render().
|
|
@@ -21747,7 +22193,8 @@ var ChromeRenderer = class {
|
|
|
21747
22193
|
this.drawTimeAxis(ctx, scene, coords, theme, dataW, dataH, fullH);
|
|
21748
22194
|
this.drawMarkLane(ctx, scene, coords, theme, dataW, dataH);
|
|
21749
22195
|
}
|
|
21750
|
-
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge
|
|
22196
|
+
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge,
|
|
22197
|
+
* but clipped to the data area so a glyph on the edge bar never paints over the price scale. */
|
|
21751
22198
|
drawMarkLane(ctx, scene, coords, theme, dataW, dataH) {
|
|
21752
22199
|
if (!scene.marks.visible || scene.timelineMarks.length === 0) {
|
|
21753
22200
|
this.markLayout = { glyphs: [], stacks: /* @__PURE__ */ new Map() };
|
|
@@ -21765,6 +22212,10 @@ var ChromeRenderer = class {
|
|
|
21765
22212
|
expanded: scene.marksExpandedStack
|
|
21766
22213
|
});
|
|
21767
22214
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
22215
|
+
ctx.save();
|
|
22216
|
+
ctx.beginPath();
|
|
22217
|
+
ctx.rect(0, 0, dataW, dataH);
|
|
22218
|
+
ctx.clip();
|
|
21768
22219
|
paintMarkLane(ctx, this.markLayout, {
|
|
21769
22220
|
axisY: dataH,
|
|
21770
22221
|
background: theme.background,
|
|
@@ -21778,6 +22229,7 @@ var ChromeRenderer = class {
|
|
|
21778
22229
|
flashKey: scene.marksFlash && scene.marksFlash.until > nowMs ? scene.marksFlash.key : null,
|
|
21779
22230
|
nowMs
|
|
21780
22231
|
});
|
|
22232
|
+
ctx.restore();
|
|
21781
22233
|
}
|
|
21782
22234
|
barTimes(scene) {
|
|
21783
22235
|
if (this.barTimesSrc !== scene.bars || this.barTimesCache.length !== scene.bars.length) {
|
|
@@ -21834,13 +22286,19 @@ var ChromeRenderer = class {
|
|
|
21834
22286
|
if (!scene.showAxisLabels) return;
|
|
21835
22287
|
ctx.fillStyle = this.axisTextColor;
|
|
21836
22288
|
ctx.textAlign = "left";
|
|
22289
|
+
const font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
22290
|
+
const majorFont = `600 ${font}`;
|
|
22291
|
+
const alpha = ctx.globalAlpha;
|
|
21837
22292
|
for (const pane of panes) {
|
|
21838
22293
|
if (pane.collapsed) continue;
|
|
21839
|
-
const
|
|
21840
|
-
for (const t of paneAxisTicks(pane.scale, pane.bounds.height, pct, scene.priceMintick, pane.axisFormat)) {
|
|
22294
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
21841
22295
|
const y = coords.priceToY(t.price, pane.scale, pane.bounds);
|
|
21842
22296
|
if (y < pane.bounds.top + 6 || y > pane.bounds.top + pane.bounds.height - 4) continue;
|
|
22297
|
+
if (t.major === true) ctx.font = majorFont;
|
|
22298
|
+
else if (t.major === false) ctx.globalAlpha = alpha * MINOR_LABEL_ALPHA;
|
|
21843
22299
|
ctx.fillText(t.label, dataW + 6, y);
|
|
22300
|
+
if (t.major === true) ctx.font = font;
|
|
22301
|
+
else if (t.major === false) ctx.globalAlpha = alpha;
|
|
21844
22302
|
}
|
|
21845
22303
|
if (pane.axisBands) {
|
|
21846
22304
|
for (const b of pane.axisBands) {
|
|
@@ -22082,15 +22540,23 @@ var CrosshairRenderer = class {
|
|
|
22082
22540
|
constructor() {
|
|
22083
22541
|
this.canvas = null;
|
|
22084
22542
|
this.ctx = null;
|
|
22543
|
+
this.shade = null;
|
|
22085
22544
|
}
|
|
22086
22545
|
mount(canvas) {
|
|
22087
22546
|
this.canvas = canvas;
|
|
22088
22547
|
this.ctx = canvas.getContext("2d");
|
|
22089
22548
|
}
|
|
22549
|
+
/** The plot area the last frame veiled for `crosshairOverride.shadeRight` — from the
|
|
22550
|
+
* picked bar's right edge to the price scale, full plot height (the union when both the
|
|
22551
|
+
* local and a synced crosshair veil) — or null when no veil was painted. */
|
|
22552
|
+
get shadeRightArea() {
|
|
22553
|
+
return this.shade;
|
|
22554
|
+
}
|
|
22090
22555
|
/** Clear the cursor canvas and (re)draw the crosshair lines + axis chips. The optional
|
|
22091
22556
|
* `separatorHoverY` highlights the draggable pane separator under the cursor;
|
|
22092
22557
|
* `external` is a SYNCED ghost crosshair (another chart's pointer, pixel-resolved). */
|
|
22093
22558
|
render(scene, coords, theme, separatorHoverY = null, external = null) {
|
|
22559
|
+
this.shade = null;
|
|
22094
22560
|
const ctx = this.ctx;
|
|
22095
22561
|
const canvas = this.canvas;
|
|
22096
22562
|
if (!ctx || !canvas) return;
|
|
@@ -22113,12 +22579,7 @@ var CrosshairRenderer = class {
|
|
|
22113
22579
|
const x = crisp(coords.logicalToX(logical), ov?.width ?? cs.width);
|
|
22114
22580
|
if (vertical && ov?.shadeRight) {
|
|
22115
22581
|
const from = Math.max(0, Math.round(coords.logicalToX(logical + 0.5)));
|
|
22116
|
-
if (from < dataW)
|
|
22117
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
22118
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
22119
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22120
|
-
ctx.globalAlpha = 1;
|
|
22121
|
-
}
|
|
22582
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
22122
22583
|
}
|
|
22123
22584
|
ctx.strokeStyle = ov?.color ?? cs.color ?? theme.textColor;
|
|
22124
22585
|
ctx.lineWidth = ov?.width ?? cs.width;
|
|
@@ -22154,6 +22615,7 @@ var CrosshairRenderer = class {
|
|
|
22154
22615
|
destroy() {
|
|
22155
22616
|
this.canvas = null;
|
|
22156
22617
|
this.ctx = null;
|
|
22618
|
+
this.shade = null;
|
|
22157
22619
|
}
|
|
22158
22620
|
/** The synced ghost: a dimmed vertical line at the bar the renderer resolved as
|
|
22159
22621
|
* CONTAINING the foreign time (+ horizontal line when a comparable price came
|
|
@@ -22173,12 +22635,7 @@ var CrosshairRenderer = class {
|
|
|
22173
22635
|
if (ov?.vertical === false) return;
|
|
22174
22636
|
if (ov?.shadeRight) {
|
|
22175
22637
|
const from = Math.max(0, Math.round(coords.logicalToX(Math.round(coords.xToLogical(ext.x)) + 0.5)));
|
|
22176
|
-
if (from < dataW)
|
|
22177
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
22178
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
22179
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22180
|
-
ctx.globalAlpha = 1;
|
|
22181
|
-
}
|
|
22638
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
22182
22639
|
}
|
|
22183
22640
|
if (ext.line === false || x < 0 || x > dataW) return;
|
|
22184
22641
|
ctx.font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
@@ -22214,6 +22671,15 @@ var CrosshairRenderer = class {
|
|
|
22214
22671
|
this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
|
|
22215
22672
|
ctx.globalAlpha = 1;
|
|
22216
22673
|
}
|
|
22674
|
+
/** Paint the `shadeRight` veil from `from` to the price scale and record the veiled area. */
|
|
22675
|
+
paintShade(ctx, shade, from, dataW, dataH) {
|
|
22676
|
+
ctx.fillStyle = shade.color;
|
|
22677
|
+
ctx.globalAlpha = shade.opacity ?? 1;
|
|
22678
|
+
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
22679
|
+
ctx.globalAlpha = 1;
|
|
22680
|
+
const x = this.shade ? Math.min(this.shade.x, from) : from;
|
|
22681
|
+
this.shade = { x, width: dataW - x, height: dataH };
|
|
22682
|
+
}
|
|
22217
22683
|
/** A soft band + a brighter crisp center line over the hovered separator, so it reads as
|
|
22218
22684
|
* a draggable handle (the cursor is already `row-resize`). Spans the full width (data +
|
|
22219
22685
|
* scale gutter) to match the separator itself. Theme-derived (textColor). */
|
|
@@ -25329,6 +25795,12 @@ var DrawingInteraction = class {
|
|
|
25329
25795
|
activeDragId() {
|
|
25330
25796
|
return this.state.kind === "pressed" && this.state.moved ? this.state.id : null;
|
|
25331
25797
|
}
|
|
25798
|
+
/** The store drawings a live drag is moving — the pressed one and its riders — or none
|
|
25799
|
+
* before the drag passes the slop. A Ctrl-drag moves copies, so its sources stay put. */
|
|
25800
|
+
movingIds() {
|
|
25801
|
+
if (this.state.kind !== "pressed" || !this.state.moved || this.state.clones) return /* @__PURE__ */ new Set();
|
|
25802
|
+
return /* @__PURE__ */ new Set([this.state.id, ...this.state.riders.map((r) => r.id)]);
|
|
25803
|
+
}
|
|
25332
25804
|
/** The drawing under a press that has not been released yet (drag or click undecided), or null. */
|
|
25333
25805
|
pressedId() {
|
|
25334
25806
|
return this.state.kind === "pressed" ? this.state.id : null;
|
|
@@ -25703,15 +26175,16 @@ var DrawingSettingsDialog = class {
|
|
|
25703
26175
|
}
|
|
25704
26176
|
open(drawing, actions, kind) {
|
|
25705
26177
|
this.close();
|
|
25706
|
-
const
|
|
26178
|
+
const live = actions.resolve() ?? drawing;
|
|
26179
|
+
const snapshot = clonePlain(live.serialize());
|
|
25707
26180
|
const grid = fieldGrid({ variant: "inputs" });
|
|
25708
26181
|
grid.style.padding = "16px 20px";
|
|
25709
26182
|
grid.style.overflowY = "auto";
|
|
25710
26183
|
grid.style.overflowX = "hidden";
|
|
25711
26184
|
grid.style.flex = "1 1 auto";
|
|
25712
|
-
if (kind === "position" &&
|
|
25713
|
-
else if (kind === "frvp" &&
|
|
25714
|
-
else if (kind === "levels") this.buildLevels(grid,
|
|
26185
|
+
if (kind === "position" && live instanceof PositionTool) this.buildPosition(grid, live, actions);
|
|
26186
|
+
else if (kind === "frvp" && live instanceof FixedRangeVolumeProfile) this.buildFrvp(grid, live, actions);
|
|
26187
|
+
else if (kind === "levels") this.buildLevels(grid, live, actions);
|
|
25715
26188
|
else return;
|
|
25716
26189
|
const ui = new Dialog({
|
|
25717
26190
|
host: this.host,
|
|
@@ -25987,6 +26460,17 @@ var DrawingSettingsDialog = class {
|
|
|
25987
26460
|
}
|
|
25988
26461
|
}));
|
|
25989
26462
|
}
|
|
26463
|
+
if (drawing instanceof FibRetracement) {
|
|
26464
|
+
const fib = drawing;
|
|
26465
|
+
grid.appendChild(fieldRow({
|
|
26466
|
+
label: "Reverse",
|
|
26467
|
+
bool: true,
|
|
26468
|
+
toggle: {
|
|
26469
|
+
checked: fib.reverse,
|
|
26470
|
+
onChange: (v) => actions.patch({ reverse: v })
|
|
26471
|
+
}
|
|
26472
|
+
}));
|
|
26473
|
+
}
|
|
25990
26474
|
levels.forEach((lv, i) => {
|
|
25991
26475
|
const row = document.createElement("div");
|
|
25992
26476
|
row.className = "vela-field-span";
|
|
@@ -26347,7 +26831,7 @@ var DrawingSettingsPopup = class {
|
|
|
26347
26831
|
this.reposition();
|
|
26348
26832
|
return;
|
|
26349
26833
|
}
|
|
26350
|
-
const text = drawing.text;
|
|
26834
|
+
const text = (actions.resolve() ?? drawing).text;
|
|
26351
26835
|
const panel = document.createElement("div");
|
|
26352
26836
|
panel.style.cssText = `padding:6px;border-top:1px solid var(--vela-border);display:flex;flex-direction:column;gap:4px;`;
|
|
26353
26837
|
const ta = new TextArea({
|
|
@@ -26943,6 +27427,7 @@ function sized(svg, size = ICON) {
|
|
|
26943
27427
|
// src/renderers/native/drawings/DrawingToolbar.ts
|
|
26944
27428
|
var TOOLBAR_WIDTH = 44;
|
|
26945
27429
|
var TOOLBAR_COLLAPSED_WIDTH = 16;
|
|
27430
|
+
var FLYOUT_GAP = 3;
|
|
26946
27431
|
var DrawingToolbar = class {
|
|
26947
27432
|
constructor(host, theme, onArm, onMagnet = () => {
|
|
26948
27433
|
}, onMeasure = () => {
|
|
@@ -26972,6 +27457,9 @@ var DrawingToolbar = class {
|
|
|
26972
27457
|
// group id (or MAGNET_ID) whose flyout is open
|
|
26973
27458
|
this.flyoutCell = null;
|
|
26974
27459
|
// the cell the open flyout is anchored to
|
|
27460
|
+
/** Closed flyouts still playing their exit animation, by owner id — reopening the same
|
|
27461
|
+
* owner takes its element back instead of stacking a second one on it. */
|
|
27462
|
+
this.exitingFlyouts = /* @__PURE__ */ new Map();
|
|
26975
27463
|
this.groupCells = /* @__PURE__ */ new Map();
|
|
26976
27464
|
// the composite cell (hover/active bg + flyout anchor)
|
|
26977
27465
|
this.groupIcons = /* @__PURE__ */ new Map();
|
|
@@ -27080,6 +27568,7 @@ var DrawingToolbar = class {
|
|
|
27080
27568
|
}
|
|
27081
27569
|
destroy() {
|
|
27082
27570
|
this.closeFlyout();
|
|
27571
|
+
for (const { exit } of [...this.exitingFlyouts.values()]) exit.finish();
|
|
27083
27572
|
for (const dispose of this.tipDisposers.splice(0)) dispose();
|
|
27084
27573
|
this.root.remove();
|
|
27085
27574
|
}
|
|
@@ -27307,16 +27796,22 @@ var DrawingToolbar = class {
|
|
|
27307
27796
|
* outside-dismiss. Callers fill it with items. */
|
|
27308
27797
|
beginFlyout(ownerId, cell) {
|
|
27309
27798
|
const t = this.theme;
|
|
27310
|
-
const
|
|
27799
|
+
const back = this.exitingFlyouts.get(ownerId);
|
|
27800
|
+
if (back) {
|
|
27801
|
+
this.exitingFlyouts.delete(ownerId);
|
|
27802
|
+
back.exit.cancel();
|
|
27803
|
+
back.fly.replaceChildren();
|
|
27804
|
+
}
|
|
27805
|
+
const fly = back?.fly ?? document.createElement("div");
|
|
27311
27806
|
fly.className = "vela-dtb-flyout";
|
|
27312
|
-
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:
|
|
27807
|
+
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;`;
|
|
27313
27808
|
applyChromeTokens(fly, t);
|
|
27314
27809
|
this.host.appendChild(fly);
|
|
27315
27810
|
const r = cell.getBoundingClientRect();
|
|
27316
27811
|
const rootR = this.root.getBoundingClientRect();
|
|
27317
27812
|
const hostR = this.host.getBoundingClientRect();
|
|
27318
27813
|
const top = r.top - hostR.top;
|
|
27319
|
-
fly.style.left = `${rootR.right - hostR.left
|
|
27814
|
+
fly.style.left = `${rootR.right - hostR.left + FLYOUT_GAP}px`;
|
|
27320
27815
|
fly.style.top = `${top}px`;
|
|
27321
27816
|
fly.style.maxHeight = `${Math.max(120, hostR.height - top - 8)}px`;
|
|
27322
27817
|
this.flyout = fly;
|
|
@@ -27332,6 +27827,7 @@ var DrawingToolbar = class {
|
|
|
27332
27827
|
if (!cell) return;
|
|
27333
27828
|
const fly = this.beginFlyout(group.id, cell);
|
|
27334
27829
|
const sections = group.sections ?? [{ label: "", tools: group.tools }];
|
|
27830
|
+
const badge = sections.some((s) => s.tools.some((tool) => !!tool.icon));
|
|
27335
27831
|
for (let si = 0; si < sections.length; si++) {
|
|
27336
27832
|
const section = sections[si];
|
|
27337
27833
|
if (section.label) {
|
|
@@ -27342,6 +27838,7 @@ var DrawingToolbar = class {
|
|
|
27342
27838
|
fly.appendChild(
|
|
27343
27839
|
this.makeFlyoutItem({
|
|
27344
27840
|
icon: tool.icon,
|
|
27841
|
+
badge,
|
|
27345
27842
|
label: tool.label,
|
|
27346
27843
|
selected: tool.type === this.active,
|
|
27347
27844
|
shortcut: this.shortcuts.get(tool.type),
|
|
@@ -27354,6 +27851,7 @@ var DrawingToolbar = class {
|
|
|
27354
27851
|
);
|
|
27355
27852
|
}
|
|
27356
27853
|
}
|
|
27854
|
+
announceSurface(fly, true, "menu", cell);
|
|
27357
27855
|
}
|
|
27358
27856
|
openMagnetFlyout() {
|
|
27359
27857
|
const cell = this.magnetCell;
|
|
@@ -27377,6 +27875,7 @@ var DrawingToolbar = class {
|
|
|
27377
27875
|
})
|
|
27378
27876
|
);
|
|
27379
27877
|
}
|
|
27878
|
+
announceSurface(fly, true, "menu", cell);
|
|
27380
27879
|
}
|
|
27381
27880
|
flyoutHeader(text) {
|
|
27382
27881
|
const t = this.theme;
|
|
@@ -27390,21 +27889,24 @@ var DrawingToolbar = class {
|
|
|
27390
27889
|
div.style.cssText = `height:1px;margin:4px 8px;background:${this.borderColor};`;
|
|
27391
27890
|
return div;
|
|
27392
27891
|
}
|
|
27393
|
-
/** A flyout row, left to right: optional leading icon, label, a check when it's the
|
|
27394
|
-
* entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
27395
|
-
* Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
27892
|
+
/** A flyout row, left to right: optional leading icon badge, label, a check when it's the
|
|
27893
|
+
* selected entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
27894
|
+
* rows). Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
27396
27895
|
makeFlyoutItem(opts) {
|
|
27397
27896
|
const t = this.theme;
|
|
27398
27897
|
const item = document.createElement("button");
|
|
27399
27898
|
item.type = "button";
|
|
27400
27899
|
item.className = "vela-dtb-item";
|
|
27401
27900
|
item.setAttribute("aria-label", opts.label);
|
|
27402
|
-
|
|
27403
|
-
|
|
27404
|
-
|
|
27405
|
-
|
|
27406
|
-
|
|
27407
|
-
|
|
27901
|
+
const badged = opts.badge === true || !!opts.icon;
|
|
27902
|
+
const box = badged ? "gap:10px;padding:4px 10px 4px 6px;" : "gap:8px;padding:5px 10px 5px 8px;";
|
|
27903
|
+
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;`;
|
|
27904
|
+
if (opts.selected) item.dataset.selected = "1";
|
|
27905
|
+
if (badged) {
|
|
27906
|
+
const badge = document.createElement("span");
|
|
27907
|
+
badge.className = "vela-dtb-badge";
|
|
27908
|
+
if (opts.icon) badge.innerHTML = opts.icon;
|
|
27909
|
+
item.appendChild(badge);
|
|
27408
27910
|
}
|
|
27409
27911
|
const label = document.createElement("span");
|
|
27410
27912
|
label.textContent = opts.label;
|
|
@@ -27445,9 +27947,16 @@ var DrawingToolbar = class {
|
|
|
27445
27947
|
}
|
|
27446
27948
|
closeFlyout() {
|
|
27447
27949
|
this.starEls.clear();
|
|
27448
|
-
|
|
27449
|
-
|
|
27950
|
+
const fly = this.flyout;
|
|
27951
|
+
if (fly) {
|
|
27450
27952
|
this.flyout = null;
|
|
27953
|
+
announceSurface(fly, false, "menu", this.flyoutCell);
|
|
27954
|
+
const owner = this.flyoutOwnerId ?? "";
|
|
27955
|
+
const exit = holdForExit(fly, () => {
|
|
27956
|
+
if (this.exitingFlyouts.get(owner)?.fly === fly) this.exitingFlyouts.delete(owner);
|
|
27957
|
+
fly.remove();
|
|
27958
|
+
});
|
|
27959
|
+
if (exit) this.exitingFlyouts.set(owner, { fly, exit });
|
|
27451
27960
|
this.flyoutCell?.classList.remove("vela-open");
|
|
27452
27961
|
this.flyoutCell = null;
|
|
27453
27962
|
this.flyoutOwnerId = null;
|
|
@@ -27531,6 +28040,11 @@ function ensureStyles4() {
|
|
|
27531
28040
|
.vela-dtb[data-collapsed='1'] .vela-dtb-collapse .vela-dtb-hit{width:14px;}
|
|
27532
28041
|
.vela-dtb-item{background:transparent;border:none;transition:background var(--vela-dur-fast) ease;}
|
|
27533
28042
|
.vela-dtb-item:hover{background:var(--vela-hover-strong);}
|
|
28043
|
+
.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;}
|
|
28044
|
+
.vela-dtb-badge:empty{background:transparent;border-color:transparent;}
|
|
28045
|
+
.vela-dtb-badge svg{width:100%;height:100%;}
|
|
28046
|
+
.vela-dtb-item:hover .vela-dtb-badge:not(:empty){background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
28047
|
+
.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);}
|
|
27534
28048
|
.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;}
|
|
27535
28049
|
.vela-dtb-star svg{width:16px;height:16px;}
|
|
27536
28050
|
.vela-dtb-item:hover .vela-dtb-star{opacity:.55;}
|
|
@@ -27772,6 +28286,8 @@ var UserDrawingController = class {
|
|
|
27772
28286
|
this.drawings = [];
|
|
27773
28287
|
/** Ids painted on an interleave layer this frame — the top canvas paints only their handles. */
|
|
27774
28288
|
this.sliced = /* @__PURE__ */ new Set();
|
|
28289
|
+
/** Interleaved drawings mid-drag: painted on the top canvas, left out of their slice. */
|
|
28290
|
+
this.lifted = /* @__PURE__ */ new Set();
|
|
27775
28291
|
/** Cached slice canvases, keyed `paneId|beforeZ`, reused across frames to avoid churn. */
|
|
27776
28292
|
this.sliceCache = /* @__PURE__ */ new Map();
|
|
27777
28293
|
/** Series boundaries per pane as of the last `prepareSlices` — lets a repaint between data
|
|
@@ -27859,7 +28375,7 @@ var UserDrawingController = class {
|
|
|
27859
28375
|
selectedIds: () => this.selectedIds,
|
|
27860
28376
|
emit: (i) => this.emit(i),
|
|
27861
28377
|
changed: () => {
|
|
27862
|
-
this.
|
|
28378
|
+
this.syncLift();
|
|
27863
28379
|
this.render();
|
|
27864
28380
|
},
|
|
27865
28381
|
openSettings: (id, x, y) => this.openSettingsById(id, x, y),
|
|
@@ -28148,6 +28664,12 @@ var UserDrawingController = class {
|
|
|
28148
28664
|
this.render();
|
|
28149
28665
|
}
|
|
28150
28666
|
}
|
|
28667
|
+
/** Drop the hover (its handles) — the pointer no longer reaches the drawings. */
|
|
28668
|
+
clearHover() {
|
|
28669
|
+
if (this.hoveredId == null) return;
|
|
28670
|
+
this.hoveredId = null;
|
|
28671
|
+
this.render();
|
|
28672
|
+
}
|
|
28151
28673
|
pointerUp(x, y, snap = "off") {
|
|
28152
28674
|
if (this.eraserMode) {
|
|
28153
28675
|
this.erasing = false;
|
|
@@ -28485,6 +29007,20 @@ var UserDrawingController = class {
|
|
|
28485
29007
|
/** A drawing that paints inside the series stack changed (content, not hover): its pixels
|
|
28486
29008
|
* live in the backend composite, so this layer alone can't show the change — ask for a
|
|
28487
29009
|
* data frame, which re-runs `prepareSlices` before the backend composites. */
|
|
29010
|
+
/** A drawing being dragged leaves its interleave slice for the top canvas, so each pointer
|
|
29011
|
+
* move repaints one 2D layer instead of re-rasterizing and re-uploading a plot-sized slice
|
|
29012
|
+
* texture through the data frame. The slices rebuild once as the drag starts and once as it
|
|
29013
|
+
* ends (release, cancel), when the lifted set changes. */
|
|
29014
|
+
syncLift() {
|
|
29015
|
+
const moving = this.interaction.movingIds();
|
|
29016
|
+
let same = moving.size === this.lifted.size;
|
|
29017
|
+
if (same) {
|
|
29018
|
+
for (const id of moving) if (!this.lifted.has(id)) same = false;
|
|
29019
|
+
}
|
|
29020
|
+
if (same) return;
|
|
29021
|
+
this.lifted = new Set(moving);
|
|
29022
|
+
this.invalidateSlices();
|
|
29023
|
+
}
|
|
28488
29024
|
invalidateSlices() {
|
|
28489
29025
|
if (this.sliced.size > 0 || this.drawings.some((d) => this.isInterleaved(d))) this.deps.requestDataPaint();
|
|
28490
29026
|
}
|
|
@@ -28503,7 +29039,7 @@ var UserDrawingController = class {
|
|
|
28503
29039
|
const theme = this.deps.theme();
|
|
28504
29040
|
const buckets = /* @__PURE__ */ new Map();
|
|
28505
29041
|
for (const d of this.drawings) {
|
|
28506
|
-
if (!d.visible) continue;
|
|
29042
|
+
if (!d.visible || this.lifted.has(d.id)) continue;
|
|
28507
29043
|
const beforeZ = sliceKeyFor(d.zIndex, this.lastBounds.get(d.paneId) ?? []);
|
|
28508
29044
|
if (beforeZ === null) continue;
|
|
28509
29045
|
const key = `${d.paneId}|${beforeZ}`;
|
|
@@ -28555,8 +29091,9 @@ var UserDrawingController = class {
|
|
|
28555
29091
|
mutedLabel: edited instanceof TextLabel ? edited.id : null
|
|
28556
29092
|
};
|
|
28557
29093
|
this.painter.seriesLook = this.deps.seriesLook();
|
|
28558
|
-
|
|
28559
|
-
this.painter.
|
|
29094
|
+
const onTop = (d) => !this.isInterleaved(d) || this.lifted.has(d.id);
|
|
29095
|
+
this.painter.paintAll(ctx, this.drawings.filter(onTop), proj, this.deps.theme(), targets);
|
|
29096
|
+
this.painter.paintHighlights(ctx, this.drawings.filter((d) => !onTop(d)), proj, handleIdsFor(targets));
|
|
28560
29097
|
const clones = this.interaction.dragClones();
|
|
28561
29098
|
if (clones) this.painter.paintAll(ctx, clones, proj, this.deps.theme(), { selected: new Set(clones.map((c) => c.id)) });
|
|
28562
29099
|
this.layoutTextEditor();
|
|
@@ -29123,9 +29660,10 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
29123
29660
|
consider(drawings.max);
|
|
29124
29661
|
}
|
|
29125
29662
|
if (min === Infinity || max === -Infinity) return { min: 0, max: 1 };
|
|
29126
|
-
if (min
|
|
29127
|
-
const
|
|
29128
|
-
|
|
29663
|
+
if (max - min <= Math.max(Math.abs(min), Math.abs(max)) * 1e-12) {
|
|
29664
|
+
const mid = (min + max) / 2;
|
|
29665
|
+
const pad = Math.abs(mid) * 0.1 || 1;
|
|
29666
|
+
return { min: mid - pad, max: mid + pad, log: log && mid - pad > 0 };
|
|
29129
29667
|
}
|
|
29130
29668
|
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
29131
29669
|
const above = margins.top / 100 / content;
|
|
@@ -29629,7 +30167,9 @@ function bandEdgeSlot(logical) {
|
|
|
29629
30167
|
return Math.ceil(logical) - 0.5;
|
|
29630
30168
|
}
|
|
29631
30169
|
var BackdropRenderer = class {
|
|
29632
|
-
|
|
30170
|
+
/** `axisTicks` is shared with the chrome layer, so gridlines and price labels agree. */
|
|
30171
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
30172
|
+
this.axisTicks = axisTicks;
|
|
29633
30173
|
this.canvas = null;
|
|
29634
30174
|
this.ctx = null;
|
|
29635
30175
|
}
|
|
@@ -29706,9 +30246,8 @@ var BackdropRenderer = class {
|
|
|
29706
30246
|
if (scene.showGrid && gridHorz.visible && !pane.collapsed) {
|
|
29707
30247
|
ctx.globalAlpha = gridAlpha;
|
|
29708
30248
|
ctx.strokeStyle = horzColor;
|
|
29709
|
-
const pct = percentScaleFor(scene, pane);
|
|
29710
30249
|
ctx.beginPath();
|
|
29711
|
-
for (const t of
|
|
30250
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
29712
30251
|
const y = Math.round(coords.priceToY(t.price, pane.scale, pane.bounds)) + 0.5;
|
|
29713
30252
|
if (y < pane.bounds.top || y > pane.bounds.top + pane.bounds.height) continue;
|
|
29714
30253
|
ctx.moveTo(0, y);
|
|
@@ -30324,8 +30863,16 @@ var NativeRenderer = class {
|
|
|
30324
30863
|
this.toolbarGutter = 0;
|
|
30325
30864
|
// px reserved on the left for the docked drawings toolbar (0 when hidden)
|
|
30326
30865
|
this.mountContainer = null;
|
|
30866
|
+
/** The host-styleable twin of the `shadeRight` veil (`.vela-shade-right`), directly under
|
|
30867
|
+
* the cursor canvas; hidden whenever the crosshair layer paints no veil. */
|
|
30868
|
+
this.shadeRightEl = null;
|
|
30869
|
+
/** The veil geometry last written to {@link shadeRightEl} ('' = hidden) — the DOM is
|
|
30870
|
+
* touched only when it changes, never on a plain pointer move. */
|
|
30871
|
+
this.shadeRightSig = "";
|
|
30327
30872
|
this.userDrawings = null;
|
|
30328
|
-
|
|
30873
|
+
/** One price-axis tick source for the gridlines (backdrop) and the labels (chrome). */
|
|
30874
|
+
this.priceAxisTicks = new PriceAxisTickSource();
|
|
30875
|
+
this.backdropRenderer = new BackdropRenderer(this.priceAxisTicks);
|
|
30329
30876
|
this.volumeRenderer = new VolumeRenderer();
|
|
30330
30877
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
30331
30878
|
/**
|
|
@@ -30358,7 +30905,7 @@ var NativeRenderer = class {
|
|
|
30358
30905
|
this.backendMode = "auto";
|
|
30359
30906
|
this.glowAmount = 0;
|
|
30360
30907
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
30361
|
-
this.chrome = new ChromeRenderer();
|
|
30908
|
+
this.chrome = new ChromeRenderer(this.priceAxisTicks);
|
|
30362
30909
|
/** Prepaints each indicator's Pine drawings into interleave slices at the model's z. */
|
|
30363
30910
|
this.indicatorSlices = new IndicatorDrawingSlices();
|
|
30364
30911
|
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
@@ -30475,6 +31022,8 @@ var NativeRenderer = class {
|
|
|
30475
31022
|
// pane just below it
|
|
30476
31023
|
this.resizeSplitStart = null;
|
|
30477
31024
|
// the two panes' shared span when the drag began
|
|
31025
|
+
/** The user drawings take part in pointer input (the runtime `drawingsInteractive` feature). */
|
|
31026
|
+
this.drawingsInteractive = true;
|
|
30478
31027
|
this.hoverSeparatorY = null;
|
|
30479
31028
|
// pixel y of the separator under the cursor (drives its hover highlight)
|
|
30480
31029
|
// ── pane management (merge / reorder / collapse / maximize) ──
|
|
@@ -30530,8 +31079,9 @@ var NativeRenderer = class {
|
|
|
30530
31079
|
this.toggleVisibleCbs = /* @__PURE__ */ new Set();
|
|
30531
31080
|
this.moveIndicatorCbs = /* @__PURE__ */ new Set();
|
|
30532
31081
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
31082
|
+
this.priceStyleWillChangeCbs = /* @__PURE__ */ new Set();
|
|
30533
31083
|
this.name = "native";
|
|
30534
|
-
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"];
|
|
31084
|
+
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"];
|
|
30535
31085
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
30536
31086
|
* so moving onto the button doesn't count as leaving). */
|
|
30537
31087
|
this.onScrollProximityMove = (e) => {
|
|
@@ -30575,6 +31125,7 @@ var NativeRenderer = class {
|
|
|
30575
31125
|
this.scene.priceStyle = opts.priceStyle;
|
|
30576
31126
|
this.scene.basePainting = basePaintingOf(opts.priceStyle);
|
|
30577
31127
|
this.scene.candleOverride = candleOverrideFor(opts.priceStyle, this.scene.style.chartTypes);
|
|
31128
|
+
if (typeof opts.priceAxisTicks === "function") this.priceAxisTicks.setHook(opts.priceAxisTicks);
|
|
30578
31129
|
}
|
|
30579
31130
|
this.theme = this.deriveTheme(DARK_THEME);
|
|
30580
31131
|
}
|
|
@@ -30713,6 +31264,15 @@ var NativeRenderer = class {
|
|
|
30713
31264
|
case "crosshairOverride":
|
|
30714
31265
|
this.scene.crosshairOverride = sanitizeCrosshairOverride(value);
|
|
30715
31266
|
break;
|
|
31267
|
+
case "drawingsInteractive":
|
|
31268
|
+
this.drawingsInteractive = value !== false;
|
|
31269
|
+
if (this.input) this.input.drawings = this.drawingsInteractive;
|
|
31270
|
+
if (!this.drawingsInteractive) this.userDrawings?.clearHover();
|
|
31271
|
+
return;
|
|
31272
|
+
// affects the next gesture only — clearHover repaints what it drops
|
|
31273
|
+
case "priceAxisTicks":
|
|
31274
|
+
this.priceAxisTicks.setHook(typeof value === "function" ? value : null);
|
|
31275
|
+
break;
|
|
30716
31276
|
case "keyboard":
|
|
30717
31277
|
this.setKeyboardEnabled(Boolean(value));
|
|
30718
31278
|
return;
|
|
@@ -30828,6 +31388,10 @@ var NativeRenderer = class {
|
|
|
30828
31388
|
return { visible: this.scene.marks.visible, groups: { ...this.scene.marks.groups } };
|
|
30829
31389
|
case "crosshairOverride":
|
|
30830
31390
|
return this.scene.crosshairOverride ? { ...this.scene.crosshairOverride } : null;
|
|
31391
|
+
case "drawingsInteractive":
|
|
31392
|
+
return this.drawingsInteractive;
|
|
31393
|
+
case "priceAxisTicks":
|
|
31394
|
+
return this.priceAxisTicks.hook;
|
|
30831
31395
|
case "keyboard":
|
|
30832
31396
|
return this.keyboardEnabled;
|
|
30833
31397
|
case "historyChords":
|
|
@@ -31501,6 +32065,12 @@ var NativeRenderer = class {
|
|
|
31501
32065
|
Object.assign(this.drawingsCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
31502
32066
|
this.cursorCanvas = document.createElement("canvas");
|
|
31503
32067
|
Object.assign(this.cursorCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
32068
|
+
this.shadeRightEl = document.createElement("div");
|
|
32069
|
+
this.shadeRightEl.className = "vela-shade-right";
|
|
32070
|
+
this.shadeRightEl.setAttribute("aria-hidden", "true");
|
|
32071
|
+
this.shadeRightEl.hidden = true;
|
|
32072
|
+
Object.assign(this.shadeRightEl.style, { position: "absolute", top: "0", left: "0", width: "0", height: "0", pointerEvents: "none", display: "none" });
|
|
32073
|
+
this.shadeRightSig = "";
|
|
31504
32074
|
this.overlayRoot = document.createElement("div");
|
|
31505
32075
|
Object.assign(this.overlayRoot.style, { position: "absolute", inset: "0", pointerEvents: "none" });
|
|
31506
32076
|
this.plot = document.createElement("div");
|
|
@@ -31508,7 +32078,7 @@ var NativeRenderer = class {
|
|
|
31508
32078
|
this.extLayers = rendererLayers().map((def) => ({ def, instance: def.create(), canvas: this.createLayerCanvas(), channel: def.id, owner: null }));
|
|
31509
32079
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
31510
32080
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
31511
|
-
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
32081
|
+
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.shadeRightEl, this.cursorCanvas, this.overlayRoot);
|
|
31512
32082
|
this.layerOrderSig = "";
|
|
31513
32083
|
this.wrapper.appendChild(this.plot);
|
|
31514
32084
|
this.factoryConfig = this.getConfig();
|
|
@@ -31566,13 +32136,14 @@ var NativeRenderer = class {
|
|
|
31566
32136
|
drawingsPointerDown: (x, y, snap, shift2, mod) => this.userDrawings?.pointerDown(x, y, snap, shift2, mod),
|
|
31567
32137
|
drawingsPointerMove: (x, y, snap, shift2, mod) => this.userDrawings?.pointerMove(x, y, snap, shift2, mod),
|
|
31568
32138
|
drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
|
|
31569
|
-
drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
32139
|
+
drawingsCursor: (x, y) => (this.drawingsInteractive ? this.userDrawings?.cursorAt(x, y) : null) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
31570
32140
|
drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
|
|
31571
32141
|
drawingsClearTransient: () => this.userDrawings?.clearTransient()
|
|
31572
32142
|
});
|
|
31573
32143
|
this.input.rightEdgeZoom = this.zoomAnchorMode === "right";
|
|
31574
32144
|
this.input.axisDrag = this.axisDragEnabled;
|
|
31575
32145
|
this.input.paneResize = this.paneResizeEnabled;
|
|
32146
|
+
this.input.drawings = this.drawingsInteractive;
|
|
31576
32147
|
this.input.attach(this.dataCanvas);
|
|
31577
32148
|
if (this.settingsEnabled) this.setSettingsEnabled(true);
|
|
31578
32149
|
this.syncCountdownTimer();
|
|
@@ -31881,6 +32452,8 @@ var NativeRenderer = class {
|
|
|
31881
32452
|
this.backend.destroy();
|
|
31882
32453
|
this.chrome.destroy();
|
|
31883
32454
|
this.crosshairLayer.destroy();
|
|
32455
|
+
this.shadeRightEl?.remove();
|
|
32456
|
+
this.shadeRightEl = null;
|
|
31884
32457
|
this.attributionEl?.remove();
|
|
31885
32458
|
this.attributionEl = null;
|
|
31886
32459
|
this.mountContainer?.style.removeProperty("--vela-toolbar-gutter");
|
|
@@ -32227,14 +32800,21 @@ var NativeRenderer = class {
|
|
|
32227
32800
|
this.priceStyleCbs.add(cb);
|
|
32228
32801
|
return () => this.priceStyleCbs.delete(cb);
|
|
32229
32802
|
}
|
|
32803
|
+
onPriceStyleWillChange(cb) {
|
|
32804
|
+
this.priceStyleWillChangeCbs.add(cb);
|
|
32805
|
+
return () => this.priceStyleWillChangeCbs.delete(cb);
|
|
32806
|
+
}
|
|
32230
32807
|
/**
|
|
32231
32808
|
* THE single write path for the base price style at runtime (feature set / settings dialog /
|
|
32232
|
-
* config template — the constructor seeds the field directly, pre-listeners).
|
|
32233
|
-
*
|
|
32234
|
-
*
|
|
32809
|
+
* config template — the constructor seeds the field directly, pre-listeners). Announces the
|
|
32810
|
+
* switch while the scene still holds the old style (a listener may paint/capture that frame),
|
|
32811
|
+
* then updates the scene, eases any reveal layer toward the new style's target, and notifies
|
|
32812
|
+
* the core — which owns the DATA side of styles that need one (a chart type's SeriesDataEngine).
|
|
32235
32813
|
*/
|
|
32236
32814
|
setPriceStyle(style) {
|
|
32237
32815
|
if (style === this.scene.priceStyle) return;
|
|
32816
|
+
const from = this.scene.priceStyle;
|
|
32817
|
+
for (const cb of this.priceStyleWillChangeCbs) cb(from, style);
|
|
32238
32818
|
this.scene.priceStyle = style;
|
|
32239
32819
|
this.scene.basePainting = basePaintingOf(style);
|
|
32240
32820
|
this.scene.candleOverride = candleOverrideFor(style, this.scene.style.chartTypes);
|
|
@@ -32464,7 +33044,7 @@ var NativeRenderer = class {
|
|
|
32464
33044
|
if (this.easeLiveBar(dtMs)) active = true;
|
|
32465
33045
|
this.skeletonClockMs += dtMs;
|
|
32466
33046
|
this.paintData();
|
|
32467
|
-
this.
|
|
33047
|
+
this.renderCrosshairLayer();
|
|
32468
33048
|
this.updateLegendValues();
|
|
32469
33049
|
this.emitViewportChange();
|
|
32470
33050
|
return active;
|
|
@@ -33039,10 +33619,31 @@ var NativeRenderer = class {
|
|
|
33039
33619
|
this.chrome.render(this.scene, this.coords, this.theme, this.axisSurface());
|
|
33040
33620
|
this.trackMarkPopover();
|
|
33041
33621
|
}
|
|
33042
|
-
this.
|
|
33622
|
+
this.renderCrosshairLayer();
|
|
33043
33623
|
if (!repaintsData(level) && this.paintedData) this.repaintCursorLayers();
|
|
33044
33624
|
this.updateLegendValues();
|
|
33045
33625
|
}
|
|
33626
|
+
/** Repaint the L2 crosshair canvas and keep the `.vela-shade-right` element on the veil it painted. */
|
|
33627
|
+
renderCrosshairLayer() {
|
|
33628
|
+
this.crosshairLayer.render(this.scene, this.coords, this.theme, this.hoverSeparatorY, this.externalCrossPx());
|
|
33629
|
+
this.syncShadeRight();
|
|
33630
|
+
}
|
|
33631
|
+
/** Position (or hide) the `.vela-shade-right` element over the veil of the last crosshair frame. */
|
|
33632
|
+
syncShadeRight() {
|
|
33633
|
+
const el = this.shadeRightEl;
|
|
33634
|
+
if (!el) return;
|
|
33635
|
+
const area = this.crosshairLayer.shadeRightArea;
|
|
33636
|
+
const sig = area ? `${area.x},${area.width},${area.height}` : "";
|
|
33637
|
+
if (sig === this.shadeRightSig) return;
|
|
33638
|
+
this.shadeRightSig = sig;
|
|
33639
|
+
if (!area) {
|
|
33640
|
+
el.hidden = true;
|
|
33641
|
+
el.style.display = "none";
|
|
33642
|
+
return;
|
|
33643
|
+
}
|
|
33644
|
+
Object.assign(el.style, { left: `${area.x}px`, width: `${area.width}px`, height: `${area.height}px`, display: "" });
|
|
33645
|
+
el.hidden = false;
|
|
33646
|
+
}
|
|
33046
33647
|
/** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
|
|
33047
33648
|
repaintCursorLayers() {
|
|
33048
33649
|
const pane = this.scene.panes.get(PRICE_PANE_ID);
|
|
@@ -33836,7 +34437,7 @@ var NativeRenderer = class {
|
|
|
33836
34437
|
if (this.animator.active) {
|
|
33837
34438
|
this.computeScales();
|
|
33838
34439
|
this.paintData();
|
|
33839
|
-
this.
|
|
34440
|
+
this.renderCrosshairLayer();
|
|
33840
34441
|
} else {
|
|
33841
34442
|
this.scheduler.flushNow(4 /* Full */);
|
|
33842
34443
|
}
|
|
@@ -37880,7 +38481,8 @@ var Vela = class {
|
|
|
37880
38481
|
glow: options.glow ?? 0,
|
|
37881
38482
|
upColor: options.upColor ?? BULLISH,
|
|
37882
38483
|
downColor: options.downColor ?? BEARISH,
|
|
37883
|
-
priceStyle: options.priceStyle ?? "candles"
|
|
38484
|
+
priceStyle: options.priceStyle ?? "candles",
|
|
38485
|
+
priceAxisTicks: options.priceAxis?.ticks ?? null
|
|
37884
38486
|
};
|
|
37885
38487
|
const RendererClass = options.renderer ?? NativeRenderer;
|
|
37886
38488
|
if (typeof RendererClass !== "function") {
|
|
@@ -38366,6 +38968,23 @@ function resolveEngines(overrides) {
|
|
|
38366
38968
|
return { ...Object.fromEntries(defaultEngines), ...overrides };
|
|
38367
38969
|
}
|
|
38368
38970
|
|
|
38971
|
+
// src/widget/context-menu-model.ts
|
|
38972
|
+
var CONTEXT_MENU_BUILTIN_ORDER = {
|
|
38973
|
+
body: { "reset-view": -30, "remove-drawings": -20, "remove-indicators": -10, settings: 1e3 },
|
|
38974
|
+
"price-axis": {
|
|
38975
|
+
auto: -80,
|
|
38976
|
+
invert: -70,
|
|
38977
|
+
"scale:regular": -60,
|
|
38978
|
+
"scale:percent": -50,
|
|
38979
|
+
"scale:indexed": -40,
|
|
38980
|
+
"scale:log": -30,
|
|
38981
|
+
labels: -20,
|
|
38982
|
+
levels: -10,
|
|
38983
|
+
settings: 1e3
|
|
38984
|
+
},
|
|
38985
|
+
"time-axis": { timezone: -10, settings: 1e3 }
|
|
38986
|
+
};
|
|
38987
|
+
|
|
38369
38988
|
// src/widget/side-panel.ts
|
|
38370
38989
|
var DEFAULT_PANEL_WIDTH = 280;
|
|
38371
38990
|
var DEFAULT_PANEL_MIN_WIDTH = 200;
|
|
@@ -38384,6 +39003,7 @@ exports.BULLISH = BULLISH;
|
|
|
38384
39003
|
exports.BarStore = BarStore;
|
|
38385
39004
|
exports.CATEGORICAL = CATEGORICAL;
|
|
38386
39005
|
exports.CHIP_PLATE = CHIP_PLATE;
|
|
39006
|
+
exports.CONTEXT_MENU_BUILTIN_ORDER = CONTEXT_MENU_BUILTIN_ORDER;
|
|
38387
39007
|
exports.CROSSHAIR = CROSSHAIR;
|
|
38388
39008
|
exports.CachingDataFeed = CachingDataFeed;
|
|
38389
39009
|
exports.DARK_THEME = DARK_THEME;
|