@luxalgo/vela 0.6.14 → 0.6.15

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 (49) hide show
  1. package/NOTICE +28 -28
  2. package/README.md +1 -1
  3. package/dist/{DataProvider-DhzstpQb.d.ts → DataProvider-XLdJB86x.d.ts} +1 -1
  4. package/dist/{DataProvider-DlMtrwqM.d.cts → DataProvider-xH2VBcFT.d.cts} +1 -1
  5. package/dist/{chunk-F4M24ANM.js → chunk-32A7KHFM.js} +73 -3
  6. package/dist/{chunk-KG4YT3TI.js → chunk-6DPAYEGO.js} +8 -1
  7. package/dist/{chunk-64TF7JAJ.js → chunk-7C7NWEXH.js} +345 -148
  8. package/dist/{chunk-N2Q5EEFH.js → chunk-J5QIR2Z3.js} +163 -130
  9. package/dist/{chunk-MHY7MVXH.js → chunk-MWDNPGU3.js} +7 -1
  10. package/dist/{chunk-G77Y7LK2.js → chunk-MWS4CUJL.js} +2 -2
  11. package/dist/{contributions-37nni40G.d.ts → contributions-h5BzEiV2.d.ts} +13 -2
  12. package/dist/{contributions-lPojhTxI.d.cts → contributions-x7iamlnd.d.cts} +13 -2
  13. package/dist/index.cjs +174 -127
  14. package/dist/index.d.cts +20 -12
  15. package/dist/index.d.ts +20 -12
  16. package/dist/index.js +4 -4
  17. package/dist/{options-CYS5Wmlx.d.ts → options-bdSAtp7y.d.cts} +5 -0
  18. package/dist/{options-CYS5Wmlx.d.cts → options-bdSAtp7y.d.ts} +5 -0
  19. package/dist/{plugin-Cwikpz1m.d.cts → plugin-2sJcf5mk.d.cts} +11 -8
  20. package/dist/{plugin-DHyaoMjW.d.ts → plugin-BLaNPh55.d.ts} +11 -8
  21. package/dist/plugin.cjs +8 -0
  22. package/dist/plugin.d.cts +5 -5
  23. package/dist/plugin.d.ts +5 -5
  24. package/dist/plugin.js +2 -2
  25. package/dist/providers/binance.d.cts +2 -2
  26. package/dist/providers/binance.d.ts +2 -2
  27. package/dist/providers/coinbase.d.cts +2 -2
  28. package/dist/providers/coinbase.d.ts +2 -2
  29. package/dist/providers/hyperliquid.d.cts +2 -2
  30. package/dist/providers/hyperliquid.d.ts +2 -2
  31. package/dist/{side-panel-CT9ZwIGz.d.cts → side-panel-HF0IAzwf.d.cts} +17 -0
  32. package/dist/{side-panel-CT9ZwIGz.d.ts → side-panel-HF0IAzwf.d.ts} +17 -0
  33. package/dist/{statusline-model-D-q_OOx9.d.ts → statusline-model--tH0VWCy.d.cts} +6 -4
  34. package/dist/{statusline-model-CiJm-riV.d.cts → statusline-model-CLpaO3Dl.d.ts} +6 -4
  35. package/dist/ui.cjs +12 -0
  36. package/dist/ui.d.cts +1 -1
  37. package/dist/ui.d.ts +1 -1
  38. package/dist/ui.js +3 -3
  39. package/dist/vela.global.js +174 -127
  40. package/dist/vela.global.min.js +52 -46
  41. package/dist/widget.cjs +585 -272
  42. package/dist/widget.d.cts +15 -11
  43. package/dist/widget.d.ts +15 -11
  44. package/dist/widget.js +7 -7
  45. package/dist/workspace.cjs +585 -272
  46. package/dist/workspace.d.cts +29 -10
  47. package/dist/workspace.d.ts +29 -10
  48. package/dist/workspace.js +6 -6
  49. package/package.json +1 -1
