@signalsandsorcery/plugin-sdk 2.28.1 → 2.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import React, { ComponentType, ReactNode, DragEvent, Dispatch, SetStateAction } from 'react';
1
+ import React$1, { ReactNode, ComponentType, DragEvent, Dispatch, SetStateAction } from 'react';
2
2
 
3
3
  /**
4
4
  * Plugin SDK Type Definitions
@@ -383,6 +383,19 @@ interface PluginHost {
383
383
  setRawPluginState(trackId: string, pluginIndex: number, stateBase64: string): Promise<void>;
384
384
  /** Get a plugin's RAW VST3/AU state (see setRawPluginState). @since SDK 2.15.0 */
385
385
  getRawPluginState(trackId: string, pluginIndex: number): Promise<string>;
386
+ /**
387
+ * Persist a preset as the track's durable sound identity (DB `preset_state`,
388
+ * the shape getTrackSound reads back). setPluginState/setRawPluginState are
389
+ * engine-only — a copied sound that is never persisted has no identity, so
390
+ * drift checks (e.g. the transition-designer preset re-sync) can never
391
+ * observe convergence. Call after applying a copied state to a layer track.
392
+ * @since SDK 2.34.0
393
+ */
394
+ persistTrackPresetState?(trackId: string, preset: {
395
+ state: string;
396
+ stateType: 'raw' | 'valuetree';
397
+ name?: string;
398
+ }): Promise<void>;
386
399
  /** List plugins currently loaded on a track. */
387
400
  getTrackPlugins(trackId: string): Promise<PluginSynthInfo[]>;
388
401
  /** Remove a plugin from a track. */
@@ -882,6 +895,30 @@ interface PluginHost {
882
895
  deleteSampleTrack(trackId: string): Promise<void>;
883
896
  /** Get all sample tracks in the active scene. Re-establishes ownership. */
884
897
  getPluginSampleTracks(): Promise<PluginSampleTrackInfo[]>;
898
+ /**
899
+ * Resolve a sample-track row id (as returned by `listSceneFamilyTracks`) back
900
+ * to its library `sampleId` + metadata, so a loop can be re-created in another
901
+ * scene (transition crossfade/fade). Returns null if not a sample track.
902
+ * @since SDK 2.31.0
903
+ */
904
+ getSampleTrackInfo?(dbId: string): Promise<{
905
+ sampleId: string;
906
+ fileName?: string;
907
+ bpm?: number;
908
+ key?: string;
909
+ } | null>;
910
+ /**
911
+ * Render an audio transition effect onto a sample (offline DSP via the audio
912
+ * tool), returning a NEW library sample to place. Used for stutter / chopped
913
+ * loop transitions. @since SDK 2.32.0
914
+ */
915
+ renderSampleEffect?(sampleId: string, spec: {
916
+ effect: 'stutter' | 'chopped';
917
+ bars: number;
918
+ bpm: number;
919
+ repeats?: number;
920
+ slices?: number;
921
+ }): Promise<PluginSampleInfo>;
885
922
  /** Time-stretch a sample to a target BPM. Returns the new sample info. */
886
923
  timeStretchSample(sampleId: string, targetBpm: number): Promise<PluginSampleInfo>;
887
924
  /**
@@ -2287,7 +2324,7 @@ interface TrackDrawerProps {
2287
2324
  /** Optional single-note preview when the user adds a note. */
2288
2325
  onAuditionNote?: (pitch: number, velocity: number, durationMs: number) => void;
2289
2326
  }
2290
- declare function TrackDrawer({ activeTab, onTabChange, trackId, fxState, onFxToggle, onFxPresetChange, onFxDryWetChange, fxDisabled, instruments, currentPluginId, isLoading, onSelect, onRefresh, editorStage, onShowEditor, onBackToInstruments, selectedInstrumentName, soundHistory, soundHistoryCursor, onRestoreSound, onToggleFavorite, onImportSound, importSoundLabel, editNotes, onNotesChange, editBars, editBpm, editSnap, onAuditionNote, }: TrackDrawerProps): React.ReactElement;
2327
+ declare function TrackDrawer({ activeTab, onTabChange, trackId, fxState, onFxToggle, onFxPresetChange, onFxDryWetChange, fxDisabled, instruments, currentPluginId, isLoading, onSelect, onRefresh, editorStage, onShowEditor, onBackToInstruments, selectedInstrumentName, soundHistory, soundHistoryCursor, onRestoreSound, onToggleFavorite, onImportSound, importSoundLabel, editNotes, onNotesChange, editBars, editBpm, editSnap, onAuditionNote, }: TrackDrawerProps): React$1.ReactElement;
2291
2328
 
2292
2329
  /**
2293
2330
  * useTrackLevels — drives the cosmetic per-track strip meters.
@@ -2488,7 +2525,7 @@ interface SDKTrackRowProps {
2488
2525
  * like CrossfadeTrackRow owns a single delete for the whole pair). */
2489
2526
  onDelete?: () => void;
2490
2527
  /** Custom content replacing the prompt input (e.g., sample info display) */
2491
- contentSlot?: React.ReactNode;
2528
+ contentSlot?: React$1.ReactNode;
2492
2529
  /** Toggle mute */
2493
2530
  onMuteToggle: () => void;
2494
2531
  /** Toggle solo */
@@ -2562,7 +2599,7 @@ interface SDKTrackRowProps {
2562
2599
  * a thin peak meter welds to the bottom of the row. Omit to hide it. */
2563
2600
  levels?: TrackLevelsHandle;
2564
2601
  }
2565
- declare function TrackRow({ track, prompt, runtimeState, soloedOut, fxDetailState, drawerOpen, drawerTab, onTabChange, isGenerating, isAuthenticated, error, hasMidi, generationProgress, estimatedGenerationMs, onPromptChange, onGenerate, onShuffle, onCopy, onDelete, contentSlot, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onFxToggle, onFxPresetChange, onFxDryWetChange, onToggleFxDrawer, onProgressChange, accentColor, instrumentName, instrumentMissing, onToggleDrawer, availableInstruments, currentInstrumentPluginId, onInstrumentSelect, instrumentsLoading, onRefreshInstruments, editorStage, onShowEditor, onBackToInstruments, soundHistory, soundHistoryCursor, onRestoreSound, onToggleFavorite, onImportSound, importSoundLabel, editNotes, onNotesChange, editBars, editBpm, editSnap, onAuditionNote, drag, levels, }: SDKTrackRowProps): React.ReactElement;
2602
+ declare function TrackRow({ track, prompt, runtimeState, soloedOut, fxDetailState, drawerOpen, drawerTab, onTabChange, isGenerating, isAuthenticated, error, hasMidi, generationProgress, estimatedGenerationMs, onPromptChange, onGenerate, onShuffle, onCopy, onDelete, contentSlot, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onFxToggle, onFxPresetChange, onFxDryWetChange, onToggleFxDrawer, onProgressChange, accentColor, instrumentName, instrumentMissing, onToggleDrawer, availableInstruments, currentInstrumentPluginId, onInstrumentSelect, instrumentsLoading, onRefreshInstruments, editorStage, onShowEditor, onBackToInstruments, soundHistory, soundHistoryCursor, onRestoreSound, onToggleFavorite, onImportSound, importSoundLabel, editNotes, onNotesChange, editBars, editBpm, editSnap, onAuditionNote, drag, levels, }: SDKTrackRowProps): React$1.ReactElement;
2566
2603
 
2567
2604
  /**
2568
2605
  * Crossfade-pair metadata — family-agnostic types + parsing shared by every
@@ -2577,6 +2614,16 @@ declare function TrackRow({ track, prompt, runtimeState, soloedOut, fxDetailStat
2577
2614
  *
2578
2615
  * @since SDK 2.23.0
2579
2616
  */
2617
+
2618
+ /**
2619
+ * Stable, state-aware identity for a track's sound — used to auto-detect when a
2620
+ * transition's SOURCE preset/sample has drifted from the copy on its layer. A
2621
+ * preset hashes its full STATE blob (so a same-name param tweak still differs); a
2622
+ * sample uses its path; an instrument uses its id + zones. Empty string = no sound.
2623
+ * @since SDK 2.32.0
2624
+ */
2625
+ declare function hashString(s: string): string;
2626
+ declare function soundIdentity(snap: TrackSoundSnapshot | null | undefined): string;
2580
2627
  /** Which half of the pair a per-layer control / member targets. */
2581
2628
  type CrossfadeSlot = 'origin' | 'target';
2582
2629
  /**
@@ -2715,7 +2762,7 @@ interface CrossfadeTrackRowProps {
2715
2762
  /** Left-border accent. Defaults to transition purple. */
2716
2763
  accentColor?: string;
2717
2764
  }
2718
- declare function CrossfadeTrackRow({ origin, target, sliderPos, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onDelete, onSliderChange, levels, accentColor, }: CrossfadeTrackRowProps): React.ReactElement;
2765
+ declare function CrossfadeTrackRow({ origin, target, sliderPos, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onDelete, onSliderChange, levels, accentColor, }: CrossfadeTrackRowProps): React$1.ReactElement;
2719
2766
 
2720
2767
  /**
2721
2768
  * Crossfade MIDI inpainting — builds the LLM user-prompt for a bridge that
@@ -2809,6 +2856,13 @@ type FadeGesture = 'volume' | 'build';
2809
2856
  interface FadeMeta {
2810
2857
  direction: FadeDirection;
2811
2858
  gesture: FadeGesture;
2859
+ /**
2860
+ * Audio transition variant for one-sided LOOP transitions. `'fade'` (default,
2861
+ * and the only value MIDI panels write) is a plain level ramp; `stutter` /
2862
+ * `chopped` re-render the loop's audio, `delay` adds a delay-throw FX. Shown as
2863
+ * a badge on the row. @since SDK 2.32.0
2864
+ */
2865
+ effect?: 'fade' | 'stutter' | 'chopped' | 'delay';
2812
2866
  /** DB id of the SOURCE track this fade's preset/sample + pattern was seeded from. */
2813
2867
  sourceTrackDbId: string;
2814
2868
  /** DB id of the scene the source track lives in (the from/to scene). */
@@ -2909,6 +2963,8 @@ interface FadeTrackRowProps {
2909
2963
  direction: FadeDirection;
2910
2964
  /** How the fade is shaped — shown read-only (volume = level ramp, build = notes). */
2911
2965
  gesture: FadeGesture;
2966
+ /** Audio transition variant — relabels the badge (Stutter/Chopped/Delay). @since SDK 2.32.0 */
2967
+ effect?: 'fade' | 'stutter' | 'chopped' | 'delay';
2912
2968
  /** Fade position 0..1 — WHERE in time the fade sits. Defaults centered. */
2913
2969
  sliderPos?: number;
2914
2970
  /** Toggle mute. */
@@ -2928,7 +2984,7 @@ interface FadeTrackRowProps {
2928
2984
  /** Left-border accent. Defaults to transition purple. */
2929
2985
  accentColor?: string;
2930
2986
  }
2931
- declare function FadeTrackRow({ layer, direction, gesture, sliderPos, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onDelete, onSliderChange, levels, accentColor, }: FadeTrackRowProps): React.ReactElement;
2987
+ declare function FadeTrackRow({ layer, direction, gesture, effect, sliderPos, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onDelete, onSliderChange, levels, accentColor, }: FadeTrackRowProps): React$1.ReactElement;
2932
2988
 
2933
2989
  /**
2934
2990
  * FadeModal — "add a fade" picker for a transition scene.
@@ -2983,7 +3039,7 @@ interface FadeModalProps {
2983
3039
  /** data-testid prefix. */
2984
3040
  testIdPrefix?: string;
2985
3041
  }
2986
- declare function FadeModal({ host, open, fromSceneId, toSceneId, fromSceneName, toSceneName, excludeSourceDbIds, onClose, onCreate, testIdPrefix, }: FadeModalProps): React.ReactElement | null;
3042
+ declare function FadeModal({ host, open, fromSceneId, toSceneId, fromSceneName, toSceneName, excludeSourceDbIds, onClose, onCreate, testIdPrefix, }: FadeModalProps): React$1.ReactElement | null;
2987
3043
 
2988
3044
  /**
2989
3045
  * ImportTrackModal — "import a track from another scene" picker (SDK component).
@@ -3041,7 +3097,7 @@ interface ImportTrackModalProps {
3041
3097
  role?: string;
3042
3098
  }) => void | Promise<void>;
3043
3099
  }
3044
- declare function ImportTrackModal({ host, open, onClose, onImported, title, testIdPrefix, mode, onPick, onPortTrack, }: ImportTrackModalProps): React.ReactElement | null;
3100
+ declare function ImportTrackModal({ host, open, onClose, onImported, title, testIdPrefix, mode, onPick, onPortTrack, }: ImportTrackModalProps): React$1.ReactElement | null;
3045
3101
 
3046
3102
  /**
3047
3103
  * CrossfadeModal — "add a crossfade track" picker for a transition scene.
@@ -3097,7 +3153,182 @@ interface CrossfadeModalProps {
3097
3153
  /** data-testid prefix. */
3098
3154
  testIdPrefix?: string;
3099
3155
  }
3100
- declare function CrossfadeModal({ host, open, fromSceneId, toSceneId, fromSceneName, toSceneName, excludeSourceDbIds, onClose, onCreate, testIdPrefix, }: CrossfadeModalProps): React.ReactElement | null;
3156
+ declare function CrossfadeModal({ host, open, fromSceneId, toSceneId, fromSceneName, toSceneName, excludeSourceDbIds, onClose, onCreate, testIdPrefix, }: CrossfadeModalProps): React$1.ReactElement | null;
3157
+
3158
+ /**
3159
+ * TransitionDesigner — the per-panel transition staging board, rendered INLINE as
3160
+ * a toggled view inside a generator panel (NOT a modal).
3161
+ *
3162
+ * The multi-row, persistent successor to {@link CrossfadeModal} + {@link FadeModal}:
3163
+ * instead of opening a single-pair dialog ~20 times, the panel header gains a
3164
+ * Tracks ⇄ Transition toggle; flipping to Transition replaces the panel's track
3165
+ * list with this board. Playback is unaffected (the engine keeps playing the
3166
+ * scene's tracks — they're just not shown until you toggle back). Shown only
3167
+ * inside a `scene_type='transition'` scene and scoped to one panel family (a synth
3168
+ * board shows only synth tracks — "drums can't crossfade to synth" is enforced
3169
+ * structurally because each board asks its own family-scoped host).
3170
+ *
3171
+ * Two index-aligned, drag-reorderable columns: origin (scene A, left) and target
3172
+ * (scene B, right). Row i pairs origin[i] with target[i]; the pairing derives the
3173
+ * transition type (both → crossfade, origin-only → fade out, target-only → fade
3174
+ * in). Insert a "gap" above a cell to push a track so it fades instead of
3175
+ * crossfading with whatever sits opposite. The pool per column is the scene's
3176
+ * family tracks MINUS sources already consumed by a committed crossfade/fade
3177
+ * (`excludeSourceDbIds`).
3178
+ *
3179
+ * Per-row **Create** reuses the panel's EXISTING orchestration via `onCreateCrossfade`
3180
+ * / `onCreateFade`. Creates run CONCURRENTLY — fire several at once, or **Create all**
3181
+ * (a bounded pool). Each in-flight row shows its own progress bar and locks just its
3182
+ * own cells; the rest of the board stays editable. On success the source leaves the
3183
+ * pool (the panel updates `excludeSourceDbIds`) and the row collapses; deleting the
3184
+ * committed crossfade/fade on the deck returns the source here. The staged
3185
+ * arrangement persists to the transition scene's plugin_data so it survives toggles.
3186
+ *
3187
+ * @since SDK 2.29.0 (modal); inline toggle view + concurrent creation since 2.30.0.
3188
+ */
3189
+
3190
+ interface TransitionDesignerProps {
3191
+ /** Scoped host — the board calls listSceneFamilyTracks / getSceneName itself. */
3192
+ host: PluginHost;
3193
+ /** DB id of the transition's FROM (origin) scene. */
3194
+ fromSceneId: string;
3195
+ /** DB id of the transition's TO (target) scene. */
3196
+ toSceneId: string;
3197
+ /** DB id of the transition scene itself — the staged draft is persisted here. */
3198
+ transitionSceneId: string;
3199
+ /**
3200
+ * Source-track DB ids already consumed by a committed crossfade OR fade in this
3201
+ * panel. Hidden from both columns so each source is used at most once; when the
3202
+ * deck row is deleted the panel drops the id and the source reappears here.
3203
+ */
3204
+ excludeSourceDbIds?: readonly string[];
3205
+ /**
3206
+ * Build a crossfade pair — the panel's existing handler (create two tracks, one
3207
+ * morphed clip, copy each preset). Should reject on failure. Safe to call
3208
+ * concurrently.
3209
+ */
3210
+ onCreateCrossfade: (origin: CrossfadeSelection, target: CrossfadeSelection) => Promise<void>;
3211
+ /** Build a one-sided fade — the panel's existing handler. Should reject on failure. */
3212
+ onCreateFade: (selection: FadeSelection, direction: FadeDirection, gesture: FadeGesture) => Promise<void>;
3213
+ /**
3214
+ * Build an AUDIO-only one-sided transition (stutter / chopped / delay). When
3215
+ * provided, one-sided rows render an effect selector; absent (MIDI panels) →
3216
+ * one-sided rows stay plain fades. @since SDK 2.32.0
3217
+ */
3218
+ onCreateAudioTransition?: (selection: FadeSelection, direction: FadeDirection, effect: 'stutter' | 'chopped' | 'delay') => Promise<void>;
3219
+ /** Short family label for the heading, e.g. "Synths". */
3220
+ familyLabel?: string;
3221
+ /** data-testid prefix. */
3222
+ testIdPrefix?: string;
3223
+ }
3224
+ declare function TransitionDesigner({ host, fromSceneId, toSceneId, transitionSceneId, excludeSourceDbIds, onCreateCrossfade, onCreateFade, onCreateAudioTransition, familyLabel, testIdPrefix, }: TransitionDesignerProps): React$1.ReactElement;
3225
+
3226
+ /**
3227
+ * Transition Designer — pure helpers for the per-panel transition staging board.
3228
+ *
3229
+ * The designer is the multi-row, persistent successor to CrossfadeModal/FadeModal:
3230
+ * it lays out ONE panel-family's origin (scene A) and target (scene B) source
3231
+ * tracks as two index-aligned, drag-reorderable columns. Row i pairs the origin
3232
+ * slot at index i with the target slot at index i, and the pairing DERIVES the
3233
+ * transition type:
3234
+ * - both filled → crossfade (morph A→B)
3235
+ * - origin filled, target blank → fade out (the track leaves)
3236
+ * - target filled, origin blank → fade in (the track enters)
3237
+ * A slot may be a source-track dbId or `null` (a blank spacer). Blanks let the
3238
+ * user open a gap so a mid-list track becomes a fade instead of crossfading with
3239
+ * whatever happens to sit opposite it (the CSV-style layout).
3240
+ *
3241
+ * The "available pool" per column is the scene's family tracks MINUS the sources
3242
+ * already consumed by a committed crossfade/fade (excludeSourceDbIds). Creating a
3243
+ * row reuses the panel's existing crossfade/fade orchestration; deleting the
3244
+ * committed crossfade/fade on the deck returns its source to the pool.
3245
+ *
3246
+ * This module owns only the shape + the pure slot/row math so it can be unit
3247
+ * tested without a DOM and can't drift across the three panels. The component
3248
+ * (TransitionDesigner.tsx) owns the overlay, drag wiring, and persistence.
3249
+ *
3250
+ * @since SDK 2.29.0
3251
+ */
3252
+ /**
3253
+ * Persisted per-transition-scene draft: the two columns' slot orders. A slot is
3254
+ * a source-track dbId, or `null` for a blank spacer (an intentional gap). Stored
3255
+ * in the transition scene's plugin_data under {@link TRANSITION_DESIGNER_DRAFT_KEY};
3256
+ * because plugin_data is scoped by (plugin_id, sceneId), each panel family keeps
3257
+ * its own draft automatically.
3258
+ */
3259
+ interface TransitionDesignerDraft {
3260
+ /** Origin (scene A) column order — dbIds or `null` blanks. */
3261
+ originOrder: (string | null)[];
3262
+ /** Target (scene B) column order — dbIds or `null` blanks. */
3263
+ targetOrder: (string | null)[];
3264
+ /** Per one-sided-row audio effect, keyed by the source dbId. @since SDK 2.32.0 */
3265
+ rowEffects?: Record<string, AudioEffect>;
3266
+ }
3267
+ /** scene-data key (under the transition scene) holding the staged draft. */
3268
+ declare const TRANSITION_DESIGNER_DRAFT_KEY = "transitionDesigner:draft";
3269
+ /** The transition a single aligned row represents (derived from its two slots). */
3270
+ type TransitionRowType = 'crossfade' | 'fade-out' | 'fade-in';
3271
+ /**
3272
+ * Audio-only transition gesture for a ONE-SIDED (orphan) loop. `'fade'` is the
3273
+ * default level ramp (works for any family); `stutter`/`chopped`/`delay` are
3274
+ * audio panels only, surfaced via the row's effect selector when the panel
3275
+ * passes `onCreateAudioTransition`. @since SDK 2.32.0
3276
+ */
3277
+ type AudioEffect = 'fade' | 'stutter' | 'chopped' | 'delay';
3278
+ declare const AUDIO_EFFECTS: readonly AudioEffect[];
3279
+ declare const AUDIO_EFFECT_LABEL: Record<AudioEffect, string>;
3280
+ declare function asAudioEffect(v: unknown): AudioEffect | null;
3281
+ /** Derive a row's transition type from which slots are filled. `null` = empty row. */
3282
+ declare function rowType(hasOrigin: boolean, hasTarget: boolean): TransitionRowType | null;
3283
+ /** Narrow an unknown scene-data value to a TransitionDesignerDraft (defensive). */
3284
+ declare function asTransitionDesignerDraft(val: unknown): TransitionDesignerDraft | null;
3285
+ /**
3286
+ * Reconcile a saved slot order against the current pool of available source ids:
3287
+ * - keep saved ids still in the pool (in their saved position),
3288
+ * - keep `null` blanks,
3289
+ * - drop ids no longer in the pool (consumed by a created crossfade/fade, or the
3290
+ * source track was deleted) and any duplicates,
3291
+ * - append pool ids missing from the saved order (newly added tracks) at the end.
3292
+ *
3293
+ * Pure; exported for unit testing.
3294
+ */
3295
+ declare function reconcileSlots(saved: readonly (string | null)[] | undefined, poolIds: readonly string[]): (string | null)[];
3296
+ /** One assembled designer row: the two source dbIds (or `null`) + derived type. */
3297
+ interface DesignerRowSlots {
3298
+ originId: string | null;
3299
+ targetId: string | null;
3300
+ type: TransitionRowType | null;
3301
+ }
3302
+ /** Zip two slot columns into index-aligned rows with their derived type. */
3303
+ declare function buildRowSlots(originSlots: readonly (string | null)[], targetSlots: readonly (string | null)[]): DesignerRowSlots[];
3304
+ /**
3305
+ * Tidy the columns for persistence: drop rows where BOTH slots are blank (a
3306
+ * meaningless gap) and trim trailing blanks per column. Returns clean columns
3307
+ * suitable for {@link TransitionDesignerDraft}.
3308
+ */
3309
+ declare function normalizeSlots(originSlots: readonly (string | null)[], targetSlots: readonly (string | null)[]): TransitionDesignerDraft;
3310
+ /** Pad a column with trailing `null`s up to `n` (so both columns render aligned). */
3311
+ declare function padSlots(slots: readonly (string | null)[], n: number): (string | null)[];
3312
+ /** Pad both columns to equal length (= the longer column). */
3313
+ declare function padPair(originSlots: readonly (string | null)[], targetSlots: readonly (string | null)[]): [(string | null)[], (string | null)[]];
3314
+ /** Shallow element-wise equality for two slot columns. */
3315
+ declare function slotsEqual(a: readonly (string | null)[], b: readonly (string | null)[]): boolean;
3316
+ /**
3317
+ * Stable key identifying an in-flight create, derived from the row's SOURCE dbIds
3318
+ * (not its row index) — so reordering or inserting a gap mid-create still maps the
3319
+ * progress indicator to the right row, and concurrent creates never collide. dbIds
3320
+ * are UUIDs, so `|` is a safe origin/target separator. `null` for an empty row.
3321
+ *
3322
+ * @since SDK 2.30.0
3323
+ */
3324
+ declare function rowKey(row: DesignerRowSlots): string | null;
3325
+ /**
3326
+ * The set of source dbIds referenced by a collection of in-flight {@link rowKey}s —
3327
+ * used to lock those cells (no drag / gap edits) while their create runs.
3328
+ *
3329
+ * @since SDK 2.30.0
3330
+ */
3331
+ declare function dbIdsFromKeys(keys: Iterable<string>): Set<string>;
3101
3332
 
