@luxalgo/vela 0.8.2 → 0.8.4

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 (48) hide show
  1. package/dist/{DataProvider-COGDs9Xg.d.cts → DataProvider-Bw58CjYy.d.cts} +1 -1
  2. package/dist/{DataProvider-DpOWZD8F.d.ts → DataProvider-Byo8pEv0.d.ts} +1 -1
  3. package/dist/{chunk-DPAA3SRT.js → chunk-ALTGHKIM.js} +836 -32
  4. package/dist/{chunk-27UTYE7T.js → chunk-AUOB7A5Y.js} +49 -7
  5. package/dist/{chunk-HKNO24GD.js → chunk-I2U4NZD2.js} +56 -1
  6. package/dist/{chunk-R2JMNIHY.js → chunk-MOJ4T26N.js} +341 -169
  7. package/dist/{chunk-T3DZIR5Y.js → chunk-UNUVPQFW.js} +2214 -886
  8. package/dist/{chunk-NLDTVFOI.js → chunk-WY5H4S4I.js} +129 -10
  9. package/dist/{contributions-Cgm9RJYP.d.cts → contributions-BY3zSYiZ.d.ts} +98 -7
  10. package/dist/{contributions-bWgwyiVi.d.ts → contributions-DOm40g5-.d.cts} +98 -7
  11. package/dist/index.cjs +3066 -893
  12. package/dist/index.d.cts +36 -22
  13. package/dist/index.d.ts +36 -22
  14. package/dist/index.js +5 -5
  15. package/dist/{options-NAUHiQEC.d.ts → options-CSXbjMM5.d.cts} +129 -4
  16. package/dist/{options-NAUHiQEC.d.cts → options-CSXbjMM5.d.ts} +129 -4
  17. package/dist/{plugin-CqxWoDKS.d.ts → plugin-BDxUhGO3.d.ts} +4 -4
  18. package/dist/{plugin-CjjzZX2F.d.cts → plugin-CiQVyETg.d.cts} +4 -4
  19. package/dist/plugin.cjs +573 -17
  20. package/dist/plugin.d.cts +5 -5
  21. package/dist/plugin.d.ts +5 -5
  22. package/dist/plugin.js +3 -3
  23. package/dist/providers/binance.d.cts +2 -2
  24. package/dist/providers/binance.d.ts +2 -2
  25. package/dist/providers/coinbase.d.cts +2 -2
  26. package/dist/providers/coinbase.d.ts +2 -2
  27. package/dist/providers/hyperliquid.d.cts +2 -2
  28. package/dist/providers/hyperliquid.d.ts +2 -2
  29. package/dist/{side-panel-wMNfgyZb.d.cts → side-panel-_2UK1gSm.d.cts} +45 -3
  30. package/dist/{side-panel-wMNfgyZb.d.ts → side-panel-_2UK1gSm.d.ts} +45 -3
  31. package/dist/{statusline-model-Dih6FGWP.d.ts → statusline-model-C9tYXuux.d.ts} +19 -4
  32. package/dist/{statusline-model-Dpfpq54A.d.cts → statusline-model-DUBX6Fib.d.cts} +19 -4
  33. package/dist/ui.cjs +224 -14
  34. package/dist/ui.d.cts +119 -87
  35. package/dist/ui.d.ts +119 -87
  36. package/dist/ui.js +3 -3
  37. package/dist/vela.global.js +3073 -900
  38. package/dist/vela.global.min.js +158 -54
  39. package/dist/widget.cjs +3640 -1119
  40. package/dist/widget.d.cts +24 -10
  41. package/dist/widget.d.ts +24 -10
  42. package/dist/widget.js +7 -7
  43. package/dist/workspace.cjs +3639 -1119
  44. package/dist/workspace.d.cts +61 -20
  45. package/dist/workspace.d.ts +61 -20
  46. package/dist/workspace.js +6 -6
  47. package/package.json +1 -1
  48. /package/dist/{chunk-JU4CWEBF.js → chunk-W5YPYZ2P.js} +0 -0
package/dist/plugin.cjs CHANGED
@@ -223,6 +223,23 @@ function resolveEngines(overrides) {
223
223
  return { ...Object.fromEntries(defaultEngines), ...overrides };
224
224
  }
225
225
 
226
+ // src/widget/context-menu-model.ts
227
+ var CONTEXT_MENU_BUILTIN_ORDER = {
228
+ body: { "reset-view": -30, "remove-drawings": -20, "remove-indicators": -10, settings: 1e3 },
229
+ "price-axis": {
230
+ auto: -80,
231
+ invert: -70,
232
+ "scale:regular": -60,
233
+ "scale:percent": -50,
234
+ "scale:indexed": -40,
235
+ "scale:log": -30,
236
+ labels: -20,
237
+ levels: -10,
238
+ settings: 1e3
239
+ },
240
+ "time-axis": { timezone: -10, settings: 1e3 }
241
+ };
242
+
226
243
  // src/core/icons.ts
227
244
  var registry4 = /* @__PURE__ */ new Map();
