Skip to content

Commit dd1a1ec

Browse files
authored
Merge pull request #11 from ChartGPU/upgrade-chartgpu-version
Upgrade to version 0.1.3 and enhance ChartGPU API
2 parents ff2ea6d + d830839 commit dd1a1ec

File tree

17 files changed

+741
-53
lines changed

17 files changed

+741
-53
lines changed

.github/workflows/publish-gpr.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Publish to GitHub Packages
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
publish:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
packages: write
13+
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
18+
- name: Setup Node
19+
uses: actions/setup-node@v4
20+
with:
21+
node-version: "20.x"
22+
cache: "npm"
23+
24+
- name: Install dependencies
25+
run: npm ci
26+
27+
- name: Verify release tag matches package version
28+
env:
29+
RELEASE_TAG: ${{ github.event.release.tag_name }}
30+
run: |
31+
node -e "const fs=require('fs');const v=JSON.parse(fs.readFileSync('package.json','utf8')).version;const t=(process.env.RELEASE_TAG||'').replace(/^v/,'');if(!t){console.error('Missing release tag');process.exit(1)};if(t!==v){console.error(`Release tag (${process.env.RELEASE_TAG}) does not match package.json version (${v})`);process.exit(1)};console.log(`Version OK: ${v}`);"
32+
33+
- name: Build
34+
run: npm run build
35+
36+
# GitHub Packages npm registry requires a scoped package name that matches the owner/org.
37+
# We scope it only at publish-time so the source package name can stay unscoped for npmjs.org if desired.
38+
- name: Scope package name for GitHub Packages
39+
run: |
40+
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.name='@chartgpu/chartgpu-react';fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n');"
41+
42+
- name: Configure npm for GitHub Packages
43+
uses: actions/setup-node@v4
44+
with:
45+
node-version: "20.x"
46+
registry-url: "https://npm.pkg.github.com"
47+
48+
- name: Publish
49+
run: npm publish --registry=https://npm.pkg.github.com
50+
env:
51+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
## Highlights
2929

3030
- **`ChartGPU` component (recommended)**: async create/dispose lifecycle + debounced `ResizeObserver` sizing
31-
- **Event props**: `onClick`, `onCrosshairMove`, `onZoomChange`, etc.
32-
- **Imperative `ref` API**: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`)
33-
- **Hooks**: `useChartGPU(...)`, `useConnectCharts(..., syncOptions?)`
34-
- **Helper re-exports (from `@chartgpu/chartgpu`)**: `createChart`, `connectCharts`, `createAnnotationAuthoring`
31+
- **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`)
33+
- **Hooks**: `useChartGPU(...)`, `useGPUContext()`, `useConnectCharts(..., syncOptions?)`
34+
- **Helper re-exports (from `@chartgpu/chartgpu`)**: `createChart`, `connectCharts`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache`, `createAnnotationAuthoring`
3535

3636
## Quick start
3737

@@ -85,15 +85,17 @@ Check browser compatibility at [caniuse.com/webgpu](https://caniuse.com/webgpu).
8585
- **`ChartGPU`** React component (recommended)
8686
- lifecycle management (async create + dispose)
8787
- `ResizeObserver` resize (debounced)
88-
- event props: `onClick`, `onCrosshairMove`, `onZoomChange`, etc.
89-
- imperative `ref` API: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`)
88+
- event props: `onClick`, `onCrosshairMove`, `onZoomChange`, `onDataAppend`, `onDeviceLost`, etc.
89+
- multi-chart dashboards: `gpuContext` prop (share a `GPUDevice` across charts)
90+
- imperative `ref` API: `ChartGPUHandle` (`getChart`, `getContainer`, `appendData`, `setOption`, `setZoomRange`, `setInteractionX`, `getInteractionX`, `hitTest`, `needsRender`, `renderFrame`, `getRenderMode`, `setRenderMode`)
9091
- **Hooks**
91-
- `useChartGPU(containerRef, options)` — create/manage a chart instance
92+
- `useChartGPU(containerRef, options, gpuContext?)` — create/manage a chart instance (optionally share GPU resources)
93+
- `useGPUContext()` — create a shared `GPUAdapter` + `GPUDevice` + `PipelineCache` for multi-chart dashboards
9294
- `useConnectCharts([chartA, chartB, ...], syncOptions?)` — sync crosshair/interaction-x (and optionally zoom) across charts
9395
- **Deprecated**
9496
- `ChartGPUChart` (legacy adapter; use `ChartGPU` instead)
9597
- **Helper re-exports** (from peer dependency `@chartgpu/chartgpu`)
96-
- `createChart`, `connectCharts`, `createAnnotationAuthoring`
98+
- `createChart`, `connectCharts`, `createAnnotationAuthoring`, `createPipelineCache`, `getPipelineCacheStats`, `destroyPipelineCache`
9799

