@luxalgo/vela 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DataProvider-DHX4x6-r.d.ts → DataProvider-BmHkcwvJ.d.ts} +1 -1
- package/dist/{DataProvider-CNmk84SH.d.cts → DataProvider-DrN7ZIou.d.cts} +1 -1
- package/dist/{chunk-TP56QRMK.js → chunk-2R7BANEM.js} +1 -1
- package/dist/{chunk-U5KCQHAC.js → chunk-3QX6QOCX.js} +80 -12
- package/dist/{chunk-7D6YIF34.js → chunk-6FA4TDUF.js} +325 -67
- package/dist/{chunk-7Y7CJ7EN.js → chunk-BEJ57HP4.js} +8 -1
- package/dist/{chunk-QHZ7IXFL.js → chunk-OICPLNU7.js} +59 -9
- package/dist/{contributions-DDpv7hEL.d.cts → contributions-2bEmCF9S.d.cts} +2 -2
- package/dist/{contributions-ciV6Neyd.d.ts → contributions-DatoAzMx.d.ts} +2 -2
- package/dist/{history-CFqZm5re.d.ts → history-BmyjaVir.d.cts} +6 -6
- package/dist/{history-ChoT34nz.d.cts → history-CvOtsjT5.d.ts} +6 -6
- package/dist/index.cjs +394 -78
- package/dist/index.d.cts +36 -9
- package/dist/index.d.ts +36 -9
- package/dist/index.js +4 -4
- package/dist/{options-BqGeFHtp.d.ts → options-YdiaaVjt.d.cts} +20 -1
- package/dist/{options-BqGeFHtp.d.cts → options-YdiaaVjt.d.ts} +20 -1
- package/dist/{plugin-C97gUCVf.d.ts → plugin-BeCMF1VQ.d.ts} +3 -3
- package/dist/{plugin-CSb_sTiN.d.cts → plugin-CkiStRaI.d.cts} +3 -3
- package/dist/plugin.cjs +8 -0
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/ui.cjs +59 -9
- package/dist/ui.d.cts +10 -2
- package/dist/ui.d.ts +10 -2
- package/dist/ui.js +2 -2
- package/dist/vela.global.js +394 -78
- package/dist/vela.global.min.js +84 -47
- package/dist/widget.cjs +646 -191
- package/dist/widget.d.cts +29 -8
- package/dist/widget.d.ts +29 -8
- package/dist/widget.js +145 -73
- package/dist/workspace.cjs +601 -142
- package/dist/workspace.d.cts +29 -5
- package/dist/workspace.d.ts +29 -5
- package/dist/workspace.js +143 -67
- package/package.json +1 -1
- /package/dist/{chunk-Q3XQHLIH.js → chunk-AOGZBKUE.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS } from './chunk-
|
|
2
|
-
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-
|
|
3
|
-
import { icon, iconAt, withAlpha, NEUTRAL, BEARISH, BULLISH, isDarkColor,
|
|
1
|
+
import { deserializeDrawing, chartTypes, rendererLayers, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, lineSegmentIntersection, DEFAULT_DRAWING_COLOR, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS } from './chunk-BEJ57HP4.js';
|
|
2
|
+
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, blendOver, splitColor, Menu, TextArea, Popover, NumberInput, buildColorPicker, isPopoverOpen, eventDismissedPopover, toggleSelectList, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-OICPLNU7.js';
|
|
3
|
+
import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, BEARISH, BULLISH, isDarkColor, SERIES_LINE, SLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-PN5KWFZ4.js';
|
|
4
4
|
|
|
5
5
|
// src/core/events/EventBus.ts
|
|
6
6
|
var TypedEventBus = class {
|
|
@@ -2184,7 +2184,7 @@ function attachChromeTooltip(anchor, opts) {
|
|
|
2184
2184
|
} else {
|
|
2185
2185
|
const left = Math.min(a.left - h.left, Math.max(0, h.width - tip.offsetWidth - 4));
|
|
2186
2186
|
tip.style.left = `${Math.max(0, left)}px`;
|
|
2187
|
-
tip.style.top = `${a.bottom - h.top + 6}px`;
|
|
2187
|
+
tip.style.top = opts.placement === "above" ? `${Math.max(0, a.top - h.top - tip.offsetHeight - 6)}px` : `${a.bottom - h.top + 6}px`;
|
|
2188
2188
|
}
|
|
2189
2189
|
};
|
|
2190
2190
|
const arm = (e) => {
|
|
@@ -2844,6 +2844,8 @@ var IndicatorInputsDialog = class {
|
|
|
2844
2844
|
this.row = null;
|
|
2845
2845
|
this.snapshot = null;
|
|
2846
2846
|
this.dialogTips = [];
|
|
2847
|
+
/** Re-applies every `when` gate against current values; set while the dialog is open. */
|
|
2848
|
+
this.refreshVisibility = null;
|
|
2847
2849
|
this.choicePop = null;
|
|
2848
2850
|
this.calendarPop = null;
|
|
2849
2851
|
this.calendarAnchor = null;
|
|
@@ -2906,14 +2908,17 @@ var IndicatorInputsDialog = class {
|
|
|
2906
2908
|
tabs.style.cssText = `display:flex;gap:12px;padding:0 20px;border-bottom:1px solid ${border};flex:0 0 auto;`;
|
|
2907
2909
|
const tabEls = [];
|
|
2908
2910
|
const bodies = [];
|
|
2911
|
+
const gates = [];
|
|
2912
|
+
let active = 0;
|
|
2909
2913
|
const activate = (idx) => {
|
|
2914
|
+
active = idx;
|
|
2910
2915
|
for (let i = 0; i < tabEls.length; i += 1) {
|
|
2911
|
-
const
|
|
2916
|
+
const on = i === idx;
|
|
2912
2917
|
const el = tabEls[i];
|
|
2913
|
-
el.style.borderBottomColor =
|
|
2914
|
-
el.style.color =
|
|
2915
|
-
el.classList.toggle("vela-ind-tab-active",
|
|
2916
|
-
bodies[i].style.display =
|
|
2918
|
+
el.style.borderBottomColor = on ? fg : "transparent";
|
|
2919
|
+
el.style.color = on ? "inherit" : "var(--vela-fg-muted)";
|
|
2920
|
+
el.classList.toggle("vela-ind-tab-active", on);
|
|
2921
|
+
bodies[i].style.display = on ? "grid" : "none";
|
|
2917
2922
|
}
|
|
2918
2923
|
};
|
|
2919
2924
|
for (const [idx, def] of tabDefs.entries()) {
|
|
@@ -2924,11 +2929,24 @@ var IndicatorInputsDialog = class {
|
|
|
2924
2929
|
tab.addEventListener("click", () => activate(idx));
|
|
2925
2930
|
tabs.appendChild(tab);
|
|
2926
2931
|
tabEls.push(tab);
|
|
2927
|
-
bodies.push(this.buildTabBody(def.inputs, row));
|
|
2932
|
+
bodies.push(this.buildTabBody(def.inputs, row, gates));
|
|
2933
|
+
if (def.inputs.every((d) => d.when)) {
|
|
2934
|
+
gates.push({ el: tab, visible: (bag) => def.inputs.some((d) => inputVisible(d.when, bag)) });
|
|
2935
|
+
}
|
|
2928
2936
|
}
|
|
2929
2937
|
ui.body.appendChild(tabs);
|
|
2930
2938
|
for (const b of bodies) ui.body.appendChild(b);
|
|
2931
2939
|
activate(0);
|
|
2940
|
+
const refresh = () => {
|
|
2941
|
+
const bag = valuesBag(row);
|
|
2942
|
+
for (const g of gates) g.el.style.display = g.visible(bag) ? "" : "none";
|
|
2943
|
+
if (tabEls[active]?.style.display === "none") {
|
|
2944
|
+
const next = tabEls.findIndex((el) => el.style.display !== "none");
|
|
2945
|
+
if (next >= 0) activate(next);
|
|
2946
|
+
}
|
|
2947
|
+
};
|
|
2948
|
+
this.refreshVisibility = refresh;
|
|
2949
|
+
refresh();
|
|
2932
2950
|
const onBackdropDown = (e) => {
|
|
2933
2951
|
if (e.target !== ui.backdrop && e.target !== ui.positioner) return;
|
|
2934
2952
|
if (isPopoverOpen() || eventDismissedPopover(e)) {
|
|
@@ -2948,16 +2966,27 @@ var IndicatorInputsDialog = class {
|
|
|
2948
2966
|
}
|
|
2949
2967
|
/** One tab's scrollable body — one shared grid so every group's controls share a
|
|
2950
2968
|
* left edge (a 100px number field lines up with a wider session/time pair). */
|
|
2951
|
-
buildTabBody(inputs, row) {
|
|
2969
|
+
buildTabBody(inputs, row, gates) {
|
|
2952
2970
|
const body = document.createElement("div");
|
|
2953
2971
|
body.style.cssText = inputDialogBodyStyle(this.host.mobile());
|
|
2954
2972
|
for (const group of groupInputs(inputs)) {
|
|
2955
2973
|
const section = document.createElement("div");
|
|
2956
2974
|
section.style.cssText = "display:contents;";
|
|
2975
|
+
let header = null;
|
|
2957
2976
|
if (group.name) {
|
|
2958
|
-
|
|
2977
|
+
header = fieldSection(group.name, { variant: "inputs", first: body.childElementCount === 0 });
|
|
2978
|
+
section.appendChild(header);
|
|
2979
|
+
}
|
|
2980
|
+
for (const inputRow of group.rows) {
|
|
2981
|
+
const el = this.buildInputRow(section, inputRow, row);
|
|
2982
|
+
if (inputRow.some((d) => d.when)) {
|
|
2983
|
+
gates.push({ el, visible: (bag) => rowVisible(inputRow, bag) });
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
if (header && group.rows.every((r) => r.every((d) => d.when))) {
|
|
2987
|
+
const rows = group.rows;
|
|
2988
|
+
gates.push({ el: header, visible: (bag) => rows.some((r) => rowVisible(r, bag)) });
|
|
2959
2989
|
}
|
|
2960
|
-
for (const inputRow of group.rows) this.buildInputRow(section, inputRow, row);
|
|
2961
2990
|
body.appendChild(section);
|
|
2962
2991
|
}
|
|
2963
2992
|
return body;
|
|
@@ -2965,6 +2994,7 @@ var IndicatorInputsDialog = class {
|
|
|
2965
2994
|
close() {
|
|
2966
2995
|
document.removeEventListener("keydown", this.onDialogKey);
|
|
2967
2996
|
closeOpenPopovers();
|
|
2997
|
+
this.refreshVisibility = null;
|
|
2968
2998
|
this.choicePop = null;
|
|
2969
2999
|
this.calendarPop = null;
|
|
2970
3000
|
this.calendarAnchor = null;
|
|
@@ -2994,11 +3024,21 @@ var IndicatorInputsDialog = class {
|
|
|
2994
3024
|
}
|
|
2995
3025
|
this.close();
|
|
2996
3026
|
}
|
|
3027
|
+
/** Write one edit through: store it, notify the host, and re-apply the `when` gates. */
|
|
3028
|
+
commit(row, key, value) {
|
|
3029
|
+
row.values[key] = value;
|
|
3030
|
+
this.host.onChange?.({ indicatorId: row.id, key, value });
|
|
3031
|
+
this.refreshVisibility?.();
|
|
3032
|
+
}
|
|
2997
3033
|
/** One settings row (or several `inline=` inputs) placed into a section's grid. */
|
|
2998
3034
|
buildInputRow(grid, decls, row) {
|
|
2999
3035
|
const idOf = (inp) => `vela-inp-${row.id}-${inp.key}`;
|
|
3036
|
+
const append = (el) => {
|
|
3037
|
+
grid.appendChild(el);
|
|
3038
|
+
return el;
|
|
3039
|
+
};
|
|
3000
3040
|
if (decls.length > 1) {
|
|
3001
|
-
|
|
3041
|
+
return append(fieldRow({
|
|
3002
3042
|
label: "",
|
|
3003
3043
|
inline: decls.map((d) => ({
|
|
3004
3044
|
label: nameOf(d) || void 0,
|
|
@@ -3009,14 +3049,13 @@ var IndicatorInputsDialog = class {
|
|
|
3009
3049
|
})),
|
|
3010
3050
|
info: [...decls].reverse().find((d) => d.tooltip)?.tooltip ? this.infoButton([...decls].reverse().find((d) => d.tooltip).tooltip) : void 0
|
|
3011
3051
|
}));
|
|
3012
|
-
return;
|
|
3013
3052
|
}
|
|
3014
3053
|
const lead = decls[0];
|
|
3015
3054
|
const id = idOf(lead);
|
|
3016
3055
|
const info = lead.tooltip ? this.infoButton(lead.tooltip) : void 0;
|
|
3017
3056
|
if (lead.type === "bool") {
|
|
3018
3057
|
const current = Boolean(row.values[lead.key] ?? lead.defval);
|
|
3019
|
-
|
|
3058
|
+
return append(fieldRow({
|
|
3020
3059
|
label: nameOf(lead),
|
|
3021
3060
|
id,
|
|
3022
3061
|
bool: true,
|
|
@@ -3024,26 +3063,21 @@ var IndicatorInputsDialog = class {
|
|
|
3024
3063
|
toggle: {
|
|
3025
3064
|
id,
|
|
3026
3065
|
checked: current,
|
|
3027
|
-
onChange: (v) =>
|
|
3028
|
-
row.values[lead.key] = v;
|
|
3029
|
-
this.host.onChange?.({ indicatorId: row.id, key: lead.key, value: v });
|
|
3030
|
-
}
|
|
3066
|
+
onChange: (v) => this.commit(row, lead.key, v)
|
|
3031
3067
|
}
|
|
3032
3068
|
}));
|
|
3033
|
-
return;
|
|
3034
3069
|
}
|
|
3035
3070
|
if (lead.type === "text_area") {
|
|
3036
|
-
|
|
3071
|
+
return append(fieldRow({
|
|
3037
3072
|
label: nameOf(lead),
|
|
3038
3073
|
id,
|
|
3039
3074
|
stacked: true,
|
|
3040
3075
|
info,
|
|
3041
3076
|
control: this.buildControl(row, lead, id)
|
|
3042
3077
|
}));
|
|
3043
|
-
return;
|
|
3044
3078
|
}
|
|
3045
3079
|
const fit = lead.type === "color" || lead.type === "session" || lead.type === "time";
|
|
3046
|
-
|
|
3080
|
+
return append(fieldRow({
|
|
3047
3081
|
label: nameOf(lead),
|
|
3048
3082
|
id,
|
|
3049
3083
|
fit,
|
|
@@ -3055,10 +3089,7 @@ var IndicatorInputsDialog = class {
|
|
|
3055
3089
|
/** Build the typed control for one input, committing edits live via `onChange`. */
|
|
3056
3090
|
buildControl(row, inp, id) {
|
|
3057
3091
|
const current = row.values[inp.key] ?? inp.defval;
|
|
3058
|
-
const emit = (value) =>
|
|
3059
|
-
row.values[inp.key] = value;
|
|
3060
|
-
this.host.onChange?.({ indicatorId: row.id, key: inp.key, value });
|
|
3061
|
-
};
|
|
3092
|
+
const emit = (value) => this.commit(row, inp.key, value);
|
|
3062
3093
|
if (inp.type === "bool") {
|
|
3063
3094
|
return buildFieldControl({ kind: "switch", id, checked: Boolean(current), onChange: (v) => emit(v) }).el;
|
|
3064
3095
|
}
|
|
@@ -3479,6 +3510,14 @@ function tabInputs(inputs) {
|
|
|
3479
3510
|
}
|
|
3480
3511
|
return names.map((name) => ({ name, inputs: byTab.get(name) }));
|
|
3481
3512
|
}
|
|
3513
|
+
function rowVisible(decls, bag) {
|
|
3514
|
+
return decls.some((d) => inputVisible(d.when, bag));
|
|
3515
|
+
}
|
|
3516
|
+
function valuesBag(row) {
|
|
3517
|
+
const bag = {};
|
|
3518
|
+
for (const inp of row.inputs) bag[inp.key] = row.values[inp.key] ?? inp.defval;
|
|
3519
|
+
return bag;
|
|
3520
|
+
}
|
|
3482
3521
|
function groupInputs(inputs) {
|
|
3483
3522
|
const order = [];
|
|
3484
3523
|
const byGroup = /* @__PURE__ */ new Map();
|
|
@@ -3941,6 +3980,8 @@ var InputsUI = class {
|
|
|
3941
3980
|
host: this.container,
|
|
3942
3981
|
items,
|
|
3943
3982
|
placement: "bottom-start",
|
|
3983
|
+
// Right-click action menu — checked state reads as a leading ✓ (see the kit Menu).
|
|
3984
|
+
checkmarks: true,
|
|
3944
3985
|
onSelect: (itemId) => {
|
|
3945
3986
|
if (itemId !== "values" || !this.rowMenuId) return;
|
|
3946
3987
|
const target = this.rows.get(this.rowMenuId);
|
|
@@ -4276,6 +4317,8 @@ var InputsUI = class {
|
|
|
4276
4317
|
syncRowActions(row) {
|
|
4277
4318
|
const open = row.highlighted;
|
|
4278
4319
|
row.controlsEl.style.display = open || row.hidden ? "inline-flex" : "none";
|
|
4320
|
+
if (open) row.el.appendChild(row.statusEl);
|
|
4321
|
+
else row.el.insertBefore(row.statusEl, row.valuesEl);
|
|
4279
4322
|
for (const child of Array.from(row.controlsEl.children)) {
|
|
4280
4323
|
if (!(child instanceof HTMLElement) || child === row.eyeEl) continue;
|
|
4281
4324
|
if (child === row.extrasEl) {
|
|
@@ -4519,6 +4562,148 @@ var PaneControls = class {
|
|
|
4519
4562
|
}
|
|
4520
4563
|
};
|
|
4521
4564
|
|
|
4565
|
+
// src/renderers/native/chrome/axisLayout.ts
|
|
4566
|
+
var AXIS_MASTER_W = 64;
|
|
4567
|
+
var AXIS_MERGED_W = 56;
|
|
4568
|
+
var PANE_SEPARATOR_PX = 3;
|
|
4569
|
+
function axisColumnX(dataW, column) {
|
|
4570
|
+
return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4573
|
+
// src/renderers/native/chrome/AxisScaleButtons.ts
|
|
4574
|
+
var STYLE_ID3 = "vela-axis-scale-buttons";
|
|
4575
|
+
var BTN_PX = 18;
|
|
4576
|
+
var BTN_GAP = 4;
|
|
4577
|
+
var AXIS_BORDER = 1;
|
|
4578
|
+
var CLUSTER_W = AXIS_MASTER_W - AXIS_BORDER;
|
|
4579
|
+
var PAD_TOP = 8;
|
|
4580
|
+
var PAD_BOTTOM = 6;
|
|
4581
|
+
var CLUSTER_H = BTN_PX + PAD_TOP + PAD_BOTTOM;
|
|
4582
|
+
var MIN_PANE_H = 48;
|
|
4583
|
+
function ensureStyles3() {
|
|
4584
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID3)) return;
|
|
4585
|
+
const st = document.createElement("style");
|
|
4586
|
+
st.id = STYLE_ID3;
|
|
4587
|
+
st.textContent = `
|
|
4588
|
+
.vela-axis-btn{display:inline-flex;align-items:center;justify-content:center;padding:0;border:none;border-radius:var(--vela-radius-sm);background:transparent;cursor:pointer;font:600 10px/1 -apple-system,Segoe UI,sans-serif;color:var(--vela-fg-muted);}
|
|
4589
|
+
.vela-axis-btn:hover{background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
4590
|
+
.vela-axis-on,.vela-axis-on:hover{background:var(--vela-selected-bg);color:var(--vela-selected-fg);}
|
|
4591
|
+
`;
|
|
4592
|
+
document.head.appendChild(st);
|
|
4593
|
+
}
|
|
4594
|
+
var AxisScaleButtons = class {
|
|
4595
|
+
constructor(plot, theme, deps) {
|
|
4596
|
+
this.plot = plot;
|
|
4597
|
+
this.theme = theme;
|
|
4598
|
+
this.deps = deps;
|
|
4599
|
+
this.tipDisposers = [];
|
|
4600
|
+
this.hoverPaneId = null;
|
|
4601
|
+
/** Reveal over the master scale column of the pane under the cursor (mouse only —
|
|
4602
|
+
* touch has no hover, and a drag-rescale on the axis must not sprout buttons). */
|
|
4603
|
+
this.onPlotMove = (e) => {
|
|
4604
|
+
if (e.pointerType !== "mouse") return;
|
|
4605
|
+
const rect = this.plot.getBoundingClientRect();
|
|
4606
|
+
const x = e.clientX - rect.left;
|
|
4607
|
+
const gutterLeft = rect.width - this.deps.rightAxis();
|
|
4608
|
+
let hit = null;
|
|
4609
|
+
if (x >= gutterLeft && x <= gutterLeft + AXIS_MASTER_W) {
|
|
4610
|
+
const y = e.clientY - rect.top;
|
|
4611
|
+
for (const p of this.deps.panes()) {
|
|
4612
|
+
if (p.collapsed || p.height < MIN_PANE_H) continue;
|
|
4613
|
+
if (y >= p.top && y <= p.top + p.height) {
|
|
4614
|
+
hit = p.id;
|
|
4615
|
+
break;
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
this.setHoverPane(hit);
|
|
4620
|
+
if (hit) this.sync();
|
|
4621
|
+
};
|
|
4622
|
+
this.onPlotLeave = () => this.setHoverPane(null);
|
|
4623
|
+
ensureStyles3();
|
|
4624
|
+
this.cluster = document.createElement("div");
|
|
4625
|
+
Object.assign(this.cluster.style, {
|
|
4626
|
+
position: "absolute",
|
|
4627
|
+
display: "none",
|
|
4628
|
+
justifyContent: "center",
|
|
4629
|
+
alignItems: "center",
|
|
4630
|
+
gap: `${BTN_GAP}px`,
|
|
4631
|
+
padding: `${PAD_TOP}px 0 ${PAD_BOTTOM}px`,
|
|
4632
|
+
width: `${CLUSTER_W}px`,
|
|
4633
|
+
boxSizing: "border-box",
|
|
4634
|
+
zIndex: "6",
|
|
4635
|
+
pointerEvents: "auto"
|
|
4636
|
+
});
|
|
4637
|
+
this.applyTheme();
|
|
4638
|
+
this.autoBtn = this.button("A", "Auto (fits data to screen)", () => this.deps.onToggleAuto(this.hoverPaneId ?? ""));
|
|
4639
|
+
this.logBtn = this.button("L", "Logarithmic scale", () => this.deps.onToggleLog(this.hoverPaneId ?? ""));
|
|
4640
|
+
this.cluster.append(this.autoBtn, this.logBtn);
|
|
4641
|
+
this.plot.appendChild(this.cluster);
|
|
4642
|
+
this.plot.addEventListener("pointermove", this.onPlotMove);
|
|
4643
|
+
this.plot.addEventListener("pointerleave", this.onPlotLeave);
|
|
4644
|
+
}
|
|
4645
|
+
button(label, title, onClick) {
|
|
4646
|
+
const b = document.createElement("button");
|
|
4647
|
+
b.type = "button";
|
|
4648
|
+
b.setAttribute("aria-label", title);
|
|
4649
|
+
b.textContent = label;
|
|
4650
|
+
b.className = "vela-axis-btn";
|
|
4651
|
+
Object.assign(b.style, { width: `${BTN_PX}px`, height: `${BTN_PX}px` });
|
|
4652
|
+
this.tipDisposers.push(attachChromeTooltip(b, {
|
|
4653
|
+
host: this.plot,
|
|
4654
|
+
theme: () => this.theme,
|
|
4655
|
+
text: () => title,
|
|
4656
|
+
placement: "above"
|
|
4657
|
+
}));
|
|
4658
|
+
b.addEventListener("click", (e) => {
|
|
4659
|
+
e.stopPropagation();
|
|
4660
|
+
onClick();
|
|
4661
|
+
this.sync();
|
|
4662
|
+
});
|
|
4663
|
+
return b;
|
|
4664
|
+
}
|
|
4665
|
+
/** Re-tint the chip when the plot background/border change (theme swap, config edit). */
|
|
4666
|
+
setTheme(theme) {
|
|
4667
|
+
this.theme = theme;
|
|
4668
|
+
this.applyTheme();
|
|
4669
|
+
}
|
|
4670
|
+
/** The strip is SOLID chart background: the buttons cover axis labels, never blend with them. */
|
|
4671
|
+
applyTheme() {
|
|
4672
|
+
this.cluster.style.background = this.theme.background;
|
|
4673
|
+
}
|
|
4674
|
+
setHoverPane(paneId) {
|
|
4675
|
+
if (this.hoverPaneId === paneId) return;
|
|
4676
|
+
this.hoverPaneId = paneId;
|
|
4677
|
+
this.reposition();
|
|
4678
|
+
}
|
|
4679
|
+
/** Re-place (or hide) the cluster after a hover or pane-layout change. */
|
|
4680
|
+
reposition() {
|
|
4681
|
+
const pane = this.hoverPaneId ? this.deps.panes().find((p) => p.id === this.hoverPaneId) : void 0;
|
|
4682
|
+
if (!pane || pane.collapsed || pane.height < MIN_PANE_H) {
|
|
4683
|
+
this.cluster.style.display = "none";
|
|
4684
|
+
return;
|
|
4685
|
+
}
|
|
4686
|
+
this.cluster.style.right = `${this.deps.rightAxis() - AXIS_MASTER_W}px`;
|
|
4687
|
+
this.cluster.style.top = `${pane.top + pane.height - CLUSTER_H}px`;
|
|
4688
|
+
this.cluster.style.display = "flex";
|
|
4689
|
+
this.sync();
|
|
4690
|
+
}
|
|
4691
|
+
/** Reflect the hovered pane's current auto/log state on the buttons. */
|
|
4692
|
+
sync() {
|
|
4693
|
+
const pane = this.deps.panes().find((p) => p.id === this.hoverPaneId);
|
|
4694
|
+
if (!pane) return;
|
|
4695
|
+
this.autoBtn.classList.toggle("vela-axis-on", pane.auto);
|
|
4696
|
+
this.logBtn.classList.toggle("vela-axis-on", pane.log);
|
|
4697
|
+
}
|
|
4698
|
+
destroy() {
|
|
4699
|
+
this.plot.removeEventListener("pointermove", this.onPlotMove);
|
|
4700
|
+
this.plot.removeEventListener("pointerleave", this.onPlotLeave);
|
|
4701
|
+
for (const dispose of this.tipDisposers) dispose();
|
|
4702
|
+
this.tipDisposers.length = 0;
|
|
4703
|
+
this.cluster.remove();
|
|
4704
|
+
}
|
|
4705
|
+
};
|
|
4706
|
+
|
|
4522
4707
|
// src/renderers/shared/TableOverlay.ts
|
|
4523
4708
|
var SIZE_PX = {
|
|
4524
4709
|
auto: 13,
|
|
@@ -4876,6 +5061,8 @@ var BASELINE_BOTTOM_LINE = BEARISH;
|
|
|
4876
5061
|
var BASELINE_FILL_ALPHA = 0.25;
|
|
4877
5062
|
var BASELINE_FILL_ALPHA_FAR = 0.05;
|
|
4878
5063
|
var BASELINE_LEVEL_DEFAULT = 50;
|
|
5064
|
+
var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
|
|
5065
|
+
var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
|
|
4879
5066
|
function defaultChartStyle() {
|
|
4880
5067
|
return {
|
|
4881
5068
|
chartTypes: {},
|
|
@@ -4906,7 +5093,8 @@ function defaultChartStyle() {
|
|
|
4906
5093
|
bottomFillColor2: null,
|
|
4907
5094
|
width: 2,
|
|
4908
5095
|
baselineLevel: BASELINE_LEVEL_DEFAULT
|
|
4909
|
-
}
|
|
5096
|
+
},
|
|
5097
|
+
sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE }
|
|
4910
5098
|
};
|
|
4911
5099
|
}
|
|
4912
5100
|
var LINE_STYLES = ["solid", "dashed", "dotted"];
|
|
@@ -5040,6 +5228,7 @@ function mergeConfig(base, patch) {
|
|
|
5040
5228
|
const area = asObject(p.area);
|
|
5041
5229
|
const baseline = asObject(p.baseline);
|
|
5042
5230
|
const series = asObject(p.series);
|
|
5231
|
+
const sessions = asObject(p.sessions);
|
|
5043
5232
|
const stacking = asObject(p.stacking);
|
|
5044
5233
|
const stackSeries = asObject(stacking.series);
|
|
5045
5234
|
return {
|
|
@@ -5132,6 +5321,10 @@ function mergeConfig(base, patch) {
|
|
|
5132
5321
|
baseline: series.baseline === null ? null : isNum(series.baseline) ? series.baseline : base.series.baseline,
|
|
5133
5322
|
spacing: isNum(series.spacing) ? clampSpacing(series.spacing) : base.series.spacing
|
|
5134
5323
|
},
|
|
5324
|
+
sessions: {
|
|
5325
|
+
premarketColor: isColor(sessions.premarketColor) ? sessions.premarketColor : base.sessions.premarketColor,
|
|
5326
|
+
postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor
|
|
5327
|
+
},
|
|
5135
5328
|
stacking: {
|
|
5136
5329
|
candles: isNum(stacking.candles) ? stacking.candles : base.stacking.candles,
|
|
5137
5330
|
// Additive like `chartTypes`: a patch names only the ids it carries; the rest keep
|
|
@@ -5378,6 +5571,8 @@ var SceneGraph = class {
|
|
|
5378
5571
|
this.tradeMarkers = defaultTradeMarkersState();
|
|
5379
5572
|
/** Renderer-owned shaded time bands (session highlighting), behind grid + data. */
|
|
5380
5573
|
this.highlights = [];
|
|
5574
|
+
/** Pre/post-market bands pushed by the host (`sessionZones` feature); null ⇒ no sessions. */
|
|
5575
|
+
this.sessionZones = null;
|
|
5381
5576
|
/** Draw-order key of the price candles, relative to indicator series z (see `seriesZ`).
|
|
5382
5577
|
* Indicators with z below this draw BEHIND the candles; at/above draw in front.
|
|
5383
5578
|
* Default 0 with indicators mounting at z < 0 ⇒ the price reads on top of every overlay,
|
|
@@ -5412,6 +5607,15 @@ var SceneGraph = class {
|
|
|
5412
5607
|
if (!this.orderedCache) this.orderedCache = [...this.panes.values()].sort((a, b) => a.order - b.order);
|
|
5413
5608
|
return this.orderedCache;
|
|
5414
5609
|
}
|
|
5610
|
+
/** The session zones resolved into colored bands (pre/post-market washes from the
|
|
5611
|
+
* config's session colors) — consumed by the same painting path as {@link highlights}. */
|
|
5612
|
+
sessionHighlightBands() {
|
|
5613
|
+
if (!this.sessionZones) return [];
|
|
5614
|
+
const out = [];
|
|
5615
|
+
for (const [from, to] of this.sessionZones.pre) out.push({ from, to, color: this.style.sessions.premarketColor });
|
|
5616
|
+
for (const [from, to] of this.sessionZones.post) out.push({ from, to, color: this.style.sessions.postmarketColor });
|
|
5617
|
+
return out;
|
|
5618
|
+
}
|
|
5415
5619
|
indicatorsForPane(paneId) {
|
|
5416
5620
|
const out = [];
|
|
5417
5621
|
for (const model of this.indicators.values()) if (model.paneId === paneId) out.push(model);
|
|
@@ -6342,10 +6546,12 @@ var WebGL2Backend = class {
|
|
|
6342
6546
|
}
|
|
6343
6547
|
}
|
|
6344
6548
|
}
|
|
6345
|
-
/** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
|
|
6549
|
+
/** Renderer-owned session highlight bands, clipped per-pane (scissor reconstructs full height).
|
|
6550
|
+
* Session-zone washes (pre/post-market) paint first, host highlights on top. */
|
|
6346
6551
|
emitHighlights(b, scene, pane, coords) {
|
|
6347
|
-
|
|
6348
|
-
|
|
6552
|
+
const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
|
|
6553
|
+
if (bands.length === 0) return;
|
|
6554
|
+
for (const band of bands) {
|
|
6349
6555
|
const x1 = coords.timeToX(band.from);
|
|
6350
6556
|
const x2 = coords.timeToX(band.to);
|
|
6351
6557
|
if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
|
|
@@ -8319,10 +8525,12 @@ var Canvas2dBackend = class {
|
|
|
8319
8525
|
ctx.fillStyle = bg.color;
|
|
8320
8526
|
ctx.fillRect(x1, pane.bounds.top, x2 - x1, pane.bounds.height);
|
|
8321
8527
|
}
|
|
8322
|
-
/** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
|
|
8528
|
+
/** Renderer-owned session highlight bands: full-height (all panes), behind grid + data.
|
|
8529
|
+
* Session-zone washes (pre/post-market) paint first, host highlights on top. */
|
|
8323
8530
|
drawHighlights(ctx, scene, coords) {
|
|
8324
|
-
|
|
8325
|
-
|
|
8531
|
+
const bands = [...scene.sessionHighlightBands(), ...scene.highlights];
|
|
8532
|
+
if (bands.length === 0) return;
|
|
8533
|
+
for (const band of bands) {
|
|
8326
8534
|
const x1 = coords.timeToX(band.from);
|
|
8327
8535
|
const x2 = coords.timeToX(band.to);
|
|
8328
8536
|
if (x2 < 0 || x1 > coords.width || x2 <= x1) continue;
|
|
@@ -9096,14 +9304,6 @@ var DrawingSceneRenderer = class {
|
|
|
9096
9304
|
}
|
|
9097
9305
|
};
|
|
9098
9306
|
|
|
9099
|
-
// src/renderers/native/chrome/axisLayout.ts
|
|
9100
|
-
var AXIS_MASTER_W = 64;
|
|
9101
|
-
var AXIS_MERGED_W = 56;
|
|
9102
|
-
var PANE_SEPARATOR_PX = 3;
|
|
9103
|
-
function axisColumnX(dataW, column) {
|
|
9104
|
-
return column === 0 ? dataW : dataW + AXIS_MASTER_W + (column - 1) * AXIS_MERGED_W;
|
|
9105
|
-
}
|
|
9106
|
-
|
|
9107
9307
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
9108
9308
|
var ChromeRenderer = class {
|
|
9109
9309
|
constructor() {
|
|
@@ -9909,6 +10109,7 @@ var SettingsDialog = class {
|
|
|
9909
10109
|
for (const hr of hs.rows) {
|
|
9910
10110
|
if (hr.kind === "heading") body.append(this.sectionTitle(hr.label));
|
|
9911
10111
|
else if (hr.kind === "toggle") body.append(this.boolRow(hr.label, hr.get(), (v) => hr.set(v)));
|
|
10112
|
+
else if (hr.kind === "color") body.append(this.colorRow(hr.label, hr.get(), (v) => hr.set(v)));
|
|
9912
10113
|
else body.append(this.selectRowLabeled(hr.label, hr.get(), hr.options.map((o) => [o, o]), (v) => hr.set(v)));
|
|
9913
10114
|
}
|
|
9914
10115
|
}
|
|
@@ -12620,14 +12821,14 @@ function btn(label, primary, onClick) {
|
|
|
12620
12821
|
// src/renderers/native/drawings/DrawingSettingsPopup.ts
|
|
12621
12822
|
var BTN = 30;
|
|
12622
12823
|
var ICON = 21;
|
|
12623
|
-
var
|
|
12824
|
+
var STYLE_ID4 = "vela-drawing-popup-styles";
|
|
12624
12825
|
var STYLE_REV = "3";
|
|
12625
|
-
function
|
|
12826
|
+
function ensureStyles4() {
|
|
12626
12827
|
if (typeof document === "undefined") return;
|
|
12627
|
-
const existing = document.getElementById(
|
|
12828
|
+
const existing = document.getElementById(STYLE_ID4);
|
|
12628
12829
|
if (existing?.dataset.rev === STYLE_REV) return;
|
|
12629
12830
|
const s = existing ?? document.createElement("style");
|
|
12630
|
-
s.id =
|
|
12831
|
+
s.id = STYLE_ID4;
|
|
12631
12832
|
s.dataset.rev = STYLE_REV;
|
|
12632
12833
|
s.textContent = `
|
|
12633
12834
|
.vela-dpop-btn{background:transparent;color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
|
|
@@ -12684,7 +12885,7 @@ var DrawingSettingsPopup = class {
|
|
|
12684
12885
|
* `onClose` fires on an outside-click dismissal (not a programmatic close). */
|
|
12685
12886
|
open(drawing, anchor, actions, onClose) {
|
|
12686
12887
|
this.close();
|
|
12687
|
-
|
|
12888
|
+
ensureStyles4();
|
|
12688
12889
|
this.onClose = onClose ?? null;
|
|
12689
12890
|
const t = this.theme;
|
|
12690
12891
|
const schema = drawing.schema();
|
|
@@ -14522,7 +14723,7 @@ var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path
|
|
|
14522
14723
|
|
|
14523
14724
|
// src/renderers/native/chrome/AttributionMark.ts
|
|
14524
14725
|
var ATTRIBUTION_URL = "https://luxalgo.com/vela";
|
|
14525
|
-
var
|
|
14726
|
+
var STYLE_ID5 = "vela-attribution-styles";
|
|
14526
14727
|
var CSS = `
|
|
14527
14728
|
.vela-attribution {
|
|
14528
14729
|
text-decoration: none;
|
|
@@ -14585,11 +14786,11 @@ var CSS = `
|
|
|
14585
14786
|
[data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
|
|
14586
14787
|
[data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
|
|
14587
14788
|
`;
|
|
14588
|
-
function
|
|
14589
|
-
let s = doc.getElementById(
|
|
14789
|
+
function ensureStyles5(doc) {
|
|
14790
|
+
let s = doc.getElementById(STYLE_ID5);
|
|
14590
14791
|
if (!s) {
|
|
14591
14792
|
s = doc.createElement("style");
|
|
14592
|
-
s.id =
|
|
14793
|
+
s.id = STYLE_ID5;
|
|
14593
14794
|
doc.head.appendChild(s);
|
|
14594
14795
|
}
|
|
14595
14796
|
s.textContent = CSS;
|
|
@@ -14618,7 +14819,7 @@ function createCustomMark(doc, html, background) {
|
|
|
14618
14819
|
return el;
|
|
14619
14820
|
}
|
|
14620
14821
|
function createAttributionMark(doc, background) {
|
|
14621
|
-
|
|
14822
|
+
ensureStyles5(doc);
|
|
14622
14823
|
const a = doc.createElement("a");
|
|
14623
14824
|
a.className = "vela-attribution";
|
|
14624
14825
|
a.href = ATTRIBUTION_URL;
|
|
@@ -15130,6 +15331,8 @@ var NativeRenderer = class {
|
|
|
15130
15331
|
// total right-gutter width (master column + merged-scale columns)
|
|
15131
15332
|
this.paneControls = null;
|
|
15132
15333
|
// per-pane hover button cluster (top-right)
|
|
15334
|
+
this.axisScaleButtons = null;
|
|
15335
|
+
// A/L hover buttons at the bottom of each pane's price scale
|
|
15133
15336
|
this.paneActionCbs = /* @__PURE__ */ new Set();
|
|
15134
15337
|
// ── data window (crosshair OHLC + per-series readout, pulled by host panels) ──
|
|
15135
15338
|
this.hoverLogical = null;
|
|
@@ -15173,7 +15376,7 @@ var NativeRenderer = class {
|
|
|
15173
15376
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
15174
15377
|
this.tableOverlays = /* @__PURE__ */ new Map();
|
|
15175
15378
|
this.name = "native";
|
|
15176
|
-
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "indicatorTitles", "indicatorValues"];
|
|
15379
|
+
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "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", "indicatorTitles", "indicatorValues"];
|
|
15177
15380
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
15178
15381
|
* so moving onto the button doesn't count as leaving). */
|
|
15179
15382
|
this.onScrollProximityMove = (e) => {
|
|
@@ -15289,6 +15492,9 @@ var NativeRenderer = class {
|
|
|
15289
15492
|
case "highlights":
|
|
15290
15493
|
this.scene.highlights = sanitizeHighlights(value);
|
|
15291
15494
|
break;
|
|
15495
|
+
case "sessionZones":
|
|
15496
|
+
this.scene.sessionZones = sanitizeSessionZones(value);
|
|
15497
|
+
break;
|
|
15292
15498
|
case "gridlines":
|
|
15293
15499
|
this.scene.showGrid = Boolean(value);
|
|
15294
15500
|
break;
|
|
@@ -15405,6 +15611,8 @@ var NativeRenderer = class {
|
|
|
15405
15611
|
return this.scene.indicatorZOrder();
|
|
15406
15612
|
case "highlights":
|
|
15407
15613
|
return this.scene.highlights;
|
|
15614
|
+
case "sessionZones":
|
|
15615
|
+
return this.scene.sessionZones;
|
|
15408
15616
|
case "gridlines":
|
|
15409
15617
|
return this.scene.showGrid;
|
|
15410
15618
|
case "axisLabels":
|
|
@@ -15614,7 +15822,8 @@ var NativeRenderer = class {
|
|
|
15614
15822
|
baselineLevel: s.baseline.baselineLevel
|
|
15615
15823
|
};
|
|
15616
15824
|
})(),
|
|
15617
|
-
series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale }
|
|
15825
|
+
series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale },
|
|
15826
|
+
sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor }
|
|
15618
15827
|
};
|
|
15619
15828
|
}
|
|
15620
15829
|
/**
|
|
@@ -15696,6 +15905,7 @@ var NativeRenderer = class {
|
|
|
15696
15905
|
width: next.baseline.width,
|
|
15697
15906
|
baselineLevel: next.baseline.baselineLevel
|
|
15698
15907
|
};
|
|
15908
|
+
s.sessions = { premarketColor: next.sessions.premarketColor, postmarketColor: next.sessions.postmarketColor };
|
|
15699
15909
|
this.setPriceStyle(next.series.style);
|
|
15700
15910
|
this.scene.candleOverride = candleOverrideFor(this.scene.priceStyle, s.chartTypes);
|
|
15701
15911
|
this.scene.baselineValue = next.series.baseline;
|
|
@@ -15709,6 +15919,7 @@ var NativeRenderer = class {
|
|
|
15709
15919
|
}
|
|
15710
15920
|
this.inputsUI?.setTheme(this.theme);
|
|
15711
15921
|
this.paneControls?.setTheme(this.theme);
|
|
15922
|
+
this.axisScaleButtons?.setTheme(this.theme);
|
|
15712
15923
|
this.userDrawings?.setTheme(this.chromeTheme());
|
|
15713
15924
|
this.refreshScrollButtonTheme();
|
|
15714
15925
|
this.refreshAttributionColor();
|
|
@@ -16047,7 +16258,7 @@ var NativeRenderer = class {
|
|
|
16047
16258
|
this.surfaceBackground = theme.background;
|
|
16048
16259
|
this.surfaceTextColor = theme.textColor;
|
|
16049
16260
|
this.wrapper = document.createElement("div");
|
|
16050
|
-
Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair" });
|
|
16261
|
+
Object.assign(this.wrapper.style, { position: "relative", width: "100%", height: "100%", overflow: "hidden", cursor: "crosshair", userSelect: "none", webkitUserSelect: "none" });
|
|
16051
16262
|
applyChromeTokens(this.wrapper, this.chromeTheme());
|
|
16052
16263
|
this.volumeCanvas = document.createElement("canvas");
|
|
16053
16264
|
Object.assign(this.volumeCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
@@ -16216,6 +16427,15 @@ var NativeRenderer = class {
|
|
|
16216
16427
|
this.emitPaneAction({ type: "maximize", paneId, maximized });
|
|
16217
16428
|
}
|
|
16218
16429
|
});
|
|
16430
|
+
this.axisScaleButtons = new AxisScaleButtons(this.plot, theme, {
|
|
16431
|
+
panes: () => this.axisScaleViews(),
|
|
16432
|
+
rightAxis: () => this.rightAxisW,
|
|
16433
|
+
onToggleAuto: (paneId) => this.togglePaneAuto(paneId),
|
|
16434
|
+
onToggleLog: (paneId) => {
|
|
16435
|
+
const pane = this.scene.panes.get(paneId);
|
|
16436
|
+
if (pane) this.setPaneLog(paneId, !paneLogScale(this.scene, pane));
|
|
16437
|
+
}
|
|
16438
|
+
});
|
|
16219
16439
|
this.resizeObserver = new ResizeObserver(() => this.resize());
|
|
16220
16440
|
this.resizeObserver.observe(this.wrapper);
|
|
16221
16441
|
this.watchDpr();
|
|
@@ -16292,6 +16512,7 @@ var NativeRenderer = class {
|
|
|
16292
16512
|
this.applyBackground();
|
|
16293
16513
|
this.inputsUI.setTheme(theme);
|
|
16294
16514
|
this.paneControls?.setTheme(this.theme);
|
|
16515
|
+
this.axisScaleButtons?.setTheme(this.theme);
|
|
16295
16516
|
this.settingsDialog?.refreshConfig(this.getConfig());
|
|
16296
16517
|
this.syncThemeControl();
|
|
16297
16518
|
this.settingsDialog?.setTheme(this.theme);
|
|
@@ -16340,6 +16561,7 @@ var NativeRenderer = class {
|
|
|
16340
16561
|
this.liveRegion = null;
|
|
16341
16562
|
this.inputsUI?.destroy();
|
|
16342
16563
|
this.paneControls?.destroy();
|
|
16564
|
+
this.axisScaleButtons?.destroy();
|
|
16343
16565
|
for (const overlay of this.tableOverlays.values()) overlay.destroy();
|
|
16344
16566
|
this.tableOverlays.clear();
|
|
16345
16567
|
this.resizeObserver?.disconnect();
|
|
@@ -17734,6 +17956,25 @@ var NativeRenderer = class {
|
|
|
17734
17956
|
invert: paneInvert(this.scene, p)
|
|
17735
17957
|
}));
|
|
17736
17958
|
}
|
|
17959
|
+
/** Thin projection of the panes for the axis A/L hover buttons (top-to-bottom order). */
|
|
17960
|
+
axisScaleViews() {
|
|
17961
|
+
return this.scene.orderedPanes().map((p) => ({
|
|
17962
|
+
id: p.id,
|
|
17963
|
+
top: p.bounds.top,
|
|
17964
|
+
height: p.bounds.height,
|
|
17965
|
+
collapsed: p.collapsed,
|
|
17966
|
+
auto: p.manualScale == null,
|
|
17967
|
+
log: paneLogScale(this.scene, p)
|
|
17968
|
+
}));
|
|
17969
|
+
}
|
|
17970
|
+
/** Toggle one pane's autoscale (the A axis button): off freezes the current window into
|
|
17971
|
+
* manual mode, on drops it — the per-pane twin of the chart-level `autoScale` feature. */
|
|
17972
|
+
togglePaneAuto(paneId) {
|
|
17973
|
+
const pane = this.scene.panes.get(paneId);
|
|
17974
|
+
if (!pane) return;
|
|
17975
|
+
pane.manualScale = pane.manualScale == null ? { ...pane.scale } : null;
|
|
17976
|
+
this.scheduler?.invalidate(4 /* Full */);
|
|
17977
|
+
}
|
|
17737
17978
|
/** Set one pane's axis mode. The price pane routes to the scene-level setting (persisted +
|
|
17738
17979
|
* keyboard shortcuts); a study pane keeps its own, so panes never affect each other. */
|
|
17739
17980
|
setPaneScaleMode(paneId, mode) {
|
|
@@ -17745,19 +17986,18 @@ var NativeRenderer = class {
|
|
|
17745
17986
|
}
|
|
17746
17987
|
this.scheduler?.invalidate(4 /* Full */);
|
|
17747
17988
|
}
|
|
17748
|
-
/** Set one pane's logarithmic flag
|
|
17749
|
-
*
|
|
17989
|
+
/** Set one pane's logarithmic flag; the price pane routes to the scene-level flag.
|
|
17990
|
+
* Log and auto are INDEPENDENT: a frozen (manual) window is kept — its price bounds
|
|
17991
|
+
* stay put and only its `log` tag flips, so the same range re-renders in the new
|
|
17992
|
+
* space instead of snapping back to autoscale. */
|
|
17750
17993
|
setPaneLog(paneId, log) {
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
if (
|
|
17755
|
-
|
|
17756
|
-
const p = this.scene.panes.get(paneId);
|
|
17757
|
-
if (!p) return;
|
|
17758
|
-
p.logScale = log;
|
|
17759
|
-
p.manualScale = null;
|
|
17994
|
+
const pane = paneId === PRICE_PANE_ID ? this.scene.orderedPanes().find((p) => p.kind === "price") : this.scene.panes.get(paneId);
|
|
17995
|
+
if (paneId === PRICE_PANE_ID) this.scene.logScale = log;
|
|
17996
|
+
else {
|
|
17997
|
+
if (!pane) return;
|
|
17998
|
+
pane.logScale = log;
|
|
17760
17999
|
}
|
|
18000
|
+
if (pane?.manualScale) pane.manualScale.log = log;
|
|
17761
18001
|
this.scheduler?.invalidate(4 /* Full */);
|
|
17762
18002
|
}
|
|
17763
18003
|
/** Flip one pane's axis (high at the bottom). The price pane routes to the scene-level flag
|
|
@@ -17856,6 +18096,7 @@ var NativeRenderer = class {
|
|
|
17856
18096
|
}
|
|
17857
18097
|
this.inputsUI?.reposition();
|
|
17858
18098
|
this.paneControls?.reposition();
|
|
18099
|
+
this.axisScaleButtons?.reposition();
|
|
17859
18100
|
this.repositionScrollButton();
|
|
17860
18101
|
this.positionAttribution();
|
|
17861
18102
|
this.publishPricePaneBounds();
|
|
@@ -17874,6 +18115,7 @@ var NativeRenderer = class {
|
|
|
17874
18115
|
}
|
|
17875
18116
|
this.inputsUI?.reposition();
|
|
17876
18117
|
this.paneControls?.reposition();
|
|
18118
|
+
this.axisScaleButtons?.reposition();
|
|
17877
18119
|
this.repositionScrollButton();
|
|
17878
18120
|
this.positionAttribution();
|
|
17879
18121
|
this.publishPricePaneBounds();
|
|
@@ -18081,6 +18323,22 @@ function sanitizeHighlights(value) {
|
|
|
18081
18323
|
}
|
|
18082
18324
|
return out.sort((a, b) => a.from - b.from);
|
|
18083
18325
|
}
|
|
18326
|
+
function sanitizeSessionZones(value) {
|
|
18327
|
+
if (value == null || typeof value !== "object") return null;
|
|
18328
|
+
const v = value;
|
|
18329
|
+
const windows = (raw) => {
|
|
18330
|
+
if (!Array.isArray(raw)) return [];
|
|
18331
|
+
const out = [];
|
|
18332
|
+
for (const w of raw) {
|
|
18333
|
+
if (!Array.isArray(w)) continue;
|
|
18334
|
+
const from = Number(w[0]);
|
|
18335
|
+
const to = Number(w[1]);
|
|
18336
|
+
if (Number.isFinite(from) && Number.isFinite(to) && to > from) out.push([from, to]);
|
|
18337
|
+
}
|
|
18338
|
+
return out.sort((a, b) => a[0] - b[0]);
|
|
18339
|
+
};
|
|
18340
|
+
return { pre: windows(v.pre), post: windows(v.post) };
|
|
18341
|
+
}
|
|
18084
18342
|
function clamp012(v) {
|
|
18085
18343
|
return Math.max(0, Math.min(1, v));
|
|
18086
18344
|
}
|