228
245
  function registerIcon(id, svg) {
@@ -436,6 +453,28 @@ function defaultText(value = "") {
436
453
  return { value, size: "normal", hAlign: "left", vAlign: "top" };
437
454
  }
438
455
 
456
+ // src/core/drawings/visibility.ts
457
+ var MIN = 6e4;
458
+ var HOUR = 60 * MIN;
459
+ var DAY = 24 * HOUR;
460
+ var TIMEFRAME_BANDS = [
461
+ { key: "s", label: "Sec", fromMs: 0 },
462
+ { key: "1", label: "1m", fromMs: MIN },
463
+ { key: "5", label: "5m", fromMs: 5 * MIN },
464
+ { key: "15", label: "15m", fromMs: 15 * MIN },
465
+ { key: "60", label: "1H", fromMs: HOUR },
466
+ { key: "240", label: "4H", fromMs: 4 * HOUR },
467
+ { key: "D", label: "D", fromMs: DAY },
468
+ { key: "W", label: "W", fromMs: 7 * DAY },
469
+ { key: "M", label: "M", fromMs: 28 * DAY }
470
+ ];
471
+ var BAND_KEYS = new Set(TIMEFRAME_BANDS.map((b) => b.key));
472
+ function sanitizeShowOn(v) {
473
+ if (!Array.isArray(v)) return void 0;
474
+ const wanted = new Set(v.filter((k) => typeof k === "string" && BAND_KEYS.has(k)));
475
+ return TIMEFRAME_BANDS.map((b) => b.key).filter((k) => wanted.has(k));
476
+ }
477
+
439
478
  // src/core/drawings/Drawing.ts
440
479
  var _Drawing = class _Drawing {
441
480
  // Public so the registry can construct concrete subclasses; `abstract` still
@@ -452,6 +491,7 @@ var _Drawing = class _Drawing {
452
491
  this.locked = init.locked ?? false;
453
492
  this.visible = init.visible ?? true;
454
493
  this.zIndex = init.zIndex ?? 0;
494
+ this.showOn = sanitizeShowOn(init.showOn);
455
495
  this.createdAt = init.createdAt ?? Date.now();
456
496
  if (init.props) this.readProps(init.props);
457
497
  }
@@ -538,7 +578,10 @@ var _Drawing = class _Drawing {
538
578
  visible: this.visible,
539
579
  zIndex: this.zIndex,
540
580
  createdAt: this.createdAt,
541
- props: this.writeProps()
581
+ props: this.writeProps(),
582
+ // Always present (undefined = every timeframe) so an edit can lift a limit; JSON
583
+ // drops the undefined value, so unlimited drawings save as before.
584
+ showOn: this.showOn ? [...this.showOn] : void 0
542
585
  };
543
586
  }
544
587
  /** Per-type extras to serialize into `props` (override in subclasses). */
@@ -673,9 +716,16 @@ var TrendLine = class extends Drawing {
673
716
  if (y1 == null || y2 == null) return null;
674
717
  return [proj.xOf(a.time), y1, proj.xOf(b.time), y2];
675
718
  }
719
+ /** Which ends run on past the anchors. */
720
+ extension() {
721
+ const { extendLeft: l, extendRight: r } = this.style;
722
+ return l && r ? "both" : l ? "left" : r ? "right" : "none";
723
+ }
676
724
  hitTest(px, py, proj, tol) {
677
725
  const p = this.pixels(proj);
678
- return p != null && distToSegment(px, py, p[0], p[1], p[2], p[3]) <= tol;
726
+ if (!p) return false;
727
+ const [x1, y1, x2, y2] = extendRay(p[0], p[1], p[2], p[3], this.extension(), proj.width, proj.height);
728
+ return distToSegment(px, py, x1, y1, x2, y2) <= tol;
679
729
  }
680
730
  handlePoints(proj) {
681
731
  const p = this.pixels(proj);
@@ -702,6 +752,8 @@ var TrendLine = class extends Drawing {
702
752
  fields: [
703
753
  ...LINE_FIELDS,
704
754
  { path: "style.arrowRight", label: "Arrow", kind: "boolean", group: "line" },
755
+ { path: "style.extendLeft", label: "Extend left", kind: "boolean", group: "line" },
756
+ { path: "style.extendRight", label: "Extend right", kind: "boolean", group: "line" },
705
757
  ...TEXT_FIELDS
706
758
  ]
707
759
  };
@@ -3161,6 +3213,14 @@ var FibRatios = class extends Drawing {
3161
3213
 
3162
3214
  // src/core/drawings/types/FibLevels.ts
3163
3215
  var FibLevels = class extends FibRatios {
3216
+ // `declare`d: the base constructor reads saved props before these could be initialized.
3217
+ constructor(init) {
3218
+ super(init);
3219
+ if (this.showRatios === void 0) this.showRatios = true;
3220
+ if (this.showPrices === void 0) this.showPrices = true;
3221
+ if (this.labelSide === void 0) this.labelSide = "left";
3222
+ if (this.background === void 0) this.background = true;
3223
+ }
3164
3224
  /** Price of a level for the two anchor prices; subclasses override to change which anchor ratio 0 sits on. */
3165
3225
  levelPrice(ratio, p1, p2) {
3166
3226
  return p1 + ratio * (p2 - p1);
@@ -3172,8 +3232,8 @@ var FibLevels = class extends FibRatios {
3172
3232
  if (!a || !b) return null;
3173
3233
  const xa = proj.xOf(a.time);
3174
3234
  const xb = proj.xOf(b.time);
3175
- const x1 = Math.min(xa, xb);
3176
- const x2 = Math.max(xa, xb);
3235
+ const x1 = this.style.extendLeft ? 0 : Math.min(xa, xb);
3236
+ const x2 = this.style.extendRight ? proj.width : Math.max(xa, xb);
3177
3237
  const out = [];
3178
3238
  for (const lv of this.levels) {
3179
3239
  if (!lv.enabled) continue;
@@ -3187,6 +3247,7 @@ var FibLevels = class extends FibRatios {
3187
3247
  entryLines(proj) {
3188
3248
  const lines = this.levelLines(proj);
3189
3249
  if (!lines) return null;
3250
+ const right = this.labelSide === "right";
3190
3251
  return lines.map((l) => ({
3191
3252
  color: l.color,
3192
3253
  label: l.label,
@@ -3194,16 +3255,24 @@ var FibLevels = class extends FibRatios {
3194
3255
  y1: l.y,
3195
3256
  x2: l.x2,
3196
3257
  y2: l.y,
3197
- numberText: `${l.ratio} (${l.price.toFixed(2)})`,
3198
- numberX: l.x1 + 4,
3258
+ numberText: this.levelText(l.ratio, l.price),
3259
+ numberX: right ? l.x2 - 4 : l.x1 + 4,
3199
3260
  numberY: l.y - 7,
3200
- numberAlign: "left",
3261
+ numberAlign: right ? "right" : "left",
3201
3262
  labelX: (l.x1 + l.x2) / 2,
3202
3263
  labelY: l.y - 7
3203
3264
  }));
3204
3265
  }
3266
+ /** The text beside a level: its ratio, its price, both, or nothing. */
3267
+ levelText(ratio, price) {
3268
+ const p = price.toFixed(2);
3269
+ if (this.showRatios && this.showPrices) return `${ratio} (${p})`;
3270
+ if (this.showRatios) return String(ratio);
3271
+ return this.showPrices ? p : "";
3272
+ }
3205
3273
  fillBands(proj) {
3206
- const lines = this.levelLines(proj);
3274
+ if (!this.background) return [];
3275
+ const lines = this.levelLines(proj)?.sort((p, q) => p.ratio - q.ratio);
3207
3276
  if (!lines) return [];
3208
3277
  const bands = [];
3209
3278
  for (let i = 1; i < lines.length; i += 1) {
@@ -3221,6 +3290,26 @@ var FibLevels = class extends FibRatios {
3221
3290
  if (prices.length === 0) return null;
3222
3291
  return { min: Math.min(...prices), max: Math.max(...prices) };
3223
3292
  }
3293
+ schema() {
3294
+ const base = super.schema();
3295
+ return {
3296
+ fields: [
3297
+ ...base.fields,
3298
+ { path: "style.extendLeft", label: "Extend left", kind: "boolean", group: "line" },
3299
+ { path: "style.extendRight", label: "Extend right", kind: "boolean", group: "line" }
3300
+ ]
3301
+ };
3302
+ }
3303
+ writeProps() {
3304
+ return { ...super.writeProps(), showRatios: this.showRatios, showPrices: this.showPrices, labelSide: this.labelSide, background: this.background };
3305
+ }
3306
+ readProps(props) {
3307
+ super.readProps(props);
3308
+ if (typeof props.showRatios === "boolean") this.showRatios = props.showRatios;
3309
+ if (typeof props.showPrices === "boolean") this.showPrices = props.showPrices;
3310
+ if (props.labelSide === "left" || props.labelSide === "right") this.labelSide = props.labelSide;
3311
+ if (typeof props.background === "boolean") this.background = props.background;
3312
+ }
3224
3313
  };
3225
3314
 
3226
3315
  // src/core/drawings/levelPalette.ts
@@ -3266,14 +3355,31 @@ var RATIO_COLORS = [
3266
3355
  [0.75, LEVEL_BLUE],
3267
3356
  [0.786, LEVEL_BLUE],
3268
3357
  [1, LEVEL_ANCHOR],
3358
+ [0.705, LEVEL_AMBER],
3359
+ [0.886, LEVEL_PURPLE],
3269
3360
  [1.272, LEVEL_BLUE],
3270
3361
  [1.382, LEVEL_ORANGE],
3362
+ [1.414, LEVEL_AQUA],
3271
3363
  [1.618, LEVEL_RED],
3272
3364
  [2, LEVEL_ANCHOR],
3365
+ [2.272, LEVEL_VIOLET],
3273
3366
  [2.382, LEVEL_ORANGE],
3367
+ [2.414, LEVEL_PINK],
3274
3368
  [2.618, LEVEL_ORANGE],
3275
3369
  [3, LEVEL_ANCHOR],
3370
+ [3.272, LEVEL_CORAL],
3371
+ [3.618, LEVEL_PURPLE],
3276
3372
  [4.236, LEVEL_GREEN],
3373
+ [4.618, LEVEL_PINK],
3374
+ // Beyond the swing's end (targets): blues, deepening with distance.
3375
+ [-0.27, LEVEL_CYAN],
3376
+ [-0.272, LEVEL_CYAN],
3377
+ [-0.62, LEVEL_BLUE],
3378
+ [-0.618, LEVEL_BLUE],
3379
+ [-1, LEVEL_AQUA],
3380
+ [-1.5, LEVEL_VIOLET],
3381
+ [-2, LEVEL_PURPLE],
3382
+ [-2.5, LEVEL_PINK],
3277
3383
  [6.854, LEVEL_TEAL],
3278
3384
  [11.09, LEVEL_BLUE]
3279
3385
  ];
@@ -3300,8 +3406,36 @@ function cycleLevels(count, enabledCount = count) {
3300
3406
  }));
3301
3407
  }
3302
3408
 
3409
+ // src/core/drawings/fibPresets.ts
3410
+ var LADDER = [0, 0.236, 0.382, 0.5, 0.618, 0.705, 0.786, 0.886, 1, 1.272, 1.414, 1.618, 2, 2.272, 2.414, 2.618, 3, 3.272, 3.618, 4.236, 4.618, -0.272, -0.618, -1];
3411
+ var OTE_LADDER = [0, 0.236, 0.382, 0.5, 0.62, 0.705, 0.79, 0.886, 1, 1.272, 1.414, 1.618, 2, 2.618, 3, 3.618, 4.236, 4.618, -0.27, -0.62, -1, -1.5, -2, -2.5];
3412
+ function ladder(ratios, on, colors = {}) {
3413
+ return fibLevels(ratios.map((ratio) => ({ ratio, enabled: on.includes(ratio), color: colors[String(ratio)] })));
3414
+ }
3415
+ var FIB_PRESETS = [
3416
+ {
3417
+ key: "classic",
3418
+ label: "Classic",
3419
+ reverse: false,
3420
+ levels: ladder(LADDER, [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1])
3421
+ },
3422
+ {
3423
+ key: "extension",
3424
+ label: "Extension",
3425
+ reverse: true,
3426
+ levels: ladder(LADDER, [0, 1, 1.272, 1.414, 1.618, 2, 2.618, 3.618, 4.236])
3427
+ },
3428
+ {
3429
+ key: "ote",
3430
+ label: "OTE",
3431
+ reverse: false,
3432
+ // The entry zone reads as one band.
3433
+ levels: ladder(OTE_LADDER, [0, 0.5, 0.62, 0.705, 0.79, 1, -0.27, -0.62], { "0.62": LEVEL_AMBER, "0.79": LEVEL_AMBER })
3434
+ }
3435
+ ];
3436
+
3303
3437
  // src/core/drawings/types/FibRetracement.ts
3304
- var LEVELS = fibLevels([0, 0.236, 0.382, 0.5, 0.618, 0.786, 1]);
3438
+ var LEVELS = FIB_PRESETS[0].levels;
3305
3439
  var FibRetracement = class extends FibLevels {
3306
3440
  constructor(init) {
3307
3441
  super(init);
@@ -3324,7 +3458,11 @@ var FibRetracement = class extends FibLevels {
3324
3458
  };
3325
3459
 
3326
3460
  // src/core/drawings/types/FibExtension.ts
3327
- var LEVELS2 = fibLevels([0, 0.382, 0.618, 1, 1.272, 1.618, { ratio: 2.618, color: LEVEL_PURPLE }]);
3461
+ var LEVELS2 = ladder(
3462
+ [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.272, 1.414, 1.618, 2, 2.272, 2.414, 2.618, 3, 3.272, 3.618, 4.236, 4.618],
3463
+ [0, 0.382, 0.618, 1, 1.272, 1.618, 2.618],
3464
+ { "2.618": LEVEL_PURPLE }
3465
+ );
3328
3466
  var FibExtension = class extends FibLevels {
3329
3467
  constructor() {
3330
3468
  super(...arguments);
@@ -4902,20 +5040,20 @@ function signed(n) {
4902
5040
  }
4903
5041
  function formatDuration(ms) {
4904
5042
  const m = Math.abs(ms);
4905
- const DAY = 864e5;
5043
+ const DAY2 = 864e5;
4906
5044
  const HR = 36e5;
4907
- const MIN = 6e4;
4908
- if (m >= DAY) {
4909
- const d = Math.floor(m / DAY);
4910
- const h = Math.round(m % DAY / HR);
5045
+ const MIN2 = 6e4;
5046
+ if (m >= DAY2) {
5047
+ const d = Math.floor(m / DAY2);
5048
+ const h = Math.round(m % DAY2 / HR);
4911
5049
  return h ? `${d}d ${h}h` : `${d}d`;
4912
5050
  }
4913
5051
  if (m >= HR) {
4914
5052
  const h = Math.floor(m / HR);
4915
- const mn = Math.round(m % HR / MIN);
5053
+ const mn = Math.round(m % HR / MIN2);
4916
5054
  return mn ? `${h}h ${mn}m` : `${h}h`;
4917
5055
  }
4918
- if (m >= MIN) return `${Math.round(m / MIN)}m`;
5056
+ if (m >= MIN2) return `${Math.round(m / MIN2)}m`;
4919
5057
  return `${Math.round(m / 1e3)}s`;
4920
5058
  }
4921
5059
  var MeasureBox = class extends Drawing {
@@ -5478,6 +5616,393 @@ var Magnifier = class extends Drawing {
5478
5616
  }
5479
5617
  };
5480
5618
 
5619
+ // src/core/drawings/priceAction.ts
5620
+ var GAP_REACH = 3;
5621
+ function nearestBarIndex(bars, time) {
5622
+ if (bars.length === 0) return -1;
5623
+ let lo = 0;
5624
+ let hi = bars.length - 1;
5625
+ while (lo < hi) {
5626
+ const mid = lo + hi >> 1;
5627
+ if (bars[mid].time < time) lo = mid + 1;
5628
+ else hi = mid;
5629
+ }
5630
+ if (lo > 0 && Math.abs(bars[lo - 1].time - time) <= Math.abs(bars[lo].time - time)) return lo - 1;
5631
+ return lo;
5632
+ }
5633
+ function gapAt(bars, i) {
5634
+ const prev = bars[i - 1];
5635
+ const next = bars[i + 1];
5636
+ if (!prev || !next || !bars[i]) return null;
5637
+ if (next.low > prev.high) return { bull: true, top: next.low, bottom: prev.high };
5638
+ if (next.high < prev.low) return { bull: false, top: prev.low, bottom: next.high };
5639
+ return null;
5640
+ }
5641
+ function nearestGap(bars, i) {
5642
+ for (let k = 0; k <= GAP_REACH; k += 1) {
5643
+ for (const j of k === 0 ? [i] : [i - k, i + k]) {
5644
+ const g = gapAt(bars, j);
5645
+ if (g) return { index: j, ...g };
5646
+ }
5647
+ }
5648
+ return null;
5649
+ }
5650
+ function gapState(bars, i) {
5651
+ const g = nearestGap(bars, i);
5652
+ if (!g) return null;
5653
+ const { index, bull, top, bottom } = g;
5654
+ const mid = (top + bottom) / 2;
5655
+ let tested = null;
5656
+ let half = null;
5657
+ let filled = null;
5658
+ let inverted = null;
5659
+ for (let j = index + 2; j < bars.length && inverted === null; j += 1) {
5660
+ const b = bars[j];
5661
+ const reach = bull ? b.low : b.high;
5662
+ const into = (v) => bull ? reach <= v : reach >= v;
5663
+ if (tested === null && into(bull ? top : bottom)) tested = j;
5664
+ if (half === null && into(mid)) half = j;
5665
+ if (filled === null && into(bull ? bottom : top)) filled = j;
5666
+ if (filled !== null && (bull ? b.close < bottom : b.close > top)) inverted = j;
5667
+ }
5668
+ return { index, bull, top, bottom, mid, tested, half, filled, inverted };
5669
+ }
5670
+ function blockState(bars, i, range) {
5671
+ const c = bars[i];
5672
+ if (!c) return null;
5673
+ const bull = c.close < c.open;
5674
+ const top = range === "body" ? Math.max(c.open, c.close) : c.high;
5675
+ const bottom = range === "body" ? Math.min(c.open, c.close) : c.low;
5676
+ const mid = (top + bottom) / 2;
5677
+ let left = false;
5678
+ let tested = null;
5679
+ let half = null;
5680
+ let broken = null;
5681
+ let leftAfterBreak = false;
5682
+ let retest = null;
5683
+ for (let j = i + 1; j < bars.length && retest === null; j += 1) {
5684
+ const b = bars[j];
5685
+ if (broken === null) {
5686
+ if (!left) left = bull ? b.low > top : b.high < bottom;
5687
+ else {
5688
+ if (tested === null && (bull ? b.low <= top : b.high >= bottom)) tested = j;
5689
+ if (half === null && (bull ? b.low <= mid : b.high >= mid)) half = j;
5690
+ }
5691
+ if (bull ? b.close < bottom : b.close > top) broken = j;
5692
+ } else if (!leftAfterBreak) leftAfterBreak = bull ? b.high < bottom : b.low > top;
5693
+ else if (bull ? b.high >= bottom : b.low <= top) retest = j;
5694
+ }
5695
+ return { index: i, bull, top, bottom, mid, tested, half, broken, retest };
5696
+ }
5697
+ function liquidityState(bars, i, side) {
5698
+ const c = bars[i];
5699
+ if (!c) return null;
5700
+ const high = side === "high";
5701
+ const price = high ? c.high : c.low;
5702
+ let swept = null;
5703
+ let broken = null;
5704
+ for (let j = i + 1; j < bars.length && broken === null; j += 1) {
5705
+ const b = bars[j];
5706
+ if (high ? b.close > price : b.close < price) broken = j;
5707
+ else if (swept === null && (high ? b.high > price : b.low < price)) swept = j;
5708
+ }
5709
+ return { index: i, high, price, swept, broken };
5710
+ }
5711
+
5712
+ // src/core/drawings/types/PriceActionZone.ts
5713
+ var candlesSeen = /* @__PURE__ */ new WeakMap();
5714
+ var PriceActionTool = class extends Drawing {
5715
+ anchorSchema() {
5716
+ return { min: 1, max: 1, slots: [{ role: "candle", free: "x" }] };
5717
+ }
5718
+ /** The chart's candles, and the one nearest the anchor. */
5719
+ candles(proj) {
5720
+ const a = this.anchors[0];
5721
+ if (!a) return null;
5722
+ let bars = candlesSeen.get(proj);
5723
+ if (!bars) {
5724
+ bars = proj.barsInRange?.(Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY) ?? [];
5725
+ candlesSeen.set(proj, bars);
5726
+ }
5727
+ return bars.length > 0 ? { bars, at: nearestBarIndex(bars, a.time) } : null;
5728
+ }
5729
+ /** Half a candle's width at bar `i`, in pixels. */
5730
+ halfBar(proj, bars, i) {
5731
+ const a = bars[Math.max(0, i - 1)];
5732
+ const b = bars[Math.min(bars.length - 1, Math.max(1, i))];
5733
+ if (!a || !b || a === b) return 3;
5734
+ return Math.max(1, Math.abs(proj.xOf(b.time) - proj.xOf(a.time)) / 2);
5735
+ }
5736
+ /** Pixel x for the right edge of a zone that stops at bar `end`, or runs on to the chart's edge. */
5737
+ rightEdge(proj, bars, end) {
5738
+ if (end === null) return Math.max(proj.width, proj.xOf(bars[bars.length - 1].time));
5739
+ return proj.xOf(bars[end].time) + this.halfBar(proj, bars, end);
5740
+ }
5741
+ /** The anchor itself, for a tool with nothing on the candles to hang its handle on. */
5742
+ anchorHandle(proj) {
5743
+ const a = this.anchors[0];
5744
+ const y = a ? proj.yOf(a.price, this.paneId) : null;
5745
+ return a && y != null ? [[proj.xOf(a.time), y]] : [];
5746
+ }
5747
+ hitHandle(px, py, proj, tol) {
5748
+ return handleAt(px, py, this.handlePoints(proj), tol + 3);
5749
+ }
5750
+ priceRange() {
5751
+ return null;
5752
+ }
5753
+ };
5754
+ var PriceActionZone = class extends PriceActionTool {
5755
+ /** The zone's pixel layout, or null when the candles don't form one. */
5756
+ zone(proj) {
5757
+ const c = this.candles(proj);
5758
+ const f = c && this.formation(c.bars, c.at);
5759
+ if (!c || !f) return null;
5760
+ const yt = proj.yOf(f.top, this.paneId);
5761
+ const yb = proj.yOf(f.bottom, this.paneId);
5762
+ if (yt == null || yb == null) return null;
5763
+ const { bars } = c;
5764
+ const s = this.settings();
5765
+ const left = (i) => proj.xOf(bars[i].time) - this.halfBar(proj, bars, i);
5766
+ const yTop = Math.min(yt, yb);
5767
+ const yBottom = Math.max(yt, yb);
5768
+ const midY = proj.yOf(f.mid, this.paneId) ?? (yTop + yBottom) / 2;
5769
+ const flip = f.flip && { x1: left(f.flip.from), x2: this.rightEdge(proj, bars, f.flip.to), fill: f.bull ? s.downColor : s.upColor };
5770
+ const edge = s.extend === "tested" ? f.bull ? f.top : f.bottom : s.extend === "half" ? f.mid : f.bull ? f.bottom : f.top;
5771
+ const edgeY = proj.yOf(edge, this.paneId);
5772
+ return {
5773
+ x1: left(f.start),
5774
+ x2: this.rightEdge(proj, bars, f.end),
5775
+ yTop,
5776
+ yBottom,
5777
+ fill: f.bull ? s.upColor : s.downColor,
5778
+ border: s.border,
5779
+ midY: s.midline ? midY : null,
5780
+ faded: f.end !== null && s.fade && !flip,
5781
+ marker: f.end !== null && !flip && edgeY != null ? { x: proj.xOf(bars[f.end].time), y: edgeY } : null,
5782
+ flip,
5783
+ handle: [proj.xOf(bars[f.index].time), midY]
5784
+ };
5785
+ }
5786
+ hitTest(px, py, proj, tol) {
5787
+ const z = this.zone(proj);
5788
+ if (!z) {
5789
+ const a = this.anchors[0];
5790
+ return a != null && Math.abs(px - proj.xOf(a.time)) <= tol;
5791
+ }
5792
+ const inY = py >= z.yTop - tol && py <= z.yBottom + tol;
5793
+ if (inY && px >= z.x1 - tol && px <= z.x2 + tol) return true;
5794
+ return z.flip != null && inY && px >= z.flip.x1 - tol && px <= z.flip.x2 + tol;
5795
+ }
5796
+ handlePoints(proj) {
5797
+ const z = this.zone(proj);
5798
+ return z ? [z.handle] : this.anchorHandle(proj);
5799
+ }
5800
+ bounds(proj) {
5801
+ const z = this.zone(proj);
5802
+ if (!z) return null;
5803
+ const right = Math.min(proj.width, z.flip ? z.flip.x2 : z.x2);
5804
+ return { x: z.x1, y: z.yTop, w: Math.max(1, right - z.x1), h: Math.max(1, z.yBottom - z.yTop) };
5805
+ }
5806
+ };
5807
+
5808
+ // src/core/drawings/types/FairValueGap.ts
5809
+ var EXTEND_OPTIONS = [
5810
+ { value: "tested", label: "Until tested" },
5811
+ { value: "half", label: "Until 50%" },
5812
+ { value: "filled", label: "Until filled" },
5813
+ { value: "always", label: "Always" }
5814
+ ];
5815
+ function defaults2() {
5816
+ return { extend: "filled", upColor: `${BULLISH}33`, downColor: `${BEARISH}33`, midline: true, border: false, fade: true, inverse: false };
5817
+ }
5818
+ var FairValueGap = class extends PriceActionZone {
5819
+ constructor(init) {
5820
+ super(init);
5821
+ this.type = "fairvaluegap";
5822
+ if (!this.gap) this.gap = defaults2();
5823
+ }
5824
+ settings() {
5825
+ return this.gap;
5826
+ }
5827
+ formation(bars, at) {
5828
+ const s = gapState(bars, at);
5829
+ if (!s) return null;
5830
+ const { extend, inverse } = this.gap;
5831
+ const flip = inverse && extend === "filled" && s.inverted !== null ? { from: s.inverted, to: null } : null;
5832
+ return { ...s, start: s.index - 1, end: extend === "always" ? null : s[extend], flip };
5833
+ }
5834
+ schema() {
5835
+ return {
5836
+ fields: [
5837
+ { path: "gap.extend", label: "Extend right", kind: "select", options: EXTEND_OPTIONS, group: "behavior" },
5838
+ { path: "gap.upColor", label: "Bullish", kind: "color", group: "fill" },
5839
+ { path: "gap.downColor", label: "Bearish", kind: "color", group: "fill" },
5840
+ { path: "gap.midline", label: "Midline", kind: "boolean", group: "line" },
5841
+ { path: "gap.border", label: "Border", kind: "boolean", group: "line" },
5842
+ { path: "gap.fade", label: "Fade at end", kind: "boolean", group: "behavior", when: { path: "gap.extend", in: ["tested", "half", "filled"] } },
5843
+ { path: "gap.inverse", label: "Flip on close", kind: "boolean", group: "behavior", when: { path: "gap.extend", in: ["filled"] } }
5844
+ ]
5845
+ };
5846
+ }
5847
+ writeProps() {
5848
+ return { ...this.gap };
5849
+ }
5850
+ readProps(props) {
5851
+ const d = defaults2();
5852
+ const p = props;
5853
+ this.gap = {
5854
+ extend: EXTEND_OPTIONS.some((o) => o.value === p.extend) ? p.extend : d.extend,
5855
+ upColor: typeof p.upColor === "string" ? p.upColor : d.upColor,
5856
+ downColor: typeof p.downColor === "string" ? p.downColor : d.downColor,
5857
+ midline: typeof p.midline === "boolean" ? p.midline : d.midline,
5858
+ border: typeof p.border === "boolean" ? p.border : d.border,
5859
+ fade: typeof p.fade === "boolean" ? p.fade : d.fade,
5860
+ inverse: typeof p.inverse === "boolean" ? p.inverse : d.inverse
5861
+ };
5862
+ }
5863
+ };
5864
+
5865
+ // src/core/drawings/types/OrderBlock.ts
5866
+ var RANGE_OPTIONS = [
5867
+ { value: "body", label: "Body" },
5868
+ { value: "wick", label: "Full range" }
5869
+ ];
5870
+ var EXTEND_OPTIONS2 = [
5871
+ { value: "tested", label: "Until tested" },
5872
+ { value: "half", label: "Until 50%" },
5873
+ { value: "broken", label: "Until broken" },
5874
+ { value: "always", label: "Always" }
5875
+ ];
5876
+ function defaults3() {
5877
+ return { range: "body", extend: "broken", upColor: `${BULLISH}29`, downColor: `${BEARISH}29`, midline: true, border: true, fade: true, breaker: true };
5878
+ }
5879
+ var OrderBlock = class extends PriceActionZone {
5880
+ constructor(init) {
5881
+ super(init);
5882
+ this.type = "orderblock";
5883
+ if (!this.block) this.block = defaults3();
5884
+ }
5885
+ settings() {
5886
+ return this.block;
5887
+ }
5888
+ formation(bars, at) {
5889
+ const s = blockState(bars, at, this.block.range);
5890
+ if (!s) return null;
5891
+ const { extend, breaker } = this.block;
5892
+ const flip = breaker && extend === "broken" && s.broken !== null ? { from: s.broken, to: s.retest } : null;
5893
+ return { ...s, start: s.index, end: extend === "always" ? null : s[extend], flip };
5894
+ }
5895
+ schema() {
5896
+ return {
5897
+ fields: [
5898
+ { path: "block.range", label: "Zone", kind: "select", options: RANGE_OPTIONS, group: "behavior" },
5899
+ { path: "block.extend", label: "Extend right", kind: "select", options: EXTEND_OPTIONS2, group: "behavior" },
5900
+ { path: "block.upColor", label: "Bullish", kind: "color", group: "fill" },
5901
+ { path: "block.downColor", label: "Bearish", kind: "color", group: "fill" },
5902
+ { path: "block.midline", label: "Midline", kind: "boolean", group: "line" },
5903
+ { path: "block.border", label: "Border", kind: "boolean", group: "line" },
5904
+ { path: "block.fade", label: "Fade at end", kind: "boolean", group: "behavior", when: { path: "block.extend", in: ["tested", "half", "broken"] } },
5905
+ { path: "block.breaker", label: "Breaker", kind: "boolean", group: "behavior", when: { path: "block.extend", in: ["broken"] } }
5906
+ ]
5907
+ };
5908
+ }
5909
+ writeProps() {
5910
+ return { ...this.block };
5911
+ }
5912
+ readProps(props) {
5913
+ const d = defaults3();
5914
+ const p = props;
5915
+ this.block = {
5916
+ range: p.range === "body" || p.range === "wick" ? p.range : d.range,
5917
+ extend: EXTEND_OPTIONS2.some((o) => o.value === p.extend) ? p.extend : d.extend,
5918
+ upColor: typeof p.upColor === "string" ? p.upColor : d.upColor,
5919
+ downColor: typeof p.downColor === "string" ? p.downColor : d.downColor,
5920
+ midline: typeof p.midline === "boolean" ? p.midline : d.midline,
5921
+ border: typeof p.border === "boolean" ? p.border : d.border,
5922
+ fade: typeof p.fade === "boolean" ? p.fade : d.fade,
5923
+ breaker: typeof p.breaker === "boolean" ? p.breaker : d.breaker
5924
+ };
5925
+ }
5926
+ };
5927
+
5928
+ // src/core/drawings/types/Liquidity.ts
5929
+ var EXTEND_OPTIONS3 = [
5930
+ { value: "swept", label: "Until swept" },
5931
+ { value: "broken", label: "Until broken" },
5932
+ { value: "always", label: "Always" }
5933
+ ];
5934
+ function defaults4() {
5935
+ return { extend: "swept", markSweep: true };
5936
+ }
5937
+ var Liquidity = class extends PriceActionTool {
5938
+ constructor(init) {
5939
+ super(init);
5940
+ this.type = "liquidity";
5941
+ if (!this.liq) this.liq = defaults4();
5942
+ }
5943
+ anchorSchema() {
5944
+ return { min: 1, max: 1, slots: [{ role: "candle", free: "both" }] };
5945
+ }
5946
+ state(proj) {
5947
+ const c = this.candles(proj);
5948
+ const a = this.anchors[0];
5949
+ if (!c || !a) return null;
5950
+ const bar = c.bars[c.at];
5951
+ return liquidityState(c.bars, c.at, a.price >= (bar.high + bar.low) / 2 ? "high" : "low");
5952
+ }
5953
+ line(proj) {
5954
+ const c = this.candles(proj);
5955
+ const s = this.state(proj);
5956
+ if (!c || !s) return null;
5957
+ const y = proj.yOf(s.price, this.paneId);
5958
+ if (y == null) return null;
5959
+ const { bars } = c;
5960
+ const { extend, markSweep } = this.liq;
5961
+ const end = extend === "always" ? null : extend === "broken" ? s.broken : s.swept ?? s.broken;
5962
+ const x1 = proj.xOf(bars[s.index].time);
5963
+ let sweep = null;
5964
+ if (markSweep && s.swept !== null && (end === null || s.swept <= end)) {
5965
+ const b = bars[s.swept];
5966
+ const wick = proj.yOf(s.high ? b.high : b.low, this.paneId);
5967
+ if (wick != null) sweep = { x: proj.xOf(b.time), y: wick + (s.high ? -9 : 9) };
5968
+ }
5969
+ return { x1, x2: this.rightEdge(proj, bars, end), y, sweep, handle: [x1, y] };
5970
+ }
5971
+ hitTest(px, py, proj, tol) {
5972
+ const l = this.line(proj);
5973
+ return l != null && px >= l.x1 - tol && px <= l.x2 + tol && Math.abs(py - l.y) <= tol;
5974
+ }
5975
+ handlePoints(proj) {
5976
+ const l = this.line(proj);
5977
+ return l ? [l.handle] : this.anchorHandle(proj);
5978
+ }
5979
+ bounds(proj) {
5980
+ const l = this.line(proj);
5981
+ if (!l) return null;
5982
+ return { x: l.x1, y: l.y - 4, w: Math.max(1, Math.min(proj.width, l.x2) - l.x1), h: 8 };
5983
+ }
5984
+ schema() {
5985
+ return {
5986
+ fields: [
5987
+ ...LINE_FIELDS,
5988
+ { path: "liq.extend", label: "Extend right", kind: "select", options: EXTEND_OPTIONS3, group: "behavior" },
5989
+ { path: "liq.markSweep", label: "Mark sweep", kind: "boolean", group: "behavior" }
5990
+ ]
5991
+ };
5992
+ }
5993
+ writeProps() {
5994
+ return { ...this.liq };
5995
+ }
5996
+ readProps(props) {
5997
+ const d = defaults4();
5998
+ const p = props;
5999
+ this.liq = {
6000
+ extend: EXTEND_OPTIONS3.some((o) => o.value === p.extend) ? p.extend : d.extend,
6001
+ markSweep: typeof p.markSweep === "boolean" ? p.markSweep : d.markSweep
6002
+ };
6003
+ }
6004
+ };
6005
+
5481
6006
  // src/core/drawings/registry.ts
5482
6007
  var REGISTRY2 = /* @__PURE__ */ new Map();
5483
6008
  function registerDrawingType(meta) {
@@ -6147,6 +6672,36 @@ registerDrawingType({
6147
6672
  defaultStyle: { lineColor: BULLISH, lineWidth: 1, lineStyle: "solid" },
6148
6673
  create: (init) => new FixedRangeVolumeProfile(init)
6149
6674
  });
6675
+ var FVG_ICON = svg24('<path d="M6 15v6M12 3v18M18 2v6"/><rect x="3.5" y="9.5" width="17" height="5" rx="1" fill="currentColor" fill-opacity="0.28" stroke-width="1.4"/>');
6676
+ var ORDER_BLOCK_ICON = svg24('<rect x="4" y="7.5" width="5" height="8" rx="1"/><path d="M6.5 4v3.5M6.5 15.5V19"/><rect x="9" y="7.5" width="11.5" height="8" rx="1" fill="currentColor" fill-opacity="0.22" stroke-width="1.4"/>');
6677
+ var LIQUIDITY_ICON = svg24('<path d="M3 8h12" stroke-dasharray="2.5 2.5"/><path d="m16.5 4.5 4 4m0-4-4 4"/><path d="M3.5 20 8 13.5l4 3.5 5-8"/>');
6678
+ registerDrawingType({
6679
+ type: "fairvaluegap",
6680
+ group: "priceaction",
6681
+ label: "Fair Value Gap",
6682
+ icon: FVG_ICON,
6683
+ defaultStyle: { lineColor: BULLISH, lineWidth: 1, lineStyle: "solid" },
6684
+ placementHint: "Click the middle candle of a gap",
6685
+ create: (init) => new FairValueGap(init)
6686
+ });
6687
+ registerDrawingType({
6688
+ type: "orderblock",
6689
+ group: "priceaction",
6690
+ label: "Order Block",
6691
+ icon: ORDER_BLOCK_ICON,
6692
+ defaultStyle: { lineColor: BULLISH, lineWidth: 1, lineStyle: "solid" },
6693
+ placementHint: "Click the candle that forms the block",
6694
+ create: (init) => new OrderBlock(init)
6695
+ });
6696
+ registerDrawingType({
6697
+ type: "liquidity",
6698
+ group: "priceaction",
6699
+ label: "Liquidity",
6700
+ icon: LIQUIDITY_ICON,
6701
+ defaultStyle: { lineColor: WARNING, lineWidth: 1, lineStyle: "solid" },
6702
+ placementHint: "Click near a candle's high or low",
6703
+ create: (init) => new Liquidity(init)
6704
+ });
6150
6705
 
6151
6706
  // src/core/model/inputs.ts
6152
6707
  function inputVisible(when, values) {
@@ -6184,6 +6739,7 @@ exports.BEARISH = BEARISH;
6184
6739
  exports.BULLISH = BULLISH;
6185
6740
  exports.CATEGORICAL = CATEGORICAL;
6186
6741
  exports.CHIP_PLATE = CHIP_PLATE;
6742
+ exports.CONTEXT_MENU_BUILTIN_ORDER = CONTEXT_MENU_BUILTIN_ORDER;
6187
6743
  exports.CROSSHAIR = CROSSHAIR;
6188
6744
  exports.DEFAULT_PANEL_MAX_WIDTH = DEFAULT_PANEL_MAX_WIDTH;
6189
6745
  exports.DEFAULT_PANEL_MIN_WIDTH = DEFAULT_PANEL_MIN_WIDTH;