@rioaxyz/sonrchart 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rioa, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # sonrchart
2
+
3
+ **Own the glass, not the ocean.** A themed, data-source-agnostic React charting
4
+ component for financial candlestick charts, built on
5
+ [KLineChart](https://github.com/klinecharts/KLineChart) (MIT). Extracted from the
6
+ Sonr trading app so the chart engine can be developed, versioned, and tested on
7
+ its own.
8
+
9
+ > New here? Read the [lore](LORE.md) for the why. MIT-licensed — see
10
+ > [LICENSE](LICENSE).
11
+
12
+ The design goal: KLineChart gives us candles, ~50+ indicators, drawing tools,
13
+ and sub-panes for free; sonrchart wraps it with the Sonr look, a clean React
14
+ API, and one integration seam — a `DataSource` — so the host app owns where
15
+ bars come from.
16
+
17
+ ## Features
18
+
19
+ - **One React component, one data seam.** `<SonrChart>` renders from a single
20
+ `DataSource` you implement (REST, websocket, anything) — the library never
21
+ knows where bars come from.
22
+ - **Themed out of the box.** Sonr dark/light palette baked in; override any
23
+ KLineChart style.
24
+ - **Candle styles.** Solid, hollow, hollow-up, OHLC/HLC bars, area, and Heikin
25
+ Ashi (a built-in data transform).
26
+ - **Indicators.** All of KLineChart's ~50+ built-ins, placed on the price pane
27
+ or their own sub-pane, with per-pane heights — plus a drop-in `IndicatorMenu`
28
+ to toggle them.
29
+ - **Sonr custom indicators.** Session tinting (pre/after-hours, ET, DST-aware)
30
+ and pattern-context markers.
31
+ - **Trade plan on the chart.** Static `priceLines` (entry/stop/target) and
32
+ **draggable** `orderLines` with an `onMove` callback.
33
+ - **Compare / multi-symbol overlay.** Overlay other symbols, ratio-scaled to
34
+ share a starting price.
35
+ - **Drawing tools.** Curated trend/ray/Fibonacci/channel/note tools with a
36
+ `DrawingToolbar`, magnet-to-OHLC snapping, and **undo/redo**.
37
+ - **Price scale modes.** Normal / percentage / logarithmic, invert, labels
38
+ inside.
39
+ - **Persistence & templates.** Export/import chart state (drawings + indicators)
40
+ and save/apply named indicator sets.
41
+ - **Snapshots.** Capture the chart to a PNG data URL (or trigger a download).
42
+ - **Composable chrome.** `ChartToolbar` bundles intervals + indicators +
43
+ drawing tools + snapshot; add your own buttons via its children slot.
44
+ - **Fast & dependency-light.** The only runtime dependency is KLineChart;
45
+ React is a peer. The per-frame hot paths are tuned (see
46
+ [PERFORMANCE.md](PERFORMANCE.md)).
47
+
48
+ See [`docs/charting-feature-inventory.md`](docs/charting-feature-inventory.md)
49
+ for the full roadmap and what KLineChart provides vs. what this library adds.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ npm install sonrchart klinecharts react react-dom
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ```tsx
60
+ import { SonrChart, type DataSource } from "sonrchart";
61
+
62
+ // You implement this — REST, websocket, anything. `time` is epoch ms.
63
+ const source: DataSource = {
64
+ async getBars(symbol, interval, count) {
65
+ const r = await fetch(`/bars?symbol=${symbol}&interval=${interval}&limit=${count}`);
66
+ return (await r.json()).bars; // [{ time, open, high, low, close, volume }]
67
+ },
68
+ subscribe(symbol, interval, onBar) {
69
+ const ws = new WebSocket(`/stream?symbol=${symbol}`);
70
+ ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.bar) onBar(m.bar); };
71
+ return () => ws.close();
72
+ },
73
+ };
74
+
75
+ export function Chart() {
76
+ return (
77
+ <div style={{ height: 480 }}>
78
+ <SonrChart
79
+ symbol="AAPL"
80
+ interval="1"
81
+ dataSource={source}
82
+ theme="dark"
83
+ indicators={["VOL"]}
84
+ priceLines={[
85
+ { price: 101.5, color: "#35c98d", label: "Target", style: "dashed" },
86
+ { price: 100.0, color: "#7d93e8", label: "Entry" },
87
+ { price: 98.8, color: "#ff6a76", label: "Stop", style: "dashed" },
88
+ ]}
89
+ />
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
95
+ The component fills its parent, so give the parent a height.
96
+
97
+ ## Integrating into an app
98
+
99
+ 1. **Install** the package and its peers:
100
+ `npm install sonrchart klinecharts react react-dom`.
101
+ 2. **Implement a `DataSource`.** This is the whole integration — one object with
102
+ `getBars(symbol, interval, count)` returning `[{ time, open, high, low,
103
+ close, volume }]` (`time` in epoch ms), and an optional `subscribe(symbol,
104
+ interval, onBar)` for live updates that returns an unsubscribe function.
105
+ Point them at your own API/feed.
106
+ 3. **Render `<SonrChart>`** inside a sized container (it fills its parent),
107
+ passing `symbol`, `interval`, and your `dataSource`. That's a working chart.
108
+ 4. **Turn on features with props** as you need them — `indicators`, `sessions`,
109
+ `patterns`, `priceLines`, `orderLines`, `compareSeries`, `priceScale`,
110
+ `candleStyle`, `theme`. Each is independent and optional.
111
+ 5. **Add chrome (optional).** Grab a `ref<SonrChartHandle>` and drop in
112
+ `<ChartToolbar chartRef={ref} … />` (or the individual `IndicatorMenu` /
113
+ `DrawingToolbar`) for interval buttons, indicator toggles, drawing tools, and
114
+ a snapshot button. Add your own controls via the toolbar's children slot.
115
+ 6. **Drive it imperatively (optional)** through the same `ref` —
116
+ `startDrawing`, `undo`/`redo`, `snapshot`, `exportState`/`importState`,
117
+ `applyIndicatorSet`, etc. (full list under [Props](#props)).
118
+ 7. **Style it.** The chart uses your `theme`; all toolbar/button classes are
119
+ `sonr-*-btn`, so your app's CSS controls the chrome.
120
+
121
+ ```tsx
122
+ import { useRef } from "react";
123
+ import { SonrChart, ChartToolbar, type SonrChartHandle, type DataSource } from "sonrchart";
124
+
125
+ function TradingChart({ source }: { source: DataSource }) {
126
+ const ref = useRef<SonrChartHandle>(null);
127
+ return (
128
+ <div style={{ display: "flex", flexDirection: "column", height: 520 }}>
129
+ <ChartToolbar chartRef={ref} intervals={["1", "5", "60", "D"]} interval="1" onIntervalChange={/* ... */ () => {}} />
130
+ <div style={{ flex: 1, minHeight: 0 }}>
131
+ <SonrChart ref={ref} symbol="AAPL" interval="1" dataSource={source} theme="dark" sessions indicators={["VOL"]} />
132
+ </div>
133
+ </div>
134
+ );
135
+ }
136
+ ```
137
+
138
+ > **Bundler note:** sonrchart ships ESM + CJS with types, and bundles KLineChart
139
+ > in. It's browser-only (canvas), so if you server-render, load it client-side.
140
+
141
+ ## Props
142
+
143
+ | Prop | Type | Notes |
144
+ |---|---|---|
145
+ | `symbol` | `string` | Ticker to chart. |
146
+ | `interval` | `Interval` | `"10S" \| "30S" \| "1" \| "5" \| "15" \| "30" \| "60" \| "D"` (or any string your DataSource understands). Default `"1"`. |
147
+ | `dataSource` | `DataSource` | `getBars(symbol, interval, count)` + optional `subscribe(...)`. The only integration seam. |
148
+ | `theme` | `"dark" \| "light"` | Default `"dark"` (the Sonr console palette). |
149
+ | `styles` | `DeepPartial<Styles>` | KLineChart style overrides merged over the theme. |
150
+ | `indicators` | `(string \| IndicatorSpec)[]` | Indicators to show — a name (`"VOL"`) or a spec (`{ name, pane: "main" \| "new", calcParams }`). Default `["VOL"]`. |
151
+ | `paneHeights` | `Record<string, number>` | Sub-pane pixel heights, keyed by indicator name. |
152
+ | `sessions` | `boolean \| { premarket?, afterhours? }` | Tint pre/after-hours session backgrounds (ET). |
153
+ | `patterns` | `PatternMarker[]` | Setup markers (`{ time, kind, label }`) drawn on the price pane. |
154
+ | `priceLines` | `PriceLine[]` | Static horizontal reference lines (entry / stop / target). |
155
+ | `orderLines` | `OrderLine[]` | Draggable order/position lines; `onMove(price, phase)` fires as the user drags. |
156
+ | `priceScale` | `{ type?, invert?, inside? }` | Scale mode — `"normal" \| "percentage" \| "logarithm"`, plus invert / labels-inside. |
157
+ | `magnet` | `boolean \| "weak" \| "strong"` | Snap drawing points to nearby OHLC. |
158
+ | `candleStyle` | `"candle" \| "hollow" \| "hollow_up" \| "ohlc" \| "area" \| "heikin_ashi"` | Candle rendering style (Heikin Ashi transforms the data). |
159
+ | `compareSeries` | `{ symbol, color? }[]` | Overlay other symbols on the main pane, ratio-scaled to share a start price. |
160
+ | `barCount` | `number` | Bars requested on load. Default 500. |
161
+ | `onReady` | `(chart) => void` | The raw KLineChart instance, for advanced use. |
162
+ | `onSymbolError` | `(symbol) => void` | Fired when `dataSource.resolveSymbol` returns `null`. |
163
+
164
+ A `ref` exposes the full control surface:
165
+
166
+ ```ts
167
+ handle.addIndicator(spec); handle.removeIndicator(name); handle.listIndicators();
168
+ handle.getIndicatorSet(); handle.applyIndicatorSet(specs); // study templates
169
+ handle.startDrawing(tool); handle.clearDrawings(); // drawing tools
170
+ handle.undo(); handle.redo(); handle.canUndo(); handle.canRedo(); // drawing history
171
+ handle.snapshot({ type: "png" }); // -> data URL
172
+ handle.exportState(); handle.importState(state); // save / load
173
+ handle.chart(); // raw KLineChart
174
+ ```
175
+
176
+ Custom header controls: drop `ToolbarButton`s (or any element) into
177
+ `<ChartToolbar>`'s children slot; they inherit the `sonr-*-btn` classes so the
178
+ host styles all chrome through its own CSS.
179
+
180
+ ### Chrome components
181
+
182
+ Drop-in, ref-driven UI you can use as-is or replace:
183
+
184
+ - **`<IndicatorMenu chartRef={ref} />`** — toggle indicators.
185
+ - **`<DrawingToolbar chartRef={ref} />`** — the curated drawing tools + Clear.
186
+ - **`<ChartToolbar chartRef={ref} intervals=… />`** — composes the interval
187
+ buttons, indicator menu, drawing tools, and a Snapshot button into one bar.
188
+ - **`downloadSnapshot(handle, "chart.png")`** — save the chart as an image.
189
+
190
+ ### Symbol resolution
191
+
192
+ Add `resolveSymbol(symbol)` to your `DataSource` to validate/enrich a ticker
193
+ before it loads — return a `SymbolInfo` (name, price/volume precision) or `null`
194
+ for an unknown symbol (the chart then fires `onSymbolError` and skips loading).
195
+
196
+ ### Persistence
197
+
198
+ `exportState()` returns a plain-JSON `ChartState` (user drawings + indicators +
199
+ scale) for the host to store; `importState(state)` restores the drawings and
200
+ indicators.
201
+
202
+ ### Indicator config UI
203
+
204
+ `IndicatorMenu` is a drop-in control that drives a chart's `ref` — toggling a
205
+ button adds/removes that indicator live:
206
+
207
+ ```tsx
208
+ import { SonrChart, IndicatorMenu, type SonrChartHandle } from "sonrchart";
209
+ const ref = useRef<SonrChartHandle>(null);
210
+ // ...
211
+ <IndicatorMenu chartRef={ref} />
212
+ <SonrChart ref={ref} symbol="AAPL" dataSource={source} />
213
+ ```
214
+
215
+ ### Sonr custom indicators
216
+
217
+ `sessions` and `patterns` are Sonr's own custom KLineChart indicators
218
+ (registered automatically). Session tinting shades pre-market (04:00–09:30 ET)
219
+ and after-hours (16:00–20:00 ET); pattern markers draw a labeled triangle at
220
+ each setup's bar (green up for longs, red down for shorts).
221
+
222
+ ## Develop
223
+
224
+ ```bash
225
+ npm install
226
+ npm run dev # live demo at http://localhost:5300 (synthetic data, no backend)
227
+ npm run build # tsup -> dist/ (ESM + CJS + d.ts)
228
+ npm run typecheck
229
+ npm test # vitest run
230
+ npm run coverage # vitest run --coverage
231
+ npm run check # typecheck + test + build (what CI runs)
232
+ ```
233
+
234
+ The demo (`demo/`) drives the chart from a synthetic random-walk source, so it
235
+ runs with no backend — the same shape a real host adapter implements.
236
+
237
+ ## Testing
238
+
239
+ Unit + integration tests run under [Vitest](https://vitest.dev) (jsdom) and
240
+ gate every push/PR via GitHub Actions (`.github/workflows/ci.yml`):
241
+
242
+ - **Pure logic** (`test/internal.test.ts`, `test/session.test.ts`) — interval→
243
+ period mapping, bar conversion, style merge, and the ET session classifier
244
+ including its DST behavior.
245
+ - **Feature wiring** (`test/SonrChart.test.tsx`) — KLineChart is mocked so each
246
+ feature is asserted at the integration boundary: data-loader plumbing, the
247
+ indicator API + specs (#4), session-indicator create/override/remove (#10),
248
+ pattern-indicator lifecycle (#5), and static vs. draggable overlays with the
249
+ `onMove` drag callback (#19).
250
+ - **Custom-indicator drawing** (`test/indicatorsDraw.test.ts`) — the canvas
251
+ `draw` callbacks run against a fake 2D context: session bands fill pre/after-
252
+ hours only, pattern markers draw a triangle + label colored by direction.
253
+ - **Config UI** (`test/IndicatorMenu.test.tsx`) and the demo data source.
254
+
255
+ Coverage sits near 98% of `src/`. The one path not unit-tested is real
256
+ pixel rendering, which the demo harness (`npm run dev`) exercises end to end.
257
+
258
+ ## Performance
259
+
260
+ The features that run in the render hot path (session/pattern tinting) are
261
+ tuned so they cost near-zero per frame — no added dependencies. The ET session
262
+ classifier alone is **~80× faster** than a naive `Intl`-per-bar approach and
263
+ stays DST-correct. See [PERFORMANCE.md](PERFORMANCE.md); reproduce with
264
+ `npm run bench`.
265
+
266
+ ## License
267
+
268
+ [MIT](LICENSE) © Rioa, LLC