@luxalgo/vela 0.7.6 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZADTVRUO.js → chunk-PVGWNF7V.js} +1 -1
- package/dist/{chunk-JSVCWYBF.js → chunk-RVQWJOEE.js} +232 -112
- package/dist/{chunk-BKHSQ4YM.js → chunk-WTWGEB7K.js} +23 -9
- package/dist/{chunk-EMB53WNQ.js → chunk-ZA2XWTGH.js} +84 -31
- package/dist/{contributions-D6p1RB38.d.cts → contributions-DF_Ea07m.d.cts} +17 -0
- package/dist/{contributions-C7Wsm_S9.d.ts → contributions-nKOSeLy8.d.ts} +17 -0
- package/dist/index.cjs +240 -119
- package/dist/index.d.cts +19 -4
- package/dist/index.d.ts +19 -4
- package/dist/index.js +2 -2
- package/dist/{plugin-SodaQhjQ.d.ts → plugin-0oFDnexb.d.ts} +1 -1
- package/dist/{plugin-CxWfg6vK.d.cts → plugin-DPyVJFoA.d.cts} +1 -1
- package/dist/plugin.d.cts +2 -2
- package/dist/plugin.d.ts +2 -2
- package/dist/{statusline-model-gWHrsOy3.d.cts → statusline-model-BoWJ3cWQ.d.cts} +16 -3
- package/dist/{statusline-model-xBiVsKoE.d.ts → statusline-model-DAFyqVmR.d.ts} +16 -3
- package/dist/ui.cjs +23 -9
- package/dist/ui.d.cts +19 -2
- package/dist/ui.d.ts +19 -2
- package/dist/ui.js +2 -2
- package/dist/vela.global.js +240 -119
- package/dist/vela.global.min.js +61 -51
- package/dist/widget.cjs +334 -147
- package/dist/widget.d.cts +5 -5
- package/dist/widget.d.ts +5 -5
- package/dist/widget.js +5 -5
- package/dist/workspace.cjs +334 -147
- package/dist/workspace.d.cts +33 -6
- package/dist/workspace.d.ts +33 -6
- package/dist/workspace.js +4 -4
- package/package.json +21 -1
package/dist/widget.cjs
CHANGED
|
@@ -3521,7 +3521,8 @@ function addRecent(hex6) {
|
|
|
3521
3521
|
recents.unshift(hex6);
|
|
3522
3522
|
if (recents.length > 10) recents.length = 10;
|
|
3523
3523
|
}
|
|
3524
|
-
function buildColorPicker(color, theme, onChange) {
|
|
3524
|
+
function buildColorPicker(color, theme, onChange, options = {}) {
|
|
3525
|
+
const commit = options.commit ?? "live";
|
|
3525
3526
|
const parsed = splitColor(color);
|
|
3526
3527
|
let curHex = parsed.hex6;
|
|
3527
3528
|
let curAlpha = parsed.alpha;
|
|
@@ -3604,7 +3605,12 @@ function buildColorPicker(color, theme, onChange) {
|
|
|
3604
3605
|
const r = track.getBoundingClientRect();
|
|
3605
3606
|
curAlpha = Math.max(0, Math.min(1, (clientX - r.left) / r.width));
|
|
3606
3607
|
paintOpacity();
|
|
3607
|
-
emit();
|
|
3608
|
+
if (commit === "live") emit();
|
|
3609
|
+
};
|
|
3610
|
+
const endDrag = () => {
|
|
3611
|
+
if (!dragging) return;
|
|
3612
|
+
dragging = false;
|
|
3613
|
+
if (commit === "release") emit();
|
|
3608
3614
|
};
|
|
3609
3615
|
track.addEventListener("pointerdown", (e) => {
|
|
3610
3616
|
e.stopPropagation();
|
|
@@ -3615,7 +3621,8 @@ function buildColorPicker(color, theme, onChange) {
|
|
|
3615
3621
|
track.addEventListener("pointermove", (e) => {
|
|
3616
3622
|
if (dragging) onDrag(e.clientX);
|
|
3617
3623
|
});
|
|
3618
|
-
track.addEventListener("pointerup",
|
|
3624
|
+
track.addEventListener("pointerup", endDrag);
|
|
3625
|
+
track.addEventListener("pointercancel", endDrag);
|
|
3619
3626
|
function emit() {
|
|
3620
3627
|
onChange(combineColor(curHex, curAlpha));
|
|
3621
3628
|
}
|
|
@@ -3638,7 +3645,7 @@ function buildColorPicker(color, theme, onChange) {
|
|
|
3638
3645
|
return root;
|
|
3639
3646
|
}
|
|
3640
3647
|
function colorField(theme, getVal, onVal, opts) {
|
|
3641
|
-
return new ColorField({ theme, getVal, onVal, shape: opts?.shape, id: opts?.id, popover: opts?.popover }).el;
|
|
3648
|
+
return new ColorField({ theme, getVal, onVal, shape: opts?.shape, id: opts?.id, popover: opts?.popover, commit: opts?.commit }).el;
|
|
3642
3649
|
}
|
|
3643
3650
|
var ColorField = class {
|
|
3644
3651
|
constructor(opts) {
|
|
@@ -3647,6 +3654,7 @@ var ColorField = class {
|
|
|
3647
3654
|
this.getVal = opts.getVal;
|
|
3648
3655
|
this.onVal = opts.onVal;
|
|
3649
3656
|
this.popoverOpts = opts.popover;
|
|
3657
|
+
this.commit = opts.commit;
|
|
3650
3658
|
const trigger = document.createElement("button");
|
|
3651
3659
|
trigger.type = "button";
|
|
3652
3660
|
if (opts.id) trigger.id = opts.id;
|
|
@@ -3686,10 +3694,15 @@ var ColorField = class {
|
|
|
3686
3694
|
boundary: this.popoverOpts?.boundary,
|
|
3687
3695
|
zIndex: this.popoverOpts?.zIndex,
|
|
3688
3696
|
className: "vela-color-field-pop",
|
|
3689
|
-
content: buildColorPicker(
|
|
3690
|
-
this.
|
|
3691
|
-
this.
|
|
3692
|
-
|
|
3697
|
+
content: buildColorPicker(
|
|
3698
|
+
this.getVal(),
|
|
3699
|
+
this.theme,
|
|
3700
|
+
(val) => {
|
|
3701
|
+
this.onVal(val);
|
|
3702
|
+
this.paint();
|
|
3703
|
+
},
|
|
3704
|
+
{ commit: this.commit }
|
|
3705
|
+
)
|
|
3693
3706
|
});
|
|
3694
3707
|
pop.show();
|
|
3695
3708
|
}
|
|
@@ -4277,7 +4290,8 @@ function buildFieldControl(desc) {
|
|
|
4277
4290
|
const el2 = colorField(desc.theme, desc.get, desc.onChange, {
|
|
4278
4291
|
shape: "circle",
|
|
4279
4292
|
id: desc.id,
|
|
4280
|
-
popover: desc.popover
|
|
4293
|
+
popover: desc.popover,
|
|
4294
|
+
commit: desc.commit
|
|
4281
4295
|
});
|
|
4282
4296
|
if (desc.title) el2.title = desc.title;
|
|
4283
4297
|
return { el: el2 };
|
|
@@ -5070,10 +5084,12 @@ var BASELINE_LEVEL_DEFAULT = 50;
|
|
|
5070
5084
|
var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
|
|
5071
5085
|
var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
|
|
5072
5086
|
var EXTENDED_SHADE = POSTMARKET_SHADE;
|
|
5087
|
+
var DEFAULT_MARGINS = { top: 10, bottom: 10, right: 10 };
|
|
5073
5088
|
function defaultChartStyle() {
|
|
5074
5089
|
return {
|
|
5075
5090
|
chartTypes: {},
|
|
5076
5091
|
fontSize: 11,
|
|
5092
|
+
margins: { ...DEFAULT_MARGINS },
|
|
5077
5093
|
gridVert: { visible: true, color: null },
|
|
5078
5094
|
gridHorz: { visible: true, color: null },
|
|
5079
5095
|
borderColor: null,
|
|
@@ -5197,6 +5213,12 @@ function clampLevel(v) {
|
|
|
5197
5213
|
function clampSpacing(v) {
|
|
5198
5214
|
return v < 0.1 ? 0.1 : v > 10 ? 10 : v;
|
|
5199
5215
|
}
|
|
5216
|
+
function clampMarginPct(v) {
|
|
5217
|
+
return v < 0 ? 0 : v > 40 ? 40 : v;
|
|
5218
|
+
}
|
|
5219
|
+
function clampMarginBars(v) {
|
|
5220
|
+
return Math.round(v < 0 ? 0 : v > 200 ? 200 : v);
|
|
5221
|
+
}
|
|
5200
5222
|
function factoryResetConfig(factory, priceStyle = factory.series.style) {
|
|
5201
5223
|
const bag = {};
|
|
5202
5224
|
for (const t of chartTypes()) {
|
|
@@ -5245,6 +5267,7 @@ function mergeConfig(base, patch) {
|
|
|
5245
5267
|
const ps = asObject(p.priceScale);
|
|
5246
5268
|
const anim = asObject(p.animations);
|
|
5247
5269
|
const panes = asObject(p.panes);
|
|
5270
|
+
const margins = asObject(p.margins);
|
|
5248
5271
|
const trades = asObject(p.trades);
|
|
5249
5272
|
const ts = asObject(p.timeScale);
|
|
5250
5273
|
const marks = asObject(p.marks);
|
|
@@ -5304,6 +5327,11 @@ function mergeConfig(base, patch) {
|
|
|
5304
5327
|
panes: {
|
|
5305
5328
|
separatorColor: isColor(panes.separatorColor) ? panes.separatorColor : base.panes.separatorColor
|
|
5306
5329
|
},
|
|
5330
|
+
margins: {
|
|
5331
|
+
top: isNum(margins.top) ? clampMarginPct(margins.top) : base.margins.top,
|
|
5332
|
+
bottom: isNum(margins.bottom) ? clampMarginPct(margins.bottom) : base.margins.bottom,
|
|
5333
|
+
right: isNum(margins.right) ? clampMarginBars(margins.right) : base.margins.right
|
|
5334
|
+
},
|
|
5307
5335
|
trades: {
|
|
5308
5336
|
visible: isBool(trades.visible) ? trades.visible : base.trades.visible,
|
|
5309
5337
|
labels: isBool(trades.labels) ? trades.labels : base.trades.labels,
|
|
@@ -6125,9 +6153,22 @@ var TIMEZONES = [
|
|
|
6125
6153
|
{ value: "Pacific/Apia", label: "Apia" },
|
|
6126
6154
|
{ value: "Pacific/Kiritimati", label: "Kiritimati" }
|
|
6127
6155
|
];
|
|
6156
|
+
var EXCHANGE_TIMEZONE = "exchange";
|
|
6157
|
+
function isExchangeTimezone(zone) {
|
|
6158
|
+
return zone === EXCHANGE_TIMEZONE;
|
|
6159
|
+
}
|
|
6160
|
+
function resolveTimezone(zone, exchangeZone) {
|
|
6161
|
+
if (!isExchangeTimezone(zone)) return zone;
|
|
6162
|
+
return exchangeZone && exchangeZone !== "" ? exchangeZone : "Etc/UTC";
|
|
6163
|
+
}
|
|
6128
6164
|
function normalizeTimezone(zone) {
|
|
6129
6165
|
return zone === "UTC" || zone === "Etc/UTC" || zone === "Etc/GMT" ? "Etc/UTC" : zone;
|
|
6130
6166
|
}
|
|
6167
|
+
function timezoneMenuRows(current) {
|
|
6168
|
+
const active = normalizeTimezone(current);
|
|
6169
|
+
const [utc, ...zones] = TIMEZONES.map((t) => ({ value: t.value, label: tzMenuLabel(t.value, t.label), checked: t.value === active }));
|
|
6170
|
+
return [utc, { value: EXCHANGE_TIMEZONE, label: "Exchange", checked: isExchangeTimezone(current) }, ...zones];
|
|
6171
|
+
}
|
|
6131
6172
|
function tzOffset(zone, date = /* @__PURE__ */ new Date()) {
|
|
6132
6173
|
try {
|
|
6133
6174
|
const parts = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "shortOffset" }).formatToParts(date);
|
|
@@ -6250,6 +6291,7 @@ var Bottombar = class {
|
|
|
6250
6291
|
this.sessionButtons = /* @__PURE__ */ new Map();
|
|
6251
6292
|
this.sessionEl = null;
|
|
6252
6293
|
this.timezone = opts.timezone;
|
|
6294
|
+
this.exchangeTimezone = opts.exchangeTimezone;
|
|
6253
6295
|
const doc = host.ownerDocument;
|
|
6254
6296
|
injectStyles(STYLE_ID3, CSS4, doc);
|
|
6255
6297
|
this.el = doc.createElement("div");
|
|
@@ -6273,7 +6315,7 @@ var Bottombar = class {
|
|
|
6273
6315
|
this.clockEl = doc.createElement("span");
|
|
6274
6316
|
this.clockEl.className = "vela-bb-clock";
|
|
6275
6317
|
this.tzLabelEl = doc.createElement("span");
|
|
6276
|
-
this.tzLabelEl.textContent = tzButtonLabel(this.
|
|
6318
|
+
this.tzLabelEl.textContent = tzButtonLabel(this.displayZone);
|
|
6277
6319
|
this.tzButton.append(this.clockEl, this.tzLabelEl);
|
|
6278
6320
|
const session = doc.createElement("span");
|
|
6279
6321
|
session.className = "vela-bb-session";
|
|
@@ -6308,19 +6350,32 @@ var Bottombar = class {
|
|
|
6308
6350
|
placement: "top-end",
|
|
6309
6351
|
items: this.tzItems(),
|
|
6310
6352
|
onSelect: (zone) => {
|
|
6311
|
-
this.setTimezone(zone);
|
|
6353
|
+
this.setTimezone(zone, this.exchangeTimezone);
|
|
6312
6354
|
opts.onTimezone(zone);
|
|
6313
6355
|
}
|
|
6314
6356
|
});
|
|
6315
6357
|
this.tick();
|
|
6316
6358
|
this.unsubClock = (opts.clock ?? new SecondClock()).onTick(() => this.tick());
|
|
6317
6359
|
}
|
|
6318
|
-
|
|
6360
|
+
/**
|
|
6361
|
+
* Reflect the stored choice AND the active chart's market zone. The clock and the
|
|
6362
|
+
* offset label read the RESOLVED zone, so a workspace on the exchange rule re-labels
|
|
6363
|
+
* when the active cell (or its symbol) changes market — the host re-projects on
|
|
6364
|
+
* both. Idempotent: unchanged inputs leave the menu alone.
|
|
6365
|
+
*/
|
|
6366
|
+
setTimezone(zone, exchangeTimezone) {
|
|
6367
|
+
if (zone === this.timezone && exchangeTimezone === this.exchangeTimezone) return;
|
|
6368
|
+
const choiceChanged = zone !== this.timezone;
|
|
6319
6369
|
this.timezone = zone;
|
|
6320
|
-
this.
|
|
6321
|
-
this.
|
|
6370
|
+
this.exchangeTimezone = exchangeTimezone;
|
|
6371
|
+
this.tzLabelEl.textContent = tzButtonLabel(this.displayZone);
|
|
6372
|
+
if (choiceChanged) this.tzMenu.setItems(this.tzItems());
|
|
6322
6373
|
this.tick();
|
|
6323
6374
|
}
|
|
6375
|
+
/** The zone the bar's clock and label render in. */
|
|
6376
|
+
get displayZone() {
|
|
6377
|
+
return resolveTimezone(this.timezone, this.exchangeTimezone);
|
|
6378
|
+
}
|
|
6324
6379
|
/** Highlight (or clear with null) the active range chip — cleared on manual tf changes. */
|
|
6325
6380
|
setActiveRange(id) {
|
|
6326
6381
|
for (const [key, b] of this.rangeButtons) {
|
|
@@ -6348,11 +6403,7 @@ var Bottombar = class {
|
|
|
6348
6403
|
this.el.remove();
|
|
6349
6404
|
}
|
|
6350
6405
|
tzItems() {
|
|
6351
|
-
return
|
|
6352
|
-
id: t.value,
|
|
6353
|
-
label: tzMenuLabel(t.value, t.label),
|
|
6354
|
-
checked: t.value === normalizeTimezone(this.timezone)
|
|
6355
|
-
}));
|
|
6406
|
+
return timezoneMenuRows(this.timezone).map((r) => ({ id: r.value, label: r.label, checked: r.checked }));
|
|
6356
6407
|
}
|
|
6357
6408
|
tick() {
|
|
6358
6409
|
try {
|
|
@@ -6361,7 +6412,7 @@ var Bottombar = class {
|
|
|
6361
6412
|
minute: "2-digit",
|
|
6362
6413
|
second: "2-digit",
|
|
6363
6414
|
hour12: false,
|
|
6364
|
-
timeZone: this.
|
|
6415
|
+
timeZone: this.displayZone
|
|
6365
6416
|
}).format(/* @__PURE__ */ new Date());
|
|
6366
6417
|
} catch {
|
|
6367
6418
|
this.clockEl.textContent = "";
|
|
@@ -16011,15 +16062,14 @@ var TimezoneDrawer = class {
|
|
|
16011
16062
|
this.drawer.body.replaceChildren();
|
|
16012
16063
|
const list = doc.createElement("div");
|
|
16013
16064
|
list.className = "vela-tzd-list";
|
|
16014
|
-
const
|
|
16015
|
-
for (const tz of TIMEZONES) {
|
|
16065
|
+
for (const tz of timezoneMenuRows(this.opts.timezone())) {
|
|
16016
16066
|
const row = doc.createElement("div");
|
|
16017
16067
|
row.className = "vela-tzd-row";
|
|
16018
16068
|
const label = doc.createElement("span");
|
|
16019
16069
|
label.className = "vela-tzd-row-label";
|
|
16020
|
-
label.textContent =
|
|
16070
|
+
label.textContent = tz.label;
|
|
16021
16071
|
row.appendChild(label);
|
|
16022
|
-
if (tz.
|
|
16072
|
+
if (tz.checked) row.appendChild(iconEl("check", doc));
|
|
16023
16073
|
row.addEventListener("click", () => {
|
|
16024
16074
|
this.opts.onTimezone(tz.value);
|
|
16025
16075
|
this.drawer.hide();
|
|
@@ -16106,12 +16156,11 @@ function priceAxisItems(s) {
|
|
|
16106
16156
|
];
|
|
16107
16157
|
}
|
|
16108
16158
|
function timeAxisItems(timezone) {
|
|
16109
|
-
const active = timezone === "UTC" ? "Etc/UTC" : timezone;
|
|
16110
16159
|
return [
|
|
16111
16160
|
{
|
|
16112
16161
|
id: "timezone",
|
|
16113
16162
|
label: "Time zone",
|
|
16114
|
-
submenu:
|
|
16163
|
+
submenu: timezoneMenuRows(timezone).map((r) => ({ id: `tz:${r.value}`, label: r.label, checked: r.checked }))
|
|
16115
16164
|
},
|
|
16116
16165
|
settingsItem("time-axis", "More settings\u2026")
|
|
16117
16166
|
];
|
|
@@ -21770,7 +21819,10 @@ var IndicatorInputsDialog = class {
|
|
|
21770
21819
|
id,
|
|
21771
21820
|
theme: this.host.theme(),
|
|
21772
21821
|
get: () => String(bagOf(row, inp)[inp.key] ?? inp.defval),
|
|
21773
|
-
onChange: (v) => emit(v)
|
|
21822
|
+
onChange: (v) => emit(v),
|
|
21823
|
+
// An input change re-executes the script over its whole history: commit the
|
|
21824
|
+
// opacity drag once on release, not once per pointer move.
|
|
21825
|
+
commit: "release"
|
|
21774
21826
|
}).el;
|
|
21775
21827
|
}
|
|
21776
21828
|
if (inp.type === "symbol") return this.buildSymbol(id, String(current), emit);
|
|
@@ -22017,10 +22069,6 @@ var IndicatorInputsDialog = class {
|
|
|
22017
22069
|
}
|
|
22018
22070
|
/** Open a themed month calendar under `anchor` — same surface + shadow as the choice list. */
|
|
22019
22071
|
openCalendar(anchor, current, onPick) {
|
|
22020
|
-
const parsed = parseIsoDate(current);
|
|
22021
|
-
let year = parsed?.getFullYear() ?? (/* @__PURE__ */ new Date()).getFullYear();
|
|
22022
|
-
let month = parsed?.getMonth() ?? (/* @__PURE__ */ new Date()).getMonth();
|
|
22023
|
-
const selected = parsed ? isoDate(parsed) : current;
|
|
22024
22072
|
const pop = new Popover({
|
|
22025
22073
|
trigger: anchor,
|
|
22026
22074
|
theme: this.host.theme(),
|
|
@@ -22032,80 +22080,10 @@ var IndicatorInputsDialog = class {
|
|
|
22032
22080
|
this.calendarPop = null;
|
|
22033
22081
|
this.calendarAnchor = null;
|
|
22034
22082
|
},
|
|
22035
|
-
content: (el) => {
|
|
22036
|
-
|
|
22037
|
-
|
|
22038
|
-
|
|
22039
|
-
prev2.type = "button";
|
|
22040
|
-
prev2.className = "vela-ind-cal-nav";
|
|
22041
|
-
prev2.setAttribute("aria-label", "Previous month");
|
|
22042
|
-
prev2.innerHTML = iconAt("chevron-left", 14);
|
|
22043
|
-
const next = document.createElement("button");
|
|
22044
|
-
next.type = "button";
|
|
22045
|
-
next.className = "vela-ind-cal-nav";
|
|
22046
|
-
next.setAttribute("aria-label", "Next month");
|
|
22047
|
-
next.innerHTML = iconAt("chevron-right", 14);
|
|
22048
|
-
const head = document.createElement("div");
|
|
22049
|
-
head.className = "vela-ind-cal-head";
|
|
22050
|
-
head.append(prev2, title, next);
|
|
22051
|
-
const week = document.createElement("div");
|
|
22052
|
-
week.className = "vela-ind-cal-week";
|
|
22053
|
-
for (const d of WEEKDAY_LABELS) {
|
|
22054
|
-
const cell = document.createElement("span");
|
|
22055
|
-
cell.textContent = d;
|
|
22056
|
-
week.appendChild(cell);
|
|
22057
|
-
}
|
|
22058
|
-
const grid = document.createElement("div");
|
|
22059
|
-
grid.className = "vela-ind-cal-grid";
|
|
22060
|
-
const paint = () => {
|
|
22061
|
-
title.textContent = `${MONTH_LABELS[month]} ${year}`;
|
|
22062
|
-
grid.replaceChildren();
|
|
22063
|
-
const first = new Date(year, month, 1);
|
|
22064
|
-
const startPad = first.getDay();
|
|
22065
|
-
const days = new Date(year, month + 1, 0).getDate();
|
|
22066
|
-
const today = isoDate(/* @__PURE__ */ new Date());
|
|
22067
|
-
for (let i = 0; i < startPad; i++) {
|
|
22068
|
-
const blank = document.createElement("span");
|
|
22069
|
-
blank.className = "vela-ind-cal-blank";
|
|
22070
|
-
grid.appendChild(blank);
|
|
22071
|
-
}
|
|
22072
|
-
for (let day = 1; day <= days; day++) {
|
|
22073
|
-
const iso = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
22074
|
-
const b = document.createElement("button");
|
|
22075
|
-
b.type = "button";
|
|
22076
|
-
b.className = "vela-ind-cal-day";
|
|
22077
|
-
b.textContent = String(day);
|
|
22078
|
-
if (iso === selected) b.dataset.checked = "1";
|
|
22079
|
-
if (iso === today) b.dataset.today = "1";
|
|
22080
|
-
b.addEventListener("click", (e) => {
|
|
22081
|
-
e.stopPropagation();
|
|
22082
|
-
pop.hide();
|
|
22083
|
-
onPick(iso);
|
|
22084
|
-
});
|
|
22085
|
-
grid.appendChild(b);
|
|
22086
|
-
}
|
|
22087
|
-
};
|
|
22088
|
-
prev2.addEventListener("click", (e) => {
|
|
22089
|
-
e.stopPropagation();
|
|
22090
|
-
month -= 1;
|
|
22091
|
-
if (month < 0) {
|
|
22092
|
-
month = 11;
|
|
22093
|
-
year -= 1;
|
|
22094
|
-
}
|
|
22095
|
-
paint();
|
|
22096
|
-
});
|
|
22097
|
-
next.addEventListener("click", (e) => {
|
|
22098
|
-
e.stopPropagation();
|
|
22099
|
-
month += 1;
|
|
22100
|
-
if (month > 11) {
|
|
22101
|
-
month = 0;
|
|
22102
|
-
year += 1;
|
|
22103
|
-
}
|
|
22104
|
-
paint();
|
|
22105
|
-
});
|
|
22106
|
-
paint();
|
|
22107
|
-
el.append(head, week, grid);
|
|
22108
|
-
}
|
|
22083
|
+
content: (el) => fillCalendar(el, current, (iso) => {
|
|
22084
|
+
pop.hide();
|
|
22085
|
+
onPick(iso);
|
|
22086
|
+
})
|
|
22109
22087
|
});
|
|
22110
22088
|
this.calendarPop = pop;
|
|
22111
22089
|
this.calendarAnchor = anchor;
|
|
@@ -22214,7 +22192,7 @@ function groupInputs(inputs) {
|
|
|
22214
22192
|
var CALENDAR_SVG = iconAt("calendar", 14);
|
|
22215
22193
|
var CLOCK_SVG = iconAt("clock", 14);
|
|
22216
22194
|
var DIALOG_STYLE_ID2 = "vela-ind-dialog-styles";
|
|
22217
|
-
var DIALOG_STYLE_REV = "
|
|
22195
|
+
var DIALOG_STYLE_REV = "30";
|
|
22218
22196
|
var LEGEND_ICON_PX = 16;
|
|
22219
22197
|
function ensureDialogStyles() {
|
|
22220
22198
|
if (typeof document === "undefined") return;
|
|
@@ -22230,8 +22208,12 @@ function ensureDialogStyles() {
|
|
|
22230
22208
|
.vela-ind-combo-chevron{position:absolute;right:0;top:0;bottom:0;width:26px;border:none;background:transparent;color:inherit;opacity:0.55;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;}
|
|
22231
22209
|
.vela-ind-combo-chevron:hover{opacity:0.9;}
|
|
22232
22210
|
.vela-ind-cal{background:var(--vela-bg);color:var(--vela-fg);border:none;border-radius:6px;box-shadow:var(--vela-shadow);font:14px var(--vela-font);padding:10px 12px;user-select:none;}
|
|
22211
|
+
.vela-ind-cal [hidden]{display:none !important;}
|
|
22233
22212
|
.vela-ind-cal-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px;}
|
|
22234
|
-
.vela-ind-cal-title{flex:1;
|
|
22213
|
+
.vela-ind-cal-title{flex:1;display:flex;align-items:center;justify-content:center;gap:2px;min-width:0;}
|
|
22214
|
+
.vela-ind-cal-switch{border:none;background:transparent;color:var(--vela-fg-bright);font:inherit;font-weight:600;font-size:14px;padding:2px 6px;border-radius:4px;cursor:pointer;}
|
|
22215
|
+
.vela-ind-cal-switch:not(:disabled):hover{background:var(--vela-hover);}
|
|
22216
|
+
.vela-ind-cal-switch:disabled{cursor:default;}
|
|
22235
22217
|
.vela-ind-cal-nav{width:24px;height:24px;border:none;background:transparent;color:var(--vela-fg-muted);border-radius:4px;padding:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;}
|
|
22236
22218
|
.vela-ind-cal-nav:hover{background:var(--vela-hover);color:var(--vela-fg-bright);}
|
|
22237
22219
|
.vela-ind-cal-week,.vela-ind-cal-grid{display:grid;grid-template-columns:repeat(7,28px);gap:2px;}
|
|
@@ -22242,6 +22224,12 @@ function ensureDialogStyles() {
|
|
|
22242
22224
|
.vela-ind-cal-day:hover{background:var(--vela-hover);}
|
|
22243
22225
|
.vela-ind-cal-day[data-checked]{background:var(--vela-hover-strong);color:var(--vela-fg-bright);}
|
|
22244
22226
|
.vela-ind-cal-day[data-today]:not([data-checked]){box-shadow:inset 0 0 0 1px var(--vela-border-strong);}
|
|
22227
|
+
.vela-ind-cal-cells{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;width:calc(7 * 28px + 6 * 2px);}
|
|
22228
|
+
.vela-ind-cal-cell{height:36px;border:none;background:transparent;color:inherit;border-radius:4px;padding:0 4px;cursor:pointer;font:inherit;font-size:14px;}
|
|
22229
|
+
.vela-ind-cal-cell:hover{background:var(--vela-hover);}
|
|
22230
|
+
.vela-ind-cal-cell[data-checked]{background:var(--vela-hover-strong);color:var(--vela-fg-bright);}
|
|
22231
|
+
.vela-ind-cal-cell[data-today]:not([data-checked]){box-shadow:inset 0 0 0 1px var(--vela-border-strong);}
|
|
22232
|
+
.vela-ind-cal-cell[data-outside]{opacity:0.45;}
|
|
22245
22233
|
${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
|
|
22246
22234
|
.vela-ind-tab{font-weight:600;font-size:13px;line-height:20px;transition:color var(--vela-dur-fast) ease,border-color var(--vela-dur-fast) ease;}
|
|
22247
22235
|
.vela-ind-tab:not(.vela-ind-tab-active):hover{color:var(--vela-fg-bright);}
|
|
@@ -22276,6 +22264,7 @@ var TIME_OPTIONS = Array.from({ length: 48 }, (_, i) => {
|
|
|
22276
22264
|
return { value: v, label: v };
|
|
22277
22265
|
});
|
|
22278
22266
|
var MONTH_LABELS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
22267
|
+
var MONTH_SHORT = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
22279
22268
|
var WEEKDAY_LABELS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
22280
22269
|
function normalizeDateInput(raw) {
|
|
22281
22270
|
const t = raw.trim();
|
|
@@ -22297,6 +22286,151 @@ function parseIsoDate(raw) {
|
|
|
22297
22286
|
function isoDate(d) {
|
|
22298
22287
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
22299
22288
|
}
|
|
22289
|
+
function fillCalendar(el, current, onPick) {
|
|
22290
|
+
const today = /* @__PURE__ */ new Date();
|
|
22291
|
+
const selected = parseIsoDate(current);
|
|
22292
|
+
let year = selected?.getFullYear() ?? today.getFullYear();
|
|
22293
|
+
let month = selected?.getMonth() ?? today.getMonth();
|
|
22294
|
+
let mode = "date";
|
|
22295
|
+
const prev2 = document.createElement("button");
|
|
22296
|
+
prev2.type = "button";
|
|
22297
|
+
prev2.className = "vela-ind-cal-nav";
|
|
22298
|
+
prev2.innerHTML = iconAt("chevron-left", 14);
|
|
22299
|
+
const next = document.createElement("button");
|
|
22300
|
+
next.type = "button";
|
|
22301
|
+
next.className = "vela-ind-cal-nav";
|
|
22302
|
+
next.innerHTML = iconAt("chevron-right", 14);
|
|
22303
|
+
const monthBtn = document.createElement("button");
|
|
22304
|
+
monthBtn.type = "button";
|
|
22305
|
+
monthBtn.className = "vela-ind-cal-switch";
|
|
22306
|
+
monthBtn.setAttribute("aria-label", "Choose month");
|
|
22307
|
+
const yearBtn = document.createElement("button");
|
|
22308
|
+
yearBtn.type = "button";
|
|
22309
|
+
yearBtn.className = "vela-ind-cal-switch";
|
|
22310
|
+
yearBtn.setAttribute("aria-label", "Choose year");
|
|
22311
|
+
const title = document.createElement("div");
|
|
22312
|
+
title.className = "vela-ind-cal-title";
|
|
22313
|
+
title.append(monthBtn, yearBtn);
|
|
22314
|
+
const head = document.createElement("div");
|
|
22315
|
+
head.className = "vela-ind-cal-head";
|
|
22316
|
+
head.append(prev2, title, next);
|
|
22317
|
+
const week = document.createElement("div");
|
|
22318
|
+
week.className = "vela-ind-cal-week";
|
|
22319
|
+
for (const d of WEEKDAY_LABELS) {
|
|
22320
|
+
const cell = document.createElement("span");
|
|
22321
|
+
cell.textContent = d;
|
|
22322
|
+
week.appendChild(cell);
|
|
22323
|
+
}
|
|
22324
|
+
const grid = document.createElement("div");
|
|
22325
|
+
const paint = () => {
|
|
22326
|
+
const decade = Math.floor(year / 10) * 10;
|
|
22327
|
+
monthBtn.hidden = mode !== "date";
|
|
22328
|
+
monthBtn.textContent = MONTH_LABELS[month] ?? "";
|
|
22329
|
+
yearBtn.textContent = mode === "year" ? `${decade}-${decade + 9}` : String(year);
|
|
22330
|
+
yearBtn.disabled = mode === "year";
|
|
22331
|
+
week.hidden = mode !== "date";
|
|
22332
|
+
prev2.setAttribute("aria-label", mode === "date" ? "Previous month" : mode === "month" ? "Previous year" : "Previous decade");
|
|
22333
|
+
next.setAttribute("aria-label", mode === "date" ? "Next month" : mode === "month" ? "Next year" : "Next decade");
|
|
22334
|
+
grid.className = mode === "date" ? "vela-ind-cal-grid" : "vela-ind-cal-cells";
|
|
22335
|
+
grid.replaceChildren();
|
|
22336
|
+
if (mode === "date") {
|
|
22337
|
+
const startPad = new Date(year, month, 1).getDay();
|
|
22338
|
+
const days = new Date(year, month + 1, 0).getDate();
|
|
22339
|
+
const todayIso = isoDate(today);
|
|
22340
|
+
const selectedIso = selected ? isoDate(selected) : "";
|
|
22341
|
+
for (let i = 0; i < startPad; i++) {
|
|
22342
|
+
const blank = document.createElement("span");
|
|
22343
|
+
blank.className = "vela-ind-cal-blank";
|
|
22344
|
+
grid.appendChild(blank);
|
|
22345
|
+
}
|
|
22346
|
+
for (let day = 1; day <= days; day++) {
|
|
22347
|
+
const iso = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
22348
|
+
const b = document.createElement("button");
|
|
22349
|
+
b.type = "button";
|
|
22350
|
+
b.className = "vela-ind-cal-day";
|
|
22351
|
+
b.textContent = String(day);
|
|
22352
|
+
if (iso === selectedIso) b.dataset.checked = "1";
|
|
22353
|
+
if (iso === todayIso) b.dataset.today = "1";
|
|
22354
|
+
b.addEventListener("click", (e) => {
|
|
22355
|
+
e.stopPropagation();
|
|
22356
|
+
onPick(iso);
|
|
22357
|
+
});
|
|
22358
|
+
grid.appendChild(b);
|
|
22359
|
+
}
|
|
22360
|
+
return;
|
|
22361
|
+
}
|
|
22362
|
+
if (mode === "month") {
|
|
22363
|
+
for (let m = 0; m < 12; m++) {
|
|
22364
|
+
const b = document.createElement("button");
|
|
22365
|
+
b.type = "button";
|
|
22366
|
+
b.className = "vela-ind-cal-cell";
|
|
22367
|
+
b.textContent = MONTH_SHORT[m] ?? "";
|
|
22368
|
+
if (selected?.getFullYear() === year && selected.getMonth() === m) b.dataset.checked = "1";
|
|
22369
|
+
if (today.getFullYear() === year && today.getMonth() === m) b.dataset.today = "1";
|
|
22370
|
+
b.addEventListener("click", (e) => {
|
|
22371
|
+
e.stopPropagation();
|
|
22372
|
+
month = m;
|
|
22373
|
+
mode = "date";
|
|
22374
|
+
paint();
|
|
22375
|
+
});
|
|
22376
|
+
grid.appendChild(b);
|
|
22377
|
+
}
|
|
22378
|
+
return;
|
|
22379
|
+
}
|
|
22380
|
+
for (let y = decade - 1; y <= decade + 10; y++) {
|
|
22381
|
+
const b = document.createElement("button");
|
|
22382
|
+
b.type = "button";
|
|
22383
|
+
b.className = "vela-ind-cal-cell";
|
|
22384
|
+
b.textContent = String(y);
|
|
22385
|
+
if (y < decade || y > decade + 9) b.dataset.outside = "1";
|
|
22386
|
+
if (selected?.getFullYear() === y) b.dataset.checked = "1";
|
|
22387
|
+
if (today.getFullYear() === y) b.dataset.today = "1";
|
|
22388
|
+
b.addEventListener("click", (e) => {
|
|
22389
|
+
e.stopPropagation();
|
|
22390
|
+
year = y;
|
|
22391
|
+
mode = "month";
|
|
22392
|
+
paint();
|
|
22393
|
+
});
|
|
22394
|
+
grid.appendChild(b);
|
|
22395
|
+
}
|
|
22396
|
+
};
|
|
22397
|
+
const step = (dir) => {
|
|
22398
|
+
if (mode === "date") {
|
|
22399
|
+
month += dir;
|
|
22400
|
+
if (month < 0) {
|
|
22401
|
+
month = 11;
|
|
22402
|
+
year -= 1;
|
|
22403
|
+
}
|
|
22404
|
+
if (month > 11) {
|
|
22405
|
+
month = 0;
|
|
22406
|
+
year += 1;
|
|
22407
|
+
}
|
|
22408
|
+
} else {
|
|
22409
|
+
year += mode === "month" ? dir : dir * 10;
|
|
22410
|
+
}
|
|
22411
|
+
paint();
|
|
22412
|
+
};
|
|
22413
|
+
prev2.addEventListener("click", (e) => {
|
|
22414
|
+
e.stopPropagation();
|
|
22415
|
+
step(-1);
|
|
22416
|
+
});
|
|
22417
|
+
next.addEventListener("click", (e) => {
|
|
22418
|
+
e.stopPropagation();
|
|
22419
|
+
step(1);
|
|
22420
|
+
});
|
|
22421
|
+
monthBtn.addEventListener("click", (e) => {
|
|
22422
|
+
e.stopPropagation();
|
|
22423
|
+
mode = "month";
|
|
22424
|
+
paint();
|
|
22425
|
+
});
|
|
22426
|
+
yearBtn.addEventListener("click", (e) => {
|
|
22427
|
+
e.stopPropagation();
|
|
22428
|
+
mode = "year";
|
|
22429
|
+
paint();
|
|
22430
|
+
});
|
|
22431
|
+
paint();
|
|
22432
|
+
el.append(head, week, grid);
|
|
22433
|
+
}
|
|
22300
22434
|
function normalizeTimeInput(raw) {
|
|
22301
22435
|
const t = raw.trim();
|
|
22302
22436
|
const m = /^(\d{1,2}):(\d{2})$/.exec(t) ?? /^(\d{2})(\d{2})$/.exec(t);
|
|
@@ -28784,6 +28918,10 @@ var BUILTIN_SETTINGS_IDS = [
|
|
|
28784
28918
|
"canvas.grid",
|
|
28785
28919
|
"canvas.grid.vertical",
|
|
28786
28920
|
"canvas.grid.horizontal",
|
|
28921
|
+
"canvas.margins",
|
|
28922
|
+
"canvas.margins.top",
|
|
28923
|
+
"canvas.margins.bottom",
|
|
28924
|
+
"canvas.margins.right",
|
|
28787
28925
|
"canvas.theme"
|
|
28788
28926
|
];
|
|
28789
28927
|
function settingsIdCatalog(hostSections, markGroups = []) {
|
|
@@ -29314,6 +29452,10 @@ var SettingsDialog = class {
|
|
|
29314
29452
|
body.append(sid(this.toggleRow("Horizontal", config.grid.horzLines.visible, (v) => this.emit({ grid: { horzLines: { visible: v } } }), [
|
|
29315
29453
|
this.swatch(config.grid.horzLines.color, (v) => this.emit({ grid: { horzLines: { color: v } } }))
|
|
29316
29454
|
]), "canvas.grid.horizontal"));
|
|
29455
|
+
body.append(sid(this.sectionTitle("Margins"), "canvas.margins"));
|
|
29456
|
+
body.append(sid(this.numberRow("Top", config.margins.top, 0, 40, 1, (v) => this.emit({ margins: { top: v } }), "%"), "canvas.margins.top"));
|
|
29457
|
+
body.append(sid(this.numberRow("Bottom", config.margins.bottom, 0, 40, 1, (v) => this.emit({ margins: { bottom: v } }), "%"), "canvas.margins.bottom"));
|
|
29458
|
+
body.append(sid(this.numberRow("Right", config.margins.right, 0, 200, 1, (v) => this.emit({ margins: { right: v } }), "bars"), "canvas.margins.right"));
|
|
29317
29459
|
if (this.themeControl) {
|
|
29318
29460
|
const tc = this.themeControl;
|
|
29319
29461
|
body.append(sid(this.sectionTitle("Theme"), "canvas.theme"));
|
|
@@ -29885,23 +30027,16 @@ var SettingsDialog = class {
|
|
|
29885
30027
|
this.hintTips.push(dispose);
|
|
29886
30028
|
return el;
|
|
29887
30029
|
}
|
|
29888
|
-
|
|
29889
|
-
|
|
29890
|
-
|
|
29891
|
-
|
|
29892
|
-
|
|
29893
|
-
|
|
29894
|
-
|
|
29895
|
-
|
|
29896
|
-
|
|
29897
|
-
|
|
29898
|
-
step,
|
|
29899
|
-
fill: false,
|
|
29900
|
-
commit: "live",
|
|
29901
|
-
steppers: true,
|
|
29902
|
-
onChange
|
|
29903
|
-
}).el
|
|
29904
|
-
});
|
|
30030
|
+
/** `unit` (optional) trails the input as muted text — "%", "bars". */
|
|
30031
|
+
numberRow(label, value, min, max, step, onChange, unit) {
|
|
30032
|
+
const controls = [buildFieldControl({ kind: "number", value, min, max, step, fill: false, commit: "live", steppers: true, onChange }).el];
|
|
30033
|
+
if (unit) {
|
|
30034
|
+
const u = document.createElement("span");
|
|
30035
|
+
u.textContent = unit;
|
|
30036
|
+
u.style.cssText = "color:var(--vela-fg-muted);";
|
|
30037
|
+
controls.push(u);
|
|
30038
|
+
}
|
|
30039
|
+
return this.rowWith(label, controls);
|
|
29905
30040
|
}
|
|
29906
30041
|
/** A dropdown whose option values differ from their display labels. */
|
|
29907
30042
|
selectRowLabeled(label, value, options, onChange) {
|
|
@@ -34775,9 +34910,7 @@ function createProjector(coords, paneOf, paneIdAtY, barsInRange, seriesInRange)
|
|
|
34775
34910
|
}
|
|
34776
34911
|
|
|
34777
34912
|
// src/renderers/native/core/autoscale.ts
|
|
34778
|
-
|
|
34779
|
-
var MARGIN_BOTTOM = 1 / 7;
|
|
34780
|
-
function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log = false, offsetOf = () => 0) {
|
|
34913
|
+
function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log = false, offsetOf = () => 0, margins = DEFAULT_MARGINS) {
|
|
34781
34914
|
let min = Infinity;
|
|
34782
34915
|
let max = -Infinity;
|
|
34783
34916
|
const consider = (v) => {
|
|
@@ -34812,14 +34945,17 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
34812
34945
|
const pad = Math.abs(min) * 0.1 || 1;
|
|
34813
34946
|
return { min: min - pad, max: max + pad, log: log && min - pad > 0 };
|
|
34814
34947
|
}
|
|
34948
|
+
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
34949
|
+
const above = margins.top / 100 / content;
|
|
34950
|
+
const below = margins.bottom / 100 / content;
|
|
34815
34951
|
if (log && min > 0) {
|
|
34816
34952
|
const lmin = Math.log(min);
|
|
34817
34953
|
const lmax = Math.log(max);
|
|
34818
34954
|
const lspan = lmax - lmin;
|
|
34819
|
-
return { min: Math.exp(lmin - lspan *
|
|
34955
|
+
return { min: Math.exp(lmin - lspan * below), max: Math.exp(lmax + lspan * above), log: true };
|
|
34820
34956
|
}
|
|
34821
34957
|
const span = max - min;
|
|
34822
|
-
return { min: min - span *
|
|
34958
|
+
return { min: min - span * below, max: max + span * above };
|
|
34823
34959
|
}
|
|
34824
34960
|
function considerSeries(s, i0, i1, off, consider) {
|
|
34825
34961
|
if (!seriesInScale(s)) return;
|
|
@@ -36510,6 +36646,7 @@ var NativeRenderer = class {
|
|
|
36510
36646
|
intro: this.intro.style !== false
|
|
36511
36647
|
},
|
|
36512
36648
|
panes: { separatorColor: s.separatorColor ?? t.borderColor },
|
|
36649
|
+
margins: { ...s.margins },
|
|
36513
36650
|
trades: {
|
|
36514
36651
|
visible: this.scene.tradeMarkers.visible,
|
|
36515
36652
|
labels: this.scene.tradeMarkers.labels,
|
|
@@ -36622,6 +36759,10 @@ var NativeRenderer = class {
|
|
|
36622
36759
|
this.animAutoscale.toggle(next.animations.autoscale);
|
|
36623
36760
|
this.intro = { style: next.animations.intro ? this.introOnStyle : false, duration: this.intro.duration || INTRO_DURATION_DEFAULT_MS };
|
|
36624
36761
|
s.separatorColor = keepInherit(s.separatorColor, next.panes.separatorColor, prevTheme.borderColor);
|
|
36762
|
+
if (next.margins.right !== s.margins.right && this.coords.barCount > 0) {
|
|
36763
|
+
this.applyViewport({ barSpacing: this.coords.getViewport().barSpacing, rightOffset: next.margins.right });
|
|
36764
|
+
}
|
|
36765
|
+
s.margins = { ...next.margins };
|
|
36625
36766
|
this.scene.tradeMarkers = {
|
|
36626
36767
|
visible: next.trades.visible,
|
|
36627
36768
|
labels: next.trades.labels,
|
|
@@ -36865,7 +37006,7 @@ var NativeRenderer = class {
|
|
|
36865
37006
|
/** Glide the view back to the most recent bars, keeping the current zoom (barSpacing). */
|
|
36866
37007
|
scrollToRealtime() {
|
|
36867
37008
|
if (this.coords.barCount === 0) return;
|
|
36868
|
-
this.glideRightOffset(
|
|
37009
|
+
this.glideRightOffset(this.scene.style.margins.right);
|
|
36869
37010
|
}
|
|
36870
37011
|
/** Ease rightOffset to `target` at constant zoom (see animTick's scroll glide);
|
|
36871
37012
|
* instant when the scroll glide is off. Shared by scroll-to-latest and panBy. */
|
|
@@ -38807,6 +38948,7 @@ var NativeRenderer = class {
|
|
|
38807
38948
|
const pricePane = panes.find((p) => p.kind === "price") ?? null;
|
|
38808
38949
|
this.chrome.prepare(this.scene, this.coords, this.theme);
|
|
38809
38950
|
const animating = this.animator.active;
|
|
38951
|
+
const margins = this.scene.style.margins;
|
|
38810
38952
|
for (const pane of panes) {
|
|
38811
38953
|
if (pane.manualScale) {
|
|
38812
38954
|
pane.scaleTarget = pane.manualScale;
|
|
@@ -38822,7 +38964,7 @@ var NativeRenderer = class {
|
|
|
38822
38964
|
if (or) dr = dr ? { min: Math.min(dr.min, or.min), max: Math.max(dr.max, or.max) } : or;
|
|
38823
38965
|
}
|
|
38824
38966
|
const includeCandles = pane.kind === "price" && (!this.scene.candlesHidden || this.priceLayersAnchoredToBars(masterModels) || !this.paneHasMeasurableContent(masterModels, dr));
|
|
38825
|
-
pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
38967
|
+
pane.scaleTarget = computePaneScale(masterModels, this.bars, includeCandles, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id), margins);
|
|
38826
38968
|
pane.percentBaseline = pane.kind === "price" ? this.bars[i0]?.close ?? 0 : this.firstVisibleValue(masterModels, i0);
|
|
38827
38969
|
pane.axisFormat = void 0;
|
|
38828
38970
|
pane.axisBands = void 0;
|
|
@@ -38833,7 +38975,7 @@ var NativeRenderer = class {
|
|
|
38833
38975
|
pane.axisFormat = "volume";
|
|
38834
38976
|
}
|
|
38835
38977
|
} else if (this.layerNativesOwnPane(pane, masterModels)) {
|
|
38836
|
-
pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id));
|
|
38978
|
+
pane.scaleTarget = computePaneScale([], this.bars, true, i0, i1, dr, paneLogScale(this.scene, pane), (id) => this.scene.offsetOf(id), margins);
|
|
38837
38979
|
pane.percentBaseline = this.bars[i0]?.close ?? 0;
|
|
38838
38980
|
if (masterModels.every((m) => m.paneAxis != null)) {
|
|
38839
38981
|
pane.axisFormat = "none";
|
|
@@ -38862,7 +39004,7 @@ var NativeRenderer = class {
|
|
|
38862
39004
|
continue;
|
|
38863
39005
|
}
|
|
38864
39006
|
const mdr = this.chrome.paneDrawingsRange([model], this.scene, false, vr);
|
|
38865
|
-
sl.scaleTarget = computePaneScale([model], this.bars, false, i0, i1, mdr, false, (id) => this.scene.offsetOf(id));
|
|
39007
|
+
sl.scaleTarget = computePaneScale([model], this.bars, false, i0, i1, mdr, false, (id) => this.scene.offsetOf(id), margins);
|
|
38866
39008
|
if (!animating || !sl.initialized) {
|
|
38867
39009
|
sl.scale = { ...sl.scaleTarget };
|
|
38868
39010
|
sl.initialized = true;
|
|
@@ -38903,13 +39045,13 @@ var NativeRenderer = class {
|
|
|
38903
39045
|
for (const pane of this.scene.panes.values()) pane.manualScale = null;
|
|
38904
39046
|
for (const sl of this.scene.indicatorScales.values()) sl.manualScale = null;
|
|
38905
39047
|
const visibleBars = Math.min(n, 200);
|
|
38906
|
-
const rightOffset =
|
|
39048
|
+
const rightOffset = this.scene.style.margins.right;
|
|
38907
39049
|
const v = this.clampViewport(w / ((visibleBars + rightOffset) * this.coords.spacingScale), rightOffset);
|
|
38908
39050
|
this.coords.setViewport(v);
|
|
38909
39051
|
this.targetBarSpacing = v.barSpacing;
|
|
38910
39052
|
}
|
|
38911
39053
|
/** Re-frame after a series replacement (a symbol/timeframe switch): keep the user's
|
|
38912
|
-
* zoom (bar spacing), re-anchor the newest bars at the
|
|
39054
|
+
* zoom (bar spacing), re-anchor the newest bars at the configured right margin.
|
|
38913
39055
|
* `clampViewport`'s fit-all-bars floor deliberately does NOT apply — a progressive
|
|
38914
39056
|
* head may still be backfilling toward the previous depth, and raising the spacing
|
|
38915
39057
|
* to its temporary bar count would lose the zoom this exists to keep. */
|
|
@@ -38918,7 +39060,7 @@ var NativeRenderer = class {
|
|
|
38918
39060
|
this.panVelocity = 0;
|
|
38919
39061
|
for (const pane of this.scene.panes.values()) pane.manualScale = null;
|
|
38920
39062
|
for (const sl of this.scene.indicatorScales.values()) sl.manualScale = null;
|
|
38921
|
-
const v = { barSpacing: clampBarSpacing(this.coords.getViewport().barSpacing), rightOffset:
|
|
39063
|
+
const v = { barSpacing: clampBarSpacing(this.coords.getViewport().barSpacing), rightOffset: this.scene.style.margins.right };
|
|
38922
39064
|
this.coords.setViewport(v);
|
|
38923
39065
|
this.targetBarSpacing = v.barSpacing;
|
|
38924
39066
|
}
|
|
@@ -43530,7 +43672,7 @@ var ChartContextMenu = class {
|
|
|
43530
43672
|
} else if (id.startsWith("tz:")) {
|
|
43531
43673
|
const zone = id.slice("tz:".length);
|
|
43532
43674
|
if (this.cbs.setTimezone) this.cbs.setTimezone(zone);
|
|
43533
|
-
else chart.renderer.set("timezone", zone);
|
|
43675
|
+
else chart.renderer.set("timezone", resolveTimezone(zone, void 0));
|
|
43534
43676
|
} else if (id === "auto") {
|
|
43535
43677
|
chart.renderer.set("autoScale", chart.renderer.get("autoScale") === false);
|
|
43536
43678
|
} else if (id === "invert") {
|
|
@@ -43763,7 +43905,7 @@ var ChartCell = class {
|
|
|
43763
43905
|
this.history.onChart(this.inner);
|
|
43764
43906
|
this.inner.renderer.onConfigChanged(() => {
|
|
43765
43907
|
const zone = this.inner?.renderer.get("timezone");
|
|
43766
|
-
if (typeof zone === "string" && normalizeTimezone(zone) !== normalizeTimezone(this.
|
|
43908
|
+
if (typeof zone === "string" && normalizeTimezone(zone) !== normalizeTimezone(this.displayTimezone)) {
|
|
43767
43909
|
this.deps.setTimezone(normalizeTimezone(zone));
|
|
43768
43910
|
}
|
|
43769
43911
|
const style = this.priceStyle;
|
|
@@ -43805,8 +43947,7 @@ var ChartCell = class {
|
|
|
43805
43947
|
this.refreshSessionShading();
|
|
43806
43948
|
});
|
|
43807
43949
|
this.inner.on("viewport:changed", (range) => this.sessionShading.updateRange(range));
|
|
43808
|
-
|
|
43809
|
-
if (tz !== "Etc/UTC") this.inner.renderer.set("timezone", tz);
|
|
43950
|
+
this.applyTimezone();
|
|
43810
43951
|
this.indicatorTitlesOn = seed.indicatorTitles ?? true;
|
|
43811
43952
|
if (!this.indicatorTitlesOn) this.inner.renderer.set("indicatorTitles", false);
|
|
43812
43953
|
this.indicatorValuesOn = seed.indicatorValues ?? true;
|
|
@@ -43828,7 +43969,7 @@ var ChartCell = class {
|
|
|
43828
43969
|
this.statusline?.setSymbol(this.state.symbol);
|
|
43829
43970
|
this.statusline?.setMeta(this.state.timeframe ?? "60", this.inner.data.displayPrefix(this.state.symbol) ?? this.state.provider ?? "");
|
|
43830
43971
|
}
|
|
43831
|
-
this.
|
|
43972
|
+
this.refreshSymbolMetadata();
|
|
43832
43973
|
if (this.inner && this.state.symbol) this.marketStatus?.track(this.inner.data, this.state.symbol);
|
|
43833
43974
|
});
|
|
43834
43975
|
this.syncStatuslineColors();
|
|
@@ -43902,7 +44043,7 @@ var ChartCell = class {
|
|
|
43902
44043
|
this.offMarket = this.inner.on("market:changed", ({ symbol: symbol2, timeframe }) => {
|
|
43903
44044
|
this.projectMarket(symbol2, timeframe);
|
|
43904
44045
|
this.refreshNativeCatalog();
|
|
43905
|
-
this.
|
|
44046
|
+
this.refreshSymbolMetadata();
|
|
43906
44047
|
if (this.inner) this.marketStatus?.track(this.inner.data, symbol2);
|
|
43907
44048
|
this.deps.onMarketChanged(this.id);
|
|
43908
44049
|
});
|
|
@@ -43943,7 +44084,35 @@ var ChartCell = class {
|
|
|
43943
44084
|
this.deps.onStateDirty();
|
|
43944
44085
|
void this.inner?.setMarket({ session });
|
|
43945
44086
|
}
|
|
43946
|
-
|
|
44087
|
+
/**
|
|
44088
|
+
* The symbol's own trading zone, once its metadata has landed — what the exchange
|
|
44089
|
+
* rule resolves to on this cell (the workspace labels the shared bottom bar with the
|
|
44090
|
+
* ACTIVE cell's). Undefined = unknown or undeclared (the rule then renders UTC).
|
|
44091
|
+
*/
|
|
44092
|
+
get exchangeTimezone() {
|
|
44093
|
+
return this.exchangeZone;
|
|
44094
|
+
}
|
|
44095
|
+
/** The IANA zone this cell's axis renders in: the workspace choice, resolved. */
|
|
44096
|
+
get displayTimezone() {
|
|
44097
|
+
return resolveTimezone(this.deps.timezone(), this.exchangeZone);
|
|
44098
|
+
}
|
|
44099
|
+
/**
|
|
44100
|
+
* Push the resolved zone to the renderer — on the workspace choice changing, and on
|
|
44101
|
+
* this cell's market zone changing under the exchange rule. Skips the write when the
|
|
44102
|
+
* renderer already holds it (its config default is the bare `'UTC'` alias).
|
|
44103
|
+
*/
|
|
44104
|
+
applyTimezone() {
|
|
44105
|
+
const chart = this.inner;
|
|
44106
|
+
if (!chart) return;
|
|
44107
|
+
const zone = this.displayTimezone;
|
|
44108
|
+
const current = chart.renderer.get("timezone");
|
|
44109
|
+
const held = typeof current === "string" && current ? current : "UTC";
|
|
44110
|
+
if (normalizeTimezone(held) === normalizeTimezone(zone)) return;
|
|
44111
|
+
chart.renderer.set("timezone", zone);
|
|
44112
|
+
}
|
|
44113
|
+
/** Re-read the symbol's metadata: session posture (RTH/ETH toggle, shading) and its
|
|
44114
|
+
* trading zone (the exchange rule). Async — the workspace re-projects when a verdict lands. */
|
|
44115
|
+
refreshSymbolMetadata() {
|
|
43947
44116
|
const chart = this.inner;
|
|
43948
44117
|
const symbol = this.state.symbol;
|
|
43949
44118
|
if (!chart || !symbol) return;
|
|
@@ -43951,7 +44120,13 @@ var ChartCell = class {
|
|
|
43951
44120
|
if (this.inner !== chart) return;
|
|
43952
44121
|
const available = typeof si?.session === "string" && si.session !== "" && si.session !== "24x7";
|
|
43953
44122
|
const overnight = parseSessionSpec(si)?.overnight === true;
|
|
43954
|
-
|
|
44123
|
+
const zone = typeof si?.timezone === "string" && si.timezone !== "" ? si.timezone : void 0;
|
|
44124
|
+
const zoneChanged = zone !== this.exchangeZone;
|
|
44125
|
+
if (zoneChanged) {
|
|
44126
|
+
this.exchangeZone = zone;
|
|
44127
|
+
this.applyTimezone();
|
|
44128
|
+
}
|
|
44129
|
+
if (available !== this.sessionAvailableFlag || overnight !== this.sessionOvernightFlag || zoneChanged) {
|
|
43955
44130
|
this.sessionAvailableFlag = available;
|
|
43956
44131
|
this.sessionOvernightFlag = overnight;
|
|
43957
44132
|
this.deps.onMarketChanged(this.id);
|
|
@@ -45721,13 +45896,23 @@ var VelaWorkspace = class {
|
|
|
45721
45896
|
}
|
|
45722
45897
|
}
|
|
45723
45898
|
}
|
|
45724
|
-
/**
|
|
45899
|
+
/**
|
|
45900
|
+
* Set the workspace-global display timezone — applied to EVERY cell. An IANA zone,
|
|
45901
|
+
* or `'exchange'` (the exchange rule): each cell then renders in its OWN market's
|
|
45902
|
+
* zone (Chicago for a CME future, New York for a US equity, UTC for crypto), and the
|
|
45903
|
+
* bottom bar follows the active cell's.
|
|
45904
|
+
*/
|
|
45725
45905
|
setTimezone(zone) {
|
|
45726
45906
|
this.timezone = zone;
|
|
45727
|
-
this.
|
|
45728
|
-
for (const cell of this.cellsById.values()) cell.
|
|
45907
|
+
this.projectTimezone();
|
|
45908
|
+
for (const cell of this.cellsById.values()) cell.applyTimezone();
|
|
45729
45909
|
this.markStateDirty();
|
|
45730
45910
|
}
|
|
45911
|
+
/** Bottom bar ⇐ the stored choice + the ACTIVE cell's market zone (labels the exchange row). */
|
|
45912
|
+
projectTimezone() {
|
|
45913
|
+
const active = this.activeId ? this.cellsById.get(this.activeId) : void 0;
|
|
45914
|
+
this.bottombar?.setTimezone(this.timezone, active?.exchangeTimezone);
|
|
45915
|
+
}
|
|
45731
45916
|
/**
|
|
45732
45917
|
* Swap the workspace theme at runtime — `'dark'`, `'light'`, or a full custom theme,
|
|
45733
45918
|
* applied to the shared chrome (topbar, panels, drawing toolbar) and EVERY cell.
|
|
@@ -45932,6 +46117,7 @@ var VelaWorkspace = class {
|
|
|
45932
46117
|
this.dock.onChart(cell.chart);
|
|
45933
46118
|
this.bottombar?.setActiveRange(cell.activeRangeId);
|
|
45934
46119
|
this.bottombar?.setSession({ session: cell.session, enabled: cell.sessionAvailable });
|
|
46120
|
+
this.projectTimezone();
|
|
45935
46121
|
this.indicatorPicker?.sync();
|
|
45936
46122
|
this.glider.stop();
|
|
45937
46123
|
const d = cell.chart.drawings;
|
|
@@ -46516,6 +46702,7 @@ var VelaWorkspace = class {
|
|
|
46516
46702
|
this.mobileBar?.setTimeframe(cell.timeframe);
|
|
46517
46703
|
this.objectTree.setSymbol(cell.symbol);
|
|
46518
46704
|
this.bottombar?.setSession({ session: cell.session, enabled: cell.sessionAvailable });
|
|
46705
|
+
this.projectTimezone();
|
|
46519
46706
|
this.bottombar?.setActiveRange(cell.activeRangeId);
|
|
46520
46707
|
}
|
|
46521
46708
|
/** Trigger ② — a cell's price style changed: the topbar button/menu only if active.
|