Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: CI

on:
pull_request:
push:
branches: [main]

concurrency:
group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
typecheck-and-test:
name: Typecheck & test
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20.x"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Test
run: npm test

- name: Build
run: npm run build
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ coverage
*.swp
*.swo
*~
.hallmark


# Claude
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Changelog

## 0.3.0

Aligned with **@chartgpu/chartgpu ^0.3.6** and modern React/TypeScript tooling.

### Dependencies

- Peer: `@chartgpu/chartgpu` **^0.3.6** (was ^0.2.8)
- Peer: React **≥18** (dev/tested on **React 19.2**)
- Dev: **TypeScript 7.0**, Vite 7, Vitest 4, `@types/react` 19

### API

- `ChartGPUHandle.appendData` accepts optional `{ maxPoints }` for FIFO / fixed-capacity streaming (ChartGPU 0.3.x)
- `ChartGPUHandle.setZoomRange` passes optional `source` through to core
- `CartesianSeriesData` allows `null` gaps (matches core)
- Expanded type re-exports: heatmap, band, errorBar, impulse, OHLC, 3D series, `ZoomChangeSourceKind`, etc.
- New exported type: `ChartGPUAppendDataOptions`

### Tooling

- Build: Vite (ESM bundle) + `tsc` declaration emit
(`vite-plugin-dts` is incompatible with TypeScript 7’s slim package API)
- Package name restored to **`chartgpu-react`** for npmjs.org (GitHub Packages still scopes at publish)
- Test coverage for handle/hooks 0.3.x: `appendData`/`maxPoints`, `setZoomRange` source, external render, `useConnectCharts`, `useGPUContext`, `gpuContext` create path, `onDataAppend`/`onDeviceLost`, export smoke

### Examples

- Streaming multi-chart demos use `appendData(..., { maxPoints })`
44 changes: 41 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@

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

## Quick start
Expand Down Expand Up @@ -70,13 +71,16 @@ function MyChart() {
npm install chartgpu-react @chartgpu/chartgpu react react-dom
```

Peer dependency: **`@chartgpu/chartgpu` ^0.3.6** (aligned with this package’s 0.3.x line).

### Requirements

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

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

Expand Down Expand Up @@ -135,6 +139,40 @@ disconnect();

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

### Streaming append with FIFO window (`maxPoints`)

```tsx
import { useEffect, useRef } from 'react';
import { ChartGPU } from 'chartgpu-react';
import type { ChartGPUHandle } from 'chartgpu-react';

function StreamingChart() {
const ref = useRef<ChartGPUHandle>(null);
const xRef = useRef(0);

useEffect(() => {
const id = window.setInterval(() => {
const x = xRef.current++;
ref.current?.appendData(0, [{ x, y: Math.sin(x * 0.05) }], { maxPoints: 50_000 });
}, 16);
return () => window.clearInterval(id);
}, []);

return (
<ChartGPU
ref={ref}
options={{
autoScroll: true,
series: [{ type: 'line', data: [], lineStyle: { width: 2, color: '#4facfe' } }],
xAxis: { type: 'value' },
yAxis: { type: 'value' },
}}
style={{ width: '100%', height: 320 }}
/>
);
}
```

### External render mode (app-owned render loop)

```tsx
Expand Down
27 changes: 24 additions & 3 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ npm install chartgpu-react @chartgpu/chartgpu react react-dom

## Requirements

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

If WebGPU is not available, chart creation will fail.

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

## React 18 StrictMode

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).
In development, React 18 StrictMode intentionally runs effects twice (mount → unmount → mount). `ChartGPU`, `useChartGPU`, and `useGPUContext` are written to be safe under this behavior:

- **`ChartGPU` / `useChartGPU`**: async create + cleanup ordering (dispose if unmounted before create resolves).
- **`useGPUContext`**: a shared init promise so StrictMode remount reuses one adapter/device/`PipelineCache` acquisition instead of requesting a second device.

## Testing (unit coverage map)

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`).

| Area | File |
|------|------|
| Create / `setOption` race (issue #16) | `src/__tests__/ChartGPU.test.tsx`, `src/__tests__/useChartGPU.test.tsx` |
| Handle `appendData` + `{ maxPoints }`, `setZoomRange` source, external render | `src/__tests__/ChartGPU.test.tsx` |
| Handle smoke (`getChart`, `setOption`, interaction X, `hitTest`) | `src/__tests__/ChartGPU.test.tsx` |
| Event props (`onDataAppend`, `onDeviceLost`) | `src/__tests__/ChartGPU.test.tsx` |
| `gpuContext` → `ChartGPU.create` third arg | `src/__tests__/ChartGPU.test.tsx`, `src/__tests__/useChartGPU.test.tsx` |
| `useConnectCharts` | `src/__tests__/useConnectCharts.test.tsx` |
| `useGPUContext` | `src/__tests__/useGPUContext.test.tsx` |
| Public export surface | `src/__tests__/exports.test.ts` |

Run: `npm test`, `npm run typecheck`, `npm run build`.

## Next steps

Expand Down
5 changes: 3 additions & 2 deletions docs/api/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ function useGPUContext(): {
- On mount, requests a `GPUAdapter` (high-performance preference) and `GPUDevice`, then creates a `PipelineCache`.
- All fields are `null` until initialization completes. `isReady` becomes `true` once both `adapter` and `device` are available.
- If WebGPU is not supported or adapter/device acquisition fails, `error` is set and other fields remain `null`.
- Safe in React 18 StrictMode dev (uses a ref guard to prevent double-initialization).
- Initialization runs once on mount and cannot be re-triggered.
- 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).
- Initialization runs once per hook instance and cannot be re-triggered.
- **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.

### Usage with `<ChartGPU>`

Expand Down
Binary file added examples/assets/chartgpu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading