# learn-mode _(no submodules)_ ## Purpose Functional helper around the `MidiControllerPlugin.enterLearnMode` / `exitLearnMode` toggle. The base class natively swallows the next unmapped CC message and emits a `learn` event when in learn mode; this module wraps that flow into a promise so application code can simply `await captureNextLearn(controller)` to get the captured `(channel, controller)` pair. It also exposes a cancellation handle for UI flows that need to abort the capture (user closes the dialog, controller disconnects, etc.). ## Features - Promise-based capture of the next CC message. - `cancel()` releases the subscription without exiting learn mode, so callers can compose multiple captures. - Zero new state on the plugin — uses the existing `on('learn', ...)` surface and `enterLearnMode()` toggle. ## Usage: Basic ```ts const { channel, controller: cc } = await captureNextLearn(controller); console.log("Mapped to channel", channel, "CC", cc); ``` ## Usage: Advanced ```ts const capture = captureNextLearn(controller); const timeout = setTimeout(() => capture.cancel(), 5000); try { const { channel, controller: cc } = await capture; controller.defineMappings([ { channel, controller: cc, target: "volume", min: 0, max: 1 }, ]); } finally { clearTimeout(timeout); } ``` ## API Reference - `captureNextLearn(plugin)` → `Promise & { cancel(): void }`. - `LearnResult` — `{ channel: number, controller: number }`. ## Implementation Notes The promise resolves at most once. After the first `learn` emission the internal subscription is released so the resolver cannot fire twice; the base class also auto-exits learn mode on first capture, layering defence in depth. `cancel()` simply unsubscribes the listener, but intentionally does not call `exitLearnMode()` — that's the caller's policy choice if the UI hand-off requires keeping learn-mode armed across multiple controls in the same session for batch learn-mode capture workflows.