react-native-vroom-chart 0.1.4 → 0.4.0

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.
@@ -0,0 +1,112 @@
1
+ #include "liquidity.h"
2
+
3
+ #include <algorithm>
4
+ #include <cstdint>
5
+
6
+ #pragma clang diagnostic push
7
+ #pragma clang diagnostic ignored "-Wdocumentation"
8
+ #include "include/core/SkCanvas.h"
9
+ #include "include/core/SkColor.h"
10
+ #include "include/core/SkPaint.h"
11
+ #include "include/core/SkPoint.h"
12
+ #include "include/core/SkRect.h"
13
+ #include "include/core/SkShader.h"
14
+ #include "include/core/SkTileMode.h"
15
+ // Skia's linear-gradient API differs across the Skia versions we build against:
16
+ // the WASM build uses a newer Skia (SkGradient.h + SkShaders::LinearGradient),
17
+ // while react-native-skia bundles an older one (SkGradientShader::MakeLinear).
18
+ // Neither ships both headers, so select whichever is present.
19
+ #if __has_include("include/effects/SkGradient.h")
20
+ # include "include/core/SkSpan.h"
21
+ # include "include/effects/SkGradient.h"
22
+ # define VROOM_SK_MODERN_GRADIENT 1
23
+ #else
24
+ # include "include/effects/SkGradientShader.h"
25
+ # define VROOM_SK_MODERN_GRADIENT 0
26
+ #endif
27
+ #pragma clang diagnostic pop
28
+
29
+ #include "chart.h"
30
+
31
+ namespace vroom::liquidity {
32
+ namespace {
33
+
34
+ // A horizontal shader fading from transparent at the left of `pts` to `base` at
35
+ // opacity `alpha` on the right, keeping RGB constant so the faded edge doesn't
36
+ // darken toward black. Abstracts over the two Skia gradient APIs above.
37
+ sk_sp<SkShader> left_fade_shader(const SkPoint pts[2], SkColor base, float alpha) {
38
+ #if VROOM_SK_MODERN_GRADIENT
39
+ const float r = SkColorGetR(base) / 255.f;
40
+ const float g = SkColorGetG(base) / 255.f;
41
+ const float b = SkColorGetB(base) / 255.f;
42
+ const SkColor4f colors[2] = {SkColor4f{r, g, b, 0.f}, SkColor4f{r, g, b, alpha}};
43
+ const SkGradient::Colors grad_colors(
44
+ SkSpan<const SkColor4f>(colors, 2), SkTileMode::kClamp);
45
+ const SkGradient grad(grad_colors, SkGradient::Interpolation{});
46
+ return SkShaders::LinearGradient(pts, grad);
47
+ #else
48
+ const auto a = static_cast<U8CPU>(alpha * 255.f + 0.5f);
49
+ const SkColor colors[2] = {SkColorSetA(base, 0), SkColorSetA(base, a)};
50
+ return SkGradientShader::MakeLinear(pts, colors, nullptr, 2, SkTileMode::kClamp);
51
+ #endif
52
+ }
53
+
54
+ } // namespace
55
+
56
+ void draw(SkCanvas* canvas,
57
+ const VroomChart& chart,
58
+ const Layout& lay,
59
+ const PriceBounds& bounds,
60
+ float candle_right,
61
+ float candle_area_h) {
62
+ if (!canvas || candle_right <= 0.f || candle_area_h <= 0.f) return;
63
+ if (chart.bands.empty()) return;
64
+
65
+ const VroomLiquidityStyle& style = chart.liquidity_style;
66
+
67
+ // Leftward reach: the smaller of the px cap and the pane-fraction, clamped so
68
+ // the band never starts left of the pane.
69
+ const float frac_reach = style.width_frac > 0.f ? style.width_frac * candle_right
70
+ : candle_right;
71
+ const float px_reach = style.width_px > 0.f ? style.width_px : candle_right;
72
+ const float reach = std::min(px_reach, frac_reach);
73
+ const float left_x = std::max(0.f, candle_right - reach);
74
+
75
+ // Volume that maps to peak opacity: explicit, or the largest band's volume.
76
+ double max_vol = style.max_volume;
77
+ if (max_vol <= 0.0) {
78
+ for (const VroomBand& b : chart.bands) max_vol = std::max(max_vol, b.volume);
79
+ }
80
+
81
+ const float min_op = std::clamp(style.min_opacity, 0.f, 1.f);
82
+ const float max_op = std::clamp(style.max_opacity, 0.f, 1.f);
83
+
84
+ canvas->save();
85
+ canvas->clipRect(SkRect::MakeLTRB(0.f, 0.f, candle_right, candle_area_h));
86
+
87
+ const SkPoint pts[2] = {SkPoint{left_x, 0.f}, SkPoint{candle_right, 0.f}};
88
+
89
+ for (const VroomBand& b : chart.bands) {
90
+ // Higher price -> smaller y, so max_price is the top edge.
91
+ const float y_top = vroom::price_to_y(lay, bounds, b.max_price);
92
+ const float y_bot = vroom::price_to_y(lay, bounds, b.min_price);
93
+ if (y_bot < 0.f || y_top > candle_area_h) continue; // fully off-pane
94
+
95
+ const SkColor base =
96
+ static_cast<SkColor>(b.side == 0 ? style.buy_color : style.sell_color);
97
+
98
+ const double t = max_vol > 0.0
99
+ ? std::clamp(b.volume / max_vol, 0.0, 1.0)
100
+ : 0.0;
101
+ const float alpha = min_op + (max_op - min_op) * static_cast<float>(t);
102
+
103
+ SkPaint paint;
104
+ paint.setAntiAlias(true);
105
+ paint.setShader(left_fade_shader(pts, base, alpha));
106
+ canvas->drawRect(SkRect::MakeLTRB(left_x, y_top, candle_right, y_bot), paint);
107
+ }
108
+
109
+ canvas->restore();
110
+ }
111
+
112
+ } // namespace vroom::liquidity
@@ -0,0 +1,35 @@
1
+ // Liquidity bands — resting-order / order-book depth rendered on the price pane.
2
+ //
3
+ // Each band (chart.bands) is a price interval carrying a total size on one side
4
+ // of the book. It is drawn as a horizontal rectangle anchored at the inner edge
5
+ // of the price axis (`candle_right`), stretching left by the configured reach
6
+ // and fading to transparent with a left->right gradient. Color comes from the
7
+ // band's side (buy/sell); the band's alpha comes from its volume mapped into
8
+ // [min_opacity, max_opacity]. Because the vertical extent is derived from price
9
+ // via price_to_y, bands scale with the y-axis on zoom.
10
+ //
11
+ // Like ma_overlay / drawings this is its own module so the chart orchestrator
12
+ // stays thin. Drawn BEHIND the candles (before candles::draw) so candle bodies
13
+ // paint over the bands.
14
+
15
+ #pragma once
16
+
17
+ #include "viewport.h"
18
+ #include "vroom/vroom_chart.h"
19
+
20
+ class SkCanvas;
21
+ struct VroomChart;
22
+
23
+ namespace vroom::liquidity {
24
+
25
+ // Draws the liquidity bands. `candle_right` is the x of the price-axis strip
26
+ // (bands' right edge); `candle_area_h` is the price-pane bottom. Geometry is
27
+ // clipped to that rectangle so bands never bleed into the axis strips.
28
+ void draw(SkCanvas* canvas,
29
+ const VroomChart& chart,
30
+ const vroom::Layout& lay,
31
+ const vroom::PriceBounds& bounds,
32
+ float candle_right,
33
+ float candle_area_h);
34
+
35
+ } // namespace vroom::liquidity
@@ -10,6 +10,7 @@
10
10
  #include "include/core/SkRect.h"
