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.
@@ -951,30 +951,54 @@ function createChart(element, options = {}) {
951
951
  }
952
952
  };
953
953
  };
954
- const normalizeDrawingState = (drawing) => ({
955
- id: drawing.id ?? `drawing-${generatedDrawingId++}`,
956
- type: drawing.type,
957
- points: drawing.points.map((point) => ({
958
- index: Number(point.index) || 0,
959
- price: Number(point.price) || 0,
960
- ...point.time ? { time: point.time } : {}
961
- })),
962
- visible: drawing.visible ?? true,
963
- color: drawing.color ?? "#94a3b8",
964
- colors: Array.isArray(drawing.colors) ? drawing.colors.filter((value) => typeof value === "string") : [],
965
- style: drawing.style ?? "dotted",
966
- width: Math.max(1, Number(drawing.width) || 1),
967
- locked: drawing.locked ?? false,
968
- accountSize: Number(drawing.accountSize) || 0,
969
- lotSize: Number(drawing.lotSize) || 1,
970
- risk: Number(drawing.risk) || 0,
971
- riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
972
- leverage: Number(drawing.leverage) || 1,
973
- pointValue: Number(drawing.pointValue) || 1,
974
- qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
975
- fontSize: Math.max(6, Number(drawing.fontSize) || 14),
976
- ...drawing.label === void 0 ? {} : { label: drawing.label }
977
- });
954
+ const bumpGeneratedDrawingIdPast = (id) => {
955
+ const match = /^drawing-(\d+)$/.exec(id);
956
+ if (!match) return;
957
+ const n = Number(match[1]);
958
+ if (Number.isFinite(n) && n >= generatedDrawingId) {
959
+ generatedDrawingId = n + 1;
960
+ }
961
+ };
962
+ const normalizeDrawingState = (drawing) => {
963
+ const id = drawing.id ?? `drawing-${generatedDrawingId++}`;
964
+ bumpGeneratedDrawingIdPast(id);
965
+ return {
966
+ id,
967
+ type: drawing.type,
968
+ points: drawing.points.map((point) => ({
969
+ index: Number(point.index) || 0,
970
+ price: Number(point.price) || 0,
971
+ ...point.time ? { time: point.time } : {}
972
+ })),
973
+ visible: drawing.visible ?? true,
974
+ color: drawing.color ?? "#94a3b8",
975
+ colors: Array.isArray(drawing.colors) ? drawing.colors.filter((value) => typeof value === "string") : [],
976
+ style: drawing.style ?? "dotted",
977
+ width: Math.max(1, Number(drawing.width) || 1),
978
+ locked: drawing.locked ?? false,
979
+ accountSize: Number(drawing.accountSize) || 0,
980
+ lotSize: Number(drawing.lotSize) || 1,
981
+ risk: Number(drawing.risk) || 0,
982
+ riskMode: drawing.riskMode === "amount" ? "amount" : "percent",
983
+ leverage: Number(drawing.leverage) || 1,
984
+ pointValue: Number(drawing.pointValue) || 1,
985
+ qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
986
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
987
+ ...drawing.label === void 0 ? {} : { label: drawing.label }
988
+ };
989
+ };
990
+ const dedupeDrawingIds = (list) => {
991
+ const seen = /* @__PURE__ */ new Set();
992
+ return list.map((drawing) => {
993
+ if (!seen.has(drawing.id)) {
994
+ seen.add(drawing.id);
995
+ return drawing;
996
+ }
997
+ const id = `drawing-${generatedDrawingId++}`;
998
+ seen.add(id);
999
+ return { ...drawing, id };
1000
+ });
1001
+ };
978
1002
  const serializeDrawing = (drawing) => ({
979
1003
  id: drawing.id,
980
1004
  type: drawing.type,
@@ -997,7 +1021,9 @@ function createChart(element, options = {}) {
997
1021
  });
998
1022
  let tradeMarkers = [];
999
1023
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
1000
- let drawings = (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing));
1024
+ let drawings = dedupeDrawingIds(
1025
+ (options.drawings ?? []).map((drawing) => normalizeDrawingState(drawing))
1026
+ );
1001
1027
  let activeDrawingTool = null;
