hyperprop-charting-library 0.1.85 → 0.1.87

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.
@@ -968,6 +968,7 @@ function createChart(element, options = {}) {
968
968
  leverage: Number(drawing.leverage) || 1,
969
969
  pointValue: Number(drawing.pointValue) || 1,
970
970
  qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
971
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
971
972
  ...drawing.label === void 0 ? {} : { label: drawing.label }
972
973
  });
973
974
  const serializeDrawing = (drawing) => ({
@@ -987,6 +988,7 @@ function createChart(element, options = {}) {
987
988
  leverage: drawing.leverage,
988
989
  pointValue: drawing.pointValue,
989
990
  qtyPrecision: drawing.qtyPrecision,
991
+ fontSize: drawing.fontSize,
990
992
  ...drawing.label === void 0 ? {} : { label: drawing.label }
991
993
  });
992
994
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -997,6 +999,7 @@ function createChart(element, options = {}) {
997
999
  let drawingsChangeHandler = null;
998
1000
  let drawingSelectHandler = null;
999
1001
  let drawingDoubleClickHandler = null;
1002
+ let drawingEditTextHandler = null;
1000
1003
  let drawingHoverHandler = null;
1001
1004
  let lastHoveredDrawingId = null;
1002
1005
  let selectedDrawingId = null;
@@ -2635,6 +2638,46 @@ function createChart(element, options = {}) {
2635
2638
  drawDrawingLabel(drawing.label, midX, topY - 4, drawing.color);
2636
2639
  }
2637
2640
  }
2641
+ } else if (drawing.type === "text" || drawing.type === "note") {
2642
+ const anchor = drawing.points[0];
2643
+ if (anchor) {
2644
+ const ax = xFromDrawingPoint(anchor);
2645
+ const ay = yFromPrice(anchor.price);
2646
+ const isNote = drawing.type === "note";
2647
+ const fontSize = Math.max(6, drawing.fontSize);
2648
+ const prevFont = ctx.font;
2649
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
2650
+ ctx.textAlign = "left";
2651
+ ctx.textBaseline = "top";
2652
+ const lines = (drawing.label ?? "").split("\n");
2653
+ const lineH = Math.round(fontSize * 1.35);
2654
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
2655
+ const padX = isNote ? 8 : 2;
2656
+ const padY = isNote ? 6 : 1;
2657
+ const blockW = textW + padX * 2;
2658
+ const blockH = lines.length * lineH + padY * 2;
2659
+ ctx.save();
2660
+ ctx.globalAlpha = draft ? 0.7 : 1;
2661
+ if (isNote) {
2662
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2663
+ fillRoundedRect(ax, ay, blockW, blockH, 4);
2664
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.5);
2665
+ ctx.lineWidth = 1;
2666
+ strokeRoundedRect(ax, ay, blockW, blockH, 4);
2667
+ }
2668
+ ctx.fillStyle = drawing.color;
2669
+ lines.forEach((line, lineIndex) => ctx.fillText(line, ax + padX, ay + padY + lineIndex * lineH));
2670
+ ctx.restore();
2671
+ ctx.font = prevFont;
2672
+ if (isSelected) {
2673
+ ctx.save();
2674
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.9);
2675
+ ctx.lineWidth = 1;
2676
+ ctx.setLineDash([]);
2677
+ strokeRoundedRect(ax - 2, ay - 2, blockW + 4, blockH + 4, 3);
2678
+ ctx.restore();
2679
+ }
2680
+ }
2638
2681
  }
2639
2682
  ctx.restore();
2640
2683
  };
@@ -3883,6 +3926,27 @@ function createChart(element, options = {}) {
3883
3926
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3884
3927
  return { drawing, target: "line" };
3885
3928
  }
3929
+ } else if (drawing.type === "text" || drawing.type === "note") {
3930
+ const anchor = drawing.points[0];
3931
+ if (!anchor) continue;
3932
+ const ax = canvasXFromDrawingPoint(anchor);
3933
+ const ay = canvasYFromDrawingPrice(anchor.price);
3934
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3935
+ const fontSize = Math.max(6, drawing.fontSize);
3936
+ const prevFont = ctx.font;
3937
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3938
+ const lines = (drawing.label ?? "").split("\n");
3939
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3940
+ ctx.font = prevFont;
3941
+ const isNote = drawing.type === "note";
3942
+ const padX = isNote ? 8 : 2;
3943
+ const padY = isNote ? 6 : 1;
3944
+ const lineH = Math.round(fontSize * 1.35);
3945
+ const blockW = textW + padX * 2;
3946
+ const blockH = lines.length * lineH + padY * 2;
3947
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3948
+ return { drawing, target: "line" };
3949
+ }
3886
3950
  }