11
11
  #pragma clang diagnostic pop
12
12
 
13
+ #include <algorithm>
13
14
  #include <cmath>
14
15
 
15
16
  #include "viewport.h"
@@ -29,11 +30,14 @@ void draw(SkCanvas* canvas,
29
30
  float candle_area_h,
30
31
  uint32_t color,
31
32
  float width,
32
- const unsigned char* break_before) {
33
+ const unsigned char* break_before,
34
+ float opacity) {
33
35
  if (!canvas || !values_visible || n == 0 || candle_right <= 0.f ||
34
36
  candle_area_h <= 0.f) {
35
37
  return;
36
38
  }
39
+ opacity = std::clamp(opacity, 0.f, 1.f);
40
+ if (opacity <= 0.f) return;
37
41
 
38
42
  // SkPathBuilder (not SkPath's edit methods, removed in newer Skia tips).
39
43
  SkPathBuilder path;
@@ -59,6 +63,10 @@ void draw(SkCanvas* canvas,
59
63
  SkPaint line;
60
64
  line.setAntiAlias(true);
61
65
  line.setColor(static_cast<SkColor>(color));
66
+ // Fade the line in during the candle→line morph (multiplies the color alpha).
67
+ if (opacity < 1.f) {
68
+ line.setAlphaf(line.getAlphaf() * opacity);
69
+ }
62
70
  line.setStyle(SkPaint::kStroke_Style);
63
71
  line.setStrokeWidth(width > 0.f ? width : 1.5f);
64
72
 
@@ -37,6 +37,7 @@ void draw(SkCanvas* canvas,
37
37
  float candle_area_h,
38
38
  uint32_t color,
39
39
  float width,
40
- const unsigned char* break_before = nullptr);
40
+ const unsigned char* break_before = nullptr,
41
+ float opacity = 1.f);
41
42
 
42
43
  } // namespace vroom::ma_overlay
