@pond-ts/react 0.51.0 → 0.53.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/CHANGELOG.md +161 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,7 +8,9 @@ The `@pond-ts` packages — `pond-ts`, `@pond-ts/react`, `@pond-ts/charts`,
|
|
|
8
8
|
tag, so this file covers them all. Pre-1.0: minor bumps may include new features
|
|
9
9
|
and type-level changes; patch bumps are strictly additive.
|
|
10
10
|
|
|
11
|
-
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.
|
|
11
|
+
[Unreleased]: https://github.com/pond-ts/pond/compare/v0.53.0...HEAD
|
|
12
|
+
[0.53.0]: https://github.com/pond-ts/pond/compare/v0.52.0...v0.53.0
|
|
13
|
+
[0.52.0]: https://github.com/pond-ts/pond/compare/v0.51.0...v0.52.0
|
|
12
14
|
[0.51.0]: https://github.com/pond-ts/pond/compare/v0.50.0...v0.51.0
|
|
13
15
|
[0.50.0]: https://github.com/pond-ts/pond/compare/v0.49.0...v0.50.0
|
|
14
16
|
[0.49.0]: https://github.com/pond-ts/pond/compare/v0.48.1...v0.49.0
|
|
@@ -51,6 +53,164 @@ and type-level changes; patch bumps are strictly additive.
|
|
|
51
53
|
|
|
52
54
|
## [Unreleased]
|
|
53
55
|
|
|
56
|
+
## [0.53.0] — 2026-07-25
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- **fit (breaking):** **Power bins and zones now use pond's canonical bin
|
|
61
|
+
edges**, so they feed `@pond-ts/charts` with no mapping step:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<BarChart bins={power.distribution} column="seconds" />
|
|
65
|
+
<BarChart bins={power.zones} column="seconds" orientation="horizontal" ordinal />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Each type previously spoke its own dialect for the same concept —
|
|
69
|
+
`PowerBin.wattsFrom` (with **no upper edge at all**), `ZoneTime.lo`/`hi`, and
|
|
70
|
+
`PowerZone.minWatts`/`maxWatts` — while core's `byColumn` and charts' `BinRecord`
|
|
71
|
+
both use `{ start, end, …aggregates }`. Every caller had to hand-map before
|
|
72
|
+
drawing, even though the internals already computed the canonical shape and
|
|
73
|
+
discarded it.
|
|
74
|
+
|
|
75
|
+
**Migration** (pre-1.0, so the old names are gone rather than deprecated):
|
|
76
|
+
|
|
77
|
+
| Was | Now |
|
|
78
|
+
| --------------------- | -------------------------------------- |
|
|
79
|
+
| `PowerBin.wattsFrom` | `PowerBin.start` (+ new `end`) |
|
|
80
|
+
| `ZoneTime.lo` / `.hi` | `ZoneTime.start` / `.end`, `openEnded` |
|
|
81
|
+
| `PowerZone.minWatts` | `PowerZone.start` |
|
|
82
|
+
| `PowerZone.maxWatts` | `PowerZone.end`, `openEnded` |
|
|
83
|
+
|
|
84
|
+
Only **`PowerZone.maxWatts`** — a zone's upper edge — is affected. The
|
|
85
|
+
identically-named `PowerSummary.maxWatts` and the per-lap / per-section peak
|
|
86
|
+
power are a different concept and are unchanged.
|
|
87
|
+
|
|
88
|
+
`end` is now **always finite and always `> start`** — the guarantee core
|
|
89
|
+
enforces (`byColumn` throws on a zero-width bin) and charts need (an infinite
|
|
90
|
+
edge blows up an axis domain). The open-ended top band, which previously
|
|
91
|
+
carried only `Infinity`, gets a **drawable stand-in** edge: wide enough to
|
|
92
|
+
cover the highest value observed, and at least as wide as the band below it.
|
|
93
|
+
Treat it as a drawing bound rather than data, and test for the band with the
|
|
94
|
+
new **`openEnded`** flag rather than comparing an edge against `Infinity`
|
|
95
|
+
(`openEnded` is now also strictly positional — only the final band can carry
|
|
96
|
+
it). Rounding zone edges to whole watts no longer collapses bands at very low
|
|
97
|
+
FTPs.
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- **charts:** **Duration (elapsed) x axis** — `<ChartContainer origin>` labels
|
|
102
|
+
the shared x axis as offsets from a zero point instead of absolute values, so
|
|
103
|
+
a workout / lab run / load test reads `00:00 00:05 00:10` rather than
|
|
104
|
+
`10:35 10:40 10:45`:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<ChartContainer width={620} origin="data">
|
|
108
|
+
…
|
|
109
|
+
<XAxis label="Elapsed" />
|
|
110
|
+
</ChartContainer>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`'data'` zeroes at the start of the data (and stays there as you pan); a
|
|
114
|
+
**number** sets an explicit zero point — a gun, a trigger, a lap — with ticks
|
|
115
|
+
before it reading negative (`-00:05`). Ticks are placed at round durations
|
|
116
|
+
**measured from the origin** (a ride starting at 10:33:17 ticks 10:33:17,
|
|
117
|
+
10:38:17, …), off a clock ladder (…15s, 30s, 1m, 2m, 5m, …, 12h, then whole
|
|
118
|
+
days) rather than the 1-2-5 ladder — the part a formatter alone can't do.
|
|
119
|
+
Labels pick their shape from the step and the axis's magnitude
|
|
120
|
+
(`00:00.500` · `00:15` · `01:01:30` · `1d 12:00` · `5d`), gridlines follow the
|
|
121
|
+
same ticks, and the cursor / marker pills read one grain finer (`00:05:12`).
|
|
122
|
+
|
|
123
|
+
It's a **labelling** mode, not a data transform: `range`, `<Marker at>`,
|
|
124
|
+
`onRegionSelect`, `trackerPosition` all stay in absolute axis units. The same
|
|
125
|
+
prop works on a **value** x axis (distance travelled, not distance recorded).
|
|
126
|
+
An explicit format still wins — on a time axis a d3 _time_ specifier can only
|
|
127
|
+
describe an instant, so it labels the wall clock, which is the lever for
|
|
128
|
+
stacking a wall-clock strip under a duration strip on one shared tick set; on
|
|
129
|
+
a value axis a number specifier formats the offset. Ignored on a category
|
|
130
|
+
axis; on a trading calendar the durations are wall-clock, so ticks spanning a
|
|
131
|
+
collapsed session gap sit unevenly.
|
|
132
|
+
|
|
133
|
+
- **fit:** `computePower` takes an options object — **`{ binWatts }`** sets the
|
|
134
|
+
width of the `distribution` buckets (default `1`, unchanged). 1 W bins draw as
|
|
135
|
+
hairlines, so pass the width you intend to render rather than re-bucketing the
|
|
136
|
+
output yourself. It throws `RangeError` on a non-positive or non-finite
|
|
137
|
+
`binWatts`. New exported type `ComputePowerOptions`, also accepted by the
|
|
138
|
+
activity façade: `Activity.power(ftp, options)` and
|
|
139
|
+
`ProfiledActivity.power(options)`.
|
|
140
|
+
|
|
141
|
+
## [0.52.0] — 2026-07-23
|
|
142
|
+
|
|
143
|
+
### Changed
|
|
144
|
+
|
|
145
|
+
- **core / financial:** **Market-scale studies are now typed-array fast**
|
|
146
|
+
(the "SMA/EMA at 1M bars costs hundreds of ms" report). Three cuts along
|
|
147
|
+
the same path, all behaviour-preserving (identical values, warm-ups,
|
|
148
|
+
missing-cell semantics, and rejection errors; every fast path falls back
|
|
149
|
+
to the original sweep when it doesn't apply):
|
|
150
|
+
- **`smooth('ema')` columnar fast path** — on a packed numeric source
|
|
151
|
+
column the EMA recurrence runs straight off the typed buffer into a
|
|
152
|
+
typed result column via trusted construction (key + untouched columns
|
|
153
|
+
pass through zero-copy), replacing the per-row Event/tuple rebuild +
|
|
154
|
+
full-series intake re-pack. 1M rows: **530 ms → 4.4 ms (~120×)**.
|
|
155
|
+
- **`rolling({ count })` numeric fast path** — an all-built-in numeric
|
|
156
|
+
mapping over packed sources feeds the shared incremental reducer states
|
|
157
|
+
directly from the typed buffers and writes snapshots into typed columns
|
|
158
|
+
(no per-row snapshot arrays, no boxed accumulators, no post-pass
|
|
159
|
+
assert/re-pack). 1M rows, `avg`: **135 ms → 32 ms (~4×)**.
|
|
160
|
+
- **financial kernel reads columns, not events** — `rollingColumns` /
|
|
161
|
+
`columnValues` now read study inputs/outputs off the public column API
|
|
162
|
+
instead of materializing `series.events` (an Event + data object per
|
|
163
|
+
row, ~400 ms of pure overhead at 1M rows).
|
|
164
|
+
- End-to-end at 1M bars: `ema()` **603 ms → 2.5 ms (~240×)**, `sma()`
|
|
165
|
+
**569 ms → 56 ms (~10×)**, `bollinger()` **748 ms → 162 ms (~4.6×)**.
|
|
166
|
+
Durable benchmarks: `packages/core/scripts/perf-smooth-ema.mjs`,
|
|
167
|
+
`packages/financial/scripts/perf-studies.mjs`.
|
|
168
|
+
|
|
169
|
+
### Added
|
|
170
|
+
|
|
171
|
+
- **core:** **`TimeSeries.fromArrow(table, options?)` — ingest a decoded Apache
|
|
172
|
+
Arrow `Table`.** pond stays zero-dependency: bring your own Arrow
|
|
173
|
+
(`tableFromIPC(...)`) and hand the `Table` in; the input is duck-typed against
|
|
174
|
+
a small structural surface (`ArrowTableLike` / `ArrowVectorLike` / …, all
|
|
175
|
+
exported). Ingest is the zero-copy path — every `Float64` column's backing
|
|
176
|
+
`Float64Array` is adopted as-is (`Float32`/int columns convert; int64 value
|
|
177
|
+
columns recombine BigInt-free), and the schema is derived from the Arrow
|
|
178
|
+
fields. The time key is converted **BigInt-free**: Arrow's idiomatic int64
|
|
179
|
+
timestamps are recombined from their two int32 halves rather than
|
|
180
|
+
`Number(bigint)` per row — measured **~11× faster** on the time column (0.6ms
|
|
181
|
+
vs 6.8ms at 500k rows; `scripts/perf-from-arrow.mjs`). Options: `time` (key
|
|
182
|
+
column, default the `'time'` field), `timeUnit` (default read from the Arrow
|
|
183
|
+
Arrow type family — a `Timestamp`'s raw-unit int64 is scaled by its
|
|
184
|
+
`TimeUnit`; `Date32`/`Date64` arrive already normalized to epoch-ms and pass
|
|
185
|
+
through; overridable), `columns` (subset, in order), `name`, `sort`. Numeric
|
|
186
|
+
**and string** columns are supported — string columns (Arrow
|
|
187
|
+
`Utf8`) become dict-encoded `StringColumn`s; any other Arrow type
|
|
188
|
+
(list/struct) throws, naming it. A null time key throws; numeric nulls map to
|
|
189
|
+
`NaN` and string nulls to missing.
|
|
190
|
+
- **core:** **`TimeSeries.fromColumns` / `ValueSeries.fromColumns` now accept
|
|
191
|
+
`string` value columns** (previously numeric-only), packed to dict-encoded
|
|
192
|
+
`StringColumn`s (`null`/`undefined` → missing) — the shared columnar-ingress
|
|
193
|
+
engine now dispatches on the schema kind. Other value kinds (`boolean`,
|
|
194
|
+
arrays) still throw.
|
|
195
|
+
- **charts:** **`<ScatterChart decimate>` — dense scatter plots now decimate**
|
|
196
|
+
(PND-MARKDEC scatter half — the last un-decimated mark type). **Default
|
|
197
|
+
`true`.** When the marks are **uniform** (fixed size + colour, no data-driven
|
|
198
|
+
`radius`/`color`), **opaque**, and denser than the pixel grid, overlapping
|
|
199
|
+
marks collapse to one representative per **mark-radius cell** via a 2D
|
|
200
|
+
pixel-**occupancy** sweep. Scatter has no fill, so a line/bar's per-column
|
|
201
|
+
`[min, max]` envelope would erase interior points — the occupancy grid keeps
|
|
202
|
+
one mark per occupied cell instead, which is **visually lossless** for uniform
|
|
203
|
+
opaque marks at that density (same-cell marks overlap). Interaction (hover /
|
|
204
|
+
click / tracker) still reads **every source point**; the per-point selection
|
|
205
|
+
ring + labels are suppressed only on the decimated (dense) path. A
|
|
206
|
+
**translucent** fill (density-encoded — overlap _should_ build up) or a
|
|
207
|
+
data-driven size/colour keeps the full draw. `decimate={false}` draws every
|
|
208
|
+
mark; `{ threshold }` tunes the trigger. The occupancy sweep uses the affine
|
|
209
|
+
fast path for the per-point pixel mapping. Measured (SciChart-suite
|
|
210
|
+
point-update, real browser): **100k 18 → 73 fps (4×)**, and the ladder now
|
|
211
|
+
runs to **10M** points (previously dead by 1M). `drawScatter` now returns
|
|
212
|
+
`LayerDrawStats` (visible via `onDrawStats`).
|
|
213
|
+
|
|
54
214
|
## [0.51.0] — 2026-07-22
|
|
55
215
|
|
|
56
216
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pond-ts/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "React hooks for pond-ts live time series",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"test:runtime": "vitest run"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"pond-ts": "^0.
|
|
36
|
+
"pond-ts": "^0.53.0",
|
|
37
37
|
"react": "^18.0.0 || ^19.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|