3887
3951
  }
3888
3952
  return null;
@@ -4083,6 +4147,24 @@ function createChart(element, options = {}) {
4083
4147
  draw();
4084
4148
  return true;
4085
4149
  }
4150
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4151
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4152
+ const created = normalizeDrawingState({
4153
+ type: activeDrawingTool,
4154
+ points: [point],
4155
+ color: defaults.color ?? "#e2e8f0",
4156
+ style: defaults.style ?? "solid",
4157
+ width: defaults.width ?? 1,
4158
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4159
+ label: ""
4160
+ });
4161
+ drawings.push(created);
4162
+ selectedDrawingId = created.id;
4163
+ emitDrawingsChange();
4164
+ draw();
4165
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4166
+ return true;
4167
+ }
4086
4168
  if (activeDrawingTool === "price-range") {
4087
4169
  const tick = getConfiguredTickSize();
4088
4170
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4705,13 +4787,18 @@ function createChart(element, options = {}) {
4705
4787
  const drawingHit = getDrawingHit(point.x, point.y);
4706
4788
  if (drawingHit) {
4707
4789
  selectedDrawingId = drawingHit.drawing.id;
4708
- drawingDoubleClickHandler?.({
4790
+ const payload = {
4709
4791
  drawing: serializeDrawing(drawingHit.drawing),
4710
4792
  target: drawingHit.target,
4711
4793
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4712
4794
  x: point.x,
4713
4795
  y: point.y
4714
- });
4796
+ };
4797
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4798
+ drawingEditTextHandler?.(payload);
4799
+ } else {
4800
+ drawingDoubleClickHandler?.(payload);
4801
+ }
4715
4802
  return;
4716
4803
  }
4717
4804
  }
