@luxalgo/vela 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +16 -8
  3. package/dist/{DataProvider-BrlxntPN.d.cts → DataProvider-COGDs9Xg.d.cts} +1 -1
  4. package/dist/{DataProvider-DwFIoV4r.d.ts → DataProvider-DpOWZD8F.d.ts} +1 -1
  5. package/dist/{chunk-KA77UTRF.js → chunk-27UTYE7T.js} +25 -2
  6. package/dist/{chunk-EZ5FWVLA.js → chunk-DPAA3SRT.js} +94 -13
  7. package/dist/{chunk-BZQM2XO7.js → chunk-HKNO24GD.js} +13 -1
  8. package/dist/{chunk-2IAETM2I.js → chunk-NLDTVFOI.js} +79 -6
  9. package/dist/{chunk-E3GU6AUI.js → chunk-R2JMNIHY.js} +68 -23
  10. package/dist/{chunk-QBPUT5SU.js → chunk-T3DZIR5Y.js} +211 -54
  11. package/dist/{contributions-tjNExo1o.d.ts → contributions-Cgm9RJYP.d.cts} +38 -11
  12. package/dist/{contributions-CtlptxR8.d.cts → contributions-bWgwyiVi.d.ts} +38 -11
  13. package/dist/index.cjs +317 -62
  14. package/dist/index.d.cts +8 -8
  15. package/dist/index.d.ts +8 -8
  16. package/dist/index.js +4 -4
  17. package/dist/{options-BnEPUuVV.d.ts → options-NAUHiQEC.d.cts} +3 -3
  18. package/dist/{options-BnEPUuVV.d.cts → options-NAUHiQEC.d.ts} +3 -3
  19. package/dist/{plugin-BWxJRKzS.d.cts → plugin-CjjzZX2F.d.cts} +4 -4
  20. package/dist/{plugin-e2WUEtiI.d.ts → plugin-CqxWoDKS.d.ts} +4 -4
  21. package/dist/plugin.cjs +20 -6
  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-HF0IAzwf.d.cts → side-panel-wMNfgyZb.d.cts} +13 -0
  32. package/dist/{side-panel-HF0IAzwf.d.ts → side-panel-wMNfgyZb.d.ts} +13 -0
  33. package/dist/{statusline-model-zKXtGIjj.d.ts → statusline-model-Dih6FGWP.d.ts} +3 -3
  34. package/dist/{statusline-model-DFY7tuHj.d.cts → statusline-model-Dpfpq54A.d.cts} +3 -3
  35. package/dist/ui.cjs +115 -5
  36. package/dist/ui.d.cts +28 -2
  37. package/dist/ui.d.ts +28 -2
  38. package/dist/ui.js +3 -3
  39. package/dist/vela.global.js +317 -62
  40. package/dist/vela.global.min.js +61 -68
  41. package/dist/widget.cjs +476 -85
  42. package/dist/widget.d.cts +20 -10
  43. package/dist/widget.d.ts +20 -10
  44. package/dist/widget.js +7 -7
  45. package/dist/workspace.cjs +476 -85
  46. package/dist/workspace.d.cts +6 -6
  47. package/dist/workspace.d.ts +6 -6
  48. package/dist/workspace.js +6 -6
  49. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -238,7 +238,7 @@ var IndicatorHandleImpl = class {
238
238
  this.controller.applyProps(this.id, values);
239
239
  }
240
240
  updateCode(source) {
241
- this.controller.updateCode(this.id, source);
241
+ return this.controller.updateCode(this.id, source);
242
242
  }
