How can I implement an efficient undo/redo system for complex state changes in a React app without using external libraries?

I’m building a React application with a complex form editor where users can make multiple types of changes (e.g., add, delete, modify fields).

I want to implement an undo/redo feature to allow users to go backward and forward through their changes.

My questions:
• What is the best practice for tracking and applying undo/redo operations manually in React?
• Should I store full state snapshots, or just the diffs (changes) between states?

I’m looking for a clean, scalable approach without relying on third-party libraries like Redux Undo.

I tried saving the entire form state after each change, but it quickly became heavy and caused performance issues. I also considered tracking only the changes (diffs), but it became complicated to manage manually.

Why is my DELETE Blog API returning “Blog not found” when I try to delete a blog with a reference to a User?

I am working on a REST API where I am trying to delete a blog document from my MongoDB database. Everything works fine, and the blog is deleted when I don’t have a reference to a User in the blog document. However, when I add a reference to a User and try to delete the blog, I always get the response Blog not found.

blogController.js–

const { default: mongoose } = require("mongoose");
const blogModel = require("../models/blogModel");
const userModel = require("../models/userModel");

//get all blogs
exports.getAllBlogsController = async (req, res) => {
  try {
    const allblogs = await blogModel.find({});
    if (!allblogs) {
      return res.status(500).json({ message: "No blog is present" });
    }
    return res.status(200).json({ BlogCount: allblogs.length, allblogs });
  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// Create blog
exports.createBlogController = async (req, res) => {
  try {
    const { title, description, image,user } = req.body;
    if (!title || !description || !image || !user) {
      return res.status(400).json({ message: "All fields are required" });
    }
    const existingUser= await userModel.findById(user)
    if(!existingUser){
      return res.status(404).json({"message":"Unable to find user"})
    }
    // we will use session of mongoose and then updatethe blog 

    const newBlog = new blogModel({ title, description, image,user });
    const session= await mongoose.startSession()
     session.startTransaction()
     await newBlog.save({session})
     existingUser.blogs.push(newBlog)
     await existingUser.save({session})
     await session.commitTransaction();
     session.endSession();
    return res.status(200).json({ message: "blog created", newBlog });
  } catch (error) {
    res.status(500).json({ message: "Server error", error });
  }
};

// update blog
exports.updatBlogController = async (req, res) => {
  try {
    const {id} = req.params;
    const { title, description, image } = req.body;
    const blog = await blogModel.findByIdAndUpdate(
      id,
      { ...req.body },
      { new: true }
    );
    return res.status(200).send({
      success: true,
      message: "Blog updated",
      blog,
    });
  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// find specfic blog through id
exports.getBlogByIdController = async (req, res) => {
  try {
   const {id}= req.params
  const blog= await blogModel.findById(id);
  if(!blog){
   return res.status(500).json({"message":"No such kind of blog is present"})
  }
  return res.status(200).json({blog,"message":"fetched single blog"})


  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// delete blog
exports.deleteBlogController = async (req, res) => {
  try {
    const { id } = req.params;

    // Validate if the provided ID is a valid ObjectId
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({ message: "Invalid blog ID format" });
    }

    // Find the blog by ID and populate the user field
    const blog = await blogModel.findById(id).populate("user");

    // If no blog is found, return 404
    if (!blog) {
      return res.status(404).json({ message: "Blog not found" });
    }

    // Pull the blog's ObjectId from the user's blogs array
    if (blog.user && blog.user.blogs) {
      blog.user.blogs.pull(blog._id);
      await blog.user.save();
    } else {
      return res.status(400).json({ message: "User's blogs array not found" });
    }

    // Delete the blog from the blog collection
    await blogModel.findByIdAndDelete(id);

    // Successfully deleted the blog and updated the user
    return res.status(200).json({ message: "Blog deleted successfully" });

  } catch (error) {
    console.error(error); // Log for better debugging
    return res.status(500).json({ message: "Server error", error });
  }
};


blogModel.js —

const { default: mongoose } = require("mongoose");
const blogModel = require("../models/blogModel");
const userModel = require("../models/userModel");

//get all blogs
exports.getAllBlogsController = async (req, res) => {
  try {
    const allblogs = await blogModel.find({});
    if (!allblogs) {
      return res.status(500).json({ message: "No blog is present" });
    }
    return res.status(200).json({ BlogCount: allblogs.length, allblogs });
  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// Create blog
exports.createBlogController = async (req, res) => {
  try {
    const { title, description, image,user } = req.body;
    if (!title || !description || !image || !user) {
      return res.status(400).json({ message: "All fields are required" });
    }
    const existingUser= await userModel.findById(user)
    if(!existingUser){
      return res.status(404).json({"message":"Unable to find user"})
    }
    // we will use session of mongoose and then updatethe blog 

    const newBlog = new blogModel({ title, description, image,user });
    const session= await mongoose.startSession()
     session.startTransaction()
     await newBlog.save({session})
     existingUser.blogs.push(newBlog)
     await existingUser.save({session})
     await session.commitTransaction();
     session.endSession();
    return res.status(200).json({ message: "blog created", newBlog });
  } catch (error) {
    res.status(500).json({ message: "Server error", error });
  }
};

// update blog
exports.updatBlogController = async (req, res) => {
  try {
    const {id} = req.params;
    const { title, description, image } = req.body;
    const blog = await blogModel.findByIdAndUpdate(
      id,
      { ...req.body },
      { new: true }
    );
    return res.status(200).send({
      success: true,
      message: "Blog updated",
      blog,
    });
  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// find specfic blog through id
exports.getBlogByIdController = async (req, res) => {
  try {
   const {id}= req.params
  const blog= await blogModel.findById(id);
  if(!blog){
   return res.status(500).json({"message":"No such kind of blog is present"})
  }
  return res.status(200).json({blog,"message":"fetched single blog"})


  } catch (error) {
    return res.status(500).json({ message: "Server error", error });
  }
};

// delete blog
exports.deleteBlogController = async (req, res) => {
  try {
    const { id } = req.params;

    // Validate if the provided ID is a valid ObjectId
    if (!mongoose.Types.ObjectId.isValid(id)) {
      return res.status(400).json({ message: "Invalid blog ID format" });
    }

    // Find the blog by ID and populate the user field
    const blog = await blogModel.findById(id).populate("user");

    // If no blog is found, return 404
    if (!blog) {
      return res.status(404).json({ message: "Blog not found" });
    }

    // Pull the blog's ObjectId from the user's blogs array
    if (blog.user && blog.user.blogs) {
      blog.user.blogs.pull(blog._id);
      await blog.user.save();
    } else {
      return res.status(400).json({ message: "User's blogs array not found" });
    }

    // Delete the blog from the blog collection
    await blogModel.findByIdAndDelete(id);

    // Successfully deleted the blog and updated the user
    return res.status(200).json({ message: "Blog deleted successfully" });

  } catch (error) {
    console.error(error); // Log for better debugging
    return res.status(500).json({ message: "Server error", error });
  }
};


usermodel.js

const mongoose= require('mongoose')

const userSchema= new mongoose.Schema({
   username:{
      type:String,
      required:[true,'username is required']
   },
   email:{
      type:String,
      required:[true,'email is required'],
      unique:true
   },
   password:{
      type:String,
      required:[true,'password is required'],
      unique:true
   },
   blogs:[
      {
         type:mongoose.Types.ObjectId,
         ref: 'Blog',
         
      }
   ]
   
},{timestamps:true})

const userModel= mongoose.model('User',userSchema)
module.exports= userModel;

blogRoutes.js

//Delete blog
router.delete('/delete-blog/:id',deleteBlogController);

server.js —


//routes
app.use('/api/v1/user',userRoutes);
app.use('/api/v1/blog',blogRoutes);

enter image description hereenter image description here

Can I save some data to indexeddb when page is closed

Context

I understand that both events like pagehide and visibilitychange are less guaranteed to be fired, compared to unload and beforeunload, see: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
I want implement collection of troubleshooting data and how app features used (like, feature A activated via toolbar, then feature B via hotkey, than error happened) and than store it locally in indexeddb to give person who uses the app an option to include these logs into error report.

Problem

I need to remember prior usage events and other error details somewhere. And at some point I thought about using the localStorage for that. But as it is a “key-value” storage, it may be too much effort to store these events sequences per usage session sorted by event time.

What comes next to mind is indexedDb. But can I use it reliable within any of these events? :

  • pagehide
  • visibilitychange
  • beforeunload
  • unload

Fallback options

Due async nature of indexedDb I thought about using the localStorage instead as fallback, and thought about something like this: to store some limited sequence of events for current usage session, like 100 events max, in JSON, as array of arrays, with “row” array for these events saved in queue. if new event arrive and over 100 events queued already, next bunch stored in the another entry of localStorage having same prefix.
Like this:

{
   `_fancy_usage_log_{usage_id}`: { ep: 2 },
   `_fancy_usage_ep_{usage_id}_1`: [
      [...]
      [...]
      .....
      [...]
    ],
   `_fancy_usage_ep_{usage_id}_2`: [
      [...]
      [...]
      .....
      [...]
    ]

}

So, basically, should I use the localStorage or try use indexedDb for such usage events storage, in context of mentioned page lifecycle events such as visibilitychange, pagehide, beforeunload, and unload, if I don’t want update storage such as localStorage on every event, but save them once per minute or so, for sake of performance?

ActiveWin Only Counting VScode Tabs

I have created a code to track active time spent on websites and export it to a csv file called website_time_tracking.csv. When I ran it, it only gave the result of only tracking the amount of time spent on VScode, and when I switched to google, it didn’t track that time:

const activeWin = require('active-win');
const fs = require('fs');
const { createObjectCsvWriter } = require('csv-writer');

// Array to store usage records
const records = [];

// Setup CSV writer
const csvWriter = createObjectCsvWriter({
    path: 'activity_log.csv',
    header: [
        { id: 'start_time', title: 'Start Time' },
        { id: 'end_time', title: 'End Time' },
        { id: 'window_title', title: 'Window Title' },
        { id: 'app_name', title: 'App Name' },
        { id: 'duration_seconds', title: 'Duration (seconds)' }
    ]
});

// Track the previous active window
let previousApp = null;
let previousWindow = null;
let previousStartTime = null;

// Save the interval ID to clear later
const interval = setInterval(trackActiveWindow, 1000);

// Flag to prevent multiple saves
let isSaving = false;

// Function to check active window every second
async function trackActiveWindow() {
    try {
        const activeWindow = await activeWin();

        const now = new Date();

        if (!activeWindow) {
            // No active window detected (maybe locked screen?)
            if (previousApp && previousStartTime) {
                const durationSeconds = (now - previousStartTime) / 1000;

                records.push({
                    start_time: previousStartTime.toISOString(),
                    end_time: now.toISOString(),
                    window_title: previousWindow + ' (screen locked?)',
                    app_name: previousApp,
                    duration_seconds: durationSeconds.toFixed(2)
                });

                console.log(`Logged (locked screen): ${previousApp} - "${previousWindow}" for ${durationSeconds.toFixed(2)} seconds`);

                // Reset previous info
                previousApp = null;
                previousWindow = null;
                previousStartTime = null;
            }
            return;
        }

        const appName = activeWindow.owner.name;
        const windowTitle = activeWindow.title;

        // If we have a different window or app
        if (appName !== previousApp || windowTitle !== previousWindow) {
            // If we had a previous active window, log the duration
            if (previousApp && previousStartTime) {
                const durationSeconds = (now - previousStartTime) / 1000;

                records.push({
                    start_time: previousStartTime.toISOString(),
                    end_time: now.toISOString(),
                    window_title: previousWindow,
                    app_name: previousApp,
                    duration_seconds: durationSeconds.toFixed(2)
                });

                console.log(`Logged: ${previousApp} - "${previousWindow}" for ${durationSeconds.toFixed(2)} seconds`);
            }

            // Update to the new window
            previousApp = appName;
            previousWindow = windowTitle;
            previousStartTime = now;
        }
    } catch (err) {
        console.error('Error tracking window:', err);
    }
}

// Save to CSV when exiting
async function saveOnExit() {
    if (isSaving) return;
    isSaving = true;

    clearInterval(interval);
    await trackActiveWindow(); // Capture the latest window

    console.log('nSaving activity log...');

    try {
        if (records.length > 0) {
            await csvWriter.writeRecords(records);
            console.log('Activity log saved to activity_log.csv');
        } else {
            console.log('No activity recorded.');
        }
    } catch (err) {
        console.error('Error saving activity log:', err);
    } finally {
        process.exit();
    }
}

// Handle process exit
process.on('SIGINT', saveOnExit);
process.on('SIGTERM', saveOnExit);

I’ve tried switching to using the activeWIN, as shown above. But nothing’s worked, and I’m not sure why this is happening. So why is it only counting time spent on VScode?

How to share packages between apps in pnpm monorepo

I’m extremley confused with how monorepos work, I want to build an app with next.js and sanity studio so i have 2 folders

next
studio

both uses some similar packages like

react
react-dom
@types/react
@types/react-dom
@types/node
@typescript

so I dont want each folder to install them independently so i created

pnpm-workspace.yaml

packages:
    - "apps/*"
    - "packages/**/*"

packages/libraries/react/package.json

{
  "name": "@libraries/react",
  "version": "1.0.0",
  "peerDependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "typescript": "^5.3.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0"
  }
}

so now inside my next folder

{
  "name": "next",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "@libraries/react": "workspace:*",
  },
  "devDependencies": {
    "@languages/typescript": "workspace:*",
    "@types/node": "^20"
  }
}

but then i get an error react cannot be found but the app working fine i can access it, am I misunderstanding things wrong ?

switching to full-screen yields different results when using the video button and a programmed equivalent

I have a simple web application to display media files (images as well as videos). By default, the application uses part of the screen for controls and the rest for display of the media files.
One of the possible actions the user may trigger is to switch to full-screen mode. This is implemented by invoking the requestFullscreen() method to the media container div. When this is done, the application switches to full-screen while two divs are shown on top with a reduced set of buttons (e.g. “next”, “prev”) and general info related to the shown media file.

When a video file is displays, the video player includes an equivalent button to switch to full-screen mode (and, of course, another button to return to normal). When this button is clicked, full-screen mode is entered BUT the two additional divs are now invisible. I checked setting the z-index very high but these divs remain hidden.

My question is: What is the difference in between the behavior of programmatically triggering full-screen vs. clicking on the video player button?

Alternatively, is there a way to instruct the media player to hide the full-screen button?

Thanks.

Firebase Auth import not recognizing any Modules

I installed Firebase in my Next Js, and I’ve initiallized it correctly. I’ve imported other firebase like (firestore, strage and app) and they work. yet when i try to import from firebase/auth it shows red under it’s imports (like getAuth, GoogleAuthProvider, …) and the error says “Module ‘”firebase/auth”‘ has no exported member ‘getAuth’.” and this is different for each auth imports.

Here is my config file

import { initializeApp, getApp, getApps } from "firebase/app";
import { getFirestore } from "firebase/firestore/lite";
import { getStorage } from "firebase/storage";
import { getAuth, GoogleAuthProvider, EmailAuthProvider } from "firebase/auth";

const firebaseConfig = {
    apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
    authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
    projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
    storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
    measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
};

const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
const db = getFirestore(app);
const storage = getStorage(app);
const auth = getAuth(app)

const googleProvider = new GoogleAuthProvider();
const emailProvider = new EmailAuthProvider();

export { app, db, storage, auth, googleProvider, emailProvider };

it has no problem with recognizing “firebase/auth” and i even tried clearing the cache and reinstalling. downgrading the version and even restarting my IDE. i even tried changing my package manager from pnpm to npm. what am i doing wrong here?

Issue setting up anime.js v4.0.2

I am trying to learn how to use anime.js for a project of mine, but I am having issues making it run. All the tutorials I find are for older version. I tried following the documation provided on the anime.js site, but its still not working. Here are screenshots showing what my pathway and code looks like so far.My DirectoryHTML fileJS fileCSS FileHere is what my current output looks like, just a static boxOutput

Firebase – user customClaims – admin

im trying to find a way how to add to user Admin role via custom claims.
I tried to do it with user creation cloud function, and onCall function, I dont know if claims are assigned, or not, or how to check where is code failing.

Here is my code: 2 cloud functions, I have tried to give admin role after acc creation and then manually (this function is blocked when called from button click by CORS, no idea what to do)

Any help appreciated

Cloud functions:

export const assignAdminRoleOnUserCreation = functions.auth
    .user()
    .onCreate(async (user) => {
      try {
        if (user.email === "[email protected]") {

          await admin.auth().setCustomUserClaims(user.uid, { admin: true });

          console.log(`Admin role assigned to user ${user.email} (${user.uid}).`);
        } else {
          console.log(`No admin role assigned to user ${user.email}.`);
        }
      } catch (error) {
        console.error(`Error assigning admin role to user ${user.email}:`, error);
      }
    });

  export const manuallyAssignAdmin = onCall(async (request) => {
    const targetEmail = "[email protected]"
  
    try {
      const userRecord = await getAuth().getUserByEmail(targetEmail)
  
      await getAuth().setCustomUserClaims(userRecord.uid, { admin: true })
  
      return { message: `Admin role assigned to ${targetEmail}` }
    } catch (error) {
      console.error("Error assigning admin role:", error)
      throw new Error("Failed to assign admin role")
    }
  })

how i call onCall function at front end:

async function assignAdminManually() {
const assignAdmin = httpsCallable(functions, 'manuallyAssignAdmin')

try {
  const result = await assignAdmin()
  console.log(result.data.message)
  alert('Admin role assigned successfully!')
} catch (error) {
  console.error('Error assigning admin role:', error)
  alert('Failed to assign admin role.')
}

}

How I try to check admin role:

  const isAdmin = async () => {
if (cachedIsAdmin !== null) {
  return cachedIsAdmin; 
}

const auth = getAuth();
const user = auth.currentUser;
console.log(auth)
if (user) {
  try {
    const idTokenResult = await user.getIdTokenResult();

    if (idTokenResult.claims.admin) {
      cachedIsAdmin = true;
    } else {
      cachedIsAdmin = false;
    }
  } catch (error) {
    console.error("Error getting ID token result:", error);
    cachedIsAdmin = false;
  }
} else {
  cachedIsAdmin = false;
}

return cachedIsAdmin;

};

Create a pickup Distance Calculation miles pricing in WordPress

I successfully implemented the code from this previous question How to create pickup Distance Calculation miles pricing in WordPress
What I’m trying to do is add a minimum cost of 40 to show if the calculated cost is below that, but really struggling to get an if statement included, any ideas?

document.addEventListener('DOMContentLoaded', function() {
    let map;
    let directionsService;
    let directionsRenderer;

    function initMap() {
        // Initialize the Google Maps objects
        directionsService = new google.maps.DirectionsService();
        directionsRenderer = new google.maps.DirectionsRenderer();
        map = new google.maps.Map(document.getElementById('map'), {
            center: { lat: -34.397, lng: 150.644 },
            zoom: 8
        });
        directionsRenderer.setMap(map);
    }

    function calculateDistance() {
        const pickup = document.getElementById('pickup').value;
        const destination = document.getElementById('destination').value;

        if (pickup && destination) {
            const request = {
                origin: pickup,
                destination: destination,
                travelMode: 'DRIVING'
            };

            directionsService.route(request, function(result, status) {
                if (status == 'OK') {
                    directionsRenderer.setDirections(result);

                    const distance = result.routes[0].legs[0].distance.value / 1000; // distance in km
                    const cost = distance * 2; // Example: $2 per km

                    document.getElementById('distance').textContent = distance.toFixed(2) + ' km';
                    document.getElementById('total-cost').textContent = '$' + cost.toFixed(2);
                } else {
                    alert('Could not calculate distance: ' + status);
                }
            });
        } else {
            alert('Please enter both pickup and destination locations.');
        }
    }

    document.getElementById('calculate-button').addEventListener('click', calculateDistance);

    // Load the map
    initMap();
}); here

I tried various test on IF and Else but does not work

How to make one element shrink while two other elements stay the same

async function main() {
    const numStudents = Number(await new Modal('annannannannanna', 'info', 'How many students do you have?', 'Cancel', 'Submit').response());
    const numGrades = Number(await new Modal('', 'info', 'How many grades does each student have?', 'Cancel', 'Submit').response());
}

class Modal {
    constructor(bodyText, type = 'info', headerText = null, footerText = 'Close', prompt = null, closeable = true) {
        this.type = type;
        if (headerText === null) {
            switch (this.type) {
                case 'success':
                    this.headerText = 'Success!';
                    break;
                case 'info':
                    this.headerText = 'Information';
                    break;
                case 'warning':
                    this.headerText = 'Warning!'
                    break;
                case 'error':
                    this.headerText = 'An error has occurred';
                    break;
                default:
                    this.headerText = 'Notification';
            }
        } else {
            this.headerText = headerText;
        }
        this.bodyText = bodyText;
        this.footerText = footerText;
        this.closeable = closeable;
        this.prompt = prompt;
        this.create();
        this.open();
    }

    create() {
        this.dialog = document.createElement('dialog');

        const header = document.createElement('header');
        header.classList.add(this.type, 'background');
        if (this.closeable) {
            const closeButton = document.createElement('button');
            closeButton.classList.add('close');
            closeButton.innerText = '×';
            header.appendChild(closeButton);
        }
        const headerText = document.createElement('h3');
        headerText.innerText = this.headerText;
        header.appendChild(headerText);
        this.dialog.appendChild(header);

        const form = document.createElement('form');
        form.method = 'dialog';

        const body = document.createElement('main');
        this.bodyText.split('nn').forEach((paragraph) => {
            const p = document.createElement('p');
            p.innerText = paragraph;
            body.appendChild(p);
        });
        if (this.prompt !== null) {
            this.input = document.createElement('input');
            this.input.placeholder = ' ';
            this.input.autofocus = true;
            const p = document.createElement('p');
            p.appendChild(this.input);
            body.appendChild(p);
        }
        form.appendChild(body);

        const footer = document.createElement('footer');
        footer.classList.add(this.type, 'text');
        const hiddenSubmitButton = document.createElement('button');
        hiddenSubmitButton.value = 'submit';
        hiddenSubmitButton.hidden = true;
        footer.appendChild(hiddenSubmitButton);
        const closeButton = document.createElement('button');
        closeButton.classList.add(this.type, 'text', 'animated');
        closeButton.innerText = this.footerText;
        footer.appendChild(closeButton);
        if (this.prompt === null) {
            closeButton.autofocus = true;
        } else {
            const submitButton = document.createElement('button');
            submitButton.classList.add(this.type, 'background', 'animated');
            submitButton.innerText = this.prompt;
            submitButton.value = 'submit';
            footer.appendChild(submitButton);
        }
        form.appendChild(footer);

        this.dialog.addEventListener('close', (event) => {
            this.close(event.target.returnValue);
        });
        this.dialog.appendChild(form);
        document.body.appendChild(this.dialog);
    }

    open() {
        this.dialog.showModal();
    }

    close(returnValue) {
        if (this.prompt !== null) {
            if (returnValue === '') {
                this.responseValue = null;
                if (this.rejectPromise !== undefined) {
                    this.rejectPromise('User canceled prompt');
                }
            } else {
                this.responseValue = this.input.value;
                if (this.rejectPromise !== undefined) {
                    this.resolvePromise(this.responseValue);
                }
            }
        }
    }

    response() {
        this.promise = new Promise((resolve, reject) => {
            if (this.responseValue !== undefined) {
                if (this.responseValue === null) {
                    reject('User canceled prompt')
                } else {
                    resolve(this.responseValue);
                }
            } else {
                this.resolvePromise = resolve;
                this.rejectPromise = reject;
            }
        });
        return this.promise;
    }
}

main();
:root {
    --error: #c00;
    --error-dark: #900;
    --error-light: #f00;
    --info: #36c;
    --info-dark: #039;
    --info-light: #69f;
    --muted: #ddd;
    --muted-dark: #888;
    --muted-light: #eee;
    --success: #0c0;
    --success-dark: #090;
    --success-light: #0f0;
    --warning: #cc0;
    --warning-dark: #990;
    --warning-light: #ff0;
}

body {
    font-family: Arial, Helvetica, sans-serif;
}

button {
    border: 2px solid;
    border-radius: 10px;
    cursor: pointer;
    margin: 1em 0.5em;
    padding: 10px 15px;
    transition: transform 1s;
}

button:active {
    transform: scale(90%);
}

button.animated {
    background-color: transparent;
    overflow: hidden;
    position: relative;
    transition: color 0.3s, border-color 0.3s, transform 0.2s;
    z-index: 1;
}

button.animated:hover {
    border-color: transparent;
}

button.animated::after {
    border: 0;
    border-radius: 50%;
    content: "";
    height: 200%;
    left: -50%;
    opacity: 0;
    position: absolute;
    transform: scale(0.1);
    transform-origin: center;
    transition: opacity 0.3s, transform 0.3s;
    top: -50%;
    width: 200%;
    z-index: -1;
}

button.animated:hover::after {
    opacity: 1;
    transform: scale(1);
}

input {
    border: 0;
    font: inherit;
    letter-spacing: normal;
    margin: 0;
    padding: 0;
}

input:focus, input:placeholder-shown {
    box-shadow: 0 2px 0 var(--muted);
    outline: none;
}

dialog {
    background-color: white;
    border: 0;
    border-radius: 10px;
    box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
    opacity: 0;
    outline: none;
    padding: 0;
    transform: scale(0);
    transition: all 0.4s allow-discrete;
    width: 50%;
}

dialog:open {
    opacity: 1;
    transform: scale(1);
}

@starting-style {
    dialog:open {
        opacity: 0;
        transform: scale(0);
    }
}

dialog::backdrop {
    background-color: rgba(0, 0, 0, 0);
    transition: all 0.4s allow-discrete;
}

dialog:open::backdrop {
    background-color: rgba(0, 0, 0, 0.4);
}

@starting-style {
    dialog:open::backdrop {
        background-color: rgba(0, 0, 0, 0);
    }
}

dialog header, dialog main, dialog footer {
    display: flow-root;
    padding: 0 1em;
    text-align: center;
}

dialog header {
    background-color: black;
    color: white;
}

dialog main, dialog footer {
    background-color: white;
    color: black;
}

dialog form {
    display: flex;
    flex-direction: column;
}

dialog header, dialog footer {
    flex: initial;
}

dialog main {
    flex: 1 1 auto;
    overflow-y: auto;
}

.close {
    aspect-ratio: 1 / 1;
    background-color: rgba(0, 0, 0, 0);
    border: 0;
    border-radius: 50%;
    box-sizing: border-box;
    color: var(--muted);
    font-size: 1.2em;
    font-weight: bold;
    height: 1.2em;
    margin: 0;
    padding: 0;
    position: absolute;
    right: 0.5rem;
    top: 0.5rem;
    user-select: none;
}

.close:hover,
.close:focus {
    background-color: rgba(255, 255, 255, 0.2);
    color: white;
}

.success.text, .info.text, .warning.text, .error.text, button.animated.success.background::after, button.animated.info.background::after, button.animated.warning.background::after, button.animated.error.background::after {
    background-color: white;
}

.success.background, .info.background, warning.background, error.background, button.animated.success.text:hover, button.animated.info.text:hover, button.animated.warning.text:hover, button.animated.error.text:hover {
    color: white;
}

button.animated.success.text, button.animated.info.text, button.animated.warning.text, button.animated.error.text {
    border-color: var(--muted);
}

.success.text, button.animated.success.background:hover {
    color: var(--success);
}

.success.background, button.animated.success.text::after {
    background-color: var(--success);
}

button.animated.success.text:hover, button.animated.success.background {
    border-color: var(--success);
} 

.info.text, button.animated.info.background:hover {
    color: var(--info);
}

.info.background, button.animated.info.text::after {
    background-color: var(--info);
}

button.animated.info.text:hover, button.animated.info.background {
    border-color: var(--info);
}

.warning.text, button.animated.warning.background:hover {
    color: var(--warning);
}

.warning.background, button.animated.warning.text::after {
    background-color: var(--warning);
}

button.animated.warning.text:hover, button.animated.warning.background {
    border-color: var(--warning);
}

.error.text, button.animated.error.background:hover {
    color: var(--error);
}

.error.background, button.animated.error.text::after {
    background-color: var(--error);
}

button.animated.error.text:hover, button.animated.error.background {
    border-color: var(--error);
}

I have a modal, as you can easily see in the link. When you resize the window until a scroll bar appears, you will see that the scroll bar appears for the entire modal. If you look closely, you can observe that the modal consists of a header (obviously), a main (the body text), and a footer (the button(s) at the bottom). I want the header and footer to act like a header and footer by only having the main portion have a scroll bar. How do I do this?