@luxalgo/vela 0.8.0 → 0.8.1

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/NOTICE CHANGED
@@ -1,6 +1,6 @@
1
1
  Vela
2
2
  Copyright (c) 2026 LuxAlgo
3
- https://luxalgo.com/vela
3
+ https://velacharts.dev
4
4
 
5
5
  This product includes the Vela charting library, developed by LuxAlgo.
6
6
 
package/README.md CHANGED
@@ -2,7 +2,15 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- <img src=".github/banner.png" alt="Vela™ — fast, extensible financial charts for the web" width="100%">
5
+ <a href="https://velacharts.dev">
6
+ <picture>
7
+ <source media="(prefers-color-scheme: dark)" srcset=".github/banner.png">
8
+ <source media="(prefers-color-scheme: light)" srcset=".github/banner-light.png">
9
+ <img src=".github/banner.png" alt="Vela™ by LuxAlgo" width="100%">
10
+ </picture>
11
+ </a>
12
+
13
+ # Vela™
6
14
 
7
15
  <p><strong>Fast, extensible financial charts for the web.</strong><br>
8
16
  Headless core · native WebGL2 renderer · batteries-included workspace · plugin SDK</p>
@@ -12,9 +20,9 @@
12
20
  [![License][license-img]][license-link]
13
21
 
14
22
  <p>
15
- <a href="https://luxalgo.com/vela">Homepage</a> ·
23
+ <a href="https://velacharts.dev">Homepage</a> ·
16
24
  <a href="#quick-start">Quick start</a> ·
17
- <a href="docs/index.md">Documentation</a> ·
25
+ <a href="https://docs.luxalgo.com/vela">Documentation</a> ·
18
26
  <a href="#extending-plugin-sdk">Plugin SDK</a> ·
19
27
  <a href="CHANGELOG.md">Changelog</a> ·
20
28
  <a href="#license-and-attribution">License</a>
@@ -41,7 +49,7 @@ touching the rest.
41
49
  - **`@luxalgo/vela/workspace`**: the full chart app. One chart or a grid of them under
42
50
  one shared topbar (symbol / timeframe / style / indicators), status line, symbol
43
51
  watermark, bottom bar (ranges, clock, timezone), object tree, keyboard-first UX