@@ -5028,6 +5115,9 @@ function createChart(element, options = {}) {
5028
5115
  const onDrawingDoubleClick = (handler) => {
5029
5116
  drawingDoubleClickHandler = handler;
5030
5117
  };
5118
+ const onDrawingEditText = (handler) => {
5119
+ drawingEditTextHandler = handler;
5120
+ };
5031
5121
  const onDrawingHover = (handler) => {
5032
5122
  drawingHoverHandler = handler;
5033
5123
  };
@@ -5086,6 +5176,7 @@ function createChart(element, options = {}) {
5086
5176
  onDrawingsChange,
5087
5177
  onDrawingSelect,
5088
5178
  onDrawingDoubleClick,
5179
+ onDrawingEditText,
5089
5180
  setSelectedDrawing,
5090
5181
  onDrawingHover,
5091
5182
  setDrawingDefaults,
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
@@ -73,6 +73,7 @@ interface DrawingObjectOptions {
73
73
  leverage?: number;
74
74
  pointValue?: number;
75
75
  qtyPrecision?: number;
76
+ fontSize?: number;
76
77
  }
77
78
  /** Default colors for position tools: [profit, loss, label text]. */
78
79
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -91,7 +92,7 @@ interface DrawingHoverEvent {
91
92
  x: number;
92
93
  y: number;
93
94
  }
94
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
95
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision" | "fontSize">>;
95
96
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
96
97
  id?: string;
97
98
  type: string;
@@ -428,6 +429,7 @@ interface ChartInstance {
428
429
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
429
430
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
431
  onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
432
+ onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
431
433
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
432
434
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
433
435
  setSelectedDrawing: (id: string | null) => void;
@@ -942,6 +942,7 @@ function createChart(element, options = {}) {
942
942
  leverage: Number(drawing.leverage) || 1,
943
943
  pointValue: Number(drawing.pointValue) || 1,
944
944
  qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
945
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
945
946
  ...drawing.label === void 0 ? {} : { label: drawing.label }
946
947
  });
947
948
  const serializeDrawing = (drawing) => ({
@@ -961,6 +962,7 @@ function createChart(element, options = {}) {
961
962
  leverage: drawing.leverage,
962
963
  pointValue: drawing.pointValue,
963
964
  qtyPrecision: drawing.qtyPrecision,
965
+ fontSize: drawing.fontSize,
964
966
  ...drawing.label === void 0 ? {} : { label: drawing.label }
965
967
  });
966
968
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -971,6 +973,7 @@ function createChart(element, options = {}) {
971
973
  let drawingsChangeHandler = null;
972
974
  let drawingSelectHandler = null;
973
975
  let drawingDoubleClickHandler = null;
976
+ let drawingEditTextHandler = null;
974
977
  let drawingHoverHandler = null;
975
978
  let lastHoveredDrawingId = null;
976
979
  let selectedDrawingId = null;
@@ -2609,6 +2612,46 @@ function createChart(element, options = {}) {
2609
2612
  drawDrawingLabel(drawing.label, midX, topY - 4, drawing.color);
2610
2613
  }
2611
2614
  }
2615
+ } else if (drawing.type === "text" || drawing.type === "note") {
2616
+ const anchor = drawing.points[0];
2617
+ if (anchor) {
2618
+ const ax = xFromDrawingPoint(anchor);
2619
+ const ay = yFromPrice(anchor.price);
2620
+ const isNote = drawing.type === "note";
2621
+ const fontSize = Math.max(6, drawing.fontSize);
2622
+ const prevFont = ctx.font;
2623
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
2624
+ ctx.textAlign = "left";
2625
+ ctx.textBaseline = "top";
2626
+ const lines = (drawing.label ?? "").split("\n");
2627
+ const lineH = Math.round(fontSize * 1.35);
2628
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
2629
+ const padX = isNote ? 8 : 2;
2630
+ const padY = isNote ? 6 : 1;
2631
+ const blockW = textW + padX * 2;
2632
+ const blockH = lines.length * lineH + padY * 2;
2633
+ ctx.save();
2634
+ ctx.globalAlpha = draft ? 0.7 : 1;
2635
+ if (isNote) {
2636
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2637
+ fillRoundedRect(ax, ay, blockW, blockH, 4);
2638
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.5);
2639
+ ctx.lineWidth = 1;
2640
+ strokeRoundedRect(ax, ay, blockW, blockH, 4);
2641
+ }
2642
+ ctx.fillStyle = drawing.color;
2643
+ lines.forEach((line, lineIndex) => ctx.fillText(line, ax + padX, ay + padY + lineIndex * lineH));
2644
+ ctx.restore();
2645
+ ctx.font = prevFont;
2646
+ if (isSelected) {
2647
+ ctx.save();
2648
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.9);
2649
+ ctx.lineWidth = 1;
2650
+ ctx.setLineDash([]);
2651
+ strokeRoundedRect(ax - 2, ay - 2, blockW + 4, blockH + 4, 3);
2652
+ ctx.restore();
2653
+ }
2654
+ }
2612
2655
  }
2613
2656
  ctx.restore();
2614
2657
  };
@@ -3857,6 +3900,27 @@ function createChart(element, options = {}) {
3857
3900
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3858
3901
  return { drawing, target: "line" };
3859
3902
  }
3903
+ } else if (drawing.type === "text" || drawing.type === "note") {
3904
+ const anchor = drawing.points[0];
3905
+ if (!anchor) continue;
3906
+ const ax = canvasXFromDrawingPoint(anchor);
3907
+ const ay = canvasYFromDrawingPrice(anchor.price);
3908
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3909
+ const fontSize = Math.max(6, drawing.fontSize);
3910
+ const prevFont = ctx.font;
3911
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3912
+ const lines = (drawing.label ?? "").split("\n");
3913
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3914
+ ctx.font = prevFont;
3915
+ const isNote = drawing.type === "note";
3916
+ const padX = isNote ? 8 : 2;
3917
+ const padY = isNote ? 6 : 1;
3918
+ const lineH = Math.round(fontSize * 1.35);
3919
+ const blockW = textW + padX * 2;
3920
+ const blockH = lines.length * lineH + padY * 2;
3921
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3922
+ return { drawing, target: "line" };
3923
+ }
3860
3924
  }
3861
3925
  }
3862
3926
  return null;
@@ -4057,6 +4121,24 @@ function createChart(element, options = {}) {
4057
4121
  draw();
4058
4122
  return true;
4059
4123
  }