3102
3333
  /**
3103
3334
  * ConfirmDialog — styled in-app confirmation modal (SDK component).
@@ -3120,7 +3351,7 @@ interface ConfirmDialogProps {
3120
3351
  /** Bold heading line. */
3121
3352
  title: string;
3122
3353
  /** Body copy — a string or richer node. */
3123
- message: React.ReactNode;
3354
+ message: React$1.ReactNode;
3124
3355
  /** Confirm button label (default "Delete"). */
3125
3356
  confirmLabel?: string;
3126
3357
  /** Cancel button label (default "Cancel"). */
@@ -3134,7 +3365,7 @@ interface ConfirmDialogProps {
3134
3365
  /** data-testid prefix so each dialog is addressable in tests. */
3135
3366
  testIdPrefix?: string;
3136
3367
  }
3137
- declare function ConfirmDialog({ open, title, message, confirmLabel, cancelLabel, destructive, onConfirm, onCancel, testIdPrefix, }: ConfirmDialogProps): React.ReactElement | null;
3368
+ declare function ConfirmDialog({ open, title, message, confirmLabel, cancelLabel, destructive, onConfirm, onCancel, testIdPrefix, }: ConfirmDialogProps): React$1.ReactElement | null;
3138
3369
 
3139
3370
  /**
3140
3371
  * Modal — the SDK's one modal-stacking primitive (portal + z-tier + backdrop).
@@ -3161,7 +3392,7 @@ interface ModalProps {
3161
3392
  /** Close handler — fired on Escape and backdrop click. */
3162
3393
  onClose: () => void;
3163
3394
  /** The dialog box. Give it `onClick={e => e.stopPropagation()}`. */
3164
- children: React.ReactNode;
3395
+ children: React$1.ReactNode;
3165
3396
  /** data-testid prefix; the backdrop is `${testIdPrefix}-overlay`. */
3166
3397
  testIdPrefix?: string;
3167
3398
  /** Close when the backdrop is clicked (default true). */
@@ -3169,9 +3400,9 @@ interface ModalProps {
3169
3400
  /** Close on Escape (default true). */
3170
3401
  closeOnEscape?: boolean;
3171
3402
  /** Focused when the modal opens (e.g. a Cancel button) so a reflexive Enter is safe. */
3172
- initialFocusRef?: React.RefObject<HTMLElement>;
3403
+ initialFocusRef?: React$1.RefObject<HTMLElement>;
3173
3404
  }
3174
- declare function Modal({ open, onClose, children, testIdPrefix, closeOnBackdrop, closeOnEscape, initialFocusRef, }: ModalProps): React.ReactElement | null;
3405
+ declare function Modal({ open, onClose, children, testIdPrefix, closeOnBackdrop, closeOnEscape, initialFocusRef, }: ModalProps): React$1.ReactElement | null;
3175
3406
 
3176
3407
  /**
3177
3408
  * PianoRollEditor — a compact, DOM-based MIDI note editor for the track drawer.
@@ -3276,7 +3507,7 @@ interface PianoRollEditorProps {
3276
3507
  /** Test id for the outer container. Default "sdk-piano-roll". */
3277
3508
  testId?: string;
3278
3509
  }
3279
- declare function PianoRollEditor({ notes, onChange, bars, bpm, beatsPerBar, snap, snapOptions, onSnapChange, minPitch, maxPitch, autoFit, onAuditionNote, defaultVelocity, disabled, className, testId, }: PianoRollEditorProps): React.ReactElement;
3510
+ declare function PianoRollEditor({ notes, onChange, bars, bpm, beatsPerBar, snap, snapOptions, onSnapChange, minPitch, maxPitch, autoFit, onAuditionNote, defaultVelocity, disabled, className, testId, }: PianoRollEditorProps): React$1.ReactElement;
3280
3511
 
3281
3512
  /**
3282
3513
  * VolumeSlider Component
@@ -3295,7 +3526,7 @@ interface VolumeSliderProps {
3295
3526
  /** Additional CSS classes */
3296
3527
  className?: string;
3297
3528
  }
3298
- declare const VolumeSlider: React.FC<VolumeSliderProps>;
3529
+ declare const VolumeSlider: React$1.FC<VolumeSliderProps>;
3299
3530
 
3300
3531
  /**
3301
3532
  * PanSlider Component
@@ -3315,7 +3546,7 @@ interface PanSliderProps {
3315
3546
  /** Additional CSS classes */
3316
3547
  className?: string;
3317
3548
  }
3318
- declare const PanSlider: React.FC<PanSliderProps>;
3549
+ declare const PanSlider: React$1.FC<PanSliderProps>;
3319
3550
 
3320
3551
  /**
3321
3552
  * FxToggleBar Component
@@ -3334,7 +3565,7 @@ interface FxToggleBarProps {
3334
3565
  onDryWetChange: (trackId: string, category: FxCategory, value: number) => void;
3335
3566
  disabled?: boolean;
3336
3567
  }
3337
- declare const FxToggleBar: React.FC<FxToggleBarProps>;
3568
+ declare const FxToggleBar: React$1.FC<FxToggleBarProps>;
3338
3569
 
3339
3570
  /**
3340
3571
  * SorceryProgressBar Component
@@ -3389,7 +3620,7 @@ declare function calculateTimeBasedTarget(elapsedMs: number, estimatedDurationMs
3389
3620
  /**
3390
3621
  * SorceryProgressBar - A mystical progress bar for uncertain wait times
3391
3622
  */
3392
- declare function SorceryProgressBar({ isLoading, statusText, completeText, onComplete, heightClass, initialProgress, onProgressChange, estimatedDurationMs, }: SorceryProgressBarProps): React.ReactElement | null;
3623
+ declare function SorceryProgressBar({ isLoading, statusText, completeText, onComplete, heightClass, initialProgress, onProgressChange, estimatedDurationMs, }: SorceryProgressBarProps): React$1.ReactElement | null;
3393
3624
 
3394
3625
  /**
3395
3626
  * DownloadPackButton — versioned-pack download trigger (SDK component).
@@ -3416,7 +3647,7 @@ interface DownloadPackButtonProps {
3416
3647
  /** Called once after the install completes (status === 'complete'). */
3417
3648
  onDownloadComplete?: () => void;
3418
3649
  }
3419
- declare const DownloadPackButton: React.FC<DownloadPackButtonProps>;
3650
+ declare const DownloadPackButton: React$1.FC<DownloadPackButtonProps>;
3420
3651
 
3421
3652
  /**
3422
3653
  * SamplePackCTACard — empty-state card a generator panel renders when its
@@ -3443,7 +3674,7 @@ interface SamplePackCTACardProps {
3443
3674
  status: SamplePackCTACardStatus;
3444
3675
  onDownloadComplete?: () => void;
3445
3676
  }
3446
- declare const SamplePackCTACard: React.FC<SamplePackCTACardProps>;
3677
+ declare const SamplePackCTACard: React$1.FC<SamplePackCTACardProps>;
3447
3678
 
3448
3679
  /**
3449
3680
  * WaveformView — small canvas waveform for an audio file on disk.
@@ -3476,7 +3707,7 @@ interface WaveformViewProps {
3476
3707
  */
3477
3708
  targetSamples?: number;
3478
3709
  }
3479
- declare const WaveformView: React.FC<WaveformViewProps>;
3710
+ declare const WaveformView: React$1.FC<WaveformViewProps>;
3480
3711
 
3481
3712
  /**
3482
3713
  * Shared level-meter component.
@@ -3522,7 +3753,7 @@ interface LevelMeterProps {
3522
3753
  /** Inline test id — make multiple instances distinguishable. */
3523
3754
  'data-testid'?: string;
3524
3755
  }
3525
- declare const LevelMeter: React.FC<LevelMeterProps>;
3756
+ declare const LevelMeter: React$1.FC<LevelMeterProps>;
3526
3757
 
3527
3758
  /**
3528
3759
  * TrackMeterStrip — the thin per-track peak meter welded to the bottom of a
@@ -3545,7 +3776,7 @@ interface TrackMeterStripProps {
3545
3776
  /** Optional className for layout tweaks on the wrapper. */
3546
3777
  className?: string;
3547
3778
  }
3548
- declare const TrackMeterStrip: React.FC<TrackMeterStripProps>;
3779
+ declare const TrackMeterStrip: React$1.FC<TrackMeterStripProps>;
3549
3780
 
3550
3781
  /**
3551
3782
  * ScrollingWaveform — live waveform during recording (Phase 8.10).
@@ -3576,7 +3807,7 @@ interface ScrollingWaveformProps {
3576
3807
  /** Highlight color for the wave. */
3577
3808
  fillStyle?: string;
3578
3809
  }