1002
1028
  let draftDrawing = null;
1003
1029
  let drawingDragState = null;
@@ -1072,7 +1098,7 @@ function createChart(element, options = {}) {
1072
1098
  if (Math.abs(priceDiff) < 1e-9 && Math.abs(volumeDiff) < 1e-6) {
1073
1099
  smoothedTickerPrice = tickerPriceTarget;
1074
1100
  smoothedTickerVolume = tickerVolumeTarget;
1075
- draw({ updateAutoScale: false });
1101
+ scheduleDraw({ updateAutoScale: false });
1076
1102
  return;
1077
1103
  }
1078
1104
  const tickerOpts = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
@@ -1081,7 +1107,7 @@ function createChart(element, options = {}) {
1081
1107
  const lerp = 1 - Math.exp(-speed * dt);
1082
1108
  smoothedTickerPrice += priceDiff * lerp;
1083
1109
  smoothedTickerVolume += volumeDiff * lerp;
1084
- draw({ updateAutoScale: false });
1110
+ scheduleDraw({ updateAutoScale: false });
1085
1111
  smoothingRafId = requestAnimationFrame(tickerSmoothingLoop);
1086
1112
  };
1087
1113
  const pushSmoothedTicker = (targetPrice, targetVolume) => {
@@ -1146,7 +1172,7 @@ function createChart(element, options = {}) {
1146
1172
  image.onload = () => {
1147
1173
  watermarkImage = image;
1148
1174
  watermarkImageReady = true;
1149
- draw();
1175
+ scheduleDraw();
1150
1176
  };
1151
1177
  image.onerror = () => {
1152
1178
  watermarkImage = null;
@@ -1909,7 +1935,21 @@ function createChart(element, options = {}) {
1909
1935
  }
1910
1936
  }
1911
1937
  crosshairPoint = point;
1912
- draw();
1938
+ scheduleDraw();
1939
+ };
1940
+ let drawRafId = null;
1941
+ let pendingDrawUpdateAutoScale = false;
1942
+ const scheduleDraw = (options2 = {}) => {
1943
+ pendingDrawUpdateAutoScale = pendingDrawUpdateAutoScale || (options2.updateAutoScale ?? true);
1944
+ if (drawRafId !== null) {
1945
+ return;
1946
+ }
1947
+ drawRafId = requestAnimationFrame(() => {
1948
+ drawRafId = null;
1949
+ const updateAutoScale = pendingDrawUpdateAutoScale;
1950
+ pendingDrawUpdateAutoScale = false;
1951
+ draw({ updateAutoScale });
1952
+ });
1913
1953
  };
1914
1954
  const draw = (options2 = {}) => {
1915
1955
  const shouldUpdateAutoScale = options2.updateAutoScale ?? true;
@@ -3638,7 +3678,7 @@ function createChart(element, options = {}) {
3638
3678
  clampXViewport();
3639
3679
  updateFollowLatest(false);
3640
3680
  emitViewportChange();
3641
- draw();
3681
+ scheduleDraw();
3642
3682
  };
3643
3683
  const zoomXToLatest = (factor) => {
3644
3684
  if (!drawState || data.length === 0) {
@@ -3653,7 +3693,7 @@ function createChart(element, options = {}) {
3653
3693
  xCenter = nextStart + nextSpan / 2;
3654
3694
  clampXViewport();
3655
3695
  emitViewportChange();
3656
- draw();
3696
+ scheduleDraw();
3657
3697
  };
3658
3698
  const zoomXFromAxis = (factor) => {
3659
3699
  if (!drawState) {
@@ -3684,7 +3724,7 @@ function createChart(element, options = {}) {
3684
3724
  yMinOverride = clamped.min;
3685
3725
  yMaxOverride = clamped.max;
3686
3726
  emitViewportChange();
3687
- draw();
3727
+ scheduleDraw();
3688
3728
  };
3689
3729
  const pan = (deltaX, deltaY, allowX, allowY) => {
3690
3730
  if (!drawState || data.length === 0) {
@@ -3710,7 +3750,7 @@ function createChart(element, options = {}) {
3710
3750
  if (allowX || allowY) {
3711
3751
  emitViewportChange();
3712
3752
  }
3713
- draw();
3753
+ scheduleDraw();
3714
3754
  };
3715
3755
  const resetYViewport = () => {
3716
3756
  yMinOverride = null;
@@ -3750,7 +3790,7 @@ function createChart(element, options = {}) {
3750
3790
  clampXViewport();
3751
3791
  updateFollowLatest(false);
3752
3792
  emitViewportChange();
3753
- draw();
3793
+ scheduleDraw();
3754
3794
  };
3755
3795
  const panY = (priceDelta) => {
3756
3796
  if (!drawState || !Number.isFinite(priceDelta) || priceDelta === 0) {
@@ -3762,20 +3802,20 @@ function createChart(element, options = {}) {
3762
3802
  yMinOverride = clamped.min;
3763
3803
  yMaxOverride = clamped.max;
3764
3804
  emitViewportChange();
3765
- draw();
3805
+ scheduleDraw();
3766
3806
  };
3767
3807
  const fitContent = () => {
3768
3808
  fitXViewport();
3769
3809
  updateFollowLatest(true);
3770
3810
  emitViewportChange();
3771
- draw();
3811
+ scheduleDraw();
3772
3812
  };
3773
3813
  const resetViewport = () => {
3774
3814
  fitXViewport();
3775
3815
  resetYViewport();
3776
3816
  updateFollowLatest(true);
3777
3817
  emitViewportChange();
3778
- draw();
3818
+ scheduleDraw();
3779
3819
  };
3780
3820
  const isFollowingLatest = () => followLatest;
3781
3821
  const setFollowingLatest = (follow) => {
@@ -3783,7 +3823,7 @@ function createChart(element, options = {}) {
3783
3823
  xCenter = data.length - xSpan / 2 + rightEdgePaddingBars;
3784
3824
  clampXViewport();
3785
3825
  updateFollowLatest(true);
3786
- draw();
3826
+ scheduleDraw();
3787
3827
  } else {
3788
3828
  updateFollowLatest(follow);
3789
3829
  }
@@ -3842,7 +3882,7 @@ function createChart(element, options = {}) {
3842
3882
  }
3843
3883
  if (changed) {
3844
3884
  clampXViewport();
3845
- draw();
3885
+ scheduleDraw();
3846
3886
  emitViewportChange();
3847
3887
  }
3848
3888
  };
@@ -4219,7 +4259,7 @@ function createChart(element, options = {}) {
4219
4259
  })
4220
4260
  );
4221
4261
  emitDrawingsChange();
4222
- draw();
4262
+ scheduleDraw();
4223
4263
  return true;
4224
4264
  }
4225
4265
  if (activeDrawingTool === "vertical-line") {
@@ -4234,7 +4274,7 @@ function createChart(element, options = {}) {
4234
4274
  })
4235
4275
  );
4236
4276
  emitDrawingsChange();
4237
- draw();
4277
+ scheduleDraw();
4238
4278
  return true;
4239
4279
  }
4240
4280
  if (activeDrawingTool === "trendline") {
@@ -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("trendline");
@@ -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 === "ray") {
@@ -4275,7 +4315,7 @@ function createChart(element, options = {}) {
4275
4315
  draftDrawing = null;
4276
4316
  activeDrawingTool = null;
4277
4317
  emitDrawingsChange();
4278
- draw();
4318
+ scheduleDraw();
4279
4319
  return true;
4280
4320
  }
4281
4321
  const defaults = getDrawingToolDefaults("ray");
@@ -4286,7 +4326,7 @@ function createChart(element, options = {}) {
4286
4326
  style: defaults.style ?? "solid",
4287
4327
  width: defaults.width ?? 2
4288
4328
  });
4289
- draw();
4329
+ scheduleDraw();
4290
4330
  return true;
4291
4331
  }
4292
4332
  if (activeDrawingTool === "fib-retracement") {
@@ -4299,7 +4339,7 @@ function createChart(element, options = {}) {
4299
4339
  draftDrawing = null;
4300
4340
  activeDrawingTool = null;
4301
4341
  emitDrawingsChange();
4302
- draw();
4342
+ scheduleDraw();
4303
4343
  return true;
4304
4344
  }
4305
4345
  const defaults = getDrawingToolDefaults("fib-retracement");
@@ -4311,7 +4351,7 @@ function createChart(element, options = {}) {
4311
4351
  style: defaults.style ?? "solid",
4312
4352
  width: defaults.width ?? 1
4313
4353
  });
4314
- draw();
4354
+ scheduleDraw();
4315
4355
  return true;
4316
4356
  }
4317
4357
  if (activeDrawingTool === "fib-extension") {
@@ -4321,7 +4361,7 @@ function createChart(element, options = {}) {
4321
4361
  ...serializeDrawing(draftDrawing),
4322
4362
  points: [...draftDrawing.points.slice(0, -1), point, point]
4323
4363
  });
4324
- draw();
4364
+ scheduleDraw();
4325
4365
  return true;
4326
4366
  }
4327
4367
  const completed = normalizeDrawingState({
@@ -4332,7 +4372,7 @@ function createChart(element, options = {}) {
4332
4372
  draftDrawing = null;
4333
4373
  activeDrawingTool = null;
4334
4374
  emitDrawingsChange();
4335
- draw();
4375
+ scheduleDraw();
4336
4376
  return true;
4337
4377
  }
4338
4378
  const defaults = getDrawingToolDefaults("fib-extension");
@@ -4344,7 +4384,7 @@ function createChart(element, options = {}) {
4344
4384
  style: defaults.style ?? "solid",
4345
4385
  width: defaults.width ?? 1
4346
4386
  });
4347
- draw();
4387
+ scheduleDraw();
4348
4388
  return true;
4349
4389
  }
4350
4390
  if (activeDrawingTool === "text" || activeDrawingTool === "note") {
@@ -4361,7 +4401,7 @@ function createChart(element, options = {}) {
4361
4401
  drawings.push(created);
4362
4402
  selectedDrawingId = created.id;
4363
4403
  emitDrawingsChange();
4364
- draw();
4404
+ scheduleDraw();
4365
4405
  drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4366
4406
  return true;
4367
4407
  }
@@ -4381,7 +4421,7 @@ function createChart(element, options = {}) {
4381
4421
  })
4382
4422
  );
4383
4423
  emitDrawingsChange();
4384
- draw();
4424
+ scheduleDraw();
4385
4425
  return true;
4386
4426
  }
4387
4427
  if (activeDrawingTool === "long-position" || activeDrawingTool === "short-position") {
@@ -4417,7 +4457,7 @@ function createChart(element, options = {}) {
4417
4457
  })
4418
4458
  );
4419
4459
  emitDrawingsChange();
4420
- draw();
4460
+ scheduleDraw();
4421
4461
  return true;
4422
4462
  }