4124
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4125
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4126
+ const created = normalizeDrawingState({
4127
+ type: activeDrawingTool,
4128
+ points: [point],
4129
+ color: defaults.color ?? "#e2e8f0",
4130
+ style: defaults.style ?? "solid",
4131
+ width: defaults.width ?? 1,
4132
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4133
+ label: ""
4134
+ });
4135
+ drawings.push(created);
4136
+ selectedDrawingId = created.id;
4137
+ emitDrawingsChange();
4138
+ draw();
4139
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4140
+ return true;
4141
+ }
4060
4142
  if (activeDrawingTool === "price-range") {
4061
4143
  const tick = getConfiguredTickSize();
4062
4144
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4679,13 +4761,18 @@ function createChart(element, options = {}) {
4679
4761
  const drawingHit = getDrawingHit(point.x, point.y);
4680
4762
  if (drawingHit) {
4681
4763
  selectedDrawingId = drawingHit.drawing.id;
4682
- drawingDoubleClickHandler?.({
4764
+ const payload = {
4683
4765
  drawing: serializeDrawing(drawingHit.drawing),
4684
4766
  target: drawingHit.target,
4685
4767
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4686
4768
  x: point.x,
4687
4769
  y: point.y
4688
- });
4770
+ };
4771
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4772
+ drawingEditTextHandler?.(payload);
4773
+ } else {
4774
+ drawingDoubleClickHandler?.(payload);
4775
+ }
4689
4776
  return;
4690
4777
  }
4691
4778
  }
@@ -5002,6 +5089,9 @@ function createChart(element, options = {}) {
5002
5089
  const onDrawingDoubleClick = (handler) => {
5003
5090
  drawingDoubleClickHandler = handler;
5004
5091
  };
5092
+ const onDrawingEditText = (handler) => {
5093
+ drawingEditTextHandler = handler;
5094
+ };
5005
5095
  const onDrawingHover = (handler) => {
5006
5096
  drawingHoverHandler = handler;
5007
5097
  };
@@ -5060,6 +5150,7 @@ function createChart(element, options = {}) {
5060
5150
  onDrawingsChange,
5061
5151
  onDrawingSelect,
5062
5152
  onDrawingDoubleClick,
5153
+ onDrawingEditText,
5063
5154
  setSelectedDrawing,
5064
5155
  onDrawingHover,
5065
5156
  setDrawingDefaults,
package/dist/index.cjs CHANGED
@@ -968,6 +968,7 @@ function createChart(element, options = {}) {
968
968
  leverage: Number(drawing.leverage) || 1,
969
969
  pointValue: Number(drawing.pointValue) || 1,
970
970
  qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
971
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
971
972
  ...drawing.label === void 0 ? {} : { label: drawing.label }
972
973
  });
973
974
  const serializeDrawing = (drawing) => ({
@@ -987,6 +988,7 @@ function createChart(element, options = {}) {
987
988
  leverage: drawing.leverage,
988
989
  pointValue: drawing.pointValue,
989
990
  qtyPrecision: drawing.qtyPrecision,
991
+ fontSize: drawing.fontSize,
990
992
  ...drawing.label === void 0 ? {} : { label: drawing.label }
991
993
  });
992
994
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -997,6 +999,7 @@ function createChart(element, options = {}) {
997
999
  let drawingsChangeHandler = null;
998
1000
  let drawingSelectHandler = null;
999
1001
  let drawingDoubleClickHandler = null;
1002
+ let drawingEditTextHandler = null;
1000
1003
  let drawingHoverHandler = null;
1001
1004
  let lastHoveredDrawingId = null;
1002
1005
  let selectedDrawingId = null;
@@ -2635,6 +2638,46 @@ function createChart(element, options = {}) {
2635
2638
  drawDrawingLabel(drawing.label, midX, topY - 4, drawing.color);
2636
2639
  }
2637
2640
  }
2641
+ } else if (drawing.type === "text" || drawing.type === "note") {
2642
+ const anchor = drawing.points[0];
2643
+ if (anchor) {
2644
+ const ax = xFromDrawingPoint(anchor);
2645
+ const ay = yFromPrice(anchor.price);
2646
+ const isNote = drawing.type === "note";
2647
+ const fontSize = Math.max(6, drawing.fontSize);
2648
+ const prevFont = ctx.font;
2649
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
2650
+ ctx.textAlign = "left";
2651
+ ctx.textBaseline = "top";
2652
+ const lines = (drawing.label ?? "").split("\n");
2653
+ const lineH = Math.round(fontSize * 1.35);
2654
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
2655
+ const padX = isNote ? 8 : 2;
2656
+ const padY = isNote ? 6 : 1;
2657
+ const blockW = textW + padX * 2;
2658
+ const blockH = lines.length * lineH + padY * 2;
2659
+ ctx.save();
2660
+ ctx.globalAlpha = draft ? 0.7 : 1;
2661
+ if (isNote) {
2662
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2663
+ fillRoundedRect(ax, ay, blockW, blockH, 4);
2664
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.5);
2665
+ ctx.lineWidth = 1;
2666
+ strokeRoundedRect(ax, ay, blockW, blockH, 4);
2667
+ }
2668
+ ctx.fillStyle = drawing.color;
2669
+ lines.forEach((line, lineIndex) => ctx.fillText(line, ax + padX, ay + padY + lineIndex * lineH));
2670
+ ctx.restore();
2671
+ ctx.font = prevFont;
2672
+ if (isSelected) {
2673
+ ctx.save();
2674
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.9);
2675
+ ctx.lineWidth = 1;
2676
+ ctx.setLineDash([]);
2677
+ strokeRoundedRect(ax - 2, ay - 2, blockW + 4, blockH + 4, 3);
2678
+ ctx.restore();
2679
+ }
2680
+ }
2638
2681
  }
