@luxalgo/vela 0.5.0 → 0.5.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.
Files changed (45) hide show
  1. package/dist/{DataProvider-DKNDHpNv.d.cts → DataProvider-BBQ3Hr21.d.cts} +1 -1
  2. package/dist/{DataProvider-Dzd-Erlk.d.ts → DataProvider-DGesjOJT.d.ts} +1 -1
  3. package/dist/{chunk-OVQKKXLZ.js → chunk-5SE4PL3H.js} +402 -60
  4. package/dist/{chunk-RHIDOUFL.js → chunk-AIW3EJUE.js} +2 -2
  5. package/dist/{chunk-GYD2THPV.js → chunk-EP6NDJYK.js} +5 -0
  6. package/dist/{chunk-KCCZNKH7.js → chunk-FNOMKHEZ.js} +668 -78
  7. package/dist/{chunk-7UFX5ZIG.js → chunk-S6F6SQ2L.js} +7 -2
  8. package/dist/{chunk-KM6LHB3Y.js → chunk-ULIX6AE2.js} +6 -1
  9. package/dist/{contributions-hX3EUyjG.d.ts → contributions-6N0rbrPC.d.ts} +46 -31
  10. package/dist/{contributions-o1GRKPI_.d.cts → contributions-BlV3kb0O.d.cts} +46 -31
  11. package/dist/{history-Dzxz-MQj.d.ts → history-BTd52YMA.d.ts} +10 -5
  12. package/dist/{history-LJkz4-sS.d.cts → history-DX3Zne3a.d.cts} +10 -5
  13. package/dist/index.cjs +680 -75
  14. package/dist/index.d.cts +37 -12
  15. package/dist/index.d.ts +37 -12
  16. package/dist/index.js +4 -4
  17. package/dist/{options-Q-576hIi.d.cts → options-DbQholFV.d.cts} +14 -0
  18. package/dist/{options-Q-576hIi.d.ts → options-DbQholFV.d.ts} +14 -0
  19. package/dist/{plugin-D94muTV-.d.cts → plugin-O34MbXf-.d.cts} +157 -7
  20. package/dist/{plugin-aGUD1epn.d.ts → plugin-TN3n-9YN.d.ts} +157 -7
  21. package/dist/plugin.cjs +5 -0
  22. package/dist/plugin.d.cts +4 -4
  23. package/dist/plugin.d.ts +4 -4
  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/ui.cjs +10 -0
  32. package/dist/ui.d.cts +1 -1
  33. package/dist/ui.d.ts +1 -1
  34. package/dist/ui.js +3 -3
  35. package/dist/vela.global.js +680 -75
  36. package/dist/vela.global.min.js +59 -28
  37. package/dist/widget.cjs +1081 -134
  38. package/dist/widget.d.cts +30 -16
  39. package/dist/widget.d.ts +30 -16
  40. package/dist/widget.js +9 -9
  41. package/dist/workspace.cjs +1372 -183
  42. package/dist/workspace.d.cts +66 -8
  43. package/dist/workspace.d.ts +66 -8
  44. package/dist/workspace.js +275 -37
  45. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-Q-576hIi.cjs';
1
+ import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-DbQholFV.cjs';
2
2
 