4423
4463
  return false;
@@ -4434,7 +4474,7 @@ function createChart(element, options = {}) {
4434
4474
  return;
4435
4475
  }
4436
4476
  selectedDrawingId = id;
4437
- draw();
4477
+ scheduleDraw();
4438
4478
  };
4439
4479
  const updateDrawingDrag = (x, y) => {
4440
4480
  if (!drawingDragState) {
@@ -4493,7 +4533,7 @@ function createChart(element, options = {}) {
4493
4533
  };
4494
4534
  });
4495
4535
  emitDrawingsChange();
4496
- draw();
4536
+ scheduleDraw();
4497
4537
  return true;
4498
4538
  };
4499
4539
  let isDragging = false;
@@ -4558,7 +4598,7 @@ function createChart(element, options = {}) {
4558
4598
  clampXViewport();
4559
4599
  updateFollowLatest(false);
4560
4600
  emitViewportChange();
4561
- draw();
4601
+ scheduleDraw();
4562
4602
  };
4563
4603
  const onPointerDown = (event) => {
4564
4604
  if (event.pointerType === "touch" || event.pointerType === "pen") {
@@ -4742,7 +4782,7 @@ function createChart(element, options = {}) {
4742
4782
  points: [...draftDrawing.points.slice(0, -1), nextPoint]
4743
4783
  };
4744
4784
  canvas.style.cursor = "crosshair";
4745
- draw();
4785
+ scheduleDraw();
4746
4786
  return;
4747
4787
  }
4748
4788
  }