98100
For details, start with the [API reference](./docs/API.md).
99101

@@ -133,6 +135,56 @@ disconnect();
133135

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

138+
### External render mode (app-owned render loop)
139+
140+
```tsx
141+
import { useEffect, useRef } from 'react';
142+
import { ChartGPU } from 'chartgpu-react';
143+
import type { ChartGPUHandle } from 'chartgpu-react';
144+
145+
function ExternalLoop() {
146+
const ref = useRef<ChartGPUHandle>(null);
147+
148+
useEffect(() => {
149+
let raf = 0;
150+
const loop = () => {
151+
if (ref.current?.needsRender()) {
152+
ref.current.renderFrame();
153+
}
154+
raf = requestAnimationFrame(loop);
155+
};
156+
raf = requestAnimationFrame(loop);
157+
return () => cancelAnimationFrame(raf);
158+
}, []);
159+
160+
return <ChartGPU ref={ref} options={{ ...options, renderMode: 'external' }} />;
161+
}
162+
```
163+
164+
### Multi-chart dashboards (shared GPU device + pipeline cache)
165+
166+
```tsx
167+
import { ChartGPU, useGPUContext } from 'chartgpu-react';
168+
169+
function Dashboard() {
170+
const { adapter, device, pipelineCache, isReady, error } = useGPUContext();
171+
172+
if (error) return <div>{error.message}</div>;
173+
if (!isReady || !adapter || !device) return <div>Loading…</div>;
174+
175+
const gpuContext = pipelineCache
176+
? { adapter, device, pipelineCache }
177+
: { adapter, device };
178+
179+
return (
180+
<>
181+
<ChartGPU options={optionsA} gpuContext={gpuContext} />
182+
<ChartGPU options={optionsB} gpuContext={gpuContext} />
183+
</>
184+
);
185+
}
186+
```
187+
136188
### Annotation authoring UI (`createAnnotationAuthoring`)
137189

