hyperprop-charting-library 0.1.85 → 0.1.86

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,41 @@ 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
+ drawDrawingHandle(ax, ay, drawing.color);
2674
+ }
2675
+ }
2638
2676
  }
2639
2677
  ctx.restore();
2640
2678
  };
@@ -3883,6 +3921,27 @@ function createChart(element, options = {}) {
3883
3921
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3884
3922
  return { drawing, target: "line" };
3885
3923
  }
3924
+ } else if (drawing.type === "text" || drawing.type === "note") {
3925
+ const anchor = drawing.points[0];
3926
+ if (!anchor) continue;
3927
+ const ax = canvasXFromDrawingPoint(anchor);
3928
+ const ay = canvasYFromDrawingPrice(anchor.price);
3929
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3930
+ const fontSize = Math.max(6, drawing.fontSize);
3931
+ const prevFont = ctx.font;
3932
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3933
+ const lines = (drawing.label ?? "").split("\n");
3934
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3935
+ ctx.font = prevFont;
3936
+ const isNote = drawing.type === "note";
3937
+ const padX = isNote ? 8 : 2;
3938
+ const padY = isNote ? 6 : 1;
3939
+ const lineH = Math.round(fontSize * 1.35);
3940
+ const blockW = textW + padX * 2;
3941
+ const blockH = lines.length * lineH + padY * 2;
3942
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3943
+ return { drawing, target: "line" };
3944
+ }
3886
3945
  }
3887
3946
  }
3888
3947
  return null;
@@ -4083,6 +4142,24 @@ function createChart(element, options = {}) {
4083
4142
  draw();
4084
4143
  return true;
4085
4144
  }
4145
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4146
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4147
+ const created = normalizeDrawingState({
4148
+ type: activeDrawingTool,
4149
+ points: [point],
4150
+ color: defaults.color ?? "#e2e8f0",
4151
+ style: defaults.style ?? "solid",
4152
+ width: defaults.width ?? 1,
4153
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4154
+ label: ""
4155
+ });
4156
+ drawings.push(created);
4157
+ selectedDrawingId = created.id;
4158
+ emitDrawingsChange();
4159
+ draw();
4160
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4161
+ return true;
4162
+ }
4086
4163
  if (activeDrawingTool === "price-range") {
4087
4164
  const tick = getConfiguredTickSize();
4088
4165
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4705,13 +4782,18 @@ function createChart(element, options = {}) {
4705
4782
  const drawingHit = getDrawingHit(point.x, point.y);
4706
4783
  if (drawingHit) {
4707
4784
  selectedDrawingId = drawingHit.drawing.id;
4708
- drawingDoubleClickHandler?.({
4785
+ const payload = {
4709
4786
  drawing: serializeDrawing(drawingHit.drawing),
4710
4787
  target: drawingHit.target,
4711
4788
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4712
4789
  x: point.x,
4713
4790
  y: point.y
4714
- });
4791
+ };
4792
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4793
+ drawingEditTextHandler?.(payload);
4794
+ } else {
4795
+ drawingDoubleClickHandler?.(payload);
4796
+ }
4715
4797
  return;
4716
4798
  }
4717
4799
  }
@@ -5028,6 +5110,9 @@ function createChart(element, options = {}) {
5028
5110
  const onDrawingDoubleClick = (handler) => {
5029
5111
  drawingDoubleClickHandler = handler;
5030
5112
  };
5113
+ const onDrawingEditText = (handler) => {
5114
+ drawingEditTextHandler = handler;
5115
+ };
5031
5116
  const onDrawingHover = (handler) => {
5032
5117
  drawingHoverHandler = handler;
5033
5118
  };
@@ -5086,6 +5171,7 @@ function createChart(element, options = {}) {
5086
5171
  onDrawingsChange,
5087
5172
  onDrawingSelect,
5088
5173
  onDrawingDoubleClick,
5174
+ onDrawingEditText,
5089
5175
  setSelectedDrawing,
5090
5176
  onDrawingHover,
5091
5177
  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,41 @@ 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
+ drawDrawingHandle(ax, ay, drawing.color);
2648
+ }
2649
+ }
2612
2650
  }
2613
2651
  ctx.restore();
2614
2652
  };