@@ -22,6 +22,7 @@ constexpr uint32_t kDefaultColors[VROOM_COLOR_COUNT_] = {
22
22
  0x00000000, // WICK_BEAR — transparent sentinel: inherit BEAR fill
23
23
  0xff26a69a, // ACCENT_BULL — classic teal-green (price indicator, volume, MACD)
24
24
  0xffef5350, // ACCENT_BEAR — classic red
25
+ 0xffc9d1d9, // LINE — line-chart close polyline; neutral foreground (AXIS_TEXT tone)
25
26
  };
26
27
 
27
28
  constexpr float kDefaultFloats[VROOM_FLOAT_COUNT_] = {
@@ -33,6 +34,10 @@ constexpr float kDefaultFloats[VROOM_FLOAT_COUNT_] = {
33
34
  22.f, // X_AXIS_HEIGHT_PX — bottom strip for time labels
34
35
  0.5f, // VOLUME_OPACITY — candle color at 50% transparency
35
36
  0.20f, // INDICATOR_HEIGHT_FRAC — below-chart indicator pane = 20% of height
37
+ 0.f, // CANDLE_RADIUS_PX — square by default
38
+ 0.f, // WICK_ROUND_CAP — butt caps by default
39
+ 0.f, // VOLUME_RADIUS_PX — square by default
40
+ 1.5f, // LINE_WIDTH_PX — line-chart polyline stroke width
36
41
  };
37
42
 
38
43
  } // namespace
@@ -4,6 +4,7 @@
4
4
  #pragma clang diagnostic ignored "-Wdocumentation"
5
5
  #include "include/core/SkCanvas.h"
6
6
  #include "include/core/SkPaint.h"
7
+ #include "include/core/SkRRect.h"
7
8
  #include "include/core/SkRect.h"
8
9
  #pragma clang diagnostic pop
9
10
 