@@ -4770,7 +4810,7 @@ function createChart(element, options = {}) {
4770
4810
  dragging: true,
4771
4811
  ...currentLine ? { line: currentLine } : {}
4772
4812
  });
4773
- draw();
4813
+ scheduleDraw();
4774
4814
  }
4775
4815
  canvas.style.cursor = "ns-resize";
4776
4816
  setCrosshairPoint(null);
@@ -5032,7 +5072,7 @@ function createChart(element, options = {}) {
5032
5072
  }
5033
5073
  if (region === "y-axis") {
5034
5074
  resetYViewport();
5035
- draw();
5075
+ scheduleDraw();
5036
5076
  return;
5037
5077
  }
5038
5078
  resetViewport();
@@ -5054,7 +5094,7 @@ function createChart(element, options = {}) {
5054
5094
  const changed = active !== magnetModifierActive || shift !== shiftKeyActive;
5055
5095
  magnetModifierActive = active;
5056
5096
  shiftKeyActive = shift;
5057
- if (changed && draftDrawing) draw();
5097
+ if (changed && draftDrawing) scheduleDraw();
5058
5098
  };
5059
5099
  const onWindowBlurMagnet = () => {
5060
5100
  magnetModifierActive = false;
@@ -5090,7 +5130,7 @@ function createChart(element, options = {}) {
5090
5130
  const lastPoint = data[data.length - 1];
5091
5131
  pushSmoothedTicker(lastPoint.c, lastPoint.v);
5092
5132
  }
5093
- draw();
5133
+ scheduleDraw();
5094
5134
  };