@@ -3857,6 +3895,27 @@ function createChart(element, options = {}) {
3857
3895
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3858
3896
  return { drawing, target: "line" };
3859
3897
  }
3898
+ } else if (drawing.type === "text" || drawing.type === "note") {
3899
+ const anchor = drawing.points[0];
3900
+ if (!anchor) continue;
3901
+ const ax = canvasXFromDrawingPoint(anchor);
3902
+ const ay = canvasYFromDrawingPrice(anchor.price);
3903
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3904
+ const fontSize = Math.max(6, drawing.fontSize);
3905
+ const prevFont = ctx.font;
3906
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3907
+ const lines = (drawing.label ?? "").split("\n");
3908
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3909
+ ctx.font = prevFont;
3910
+ const isNote = drawing.type === "note";
3911
+ const padX = isNote ? 8 : 2;
3912
+ const padY = isNote ? 6 : 1;
3913
+ const lineH = Math.round(fontSize * 1.35);
3914
+ const blockW = textW + padX * 2;
3915
+ const blockH = lines.length * lineH + padY * 2;
3916
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3917
+ return { drawing, target: "line" };
3918
+ }
3860
3919
  }
3861
3920
  }
3862
3921
  return null;
@@ -4057,6 +4116,24 @@ function createChart(element, options = {}) {
4057
4116
  draw();
4058
4117
  return true;
4059
4118
  }
4119
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4120
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4121
+ const created = normalizeDrawingState({
4122
+ type: activeDrawingTool,
4123
+ points: [point],
4124
+ color: defaults.color ?? "#e2e8f0",
4125
+ style: defaults.style ?? "solid",
4126
+ width: defaults.width ?? 1,
4127
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4128
+ label: ""
4129
+ });
4130
+ drawings.push(created);
4131
+ selectedDrawingId = created.id;
4132
+ emitDrawingsChange();
4133
+ draw();
4134
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4135
+ return true;
4136
+ }
4060
4137
  if (activeDrawingTool === "price-range") {
4061
4138
  const tick = getConfiguredTickSize();
4062
4139
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4679,13 +4756,18 @@ function createChart(element, options = {}) {
4679
4756
  const drawingHit = getDrawingHit(point.x, point.y);
4680
4757
  if (drawingHit) {
4681
4758
  selectedDrawingId = drawingHit.drawing.id;
4682
- drawingDoubleClickHandler?.({
4759
+ const payload = {
4683
4760
  drawing: serializeDrawing(drawingHit.drawing),
4684
4761
  target: drawingHit.target,
4685
4762
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4686
4763
  x: point.x,
4687
4764
  y: point.y
4688
- });
4765
+ };
4766
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4767
+ drawingEditTextHandler?.(payload);
4768
+ } else {
4769
+ drawingDoubleClickHandler?.(payload);
4770
+ }
4689
4771
  return;
4690
4772
  }
4691
4773
  }
@@ -5002,6 +5084,9 @@ function createChart(element, options = {}) {
5002
5084
  const onDrawingDoubleClick = (handler) => {
5003
5085
  drawingDoubleClickHandler = handler;
5004
5086
  };
5087
+ const onDrawingEditText = (handler) => {
5088
+ drawingEditTextHandler = handler;
5089
+ };
5005
5090
  const onDrawingHover = (handler) => {
5006
5091
  drawingHoverHandler = handler;
5007
5092
  };
@@ -5060,6 +5145,7 @@ function createChart(element, options = {}) {
5060
5145
  onDrawingsChange,
5061
5146
  onDrawingSelect,
5062
5147
  onDrawingDoubleClick,
5148
+ onDrawingEditText,
5063
5149
  setSelectedDrawing,
5064
5150
  onDrawingHover,
5065
5151
  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,41 @@ 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
+ drawDrawingHandle(ax, ay, drawing.color);
2674
+ }
2675
+ }
2638
2676
  }
2639
2677
  ctx.restore();
2640
2678
  };
@@ -3883,6 +3921,27 @@ function createChart(element, options = {}) {
3883
3921
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3884
3922
  return { drawing, target: "line" };
3885
3923
  }
3924
+ } else if (drawing.type === "text" || drawing.type === "note") {
3925
+ const anchor = drawing.points[0];
3926
+ if (!anchor) continue;
3927
+ const ax = canvasXFromDrawingPoint(anchor);
3928
+ const ay = canvasYFromDrawingPrice(anchor.price);
3929
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3930
+ const fontSize = Math.max(6, drawing.fontSize);
3931
+ const prevFont = ctx.font;
3932
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3933
+ const lines = (drawing.label ?? "").split("\n");
3934
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3935
+ ctx.font = prevFont;
3936
+ const isNote = drawing.type === "note";
3937
+ const padX = isNote ? 8 : 2;
3938
+ const padY = isNote ? 6 : 1;
3939
+ const lineH = Math.round(fontSize * 1.35);
3940
+ const blockW = textW + padX * 2;
3941
+ const blockH = lines.length * lineH + padY * 2;
3942
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3943
+ return { drawing, target: "line" };
3944
+ }
3886
3945
  }
3887
3946
  }
3888
3947
  return null;
@@ -4083,6 +4142,24 @@ function createChart(element, options = {}) {
4083
4142
  draw();
4084
4143
  return true;
4085
4144
  }
4145
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4146
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4147
+ const created = normalizeDrawingState({
4148
+ type: activeDrawingTool,
4149
+ points: [point],
4150
+ color: defaults.color ?? "#e2e8f0",
4151
+ style: defaults.style ?? "solid",
4152
+ width: defaults.width ?? 1,
4153
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4154
+ label: ""
4155
+ });
4156
+ drawings.push(created);
4157
+ selectedDrawingId = created.id;
4158
+ emitDrawingsChange();
4159
+ draw();
4160
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4161
+ return true;
4162
+ }
4086
4163
  if (activeDrawingTool === "price-range") {
4087
4164
  const tick = getConfiguredTickSize();
4088
4165
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4705,13 +4782,18 @@ function createChart(element, options = {}) {
4705
4782
  const drawingHit = getDrawingHit(point.x, point.y);
4706
4783
  if (drawingHit) {
4707
4784
  selectedDrawingId = drawingHit.drawing.id;
4708
- drawingDoubleClickHandler?.({
4785
+ const payload = {
4709
4786
  drawing: serializeDrawing(drawingHit.drawing),
4710
4787
  target: drawingHit.target,
4711
4788
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4712
4789
  x: point.x,
4713
4790
  y: point.y
4714
- });
4791
+ };
4792
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4793
+ drawingEditTextHandler?.(payload);
4794
+ } else {
4795
+ drawingDoubleClickHandler?.(payload);
4796
+ }
4715
4797
  return;
4716
4798
  }
4717
4799
  }
@@ -5028,6 +5110,9 @@ function createChart(element, options = {}) {
5028
5110
  const onDrawingDoubleClick = (handler) => {
5029
5111
  drawingDoubleClickHandler = handler;
5030
5112
  };
5113
+ const onDrawingEditText = (handler) => {
5114
+ drawingEditTextHandler = handler;
5115
+ };
5031
5116
  const onDrawingHover = (handler) => {
5032
5117
  drawingHoverHandler = handler;
5033
5118
  };
@@ -5086,6 +5171,7 @@ function createChart(element, options = {}) {
5086
5171
  onDrawingsChange,
5087
5172
  onDrawingSelect,
5088
5173
  onDrawingDoubleClick,
5174
+ onDrawingEditText,
5089
5175
  setSelectedDrawing,
5090
5176
  onDrawingHover,
5091
5177
  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,41 @@ 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
+ drawDrawingHandle(ax, ay, drawing.color);
2648
+ }
2649
+ }
2612
2650
  }
2613
2651
  ctx.restore();
2614
2652
  };