3579
- declare const ScrollingWaveform: React.FC<ScrollingWaveformProps>;
3810
+ declare const ScrollingWaveform: React$1.FC<ScrollingWaveformProps>;
3580
3811
 
3581
3812
  /**
3582
3813
  * OffsetScrubber — manual sample-offset slider for Lyria-generated audio.
@@ -3616,7 +3847,7 @@ interface OffsetScrubberProps {
3616
3847
  /** Disable interaction (e.g., during generation / split). */
3617
3848
  disabled?: boolean;
3618
3849
  }
3619
- declare function OffsetScrubber({ cuePoints, offsetSamples, projectBpm, meter, onChange, disabled, }: OffsetScrubberProps): React.ReactElement;
3850
+ declare function OffsetScrubber({ cuePoints, offsetSamples, projectBpm, meter, onChange, disabled, }: OffsetScrubberProps): React$1.ReactElement;
3620
3851
 
3621
3852
  /**
3622
3853
  * Shared waveform peaks + canvas drawer.
@@ -3708,51 +3939,6 @@ interface SynthesizeCuePointsOptions {
3708
3939
  }
3709
3940
  declare function synthesizeCuePoints({ bpm, sampleRate, bars, meter, }: SynthesizeCuePointsOptions): PluginCuePoints;
3710
3941
 
3711
- /**
3712
- * useSceneState — Scene-keyed state hook for plugin developers.
3713
- *
3714
- * Works like `useState`, but maintains separate state per scene.
3715
- * When the user switches scenes, the previous scene's state is preserved
3716
- * and restored when they switch back.
3717
- *
3718
- * Returns `[value, setForCurrentScene, setForScene]`:
3719
- * - `value` — state for the currently active scene
3720
- * - `setForCurrentScene(v)` — updates state for whatever scene is active at call time
3721
- * - `setForScene(sceneId, v)` — updates state for a specific scene (for async callbacks)
3722
- *
3723
- * Both setters support the functional updater pattern: `prev => next`.
3724
- *
3725
- * **Important:** For object/array `initialValue`, hoist to a module-level constant
3726
- * to keep the setter callbacks referentially stable:
3727
- * ```ts
3728
- * const EMPTY: string[] = [];
3729
- * const [items, setItems, setItemsForScene] = useSceneState(activeSceneId, EMPTY);
3730
- * ```
3731
- */
3732
- type SetSceneState<T> = (value: T | ((prev: T) => T)) => void;
3733
- type SetSceneStateForScene<T> = (sceneId: string, value: T | ((prev: T) => T)) => void;
3734
- declare function useSceneState<T>(activeSceneId: string | null, initialValue: T): [T, SetSceneState<T>, SetSceneStateForScene<T>];
3735
-
3736
- /**
3737
- * useAnySolo — reactively reports whether ANY track in the project is soloed.
3738
- *
3739
- * Solo is cross-panel: when the user solos a track in ANY panel, the engine's
3740
- * effective-mute model silences every non-soloed track. A panel uses this flag
3741
- * to DIM its own non-soloed rows without lighting their Mute buttons:
3742
- *
3743
- * ```tsx
3744
- * const anySolo = useAnySolo(host);
3745
- * // ...
3746
- * <TrackRow soloedOut={anySolo && !track.runtimeState.solo} ... />
3747
- * ```
3748
- *
3749
- * Refreshes on mount and on every track-state change. `onTrackStateChange`
3750
- * fires for tracks in ALL panels (not just this plugin's), so a solo toggled in
3751
- * another panel updates this flag too.
3752
- */
3753
-
3754
- declare function useAnySolo(host: Pick<PluginHost, 'isAnySoloActive' | 'onTrackStateChange'>): boolean;
3755
-
3756
3942
  /**
3757
3943
  * useSoundHistory — generic, per-track "what sounds has this track had?" stack.
3758
3944
  *
@@ -3814,6 +4000,656 @@ interface UseSoundHistoryResult {
3814
4000
  }
3815
4001
  declare function useSoundHistory(applySound: (trackId: string, descriptor: unknown) => Promise<void>, opts?: UseSoundHistoryOptions): UseSoundHistoryResult;
3816
4002
 
4003
+ /**
4004
+ * Per-track state model shared by every generator panel built on the
4005
+ * panel-core. Verbatim generalization of the synth panel's SynthTrackState
4006
+ * (SynthGeneratorPanel.tsx:67–100) — field names, defaults, and semantics are
4007
+ * frozen by the Phase-0 behavior pin.
4008
+ *
4009
+ * @since SDK 2.35.0
4010
+ */
4011
+
4012
+ /** Internal track state combining handle + runtime state + prompt. */
4013
+ interface GeneratorTrackState {
4014
+ handle: PluginTrackHandle;
4015
+ prompt: string;
4016
+ role: string;
4017
+ runtimeState: PluginTrackRuntimeState;
4018
+ fxDetailState: TrackFxDetailState;
4019
+ drawerOpen: boolean;
4020
+ drawerTab: DrawerTab;
4021
+ editorStage: boolean;
4022
+ isGenerating: boolean;
4023
+ error: string | null;
4024
+ hasMidi: boolean;
4025
+ generationProgress: number;
4026
+ editNotes: PluginMidiNote[];
4027
+ editBars: number;
4028
+ editBpm: number;
4029
+ instrumentPluginId: string | null;
4030
+ instrumentName: string | null;
4031
+ instrumentMissing: boolean;
4032
+ /**
4033
+ * Per-track shuffle history: sound/preset names already handed back since
4034
+ * the track was created OR since the history was reset (which happens
4035
+ * automatically when the pool is exhausted — the family shuffle adapter
4036
+ * reports "exhausted" and the core wipes the history and retries). Cycle
4037
+ * pattern: cycle through everything before any repeat.
4038
+ */
4039
+ shuffleHistory: Set<string>;
4040
+ }
4041
+ /**
4042
+ * Fresh track state with the panel defaults (the add-track literal at
4043
+ * SynthGeneratorPanel.tsx:634–654). `overrides` lets loadTracks hydrate
4044
+ * prompt/role/runtime/fx/etc. from fetched state in one construction.
4045
+ */
4046
+ declare function newTrackState(handle: PluginTrackHandle, overrides?: Partial<Omit<GeneratorTrackState, 'handle'>>): GeneratorTrackState;
4047
+
4048
+ /**
4049
+ * Generic multi-track group seam — the crossfade-pair pattern, family- and
4050
+ * meta-parameterized.
4051
+ *
4052
+ * A "track group" is N normal tracks linked by a shared `groupId` persisted in
4053
+ * scene plugin_data under one key PER MEMBER: `track:<dbId>:<metaKey>`. Groups
4054
+ * are never stored as a group-level record; they are assembled by scanning the
4055
+ * member keys (single source of truth, survives member deletion gracefully).
4056
+ * The panel-core resolves parsed groups against live tracks each render:
4057
+ * complete groups render through a custom group row and their members are
4058
+ * excluded from the normal row list; incomplete groups degrade per the
4059
+ * extension's `isComplete` policy (crossfade: both members required; a bass
4060
+ * voice-group: anchor required).
4061
+ *
4062
+ * This is the seam the crossfade/fade metas established (crossfade-meta.ts);
4063
+ * new group families (e.g. the bass plugin's voice groups) ride it without
4064
+ * panel-core changes.
4065
+ *
4066
+ * @since SDK 2.35.0
4067
+ */
4068
+ /** One parsed member: the scene-data key's dbId + its narrowed meta value. */
4069
+ interface TrackGroupMember<M> {
4070
+ dbId: string;
4071
+ meta: M;
4072
+ }
4073
+ /** One parsed group (members in `sortMembers` order when provided). */
4074
+ interface TrackGroupMeta<M> {
4075
+ groupId: string;
4076
+ members: TrackGroupMember<M>[];
4077
+ }
4078
+ /** How to scan + narrow one group family out of scene plugin_data. */
4079
+ interface GroupParseSpec<M> {
4080
+ /** Scene-data key suffix: scans `track:<dbId>:<metaKey>`. */
4081
+ metaKey: string;
4082
+ /** Defensive narrow (the `asCrossfadeMeta` pattern) — return null to skip. */
4083
+ asMeta(val: unknown): M | null;
4084
+ /** Extract the shared group id from a member meta. */
4085
+ groupIdOf(meta: M): string;
4086
+ /** Stable member order (e.g. by voiceIndex). Omit = scene-data scan order. */
4087
+ sortMembers?(a: TrackGroupMember<M>, b: TrackGroupMember<M>): number;
4088
+ }
4089
+ /**
4090
+ * Scan all `track:<dbId>:<metaKey>` keys in a scene's plugin_data and assemble
4091
+ * groups. Pure — no I/O; caller passes the already-fetched scene data map.
4092
+ */
4093
+ declare function parseTrackGroups<M>(sceneData: Record<string, unknown>, spec: GroupParseSpec<M>): TrackGroupMeta<M>[];
4094
+ /** A group resolved against live tracks (only members whose track exists). */
4095
+ interface ResolvedTrackGroup<M, T> {
4096
+ groupId: string;
4097
+ members: Array<{
4098
+ dbId: string;
4099
+ meta: M;
4100
+ track: T;
4101
+ }>;
4102
+ }
4103
+ interface ResolveGroupsOptions<M, T> {
4104
+ /**
4105
+ * Group completeness policy. A group failing this renders as loose normal
4106
+ * rows instead (its members are NOT excluded). Default: every PARSED member
4107
+ * resolved a live track — the crossfade rule (partner deleted ⇒ degrade).
4108
+ */
4109
+ isComplete?(group: ResolvedTrackGroup<M, T>, parsed: TrackGroupMeta<M>): boolean;
4110
+ }
4111
+ interface ResolvedGroupsResult<M, T> {
4112
+ /** Complete groups, ready for the group row renderer. */
4113
+ resolved: ResolvedTrackGroup<M, T>[];
4114
+ /** dbIds of members of COMPLETE groups — exclude these from normal rows. */
4115
+ memberDbIds: Set<string>;
4116
+ /**
4117
+ * dbIds whose member key exists but whose track is gone (deleted
4118
+ * out-of-band) — candidates for lazy scene-data cleanup.
4119
+ */
4120
+ staleMemberDbIds: string[];
4121
+ }
4122
+ /**
4123
+ * Resolve parsed groups against live track state. Pure; call from a useMemo
4124
+ * keyed on [tracks, parsedGroups] (fresh array identities per call are
4125
+ * expected — do NOT key effects on the arrays without a string-key guard,
4126
+ * see the drift-resync `lastResyncKeyRef` pattern).
4127
+ */
4128
+ declare function resolveTrackGroups<M, T>(parsedGroups: TrackGroupMeta<M>[], tracks: readonly T[], getDbId: (track: T) => string, opts?: ResolveGroupsOptions<M, T>): ResolvedGroupsResult<M, T>;
4129
+
4130
+ /**
4131
+ * Small pure helpers shared by every generator panel — moved verbatim out of
4132
+ * the three panel monoliths (synth/drum/instrument each carried a copy of
4133
+ * pluginFxToToggleFx and an LLM note-response parser).
4134
+ *
4135
+ * @since SDK 2.35.0
4136
+ */
4137
+
4138
+ /**
4139
+ * Build a scene plugin_data key for a track-scoped value. Scene-data keys are
4140
+ * ALWAYS constructed from the stable DB UUID (`handle.dbId`) — never the
4141
+ * engine id, which changes on project reload. This is the ONLY key builder
4142
+ * panels and generation strategies should use.
4143
+ */
4144
+ declare function trackDataKey(dbId: string, suffix: string): string;
4145
+ /** Convert SDK PluginTrackFxDetailState to the FxToggleBar's expected TrackFxDetailState. */
4146
+ declare function pluginFxToToggleFx(sdkState: PluginTrackFxDetailState): TrackFxDetailState;
4147
+ /** Shape of the parsed flat LLM JSON note response. */
4148
+ interface LLMNoteResponse {
4149
+ notes: PluginMidiNote[];
4150
+ role?: string;
4151
+ }
4152
+ /**
4153
+ * Parse the LLM JSON response and extract valid MIDI notes (flat
4154
+ * `{notes:[...], role?}` schema). Handles markdown code fences; silently
4155
+ * filters invalid notes; returns null when nothing parses.
4156
+ */
4157
+ declare function parseLLMNoteResponse(content: string): LLMNoteResponse | null;
4158
+
4159
+ /**
4160
+ * GeneratorPanelAdapter — the family-specific contract a generator panel
4161
+ * supplies to the shared panel-core (useGeneratorPanelCore + GeneratorPanelShell).
4162
+ *
4163
+ * The core owns everything the three historical panel monoliths duplicated:
4164
+ * track load/reconcile, event subscriptions, prompt persistence, mixer/FX ops,
4165
+ * drawer + piano-roll wiring, shuffle cycling, transition crossfade/fade
4166
+ * machinery, and the render skeleton. The adapter supplies what genuinely
4167
+ * differs per family: sound serialization, the 🎲 resolver, the generation
4168
+ * pipeline body, create-track options, prompts/parsers, identity strings,
4169
+ * feature flags, and (optionally) custom multi-track group rendering.
4170
+ *
4171
+ * Adapter instances MUST be referentially stable across renders — build them
4172
+ * in a `useMemo(() => createXAdapter(host), [host])`. An unstable adapter
4173
+ * re-creates the core's loadTracks callback every render (the historical
4174
+ * useSoundHistory render-loop failure mode).
4175
+ *
4176
+ * @since SDK 2.35.0
4177
+ */
4178
+
4179
+ /**
4180
+ * Family identity strings + numeric knobs. All panel test-ids derive from
4181
+ * `familyKey` (`add-<key>-track-button`, `<key>-section`, `<key>-view-toggle`,
4182
+ * `no-scene-placeholder-<key>`, `no-contract-placeholder-<key>`,
4183
+ * `<key>-import`, `<key>-sound-import`, `<key>-transition-designer`) — the
4184
+ * synth panel's historical ids are exactly this derivation with key 'synth'.
4185
+ */
4186
+ interface PanelIdentity {
4187
+ /** Test-id + focus-selector stem, e.g. 'synth'. */
4188
+ familyKey: string;
4189
+ /** Human label for the TransitionDesigner header, e.g. 'Synths'. */
4190
+ familyLabel: string;
4191
+ /** New-track name prefix, e.g. 'synth' → `synth-<ts>`. */
4192
+ trackNamePrefix: string;
4193
+ /** Console log tag, e.g. 'SynthGeneratorPanel'. */
4194
+ logTag: string;
4195
+ /** Normal row accent, e.g. '#A78BFA'. */
4196
+ accentColor: string;
4197
+ /** Crossfade/fade row accent, e.g. '#9333EA'. */
4198
+ transitionAccentColor: string;
4199
+ /** Bulk placeholder left-border accent, e.g. '#3B82F6'. */
4200
+ placeholderAccentColor: string;
4201
+ /** Per-plugin per-scene track budget (host enforces 16 too). */
4202
+ maxTracks: number;
4203
+ /** Progress-bar pacing for one generation. */
4204
+ estimatedGenerationMs: number;
4205
+ /** Header button label; default 'Add Track'. */
4206
+ addTrackLabel?: string;
4207
+ /** Header button label; default 'Import Track'. */
4208
+ importTrackLabel?: string;
4209
+ /** Export dialog default filename; default 'midi-tracks'. */
4210
+ exportDefaultName?: string;
4211
+ /**
4212
+ * Plugin id treated as the family's built-in default instrument in the Pick
4213
+ * tab (select it ⇒ close drawer instead of entering the editor stage).
4214
+ * Default 'Surge XT'.
4215
+ */
4216
+ defaultInstrumentPluginId?: string;
4217
+ }
4218
+ /** Which core surfaces this family mounts. */
4219
+ interface PanelFeatureFlags {
4220
+ /** Pick tab + instrument descriptors + editor stage (synth: true). */
4221
+ instrumentPicker: boolean;
4222
+ /** COMPOSING bar + bulk placeholder hybrid phase (synth: true). */
4223
+ bulkComposePlaceholders: boolean;
4224
+ /** "Export Tracks" ZIP button (synth: true). */
4225
+ exportMidi: boolean;
4226
+ /** Transition scene designer + crossfade/fade rows (synth: true). */
4227
+ transitionDesigner: boolean;
4228
+ /** ImportTrackModal + port-track flow + sound-import drawer action (synth: true). */
4229
+ importTracks: boolean;
4230
+ }
4231
+ /** How this family captures / applies / copies a track's SOUND. */
4232
+ interface PanelSoundAdapter {
4233
+ /**
4234
+ * Re-apply an opaque sound-history descriptor (useSoundHistory's applySound).
4235
+ * Synth: `{state, stateType}` through set(Raw)PluginState's dual path.
4236
+ */
4237
+ applySound(trackId: string, descriptor: unknown): Promise<void>;
4238
+ /**
4239
+ * Snapshot the track's current sound as a history descriptor, or null when
4240
+ * the track has no instrument. The CORE records it into soundHistory.
4241
+ */
4242
+ captureSoundDescriptor(trackId: string): Promise<{
4243
+ descriptor: unknown;
4244
+ } | null>;
4245
+ /**
4246
+ * Apply a host.getTrackSound snapshot to a track AND persist it as the
4247
+ * track's durable identity (persistTrackPresetState or family equivalent —
4248
+ * REQUIRED or the transition drift-resync never converges). Returns the
4249
+ * applied label. Used by crossfade/fade copy + drift-resync.
4250
+ */
4251
+ copySnapshot(trackId: string, snap: TrackSoundSnapshot): Promise<string>;
4252
+ /**
4253
+ * Convert a host.getTrackSound snapshot into this family's sound-history
4254
+ * descriptor (synth: `{state, stateType}`). Used by the drawer's sound
4255
+ * import so the imported sound lands in history with the right shape.
4256
+ */
4257
+ descriptorFromSnapshot(snap: TrackSoundSnapshot): unknown;
4258
+ /** Snapshot kind accepted by the drawer's "Import <noun>" (synth: 'preset'). */
4259
+ acceptedSnapshotKind: 'preset' | 'sample' | 'instrument';
4260
+ /** Sound-history cap (synth: 12 — Surge blobs are large). */
4261
+ historyMax: number;
4262
+ /** Drawer action label, e.g. 'Import Preset'. */
4263
+ importSoundLabel: string;
4264
+ /** Noun for import toasts: 'No <noun> to import' / '<Noun> imported'. */
4265
+ importNoun: string;
4266
+ /** History label for the lazily-seeded pre-shuffle sound, e.g. 'Previous preset'. */
4267
+ previousSoundLabel: string;
4268
+ }
4269
+ /** The 🎲: pick + apply one new sound, honoring the exclusion cycle. */
4270
+ interface PanelShuffleAdapter {
4271
+ /**
4272
+ * Pick + apply a sound not in `excludeNames`. Throw when the pool is
4273
+ * exhausted (`isExhaustedError` must recognize it) — the core wipes the
4274
+ * track's history and retries once with an empty exclusion list.
4275
+ */
4276
+ shuffle(track: GeneratorTrackState, excludeNames: string[]): Promise<{
4277
+ appliedName: string;
4278
+ }>;
4279
+ /** Distinguish "pool exhausted" (expected; cycle resets) from real failures. */
4280
+ isExhaustedError(err: unknown): boolean;
4281
+ }
4282
+ /**
4283
+ * Capabilities the core hands to the generation strategy (and group
4284
+ * renderers). Built fresh per call — do not cache across renders.
4285
+ */
4286
+ interface GenerationServices {
4287
+ host: PluginHost;
4288
+ activeSceneId: string | null;
4289
+ /** Live track list snapshot at call time. */
4290
+ tracks: GeneratorTrackState[];
4291
+ /** Patch one track's state (object merge or functional update). */
4292
+ updateTrack(trackId: string, patch: Partial<GeneratorTrackState> | ((t: GeneratorTrackState) => GeneratorTrackState)): void;
4293
+ /** Escape hatch for multi-track updates (reconcile flows). */
4294
+ setTracks: React.Dispatch<React.SetStateAction<GeneratorTrackState[]>>;
4295
+ reloadTracks(incremental?: boolean): Promise<void>;
4296
+ soundHistory: UseSoundHistoryResult;
4297
+ /** Engine id → stable DB UUID (falls back to the input when unknown). */
4298
+ engineToDbId(trackId: string): string;
4299
+ /** The ONLY scene-data key builder (always dbId-based). */
4300
+ trackDataKey(dbId: string, suffix: string): string;
4301
+ /** Latch a track as piano-roll-loaded (post-generation seeding). */
4302
+ markEditLoaded(trackId: string): void;
4303
+ /** Create a family track (adapter options + `<prefix>-<ts><suffix>` name). */
4304
+ createFamilyTrack(nameSuffix?: string): Promise<PluginTrackHandle>;
4305
+ /** Resolved groups for a registered group extension. */
4306
+ resolvedGroups<M>(metaKey: string): ResolvedTrackGroup<M, GeneratorTrackState>[];
4307
+ }
4308
+ /**
4309
+ * One prompt-driven generation turn. The core owns the wrapper: prompt/auth
4310
+ * gates, `isGenerating: true`, and the catch (error patch + 'Generation
4311
+ * failed' toast). The strategy owns the body — synth: LLM → clip on THIS
4312
+ * track → mute → role persist → shufflePreset → success patch; bass: LLM line
4313
+ * → validate/split → reconcile member tracks → per-voice clips + presets →
4314
+ * metas → reload.
4315
+ */
4316
+ interface PanelGenerationStrategy {
4317
+ generate(track: GeneratorTrackState, services: GenerationServices): Promise<void>;
4318
+ }
4319
+ /** Core per-track handlers, same instances the normal rows use. */
4320
+ interface CoreTrackHandlers {
4321
+ promptChange(trackId: string, prompt: string): void;
4322
+ generate(trackId: string): void;
4323
+ shuffle(trackId: string): void;
4324
+ copy(trackId: string): void;
4325
+ delete(trackId: string): void;
4326
+ muteToggle(trackId: string): void;
4327
+ soloToggle(trackId: string): void;
4328
+ volumeChange(trackId: string, volume: number): void;
4329
+ panChange(trackId: string, pan: number): void;
4330
+ tabChange(trackId: string, tab: DrawerTab): void;
4331
+ toggleDrawer(trackId: string): void;
4332
+ toggleFxDrawer(trackId: string): void;
4333
+ notesChange(trackId: string, notes: PluginMidiNote[]): void;
4334
+ progressChange(trackId: string, pct: number): void;
4335
+ }
4336
+ /** Render-time context handed to a group extension's renderGroup. */
4337
+ interface GroupRenderContext {
4338
+ services: GenerationServices;
4339
+ anySolo: boolean;
4340
+ supportsMeters: boolean;
4341
+ levels?: TrackLevelsHandle;
4342
+ handlers: CoreTrackHandlers;
4343
+ /**
4344
+ * Build the shell's default TrackRow for a member with per-row overrides —
4345
+ * group renderers stack these instead of reimplementing the ~50-prop plumbing.
4346
+ */
4347
+ renderDefaultTrackRow(track: GeneratorTrackState, overrides?: Partial<SDKTrackRowProps>, drag?: TrackRowDragProps): ReactNode;
4348
+ /** Optimistic group mute (crossfade group-control pattern). */
4349
+ setGroupMute(trackIds: string[], muted: boolean): void;
4350
+ /** Optimistic group solo. */
4351
+ setGroupSolo(trackIds: string[], solo: boolean): void;
4352
+ /**
4353
+ * Delete all member tracks + their `track:<dbId>:<suffix>` scene-data keys
4354
+ * (per cleanupKeySuffixes) + prune local state. Best-effort per member.
4355
+ */
4356
+ deleteGroup(members: Array<{
4357
+ engineId: string;
4358
+ dbId: string;
4359
+ }>, cleanupKeySuffixes: string[]): Promise<void>;
4360
+ }
4361
+ /**
4362
+ * A family-registered multi-track group row (the crossfade seam,
4363
+ * parameterized). Members of complete groups render through `renderGroup`
4364
+ * and are excluded from the normal row list; incomplete groups degrade to
4365
+ * normal rows per `isComplete`.
4366
+ */
4367
+ interface PanelGroupExtension<M = unknown> extends GroupParseSpec<M> {
4368
+ /**
4369
+ * Completeness policy (default: every parsed member's track is live).
4370
+ * Bass voice-groups: the anchor member (voiceIndex 0) must be live.
4371
+ */
4372
+ isComplete?(group: ResolvedTrackGroup<M, GeneratorTrackState>, parsed: TrackGroupMeta<M>): boolean;
4373
+ renderGroup(group: ResolvedTrackGroup<M, GeneratorTrackState>, ctx: GroupRenderContext): ReactNode;
4374
+ }
4375
+ interface GeneratorPanelAdapter<M = unknown> {
4376
+ identity: PanelIdentity;
4377
+ features: PanelFeatureFlags;
4378
+ /** Options for host.createTrack (name is core-built). Synth: `{loadSynth:true, synthName:'Surge XT'}`. */
4379
+ createTrackOptions(): Omit<CreateTrackOptions, 'name'>;
4380
+ /**
4381
+ * Port-flow sound step after the MIDI copy (cross-panel Import Track).
4382
+ * Synth: `host.shufflePreset(handle.id)` non-fatal.
4383
+ */
4384
+ applyPortedTrackSound(handle: PluginTrackHandle, role?: string): Promise<void>;
4385
+ /** System prompt for the family's LLM calls (incl. core-owned crossfade/fade generation). */
4386
+ buildSystemPrompt(validRoles: readonly string[]): string;
4387
+ /** Parse the family's LLM note responses (crossfade/fade flows). */
4388
+ parseNotesResponse(content: string): LLMNoteResponse | null;
4389
+ sound: PanelSoundAdapter;
4390
+ shuffle: PanelShuffleAdapter;
4391
+ generation: PanelGenerationStrategy;
4392
+ /** Custom multi-track group rows (bass voice groups). */
4393
+ groupExtensions?: PanelGroupExtension<M>[];
4394
+ /** Patch the default TrackRow props per row (drum's sampleName fallback). */
4395
+ mapTrackRowProps?(track: GeneratorTrackState, props: SDKTrackRowProps): SDKTrackRowProps;
4396
+ }
4397
+ /** Panel-local render extension points around the shell's row list. */
4398
+ interface GeneratorPanelSlots {
4399
+ beforeRows?: ReactNode;
4400
+ afterRows?: ReactNode;
4401
+ /** Extra modals (rendered in the normal phase only). */
4402
+ modals?: ReactNode;
4403
+ }
4404
+
4405
+ /**
4406
+ * Transition-scene machinery for panel-core panels — crossfade pair + fade
4407
+ * creation, group controls, sliders, drift re-sync, and fade curve re-apply.
4408
+ * Moved VERBATIM from the synth panel (SynthGeneratorPanel.tsx 715–987,
4409
+ * 1148–1235, 1787–1847) with the family-specific pieces routed through the
4410
+ * GeneratorPanelAdapter (sound copy/persist, system prompt, note parsing,
4411
+ * track naming). Semantics are frozen by the Phase-0 behavior pin.
4412
+ *
4413
+ * Deliberately NOT rewritten onto the generic group seam (group-meta.ts) —
4414
+ * that seam is additive for new families (bass voice groups); migrating
4415
+ * crossfades onto it is a contained follow-up.
4416
+ *
4417
+ * @since SDK 2.35.0
4418
+ */
4419
+
4420
+ /** A crossfade pair resolved against live track state (both members present). */
4421
+ interface ResolvedCrossfadePair extends CrossfadePairMeta {
4422
+ origin: GeneratorTrackState;
4423
+ target: GeneratorTrackState;
4424
+ }
4425
+ /** A fade (transition orphan) resolved against live track state. */
4426
+ interface ResolvedFade extends FadeEntry {
4427
+ track: GeneratorTrackState;
4428
+ }
4429
+ interface UseTransitionOpsInputs {
4430
+ host: PluginHost;
4431
+ adapter: GeneratorPanelAdapter;
4432
+ activeSceneId: string | null;
4433
+ isConnected: boolean;
4434
+ isAuthenticated: boolean;
4435
+ sceneContext: PluginSceneContext | null | undefined;
4436
+ tracks: GeneratorTrackState[];
4437
+ setTracks: React.Dispatch<React.SetStateAction<GeneratorTrackState[]>>;
4438
+ loadTracks(incremental?: boolean): Promise<void>;
4439
+ setCrossfadePairsMeta: React.Dispatch<React.SetStateAction<CrossfadePairMeta[]>>;
4440
+ setFadesMeta: React.Dispatch<React.SetStateAction<FadeEntry[]>>;
4441
+ resolvedCrossfadePairs: ResolvedCrossfadePair[];
4442
+ resolvedFades: ResolvedFade[];
4443
+ }
4444
+ interface TransitionOps {
4445
+ isCreatingCrossfade: boolean;
4446
+ isCreatingFade: boolean;
4447
+ handleCreateCrossfade(origin: CrossfadeSelection, target: CrossfadeSelection): Promise<void>;
4448
+ handleCreateFade(selection: FadeSelection, direction: FadeDirection, gesture: FadeGesture): Promise<void>;
4449
+ handleCrossfadeMute(pair: ResolvedCrossfadePair): void;
4450
+ handleCrossfadeSolo(pair: ResolvedCrossfadePair): void;
4451
+ handleCrossfadeDelete(pair: ResolvedCrossfadePair): Promise<void>;
4452
+ handleCrossfadeSlider(pair: ResolvedCrossfadePair, pos: number): void;
4453
+ handleFadeDelete(fade: ResolvedFade): Promise<void>;
4454
+ handleFadeSlider(fade: ResolvedFade, pos: number): void;
4455
+ }
4456
+ declare function useTransitionOps({ host, adapter, activeSceneId, isConnected, isAuthenticated, sceneContext, tracks, setTracks, loadTracks, setCrossfadePairsMeta, setFadesMeta, resolvedCrossfadePairs, resolvedFades, }: UseTransitionOpsInputs): TransitionOps;
4457
+
4458
+ /**
4459
+ * useGeneratorPanelCore — the shared state/effects/handlers engine behind
4460
+ * generator panels (synth today; bass next; drum/instrument candidates).
4461
+ *
4462
+ * Verbatim extraction of the synth panel monolith's family-agnostic ~85%
4463
+ * (SynthGeneratorPanel.tsx), parameterized by a GeneratorPanelAdapter. Every
4464
+ * timing (500ms prompt debounce, 500ms agent-mutation coalesce, 300ms notes
4465
+ * save, 350ms add-focus), every scene-data key (`track:<dbId>:…`), every
4466
+ * toast string, and every host-call sequence is frozen by the Phase-0
4467
+ * behavior pin (sas-app/src/__tests__/synth-panel-behavior.test.tsx).
4468
+ *
4469
+ * The returned `core` object is consumed by GeneratorPanelShell (render) and
4470
+ * closed over by family adapters (generation strategies, group renderers).
4471
+ *
4472
+ * @since SDK 2.35.0
4473
+ */
4474
+
4475
+ interface UseGeneratorPanelCoreOptions {
4476
+ /** The panel's PluginUIProps, passed through whole. */
4477
+ ui: PluginUIProps;
4478
+ /** Family adapter — MUST be referentially stable (useMemo on [host]). */
4479
+ adapter: GeneratorPanelAdapter;
4480
+ }
4481
+ /** Everything GeneratorPanelShell + family extensions consume. */
4482
+ interface GeneratorPanelCore {
4483
+ ui: PluginUIProps;
4484
+ adapter: GeneratorPanelAdapter;
4485
+ tracks: GeneratorTrackState[];
4486
+ setTracks: React$1.Dispatch<React$1.SetStateAction<GeneratorTrackState[]>>;
4487
+ isLoadingTracks: boolean;
4488
+ loadTracks(incremental?: boolean): Promise<void>;
4489
+ engineToDbId(trackId: string): string;
4490
+ supportsMeters: boolean;
4491
+ trackLevels: TrackLevelsHandle;
4492
+ anySolo: boolean;
4493
+ reorder: UseTrackReorderResult;
4494
+ soundHistory: ReturnType<typeof useSoundHistory>;
4495
+ isComposing: boolean;
4496
+ placeholders: BulkAddPlaceholderTrack[];
4497
+ isAddingTrack: boolean;
4498
+ isExportingMidi: boolean;
4499
+ designerView: boolean;
4500
+ canCrossfade: boolean;
4501
+ needsContract: boolean;
4502
+ xfFromId: string | null;
4503
+ xfToId: string | null;
4504
+ importOpen: boolean;
4505
+ setImportOpen(open: boolean): void;
4506
+ soundImportTarget: GeneratorTrackState | null;
4507
+ setSoundImportTarget(t: GeneratorTrackState | null): void;
4508
+ handleSoundImportPick(sel: {
4509
+ sourceTrackDbId: string;
4510
+ trackName: string;
4511
+ sceneName: string;
4512
+ }): Promise<void>;
4513
+ handlePortTrack(sel: {
4514
+ sourceTrackDbId: string;
4515
+ trackName: string;
4516
+ role?: string;
4517
+ }): Promise<void>;
4518
+ transition: TransitionOps;
4519
+ crossfadePairsMeta: CrossfadePairMeta[];
4520
+ fadesMeta: FadeEntry[];
4521
+ resolvedCrossfadePairs: ResolvedCrossfadePair[];
4522
+ crossfadeMemberDbIds: Set<string>;
4523
+ resolvedFades: ResolvedFade[];
4524
+ fadeMemberDbIds: Set<string>;
4525
+ resolvedGenericGroups: Record<string, ResolvedGroupsResult<unknown, GeneratorTrackState>>;
4526
+ genericGroupMemberDbIds: Set<string>;
4527
+ availableInstruments: InstrumentDescriptor[];
4528
+ instrumentsLoading: boolean;
4529
+ handlers: CoreTrackHandlers;
4530
+ handleGenerate(trackId: string): Promise<void>;
4531
+ handleShuffle(trackId: string): Promise<void>;
4532
+ handleAddTrack(): Promise<void>;
4533
+ handleDeleteTrack(trackId: string): Promise<void>;
4534
+ handleExportMidi(): Promise<void>;
4535
+ handlePromptChange(trackId: string, prompt: string): void;
4536
+ handleMuteToggle(trackId: string): void;
4537
+ handleSoloToggle(trackId: string): void;
4538
+ handleVolumeChange(trackId: string, volume: number): void;
4539
+ handlePanChange(trackId: string, pan: number): void;
4540
+ handleTabChange(trackId: string, tab: DrawerTab): void;
4541
+ handleToggleDrawer(trackId: string): void;
4542
+ toggleFxDrawer(trackId: string): void;
4543
+ handleNotesChange(trackId: string, notes: PluginMidiNote[]): void;
4544
+ handleProgressChange(trackId: string, pct: number): void;
4545
+ handleCopy(trackId: string): Promise<void>;
4546
+ handleFxToggle(trackId: string, category: FxCategory, enabled: boolean): void;
4547
+ handleFxPresetChange(trackId: string, category: FxCategory, presetIndex: number): void;
4548
+ handleFxDryWetChange(trackId: string, category: FxCategory, value: number): void;
4549
+ handleInstrumentSelect(trackId: string, pluginId: string): Promise<void>;
4550
+ handleShowEditor(trackId: string): Promise<void>;
4551
+ handleBackToInstruments(trackId: string): void;
4552
+ handleRefreshInstruments(): void;
4553
+ onAuditionNote(trackId: string, pitch: number, velocity: number, ms: number): void;
4554
+ makeServices(): GenerationServices;
4555
+ setGroupMute(trackIds: string[], muted: boolean): void;
4556
+ setGroupSolo(trackIds: string[], solo: boolean): void;
4557
+ deleteGroup(members: Array<{
4558
+ engineId: string;
4559
+ dbId: string;
4560
+ }>, cleanupKeySuffixes: string[]): Promise<void>;
4561
+ }
4562
+ declare function useGeneratorPanelCore({ ui, adapter, }: UseGeneratorPanelCoreOptions): GeneratorPanelCore;
4563
+
4564
+ /**
4565
+ * GeneratorPanelShell — the shared render skeleton for panel-core panels.
4566
+ *
4567
+ * Verbatim extraction of the synth panel's render phases
4568
+ * (SynthGeneratorPanel.tsx:1849–2195): no-scene gate → no-contract gate →
4569
+ * COMPOSING → placeholder hybrid → normal (modals, mounted-but-hidden
4570
+ * TransitionDesigner, crossfade/fade rows, generic group rows, normal rows,
4571
+ * export button). The ~50-prop TrackRow plumbing that the monolith duplicated
4572
+ * across the hybrid + normal phases lives here ONCE (`buildRowProps`), pinned
4573
+ * by the Phase-0 props snapshot.
4574
+ *
4575
+ * @since SDK 2.35.0
4576
+ */
4577
+
4578
+ interface GeneratorPanelShellProps {
4579
+ core: GeneratorPanelCore;
4580
+ slots?: GeneratorPanelSlots;
4581
+ }
4582
+ declare function GeneratorPanelShell({ core, slots }: GeneratorPanelShellProps): React$1.ReactElement;
4583
+
4584
+ /**
4585
+ * Surge XT sound adapter — the PanelSoundAdapter shared by every family whose
4586
+ * tracks host Surge XT (or a user-picked third-party VST3) as the instrument:
4587
+ * synth and bass today.
4588
+ *
4589
+ * A "sound" is the INSTRUMENT plugin's state: default Surge XT round-trips
4590
+ * through the Tracktion ValueTree (get/setPluginState); third-party
4591
+ * instruments (u-he Diva, Serum, …) need their RAW VST3 state
4592
+ * (get/setRawPluginState), which the ValueTree wrapper does not faithfully
4593
+ * preserve. The instrument is the first non-utility plugin on the track.
4594
+ * Matching only 'Surge' silently broke history for custom-instrument tracks
4595
+ * pre-split — hence the dual path.
4596
+ *
4597
+ * @since SDK 2.35.0
4598
+ */
4599
+
4600
+ interface SurgeSoundAdapterOverrides {
4601
+ /** Sound-history cap (default 12 — Surge state blobs are large). */
4602
+ historyMax?: number;
4603
+ /** Drawer action label (default 'Import Preset'). */
4604
+ importSoundLabel?: string;
4605
+ }
4606
+ declare function createSurgeSoundAdapter(host: PluginHost, overrides?: SurgeSoundAdapterOverrides): PanelSoundAdapter;
4607
+
4608
+ /**
4609
+ * useSceneState — Scene-keyed state hook for plugin developers.
4610
+ *
4611
+ * Works like `useState`, but maintains separate state per scene.
4612
+ * When the user switches scenes, the previous scene's state is preserved
4613
+ * and restored when they switch back.
4614
+ *
4615
+ * Returns `[value, setForCurrentScene, setForScene]`:
4616
+ * - `value` — state for the currently active scene
4617
+ * - `setForCurrentScene(v)` — updates state for whatever scene is active at call time
4618
+ * - `setForScene(sceneId, v)` — updates state for a specific scene (for async callbacks)
4619
+ *
4620
+ * Both setters support the functional updater pattern: `prev => next`.
4621
+ *
4622
+ * **Important:** For object/array `initialValue`, hoist to a module-level constant
4623
+ * to keep the setter callbacks referentially stable:
4624
+ * ```ts
4625
+ * const EMPTY: string[] = [];
4626
+ * const [items, setItems, setItemsForScene] = useSceneState(activeSceneId, EMPTY);
4627
+ * ```
4628
+ */
4629
+ type SetSceneState<T> = (value: T | ((prev: T) => T)) => void;
4630
+ type SetSceneStateForScene<T> = (sceneId: string, value: T | ((prev: T) => T)) => void;
4631
+ declare function useSceneState<T>(activeSceneId: string | null, initialValue: T): [T, SetSceneState<T>, SetSceneStateForScene<T>];
4632
+
4633
+ /**
4634
+ * useAnySolo — reactively reports whether ANY track in the project is soloed.
4635
+ *
4636
+ * Solo is cross-panel: when the user solos a track in ANY panel, the engine's
4637
+ * effective-mute model silences every non-soloed track. A panel uses this flag
4638
+ * to DIM its own non-soloed rows without lighting their Mute buttons:
4639
+ *
4640
+ * ```tsx
4641
+ * const anySolo = useAnySolo(host);
4642
+ * // ...
4643
+ * <TrackRow soloedOut={anySolo && !track.runtimeState.solo} ... />
4644
+ * ```
4645
+ *
4646
+ * Refreshes on mount and on every track-state change. `onTrackStateChange`
4647
+ * fires for tracks in ALL panels (not just this plugin's), so a solo toggled in
4648
+ * another panel updates this flag too.
4649
+ */
4650
+
4651
+ declare function useAnySolo(host: Pick<PluginHost, 'isAnySoloActive' | 'onTrackStateChange'>): boolean;
4652
+
3817
4653
  /**
3818
4654
  * Plugin SDK Version
3819
4655
  *
@@ -3822,7 +4658,7 @@ declare function useSoundHistory(applySound: (trackId: string, descriptor: unkno
3822
4658
  * Registry checks semver.gte(PLUGIN_SDK_VERSION, manifest.minHostVersion)
3823
4659
  * during activation and marks incompatible plugins accordingly.
3824
4660
  */