3
3
  /**
4
4
  * Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
@@ -1,4 +1,4 @@
1
- import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-Q-576hIi.js';
1
+ import { M as MarketConfig, O as OHLCV, U as Unsubscribe } from './options-DbQholFV.js';
2
2
 
3
3
  /**
4
4
  * Symbol metadata an engine may need (e.g. Pine `syminfo.*`). Free-form beyond
@@ -1,7 +1,7 @@
1
- import { parseSymbol, priceStyleIds, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-KCCZNKH7.js';
2
- import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-7UFX5ZIG.js';
3
- import { Tooltip, Menu, Dialog } from './chunk-RHIDOUFL.js';
4
- import { injectStyles, iconEl, iconMarkup, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, registerIcon, categoricalColor } from './chunk-GYD2THPV.js';
1
+ import { parseSymbol, priceStyleIds, tzButtonLabel, TIMEZONES, normalizeTimezone, tzMenuLabel } from './chunk-FNOMKHEZ.js';
2
+ import { chartType, widgetActions, SidePanel, sidePanels, DEFAULT_PANEL_ORDER, getDrawingType } from './chunk-S6F6SQ2L.js';
3
+ import { Tooltip, Menu, Dialog } from './chunk-AIW3EJUE.js';
4
+ import { injectStyles, iconEl, iconMarkup, SESSION_PRE, SESSION_POST, SESSION_OFF, icon, registerIcon, categoricalColor } from './chunk-EP6NDJYK.js';
5
5
 
6
6
  // src/widget/timeframe.ts
7
7
  var UNIT_MS = {
@@ -87,9 +87,329 @@ function timeframeLabel(value) {
87
87
  return parsed.short;
88
88
  }
89
89
 
90
- // src/widget/topbar.ts
91
- var STYLE_ID = "vela-topbar";
90
+ // src/widget/layout-picker.ts
91
+ var STYLE_ID = "vela-widget-layout-picker-v14";
92
92
  var CSS = `
93
+ .vela-lp-layer { position: absolute; z-index: var(--vela-z-menu); }
94
+ .vela-lp {
95
+ background: var(--vela-surface-elev);
96
+ color: var(--vela-fg);
97
+ border: 1px solid var(--vela-border-strong);
98
+ border-radius: 8px;
99
+ box-shadow: var(--vela-shadow);
100
+ padding: 10px 12px 10px;
101
+ font-size: 13px;
102
+ user-select: none;
103
+ transform-origin: top;
104
+ animation: vela-lp-in var(--vela-dur-fast) var(--vela-ease);
105
+ }
106
+ @keyframes vela-lp-in {
107
+ from { opacity: 0; transform: translateY(-4px) scale(0.98); }
108
+ to { opacity: 1; transform: translateY(0) scale(1); }
109
+ }
110
+ .vela-lp-cols { display: flex; align-items: stretch; gap: 14px; }
111
+ .vela-lp-heading {
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 8px;
115
+ font-size: 10px;
116
+ font-weight: 600;
117
+ letter-spacing: 1.2px;
118
+ text-transform: uppercase;
119
+ color: var(--vela-fg-faint);
120
+ margin-bottom: 10px;
121
+ }
122
+ /* "?" help badge: hover it for the canvas explainer. */
123
+ .vela-lp-badge {
124
+ display: inline-flex;
125
+ align-items: center;
126
+ justify-content: center;
127
+ width: 14px;
128
+ height: 14px;
129
+ border-radius: 50%;
130
+ border: 1px solid var(--vela-border-strong);
131
+ color: var(--vela-fg-muted);
132
+ font-size: 9px;
133
+ font-weight: 600;
134
+ letter-spacing: 0;
135
+ cursor: default;
136
+ transition: color var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease);
137
+ }
138
+ .vela-lp-badge:hover { color: var(--vela-fg-bright); border-color: var(--vela-fg-muted); }
139
+ .vela-lp-tip { display: flex; flex-direction: column; gap: 4px; max-width: 230px; white-space: normal; }
140
+ .vela-lp-vsep { width: 1px; flex: none; align-self: stretch; background: var(--vela-border-faint); }
141
+ .vela-lp-layout { width: 132px; display: flex; flex-direction: column; align-items: center; }
142
+ .vela-lp-layout > .vela-lp-heading,
143
+ .vela-lp-layout > .vela-lp-presets { align-self: stretch; }
144
+ .vela-lp-grid { display: grid; grid-template-columns: repeat(4, 24px); grid-auto-rows: 24px; gap: 4px; }
145
+ .vela-lp-sq {
146
+ all: unset;
147
+ box-sizing: border-box;
148
+ border-radius: 4px;
149
+ background: var(--vela-hover);
150
+ border: 1px solid var(--vela-border-faint);
151
+ cursor: pointer;
152
+ transition: background var(--vela-dur-fast) var(--vela-ease), border-color var(--vela-dur-fast) var(--vela-ease);
153
+ }
154
+ .vela-lp-sq:hover { background: var(--vela-hover-strong); border-color: var(--vela-border-strong); }
155
+ /* Selected/previewed squares \u2014 the shared monochrome selection chip. */
156
+ .vela-lp-sq[data-on='1'] {
157
+ background: var(--vela-selected-bg);
158
+ border-color: var(--vela-selected-bg);
159
+ }
160
+ .vela-lp-presets { display: flex; flex-direction: column; gap: 2px; margin-top: 8px; }
161
+ .vela-lp-preset { all: unset; padding: 5px 8px; border-radius: 4px; cursor: pointer; color: var(--vela-fg-muted); font-size: 12px; white-space: nowrap; transition: transform 120ms var(--vela-ease); }
162
+ .vela-lp-preset:hover { background: var(--vela-hover); }
163
+ .vela-lp-preset:active { transform: scale(0.98); }
164
+ .vela-lp-preset[data-checked='1'] { background: var(--vela-hover-strong); color: var(--vela-fg-bright); }
165
+ .vela-lp-sync { display: flex; flex-direction: column; gap: 4px; min-width: 118px; }
166
+ .vela-lp-sync-row { all: unset; display: flex; align-items: center; gap: 14px; padding: 6px 8px; border-radius: 5px; cursor: pointer; }
167
+ .vela-lp-sync-row:hover { background: var(--vela-hover); }
168
+ .vela-lp-sync-row .vela-lp-label { flex: 1 1 auto; }
169
+ /* Toggle pill \u2014 same control language as the menu's switch rows, monochrome ON state. */
170
+ .vela-lp-switch {
171
+ position: relative;
172
+ flex: none;
173
+ width: 34px;
174
+ height: 18px;
175
+ border-radius: 9px;
176
+ background: var(--vela-hover);
177
+ border: 1px solid var(--vela-border-soft);
178
+ transition: background 0.16s ease, border-color 0.16s ease;
179
+ }
180
+ .vela-lp-switch::after {
181
+ content: '';
182
+ position: absolute;
183
+ top: 2px;
184
+ left: 2px;
185
+ width: 12px;
186
+ height: 12px;
187
+ border-radius: 50%;
188
+ background: var(--vela-fg-muted);
189
+ transition: transform 0.16s ease, background 0.16s ease;
190
+ }
191
+ .vela-lp-switch.on { background: var(--vela-selected-bg); border-color: var(--vela-selected-bg); }
192
+ .vela-lp-switch.on::after { transform: translateX(16px); background: var(--vela-selected-fg); }
193
+ `;
194
+ var GRID = 4;
195
+ var LayoutPicker = class {
196
+ constructor(opts) {
197
+ this.squares = [];
198
+ this.isOpen = false;
199
+ /** Hover preview (1-based rows/cols), null = show current shape. */
200
+ this.hover = null;
201
+ this.onDocPointerDown = (e) => {
202
+ const t = e.target;
203
+ if (t && (this.layer.contains(t) || this.opts.trigger.contains(t))) return;
204
+ this.close();
205
+ };
206
+ this.onDocKeydown = (e) => {
207
+ if (e.key === "Escape") this.close();
208
+ };
209
+ this.opts = opts;
210
+ this.doc = opts.host.ownerDocument;
211
+ injectStyles(STYLE_ID, CSS, this.doc);
212
+ const doc = this.doc;
213
+ this.layer = doc.createElement("div");
214
+ this.layer.className = "vela-ui-layer vela-lp-layer";
215
+ this.layer.style.display = "none";
216
+ const panel = doc.createElement("div");
217
+ panel.className = "vela-lp";
218
+ this.layer.appendChild(panel);
219
+ const cols = doc.createElement("div");
220
+ cols.className = "vela-lp-cols";
221
+ panel.appendChild(cols);
222
+ const layoutCol = doc.createElement("div");
223
+ layoutCol.className = "vela-lp-layout";
224
+ const layoutHeading = doc.createElement("div");
225
+ layoutHeading.className = "vela-lp-heading";
226
+ const headingText = doc.createElement("span");
227
+ headingText.textContent = "Layout";
228
+ const badge = doc.createElement("span");
229
+ badge.className = "vela-lp-badge";
230
+ badge.textContent = "?";
231
+ layoutHeading.append(headingText, badge);
232
+ layoutCol.appendChild(layoutHeading);
233
+ this.infoTip = new Tooltip(badge, {
234
+ host: opts.host,
235
+ placement: "bottom",
236
+ content: () => this.tipNode()
237
+ });
238
+ const grid = doc.createElement("div");
239
+ grid.className = "vela-lp-grid";
240
+ for (let r = 0; r < GRID; r += 1) {
241
+ for (let c = 0; c < GRID; c += 1) {
242
+ const sq = doc.createElement("button");
243
+ sq.className = "vela-lp-sq";
244
+ sq.dataset.r = String(r);
245
+ sq.dataset.c = String(c);
246
+ sq.setAttribute("aria-label", `${c + 1} \xD7 ${r + 1}`);
247
+ this.squares.push(sq);
248
+ grid.appendChild(sq);
249
+ }
250
+ }
251
+ layoutCol.appendChild(grid);
252
+ this.presetsEl = doc.createElement("div");
253
+ this.presetsEl.className = "vela-lp-presets";
254
+ layoutCol.appendChild(this.presetsEl);
255
+ cols.appendChild(layoutCol);
256
+ const vsep = doc.createElement("div");
257
+ vsep.className = "vela-lp-vsep";
258
+ cols.appendChild(vsep);
259
+ const syncCol = doc.createElement("div");
260
+ const syncHeading = doc.createElement("div");
261
+ syncHeading.className = "vela-lp-heading";
262
+ syncHeading.textContent = "Sync";
263
+ syncCol.appendChild(syncHeading);
264
+ this.syncEl = doc.createElement("div");
265
+ this.syncEl.className = "vela-lp-sync";
266
+ syncCol.appendChild(this.syncEl);
267
+ cols.appendChild(syncCol);
268
+ grid.addEventListener("pointerdown", (e) => {
269
+ const sq = this.squareAt(e);
270
+ if (!sq) return;
271
+ e.preventDefault();
272
+ const { r, c } = this.squarePos(sq);
273
+ this.close();
274
+ this.opts.onSelectGrid(r + 1, c + 1);
275
+ });
276
+ grid.addEventListener("pointermove", (e) => {
277
+ const sq = this.squareAt(e);
278
+ if (!sq) return;
279
+ const { r, c } = this.squarePos(sq);
280
+ if (this.hover?.rows !== r + 1 || this.hover?.cols !== c + 1) {
281
+ this.hover = { rows: r + 1, cols: c + 1 };
282
+ this.render();
283
+ }
284
+ });
285
+ grid.addEventListener("pointerleave", () => {
286
+ if (this.hover) {
287
+ this.hover = null;
288
+ this.render();
289
+ }
290
+ });
291
+ opts.trigger.addEventListener("click", () => this.toggle());
292
+ opts.trigger.setAttribute("aria-haspopup", "true");
293
+ opts.trigger.setAttribute("aria-expanded", "false");
294
+ opts.host.appendChild(this.layer);
295
+ }
296
+ toggle() {
297
+ if (this.isOpen) this.close();
298
+ else this.open();
299
+ }
300
+ open() {
301
+ if (this.isOpen) return;
302
+ this.isOpen = true;
303
+ this.hover = null;
304
+ this.refresh();
305
+ this.layer.style.display = "";
306
+ this.position();
307
+ this.opts.trigger.setAttribute("aria-expanded", "true");
308
+ this.doc.addEventListener("pointerdown", this.onDocPointerDown, true);
309
+ this.doc.addEventListener("keydown", this.onDocKeydown, true);
310
+ this.opts.onOpenChange?.(true);
311
+ }
312
+ close() {
313
+ if (!this.isOpen) return;
314
+ this.isOpen = false;
315
+ this.layer.style.display = "none";
316
+ this.opts.trigger.setAttribute("aria-expanded", "false");
317
+ this.doc.removeEventListener("pointerdown", this.onDocPointerDown, true);
318
+ this.doc.removeEventListener("keydown", this.onDocKeydown, true);
319
+ this.opts.onOpenChange?.(false);
320
+ }
321
+ /** Re-read shape/presets/syncs and re-render (no-op while closed — `open` re-reads). */
322
+ refresh() {
323
+ if (!this.isOpen) return;
324
+ this.renderPresets();
325
+ this.renderSyncs();
326
+ this.render();
327
+ }
328
+ destroy() {
329
+ this.close();
330
+ this.infoTip.destroy();
331
+ this.layer.remove();
332
+ }
333
+ // ── internals ──
334
+ squareAt(e) {
335
+ const sq = e.target?.closest?.(".vela-lp-sq");
336
+ return sq instanceof HTMLButtonElement ? sq : null;
337
+ }
338
+ squarePos(sq) {
339
+ return { r: Number(sq.dataset.r), c: Number(sq.dataset.c) };
340
+ }
341
+ /** Help copy for the LAYOUT "?" badge. */
342
+ tipNode() {
343
+ const tip = this.doc.createElement("div");
344
+ tip.className = "vela-lp-tip";
345
+ tip.textContent = "Click a square to apply that columns \xD7 rows layout.";
346
+ return tip;
347
+ }
348
+ /** Project the interaction state onto the DOM (the hover/current rectangle). */
349
+ render() {
350
+ const preview = this.hover ?? this.opts.shape();
351
+ for (const sq of this.squares) {
352
+ const { r, c } = this.squarePos(sq);
353
+ const on = preview !== null && r < preview.rows && c < preview.cols;
354
+ if (on) sq.dataset.on = "1";
355
+ else delete sq.dataset.on;
356
+ }
357
+ }
358
+ renderPresets() {
359
+ const doc = this.doc;
360
+ this.presetsEl.replaceChildren();
361
+ const presets = this.opts.presets();
362
+ this.presetsEl.style.display = presets.length > 0 ? "" : "none";
363
+ for (const p of presets) {
364
+ const b = doc.createElement("button");
365
+ b.className = "vela-lp-preset";
366
+ b.textContent = p.label;
367
+ if (p.checked) b.dataset.checked = "1";
368
+ b.addEventListener("click", () => {
369
+ this.close();
370
+ this.opts.onSelectPreset(p.id);
371
+ });
372
+ this.presetsEl.appendChild(b);
373
+ }
374
+ }
375
+ renderSyncs() {
376
+ const doc = this.doc;
377
+ this.syncEl.replaceChildren();
378
+ for (const s of this.opts.syncs()) {
379
+ const row = doc.createElement("button");
380
+ row.className = "vela-lp-sync-row";
381
+ const label = doc.createElement("span");
382
+ label.className = "vela-lp-label";
383
+ label.textContent = s.label;
384
+ const pill = doc.createElement("span");
385
+ pill.className = "vela-lp-switch" + (s.checked ? " on" : "");
386
+ pill.setAttribute("aria-hidden", "true");
387
+ row.setAttribute("role", "switch");
388
+ row.setAttribute("aria-checked", String(s.checked));
389
+ row.append(label, pill);
390
+ row.addEventListener("click", () => {
391
+ this.opts.onToggleSync(s.id);
392
+ this.renderSyncs();
393
+ });
394
+ this.syncEl.appendChild(row);
395
+ }
396
+ }
397
+ /** Anchor under the trigger, clamped to the host's right edge. */
398
+ position() {
399
+ const hostRect = this.opts.host.getBoundingClientRect();
400
+ const trigRect = this.opts.trigger.getBoundingClientRect();
401
+ let left = trigRect.left - hostRect.left;
402
+ const top = trigRect.bottom - hostRect.top + 4;
403
+ const width = this.layer.offsetWidth;
404
+ if (left + width > hostRect.width - 8) left = Math.max(8, hostRect.width - 8 - width);
405
+ this.layer.style.left = `${left}px`;
406
+ this.layer.style.top = `${top}px`;
407
+ }
408
+ };
409
+
410
+ // src/widget/topbar.ts
411
+ var STYLE_ID2 = "vela-topbar";
412
+ var CSS2 = `
93
413
  .vela-widget-topbar {
94
414
  display: flex;
95
415
  align-items: center;
@@ -201,7 +521,7 @@ function priceStyleIcon(id) {
201
521
  var Topbar = class {
202
522
  constructor(host, opts) {
203
523
  this.layoutButton = null;
204
- this.layoutMenu = null;
524
+ this.layoutPicker = null;
205
525
  this.layoutId = null;
206
526
  this.tooltips = [];
207
527
  this.panelBtns = /* @__PURE__ */ new Map();
@@ -221,7 +541,7 @@ var Topbar = class {
221
541
  this.timeframe = opts.timeframe;
222
542
  this.priceStyle = opts.priceStyle;
223
543
  const doc = host.ownerDocument;
224
- injectStyles(STYLE_ID, CSS, doc);
544
+ injectStyles(STYLE_ID2, CSS2, doc);
225
545
  this.el = doc.createElement("div");
226
546
  this.el.className = "vela-widget-topbar";
227
547
  this.symbolEl = doc.createElement("button");
@@ -278,19 +598,16 @@ var Topbar = class {
278
598
  this.tooltips.push(new Tooltip(this.styleButton, { content: "Chart style", triggerId: "vela-topbar-style", host }));
279
599
  if (this.layoutButton && opts.layout) {
280
600
  this.tooltips.push(new Tooltip(this.layoutButton, { content: "Layout", triggerId: "vela-topbar-layout", host }));
281
- this.layoutMenu = new Menu({
601
+ const layout = opts.layout;
602
+ this.layoutPicker = new LayoutPicker({
282
603
  trigger: this.layoutButton,
283
- triggerId: "vela-topbar-layout",
284
604
  host,
285
- items: this.layoutItems(),
286
- onSelect: (id) => {
287
- if (id.startsWith("toggle:")) {
288
- opts.layout.onToggle?.(id.slice("toggle:".length));
289
- this.layoutMenu?.setItems(this.layoutItems());
290
- return;
291
- }
292
- opts.layout.onSelect(id);
293
- }
605
+ shape: () => layout.shape(),
606
+ presets: () => layout.presets().map((p) => ({ ...p, checked: p.id === this.layoutId })),
607
+ onSelectGrid: (rows, cols) => layout.onSelectGrid(rows, cols),
608
+ onSelectPreset: (id) => layout.onSelectPreset(id),
609
+ syncs: () => layout.syncs(),
610
+ onToggleSync: (id) => layout.onToggleSync(id)
294
611
  });
295
612
  }
296
613
  this.tfMenu = new Menu({
@@ -329,7 +646,7 @@ var Topbar = class {
329
646
  if (!this.layoutButton) return;
330
647
  this.layoutId = id;
331
648
  this.renderLayoutButton(this.layoutButton.ownerDocument);
332
- this.layoutMenu?.setItems(this.layoutItems());
649
+ this.layoutPicker?.refresh();
333
650
  }
334
651
  renderLayoutButton(doc) {
335
652
  if (!this.layoutButton) return;
@@ -338,11 +655,6 @@ var Topbar = class {
338
655
  else this.layoutButton.appendChild(doc.createTextNode(this.layoutId ?? ""));
339
656
  this.layoutButton.setAttribute("aria-label", `Layout \u2014 ${this.layoutId ?? ""}`);
340
657
  }
341
- layoutItems() {
342
- const presets = (this.opts.layout?.options() ?? []).map((l) => ({ id: l.id, label: l.label, checked: l.id === this.layoutId }));
343
- const toggles = (this.opts.layout?.toggles?.() ?? []).map((t, i) => ({ id: `toggle:${t.id}`, label: t.label, checked: t.checked, toggle: true, separatorBefore: i === 0 }));
344
- return [...presets, ...toggles];
345
- }
346
658
  renderStyleButton(doc) {
347
659
  this.styleButton.replaceChildren();
348
660
  const icon2 = priceStyleIcon(this.priceStyle);
@@ -407,7 +719,7 @@ var Topbar = class {
407
719
  if (this.hairlineRaf) win?.cancelAnimationFrame(this.hairlineRaf);
408
720
  this.tfMenu.destroy();
409
721
  this.styleMenu.destroy();
410
- this.layoutMenu?.destroy();
722
+ this.layoutPicker?.destroy();
411
723
  for (const t of [...this.tooltips, ...this.panelTooltips]) t.destroy();
412
724
  this.el.remove();
413
725
  }
@@ -509,8 +821,8 @@ function tickerIconEl(doc, base, name, className) {
509
821
  }
510
822
 
511
823
  // src/widget/statusline.ts
512
- var STYLE_ID2 = "vela-widget-statusline";
513
- var CSS2 = `
824
+ var STYLE_ID3 = "vela-widget-statusline";
825
+ var CSS3 = `
514
826
  .vela-statusline {
515
827
  position: absolute;
516
828
  top: var(--vela-space-2);
@@ -645,7 +957,7 @@ var Statusline = class {
645
957
  /** 'ohlc' for bar-shaped styles; 'value' (the single plotted close) for line styles. */
646
958
  this.readout = "ohlc";
647
959
  const doc = host.ownerDocument;
648
- injectStyles(STYLE_ID2, CSS2, doc);
960
+ injectStyles(STYLE_ID3, CSS3, doc);
649
961
  host.classList.add("vela-has-statusline");
650
962
  this.el = doc.createElement("div");
651
963
  this.el.className = "vela-statusline";
@@ -768,11 +1080,21 @@ var Statusline = class {
768
1080
  };
769
1081
 
770
1082
  // src/widget/watermark.ts
771
- var STYLE_ID3 = "vela-widget-watermark";
772
- var CSS3 = `
1083
+ var MAX_FONT_PX = 72;
1084
+ var MIN_FONT_PX = 12;
1085
+ var FILL = 0.9;
1086
+ var STYLE_ID4 = "vela-widget-watermark";
1087
+ var CSS4 = `
773
1088
  .vela-watermark {
774
1089
  position: absolute;
775
- inset: 0;
1090
+ /* Insets follow the renderer-published gutters (mount container), so the mark
1091
+ * centers and fits within the PLOT \u2014 never bleeding into the drawings toolbar
1092
+ * on the left or the price scale on the right (visible in small multi-chart
1093
+ * cells, where the scale is a large share of the width). */
1094
+ top: 0;
1095
+ bottom: 0;
1096
+ left: var(--vela-toolbar-gutter, 0px);
1097
+ right: var(--vela-scale-gutter, 0px);
776
1098
  display: flex;
777
1099
  align-items: center;
778
1100
  justify-content: center;
@@ -780,28 +1102,48 @@ var CSS3 = `
780
1102
  z-index: 1;
781
1103
  color: var(--vela-fg);
782
1104
  opacity: 0.05;
783
- font-size: clamp(28px, 7vw, 72px);
1105
+ font-size: ${MAX_FONT_PX}px;
784
1106
  font-weight: 700;
785
1107
  letter-spacing: 0.04em;
786
1108
  user-select: none;
787
1109
  white-space: nowrap;
788
1110
  }
789
1111
  `;
1112
+ function watermarkFontPx(availPx, textPxAtMax) {
1113
+ if (textPxAtMax <= 0) return MAX_FONT_PX;
1114
+ return Math.max(MIN_FONT_PX, Math.min(MAX_FONT_PX, Math.floor(MAX_FONT_PX * availPx * FILL / textPxAtMax)));
1115
+ }
790
1116
  var Watermark = class {
791
1117
  constructor(host, symbol, timeframe) {
792
- injectStyles(STYLE_ID3, CSS3, host.ownerDocument);
1118
+ this.resizeObserver = null;
1119
+ injectStyles(STYLE_ID4, CSS4, host.ownerDocument);
793
1120
  this.el = host.ownerDocument.createElement("div");
794
1121
  this.el.className = "vela-watermark";
1122
+ this.text = host.ownerDocument.createElement("span");
1123
+ this.el.appendChild(this.text);
795
1124
  host.appendChild(this.el);
1125
+ if (typeof ResizeObserver !== "undefined") {
1126
+ this.resizeObserver = new ResizeObserver(() => this.fit());
1127
+ this.resizeObserver.observe(this.el);
1128
+ }
796
1129
  this.update(symbol, timeframe);
797
1130
  }
798
1131
  setVisible(visible) {
799
1132
  this.el.style.display = visible ? "" : "none";
800
1133
  }
801
1134
  update(symbol, timeframe) {
802
- this.el.textContent = symbol ? `${parseSymbol(symbol).ticker} \xB7 ${timeframeLabel(timeframe)}` : "";
1135
+ this.text.textContent = symbol ? `${parseSymbol(symbol).ticker} \xB7 ${timeframeLabel(timeframe)}` : "";
1136
+ this.fit();
1137
+ }
1138
+ /** Measure the text at the cap and shrink it to the chart's own width. */
1139
+ fit() {
1140
+ if (this.el.clientWidth <= 0 || !this.text.textContent) return;
1141
+ this.el.style.fontSize = `${MAX_FONT_PX}px`;
1142
+ const px = watermarkFontPx(this.el.clientWidth, this.text.getBoundingClientRect().width);
1143
+ if (px !== MAX_FONT_PX) this.el.style.fontSize = `${px}px`;
803
1144
  }
804
1145
  destroy() {
1146
+ this.resizeObserver?.disconnect();
805
1147
  this.el.remove();
806
1148
  }
807
1149
  };
@@ -827,8 +1169,8 @@ var RANGE_PRESETS = [
827
1169
  { id: "ALL", tf: "W", preset: "ALL", bars: 5e3 }
828
1170
  // everything the provider serves
829
1171
  ];
830
- var STYLE_ID4 = "vela-widget-bottombar";
831
- var CSS4 = `
1172
+ var STYLE_ID5 = "vela-widget-bottombar";
1173
+ var CSS5 = `
832
1174
  .vela-widget-bottombar {
833
1175
  display: flex;
834
1176
  align-items: center;
@@ -903,7 +1245,7 @@ var Bottombar = class {
903
1245
  this.timer = null;
904
1246
  this.timezone = opts.timezone;
905
1247
  const doc = host.ownerDocument;
906
- injectStyles(STYLE_ID4, CSS4, doc);
1248
+ injectStyles(STYLE_ID5, CSS5, doc);
907
1249
  this.el = doc.createElement("div");
908
1250
  this.el.className = "vela-widget-bottombar";
909
1251
  for (const preset of RANGE_PRESETS) {
@@ -1041,8 +1383,8 @@ function filterSymbols(list, query, limit = 100) {
1041
1383
  }
1042
1384
  return [...prefix, ...substr, ...desc, ...venue].slice(0, limit);
1043
1385
  }
1044
- var STYLE_ID5 = "vela-widget-symbolpicker";
1045
- var CSS5 = `
1386
+ var STYLE_ID6 = "vela-widget-symbolpicker";
1387
+ var CSS6 = `
1046
1388
  .vela-sp-searchrow {
1047
1389
  display: flex;
1048
1390
  align-items: center;
@@ -1132,7 +1474,7 @@ var SymbolPicker = class {
1132
1474
  this.seed = "";
1133
1475
  this.activeTab = "All";
1134
1476
  const doc = (opts.host ?? document.body).ownerDocument;
1135
- injectStyles(STYLE_ID5, CSS5, doc);
1477
+ injectStyles(STYLE_ID6, CSS6, doc);
1136
1478
  this.input = doc.createElement("input");
1137
1479
  this.input.className = "vela-sp-input";
1138
1480
  this.input.placeholder = "Search symbol\u2026";
@@ -1454,8 +1796,8 @@ function treeIsEmpty(panes) {
1454
1796
  }
1455
1797
 
1456
1798
  // src/widget/object-tree.ts
1457
- var STYLE_ID6 = "vela-widget-objtree";
1458
- var CSS6 = `
1799
+ var STYLE_ID7 = "vela-widget-objtree";
1800
+ var CSS7 = `
1459
1801
  /* One pane's block. The transparent border reserves the drop-target outline. */
1460
1802
  .vela-ot-pane {
1461
1803
  border: 1px solid transparent;
@@ -1693,7 +2035,7 @@ var ObjectTree = class extends SidePanel {
1693
2035
  this.menu = null;
1694
2036
  this.menuActions = /* @__PURE__ */ new Map();
1695
2037
  this.drag = null;
1696
- injectStyles(STYLE_ID6, CSS6, host.ownerDocument);
2038
+ injectStyles(STYLE_ID7, CSS7, host.ownerDocument);
1697
2039
  this.body.addEventListener("contextmenu", (e) => this.onContextMenu(e));
1698
2040
  this.body.addEventListener("pointerdown", (e) => this.onPointerDown(e));
1699
2041
  }
@@ -2672,8 +3014,8 @@ var ObjectTree = class extends SidePanel {
2672
3014
  };
2673
3015
 
2674
3016
  // src/widget/data-window.ts
2675
- var STYLE_ID7 = "vela-widget-datawindow";
2676
- var CSS7 = `
3017
+ var STYLE_ID8 = "vela-widget-datawindow";
3018
+ var CSS8 = `
2677
3019
  .vela-dw-group {
2678
3020
  padding: 10px 8px 4px;
2679
3021
  margin-top: 4px;
@@ -2732,7 +3074,7 @@ var DataWindow = class extends SidePanel {
2732
3074
  super(host, "Data window", "vela-dw");
2733
3075
  this.chart = null;
2734
3076
  this.unsubs = [];
2735
- injectStyles(STYLE_ID7, CSS7, host.ownerDocument);
3077
+ injectStyles(STYLE_ID8, CSS8, host.ownerDocument);
2736
3078
  }
2737
3079
  toggle(open = this.el.hidden) {
2738
3080
  super.toggle(open);
@@ -2929,8 +3271,8 @@ var PanelDock = class {
2929
3271
  };
2930
3272
 
2931
3273
  // src/widget/shortcuts-help.ts
2932
- var STYLE_ID8 = "vela-widget-shortcuts";
2933
- var CSS8 = `
3274
+ var STYLE_ID9 = "vela-widget-shortcuts";
3275
+ var CSS9 = `
2934
3276
  .vela-sh-cat {
2935
3277
  color: var(--vela-fg-muted);
2936
3278
  font-size: var(--vela-font-size-sm);
@@ -2955,7 +3297,7 @@ var ShortcutsHelp = class {
2955
3297
  constructor(keymap, host, onOpenChange) {
2956
3298
  this.keymap = keymap;
2957
3299
  const doc = (host ?? document.body).ownerDocument;
2958
- injectStyles(STYLE_ID8, CSS8, doc);
3300
+ injectStyles(STYLE_ID9, CSS9, doc);
2959
3301
  this.list = doc.createElement("div");
2960
3302
  this.dialog = new Dialog({
2961
3303
  title: "Keyboard shortcuts",
@@ -3243,8 +3585,8 @@ var ChartContextMenu = class {
3243
3585
  };
3244
3586
 
3245
3587
  // src/widget/indicator-picker.ts
3246
- var STYLE_ID9 = "vela-widget-indpicker";
3247
- var CSS9 = `
3588
+ var STYLE_ID10 = "vela-widget-indpicker";
3589
+ var CSS10 = `
3248
3590
  .vela-ip-searchrow {
3249
3591
  display: flex;
3250
3592
  align-items: center;
@@ -3318,7 +3660,7 @@ var IndicatorPicker = class {
3318
3660
  this.isOpen = false;
3319
3661
  this.opts = opts;
3320
3662
  const doc = (opts.host ?? document.body).ownerDocument;
3321
- injectStyles(STYLE_ID9, CSS9, doc);
3663
+ injectStyles(STYLE_ID10, CSS10, doc);
3322
3664
  this.search = doc.createElement("input");
3323
3665
  this.search.className = "vela-ip-search";
3324
3666
  this.search.placeholder = "Search\u2026";
@@ -3446,8 +3788,8 @@ var IndicatorPicker = class {
3446
3788
  };
3447
3789
 
3448
3790
  // src/widget/timeframe-quick.ts
3449
- var STYLE_ID10 = "vela-widget-tfquick";
3450
- var CSS10 = `
3791
+ var STYLE_ID11 = "vela-widget-tfquick";
3792
+ var CSS11 = `
3451
3793
  .vela-tq-input {
3452
3794
  display: block;
3453
3795
  margin: 0 auto;
@@ -3470,7 +3812,7 @@ var CSS10 = `
3470
3812
  var TimeframeQuick = class {
3471
3813
  constructor(opts) {
3472
3814
  const doc = (opts.host ?? document.body).ownerDocument;
3473
- injectStyles(STYLE_ID10, CSS10, doc);
3815
+ injectStyles(STYLE_ID11, CSS11, doc);
3474
3816
  this.input = doc.createElement("input");
3475
3817
  this.input.className = "vela-tq-input";
3476
3818
  this.input.setAttribute("spellcheck", "false");
@@ -3667,7 +4009,7 @@ function sanitizeSync(raw) {
3667
4009
  if (raw == null || typeof raw !== "object") return null;
3668
4010
  const s = raw;
3669
4011
  const out = {};
3670
- for (const kind of ["viewport", "symbol", "timeframe", "crosshair"]) {
4012
+ for (const kind of ["viewport", "symbol", "timeframe", "crosshair", "drawings"]) {
3671
4013
  const v = s[kind];
3672
4014
  if (v === true) out[kind] = true;
3673
4015
  else if (v != null && typeof v === "object") {
@@ -3920,8 +4262,8 @@ var WidgetHistory = class {
3920
4262
  };
3921
4263
 
3922
4264
  // src/widget/toast.ts
3923
- var STYLE_ID11 = "vela-widget-toast";
3924
- var CSS11 = `
4265
+ var STYLE_ID12 = "vela-widget-toast";
4266
+ var CSS12 = `
3925
4267
  .vela-toast {
3926
4268
  position: absolute;
3927
4269
  left: 50%;
@@ -3951,7 +4293,7 @@ var CSS11 = `
3951
4293
  var Toast = class {
3952
4294
  constructor(host) {
3953
4295
  this.timer = null;
3954
- injectStyles(STYLE_ID11, CSS11, host.ownerDocument);
4296
+ injectStyles(STYLE_ID12, CSS12, host.ownerDocument);
3955
4297
  this.el = host.ownerDocument.createElement("div");
3956
4298
  this.el.className = "vela-toast";
3957
4299
  host.appendChild(this.el);