@@ -7,6 +7,7 @@ const gridEl = document.getElementById('grid');
77const vectorEl = document . getElementById ( 'vector' ) ;
88const exportBtn = document . getElementById ( 'export' ) ;
99const playAnimationBtn = document . getElementById ( 'play-animation' ) ;
10+ const stopAnimationBtn = document . getElementById ( 'stop-animation' ) ;
1011const clearBtn = document . getElementById ( 'clear' ) ;
1112const invertBtn = document . getElementById ( 'invert' ) ;
1213const rotate180Btn = document . getElementById ( 'rotate180' ) ;
@@ -257,8 +258,6 @@ async function initEditor(){
257258
258259 if ( data && data . ok && data . frame ) {
259260 const frame = data . frame ;
260- loadedFrame = frame ;
261- loadedFrameId = frame . id ;
262261
263262 // Populate grid
264263 setGridFromRows ( frame . rows || [ ] ) ;
@@ -314,14 +313,36 @@ async function exportH(){
314313makeGrid ( ) ;
315314if ( exportBtn ) exportBtn . addEventListener ( 'click' , exportH ) ; else console . warn ( '[ui] export button not found' ) ;
316315
316+ let animationTimeout = null ;
317+
318+ function displayFrame ( frame ) {
319+ if ( ! frame ) return ;
320+
321+ // Populate grid
322+ setGridFromRows ( frame . rows || [ ] ) ;
323+
324+ // Populate name input
325+ if ( frameTitle ) frameTitle . textContent = frame . name || `Frame ${ frame . id } ` ;
326+
327+ // Mark as loaded in sidebar
328+ markLoaded ( frame ) ;
329+ }
330+
317331async function playAnimation ( ) {
318332 if ( ! playAnimationBtn ) return ;
319333
334+ // Stop any previous animation loop
335+ if ( animationTimeout ) {
336+ clearTimeout ( animationTimeout ) ;
337+ animationTimeout = null ;
338+ }
339+
320340 try {
321341 playAnimationBtn . disabled = true ;
322342 const frameIds = sessionFrames . map ( f => f . id ) ;
323343 if ( frameIds . length === 0 ) {
324344 showError ( 'No frames to play' ) ;
345+ playAnimationBtn . disabled = false ; // re-enable button
325346 return ;
326347 }
327348
@@ -340,20 +361,51 @@ async function playAnimation() {
340361
341362 if ( data . error ) {
342363 showError ( 'Error: ' + data . error ) ;
364+ playAnimationBtn . disabled = false ;
343365 } else {
344366 console . debug ( '[ui] Animation played successfully, frames=' , data . frames_played ) ;
345367 showVectorText ( 'Animation played: ' + data . frames_played + ' frames' ) ;
368+
369+ // Start frontend animation simulation
370+ let currentFrameIndex = 0 ;
371+ const animateNextFrame = ( ) => {
372+ if ( currentFrameIndex >= sessionFrames . length ) {
373+ // Animation finished
374+ playAnimationBtn . disabled = false ;
375+ animationTimeout = null ;
376+ return ;
377+ }
378+
379+ const frame = sessionFrames [ currentFrameIndex ] ;
380+ displayFrame ( frame ) ;
381+
382+ const duration = frame . duration_ms || 1000 ;
383+ currentFrameIndex ++ ;
384+
385+ animationTimeout = setTimeout ( animateNextFrame , duration ) ;
386+ } ;
387+ animateNextFrame ( ) ;
346388 }
347389
348390 } catch ( err ) {
349391 console . error ( '[ui] playAnimation failed' , err ) ;
350- } finally {
351- playAnimationBtn . disabled = false ;
392+ playAnimationBtn . disabled = false ; // re-enable on error
352393 }
353394}
354395
355396if ( playAnimationBtn ) playAnimationBtn . addEventListener ( 'click' , playAnimation ) ; else console . warn ( '[ui] play animation button not found' ) ;
356397
398+ if ( stopAnimationBtn ) {
399+ stopAnimationBtn . addEventListener ( 'click' , ( ) => {
400+ if ( animationTimeout ) {
401+ clearTimeout ( animationTimeout ) ;
402+ animationTimeout = null ;
403+ playAnimationBtn . disabled = false ;
404+ showVectorText ( 'Animation stopped' ) ;
405+ }
406+ } ) ;
407+ }
408+
357409// Save frame button removed - auto-persist replaces it
358410const animControls = document . getElementById ( 'anim-controls' ) ;
359411const animNameInput = document . getElementById ( 'anim-name' ) ;
@@ -589,8 +641,6 @@ async function loadFrameIntoEditor(id){
589641
590642 if ( data && data . ok && data . frame ) {
591643 const f = data . frame ;
592- loadedFrame = f ;
593- loadedFrameId = f . id ;
594644
595645 // Populate grid
596646 setGridFromRows ( f . rows || [ ] ) ;
@@ -661,8 +711,6 @@ async function handleNewFrameClick() {
661711 } , 'json' , 'create new frame' ) ;
662712
663713 if ( data && data . ok && data . frame ) {
664- loadedFrame = data . frame ;
665- loadedFrameId = data . frame . id ;
666714 // Set name to the backend-assigned name (Frame {id})
667715 if ( frameTitle ) frameTitle . textContent = data . frame . name || `Frame ${ data . frame . id } ` ;
668716
0 commit comments