Skip to content

Commit bf06330

Browse files
committed
Release chartgpu-react 0.3.0 for ChartGPU 0.3.6
Align the React bindings with @chartgpu/chartgpu ^0.3.6, expand 0.3.x handle/hook surface and types, modernize the examples demo site, and add CI plus mock-based unit coverage for forwarding and multi-chart hooks. Tooling: upgrade to TypeScript 7.0 (^7.0.2), Vite 7, and Vitest 4; drop vite-plugin-dts (incompatible with TS 7's slim package API) and emit declarations via tsc. Fix useGPUContext StrictMode init via a shared promise. Package name restored to chartgpu-react for npmjs.org.
1 parent 2406c82 commit bf06330

29 files changed

Lines changed: 4726 additions & 3402 deletions

.github/workflows/ci.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
concurrency:
9+
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
10+
cancel-in-progress: true
11+
12+
jobs:
13+
typecheck-and-test:
14+
name: Typecheck & test
15+
runs-on: ubuntu-latest
16+
timeout-minutes: 15
17+
18+
steps:
19+
- name: Checkout
20+
uses: actions/checkout@v4
21+
22+
- name: Setup Node
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: "20.x"
26+
cache: "npm"
27+
28+
- name: Install dependencies
29+
run: npm ci
30+
31+
- name: Typecheck
32+
run: npm run typecheck
33+
34+
- name: Test
35+
run: npm test
36+
37+
- name: Build
38+
run: npm run build

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ coverage
3333
*.swp
3434
*.swo
3535
*~
36+
.hallmark
3637

3738

3839
# Claude

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Changelog
2+
3+
## 0.3.0
4+
5+
Aligned with **@chartgpu/chartgpu ^0.3.6** and modern React/TypeScript tooling.
6+
7+
### Dependencies
8+
9+
- Peer: `@chartgpu/chartgpu` **^0.3.6** (was ^0.2.8)
10+
- Peer: React **≥18** (dev/tested on **React 19.2**)
11+
- Dev: **TypeScript 7.0**, Vite 7, Vitest 4, `@types/react` 19
12+
13+
### API
14+
15+
- `ChartGPUHandle.appendData` accepts optional `{ maxPoints }` for FIFO / fixed-capacity streaming (ChartGPU 0.3.x)
16+
- `ChartGPUHandle.setZoomRange` passes optional `source` through to core
17+
- `CartesianSeriesData` allows `null` gaps (matches core)
18+
- Expanded type re-exports: heatmap, band, errorBar, impulse, OHLC, 3D series, `ZoomChangeSourceKind`, etc.
19+
- New exported type: `ChartGPUAppendDataOptions`
20+
21+
### Tooling
22+
23+
- Build: Vite (ESM bundle) + `tsc` declaration emit
24+
(`vite-plugin-dts` is incompatible with TypeScript 7’s slim package API)
25+
- Package name restored to **`chartgpu-react`** for npmjs.org (GitHub Packages still scopes at publish)
26+
- Test coverage for handle/hooks 0.3.x: `appendData`/`maxPoints`, `setZoomRange` source, external render, `useConnectCharts`, `useGPUContext`, `gpuContext` create path, `onDataAppend`/`onDeviceLost`, export smoke
27+
28+
### Examples
29+
30+
- Streaming multi-chart demos use `appendData(..., { maxPoints })`

README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@
2929

3030
- **`ChartGPU` component (recommended)**: async create/dispose lifecycle + debounced `ResizeObserver` sizing
3131
- **Event props**: `onClick`, `onCrosshairMove`, `onZoomChange`, `onDataAppend`, `onDeviceLost`, etc.
32-
- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`, `needsRender`, `renderFrame`, `getRenderMode`, `setRenderMode`)
32+
- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData` with optional `{ maxPoints }` FIFO, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`, `needsRender`, `renderFrame`, `getRenderMode`, `setRenderMode`)
3333
- **Hooks**: `useChartGPU(...)`, `useGPUContext()`, `useConnectCharts(..., syncOptions?)`
34+
- **Multi-chart + streaming**: share a `GPUDevice` via `gpuContext` / `useGPUContext`, sync with `useConnectCharts`, stream with `appendData(..., { maxPoints })`
3435
- **Helper re-exports (from `@chartgpu/chartgpu`)**: `createChart`, `connectCharts`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache`, `createAnnotationAuthoring`
3536

3637
## Quick start
@@ -70,13 +71,16 @@ function MyChart() {
7071
npm install chartgpu-react @chartgpu/chartgpu react react-dom
7172
```
7273

74+
Peer dependency: **`@chartgpu/chartgpu` ^0.3.6** (aligned with this package’s 0.3.x line).
75+
7376
### Requirements
7477

75-
- React 18.0.0 or higher
78+
- **React 18 or 19** (`react` / `react-dom` ≥ 18)
79+
- **TypeScript 5+** for consumers (this package is built and typechecked with **TypeScript 7**)
7680
- Browser with WebGPU support:
7781
- Chrome/Edge 113+
7882
- Safari 18+
79-
- Firefox (not yet supported)
83+
- Firefox: Windows 114+, Mac 145+, Linux nightly
8084

8185
Check browser compatibility at [caniuse.com/webgpu](https://caniuse.com/webgpu).
8286

@@ -135,6 +139,40 @@ disconnect();
135139

136140
If you prefer a hook-driven approach, you can use `onReady` (or `useChartGPU`) to capture instances, then call `useConnectCharts(...)` once both are available.
137141

142+
### Streaming append with FIFO window (`maxPoints`)
143+
144+
```tsx
145+
import { useEffect, useRef } from 'react';
146+
import { ChartGPU } from 'chartgpu-react';
147+
import type { ChartGPUHandle } from 'chartgpu-react';
148+
149+
function StreamingChart() {
150+
const ref = useRef<ChartGPUHandle>(null);
151+
const xRef = useRef(0);
152+
153+
useEffect(() => {
154+
const id = window.setInterval(() => {
155+
const x = xRef.current++;
156+
ref.current?.appendData(0, [{ x, y: Math.sin(x * 0.05) }], { maxPoints: 50_000 });
157+
}, 16);
158+
return () => window.clearInterval(id);
159+
}, []);
160+
161+
return (
162+
<ChartGPU
163+
ref={ref}
164+
options={{
165+
autoScroll: true,
166+
series: [{ type: 'line', data: [], lineStyle: { width: 2, color: '#4facfe' } }],
167+
xAxis: { type: 'value' },
168+
yAxis: { type: 'value' },
169+
}}
170+
style={{ width: '100%', height: 320 }}
171+
/>
172+
);
173+
}
174+
```
175+
138176
### External render mode (app-owned render loop)
139177

140178
```tsx

docs/GETTING_STARTED.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ npm install chartgpu-react @chartgpu/chartgpu react react-dom
1010

1111
## Requirements
1212

13-
- **React**: 18+
14-
- **WebGPU**: a browser with `navigator.gpu` support (Chrome/Edge 113+, Safari 18+)
13+
- **@chartgpu/chartgpu**: ^0.3.6 (peer)
14+
- **React**: 18 or 19
15+
- **WebGPU**: a browser with `navigator.gpu` support (Chrome/Edge 113+, Safari 18+, modern Firefox)
1516

1617
If WebGPU is not available, chart creation will fail.
1718

@@ -65,7 +66,27 @@ See [Streaming recipe](./recipes/streaming.md).
6566

6667
## React 18 StrictMode
6768

68-
In development, React 18 StrictMode intentionally runs effects twice (mount → unmount → mount). `ChartGPU` and `useChartGPU` are written to be safe under this behavior (async create + cleanup ordering).
69+
In development, React 18 StrictMode intentionally runs effects twice (mount → unmount → mount). `ChartGPU`, `useChartGPU`, and `useGPUContext` are written to be safe under this behavior:
70+
71+
- **`ChartGPU` / `useChartGPU`**: async create + cleanup ordering (dispose if unmounted before create resolves).
72+
- **`useGPUContext`**: a shared init promise so StrictMode remount reuses one adapter/device/`PipelineCache` acquisition instead of requesting a second device.
73+
74+
## Testing (unit coverage map)
75+
76+
Unit tests live under `src/__tests__/` (Vitest + jsdom). They mock `@chartgpu/chartgpu` and do **not** require a real WebGPU device (except `useGPUContext`, which stubs `navigator.gpu`).
77+
78+
| Area | File |
79+
|------|------|
80+
| Create / `setOption` race (issue #16) | `src/__tests__/ChartGPU.test.tsx`, `src/__tests__/useChartGPU.test.tsx` |
81+
| Handle `appendData` + `{ maxPoints }`, `setZoomRange` source, external render | `src/__tests__/ChartGPU.test.tsx` |
82+
| Handle smoke (`getChart`, `setOption`, interaction X, `hitTest`) | `src/__tests__/ChartGPU.test.tsx` |
83+
| Event props (`onDataAppend`, `onDeviceLost`) | `src/__tests__/ChartGPU.test.tsx` |
84+
| `gpuContext``ChartGPU.create` third arg | `src/__tests__/ChartGPU.test.tsx`, `src/__tests__/useChartGPU.test.tsx` |
85+
| `useConnectCharts` | `src/__tests__/useConnectCharts.test.tsx` |
86+
| `useGPUContext` | `src/__tests__/useGPUContext.test.tsx` |
87+
| Public export surface | `src/__tests__/exports.test.ts` |
88+
89+
Run: `npm test`, `npm run typecheck`, `npm run build`.
6990

7091
## Next steps
7192

docs/api/hooks.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,9 @@ function useGPUContext(): {
113113
- On mount, requests a `GPUAdapter` (high-performance preference) and `GPUDevice`, then creates a `PipelineCache`.
114114
- All fields are `null` until initialization completes. `isReady` becomes `true` once both `adapter` and `device` are available.
115115
- If WebGPU is not supported or adapter/device acquisition fails, `error` is set and other fields remain `null`.
116-
- Safe in React 18 StrictMode dev (uses a ref guard to prevent double-initialization).
117-
- Initialization runs once on mount and cannot be re-triggered.
116+
- Safe in React 18 StrictMode dev: a **shared in-flight/completed init promise** ensures a single adapter/device/`PipelineCache` acquisition. The first effect may be cancelled by StrictMode’s simulated unmount; the second effect re-subscribes to the **same** promise and applies the result (it does **not** call `requestAdapter` again).
117+
- Initialization runs once per hook instance and cannot be re-triggered.
118+
- **Lifecycle / resource ownership:** the hook does **not** call `GPUDevice.destroy()` or `destroyPipelineCache` on unmount. It is intended for a **long-lived dashboard parent**. Mounting briefly and unmounting mid-init can leave a native device alive until page unload (auto-destroy would race with StrictMode remount, which reuses the shared promise). Keep `useGPUContext()` mounted for the process lifetime of the shared charts, or manage teardown yourself if you truly need a short-lived context.
118119

119120
### Usage with `<ChartGPU>`
120121

examples/assets/chartgpu.png

110 KB
Loading

0 commit comments

Comments
 (0)