hyperprop-charting-library 0.1.107 → 0.1.109

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.
@@ -925,30 +925,54 @@ function createChart(element, options = {}) {
925
925
  }
926
926
  };
927
927
  };
928
- const normalizeDrawingState = (drawing) => ({
929
- id: drawing.id ?? `drawing-${generatedDrawingId++}`,
930
- type: drawing.type,
931
- points: drawing.points.map((point) => ({
932
- index: Number(point.index) || 0,
933
- price: Number(point.price) || 0,
934
- ...point.time ? { time: point.time } : {}
935
- })),
936
- visible: drawing.visible ?? true,
937
- color: drawing.color ?? "#94a3b8",
938
- colors: Array.isArray(drawing.colors) ? drawing.colors.filter((value) => typeof value === "string") : [],
939
- style: drawing.style ?? "dotted",
940
- width: Math.max(1, Number(drawing.width) || 1),
941
- locked: drawing.locked ?? false,
942
- accountSize: Number(drawing.accountSize) || 0,
943
- lotSize: Number(drawing.lotSize) || 1,
944
- risk: Number(drawing.risk) || 0,
945
- riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
946
- leverage: Number(drawing.leverage) || 1,
947
- pointValue: Number(drawing.pointValue) || 1,
948
- qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
949
- fontSize: Math.max(6, Number(drawing.fontSize) || 14),
950
- ...drawing.label === void 0 ? {} : { label: drawing.label }
951
- });
928
+ const bumpGeneratedDrawingIdPast = (id) => {
929
+ const match = /^drawing-(\d+)$/.exec(id);
930
+ if (!match) return;
931
+ const n = Number(match[1]);
932
+ if (Number.isFinite(n) && n >= generatedDrawingId) {
933
+ generatedDrawingId = n + 1;
934
+ }
935
+ };
936
+ const normalizeDrawingState = (drawing) => {
937
+ const id = drawing.id ?? `drawing-${generatedDrawingId++}`;
938
+ bumpGeneratedDrawingIdPast(id);
939
+ return {
940
+ id,
941
+ type: drawing.type,
942
+ points: drawing.points.map((point) => ({
943
+ index: Number(point.index) || 0,
944
+ price: Number(point.price) || 0,
945
+ ...point.time ? { time: point.time } : {}
946
+ })),
947
+ visible: drawing.visible ?? true,
948
+ color: drawing.color ?? "#94a3b8",
949
+ colors: Array.isArray(drawing.colors) ? drawing.colors.filter((value) => typeof value === "string") : [],
950
+ style: drawing.style ?? "dotted",
951
+ width: Math.max(1, Number(drawing.width) || 1),
952
+ locked: drawing.locked ?? false,
953
+ accountSize: Number(drawing.accountSize) || 0,
954
+ lotSize: Number(drawing.lotSize) || 1,
955
+ risk: Number(drawing.risk) || 0,
956
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
957
+ leverage: Number(drawing.leverage) || 1,
958
+ pointValue: Number(drawing.pointValue) || 1,
959
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
960
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
961
+ ...drawing.label === void 0 ? {} : { label: drawing.label }
962
+ };
963
+ };
964
+ const dedupeDrawingIds = (list) => {
965
+ const seen = /* @__PURE__ */ new Set();
966
+ return list.map((drawing) => {
967
+ if (!seen.has(drawing.id)) {
968
+ seen.add(drawing.id);
969
+ return drawing;
970
+ }
971
+ const id = `drawing-${generatedDrawingId++}`;
972
+ seen.add(id);
973
+ return { ...drawing, id };
974
+ });
975
+ };
952
976
  const serializeDrawing = (drawing) => ({
953
977
  id: drawing.id,
954
978
  type: drawing.type,
@@ -971,7 +995,9 @@ function createChart(element, options = {}) {
971
995
  });
972
996
  let tradeMarkers = [];
973
997
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
974
- let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
998
+ let drawings = dedupeDrawingIds(
999
+ (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing))
1000
+ );
975
1001
  let activeDrawingTool = null;
