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,74 @@
# `hooks/use-timeline`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`useTimeline()` returns the provider's single `TimelineEngine`
instance, the canonical owner of timeline state — tracks, clips,
selection, loop region, master volume, undo/redo. The engine exposes
its own typed event surface (`engine.on(event, listener)`); consumers
who want to *subscribe* to engine events should pair this hook with
`useSyncExternalStore` and pass `engine.on(...)` as the subscribe
callback. The hook itself returns the engine handle and does not
trigger re-renders on engine state changes.
## Features
- Returns the `TimelineEngine` instance owned by the provider.
- Stable identity across re-renders within a ready phase.
- Throws `REACT_AUDIO.NO_PROVIDER` outside `<AudioProvider>`.
- Throws `REACT_AUDIO.PROVIDER_PENDING` during async init.
## Usage: Basic
```tsx
import { useTimeline } from "@suaveplan/react-audio";
function AddTrackButton() {
const timeline = useTimeline();
return (
<button type="button" onClick={() => timeline.addTrack({ name: "New Track" })}>
Add track
</button>
);
}
```
## Usage: Advanced
```tsx
import { useSyncExternalStore } from "react";
import { useTimeline } from "@suaveplan/react-audio";
function useTrackCount(): number {
const timeline = useTimeline();
return useSyncExternalStore(
(listener) => timeline.on("trackChange", listener as never),
() => timeline.getState().tracks.length,
() => timeline.getState().tracks.length
);
}
```
## API Reference
- `useTimeline(): TimelineEngine` — see
`@suaveplan/audio-timeline` for the full engine surface.
## Implementation Notes
The hook deliberately stops short of re-rendering on every engine
event. The `TimelineEngine` has a rich change surface and re-rendering
on every state delta would be expensive at chain scale. Consumers
choose which events they care about and pair the engine handle with
`useSyncExternalStore` for narrow subscriptions, paying only for the
re-renders they want. Mutator methods (`addTrack`, `updateTrack`,
`removeTrack`, `setTempo`, `setMasterVolume`, …) are exposed directly
on the engine handle — callers wrap them in their own callbacks when
they need referential stability across renders. For per-track state
prefer `useTrack(id)`, which already wraps `useSyncExternalStore`
against the timeline's `trackChange` event so its subscription is
narrow by construction.