@luxalgo/vela 0.6.16 → 0.6.18

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 (46) hide show
  1. package/README.md +4 -0
  2. package/dist/{DataProvider-XLdJB86x.d.ts → DataProvider-Cb-2lC5O.d.ts} +1 -1
  3. package/dist/{DataProvider-xH2VBcFT.d.cts → DataProvider-UtWw2CgJ.d.cts} +1 -1
  4. package/dist/{chunk-GIJYO4LO.js → chunk-22QJKGXX.js} +666 -189
  5. package/dist/{chunk-SEOO6YG7.js → chunk-BXF7VNNE.js} +181 -111
  6. package/dist/{chunk-6DPAYEGO.js → chunk-CAFCLMPF.js} +1 -0
  7. package/dist/{chunk-I77F2SVE.js → chunk-IFJJPXSV.js} +1 -1
  8. package/dist/{chunk-MGU3USTP.js → chunk-T5Z5YUCF.js} +1 -1
  9. package/dist/{chunk-Q3TFJNJW.js → chunk-XCBJU674.js} +2 -2
  10. package/dist/{contributions-BEkxwA5F.d.cts → contributions-5nz3PLRe.d.ts} +9 -4
  11. package/dist/{contributions-CoAX7_SF.d.ts → contributions-DEPRtkmG.d.cts} +9 -4
  12. package/dist/index.cjs +662 -185
  13. package/dist/index.d.cts +15 -6
  14. package/dist/index.d.ts +15 -6
  15. package/dist/index.js +4 -4
  16. package/dist/{options-bdSAtp7y.d.ts → options-BCRmYALw.d.cts} +24 -5
  17. package/dist/{options-bdSAtp7y.d.cts → options-BCRmYALw.d.ts} +24 -5
  18. package/dist/{plugin-CNUgbRNq.d.ts → plugin-CUtfLeqz.d.ts} +3 -3
  19. package/dist/{plugin-Dxyq35Zf.d.cts → plugin-DVUaROPo.d.cts} +3 -3
  20. package/dist/plugin.cjs +1 -0
  21. package/dist/plugin.d.cts +4 -4
  22. package/dist/plugin.d.ts +4 -4
  23. package/dist/plugin.js +2 -2
  24. package/dist/providers/binance.d.cts +2 -2
  25. package/dist/providers/binance.d.ts +2 -2
  26. package/dist/providers/coinbase.d.cts +2 -2
  27. package/dist/providers/coinbase.d.ts +2 -2
  28. package/dist/providers/hyperliquid.d.cts +2 -2
  29. package/dist/providers/hyperliquid.d.ts +2 -2
  30. package/dist/{statusline-model-CbPza-Wc.d.cts → statusline-model-7ECSDtgt.d.cts} +3 -3
  31. package/dist/{statusline-model-D7cWkw8A.d.ts → statusline-model-D1Fhs8Dk.d.ts} +3 -3
  32. package/dist/ui.cjs +1 -0
  33. package/dist/ui.d.cts +1 -1
  34. package/dist/ui.d.ts +1 -1
  35. package/dist/ui.js +3 -3
  36. package/dist/vela.global.js +662 -185
  37. package/dist/vela.global.min.js +52 -53
  38. package/dist/widget.cjs +839 -291
  39. package/dist/widget.d.cts +39 -12
  40. package/dist/widget.d.ts +39 -12
  41. package/dist/widget.js +7 -7
  42. package/dist/workspace.cjs +839 -291
  43. package/dist/workspace.d.cts +5 -5
  44. package/dist/workspace.d.ts +5 -5
  45. package/dist/workspace.js +6 -6
  46. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
- import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-GIJYO4LO.js';
2
- import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType, inputDeltas } from './chunk-I77F2SVE.js';
3
- import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-Q3TFJNJW.js';
4
- import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-MGU3USTP.js';
5
- import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-6DPAYEGO.js';
1
+ import { parseSymbol, priceStyleIds, BUILTIN_PRICE_STYLES, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel, isGroupRow, defaultMemberOf, groupKeyOf, groupMembers, LEGEND_AT_TOP_ATTR, normalizeSession, Vela, TypedEventBus, MultiProviderFeed, registerBuiltinChartTypes, resolveTheme, buildToolbar, createCustomMark, createAttributionMark, DrawingToolbar, applyAttributionMarkTheme, sharedBarStore, timeframeToMs } from './chunk-22QJKGXX.js';
2
+ import { chartType, resolveTopbarComposition, topbarActionOverride, TOPBAR_BUILTIN_IDS, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, symbolRanking, resolveEngines, legendActionsProviderFor, legendCalloutsProviderFor, statePersistenceHandlers, topbarHas, rendererDefaults, widgetAttachments, getDrawingType, inputDeltas } from './chunk-IFJJPXSV.js';
3
+ import { Tooltip, KeymapManager, isEditableTarget, Drawer } from './chunk-XCBJU674.js';
4
+ import { Menu, Dialog, CalloutBubble, applyPlotOverlayTokens, ensureUIHost } from './chunk-T5Z5YUCF.js';
5
+ import { registerIcon, svg16, injectStyles, iconEl, iconMarkup, iconAt, SESSION_OFF, SESSION_POST, SESSION_PRE, icon, categoricalColor } from './chunk-CAFCLMPF.js';
6
6
  import { baseOf } from './chunk-W4EJWLEO.js';
7
7
 
8
8
  // src/widget/timeframe.ts