976
1002
  let draftDrawing = null;
977
1003
  let drawingDragState = null;
@@ -1046,7 +1072,7 @@ function createChart(element, options = {}) {
1046
1072
  if (Math.abs(priceDiff) < 1e-9 && Math.abs(volumeDiff) < 1e-6) {
1047
1073
  smoothedTickerPrice = tickerPriceTarget;
1048
1074
  smoothedTickerVolume = tickerVolumeTarget;
1049
- draw({ updateAutoScale: false });
1075
+ scheduleDraw({ updateAutoScale: false });
1050
1076
  return;
1051
1077
  }
1052
1078
  const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -1055,7 +1081,7 @@ function createChart(element, options = {}) {
1055
1081
  const lerp = 1 - Math.exp(-speed * dt);
1056
1082
  smoothedTickerPrice += priceDiff * lerp;
1057
1083
  smoothedTickerVolume += volumeDiff * lerp;
1058
- draw({ updateAutoScale: false });
1084
+ scheduleDraw({ updateAutoScale: false });
1059
1085
  smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
1060
1086
  };
1061
1087
  const pushSmoothedTicker = (targetPrice, targetVolume) => {
@@ -1120,7 +1146,7 @@ function createChart(element, options = {}) {
1120
1146
  image.onload = () => {
1121
1147
  watermarkImage = image;
1122
1148
  watermarkImageReady = true;
1123
- draw();
1149
+ scheduleDraw();
1124
1150
  };
1125
1151
  image.onerror = () => {
1126
1152
  watermarkImage = null;
@@ -1883,7 +1909,21 @@ function createChart(element, options = {}) {
1883
1909
  }
1884
1910
  }
1885
1911
  crosshairPoint = point;
1886
- draw();
1912
+ scheduleDraw();
1913
+ };
1914
+ let drawRafId = null;
1915
+ let pendingDrawUpdateAutoScale = false;
1916
+ const scheduleDraw = (options2 = {}) => {
1917
+ pendingDrawUpdateAutoScale = pendingDrawUpdateAutoScale || (options2.updateAutoScale ?? true);
1918
+ if (drawRafId !== null) {
1919
+ return;
1920
+ }
1921
+ drawRafId = requestAnimationFrame(() => {
1922
+ drawRafId = null;
1923
+ const updateAutoScale = pendingDrawUpdateAutoScale;
1924
+ pendingDrawUpdateAutoScale = false;
1925
+ draw({ updateAutoScale });
1926
+ });
1887
1927
  };
1888
1928
  const draw = (options2 = {}) => {
1889
1929
  const shouldUpdateAutoScale = options2.updateAutoScale ?? true;
@@ -3612,7 +3652,7 @@ function createChart(element, options = {}) {
3612
3652
  clampXViewport();
3613
3653
  updateFollowLatest(false);
3614
3654
  emitViewportChange();
3615
- draw();
3655
+ scheduleDraw();
3616
3656
  };
3617
3657
  const zoomXToLatest = (factor) => {
3618
3658
  if (!drawState || data.length === 0) {
@@ -3627,7 +3667,7 @@ function createChart(element, options = {}) {
3627
3667
  xCenter = nextStart + nextSpan / 2;
3628
3668
  clampXViewport();
3629
3669
  emitViewportChange();
3630
- draw();
3670
+ scheduleDraw();
3631
3671
  };
3632
3672
  const zoomXFromAxis = (factor) => {
3633
3673
  if (!drawState) {
@@ -3658,7 +3698,7 @@ function createChart(element, options = {}) {
3658
3698
  yMinOverride = clamped.min;
3659
3699
  yMaxOverride = clamped.max;
3660
3700
  emitViewportChange();
3661
- draw();
3701
+ scheduleDraw();
3662
3702
  };
3663
3703
  const pan = (deltaX, deltaY, allowX, allowY) => {
3664
3704
  if (!drawState || data.length === 0) {
@@ -3684,7 +3724,7 @@ function createChart(element, options = {}) {
3684
3724
  if (allowX || allowY) {
3685
3725
  emitViewportChange();
3686
3726
  }
3687
- draw();
3727
+ scheduleDraw();
3688
3728
  };
3689
3729
  const resetYViewport = () => {
3690
3730
  yMinOverride = null;
@@ -3724,7 +3764,7 @@ function createChart(element, options = {}) {
3724
3764
  clampXViewport();
3725
3765
  updateFollowLatest(false);
3726
3766
  emitViewportChange();
3727
- draw();
3767
+ scheduleDraw();
3728
3768
  };
3729
3769
  const panY = (priceDelta) => {
3730
3770
  if (!drawState || !Number.isFinite(priceDelta) || priceDelta === 0) {
@@ -3736,20 +3776,20 @@ function createChart(element, options = {}) {
3736
3776
  yMinOverride = clamped.min;
3737
3777
  yMaxOverride = clamped.max;
3738
3778
  emitViewportChange();
3739
- draw();
3779
+ scheduleDraw();
3740
3780
  };
3741
3781
  const fitContent = () => {
3742
3782
  fitXViewport();
3743
3783
  updateFollowLatest(true);
3744
3784
  emitViewportChange();
3745
- draw();
3785
+ scheduleDraw();
3746
3786
  };
3747
3787
  const resetViewport = () => {
3748
3788
  fitXViewport();
3749
3789
  resetYViewport();
3750
3790
  updateFollowLatest(true);
3751
3791
  emitViewportChange();
3752
- draw();
3792
+ scheduleDraw();
3753
3793
  };
3754
3794
  const isFollowingLatest = () => followLatest;
3755
3795
  const setFollowingLatest = (follow) => {
@@ -3757,7 +3797,7 @@ function createChart(element, options = {}) {
3757
3797
  xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
3758
3798
  clampXViewport();
3759
3799
  updateFollowLatest(true);
3760
- draw();
3800
+ scheduleDraw();
3761
3801
  } else {
3762
3802
  updateFollowLatest(follow);
3763
3803
  }
@@ -3816,7 +3856,7 @@ function createChart(element, options = {}) {
3816
3856
  }
3817
3857
  if (changed) {
3818
3858
  clampXViewport();
3819
- draw();
3859
+ scheduleDraw();
3820
3860
  emitViewportChange();
3821
3861
  }
3822
3862
  };
@@ -4193,7 +4233,7 @@ function createChart(element, options = {}) {
4193
4233
  })
4194
4234
  );
4195
4235
  emitDrawingsChange();
4196
- draw();
4236
+ scheduleDraw();
4197
4237
  return true;
4198
4238
  }
4199
4239
  if (activeDrawingTool === "vertical-line") {
@@ -4208,7 +4248,7 @@ function createChart(element, options = {}) {
4208
4248
  })
4209
4249
  );
4210
4250
  emitDrawingsChange();
4211
- draw();
4251
+ scheduleDraw();
4212
4252
  return true;
4213
4253
  }
4214
4254
  if (activeDrawingTool === "trendline") {
@@ -4223,7 +4263,7 @@ function createChart(element, options = {}) {
4223
4263
  draftDrawing = null;
4224
4264
  activeDrawingTool = null;
4225
4265
  emitDrawingsChange();
4226
- draw();
4266
+ scheduleDraw();
4227
4267
  return true;
4228
4268
  }
4229
4269
  const defaults = getDrawingToolDefaults("trendline");
@@ -4234,7 +4274,7 @@ function createChart(element, options = {}) {
4234
4274
  style: defaults.style ?? "solid",
4235
4275
  width: defaults.width ?? 2
4236
4276
  });
4237
- draw();
4277
+ scheduleDraw();
4238
4278
  return true;
4239
4279
  }
4240
4280
  if (activeDrawingTool === "ray") {
@@ -4249,7 +4289,7 @@ function createChart(element, options = {}) {
4249
4289
  draftDrawing = null;
4250
4290
  activeDrawingTool = null;
4251
4291
  emitDrawingsChange();
4252
- draw();
4292
+ scheduleDraw();
4253
4293
  return true;
4254
4294
  }
4255
4295
  const defaults = getDrawingToolDefaults("ray");
@@ -4260,7 +4300,7 @@ function createChart(element, options = {}) {
4260
4300
  style: defaults.style ?? "solid",
4261
4301
  width: defaults.width ?? 2
4262
4302
  });
4263
- draw();
4303
+ scheduleDraw();
4264
4304
  return true;
4265
4305
  }
4266
4306
  if (activeDrawingTool === "fib-retracement") {
@@ -4273,7 +4313,7 @@ function createChart(element, options = {}) {
4273
4313
  draftDrawing = null;
4274
4314
  activeDrawingTool = null;
4275
4315
  emitDrawingsChange();
4276
- draw();
4316
+ scheduleDraw();
4277
4317
  return true;
4278
4318
  }
4279
4319
  const defaults = getDrawingToolDefaults("fib-retracement");
@@ -4285,7 +4325,7 @@ function createChart(element, options = {}) {
4285
4325
  style: defaults.style ?? "solid",
4286
4326
  width: defaults.width ?? 1
4287
4327
  });
4288
- draw();
4328
+ scheduleDraw();
4289
4329
  return true;
4290
4330
  }
4291
4331
  if (activeDrawingTool === "fib-extension") {
@@ -4295,7 +4335,7 @@ function createChart(element, options = {}) {
4295
4335
  ...serializeDrawing(draftDrawing),
4296
4336
  points: [...draftDrawing.points.slice(0, -1), point, point]
4297
4337
  });
4298
- draw();
4338
+ scheduleDraw();
4299
4339
  return true;
4300
4340
  }
4301
4341
  const completed = normalizeDrawingState({
@@ -4306,7 +4346,7 @@ function createChart(element, options = {}) {
4306
4346
  draftDrawing = null;
4307
4347
  activeDrawingTool = null;
4308
4348
  emitDrawingsChange();
4309
- draw();
4349
+ scheduleDraw();
4310
4350
  return true;
4311
4351
  }
4312
4352
  const defaults = getDrawingToolDefaults("fib-extension");
@@ -4318,7 +4358,7 @@ function createChart(element, options = {}) {
4318
4358
  style: defaults.style ?? "solid",
4319
4359
  width: defaults.width ?? 1
4320
4360
  });
4321
- draw();
4361
+ scheduleDraw();
4322
4362
  return true;
4323
4363
  }
4324
4364
  if (activeDrawingTool === "text" || activeDrawingTool === "note") {
@@ -4335,7 +4375,7 @@ function createChart(element, options = {}) {
4335
4375
  drawings.push(created);
4336
4376
  selectedDrawingId = created.id;
4337
4377
  emitDrawingsChange();
4338
- draw();
4378
+ scheduleDraw();
4339
4379
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4340
4380
  return true;
4341
4381
  }
@@ -4355,7 +4395,7 @@ function createChart(element, options = {}) {
4355
4395
  })
4356
4396
  );
4357
4397
  emitDrawingsChange();
4358
- draw();
4398
+ scheduleDraw();
4359
4399
  return true;
4360
4400
  }
4361
4401
  if (activeDrawingTool === "long-position" || activeDrawingTool === "short-position") {
@@ -4391,7 +4431,7 @@ function createChart(element, options = {}) {
4391
4431
  })
4392
4432
  );