@@ -41,6 +42,7 @@ void draw(SkCanvas* canvas,
41
42
  const float half_body = body_w * 0.5f;
42
43
 
43
44
  const float opacity = theme.floats[VROOM_FLOAT_VOLUME_OPACITY];
45
+ const float vol_r = theme.floats[VROOM_FLOAT_VOLUME_RADIUS_PX];
44
46
  SkPaint bull_paint;
45
47
  bull_paint.setAntiAlias(true);
46
48
  bull_paint.setColor(theme.colors[VROOM_COLOR_ACCENT_BULL]);
@@ -61,9 +63,19 @@ void draw(SkCanvas* canvas,
61
63
  visible_start_ms, window_ms);
62
64
  const float h = std::max(
63
65
  1.f, static_cast<float>(c.volume / max_vol) * region_h);
64
- canvas->drawRect(
65
- SkRect::MakeXYWH(cx - half_body, candle_area_h - h, body_w, h),
66
- bull ? bull_paint : bear_paint);
66
+ const SkRect rect =
67
+ SkRect::MakeXYWH(cx - half_body, candle_area_h - h, body_w, h);
68
+ const SkPaint& paint = bull ? bull_paint : bear_paint;
69
+ // Round only the top corners; clamp so short/narrow bars don't over-round.
70
+ const float r = std::min({vol_r, body_w * 0.5f, h});
71
+ if (r > 0.f) {
72
+ SkRRect rr;
73
+ const SkVector radii[4] = {{r, r}, {r, r}, {0, 0}, {0, 0}}; // TL, TR, BR, BL
74
+ rr.setRectRadii(rect, radii);
75
+ canvas->drawRRect(rr, paint);
76
+ } else {
77
+ canvas->drawRect(rect, paint);
78
+ }
67
79
  }
68
80
  }
69
81
 
