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,72 @@
# `hooks/use-midi`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`useMidi()` is the most general hook in `@suaveplan/react-midi`. It
returns the live `MidiContextValue` surface as
`{ status, access, inputs, outputs, error }`, where `inputs` and
`outputs` are arrays freshly materialised from the underlying
`ReadonlyMap`s whenever the provider's `stateVersion` advances
(i.e. whenever a port plugs or unplugs). Components that only need
status or device-list snapshots can reach for this hook directly;
specialised hooks (`useMidiInput`, `useMidiDevices`) wrap it for
narrower use-cases.
The hook never throws on a `denied` / `unsupported` status — it
*reports* the state. The only throw is the structural
`REACT_MIDI.NO_PROVIDER` when rendered outside a provider.
## Features
- One snapshot of status, access, inputs, outputs, and last error.
- Arrays re-materialise on `stateVersion` bumps; stable identity
between bumps so consumers can pass them straight to `key`.
- Returns empty arrays during `loading` / `denied` / `unsupported`.
## Usage: Basic
```tsx
import { useMidi } from "@suaveplan/react-midi";
function DeviceCounts() {
const { status, inputs, outputs } = useMidi();
if (status !== "granted") return <p>{status}</p>;
return (
<p>
{inputs.length} ins / {outputs.length} outs
</p>
);
}
```
## Usage: Advanced
```tsx
function PermissionGate({ children }: { children: React.ReactNode }) {
const { status, error } = useMidi();
if (status === "loading") return <Spinner />;
if (status === "unsupported")
return <p>Web MIDI is not available in this browser.</p>;
if (status === "denied")
return <p>Permission denied: {error?.message}.</p>;
return <>{children}</>;
}
```
## API Reference
- `useMidi(): UseMidiResult`
- `interface UseMidiResult { status, access, inputs, outputs, error }`.
## Implementation Notes
The hook materialises `Array.from(access.inputs.values())` only when
either `access` (root identity) or `stateVersion` (plug/unplug
counter) changes — `useMemo` keys both into the dep list. This means
consumers that pass `inputs.length` to a downstream effect avoid the
"every render is a new array" trap that a naïve `[...map.values()]`
would create.