4393
4433
  emitDrawingsChange();
4394
- draw();
4434
+ scheduleDraw();
4395
4435
  return true;
4396
4436
  }
4397
4437
  return false;
@@ -4408,7 +4448,7 @@ function createChart(element, options = {}) {
4408
4448
  return;
4409
4449
  }
4410
4450
  selectedDrawingId = id;
4411
- draw();
4451
+ scheduleDraw();
4412
4452
  };
4413
4453
  const updateDrawingDrag = (x, y) => {
4414
4454
  if (!drawingDragState) {
@@ -4467,7 +4507,7 @@ function createChart(element, options = {}) {
4467
4507
  };
4468
4508
  });
4469
4509
  emitDrawingsChange();
4470
- draw();
4510
+ scheduleDraw();
4471
4511
  return true;
4472
4512
  };
4473
4513
  let isDragging = false;
@@ -4532,7 +4572,7 @@ function createChart(element, options = {}) {
4532
4572
  clampXViewport();
4533
4573
  updateFollowLatest(false);
4534
4574
  emitViewportChange();
4535
- draw();
4575
+ scheduleDraw();
4536
4576
  };
4537
4577
  const onPointerDown = (event) => {
4538
4578
  if (event.pointerType === "touch" || event.pointerType === "pen") {
@@ -4716,7 +4756,7 @@ function createChart(element, options = {}) {
4716
4756
  points: [...draftDrawing.points.slice(0, -1), nextPoint]
4717
4757
  };
4718
4758
  canvas.style.cursor = "crosshair";
4719
- draw();
4759
+ scheduleDraw();
4720
4760
  return;
4721
4761
  }
4722
4762
  }