138190
```tsx

docs/API.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ For an LLM-oriented navigation entrypoint, see [`docs/api/llm-context.md`](./api
1818

1919
### Hooks
2020

21-
- **`useChartGPU(containerRef, options)`** — create/manage an instance imperatively
21+
- **`useChartGPU(containerRef, options, gpuContext?)`** — create/manage an instance imperatively (3rd param optional shared context; init-only)
2222
- **`useConnectCharts([chartA, chartB, ...], syncOptions?)`** — keep crosshair/interaction-x in sync (optionally sync zoom)
2323

2424
See [`docs/api/hooks.md`](./api/hooks.md).
@@ -54,8 +54,8 @@ More recipes:
5454

5555
From `src/types.ts`:
5656

57-
- `ChartGPUProps` — props for the `ChartGPU` component
58-
- `ChartGPUHandle` — imperative ref API
57+
- `ChartGPUProps` — props for the `ChartGPU` component. Includes `gpuContext?` (init-only), `onDataAppend?``'dataAppend'`, `onDeviceLost?``'deviceLost'`
58+
- `ChartGPUHandle` — imperative ref API (`renderFrame`, `needsRender`, `getRenderMode`, `setRenderMode`, `appendData`, etc.)
5959
- `ChartInstance` — alias for `@chartgpu/chartgpu`'s `ChartGPUInstance`
6060
- `ClickParams`, `MouseOverParams` — aliases for event payloads
6161
- `ZoomRange` — derived from `ChartGPUInstance['getZoomRange']` (non-null range)
@@ -102,9 +102,10 @@ Start here: [`ChartGPU` component docs](./api/chartgpu-component.md).
102102

103103
Use the ref API when you need:
104104

105-
- `appendData` streaming
105+
- `appendData` streaming (Cartesian series data superset + OHLC array)
106106
- access to the underlying `ChartGPUInstance`
107107
- access to the container element (e.g. for annotation authoring UI overlays)
108+
- external render mode: `renderFrame()`, `needsRender()`, `getRenderMode()`, `setRenderMode()`
108109

109110
See [`ChartGPUHandle` docs](./api/chartgpu-handle.md).
110111

docs/GETTING_STARTED.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Getting started
22

3-
`chartgpu-react` is a **thin React + TypeScript wrapper** around the [`chartgpu`](https://www.npmjs.com/package/chartgpu) WebGPU charting library.
3+
`chartgpu-react` is a **thin React + TypeScript wrapper** around the [`@chartgpu/chartgpu`](https://www.npmjs.com/package/@chartgpu/chartgpu) WebGPU charting library.
44

55
## Install
66

77
```bash
8-
npm install chartgpu-react chartgpu react react-dom
8+
npm install chartgpu-react @chartgpu/chartgpu react react-dom
99
```
1010

1111
## Requirements

docs/api/chartgpu-component.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The props type is `ChartGPUProps` (defined in `src/types.ts`).
2323
| Prop | Type | Required | Notes |
2424
|---|---|---:|---|
2525
| `options` | `ChartGPUOptions` || Full chart configuration object. Updates call `setOption(...)` with a full replacement. |
26+
| `gpuContext` | `ChartGPUCreateContext` | | **Init-only.** Shared GPU context for multi-chart dashboards. Changing after mount has no effect. |
2627
| `theme` | `ChartGPUOptions['theme']` | | Theme override merged into `options` (`'dark' \| 'light' \| ThemeConfig`). |
2728
| `style` | `React.CSSProperties` | | Applied to the container `<div>`. Provide an explicit `height`. |
2829
| `className` | `string` | | Applied to the container `<div>`. |
@@ -31,6 +32,8 @@ The props type is `ChartGPUProps` (defined in `src/types.ts`).
3132
| `onMouseOver` | `(payload: ChartGPUEventPayload) => void` | | Wires to `chart.on('mouseover', ...)`. |
3233
| `onMouseOut` | `(payload: ChartGPUEventPayload) => void` | | Wires to `chart.on('mouseout', ...)`. |
3334
| `onCrosshairMove` | `(payload: ChartGPUCrosshairMovePayload) => void` | | Wires to `chart.on('crosshairMove', ...)`. |
35+
| `onDataAppend` | `(payload: ChartGPUDataAppendPayload) => void` | | Wires to `chart.on('dataAppend', ...)`. Fires when data is appended via `appendData(...)`. |
36+
| `onDeviceLost` | `(payload: ChartGPUDeviceLostPayload) => void` | | Wires to `chart.on('deviceLost', ...)`. Most relevant when using shared `gpuContext`. |
3437
| `onZoomChange` | `(range: ZoomRange) => void` | | Fires on `zoomRangeChange` event. Also emits the current range once on subscribe (initial hydration). |
3538

3639
## Imperative ref (`ChartGPUHandle`)
@@ -39,7 +42,10 @@ The props type is `ChartGPUProps` (defined in `src/types.ts`).
3942

4043
- `getChart()`
4144
- `getContainer()`
42-
- `appendData(seriesIndex, newPoints)`
45+
- `appendData(seriesIndex, newPoints)` — accepts Cartesian series data superset or `OHLCDataPoint[]`
46+
- `renderFrame(): boolean` — render one frame (external mode)
47+
- `needsRender(): boolean` — whether the chart has pending changes
48+
- `getRenderMode()`, `setRenderMode(mode)` — external render mode control
4349
- `setOption(options)`
4450
- `setZoomRange(start, end)`
4551
- `setInteractionX(x, source?)`
@@ -55,10 +61,10 @@ See [`ChartGPUHandle`](./chartgpu-handle.md).
5561
On mount, the component calls:
5662

5763
```ts
58-
ChartGPU.create(containerDiv, effectiveOptions)
64+
ChartGPU.create(containerDiv, effectiveOptions, gpuContext?)
5965
```
6066
61-
Where `effectiveOptions` is `options` with `theme` merged in when provided.
67+
Where `effectiveOptions` is `options` with `theme` merged in when provided. If `gpuContext` is passed, it is used for shared GPU resources (init-only; changing the prop after mount has no effect).
6268
6369
If the component unmounts before async creation completes (common in React 18 StrictMode dev), the newly-created chart instance is disposed immediately to avoid leaks.
6470

docs/api/chartgpu-handle.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- accessing the underlying `ChartGPUInstance`
66
- accessing the container element
77
- streaming/append updates (`appendData`)
8+
- external render mode (`renderFrame`, `needsRender`, `getRenderMode`, `setRenderMode`)
89
- replacing options (`setOption`)
910
- programmatic zoom control (`setZoomRange`)
1011
- programmatic crosshair/tooltip (`setInteractionX`, `getInteractionX`)
@@ -42,15 +43,33 @@ Returns the container `<div>` used to mount the chart.
4243

4344
Common use case: pass it to helpers like `createAnnotationAuthoring(container, chart, ...)`.
4445

45-
### `appendData(seriesIndex: number, newPoints: DataPoint[] | OHLCDataPoint[]): void`
46+
### `appendData(seriesIndex: number, newPoints: CartesianSeriesData | OHLCDataPoint[]): void`
4647

4748
Appends points to an existing series, delegating to `ChartGPUInstance.appendData(...)`.
4849

4950
- **`seriesIndex`**: zero-based index into `options.series`.
50-
- **`newPoints`**: array of new points. For candlesticks, use `OHLCDataPoint[]`.
51+
- **`newPoints`**: Cartesian series data superset (`DataPoint[]`, `XYArraysData`, `InterleavedXYData`) or `OHLCDataPoint[]` for candlesticks.
5152

5253
This is typically more efficient than replacing the entire `options.series[n].data` array.
5354

55+
### `renderFrame(): boolean`
56+
57+
Renders a single frame. Intended for external render mode.
58+
59+
- **Returns**: `true` if a frame was rendered, `false` if already clean.
60+
61+
### `needsRender(): boolean`
62+
63+
Returns whether the chart has pending changes that require rendering.
64+
65+
### `getRenderMode(): RenderMode`
66+
67+
Returns the current render mode (`'auto'` or `'external'`).
68+
69+
### `setRenderMode(mode: RenderMode): void`
70+
71+
Sets the render mode. Use `'external'` to drive frames manually via `renderFrame()` based on `needsRender()`.
72+
5473
### `setOption(options: ChartGPUOptions): void`
5574

5675
Replaces the chart options, delegating to `ChartGPUInstance.setOption(options)`.
@@ -100,6 +119,18 @@ Import the result type if needed:
100119
import type { ChartGPUHitTestResult } from 'chartgpu-react';
101120
```
102121