@@ -1263,6 +1263,9 @@ function paneLabel(pane, fallbackTitle) {
1263
1263
  function indicatorLabel(i, fallbackTitle) {
1264
1264
  return i.title || fallbackTitle(i.id) || i.id;
1265
1265
  }
1266
+ function indicatorRowLabel(i, fallbackTitle) {
1267
+ return i.shorttitle || indicatorLabel(i, fallbackTitle);
1268
+ }
1266
1269
  function paneRows(pane) {
1267
1270
  return pane.items.flatMap((it) => it.kind === "row" ? [it.row] : []);
1268
1271
  }
@@ -1394,7 +1397,7 @@ function paneItems(snap, pane, units) {
1394
1397
  const indicatorRow = (i) => ({
1395
1398
  kind: "indicator",
1396
1399
  id: i.id,
1397
- label: indicatorLabel(i, snap.handleTitle),
1400
+ label: indicatorRowLabel(i, snap.handleTitle),
1398
1401
  visible: snap.indicatorVisible(i.id),
1399
1402
  ownScale: i.ownScale
1400
1403
  });
@@ -1524,9 +1527,13 @@ var CSS4 = `
1524
1527
  .vela-ot-empty { padding: 20px 10px; text-align: center; color: var(--vela-fg-muted); font-size: 12px; }
1525
1528
 
1526
1529
  /* \u2500\u2500 drawing groups \u2500\u2500 */
1527
- /* One top-level entry in a pane's drawing list: a lone drawing, or a whole group block. The
1528
- transparent border reserves the outline used when a drawing is dropped into the group. */
1529
- .vela-ot-unit { border: 1px solid transparent; border-radius: 6px; }
1530
+ /* One top-level entry in a pane's drawing list: a lone drawing, or a whole group block. No
1531
+ border of its own \u2014 units stack flush, so consecutive highlighted rows read as one block; the
1532
+ drop-into-group outline is drawn inside the box instead (see the drag-and-drop rules). */
1533
+ .vela-ot-unit { border-radius: 6px; }
1534
+ /* Adjacent picked rows merge into one contiguous highlight: the shared edge loses its rounding. */
1535
+ .vela-ot-unit:has(> .vela-ot-row[data-picked]) + .vela-ot-unit > .vela-ot-row[data-picked] { border-top-left-radius: 0; border-top-right-radius: 0; }
1536
+ .vela-ot-unit:has(+ .vela-ot-unit > .vela-ot-row[data-picked]) > .vela-ot-row[data-picked] { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
1530
1537
  .vela-ot-row[data-row-kind='group'] > .vela-icon { width: 12px; font-size: 11px; }
1531
1538
  /* A member sits indented under its group's header. */
1532
1539
  .vela-ot-subrow { padding-left: 26px; }
@@ -1583,8 +1590,10 @@ var CSS4 = `
1583
1590
  .vela-ot-gap[data-drop] { height: 4px; background: var(--vela-fg-bright); }
1584
1591
  .vela-ot-gap[data-drop]::before { display: none; }
1585
1592
  /* A whole container accepts the drop: merging into a pane, or a pane with no drawings yet.
1586
- The pane block's transparent border reserves the room for this outline. */
1593
+ The pane block's transparent border reserves the room for this outline; a group unit has no
1594
+ border to color, so it draws the same line as an inset outline (no layout footprint). */
1587
1595
  .vela-ot [data-drop='target'] { border-color: var(--vela-fg-bright); background: var(--vela-hover); }
1596
+ .vela-ot-unit[data-drop='target'] { outline: 1px solid var(--vela-fg-bright); outline-offset: -1px; }
1588
1597
  /* Where a reorder would insert. */
1589
1598
  .vela-ot [data-drop='before'] { box-shadow: inset 0 2px 0 var(--vela-fg-bright); }
1590
1599
  .vela-ot [data-drop='after'] { box-shadow: inset 0 -2px 0 var(--vela-fg-bright); }
@@ -1647,7 +1656,8 @@ var ObjectTree = class extends SidePanel {
1647
1656
  super(host, "Object tree", "vela-ot");
1648
1657
  this.iconFor = iconFor;
1649
1658
  this.chart = null;
1650
- this.selectedDrawing = null;
1659
+ /** What the CHART has selected — every member of a multi-selection, mirrored as rows. */
1660
+ this.selectedDrawings = /* @__PURE__ */ new Set();
1651
1661
  this.symbolName = "";
1652
1662
  /** The raw (possibly venue-prefixed) symbol — what icon resolution routes on. */
1653
1663
  this.symbolRaw = "";
@@ -1698,12 +1708,9 @@ var ObjectTree = class extends SidePanel {
1698
1708
  this.unsubs.push(chart.on(ev, () => this.refresh()));
1699
1709
  }
1700
1710
  this.unsubs.push(
1701
- chart.on("drawing:selected", ({ id }) => {
1702
- this.selectedDrawing = id;
1703
- if (!this.syncingSelection) {
1704
- this.picked.clear();
1705
- if (id) this.picked.add(id);
1706
- }
1711
+ chart.on("drawing:selected", ({ ids }) => {
1712
+ this.selectedDrawings = new Set(ids);
1713
+ if (!this.syncingSelection) this.picked = new Set(ids);
1707
1714
  this.refresh();
1708
1715
  })
1709
1716
  );
@@ -2051,7 +2058,7 @@ var ObjectTree = class extends SidePanel {
2051
2058
  el.dataset.drag = "1";
2052
2059
  el.title = "Drag to restack, or onto another pane to move it there";
2053
2060
  if (inGroup) el.classList.add("vela-ot-subrow");
2054
- if (d.id === this.selectedDrawing) el.dataset.selected = "1";
2061
+ if (this.selectedDrawings.has(d.id)) el.dataset.selected = "1";
2055
2062
  if (this.picked.has(d.id)) el.dataset.picked = "1";
2056
2063
  el.addEventListener("click", (e) => this.onDrawClick(e, d.id));
2057
2064
  return el;
@@ -3228,6 +3235,20 @@ var SymbolPicker = class {
3228
3235
  this.seed = initialQuery;
3229
3236
  this.dialog.show();
3230
3237
  }
3238
+ get isOpen() {
3239
+ return this.dialog.open;
3240
+ }
3241
+ /** Text typed at the picker that never reached its field: closed, it opens seeded
3242
+ * with the text; open (the field takes focus a tick after the dialog does, and a
3243
+ * fast typist's next key lands in that tick), it extends the query. */
3244
+ type(text) {
3245
+ if (!this.isOpen) {
3246
+ this.open(text);
3247
+ return;
3248
+ }
3249
+ this.input.value = (this.input.value + text).toUpperCase();
3250
+ this.refresh();
3251
+ }
3231
3252
  close() {
3232
3253
  this.dialog.hide();
3233
3254
  }
@@ -3641,6 +3662,20 @@ var TimeframeQuick = class {
3641
3662
  this.input.setSelectionRange(this.input.value.length, this.input.value.length);
3642
3663
  }, 0);
3643
3664
  }
3665
+ get isOpen() {
3666
+ return this.dialog.open;
3667
+ }
3668
+ /** Text typed at the entry that never reached its field: closed, it opens seeded
3669
+ * with the text; open (the field takes focus a tick after the dialog does, and a
3670
+ * fast typist's next key lands in that tick), it extends the entry. */
3671
+ type(text) {
3672
+ if (!this.isOpen) {
3673
+ this.open(text);
3674
+ return;
3675
+ }
3676
+ this.input.value += text;
3677
+ this.renderHint();
3678
+ }
3644
3679
  close() {
3645
3680
  this.dialog.hide();
3646
3681
  }
@@ -4033,11 +4068,34 @@ function fmtChange(open, close) {
4033
4068
  const sign = diff >= 0 ? "+" : "";
4034
4069
  return `${sign}${fmtPrice(diff, decimalsFor(close))} (${sign}${pct.toFixed(2)}%)`;
4035
4070
  }
4071
+ function fmtChangePct(open, close) {
4072
+ if (open == null || close == null || !Number.isFinite(open) || !Number.isFinite(close) || open === 0) return "";
4073
+ const pct = (close - open) / open * 100;
4074
+ return `${pct >= 0 ? "+" : ""}${pct.toFixed(2)}%`;
4075
+ }
4036
4076
 
4037
4077
  // src/widget/statusline-model.ts
4038
4078
  function segmentVisibility(parts, chartHidden = false) {
4039
4079
  return { avatar: parts.logo, symbol: parts.name, meta: parts.name, market: parts.market, ohlc: parts.ohlc && !chartHidden, change: parts.change && !chartHidden, eye: chartHidden };
4040
4080
  }
4081
+ var STATUSLINE_LADDER = [
4082
+ { stacked: false, level: "full" },
4083
+ { stacked: true, level: "full" },
4084
+ { stacked: true, level: "compact" },
4085
+ { stacked: true, level: "minimal" }
4086
+ ];
4087
+ function readoutCells(level, readout) {
4088
+ if (readout === "value") return [{ key: "close", label: "" }];
4089
+ if (level === "full") {
4090
+ return [
4091
+ { key: "open", label: "O" },
4092
+ { key: "high", label: "H" },
4093
+ { key: "low", label: "L" },
4094
+ { key: "close", label: "C" }
4095
+ ];
4096
+ }
4097
+ return [{ key: "close", label: level === "compact" ? "C" : "" }];
4098
+ }
4041
4099
  function statuslineMenuItems(parts, chartVisible) {
4042
4100
  return [
4043
4101
  { id: "part:logo", label: "Symbol logo", checked: parts.logo },
@@ -4064,6 +4122,11 @@ var CSS10 = `
4064
4122
  display: flex;
4065
4123
  align-items: baseline;
4066
4124
  gap: var(--vela-space-2);
4125
+ /* The chip never grows past the plot: fit() measures overflow against this width to
4126
+ * walk the layout ladder (stack, then shed values), so overflow:hidden only guards
4127
+ * the transient between a resize and the next measure. */
4128
+ max-width: calc(100% - var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px) - 24px);
4129
+ overflow: hidden;
4067
4130
  color: var(--vela-fg);
4068
4131
  font-size: var(--vela-font-size-md);
4069
4132
  /* Same chip treatment as the indicator legend rows (InputsUI): a translucent wash of
@@ -4085,6 +4148,31 @@ var CSS10 = `
4085
4148
  /* Chart hidden (the price series' eye \u2014 renderer 'candleVisible'): the line dims to
4086
4149
  * the same 0.5 wash a hidden indicator's legend row wears. */
4087
4150
  .vela-statusline.vela-sl-chart-hidden { opacity: 0.5; }
4151
+ /* Two rows inside the chip \u2014 the identity (logo / symbol / meta / market badge) and the
4152
+ * value readout (O/H/L/C + change, or the show-chart eye). In the widest layout they sit
4153
+ * side by side on one line; the STACKED layouts (fit() decides, see the ladder in
4154
+ * statusline-model) turn the chip into a column so the values drop under the symbol. */
4155
+ .vela-statusline .vela-sl-row {
4156
+ display: flex;
4157
+ align-items: baseline;
4158
+ gap: var(--vela-space-2);
4159
+ white-space: nowrap;
4160
+ }
4161
+ .vela-statusline.vela-sl-stacked {
4162
+ flex-direction: column;
4163
+ align-items: flex-start;
4164
+ /* The same air between the two rows as between the values row and the legend row
4165
+ * that follows the chip (see the stacked legend shift below). */
4166
+ row-gap: var(--vela-space-1);
4167
+ }
4168
+ /* Stacked, the identity row may shrink (the meta carries the ellipsis) \u2014 the values row
4169
+ * never does, so its overflow is what fit() measures to keep descending the ladder. */
4170
+ .vela-statusline.vela-sl-stacked .vela-sl-identity { max-width: 100%; }
4171
+ .vela-statusline.vela-sl-stacked .vela-sl-meta {
4172
+ min-width: 0;
4173
+ overflow: hidden;
4174
+ text-overflow: ellipsis;
4175
+ }
4088
4176
  .vela-statusline .vela-sl-avatar {
4089
4177
  width: 18px;
4090
4178
  height: 18px;
@@ -4136,59 +4224,13 @@ var CSS10 = `
4136
4224
  * stylesheet is document-global, so a bare attribute selector here would shift every
4137
4225
  * chart on the page, including statusline-less ones. */
4138
4226
  .vela-has-statusline [${LEGEND_AT_TOP_ATTR}] { transform: translateY(26px); }
4139
- /* Mobile: two-line chip \u2014 logo / symbol / meta / market status on one aligned row, the
4140
- * bar change on the next. Full O/H/L/C stays hidden (too dense on a phone-width plot).
4141
- * GRID, not a wrapping flexbox: an absolutely positioned wrapping flex container sizes
4142
- * to the one-line sum of ALL segments (max-content), so its background used to stretch
4143
- * far past the market badge; a grid hugs the widest actual row. The meta column may
4144
- * shrink (it carries the ellipsis), the others wrap their content. */
4145
- [data-layout='mobile'] .vela-statusline {
4146
- display: grid;
4147
- grid-template-columns: auto auto minmax(0, auto) auto;
4148
- justify-content: start;
4149
- align-items: center;
4150
- row-gap: 1px;
4151
- max-width: calc(100% - var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px) - 24px);
4152
- font-size: var(--vela-font-size-sm);
4153
- }
4154
- [data-layout='mobile'] .vela-statusline .vela-sl-symbol {
4155
- font-size: var(--vela-font-size-md);
4156
- line-height: 1;
4157
- }
4158
- [data-layout='mobile'] .vela-statusline .vela-sl-meta {
4159
- min-width: 0;
4160
- overflow: hidden;
4161
- text-overflow: ellipsis;
4162
- white-space: nowrap;
4163
- line-height: 1;
4164
- }
4165
- [data-layout='mobile'] .vela-statusline .vela-sl-avatar,
4166
- [data-layout='mobile'] .vela-statusline .vela-sl-market { align-self: center; }
4167
- [data-layout='mobile'] .vela-statusline .vela-sl-ohlc { display: none !important; }
4168
- [data-layout='mobile'] .vela-statusline .vela-sl-change {
4169
- /* Second row, under the text column \u2014 the avatar keeps the first column. */
4170
- grid-column: 2 / -1;
4171
- font-size: var(--vela-font-size-sm);
4172
- line-height: 1.2;
4173
- }
4174
- [data-layout='mobile'] .vela-has-statusline [${LEGEND_AT_TOP_ATTR}] { transform: translateY(40px); }
4175
- /* FIT mode (multi-chart cells \u2014 see setFitMode): the line never wraps; segments that
4176
- * don't fit are HIDDEN by fit() (change first, then meta, then the market badge), so
4177
- * overflow:hidden only guards the transient between a resize and the next measure.
4178
- * Placed after the mobile block on purpose: same specificity, later wins. */
4179
- .vela-statusline.vela-sl-fit {
4180
- display: flex; /* undo the mobile grid \u2014 fit mode is one flex row again */
4181
- flex-wrap: nowrap;
4182
- align-items: center;
4183
- max-width: calc(100% - var(--vela-toolbar-gutter, 0px) - var(--vela-scale-gutter, 0px) - 24px);
4184
- overflow: hidden;
4185
- }
4186
- .vela-statusline.vela-sl-fit .vela-sl-change {
4187
- flex-basis: auto;
4188
- padding-left: 0;
4189
- }
4190
- /* One row again \u2014 the mobile two-line shift doesn't apply in fit mode. */
4191
- [data-layout='mobile'] .vela-sl-fit-host.vela-has-statusline [${LEGEND_AT_TOP_ATTR}] { transform: translateY(26px); }
4227
+ /* A stacked chip is two lines tall \u2014 the legend shifts one more line. fit() flags the
4228
+ * host whenever the ladder lands on a stacked layout. */
4229
+ .vela-has-statusline.vela-sl-stacked-host [${LEGEND_AT_TOP_ATTR}] { transform: translateY(40px); }
4230
+ /* Mobile: the same ladder at the phone's type scale \u2014 the width alone decides how much
4231
+ * of the readout fits, exactly as on a narrow desktop chart. */
4232
+ [data-layout='mobile'] .vela-statusline { font-size: var(--vela-font-size-sm); }
4233
+ [data-layout='mobile'] .vela-statusline .vela-sl-symbol { font-size: var(--vela-font-size-md); }
4192
4234
  `;
4193
4235
  function baseOfTicker(ticker) {
4194
4236
  return ticker.replace(/[-_/]?(USDT|USDC|USD1|USDS|BUSD|USD|EUR|PERP)$/i, "") || ticker;
@@ -4259,6 +4301,8 @@ var Statusline = class {
4259
4301
  /** Fit mode (multi-chart cells): one row, overflowing segments hidden — see {@link setFitMode}. */
4260
4302
  this.fitMode = false;
4261
4303
  this.fitRO = null;
4304
+ /** The ladder rung currently applied — see {@link fit}. */
4305
+ this.layout = { stacked: false, level: "full" };
4262
4306
  this.onContextMenu = (e) => {
4263
4307
  if (!this.menu || !this.menuHooks) return;
4264
4308
  e.preventDefault();
@@ -4305,8 +4349,18 @@ var Statusline = class {
4305
4349
  this.menuHooks?.setChartVisible(true);
4306
4350
  this.setChartHidden(false);
4307
4351
  });
4308
- this.el.append(this.avatarEl, this.symbolEl, this.metaEl, this.marketEl, this.ohlcEl, this.changeEl, this.eyeEl);
4352
+ this.identityRow = doc.createElement("span");
4353
+ this.identityRow.className = "vela-sl-row vela-sl-identity";
4354
+ this.identityRow.append(this.avatarEl, this.symbolEl, this.metaEl, this.marketEl);
4355
+ this.valuesRow = doc.createElement("span");
4356
+ this.valuesRow.className = "vela-sl-row vela-sl-values";
4357
+ this.valuesRow.append(this.ohlcEl, this.changeEl, this.eyeEl);
4358
+ this.el.append(this.identityRow, this.valuesRow);
4309
4359
  host.appendChild(this.el);
4360
+ if (typeof ResizeObserver !== "undefined") {
4361
+ this.fitRO = new ResizeObserver(() => this.fit());
4362
+ this.fitRO.observe(host);
4363
+ }
4310
4364
  this.marketTip = new Tooltip(this.marketEl, { content: MARKET_LABELS.open, placement: "bottom" });
4311
4365
  this.eyeTip = new Tooltip(this.eyeEl, { content: "Show chart", placement: "bottom" });
4312
4366
  this.setMarketStatus("open");
@@ -4322,27 +4376,15 @@ var Statusline = class {
4322
4376
  this.fit();
4323
4377
  }
4324
4378
  /**
4325
- * Multi-chart cells: keep the line on ONE row whatever the cell width — never wrap.
4326
- * Segments that don't fit are hidden outright rather than clipped mid-glyph, least
4327
- * important first: OHLC, then the bar change, the venue/timeframe meta, and the
4328
- * market badge; the logo + ticker always stay. Re-fits live on host resizes.
4379
+ * Multi-chart cells: the same width ladder as a single chart, plus a last resort for
4380
+ * cells too narrow for even its bottom rung — whatever still overflows is hidden
4381
+ * outright rather than clipped mid-glyph, least important first: the values, the
4382
+ * venue/timeframe meta, then the market badge; the logo + ticker always stay.
4329
4383
  */
4330
4384
  setFitMode(on) {
4331
4385
  if (on === this.fitMode) return;
4332
4386
  this.fitMode = on;
4333
- this.el.classList.toggle("vela-sl-fit", on);
4334
- this.host.classList.toggle("vela-sl-fit-host", on);
4335
- if (on) {
4336
- if (typeof ResizeObserver !== "undefined" && !this.fitRO) {
4337
- this.fitRO = new ResizeObserver(() => this.fit());
4338
- this.fitRO.observe(this.host);
4339
- }
4340
- this.fit();
4341
- } else {
4342
- this.fitRO?.disconnect();
4343
- this.fitRO = null;
4344
- this.syncParts();
4345
- }
4387
+ this.fit();
4346
4388
  }
4347
4389
  /** Project the parts config onto the segments (the baseline fit() prunes from). */
4348
4390
  syncParts() {
@@ -4354,22 +4396,42 @@ var Statusline = class {
4354
4396
  this.ohlcEl.style.display = seg.ohlc ? "" : "none";
4355
4397
  this.changeEl.style.display = seg.change ? "" : "none";
4356
4398
  this.eyeEl.style.display = seg.eye ? "inline-flex" : "none";
4399
+ this.valuesRow.style.display = seg.ohlc || seg.change || seg.eye ? "" : "none";
4400
+ }
4401
+ /** Apply one ladder rung: the stacked/inline shape and the readout's level. */
4402
+ applyLayout(layout) {
4403
+ this.layout = layout;
4404
+ this.el.classList.toggle("vela-sl-stacked", layout.stacked);
4405
+ this.host.classList.toggle("vela-sl-stacked-host", layout.stacked);
4406
+ this.renderValues();
4357
4407
  }
4358
- /** Hide overflowing segments until the row fits its max-width (fit mode only). */
4408
+ /**
4409
+ * Walk the width ladder (see {@link STATUSLINE_LADDER}) until the chip fits its
4410
+ * max-width: the full one-line readout, then the values stacked under the symbol
4411
+ * line, then O/H/L shed, then the absolute change. In fit mode, when the last rung
4412
+ * still overflows, whole segments hide, least important first. Without layout
4413
+ * (detached host, node tests) nothing overflows, so the widest rung stays.
4414
+ */
4359
4415
  fit() {
4360
- if (!this.fitMode) return;
4361
4416
  this.syncParts();
4362
4417
  const seg = segmentVisibility(this.parts, this.chartHidden);
4418
+ const overflows = () => this.el.scrollWidth > this.el.clientWidth;
4419
+ const hasValues = seg.ohlc || seg.change;
4420
+ for (const rung of STATUSLINE_LADDER) {
4421
+ this.applyLayout(rung);
4422
+ if (!hasValues || !overflows()) break;
4423
+ }
4424
+ if (!this.fitMode) return;
4363
4425
  const order = [
4364
- [this.ohlcEl, seg.ohlc],
4365
- [this.changeEl, seg.change],
4426
+ [this.valuesRow, hasValues],
4366
4427
  [this.metaEl, seg.meta],
4367
4428
  [this.marketEl, seg.market]
4368
4429
  ];
4369
4430
  for (const [el, shown] of order) {
4370
- if (this.el.scrollWidth <= this.el.clientWidth) break;
4431
+ if (!overflows()) break;
4371
4432
  if (shown) el.style.display = "none";
4372
4433
  }
4434
+ if (this.valuesRow.style.display === "none") this.applyLayout({ ...this.layout, stacked: false });
4373
4435
  }
4374
4436
  /** Shape + color the value readout after the active price style: its own up/down
4375
4437
  * colors (candle bodies, bar ticks, the line color, …), the direction rule that
@@ -4482,7 +4544,7 @@ var Statusline = class {
4482
4544
  this.marketTip.destroy();
4483
4545
  this.eyeTip.destroy();
4484
4546
  this.marketBubble.destroy();
4485
- this.host.classList.remove("vela-has-statusline", "vela-sl-fit-host");
4547
+ this.host.classList.remove("vela-has-statusline", "vela-sl-stacked-host");
4486
4548
  this.el.remove();
4487
4549
  }
4488
4550
  detach() {
@@ -4491,6 +4553,10 @@ var Statusline = class {
4491
4553
  }
4492
4554
  render() {
4493
4555
  if (this.menuHooks) this.setChartHidden(!this.menuHooks.chartVisible());
4556
+ this.fit();
4557
+ }
4558
+ /** Write the value readout for the current bar at the current ladder level. */
4559
+ renderValues() {
4494
4560
  const bar = this.hoverBar ?? this.lastBar;
4495
4561
  if (!bar) {
4496
4562
  this.ohlcEl.replaceChildren();
@@ -4510,12 +4576,11 @@ var Statusline = class {
4510
4576
  s.appendChild(b);
4511
4577
  return s;
4512
4578
  };
4513
- if (this.readout === "value") this.ohlcEl.replaceChildren(cell("", bar.close));
4514
- else this.ohlcEl.replaceChildren(cell("O", bar.open), cell("H", bar.high), cell("L", bar.low), cell("C", bar.close));
4515
- this.changeEl.textContent = fmtChange(bar.open, bar.close);
4579
+ const { level } = this.layout;
4580
+ this.ohlcEl.replaceChildren(...readoutCells(level, this.readout).map((c) => cell(c.label, bar[c.key])));
4581
+ this.changeEl.textContent = level === "minimal" ? fmtChangePct(bar.open, bar.close) : fmtChange(bar.open, bar.close);
4516
4582
  this.changeEl.dataset.dir = up ? "up" : "down";
4517
4583
  this.changeEl.style.color = ink;
4518
- this.fit();
4519
4584
  }
4520
4585
  };
4521
4586
 
@@ -4826,9 +4891,6 @@ var ChartContextMenu = class {
4826
4891
  }
4827
4892
  };
4828
4893
 
4829
- // src/core/options.ts
4830
- var normalizeSession = (v) => v === "regular" || v === "extended" ? v : void 0;
4831
-
4832
4894
  // src/widget/session-shading.ts
4833
4895
  var DAY_MS = 864e5;
4834
4896
  var MINUTE_MS = 6e4;
@@ -8056,6 +8118,17 @@ function nextAutoCellId(taken) {
8056
8118
  if (!taken.has(`c${i}`)) return `c${i}`;
8057
8119
  }
8058
8120
  }
8121
+ function resolveTyping(key, open) {
8122
+ if (open.dialogs > 0) {
8123
+ if (key.length !== 1) return null;
8124
+ if (open.symbolSearch) return { target: "symbol", text: key.toUpperCase() };
8125
+ if (open.timeframeEntry) return { target: "timeframe", text: key };
8126
+ return null;
8127
+ }
8128
+ if (/^[a-zA-Z]$/.test(key)) return { target: "symbol", text: key.toUpperCase() };
8129
+ if (/^[0-9]$/.test(key)) return { target: "timeframe", text: key };
8130
+ return null;
8131
+ }
8059
8132
  var VelaWorkspace = class {
8060
8133
  constructor(container, opts = {}) {
8061
8134
  this.events = new TypedEventBus();
@@ -9689,17 +9762,14 @@ var VelaWorkspace = class {
9689
9762
  }
9690
9763
  /** Bare-typing router: letters → symbol search (seeded), digits → timeframe entry. */
9691
9764
  routeTyping(ev) {
9692
- if (this.destroyed || this.openDialogs > 0) return;
9765
+ if (this.destroyed) return;
9693
9766
  if (ev.ctrlKey || ev.metaKey || ev.altKey) return;
9694
9767
  if (isEditableTarget(ev)) return;
9695
- const key = ev.key;
9696
- if (/^[a-zA-Z]$/.test(key)) {
9697
- ev.preventDefault();
9698
- this.symbolPicker.open(key.toUpperCase());
9699
- } else if (/^[0-9]$/.test(key)) {
9700
- ev.preventDefault();
9701
- this.tfQuick.open(key);
9702
- }
9768
+ const hit = resolveTyping(ev.key, { dialogs: this.openDialogs, symbolSearch: this.symbolPicker.isOpen, timeframeEntry: this.tfQuick.isOpen });
9769
+ if (!hit) return;
9770
+ ev.preventDefault();
9771
+ if (hit.target === "symbol") this.symbolPicker.type(hit.text);
9772
+ else this.tfQuick.type(hit.text);
9703
9773
  }
9704
9774
  trackDialog(open) {
9705
9775
  this.openDialogs = Math.max(0, this.openDialogs + (open ? 1 : -1));
@@ -151,6 +151,7 @@ registerIcon("star-filled", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L
151
151
  registerIcon("grip", S('<circle cx="6" cy="3.5" r="1"/><circle cx="10" cy="3.5" r="1"/><circle cx="6" cy="8" r="1"/><circle cx="10" cy="8" r="1"/><circle cx="6" cy="12.5" r="1"/><circle cx="10" cy="12.5" r="1"/>', 'fill="currentColor" stroke="none"'));
152
152
  registerIcon("kebab", S('<circle cx="8" cy="3.2" r="1.2"/><circle cx="8" cy="8" r="1.2"/><circle cx="8" cy="12.8" r="1.2"/>', 'fill="currentColor" stroke="none"'));
153
153
  registerIcon("burger", S('<path d="M2.5 4.5h11M2.5 8h11M2.5 11.5h11"/>'));
154
+ registerIcon("info", S('<path d="M8 7.4v4.4"/><circle cx="8" cy="4.6" r="0.9" fill="currentColor" stroke="none"/>'));
154
155
  registerIcon("reset", S('<path d="M2 8a6 6 0 1 0 6-6 6.5 6.5 0 0 0-4.5 1.83L2 5.33"/><path d="M2 2v3.33h3.33"/>'));
155
156
  registerIcon("pane-collapse", S('<path d="M2.6 9.4h4v4M13.4 6.6h-4v-4"/><path d="m9.4 6.6 4.2-4.2M2.4 13.6l4.2-4.2"/>'));
156
157
  registerIcon("pane-expand", S('<path d="M9.8 2.4h3.8v3.8M6.2 13.6H2.4V9.8"/><path d="m13.6 2.4-4.4 4.4M2.4 13.6l4.4-4.4"/>'));
@@ -1,4 +1,4 @@
1
- import { svg24, NEUTRAL, BEARISH, WARNING, BULLISH, INFO, ACCENT_BRIGHT, VALID, INVALID, MARKER, ACCENT, injectStyles, iconEl } from './chunk-6DPAYEGO.js';
1
+ import { svg24, NEUTRAL, BEARISH, WARNING, BULLISH, INFO, ACCENT_BRIGHT, VALID, INVALID, MARKER, ACCENT, injectStyles, iconEl } from './chunk-CAFCLMPF.js';
2
2
 
3
3
  // src/core/native-indicators/NativeIndicator.ts
4
4
  var REGISTRY = /* @__PURE__ */ new Map();
@@ -1,4 +1,4 @@
1
- import { overlayScrollbarCss, injectStyles, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, iconAt, iconEl, BULLISH, BEARISH, WARNING, NEUTRAL } from './chunk-6DPAYEGO.js';
1
+ import { overlayScrollbarCss, injectStyles, isDarkColor, mix, HIGHLIGHT, ACCENT_BRIGHT, ACCENT, withAlpha, 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';
@@ -1,5 +1,5 @@
1
- import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-MGU3USTP.js';
2
- import { injectStyles } from './chunk-6DPAYEGO.js';
1
+ import { normalizeProps, nextUid, runMachine, spreadProps } from './chunk-T5Z5YUCF.js';
2
+ import { injectStyles } from './chunk-CAFCLMPF.js';
3
3
  import * as tooltip from '@zag-js/tooltip';
4
4
  import * as dialog from '@zag-js/dialog';
5
5
 
@@ -1,5 +1,5 @@
1
- import { u as Millis, ai as InputSchema, ag as IndicatorMeta, P as PriceStyle, O as OHLCV, I as InputValue, l as IndicatorModel, k as MoveTarget, au as PaneAxis, aH as SeriesSpec, ae as Fill, B as Background, az as PriceLine, a1 as DrawingLine, Z as DrawingBox, a0 as DrawingLabel, a5 as DrawingPolyline, a2 as DrawingLinefill, aa as DrawingTable, h as IndicatorStatus, a as VisibleRange, c as VelaTheme, S as SerializedDrawing, w as DrawingTypeKey, v as SnapMode, a3 as DrawingMode, e as IChartRenderer, R as RendererCapabilities, o as LegendActionView, p as LegendCalloutView, U as Unsubscribe, C as CrosshairEvent, A as AxisLongPressEvent, s as DataWindowReadout, n as SymbolPickerFn, aW as PaneInfo, ad as DrawingsOption, a7 as DrawingSeriesGateway, D as Drawing, b as VelaOptions, y as AddIndicatorOptions, at as MarketSwitch, as as MarketSnapshot, V as VisibleRangePreset, T as ThemeName } from './options-bdSAtp7y.cjs';
2
- import { S as SymbolInfo, B as BarRange, M as MarketDataFeed, D as DataProvider, P as ProviderInfo, a as SymbolDescriptor, b as ProviderCapabilities } from './DataProvider-xH2VBcFT.cjs';
1
+ import { u as Millis, aj as InputSchema, ah as IndicatorMeta, P as PriceStyle, O as OHLCV, I as InputValue, l as IndicatorModel, k as MoveTarget, av as PaneAxis, aI as SeriesSpec, af as Fill, B as Background, aA as PriceLine, a2 as DrawingLine, _ as DrawingBox, a1 as DrawingLabel, a6 as DrawingPolyline, a3 as DrawingLinefill, ab as DrawingTable, h as IndicatorStatus, a as VisibleRange, c as VelaTheme, S as SerializedDrawing, w as DrawingTypeKey, v as SnapMode, a4 as DrawingMode, e as IChartRenderer, R as RendererCapabilities, o as LegendActionView, p as LegendCalloutView, U as Unsubscribe, C as CrosshairEvent, A as AxisLongPressEvent, s as DataWindowReadout, n as SymbolPickerFn, aX as PaneInfo, ae as DrawingsOption, a8 as DrawingSeriesGateway, D as Drawing, b as VelaOptions, y as AddIndicatorOptions, au as MarketSwitch, at as MarketSnapshot, V as VisibleRangePreset, T as ThemeName } from './options-BCRmYALw.js';
2
+ import { S as SymbolInfo, B as BarRange, M as MarketDataFeed, D as DataProvider, P as ProviderInfo, a as SymbolDescriptor, b as ProviderCapabilities } from './DataProvider-Cb-2lC5O.js';
3
3
 
4
4
  /**
5
5
  * A strategy's broker state at ONE bar — the flat summary a host reads while a script
@@ -492,7 +492,8 @@ interface NativeIndicatorDescriptor {
492
492
  /** Full display name (picker, settings header, handle title). */
493
493
  readonly title: string;
494
494
  /**
495
- * Compact name for the on-chart legend chip and the settings-dialog header.
495
+ * Compact name for the on-chart legend chip, the settings-dialog header and the
496
+ * object tree's indicator rows.
496
497
  * Absent ⇒ {@link title}. The picker keeps the full title either way.
497
498
  */
498
499
  readonly shortTitle?: string;
@@ -734,9 +735,11 @@ interface VelaEventMap extends Record<string, unknown> {
734
735
  "drawing:edited": {
735
736
  id: string;
736
737
  };
737
- /** Selection changed (`id` is null when nothing is selected). */
738
+ /** Selection changed. `ids` is every selected drawing in selection order; `id` is the
739
+ * primary (`ids[0]`, the one a settings popup edits), null when nothing is selected. */
738
740
  "drawing:selected": {
739
741
  id: string | null;
742
+ ids: string[];
740
743
  };
741
744
  /** The favorite-tool set changed (star toggles or a bulk restore). */
742
745
  "drawing:favorites": {
@@ -1197,6 +1200,8 @@ declare class DrawingController {
1197
1200
  destroy(): void;
1198
1201
  /** Set/extend the selection. `additive` toggles membership (shift-click) vs replacing it. */
1199
1202
  private setSelection;
1203
+ /** Push the selection to the renderer and announce it (primary + the full list). */
1204
+ private announceSelection;
1200
1205
  /** Restore a history snapshot, reconciling selection against what survived. */
1201
1206
  private restoreSnapshot;
1202
1207
  /** Delete drawings as one undo step; prune them from the selection. */
@@ -1,5 +1,5 @@
1
- import { u as Millis, ai as InputSchema, ag as IndicatorMeta, P as PriceStyle, O as OHLCV, I as InputValue, l as IndicatorModel, k as MoveTarget, au as PaneAxis, aH as SeriesSpec, ae as Fill, B as Background, az as PriceLine, a1 as DrawingLine, Z as DrawingBox, a0 as DrawingLabel, a5 as DrawingPolyline, a2 as DrawingLinefill, aa as DrawingTable, h as IndicatorStatus, a as VisibleRange, c as VelaTheme, S as SerializedDrawing, w as DrawingTypeKey, v as SnapMode, a3 as DrawingMode, e as IChartRenderer, R as RendererCapabilities, o as LegendActionView, p as LegendCalloutView, U as Unsubscribe, C as CrosshairEvent, A as AxisLongPressEvent, s as DataWindowReadout, n as SymbolPickerFn, aW as PaneInfo, ad as DrawingsOption, a7 as DrawingSeriesGateway, D as Drawing, b as VelaOptions, y as AddIndicatorOptions, at as MarketSwitch, as as MarketSnapshot, V as VisibleRangePreset, T as ThemeName } from './options-bdSAtp7y.js';
2
- import { S as SymbolInfo, B as BarRange, M as MarketDataFeed, D as DataProvider, P as ProviderInfo, a as SymbolDescriptor, b as ProviderCapabilities } from './DataProvider-XLdJB86x.js';
1
+ import { u as Millis, aj as InputSchema, ah as IndicatorMeta, P as PriceStyle, O as OHLCV, I as InputValue, l as IndicatorModel, k as MoveTarget, av as PaneAxis, aI as SeriesSpec, af as Fill, B as Background, aA as PriceLine, a2 as DrawingLine, _ as DrawingBox, a1 as DrawingLabel, a6 as DrawingPolyline, a3 as DrawingLinefill, ab as DrawingTable, h as IndicatorStatus, a as VisibleRange, c as VelaTheme, S as SerializedDrawing, w as DrawingTypeKey, v as SnapMode, a4 as DrawingMode, e as IChartRenderer, R as RendererCapabilities, o as LegendActionView, p as LegendCalloutView, U as Unsubscribe, C as CrosshairEvent, A as AxisLongPressEvent, s as DataWindowReadout, n as SymbolPickerFn, aX as PaneInfo, ae as DrawingsOption, a8 as DrawingSeriesGateway, D as Drawing, b as VelaOptions, y as AddIndicatorOptions, au as MarketSwitch, at as MarketSnapshot, V as VisibleRangePreset, T as ThemeName } from './options-BCRmYALw.cjs';
2
+ import { S as SymbolInfo, B as BarRange, M as MarketDataFeed, D as DataProvider, P as ProviderInfo, a as SymbolDescriptor, b as ProviderCapabilities } from './DataProvider-UtWw2CgJ.cjs';
3
3
 
4
4
  /**
5
5
  * A strategy's broker state at ONE bar — the flat summary a host reads while a script
@@ -492,7 +492,8 @@ interface NativeIndicatorDescriptor {
492
492
  /** Full display name (picker, settings header, handle title). */
493
493
  readonly title: string;
494
494
  /**
495
- * Compact name for the on-chart legend chip and the settings-dialog header.
495
+ * Compact name for the on-chart legend chip, the settings-dialog header and the
496
+ * object tree's indicator rows.
496
497
  * Absent ⇒ {@link title}. The picker keeps the full title either way.
497
498
  */
498
499
  readonly shortTitle?: string;
@@ -734,9 +735,11 @@ interface VelaEventMap extends Record<string, unknown> {
734
735
  "drawing:edited": {
735
736
  id: string;
736
737
  };
737
- /** Selection changed (`id` is null when nothing is selected). */
738
+ /** Selection changed. `ids` is every selected drawing in selection order; `id` is the
739
+ * primary (`ids[0]`, the one a settings popup edits), null when nothing is selected. */
738
740
  "drawing:selected": {
739
741
  id: string | null;
742
+ ids: string[];
740
743
  };
741
744
  /** The favorite-tool set changed (star toggles or a bulk restore). */
742
745
  "drawing:favorites": {
@@ -1197,6 +1200,8 @@ declare class DrawingController {
1197
1200
  destroy(): void;
1198
1201
  /** Set/extend the selection. `additive` toggles membership (shift-click) vs replacing it. */
1199
1202
  private setSelection;
1203
+ /** Push the selection to the renderer and announce it (primary + the full list). */
1204
+ private announceSelection;
1200
1205
  /** Restore a history snapshot, reconciling selection against what survived. */
1201
1206
  private restoreSnapshot;
1202
1207
  /** Delete drawings as one undo step; prune them from the selection. */