package/lib/index.d.mts CHANGED
@@ -68,14 +68,22 @@ type VroomTheme = {
68
68
  accentBull?: VroomColor;
69
69
  /** Generic down color for the price indicator, volume bars, and MACD histogram. Defaults to red; independent of `bear`. */
70
70
  accentBear?: VroomColor;
71
- /** Up candle body 1px border. Defaults to the bull fill color. */
71
+ /** Up candle body border (1px, drawn *inside* the body so it never changes candle width). Omit or set to the bull fill color to hide it. */
72
72
  borderBull?: VroomColor;
73
- /** Down candle body 1px border. Defaults to the bear fill color. */
73
+ /** Down candle body border (1px, drawn *inside* the body so it never changes candle width). Omit or set to the bear fill color to hide it. */
74
74
  borderBear?: VroomColor;
75
75
  /** Up candle wick color. Defaults to the bull fill color. */
76
76
  wickBull?: VroomColor;
77
77
  /** Down candle wick color. Defaults to the bear fill color. */
78
78
  wickBear?: VroomColor;
79
+ /** Wick stroke width in px (applies to both up and down wicks). Defaults to 1. */
80
+ wickWidth?: number;
81
+ /** Corner radius (px) of candle bodies. Defaults to 0 (square). */
82
+ candleRadius?: number;
83
+ /** Round the wick end caps. Defaults to false. */
84
+ wickRoundCap?: boolean;
85
+ /** Corner radius (px) of the *top* of volume bars. Defaults to 0 (square). */
86
+ volumeRadius?: number;
79
87
  /** Gridlines. */
80
88
  grid?: VroomColor;
81
89
  /** Axis label text (price + time). */
@@ -84,6 +92,10 @@ type VroomTheme = {
84
92
  crosshair?: VroomColor;
85
93
  /** Crosshair target — the hollow ring/dot at the intersection. */
86
94
  crosshairTarget?: VroomColor;
95
+ /** Line-chart-mode close polyline color. Defaults to a neutral foreground. */
96
+ lineColor?: VroomColor;
97
+ /** Line-chart-mode polyline stroke width in px. Defaults to 1.5. */
98
+ lineWidth?: number;
87
99
  };
88
100
  /** A time window over the candle data, as Unix epoch milliseconds. */
89
101
  type VisibleRange = {
@@ -98,8 +110,15 @@ type VisibleRange = {
98
110
  * 'draw' — left-clicks place drawing points; panning/zooming are suppressed.
99
111
  */
100
112
  type ChartMode = 'pan' | 'draw';
113
+ /**
114
+ * How the price series is drawn.
115
+ * 'candles' — default: candlestick bodies + wicks.
116
+ * 'line' — a single polyline through each candle's close. Volume, indicators,
117
+ * overlays, crosshair, and drawings still render.
118
+ */
119
+ type ChartType = 'candles' | 'line';
101
120
  /** Active drawing tool while in `draw` mode. `null` draws nothing. */
102
- type DrawTool = null | 'line';
121
+ type DrawTool = null | 'line' | 'box' | 'pencil';
103
122
  /** A drawing anchor in data space, so it stays glued to the candles on pan/zoom. */
104
123
  type DrawPoint = {
105
124
  /** Anchor time as Unix epoch milliseconds (not snapped to a candle slot). */
@@ -107,23 +126,81 @@ type DrawPoint = {
107
126
  /** Anchor price. */
108
127
  price: number;
109
128
  };
110
- /**
111
- * A committed drawing. Pass an array of these via the `drawings` prop to render
112
- * persisted annotations; the chart appends a new one (via `onDrawingComplete`)
113
- * each time the user finishes drawing. For now only the `'line'` (two-point
114
- * trendline) type exists.
115
- */
116
- type Drawing = {
129
+ /** Fields shared by every drawing type. */
130
+ type DrawingBase = {
117
131
  /** Stable unique id (the chart generates one for drawings it creates). */
118
132
  id: string;
119
- type: 'line';
120
- /** The two endpoints, in data space. */
121
- points: [DrawPoint, DrawPoint];
122
- /** Line color (hex string or packed ARGB number). Default solid blue. */
133
+ /** Stroke color (hex string or packed ARGB number). Default solid blue. */
123
134
  color?: VroomColor;
124
135
  /** Stroke width in px. Default 2. */
125
136
  width?: number;
126
137
  };
138
+ /** A two-point trendline from `points[0]` to `points[1]`. */
139
+ type LineDrawing = DrawingBase & {
140
+ type: 'line';
141
+ /** The two endpoints, in data space. */
142
+ points: [DrawPoint, DrawPoint];
143
+ };
144
+ /**
145
+ * An axis-aligned rectangle whose two opposite corners are `points[0]` and
146
+ * `points[1]` (the other two corners are derived).
147
+ */
148
+ type BoxDrawing = DrawingBase & {
149
+ type: 'box';
150
+ /** Two opposite corners, in data space. */
151
+ points: [DrawPoint, DrawPoint];
152
+ };
153
+ /**
154
+ * A freehand pencil stroke: an open path through `points`, in order. Unlike the
155
+ * other tools a stroke has a variable number of points, and once committed it
156
+ * can only be translated — never reshaped.
157
+ */
158
+ type PencilDrawing = DrawingBase & {
159
+ type: 'pencil';
160
+ /** The path's points in draw order (at least 2), in data space. */
161
+ points: DrawPoint[];
162
+ };
163
+ /**
164
+ * A committed drawing. Pass an array of these via the `drawings` prop to render
165
+ * persisted annotations; the chart appends a new one (via `onDrawingComplete`)
166
+ * each time the user finishes drawing.
167
+ *
168
+ * This is a discriminated union on `type` — narrow on it before reading
169
+ * `points[1]`, since a `'pencil'` stroke has a variable-length array while
170
+ * `'line'` and `'box'` are always exactly two points.
171
+ */
172
+ type Drawing = LineDrawing | BoxDrawing | PencilDrawing;
173
+ /**
174
+ * Storage adapter for **managed** drawing persistence. Provide it via the
175
+ * `drawingStore` prop and the chart owns the drawings array itself — loading and
176
+ * saving through this adapter instead of you wiring the controlled `drawings`
177
+ * prop + `onDrawing*` callbacks.
178
+ *
179
+ * The adapter is an **opaque string key-value store** — the chart serializes
180
+ * drawings into a **versioned envelope** (`{ v, drawings }`) and hands you the
181
+ * string; you just persist bytes. Because the library owns the schema and
182
+ * migrates old payloads on load, adding drawing tools or persisted fields later
183
+ * never changes this interface — your adapter is written once.
184
+ *
185
+ * `marketId` is the chart's `seriesKey`, so drawings are bucketed per market:
186
+ * they persist across timeframe changes (same key) but not across markets. Both
187
+ * methods may be async (localStorage is sync; AsyncStorage / MMKV / a REST
188
+ * backend are async). The chart debounces `save`. Consumers that only handle a
189
+ * single market can ignore `marketId`.
190
+ */
191
+ type DrawingStore = {
192
+ /**
193
+ * Return the raw string previously handed to `save` for `marketId`, or
194
+ * `null`/`undefined`/`''` if nothing is stored. Sync or async.
195
+ */
196
+ load: (marketId: string) => string | null | undefined | Promise<string | null | undefined>;
197
+ /**
198
+ * Persist the opaque `data` string for `marketId`. Sync or async; the chart
199
+ * debounces calls. The string is a versioned envelope owned by the library —
200
+ * store it verbatim, don't parse or reshape it.
201
+ */
202
+ save: (marketId: string, data: string) => void | Promise<void>;
203
+ };
127
204
  /** RSI indicator config. Rendered in a pane below the candles when enabled. */
128
205
  type RSIConfig = {
129
206
  enabled?: boolean;
@@ -169,6 +246,51 @@ type VWAPConfig = {
169
246
  /** Stroke width in px. Default 1.5. */
170
247
  width?: number;
171
248
  };
249
+ /**
250
+ * A single resting-liquidity band: a price interval carrying a total order size
251
+ * on one side of the book. Consolidate raw L2 levels into these buckets before
252
+ * passing them. The vertical extent is defined in price space, so bands scale
253
+ * with the y-axis on zoom.
254
+ */
255
+ type LiquidityBand = {
256
+ /** Bottom of the price interval. */
257
+ minPrice: number;
258
+ /** Top of the price interval. A per-level order uses a thin interval. */
259
+ maxPrice: number;
260
+ /** Which side of the book — selects the buy vs sell color. */
261
+ side: 'buy' | 'sell';
262
+ /** Total resting size in this band; drives the band's opacity. */
263
+ volume: number;
264
+ };
265
+ /**
266
+ * Resting-order / order-book liquidity overlay. Each band is drawn as a
267
+ * horizontal rectangle anchored at the inner edge of the price axis, stretching
268
+ * left (~`widthPx`/`widthFrac` of the pane) and fading out with a gradient,
269
+ * colored by side and made more opaque with volume. Rendered behind the candles.
270
+ * Omit the prop (or pass no bands) to hide the overlay.
271
+ */
272
+ type LiquidityConfig = {
273
+ /** The bands to render. */
274
+ bands: LiquidityBand[];
275
+ /** Buy-side color (hex string or packed ARGB number). Default teal-green. */
276
+ buyColor?: VroomColor;
277
+ /** Sell-side color (hex string or packed ARGB number). Default red. */
278
+ sellColor?: VroomColor;
279
+ /**
280
+ * Volume mapped to the peak (`maxOpacity`) band. Omit to auto-scale to the
281
+ * largest band's volume each render.
282
+ */
283
+ maxVolume?: number;
284
+ /** Opacity floor for the smallest band. Default 0.05. */
285
+ minOpacity?: number;
286
+ /** Opacity at `maxVolume`. Default 0.8. */
287
+ maxOpacity?: number;
288
+ /** Leftward reach from the axis in px. Default 300. */
289
+ widthPx?: number;
290
+ /** Leftward reach as a fraction of pane width. Default 0.25. The smaller of
291
+ * `widthPx` and `widthFrac * paneWidth` is used. */
292
+ widthFrac?: number;
293
+ };
172
294
  /** MACD indicator config. Rendered in its own pane below the candles. */
173
295
  type MACDConfig = {
174
296
  enabled?: boolean;
@@ -204,6 +326,26 @@ type VroomChartCoreProps = {
204
326
  height?: number;
205
327
  /** Time window to render. Omit (or both 0) to show every candle. */
206
328
  visibleRange?: VisibleRange;
329
+ /**
330
+ * Target candle *body* width in logical px for the INITIAL framing. Larger →
331
+ * more zoomed in (fewer candles); smaller → more zoomed out. Only affects
332
+ * first load / reset; an explicit `visibleRange` takes precedence. Omit for
333
+ * the default (~80 candles). Useful to keep candles a consistent size across
334
+ * devices of different widths.
335
+ */
336
+ defaultCandleWidth?: number;
337
+ /**
338
+ * Price-series render style. `'candles'` (default) draws candlesticks;
339
+ * `'line'` draws a polyline through each candle's close (style it with
340
+ * `theme.lineColor` / `theme.lineWidth`). All other layers are unaffected.
341
+ */
342
+ chartType?: ChartType;
343
+ /**
344
+ * Duration (ms) of the animated candle↔line transition when `chartType`
345
+ * changes. Default ~300. `0` snaps instantly. Ignored (snaps) when the OS
346
+ * requests reduced motion, which instead uses a plain cross-fade.
347
+ */
348
+ transitionMs?: number;
207
349
  theme?: VroomTheme;
208
350
  /** RSI indicator (pane below the candles). Omit/disable to hide it. */
209
351
  rsi?: RSIConfig;
@@ -213,6 +355,8 @@ type VroomChartCoreProps = {
213
355
  movingAverages?: MovingAverageOverlay[];
214
356
  /** VWAP overlay (session anchor, configurable reset). */
215
357
  vwap?: VWAPConfig;
358
+ /** Resting-order / order-book liquidity bands drawn behind the candles. */
359
+ liquidity?: LiquidityConfig;
216
360
  /**
217
361
  * Pixels the crosshair dot / horizontal line sit *above* the touch point so
218
362
  * they aren't hidden under the thumb. The vertical line stays centered on the
@@ -243,10 +387,30 @@ type VroomChartCoreProps = {
243
387
  * Committed drawings to render, anchored to data so they track the candles on
244
388
  * pan/zoom. This is a controlled prop: append the value the chart hands you in
245
389
  * `onDrawingComplete` to persist it.
390
+ *
391
+ * Ignored when `drawingStore` is set (the chart then owns the array itself).
246
392
  */
247
393
  drawings?: Drawing[];
394
+ /**
395
+ * Opt into **managed** drawing persistence: the chart owns the drawings array
396
+ * internally and loads/saves it through this adapter, keyed by `seriesKey`.
397
+ * When set, `drawings` and the `onDrawing*` callbacks are ignored. Web only.
398
+ * Requires `seriesKey` — without one, drawings work in-session but aren't saved.
399
+ */
400
+ drawingStore?: DrawingStore;
248
401
  /** Fired with the finished drawing when the user completes one. */
249
402
  onDrawingComplete?: (drawing: Drawing) => void;
403
+ /**
404
+ * Fired after the user drags a selected line's endpoint handle. The payload is
405
+ * the same drawing (same `id`) with updated `points`; apply it to your
406
+ * controlled `drawings` state (replace by id). Web only.
407
+ */
408
+ onDrawingChange?: (drawing: Drawing) => void;
409
+ /**
410
+ * Fired when the user deletes the selected line (Backspace/Delete). Remove the
411
+ * drawing with this `id` from your controlled `drawings` state. Web only.
412
+ */
413
+ onDrawingDelete?: (id: string) => void;
250
414
  /**
251
415
  * Fired when the chart wants the mode changed — e.g. it requests `'pan'` after
252
416
  * the user clicks away from a just-drawn line. Since `mode` is controlled, the
@@ -286,4 +450,4 @@ declare global {
286
450
  */
287
451
  declare function VroomChart(props: VroomChartProps): React.JSX.Element;
288
452
 
289
- export { type Candle, type CrosshairEvent, type MACDConfig, type MASource, type MovingAverageOverlay, type RSIConfig, type VWAPConfig, type VisibleRange, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme };
453
+ export { type Candle, type ChartType, type CrosshairEvent, type MACDConfig, type MASource, type MovingAverageOverlay, type RSIConfig, type VWAPConfig, type VisibleRange, VroomChart, type VroomChartProps, type VroomColor, type VroomTheme };