@@ -236,6 +236,9 @@ class Blocks {
236236
237237 /** We keep a list of stacks in the trash. */
238238 this . trashStacks = [ ] ;
239+ this . actionHistory = [ ] ;
240+ this . redoActionHistory = [ ] ;
241+ this . isUndoingOrRedoing = false ;
239242 /** We keep a list of previews of stacks in the trash. */
240243 this . trashPreviews = { } ;
241244
@@ -1691,6 +1694,33 @@ class Blocks {
16911694 return ;
16921695 }
16931696
1697+ // Record position changes for undo/redo
1698+ if ( this . dragStartX !== undefined && this . dragStartY !== undefined ) {
1699+ const myBlock = this . blockList [ thisBlock ] ;
1700+ if ( myBlock && myBlock . container ) {
1701+ if (
1702+ myBlock . container . x !== this . dragStartX ||
1703+ myBlock . container . y !== this . dragStartY
1704+ ) {
1705+ this . actionHistory . push ( {
1706+ type : "move" ,
1707+ blockId : thisBlock ,
1708+ oldX : this . dragStartX ,
1709+ oldY : this . dragStartY ,
1710+ newX : myBlock . container . x ,
1711+ newY : myBlock . container . y
1712+ } ) ;
1713+
1714+ // Clear redo history on new action unless we are actively undoing/redoing
1715+ if ( ! this . isUndoingOrRedoing ) {
1716+ this . redoActionHistory = [ ] ;
1717+ }
1718+ }
1719+ }
1720+ this . dragStartX = undefined ;
1721+ this . dragStartY = undefined ;
1722+ }
1723+
16941724 let blk = this . insideExpandableBlock ( thisBlock ) ;
16951725 let expandableLoopCounter = 0 ;
16961726
@@ -7347,6 +7377,67 @@ class Blocks {
73477377 * @public
73487378 * @returns {void }
73497379 */
7380+ this . undoAction = ( ) => {
7381+ if ( ! this . actionHistory || this . actionHistory . length === 0 ) {
7382+ this . activity . textMsg ( _ ( "Nothing to undo." ) , 3000 ) ;
7383+ return ;
7384+ }
7385+
7386+ const action = this . actionHistory . pop ( ) ;
7387+ this . isUndoingOrRedoing = true ;
7388+
7389+ if ( action . type === "move" ) {
7390+ this . moveBlock ( action . blockId , action . oldX , action . oldY ) ;
7391+ this . blockMoved ( action . blockId ) ;
7392+ this . activity . refreshCanvas ( ) ;
7393+ this . activity . textMsg ( _ ( "Block position restored." ) , 3000 ) ;
7394+ } else if ( action . type === "trash" ) {
7395+ this . activity . _restoreTrashById ( action . blockId ) ;
7396+ this . activity . textMsg ( _ ( "Item restored from the trash." ) , 3000 ) ;
7397+ } else if ( action . type === "restore" ) {
7398+ const block = this . blockList [ action . blockId ] ;
7399+ if ( block ) this . sendStackToTrash ( block ) ;
7400+ this . activity . textMsg ( _ ( "Item returned to the trash." ) , 3000 ) ;
7401+ }
7402+
7403+ this . redoActionHistory . push ( action ) ;
7404+ this . isUndoingOrRedoing = false ;
7405+
7406+ // Cache DOM element reference for performance
7407+ const helpfulWheelDiv = document . getElementById ( "helpfulWheelDiv" ) ;
7408+ if ( helpfulWheelDiv && helpfulWheelDiv . style . display !== "none" ) {
7409+ helpfulWheelDiv . style . display = "none" ;
7410+ this . activity . __tick ( ) ;
7411+ }
7412+ } ;
7413+
7414+ this . redoAction = ( ) => {
7415+ if ( ! this . redoActionHistory || this . redoActionHistory . length === 0 ) {
7416+ this . activity . textMsg ( _ ( "Nothing to redo." ) , 3000 ) ;
7417+ return ;
7418+ }
7419+
7420+ const action = this . redoActionHistory . pop ( ) ;
7421+ this . isUndoingOrRedoing = true ;
7422+
7423+ if ( action . type === "move" ) {
7424+ this . moveBlock ( action . blockId , action . newX , action . newY ) ;
7425+ this . blockMoved ( action . blockId ) ;
7426+ this . activity . refreshCanvas ( ) ;
7427+ this . activity . textMsg ( _ ( "Block position restored." ) , 3000 ) ;
7428+ } else if ( action . type === "trash" ) {
7429+ const block = this . blockList [ action . blockId ] ;
7430+ if ( block ) this . sendStackToTrash ( block ) ;
7431+ this . activity . textMsg ( _ ( "Item returned to the trash." ) , 3000 ) ;
7432+ } else if ( action . type === "restore" ) {
7433+ this . activity . _restoreTrashById ( action . blockId ) ;
7434+ this . activity . textMsg ( _ ( "Item restored from the trash." ) , 3000 ) ;
7435+ }
7436+
7437+ this . actionHistory . push ( action ) ;
7438+ this . isUndoingOrRedoing = false ;
7439+ } ;
7440+
73507441 this . sendStackToTrash = myBlock => {
73517442 /** First, hide the palettes as they may need updating. */
73527443 for ( const name in this . activity . palettes . dict ) {
@@ -7365,6 +7456,10 @@ class Blocks {
73657456
73667457 /** Add this block to the list of blocks in the trash so we can undo this action. */
73677458 this . trashStacks . push ( thisBlock ) ;
7459+ if ( ! this . isUndoingOrRedoing ) {
7460+ this . actionHistory . push ( { type : "trash" , blockId : thisBlock } ) ;
7461+ this . redoActionHistory = [ ] ;
7462+ }
73687463
73697464 // Cap the undo history to prevent unbounded memory growth.
73707465 // Keep the 100 most recent trashed stacks.
0 commit comments