first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
# `hooks/use-chain-snapshot`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`useChainSnapshot(chain)` exposes chain-level snapshot operations.
`get` reads the in-memory `ChainSnapshot`, `restore` applies one,
and `save` / `load` persist through the provider's optional preset
store, keyed by id. The hook is the natural complement to
`usePluginChain` for "FX rack" UIs that save and load entire chains
as a unit.
## Features
- `get()` — synchronous in-memory read of `chain.snapshot()`.
- `restore(input)` — async; delegates to `chain.restore(input, registry)`.
- `save(id)` — persists the snapshot via the preset store.
- `load(id)` — loads + applies via the preset store.
- Structured errors: `PRESET_STORE_FAILED` for every failure mode.
## Usage: Basic
```tsx
import { useChainSnapshot } from "@suaveplan/react-audio";
function ChainPresetButtons({ chain }: { chain: PluginChain }) {
const { save, load } = useChainSnapshot(chain);
return (
<>
<button type="button" onClick={() => save("default")}>Save chain</button>
<button type="button" onClick={() => load("default")}>Load chain</button>
</>
);
}
```
## Usage: Advanced
```tsx
import { useChainSnapshot } from "@suaveplan/react-audio";
function ExportImport({ chain }: { chain: PluginChain }) {
const { get, restore } = useChainSnapshot(chain);
return (
<>
<button onClick={() => navigator.clipboard.writeText(JSON.stringify(get()))}>
Copy JSON
</button>
<button
onClick={async () => {
const text = await navigator.clipboard.readText();
await restore(JSON.parse(text));
}}
>
Paste JSON
</button>
</>
);
}
```
## API Reference
- `useChainSnapshot(chain): { get, restore, save, load }`.
## Implementation Notes
The hook needs the provider's `PluginRegistry` to instantiate
plugins during `restore`, so it consumes `useProviderStateOrThrow`.
Every failure mode of the preset store is wrapped into a
`ReactAudioError(PRESET_STORE_FAILED)` with the underlying error as
`cause`; the absence of a preset store and the "preset id not
found" case use the same code with different `messageKey`s so a
catch-handler can branch on the message resolver if needed. The
`get()` helper returns the raw `chain.snapshot()` which includes
the chain's `blockSize` and `sampleRate`; consumers exporting JSON
for cross-instance restore should preserve those fields verbatim,
because `chain.restore` re-validates them against the chain's own
config on the receiving side. Non-Error throws from the store are
narrowed to `undefined` causes (the JS `instanceof Error` ternary).