@@ -3857,6 +3895,27 @@ function createChart(element, options = {}) {
3857
3895
  if (x >= Math.min(px0, px1) && x <= Math.max(px0, px1) && y >= Math.min(y0, y1) && y <= Math.max(y0, y1)) {
3858
3896
  return { drawing, target: "line" };
3859
3897
  }
3898
+ } else if (drawing.type === "text" || drawing.type === "note") {
3899
+ const anchor = drawing.points[0];
3900
+ if (!anchor) continue;
3901
+ const ax = canvasXFromDrawingPoint(anchor);
3902
+ const ay = canvasYFromDrawingPrice(anchor.price);
3903
+ if (Math.hypot(x - ax, y - ay) <= 9) return { drawing, target: "handle", pointIndex: 0 };
3904
+ const fontSize = Math.max(6, drawing.fontSize);
3905
+ const prevFont = ctx.font;
3906
+ ctx.font = `500 ${fontSize}px ${mergedOptions.fontFamily}`;
3907
+ const lines = (drawing.label ?? "").split("\n");
3908
+ const textW = Math.max(1, ...lines.map((line) => ctx.measureText(line).width));
3909
+ ctx.font = prevFont;
3910
+ const isNote = drawing.type === "note";
3911
+ const padX = isNote ? 8 : 2;
3912
+ const padY = isNote ? 6 : 1;
3913
+ const lineH = Math.round(fontSize * 1.35);
3914
+ const blockW = textW + padX * 2;
3915
+ const blockH = lines.length * lineH + padY * 2;
3916
+ if (x >= ax && x <= ax + blockW && y >= ay && y <= ay + blockH) {
3917
+ return { drawing, target: "line" };
3918
+ }
3860
3919
  }
3861
3920
  }
3862
3921
  return null;
@@ -4057,6 +4116,24 @@ function createChart(element, options = {}) {
4057
4116
  draw();
4058
4117
  return true;
4059
4118
  }
4119
+ if (activeDrawingTool === "text" || activeDrawingTool === "note") {
4120
+ const defaults = getDrawingToolDefaults(activeDrawingTool);
4121
+ const created = normalizeDrawingState({
4122
+ type: activeDrawingTool,
4123
+ points: [point],
4124
+ color: defaults.color ?? "#e2e8f0",
4125
+ style: defaults.style ?? "solid",
4126
+ width: defaults.width ?? 1,
4127
+ ...defaults.fontSize === void 0 ? {} : { fontSize: defaults.fontSize },
4128
+ label: ""
4129
+ });
4130
+ drawings.push(created);
4131
+ selectedDrawingId = created.id;
4132
+ emitDrawingsChange();
4133
+ draw();
4134
+ drawingEditTextHandler?.({ drawing: serializeDrawing(created), target: "line", x, y });
4135
+ return true;
4136
+ }
4060
4137
  if (activeDrawingTool === "price-range") {
4061
4138
  const tick = getConfiguredTickSize();
4062
4139
  const visibleRange = drawState.yMax - drawState.yMin;
@@ -4679,13 +4756,18 @@ function createChart(element, options = {}) {
4679
4756
  const drawingHit = getDrawingHit(point.x, point.y);
4680
4757
  if (drawingHit) {
4681
4758
  selectedDrawingId = drawingHit.drawing.id;
4682
- drawingDoubleClickHandler?.({
4759
+ const payload = {
4683
4760
  drawing: serializeDrawing(drawingHit.drawing),
4684
4761
  target: drawingHit.target,
4685
4762
  ...drawingHit.pointIndex === void 0 ? {} : { pointIndex: drawingHit.pointIndex },
4686
4763
  x: point.x,
4687
4764
  y: point.y
4688
- });
4765
+ };
4766
+ if (drawingHit.drawing.type === "text" || drawingHit.drawing.type === "note") {
4767
+ drawingEditTextHandler?.(payload);
4768
+ } else {
4769
+ drawingDoubleClickHandler?.(payload);
4770
+ }
4689
4771
  return;
4690
4772
  }
4691
4773
  }
@@ -5002,6 +5084,9 @@ function createChart(element, options = {}) {
5002
5084
  const onDrawingDoubleClick = (handler) => {
5003
5085
  drawingDoubleClickHandler = handler;
5004
5086
  };
5087
+ const onDrawingEditText = (handler) => {
5088
+ drawingEditTextHandler = handler;
5089
+ };
5005
5090
  const onDrawingHover = (handler) => {
5006
5091
  drawingHoverHandler = handler;
5007
5092
  };
@@ -5060,6 +5145,7 @@ function createChart(element, options = {}) {
5060
5145
  onDrawingsChange,
5061
5146
  onDrawingSelect,
5062
5147
  onDrawingDoubleClick,
5148
+ onDrawingEditText,
5063
5149
  setSelectedDrawing,
5064
5150
  onDrawingHover,
5065
5151
  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.86",
4
4
  "description": "Lightweight TypeScript charting core",
5
5
  "type": "module",
6
6
  "main": "./dist/hyperprop-charting-library.cjs",