243
243
  setVisible(visible) {
244
244
  this.controller.setVisible(this.id, visible);
@@ -641,6 +641,7 @@ registerIcon("expand", S('<rect x="2.2" y="2.2" width="11.6" height="11.6" rx="1
641
641
  registerIcon("plus", S('<path d="M8 2.8v10.4M2.8 8h10.4"/>'));
642
642
  registerIcon("minus", S('<path d="M2.8 8h10.4"/>'));
643
643
  registerIcon("maximize", S('<path d="M2.5 6V3a.5.5 0 0 1 .5-.5h3M10 2.5h3a.5.5 0 0 1 .5.5v3M13.5 10v3a.5.5 0 0 1-.5.5h-3M6 13.5H3a.5.5 0 0 1-.5-.5v-3"/>'));
644
+ registerIcon("restore", S('<path d="M6 2.5v3a.5.5 0 0 1-.5.5h-3M13.5 6h-3a.5.5 0 0 1-.5-.5v-3M10 13.5v-3a.5.5 0 0 1 .5-.5h3M2.5 10h3a.5.5 0 0 1 .5.5v3"/>'));
644
645
  registerIcon("restore", S('<path d="M6.2 2.5v3.7H2.5M9.8 13.5V9.8h3.7M13.5 6.2H9.8V2.5M2.5 9.8h3.7v3.7"/>'));
645
646
  registerIcon("star", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>'));
646
647
  registerIcon("star-filled", S('<path d="M8 2.2l1.75 3.55 3.9.55-2.8 2.75.65 3.9L8 11.1l-3.5 1.85.65-3.9-2.8-2.75 3.9-.55z"/>', 'fill="currentColor"'));
@@ -3299,6 +3300,10 @@ var FibRatios = class extends Drawing {
3299
3300
 
3300
3301
  // src/core/drawings/types/FibLevels.ts
3301
3302
  var FibLevels = class extends FibRatios {
3303
+ /** Price of a level for the two anchor prices; subclasses override to change which anchor ratio 0 sits on. */
3304
+ levelPrice(ratio, p1, p2) {
3305
+ return p1 + ratio * (p2 - p1);
3306
+ }
3302
3307
  /** Per-level pixel line + price for the ENABLED levels, spanning the anchors' time range. */
3303
3308
  levelLines(proj) {
3304
3309
  const a = this.anchors[0];
@@ -3308,11 +3313,10 @@ var FibLevels = class extends FibRatios {
3308
3313
  const xb = proj.xOf(b.time);
3309
3314
  const x1 = Math.min(xa, xb);
3310
3315
  const x2 = Math.max(xa, xb);
3311
- const delta = b.price - a.price;
3312
3316
  const out = [];
3313
3317
  for (const lv of this.levels) {
3314
3318
  if (!lv.enabled) continue;
3315
- const price = a.price + lv.ratio * delta;
3319
+ const price = this.levelPrice(lv.ratio, a.price, b.price);
3316
3320
  const y = proj.yOf(price, this.paneId);
3317
3321
  if (y == null) continue;
3318
3322
  out.push({ ratio: lv.ratio, color: lv.color, label: lv.label, price, x1, x2, y });
@@ -3352,8 +3356,7 @@ var FibLevels = class extends FibRatios {
3352
3356
  const a = this.anchors[0];
3353
3357
  const b = this.anchors[1];
3354
3358
  if (!a || !b) return null;
3355
- const delta = b.price - a.price;
3356
- const prices = this.levels.filter((l) => l.enabled).map((l) => a.price + l.ratio * delta);
3359
+ const prices = this.levels.filter((l) => l.enabled).map((l) => this.levelPrice(l.ratio, a.price, b.price));
3357
3360
  if (prices.length === 0) return null;
3358
3361
  return { min: Math.min(...prices), max: Math.max(...prices) };
3359
3362
  }
@@ -3439,13 +3442,24 @@ function cycleLevels(count, enabledCount = count) {
3439
3442
  // src/core/drawings/types/FibRetracement.ts
3440
3443
  var LEVELS = fibLevels([0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]);
3441
3444
  var FibRetracement = class extends FibLevels {
3442
- constructor() {
3443
- super(...arguments);
3445
+ constructor(init) {
3446
+ super(init);
3444
3447
  this.type = "fibretracement";
3448
+ if (this.reverse === void 0) this.reverse = false;
3445
3449
  }
3446
3450
  defaultLevels() {
3447
3451
  return LEVELS;
3448
3452
  }
3453
+ levelPrice(ratio, p1, p2) {
3454
+ return this.reverse ? p1 + ratio * (p2 - p1) : p2 + ratio * (p1 - p2);
3455
+ }
3456
+ writeProps() {
3457
+ return { ...super.writeProps(), reverse: this.reverse };
3458
+ }
3459
+ readProps(props) {
3460
+ super.readProps(props);
3461
+ this.reverse = typeof props.reverse === "boolean" ? props.reverse : true;
3462
+ }
3449
3463
  };
3450
3464
 
3451
3465
  // src/core/drawings/types/FibExtension.ts
@@ -9258,20 +9272,26 @@ var EngineOrchestrator = class _EngineOrchestrator {
9258
9272
  /**
9259
9273
  * IndicatorController: replace a SCRIPT indicator's source in place (see
9260
9274
  * {@link IndicatorHandle.updateCode}). Prepare-first: the running session keeps
9261
- * computing until the new source has compiled, so a broken edit costs the user
9262
- * nothing but an `error` event. Natives have no script — warn and leave them alone.
9275
+ * computing until the new source has compiled; then the new code runs on TRIAL until
9276
+ * its first model lands, and a failure before that puts the previous code back.
9277
+ * Natives have no script — warn and leave them alone.
9263
9278
  */
9264
9279
  updateCode(id, source) {
9265
9280
  const record = this.registry.get(id);
9266
9281
  const handle = this.handles.get(id);
9267
- if (!record || !handle) return;
9282
+ if (!record || !handle) return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator is no longer on the chart.`) });
9268
9283
  if (record.native) {
9269
9284
  console.warn(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`);
9270
- return;
9285
+ return Promise.resolve({ ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 a native indicator has no script to update.`) });
9271
9286
  }
9272
- if (source === record.source && record.pendingSource === void 0) return;
9273
- record.pendingSource = source;
9274
- void this.swapSource(id, source, handle);
9287
+ const busy = record.pendingSource !== void 0 || record.codeTrial !== void 0;
9288
+ if (!busy && source === record.source) return Promise.resolve({ ok: true, error: null });
9289
+ return new Promise((resolve) => {
9290
+ (record.codeWaiters ?? (record.codeWaiters = [])).push(resolve);
9291
+ if (record.pendingSource === source || record.pendingSource === void 0 && record.codeTrial && record.source === source) return;
9292
+ record.pendingSource = source;
9293
+ void this.swapSource(id, source, handle);
9294
+ });
9275
9295
  }
9276
9296
  async swapSource(id, source, handle) {
9277
9297
  const engine = this.registry.get(id)?.engine ?? this.engineFor(this.registry.get(id)?.options?.language);
@@ -9282,26 +9302,41 @@ var EngineOrchestrator = class _EngineOrchestrator {
9282
9302
  const record2 = this.registry.get(id);
9283
9303
  if (record2?.pendingSource === source) {
9284
9304
  record2.pendingSource = void 0;
9285
- this.fail(id, handle, err);
9305
+ this.rejectCode(id, record2, handle, err);
9286
9306
  }
9287
9307
  return;
9288
9308
  }
9289
9309
  const record = this.registry.get(id);
9290
9310
  if (!record || record.pendingSource !== source) return;
9291
9311
  record.pendingSource = void 0;
9312
+ const previous = record.codeTrial?.previous ?? {
9313
+ source: record.source,
9314
+ engine: record.engine,
9315
+ prepared: record.prepared,
9316
+ inputValues: record.inputValues,
9317
+ propValues: record.propValues,
9318
+ inputs: [...handle.inputs],
9319
+ props: [...handle.props]
9320
+ };
9321
+ record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues, record.prepared?.inputs);
9322
+ record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues, record.prepared?.props);
9292
9323
  record.source = source;
9293
9324
  record.engine = engine;
9294
9325
  record.prepared = prepared;
9295
9326
  handle.setSource(source);
9296
- record.inputValues = valuesOnSchema(prepared.inputs, record.inputValues);
9297
- record.propValues = valuesOnSchema(prepared.props ?? [], record.propValues);
9298
9327
  handle.setSchema(prepared.inputs);
9299
9328
  handle.setPropsSchema(prepared.props ?? []);
9300
9329
  record.session?.stop();
9301
9330
  record.session = void 0;
9302
9331
  record.pendingStructural = true;
9303
9332
  record.pendingCause = "code";
9304
- if (record.hidden) return;
9333
+ if (record.hidden) {
9334
+ record.codeTrial = void 0;
9335
+ if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
9336
+ this.settleCode(record, { ok: true, error: null });
9337
+ return;
9338
+ }
9339
+ record.codeTrial = { previous };
9305
9340
  if (record.renderHandle) {
9306
9341
  this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
9307
9342
  this.setLoading(record, true);
@@ -9309,6 +9344,46 @@ var EngineOrchestrator = class _EngineOrchestrator {
9309
9344
  this.mountLoadingPlaceholder(id, record);
9310
9345
  }
9311
9346
  this.executeIndicator(id, handle);
9347
+ if (this.bars.length === 0 && record.codeTrial) {
9348
+ record.codeTrial = void 0;
9349
+ this.settleCode(record, { ok: true, error: null });
9350
+ }
9351
+ }
9352
+ /** Resolve every `updateCode` caller waiting on this record's chain of edits. */
9353
+ settleCode(record, result) {
9354
+ const waiters = record.codeWaiters;
9355
+ record.codeWaiters = void 0;
9356
+ if (waiters) for (const resolve of waiters) resolve(result);
9357
+ }
9358
+ /**
9359
+ * An update failed — at prepare, or on trial before its first model. A trial puts the
9360
+ * previous code back (source, engine, prepared script, values, schema) and restarts it;
9361
+ * its visuals were never dropped, so the chart keeps painting throughout. Then the
9362
+ * failure reports the usual way and every waiting caller learns it.
9363
+ */
9364
+ rejectCode(id, record, handle, err) {
9365
+ const trial = record.codeTrial;
9366
+ record.codeTrial = void 0;
9367
+ this.fail(id, handle, err);
9368
+ if (trial) {
9369
+ const p = trial.previous;
9370
+ record.session?.stop();
9371
+ record.session = void 0;
9372
+ record.source = p.source;
9373
+ record.engine = p.engine;
9374
+ record.prepared = p.prepared;
9375
+ record.inputValues = p.inputValues;
9376
+ record.propValues = p.propValues;
9377
+ handle.setSource(p.source);
9378
+ handle.setSchema(p.inputs);
9379
+ handle.setPropsSchema(p.props);
9380
+ record.pendingStructural = true;
9381
+ record.pendingCause = void 0;
9382
+ if (record.renderHandle) this.renderer.setIndicatorInputs(record.renderHandle, record.inputValues, record.propValues);
9383
+ if (!record.hidden) this.executeIndicator(id, handle);
9384
+ }
9385
+ const error = err instanceof Error ? err : new Error(String(err));
9386
+ this.settleCode(record, { ok: false, error });
9312
9387
  }
9313
9388
  /** IndicatorController: tear down an indicator and (if now empty) its pane. */
9314
9389
  /** Live handles of every indicator on the chart (script + native), insertion order. */
@@ -9323,6 +9398,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
9323
9398
  removeIndicator(id) {
9324
9399
  const record = this.registry.remove(id);
9325
9400
  if (record?.native?.type === "volume") this.volumeOptedOut = true;
9401
+ if (record) this.settleCode(record, { ok: false, error: new Error(`[vela] updateCode("${id}") \u2014 the indicator was removed.`) });
9326
9402
  record?.session?.stop();
9327
9403
  if (record?.native?.started) record.native.instance.stop();
9328
9404
  this.handles.delete(id);
@@ -9350,6 +9426,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
9350
9426
  if (!visible) {
9351
9427
  record.session?.stop();
9352
9428
  record.session = void 0;
9429
+ if (record.codeTrial) {
9430
+ record.codeTrial = void 0;
9431
+ this.settleCode(record, { ok: true, error: null });
9432
+ }
9353
9433
  if (record.native?.started) record.native.instance.suspend();
9354
9434
  if (record.renderHandle) this.renderer.setIndicatorVisible?.(record.renderHandle, false);
9355
9435
  else if (record.native) this.mountHiddenNativeRow(id, record);
@@ -9437,6 +9517,7 @@ var EngineOrchestrator = class _EngineOrchestrator {
9437
9517
  this.viewportUnsub?.();
9438
9518
  this.paneActionUnsub?.();
9439
9519
  for (const record of this.registry.all()) {
9520
+ this.settleCode(record, { ok: false, error: new Error("[vela] updateCode \u2014 the chart was destroyed.") });
9440
9521
  record.session?.stop();
9441
9522
  if (record.native?.started) record.native.instance.stop();
9442
9523
  }
@@ -9505,7 +9586,8 @@ var EngineOrchestrator = class _EngineOrchestrator {
9505
9586
  const record = this.registry.get(id);
9506
9587
  if (!record || !record.engine || !record.prepared || record.hidden) return;
9507
9588
  const mode = this.config.live && !record.prepared.reactsToViewport && record.engine.capabilities.streaming ? "live" : "static";
9508
- record.session = record.engine.execute(
9589
+ const prepared = record.prepared;
9590
+ const session = record.engine.execute(
9509
9591
  {
9510
9592
  prepared: record.prepared,
9511
9593
  market: this.market(),
@@ -9526,6 +9608,10 @@ var EngineOrchestrator = class _EngineOrchestrator {
9526
9608
  const cause = record.pendingCause ?? "history";
9527
9609
  if (!this.applyModel(id, model)) return;
9528
9610
  record.pendingCause = void 0;
9611
+ if (record.codeTrial) {
9612
+ record.codeTrial = void 0;
9613
+ this.settleCode(record, { ok: true, error: null });
9614
+ }
9529
9615
  this.emitContextChanged(id);
9530
9616
  this.emitScriptRun(id, cause, first);
9531
9617
  },
@@ -9536,9 +9622,17 @@ var EngineOrchestrator = class _EngineOrchestrator {
9536
9622
  handle.emit("alert", { id: a.id, message: a.message, title: a.title, time: a.time });
9537
9623
  },
9538
9624
  onWarning: (w) => this.events.emit("warning", w),
9539
- onError: (err) => this.fail(id, handle, err)
9625
+ onError: (err) => {
9626
+ if (record.codeTrial) this.rejectCode(id, record, handle, err);
9627
+ else this.fail(id, handle, err);
9628
+ }
9540
9629
  }
9541
9630
  );
9631
+ if (record.prepared !== prepared) {
9632
+ session.stop();
9633
+ return;
9634
+ }
9635
+ record.session = session;
9542
9636
  this.emitContextChanged(id);
9543
9637
  }
9544
9638
  /**
@@ -10039,7 +10133,7 @@ function yieldToPaint() {
10039
10133
  }
10040
10134
  return new Promise((resolve) => setTimeout(resolve, 0));
10041
10135
  }
10042
- function valuesOnSchema(schema, previous) {
10136
+ function valuesOnSchema(schema, previous, oldSchema = []) {
10043
10137
  const out = {};
10044
10138
  const declared = /* @__PURE__ */ new Set();
10045
10139
  for (const s of schema) {
@@ -10047,9 +10141,22 @@ function valuesOnSchema(schema, previous) {
10047
10141
  declared.add(s.key);
10048
10142
  declared.add(s.title);
10049
10143
  }
10050
- for (const [k, v] of Object.entries(previous)) if (declared.has(k)) out[k] = v;
10144
+ const oldDefaults = /* @__PURE__ */ new Map();
10145
+ for (const s of oldSchema) {
10146
+ oldDefaults.set(s.key, s.defval);
10147
+ if (!oldDefaults.has(s.title)) oldDefaults.set(s.title, s.defval);
10148
+ }
10149
+ for (const [k, v] of Object.entries(previous)) {
10150
+ if (!declared.has(k)) continue;
10151
+ if (oldDefaults.has(k) && sameInputValue(oldDefaults.get(k), v)) continue;
10152
+ out[k] = v;
10153
+ }
10051
10154
  return out;
10052
10155
  }
10156
+ function sameInputValue(a, b) {
10157
+ if (a === b) return true;
10158
+ return typeof a === "object" && a !== null && typeof b === "object" && b !== null && JSON.stringify(a) === JSON.stringify(b);
10159
+ }
10053
10160
  function blankedModel(model) {
10054
10161
  return {
10055
10162
  ...model,
@@ -11693,6 +11800,17 @@ function iconEl(id, doc = document) {
11693
11800
  if (svg) span.innerHTML = svg;
11694
11801
  return span;
11695
11802
  }
11803
+
11804
+ // src/ui/surface-events.ts
11805
+ var SURFACE_OPEN_EVENT = "vela:surface-open";
11806
+ var SURFACE_CLOSE_EVENT = "vela:surface-close";
11807
+ function announceSurface(el, open2, kind, trigger = null) {
11808
+ const View = el.ownerDocument.defaultView;
11809
+ if (!View) return;
11810
+ el.dispatchEvent(
11811
+ new View.CustomEvent(open2 ? SURFACE_OPEN_EVENT : SURFACE_CLOSE_EVENT, { bubbles: true, composed: true, detail: { kind, trigger } })
11812
+ );
11813
+ }
11696
11814
  function menuController(opts) {
11697
11815
  return {
11698
11816
  machine: menu__namespace.machine,
@@ -11838,6 +11956,8 @@ var Surface = class _Surface {
11838
11956
  /** Trigger rect captured when this level opened — the list stays put if the
11839
11957
  * trigger then moves (favorite chips shifting the caret). */
11840
11958
  this.pinnedAnchor = null;
11959
+ /** The open state last announced — see {@link announce}. */
11960
+ this.shown = false;
11841
11961
  this.doc = doc;
11842
11962
  this.host = opts.host;
11843
11963
  this.onSelect = opts.onSelect;
@@ -11851,6 +11971,7 @@ var Surface = class _Surface {
11851
11971
  this.positioner.appendChild(this.list);
11852
11972
  this.host.appendChild(this.positioner);
11853
11973
  const trigger = opts.trigger;
11974
+ this.opener = trigger ?? opts.opener ?? null;
11854
11975
  this.ctrl = menuController({
11855
11976
  items: [],
11856
11977
  id: opts.id,
@@ -11872,15 +11993,21 @@ var Surface = class _Surface {
11872
11993
  if (opts.triggerId && trigger) trigger.id = opts.triggerId;
11873
11994
  this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
11874
11995
  const api = this.ctrl.connect(service);
11996
+ if (this.shown && !api.open) this.announce(false);
11875
11997
  if (trigger) vanilla.spreadProps(trigger, api.getTriggerProps(), this.mid);
11876
11998
  vanilla.spreadProps(this.positioner, api.getPositionerProps(), this.mid);
11877
11999
  vanilla.spreadProps(this.list, api.getContentProps(), this.mid);
11878
12000
  this.project(api);
12001
+ if (api.open && !this.shown) this.announce(true);
11879
12002
  });
11880
12003
  }
11881
12004
  get api() {
11882
12005
  return this.ctrl.connect(this.handle.service);
11883
12006
  }
12007
+ announce(open2) {
12008
+ this.shown = open2;
12009
+ announceSurface(this.list, open2, "menu", this.opener);
12010
+ }
11884
12011
  setItems(items) {
11885
12012
  this.items = items;
11886
12013
  for (const sub2 of this.subs.values()) sub2.destroy();
@@ -11898,6 +12025,7 @@ var Surface = class _Surface {
11898
12025
  for (const sub2 of this.subs.values()) sub2.destroy();
11899
12026
  this.subs.clear();
11900
12027
  this.handle.stop();
12028
+ if (this.shown) this.announce(false);
11901
12029
  this.positioner.remove();
11902
12030
  }
11903
12031
  byId(id) {
@@ -11991,6 +12119,7 @@ var Surface = class _Surface {
11991
12119
  const sub2 = new _Surface(doc, {
11992
12120
  host: this.host,
11993
12121
  placement: "right-start",
12122
+ opener: li,
11994
12123
  onSelect: this.onSelect,
11995
12124
  onFavorite: this.onFavorite,
11996
12125
  checkmarks: this.checkmarks,
@@ -12210,16 +12339,21 @@ var Popover = class {
12210
12339
  this.hide();
12211
12340
  };
12212
12341
  const onReflow = () => this.place();
12213
- setTimeout(() => document.addEventListener("pointerdown", onOutside, true), 0);
12342
+ setTimeout(() => {
12343
+ if (this.onOutside === onOutside) document.addEventListener("pointerdown", onOutside, true);
12344
+ }, 0);
12214
12345
  document.addEventListener("keydown", onKey, true);
12215
12346
  window.addEventListener("resize", onReflow, true);
12216
12347
  document.addEventListener("scroll", onReflow, true);
12217
12348
  this.onOutside = onOutside;
12218
12349
  this.onKey = onKey;
12219
12350
  this.onReflow = onReflow;
12351
+ announceSurface(this.el, true, "popover", this.trigger);
12220
12352
  }
12221
12353
  hide() {
12222
12354
  if (!this.shown) return;
12355
+ this.shown = false;
12356
+ announceSurface(this.el, false, "popover", this.trigger);
12223
12357
  if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
12224
12358
  if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
12225
12359
  if (this.onReflow) {
@@ -12239,7 +12373,6 @@ var Popover = class {
12239
12373
  } else {
12240
12374
  this.el.remove();
12241
12375
  }
12242
- this.shown = false;
12243
12376
  if (open === this) open = null;
12244
12377
  this.ctrl.onClose?.();
12245
12378
  }
@@ -12940,6 +13073,8 @@ ${overlayScrollbarCss(".vela-dialog *")}
12940
13073
  `;
12941
13074
  var Dialog = class {
12942
13075
  constructor(opts = {}) {
13076
+ this.opener = null;
13077
+ this.wasOpen = false;
12943
13078
  const doc = (opts.host ?? document.body).ownerDocument;
12944
13079
  injectStyles(DIALOG_STYLE_ID, DIALOG_CSS, doc);
12945
13080
  const host = opts.host ?? doc.body;
@@ -13036,6 +13171,7 @@ var Dialog = class {
13036
13171
  const mid = String(this.ctrl.props.id);
13037
13172
  this.handle = runMachine(this.ctrl.machine, this.ctrl.props, (service) => {
13038
13173
  const api = this.ctrl.connect(service);
13174
+ if (this.wasOpen && !api.open) this.announce(false);
13039
13175
  vanilla.spreadProps(this.backdrop, api.getBackdropProps(), mid);
13040
13176
  vanilla.spreadProps(this.positioner, api.getPositionerProps(), mid);
13041
13177
  vanilla.spreadProps(this.panel, api.getContentProps(), mid);
@@ -13043,12 +13179,27 @@ var Dialog = class {
13043
13179
  vanilla.spreadProps(close, api.getCloseTriggerProps(), mid);
13044
13180
  this.backdrop.style.display = api.open ? "" : "none";
13045
13181
  this.positioner.style.display = api.open ? "" : "none";
13182
+ if (api.open && !this.wasOpen) this.announce(true);
13183
+ if (this.wasOpen && !api.open) this.restoreOpener();
13184
+ this.wasOpen = api.open;
13046
13185
  });
13047
13186
  }
13187
+ /** Closing hands focus back to whatever opened it (Escape, the ✕, hide(), or a
13188
+ * teardown that outruns the machine's close notification). */
13189
+ restoreOpener() {
13190
+ const opener = this.opener;
13191
+ this.opener = null;
13192
+ if (opener?.isConnected) opener.focus({ preventScroll: true });
13193
+ }
13194
+ announce(open2) {
13195
+ const opener = this.opener === this.panel.ownerDocument.body ? null : this.opener;
13196
+ announceSurface(this.panel, open2, "dialog", opener);
13197
+ }
13048
13198
  get open() {
13049
13199
  return this.ctrl.connect(this.handle.service).open;
13050
13200
  }
13051
13201
  show() {
13202
+ this.opener = this.panel.ownerDocument.activeElement;
13052
13203
  this.ctrl.connect(this.handle.service).setOpen(true);
13053
13204
  }
13054
13205
  hide() {
@@ -13063,6 +13214,11 @@ var Dialog = class {
13063
13214
  destroy() {
13064
13215
  if (this.open) this.hide();
13065
13216
  this.handle.stop();
13217
+ if (this.wasOpen) {
13218
+ this.wasOpen = false;
13219
+ this.announce(false);
13220
+ }
13221
+ this.restoreOpener();
13066
13222
  this.backdrop.remove();
13067
13223
  this.positioner.remove();
13068
13224
  }
@@ -14047,14 +14203,49 @@ function buildColorPicker(color, theme, onChange, options = {}) {
14047
14203
  const knob = document.createElement("div");
14048
14204
  knob.style.cssText = "position:absolute;top:50%;width:15px;height:15px;border-radius:50%;background:var(--vela-selected-bg);box-shadow:0 1px 3px rgba(0,0,0,0.55);transform:translate(-50%,-50%);pointer-events:none;";
14049
14205
  track.appendChild(knob);
14050
- const pctBox = document.createElement("div");
14051
- pctBox.style.cssText = `min-width:42px;text-align:center;font:var(--vela-font-size-md) ${theme.fontFamily};color:var(--vela-fg);border:1px solid var(--vela-border);border-radius:5px;padding:3px 4px;`;
14206
+ const pctBox = document.createElement("input");
14207
+ pctBox.type = "text";
14208
+ pctBox.inputMode = "numeric";
14209
+ pctBox.title = "Opacity (%)";
14210
+ pctBox.style.cssText = `width:46px;box-sizing:border-box;text-align:center;font:var(--vela-font-size-md) ${theme.fontFamily};color:var(--vela-fg);background:transparent;border:1px solid var(--vela-border);border-radius:5px;padding:3px 4px;outline:none;`;
14211
+ pctBox.addEventListener("focus", () => {
14212
+ pctBox.style.borderColor = "var(--vela-border-strong)";
14213
+ pctBox.select();
14214
+ });
14052
14215
  opRow.append(track, pctBox);
14053
14216
  const paintOpacity = () => {
14054
14217
  track.style.background = `linear-gradient(to right, ${curHex}00, ${curHex}ff), ${CHECKER}`;
14055
14218
  knob.style.left = `${curAlpha * 100}%`;
14056
- pctBox.textContent = `${Math.round(curAlpha * 100)}%`;
14219
+ pctBox.value = `${Math.round(curAlpha * 100)}%`;
14057
14220
  };
14221
+ const commitTyped = () => {
14222
+ pctBox.style.borderColor = "var(--vela-border)";
14223
+ const n = parseFloat(pctBox.value.replace("%", ""));
14224
+ if (Number.isFinite(n)) {
14225
+ const next = Math.max(0, Math.min(100, n)) / 100;
14226
+ if (Math.round(next * 100) !== Math.round(curAlpha * 100)) {
14227
+ curAlpha = next;
14228
+ paintOpacity();
14229
+ emit();
14230
+ return;
14231
+ }
14232
+ }
14233
+ paintOpacity();
14234
+ };
14235
+ pctBox.addEventListener("change", commitTyped);
14236
+ pctBox.addEventListener("blur", () => {
14237
+ pctBox.style.borderColor = "var(--vela-border)";
14238
+ paintOpacity();
14239
+ });
14240
+ pctBox.addEventListener("keydown", (e) => {
14241
+ if (e.key === "Escape") return;
14242
+ e.stopPropagation();
14243
+ if (e.key === "Enter") {
14244
+ e.preventDefault();
14245
+ commitTyped();
14246
+ pctBox.blur();
14247
+ }
14248
+ });
14058
14249
  let dragging = false;
14059
14250
  const onDrag = (clientX) => {
14060
14251
  const r = track.getBoundingClientRect();
@@ -17197,15 +17388,23 @@ var Batch = class {
17197
17388
  this.seg(x + w, y + h, x, y + h, lineWidth, c);
17198
17389
  this.seg(x, y + h, x, y, lineWidth, c);
17199
17390
  }
17200
- /** Filled circle (triangle fan). */
17391
+ /**
17392
+ * Filled circle (triangle fan), feathered like a line: the rim is padded by {@link AA_PAD} and
17393
+ * edgeDist runs from 0 at the center to the padded radius at the rim, with edgeHalf = r. The
17394
+ * distance grows linearly along each spoke, so the shader's line feather gives a round soft edge.
17395
+ */
17201
17396
  circle(cx, cy, r, c, segments = 16) {
17202
- let px = cx + r;
17397
+ const ext = r + AA_PAD;
17398
+ let px = cx + ext;
17203
17399
  let py = cy;
17400
+ this.ensure(segments * 3);
17204
17401
  for (let i = 1; i <= segments; i += 1) {
17205
17402
  const a = i / segments * Math.PI * 2;
17206
- const nx = cx + Math.cos(a) * r;
17207
- const ny = cy + Math.sin(a) * r;
17208
- this.tri(cx, cy, c, px, py, c, nx, ny, c);
17403
+ const nx = cx + Math.cos(a) * ext;
17404
+ const ny = cy + Math.sin(a) * ext;
17405
+ this.v(cx, cy, c, 0, r);
17406
+ this.v(px, py, c, ext, r);
17407
+ this.v(nx, ny, c, ext, r);
17209
17408
  px = nx;
17210
17409
  py = ny;
17211
17410
  }
@@ -17386,7 +17585,7 @@ var WebGL2Backend = class {
17386
17585
  }
17387
17586
  mount(canvas) {
17388
17587
  this.canvas = canvas;
17389
- this.gl = canvas.getContext("webgl2", { alpha: true, antialias: true, depth: false, stencil: false, premultipliedAlpha: true });
17588
+ this.gl = canvas.getContext("webgl2", { alpha: true, antialias: false, depth: false, stencil: false, premultipliedAlpha: true });
17390
17589
  if (!this.gl) return;
17391
17590
  if (!this.initGL()) {
17392
17591
  this.gl = null;
@@ -18645,6 +18844,12 @@ var InputController = class {
18645
18844
  this.lastTapT = 0;
18646
18845
  this.lastTapX = 0;
18647
18846
  this.lastTapY = 0;
18847
+ // Whether the last two primary presses were claimed by the drawings layer. A double-click
18848
+ // is the last press plus the one before it; a tool-driven click pair must never fall through
18849
+ // to the pane maximize toggle, even when the tool disarmed itself between the two clicks
18850
+ // (a finished placement leaves plain cursor mode for the second click).
18851
+ this.lastPressDrawing = false;
18852
+ this.prevPressDrawing = false;
18648
18853
  this.onTouchContextMenu = (e) => {
18649
18854
  if (this.touches.size === 0) return;
18650
18855
  if (!(e.target instanceof Node) || !this.el?.contains(e.target)) return;
@@ -18699,19 +18904,24 @@ var InputController = class {
18699
18904
  this.moved = false;
18700
18905
  this.startX = x;
18701
18906
  this.startY = y;
18907
+ this.prevPressDrawing = this.lastPressDrawing;
18908
+ this.lastPressDrawing = false;
18702
18909
  if (this.deps.drawingsClaim?.(x, y)) {
18703
18910
  this.region = "drawing";
18911
+ this.lastPressDrawing = true;
18704
18912
  this.deps.drawingsPointerDown?.(x, y, this.snapMode(e), e.shiftKey, e.ctrlKey || e.metaKey);
18705
18913
  this.capture(e.pointerId);
18706
18914
  return;
18707
18915
  }
18708
18916
  if (e.shiftKey && this.regionAt(x, y) === "data" && this.deps.drawingsMeasureStart?.(x, y, this.snapMode(e))) {
18709
18917
  this.region = "drawing";
18918
+ this.lastPressDrawing = true;
18710
18919
  this.capture(e.pointerId);
18711
18920
  return;
18712
18921
  }
18713
18922
  if ((e.ctrlKey || e.metaKey) && this.regionAt(x, y) === "data" && this.deps.drawingsMarqueeStart?.(x, y)) {
18714
18923
  this.region = "drawing";
18924
+ this.lastPressDrawing = true;
18715
18925
  this.capture(e.pointerId);
18716
18926
  return;
18717
18927
  }
@@ -18827,7 +19037,7 @@ var InputController = class {
18827
19037
  this.deps.drawingsPointerUp?.(x, y, this.snapMode(e));
18828
19038
  } else if (tapRelease && this.region === "data") {
18829
19039
  this.deps.onClick(x, y);
18830
- } else if (this.dragging && this.region === "data") {
19040
+ } else if (this.dragging && this.region === "data" && wasTouch) {
18831
19041
  const stale = e.timeStamp - this.lastT > FLING_STALE_MS;
18832
19042
  if (!stale && Math.abs(this.vx) > FLING_MIN_SPEED) {
18833
19043
  const pitch = this.deps.getCoords().pxPerBar();
@@ -19033,6 +19243,7 @@ var InputController = class {
19033
19243
  if (region === "price") this.deps.resetPriceScale(x, y);
19034
19244
  else if (region === "separator") this.deps.resetPaneSize(y);
19035
19245
  else if (region === "time") this.deps.resetView();
19246
+ else if (this.lastPressDrawing || this.prevPressDrawing) return;
19036
19247
  else this.deps.dataDblClick(x, y);
19037
19248
  }
19038
19249
  };
@@ -21198,6 +21409,7 @@ function timeTicks(fromMs, toMs, target = 8, offsetMs = 0) {
21198
21409
  const step = pickStep(span / Math.max(1, target));
21199
21410
  const zFrom = fromMs + offsetMs;
21200
21411
  const zTo = toMs + offsetMs;
21412
+ if (step >= MONTH) return calendarTicks(zFrom, zTo, step >= YEAR ? 12 : step >= 3 * MONTH ? 3 : 1, offsetMs);
21201
21413
  const first = Math.ceil(zFrom / step) * step;
21202
21414
  const out = [];
21203
21415
  for (let zt = first; zt <= zTo; zt += step) {
@@ -21228,6 +21440,18 @@ function timeTicks(fromMs, toMs, target = 8, offsetMs = 0) {
21228
21440
  }
21229
21441
  return out;
21230
21442
  }
21443
+ function calendarTicks(zFrom, zTo, months, offsetMs) {
21444
+ const d0 = new Date(zFrom);
21445
+ let idx = d0.getUTCFullYear() * 12 + d0.getUTCMonth();
21446
+ if (Date.UTC(Math.floor(idx / 12), idx % 12, 1) < zFrom) idx += 1;
21447
+ idx = Math.ceil(idx / months) * months;
21448
+ const out = [];
21449
+ for (let zt = Date.UTC(Math.floor(idx / 12), idx % 12, 1); zt <= zTo; idx += months, zt = Date.UTC(Math.floor(idx / 12), idx % 12, 1)) {
21450
+ const label = months === 12 ? String(Math.floor(idx / 12)) : MONTHS[idx % 12];
21451
+ out.push({ time: zt - offsetMs, label, major: true });
21452
+ }
21453
+ return out;
21454
+ }
21231
21455
 
21232
21456
  // src/renderers/native/chrome/countdown.ts
21233
21457
  function countdownText(barOpen, barMs, now2) {
@@ -25269,6 +25493,12 @@ var DrawingInteraction = class {
25269
25493
  activeDragId() {
25270
25494
  return this.state.kind === "pressed" && this.state.moved ? this.state.id : null;
25271
25495
  }
25496
+ /** The store drawings a live drag is moving — the pressed one and its riders — or none
25497
+ * before the drag passes the slop. A Ctrl-drag moves copies, so its sources stay put. */
25498
+ movingIds() {
25499
+ if (this.state.kind !== "pressed" || !this.state.moved || this.state.clones) return /* @__PURE__ */ new Set();
25500
+ return /* @__PURE__ */ new Set([this.state.id, ...this.state.riders.map((r) => r.id)]);
25501
+ }
25272
25502
  /** The drawing under a press that has not been released yet (drag or click undecided), or null. */
25273
25503
  pressedId() {
25274
25504
  return this.state.kind === "pressed" ? this.state.id : null;
@@ -25927,6 +26157,17 @@ var DrawingSettingsDialog = class {
25927
26157
  }
25928
26158
  }));
25929
26159
  }
26160
+ if (drawing instanceof FibRetracement) {
26161
+ const fib = drawing;
26162
+ grid.appendChild(fieldRow({
26163
+ label: "Reverse",
26164
+ bool: true,
26165
+ toggle: {
26166
+ checked: fib.reverse,
26167
+ onChange: (v) => actions.patch({ reverse: v })
26168
+ }
26169
+ }));
26170
+ }
25930
26171
  levels.forEach((lv, i) => {
25931
26172
  const row = document.createElement("div");
25932
26173
  row.className = "vela-field-span";
@@ -27294,6 +27535,7 @@ var DrawingToolbar = class {
27294
27535
  );
27295
27536
  }
27296
27537
  }
27538
+ announceSurface(fly, true, "menu", cell);
27297
27539
  }
27298
27540
  openMagnetFlyout() {
27299
27541
  const cell = this.magnetCell;
@@ -27317,6 +27559,7 @@ var DrawingToolbar = class {
27317
27559
  })
27318
27560
  );
27319
27561
  }
27562
+ announceSurface(fly, true, "menu", cell);
27320
27563
  }
27321
27564
  flyoutHeader(text) {
27322
27565
  const t = this.theme;
@@ -27385,9 +27628,11 @@ var DrawingToolbar = class {
27385
27628
  }
27386
27629
  closeFlyout() {
27387
27630
  this.starEls.clear();
27388
- if (this.flyout) {
27389
- this.flyout.remove();
27631
+ const fly = this.flyout;
27632
+ if (fly) {
27390
27633
  this.flyout = null;
27634
+ announceSurface(fly, false, "menu", this.flyoutCell);
27635
+ fly.remove();
27391
27636
  this.flyoutCell?.classList.remove("vela-open");
27392
27637
  this.flyoutCell = null;
27393
27638
  this.flyoutOwnerId = null;
@@ -27712,6 +27957,8 @@ var UserDrawingController = class {
27712
27957
  this.drawings = [];
27713
27958
  /** Ids painted on an interleave layer this frame — the top canvas paints only their handles. */
27714
27959
  this.sliced = /* @__PURE__ */ new Set();
27960
+ /** Interleaved drawings mid-drag: painted on the top canvas, left out of their slice. */
27961
+ this.lifted = /* @__PURE__ */ new Set();
27715
27962
  /** Cached slice canvases, keyed `paneId|beforeZ`, reused across frames to avoid churn. */
27716
27963
  this.sliceCache = /* @__PURE__ */ new Map();
27717
27964
  /** Series boundaries per pane as of the last `prepareSlices` — lets a repaint between data
@@ -27799,7 +28046,7 @@ var UserDrawingController = class {
27799
28046
  selectedIds: () => this.selectedIds,
27800
28047
  emit: (i) => this.emit(i),
27801
28048
  changed: () => {
27802
- this.invalidateSlices();
28049
+ this.syncLift();
27803
28050
  this.render();
27804
28051
  },
27805
28052
  openSettings: (id, x, y) => this.openSettingsById(id, x, y),
@@ -28425,6 +28672,20 @@ var UserDrawingController = class {
28425
28672
  /** A drawing that paints inside the series stack changed (content, not hover): its pixels
28426
28673
  * live in the backend composite, so this layer alone can't show the change — ask for a
28427
28674
  * data frame, which re-runs `prepareSlices` before the backend composites. */
28675
+ /** A drawing being dragged leaves its interleave slice for the top canvas, so each pointer
28676
+ * move repaints one 2D layer instead of re-rasterizing and re-uploading a plot-sized slice
28677
+ * texture through the data frame. The slices rebuild once as the drag starts and once as it
28678
+ * ends (release, cancel), when the lifted set changes. */
28679
+ syncLift() {
28680
+ const moving = this.interaction.movingIds();
28681
+ let same = moving.size === this.lifted.size;
28682
+ if (same) {
28683
+ for (const id of moving) if (!this.lifted.has(id)) same = false;
28684
+ }
28685
+ if (same) return;
28686
+ this.lifted = new Set(moving);
28687
+ this.invalidateSlices();
28688
+ }
28428
28689
  invalidateSlices() {
28429
28690
  if (this.sliced.size > 0 || this.drawings.some((d) => this.isInterleaved(d))) this.deps.requestDataPaint();
28430
28691
  }
@@ -28443,7 +28704,7 @@ var UserDrawingController = class {
28443
28704
  const theme = this.deps.theme();
28444
28705
  const buckets = /* @__PURE__ */ new Map();
28445
28706
  for (const d of this.drawings) {
28446
- if (!d.visible) continue;
28707
+ if (!d.visible || this.lifted.has(d.id)) continue;
28447
28708
  const beforeZ = sliceKeyFor(d.zIndex, this.lastBounds.get(d.paneId) ?? []);
28448
28709
  if (beforeZ === null) continue;
28449
28710
  const key = `${d.paneId}|${beforeZ}`;
@@ -28495,8 +28756,9 @@ var UserDrawingController = class {
28495
28756
  mutedLabel: edited instanceof TextLabel ? edited.id : null
28496
28757
  };
28497
28758
  this.painter.seriesLook = this.deps.seriesLook();
28498
- this.painter.paintAll(ctx, this.drawings.filter((d) => !this.isInterleaved(d)), proj, this.deps.theme(), targets);
28499
- this.painter.paintHighlights(ctx, this.drawings.filter((d) => this.isInterleaved(d)), proj, handleIdsFor(targets));
28759
+ const onTop = (d) => !this.isInterleaved(d) || this.lifted.has(d.id);
28760
+ this.painter.paintAll(ctx, this.drawings.filter(onTop), proj, this.deps.theme(), targets);
28761
+ this.painter.paintHighlights(ctx, this.drawings.filter((d) => !onTop(d)), proj, handleIdsFor(targets));
28500
28762
  const clones = this.interaction.dragClones();
28501
28763
  if (clones) this.painter.paintAll(ctx, clones, proj, this.deps.theme(), { selected: new Set(clones.map((c) => c.id)) });
28502
28764
  this.layoutTextEditor();
@@ -29773,23 +30035,21 @@ function stackLayers(entries, candleZ) {
29773
30035
  return { below, above };
29774
30036
  }
29775
30037
 
29776
- // src/renderers/native/chrome/luxalgo-logos.ts
29777
- var LUXALGO_SYMBOL_SVG = '<svg viewBox="0 0 45 40" aria-hidden="true"><g fill="currentColor"><path d="m40.25 38 4.58-7.998L28.802 2l-16.03 28 9.16-.001 6.87-12z"/><path d="M34.525 32.002 9.33 31.997 27.655 0h-9.158L.18 31.993 4.759 40h34.347z"/></g></svg>';
29778
- var LUXALGO_WORDMARK_SVG = '<svg viewBox="0 0 964 252" aria-hidden="true"><path fill="currentColor" fill-rule="evenodd" d="M 614 94.500 L 614 189 633.500 189 L 653 189 653 94.500 L 653 0 633.500 0 L 614 0 614 94.500 M 0 97.500 L 0 189 63.023 189 L 126.045 189 125.773 170.750 L 125.500 152.500 84.250 152.239 L 43 151.978 43 78.989 L 43 6 21.500 6 L 0 6 0 97.500 M 450.387 96.451 C 421.871 146.253, 398.306 186.994, 398.020 186.987 C 397.423 186.972, 362 124.724, 362 123.691 C 362 123.316, 370.100 108.818, 380 91.472 C 389.900 74.127, 398 59.500, 398 58.968 C 398 58.380, 391.007 58, 380.210 58 L 362.420 58 348.060 83.138 C 334.400 107.052, 333.637 108.160, 332.399 105.888 C 331.682 104.575, 325.287 93.375, 318.187 81 L 305.277 58.500 287.139 58.227 C 277.162 58.078, 269 58.186, 269 58.469 C 269 58.751, 277.154 73.274, 287.120 90.741 C 297.086 108.209, 305.261 122.983, 305.287 123.574 C 305.312 124.164, 297.318 138.789, 287.522 156.074 C 277.725 173.358, 269.524 187.838, 269.296 188.250 C 269.068 188.662, 276.977 189, 286.870 189 L 304.859 189 319.163 163.959 C 331.321 142.674, 333.647 139.165, 334.670 140.563 C 335.331 141.468, 341.864 152.737, 349.186 165.606 L 362.500 189.003 402.442 188.752 L 442.384 188.500 472.080 136.695 C 488.413 108.203, 501.939 84.729, 502.138 84.531 C 502.337 84.333, 516 107.757, 532.500 136.583 L 562.500 188.994 584.750 188.997 C 596.987 188.999, 607 188.854, 607 188.675 C 607 188.319, 503.014 6.686, 502.532 6.201 C 502.369 6.037, 478.904 46.649, 450.387 96.451 M 717.500 55.017 C 697.175 57.389, 680.641 68.752, 672.036 86.264 C 665.866 98.818, 663.840 108.024, 663.840 123.500 C 663.840 133.409, 664.328 138.355, 665.892 144.300 C 672.076 167.810, 689.191 184.757, 711.529 189.489 C 730.549 193.518, 747.264 189.540, 761.282 177.648 C 764.944 174.542, 768.178 172, 768.470 172 C 769.345 172, 769.030 185.018, 767.979 192.285 C 765.618 208.613, 755.148 218.672, 738.473 220.632 C 732.586 221.324, 723.136 219.599, 717.709 216.843 C 712.693 214.295, 706.636 207.946, 703.972 202.443 C 702.789 199.999, 701.749 198, 701.661 198.001 C 700.201 198.007, 665.596 205.071, 665.230 205.437 C 664.326 206.341, 669.156 218.313, 672.607 223.721 C 682.146 238.672, 700.008 248.257, 723.500 251.033 C 758.132 255.124, 788.930 241.875, 800.287 218 C 806.923 204.047, 807 203.001, 807 126.542 L 807 58 788.500 58 L 770 58 770 66.500 C 770 71.175, 769.641 75, 769.203 75 C 768.765 75, 766.627 73.142, 764.453 70.872 C 752.805 58.708, 735.614 52.903, 717.500 55.017 M 878.500 55.031 C 850.107 59.412, 830.755 75.064, 822.285 100.500 C 819.905 107.644, 819.614 110.051, 819.565 123 C 819.501 139.581, 820.786 145.825, 826.763 158 C 832.104 168.880, 842.842 179.424, 854.077 184.822 C 867.651 191.343, 873.132 192.462, 891.500 192.462 C 909.868 192.462, 915.349 191.343, 928.923 184.822 C 944.653 177.265, 956.667 162.306, 961.710 144 C 963.074 139.048, 963.481 134.047, 963.418 123 C 963.346 110.140, 963.038 107.595, 960.694 100.500 C 953.325 78.193, 936.695 62.854, 913.500 56.969 C 904.262 54.625, 887.240 53.682, 878.500 55.031 M 134.015 102.750 C 134.027 139.250, 134.310 148.689, 135.549 153.947 C 141.139 177.663, 159.815 193, 183.105 193 C 196.453 193, 210.163 186.171, 217.936 175.650 C 219.826 173.093, 221.738 171, 222.186 171 C 222.634 171, 223 175.050, 223 180 L 223 189 242.507 189 L 262.014 189 261.757 123.750 L 261.500 58.500 242.500 58.500 L 223.500 58.500 223 99.500 C 222.513 139.406, 222.441 140.610, 220.289 144.634 C 209.729 164.377, 183.014 164.880, 174.299 145.500 C 172.677 141.895, 172.469 137.382, 172.190 99.750 L 171.880 58 152.940 58 L 134 58 134.015 102.750 M 729.932 87.998 C 719.311 89.821, 710.155 97.164, 705.141 107.880 C 702.851 112.772, 702.510 114.798, 702.505 123.521 C 702.500 132.834, 702.732 134.014, 705.787 140.218 C 709.761 148.290, 715.989 154.097, 723.853 157.063 C 731.409 159.912, 744.359 159.466, 750.795 156.136 C 767.289 147.601, 774.103 127.713, 767.019 108.780 C 761.433 93.851, 746.051 85.232, 729.932 87.998 M 883.972 88.348 C 865.749 92.310, 854.873 109.966, 858.119 130.316 C 860.933 147.963, 872.346 158.830, 889.084 159.799 C 900.714 160.472, 906.867 158.234, 914.650 150.497 C 919.856 145.323, 921.123 143.340, 922.900 137.587 C 925.412 129.456, 925.655 118.570, 923.489 111.275 C 918.479 94.405, 901.478 84.541, 883.972 88.348"/></svg>';
30038
+ // src/renderers/native/chrome/vela-logos.ts
30039
+ var VELA_MARK_SVG = '<svg viewBox="0 -142 161.66 148" aria-hidden="true"><g fill="currentColor"><path d="M0 -140L32.91 -140L77.52 -62.73L61.06 -34.24Z"/><path d="M161.66 -140L128.75 -140L84.14 -62.73L100.59 -34.24Z"/><path d="M80.83 -52.04L95.85 -26.02L80.83 0L65.81 -26.02Z"/></g></svg>';
30040
+ var VELA_LETTERS_SVG = '<svg viewBox="161.66 -142 310.57 148" aria-hidden="true"><g fill="currentColor"><path d="M226.3 1.17Q200.81 1.17 186.12 -12.99Q171.42 -27.15 171.42 -51.17Q171.42 -75.2 185.87 -89.6Q200.33 -104 224.25 -104Q247.4 -104 261.7 -90.58Q276.01 -77.15 276.3 -54.59Q276.3 -49.9 275.42 -45.02H200.81V-43.55Q201.2 -32.62 207.75 -26.42Q214.29 -20.21 225.03 -20.21Q233.72 -20.21 239.34 -23.93Q244.95 -27.64 246.81 -34.77H274.64Q272.1 -19.14 259.16 -8.98Q246.22 1.17 226.3 1.17ZM201.5 -63.38H247.88Q246.42 -72.75 240.27 -77.88Q234.12 -83.01 224.45 -83.01Q215.17 -83.01 208.92 -77.73Q202.67 -72.46 201.5 -63.38ZM287.54 0V-140.04H315.76V0ZM429.34 -23.83H432.56V0H418.11Q407.76 0 403.36 -4.44Q398.97 -8.89 399.16 -16.8Q387.83 1.17 365.57 1.17Q348.48 1.17 337.88 -6.74Q327.29 -14.65 327.29 -29Q327.29 -45.41 339.45 -54.3Q351.6 -63.18 374.55 -63.18H394.96V-68.16Q394.96 -75.2 390.08 -79.3Q385.2 -83.4 376.51 -83.4Q368.3 -83.4 363.03 -79.98Q357.76 -76.56 356.88 -70.8H329.73Q331.19 -85.84 344.08 -94.92Q356.97 -104 377.29 -104Q399.06 -104 410.88 -94.43Q422.7 -84.86 422.7 -66.6V-30.18Q422.7 -26.37 424.26 -25.1Q425.82 -23.83 429.34 -23.83ZM394.96 -42.38V-44.43H374.36Q365.47 -44.43 360.59 -40.92Q355.7 -37.4 355.7 -30.96Q355.7 -25.59 359.81 -22.41Q363.91 -19.24 370.94 -19.24Q381.97 -19.24 388.37 -25.54Q394.77 -31.84 394.96 -42.38Z"/><path fill-opacity="0.55" d="M437.44 -124.28H441.96V-113.41H445.34V-124.28H449.87V-127.4H437.44ZM451.43 -113.41H454.7V-123.29L458.97 -113.41H461.62L465.99 -123.29V-113.41H469.21V-127.4H464.53L460.42 -117.68L456.16 -127.4H451.43Z"/></g></svg>';
29779
30041
 
29780
30042
  // src/renderers/native/chrome/AttributionMark.ts
29781
- var ATTRIBUTION_URL = "https://luxalgo.com/vela";
30043
+ var ATTRIBUTION_URL = "https://velacharts.dev/?utm_source=vela&utm_medium=attribution";
29782
30044
  var STYLE_ID5 = "vela-attribution-styles";
29783
30045
  var CSS2 = `
29784
30046
  .vela-attribution {
29785
30047
  text-decoration: none;
29786
30048
  display: flex;
29787
30049
  align-items: center;
29788
- /* Gutter, wordmark size and offset below are the brand lockup's own ratios,
29789
- measured off the official horizontal logo: gutter 0.157, wordmark height
29790
- 0.966, and wordmark 0.109 LOWER than the symbol \u2014 box-centering the two
29791
- reads wrong because the symbol's ink hangs low and the wordmark descends. */
29792
- gap: 4px;
30050
+ /* No gutter: the V is the wordmark's first letter, and the letters' viewBox starts
30051
+ where the V's ends, so the kit's own letter spacing comes with them. */
30052
+ gap: 0;
29793
30053
  }
29794
30054
  .vela-attribution .vela-attr-symbol {
29795
30055
  flex: none;
@@ -29797,7 +30057,7 @@ var CSS2 = `
29797
30057
  line-height: 0;
29798
30058
  }
29799
30059
  .vela-attribution .vela-attr-symbol svg {
29800
- height: 28px;
30060
+ height: 24px;
29801
30061
  width: auto;
29802
30062
  display: block;
29803
30063
  filter: drop-shadow(0 1px 2px var(--vela-attr-shadow, rgba(0,0,0,0.45)));
@@ -29808,15 +30068,13 @@ var CSS2 = `
29808
30068
  opacity: 0;
29809
30069
  display: flex;
29810
30070
  align-items: center;
29811
- /* Offset here, not on the image: the clip box must not crop the descender. */
29812
- position: relative;
29813
- top: 3px;
30071
+ /* Same height and baseline as the V (one viewBox height), so no vertical offset. */
29814
30072
  transform: translateX(-8px);
29815
30073
  transition: max-width 0.3s ease, opacity 0.25s ease, transform 0.3s ease;
29816
30074
  flex: none;
29817
30075
  }
29818
30076
  .vela-attribution .vela-attr-wordmark svg {
29819
- height: 27px;
30077
+ height: 24px;
29820
30078
  width: auto;
29821
30079
  display: block;
29822
30080
  filter: drop-shadow(0 1px 2px var(--vela-attr-shadow, rgba(0,0,0,0.45)));
@@ -29831,14 +30089,11 @@ var CSS2 = `
29831
30089
  (data-layout): the workspace's single grid-wide mark lives OUTSIDE any renderer
29832
30090
  container, so only the shell attribute reaches it. */
29833
30091
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-symbol svg,
29834
- [data-layout='mobile'] .vela-attribution .vela-attr-symbol svg { height: 22px; }
30092
+ [data-layout='mobile'] .vela-attribution .vela-attr-symbol svg { height: 20px; }
29835
30093
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-wordmark,
29836
- [data-layout='mobile'] .vela-attribution .vela-attr-wordmark {
29837
- top: 2px;
29838
- transform: translateX(-6px);
29839
- }
30094
+ [data-layout='mobile'] .vela-attribution .vela-attr-wordmark { transform: translateX(-6px); }
29840
30095
  [data-vela-layout='mobile'] .vela-attribution .vela-attr-wordmark svg,
29841
- [data-layout='mobile'] .vela-attribution .vela-attr-wordmark svg { height: 21px; }
30096
+ [data-layout='mobile'] .vela-attribution .vela-attr-wordmark svg { height: 20px; }
29842
30097
  [data-vela-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark,
29843
30098
  [data-layout='mobile'] .vela-attribution:hover .vela-attr-wordmark { transform: translateX(0); }
29844
30099
  `;
@@ -29895,10 +30150,10 @@ function createAttributionMark(doc, background) {
29895
30150
  const symbol = doc.createElement("span");
29896
30151
  symbol.className = "vela-attr-symbol";
29897
30152
  symbol.setAttribute("aria-hidden", "true");
29898
- symbol.innerHTML = LUXALGO_SYMBOL_SVG;
30153
+ symbol.innerHTML = VELA_MARK_SVG;
29899
30154
  const wordmark = doc.createElement("span");
29900
30155
  wordmark.className = "vela-attr-wordmark";
29901
- wordmark.innerHTML = LUXALGO_WORDMARK_SVG;
30156
+ wordmark.innerHTML = VELA_LETTERS_SVG;
29902
30157
  a.append(symbol, wordmark);
29903
30158
  return a;
29904
30159
  }