3825
- declare const PLUGIN_SDK_VERSION = "2.28.0";
4661
+ declare const PLUGIN_SDK_VERSION = "2.35.0";
3826
4662
 
3827
4663
  /**
3828
4664
  * FX Preset Definitions
@@ -3970,4 +4806,4 @@ interface PickTopKOptions {
3970
4806
  */
3971
4807
  declare function pickTopKWeighted<T>(scored: ReadonlyArray<ScoredCandidate<T>>, options?: PickTopKOptions): T | null;
3972
4808
 
3973
- export { type AudioInputDevice, type BulkAddPlaceholderTrack, type ComposeProgressEvent, type ComposeProgressListener, type ComposeSceneOptions, type ComposeSceneResult, ConfirmDialog, type ConfirmDialogProps, type CreateTrackOptions, type CrossfadeInpaintInput, type CrossfadeLayer, type CrossfadeMeta, CrossfadeModal, type CrossfadeModalProps, type CrossfadePairMeta, type CrossfadeSelection, type CrossfadeSlot, CrossfadeTrackRow, type CrossfadeTrackRowProps, type CrossfadeVolumeCurves, DB_MAX, DB_MIN, DEFAULT_FX_CATEGORY_DETAIL, DEFAULT_FX_DRY_WET, DRAG_DEAD_ZONE, type DeckBoundaryEvent, type DeckBoundaryListener, DownloadPackButton, type DownloadPackButtonProps, type DownloadPackButtonVariant, type DrawerTab, type DrumKit, EMPTY_FX_DETAIL_STATE, EMPTY_FX_STATE, EQUAL_POWER_GAIN, type ExportMidiBundleOptions, type ExportMidiBundleResult, type ExportedPluginData, FX_CATEGORIES, FX_CHAIN_ORDER, FX_DISPLAY_LABELS, FX_ENGINE_PLUGIN_NAMES, FX_PRESET_CONFIGS, type FadeDirection, type FadeEntry, type FadeGesture, type FadeLayer, type FadeMeta, FadeModal, type FadeModalProps, type FadeSelection, FadeTrackRow, type FadeTrackRowProps, type FxCategory, type FxCategoryDetailState, type FxPreset, type FxPresetConfig, type FxPresetData, type FxPresetDataEntry, FxToggleBar, type FxToggleBarProps, GUTTER_W, type GeneratorPlugin, type GeneratorType, type ImportCandidateScene, type ImportCandidateTrack, ImportTrackModal, type ImportTrackModalProps, type InstrumentDescriptor, TrackDrawer as InstrumentDrawer, type TrackDrawerProps as InstrumentDrawerProps, type InstrumentSampler, type InstrumentZone, type LLMCandidate, type LLMContent, type LLMFunctionDeclaration, type LLMGenerationConfig, type LLMGenerationRequest, type LLMGenerationResult, type LLMPart, type LLMSystemInstruction, type LLMTool, type LLMToolUseRequest, type LLMToolUseResponse, type LLMUsageMetadata, LevelMeter, type LevelMeterProps, type ListAudioFilesOptions, type ListImportableTracksOptions, type MidiClipData, type MidiWriteResult, type MixInterpolation, Modal, type ModalProps, type MusicalContext, OffsetScrubber, type OffsetScrubberProps, PLUGIN_SDK_VERSION, PX_PER_BEAT, PanSlider, type PeakAnalysis, PianoRollEditor, type PianoRollEditorProps, type PickTopKOptions, type PluginAppTool, type PluginAppToolInputSchema, type PluginAppToolResult, type PluginAudioTextureRequest, type PluginAudioTextureResult, type PluginCapabilities, type PluginChordSegment, type PluginChordTiming, type PluginConcurrentTrackInfo, type PluginCuePoints, type PluginDownloadOptions, PluginError, type PluginErrorCode, type PluginFileDialogOptions, type PluginFxCategoryDetailState, type PluginGenerationContext, type PluginHost, type PluginHttpRequestOptions, type PluginHttpResponse, type PluginManifest, type PluginMidiNote, type PluginPresetData, type PluginPresetInfo, type PluginRegistration, type PluginSampleFilter, type PluginSampleImportResult, type PluginSampleInfo, type PluginSampleTrackInfo, type PluginSceneContext, type PluginSceneInfo, type PluginSettingsSchema, type PluginSettingsStore, type PluginSkill, type PluginSkillInputSchema, type PluginStatus, type PluginStemSplitResult, type PluginStemTrackInfo, type PluginSynthInfo, type PluginTrackFxDetailState, type PluginTrackHandle, type PluginTrackInfo, type PluginTrackLevel, type PluginTrackRuntimeState, type PluginTransportState, type PluginTrimWindow, type PluginUIProps, type PostProcessOptions, RESIZE_HANDLE_PX, ROW_HEIGHT, type ReadMidiClip, type ReadMidiResult, type RecordingChunkFinalizedEvent, type RecordingTargetInfo, type SDKTrackRowProps, SLIDER_UNITY, SamplePackCTACard, type SamplePackCTACardProps, type SamplePackCTACardStatus, type SamplePackCardInfo, type SavePluginPresetOptions, type SceneChangeListener, type SceneFamilyTrack, type ScoredCandidate, ScrollingWaveform, type ScrollingWaveformProps, type SettingDefinition, type ShufflePresetResult, SorceryProgressBar, type SoundHistoryEntry, type StemType, type SynthesizeCuePointsOptions, TEXTURAL_ROLES, TrackDrawer, type TrackDrawerProps, type TrackFxDetailState, type TrackFxState, type TrackLevelsHandle, TrackMeterStrip, type TrackMeterStripProps, type TrackMeterView, TrackRow, type TrackRowDragProps, type TrackSoundHistory, type TrackSoundSnapshot, type TrackStateChangeListener, type TransportEvent, type TransportEventListener, type UnsubscribeFn, type UseSoundHistoryOptions, type UseSoundHistoryResult, type UseTrackReorderOptions, type UseTrackReorderResult, type VolumeAutomationPoint, VolumeSlider, type WaveformPeaks, WaveformView, type WaveformViewProps, analyzeWavPeak, asCrossfadeMeta, asFadeMeta, buildCrossfadeInpaintPrompt, buildCrossfadeVolumeCurves, buildFadeVolumeCurve, calculateTimeBasedTarget, cellToPx, centerScrollTop, computePeaks, dbToSlider, defaultFadeGesture, drawWaveform, formatConcurrentTracks, moveItem, parseCrossfadePairs, parseFades, pickTopKWeighted, pitchToName, pxToCell, resizeNoteDuration, scorePromptMatch, sliderToDb, synthesizeCuePoints, tokenizePrompt, transposeNotes, useAnySolo, useSceneState, useSoundHistory, useTrackLevel, useTrackLevels, useTrackMeter, useTrackReorder, useTransportPlaying };
4809
+ export { AUDIO_EFFECTS, AUDIO_EFFECT_LABEL, type AudioEffect, type AudioInputDevice, type BulkAddPlaceholderTrack, type ComposeProgressEvent, type ComposeProgressListener, type ComposeSceneOptions, type ComposeSceneResult, ConfirmDialog, type ConfirmDialogProps, type CoreTrackHandlers, type CreateTrackOptions, type CrossfadeInpaintInput, type CrossfadeLayer, type CrossfadeMeta, CrossfadeModal, type CrossfadeModalProps, type CrossfadePairMeta, type CrossfadeSelection, type CrossfadeSlot, CrossfadeTrackRow, type CrossfadeTrackRowProps, type CrossfadeVolumeCurves, DB_MAX, DB_MIN, DEFAULT_FX_CATEGORY_DETAIL, DEFAULT_FX_DRY_WET, DRAG_DEAD_ZONE, type DeckBoundaryEvent, type DeckBoundaryListener, type DesignerRowSlots, DownloadPackButton, type DownloadPackButtonProps, type DownloadPackButtonVariant, type DrawerTab, type DrumKit, EMPTY_FX_DETAIL_STATE, EMPTY_FX_STATE, EQUAL_POWER_GAIN, type ExportMidiBundleOptions, type ExportMidiBundleResult, type ExportedPluginData, FX_CATEGORIES, FX_CHAIN_ORDER, FX_DISPLAY_LABELS, FX_ENGINE_PLUGIN_NAMES, FX_PRESET_CONFIGS, type FadeDirection, type FadeEntry, type FadeGesture, type FadeLayer, type FadeMeta, FadeModal, type FadeModalProps, type FadeSelection, FadeTrackRow, type FadeTrackRowProps, type FxCategory, type FxCategoryDetailState, type FxPreset, type FxPresetConfig, type FxPresetData, type FxPresetDataEntry, FxToggleBar, type FxToggleBarProps, GUTTER_W, type GenerationServices, type GeneratorPanelAdapter, type GeneratorPanelCore, GeneratorPanelShell, type GeneratorPanelShellProps, type GeneratorPanelSlots, type GeneratorPlugin, type GeneratorTrackState, type GeneratorType, type GroupParseSpec, type GroupRenderContext, type ImportCandidateScene, type ImportCandidateTrack, ImportTrackModal, type ImportTrackModalProps, type InstrumentDescriptor, TrackDrawer as InstrumentDrawer, type TrackDrawerProps as InstrumentDrawerProps, type InstrumentSampler, type InstrumentZone, type LLMCandidate, type LLMContent, type LLMFunctionDeclaration, type LLMGenerationConfig, type LLMGenerationRequest, type LLMGenerationResult, type LLMNoteResponse, type LLMPart, type LLMSystemInstruction, type LLMTool, type LLMToolUseRequest, type LLMToolUseResponse, type LLMUsageMetadata, LevelMeter, type LevelMeterProps, type ListAudioFilesOptions, type ListImportableTracksOptions, type MidiClipData, type MidiWriteResult, type MixInterpolation, Modal, type ModalProps, type MusicalContext, OffsetScrubber, type OffsetScrubberProps, PLUGIN_SDK_VERSION, PX_PER_BEAT, PanSlider, type PanelFeatureFlags, type PanelGenerationStrategy, type PanelGroupExtension, type PanelIdentity, type PanelShuffleAdapter, type PanelSoundAdapter, type PeakAnalysis, PianoRollEditor, type PianoRollEditorProps, type PickTopKOptions, type PluginAppTool, type PluginAppToolInputSchema, type PluginAppToolResult, type PluginAudioTextureRequest, type PluginAudioTextureResult, type PluginCapabilities, type PluginChordSegment, type PluginChordTiming, type PluginConcurrentTrackInfo, type PluginCuePoints, type PluginDownloadOptions, PluginError, type PluginErrorCode, type PluginFileDialogOptions, type PluginFxCategoryDetailState, type PluginGenerationContext, type PluginHost, type PluginHttpRequestOptions, type PluginHttpResponse, type PluginManifest, type PluginMidiNote, type PluginPresetData, type PluginPresetInfo, type PluginRegistration, type PluginSampleFilter, type PluginSampleImportResult, type PluginSampleInfo, type PluginSampleTrackInfo, type PluginSceneContext, type PluginSceneInfo, type PluginSettingsSchema, type PluginSettingsStore, type PluginSkill, type PluginSkillInputSchema, type PluginStatus, type PluginStemSplitResult, type PluginStemTrackInfo, type PluginSynthInfo, type PluginTrackFxDetailState, type PluginTrackHandle, type PluginTrackInfo, type PluginTrackLevel, type PluginTrackRuntimeState, type PluginTransportState, type PluginTrimWindow, type PluginUIProps, type PostProcessOptions, RESIZE_HANDLE_PX, ROW_HEIGHT, type ReadMidiClip, type ReadMidiResult, type RecordingChunkFinalizedEvent, type RecordingTargetInfo, type ResolveGroupsOptions, type ResolvedCrossfadePair, type ResolvedFade, type ResolvedGroupsResult, type ResolvedTrackGroup, type SDKTrackRowProps, SLIDER_UNITY, SamplePackCTACard, type SamplePackCTACardProps, type SamplePackCTACardStatus, type SamplePackCardInfo, type SavePluginPresetOptions, type SceneChangeListener, type SceneFamilyTrack, type ScoredCandidate, ScrollingWaveform, type ScrollingWaveformProps, type SettingDefinition, type ShufflePresetResult, SorceryProgressBar, type SoundHistoryEntry, type StemType, type SurgeSoundAdapterOverrides, type SynthesizeCuePointsOptions, TEXTURAL_ROLES, TRANSITION_DESIGNER_DRAFT_KEY, TrackDrawer, type TrackDrawerProps, type TrackFxDetailState, type TrackFxState, type TrackGroupMember, type TrackGroupMeta, type TrackLevelsHandle, TrackMeterStrip, type TrackMeterStripProps, type TrackMeterView, TrackRow, type TrackRowDragProps, type TrackSoundHistory, type TrackSoundSnapshot, type TrackStateChangeListener, TransitionDesigner, type TransitionDesignerDraft, type TransitionDesignerProps, type TransitionOps, type TransitionRowType, type TransportEvent, type TransportEventListener, type UnsubscribeFn, type UseGeneratorPanelCoreOptions, type UseSoundHistoryOptions, type UseSoundHistoryResult, type UseTrackReorderOptions, type UseTrackReorderResult, type UseTransitionOpsInputs, type VolumeAutomationPoint, VolumeSlider, type WaveformPeaks, WaveformView, type WaveformViewProps, analyzeWavPeak, asAudioEffect, asCrossfadeMeta, asFadeMeta, asTransitionDesignerDraft, buildCrossfadeInpaintPrompt, buildCrossfadeVolumeCurves, buildFadeVolumeCurve, buildRowSlots, calculateTimeBasedTarget, cellToPx, centerScrollTop, computePeaks, createSurgeSoundAdapter, dbIdsFromKeys, dbToSlider, defaultFadeGesture, drawWaveform, formatConcurrentTracks, hashString, moveItem, newTrackState, normalizeSlots, padPair, padSlots, parseCrossfadePairs, parseFades, parseLLMNoteResponse, parseTrackGroups, pickTopKWeighted, pitchToName, pluginFxToToggleFx, pxToCell, reconcileSlots, resizeNoteDuration, resolveTrackGroups, rowKey, rowType, scorePromptMatch, sliderToDb, slotsEqual, soundIdentity, synthesizeCuePoints, tokenizePrompt, trackDataKey, transposeNotes, useAnySolo, useGeneratorPanelCore, useSceneState, useSoundHistory, useTrackLevel, useTrackLevels, useTrackMeter, useTrackReorder, useTransitionOps, useTransportPlaying };