hyperprop-charting-library 0.1.108 → 0.1.110

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;
@@ -3918,13 +3944,14 @@ function createChart(element, options = {}) {
3918
3944
  }
3919
3945
  return { index: barIndex, price: nearest };
3920
3946
  };
3921
- const drawingPointFromCanvas = (x, y) => {
3947
+ const drawingPointFromCanvas = (x, y, snap = true) => {
3922
3948
  if (!drawState) {
3923
3949
  return null;
3924
3950
  }
3925
3951
  const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
3926
3952
  const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
3927
- const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
3953
+ const rawPrice = priceFromCanvasY(y);
3954
+ const snapped = snap ? applyMagnet(y, rawIndex, rawPrice) : { index: rawIndex, price: rawPrice };
3928
3955
  const nearestIndex = Math.round(snapped.index);
3929
3956
  const time = getTimeForIndex(nearestIndex);
3930
3957
  return {
@@ -4454,7 +4481,7 @@ function createChart(element, options = {}) {
4454
4481
  if (!drawingDragState) {
4455
4482
  return false;
4456
4483
  }
4457
- const currentPoint = drawingPointFromCanvas(x, y);
4484
+ const currentPoint = drawingPointFromCanvas(x, y, drawingDragState.target === "handle");
4458
4485
  if (!currentPoint) {
4459
4486
  return true;
4460
4487
  }
@@ -4653,7 +4680,7 @@ function createChart(element, options = {}) {
4653
4680
  y: point.y
4654
4681
  });
4655
4682
  if (!drawingHit.drawing.locked) {
4656
- const startCanvasPoint = drawingPointFromCanvas(point.x, point.y);
4683
+ const startCanvasPoint = drawingPointFromCanvas(point.x, point.y, false);
4657
4684
  if (startCanvasPoint) {
4658
4685
  drawingDragState = {
4659
4686
  drawingId: drawingHit.drawing.id,
@@ -5336,7 +5363,7 @@ function createChart(element, options = {}) {
5336
5363
  const getMagnetMode = () => magnetMode;
5337
5364
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5338
5365
  const setDrawings = (nextDrawings) => {
5339
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5366
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5340
5367
  draftDrawing = null;
5341
5368
  emitDrawingsChange();
5342
5369
  scheduleDraw();
@@ -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;
@@ -3892,13 +3918,14 @@ function createChart(element, options = {}) {
3892
3918
  }
3893
3919
  return { index: barIndex, price: nearest };
3894
3920
  };
3895
- const drawingPointFromCanvas = (x, y) => {
3921
+ const drawingPointFromCanvas = (x, y, snap = true) => {
3896
3922
  if (!drawState) {
3897
3923
  return null;
3898
3924
  }
3899
3925
  const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
3900
3926
  const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
3901
- const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
3927
+ const rawPrice = priceFromCanvasY(y);
3928
+ const snapped = snap ? applyMagnet(y, rawIndex, rawPrice) : { index: rawIndex, price: rawPrice };
3902
3929
  const nearestIndex = Math.round(snapped.index);
3903
3930
  const time = getTimeForIndex(nearestIndex);
3904
3931
  return {
@@ -4428,7 +4455,7 @@ function createChart(element, options = {}) {
4428
4455
  if (!drawingDragState) {
4429
4456
  return false;
4430
4457
  }
4431
- const currentPoint = drawingPointFromCanvas(x, y);
4458
+ const currentPoint = drawingPointFromCanvas(x, y, drawingDragState.target === "handle");
4432
4459
  if (!currentPoint) {
4433
4460
  return true;
4434
4461
  }
@@ -4627,7 +4654,7 @@ function createChart(element, options = {}) {
4627
4654
  y: point.y
4628
4655
  });
4629
4656
  if (!drawingHit.drawing.locked) {
4630
- const startCanvasPoint = drawingPointFromCanvas(point.x, point.y);
4657
+ const startCanvasPoint = drawingPointFromCanvas(point.x, point.y, false);
4631
4658
  if (startCanvasPoint) {
4632
4659
  drawingDragState = {
4633
4660
  drawingId: drawingHit.drawing.id,
@@ -5310,7 +5337,7 @@ function createChart(element, options = {}) {
5310
5337
  const getMagnetMode = () => magnetMode;
5311
5338
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5312
5339
  const setDrawings = (nextDrawings) => {
5313
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5340
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5314
5341
  draftDrawing = null;
5315
5342
  emitDrawingsChange();
5316
5343
  scheduleDraw();
package/dist/index.cjs CHANGED
@@ -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;
@@ -3918,13 +3944,14 @@ function createChart(element, options = {}) {
3918
3944
  }
3919
3945
  return { index: barIndex, price: nearest };
3920
3946
  };
3921
- const drawingPointFromCanvas = (x, y) => {
3947
+ const drawingPointFromCanvas = (x, y, snap = true) => {
3922
3948
  if (!drawState) {
3923
3949
  return null;
3924
3950
  }
3925
3951
  const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
3926
3952
  const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
3927
- const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
3953
+ const rawPrice = priceFromCanvasY(y);
3954
+ const snapped = snap ? applyMagnet(y, rawIndex, rawPrice) : { index: rawIndex, price: rawPrice };
3928
3955
  const nearestIndex = Math.round(snapped.index);
3929
3956
  const time = getTimeForIndex(nearestIndex);
3930
3957
  return {
@@ -4454,7 +4481,7 @@ function createChart(element, options = {}) {
4454
4481
  if (!drawingDragState) {
4455
4482
  return false;
4456
4483
  }
4457
- const currentPoint = drawingPointFromCanvas(x, y);
4484
+ const currentPoint = drawingPointFromCanvas(x, y, drawingDragState.target === "handle");
4458
4485
  if (!currentPoint) {
4459
4486
  return true;
4460
4487
  }
@@ -4653,7 +4680,7 @@ function createChart(element, options = {}) {
4653
4680
  y: point.y
4654
4681
  });
4655
4682
  if (!drawingHit.drawing.locked) {
4656
- const startCanvasPoint = drawingPointFromCanvas(point.x, point.y);
4683
+ const startCanvasPoint = drawingPointFromCanvas(point.x, point.y, false);
4657
4684
  if (startCanvasPoint) {
4658
4685
  drawingDragState = {
4659
4686
  drawingId: drawingHit.drawing.id,
@@ -5336,7 +5363,7 @@ function createChart(element, options = {}) {
5336
5363
  const getMagnetMode = () => magnetMode;
5337
5364
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5338
5365
  const setDrawings = (nextDrawings) => {
5339
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5366
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5340
5367
  draftDrawing = null;
5341
5368
  emitDrawingsChange();
5342
5369
  scheduleDraw();
package/dist/index.js CHANGED
@@ -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;
@@ -3892,13 +3918,14 @@ function createChart(element, options = {}) {
3892
3918
  }
3893
3919
  return { index: barIndex, price: nearest };
3894
3920
  };
3895
- const drawingPointFromCanvas = (x, y) => {
3921
+ const drawingPointFromCanvas = (x, y, snap = true) => {
3896
3922
  if (!drawState) {
3897
3923
  return null;
3898
3924
  }
3899
3925
  const ratio = clamp((x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
3900
3926
  const rawIndex = drawState.xStart + ratio * drawState.xSpan - 0.5;
3901
- const snapped = applyMagnet(y, rawIndex, priceFromCanvasY(y));
3927
+ const rawPrice = priceFromCanvasY(y);
3928
+ const snapped = snap ? applyMagnet(y, rawIndex, rawPrice) : { index: rawIndex, price: rawPrice };
3902
3929
  const nearestIndex = Math.round(snapped.index);
3903
3930
  const time = getTimeForIndex(nearestIndex);
3904
3931
  return {
@@ -4428,7 +4455,7 @@ function createChart(element, options = {}) {
4428
4455
  if (!drawingDragState) {
4429
4456
  return false;
4430
4457
  }
4431
- const currentPoint = drawingPointFromCanvas(x, y);
4458
+ const currentPoint = drawingPointFromCanvas(x, y, drawingDragState.target === "handle");
4432
4459
  if (!currentPoint) {
4433
4460
  return true;
4434
4461
  }
@@ -4627,7 +4654,7 @@ function createChart(element, options = {}) {
4627
4654
  y: point.y
4628
4655
  });
4629
4656
  if (!drawingHit.drawing.locked) {
4630
- const startCanvasPoint = drawingPointFromCanvas(point.x, point.y);
4657
+ const startCanvasPoint = drawingPointFromCanvas(point.x, point.y, false);
4631
4658
  if (startCanvasPoint) {
4632
4659
  drawingDragState = {
4633
4660
  drawingId: drawingHit.drawing.id,
@@ -5310,7 +5337,7 @@ function createChart(element, options = {}) {
5310
5337
  const getMagnetMode = () => magnetMode;
5311
5338
  const getDrawings = () => drawings.map((drawing) => serializeDrawing(drawing));
5312
5339
  const setDrawings = (nextDrawings) => {
5313
- drawings = nextDrawings.map((drawing) => normalizeDrawingState(drawing));
5340
+ drawings = dedupeDrawingIds(nextDrawings.map((drawing) => normalizeDrawingState(drawing)));
5314
5341
  draftDrawing = null;
5315
5342
  emitDrawingsChange();
5316
5343
  scheduleDraw();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.108",
3
+ "version": "0.1.110",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",