@signalsandsorcery/plugin-sdk 1.0.0

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.
@@ -0,0 +1,1136 @@
1
+ import React, { ComponentType, ReactNode } from 'react';
2
+
3
+ /**
4
+ * Plugin SDK Type Definitions
5
+ *
6
+ * Complete type system for the generator plugin architecture.
7
+ * Plugins implement GeneratorPlugin and interact with the host via PluginHost.
8
+ * All plugin output flows through TracktionEngine (MIDI or audio clips).
9
+ */
10
+
11
+ /** What kind of Tracktion content this plugin creates */
12
+ type GeneratorType = 'midi' | 'audio' | 'sample' | 'hybrid';
13
+ /** Describes an available instrument plugin (VST3/AU synth) on the system. */
14
+ interface InstrumentDescriptor {
15
+ /** Stable plugin identifier for loading (VST3 TUID or AU component ID) */
16
+ pluginId: string;
17
+ /** Display name */
18
+ name: string;
19
+ /** Plugin manufacturer */
20
+ manufacturer: string;
21
+ /** Plugin format */
22
+ type: 'vst3' | 'au' | 'vst' | 'internal';
23
+ /** Plugin category (from scan) */
24
+ category: string;
25
+ /** Whether this plugin is currently installed/available */
26
+ missing?: boolean;
27
+ }
28
+ /** Every generator plugin must implement this interface. */
29
+ interface GeneratorPlugin {
30
+ /** Unique ID, npm-style scope: '@sas/synth-generator', '@user/my-plugin' */
31
+ readonly id: string;
32
+ /** Human-readable name shown in accordion header */
33
+ readonly displayName: string;
34
+ /** Semver version string */
35
+ readonly version: string;
36
+ /** Short description for settings/marketplace */
37
+ readonly description: string;
38
+ /** 24x24 icon — data URL, relative path from plugin dir, or undefined */
39
+ readonly icon?: string;
40
+ /** What kind of Tracktion content this plugin creates */
41
+ readonly generatorType: GeneratorType;
42
+ /** Minimum host SDK version this plugin requires */
43
+ readonly minHostVersion?: string;
44
+ /**
45
+ * Called once when plugin is loaded. Receives the PluginHost API.
46
+ * If this throws, plugin is marked as failed and not rendered.
47
+ */
48
+ activate(host: PluginHost): Promise<void>;
49
+ /**
50
+ * Called when plugin is being unloaded (disable, uninstall, app quit).
51
+ * Must complete within 5 seconds or host force-kills.
52
+ */
53
+ deactivate(): Promise<void>;
54
+ /**
55
+ * Return the React component rendered inside the accordion section.
56
+ * Component receives PluginUIProps from the host.
57
+ */
58
+ getUIComponent(): ComponentType<PluginUIProps>;
59
+ /**
60
+ * Return JSON Schema for plugin-specific settings.
61
+ * Host auto-renders a settings form. Return null if no settings.
62
+ */
63
+ getSettingsSchema(): PluginSettingsSchema | null;
64
+ /**
65
+ * Optional: Called when the active scene changes.
66
+ */
67
+ onSceneChanged?(sceneId: string | null): Promise<void>;
68
+ /**
69
+ * Optional: Called when the generation context changes
70
+ * (chords updated, tracks added/removed, BPM changed).
71
+ */
72
+ onContextChanged?(context: MusicalContext): void;
73
+ }
74
+ /** Props passed to every plugin's React component by the host */
75
+ interface PluginUIProps {
76
+ /** The scoped PluginHost API instance for this plugin */
77
+ host: PluginHost;
78
+ /** Currently active scene ID (null if none selected) */
79
+ activeSceneId: string | null;
80
+ /** Whether the user is authenticated (for LLM access) */
81
+ isAuthenticated: boolean;
82
+ /** Whether all systems are connected (engine, gateway) */
83
+ isConnected: boolean;
84
+ /** Which workstation deck this is rendered in */
85
+ deckId?: 'left' | 'right';
86
+ /** Plugin calls this to set/clear header buttons. Pass null to clear. */
87
+ onHeaderContent?: (content: ReactNode | null) => void;
88
+ /** Plugin calls this to show/hide the loading spinner in the header. */
89
+ onLoading?: (loading: boolean) => void;
90
+ /** Scene-level context: contract state, chords, BPM, etc. Null if no scene. */
91
+ sceneContext?: PluginSceneContext | null;
92
+ /** Callback to open the scene selector (Scenes accordion section). */
93
+ onSelectScene?: (() => void) | null;
94
+ /** Callback to open the contract/chords section (for "Generate a Contract" CTA). */
95
+ onOpenContract?: (() => void) | null;
96
+ /** Callback to expand this plugin's own accordion section. */
97
+ onExpandSelf?: (() => void) | null;
98
+ }
99
+ /** Scoped API surface that plugins interact with. Plugins NEVER get direct TracktionEngine access. */
100
+ interface PluginHost {
101
+ /** Create a new track in the active scene. Host enforces ownership and scene routing. */
102
+ createTrack(options: CreateTrackOptions): Promise<PluginTrackHandle>;
103
+ /** Delete a track previously created by THIS plugin. */
104
+ deleteTrack(trackId: string): Promise<void>;
105
+ /** Get all tracks this plugin owns in the active scene. */
106
+ getPluginTracks(): Promise<PluginTrackHandle[]>;
107
+ /** Adopt unowned tracks in the active scene matching this plugin's generator type. */
108
+ adoptSceneTracks(): Promise<PluginTrackHandle[]>;
109
+ /** Get info about a specific owned track. */
110
+ getTrackInfo(trackId: string): Promise<PluginTrackInfo>;
111
+ /** Set track mute state. Only works on owned tracks. */
112
+ setTrackMute(trackId: string, muted: boolean): Promise<void>;
113
+ /** Set track volume (linear 0.0 - 1.0). Only works on owned tracks. */
114
+ setTrackVolume(trackId: string, volume: number): Promise<void>;
115
+ /** Set track pan (-1.0 left to 1.0 right). Only works on owned tracks. */
116
+ setTrackPan(trackId: string, pan: number): Promise<void>;
117
+ /** Set track solo state. Only works on owned tracks. */
118
+ setTrackSolo(trackId: string, solo: boolean): Promise<void>;
119
+ /** Rename a track. Only works on owned tracks. */
120
+ setTrackName(trackId: string, name: string): Promise<void>;
121
+ /** Shuffle preset: keep MIDI, apply a random preset from the same category. Only works on owned tracks. */
122
+ shufflePreset(trackId: string): Promise<ShufflePresetResult>;
123
+ /** Duplicate track: copy MIDI + role to a new track with a different preset. Only works on owned tracks. */
124
+ duplicateTrack(trackId: string): Promise<PluginTrackHandle>;
125
+ /** Get detailed FX state for a track (enabled, preset, dry/wet per category). */
126
+ getTrackFxState(trackId: string): Promise<PluginTrackFxDetailState>;
127
+ /** Toggle an FX category on/off for a track. */
128
+ toggleTrackFx(trackId: string, category: string, enabled: boolean): Promise<void>;
129
+ /** Set FX preset for a track. Returns the new dry/wet value if applicable. */
130
+ setTrackFxPreset(trackId: string, category: string, presetIndex: number): Promise<{
131
+ dryWet?: number;
132
+ }>;
133
+ /** Set FX dry/wet level for a track. */
134
+ setTrackFxDryWet(trackId: string, category: string, value: number): Promise<void>;
135
+ /** Subscribe to real-time track state changes (mute, solo, volume, pan). Returns unsubscribe fn. */
136
+ onTrackStateChange(listener: TrackStateChangeListener): UnsubscribeFn;
137
+ /** Write MIDI notes to a track this plugin owns. Replaces existing MIDI. */
138
+ writeMidiClip(trackId: string, clip: MidiClipData): Promise<MidiWriteResult>;
139
+ /** Clear all MIDI from a track this plugin owns. */
140
+ clearMidi(trackId: string): Promise<void>;
141
+ /**
142
+ * Run the host's MIDI post-processing pipeline on raw notes.
143
+ * Wraps MidiProcessor: quantize -> swing -> scale -> register -> overlaps -> humanize.
144
+ */
145
+ postProcessMidi(notes: PluginMidiNote[], options: PostProcessOptions): Promise<PluginMidiNote[]>;
146
+ /** Place an audio file on a track this plugin owns. */
147
+ writeAudioClip(trackId: string, filePath: string, position?: number): Promise<void>;
148
+ /** Load a VST3/AU plugin onto a track this plugin owns. */
149
+ loadSynthPlugin(trackId: string, pluginName: string): Promise<number>;
150
+ /** Set plugin state (base64-encoded preset data). */
151
+ setPluginState(trackId: string, pluginIndex: number, stateBase64: string): Promise<void>;
152
+ /** Get current plugin state (base64-encoded). */
153
+ getPluginState(trackId: string, pluginIndex: number): Promise<string>;
154
+ /** List plugins currently loaded on a track. */
155
+ getTrackPlugins(trackId: string): Promise<PluginSynthInfo[]>;
156
+ /** Remove a plugin from a track. */
157
+ removePlugin(trackId: string, pluginIndex: number): Promise<void>;
158
+ /** Check if a specific VST/AU plugin is available on the system. */
159
+ isPluginAvailable(pluginName: string): Promise<boolean>;
160
+ /** Get available instrument plugins (VST3/AU synths) scanned by the engine. */
161
+ getAvailableInstruments(): Promise<InstrumentDescriptor[]>;
162
+ /** Get the instrument plugin currently loaded on a track. Null = default (Surge XT). */
163
+ getTrackInstrument(trackId: string): Promise<InstrumentDescriptor | null>;
164
+ /** Change the instrument plugin on a track. Preserves MIDI data. */
165
+ setTrackInstrument(trackId: string, pluginId: string): Promise<void>;
166
+ /** Get the FULL generation context for the active scene. */
167
+ getGenerationContext(excludeTrackId?: string): Promise<PluginGenerationContext>;
168
+ /** Get lightweight musical context (no concurrent track MIDI data). */
169
+ getMusicalContext(): Promise<MusicalContext>;
170
+ /** Get the active scene ID. Null if no scene is active. */
171
+ getActiveSceneId(): string | null;
172
+ /** Get list of all scenes in the project. */
173
+ getSceneList(): Promise<PluginSceneInfo[]>;
174
+ /** Subscribe to transport state changes. Returns unsubscribe function. */
175
+ onTransportEvent(listener: TransportEventListener): UnsubscribeFn;
176
+ /** Subscribe to deck boundary events. Returns unsubscribe function. */
177
+ onDeckBoundary(listener: DeckBoundaryListener): UnsubscribeFn;
178
+ /** Subscribe to scene change events. Returns unsubscribe function. */
179
+ onSceneChange(listener: SceneChangeListener): UnsubscribeFn;
180
+ /** Get current transport state (one-shot). */
181
+ getTransportState(): Promise<PluginTransportState>;
182
+ /** Generate text/JSON via the host's authenticated LLM service. */
183
+ generateWithLLM(request: LLMGenerationRequest): Promise<LLMGenerationResult>;
184
+ /** Check if LLM access is available (user authenticated + gateway reachable). */
185
+ isLLMAvailable(): Promise<boolean>;
186
+ /** Get available preset categories for a synth plugin. */
187
+ getPresetCategories(pluginName: string): Promise<string[]>;
188
+ /** Get a random preset from a category. */
189
+ getRandomPreset(category: string): Promise<PluginPresetData | null>;
190
+ /** Get a specific preset by name from a category. */
191
+ getPresetByName(category: string, name: string): Promise<PluginPresetData | null>;
192
+ /** Use LLM to classify a text description into a preset category. */
193
+ classifyPresetCategory(description: string): Promise<string>;
194
+ /** Get absolute path to this plugin's isolated data directory. */
195
+ getDataDirectory(): string;
196
+ /** Persisted key-value settings store. */
197
+ settings: PluginSettingsStore;
198
+ /** Get a value from scene-scoped plugin data. */
199
+ getSceneData<T = unknown>(sceneId: string, key: string): Promise<T | null>;
200
+ /** Set a value in scene-scoped plugin data. */
201
+ setSceneData(sceneId: string, key: string, value: unknown): Promise<void>;
202
+ /** Get all key-value pairs for a scene. */
203
+ getAllSceneData(sceneId: string): Promise<Record<string, unknown>>;
204
+ /** Delete a key from scene-scoped plugin data. */
205
+ deleteSceneData(sceneId: string, key: string): Promise<void>;
206
+ /** Get the full project-scoped state object. */
207
+ getProjectData<T = unknown>(key: string): Promise<T | null>;
208
+ /** Set a project-scoped data value. */
209
+ setProjectData(key: string, value: unknown): Promise<void>;
210
+ /** Show a toast notification to the user. */
211
+ showToast(type: 'info' | 'success' | 'warning' | 'error', title: string, message?: string): void;
212
+ /** Set progress indicator on a specific track. -1 to hide. */
213
+ setProgress(trackId: string, progress: number): void;
214
+ /** Set a global status message in the plugin's accordion header. */
215
+ setStatusMessage(message: string | null): void;
216
+ /** Request user confirmation via a modal dialog. */
217
+ confirmAction(title: string, message: string): Promise<boolean>;
218
+ /** Show a native file open dialog. Requires 'fileDialog' capability. */
219
+ showOpenDialog(options: PluginFileDialogOptions): Promise<string[] | null>;
220
+ /** Show a native file save dialog. Requires 'fileDialog' capability. */
221
+ showSaveDialog(options: PluginFileDialogOptions): Promise<string | null>;
222
+ /** Download a file to the plugin's data directory. */
223
+ downloadFile(url: string, filename: string, options?: PluginDownloadOptions): Promise<string>;
224
+ /** Copy a file into the plugin's data directory. */
225
+ importFile(sourcePath: string, destFilename: string): Promise<string>;
226
+ /** Make an HTTP request. Requires 'network' capability with allowedHosts. */
227
+ httpRequest(options: PluginHttpRequestOptions): Promise<PluginHttpResponse>;
228
+ /** Store a secret in the OS keychain (plugin-scoped). */
229
+ storeSecret(key: string, value: string): Promise<void>;
230
+ /** Retrieve a secret from the OS keychain (plugin-scoped). */
231
+ getSecret(key: string): Promise<string | null>;
232
+ /** Delete a secret from the OS keychain (plugin-scoped). */
233
+ deleteSecret(key: string): Promise<void>;
234
+ /** Query the sample library with optional filters. */
235
+ getSamples(filter?: PluginSampleFilter): Promise<PluginSampleInfo[]>;
236
+ /** Get a single sample by ID. */
237
+ getSampleById(id: string): Promise<PluginSampleInfo | null>;
238
+ /** Import audio files into the sample library. */
239
+ importSamples(filePaths: string[]): Promise<PluginSampleImportResult>;
240
+ /** Create a sample track in the active scene. */
241
+ createSampleTrack(sampleId: string, options?: {
242
+ name?: string;
243
+ }): Promise<PluginTrackHandle>;
244
+ /** Delete a sample track. */
245
+ deleteSampleTrack(trackId: string): Promise<void>;
246
+ /** Get all sample tracks in the active scene. Re-establishes ownership. */
247
+ getPluginSampleTracks(): Promise<PluginSampleTrackInfo[]>;
248
+ /** Time-stretch a sample to a target BPM. Returns the new sample info. */
249
+ timeStretchSample(sampleId: string, targetBpm: number): Promise<PluginSampleInfo>;
250
+ /** Invoke the host's audio texture generation pipeline. */
251
+ generateAudioTexture(request: PluginAudioTextureRequest): Promise<PluginAudioTextureResult>;
252
+ /** Trigger bulk composition for the active scene (LLM plans arrangement, creates tracks, generates MIDI). */
253
+ composeScene(options: ComposeSceneOptions): Promise<ComposeSceneResult>;
254
+ /** Subscribe to composition progress events (planning, generating, complete, error). */
255
+ onComposeProgress(listener: ComposeProgressListener): UnsubscribeFn;
256
+ /** Subscribe to engine ready events (fires when the engine finishes loading tracks after a scene change). */
257
+ onEngineReady(listener: () => void): UnsubscribeFn;
258
+ /** Audition a single note on a track (fire-and-forget preview). */
259
+ auditionNote(trackId: string, pitch: number, velocity: number, durationMs: number): Promise<void>;
260
+ /** Get presets for this plugin, optionally filtered by category. */
261
+ getPluginPresets(category?: string): Promise<PluginPresetInfo[]>;
262
+ /** Save a new preset for this plugin. */
263
+ savePluginPreset(options: SavePluginPresetOptions): Promise<PluginPresetInfo>;
264
+ /** Delete a plugin preset by ID. */
265
+ deletePluginPreset(id: string): Promise<void>;
266
+ /** Log a performance metric. */
267
+ logMetric(name: string, durationMs: number, metadata?: Record<string, unknown>): void;
268
+ /** Start a timer. Returns a stop function that logs the duration. */
269
+ startTimer(name: string): () => void;
270
+ }
271
+ interface ExportedPluginData {
272
+ pluginId: string;
273
+ scope: 'project' | 'scene' | 'global';
274
+ scopeId: string | null;
275
+ key: string;
276
+ value: string;
277
+ }
278
+ interface CreateTrackOptions {
279
+ /** Display name for the track. Auto-generated if omitted. */
280
+ name?: string;
281
+ /** Musical role hint: 'bass', 'drums', 'lead', 'chords', 'pad', 'arp', 'fx' */
282
+ role?: string;
283
+ /** Load a synth plugin immediately (default: false) */
284
+ loadSynth?: boolean;
285
+ /** Which synth to load (default: 'Surge XT'). Ignored if loadSynth=false. */
286
+ synthName?: string;
287
+ /**
288
+ * Stable plugin identifier for a custom instrument (VST3 TUID or AU component ID).
289
+ * If provided with loadSynth=true, loads this plugin instead of synthName.
290
+ * Null/undefined = use default (Surge XT).
291
+ */
292
+ instrumentPluginId?: string | null;
293
+ /** Metadata stored in DB. Plugins can use this for plugin-specific data. */
294
+ metadata?: Record<string, unknown>;
295
+ }
296
+ interface PluginTrackHandle {
297
+ /** Tracktion engine track ID (stable, GUID-based) */
298
+ id: string;
299
+ /** Display name */
300
+ name: string;
301
+ /** Database row ID */
302
+ dbId: string;
303
+ /** Musical role (if set) */
304
+ role?: string;
305
+ /** Prompt from tracks table (fallback when plugin_data not yet populated) */
306
+ prompt?: string;
307
+ /** Custom instrument plugin ID (null = default Surge XT) */
308
+ instrumentPluginId?: string | null;
309
+ /** Custom instrument display name (null = Surge XT) */
310
+ instrumentName?: string | null;
311
+ }
312
+ interface PluginTrackInfo extends PluginTrackHandle {
313
+ /** Is track muted? */
314
+ muted: boolean;
315
+ /** Is track soloed? */
316
+ soloed: boolean;
317
+ /** Volume (linear 0-1) */
318
+ volume: number;
319
+ /** Pan (-1 to 1) */
320
+ pan: number;
321
+ /** Loaded plugins on this track */
322
+ plugins: PluginSynthInfo[];
323
+ /** Has MIDI clips? */
324
+ hasMidi: boolean;
325
+ /** Has audio clips? */
326
+ hasAudio: boolean;
327
+ }
328
+ interface PluginSynthInfo {
329
+ index: number;
330
+ name: string;
331
+ type: string;
332
+ enabled: boolean;
333
+ }
334
+ /** Real-time runtime state of a track (pushed from engine) */
335
+ interface PluginTrackRuntimeState {
336
+ id: string;
337
+ muted: boolean;
338
+ solo: boolean;
339
+ volume: number;
340
+ pan: number;
341
+ }
342
+ /** Listener for real-time track state changes */
343
+ type TrackStateChangeListener = (trackId: string, state: PluginTrackRuntimeState) => void;
344
+ /** Per-category FX detail state */
345
+ interface PluginFxCategoryDetailState {
346
+ enabled: boolean;
347
+ presetIndex: number;
348
+ dryWet: number;
349
+ }
350
+ /** Full FX detail state for a track — one entry per FX category */
351
+ type PluginTrackFxDetailState = Record<string, PluginFxCategoryDetailState>;
352
+ interface MidiClipData {
353
+ /** Start time in seconds */
354
+ startTime: number;
355
+ /** End time in seconds */
356
+ endTime: number;
357
+ /** BPM for beat<->time conversion */
358
+ tempo: number;
359
+ /** MIDI notes */
360
+ notes: PluginMidiNote[];
361
+ }
362
+ interface PluginMidiNote {
363
+ /** MIDI pitch 0-127 */
364
+ pitch: number;
365
+ /** Start position in quarter-note beats (0 = beginning of clip) */
366
+ startBeat: number;
367
+ /** Duration in quarter-note beats */
368
+ durationBeats: number;
369
+ /** Velocity 1-127 */
370
+ velocity: number;
371
+ /** MIDI channel 0-15 (default: 0) */
372
+ channel?: number;
373
+ }
374
+ interface MidiWriteResult {
375
+ /** Number of notes written */
376
+ notesInserted: number;
377
+ /** Actual bars covered */
378
+ bars: number;
379
+ }
380
+ interface PostProcessOptions {
381
+ /** Snap notes to grid (default: true) */
382
+ quantize?: boolean;
383
+ /** Grid size: '1/4', '1/8', '1/16', '1/32', '1/8T', '1/16T' (default: '1/16') */
384
+ quantizeGrid?: string;
385
+ /** Quantize strength 0-100 (default: 75) */
386
+ quantizeStrength?: number;
387
+ /** Swing amount 0-100 (default: 0) */
388
+ swing?: number;
389
+ /** Humanize timing/velocity variation 0-100 (default: 0) */
390
+ humanize?: number;
391
+ /** Enforce diatonic scale (default: false). Uses scene key/mode. */
392
+ enforceScale?: boolean;
393
+ /** Clamp notes to pitch range [low, high] */
394
+ clampRegister?: [number, number];
395
+ /** Remove overlapping notes on same pitch/channel (default: true) */
396
+ removeOverlaps?: boolean;
397
+ }
398
+ interface MusicalContext {
399
+ key: string;
400
+ mode: string;
401
+ bpm: number;
402
+ bars: number;
403
+ genre: string | null;
404
+ timeSignature: string;
405
+ chordProgression: PluginChordTiming[];
406
+ }
407
+ interface PluginChordTiming {
408
+ /** Chord symbol: 'Cm7', 'G', 'Fmaj7', etc. */
409
+ symbol: string;
410
+ /** Start position in quarter notes */
411
+ startQn: number;
412
+ /** End position in quarter notes */
413
+ endQn: number;
414
+ }
415
+ /** Full generation context — includes concurrent track MIDI data */
416
+ interface PluginGenerationContext {
417
+ chordProgression: {
418
+ key: {
419
+ tonic: string;
420
+ mode: string;
421
+ };
422
+ chordsWithTiming: PluginChordTiming[];
423
+ genre: string | null;
424
+ };
425
+ concurrentTracks: PluginConcurrentTrackInfo[];
426
+ }
427
+ interface PluginConcurrentTrackInfo {
428
+ trackId: string;
429
+ role: string | undefined;
430
+ presetCategory: string | null;
431
+ /** Notes organized by which chord they fall under */
432
+ notesByChord: PluginChordSegment[];
433
+ }
434
+ interface PluginChordSegment {
435
+ chord: string;
436
+ chordRangeQn: [number, number];
437
+ notes: PluginMidiNote[];
438
+ }
439
+ interface TransportEvent {
440
+ type: 'play' | 'stop' | 'pause' | 'bpmChange' | 'positionChange';
441
+ bpm?: number;
442
+ position?: number;
443
+ isPlaying?: boolean;
444
+ }
445
+ interface DeckBoundaryEvent {
446
+ deckId: string;
447
+ bar: number;
448
+ beat: number;
449
+ loopCount: number;
450
+ }
451
+ interface PluginTransportState {
452
+ isPlaying: boolean;
453
+ isPaused: boolean;
454
+ bpm: number;
455
+ position: number;
456
+ timeSignature: string;
457
+ }
458
+ interface PluginSceneInfo {
459
+ id: string;
460
+ name: string;
461
+ isMuted: boolean;
462
+ }
463
+ /** Scene-level contract/context state passed to plugin UIs as a prop */
464
+ interface PluginSceneContext {
465
+ /** Whether a contract has been generated (genre or contractPrompt exists AND chords exist) */
466
+ hasContract: boolean;
467
+ /** Original user prompt text (e.g., "dark psytrance"). Null if none. */
468
+ contractPrompt: string | null;
469
+ /** Extracted genre. Null if none. */
470
+ genre: string | null;
471
+ /** Musical key. Null if no chord progression. */
472
+ key: {
473
+ tonic: string;
474
+ mode: string;
475
+ } | null;
476
+ /** Chord symbols (e.g., ["Cm", "Fm", "G"]). Empty if no chords. */
477
+ chords: string[];
478
+ /** BPM from project tempo */
479
+ bpm: number;
480
+ /** Scene length in bars */
481
+ bars: number;
482
+ /** Whether any synth tracks exist in this scene */
483
+ hasTracks: boolean;
484
+ /** Whether bulk generation is currently in progress */
485
+ isBulkGenerating: boolean;
486
+ }
487
+ /** Placeholder track state for the progressive bulk-add UX */
488
+ interface BulkAddPlaceholderTrack {
489
+ id: string;
490
+ planIndex: number;
491
+ role: string;
492
+ description: string;
493
+ status: 'planned' | 'creating' | 'completed' | 'failed';
494
+ error?: string;
495
+ }
496
+ type TransportEventListener = (event: TransportEvent) => void;
497
+ type DeckBoundaryListener = (event: DeckBoundaryEvent) => void;
498
+ type SceneChangeListener = (sceneId: string | null) => void;
499
+ type UnsubscribeFn = () => void;
500
+ interface LLMGenerationRequest {
501
+ /** System prompt (instructions, role, output format) */
502
+ system: string;
503
+ /** User prompt (the actual request) */
504
+ user: string;
505
+ /** Max tokens for response (host may cap this) */
506
+ maxTokens?: number;
507
+ /** Expected response format hint */
508
+ responseFormat?: 'text' | 'json';
509
+ /**
510
+ * If true, the host will NOT auto-prefix the user prompt with musical
511
+ * context (key, BPM, chords, genre, etc.). Default: false (context IS
512
+ * prefixed automatically).
513
+ */
514
+ skipContextPrefix?: boolean;
515
+ }
516
+ interface LLMGenerationResult {
517
+ /** Raw response text */
518
+ content: string;
519
+ /** Tokens consumed */
520
+ tokensUsed: number;
521
+ /** Model that generated the response */
522
+ model: string;
523
+ }
524
+ interface PluginPresetData {
525
+ name: string;
526
+ category: string;
527
+ /** Base64-encoded plugin state — pass to setPluginState() */
528
+ state: string;
529
+ }
530
+ /** Result of shufflePreset() — the new preset that was applied */
531
+ interface ShufflePresetResult {
532
+ presetName: string;
533
+ presetCategory: string;
534
+ }
535
+ interface PluginSettingsSchema {
536
+ type: 'object';
537
+ properties: Record<string, SettingDefinition>;
538
+ }
539
+ interface SettingDefinition {
540
+ type: 'string' | 'number' | 'boolean' | 'select';
541
+ label: string;
542
+ description?: string;
543
+ default?: unknown;
544
+ /** For 'select' type */
545
+ options?: Array<{
546
+ label: string;
547
+ value: string;
548
+ }>;
549
+ /** For 'number' type */
550
+ min?: number;
551
+ max?: number;
552
+ }
553
+ interface PluginSettingsStore {
554
+ get<T>(key: string, defaultValue: T): T;
555
+ set(key: string, value: unknown): void;
556
+ getAll(): Record<string, unknown>;
557
+ /** Subscribe to settings changes. Returns unsubscribe fn. */
558
+ onChange(listener: (key: string, value: unknown) => void): UnsubscribeFn;
559
+ }
560
+ type PluginErrorCode = 'NOT_OWNED' | 'TRACK_NOT_FOUND' | 'TRACK_LIMIT_EXCEEDED' | 'NO_ACTIVE_SCENE' | 'ENGINE_ERROR' | 'INVALID_MIDI' | 'FILE_NOT_FOUND' | 'INVALID_FORMAT' | 'PLUGIN_NOT_FOUND' | 'LLM_BUDGET_EXCEEDED' | 'LLM_UNAVAILABLE' | 'NOT_AUTHENTICATED' | 'TIMEOUT' | 'CANCELLED' | 'INCOMPATIBLE' | 'CAPABILITY_DENIED' | 'SECRET_NOT_FOUND';
561
+ declare class PluginError extends Error {
562
+ readonly code: PluginErrorCode;
563
+ readonly details?: Record<string, unknown>;
564
+ constructor(code: PluginErrorCode, message: string, details?: Record<string, unknown>);
565
+ }
566
+ interface PluginManifest {
567
+ id: string;
568
+ displayName: string;
569
+ version: string;
570
+ description: string;
571
+ generatorType: GeneratorType;
572
+ main: string;
573
+ renderer?: string;
574
+ icon?: string;
575
+ author?: string;
576
+ license?: string;
577
+ minHostVersion?: string;
578
+ capabilities?: PluginCapabilities;
579
+ settings?: Record<string, SettingDefinition>;
580
+ builtIn?: boolean;
581
+ repository?: string;
582
+ }
583
+ interface PluginCapabilities {
584
+ requiresLLM?: boolean;
585
+ requiresSurgeXT?: boolean;
586
+ requiresNetwork?: boolean;
587
+ /** Allowed network hosts for httpRequest (e.g., ['api.splice.com']) */
588
+ network?: {
589
+ allowedHosts?: string[];
590
+ };
591
+ /** Plugin needs native file dialog access */
592
+ fileDialog?: boolean;
593
+ }
594
+ interface PluginFileDialogOptions {
595
+ title?: string;
596
+ defaultPath?: string;
597
+ filters?: Array<{
598
+ name: string;
599
+ extensions: string[];
600
+ }>;
601
+ /** For open dialog: allow selecting multiple files */
602
+ multiSelections?: boolean;
603
+ /** For open dialog: allow selecting directories */
604
+ directories?: boolean;
605
+ }
606
+ interface PluginDownloadOptions {
607
+ /** HTTP headers to include */
608
+ headers?: Record<string, string>;
609
+ /** Overwrite if file exists (default: false) */
610
+ overwrite?: boolean;
611
+ }
612
+ interface PluginHttpRequestOptions {
613
+ url: string;
614
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
615
+ headers?: Record<string, string>;
616
+ body?: string | Record<string, unknown>;
617
+ /** Timeout in milliseconds (default: 30000) */
618
+ timeoutMs?: number;
619
+ }
620
+ interface PluginHttpResponse {
621
+ status: number;
622
+ statusText: string;
623
+ headers: Record<string, string>;
624
+ body: string;
625
+ }
626
+ interface PluginSampleFilter {
627
+ bpm?: number;
628
+ key?: {
629
+ tonic: string;
630
+ mode?: string;
631
+ };
632
+ category?: string;
633
+ searchQuery?: string;
634
+ }
635
+ interface PluginSampleInfo {
636
+ id: string;
637
+ filename: string;
638
+ filePath: string;
639
+ category: string | null;
640
+ bpm: number | null;
641
+ keyTonic: string | null;
642
+ keyMode: string | null;
643
+ durationSeconds: number | null;
644
+ fileSizeBytes: number | null;
645
+ tags: string[] | null;
646
+ }
647
+ interface PluginSampleImportResult {
648
+ imported: number;
649
+ skipped: number;
650
+ errors: string[];
651
+ }
652
+ /** Sample track with associated sample metadata (returned by getPluginSampleTracks) */
653
+ interface PluginSampleTrackInfo {
654
+ track: PluginTrackHandle;
655
+ sample: PluginSampleInfo;
656
+ volume: number;
657
+ pan: number;
658
+ }
659
+ interface PluginAudioTextureRequest {
660
+ /** Text prompt describing the audio texture */
661
+ prompt: string;
662
+ /** Duration in seconds (default: scene length) */
663
+ durationSeconds?: number;
664
+ /** Target BPM (default: project BPM) */
665
+ bpm?: number;
666
+ }
667
+ interface PluginAudioTextureResult {
668
+ /** Path to the generated audio file */
669
+ filePath: string;
670
+ /** Duration of the generated audio in seconds */
671
+ durationSeconds: number;
672
+ }
673
+ /** Options for composing a full scene arrangement via LLM. */
674
+ interface ComposeSceneOptions {
675
+ /** The contract prompt / musical direction for the arrangement. */
676
+ contractPrompt: string;
677
+ /** Genre hint (e.g. 'techno', 'jazz'). Optional. */
678
+ genre?: string | null;
679
+ }
680
+ /** Result from a scene composition. */
681
+ interface ComposeSceneResult {
682
+ /** Whether the composition completed successfully. */
683
+ success: boolean;
684
+ /** Number of tracks created. */
685
+ tracksCreated: number;
686
+ /** Error message if not successful. */
687
+ error?: string;
688
+ }
689
+ /** Listener for composition progress events. */
690
+ type ComposeProgressListener = (event: ComposeProgressEvent) => void;
691
+ /** Progress event emitted during scene composition. */
692
+ interface ComposeProgressEvent {
693
+ /** Current phase: 'planning' (LLM deciding tracks), 'generating' (creating MIDI), 'complete', 'error'. */
694
+ phase: 'planning' | 'generating' | 'complete' | 'error';
695
+ /** Per-track placeholder state (available once planning is done). */
696
+ placeholders?: BulkAddPlaceholderTrack[];
697
+ /** Error message when phase is 'error'. */
698
+ error?: string;
699
+ /** Scene ID this compose event belongs to (for scene-keyed UI state). */
700
+ sceneId?: string;
701
+ }
702
+ interface PluginPresetInfo {
703
+ id: string;
704
+ name: string;
705
+ category: string | null;
706
+ isBuiltIn: boolean;
707
+ data: Record<string, unknown>;
708
+ }
709
+ interface SavePluginPresetOptions {
710
+ name: string;
711
+ category?: string;
712
+ data: Record<string, unknown>;
713
+ }
714
+ type PluginStatus = 'pending' | 'active' | 'failed' | 'disabled' | 'incompatible';
715
+ interface PluginRegistration {
716
+ /** The loaded plugin instance */
717
+ plugin: GeneratorPlugin;
718
+ /** Current status */
719
+ status: PluginStatus;
720
+ /** Resolved manifest from disk */
721
+ manifest: PluginManifest;
722
+ /** The scoped PluginHost instance for this plugin */
723
+ host: PluginHost | null;
724
+ /** Sort order for accordion display */
725
+ sortOrder: number;
726
+ /** Whether the plugin is enabled */
727
+ enabled: boolean;
728
+ /** Error message if status is 'failed' */
729
+ error?: string;
730
+ }
731
+
732
+ /**
733
+ * FX Toggle Types
734
+ *
735
+ * Types and constants for per-track FX toggle buttons.
736
+ * Each track can enable/disable 6 FX categories independently.
737
+ * The engine is the source of truth — no database persistence needed.
738
+ */
739
+ /** Available FX categories in signal chain order */
740
+ type FxCategory = 'eq' | 'compressor' | 'chorus' | 'phaser' | 'delay' | 'reverb';
741
+ /** All FX categories in signal chain order */
742
+ declare const FX_CATEGORIES: readonly FxCategory[];
743
+ /** Position in the signal chain (lower = earlier) */
744
+ declare const FX_CHAIN_ORDER: Record<FxCategory, number>;
745
+ /** Map from FxCategory to Tracktion Engine built-in plugin xmlTypeName */
746
+ declare const FX_ENGINE_PLUGIN_NAMES: Record<FxCategory, string>;
747
+ /** Display labels for UI buttons */
748
+ declare const FX_DISPLAY_LABELS: Record<FxCategory, string>;
749
+ /** Per-track FX state: which categories are active */
750
+ interface TrackFxState {
751
+ eq: boolean;
752
+ compressor: boolean;
753
+ chorus: boolean;
754
+ phaser: boolean;
755
+ delay: boolean;
756
+ reverb: boolean;
757
+ }
758
+ /** Default state: all FX disabled */
759
+ declare const EMPTY_FX_STATE: TrackFxState;
760
+ /** A single FX preset definition */
761
+ interface FxPreset {
762
+ /** Display name (e.g. "Room", "Hall") */
763
+ name: string;
764
+ /** Short label for button (e.g. "RM", "HL") */
765
+ shortLabel: string;
766
+ /** Map from automatable parameter name -> value (set via setPluginParameter) */
767
+ params: Record<string, number>;
768
+ /** CachedValue params set via XML state (getPluginState/setPluginState) */
769
+ xmlStateParams?: Record<string, number>;
770
+ /** BPM-relative delay time multiplier (1.0 = quarter note). When set, Delay Time is computed at apply time. */
771
+ noteMultiplier?: number;
772
+ /** Fixed delay time in ms (non-BPM-synced). Mutually exclusive with noteMultiplier. */
773
+ fixedLengthMs?: number;
774
+ }
775
+ /** How dry/wet is applied to the plugin */
776
+ type MixInterpolation = 'direct' | 'gain-scale' | 'ratio-scale';
777
+ /** Preset configuration for an FX category */
778
+ interface FxPresetConfig {
779
+ /** Exactly 5 presets */
780
+ presets: [FxPreset, FxPreset, FxPreset, FxPreset, FxPreset];
781
+ /** Name of the native mix/wet parameter, or null if no native dry/wet */
782
+ mixParamName: string | null;
783
+ /** XML attribute name for dry/wet control (for plugins with no automatable mix param, e.g. chorus/phaser) */
784
+ mixXmlAttr?: string;
785
+ /** How to apply dry/wet (defaults to 'direct') */
786
+ mixInterpolation: MixInterpolation;
787
+ }
788
+ /** Per-category detail state for a single FX on a track */
789
+ interface FxCategoryDetailState {
790
+ enabled: boolean;
791
+ presetIndex: number;
792
+ dryWet: number;
793
+ }
794
+ /** Extended FX state per track with preset and dry/wet info */
795
+ type TrackFxDetailState = Record<FxCategory, FxCategoryDetailState>;
796
+ /** Default dry/wet mix level (33% — musically useful for most effects) */
797
+ declare const DEFAULT_FX_DRY_WET = 0.33;
798
+ /** Default detail state for a single category */
799
+ declare const DEFAULT_FX_CATEGORY_DETAIL: FxCategoryDetailState;
800
+ /** Default detail state: all FX disabled, preset 0, full wet */
801
+ declare const EMPTY_FX_DETAIL_STATE: TrackFxDetailState;
802
+ /** Persisted FX data for a single category (stored as JSON in database) */
803
+ interface FxPresetDataEntry {
804
+ presetIndex: number;
805
+ dryWet: number;
806
+ enabled: boolean;
807
+ }
808
+ /** Persisted FX data format (stored as JSON in database) */
809
+ type FxPresetData = Partial<Record<FxCategory, FxPresetDataEntry>>;
810
+
811
+ /**
812
+ * SDK TrackRow — Reusable track row component for generator plugins.
813
+ *
814
+ * Renders a complete track UI with prompt input, generation controls,
815
+ * shuffle/copy, volume/pan, mute/solo, FX drawer, and visual states
816
+ * (amber pulse for "needs generation", progress overlay, error indicator).
817
+ *
818
+ * Layout matches TrackInput (main branch) for visual parity.
819
+ *
820
+ * Depends only on PluginHost types + existing shared renderer components.
821
+ */
822
+
823
+ interface SDKTrackRowProps {
824
+ /** Track identity */
825
+ track: {
826
+ id: string;
827
+ name: string;
828
+ role?: string;
829
+ };
830
+ /** Current prompt text (optional — omit when using contentSlot) */
831
+ prompt?: string;
832
+ /** Playback state */
833
+ runtimeState: {
834
+ muted: boolean;
835
+ solo: boolean;
836
+ volume: number;
837
+ pan: number;
838
+ };
839
+ /** FX category states */
840
+ fxDetailState: TrackFxDetailState;
841
+ /** FX panel visibility */
842
+ fxDrawerOpen: boolean;
843
+ /** Generation in progress */
844
+ isGenerating?: boolean;
845
+ /** Auth state */
846
+ isAuthenticated?: boolean;
847
+ /** Error from last generation */
848
+ error?: string | null;
849
+ /** Enables shuffle/copy buttons */
850
+ hasMidi?: boolean;
851
+ /** Progress % (for persistence across scene switches) */
852
+ generationProgress?: number;
853
+ /** For progress bar pacing */
854
+ estimatedGenerationMs?: number;
855
+ /** Prompt edit (optional — omit to hide prompt input) */
856
+ onPromptChange?: (prompt: string) => void;
857
+ /** "Create" button / Enter key (optional — omit to hide Create button) */
858
+ onGenerate?: () => void;
859
+ /** Shuffle preset (optional — omit to hide Shuffle button) */
860
+ onShuffle?: () => void;
861
+ /** Duplicate track (optional — omit to hide Copy button) */
862
+ onCopy?: () => void;
863
+ /** Delete track */
864
+ onDelete: () => void;
865
+ /** Custom content replacing the prompt input (e.g., sample info display) */
866
+ contentSlot?: React.ReactNode;
867
+ /** Toggle mute */
868
+ onMuteToggle: () => void;
869
+ /** Toggle solo */
870
+ onSoloToggle: () => void;
871
+ /** Volume slider */
872
+ onVolumeChange: (vol: number) => void;
873
+ /** Pan slider */
874
+ onPanChange: (pan: number) => void;
875
+ /** FX category toggle (optional — omit to hide FX button) */
876
+ onFxToggle?: (cat: FxCategory, enabled: boolean) => void;
877
+ /** FX preset select */
878
+ onFxPresetChange?: (cat: FxCategory, idx: number) => void;
879
+ /** FX dry/wet */
880
+ onFxDryWetChange?: (cat: FxCategory, val: number) => void;
881
+ /** Open/close FX (optional — omit to hide FX button) */
882
+ onToggleFxDrawer?: () => void;
883
+ /** Progress persistence callback */
884
+ onProgressChange?: (pct: number) => void;
885
+ /** Left border accent color */
886
+ accentColor?: string;
887
+ /** Current instrument display name (null/undefined = Surge XT default) */
888
+ instrumentName?: string | null;
889
+ /** Whether the current instrument plugin is missing from the system */
890
+ instrumentMissing?: boolean;
891
+ /** Whether the instrument drawer is open */
892
+ instrumentDrawerOpen?: boolean;
893
+ /** Toggle the instrument drawer */
894
+ onToggleInstrumentDrawer?: () => void;
895
+ /** Available instrument plugins for the drawer */
896
+ availableInstruments?: InstrumentDescriptor[];
897
+ /** Currently loaded instrument plugin ID */
898
+ currentInstrumentPluginId?: string | null;
899
+ /** Called when user selects an instrument from the drawer */
900
+ onInstrumentSelect?: (pluginId: string) => void;
901
+ /** Whether instrument scan is loading */
902
+ instrumentsLoading?: boolean;
903
+ /** Re-scan for instruments */
904
+ onRefreshInstruments?: () => void;
905
+ }
906
+ declare function TrackRow({ track, prompt, runtimeState, fxDetailState, fxDrawerOpen, isGenerating, isAuthenticated, error, hasMidi, generationProgress, estimatedGenerationMs, onPromptChange, onGenerate, onShuffle, onCopy, onDelete, contentSlot, onMuteToggle, onSoloToggle, onVolumeChange, onPanChange, onFxToggle, onFxPresetChange, onFxDryWetChange, onToggleFxDrawer, onProgressChange, accentColor, instrumentName, instrumentMissing, instrumentDrawerOpen, onToggleInstrumentDrawer, availableInstruments, currentInstrumentPluginId, onInstrumentSelect, instrumentsLoading, onRefreshInstruments, }: SDKTrackRowProps): React.ReactElement;
907
+
908
+ /**
909
+ * InstrumentDrawer — Sliding drawer for selecting instrument plugins (VST3/AU).
910
+ *
911
+ * Appears below the track controls when the "P" button is toggled.
912
+ * Shows a searchable grid of available instrument plugins.
913
+ */
914
+
915
+ interface InstrumentDrawerProps {
916
+ /** Available instrument plugins from engine scan */
917
+ instruments: InstrumentDescriptor[];
918
+ /** Currently loaded instrument plugin ID (null = default Surge XT) */
919
+ currentPluginId: string | null;
920
+ /** Whether the scan is still in progress */
921
+ isLoading: boolean;
922
+ /** Called when user selects an instrument */
923
+ onSelect: (pluginId: string) => void;
924
+ /** Called when user clicks refresh to re-scan plugins */
925
+ onRefresh: () => void;
926
+ }
927
+ declare function InstrumentDrawer({ instruments, currentPluginId, isLoading, onSelect, onRefresh, }: InstrumentDrawerProps): React.ReactElement;
928
+
929
+ /**
930
+ * VolumeSlider Component
931
+ *
932
+ * Compact horizontal volume slider for track volume control.
933
+ * Uses native HTML range input with custom styling.
934
+ */
935
+
936
+ interface VolumeSliderProps {
937
+ /** Volume value from 0 to 1 */
938
+ value: number;
939
+ /** Called when volume changes (debounced) */
940
+ onChange: (value: number) => void;
941
+ /** Disable the slider */
942
+ disabled?: boolean;
943
+ /** Additional CSS classes */
944
+ className?: string;
945
+ }
946
+ declare const VolumeSlider: React.FC<VolumeSliderProps>;
947
+
948
+ /**
949
+ * PanSlider Component
950
+ *
951
+ * Compact horizontal pan slider for track stereo positioning.
952
+ * Range: -1 (left) to +1 (right), 0 = center.
953
+ * No text label - tooltip only.
954
+ */
955
+
956
+ interface PanSliderProps {
957
+ /** Pan value from -1 (left) to 1 (right), 0 = center */
958
+ value: number;
959
+ /** Called when pan changes (debounced) */
960
+ onChange: (value: number) => void;
961
+ /** Disable the slider */
962
+ disabled?: boolean;
963
+ /** Additional CSS classes */
964
+ className?: string;
965
+ }
966
+ declare const PanSlider: React.FC<PanSliderProps>;
967
+
968
+ /**
969
+ * FxToggleBar Component
970
+ *
971
+ * Per-track FX control panel with 6 rows (one per FX category).
972
+ * Each row: [Category toggle] [Preset 1-5 buttons] [Dry/Wet slider]
973
+ *
974
+ * Signal chain order: EQ -> Compressor -> Chorus -> Phaser -> Delay -> Reverb
975
+ */
976
+
977
+ interface FxToggleBarProps {
978
+ trackId: string;
979
+ fxState: TrackFxDetailState;
980
+ onToggle: (trackId: string, category: FxCategory, enabled: boolean) => void;
981
+ onPresetChange: (trackId: string, category: FxCategory, presetIndex: number) => void;
982
+ onDryWetChange: (trackId: string, category: FxCategory, value: number) => void;
983
+ disabled?: boolean;
984
+ }
985
+ declare const FxToggleBar: React.FC<FxToggleBarProps>;
986
+
987
+ /**
988
+ * SorceryProgressBar Component
989
+ *
990
+ * A progress bar for long, uncertain wait times (10-30s). Supports two modes:
991
+ *
992
+ * 1. **Time-based mode** (when `estimatedDurationMs` is provided):
993
+ * Uses elapsed time and an ease-out curve to pace progress realistically.
994
+ * Reaches ~90% at the estimated completion time, then asymptotically
995
+ * approaches 95% if the operation runs long.
996
+ *
997
+ * 2. **Phase-based mode** (legacy fallback, no `estimatedDurationMs`):
998
+ * "Zeno's Paradox" style - progress moves quickly at first, then
999
+ * asymptotically slows toward 95%.
1000
+ *
1001
+ * Visual style: Segmented "retro CLI" look with glowing teal accent,
1002
+ * diagonal stripes, and subtle pulse animation.
1003
+ */
1004
+
1005
+ /**
1006
+ * Props for SorceryProgressBar component
1007
+ */
1008
+ interface SorceryProgressBarProps {
1009
+ /** Whether loading is in progress */
1010
+ isLoading: boolean;
1011
+ /** Text shown during loading (default: "CONJURING...") */
1012
+ statusText?: string;
1013
+ /** Text shown on completion (default: "COMPLETE") */
1014
+ completeText?: string;
1015
+ /** Callback when loading completes */
1016
+ onComplete?: () => void;
1017
+ /** Height class override (default: "h-10") */
1018
+ heightClass?: string;
1019
+ /** Initial progress value (0-100) to resume from - persists across scene switches */
1020
+ initialProgress?: number;
1021
+ /** Callback when progress changes - use to persist progress in parent state */
1022
+ onProgressChange?: (progress: number) => void;
1023
+ /** Estimated total duration in ms - enables time-aware pacing */
1024
+ estimatedDurationMs?: number;
1025
+ }
1026
+ /**
1027
+ * Calculates target progress based on elapsed time and estimated duration.
1028
+ * Uses an ease-out power curve for natural-feeling progress:
1029
+ * - At 10% of estimated time: ~21% (feels responsive early)
1030
+ * - At 30% of estimated time: ~53% (good midpoint feel)
1031
+ * - At 50% of estimated time: ~74% (past halfway visually)
1032
+ * - At 80% of estimated time: ~88% (approaching completion)
1033
+ * - At 100% of estimated time: 90% (leaves room for overshoot)
1034
+ * - Beyond estimate: asymptotically approaches 95%
1035
+ */
1036
+ declare function calculateTimeBasedTarget(elapsedMs: number, estimatedDurationMs: number): number;
1037
+ /**
1038
+ * SorceryProgressBar - A mystical progress bar for uncertain wait times
1039
+ */
1040
+ declare function SorceryProgressBar({ isLoading, statusText, completeText, onComplete, heightClass, initialProgress, onProgressChange, estimatedDurationMs, }: SorceryProgressBarProps): React.ReactElement | null;
1041
+
1042
+ /**
1043
+ * useSceneState — Scene-keyed state hook for plugin developers.
1044
+ *
1045
+ * Works like `useState`, but maintains separate state per scene.
1046
+ * When the user switches scenes, the previous scene's state is preserved
1047
+ * and restored when they switch back.
1048
+ *
1049
+ * Returns `[value, setForCurrentScene, setForScene]`:
1050
+ * - `value` — state for the currently active scene
1051
+ * - `setForCurrentScene(v)` — updates state for whatever scene is active at call time
1052
+ * - `setForScene(sceneId, v)` — updates state for a specific scene (for async callbacks)
1053
+ *
1054
+ * Both setters support the functional updater pattern: `prev => next`.
1055
+ *
1056
+ * **Important:** For object/array `initialValue`, hoist to a module-level constant
1057
+ * to keep the setter callbacks referentially stable:
1058
+ * ```ts
1059
+ * const EMPTY: string[] = [];
1060
+ * const [items, setItems, setItemsForScene] = useSceneState(activeSceneId, EMPTY);
1061
+ * ```
1062
+ */
1063
+ type SetSceneState<T> = (value: T | ((prev: T) => T)) => void;
1064
+ type SetSceneStateForScene<T> = (sceneId: string, value: T | ((prev: T) => T)) => void;
1065
+ declare function useSceneState<T>(activeSceneId: string | null, initialValue: T): [T, SetSceneState<T>, SetSceneStateForScene<T>];
1066
+
1067
+ /**
1068
+ * Valid instrument roles for synth track generation.
1069
+ * Used in LLM prompts and preset selection.
1070
+ * Canonical source — imported by both plugin SDK and core services.
1071
+ */
1072
+ declare const VALID_INSTRUMENT_ROLES: readonly string[];
1073
+
1074
+ /**
1075
+ * Plugin SDK Version
1076
+ *
1077
+ * Semver version of the plugin SDK contract.
1078
+ * Plugins declare minSdkVersion in their manifest.
1079
+ * Registry checks semver.gte(PLUGIN_SDK_VERSION, manifest.minHostVersion)
1080
+ * during activation and marks incompatible plugins accordingly.
1081
+ */
1082
+ declare const PLUGIN_SDK_VERSION = "1.0.0";
1083
+
1084
+ /**
1085
+ * FX Preset Definitions
1086
+ *
1087
+ * 5 presets per FX category (30 total).
1088
+ *
1089
+ * Parameter names must match the Tracktion Engine's AutomatableParameter names
1090
+ * for each built-in plugin exactly (case-sensitive).
1091
+ *
1092
+ * Chorus & Phaser have ZERO automatable parameters — all values are set via
1093
+ * XML state (CachedValues on the plugin ValueTree).
1094
+ *
1095
+ * Lives in shared/ so both main process (services) and renderer (UI) can import.
1096
+ */
1097
+
1098
+ /** All preset configs keyed by FX category */
1099
+ declare const FX_PRESET_CONFIGS: Record<FxCategory, FxPresetConfig>;
1100
+
1101
+ /**
1102
+ * Volume Conversion Utilities
1103
+ *
1104
+ * Converts between UI slider position (0-1) and engine dB values using a power
1105
+ * curve with +6 dB headroom. The curve places unity gain (0 dB) at slider 0.75,
1106
+ * giving the top 25% of the slider a meaningful 6 dB boost range instead of the
1107
+ * previous perceptual dead zone.
1108
+ *
1109
+ * Mapping:
1110
+ * slider 0.00 → -60 dB (silence)
1111
+ * slider 0.75 → 0 dB (unity gain)
1112
+ * slider 1.00 → +6 dB (max boost)
1113
+ */
1114
+ /** Slider position that maps to 0 dB (unity gain) */
1115
+ declare const SLIDER_UNITY = 0.75;
1116
+ /** Maximum dB value at slider = 1.0 */
1117
+ declare const DB_MAX = 6;
1118
+ /** Minimum dB value (silence floor) */
1119
+ declare const DB_MIN = -60;
1120
+ /**
1121
+ * Convert a UI slider position (0-1) to engine dB.
1122
+ *
1123
+ * @param slider - Slider value in [0, 1]
1124
+ * @returns dB value in [DB_MIN, DB_MAX]
1125
+ */
1126
+ declare function sliderToDb(slider: number): number;
1127
+ /**
1128
+ * Convert an engine dB value back to a UI slider position (0-1).
1129
+ * Inverse of sliderToDb().
1130
+ *
1131
+ * @param db - Volume in dB
1132
+ * @returns Slider value in [0, 1]
1133
+ */
1134
+ declare function dbToSlider(db: number): number;
1135
+
1136
+ export { type BulkAddPlaceholderTrack, type ComposeProgressEvent, type ComposeProgressListener, type ComposeSceneOptions, type ComposeSceneResult, type CreateTrackOptions, DB_MAX, DB_MIN, DEFAULT_FX_CATEGORY_DETAIL, DEFAULT_FX_DRY_WET, type DeckBoundaryEvent, type DeckBoundaryListener, EMPTY_FX_DETAIL_STATE, EMPTY_FX_STATE, type ExportedPluginData, FX_CATEGORIES, FX_CHAIN_ORDER, FX_DISPLAY_LABELS, FX_ENGINE_PLUGIN_NAMES, FX_PRESET_CONFIGS, type FxCategory, type FxCategoryDetailState, type FxPreset, type FxPresetConfig, type FxPresetData, type FxPresetDataEntry, FxToggleBar, type FxToggleBarProps, type GeneratorPlugin, type GeneratorType, type InstrumentDescriptor, InstrumentDrawer, type InstrumentDrawerProps, type LLMGenerationRequest, type LLMGenerationResult, type MidiClipData, type MidiWriteResult, type MixInterpolation, type MusicalContext, PLUGIN_SDK_VERSION, PanSlider, type PluginAudioTextureRequest, type PluginAudioTextureResult, type PluginCapabilities, type PluginChordSegment, type PluginChordTiming, type PluginConcurrentTrackInfo, 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 PluginStatus, type PluginSynthInfo, type PluginTrackFxDetailState, type PluginTrackHandle, type PluginTrackInfo, type PluginTrackRuntimeState, type PluginTransportState, type PluginUIProps, type PostProcessOptions, type SDKTrackRowProps, SLIDER_UNITY, type SavePluginPresetOptions, type SceneChangeListener, type SettingDefinition, type ShufflePresetResult, SorceryProgressBar, type TrackFxDetailState, type TrackFxState, TrackRow, type TrackStateChangeListener, type TransportEvent, type TransportEventListener, type UnsubscribeFn, VALID_INSTRUMENT_ROLES, VolumeSlider, calculateTimeBasedTarget, dbToSlider, sliderToDb, useSceneState };