@luxalgo/vela 0.7.0 → 0.7.2

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.
Files changed (45) hide show
  1. package/dist/{DataProvider-Bu9E5Zh5.d.ts → DataProvider-BALR0qPq.d.ts} +1 -1
  2. package/dist/{DataProvider-Ck2qyS_9.d.cts → DataProvider-D5uMiJYz.d.cts} +1 -1
  3. package/dist/{chunk-IFJJPXSV.js → chunk-BFA32GOU.js} +44 -16
  4. package/dist/{chunk-CZRNTHD6.js → chunk-BKHSQ4YM.js} +129 -125
  5. package/dist/{chunk-A4G64KVF.js → chunk-PTNO3YK2.js} +311 -111
  6. package/dist/{chunk-A347YL2P.js → chunk-QBWLINWL.js} +67 -9
  7. package/dist/{chunk-UHXG5J7C.js → chunk-ZADTVRUO.js} +1 -1
  8. package/dist/{contributions-BjQ7hyIx.d.ts → contributions-BbhbZd3c.d.ts} +31 -9
  9. package/dist/{contributions-Cv2Mutvn.d.cts → contributions-CcBFe-91.d.cts} +31 -9
  10. package/dist/index.cjs +391 -157
  11. package/dist/index.d.cts +52 -17
  12. package/dist/index.d.ts +52 -17
  13. package/dist/index.js +4 -4
  14. package/dist/{options-ex2_gtKp.d.ts → options-C7b_Qssu.d.cts} +42 -1
  15. package/dist/{options-ex2_gtKp.d.cts → options-C7b_Qssu.d.ts} +42 -1
  16. package/dist/{plugin-CiyhU7pi.d.ts → plugin-Cnix5ZBh.d.ts} +6 -4
  17. package/dist/{plugin-absNH7Yn.d.cts → plugin-D6MlX4--.d.cts} +6 -4
  18. package/dist/plugin.cjs +39 -15
  19. package/dist/plugin.d.cts +4 -4
  20. package/dist/plugin.d.ts +4 -4
  21. package/dist/plugin.js +2 -2
  22. package/dist/providers/binance.d.cts +2 -2
  23. package/dist/providers/binance.d.ts +2 -2
  24. package/dist/providers/coinbase.d.cts +2 -2
  25. package/dist/providers/coinbase.d.ts +2 -2
  26. package/dist/providers/hyperliquid.d.cts +2 -2
  27. package/dist/providers/hyperliquid.d.ts +2 -2
  28. package/dist/{statusline-model-BXoU4Kua.d.ts → statusline-model-D8NL7o4L.d.ts} +3 -3
  29. package/dist/{statusline-model-cRhPBLPO.d.cts → statusline-model-DUKt7_TQ.d.cts} +3 -3
  30. package/dist/ui.cjs +8 -4
  31. package/dist/ui.d.cts +10 -7
  32. package/dist/ui.d.ts +10 -7
  33. package/dist/ui.js +2 -2
  34. package/dist/vela.global.js +391 -157
  35. package/dist/vela.global.min.js +70 -70
  36. package/dist/widget.cjs +418 -128
  37. package/dist/widget.d.cts +6 -6
  38. package/dist/widget.d.ts +6 -6
  39. package/dist/widget.js +6 -6
  40. package/dist/workspace.cjs +418 -128
  41. package/dist/workspace.d.cts +39 -6
  42. package/dist/workspace.d.ts +39 -6
  43. package/dist/workspace.js +5 -5
  44. package/package.json +1 -1
  45. /package/dist/{chunk-FE6V2YAQ.js → chunk-JU4CWEBF.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-ex2_gtKp.js';
1
+ import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-C7b_Qssu.js';
2
2
 
3
3
  /**
4
4
  * Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
@@ -1,4 +1,4 @@
1
- import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-ex2_gtKp.cjs';
1
+ import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-C7b_Qssu.cjs';
2
2
 
3
3
  /**
4
4
  * Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
@@ -1,6 +1,21 @@
1
1
  import { svg24, NEUTRAL, BEARISH, WARNING, BULLISH, INFO, ACCENT_BRIGHT, VALID, INVALID, MARKER, ACCENT, injectStyles, iconEl } from './chunk-CAFCLMPF.js';
2
2
 
3
+ // src/core/model/series.ts
4
+ function isLineLikeSeries(spec) {
5
+ return spec.kind !== "candle" && spec.kind !== "bar" && spec.kind !== "markers";
6
+ }
7
+ function seriesShownOn(spec, surface) {
8
+ if (spec.display) return spec.display[surface] !== false;
9
+ return spec.visible !== false;
10
+ }
11
+ function seriesInScale(spec) {
12
+ return seriesShownOn(spec, "pane") || seriesShownOn(spec, "priceScale");
13
+ }
14
+
3
15
  // src/core/native-indicators/NativeIndicator.ts
16
+ function nativeInstanceChannel(type, instanceId) {
17
+ return `${type}#${instanceId}`;
18
+ }
4
19
  var REGISTRY = /* @__PURE__ */ new Map();
5
20
  function registerNativeIndicator(descriptor) {
6
21
  REGISTRY.set(descriptor.type, descriptor);
@@ -1837,16 +1852,18 @@ var Pitchfork = class extends SegmentDrawing {
1837
1852
  geometry(proj) {
1838
1853
  const a = this.anchors[0];
1839
1854
  const b = this.anchors[1];
1840
- const c = this.anchors[2];
1841
- if (!a || !b || !c) return null;
1855
+ if (!a || !b) return null;
1842
1856
  const px = (p) => {
1843
1857
  const y = proj.yOf(p.price, this.paneId);
1844
1858
  return y == null ? null : [proj.xOf(p.time), y];
1845
1859
  };
1846
1860
  const P0 = px(a);
1847
1861
  const P1 = px(b);
1862
+ if (!P0 || !P1) return null;
1863
+ const c = this.anchors[2];
1864
+ if (!c) return { segments: [[P0[0], P0[1], P1[0], P1[1]]], fill: null };
1848
1865
  const P2 = px(c);
1849
- if (!P0 || !P1 || !P2) return null;
1866
+ if (!P2) return null;
1850
1867
  const mx = (P1[0] + P2[0]) / 2;
1851
1868
  const my = (P1[1] + P2[1]) / 2;
1852
1869
  const dx = mx - P0[0];
@@ -1893,16 +1910,19 @@ var PitchforkVariant = class extends SegmentDrawing {
1893
1910
  geometry(proj) {
1894
1911
  const a = this.anchors[0];
1895
1912
  const b = this.anchors[1];
1896
- const c = this.anchors[2];
1897
- if (!a || !b || !c) return null;
1913
+ if (!a || !b) return null;
1898
1914
  const px = (p) => {
1899
1915
  const y = proj.yOf(p.price, this.paneId);
1900
1916
  return y == null ? null : [proj.xOf(p.time), y];
1901
1917
  };
1902
- const S = px(this.medianStart(a, b, c));
1918
+ const P0 = px(a);
1903
1919
  const P1 = px(b);
1920
+ if (!P0 || !P1) return null;
1921
+ const c = this.anchors[2];
1922
+ if (!c) return { segments: [[P0[0], P0[1], P1[0], P1[1]]], fill: null };
1923
+ const S = px(this.medianStart(a, b, c));
1904
1924
  const P2 = px(c);
1905
- if (!S || !P1 || !P2) return null;
1925
+ if (!S || !P2) return null;
1906
1926
  const mx = (P1[0] + P2[0]) / 2;
1907
1927
  const my = (P1[1] + P2[1]) / 2;
1908
1928
  const dx = mx - S[0];
@@ -1917,8 +1937,12 @@ var PitchforkVariant = class extends SegmentDrawing {
1917
1937
  // upper tine
1918
1938
  extendRay(P2[0], P2[1], P2[0] + dx, P2[1] + dy, "right", w, h),
1919
1939
  // lower tine
1920
- [P1[0], P1[1], P2[0], P2[1]]
1940
+ [P1[0], P1[1], P2[0], P2[1]],
1921
1941
  // base line
1942
+ // The median leaves from the shifted origin, not the pivot — without this construction
1943
+ // line the pivot handle would float unattached to the shape it defines.
1944
+ [P0[0], P0[1], P1[0], P1[1]]
1945
+ // pivot → first tine anchor
1922
1946
  ],
1923
1947
  fill: null
1924
1948
  };
@@ -1975,17 +1999,19 @@ var InsidePitchfork = class extends SegmentDrawing {
1975
1999
  geometry(proj) {
1976
2000
  const a = this.anchors[0];
1977
2001
  const b = this.anchors[1];
1978
- const c = this.anchors[2];
1979
- if (!a || !b || !c) return null;
2002
+ if (!a || !b) return null;
1980
2003
  const px = (p) => {
1981
2004
  const y = proj.yOf(p.price, this.paneId);
1982
2005
  return y == null ? null : [proj.xOf(p.time), y];
1983
2006
  };
1984
- const B = px({ time: (a.time + b.time) / 2, price: (a.price + b.price) / 2 });
1985
2007
  const P0 = px(a);
1986
2008
  const P1 = px(b);
2009
+ if (!P0 || !P1) return null;
2010
+ const c = this.anchors[2];
2011
+ if (!c) return { segments: [[P0[0], P0[1], P1[0], P1[1]]], fill: null };
2012
+ const B = px({ time: (a.time + b.time) / 2, price: (a.price + b.price) / 2 });
1987
2013
  const P2 = px(c);
1988
- if (!B || !P0 || !P1 || !P2) return null;
2014
+ if (!B || !P2) return null;
1989
2015
  const mx = (P1[0] + P2[0]) / 2;
1990
2016
  const my = (P1[1] + P2[1]) / 2;
1991
2017
  const dx = P2[0] - B[0];
@@ -2156,16 +2182,18 @@ var Triangle = class extends SegmentDrawing {
2156
2182
  geometry(proj) {
2157
2183
  const a = this.anchors[0];
2158
2184
  const b = this.anchors[1];
2159
- const c = this.anchors[2];
2160
- if (!a || !b || !c) return null;
2185
+ if (!a || !b) return null;
2161
2186
  const px = (p) => {
2162
2187
  const y = proj.yOf(p.price, this.paneId);
2163
2188
  return y == null ? null : [proj.xOf(p.time), y];
2164
2189
  };
2165
2190
  const A = px(a);
2166
2191
  const B = px(b);
2192
+ if (!A || !B) return null;
2193
+ const c = this.anchors[2];
2194
+ if (!c) return { segments: [[A[0], A[1], B[0], B[1]]], fill: null };
2167
2195
  const C = px(c);
2168
- if (!A || !B || !C) return null;
2196
+ if (!C) return null;
2169
2197
  return {
2170
2198
  segments: [
2171
2199
  [A[0], A[1], B[0], B[1]],
@@ -6365,4 +6393,4 @@ var SidePanel = class {
6365
6393
  }
6366
6394
  };
6367
6395
 
6368
- export { AnchoredVwap, ArrowMark, Callout, CalloutBase, Comment, DEDEKIND_CURVATURE_OPTIONS, DEFAULT_DRAWING_COLOR, DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, DIRECTION_OPTIONS, DedekindTessellation, Drawing, FibRatios, FibSpiral, FixedRangeVolumeProfile, GANN_SQUARE_ARCS, GLYPH_OPTIONS, GannSquare, GlyphStamp, LINE_STYLE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, MAGNIFIER_TIMEFRAME_OPTIONS, MachFigure, Magnifier, MeasureBox, Note, OVERRIDABLE_TOPBAR_IDS, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, foldBaseModulation, formatDuration, getDrawingType, getNativeIndicator, inputDeltas, inputVisible, legendActions, legendActionsProviderFor, legendCallouts, legendCalloutsProviderFor, lineSegmentIntersection, magnifierTimeframeLabel, nativeIndicatorDescriptors, nativeIndicatorTypes, normalizeSettingsRow, pinnedTopbarActionIds, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerLegendCallout, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, resolveTopbarComposition, settingsRowValueKeys, settingsRowVisible, sidePanels, stableSeriesId, statePersistenceHandlers, symbolRanking, tickerModifierIds, topbarActionOverride, topbarHas, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterLegendCallout, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
6396
+ export { AnchoredVwap, ArrowMark, Callout, CalloutBase, Comment, DEDEKIND_CURVATURE_OPTIONS, DEFAULT_DRAWING_COLOR, DEFAULT_PANEL_MAX_WIDTH, DEFAULT_PANEL_MIN_WIDTH, DEFAULT_PANEL_ORDER, DEFAULT_PANEL_WIDTH, DIRECTION_OPTIONS, DedekindTessellation, Drawing, FibRatios, FibSpiral, FixedRangeVolumeProfile, GANN_SQUARE_ARCS, GLYPH_OPTIONS, GannSquare, GlyphStamp, LINE_STYLE_OPTIONS, MACH_NUMBER_OPTIONS, MACH_WAVE_COUNT_OPTIONS, MAGNIFIER_TIMEFRAME_OPTIONS, MachFigure, Magnifier, MeasureBox, Note, OVERRIDABLE_TOPBAR_IDS, PatternDrawing, PositionTool, PriceLabel, PriceNote, RadialFib, RegressionChannel, STAMP_SIZE_OPTIONS, SegmentDrawing, SidePanel, Signpost, TEXT_SIZE_OPTIONS, TOPBAR_BUILTIN_IDS, TOPBAR_DEFAULT_LEFT, TOPBAR_DEFAULT_RIGHT, TextLabel, chartType, chartTypes, clampPanelWidth, createDrawing, deserializeDrawing, drawingTypes, foldBaseModulation, formatDuration, getDrawingType, getNativeIndicator, inputDeltas, inputVisible, isLineLikeSeries, legendActions, legendActionsProviderFor, legendCallouts, legendCalloutsProviderFor, lineSegmentIntersection, magnifierTimeframeLabel, nativeIndicatorDescriptors, nativeIndicatorTypes, nativeInstanceChannel, normalizeSettingsRow, pinnedTopbarActionIds, registerChartType, registerDefaultEngine, registerDrawingType, registerLegendAction, registerLegendCallout, registerNativeIndicator, registerRendererDefaults, registerRendererLayer, registerSidePanel, registerStatePersistence, registerSymbolRanking, registerWidgetAction, registerWidgetAttachment, rendererDefaults, rendererLayers, resetDrawingSettings, resolveEngines, resolveTopbarComposition, seriesInScale, seriesShownOn, settingsRowValueKeys, settingsRowVisible, sidePanels, stableSeriesId, statePersistenceHandlers, symbolRanking, tickerModifierIds, topbarActionOverride, topbarHas, unregisterChartType, unregisterDefaultEngine, unregisterLegendAction, unregisterLegendCallout, unregisterNativeIndicator, unregisterRendererDefaults, unregisterRendererLayer, unregisterSidePanel, unregisterStatePersistence, unregisterWidgetAction, unregisterWidgetAttachment, widgetActions, widgetAttachments };
@@ -1,4 +1,4 @@
1
- import { overlayScrollbarCss, injectStyles, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, iconAt, iconEl, BULLISH, BEARISH, WARNING, NEUTRAL } from './chunk-CAFCLMPF.js';
1
+ import { overlayScrollbarCss, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, injectStyles, iconAt, iconEl, BULLISH, BEARISH, WARNING, NEUTRAL } from './chunk-CAFCLMPF.js';
2
2
  import { VanillaMachine, normalizeProps, spreadProps } from '@zag-js/vanilla';
3
3
  export { normalizeProps, spreadProps } from '@zag-js/vanilla';
4
4
  import * as menu from '@zag-js/menu';
@@ -22,6 +22,129 @@ var uid = 0;
22
22
  function nextUid(prefix) {
23
23
  return `${prefix}-${++uid}`;
24
24
  }
25
+
26
+ // src/core/tokens.ts
27
+ var STATIC_TOKENS = {
28
+ "--vela-space-1": "4px",
29
+ "--vela-space-2": "8px",
30
+ "--vela-space-3": "12px",
31
+ "--vela-space-4": "16px",
32
+ "--vela-radius-sm": "4px",
33
+ "--vela-radius-md": "6px",
34
+ "--vela-radius-lg": "10px",
35
+ "--vela-z-tooltip": "60",
36
+ "--vela-z-menu": "50",
37
+ "--vela-z-dialog": "40",
38
+ // Form popovers portal to <body> (or a chart host) and must sit above a dialog
39
+ // whose own stacking context may be nested inside the chart container.
40
+ "--vela-z-popover": "6000",
41
+ "--vela-ease": "cubic-bezier(0.22, 1, 0.36, 1)",
42
+ "--vela-dur-fast": "90ms",
43
+ "--vela-dur-med": "160ms",
44
+ "--vela-font-size-sm": "11px",
45
+ "--vela-font-size-md": "12px",
46
+ "--vela-font-size-lg": "14px"
47
+ };
48
+ function themeTokens(t) {
49
+ const dark = isDarkColor(t.background);
50
+ const wash = (a) => dark ? `rgba(255,255,255,${a})` : withAlpha(t.textColor, a + 0.02);
51
+ const elevated = mix(t.background, dark ? "#ffffff" : t.textColor, dark ? 0.03 : 0.05);
52
+ return {
53
+ "--vela-font": t.fontFamily,
54
+ "--vela-bg": t.background,
55
+ // Chrome text is slightly brighter than the chart's own axis text, which is
56
+ // deliberately recessive; the chart keeps using `t.textColor` directly.
57
+ "--vela-fg": dark ? "#d1d4dc" : t.textColor,
58
+ "--vela-fg-muted": dark ? "#868a96" : withAlpha(t.textColor, 0.62),
59
+ "--vela-fg-faint": withAlpha(t.textColor, 0.35),
60
+ "--vela-fg-bright": dark ? "#f0f3fa" : "#000000",
61
+ "--vela-surface": t.background,
62
+ // Panels and menus float ABOVE the chart, so their surface is the wash flattened onto
63
+ // the chart background — opaque, or candles read through the panel.
64
+ "--vela-surface-elev": elevated,
65
+ "--vela-surface-overlay": elevated,
66
+ // Recessed fields (inputs, selects) read as cut INTO their panel, so they fall back
67
+ // to the chart surface and are separated from the panel by their border alone.
68
+ "--vela-surface-sunken": t.background,
69
+ "--vela-border": t.borderColor,
70
+ "--vela-border-strong": dark ? "#34353b" : withAlpha(t.textColor, 0.28),
71
+ "--vela-border-soft": t.borderColor,
72
+ // Barely-there rules INSIDE a panel (row separators), where a full border would
73
+ // chop the list into boxes.
74
+ "--vela-border-faint": withAlpha(t.textColor, 0.08),
75
+ "--vela-hover": wash(0.06),
76
+ "--vela-active": wash(0.1),
77
+ // A deliberately stronger hover for rows inside an already-tinted surface (menu
78
+ // items in an active flyout), where the normal wash would not separate from it.
79
+ "--vela-hover-strong": wash(0.16),
80
+ "--vela-focus": withAlpha(t.textColor, 0.5),
81
+ "--vela-focus-soft": withAlpha(t.textColor, 0.12),
82
+ // Separator hover — the SAME wash the chart's pane separators paint on hover
83
+ // (soft full-thickness band + solid 2px center line), so DOM-drawn dividers
84
+ // (the workspace grid) and canvas-drawn ones read as one family.
85
+ "--vela-separator-hover-band": withAlpha(t.textColor, 0.1),
86
+ "--vela-separator-hover-line": withAlpha(t.textColor, 0.55),
87
+ "--vela-scroll": withAlpha(t.textColor, 0.3),
88
+ "--vela-accent": ACCENT,
89
+ "--vela-accent-bright": ACCENT_BRIGHT,
90
+ "--vela-highlight": HIGHLIGHT,
91
+ // The inverse chip: a filled selected state (active tab, ticked checkbox). Its ink
92
+ // must contrast the fill, so the pair flips together with the theme.
93
+ "--vela-selected-bg": dark ? "#f0f3fa" : t.textColor,
94
+ "--vela-selected-fg": dark ? t.background : "#ffffff",
95
+ // Fixed ink for saturated fills (accent buttons, categorical avatars) — those fills
96
+ // are theme-independent, so their ink is too.
97
+ "--vela-fg-on-fill": "#ffffff",
98
+ "--vela-up": t.upColor,
99
+ "--vela-down": t.downColor,
100
+ "--vela-danger": t.downColor,
101
+ "--vela-shadow": "0 8px 30px rgba(0,0,0,0.5)",
102
+ "--vela-shadow-dialog": "0 20px 60px rgba(0,0,0,0.5)",
103
+ "--vela-backdrop": "rgba(0,0,0,0.45)"
104
+ };
105
+ }
106
+
107
+ // src/ui/tokens.ts
108
+ var STATIC_ID = "vela-ui-tokens";
109
+ var STATIC_DECLS = Object.entries(STATIC_TOKENS).map(([k, v]) => ` ${k}: ${v};`).join("\n");
110
+ var STATIC_CSS = `
111
+ .vela-ui, .vela-ui-layer {
112
+ ${STATIC_DECLS}
113
+ font-family: var(--vela-font, -apple-system, system-ui, sans-serif);
114
+ box-sizing: border-box;
115
+ /* Chrome text (titles, buttons, menus, readouts) is UI, not copy \u2014 never selectable. */
116
+ user-select: none;
117
+ -webkit-user-select: none;
118
+ }
119
+ .vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
120
+ /* Text ENTRY is the one exception: selection is part of editing. */
121
+ .vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
122
+ /* iOS Safari zooms the page when a focused field is under 16px, and often
123
+ keeps that zoom after the field blurs or a dialog closes. The shell's
124
+ mobile size class is the honest gate \u2014 not a media query \u2014 so an embedded
125
+ chart on a wide desktop still gets the rule when it is in the phone layout.
126
+ Scoped to the kit root so a host page's own [data-layout] is never touched. */
127
+ .vela-ui[data-layout='mobile'] :is(input, textarea, select) { font-size: 16px; }
128
+ .vela-icon { display: inline-flex; align-items: center; flex: none; }
129
+ .vela-icon svg { display: block; }
130
+ `;
131
+ function applyThemeTokens(el, t) {
132
+ const tokens = themeTokens(t);
133
+ for (const key in tokens) el.style.setProperty(key, tokens[key]);
134
+ }
135
+ function applyPlotOverlayTokens(host, base, config) {
136
+ const layout = config?.layout;
137
+ const t = layout?.background && layout.textColor ? { ...base, background: layout.background, textColor: layout.textColor } : base;
138
+ applyThemeTokens(host, t);
139
+ }
140
+ function floatingLayerHost(from, fallback) {
141
+ return from.closest(".vela-ui") ?? fallback ?? from;
142
+ }
143
+ function ensureUIHost(el, theme) {
144
+ injectStyles(STATIC_ID, STATIC_CSS, el.getRootNode());
145
+ el.classList.add("vela-ui");
146
+ if (theme) applyThemeTokens(el, theme);
147
+ }
25
148
  function menuController(opts) {
26
149
  return {
27
150
  machine: menu.machine,
@@ -46,7 +169,7 @@ var MENU_CSS = `
46
169
  /* The list is a <ul>: without this, block rows (the separator) paint a ::marker dot. */
47
170
  list-style: none;
48
171
  margin: 0;
49
- background: var(--vela-surface-elev);
172
+ background: var(--vela-surface);
50
173
  color: var(--vela-fg);
51
174
  border: 1px solid var(--vela-border-strong);
52
175
  border-radius: 6px;
@@ -337,7 +460,7 @@ var Menu = class {
337
460
  const anchor = opts.trigger ?? opts.host ?? document.body;
338
461
  const doc = anchor.ownerDocument;
339
462
  injectStyles(MENU_STYLE_ID, MENU_CSS, doc);
340
- const host = opts.host ?? anchor.closest?.(".vela-ui") ?? doc.body;
463
+ const host = floatingLayerHost(opts.host ?? anchor, doc.body);
341
464
  this.root = new Surface(doc, {
342
465
  host,
343
466
  placement: opts.placement,
@@ -376,126 +499,6 @@ var Menu = class {
376
499
  }
377
500
  };
378
501
 
379
- // src/core/tokens.ts
380
- var STATIC_TOKENS = {
381
- "--vela-space-1": "4px",
382
- "--vela-space-2": "8px",
383
- "--vela-space-3": "12px",
384
- "--vela-space-4": "16px",
385
- "--vela-radius-sm": "4px",
386
- "--vela-radius-md": "6px",
387
- "--vela-radius-lg": "10px",
388
- "--vela-z-tooltip": "60",
389
- "--vela-z-menu": "50",
390
- "--vela-z-dialog": "40",
391
- // Form popovers portal to <body> (or a chart host) and must sit above a dialog
392
- // whose own stacking context may be nested inside the chart container.
393
- "--vela-z-popover": "6000",
394
- "--vela-ease": "cubic-bezier(0.22, 1, 0.36, 1)",
395
- "--vela-dur-fast": "90ms",
396
- "--vela-dur-med": "160ms",
397
- "--vela-font-size-sm": "11px",
398
- "--vela-font-size-md": "12px",
399
- "--vela-font-size-lg": "14px"
400
- };
401
- function themeTokens(t) {
402
- const dark = isDarkColor(t.background);
403
- const wash = (a) => dark ? `rgba(255,255,255,${a})` : withAlpha(t.textColor, a + 0.02);
404
- const elevated = mix(t.background, dark ? "#ffffff" : t.textColor, dark ? 0.03 : 0.05);
405
- return {
406
- "--vela-font": t.fontFamily,
407
- "--vela-bg": t.background,
408
- // Chrome text is slightly brighter than the chart's own axis text, which is
409
- // deliberately recessive; the chart keeps using `t.textColor` directly.
410
- "--vela-fg": dark ? "#d1d4dc" : t.textColor,
411
- "--vela-fg-muted": dark ? "#868a96" : withAlpha(t.textColor, 0.62),
412
- "--vela-fg-faint": withAlpha(t.textColor, 0.35),
413
- "--vela-fg-bright": dark ? "#f0f3fa" : "#000000",
414
- "--vela-surface": t.background,
415
- // Panels and menus float ABOVE the chart, so their surface is the wash flattened onto
416
- // the chart background — opaque, or candles read through the panel.
417
- "--vela-surface-elev": elevated,
418
- "--vela-surface-overlay": elevated,
419
- // Recessed fields (inputs, selects) read as cut INTO their panel, so they fall back
420
- // to the chart surface and are separated from the panel by their border alone.
421
- "--vela-surface-sunken": t.background,
422
- "--vela-border": t.borderColor,
423
- "--vela-border-strong": dark ? "#34353b" : withAlpha(t.textColor, 0.28),
424
- "--vela-border-soft": t.borderColor,
425
- // Barely-there rules INSIDE a panel (row separators), where a full border would
426
- // chop the list into boxes.
427
- "--vela-border-faint": withAlpha(t.textColor, 0.08),
428
- "--vela-hover": wash(0.06),
429
- "--vela-active": wash(0.1),
430
- // A deliberately stronger hover for rows inside an already-tinted surface (menu
431
- // items in an active flyout), where the normal wash would not separate from it.
432
- "--vela-hover-strong": wash(0.16),
433
- "--vela-focus": withAlpha(t.textColor, 0.5),
434
- "--vela-focus-soft": withAlpha(t.textColor, 0.12),
435
- // Separator hover — the SAME wash the chart's pane separators paint on hover
436
- // (soft full-thickness band + solid 2px center line), so DOM-drawn dividers
437
- // (the workspace grid) and canvas-drawn ones read as one family.
438
- "--vela-separator-hover-band": withAlpha(t.textColor, 0.1),
439
- "--vela-separator-hover-line": withAlpha(t.textColor, 0.55),
440
- "--vela-scroll": withAlpha(t.textColor, 0.3),
441
- "--vela-accent": ACCENT,
442
- "--vela-accent-bright": ACCENT_BRIGHT,
443
- "--vela-highlight": HIGHLIGHT,
444
- // The inverse chip: a filled selected state (active tab, ticked checkbox). Its ink
445
- // must contrast the fill, so the pair flips together with the theme.
446
- "--vela-selected-bg": dark ? "#f0f3fa" : t.textColor,
447
- "--vela-selected-fg": dark ? t.background : "#ffffff",
448
- // Fixed ink for saturated fills (accent buttons, categorical avatars) — those fills
449
- // are theme-independent, so their ink is too.
450
- "--vela-fg-on-fill": "#ffffff",
451
- "--vela-up": t.upColor,
452
- "--vela-down": t.downColor,
453
- "--vela-danger": t.downColor,
454
- "--vela-shadow": "0 8px 30px rgba(0,0,0,0.5)",
455
- "--vela-shadow-dialog": "0 20px 60px rgba(0,0,0,0.5)",
456
- "--vela-backdrop": "rgba(0,0,0,0.45)"
457
- };
458
- }
459
-
460
- // src/ui/tokens.ts
461
- var STATIC_ID = "vela-ui-tokens";
462
- var STATIC_DECLS = Object.entries(STATIC_TOKENS).map(([k, v]) => ` ${k}: ${v};`).join("\n");
463
- var STATIC_CSS = `
464
- .vela-ui, .vela-ui-layer {
465
- ${STATIC_DECLS}
466
- font-family: var(--vela-font, -apple-system, system-ui, sans-serif);
467
- box-sizing: border-box;
468
- /* Chrome text (titles, buttons, menus, readouts) is UI, not copy \u2014 never selectable. */
469
- user-select: none;
470
- -webkit-user-select: none;
471
- }
472
- .vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
473
- /* Text ENTRY is the one exception: selection is part of editing. */
474
- .vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
475
- /* iOS Safari zooms the page when a focused field is under 16px, and often
476
- keeps that zoom after the field blurs or a dialog closes. The shell's
477
- mobile size class is the honest gate \u2014 not a media query \u2014 so an embedded
478
- chart on a wide desktop still gets the rule when it is in the phone layout.
479
- Scoped to the kit root so a host page's own [data-layout] is never touched. */
480
- .vela-ui[data-layout='mobile'] :is(input, textarea, select) { font-size: 16px; }
481
- .vela-icon { display: inline-flex; align-items: center; flex: none; }
482
- .vela-icon svg { display: block; }
483
- `;
484
- function applyThemeTokens(el, t) {
485
- const tokens = themeTokens(t);
486
- for (const key in tokens) el.style.setProperty(key, tokens[key]);
487
- }
488
- function applyPlotOverlayTokens(host, base, config) {
489
- const layout = config?.layout;
490
- const t = layout?.background && layout.textColor ? { ...base, background: layout.background, textColor: layout.textColor } : base;
491
- applyThemeTokens(host, t);
492
- }
493
- function ensureUIHost(el, theme) {
494
- injectStyles(STATIC_ID, STATIC_CSS, el.getRootNode());
495
- el.classList.add("vela-ui");
496
- if (theme) applyThemeTokens(el, theme);
497
- }
498
-
499
502
  // src/ui/components/popover/controller.ts
500
503
  function insetRect(r, inset) {
501
504
  return {
@@ -602,7 +605,8 @@ var Popover = class {
602
605
  const doc = opts.trigger.ownerDocument;
603
606
  injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
604
607
  this.trigger = opts.trigger;
605
- this.host = opts.host ?? doc.body;
608
+ this.host = opts.host ?? floatingLayerHost(opts.trigger, doc.body);
609
+ this.inheritsTokens = !opts.host && this.host !== doc.body;
606
610
  this.ctrl = popoverController(opts);
607
611
  this.boundary = opts.boundary ?? "viewport";
608
612
  this.theme = opts.theme;
@@ -634,7 +638,7 @@ var Popover = class {
634
638
  clearTimeout(this.leaveTimer);
635
639
  this.leaveTimer = null;
636
640
  }
637
- ensureUIHost(this.el, this.theme);
641
+ ensureUIHost(this.el, this.inheritsTokens ? void 0 : this.theme);
638
642
  if (this.fadeMs > 0) {
639
643
  this.el.style.transition = `opacity ${this.fadeMs}ms ease`;
640
644
  this.el.style.opacity = "0";