package/dist/ui.js CHANGED
@@ -1,3 +1,3 @@
1
- export { Drawer, KeymapManager, Tooltip, drawerController, tooltipController } from './chunk-G77Y7LK2.js';
2
- export { CALLOUT_CSS, CALLOUT_STYLE_ID, CalloutBubble, ColorField, Dialog, FIELD_GAP_PX, GlyphSelect, Menu, NumberInput, Popover, Select, Switch, TextArea, TextField, WIDTH_FIELD_OPTIONS, applyPlotOverlayTokens, applyThemeTokens, blendOver, buildColorPicker, buildFieldControl, buildPalette, calloutPanelRows, clampNumber, closeColorPopover, closeOpenPopovers, closeWidthPopover, closesPanel, colorField, colorPickerController, combineColor, decorateSelectScroll, dialogController, ensureUIHost, eventDismissedPopover, fieldGrid, fieldGridColumns, fieldRow, fieldSection, fieldSeparator, fillSelectList, glyphSelectController, hslHex, insetRect, intersectRects, isPopoverOpen, lineWidthGlyph, menuController, nextUid, normalizeProps, numberInputController, openPopoverTrigger, openSelectList, placePopover, popoverController, runMachine, selectController, snapToStep, splitColor, spreadProps, switchController, textAreaController, textFieldController, toggleSelectList, transparencyChecker, viewportRect, widthField, widthFieldOptions } from './chunk-MHY7MVXH.js';
3
- export { FIELD_FOCUS_CSS, FIELD_FOCUS_RING, iconEl, iconMarkup, injectStyles, overlayScrollbarCss, registerIcon, svg16, svg24, withAlpha } from './chunk-KG4YT3TI.js';
1
+ export { Drawer, KeymapManager, Tooltip, drawerController, tooltipController } from './chunk-MWS4CUJL.js';
2
+ export { CALLOUT_CSS, CALLOUT_STYLE_ID, CalloutBubble, ColorField, Dialog, FIELD_GAP_PX, GlyphSelect, Menu, NumberInput, Popover, Select, Switch, TextArea, TextField, WIDTH_FIELD_OPTIONS, applyPlotOverlayTokens, applyThemeTokens, blendOver, buildColorPicker, buildFieldControl, buildPalette, calloutPanelRows, clampNumber, closeColorPopover, closeOpenPopovers, closeWidthPopover, closesPanel, colorField, colorPickerController, combineColor, decorateSelectScroll, dialogController, ensureUIHost, eventDismissedPopover, fieldGrid, fieldGridColumns, fieldRow, fieldSection, fieldSeparator, fillSelectList, glyphSelectController, hslHex, insetRect, intersectRects, isPopoverOpen, lineWidthGlyph, menuController, nextUid, normalizeProps, numberInputController, openPopoverTrigger, openSelectList, placePopover, popoverController, runMachine, selectController, snapToStep, splitColor, spreadProps, switchController, textAreaController, textFieldController, toggleSelectList, transparencyChecker, viewportRect, widthField, widthFieldOptions } from './chunk-MWDNPGU3.js';
3
+ export { FIELD_FOCUS_CSS, FIELD_FOCUS_RING, iconEl, iconMarkup, injectStyles, overlayScrollbarCss, registerIcon, svg16, svg24, withAlpha } from './chunk-6DPAYEGO.js';
@@ -276,6 +276,7 @@ var Vela = (function (exports) {
276
276
  var CROSSHAIR = "#9aa0ad";
277
277
  var SLATE_DEEP = "#1e293b";
278
278
  var SLATE = "#475569";
279
+ var CHIP_PLATE = "#595959";
279
280
  var TRADE_LONG = ACCENT;
280
281
  var TRADE_SHORT = BEARISH;
281
282
  var TRADE_EXIT = "#d500f9";
@@ -503,6 +504,10 @@ var Vela = (function (exports) {
503
504
  "market-post",
504
505
  svg24('<path d="M12 10V2"/><path d="m4.93 10.93 1.41 1.41"/><path d="M2 18h2"/><path d="M20 18h2"/><path d="m19.07 10.93-1.41 1.41"/><path d="M22 22H2"/><path d="m16 14-4 4-4-4"/><path d="M16 6a4 4 0 0 0-8 0"/>')
505
506
  );
507
+ registerIcon(
508
+ "market-extended",
509
+ svg24('<path d="M22 22H2"/><path d="M12 3a5 5 0 0 0 7.5 7.5A7.5 7.5 0 1 1 12 3Z"/>')
510
+ );
506
511
  registerIcon("market-closed", svg24('<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>'));
507
512
  registerIcon(
508
513
  "market-holiday",
@@ -516,6 +521,8 @@ var Vela = (function (exports) {
516
521
  registerIcon("chevrons-left", S('<path d="M12 3.5 7.5 8l4.5 4.5"/><path d="M7.5 3.5 3 8l4.5 4.5"/>'));
517
522
  registerIcon("check", S('<path d="m2.8 8.4 3.4 3.4 7-7.6"/>'));
518
523
  registerIcon("close", S('<path d="m3.8 3.8 8.4 8.4M12.2 3.8l-8.4 8.4"/>'));
524
+ registerIcon("pin", S('<path d="M5.6 2.5h4.8M6.4 2.5v3.6L4.4 8.4v.9h7.2v-.9L9.6 6.1V2.5M8 9.3v4.2"/>'));
525
+ registerIcon("pin-filled", S('<path d="M5.6 2.5h4.8M6.4 2.5v3.6L4.4 8.4v.9h7.2v-.9L9.6 6.1V2.5M8 9.3v4.2"/><path d="M6.4 2.5h3.2v3.6l2 2.3v.9H4.4v-.9l2-2.3z" fill="currentColor" stroke="none"/>'));
519
526
  registerIcon("eye", S('<path d="M1.5 8s2.5-5.4 6.5-5.4S14.5 8 14.5 8 12 13.4 8 13.4 1.5 8 1.5 8z"/><circle cx="8" cy="8" r="1.8"/>'));
520
527
  registerIcon("eye-off", S('<path d="M1.5 8s2.5-5.4 6.5-5.4S14.5 8 14.5 8 12 13.4 8 13.4 1.5 8 1.5 8z" opacity="0.45"/><path d="m3 13 10-10"/>'));
521
528
  registerIcon("lock", S('<rect x="3.2" y="7" width="9.6" height="6.6" rx="1.2"/><path d="M5.6 7V5.2a2.4 2.4 0 0 1 4.8 0V7"/>'));
@@ -9272,6 +9279,15 @@ var Vela = (function (exports) {
9272
9279
  console.warn(`[vela] renderer "${this.renderer.name}" does not support screenshot().`);
9273
9280
  return null;
9274
9281
  }
9282
+ /**
9283
+ * Raster of the current chart onto a canvas (same pixels as {@link screenshot}),
9284
+ * or null if the renderer has no canvas export. Silent — a host compositing
9285
+ * several charts skips a renderer that cannot contribute.
9286
+ */
9287
+ screenshotCanvas() {
9288
+ if (typeof this.renderer.screenshotCanvas === "function") return this.renderer.screenshotCanvas();
9289
+ return null;
9290
+ }
9275
9291
  /**
9276
9292
  * The active renderer's full cosmetic config as a serializable, versioned JSON
9277
9293
  * document — persist it (templates, saved settings) and feed it back to
@@ -17141,6 +17157,12 @@ ${STATIC_DECLS}
17141
17157
  .vela-ui *, .vela-ui-layer * { box-sizing: border-box; }
17142
17158
  /* Text ENTRY is the one exception: selection is part of editing. */
17143
17159
  .vela-ui :is(input, textarea), .vela-ui-layer :is(input, textarea) { user-select: text; -webkit-user-select: text; }
17160
+ /* iOS Safari zooms the page when a focused field is under 16px, and often
17161
+ keeps that zoom after the field blurs or a dialog closes. The shell's
17162
+ mobile size class is the honest gate \u2014 not a media query \u2014 so an embedded
17163
+ chart on a wide desktop still gets the rule when it is in the phone layout.
17164
+ Scoped to the kit root so a host page's own [data-layout] is never touched. */
17165
+ .vela-ui[data-layout='mobile'] :is(input, textarea, select) { font-size: 16px; }
17144
17166
  .vela-icon { display: inline-flex; align-items: center; flex: none; }
17145
17167
  .vela-icon svg { display: block; }
17146
17168
  `;
@@ -22761,6 +22783,66 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22761
22783
  center: (wickLeftDev + wickDev / 2) / dpr
22762
22784
  };
22763
22785
  }
22786
+ function aggregateCandleColumns(bars, i0, i1, xOf, yOf) {
22787
+ const out = [];
22788
+ let col = NaN;
22789
+ let intervals = [];
22790
+ const flush = () => {
22791
+ if (intervals.length === 0) return;
22792
+ const merged = [intervals[0]];
22793
+ for (let k = 1; k < intervals.length; k += 1) {
22794
+ const prev3 = merged[merged.length - 1];
22795
+ const next2 = intervals[k];
22796
+ if (Math.abs(yOf(prev3.hi) - yOf(next2.lo)) < 1) {
22797
+ prev3.hi = next2.hi;
22798
+ if (next2.headIdx < prev3.headIdx) {
22799
+ prev3.headIdx = next2.headIdx;
22800
+ prev3.headTime = next2.headTime;
22801
+ prev3.open = next2.open;
22802
+ }
22803
+ if (next2.lastIdx > prev3.lastIdx) {
22804
+ prev3.lastIdx = next2.lastIdx;
22805
+ prev3.close = next2.close;
22806
+ }
22807
+ } else {
22808
+ merged.push(next2);
22809
+ }
22810
+ }
22811
+ for (const iv of merged) out.push({ x: col, hi: iv.hi, lo: iv.lo, headTime: iv.headTime, open: iv.open, close: iv.close });
22812
+ intervals = [];
22813
+ };
22814
+ for (let i = i0; i <= i1; i += 1) {
22815
+ const b = bars[i];
22816
+ if (!b || b.high <= b.low) continue;
22817
+ const x = Math.round(xOf(i));
22818
+ if (x !== col) {
22819
+ flush();
22820
+ col = x;
22821
+ }
22822
+ let lo = b.low;
22823
+ let hi = b.high;
22824
+ let headIdx = i;
22825
+ let headTime = b.time;
22826
+ let open2 = b.open;
22827
+ for (let k = intervals.length - 1; k >= 0; k -= 1) {
22828
+ const iv = intervals[k];
22829
+ if (iv.lo > hi || iv.hi < lo) continue;
22830
+ if (iv.lo < lo) lo = iv.lo;
22831
+ if (iv.hi > hi) hi = iv.hi;
22832
+ if (iv.headIdx < headIdx) {
22833
+ headIdx = iv.headIdx;
22834
+ headTime = iv.headTime;
22835
+ open2 = iv.open;
22836
+ }
22837
+ intervals.splice(k, 1);
22838
+ }
22839
+ let insertAt = 0;
22840
+ while (insertAt < intervals.length && intervals[insertAt].lo < lo) insertAt += 1;
22841
+ intervals.splice(insertAt, 0, { lo, hi, headIdx, headTime, open: open2, lastIdx: i, close: b.close });
22842
+ }
22843
+ flush();
22844
+ return out;
22845
+ }
22764
22846
 
22765
22847
  // src/renderers/native/core/chartConfig.ts
22766
22848
  var CHART_CONFIG_VERSION = 1;
@@ -22771,6 +22853,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22771
22853
  var BASELINE_LEVEL_DEFAULT = 50;
22772
22854
  var PREMARKET_SHADE = withAlpha(WARNING, 0.08);
22773
22855
  var POSTMARKET_SHADE = withAlpha(ACCENT, 0.08);
22856
+ var EXTENDED_SHADE = POSTMARKET_SHADE;
22774
22857
  function defaultChartStyle() {
22775
22858
  return {
22776
22859
  chartTypes: {},
@@ -22779,7 +22862,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22779
22862
  gridHorz: { visible: true, color: null },
22780
22863
  borderColor: null,
22781
22864
  separatorColor: null,
22782
- crosshair: { color: CROSSHAIR, width: 1, style: "dashed", opacity: 0.4, labelBackground: SLATE },
22865
+ crosshair: { color: CROSSHAIR, width: 1, style: "dashed", opacity: 0.4, labelBackground: CHIP_PLATE },
22783
22866
  candle: {
22784
22867
  bodyVisible: true,
22785
22868
  borderVisible: false,
@@ -22802,7 +22885,7 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
22802
22885
  width: 2,
22803
22886
  baselineLevel: BASELINE_LEVEL_DEFAULT
22804
22887
  },
22805
- sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE }
22888
+ sessions: { premarketColor: PREMARKET_SHADE, postmarketColor: POSTMARKET_SHADE, extendedColor: EXTENDED_SHADE }
22806
22889
  };
22807
22890
  }
22808
22891
  var LINE_STYLES = ["solid", "dashed", "dotted"];
@@ -23031,7 +23114,8 @@ ${overlayScrollbarCss(".vela-dialog.vela-ind-dialog *", 9)}
23031
23114
  },
23032
23115
  sessions: {
23033
23116
  premarketColor: isColor(sessions.premarketColor) ? sessions.premarketColor : base.sessions.premarketColor,
23034
- postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor
23117
+ postmarketColor: isColor(sessions.postmarketColor) ? sessions.postmarketColor : base.sessions.postmarketColor,
23118
+ extendedColor: isColor(sessions.extendedColor) ? sessions.extendedColor : base.sessions.extendedColor
23035
23119
  },
23036
23120
  stacking: {
23037
23121
  candles: isNum(stacking.candles) ? stacking.candles : base.stacking.candles,
@@ -24016,44 +24100,19 @@ void main() {
24016
24100
  }
24017
24101
  }
24018
24102
  /**
24019
- * Sub-pixel LOD (mirrors Canvas2dBackend.drawCandlesAggregated): bucket bars
24020
- * sharing a rounded pixel column into one high-low stick draw cost bounded by
24021
- * screen width, not bar count. Bucket color follows first-open→last-close.
24103
+ * Sub-pixel LOD (mirrors Canvas2dBackend.drawCandlesAggregated): bars sharing a
24104
+ * rounded pixel column collapse into high-low sticks one per contiguous
24105
+ * coverage run (see {@link aggregateCandleColumns}), so a price gap inside the
24106
+ * column stays a void. Draw cost stays bounded by screen width, not bar count;
24107
+ * stick color follows its own first-open→last-close.
24022
24108
  */
24023
24109
  emitCandlesAggregated(b, bars, i0, i1, coords, pane, up, down, barColors) {
24024
- let px = NaN;
24025
- let lo = Infinity;
24026
- let hi = -Infinity;
24027
- let openV = 0;
24028
- let closeV = 0;
24029
- let headTime = 0;
24030
- let active = false;
24031
- const flush = () => {
24032
- if (!active) return;
24033
- const c = parseColor(barColors.get(headTime) ?? (closeV >= openV ? up : down));
24034
- const hY = coords.priceToY(hi, pane.scale, pane.bounds);
24035
- const lY = coords.priceToY(lo, pane.scale, pane.bounds);
24036
- b.rect(px - 0.5, hY, 1, lY - hY, c);
24037
- };
24038
- for (let i = i0; i <= i1; i += 1) {
24039
- const bar = bars[i];
24040
- if (!bar || bar.high <= bar.low) continue;
24041
- const x = Math.round(coords.logicalToX(i));
24042
- if (!active || x !== px) {
24043
- flush();
24044
- px = x;
24045
- lo = bar.low;
24046
- hi = bar.high;
24047
- openV = bar.open;
24048
- headTime = bar.time;
24049
- active = true;
24050
- } else {
24051
- if (bar.low < lo) lo = bar.low;
24052
- if (bar.high > hi) hi = bar.high;
24053
- }
24054
- closeV = bar.close;
24110
+ const yOf = (price) => coords.priceToY(price, pane.scale, pane.bounds);
24111
+ for (const s of aggregateCandleColumns(bars, i0, i1, (i) => coords.logicalToX(i), yOf)) {
24112
+ const c = parseColor(barColors.get(s.headTime) ?? (s.close >= s.open ? up : down));
24113
+ const hY = yOf(s.hi);
24114
+ b.rect(s.x - 0.5, hY, 1, yOf(s.lo) - hY, c);
24055
24115
  }
24056
- flush();
24057
24116
  }
24058
24117
  emitSeries(b, spec, pane, coords, i0, i1, theme, off = 0) {
24059
24118
  if (spec.kind === "candle" || spec.kind === "bar") {
@@ -25351,13 +25410,15 @@ void main() {
25351
25410
  if (!this.orderedCache) this.orderedCache = [...this.panes.values()].sort((a, b) => a.order - b.order);
25352
25411
  return this.orderedCache;
25353
25412
  }
25354
- /** The session zones resolved into colored bands (pre/post-market washes from the
25355
- * config's session colors) — consumed by the same painting path as {@link highlights}. */
25413
+ /** The session zones resolved into colored bands (pre/post-market or extended-hours
25414
+ * washes from the config's session colors) — consumed by the same painting path as
25415
+ * {@link highlights}. */
25356
25416
  sessionHighlightBands() {
25357
25417
  if (!this.sessionZones) return [];
25358
25418
  const out = [];
25359
25419
  for (const [from, to] of this.sessionZones.pre) out.push({ from, to, color: this.style.sessions.premarketColor });
25360
25420
  for (const [from, to] of this.sessionZones.post) out.push({ from, to, color: this.style.sessions.postmarketColor });
25421
+ for (const [from, to] of this.sessionZones.extended) out.push({ from, to, color: this.style.sessions.extendedColor });
25361
25422
  return out;
25362
25423
  }
25363
25424
  indicatorsForPane(paneId) {
@@ -25896,50 +25957,23 @@ void main() {
25896
25957
  }
25897
25958
  }
25898
25959
  /**
25899
- * Sub-pixel LOD: bucket every bar sharing a rounded pixel column into ONE
25900
- * high-low stick, so draw cost is bounded by screen width (not bar count) when
25901
- * zoomed far out. The bucket's color follows its first-open→last-close
25902
- * direction (a barcolor() on the head bar still wins).
25960
+ * Sub-pixel LOD: bars sharing a rounded pixel column collapse into high-low
25961
+ * sticks (one per contiguous coverage run see {@link aggregateCandleColumns}),
25962
+ * so draw cost is bounded by screen width (not bar count) when zoomed far out
25963
+ * and a price gap inside the column stays a void. Each stick's color follows
25964
+ * its first-open→last-close direction (a barcolor() on the head bar still wins).
25903
25965
  */
25904
25966
  drawCandlesAggregated(ctx, bars, i0, i1, coords, pane, up, down, barColors) {
25905
- let px = NaN;
25906
- let lo = Infinity;
25907
- let hi = -Infinity;
25908
- let openV = 0;
25909
- let closeV = 0;
25910
- let headTime = 0;
25911
- let active = false;
25912
- const flush = () => {
25913
- if (!active) return;
25914
- const color = barColors.get(headTime) ?? (closeV >= openV ? up : down);
25915
- const hY = coords.priceToY(hi, pane.scale, pane.bounds);
25916
- const lY = coords.priceToY(lo, pane.scale, pane.bounds);
25917
- ctx.strokeStyle = color;
25918
- ctx.lineWidth = 1;
25967
+ const yOf = (price) => coords.priceToY(price, pane.scale, pane.bounds);
25968
+ ctx.lineWidth = 1;
25969
+ for (const s of aggregateCandleColumns(bars, i0, i1, (i) => coords.logicalToX(i), yOf)) {
25970
+ const x = s.x + 0.5;
25971
+ ctx.strokeStyle = barColors.get(s.headTime) ?? (s.close >= s.open ? up : down);
25919
25972
  ctx.beginPath();
25920
- ctx.moveTo(px, hY);
25921
- ctx.lineTo(px, lY);
25973
+ ctx.moveTo(x, yOf(s.hi));
25974
+ ctx.lineTo(x, yOf(s.lo));
25922
25975
  ctx.stroke();
25923
- };
25924
- for (let i = i0; i <= i1; i += 1) {
25925
- const b = bars[i];
25926
- if (!b || b.high <= b.low) continue;
25927
- const x = Math.round(coords.logicalToX(i)) + 0.5;
25928
- if (!active || x !== px) {
25929
- flush();
25930
- px = x;
25931
- lo = b.low;
25932
- hi = b.high;
25933
- openV = b.open;
25934
- headTime = b.time;
25935
- active = true;
25936
- } else {
25937
- if (b.low < lo) lo = b.low;
25938
- if (b.high > hi) hi = b.high;
25939
- }
25940
- closeV = b.close;
25941
25976
  }
25942
- flush();
25943
25977
  }
25944
25978
  drawSeries(ctx, spec, pane, coords, i0, i1, theme, off = 0) {
25945
25979
  if (spec.kind === "candle" || spec.kind === "bar") {
@@ -26963,6 +26997,32 @@ void main() {
26963
26997
  }
26964
26998
  };
26965
26999
 
27000
+ // src/renderers/native/chrome/tz.ts
27001
+ function tzOffsetMs(ms, timeZone) {
27002
+ if (!timeZone || timeZone === "UTC") return 0;
27003
+ try {
27004
+ const dtf = new Intl.DateTimeFormat("en-US", {
27005
+ timeZone,
27006
+ hourCycle: "h23",
27007
+ year: "numeric",
27008
+ month: "2-digit",
27009
+ day: "2-digit",
27010
+ hour: "2-digit",
27011
+ minute: "2-digit",
27012
+ second: "2-digit"
27013
+ });
27014
+ const parts3 = dtf.formatToParts(new Date(ms));
27015
+ const get = (t) => Number(parts3.find((p) => p.type === t)?.value);
27016
+ const asUTC = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour") % 24, get("minute"), get("second"));
27017
+ return asUTC - ms;
27018
+ } catch {
27019
+ return 0;
27020
+ }
27021
+ }
27022
+ function zonedDate(ms, timeZone) {
27023
+ return new Date(ms + tzOffsetMs(ms, timeZone));
27024
+ }
27025
+
26966
27026
  // src/renderers/native/chrome/ticks.ts
26967
27027
  function priceTicks(min2, max2, target = 6) {
26968
27028
  if (!(max2 > min2) || !Number.isFinite(min2) || !Number.isFinite(max2)) return [];
@@ -27110,7 +27170,14 @@ void main() {
27110
27170
  YEAR
27111
27171
  ];
27112
27172
  var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
27173
+ var WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
27113
27174
  var pad2 = (n) => n < 10 ? `0${n}` : String(n);
27175
+ function formatTimeStamp(ms, timeZone, barIntervalMs) {
27176
+ const d = zonedDate(ms, timeZone);
27177
+ const date = `${WEEKDAYS[d.getUTCDay()]} ${d.getUTCDate()} ${MONTHS[d.getUTCMonth()]} '${pad2(d.getUTCFullYear() % 100)}`;
27178
+ if (barIntervalMs >= DAY) return date;
27179
+ return `${date} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}`;
27180
+ }
27114
27181
  function pickStep(targetMs) {
27115
27182
  for (const step of STEP_LADDER) if (step >= targetMs) return step;
27116
27183
  return STEP_LADDER[STEP_LADDER.length - 1];
@@ -27152,32 +27219,6 @@ void main() {
27152
27219
  return out;
27153
27220
  }
27154
27221
 
27155
- // src/renderers/native/chrome/tz.ts
27156
- function tzOffsetMs(ms, timeZone) {
27157
- if (!timeZone || timeZone === "UTC") return 0;
27158
- try {
27159
- const dtf = new Intl.DateTimeFormat("en-US", {
27160
- timeZone,
27161
- hourCycle: "h23",
27162
- year: "numeric",
27163
- month: "2-digit",
27164
- day: "2-digit",
27165
- hour: "2-digit",
27166
- minute: "2-digit",
27167
- second: "2-digit"
27168
- });
27169
- const parts3 = dtf.formatToParts(new Date(ms));
27170
- const get = (t) => Number(parts3.find((p) => p.type === t)?.value);
27171
- const asUTC = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour") % 24, get("minute"), get("second"));
27172
- return asUTC - ms;
27173
- } catch {
27174
- return 0;
27175
- }
27176
- }
27177
- function zonedDate(ms, timeZone) {
27178
- return new Date(ms + tzOffsetMs(ms, timeZone));
27179
- }
27180
-
27181
27222
  // src/renderers/native/chrome/ChromeRenderer.ts
27182
27223
  var ChromeRenderer = class {
27183
27224
  constructor() {
@@ -27624,7 +27665,7 @@ void main() {
27624
27665
  const price = coords.yToPrice(ch.y, pane.scale, pane.bounds);
27625
27666
  this.chip(ctx, dataW + 1, ch.y, formatAxisValue(pane.scale, pane.bounds.height, price, percentScaleFor(scene, pane), scene.priceMintick, pane.axisFormat), chipBg, "left", false, theme.background);
27626
27667
  }
27627
- this.chip(ctx, x, dataH + 1, formatStamp(coords.logicalToTime(logical), scene.timezone), chipBg, "center", true, theme.background);
27668
+ this.chip(ctx, x, dataH + 1, formatTimeStamp(coords.logicalToTime(logical), scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
27628
27669
  }
27629
27670
  destroy() {
27630
27671
  this.canvas = null;
@@ -27672,7 +27713,7 @@ void main() {
27672
27713
  this.chip(ctx, dataW + 1, ext.y, formatAxisValue(pane.scale, pane.bounds.height, ext.price, percentScaleFor(scene, pane), scene.priceMintick, pane.axisFormat), chipBg, "left", false, theme.background);
27673
27714
  }
27674
27715
  }
27675
- this.chip(ctx, x, dataH + 1, formatStamp(ext.time, scene.timezone), chipBg, "center", true, theme.background);
27716
+ this.chip(ctx, x, dataH + 1, formatTimeStamp(ext.time, scene.timezone, coords.barInterval), chipBg, "center", true, theme.background);
27676
27717
  ctx.globalAlpha = 1;
27677
27718
  }
27678
27719
  /** A soft band + a brighter crisp center line over the hovered separator, so it reads as
@@ -27705,11 +27746,6 @@ void main() {
27705
27746
  else if (style === "dotted") ctx.setLineDash([2, 3]);
27706
27747
  else ctx.setLineDash([]);
27707
27748
  }
27708
- function formatStamp(ms, timeZone) {
27709
- const d = zonedDate(ms, timeZone);
27710
- const pad = (n) => n < 10 ? `0${n}` : String(n);
27711
- return `${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
27712
- }
27713
27749
 
27714
27750
  // src/core/timezones.ts
27715
27751
  var TIMEZONES = [
@@ -34198,6 +34234,9 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34198
34234
  const end = Math.min(right, x2);
34199
34235
  return end > x ? { x, width: end - x } : null;
34200
34236
  }
34237
+ function bandEdgeSlot(logical) {
34238
+ return Math.ceil(logical) - 0.5;
34239
+ }
34201
34240
  var BackdropRenderer = class {
34202
34241
  constructor() {
34203
34242
  this.canvas = null;
@@ -34228,21 +34267,22 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34228
34267
  this.drawGrid(ctx, scene, coords, theme, coords.width, gridAlpha);
34229
34268
  }
34230
34269
  /** Renderer-owned session highlight bands: full-height (all panes), behind the grid.
34231
- * Session-zone washes (pre/post-market) paint first, host highlights on top. */
34270
+ * Session-zone washes paint first (edges snapped to bar-slot boundaries so a band
34271
+ * never cuts a candle in two), host highlights on top (continuous time mapping —
34272
+ * hosts may paint sub-bar spans). */
34232
34273
  drawHighlights(ctx, scene, coords) {
34233
34274
  const sessions = scene.sessionHighlightBands();
34234
34275
  if (sessions.length > 0) {
34235
34276
  const left = Math.max(0, coords.logicalToX(-0.5));
34236
34277
  const right = Math.min(coords.width, coords.logicalToX(coords.barCount - 0.5));
34237
- this.drawHighlightSet(ctx, sessions, coords, left, right);
34278
+ this.drawHighlightSet(ctx, sessions, coords, left, right, true);
34238
34279
  }
34239
- this.drawHighlightSet(ctx, scene.highlights, coords, 0, coords.width);
34280
+ this.drawHighlightSet(ctx, scene.highlights, coords, 0, coords.width, false);
34240
34281
  }
34241
- drawHighlightSet(ctx, bands, coords, left, right) {
34282
+ drawHighlightSet(ctx, bands, coords, left, right, snapToSlots) {
34283
+ const edgeX = (ms) => snapToSlots ? coords.logicalToX(bandEdgeSlot(coords.timeToLogical(ms))) : coords.timeToX(ms);
34242
34284
  for (const band of bands) {
34243
- const x1 = coords.timeToX(band.from);
34244
- const x2 = coords.timeToX(band.to);
34245
- const rect = clipHighlightRect(x1, x2, left, right);
34285
+ const rect = clipHighlightRect(edgeX(band.from), edgeX(band.to), left, right);
34246
34286
  if (!rect) continue;
34247
34287
  ctx.fillStyle = band.color;
34248
34288
  ctx.fillRect(rect.x, 0, rect.width, coords.height);
@@ -34501,6 +34541,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34501
34541
  });
34502
34542
  el.style.color = attributionMarkColor(background);
34503
34543
  el.innerHTML = html;
34544
+ el.dataset.velaScreenshot = "1";
34504
34545
  return el;
34505
34546
  }
34506
34547
  function createAttributionMark(doc, background) {
@@ -34519,6 +34560,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
34519
34560
  cursor: "pointer"
34520
34561
  });
34521
34562
  applyAttributionMarkTheme(a, background);
34563
+ a.dataset.velaScreenshot = "1";
34522
34564
  const symbol = doc.createElement("span");
34523
34565
  symbol.className = "vela-attr-symbol";
34524
34566
  symbol.setAttribute("aria-hidden", "true");
@@ -35523,7 +35565,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35523
35565
  };
35524
35566
  })(),
35525
35567
  series: { style: this.scene.priceStyle, baseline: this.scene.baselineValue, spacing: this.coords.spacingScale },
35526
- sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor }
35568
+ sessions: { premarketColor: s.sessions.premarketColor, postmarketColor: s.sessions.postmarketColor, extendedColor: s.sessions.extendedColor }
35527
35569
  };
35528
35570
  }
35529
35571
  /**
@@ -35605,7 +35647,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35605
35647
  width: next2.baseline.width,
35606
35648
  baselineLevel: next2.baseline.baselineLevel
35607
35649
  };
35608
- s.sessions = { premarketColor: next2.sessions.premarketColor, postmarketColor: next2.sessions.postmarketColor };
35650
+ s.sessions = { premarketColor: next2.sessions.premarketColor, postmarketColor: next2.sessions.postmarketColor, extendedColor: next2.sessions.extendedColor };
35609
35651
  this.setPriceStyle(next2.series.style);
35610
35652
  this.scene.candleOverride = candleOverrideFor(this.scene.priceStyle, s.chartTypes);
35611
35653
  this.scene.baselineValue = next2.series.baseline;
@@ -35827,9 +35869,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35827
35869
  * opts in with a `data-vela-screenshot` attribute on the mount container's
35828
35870
  * subtree (the widget marks its status line, and its symbol watermark with
35829
35871
  * `"under"` — drawn beneath the canvases, where it sits on screen). Only the
35830
- * crosshair (L2) is intentionally excluded.
35872
+ * crosshair (L2) is intentionally excluded. {@link screenshot} is this canvas
35873
+ * as a PNG data URL.
35831
35874
  */
35832
- screenshot() {
35875
+ screenshotCanvas() {
35833
35876
  if (!this.dataCanvas) return null;
35834
35877
  this.computeScales();
35835
35878
  this.paintData();
@@ -35857,7 +35900,10 @@ ${overlayScrollbarCss(".vela-sd-pane")}
35857
35900
  if (el.getAttribute("data-vela-screenshot") !== "under") rasterizeOverlay(ctx, el, frame);
35858
35901
  }
35859
35902
  }
35860
- return out.toDataURL("image/png");
35903
+ return out;
35904
+ }
35905
+ screenshot() {
35906
+ return this.screenshotCanvas()?.toDataURL("image/png") ?? null;
35861
35907
  }
35862
35908
  /**
35863
35909
  * The candles-first-appear reveal — animates the **candles themselves** (no extra
@@ -37252,7 +37298,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
37252
37298
  const pricePane = this.dataWindowPricePane();
37253
37299
  const ohlc = this.dataWindowOHLC(bar, pricePane);
37254
37300
  const parts3 = [];
37255
- if (bar) parts3.push(formatStamp2(bar.time, this.scene.timezone));
37301
+ if (bar) parts3.push(formatStamp(bar.time, this.scene.timezone));
37256
37302
  if (ohlc) parts3.push(`Open ${ohlc.o}, High ${ohlc.h}, Low ${ohlc.l}, Close ${ohlc.c}`);
37257
37303
  for (const group of this.dataWindowGroups(idx, pricePane)) {
37258
37304
  for (const row of group.rows) parts3.push(`${row.label} ${row.value}`);
@@ -38139,7 +38185,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
38139
38185
  time: `${p(d.getUTCHours())}:${p(d.getUTCMinutes())}`
38140
38186
  };
38141
38187
  }
38142
- function formatStamp2(ms, timeZone) {
38188
+ function formatStamp(ms, timeZone) {
38143
38189
  const { date, time } = formatStampParts(ms, timeZone);
38144
38190
  return `${date} ${time}`;
38145
38191
  }
@@ -38170,7 +38216,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
38170
38216
  }
38171
38217
  return out.sort((a, b) => a[0] - b[0]);
38172
38218
  };
38173
- return { pre: windows(v.pre), post: windows(v.post) };
38219
+ return { pre: windows(v.pre), post: windows(v.post), extended: windows(v.extended) };
38174
38220
  }
38175
38221
  function clamp012(v) {
38176
38222
  return Math.max(0, Math.min(1, v));
@@ -40078,6 +40124,7 @@ ${overlayScrollbarCss(".vela-sd-pane")}
40078
40124
  exports.BarStore = BarStore;
40079
40125
  exports.BinanceProvider = BinanceProvider;
40080
40126
  exports.CATEGORICAL = CATEGORICAL;
40127
+ exports.CHIP_PLATE = CHIP_PLATE;
40081
40128
  exports.CROSSHAIR = CROSSHAIR;
40082
40129
  exports.CachingDataFeed = CachingDataFeed;
40083
40130
  exports.CoinbaseProvider = CoinbaseProvider;