2639
2682
  ctx.restore();
2640
2683
  };
@@ -3883,6 +3926,27 @@ function createChart(element, options = {}) {
3883
3926
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3884
3927
  return { drawing, target: "line" };
3885
3928
  }
3929
+ } else if (drawing.type === "text" || drawing.type === "note") {
3930
+ const anchor = drawing.points[0];
3931
+ if (!anchor) continue;
3932
+ const ax = canvasXFromDrawingPoint(anchor);
3933
+ const ay = canvasYFromDrawingPrice(anchor.price);
3934
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3935
+ const fontSize = Math.max(6, drawing.fontSize);
3936
+ const prevFont = ctx.font;
3937
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3938
+ const lines = (drawing.label ?? "").split("\n");
3939
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3940
+ ctx.font = prevFont;
3941
+ const isNote = drawing.type === "note";
3942
+ const padX = isNote ? 8 : 2;
3943
+ const padY = isNote ? 6 : 1;
3944
+ const lineH = Math.round(fontSize * 1.35);
3945
+ const blockW = textW + padX * 2;
3946
+ const blockH = lines.length * lineH + padY * 2;
3947
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3948
+ return { drawing, target: "line" };
3949
+ }
3886
3950
  }
3887
3951
  }
3888
3952
  return null;
@@ -4083,6 +4147,24 @@ function createChart(element, options = {}) {
4083
4147
  draw();
4084
4148
  return true;
4085
4149
  }
4150
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4151
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4152
+ const created = normalizeDrawingState({
4153
+ type: activeDrawingTool,
4154
+ points: [point],
4155
+ color: defaults.color ?? "#e2e8f0",
4156
+ style: defaults.style ?? "solid",
4157
+ width: defaults.width ?? 1,
4158
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4159
+ label: ""
4160
+ });
4161
+ drawings.push(created);
4162
+ selectedDrawingId = created.id;
4163
+ emitDrawingsChange();
4164
+ draw();
4165
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4166
+ return true;
4167
+ }
4086
4168
  if (activeDrawingTool === "price-range") {
4087
4169
  const tick = getConfiguredTickSize();
4088
4170
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4705,13 +4787,18 @@ function createChart(element, options = {}) {
4705
4787
  const drawingHit = getDrawingHit(point.x, point.y);
4706
4788
  if (drawingHit) {
4707
4789
  selectedDrawingId = drawingHit.drawing.id;
4708
- drawingDoubleClickHandler?.({
4790
+ const payload = {
4709
4791
  drawing: serializeDrawing(drawingHit.drawing),
4710
4792
  target: drawingHit.target,
4711
4793
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4712
4794
  x: point.x,
4713
4795
  y: point.y
4714
- });
4796
+ };
4797
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4798
+ drawingEditTextHandler?.(payload);
4799
+ } else {
4800
+ drawingDoubleClickHandler?.(payload);
4801
+ }
4715
4802
  return;
4716
4803
  }
4717
4804
  }
