@@ -12,6 +12,17 @@ function cloneSound(sound) {
1212 } ;
1313}
1414
15+ function nextSoundNumberAfter ( soundEntries ) {
16+ let highest = 0 ;
17+ soundEntries . forEach ( ( entry ) => {
18+ const match = / ^ s f x - ( \d + ) $ / . exec ( entry . id ) ;
19+ if ( match ) {
20+ highest = Math . max ( highest , Number . parseInt ( match [ 1 ] , 10 ) ) ;
21+ }
22+ } ) ;
23+ return highest + 1 ;
24+ }
25+
1526export class AudioSfxPlaygroundV2App {
1627 constructor ( { accordions, actionNav, audioEngine, controls, inspector, preview, serializer, shell, statusLog, tileList, windowRef = window } ) {
1728 this . accordions = accordions ;
@@ -37,7 +48,10 @@ export class AudioSfxPlaygroundV2App {
3748 onToolCopyJson : ( ) => {
3849 void this . copyJson ( ) ;
3950 } ,
40- onToolExportToolState : ( ) => this . exportToolState ( ) ,
51+ onToolExportToolState : ( ) => this . exportJson ( ) ,
52+ onToolImportJson : ( file ) => {
53+ void this . importJson ( file ) ;
54+ } ,
4155 onToolPlay : ( ) => {
4256 void this . play ( ) ;
4357 } ,
@@ -47,7 +61,8 @@ export class AudioSfxPlaygroundV2App {
4761 } ) ;
4862 this . controls . mount ( {
4963 onAdd : ( ) => this . addCurrentSound ( ) ,
50- onChange : ( ) => this . handleEditorChange ( )
64+ onChange : ( ) => this . handleEditorChange ( ) ,
65+ onDelete : ( ) => this . deleteCurrentSound ( )
5166 } ) ;
5267 this . tileList . mount ( {
5368 onSelect : ( soundId ) => this . selectSound ( soundId )
@@ -132,6 +147,7 @@ export class AudioSfxPlaygroundV2App {
132147 activeSoundId : this . activeSoundId ,
133148 soundEntries : this . soundEntries
134149 } ) ;
150+ this . controls . setDeleteEnabled ( Boolean ( this . activeSoundId ) ) ;
135151 }
136152
137153 async play ( ) {
@@ -150,15 +166,53 @@ export class AudioSfxPlaygroundV2App {
150166 this . refreshPreview ( ) ;
151167 }
152168
153- exportToolState ( ) {
169+ deleteCurrentSound ( ) {
170+ const entryIndex = this . soundEntries . findIndex ( ( entry ) => entry . id === this . activeSoundId ) ;
171+ if ( entryIndex === - 1 ) {
172+ this . statusLog . error ( "Select a saved SFX tile before deleting." ) ;
173+ this . controls . setDeleteEnabled ( false ) ;
174+ return ;
175+ }
176+ const [ entry ] = this . soundEntries . splice ( entryIndex , 1 ) ;
177+ this . activeSoundId = "" ;
178+ this . renderSoundList ( ) ;
179+ this . refreshPreview ( ) ;
180+ this . statusLog . write ( `Deleted ${ entry . sound . name } .` ) ;
181+ }
182+
183+ exportJson ( ) {
154184 const { toolState, validation } = this . currentToolState ( ) ;
155185 if ( ! validation . valid ) {
156186 this . statusLog . error ( validation . message ) ;
157187 this . refreshPreview ( ) ;
158188 return ;
159189 }
160190 this . inspector . showObject ( toolState ) ;
161- this . statusLog . write ( "toolState preview written to Output Summary." ) ;
191+ this . statusLog . write ( "JSON preview written to Output Summary." ) ;
192+ }
193+
194+ async importJson ( file ) {
195+ try {
196+ const text = await file . text ( ) ;
197+ const parsed = JSON . parse ( text ) ;
198+ const result = this . serializer . readToolState ( parsed ) ;
199+ if ( ! result . valid ) {
200+ this . statusLog . error ( `Import JSON failed: ${ result . message } ` ) ;
201+ return ;
202+ }
203+ this . activeSoundId = result . value . activeSoundId ;
204+ this . soundEntries = result . value . soundEntries . map ( ( entry ) => ( {
205+ id : entry . id ,
206+ sound : cloneSound ( entry . sound )
207+ } ) ) ;
208+ this . nextSoundNumber = nextSoundNumberAfter ( this . soundEntries ) ;
209+ this . controls . loadSound ( result . value . sound ) ;
210+ this . renderSoundList ( ) ;
211+ this . refreshPreview ( ) ;
212+ this . statusLog . write ( `Imported JSON from ${ file . name } .` ) ;
213+ } catch ( error ) {
214+ this . statusLog . error ( `Import JSON failed: ${ error . message } ` ) ;
215+ }
162216 }
163217
164218 async copyJson ( ) {
0 commit comments