@luxalgo/vela 0.8.1 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{DataProvider-DwFIoV4r.d.ts → DataProvider-oH78tYXL.d.ts} +1 -1
- package/dist/{DataProvider-BrlxntPN.d.cts → DataProvider-wbtJ3-Sa.d.cts} +1 -1
- package/dist/{chunk-DBDXKZUF.js → chunk-2IPXYM4B.js} +306 -177
- package/dist/{chunk-NELQJCGK.js → chunk-G7B7ZCBF.js} +167 -10
- package/dist/{chunk-ATGTCWHJ.js → chunk-HMHA733L.js} +473 -196
- package/dist/{chunk-EZ5FWVLA.js → chunk-KWI3YDBW.js} +343 -17
- package/dist/{chunk-LMBYEGUR.js → chunk-TO3GIK7Z.js} +71 -6
- package/dist/{chunk-BZQM2XO7.js → chunk-VHGACEHO.js} +62 -1
- package/dist/{contributions-tjNExo1o.d.ts → contributions-BhOBRenr.d.cts} +79 -12
- package/dist/{contributions-CtlptxR8.d.cts → contributions-HGqnNb_P.d.ts} +79 -12
- package/dist/index.cjs +733 -113
- package/dist/index.d.cts +34 -14
- package/dist/index.d.ts +34 -14
- package/dist/index.js +5 -5
- package/dist/{options-BnEPUuVV.d.ts → options-lMUwmRwh.d.cts} +69 -4
- package/dist/{options-BnEPUuVV.d.cts → options-lMUwmRwh.d.ts} +69 -4
- package/dist/{plugin-e2WUEtiI.d.ts → plugin-Cr2Pj8q8.d.ts} +4 -4
- package/dist/{plugin-BWxJRKzS.d.cts → plugin-Do5J5xuF.d.cts} +4 -4
- package/dist/plugin.cjs +38 -6
- package/dist/plugin.d.cts +5 -5
- package/dist/plugin.d.ts +5 -5
- package/dist/plugin.js +3 -3
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/{side-panel-HF0IAzwf.d.cts → side-panel-_2UK1gSm.d.cts} +58 -3
- package/dist/{side-panel-HF0IAzwf.d.ts → side-panel-_2UK1gSm.d.ts} +58 -3
- package/dist/{statusline-model-DFY7tuHj.d.cts → statusline-model-B-2xGlB_.d.cts} +3 -3
- package/dist/{statusline-model-zKXtGIjj.d.ts → statusline-model-CY1wzxHg.d.ts} +3 -3
- package/dist/ui.cjs +298 -13
- package/dist/ui.d.cts +132 -74
- package/dist/ui.d.ts +132 -74
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +734 -114
- package/dist/vela.global.min.js +86 -52
- package/dist/widget.cjs +1227 -211
- package/dist/widget.d.cts +36 -12
- package/dist/widget.d.ts +36 -12
- package/dist/widget.js +7 -7
- package/dist/workspace.cjs +1226 -211
- package/dist/workspace.d.cts +47 -20
- package/dist/workspace.d.ts +47 -20
- package/dist/workspace.js +6 -6
- package/package.json +2 -2
- /package/dist/{chunk-JU4CWEBF.js → chunk-W5YPYZ2P.js} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { deserializeDrawing, chartTypes, rendererLayers, isLineLikeSeries, seriesShownOn, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, settingsRowVisible, normalizeSettingsRow, Magnifier, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, nativeInstanceChannel, drawingTypes, settingsRowValueKeys, formatDuration, RegressionChannel, FixedRangeVolumeProfile, DEFAULT_DRAWING_COLOR, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, magnifierTimeframeLabel, lineSegmentIntersection, MAGNIFIER_TIMEFRAME_OPTIONS, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, seriesInScale, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS, stableSeriesId } from './chunk-
|
|
2
|
-
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, CALLOUT_STYLE_ID, CALLOUT_CSS, Popover, blendOver, splitColor, CalloutBubble, Menu, TextArea, NumberInput, buildColorPicker, isPopoverOpen, DatePicker, eventDismissedPopover, toggleSelectList, normalizeDateInput, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-
|
|
3
|
-
import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, INFO, BULLISH, BEARISH, isDarkColor, injectStyles, SERIES_LINE, CHIP_PLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, iconMarkup, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, SLATE, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-
|
|
1
|
+
import { deserializeDrawing, chartTypes, rendererLayers, isLineLikeSeries, seriesShownOn, foldBaseModulation, registerChartType, rendererDefaults, getDrawingType, normalizeTimezone, settingsRowVisible, normalizeSettingsRow, Magnifier, TextLabel, Callout, resetDrawingSettings, registerNativeIndicator, chartType, getNativeIndicator, nativeIndicatorDescriptors, nativeInstanceChannel, drawingTypes, settingsRowValueKeys, TIMEZONES, tzMenuLabel, formatDuration, RegressionChannel, FixedRangeVolumeProfile, DEFAULT_DRAWING_COLOR, SegmentDrawing, ArrowMark, GlyphStamp, RadialFib, FibSpiral, DedekindTessellation, MachFigure, GannSquare, FibRatios, MeasureBox, PositionTool, AnchoredVwap, PatternDrawing, Comment, PriceNote, Signpost, Note, PriceLabel, GANN_SQUARE_ARCS, magnifierTimeframeLabel, lineSegmentIntersection, MAGNIFIER_TIMEFRAME_OPTIONS, GLYPH_OPTIONS, STAMP_SIZE_OPTIONS, LINE_STYLE_OPTIONS, DEDEKIND_CURVATURE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, TEXT_SIZE_OPTIONS, createDrawing, inputVisible, seriesInScale, tickerModifierIds, CalloutBase, DIRECTION_OPTIONS, FibRetracement, stableSeriesId } from './chunk-KWI3YDBW.js';
|
|
2
|
+
import { themeTokens, Dialog, closeOpenPopovers, closeWidthPopover, fieldSection, buildFieldControl, fieldSeparator, fieldGrid, fieldRow, CALLOUT_STYLE_ID, CALLOUT_CSS, Popover, blendOver, splitColor, CalloutBubble, Menu, TextArea, NumberInput, buildColorPicker, isPopoverOpen, DatePicker, eventDismissedPopover, toggleSelectList, normalizeDateInput, openPopoverTrigger, fieldGridColumns, FIELD_GAP_PX, STATIC_TOKENS } from './chunk-G7B7ZCBF.js';
|
|
3
|
+
import { icon, iconAt, withAlpha, WARNING, ACCENT, NEUTRAL, INFO, BULLISH, BEARISH, announceSurface, holdForExit, isDarkColor, injectStyles, SERIES_LINE, CHIP_PLATE, CROSSHAIR, TRADE_EXIT, TRADE_SHORT, TRADE_LONG, iconMarkup, overlayScrollbarCss, SLATE_DEEP, VALID, INVALID, SLATE, FIELD_FOCUS_CSS, FIELD_FOCUS_RING, svg24, svg24Solid } from './chunk-VHGACEHO.js';
|
|
4
4
|
|
|
5
5
|
// src/core/events/EventBus.ts
|
|
6
6
|
var TypedEventBus = class {
|
|
@@ -1885,100 +1885,6 @@ var SecondClock = class _SecondClock {
|
|
|
1885
1885
|
}
|
|
1886
1886
|
};
|
|
1887
1887
|
|
|
1888
|
-
// src/core/timezones.ts
|
|
1889
|
-
var TIMEZONES = [
|
|
1890
|
-
{ value: "Etc/UTC", label: "UTC" },
|
|
1891
|
-
{ value: "Etc/GMT+12", label: "International Date Line West" },
|
|
1892
|
-
{ value: "Pacific/Pago_Pago", label: "Pago Pago" },
|
|
1893
|
-
{ value: "Pacific/Honolulu", label: "Honolulu" },
|
|
1894
|
-
{ value: "Pacific/Marquesas", label: "Marquesas Islands" },
|
|
1895
|
-
{ value: "America/Anchorage", label: "Anchorage" },
|
|
1896
|
-
{ value: "America/Los_Angeles", label: "Los Angeles" },
|
|
1897
|
-
{ value: "America/Phoenix", label: "Phoenix" },
|
|
1898
|
-
{ value: "America/Denver", label: "Denver" },
|
|
1899
|
-
{ value: "America/Chicago", label: "Chicago" },
|
|
1900
|
-
{ value: "America/Mexico_City", label: "Mexico City" },
|
|
1901
|
-
{ value: "America/New_York", label: "New York" },
|
|
1902
|
-
{ value: "America/Bogota", label: "Bogot\xE1" },
|
|
1903
|
-
{ value: "America/Caracas", label: "Caracas" },
|
|
1904
|
-
{ value: "America/Santiago", label: "Santiago" },
|
|
1905
|
-
{ value: "America/St_Johns", label: "St. John's" },
|
|
1906
|
-
{ value: "America/Sao_Paulo", label: "S\xE3o Paulo" },
|
|
1907
|
-
{ value: "America/Argentina/Buenos_Aires", label: "Buenos Aires" },
|
|
1908
|
-
{ value: "America/Noronha", label: "Fernando de Noronha" },
|
|
1909
|
-
{ value: "Atlantic/Azores", label: "Azores" },
|
|
1910
|
-
{ value: "Atlantic/Reykjavik", label: "Reykjavik" },
|
|
1911
|
-
{ value: "Europe/London", label: "London" },
|
|
1912
|
-
{ value: "Europe/Paris", label: "Paris" },
|
|
1913
|
-
{ value: "Europe/Berlin", label: "Berlin" },
|
|
1914
|
-
{ value: "Europe/Athens", label: "Athens" },
|
|
1915
|
-
{ value: "Africa/Cairo", label: "Cairo" },
|
|
1916
|
-
{ value: "Africa/Johannesburg", label: "Johannesburg" },
|
|
1917
|
-
{ value: "Europe/Moscow", label: "Moscow" },
|
|
1918
|
-
{ value: "Europe/Istanbul", label: "Istanbul" },
|
|
1919
|
-
{ value: "Asia/Tehran", label: "Tehran" },
|
|
1920
|
-
{ value: "Asia/Dubai", label: "Dubai" },
|
|
1921
|
-
{ value: "Asia/Kabul", label: "Kabul" },
|
|
1922
|
-
{ value: "Asia/Karachi", label: "Karachi" },
|
|
1923
|
-
{ value: "Asia/Kolkata", label: "Mumbai" },
|
|
1924
|
-
{ value: "Asia/Kathmandu", label: "Kathmandu" },
|
|
1925
|
-
{ value: "Asia/Dhaka", label: "Dhaka" },
|
|
1926
|
-
{ value: "Asia/Yangon", label: "Yangon" },
|
|
1927
|
-
{ value: "Asia/Bangkok", label: "Bangkok" },
|
|
1928
|
-
{ value: "Asia/Shanghai", label: "Shanghai" },
|
|
1929
|
-
{ value: "Asia/Hong_Kong", label: "Hong Kong" },
|
|
1930
|
-
{ value: "Asia/Singapore", label: "Singapore" },
|
|
1931
|
-
{ value: "Australia/Eucla", label: "Eucla" },
|
|
1932
|
-
{ value: "Asia/Tokyo", label: "Tokyo" },
|
|
1933
|
-
{ value: "Asia/Seoul", label: "Seoul" },
|
|
1934
|
-
{ value: "Australia/Adelaide", label: "Adelaide" },
|
|
1935
|
-
{ value: "Australia/Sydney", label: "Sydney" },
|
|
1936
|
-
{ value: "Australia/Lord_Howe", label: "Lord Howe Island" },
|
|
1937
|
-
{ value: "Pacific/Noumea", label: "Noum\xE9a" },
|
|
1938
|
-
{ value: "Pacific/Auckland", label: "Auckland" },
|
|
1939
|
-
{ value: "Pacific/Chatham", label: "Chatham Islands" },
|
|
1940
|
-
{ value: "Pacific/Apia", label: "Apia" },
|
|
1941
|
-
{ value: "Pacific/Kiritimati", label: "Kiritimati" }
|
|
1942
|
-
];
|
|
1943
|
-
var EXCHANGE_TIMEZONE = "exchange";
|
|
1944
|
-
function isExchangeTimezone(zone) {
|
|
1945
|
-
return zone === EXCHANGE_TIMEZONE;
|
|
1946
|
-
}
|
|
1947
|
-
function resolveTimezone(zone, exchangeZone) {
|
|
1948
|
-
if (!isExchangeTimezone(zone)) return zone;
|
|
1949
|
-
return exchangeZone && exchangeZone !== "" ? exchangeZone : "Etc/UTC";
|
|
1950
|
-
}
|
|
1951
|
-
function normalizeTimezone(zone) {
|
|
1952
|
-
return zone === "UTC" || zone === "Etc/UTC" || zone === "Etc/GMT" ? "Etc/UTC" : zone;
|
|
1953
|
-
}
|
|
1954
|
-
function timezoneMenuRows(current) {
|
|
1955
|
-
const active = normalizeTimezone(current);
|
|
1956
|
-
const [utc, ...zones] = TIMEZONES.map((t) => ({ value: t.value, label: tzMenuLabel(t.value, t.label), checked: t.value === active }));
|
|
1957
|
-
return [utc, { value: EXCHANGE_TIMEZONE, label: "Exchange", checked: isExchangeTimezone(current) }, ...zones];
|
|
1958
|
-
}
|
|
1959
|
-
function tzOffset(zone, date = /* @__PURE__ */ new Date()) {
|
|
1960
|
-
try {
|
|
1961
|
-
const parts = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "shortOffset" }).formatToParts(date);
|
|
1962
|
-
const raw = parts.find((p) => p.type === "timeZoneName")?.value ?? "";
|
|
1963
|
-
const m = raw.match(/GMT(?:(\+|-)(\d{1,2})(?::(\d{2}))?)?/);
|
|
1964
|
-
if (!m || !m[1]) return "UTC";
|
|
1965
|
-
const sign = m[1] === "-" ? "-" : "+";
|
|
1966
|
-
const hrs = m[2] ?? "0";
|
|
1967
|
-
const mins = m[3] ?? "";
|
|
1968
|
-
return mins ? `UTC${sign}${hrs}:${mins}` : `UTC${sign}${hrs}`;
|
|
1969
|
-
} catch {
|
|
1970
|
-
return "UTC";
|
|
1971
|
-
}
|
|
1972
|
-
}
|
|
1973
|
-
function tzMenuLabel(zone, location) {
|
|
1974
|
-
if (normalizeTimezone(zone) === "Etc/UTC") return location;
|
|
1975
|
-
return `(${tzOffset(zone)}) ${location}`;
|
|
1976
|
-
}
|
|
1977
|
-
function tzButtonLabel(zone) {
|
|
1978
|
-
if (normalizeTimezone(zone) === "Etc/UTC") return "UTC";
|
|
1979
|
-
return tzOffset(zone);
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
1888
|
// src/core/drawings/effectiveColor.ts
|
|
1983
1889
|
var VALID_FILL = VALID;
|
|
1984
1890
|
var INVALID_FILL = INVALID;
|
|
@@ -2602,6 +2508,7 @@ function chromeHint(text, opts) {
|
|
|
2602
2508
|
// src/renderers/native/drawings/DrawingToolbar.ts
|
|
2603
2509
|
var TOOLBAR_WIDTH = 44;
|
|
2604
2510
|
var TOOLBAR_COLLAPSED_WIDTH = 16;
|
|
2511
|
+
var FLYOUT_GAP = 3;
|
|
2605
2512
|
var DrawingToolbar = class {
|
|
2606
2513
|
constructor(host, theme, onArm, onMagnet = () => {
|
|
2607
2514
|
}, onMeasure = () => {
|
|
@@ -2631,6 +2538,9 @@ var DrawingToolbar = class {
|
|
|
2631
2538
|
// group id (or MAGNET_ID) whose flyout is open
|
|
2632
2539
|
this.flyoutCell = null;
|
|
2633
2540
|
// the cell the open flyout is anchored to
|
|
2541
|
+
/** Closed flyouts still playing their exit animation, by owner id — reopening the same
|
|
2542
|
+
* owner takes its element back instead of stacking a second one on it. */
|
|
2543
|
+
this.exitingFlyouts = /* @__PURE__ */ new Map();
|
|
2634
2544
|
this.groupCells = /* @__PURE__ */ new Map();
|
|
2635
2545
|
// the composite cell (hover/active bg + flyout anchor)
|
|
2636
2546
|
this.groupIcons = /* @__PURE__ */ new Map();
|
|
@@ -2739,6 +2649,7 @@ var DrawingToolbar = class {
|
|
|
2739
2649
|
}
|
|
2740
2650
|
destroy() {
|
|
2741
2651
|
this.closeFlyout();
|
|
2652
|
+
for (const { exit } of [...this.exitingFlyouts.values()]) exit.finish();
|
|
2742
2653
|
for (const dispose of this.tipDisposers.splice(0)) dispose();
|
|
2743
2654
|
this.root.remove();
|
|
2744
2655
|
}
|
|
@@ -2966,16 +2877,22 @@ var DrawingToolbar = class {
|
|
|
2966
2877
|
* outside-dismiss. Callers fill it with items. */
|
|
2967
2878
|
beginFlyout(ownerId, cell) {
|
|
2968
2879
|
const t = this.theme;
|
|
2969
|
-
const
|
|
2880
|
+
const back = this.exitingFlyouts.get(ownerId);
|
|
2881
|
+
if (back) {
|
|
2882
|
+
this.exitingFlyouts.delete(ownerId);
|
|
2883
|
+
back.exit.cancel();
|
|
2884
|
+
back.fly.replaceChildren();
|
|
2885
|
+
}
|
|
2886
|
+
const fly = back?.fly ?? document.createElement("div");
|
|
2970
2887
|
fly.className = "vela-dtb-flyout";
|
|
2971
|
-
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:
|
|
2888
|
+
fly.style.cssText = `position:absolute;z-index:23;display:flex;flex-direction:column;gap:2px;padding:4px;border-radius:var(--vela-radius-md);background:var(--vela-surface);border:1px solid ${this.borderOverride ?? "var(--vela-border-strong)"};box-shadow:var(--vela-shadow);pointer-events:auto;overflow-y:auto;overscroll-behavior:contain;`;
|
|
2972
2889
|
applyChromeTokens(fly, t);
|
|
2973
2890
|
this.host.appendChild(fly);
|
|
2974
2891
|
const r = cell.getBoundingClientRect();
|
|
2975
2892
|
const rootR = this.root.getBoundingClientRect();
|
|
2976
2893
|
const hostR = this.host.getBoundingClientRect();
|
|
2977
2894
|
const top = r.top - hostR.top;
|
|
2978
|
-
fly.style.left = `${rootR.right - hostR.left
|
|
2895
|
+
fly.style.left = `${rootR.right - hostR.left + FLYOUT_GAP}px`;
|
|
2979
2896
|
fly.style.top = `${top}px`;
|
|
2980
2897
|
fly.style.maxHeight = `${Math.max(120, hostR.height - top - 8)}px`;
|
|
2981
2898
|
this.flyout = fly;
|
|
@@ -2991,6 +2908,7 @@ var DrawingToolbar = class {
|
|
|
2991
2908
|
if (!cell) return;
|
|
2992
2909
|
const fly = this.beginFlyout(group.id, cell);
|
|
2993
2910
|
const sections = group.sections ?? [{ label: "", tools: group.tools }];
|
|
2911
|
+
const badge = sections.some((s) => s.tools.some((tool) => !!tool.icon));
|
|
2994
2912
|
for (let si = 0; si < sections.length; si++) {
|
|
2995
2913
|
const section = sections[si];
|
|
2996
2914
|
if (section.label) {
|
|
@@ -3001,6 +2919,7 @@ var DrawingToolbar = class {
|
|
|
3001
2919
|
fly.appendChild(
|
|
3002
2920
|
this.makeFlyoutItem({
|
|
3003
2921
|
icon: tool.icon,
|
|
2922
|
+
badge,
|
|
3004
2923
|
label: tool.label,
|
|
3005
2924
|
selected: tool.type === this.active,
|
|
3006
2925
|
shortcut: this.shortcuts.get(tool.type),
|
|
@@ -3013,6 +2932,7 @@ var DrawingToolbar = class {
|
|
|
3013
2932
|
);
|
|
3014
2933
|
}
|
|
3015
2934
|
}
|
|
2935
|
+
announceSurface(fly, true, "menu", cell);
|
|
3016
2936
|
}
|
|
3017
2937
|
openMagnetFlyout() {
|
|
3018
2938
|
const cell = this.magnetCell;
|
|
@@ -3036,6 +2956,7 @@ var DrawingToolbar = class {
|
|
|
3036
2956
|
})
|
|
3037
2957
|
);
|
|
3038
2958
|
}
|
|
2959
|
+
announceSurface(fly, true, "menu", cell);
|
|
3039
2960
|
}
|
|
3040
2961
|
flyoutHeader(text) {
|
|
3041
2962
|
const t = this.theme;
|
|
@@ -3049,21 +2970,24 @@ var DrawingToolbar = class {
|
|
|
3049
2970
|
div.style.cssText = `height:1px;margin:4px 8px;background:${this.borderColor};`;
|
|
3050
2971
|
return div;
|
|
3051
2972
|
}
|
|
3052
|
-
/** A flyout row, left to right: optional leading icon, label, a check when it's the
|
|
3053
|
-
* entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
3054
|
-
* Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
2973
|
+
/** A flyout row, left to right: optional leading icon badge, label, a check when it's the
|
|
2974
|
+
* selected entry, an optional shortcut hint, and — at the far right — the favorite star (tool
|
|
2975
|
+
* rows). Hover tint is CSS (`.vela-dtb-item:hover`), so navigating the menu stays smooth. */
|
|
3055
2976
|
makeFlyoutItem(opts) {
|
|
3056
2977
|
const t = this.theme;
|
|
3057
2978
|
const item = document.createElement("button");
|
|
3058
2979
|
item.type = "button";
|
|
3059
2980
|
item.className = "vela-dtb-item";
|
|
3060
2981
|
item.setAttribute("aria-label", opts.label);
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
2982
|
+
const badged = opts.badge === true || !!opts.icon;
|
|
2983
|
+
const box = badged ? "gap:10px;padding:4px 10px 4px 6px;" : "gap:8px;padding:5px 10px 5px 8px;";
|
|
2984
|
+
item.style.cssText = `display:flex;align-items:center;${box}cursor:pointer;color:${t.textColor};border-radius:var(--vela-radius-sm);font:13px ${t.fontFamily};white-space:nowrap;min-width:148px;`;
|
|
2985
|
+
if (opts.selected) item.dataset.selected = "1";
|
|
2986
|
+
if (badged) {
|
|
2987
|
+
const badge = document.createElement("span");
|
|
2988
|
+
badge.className = "vela-dtb-badge";
|
|
2989
|
+
if (opts.icon) badge.innerHTML = opts.icon;
|
|
2990
|
+
item.appendChild(badge);
|
|
3067
2991
|
}
|
|
3068
2992
|
const label = document.createElement("span");
|
|
3069
2993
|
label.textContent = opts.label;
|
|
@@ -3104,9 +3028,16 @@ var DrawingToolbar = class {
|
|
|
3104
3028
|
}
|
|
3105
3029
|
closeFlyout() {
|
|
3106
3030
|
this.starEls.clear();
|
|
3107
|
-
|
|
3108
|
-
|
|
3031
|
+
const fly = this.flyout;
|
|
3032
|
+
if (fly) {
|
|
3109
3033
|
this.flyout = null;
|
|
3034
|
+
announceSurface(fly, false, "menu", this.flyoutCell);
|
|
3035
|
+
const owner = this.flyoutOwnerId ?? "";
|
|
3036
|
+
const exit = holdForExit(fly, () => {
|
|
3037
|
+
if (this.exitingFlyouts.get(owner)?.fly === fly) this.exitingFlyouts.delete(owner);
|
|
3038
|
+
fly.remove();
|
|
3039
|
+
});
|
|
3040
|
+
if (exit) this.exitingFlyouts.set(owner, { fly, exit });
|
|
3110
3041
|
this.flyoutCell?.classList.remove("vela-open");
|
|
3111
3042
|
this.flyoutCell = null;
|
|
3112
3043
|
this.flyoutOwnerId = null;
|
|
@@ -3190,6 +3121,11 @@ function ensureStyles() {
|
|
|
3190
3121
|
.vela-dtb[data-collapsed='1'] .vela-dtb-collapse .vela-dtb-hit{width:14px;}
|
|
3191
3122
|
.vela-dtb-item{background:transparent;border:none;transition:background var(--vela-dur-fast) ease;}
|
|
3192
3123
|
.vela-dtb-item:hover{background:var(--vela-hover-strong);}
|
|
3124
|
+
.vela-dtb-badge{width:24px;height:24px;padding:3px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;border-radius:var(--vela-radius-md);background:var(--vela-hover);border:1px solid var(--vela-border);color:var(--vela-fg-muted);transition:background var(--vela-dur-fast) ease,border-color var(--vela-dur-fast) ease,color var(--vela-dur-fast) ease;}
|
|
3125
|
+
.vela-dtb-badge:empty{background:transparent;border-color:transparent;}
|
|
3126
|
+
.vela-dtb-badge svg{width:100%;height:100%;}
|
|
3127
|
+
.vela-dtb-item:hover .vela-dtb-badge:not(:empty){background:var(--vela-active);color:var(--vela-fg-bright);}
|
|
3128
|
+
.vela-dtb-item[data-selected='1'] .vela-dtb-badge:not(:empty){background:color-mix(in srgb,var(--vela-fg-bright) 83%,var(--vela-surface));border-color:color-mix(in srgb,var(--vela-fg-bright) 40%,transparent);color:var(--vela-surface);}
|
|
3193
3129
|
.vela-dtb-star{width:26px;height:22px;margin:-3px -5px -3px 0;padding:3px 5px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;flex:none;opacity:0;color:inherit;border-radius:var(--vela-radius-sm);transition:opacity .1s ease,color .1s ease,background .1s ease;}
|
|
3194
3130
|
.vela-dtb-star svg{width:16px;height:16px;}
|
|
3195
3131
|
.vela-dtb-item:hover .vela-dtb-star{opacity:.55;}
|
|
@@ -4075,7 +4011,10 @@ var LEGEND_ICON_PX2 = 16;
|
|
|
4075
4011
|
var LEGEND_CTL_PX = 18;
|
|
4076
4012
|
var LEGEND_ROW_PAD_Y = 2;
|
|
4077
4013
|
var LEGEND_ROW_PAD_X = 6;
|
|
4014
|
+
var LEGEND_LEFT_PX = 10;
|
|
4015
|
+
var LEGEND_SCALE_GAP_PX = 6;
|
|
4078
4016
|
var LEGEND_TITLE_VALUES_GAP_PX = 6;
|
|
4017
|
+
var LEGEND_VALUES_GAP_PX = 5;
|
|
4079
4018
|
var LEGEND_TITLE_STATUS_GAP_PX = 8;
|
|
4080
4019
|
var LEGEND_ACTION_GAP_PX = 6;
|
|
4081
4020
|
var LEGEND_CTL_CSS = `cursor:pointer;display:inline-flex;align-items:center;justify-content:center;border:none;line-height:0;padding:0;width:${LEGEND_CTL_PX}px;height:${LEGEND_CTL_PX}px;border-radius:3px;box-sizing:border-box;flex:none;`;
|
|
@@ -4361,14 +4300,15 @@ var InputsUI = class {
|
|
|
4361
4300
|
row.valuesEl.replaceChildren();
|
|
4362
4301
|
return;
|
|
4363
4302
|
}
|
|
4364
|
-
row.valuesEl.style.display = "
|
|
4303
|
+
row.valuesEl.style.display = "block";
|
|
4365
4304
|
row.valuesEl.replaceChildren();
|
|
4366
|
-
|
|
4305
|
+
row.plotValues.forEach((v, i) => {
|
|
4367
4306
|
const span = document.createElement("span");
|
|
4368
4307
|
span.textContent = v.value;
|
|
4369
4308
|
span.style.color = v.color;
|
|
4309
|
+
if (i > 0) span.style.marginLeft = `${LEGEND_VALUES_GAP_PX}px`;
|
|
4370
4310
|
row.valuesEl.appendChild(span);
|
|
4371
|
-
}
|
|
4311
|
+
});
|
|
4372
4312
|
}
|
|
4373
4313
|
// ── legend move/merge (menu + drag) ─────────────────────────────────────
|
|
4374
4314
|
/** Open the "Move to" menu for a row, anchored under its move button. */
|
|
@@ -4469,7 +4409,7 @@ var InputsUI = class {
|
|
|
4469
4409
|
if (!lg) {
|
|
4470
4410
|
lg = document.createElement("div");
|
|
4471
4411
|
lg.dataset.velaPane = paneId;
|
|
4472
|
-
lg.style.cssText =
|
|
4412
|
+
lg.style.cssText = `position:absolute;left:${LEGEND_LEFT_PX}px;z-index:5;display:flex;flex-direction:column;align-items:flex-start;gap:0;pointer-events:none;font:12px -apple-system,Segoe UI,sans-serif;`;
|
|
4473
4413
|
applyChromeTokens(lg, this.theme);
|
|
4474
4414
|
this.positionLegend(lg, paneId);
|
|
4475
4415
|
this.container.appendChild(lg);
|
|
@@ -4481,6 +4421,7 @@ var InputsUI = class {
|
|
|
4481
4421
|
const bounds = this.paneBoundsOf ? this.paneBoundsOf(paneId) : { top: 0, height: Infinity };
|
|
4482
4422
|
lg.toggleAttribute(LEGEND_AT_TOP_ATTR, bounds.top === 0);
|
|
4483
4423
|
lg.style.display = bounds.height < 4 || !this.titlesVisible ? "none" : "flex";
|
|
4424
|
+
lg.style.maxWidth = `calc(100% - ${(bounds.rightAxis ?? 0) + LEGEND_LEFT_PX + LEGEND_SCALE_GAP_PX}px)`;
|
|
4484
4425
|
const collapsed = this.paneCollapse.has(paneId);
|
|
4485
4426
|
const masterId = this.paneCollapse.get(paneId) ?? null;
|
|
4486
4427
|
for (const row of this.rows.values()) {
|
|
@@ -4572,7 +4513,7 @@ var InputsUI = class {
|
|
|
4572
4513
|
}
|
|
4573
4514
|
ensureDialogStyles();
|
|
4574
4515
|
const el = document.createElement("div");
|
|
4575
|
-
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
4516
|
+
el.style.cssText = `pointer-events:auto;display:flex;align-items:center;background:${this.idleRowFill()};border-radius:4px;padding:${LEGEND_ROW_PAD_Y}px ${LEGEND_ROW_PAD_X}px;margin-left:-${LEGEND_ROW_PAD_X}px;max-width:calc(100% + ${LEGEND_ROW_PAD_X}px);min-height:${LEGEND_ROW_MIN_H}px;box-sizing:border-box;color:${this.theme.textColor};user-select:none;-webkit-user-select:none;`;
|
|
4576
4517
|
el.addEventListener("mouseleave", () => {
|
|
4577
4518
|
if (this.selectedId !== id) this.setRowHighlighted(id, false);
|
|
4578
4519
|
});
|
|
@@ -4600,7 +4541,7 @@ var InputsUI = class {
|
|
|
4600
4541
|
const statusEl = document.createElement("span");
|
|
4601
4542
|
statusEl.style.cssText = "display:none;box-sizing:border-box;flex:none;";
|
|
4602
4543
|
const titleWrap = document.createElement("span");
|
|
4603
|
-
titleWrap.style.cssText = "white-space:nowrap;";
|
|
4544
|
+
titleWrap.style.cssText = "white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;";
|
|
4604
4545
|
titleWrap.addEventListener("mouseenter", () => this.setRowHighlighted(id, true));
|
|
4605
4546
|
const titleEl = document.createElement("span");
|
|
4606
4547
|
titleEl.textContent = title;
|
|
@@ -4618,7 +4559,7 @@ var InputsUI = class {
|
|
|
4618
4559
|
el.appendChild(calloutsEl);
|
|
4619
4560
|
el.appendChild(statusEl);
|
|
4620
4561
|
const valuesEl = document.createElement("span");
|
|
4621
|
-
valuesEl.style.cssText = `display:none;
|
|
4562
|
+
valuesEl.style.cssText = `display:none;flex:1 1 0%;min-width:0;overflow:hidden;text-overflow:ellipsis;margin-left:${LEGEND_TITLE_VALUES_GAP_PX}px;white-space:nowrap;font-variant-numeric:tabular-nums;`;
|
|
4622
4563
|
el.appendChild(valuesEl);
|
|
4623
4564
|
const controlsEl = document.createElement("span");
|
|
4624
4565
|
controlsEl.style.cssText = `display:none;align-items:center;gap:${LEGEND_ACTION_GAP_PX}px;flex:none;margin-left:${LEGEND_ACTION_GAP_PX}px;`;
|
|
@@ -5791,15 +5732,23 @@ var Batch = class {
|
|
|
5791
5732
|
this.seg(x + w, y + h, x, y + h, lineWidth, c);
|
|
5792
5733
|
this.seg(x, y + h, x, y, lineWidth, c);
|
|
5793
5734
|
}
|
|
5794
|
-
/**
|
|
5735
|
+
/**
|
|
5736
|
+
* Filled circle (triangle fan), feathered like a line: the rim is padded by {@link AA_PAD} and
|
|
5737
|
+
* edgeDist runs from 0 at the center to the padded radius at the rim, with edgeHalf = r. The
|
|
5738
|
+
* distance grows linearly along each spoke, so the shader's line feather gives a round soft edge.
|
|
5739
|
+
*/
|
|
5795
5740
|
circle(cx, cy, r, c, segments = 16) {
|
|
5796
|
-
|
|
5741
|
+
const ext = r + AA_PAD;
|
|
5742
|
+
let px = cx + ext;
|
|
5797
5743
|
let py = cy;
|
|
5744
|
+
this.ensure(segments * 3);
|
|
5798
5745
|
for (let i = 1; i <= segments; i += 1) {
|
|
5799
5746
|
const a = i / segments * Math.PI * 2;
|
|
5800
|
-
const nx = cx + Math.cos(a) *
|
|
5801
|
-
const ny = cy + Math.sin(a) *
|
|
5802
|
-
this.
|
|
5747
|
+
const nx = cx + Math.cos(a) * ext;
|
|
5748
|
+
const ny = cy + Math.sin(a) * ext;
|
|
5749
|
+
this.v(cx, cy, c, 0, r);
|
|
5750
|
+
this.v(px, py, c, ext, r);
|
|
5751
|
+
this.v(nx, ny, c, ext, r);
|
|
5803
5752
|
px = nx;
|
|
5804
5753
|
py = ny;
|
|
5805
5754
|
}
|
|
@@ -5980,7 +5929,7 @@ var WebGL2Backend = class {
|
|
|
5980
5929
|
}
|
|
5981
5930
|
mount(canvas) {
|
|
5982
5931
|
this.canvas = canvas;
|
|
5983
|
-
this.gl = canvas.getContext("webgl2", { alpha: true, antialias:
|
|
5932
|
+
this.gl = canvas.getContext("webgl2", { alpha: true, antialias: false, depth: false, stencil: false, premultipliedAlpha: true });
|
|
5984
5933
|
if (!this.gl) return;
|
|
5985
5934
|
if (!this.initGL()) {
|
|
5986
5935
|
this.gl = null;
|
|
@@ -7205,6 +7154,11 @@ var InputController = class {
|
|
|
7205
7154
|
/** When true (the default), the separators between stacked panes are draggable to
|
|
7206
7155
|
* resize the panes above/below. When false, a press there is a normal data-area pan. */
|
|
7207
7156
|
this.paneResize = true;
|
|
7157
|
+
/** When true (the default), the user drawings take part in pointer input: they claim a
|
|
7158
|
+
* press over a drawing or with a tool armed, and see hover moves. When false, every press
|
|
7159
|
+
* is a plain pan/click and hover leaves them alone; a drawing gesture already under way
|
|
7160
|
+
* still finishes. */
|
|
7161
|
+
this.drawings = true;
|
|
7208
7162
|
this.el = null;
|
|
7209
7163
|
this.dragging = false;
|
|
7210
7164
|
this.moved = false;
|
|
@@ -7268,18 +7222,18 @@ var InputController = class {
|
|
|
7268
7222
|
this.onModifier = (e) => {
|
|
7269
7223
|
if (e.repeat || e.key !== "Shift" && e.key !== "Control" && e.key !== "Meta") return;
|
|
7270
7224
|
if (Number.isNaN(this.cursorX)) return;
|
|
7271
|
-
if (this.dragging
|
|
7225
|
+
if (this.dragging ? this.region !== "drawing" : !this.drawings) return;
|
|
7272
7226
|
this.deps.drawingsPointerMove?.(this.cursorX, this.cursorY, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
7273
7227
|
};
|
|
7274
7228
|
this.onDown = (e) => {
|
|
7275
7229
|
if (e.button === 1) {
|
|
7276
7230
|
const { x: x2, y: y2 } = this.local(e);
|
|
7277
|
-
this.middleDeleted = this.deps.drawingsDeleteAt?.(x2, y2) ?? false;
|
|
7231
|
+
this.middleDeleted = this.drawings && (this.deps.drawingsDeleteAt?.(x2, y2) ?? false);
|
|
7278
7232
|
if (this.middleDeleted) e.preventDefault();
|
|
7279
7233
|
return;
|
|
7280
7234
|
}
|
|
7281
7235
|
if (e.button === 2) {
|
|
7282
|
-
this.rightCancelled = this.deps.drawingsCancelPlacement?.() ?? false;
|
|
7236
|
+
this.rightCancelled = this.drawings && (this.deps.drawingsCancelPlacement?.() ?? false);
|
|
7283
7237
|
if (this.rightCancelled) e.preventDefault();
|
|
7284
7238
|
return;
|
|
7285
7239
|
}
|
|
@@ -7301,20 +7255,20 @@ var InputController = class {
|
|
|
7301
7255
|
this.startY = y;
|
|
7302
7256
|
this.prevPressDrawing = this.lastPressDrawing;
|
|
7303
7257
|
this.lastPressDrawing = false;
|
|
7304
|
-
if (this.deps.drawingsClaim?.(x, y)) {
|
|
7258
|
+
if (this.drawings && this.deps.drawingsClaim?.(x, y)) {
|
|
7305
7259
|
this.region = "drawing";
|
|
7306
7260
|
this.lastPressDrawing = true;
|
|
7307
7261
|
this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
7308
7262
|
this.capture(e.pointerId);
|
|
7309
7263
|
return;
|
|
7310
7264
|
}
|
|
7311
|
-
if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
7265
|
+
if (this.drawings && e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
|
|
7312
7266
|
this.region = "drawing";
|
|
7313
7267
|
this.lastPressDrawing = true;
|
|
7314
7268
|
this.capture(e.pointerId);
|
|
7315
7269
|
return;
|
|
7316
7270
|
}
|
|
7317
|
-
if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
7271
|
+
if (this.drawings && (e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
|
|
7318
7272
|
this.region = "drawing";
|
|
7319
7273
|
this.lastPressDrawing = true;
|
|
7320
7274
|
this.capture(e.pointerId);
|
|
@@ -7391,7 +7345,7 @@ var InputController = class {
|
|
|
7391
7345
|
const drawCursor = this.deps.drawingsCursor?.(x, y);
|
|
7392
7346
|
const r = this.regionAt(x, y);
|
|
7393
7347
|
this.el.style.cursor = drawCursor ?? (r === "price" ? "ns-resize" : r === "time" ? "ew-resize" : r === "separator" ? "row-resize" : "");
|
|
7394
|
-
this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
7348
|
+
if (this.drawings) this.deps.drawingsPointerMove?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
|
|
7395
7349
|
}
|
|
7396
7350
|
if (e.pointerType !== "touch") this.deps.onPointerMove(x, y);
|
|
7397
7351
|
};
|
|
@@ -7432,7 +7386,7 @@ var InputController = class {
|
|
|
7432
7386
|
this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
|
|
7433
7387
|
} else if (tapRelease && this.region === "data") {
|
|
7434
7388
|
this.deps.onClick(x, y);
|
|
7435
|
-
} else if (this.dragging && this.region === "data") {
|
|
7389
|
+
} else if (this.dragging && this.region === "data" && wasTouch) {
|
|
7436
7390
|
const stale = e.timeStamp - this.lastT > FLING_STALE_MS;
|
|
7437
7391
|
if (!stale && Math.abs(this.vx) > FLING_MIN_SPEED) {
|
|
7438
7392
|
const pitch = this.deps.getCoords().pxPerBar();
|
|
@@ -7633,7 +7587,7 @@ var InputController = class {
|
|
|
7633
7587
|
}
|
|
7634
7588
|
/** Shared double-click / double-tap routing, by the region under the point. */
|
|
7635
7589
|
doubleActivate(x, y) {
|
|
7636
|
-
if (this.deps.drawingsDblClick?.(x, y)) return;
|
|
7590
|
+
if (this.drawings && this.deps.drawingsDblClick?.(x, y)) return;
|
|
7637
7591
|
const region = this.regionAt(x, y);
|
|
7638
7592
|
if (region === "price") this.deps.resetPriceScale(x, y);
|
|
7639
7593
|
else if (region === "separator") this.deps.resetPaneSize(y);
|
|
@@ -9683,10 +9637,13 @@ function priceTicks(min, max, target = 6) {
|
|
|
9683
9637
|
const norm = raw / mag;
|
|
9684
9638
|
const step = (norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10) * mag;
|
|
9685
9639
|
const decimals = Math.max(0, -Math.floor(Math.log10(step)) + 1);
|
|
9686
|
-
const out = [];
|
|
9687
9640
|
const start = Math.ceil(min / step) * step;
|
|
9688
|
-
|
|
9689
|
-
|
|
9641
|
+
if (!(step > 0) || start + step === start) return [];
|
|
9642
|
+
const count = Math.floor((max - start) / step + 1e-6) + 1;
|
|
9643
|
+
if (!(count > 0) || count > 1e3) return [];
|
|
9644
|
+
const out = [];
|
|
9645
|
+
for (let i = 0; i < count; i += 1) {
|
|
9646
|
+
out.push(Number((start + i * step).toFixed(decimals)));
|
|
9690
9647
|
}
|
|
9691
9648
|
return out;
|
|
9692
9649
|
}
|
|
@@ -9884,6 +9841,100 @@ function calendarTicks(zFrom, zTo, months, offsetMs) {
|
|
|
9884
9841
|
return out;
|
|
9885
9842
|
}
|
|
9886
9843
|
|
|
9844
|
+
// src/renderers/native/chrome/priceAxisTicks.ts
|
|
9845
|
+
var PriceAxisTickSource = class {
|
|
9846
|
+
constructor() {
|
|
9847
|
+
this.fn = null;
|
|
9848
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
9849
|
+
this.warned = false;
|
|
9850
|
+
}
|
|
9851
|
+
/** The host function, or null for the built-in ticks. */
|
|
9852
|
+
get hook() {
|
|
9853
|
+
return this.fn;
|
|
9854
|
+
}
|
|
9855
|
+
/** Set (or clear) the host function. Drops every memoized result, so setting the same
|
|
9856
|
+
* function again recomputes — how a host refreshes ticks that depend on its own state. */
|
|
9857
|
+
setHook(fn) {
|
|
9858
|
+
this.fn = fn;
|
|
9859
|
+
this.memo = /* @__PURE__ */ new WeakMap();
|
|
9860
|
+
}
|
|
9861
|
+
/** The pane's master-scale ticks for this frame. An unscaled pane (`axisFormat: 'none'`)
|
|
9862
|
+
* gets none, and the host is never asked for it. */
|
|
9863
|
+
ticksFor(scene, pane, coords) {
|
|
9864
|
+
const format = pane.axisFormat;
|
|
9865
|
+
if (format === "none") return [];
|
|
9866
|
+
const pct = percentScaleFor(scene, pane);
|
|
9867
|
+
const height = pane.bounds.height;
|
|
9868
|
+
const fn = this.fn;
|
|
9869
|
+
if (!fn || !(height > 0)) return paneAxisTicks(pane.scale, height, pct, scene.priceMintick, format);
|
|
9870
|
+
const s = pane.scale;
|
|
9871
|
+
const m = this.memo.get(pane);
|
|
9872
|
+
const fontSize = scene.style.fontSize;
|
|
9873
|
+
if (m && m.kind === pane.kind && m.min === s.min && m.max === s.max && m.log === !!s.log && m.invert === !!s.invert && m.height === height && m.baseline === pct?.baseline && m.indexed === !!pct?.indexed && m.mintick === scene.priceMintick && m.format === format && m.fontSize === fontSize) {
|
|
9874
|
+
return m.ticks;
|
|
9875
|
+
}
|
|
9876
|
+
const defaults = paneAxisTicks(s, height, pct, scene.priceMintick, format);
|
|
9877
|
+
const ticks = this.callHook(fn, scene, pane, coords, defaults, pct?.baseline, pct ? pct.indexed ? "indexed" : "percent" : "price");
|
|
9878
|
+
this.memo.set(pane, {
|
|
9879
|
+
kind: pane.kind,
|
|
9880
|
+
min: s.min,
|
|
9881
|
+
max: s.max,
|
|
9882
|
+
log: !!s.log,
|
|
9883
|
+
invert: !!s.invert,
|
|
9884
|
+
height,
|
|
9885
|
+
baseline: pct?.baseline,
|
|
9886
|
+
indexed: !!pct?.indexed,
|
|
9887
|
+
mintick: scene.priceMintick,
|
|
9888
|
+
format,
|
|
9889
|
+
fontSize,
|
|
9890
|
+
ticks
|
|
9891
|
+
});
|
|
9892
|
+
return ticks;
|
|
9893
|
+
}
|
|
9894
|
+
callHook(fn, scene, pane, coords, defaults, baseline, mode) {
|
|
9895
|
+
const scale = { min: pane.scale.min, max: pane.scale.max, log: !!pane.scale.log, invert: !!pane.scale.invert };
|
|
9896
|
+
const bounds = { top: 0, height: pane.bounds.height };
|
|
9897
|
+
const ctx = {
|
|
9898
|
+
pane: { id: pane.id, kind: pane.kind },
|
|
9899
|
+
min: Math.min(scale.min, scale.max),
|
|
9900
|
+
max: Math.max(scale.min, scale.max),
|
|
9901
|
+
log: !!scale.log,
|
|
9902
|
+
height: bounds.height,
|
|
9903
|
+
mode,
|
|
9904
|
+
baseline,
|
|
9905
|
+
mintick: scene.priceMintick,
|
|
9906
|
+
fontSize: scene.style.fontSize,
|
|
9907
|
+
priceToY: (price) => coords.priceToY(price, scale, bounds),
|
|
9908
|
+
defaults
|
|
9909
|
+
};
|
|
9910
|
+
let out;
|
|
9911
|
+
try {
|
|
9912
|
+
out = fn(ctx);
|
|
9913
|
+
} catch (err) {
|
|
9914
|
+
this.warnOnce("threw", err);
|
|
9915
|
+
return defaults;
|
|
9916
|
+
}
|
|
9917
|
+
if (out == null) return defaults;
|
|
9918
|
+
if (!Array.isArray(out)) {
|
|
9919
|
+
this.warnOnce("returned a non-array", out);
|
|
9920
|
+
return defaults;
|
|
9921
|
+
}
|
|
9922
|
+
const ticks = [];
|
|
9923
|
+
for (const t of out) {
|
|
9924
|
+
if (!t || typeof t !== "object") continue;
|
|
9925
|
+
const { price, label, major } = t;
|
|
9926
|
+
if (typeof price !== "number" || !Number.isFinite(price) || typeof label !== "string") continue;
|
|
9927
|
+
ticks.push(typeof major === "boolean" ? { price, label, major } : { price, label });
|
|
9928
|
+
}
|
|
9929
|
+
return ticks;
|
|
9930
|
+
}
|
|
9931
|
+
warnOnce(what, detail) {
|
|
9932
|
+
if (this.warned) return;
|
|
9933
|
+
this.warned = true;
|
|
9934
|
+
console.warn(`[vela] the priceAxisTicks function ${what} \u2014 the default ticks are used instead.`, detail);
|
|
9935
|
+
}
|
|
9936
|
+
};
|
|
9937
|
+
|
|
9887
9938
|
// src/renderers/native/chrome/countdown.ts
|
|
9888
9939
|
function countdownText(barOpen, barMs, now2) {
|
|
9889
9940
|
if (!(barMs > 0)) return null;
|
|
@@ -10236,8 +10287,11 @@ function standaloneSvg(markup, ink, px) {
|
|
|
10236
10287
|
}
|
|
10237
10288
|
|
|
10238
10289
|
// src/renderers/native/chrome/ChromeRenderer.ts
|
|
10290
|
+
var MINOR_LABEL_ALPHA = 0.5;
|
|
10239
10291
|
var ChromeRenderer = class {
|
|
10240
|
-
|
|
10292
|
+
/** `axisTicks` is shared with the backdrop layer, so price labels and gridlines agree. */
|
|
10293
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
10294
|
+
this.axisTicks = axisTicks;
|
|
10241
10295
|
this.canvas = null;
|
|
10242
10296
|
this.ctx = null;
|
|
10243
10297
|
// The color for axis tick labels — the host-passed surface text, set each frame in render().
|
|
@@ -10342,7 +10396,8 @@ var ChromeRenderer = class {
|
|
|
10342
10396
|
this.drawTimeAxis(ctx, scene, coords, theme, dataW, dataH, fullH);
|
|
10343
10397
|
this.drawMarkLane(ctx, scene, coords, theme, dataW, dataH);
|
|
10344
10398
|
}
|
|
10345
|
-
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge
|
|
10399
|
+
/** The timeline-mark lane — after the axis, so the tokens read over the plot's bottom edge,
|
|
10400
|
+
* but clipped to the data area so a glyph on the edge bar never paints over the price scale. */
|
|
10346
10401
|
drawMarkLane(ctx, scene, coords, theme, dataW, dataH) {
|
|
10347
10402
|
if (!scene.marks.visible || scene.timelineMarks.length === 0) {
|
|
10348
10403
|
this.markLayout = { glyphs: [], stacks: /* @__PURE__ */ new Map() };
|
|
@@ -10360,6 +10415,10 @@ var ChromeRenderer = class {
|
|
|
10360
10415
|
expanded: scene.marksExpandedStack
|
|
10361
10416
|
});
|
|
10362
10417
|
const nowMs = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
10418
|
+
ctx.save();
|
|
10419
|
+
ctx.beginPath();
|
|
10420
|
+
ctx.rect(0, 0, dataW, dataH);
|
|
10421
|
+
ctx.clip();
|
|
10363
10422
|
paintMarkLane(ctx, this.markLayout, {
|
|
10364
10423
|
axisY: dataH,
|
|
10365
10424
|
background: theme.background,
|
|
@@ -10373,6 +10432,7 @@ var ChromeRenderer = class {
|
|
|
10373
10432
|
flashKey: scene.marksFlash && scene.marksFlash.until > nowMs ? scene.marksFlash.key : null,
|
|
10374
10433
|
nowMs
|
|
10375
10434
|
});
|
|
10435
|
+
ctx.restore();
|
|
10376
10436
|
}
|
|
10377
10437
|
barTimes(scene) {
|
|
10378
10438
|
if (this.barTimesSrc !== scene.bars || this.barTimesCache.length !== scene.bars.length) {
|
|
@@ -10429,13 +10489,19 @@ var ChromeRenderer = class {
|
|
|
10429
10489
|
if (!scene.showAxisLabels) return;
|
|
10430
10490
|
ctx.fillStyle = this.axisTextColor;
|
|
10431
10491
|
ctx.textAlign = "left";
|
|
10492
|
+
const font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
10493
|
+
const majorFont = `600 ${font}`;
|
|
10494
|
+
const alpha = ctx.globalAlpha;
|
|
10432
10495
|
for (const pane of panes) {
|
|
10433
10496
|
if (pane.collapsed) continue;
|
|
10434
|
-
const
|
|
10435
|
-
for (const t of paneAxisTicks(pane.scale, pane.bounds.height, pct, scene.priceMintick, pane.axisFormat)) {
|
|
10497
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
10436
10498
|
const y = coords.priceToY(t.price, pane.scale, pane.bounds);
|
|
10437
10499
|
if (y < pane.bounds.top + 6 || y > pane.bounds.top + pane.bounds.height - 4) continue;
|
|
10500
|
+
if (t.major === true) ctx.font = majorFont;
|
|
10501
|
+
else if (t.major === false) ctx.globalAlpha = alpha * MINOR_LABEL_ALPHA;
|
|
10438
10502
|
ctx.fillText(t.label, dataW + 6, y);
|
|
10503
|
+
if (t.major === true) ctx.font = font;
|
|
10504
|
+
else if (t.major === false) ctx.globalAlpha = alpha;
|
|
10439
10505
|
}
|
|
10440
10506
|
if (pane.axisBands) {
|
|
10441
10507
|
for (const b of pane.axisBands) {
|
|
@@ -10677,15 +10743,23 @@ var CrosshairRenderer = class {
|
|
|
10677
10743
|
constructor() {
|
|
10678
10744
|
this.canvas = null;
|
|
10679
10745
|
this.ctx = null;
|
|
10746
|
+
this.shade = null;
|
|
10680
10747
|
}
|
|
10681
10748
|
mount(canvas) {
|
|
10682
10749
|
this.canvas = canvas;
|
|
10683
10750
|
this.ctx = canvas.getContext("2d");
|
|
10684
10751
|
}
|
|
10752
|
+
/** The plot area the last frame veiled for `crosshairOverride.shadeRight` — from the
|
|
10753
|
+
* picked bar's right edge to the price scale, full plot height (the union when both the
|
|
10754
|
+
* local and a synced crosshair veil) — or null when no veil was painted. */
|
|
10755
|
+
get shadeRightArea() {
|
|
10756
|
+
return this.shade;
|
|
10757
|
+
}
|
|
10685
10758
|
/** Clear the cursor canvas and (re)draw the crosshair lines + axis chips. The optional
|
|
10686
10759
|
* `separatorHoverY` highlights the draggable pane separator under the cursor;
|
|
10687
10760
|
* `external` is a SYNCED ghost crosshair (another chart's pointer, pixel-resolved). */
|
|
10688
10761
|
render(scene, coords, theme, separatorHoverY = null, external = null) {
|
|
10762
|
+
this.shade = null;
|
|
10689
10763
|
const ctx = this.ctx;
|
|
10690
10764
|
const canvas = this.canvas;
|
|
10691
10765
|
if (!ctx || !canvas) return;
|
|
@@ -10708,12 +10782,7 @@ var CrosshairRenderer = class {
|
|
|
10708
10782
|
const x = crisp(coords.logicalToX(logical), ov?.width ?? cs.width);
|
|
10709
10783
|
if (vertical && ov?.shadeRight) {
|
|
10710
10784
|
const from = Math.max(0, Math.round(coords.logicalToX(logical + 0.5)));
|
|
10711
|
-
if (from < dataW)
|
|
10712
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
10713
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
10714
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
10715
|
-
ctx.globalAlpha = 1;
|
|
10716
|
-
}
|
|
10785
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
10717
10786
|
}
|
|
10718
10787
|
ctx.strokeStyle = ov?.color ?? cs.color ?? theme.textColor;
|
|
10719
10788
|
ctx.lineWidth = ov?.width ?? cs.width;
|
|
@@ -10749,6 +10818,7 @@ var CrosshairRenderer = class {
|
|
|
10749
10818
|
destroy() {
|
|
10750
10819
|
this.canvas = null;
|
|
10751
10820
|
this.ctx = null;
|
|
10821
|
+
this.shade = null;
|
|
10752
10822
|
}
|
|
10753
10823
|
/** The synced ghost: a dimmed vertical line at the bar the renderer resolved as
|
|
10754
10824
|
* CONTAINING the foreign time (+ horizontal line when a comparable price came
|
|
@@ -10768,12 +10838,7 @@ var CrosshairRenderer = class {
|
|
|
10768
10838
|
if (ov?.vertical === false) return;
|
|
10769
10839
|
if (ov?.shadeRight) {
|
|
10770
10840
|
const from = Math.max(0, Math.round(coords.logicalToX(Math.round(coords.xToLogical(ext.x)) + 0.5)));
|
|
10771
|
-
if (from < dataW)
|
|
10772
|
-
ctx.fillStyle = ov.shadeRight.color;
|
|
10773
|
-
ctx.globalAlpha = ov.shadeRight.opacity ?? 1;
|
|
10774
|
-
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
10775
|
-
ctx.globalAlpha = 1;
|
|
10776
|
-
}
|
|
10841
|
+
if (from < dataW) this.paintShade(ctx, ov.shadeRight, from, dataW, dataH);
|
|
10777
10842
|
}
|
|
10778
10843
|
if (ext.line === false || x < 0 || x > dataW) return;
|
|
10779
10844
|
ctx.font = `${scene.style.fontSize}px ${theme.fontFamily}`;
|
|
@@ -10809,6 +10874,15 @@ var CrosshairRenderer = class {
|
|
|
10809
10874
|
this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
|
|
10810
10875
|
ctx.globalAlpha = 1;
|
|
10811
10876
|
}
|
|
10877
|
+
/** Paint the `shadeRight` veil from `from` to the price scale and record the veiled area. */
|
|
10878
|
+
paintShade(ctx, shade, from, dataW, dataH) {
|
|
10879
|
+
ctx.fillStyle = shade.color;
|
|
10880
|
+
ctx.globalAlpha = shade.opacity ?? 1;
|
|
10881
|
+
ctx.fillRect(from, 0, dataW - from, dataH);
|
|
10882
|
+
ctx.globalAlpha = 1;
|
|
10883
|
+
const x = this.shade ? Math.min(this.shade.x, from) : from;
|
|
10884
|
+
this.shade = { x, width: dataW - x, height: dataH };
|
|
10885
|
+
}
|
|
10812
10886
|
/** A soft band + a brighter crisp center line over the hovered separator, so it reads as
|
|
10813
10887
|
* a draggable handle (the cursor is already `row-resize`). Spans the full width (data +
|
|
10814
10888
|
* scale gutter) to match the separator itself. Theme-derived (textColor). */
|
|
@@ -13846,6 +13920,12 @@ var DrawingInteraction = class {
|
|
|
13846
13920
|
activeDragId() {
|
|
13847
13921
|
return this.state.kind === "pressed" && this.state.moved ? this.state.id : null;
|
|
13848
13922
|
}
|
|
13923
|
+
/** The store drawings a live drag is moving — the pressed one and its riders — or none
|
|
13924
|
+
* before the drag passes the slop. A Ctrl-drag moves copies, so its sources stay put. */
|
|
13925
|
+
movingIds() {
|
|
13926
|
+
if (this.state.kind !== "pressed" || !this.state.moved || this.state.clones) return /* @__PURE__ */ new Set();
|
|
13927
|
+
return /* @__PURE__ */ new Set([this.state.id, ...this.state.riders.map((r) => r.id)]);
|
|
13928
|
+
}
|
|
13849
13929
|
/** The drawing under a press that has not been released yet (drag or click undecided), or null. */
|
|
13850
13930
|
pressedId() {
|
|
13851
13931
|
return this.state.kind === "pressed" ? this.state.id : null;
|
|
@@ -14220,15 +14300,16 @@ var DrawingSettingsDialog = class {
|
|
|
14220
14300
|
}
|
|
14221
14301
|
open(drawing, actions, kind) {
|
|
14222
14302
|
this.close();
|
|
14223
|
-
const
|
|
14303
|
+
const live = actions.resolve() ?? drawing;
|
|
14304
|
+
const snapshot = clonePlain(live.serialize());
|
|
14224
14305
|
const grid = fieldGrid({ variant: "inputs" });
|
|
14225
14306
|
grid.style.padding = "16px 20px";
|
|
14226
14307
|
grid.style.overflowY = "auto";
|
|
14227
14308
|
grid.style.overflowX = "hidden";
|
|
14228
14309
|
grid.style.flex = "1 1 auto";
|
|
14229
|
-
if (kind === "position" &&
|
|
14230
|
-
else if (kind === "frvp" &&
|
|
14231
|
-
else if (kind === "levels") this.buildLevels(grid,
|
|
14310
|
+
if (kind === "position" && live instanceof PositionTool) this.buildPosition(grid, live, actions);
|
|
14311
|
+
else if (kind === "frvp" && live instanceof FixedRangeVolumeProfile) this.buildFrvp(grid, live, actions);
|
|
14312
|
+
else if (kind === "levels") this.buildLevels(grid, live, actions);
|
|
14232
14313
|
else return;
|
|
14233
14314
|
const ui = new Dialog({
|
|
14234
14315
|
host: this.host,
|
|
@@ -14504,6 +14585,17 @@ var DrawingSettingsDialog = class {
|
|
|
14504
14585
|
}
|
|
14505
14586
|
}));
|
|
14506
14587
|
}
|
|
14588
|
+
if (drawing instanceof FibRetracement) {
|
|
14589
|
+
const fib = drawing;
|
|
14590
|
+
grid.appendChild(fieldRow({
|
|
14591
|
+
label: "Reverse",
|
|
14592
|
+
bool: true,
|
|
14593
|
+
toggle: {
|
|
14594
|
+
checked: fib.reverse,
|
|
14595
|
+
onChange: (v) => actions.patch({ reverse: v })
|
|
14596
|
+
}
|
|
14597
|
+
}));
|
|
14598
|
+
}
|
|
14507
14599
|
levels.forEach((lv, i) => {
|
|
14508
14600
|
const row = document.createElement("div");
|
|
14509
14601
|
row.className = "vela-field-span";
|
|
@@ -14864,7 +14956,7 @@ var DrawingSettingsPopup = class {
|
|
|
14864
14956
|
this.reposition();
|
|
14865
14957
|
return;
|
|
14866
14958
|
}
|
|
14867
|
-
const text = drawing.text;
|
|
14959
|
+
const text = (actions.resolve() ?? drawing).text;
|
|
14868
14960
|
const panel = document.createElement("div");
|
|
14869
14961
|
panel.style.cssText = `padding:6px;border-top:1px solid var(--vela-border);display:flex;flex-direction:column;gap:4px;`;
|
|
14870
14962
|
const ta = new TextArea({
|
|
@@ -15657,6 +15749,8 @@ var UserDrawingController = class {
|
|
|
15657
15749
|
this.drawings = [];
|
|
15658
15750
|
/** Ids painted on an interleave layer this frame — the top canvas paints only their handles. */
|
|
15659
15751
|
this.sliced = /* @__PURE__ */ new Set();
|
|
15752
|
+
/** Interleaved drawings mid-drag: painted on the top canvas, left out of their slice. */
|
|
15753
|
+
this.lifted = /* @__PURE__ */ new Set();
|
|
15660
15754
|
/** Cached slice canvases, keyed `paneId|beforeZ`, reused across frames to avoid churn. */
|
|
15661
15755
|
this.sliceCache = /* @__PURE__ */ new Map();
|
|
15662
15756
|
/** Series boundaries per pane as of the last `prepareSlices` — lets a repaint between data
|
|
@@ -15744,7 +15838,7 @@ var UserDrawingController = class {
|
|
|
15744
15838
|
selectedIds: () => this.selectedIds,
|
|
15745
15839
|
emit: (i) => this.emit(i),
|
|
15746
15840
|
changed: () => {
|
|
15747
|
-
this.
|
|
15841
|
+
this.syncLift();
|
|
15748
15842
|
this.render();
|
|
15749
15843
|
},
|
|
15750
15844
|
openSettings: (id, x, y) => this.openSettingsById(id, x, y),
|
|
@@ -16033,6 +16127,12 @@ var UserDrawingController = class {
|
|
|
16033
16127
|
this.render();
|
|
16034
16128
|
}
|
|
16035
16129
|
}
|
|
16130
|
+
/** Drop the hover (its handles) — the pointer no longer reaches the drawings. */
|
|
16131
|
+
clearHover() {
|
|
16132
|
+
if (this.hoveredId == null) return;
|
|
16133
|
+
this.hoveredId = null;
|
|
16134
|
+
this.render();
|
|
16135
|
+
}
|
|
16036
16136
|
pointerUp(x, y, snap = "off") {
|
|
16037
16137
|
if (this.eraserMode) {
|
|
16038
16138
|
this.erasing = false;
|
|
@@ -16370,6 +16470,20 @@ var UserDrawingController = class {
|
|
|
16370
16470
|
/** A drawing that paints inside the series stack changed (content, not hover): its pixels
|
|
16371
16471
|
* live in the backend composite, so this layer alone can't show the change — ask for a
|
|
16372
16472
|
* data frame, which re-runs `prepareSlices` before the backend composites. */
|
|
16473
|
+
/** A drawing being dragged leaves its interleave slice for the top canvas, so each pointer
|
|
16474
|
+
* move repaints one 2D layer instead of re-rasterizing and re-uploading a plot-sized slice
|
|
16475
|
+
* texture through the data frame. The slices rebuild once as the drag starts and once as it
|
|
16476
|
+
* ends (release, cancel), when the lifted set changes. */
|
|
16477
|
+
syncLift() {
|
|
16478
|
+
const moving = this.interaction.movingIds();
|
|
16479
|
+
let same = moving.size === this.lifted.size;
|
|
16480
|
+
if (same) {
|
|
16481
|
+
for (const id of moving) if (!this.lifted.has(id)) same = false;
|
|
16482
|
+
}
|
|
16483
|
+
if (same) return;
|
|
16484
|
+
this.lifted = new Set(moving);
|
|
16485
|
+
this.invalidateSlices();
|
|
16486
|
+
}
|
|
16373
16487
|
invalidateSlices() {
|
|
16374
16488
|
if (this.sliced.size > 0 || this.drawings.some((d) => this.isInterleaved(d))) this.deps.requestDataPaint();
|
|
16375
16489
|
}
|
|
@@ -16388,7 +16502,7 @@ var UserDrawingController = class {
|
|
|
16388
16502
|
const theme = this.deps.theme();
|
|
16389
16503
|
const buckets = /* @__PURE__ */ new Map();
|
|
16390
16504
|
for (const d of this.drawings) {
|
|
16391
|
-
if (!d.visible) continue;
|
|
16505
|
+
if (!d.visible || this.lifted.has(d.id)) continue;
|
|
16392
16506
|
const beforeZ = sliceKeyFor(d.zIndex, this.lastBounds.get(d.paneId) ?? []);
|
|
16393
16507
|
if (beforeZ === null) continue;
|
|
16394
16508
|
const key = `${d.paneId}|${beforeZ}`;
|
|
@@ -16440,8 +16554,9 @@ var UserDrawingController = class {
|
|
|
16440
16554
|
mutedLabel: edited instanceof TextLabel ? edited.id : null
|
|
16441
16555
|
};
|
|
16442
16556
|
this.painter.seriesLook = this.deps.seriesLook();
|
|
16443
|
-
|
|
16444
|
-
this.painter.
|
|
16557
|
+
const onTop = (d) => !this.isInterleaved(d) || this.lifted.has(d.id);
|
|
16558
|
+
this.painter.paintAll(ctx, this.drawings.filter(onTop), proj, this.deps.theme(), targets);
|
|
16559
|
+
this.painter.paintHighlights(ctx, this.drawings.filter((d) => !onTop(d)), proj, handleIdsFor(targets));
|
|
16445
16560
|
const clones = this.interaction.dragClones();
|
|
16446
16561
|
if (clones) this.painter.paintAll(ctx, clones, proj, this.deps.theme(), { selected: new Set(clones.map((c) => c.id)) });
|
|
16447
16562
|
this.layoutTextEditor();
|
|
@@ -17008,9 +17123,10 @@ function computePaneScale(models, bars, includeCandles, i0, i1, drawings, log =
|
|
|
17008
17123
|
consider(drawings.max);
|
|
17009
17124
|
}
|
|
17010
17125
|
if (min === Infinity || max === -Infinity) return { min: 0, max: 1 };
|
|
17011
|
-
if (min
|
|
17012
|
-
const
|
|
17013
|
-
|
|
17126
|
+
if (max - min <= Math.max(Math.abs(min), Math.abs(max)) * 1e-12) {
|
|
17127
|
+
const mid = (min + max) / 2;
|
|
17128
|
+
const pad = Math.abs(mid) * 0.1 || 1;
|
|
17129
|
+
return { min: mid - pad, max: mid + pad, log: log && mid - pad > 0 };
|
|
17014
17130
|
}
|
|
17015
17131
|
const content = Math.max(0.1, 1 - (margins.top + margins.bottom) / 100);
|
|
17016
17132
|
const above = margins.top / 100 / content;
|
|
@@ -17514,7 +17630,9 @@ function bandEdgeSlot(logical) {
|
|
|
17514
17630
|
return Math.ceil(logical) - 0.5;
|
|
17515
17631
|
}
|
|
17516
17632
|
var BackdropRenderer = class {
|
|
17517
|
-
|
|
17633
|
+
/** `axisTicks` is shared with the chrome layer, so gridlines and price labels agree. */
|
|
17634
|
+
constructor(axisTicks = new PriceAxisTickSource()) {
|
|
17635
|
+
this.axisTicks = axisTicks;
|
|
17518
17636
|
this.canvas = null;
|
|
17519
17637
|
this.ctx = null;
|
|
17520
17638
|
}
|
|
@@ -17591,9 +17709,8 @@ var BackdropRenderer = class {
|
|
|
17591
17709
|
if (scene.showGrid && gridHorz.visible && !pane.collapsed) {
|
|
17592
17710
|
ctx.globalAlpha = gridAlpha;
|
|
17593
17711
|
ctx.strokeStyle = horzColor;
|
|
17594
|
-
const pct = percentScaleFor(scene, pane);
|
|
17595
17712
|
ctx.beginPath();
|
|
17596
|
-
for (const t of
|
|
17713
|
+
for (const t of this.axisTicks.ticksFor(scene, pane, coords)) {
|
|
17597
17714
|
const y = Math.round(coords.priceToY(t.price, pane.scale, pane.bounds)) + 0.5;
|
|
17598
17715
|
if (y < pane.bounds.top || y > pane.bounds.top + pane.bounds.height) continue;
|
|
17599
17716
|
ctx.moveTo(0, y);
|
|
@@ -18183,8 +18300,16 @@ var NativeRenderer = class {
|
|
|
18183
18300
|
this.toolbarGutter = 0;
|
|
18184
18301
|
// px reserved on the left for the docked drawings toolbar (0 when hidden)
|
|
18185
18302
|
this.mountContainer = null;
|
|
18303
|
+
/** The host-styleable twin of the `shadeRight` veil (`.vela-shade-right`), directly under
|
|
18304
|
+
* the cursor canvas; hidden whenever the crosshair layer paints no veil. */
|
|
18305
|
+
this.shadeRightEl = null;
|
|
18306
|
+
/** The veil geometry last written to {@link shadeRightEl} ('' = hidden) — the DOM is
|
|
18307
|
+
* touched only when it changes, never on a plain pointer move. */
|
|
18308
|
+
this.shadeRightSig = "";
|
|
18186
18309
|
this.userDrawings = null;
|
|
18187
|
-
|
|
18310
|
+
/** One price-axis tick source for the gridlines (backdrop) and the labels (chrome). */
|
|
18311
|
+
this.priceAxisTicks = new PriceAxisTickSource();
|
|
18312
|
+
this.backdropRenderer = new BackdropRenderer(this.priceAxisTicks);
|
|
18188
18313
|
this.volumeRenderer = new VolumeRenderer();
|
|
18189
18314
|
/** SDK renderer layers instantiated at mount ({@link registerRendererLayer}). */
|
|
18190
18315
|
/**
|
|
@@ -18217,7 +18342,7 @@ var NativeRenderer = class {
|
|
|
18217
18342
|
this.backendMode = "auto";
|
|
18218
18343
|
this.glowAmount = 0;
|
|
18219
18344
|
// WebGL2 neon-glow intensity (canvas2d ignores it)
|
|
18220
|
-
this.chrome = new ChromeRenderer();
|
|
18345
|
+
this.chrome = new ChromeRenderer(this.priceAxisTicks);
|
|
18221
18346
|
/** Prepaints each indicator's Pine drawings into interleave slices at the model's z. */
|
|
18222
18347
|
this.indicatorSlices = new IndicatorDrawingSlices();
|
|
18223
18348
|
/** Hover tooltips for Pine labels (canvas hit-rects collected by the chrome layer). */
|
|
@@ -18334,6 +18459,8 @@ var NativeRenderer = class {
|
|
|
18334
18459
|
// pane just below it
|
|
18335
18460
|
this.resizeSplitStart = null;
|
|
18336
18461
|
// the two panes' shared span when the drag began
|
|
18462
|
+
/** The user drawings take part in pointer input (the runtime `drawingsInteractive` feature). */
|
|
18463
|
+
this.drawingsInteractive = true;
|
|
18337
18464
|
this.hoverSeparatorY = null;
|
|
18338
18465
|
// pixel y of the separator under the cursor (drives its hover highlight)
|
|
18339
18466
|
// ── pane management (merge / reorder / collapse / maximize) ──
|
|
@@ -18389,8 +18516,9 @@ var NativeRenderer = class {
|
|
|
18389
18516
|
this.toggleVisibleCbs = /* @__PURE__ */ new Set();
|
|
18390
18517
|
this.moveIndicatorCbs = /* @__PURE__ */ new Set();
|
|
18391
18518
|
this.priceStyleCbs = /* @__PURE__ */ new Set();
|
|
18519
|
+
this.priceStyleWillChangeCbs = /* @__PURE__ */ new Set();
|
|
18392
18520
|
this.name = "native";
|
|
18393
|
-
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride"];
|
|
18521
|
+
this.features = ["logScale", "currentPriceLine", "priceLabel", "countdown", "upColor", "downColor", "glow", "animZoom", "animPan", "animScroll", "animAutoscale", "animLiveBar", "intro", "zoomAnchor", "axisDrag", "paneResize", "candleZOrder", "candleVisible", "seriesOrder", "highlights", "sessionZones", "gridlines", "axisLabels", "scaleMode", "invertScale", "paneScales", "autoScale", "timezone", "keyboard", "historyChords", "priceStyle", "priceBaseline", "baselinePrice", "settings", "attribution", "dialogHost", "tradeMarkers", "marks", "indicatorTitles", "indicatorValues", "crosshairOverride", "drawingsInteractive", "priceAxisTicks"];
|
|
18394
18522
|
/** Track cursor proximity to the scroll button on the plot (bubbles from the button too,
|
|
18395
18523
|
* so moving onto the button doesn't count as leaving). */
|
|
18396
18524
|
this.onScrollProximityMove = (e) => {
|
|
@@ -18434,6 +18562,7 @@ var NativeRenderer = class {
|
|
|
18434
18562
|
this.scene.priceStyle = opts.priceStyle;
|
|
18435
18563
|
this.scene.basePainting = basePaintingOf(opts.priceStyle);
|
|
18436
18564
|
this.scene.candleOverride = candleOverrideFor(opts.priceStyle, this.scene.style.chartTypes);
|
|
18565
|
+
if (typeof opts.priceAxisTicks === "function") this.priceAxisTicks.setHook(opts.priceAxisTicks);
|
|
18437
18566
|
}
|
|
18438
18567
|
this.theme = this.deriveTheme(DARK_THEME);
|
|
18439
18568
|
}
|
|
@@ -18572,6 +18701,15 @@ var NativeRenderer = class {
|
|
|
18572
18701
|
case "crosshairOverride":
|
|
18573
18702
|
this.scene.crosshairOverride = sanitizeCrosshairOverride(value);
|
|
18574
18703
|
break;
|
|
18704
|
+
case "drawingsInteractive":
|
|
18705
|
+
this.drawingsInteractive = value !== false;
|
|
18706
|
+
if (this.input) this.input.drawings = this.drawingsInteractive;
|
|
18707
|
+
if (!this.drawingsInteractive) this.userDrawings?.clearHover();
|
|
18708
|
+
return;
|
|
18709
|
+
// affects the next gesture only — clearHover repaints what it drops
|
|
18710
|
+
case "priceAxisTicks":
|
|
18711
|
+
this.priceAxisTicks.setHook(typeof value === "function" ? value : null);
|
|
18712
|
+
break;
|
|
18575
18713
|
case "keyboard":
|
|
18576
18714
|
this.setKeyboardEnabled(Boolean(value));
|
|
18577
18715
|
return;
|
|
@@ -18687,6 +18825,10 @@ var NativeRenderer = class {
|
|
|
18687
18825
|
return { visible: this.scene.marks.visible, groups: { ...this.scene.marks.groups } };
|
|
18688
18826
|
case "crosshairOverride":
|
|
18689
18827
|
return this.scene.crosshairOverride ? { ...this.scene.crosshairOverride } : null;
|
|
18828
|
+
case "drawingsInteractive":
|
|
18829
|
+
return this.drawingsInteractive;
|
|
18830
|
+
case "priceAxisTicks":
|
|
18831
|
+
return this.priceAxisTicks.hook;
|
|
18690
18832
|
case "keyboard":
|
|
18691
18833
|
return this.keyboardEnabled;
|
|
18692
18834
|
case "historyChords":
|
|
@@ -19360,6 +19502,12 @@ var NativeRenderer = class {
|
|
|
19360
19502
|
Object.assign(this.drawingsCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
19361
19503
|
this.cursorCanvas = document.createElement("canvas");
|
|
19362
19504
|
Object.assign(this.cursorCanvas.style, { position: "absolute", inset: "0", width: "100%", height: "100%", pointerEvents: "none" });
|
|
19505
|
+
this.shadeRightEl = document.createElement("div");
|
|
19506
|
+
this.shadeRightEl.className = "vela-shade-right";
|
|
19507
|
+
this.shadeRightEl.setAttribute("aria-hidden", "true");
|
|
19508
|
+
this.shadeRightEl.hidden = true;
|
|
19509
|
+
Object.assign(this.shadeRightEl.style, { position: "absolute", top: "0", left: "0", width: "0", height: "0", pointerEvents: "none", display: "none" });
|
|
19510
|
+
this.shadeRightSig = "";
|
|
19363
19511
|
this.overlayRoot = document.createElement("div");
|
|
19364
19512
|
Object.assign(this.overlayRoot.style, { position: "absolute", inset: "0", pointerEvents: "none" });
|
|
19365
19513
|
this.plot = document.createElement("div");
|
|
@@ -19367,7 +19515,7 @@ var NativeRenderer = class {
|
|
|
19367
19515
|
this.extLayers = rendererLayers().map((def) => ({ def, instance: def.create(), canvas: this.createLayerCanvas(), channel: def.id, owner: null }));
|
|
19368
19516
|
const below = this.extLayers.filter((l) => l.def.placement === "below-data").map((l) => l.canvas);
|
|
19369
19517
|
const above = this.extLayers.filter((l) => l.def.placement !== "below-data").map((l) => l.canvas);
|
|
19370
|
-
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.cursorCanvas, this.overlayRoot);
|
|
19518
|
+
this.plot.append(this.backdropCanvas, ...below, this.dataCanvas, this.volumeCanvas, this.vpvrCanvas, ...above, this.chromeCanvas, this.drawingsCanvas, this.shadeRightEl, this.cursorCanvas, this.overlayRoot);
|
|
19371
19519
|
this.layerOrderSig = "";
|
|
19372
19520
|
this.wrapper.appendChild(this.plot);
|
|
19373
19521
|
this.factoryConfig = this.getConfig();
|
|
@@ -19425,13 +19573,14 @@ var NativeRenderer = class {
|
|
|
19425
19573
|
drawingsPointerDown: (x, y, snap, shift2, mod) => this.userDrawings?.pointerDown(x, y, snap, shift2, mod),
|
|
19426
19574
|
drawingsPointerMove: (x, y, snap, shift2, mod) => this.userDrawings?.pointerMove(x, y, snap, shift2, mod),
|
|
19427
19575
|
drawingsPointerUp: (x, y, snap) => this.userDrawings?.pointerUp(x, y, snap),
|
|
19428
|
-
drawingsCursor: (x, y) => this.userDrawings?.cursorAt(x, y) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
19576
|
+
drawingsCursor: (x, y) => (this.drawingsInteractive ? this.userDrawings?.cursorAt(x, y) : null) ?? (this.chrome.markGlyphAt(x, y) ? "pointer" : null),
|
|
19429
19577
|
drawingsDblClick: (x, y) => this.userDrawings?.dblClick(x, y) ?? false,
|
|
19430
19578
|
drawingsClearTransient: () => this.userDrawings?.clearTransient()
|
|
19431
19579
|
});
|
|
19432
19580
|
this.input.rightEdgeZoom = this.zoomAnchorMode === "right";
|
|
19433
19581
|
this.input.axisDrag = this.axisDragEnabled;
|
|
19434
19582
|
this.input.paneResize = this.paneResizeEnabled;
|
|
19583
|
+
this.input.drawings = this.drawingsInteractive;
|
|
19435
19584
|
this.input.attach(this.dataCanvas);
|
|
19436
19585
|
if (this.settingsEnabled) this.setSettingsEnabled(true);
|
|
19437
19586
|
this.syncCountdownTimer();
|
|
@@ -19740,6 +19889,8 @@ var NativeRenderer = class {
|
|
|
19740
19889
|
this.backend.destroy();
|
|
19741
19890
|
this.chrome.destroy();
|
|
19742
19891
|
this.crosshairLayer.destroy();
|
|
19892
|
+
this.shadeRightEl?.remove();
|
|
19893
|
+
this.shadeRightEl = null;
|
|
19743
19894
|
this.attributionEl?.remove();
|
|
19744
19895
|
this.attributionEl = null;
|
|
19745
19896
|
this.mountContainer?.style.removeProperty("--vela-toolbar-gutter");
|
|
@@ -20086,14 +20237,21 @@ var NativeRenderer = class {
|
|
|
20086
20237
|
this.priceStyleCbs.add(cb);
|
|
20087
20238
|
return () => this.priceStyleCbs.delete(cb);
|
|
20088
20239
|
}
|
|
20240
|
+
onPriceStyleWillChange(cb) {
|
|
20241
|
+
this.priceStyleWillChangeCbs.add(cb);
|
|
20242
|
+
return () => this.priceStyleWillChangeCbs.delete(cb);
|
|
20243
|
+
}
|
|
20089
20244
|
/**
|
|
20090
20245
|
* THE single write path for the base price style at runtime (feature set / settings dialog /
|
|
20091
|
-
* config template — the constructor seeds the field directly, pre-listeners).
|
|
20092
|
-
*
|
|
20093
|
-
*
|
|
20246
|
+
* config template — the constructor seeds the field directly, pre-listeners). Announces the
|
|
20247
|
+
* switch while the scene still holds the old style (a listener may paint/capture that frame),
|
|
20248
|
+
* then updates the scene, eases any reveal layer toward the new style's target, and notifies
|
|
20249
|
+
* the core — which owns the DATA side of styles that need one (a chart type's SeriesDataEngine).
|
|
20094
20250
|
*/
|
|
20095
20251
|
setPriceStyle(style) {
|
|
20096
20252
|
if (style === this.scene.priceStyle) return;
|
|
20253
|
+
const from = this.scene.priceStyle;
|
|
20254
|
+
for (const cb of this.priceStyleWillChangeCbs) cb(from, style);
|
|
20097
20255
|
this.scene.priceStyle = style;
|
|
20098
20256
|
this.scene.basePainting = basePaintingOf(style);
|
|
20099
20257
|
this.scene.candleOverride = candleOverrideFor(style, this.scene.style.chartTypes);
|
|
@@ -20323,7 +20481,7 @@ var NativeRenderer = class {
|
|
|
20323
20481
|
if (this.easeLiveBar(dtMs)) active = true;
|
|
20324
20482
|
this.skeletonClockMs += dtMs;
|
|
20325
20483
|
this.paintData();
|
|
20326
|
-
this.
|
|
20484
|
+
this.renderCrosshairLayer();
|
|
20327
20485
|
this.updateLegendValues();
|
|
20328
20486
|
this.emitViewportChange();
|
|
20329
20487
|
return active;
|
|
@@ -20898,10 +21056,31 @@ var NativeRenderer = class {
|
|
|
20898
21056
|
this.chrome.render(this.scene, this.coords, this.theme, this.axisSurface());
|
|
20899
21057
|
this.trackMarkPopover();
|
|
20900
21058
|
}
|
|
20901
|
-
this.
|
|
21059
|
+
this.renderCrosshairLayer();
|
|
20902
21060
|
if (!repaintsData(level) && this.paintedData) this.repaintCursorLayers();
|
|
20903
21061
|
this.updateLegendValues();
|
|
20904
21062
|
}
|
|
21063
|
+
/** Repaint the L2 crosshair canvas and keep the `.vela-shade-right` element on the veil it painted. */
|
|
21064
|
+
renderCrosshairLayer() {
|
|
21065
|
+
this.crosshairLayer.render(this.scene, this.coords, this.theme, this.hoverSeparatorY, this.externalCrossPx());
|
|
21066
|
+
this.syncShadeRight();
|
|
21067
|
+
}
|
|
21068
|
+
/** Position (or hide) the `.vela-shade-right` element over the veil of the last crosshair frame. */
|
|
21069
|
+
syncShadeRight() {
|
|
21070
|
+
const el = this.shadeRightEl;
|
|
21071
|
+
if (!el) return;
|
|
21072
|
+
const area = this.crosshairLayer.shadeRightArea;
|
|
21073
|
+
const sig = area ? `${area.x},${area.width},${area.height}` : "";
|
|
21074
|
+
if (sig === this.shadeRightSig) return;
|
|
21075
|
+
this.shadeRightSig = sig;
|
|
21076
|
+
if (!area) {
|
|
21077
|
+
el.hidden = true;
|
|
21078
|
+
el.style.display = "none";
|
|
21079
|
+
return;
|
|
21080
|
+
}
|
|
21081
|
+
Object.assign(el.style, { left: `${area.x}px`, width: `${area.width}px`, height: `${area.height}px`, display: "" });
|
|
21082
|
+
el.hidden = false;
|
|
21083
|
+
}
|
|
20905
21084
|
/** Repaint the SDK layers that opted into cursor tracking (their own canvas only). */
|
|
20906
21085
|
repaintCursorLayers() {
|
|
20907
21086
|
const pane = this.scene.panes.get(PRICE_PANE_ID);
|
|
@@ -21695,7 +21874,7 @@ var NativeRenderer = class {
|
|
|
21695
21874
|
if (this.animator.active) {
|
|
21696
21875
|
this.computeScales();
|
|
21697
21876
|
this.paintData();
|
|
21698
|
-
this.
|
|
21877
|
+
this.renderCrosshairLayer();
|
|
21699
21878
|
} else {
|
|
21700
21879
|
this.scheduler.flushNow(4 /* Full */);
|
|
21701
21880
|
}
|
|
@@ -21904,7 +22083,7 @@ var IndicatorHandleImpl = class {
|
|
|
21904
22083
|
this.controller.applyProps(this.id, values);
|
|
21905
22084
|
}
|
|
21906
22085
|
updateCode(source) {
|
|
21907
|
-
this.controller.updateCode(this.id, source);
|
|
22086
|
+
return this.controller.updateCode(this.id, source);
|
|
21908
22087
|
}
|
|
21909
22088
|
setVisible(visible) {
|
|
21910
22089
|
this.controller.setVisible(this.id, visible);
|
|
@@ -22335,6 +22514,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
22335
22514
|
this.activeEngineStyle = null;
|
|
22336
22515
|
/** The chart's current price style (tracked from the renderer's change events + initial read). */
|
|
22337
22516
|
this.priceStyle = "candles";
|
|
22517
|
+
this.priceStyleWillChangeUnsub = null;
|
|
22338
22518
|
this.unresolvedUnsub = null;
|
|
22339
22519
|
/** Latest chart visible range (left/right bar times), fed to viewport-dependent scripts. */
|
|
22340
22520
|
this.visibleRange = null;
|
|
@@ -22433,6 +22613,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
22433
22613
|
this.renderer.onThemeSelect?.((name) => this.setTheme(resolveTheme(name)));
|
|
22434
22614
|
this.viewportUnsub = this.renderer.onViewportChange((range) => this.onViewportChange(range));
|
|
22435
22615
|
this.renderer.onPriceStyleChange?.((style) => this.syncPriceStyle(style));
|
|
22616
|
+
this.priceStyleWillChangeUnsub = this.renderer.onPriceStyleWillChange?.((from, to) => this.events.emit("priceStyle:change", { from, to })) ?? null;
|
|
22436
22617
|
const initialStyle = this.renderer.readFeature("priceStyle");
|
|
22437
22618
|
if (typeof initialStyle === "string") this.priceStyle = initialStyle;
|
|
22438
22619
|
this.barTransform = barTransformFor(initialStyle);
|
|
@@ -23719,20 +23900,26 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23719
23900
|
/**
|
|
23720
23901
|
* IndicatorController: replace a SCRIPT indicator's source in place (see
|
|
23721
23902
|
* {@link IndicatorHandle.updateCode}). Prepare-first: the running session keeps
|
|
23722
|
-
* computing until the new source has compiled
|
|
23723
|
-
*
|
|
23903
|
+
* computing until the new source has compiled; then the new code runs on TRIAL until
|
|
23904
|
+
* its first model lands, and a failure before that puts the previous code back.
|
|
23905
|
+
* Natives have no script — warn and leave them alone.
|
|
23724
23906
|
*/
|
|
23725
23907
|
updateCode(id, source) {
|
|
23726
23908
|
const record = this.registry.get(id);
|
|
23727
23909
|
const handle = this.handles.get(id);
|
|
23728
|
-
if (!record || !handle) return;
|
|
23910
|
+
if (!record || !handle) return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator is no longer on the chart.`) });
|
|
23729
23911
|
if (record.native) {
|
|
23730
23912
|
console.warn(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`);
|
|
23731
|
-
return;
|
|
23913
|
+
return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`) });
|
|
23732
23914
|
}
|
|
23733
|
-
|
|
23734
|
-
record.
|
|
23735
|
-
|
|
23915
|
+
const busy = record.pendingSource !== void 0 || record.codeTrial !== void 0;
|
|
23916
|
+
if (!busy && source === record.source) return Promise.resolve({ ok: true, error: null });
|
|
23917
|
+
return new Promise((resolve) => {
|
|
23918
|
+
(record.codeWaiters ?? (record.codeWaiters = [])).push(resolve);
|
|
23919
|
+
if (record.pendingSource === source || record.pendingSource === void 0 && record.codeTrial && record.source === source) return;
|
|
23920
|
+
record.pendingSource = source;
|
|
23921
|
+
void this.swapSource(id, source, handle);
|
|
23922
|
+
});
|
|
23736
23923
|
}
|
|
23737
23924
|
async swapSource(id, source, handle) {
|
|
23738
23925
|
const engine = this.registry.get(id)?.engine ?? this.engineFor(this.registry.get(id)?.options?.language);
|
|
@@ -23743,26 +23930,41 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23743
23930
|
const record2 = this.registry.get(id);
|
|
23744
23931
|
if (record2?.pendingSource === source) {
|
|
23745
23932
|
record2.pendingSource = void 0;
|
|
23746
|
-
this.
|
|
23933
|
+
this.rejectCode(id, record2, handle, err);
|
|
23747
23934
|
}
|
|
23748
23935
|
return;
|
|
23749
23936
|
}
|
|
23750
23937
|
const record = this.registry.get(id);
|
|
23751
23938
|
if (!record || record.pendingSource !== source) return;
|
|
23752
23939
|
record.pendingSource = void 0;
|
|
23940
|
+
const previous = record.codeTrial?.previous ?? {
|
|
23941
|
+
source: record.source,
|
|
23942
|
+
engine: record.engine,
|
|
23943
|
+
prepared: record.prepared,
|
|
23944
|
+
inputValues: record.inputValues,
|
|
23945
|
+
propValues: record.propValues,
|
|
23946
|
+
inputs: [...handle.inputs],
|
|
23947
|
+
props: [...handle.props]
|
|
23948
|
+
};
|
|
23949
|
+
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues, record.prepared?.inputs);
|
|
23950
|
+
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues, record.prepared?.props);
|
|
23753
23951
|
record.source = source;
|
|
23754
23952
|
record.engine = engine;
|
|
23755
23953
|
record.prepared = prepared;
|
|
23756
23954
|
handle.setSource(source);
|
|
23757
|
-
record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues);
|
|
23758
|
-
record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues);
|
|
23759
23955
|
handle.setSchema(prepared.inputs);
|
|
23760
23956
|
handle.setPropsSchema(prepared.props ?? []);
|
|
23761
23957
|
record.session?.stop();
|
|
23762
23958
|
record.session = void 0;
|
|
23763
23959
|
record.pendingStructural = true;
|
|
23764
23960
|
record.pendingCause = "code";
|
|
23765
|
-
if (record.hidden)
|
|
23961
|
+
if (record.hidden) {
|
|
23962
|
+
record.codeTrial = void 0;
|
|
23963
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
23964
|
+
this.settleCode(record, { ok: true, error: null });
|
|
23965
|
+
return;
|
|
23966
|
+
}
|
|
23967
|
+
record.codeTrial = { previous };
|
|
23766
23968
|
if (record.renderHandle) {
|
|
23767
23969
|
this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
23768
23970
|
this.setLoading(record, true);
|
|
@@ -23770,6 +23972,46 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23770
23972
|
this.mountLoadingPlaceholder(id, record);
|
|
23771
23973
|
}
|
|
23772
23974
|
this.executeIndicator(id, handle);
|
|
23975
|
+
if (this.bars.length === 0 && record.codeTrial) {
|
|
23976
|
+
record.codeTrial = void 0;
|
|
23977
|
+
this.settleCode(record, { ok: true, error: null });
|
|
23978
|
+
}
|
|
23979
|
+
}
|
|
23980
|
+
/** Resolve every `updateCode` caller waiting on this record's chain of edits. */
|
|
23981
|
+
settleCode(record, result) {
|
|
23982
|
+
const waiters = record.codeWaiters;
|
|
23983
|
+
record.codeWaiters = void 0;
|
|
23984
|
+
if (waiters) for (const resolve of waiters) resolve(result);
|
|
23985
|
+
}
|
|
23986
|
+
/**
|
|
23987
|
+
* An update failed — at prepare, or on trial before its first model. A trial puts the
|
|
23988
|
+
* previous code back (source, engine, prepared script, values, schema) and restarts it;
|
|
23989
|
+
* its visuals were never dropped, so the chart keeps painting throughout. Then the
|
|
23990
|
+
* failure reports the usual way and every waiting caller learns it.
|
|
23991
|
+
*/
|
|
23992
|
+
rejectCode(id, record, handle, err) {
|
|
23993
|
+
const trial = record.codeTrial;
|
|
23994
|
+
record.codeTrial = void 0;
|
|
23995
|
+
this.fail(id, handle, err);
|
|
23996
|
+
if (trial) {
|
|
23997
|
+
const p = trial.previous;
|
|
23998
|
+
record.session?.stop();
|
|
23999
|
+
record.session = void 0;
|
|
24000
|
+
record.source = p.source;
|
|
24001
|
+
record.engine = p.engine;
|
|
24002
|
+
record.prepared = p.prepared;
|
|
24003
|
+
record.inputValues = p.inputValues;
|
|
24004
|
+
record.propValues = p.propValues;
|
|
24005
|
+
handle.setSource(p.source);
|
|
24006
|
+
handle.setSchema(p.inputs);
|
|
24007
|
+
handle.setPropsSchema(p.props);
|
|
24008
|
+
record.pendingStructural = true;
|
|
24009
|
+
record.pendingCause = void 0;
|
|
24010
|
+
if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
|
|
24011
|
+
if (!record.hidden) this.executeIndicator(id, handle);
|
|
24012
|
+
}
|
|
24013
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
24014
|
+
this.settleCode(record, { ok: false, error });
|
|
23773
24015
|
}
|
|
23774
24016
|
/** IndicatorController: tear down an indicator and (if now empty) its pane. */
|
|
23775
24017
|
/** Live handles of every indicator on the chart (script + native), insertion order. */
|
|
@@ -23784,6 +24026,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23784
24026
|
removeIndicator(id) {
|
|
23785
24027
|
const record = this.registry.remove(id);
|
|
23786
24028
|
if (record?.native?.type === "volume") this.volumeOptedOut = true;
|
|
24029
|
+
if (record) this.settleCode(record, { ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator was removed.`) });
|
|
23787
24030
|
record?.session?.stop();
|
|
23788
24031
|
if (record?.native?.started) record.native.instance.stop();
|
|
23789
24032
|
this.handles.delete(id);
|
|
@@ -23811,6 +24054,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23811
24054
|
if (!visible) {
|
|
23812
24055
|
record.session?.stop();
|
|
23813
24056
|
record.session = void 0;
|
|
24057
|
+
if (record.codeTrial) {
|
|
24058
|
+
record.codeTrial = void 0;
|
|
24059
|
+
this.settleCode(record, { ok: true, error: null });
|
|
24060
|
+
}
|
|
23814
24061
|
if (record.native?.started) record.native.instance.suspend();
|
|
23815
24062
|
if (record.renderHandle) this.renderer.setIndicatorVisible?.(record.renderHandle, false);
|
|
23816
24063
|
else if (record.native) this.mountHiddenNativeRow(id, record);
|
|
@@ -23897,7 +24144,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23897
24144
|
if (this.viewportTimer != null) clearTimeout(this.viewportTimer);
|
|
23898
24145
|
this.viewportUnsub?.();
|
|
23899
24146
|
this.paneActionUnsub?.();
|
|
24147
|
+
this.priceStyleWillChangeUnsub?.();
|
|
24148
|
+
this.priceStyleWillChangeUnsub = null;
|
|
23900
24149
|
for (const record of this.registry.all()) {
|
|
24150
|
+
this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
|
|
23901
24151
|
record.session?.stop();
|
|
23902
24152
|
if (record.native?.started) record.native.instance.stop();
|
|
23903
24153
|
}
|
|
@@ -23966,7 +24216,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23966
24216
|
const record = this.registry.get(id);
|
|
23967
24217
|
if (!record || !record.engine || !record.prepared || record.hidden) return;
|
|
23968
24218
|
const mode = this.config.live && !record.prepared.reactsToViewport && record.engine.capabilities.streaming ? "live" : "static";
|
|
23969
|
-
|
|
24219
|
+
const prepared = record.prepared;
|
|
24220
|
+
const session = record.engine.execute(
|
|
23970
24221
|
{
|
|
23971
24222
|
prepared: record.prepared,
|
|
23972
24223
|
market: this.market(),
|
|
@@ -23987,6 +24238,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23987
24238
|
const cause = record.pendingCause ?? "history";
|
|
23988
24239
|
if (!this.applyModel(id, model)) return;
|
|
23989
24240
|
record.pendingCause = void 0;
|
|
24241
|
+
if (record.codeTrial) {
|
|
24242
|
+
record.codeTrial = void 0;
|
|
24243
|
+
this.settleCode(record, { ok: true, error: null });
|
|
24244
|
+
}
|
|
23990
24245
|
this.emitContextChanged(id);
|
|
23991
24246
|
this.emitScriptRun(id, cause, first);
|
|
23992
24247
|
},
|
|
@@ -23997,9 +24252,17 @@ var EngineOrchestrator = class _EngineOrchestrator {
|
|
|
23997
24252
|
handle.emit("alert", { id: a.id, message: a.message, title: a.title, time: a.time });
|
|
23998
24253
|
},
|
|
23999
24254
|
onWarning: (w) => this.events.emit("warning", w),
|
|
24000
|
-
onError: (err) =>
|
|
24255
|
+
onError: (err) => {
|
|
24256
|
+
if (record.codeTrial) this.rejectCode(id, record, handle, err);
|
|
24257
|
+
else this.fail(id, handle, err);
|
|
24258
|
+
}
|
|
24001
24259
|
}
|
|
24002
24260
|
);
|
|
24261
|
+
if (record.prepared !== prepared) {
|
|
24262
|
+
session.stop();
|
|
24263
|
+
return;
|
|
24264
|
+
}
|
|
24265
|
+
record.session = session;
|
|
24003
24266
|
this.emitContextChanged(id);
|
|
24004
24267
|
}
|
|
24005
24268
|
/**
|
|
@@ -24500,7 +24763,7 @@ function yieldToPaint() {
|
|
|
24500
24763
|
}
|
|
24501
24764
|
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
24502
24765
|
}
|
|
24503
|
-
function valuesOnSchema(schema, previous) {
|
|
24766
|
+
function valuesOnSchema(schema, previous, oldSchema = []) {
|
|
24504
24767
|
const out = {};
|
|
24505
24768
|
const declared = /* @__PURE__ */ new Set();
|
|
24506
24769
|
for (const s of schema) {
|
|
@@ -24508,9 +24771,22 @@ function valuesOnSchema(schema, previous) {
|
|
|
24508
24771
|
declared.add(s.key);
|
|
24509
24772
|
declared.add(s.title);
|
|
24510
24773
|
}
|
|
24511
|
-
|
|
24774
|
+
const oldDefaults = /* @__PURE__ */ new Map();
|
|
24775
|
+
for (const s of oldSchema) {
|
|
24776
|
+
oldDefaults.set(s.key, s.defval);
|
|
24777
|
+
if (!oldDefaults.has(s.title)) oldDefaults.set(s.title, s.defval);
|
|
24778
|
+
}
|
|
24779
|
+
for (const [k, v] of Object.entries(previous)) {
|
|
24780
|
+
if (!declared.has(k)) continue;
|
|
24781
|
+
if (oldDefaults.has(k) && sameInputValue(oldDefaults.get(k), v)) continue;
|
|
24782
|
+
out[k] = v;
|
|
24783
|
+
}
|
|
24512
24784
|
return out;
|
|
24513
24785
|
}
|
|
24786
|
+
function sameInputValue(a, b) {
|
|
24787
|
+
if (a === b) return true;
|
|
24788
|
+
return typeof a === "object" && a !== null && typeof b === "object" && b !== null && JSON.stringify(a) === JSON.stringify(b);
|
|
24789
|
+
}
|
|
24514
24790
|
function blankedModel(model) {
|
|
24515
24791
|
return {
|
|
24516
24792
|
...model,
|
|
@@ -28533,7 +28809,8 @@ var Vela = class {
|
|
|
28533
28809
|
glow: options.glow ?? 0,
|
|
28534
28810
|
upColor: options.upColor ?? BULLISH,
|
|
28535
28811
|
downColor: options.downColor ?? BEARISH,
|
|
28536
|
-
priceStyle: options.priceStyle ?? "candles"
|
|
28812
|
+
priceStyle: options.priceStyle ?? "candles",
|
|
28813
|
+
priceAxisTicks: options.priceAxis?.ticks ?? null
|
|
28537
28814
|
};
|
|
28538
28815
|
const RendererClass = options.renderer ?? NativeRenderer;
|
|
28539
28816
|
if (typeof RendererClass !== "function") {
|
|
@@ -28877,4 +29154,4 @@ function resolveElement(container) {
|
|
|
28877
29154
|
return element;
|
|
28878
29155
|
}
|
|
28879
29156
|
|
|
28880
|
-
export { BUILTIN_PRICE_STYLES, BarStore, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LEGEND_AT_TOP_ATTR, LIGHT_THEME, MarksControl, MultiProviderFeed, NativeRenderer, RendererControl, ReplayControl, SecondClock,
|
|
29157
|
+
export { BUILTIN_PRICE_STYLES, BarStore, CANDLE_OVERRIDE_KEYS, CachingDataFeed, DARK_THEME, DataControl, DrawingStore, DrawingToolbar, DrawingsControl, LEGEND_AT_TOP_ATTR, LIGHT_THEME, MarksControl, MultiProviderFeed, NativeRenderer, RendererControl, ReplayControl, SecondClock, TypedEventBus, Vela, applyAttributionMarkTheme, buildToolbar, createAttributionMark, createCustomMark, defaultMemberOf, defaultToolbar, groupKeyOf, groupMembers, isGroupRow, normalizeSession, parseSymbol, priceStyleIds, registerBuiltinChartTypes, resolveTheme, sharedBarStore, timeframeToMs };
|