@@ -4744,7 +4784,7 @@ function createChart(element, options = {}) {
4744
4784
  dragging: true,
4745
4785
  ...currentLine ? { line: currentLine } : {}
4746
4786
  });
4747
- draw();
4787
+ scheduleDraw();
4748
4788
  }
4749
4789
  canvas.style.cursor = "ns-resize";
4750
4790
  setCrosshairPoint(null);
@@ -5006,7 +5046,7 @@ function createChart(element, options = {}) {
5006
5046
  }
5007
5047
  if (region === "y-axis") {
5008
5048
  resetYViewport();
5009
- draw();
5049
+ scheduleDraw();
5010
5050
  return;
5011
5051
  }
5012
5052
  resetViewport();
@@ -5028,7 +5068,7 @@ function createChart(element, options = {}) {
5028
5068
  const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
5029
5069
  magnetModifierActive = active;
5030
5070
  shiftKeyActive = shift;
5031
- if (changed && draftDrawing) draw();
5071
+ if (changed && draftDrawing) scheduleDraw();
5032
5072
  };
5033
5073
  const onWindowBlurMagnet = () => {
5034
5074
  magnetModifierActive = false;
@@ -5064,7 +5104,7 @@ function createChart(element, options = {}) {
5064
5104
  const lastPoint = data[data.length - 1];
5065
5105
  pushSmoothedTicker(lastPoint.c, lastPoint.v);
5066
5106
  }
5067
- draw();
5107
+ scheduleDraw();
5068
5108
  };