122+
## External render mode
123+
124+
Set `options.renderMode = 'external'` or call `setRenderMode('external')`, then drive frames yourself:
125+
126+
```ts
127+
if (handle.current?.needsRender()) {
128+
handle.current.renderFrame();
129+
}
130+
```
131+
132+
Useful when you need precise control over when the chart renders (e.g. coordinating with other animations or a custom render loop).
133+
103134
## Example: streaming with `appendData`
104135

105136
```tsx

docs/api/hooks.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
This package provides two hooks:
44

5-
- `useChartGPU(containerRef, options)` — create/manage a `ChartGPUInstance`
5+
- `useChartGPU(containerRef, options, gpuContext?)` — create/manage a `ChartGPUInstance` (3rd param optional shared context; init-only)
66
- `useConnectCharts(charts, syncOptions?)` — connect instances for synced crosshair/interaction-x (and optionally zoom)
77

88
Related:
@@ -11,35 +11,38 @@ Related:
1111
- [Chart sync recipe](../recipes/chart-sync.md)
1212
- LLM entrypoint: [`llm-context.md`](./llm-context.md)
1313

14-
## `useChartGPU(containerRef, options)`
14+
## `useChartGPU(containerRef, options, gpuContext?)`
1515

1616
Creates a `@chartgpu/chartgpu` chart instance inside a DOM element that you control.
1717

1818
### Import
1919

2020
```ts
2121
import { useChartGPU } from 'chartgpu-react';
22-
import type { ChartGPUOptions } from 'chartgpu-react';
22+
import type { ChartGPUOptions, ChartGPUCreateContext } from 'chartgpu-react';
2323
```
2424

2525
### Signature
2626

2727
```ts
2828
function useChartGPU(
2929
containerRef: React.RefObject<HTMLElement>,
30-
options: ChartGPUOptions
30+
options: ChartGPUOptions,
31+
gpuContext?: ChartGPUCreateContext
3132
): {
3233
chart: ChartGPUInstance | null;
3334
isReady: boolean;
3435
error: Error | null;
3536
}
3637
```
3738

39+
The 3rd parameter `gpuContext` is **init-only**: it is only read during chart creation. Changing it after mount has no effect.
40+
3841
### Behavior
3942

4043
- On mount:
4144
- checks WebGPU support (`'gpu' in navigator`)
42-
- calls `ChartGPU.create(containerRef.current, options)`
45+
- calls `ChartGPU.create(containerRef.current, options, gpuContext?)`
4346
- sets `chart`, `isReady`, and `error` accordingly
4447
- On `options` change:
4548
- calls `chart.setOption(options)` (full replacement)

0 commit comments

Comments
 (0)