@@ -5028,6 +5115,9 @@ function createChart(element, options = {}) {
5028
5115
  const onDrawingDoubleClick = (handler) => {
5029
5116
  drawingDoubleClickHandler = handler;
5030
5117
  };
5118
+ const onDrawingEditText = (handler) => {
5119
+ drawingEditTextHandler = handler;
5120
+ };
5031
5121
  const onDrawingHover = (handler) => {
5032
5122
  drawingHoverHandler = handler;
5033
5123
  };
@@ -5086,6 +5176,7 @@ function createChart(element, options = {}) {
5086
5176
  onDrawingsChange,
5087
5177
  onDrawingSelect,
5088
5178
  onDrawingDoubleClick,
5179
+ onDrawingEditText,
5089
5180
  setSelectedDrawing,
5090
5181
  onDrawingHover,
5091
5182
  setDrawingDefaults,
package/dist/index.d.cts CHANGED
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
@@ -73,6 +73,7 @@ interface DrawingObjectOptions {
73
73
  leverage?: number;
74
74
  pointValue?: number;
75
75
  qtyPrecision?: number;
76
+ fontSize?: number;
76
77
  }
77
78
  /** Default colors for position tools: [profit, loss, label text]. */
78
79
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -91,7 +92,7 @@ interface DrawingHoverEvent {
91
92
  x: number;
92
93
  y: number;
93
94
  }
94
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
95
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision" | "fontSize">>;
95
96
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
96
97
  id?: string;
97
98
  type: string;
@@ -428,6 +429,7 @@ interface ChartInstance {
428
429
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
429
430
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
431
  onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
432
+ onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
431
433
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
432
434
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
433
435
  setSelectedDrawing: (id: string | null) => void;
package/dist/index.d.ts CHANGED
@@ -44,7 +44,7 @@ interface ChartOptions {
44
44
  drawings?: DrawingObjectOptions[];
45
45
  }
46
46
  type IndicatorPane = "overlay" | "separate";
47
- type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range";
47
+ type DrawingToolType = "horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement" | "fib-extension" | "long-position" | "short-position" | "price-range" | "text" | "note";
48
48
  interface DrawingPoint {
49
49
  index: number;
50
50
  price: number;
@@ -73,6 +73,7 @@ interface DrawingObjectOptions {
73
73
  leverage?: number;
74
74
  pointValue?: number;
75
75
  qtyPrecision?: number;
76
+ fontSize?: number;
76
77
  }
77
78
  /** Default colors for position tools: [profit, loss, label text]. */
78
79
  declare const POSITION_DEFAULT_COLORS: string[];
@@ -91,7 +92,7 @@ interface DrawingHoverEvent {
91
92
  x: number;
92
93
  y: number;
93
94
  }
94
- type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision">>;
95
+ type DrawingDefaults = Partial<Pick<DrawingObjectOptions, "color" | "colors" | "style" | "width" | "accountSize" | "lotSize" | "risk" | "riskMode" | "leverage" | "pointValue" | "qtyPrecision" | "fontSize">>;
95
96
  interface IndicatorInstanceOptions<TInputs extends Record<string, unknown> = Record<string, unknown>> {
96
97
  id?: string;
97
98
  type: string;
@@ -428,6 +429,7 @@ interface ChartInstance {
428
429
  onDrawingsChange: (handler: ((drawings: DrawingObjectOptions[]) => void) | null) => void;
429
430
  onDrawingSelect: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
430
431
  onDrawingDoubleClick: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
432
+ onDrawingEditText: (handler: ((event: DrawingSelectEvent) => void) | null) => void;
431
433
  onDrawingHover: (handler: ((event: DrawingHoverEvent) => void) | null) => void;
432
434
  setDrawingDefaults: (tool: DrawingToolType, defaults: DrawingDefaults | null) => void;
433
435
  setSelectedDrawing: (id: string | null) => void;
package/dist/index.js CHANGED
@@ -942,6 +942,7 @@ function createChart(element, options = {}) {
942
942
  leverage: Number(drawing.leverage) || 1,
943
943
  pointValue: Number(drawing.pointValue) || 1,
944
944
  qtyPrecision: Number.isFinite(drawing.qtyPrecision) ? Math.max(0, Math.floor(Number(drawing.qtyPrecision))) : 0,
945
+ fontSize: Math.max(6, Number(drawing.fontSize) || 14),
945
946
  ...drawing.label === void 0 ? {} : { label: drawing.label }
946
947
  });
947
948
  const serializeDrawing = (drawing) => ({
@@ -961,6 +962,7 @@ function createChart(element, options = {}) {
961
962
  leverage: drawing.leverage,
962
963
  pointValue: drawing.pointValue,
963
964
  qtyPrecision: drawing.qtyPrecision,
965
+ fontSize: drawing.fontSize,
964
966
  ...drawing.label === void 0 ? {} : { label: drawing.label }
965
967
  });
966
968
  let indicators = (options.indicators ?? []).map((indicator) => normalizeIndicatorState(indicator));
@@ -971,6 +973,7 @@ function createChart(element, options = {}) {
971
973
  let drawingsChangeHandler = null;
972
974
  let drawingSelectHandler = null;
973
975
  let drawingDoubleClickHandler = null;
976
+ let drawingEditTextHandler = null;
974
977
  let drawingHoverHandler = null;
975
978
  let lastHoveredDrawingId = null;
976
979
  let selectedDrawingId = null;
@@ -2609,6 +2612,46 @@ function createChart(element, options = {}) {
2609
2612
  drawDrawingLabel(drawing.label, midX, topY - 4, drawing.color);
2610
2613
  }
2611
2614
  }
2615
+ } else if (drawing.type === "text" || drawing.type === "note") {
2616
+ const anchor = drawing.points[0];
2617
+ if (anchor) {
2618
+ const ax = xFromDrawingPoint(anchor);
2619
+ const ay = yFromPrice(anchor.price);
2620
+ const isNote = drawing.type === "note";
2621
+ const fontSize = Math.max(6, drawing.fontSize);
2622
+ const prevFont = ctx.font;
2623
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
2624
+ ctx.textAlign = "left";
2625
+ ctx.textBaseline = "top";
2626
+ const lines = (drawing.label ?? "").split("\n");
2627
+ const lineH = Math.round(fontSize * 1.35);
2628
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
2629
+ const padX = isNote ? 8 : 2;
2630
+ const padY = isNote ? 6 : 1;
2631
+ const blockW = textW + padX * 2;
2632
+ const blockH = lines.length * lineH + padY * 2;
2633
+ ctx.save();
2634
+ ctx.globalAlpha = draft ? 0.7 : 1;
2635
+ if (isNote) {
2636
+ ctx.fillStyle = hexToRgba(drawing.color, 0.14);
2637
+ fillRoundedRect(ax, ay, blockW, blockH, 4);
2638
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.5);
2639
+ ctx.lineWidth = 1;
2640
+ strokeRoundedRect(ax, ay, blockW, blockH, 4);
2641
+ }
2642
+ ctx.fillStyle = drawing.color;
2643
+ lines.forEach((line, lineIndex) => ctx.fillText(line, ax + padX, ay + padY + lineIndex * lineH));
2644
+ ctx.restore();
2645
+ ctx.font = prevFont;
2646
+ if (isSelected) {
2647
+ ctx.save();
2648
+ ctx.strokeStyle = hexToRgba(drawing.color, 0.9);
2649
+ ctx.lineWidth = 1;
2650
+ ctx.setLineDash([]);
2651
+ strokeRoundedRect(ax - 2, ay - 2, blockW + 4, blockH + 4, 3);
2652
+ ctx.restore();
2653
+ }
2654
+ }
2612
2655
  }
2613
2656
  ctx.restore();
2614
2657
  };
@@ -3857,6 +3900,27 @@ function createChart(element, options = {}) {
3857
3900
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3858
3901
  return { drawing, target: "line" };
3859
3902
  }
3903
+ } else if (drawing.type === "text" || drawing.type === "note") {
3904
+ const anchor = drawing.points[0];
3905
+ if (!anchor) continue;
3906
+ const ax = canvasXFromDrawingPoint(anchor);
3907
+ const ay = canvasYFromDrawingPrice(anchor.price);
3908
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3909
+ const fontSize = Math.max(6, drawing.fontSize);
3910
+ const prevFont = ctx.font;
3911
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3912
+ const lines = (drawing.label ?? "").split("\n");
3913
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3914
+ ctx.font = prevFont;
3915
+ const isNote = drawing.type === "note";
3916
+ const padX = isNote ? 8 : 2;
3917
+ const padY = isNote ? 6 : 1;
3918
+ const lineH = Math.round(fontSize * 1.35);
3919
+ const blockW = textW + padX * 2;
3920
+ const blockH = lines.length * lineH + padY * 2;
3921
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3922
+ return { drawing, target: "line" };
3923
+ }
3860
3924
  }
3861
3925
  }