44
- (type a letter for symbol search, a digit for timeframe entry, `?` for the shortcuts
52
+ (type a letter or `0` for symbol search, a digit 1–9 for timeframe entry, `?` for the shortcuts
45
53
  panel), named cells, sync groups, and one persisted state document.
46
54
  - **`@luxalgo/vela/ui`**: the component kit the app is built on. Design tokens, overlay
47
55
  chrome ([Zag.js](https://zagjs.com) menu/dialog/drawer/tooltip), form primitives, and
@@ -108,7 +116,7 @@ alongside them.
108
116
 
109
117
  Custom scripts still run through pluggable engines. Vela™ **ships none**: install the
110
118
  addon for the language you want, or write one against the public `ScriptingEngine` port.
111
- Pine Script lives in [`@luxalgo/vela-pinets`](https://github.com/LuxAlgo/Vela-pinets)
119
+ Pine Script® lives in [`@luxalgo/vela-pinets`](https://github.com/LuxAlgo/Vela-pinets)
112
120
  (`npm i @luxalgo/vela-pinets pinets`), which is **AGPL-3.0** because the PineTS runtime it
113
121
  executes is. Vela™ itself stays Apache-2.0 and carries no Pine code:
114
122
 
@@ -198,10 +206,10 @@ include it per Apache-2.0 §4(d):
198
206
  This is the same licensing model used by other popular charting libraries, and we're
199
207
  grateful when the watermark stays in a visible spot.
200
208
 
201
- No scripting engine ships with this package; the Pine Script addon
209
+ No scripting engine ships with this package; the Pine Script® addon
202
210
  (`@luxalgo/vela-pinets`) is AGPL-3.0 and licensed separately (see *Indicators*).
203
211
 
204
- [homepage]: https://luxalgo.com/vela
212
+ [homepage]: https://velacharts.dev
205
213
 
206
214
  [npm-version-img]: https://img.shields.io/npm/v/%40luxalgo%2Fvela.svg
207
215
  [npm-downloads-img]: https://img.shields.io/npm/dm/%40luxalgo%2Fvela.svg
@@ -1,5 +1,5 @@
1
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-EZ5FWVLA.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-2IAETM2I.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-NELQJCGK.js';
3
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-BZQM2XO7.js';
4
4
 
5
5
  // src/core/events/EventBus.ts
@@ -7239,6 +7239,12 @@ var InputController = class {
7239
7239
  this.lastTapT = 0;
7240
7240
  this.lastTapX = 0;
7241
7241
  this.lastTapY = 0;
7242
+ // Whether the last two primary presses were claimed by the drawings layer. A double-click
7243
+ // is the last press plus the one before it; a tool-driven click pair must never fall through
7244
+ // to the pane maximize toggle, even when the tool disarmed itself between the two clicks
7245
+ // (a finished placement leaves plain cursor mode for the second click).
7246
+ this.lastPressDrawing = false;
7247
+ this.prevPressDrawing = false;
7242
7248
  this.onTouchContextMenu = (e) => {
7243
7249
  if (this.touches.size === 0) return;
7244
7250
  if (!(e.target instanceof Node) || !this.el?.contains(e.target)) return;
@@ -7293,19 +7299,24 @@ var InputController = class {
7293
7299
  this.moved = false;
7294
7300
  this.startX = x;
7295
7301
  this.startY = y;
7302
+ this.prevPressDrawing = this.lastPressDrawing;
7303
+ this.lastPressDrawing = false;
7296
7304
  if (this.deps.drawingsClaim?.(x, y)) {
7297
7305
  this.region = "drawing";
7306
+ this.lastPressDrawing = true;
7298
7307
  this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
7299
7308
  this.capture(e.pointerId);
7300
7309
  return;
7301
7310
  }
7302
7311
  if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
7303
7312
  this.region = "drawing";
7313
+ this.lastPressDrawing = true;
7304
7314
  this.capture(e.pointerId);
7305
7315
  return;
7306
7316
  }
7307
7317
  if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
7308
7318
  this.region = "drawing";
7319
+ this.lastPressDrawing = true;
7309
7320
  this.capture(e.pointerId);
7310
7321
  return;
7311
7322
  }
@@ -7627,6 +7638,7 @@ var InputController = class {
7627
7638
  if (region === "price") this.deps.resetPriceScale(x, y);
7628
7639
  else if (region === "separator") this.deps.resetPaneSize(y);
7629
7640
  else if (region === "time") this.deps.resetView();
7641
+ else if (this.lastPressDrawing || this.prevPressDrawing) return;
7630
7642
  else this.deps.dataDblClick(x, y);
7631
7643
  }
7632
7644
  };
@@ -9828,6 +9840,7 @@ function timeTicks(fromMs, toMs, target = 8, offsetMs = 0) {
9828
9840
  const step = pickStep(span / Math.max(1, target));
9829
9841
  const zFrom = fromMs + offsetMs;
9830
9842
  const zTo = toMs + offsetMs;
9843
+ if (step >= MONTH) return calendarTicks(zFrom, zTo, step >= YEAR ? 12 : step >= 3 * MONTH ? 3 : 1, offsetMs);
9831
9844
  const first = Math.ceil(zFrom / step) * step;
9832
9845
  const out = [];
9833
9846
  for (let zt = first; zt <= zTo; zt += step) {
@@ -9858,6 +9871,18 @@ function timeTicks(fromMs, toMs, target = 8, offsetMs = 0) {
9858
9871
  }
9859
9872
  return out;
9860
9873
  }
9874
+ function calendarTicks(zFrom, zTo, months, offsetMs) {
9875
+ const d0 = new Date(zFrom);
9876
+ let idx = d0.getUTCFullYear() * 12 + d0.getUTCMonth();
9877
+ if (Date.UTC(Math.floor(idx / 12), idx % 12, 1) < zFrom) idx += 1;
9878
+ idx = Math.ceil(idx / months) * months;
9879
+ const out = [];
9880
+ for (let zt = Date.UTC(Math.floor(idx / 12), idx % 12, 1); zt <= zTo; idx += months, zt = Date.UTC(Math.floor(idx / 12), idx % 12, 1)) {
9881
+ const label = months === 12 ? String(Math.floor(idx / 12)) : MONTHS[idx % 12];
9882
+ out.push({ time: zt - offsetMs, label, major: true });
9883
+ }
9884
+ return out;
9885
+ }
9861
9886
 
9862
9887
  // src/renderers/native/chrome/countdown.ts
9863
9888
  function countdownText(barOpen, barMs, now2) {
@@ -17667,23 +17692,21 @@ function stackLayers(entries, candleZ) {
17667
17692
  return { below, above };
17668
17693
  }
17669
17694
 
17670
- // src/renderers/native/chrome/luxalgo-logos.ts
17671
- var LUXALGO_SYMBOL_SVG = '<svg viewBox="0 0 45 40" aria-hidden="true"><g fill="currentColor"><path d="m40.25 38 4.58-7.998L28.802 2l-16.03 28 9.16-.001 6.87-12z"/><path d="M34.525 32.002 9.33 31.997 27.655 0h-9.158L.18 31.993 4.759 40h34.347z"/></g></svg>';
17672
- var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M 614 94.500 L 614 189 633.500 189 L 653 189 653 94.500 L 653 0 633.500 0 L 614 0 614 94.500 M 0 97.500 L 0 189 63.023 189 L 126.045 189 125.773 170.750 L 125.500 152.500 84.250 152.239 L 43 151.978 43 78.989 L 43 6 21.500 6 L 0 6 0 97.500 M 450.387 96.451 C 421.871 146.253, 398.306 186.994, 398.020 186.987 C 397.423 186.972, 362 124.724, 362 123.691 C 362 123.316, 370.100 108.818, 380 91.472 C 389.900 74.127, 398 59.500, 398 58.968 C 398 58.380, 391.007 58, 380.210 58 L 362.420 58 348.060 83.138 C 334.400 107.052, 333.637 108.160, 332.399 105.888 C 331.682 104.575, 325.287 93.375, 318.187 81 L 305.277 58.500 287.139 58.227 C 277.162 58.078, 269 58.186, 269 58.469 C 269 58.751, 277.154 73.274, 287.120 90.741 C 297.086 108.209, 305.261 122.983, 305.287 123.574 C 305.312 124.164, 297.318 138.789, 287.522 156.074 C 277.725 173.358, 269.524 187.838, 269.296 188.250 C 269.068 188.662, 276.977 189, 286.870 189 L 304.859 189 319.163 163.959 C 331.321 142.674, 333.647 139.165, 334.670 140.563 C 335.331 141.468, 341.864 152.737, 349.186 165.606 L 362.500 189.003 402.442 188.752 L 442.384 188.500 472.080 136.695 C 488.413 108.203, 501.939 84.729, 502.138 84.531 C 502.337 84.333, 516 107.757, 532.500 136.583 L 562.500 188.994 584.750 188.997 C 596.987 188.999, 607 188.854, 607 188.675 C 607 188.319, 503.014 6.686, 502.532 6.201 C 502.369 6.037, 478.904 46.649, 450.387 96.451 M 717.500 55.017 C 697.175 57.389, 680.641 68.752, 672.036 86.264 C 665.866 98.818, 663.840 108.024, 663.840 123.500 C 663.840 133.409, 664.328 138.355, 665.892 144.300 C 672.076 167.810, 689.191 184.757, 711.529 189.489 C 730.549 193.518, 747.264 189.540, 761.282 177.648 C 764.944 174.542, 768.178 172, 768.470 172 C 769.345 172, 769.030 185.018, 767.979 192.285 C 765.618 208.613, 755.148 218.672, 738.473 220.632 C 732.586 221.324, 723.136 219.599, 717.709 216.843 C 712.693 214.295, 706.636 207.946, 703.972 202.443 C 702.789 199.999, 701.749 198, 701.661 198.001 C 700.201 198.007, 665.596 205.071, 665.230 205.437 C 664.326 206.341, 669.156 218.313, 672.607 223.721 C 682.146 238.672, 700.008 248.257, 723.500 251.033 C 758.132 255.124, 788.930 241.875, 800.287 218 C 806.923 204.047, 807 203.001, 807 126.542 L 807 58 788.500 58 L 770 58 770 66.500 C 770 71.175, 769.641 75, 769.203 75 C 768.765 75, 766.627 73.142, 764.453 70.872 C 752.805 58.708, 735.614 52.903, 717.500 55.017 M 878.500 55.031 C 850.107 59.412, 830.755 75.064, 822.285 100.500 C 819.905 107.644, 819.614 110.051, 819.565 123 C 819.501 139.581, 820.786 145.825, 826.763 158 C 832.104 168.880, 842.842 179.424, 854.077 184.822 C 867.651 191.343, 873.132 192.462, 891.500 192.462 C 909.868 192.462, 915.349 191.343, 928.923 184.822 C 944.653 177.265, 956.667 162.306, 961.710 144 C 963.074 139.048, 963.481 134.047, 963.418 123 C 963.346 110.140, 963.038 107.595, 960.694 100.500 C 953.325 78.193, 936.695 62.854, 913.500 56.969 C 904.262 54.625, 887.240 53.682, 878.500 55.031 M 134.015 102.750 C 134.027 139.250, 134.310 148.689, 135.549 153.947 C 141.139 177.663, 159.815 193, 183.105 193 C 196.453 193, 210.163 186.171, 217.936 175.650 C 219.826 173.093, 221.738 171, 222.186 171 C 222.634 171, 223 175.050, 223 180 L 223 189 242.507 189 L 262.014 189 261.757 123.750 L 261.500 58.500 242.500 58.500 L 223.500 58.500 223 99.500 C 222.513 139.406, 222.441 140.610, 220.289 144.634 C 209.729 164.377, 183.014 164.880, 174.299 145.500 C 172.677 141.895, 172.469 137.382, 172.190 99.750 L 171.880 58 152.940 58 L 134 58 134.015 102.750 M 729.932 87.998 C 719.311 89.821, 710.155 97.164, 705.141 107.880 C 702.851 112.772, 702.510 114.798, 702.505 123.521 C 702.500 132.834, 702.732 134.014, 705.787 140.218 C 709.761 148.290, 715.989 154.097, 723.853 157.063 C 731.409 159.912, 744.359 159.466, 750.795 156.136 C 767.289 147.601, 774.103 127.713, 767.019 108.780 C 761.433 93.851, 746.051 85.232, 729.932 87.998 M 883.972 88.348 C 865.749 92.310, 854.873 109.966, 858.119 130.316 C 860.933 147.963, 872.346 158.830, 889.084 159.799 C 900.714 160.472, 906.867 158.234, 914.650 150.497 C 919.856 145.323, 921.123 143.340, 922.900 137.587 C 925.412 129.456, 925.655 118.570, 923.489 111.275 C 918.479 94.405, 901.478 84.541, 883.972 88.348"/></svg>';
17695
+ // src/renderers/native/chrome/vela-logos.ts
17696
+ var VELA_MARK_SVG = '<svg viewBox="0 -142 161.66 148" aria-hidden="true"><g fill="currentColor"><path d="M0 -140L32.91 -140L77.52 -62.73L61.06 -34.24Z"/><path d="M161.66 -140L128.75 -140L84.14 -62.73L100.59 -34.24Z"/><path d="M80.83 -52.04L95.85 -26.02L80.83 0L65.81 -26.02Z"/></g></svg>';
17697
+ var VELA_LETTERS_SVG = '<svg viewBox="161.66 -142 310.57 148" aria-hidden="true"><g fill="currentColor"><path d="M226.3 1.17Q200.81 1.17 186.12 -12.99Q171.42 -27.15 171.42 -51.17Q171.42 -75.2 185.87 -89.6Q200.33 -104 224.25 -104Q247.4 -104 261.7 -90.58Q276.01 -77.15 276.3 -54.59Q276.3 -49.9 275.42 -45.02H200.81V-43.55Q201.2 -32.62 207.75 -26.42Q214.29 -20.21 225.03 -20.21Q233.72 -20.21 239.34 -23.93Q244.95 -27.64 246.81 -34.77H274.64Q272.1 -19.14 259.16 -8.98Q246.22 1.17 226.3 1.17ZM201.5 -63.38H247.88Q246.42 -72.75 240.27 -77.88Q234.12 -83.01 224.45 -83.01Q215.17 -83.01 208.92 -77.73Q202.67 -72.46 201.5 -63.38ZM287.54 0V-140.04H315.76V0ZM429.34 -23.83H432.56V0H418.11Q407.76 0 403.36 -4.44Q398.97 -8.89 399.16 -16.8Q387.83 1.17 365.57 1.17Q348.48 1.17 337.88 -6.74Q327.29 -14.65 327.29 -29Q327.29 -45.41 339.45 -54.3Q351.6 -63.18 374.55 -63.18H394.96V-68.16Q394.96 -75.2 390.08 -79.3Q385.2 -83.4 376.51 -83.4Q368.3 -83.4 363.03 -79.98Q357.76 -76.56 356.88 -70.8H329.73Q331.19 -85.84 344.08 -94.92Q356.97 -104 377.29 -104Q399.06 -104 410.88 -94.43Q422.7 -84.86 422.7 -66.6V-30.18Q422.7 -26.37 424.26 -25.1Q425.82 -23.83 429.34 -23.83ZM394.96 -42.38V-44.43H374.36Q365.47 -44.43 360.59 -40.92Q355.7 -37.4 355.7 -30.96Q355.7 -25.59 359.81 -22.41Q363.91 -19.24 370.94 -19.24Q381.97 -19.24 388.37 -25.54Q394.77 -31.84 394.96 -42.38Z"/><path fill-opacity="0.55" d="M437.44 -124.28H441.96V-113.41H445.34V-124.28H449.87V-127.4H437.44ZM451.43 -113.41H454.7V-123.29L458.97 -113.41H461.62L465.99 -123.29V-113.41H469.21V-127.4H464.53L460.42 -117.68L456.16 -127.4H451.43Z"/></g></svg>';
17673
17698
 
17674
17699
  // src/renderers/native/chrome/AttributionMark.ts
17675
- var ATTRIBUTION_URL = "https://luxalgo.com/vela";
17700
+ var ATTRIBUTION_URL = "https://velacharts.dev/?utm_source=vela&utm_medium=attribution";
17676
17701
  var STYLE_ID5 = "vela-attribution-styles";
17677
17702
  var CSS = `
17678
17703
  .vela-attribution {
17679
17704
  text-decoration: none;
17680
17705
  display: flex;
17681
17706
  align-items: center;
17682
- /* Gutter, wordmark size and offset below are the brand lockup's own ratios,
17683
- measured off the official horizontal logo: gutter 0.157, wordmark height
17684
- 0.966, and wordmark 0.109 LOWER than the symbol \u2014 box-centering the two
17685
- reads wrong because the symbol's ink hangs low and the wordmark descends. */
17686
- gap: 4px;
17707
+ /* No gutter: the V is the wordmark's first letter, and the letters' viewBox starts
17708
+ where the V's ends, so the kit's own letter spacing comes with them. */
17709
+ gap: 0;
17687
17710
  }
17688
17711
  .vela-attribution .vela-attr-symbol {
17689
17712
  flex: none;
@@ -17691,7 +17714,7 @@ var CSS = `
17691
17714
  line-height: 0;
17692
17715
  }
17693
17716
  .vela-attribution .vela-attr-symbol svg {
17694
- height: 28px;
17717
+ height: 24px;
17695
17718
  width: auto;
17696
17719
  display: block;
17697
17720
  filter: drop-shadow(0 1px 2px var(--vela-attr-shadow, rgba(0,0,0,0.45)));
@@ -17702,15 +17725,13 @@ var CSS = `
17702
17725
  opacity: 0;
17703
17726
  display: flex;
17704
17727
  align-items: center;
17705
- /* Offset here, not on the image: the clip box must not crop the descender. */
17706
- position: relative;
17707
- top: 3px;
17728
+ /* Same height and baseline as the V (one viewBox height), so no vertical offset. */
17708
17729
  transform: translateX(-8px);
17709
17730
  transition: max-width 0.3s ease, opacity 0.25s ease, transform 0.3s ease;
17710
17731
  flex: none;
17711
17732
  }
17712
17733
  .vela-attribution .vela-attr-wordmark svg {
17713
- height: 27px;
17734
+ height: 24px;
17714
17735
  width: auto;
17715
17736
  display: block;
17716
17737
  filter: drop-shadow(0 1px 2px var(--vela-attr-shadow, rgba(0,0,0,0.45)));
@@ -17725,14 +17746,11 @@ var CSS = `
17725
17746
  (data-layout): the workspace's single grid-wide mark lives OUTSIDE any renderer
17726
17747
  container, so only the shell attribute reaches it. */
17727
17748
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-symbol svg,
17728
- [data-layout='mobile'] .vela-attribution .vela-attr-symbol svg { height: 22px; }
17749
+ [data-layout='mobile'] .vela-attribution .vela-attr-symbol svg { height: 20px; }
17729
17750
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-wordmark,
17730
- [data-layout='mobile'] .vela-attribution .vela-attr-wordmark {
17731
- top: 2px;
17732
- transform: translateX(-6px);
17733
- }
17751
+ [data-layout='mobile'] .vela-attribution .vela-attr-wordmark { transform: translateX(-6px); }
17734
17752
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-wordmark svg,
17735
- [data-layout='mobile'] .vela-attribution .vela-attr-wordmark svg { height: 21px; }
17753
+ [data-layout='mobile'] .vela-attribution .vela-attr-wordmark svg { height: 20px; }
17736
17754
  [data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
17737
17755
  [data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
17738
17756
  `;
@@ -17789,10 +17807,10 @@ function createAttributionMark(doc, background) {
17789
17807
  const symbol = doc.createElement("span");
17790
17808
  symbol.className = "vela-attr-symbol";
17791
17809
  symbol.setAttribute("aria-hidden", "true");
17792
- symbol.innerHTML = LUXALGO_SYMBOL_SVG;
17810
+ symbol.innerHTML = VELA_MARK_SVG;
17793
17811
  const wordmark = doc.createElement("span");
17794
17812
  wordmark.className = "vela-attr-wordmark";
17795
- wordmark.innerHTML = LUXALGO_WORDMARK_SVG;
17813
+ wordmark.innerHTML = VELA_LETTERS_SVG;
17796
17814
  a.append(symbol, wordmark);
17797
17815
  return a;
17798
17816
  }
@@ -1,7 +1,7 @@
1
- import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, SecondClock, resolveTimezone, timezoneMenuRows, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, normalizeSession, Vela, normalizeTimezone, isExchangeTimezone, TypedEventBus, timeframeToMs, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore } from './chunk-QBPUT5SU.js';
1
+ import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, SecondClock, resolveTimezone, timezoneMenuRows, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, normalizeSession, Vela, normalizeTimezone, isExchangeTimezone, TypedEventBus, timeframeToMs, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore } from './chunk-ATGTCWHJ.js';
2
2
  import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, mobilePlacement, widgetAttachments, getDrawingType, inputDeltas } from './chunk-EZ5FWVLA.js';
3
- import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-KA77UTRF.js';
4
- import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-2IAETM2I.js';
3
+ import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-LMBYEGUR.js';
4
+ import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-NELQJCGK.js';
5
5
  import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-BZQM2XO7.js';
6
6
  import { baseOf } from './chunk-W4EJWLEO.js';
7
7
 
@@ -3652,7 +3652,12 @@ var TimeframeQuick = class {
3652
3652
  content: (body) => body.append(this.input, this.hint),
3653
3653
  onOpenChange: (open) => opts.onOpenChange?.(open)
3654
3654
  });
3655
- this.input.addEventListener("input", () => this.renderHint());
3655
+ this.input.addEventListener("input", () => {
3656
+ const { selectionStart, selectionEnd } = this.input;
3657
+ this.input.value = this.input.value.toUpperCase();
3658
+ this.input.setSelectionRange(selectionStart, selectionEnd);
3659
+ this.renderHint();
3660
+ });
3656
3661
  this.input.addEventListener("keydown", (e) => {
3657
3662
  if (e.key !== "Enter") return;
3658
3663
  const parsed = parseTimeframe(this.input.value);
@@ -3664,7 +3669,7 @@ var TimeframeQuick = class {
3664
3669
  }
3665
3670
  open(seed = "") {
3666
3671
  this.dialog.show();
3667
- this.input.value = seed;
3672
+ this.input.value = seed.toUpperCase();
3668
3673
  this.renderHint();
3669
3674
  setTimeout(() => {
3670
3675
  this.input.focus();
@@ -3682,7 +3687,7 @@ var TimeframeQuick = class {
3682
3687
  this.open(text);
3683
3688
  return;
3684
3689
  }
3685
- this.input.value += text;
3690
+ this.input.value += text.toUpperCase();
3686
3691
  this.renderHint();
3687
3692
  }
3688
3693
  close() {
@@ -3695,7 +3700,7 @@ var TimeframeQuick = class {
3695
3700
  const raw = this.input.value.trim();
3696
3701
  const parsed = parseTimeframe(raw);
3697
3702
  if (!raw) {
3698
- this.hint.textContent = "e.g. 15, 4h, D, 3M";
3703
+ this.hint.textContent = "e.g. 15, 4H, D, 3M";
3699
3704
  delete this.hint.dataset.invalid;
3700
3705
  } else if (parsed.valid) {
3701
3706
  this.hint.textContent = parsed.label ?? "";
@@ -3798,7 +3803,7 @@ var ShortcutsHelp = class {
3798
3803
  addRow("Delete the drawing under the cursor", ["Middle-click"]);
3799
3804
  const s = doc.createElement("div");
3800
3805
  s.className = "vela-sh-static";
3801
- s.textContent = "Typing a letter opens the symbol search; typing a digit opens the timeframe entry.";
3806
+ s.textContent = "Typing a letter or 0 opens the symbol search; typing a digit from 1 to 9 opens the timeframe entry.";
3802
3807
  this.list.appendChild(s);
3803
3808
  }
3804
3809
  };
@@ -6424,7 +6429,7 @@ var ChartCell = class {
6424
6429
  * then the manifest in the host's own order. */
6425
6430
  libraryRows() {
6426
6431
  return [
6427
- ...this.supportedNatives().map((n) => ({ name: n.title, category: "Vela", native: true, nativeType: n.type, beta: n.beta })),
6432
+ ...this.supportedNatives().map((n) => ({ name: n.title, category: "Built-in", native: true, nativeType: n.type, beta: n.beta })),
6428
6433
  ...this.manifest.map((e) => ({ name: e.name, language: e.language, category: e.category }))
6429
6434
  ];
6430
6435
  }
@@ -8676,11 +8681,11 @@ function resolveTyping(key, open) {
8676
8681
  if (open.dialogs > 0) {
8677
8682
  if (key.length !== 1) return null;
8678
8683
  if (open.symbolSearch) return { target: "symbol", text: key.toUpperCase() };
8679
- if (open.timeframeEntry) return { target: "timeframe", text: key };
8684
+ if (open.timeframeEntry) return { target: "timeframe", text: key.toUpperCase() };
8680
8685
  return null;
8681
8686
  }
8682
- if (/^[a-zA-Z]$/.test(key)) return { target: "symbol", text: key.toUpperCase() };
8683
- if (/^[0-9]$/.test(key)) return { target: "timeframe", text: key };
8687
+ if (/^[a-zA-Z0]$/.test(key)) return { target: "symbol", text: key.toUpperCase() };
8688
+ if (/^[1-9]$/.test(key)) return { target: "timeframe", text: key };
8684
8689
  return null;
8685
8690
  }
8686
8691
  var VelaWorkspace = class {
@@ -10418,7 +10423,7 @@ var VelaWorkspace = class {
10418
10423
  }
10419
10424
  });
10420
10425
  }
10421
- /** Bare-typing router: letters → symbol search (seeded), digits → timeframe entry. */
10426
+ /** Bare-typing router: letters and `0` → symbol search (seeded), digits 1–9 → timeframe entry. */
10422
10427
  routeTyping(ev) {
10423
10428
  if (this.destroyed) return;
10424
10429
  if (ev.ctrlKey || ev.metaKey || ev.altKey) return;
@@ -1,4 +1,4 @@
1
- import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-2IAETM2I.js';
1
+ import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-NELQJCGK.js';
2
2
  import { injectStyles } from './chunk-BZQM2XO7.js';
3
3
  import * as tooltip from '@zag-js/tooltip';
4
4
  import * as dialog from '@zag-js/dialog';
@@ -2506,14 +2506,49 @@ function buildColorPicker(color, theme, onChange, options = {}) {
2506
2506
  const knob = document.createElement("div");
2507
2507
  knob.style.cssText = "position:absolute;top:50%;width:15px;height:15px;border-radius:50%;background:var(--vela-selected-bg);box-shadow:0 1px 3px rgba(0,0,0,0.55);transform:translate(-50%,-50%);pointer-events:none;";
2508
2508
  track.appendChild(knob);
2509
- const pctBox = document.createElement("div");
2510
- pctBox.style.cssText = `min-width:42px;text-align:center;font:var(--vela-font-size-md) ${theme.fontFamily};color:var(--vela-fg);border:1px solid var(--vela-border);border-radius:5px;padding:3px 4px;`;
2509
+ const pctBox = document.createElement("input");
2510
+ pctBox.type = "text";
2511
+ pctBox.inputMode = "numeric";
2512
+ pctBox.title = "Opacity (%)";
2513
+ pctBox.style.cssText = `width:46px;box-sizing:border-box;text-align:center;font:var(--vela-font-size-md) ${theme.fontFamily};color:var(--vela-fg);background:transparent;border:1px solid var(--vela-border);border-radius:5px;padding:3px 4px;outline:none;`;
2514
+ pctBox.addEventListener("focus", () => {
2515
+ pctBox.style.borderColor = "var(--vela-border-strong)";
2516
+ pctBox.select();
2517
+ });
2511
2518
  opRow.append(track, pctBox);
2512
2519
  const paintOpacity = () => {
2513
2520
  track.style.background = `linear-gradient(to right, ${curHex}00, ${curHex}ff), ${CHECKER}`;
2514
2521
  knob.style.left = `${curAlpha * 100}%`;
2515
- pctBox.textContent = `${Math.round(curAlpha * 100)}%`;
2522
+ pctBox.value = `${Math.round(curAlpha * 100)}%`;
2523
+ };
2524
+ const commitTyped = () => {
2525
+ pctBox.style.borderColor = "var(--vela-border)";
2526
+ const n = parseFloat(pctBox.value.replace("%", ""));
2527
+ if (Number.isFinite(n)) {
2528
+ const next = Math.max(0, Math.min(100, n)) / 100;
2529
+ if (Math.round(next * 100) !== Math.round(curAlpha * 100)) {
2530
+ curAlpha = next;
2531
+ paintOpacity();
2532
+ emit();
2533
+ return;
2534
+ }
2535
+ }
2536
+ paintOpacity();
2516
2537
  };
2538
+ pctBox.addEventListener("change", commitTyped);
2539
+ pctBox.addEventListener("blur", () => {
2540
+ pctBox.style.borderColor = "var(--vela-border)";
2541
+ paintOpacity();
2542
+ });
2543
+ pctBox.addEventListener("keydown", (e) => {
2544
+ if (e.key === "Escape") return;
2545
+ e.stopPropagation();
2546
+ if (e.key === "Enter") {
2547
+ e.preventDefault();
2548
+ commitTyped();
2549
+ pctBox.blur();
2550
+ }
2551
+ });
2517
2552
  let dragging = false;
2518
2553
  const onDrag = (clientX) => {
2519
2554
  const r = track.getBoundingClientRect();