5069
5109
  const resize = (nextWidth, nextHeight) => {
5070
5110
  if (nextWidth && nextWidth > 0) {
@@ -5075,7 +5115,7 @@ function createChart(element, options = {}) {
5075
5115
  }
5076
5116
  mergedOptions = { ...mergedOptions, width, height };
5077
5117
  resetRightMarginCache();
5078
- draw();
5118
+ scheduleDraw();
5079
5119
  };
5080
5120
  const setData = (nextData) => {
5081
5121
  const hadData = data.length > 0;
@@ -5093,7 +5133,7 @@ function createChart(element, options = {}) {
5093
5133
  tickerVolumeTarget = null;
5094
5134
  resetRightMarginCache();
5095
5135
  resetYViewport();
5096
- draw();
5136
+ scheduleDraw();
5097
5137
  return;
5098
5138
  }
5099
5139
  if (!hadData) {
@@ -5118,7 +5158,7 @@ function createChart(element, options = {}) {
5118
5158
  if (lastPoint) {
5119
5159
  pushSmoothedTicker(lastPoint.c, lastPoint.v);
5120
5160
  }
5121
- draw();
5161
+ scheduleDraw();
5122
5162
  };
5123
5163
  const setPriceLines = (lines) => {
5124
5164
  priceLines = lines.map((line, index) => ({
@@ -5126,18 +5166,18 @@ function createChart(element, options = {}) {
5126
5166
  id: line.id ?? `line-${index + 1}`
5127
5167
  }));
5128
5168
  resetRightMarginCache();
5129
- draw();
5169
+ scheduleDraw();
5130
5170
  };
5131
5171
  const addPriceLine = (line) => {
5132
5172
  const id = line.id ?? `line-${generatedPriceLineId++}`;
5133
5173
  priceLines.push({ ...line, id });
5134
- draw();
5174
+ scheduleDraw();
5135
5175
  return id;
5136
5176
  };
5137
5177
  const removePriceLine = (id) => {
5138
5178
  priceLines = priceLines.filter((line) => line.id !== id);
5139
5179
  resetRightMarginCache();
5140
- draw();
5180
+ scheduleDraw();
5141
5181
  };
5142
5182
  const setOrderLines = (lines) => {
5143
5183
  orderLines = lines.map((line, index) => ({
@@ -5156,24 +5196,24 @@ function createChart(element, options = {}) {
5156
5196
  orderPriceTagWidthById.delete(id);
5157
5197
  }
5158
5198
  }
5159
- draw();
5199
+ scheduleDraw();
5160
5200
  };
5161
5201
  const addOrderLine = (line) => {
5162
5202
  const id = line.id ?? `order-${generatedOrderLineId++}`;
5163
5203
  orderLines.push({ ...line, id });
5164
- draw();
5204
+ scheduleDraw();
5165
5205
  return id;
5166
5206
  };
5167
5207
  const updateOrderLine = (id, patch) => {
5168
5208
  orderLines = orderLines.map((line) => line.id === id ? { ...line, ...patch, id } : line);
5169
- draw();
5209
+ scheduleDraw();
5170
5210
  };
5171
5211
  const removeOrderLine = (id) => {
5172
5212
  orderLines = orderLines.filter((line) => line.id !== id);
5173
5213
  orderWidgetWidthById.delete(id);
5174
5214
  orderPriceTagWidthById.delete(id);
5175
5215
  resetRightMarginCache();
5176
- draw();
5216
+ scheduleDraw();
5177
5217
  };
5178
5218
  const onOrderAction = (handler) => {
5179
5219
  orderActionHandler = handler;
@@ -5198,12 +5238,12 @@ function createChart(element, options = {}) {
5198
5238
  throw new Error("Invalid indicator plugin. Expected { id, draw }.");
5199
5239
  }
5200
5240
  indicatorRegistry.set(plugin.id, plugin);
5201
- draw();
5241
+ scheduleDraw();
5202
5242
  };
5203
5243
  const unregisterIndicator = (type) => {
5204
5244
  indicatorRegistry.delete(type);
5205
5245
  indicators = indicators.filter((indicator) => indicator.type !== type);
5206
- draw();
5246
+ scheduleDraw();
5207
5247
  };
5208
5248
  const listBuiltInIndicators = () => {
5209
5249
  return BUILTIN_INDICATORS.map((indicator) => ({
@@ -5228,7 +5268,7 @@ function createChart(element, options = {}) {
5228
5268
  };
5229
5269
  const setIndicators = (nextIndicators) => {
5230
5270
  indicators = nextIndicators.map((indicator) => normalizeIndicatorState(indicator));
5231
- draw();
5271
+ scheduleDraw();
5232
5272
  };
5233
5273
  const addIndicator = (type, inputs = {}, options2 = {}) => {
5234
5274
  const plugin = indicatorRegistry.get(type);
@@ -5241,7 +5281,7 @@ function createChart(element, options = {}) {
5241
5281
  inputs
5242
5282
  });
5243
5283
  indicators.push(next);
5244
- draw();
5284
+ scheduleDraw();
5245
5285
  return next.id;
5246
5286
  };
5247
5287
  const updateIndicator = (id, patch) => {
@@ -5265,30 +5305,30 @@ function createChart(element, options = {}) {
5265
5305
  }
5266
5306
  };
5267
5307
  });
5268
- draw();
5308
+ scheduleDraw();
5269
5309
  };
5270
5310
  const removeIndicator = (id) => {
5271
5311
  indicators = indicators.filter((indicator) => indicator.id !== id);
5272
- draw();
5312
+ scheduleDraw();
5273
5313
  };
5274
5314
  const setActiveDrawingTool = (tool) => {
5275
5315
  activeDrawingTool = tool;
5276
5316
  draftDrawing = null;
5277
5317
  canvas.style.cursor = tool ? "crosshair" : "default";
5278
- draw();
5318
+ scheduleDraw();
5279
5319
  };
5280
5320
  const getActiveDrawingTool = () => activeDrawingTool;
5281
5321
  const cancelDrawing = () => {
5282
5322
  if (draftDrawing) {
5283
5323
  draftDrawing = null;
5284
- draw();
5324
+ scheduleDraw();
5285
5325
  return true;
5286
5326
  }
5287
5327
  return false;
5288
5328
  };
5289
5329
  const setTradeMarkers = (markers) => {
5290
5330
  tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5291
- draw();
5331
+ scheduleDraw();
5292
5332
  };
5293
5333
  const setMagnetMode = (mode) => {
5294
5334
  magnetMode = mode;
@@ -5296,16 +5336,16 @@ function createChart(element, options = {}) {
5296
5336
  const getMagnetMode = () => magnetMode;
5297
5337
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5298
5338
  const setDrawings = (nextDrawings) => {
5299
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5339
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5300
5340
  draftDrawing = null;
5301
5341
  emitDrawingsChange();
5302
- draw();
5342
+ scheduleDraw();
5303
5343
  };
5304
5344
  const addDrawing = (drawing) => {
5305
5345
  const next = normalizeDrawingState(drawing);
5306
5346
  drawings.push(next);
5307
5347
  emitDrawingsChange();
5308
- draw();
5348
+ scheduleDraw();
5309
5349
  return next.id;
5310
5350
  };
5311
5351
  const updateDrawing = (id, patch) => {
@@ -5318,18 +5358,18 @@ function createChart(element, options = {}) {
5318
5358
  }) : drawing
5319
5359
  );
5320
5360
  emitDrawingsChange();
5321
- draw();
5361
+ scheduleDraw();
5322
5362
  };
5323
5363
  const removeDrawing = (id) => {
5324
5364
  drawings = drawings.filter((drawing) => drawing.id !== id);
5325
5365
  emitDrawingsChange();
5326
- draw();
5366
+ scheduleDraw();
5327
5367
  };
5328
5368
  const clearDrawings = () => {
5329
5369
  drawings = [];
5330
5370
  draftDrawing = null;
5331
5371
  emitDrawingsChange();
5332
- draw();
5372
+ scheduleDraw();
5333
5373
  };
5334
5374
  const onDrawingsChange = (handler) => {
5335
5375
  drawingsChangeHandler = handler;
@@ -5351,6 +5391,11 @@ function createChart(element, options = {}) {
5351
5391
  cancelAnimationFrame(smoothingRafId);
5352
5392
  smoothingRafId = null;
5353
5393
  }
5394
+ if (drawRafId !== null) {
5395
+ cancelAnimationFrame(drawRafId);
5396
+ drawRafId = null;
5397
+ pendingDrawUpdateAutoScale = false;
5398
+ }
5354
5399
  canvas.removeEventListener("pointerdown", onPointerDown);
5355
5400
  canvas.removeEventListener("pointermove", onPointerMove);
5356
5401
  canvas.removeEventListener("pointerup", endPointerDrag);