3862
3926
  return null;
@@ -4057,6 +4121,24 @@ function createChart(element, options = {}) {
4057
4121
  draw();
4058
4122
  return true;
4059
4123
  }
4124
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4125
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4126
+ const created = normalizeDrawingState({
4127
+ type: activeDrawingTool,
4128
+ points: [point],
4129
+ color: defaults.color ?? "#e2e8f0",
4130
+ style: defaults.style ?? "solid",
4131
+ width: defaults.width ?? 1,
4132
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4133
+ label: ""
4134
+ });
4135
+ drawings.push(created);
4136
+ selectedDrawingId = created.id;
4137
+ emitDrawingsChange();
4138
+ draw();
4139
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4140
+ return true;
4141
+ }
4060
4142
  if (activeDrawingTool === "price-range") {
4061
4143
  const tick = getConfiguredTickSize();
4062
4144
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4679,13 +4761,18 @@ function createChart(element, options = {}) {
4679
4761
  const drawingHit = getDrawingHit(point.x, point.y);
4680
4762
  if (drawingHit) {
4681
4763
  selectedDrawingId = drawingHit.drawing.id;
4682
- drawingDoubleClickHandler?.({
4764
+ const payload = {
4683
4765
  drawing: serializeDrawing(drawingHit.drawing),
4684
4766
  target: drawingHit.target,
4685
4767
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4686
4768
  x: point.x,
4687
4769
  y: point.y
4688
- });
4770
+ };
4771
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4772
+ drawingEditTextHandler?.(payload);
4773
+ } else {
4774
+ drawingDoubleClickHandler?.(payload);
4775
+ }
4689
4776
  return;
4690
4777
  }
4691
4778
  }