5095
5135
  const resize = (nextWidth, nextHeight) => {
5096
5136
  if (nextWidth && nextWidth > 0) {
@@ -5101,7 +5141,7 @@ function createChart(element, options = {}) {
5101
5141
  }
5102
5142
  mergedOptions = { ...mergedOptions, width, height };
5103
5143
  resetRightMarginCache();
5104
- draw();
5144
+ scheduleDraw();
5105
5145
  };
5106
5146
  const setData = (nextData) => {
5107
5147
  const hadData = data.length > 0;
@@ -5119,7 +5159,7 @@ function createChart(element, options = {}) {
5119
5159
  tickerVolumeTarget = null;
5120
5160
  resetRightMarginCache();
5121
5161
  resetYViewport();
5122
- draw();
5162
+ scheduleDraw();
5123
5163
  return;
5124
5164
  }
5125
5165
  if (!hadData) {
@@ -5144,7 +5184,7 @@ function createChart(element, options = {}) {
5144
5184
  if (lastPoint) {
5145
5185
  pushSmoothedTicker(lastPoint.c, lastPoint.v);
5146
5186
  }
5147
- draw();
5187
+ scheduleDraw();
5148
5188
  };
5149
5189
  const setPriceLines = (lines) => {
5150
5190
  priceLines = lines.map((line, index) => ({
@@ -5152,18 +5192,18 @@ function createChart(element, options = {}) {
5152
5192
  id: line.id ?? `line-${index + 1}`
5153
5193
  }));
5154
5194
  resetRightMarginCache();
5155
- draw();
5195
+ scheduleDraw();
5156
5196
  };
5157
5197
  const addPriceLine = (line) => {
5158
5198
  const id = line.id ?? `line-${generatedPriceLineId++}`;
5159
5199
  priceLines.push({ ...line, id });
5160
- draw();
5200
+ scheduleDraw();
5161
5201
  return id;
5162
5202
  };
5163
5203
  const removePriceLine = (id) => {
5164
5204
  priceLines = priceLines.filter((line) => line.id !== id);
5165
5205
  resetRightMarginCache();
5166
- draw();
5206
+ scheduleDraw();
5167
5207
  };
5168
5208
  const setOrderLines = (lines) => {
5169
5209
  orderLines = lines.map((line, index) => ({
@@ -5182,24 +5222,24 @@ function createChart(element, options = {}) {
5182
5222
  orderPriceTagWidthById.delete(id);
5183
5223
  }
5184
5224
  }
5185
- draw();
5225
+ scheduleDraw();
5186
5226
  };
5187
5227
  const addOrderLine = (line) => {
5188
5228
  const id = line.id ?? `order-${generatedOrderLineId++}`;
5189
5229
  orderLines.push({ ...line, id });
5190
- draw();
5230
+ scheduleDraw();
5191
5231
  return id;
5192
5232
  };
5193
5233
  const updateOrderLine = (id, patch) => {
5194
5234
  orderLines = orderLines.map((line) => line.id === id ? { ...line, ...patch, id } : line);
5195
- draw();
5235
+ scheduleDraw();
5196
5236
  };
5197
5237
  const removeOrderLine = (id) => {
5198
5238
  orderLines = orderLines.filter((line) => line.id !== id);
5199
5239
  orderWidgetWidthById.delete(id);
5200
5240
  orderPriceTagWidthById.delete(id);
5201
5241
  resetRightMarginCache();
5202
- draw();
5242
+ scheduleDraw();
5203
5243
  };
5204
5244
  const onOrderAction = (handler) => {
5205
5245
  orderActionHandler = handler;
@@ -5224,12 +5264,12 @@ function createChart(element, options = {}) {
5224
5264
  throw new Error("Invalid indicator plugin. Expected { id, draw }.");
5225
5265
  }
5226
5266
  indicatorRegistry.set(plugin.id, plugin);
5227
- draw();
5267
+ scheduleDraw();
5228
5268
  };
5229
5269
  const unregisterIndicator = (type) => {
5230
5270
  indicatorRegistry.delete(type);
5231
5271
  indicators = indicators.filter((indicator) => indicator.type !== type);
5232
- draw();
5272
+ scheduleDraw();
5233
5273
  };
5234
5274
  const listBuiltInIndicators = () => {
5235
5275
  return BUILTIN_INDICATORS.map((indicator) => ({
@@ -5254,7 +5294,7 @@ function createChart(element, options = {}) {
5254
5294
  };
5255
5295
  const setIndicators = (nextIndicators) => {
5256
5296
  indicators = nextIndicators.map((indicator) => normalizeIndicatorState(indicator));
5257
- draw();
5297
+ scheduleDraw();
5258
5298
  };
5259
5299
  const addIndicator = (type, inputs = {}, options2 = {}) => {
5260
5300
  const plugin = indicatorRegistry.get(type);
@@ -5267,7 +5307,7 @@ function createChart(element, options = {}) {
5267
5307
  inputs
5268
5308
  });
5269
5309
  indicators.push(next);
5270
- draw();
5310
+ scheduleDraw();
5271
5311
  return next.id;
5272
5312
  };
5273
5313
  const updateIndicator = (id, patch) => {
@@ -5291,30 +5331,30 @@ function createChart(element, options = {}) {
5291
5331
  }
5292
5332
  };
5293
5333
  });
5294
- draw();
5334
+ scheduleDraw();
5295
5335
  };
5296
5336
  const removeIndicator = (id) => {
5297
5337
  indicators = indicators.filter((indicator) => indicator.id !== id);
5298
- draw();
5338
+ scheduleDraw();
5299
5339
  };
5300
5340
  const setActiveDrawingTool = (tool) => {
5301
5341
  activeDrawingTool = tool;
5302
5342
  draftDrawing = null;
5303
5343
  canvas.style.cursor = tool ? "crosshair" : "default";
5304
- draw();
5344
+ scheduleDraw();
5305
5345
  };
5306
5346
  const getActiveDrawingTool = () => activeDrawingTool;
5307
5347
  const cancelDrawing = () => {
5308
5348
  if (draftDrawing) {
5309
5349
  draftDrawing = null;
5310
- draw();
5350
+ scheduleDraw();
5311
5351
  return true;
5312
5352
  }
5313
5353
  return false;
5314
5354
  };
5315
5355
  const setTradeMarkers = (markers) => {
5316
5356
  tradeMarkers = Array.isArray(markers) ? markers.slice() : [];
5317
- draw();
5357
+ scheduleDraw();
5318
5358
  };
5319
5359
  const setMagnetMode = (mode) => {
5320
5360
  magnetMode = mode;
@@ -5322,16 +5362,16 @@ function createChart(element, options = {}) {
5322
5362
  const getMagnetMode = () => magnetMode;
5323
5363
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5324
5364
  const setDrawings = (nextDrawings) => {
5325
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5365
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5326
5366
  draftDrawing = null;
5327
5367
  emitDrawingsChange();
5328
- draw();
5368
+ scheduleDraw();
5329
5369
  };
5330
5370
  const addDrawing = (drawing) => {
5331
5371
  const next = normalizeDrawingState(drawing);
5332
5372
  drawings.push(next);
5333
5373
  emitDrawingsChange();
5334
- draw();
5374
+ scheduleDraw();
5335
5375
  return next.id;
5336
5376
  };
5337
5377
  const updateDrawing = (id, patch) => {
@@ -5344,18 +5384,18 @@ function createChart(element, options = {}) {
5344
5384
  }) : drawing
5345
5385
  );
5346
5386
  emitDrawingsChange();
5347
- draw();
5387
+ scheduleDraw();
5348
5388
  };
5349
5389
  const removeDrawing = (id) => {
5350
5390
  drawings = drawings.filter((drawing) => drawing.id !== id);
5351
5391
  emitDrawingsChange();
5352
- draw();
5392
+ scheduleDraw();
5353
5393
  };
5354
5394
  const clearDrawings = () => {
5355
5395
  drawings = [];
5356
5396
  draftDrawing = null;
5357
5397
  emitDrawingsChange();
5358
- draw();
5398
+ scheduleDraw();
5359
5399
  };
5360
5400
  const onDrawingsChange = (handler) => {
5361
5401
  drawingsChangeHandler = handler;
@@ -5377,6 +5417,11 @@ function createChart(element, options = {}) {
5377
5417
  cancelAnimationFrame(smoothingRafId);
5378
5418
  smoothingRafId = null;
5379
5419
  }
5420
+ if (drawRafId !== null) {
5421
+ cancelAnimationFrame(drawRafId);
5422
+ drawRafId = null;
5423
+ pendingDrawUpdateAutoScale = false;
5424
+ }
5380
5425
  canvas.removeEventListener("pointerdown", onPointerDown);
5381
5426
  canvas.removeEventListener("pointermove", onPointerMove);
5382
5427
  canvas.removeEventListener("pointerup", endPointerDrag);