@@ -5002,6 +5089,9 @@ function createChart(element, options = {}) {
5002
5089
  const onDrawingDoubleClick = (handler) => {
5003
5090
  drawingDoubleClickHandler = handler;
5004
5091
  };
5092
+ const onDrawingEditText = (handler) => {
5093
+ drawingEditTextHandler = handler;
5094
+ };
5005
5095
  const onDrawingHover = (handler) => {
5006
5096
  drawingHoverHandler = handler;
5007
5097
  };
@@ -5060,6 +5150,7 @@ function createChart(element, options = {}) {
5060
5150
  onDrawingsChange,
5061
5151
  onDrawingSelect,
5062
5152
  onDrawingDoubleClick,
5153
+ onDrawingEditText,
5063
5154
  setSelectedDrawing,
5064
5155
  onDrawingHover,
5065
5156
  setDrawingDefaults,
package/docs/API.md CHANGED
@@ -483,6 +483,7 @@ Use `getDrawings()` / `setDrawings()` for persistence.
483
483
  - `resetViewport(): void` (fit x + reset y auto-scale)
484
484
  - `setSelectedDrawing(id: string | null): void` (marks a drawing selected; only the selected/drafted drawing renders its handles, and position tools render their lines/labels only while selected)
485
485
  - `onDrawingDoubleClick(handler): void` (fires when a drawing is double-clicked; use it to open a settings dialog, e.g. for position tools)
486
+ - `onDrawingEditText(handler): void` (fires when a `text`/`note` tool is placed or double-clicked; use it to show an inline text editor at `{x, y}` and write the result back via `updateDrawing(id, { label })`). `text`/`note` drawings store their content in `label` and size in `fontSize`.
486
487
  - `setActiveDrawingTool(tool: DrawingToolType | null): void` (`DrawingToolType` = `"horizontal-line" | "vertical-line" | "trendline" | "ray" | "fib-retracement"`)
487
488
  - `getActiveDrawingTool(): DrawingToolType | null`
488
489
  - `setDrawings(drawings: DrawingObjectOptions[]): void`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperprop-charting-library",
3
- "version": "0.1.85",
3
+ "version": "0.1.87",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",