Need to print a receipt using network printer in a react Web application

I made a restaurant web application. I need to print the billing receipt in react web application using a network printer but when installing necessary libraries /packages it’s not getting installed and the same error is repeating on different libraries. Need to fix this code in front-end. When I print the code the receipt design is getting printed instead the HTML code is getting printed with the tags. And I’m designing it using tailwind css. The code is connected through IP address of the printer but when the print receipt button is clicked it prints the same HTML raw code which is given as text and not printing the receipt design.

I tried packages like node-thermal-printer, npm point-of-sale, npm escpos-network but none of them are installing and showing error as Unsupported engine node version. I tried changing the node versions but still it’s not getting installed. When the print receipt button is clicked it should not show the pop-up page which shows to select the printer type, print color, and etc., instead it should print the receipt directly. This should be done in webpage application.

Implementing Deep Linking for Website-to-App Redirection in React Native

When a user visits certain pages on a website, and they have the app installed, I’d like the app to open instead. I’m not entirely sure how to achieve this, but I assume it involves adding a meta tag or similar functionality to the pages.

For example, if the hosted website is https://example.com/ and a user visits https://example.com/stream, I’d like the XYZ application to open (if installed) and load the stream page within the app.

This functionality needs to be implemented in React Native. While I’m familiar with handling deep linking, this approach seems to require a different method.

Why am I getting a code 400 from Spotify?

I am trying to get the access token from spotify, but it returns a bad request. I don’t understand what could be causing it.

async function getAccessToken(clientId, code) {
    const verifier = localStorage.getItem("verifier");
    const params = new URLSearchParams();
    params.append("client_id", clientId);
    params.append("grant_type", "authorization_code");
    params.append("code", code);
    params.append("redirect_uri", "http://localhost:5173/callback");
    params.append("code_verifier", verifier);

    const result = await fetch("https://accounts.spotify.com/api/token", {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params
    });
    
    
    const { access_token } = await result.json();
    return access_token;
}

the clientId, code, and verifier are all there, so I don’t know what is wrong.

Most cards are not displayed in button headers?

The code is working fine. Data is stored with localstroge. It can include titles and cards. Click on the title to see the cards. The problem is that when I click on the desired title, the same cards appear. I want each title to have its own cards.

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bootstrap Footer</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .flip-card {
            height: 300px;
        }

        .flip-card-inner {
            position: relative;
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            transition: transform 1s;
        }

        .flip-card.flipped .flip-card-inner {
            transform: rotateY(180deg);
        }

        .flip-card-front,
        .flip-card-back {
            position: absolute;
            width: 100%;
            height: 100%;
            backface-visibility: hidden;
        }

        .flip-card-front {
            background-color: #4CAF50;
            color: white;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .flip-card-back {
            background-color: #f44336;
            color: white;
            transform: rotateY(180deg);
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .hidden {
            display: none;
        }
    </style>
</head>

<body class="d-flex flex-column min-vh-100">
    <header class="bg-primary text-white py-3 mb-4">
        <div class="container text-center">
            <h1>My Application</h1>
            <p class="lead">Manage your titles effectively</p>
        </div>
    </header>

    <main class="container my-4">
        <div class="list-group mb-5" id="chatList">
        </div>
        <div id="playCards" class="mb-5 text-center text-success" style="font-size: 1.5rem;">
            <div class="row hidden" id="cards">
            </div>
        </div>
    </main>

    <footer class="bg-dark text-white text-center py-3 mt-auto position-fixed w-100 bottom-0">
        <button type="button" id="footerButton"
            class="btn btn-danger rounded-circle d-block justify-content-center align-items-center shadow-lg"
            data-bs-toggle="modal" data-bs-target="#exampleFooterModal"
            style="width: 60px; height: 60px; top: -30px; left: 50%; transform: translateX(-50%); position: absolute; font-size: 24px; font-weight: bold;">
            +
        </button>
        <button type="button" id="playCardFooterButton"
            class="btn btn-success rounded-circle d-none justify-content-center align-items-center shadow-lg"
            data-bs-toggle="modal" data-bs-target="#exampleFooterCardModal"
            style="width: 60px; height: 60px; top: -30px; left: 50%; transform: translateX(-50%); position: absolute; font-size: 24px; font-weight: bold; display: none;">
            +
        </button>
    </footer>

    <div class="modal fade" id="exampleFooterModal" tabindex="-1" aria-labelledby="exampleFooterModalLabel"
        aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleFooterModalLabel">Add New Title</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form id="titleForm">
                        <div class="mb-3">
                            <label for="titleInput" class="form-label">Enter Title</label>
                            <input type="text" class="form-control" id="titleInput" required>
                        </div>
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <div class="modal fade" id="exampleFooterCardModal" tabindex="-1" aria-labelledby="exampleFooterCardModalLabel"
        aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleFooterCardModalLabel">Add New Title</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form id="titleCardForm">
                        <div class="mb-3">
                            <label for="titleInputFront" class="form-label">Enter Title</label>
                            <input type="text" class="form-control" id="titleInputFront" required>
                        </div>
                        <div class="mb-3">
                            <label for="titleInputBack" class="form-label">Enter Title</label>
                            <input type="text" class="form-control" id="titleInputBack" required>
                        </div>
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </form>
                </div>
            </div>
        </div>
    </div>

    <div class="modal fade" id="contextModal" tabindex="-1" aria-labelledby="contextModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="contextModalLabel">Item Actions</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <button id="editBtn" class="btn btn-warning w-100 mb-2">Edit</button>
                    <button id="deleteBtn" class="btn btn-danger w-100">Delete</button>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        const titleForm = document.getElementById('titleForm');
        const titleCardForm = document.getElementById('titleCardForm');
        const titleInput = document.getElementById('titleInput');
        const titleCardFront = document.getElementById('titleCardFront');
        const titleCardBack = document.getElementById('titleCardBack');
        const titleInputFront = document.getElementById('titleInputFront');
        const titleInputBack = document.getElementById('titleInputBack');
        const cards = document.getElementById('cards');
        const chatList = document.getElementById('chatList');
        const playCards = document.getElementById('playCards');
        const footerButton = document.getElementById('footerButton');
        const playCardFooterButton = document.getElementById('playCardFooterButton');
        let currentTitleElement = null;
        let currentTitleCardElement = null;
        
        

        document.addEventListener('DOMContentLoaded', function () {
            const savedTitles = JSON.parse(localStorage.getItem('titles')) || [];
            savedTitles.forEach(title => addTitleToList(title));

            const savedCardFronts = JSON.parse(localStorage.getItem('cardFronts')) || [];
            const savedCardBacks = JSON.parse(localStorage.getItem('cardBacks')) || [];

            for (let i = 0; i < savedCardFronts.length; i++) {
                addCardToList(savedCardFronts[i], savedCardBacks[i]);
            }
        });


        titleForm.addEventListener('submit', function (event) {
            event.preventDefault();
            const title = titleInput.value.trim();
            if (title) {
                addTitleToList(title);

                // Save to localStorage
                const savedTitles = JSON.parse(localStorage.getItem('titles')) || [];
                savedTitles.push(title);
                localStorage.setItem('titles', JSON.stringify(savedTitles));

                titleInput.value = '';
                const modal = bootstrap.Modal.getInstance(document.getElementById('exampleFooterModal'));
                modal.hide();
            }
        });
        titleCardForm.addEventListener('submit', function (event) {
            event.preventDefault();
            const inputFront = titleInputFront.value.trim();
            const inputBack = titleInputBack.value.trim();
            if (inputFront && inputBack) {
                addCardToList(inputFront, inputBack);

                const savedCardFronts = JSON.parse(localStorage.getItem('cardFronts')) || [];
                savedCardFronts.push(inputFront);
                localStorage.setItem('cardFronts', JSON.stringify(savedCardFronts));

                const savedCardBacks = JSON.parse(localStorage.getItem('cardBacks')) || [];
                savedCardBacks.push(inputBack);
                localStorage.setItem('cardBacks', JSON.stringify(savedCardBacks));


                titleInputFront.value = ''
                titleInputBack.value = ''
                const modal = bootstrap.Modal.getInstance(document.getElementById('exampleFooterCardModal'));
                modal.hide();
            }
        });

        function addTitleToList(title) {
            const newItem = document.createElement('button');
            newItem.className = 'list-group-item d-flex justify-content-between align-items-center';
            newItem.innerHTML = `<h5 class="mb-1">${title}</h5>`;
            cards.classList.add('hidden'); 
            
            newItem.addEventListener('contextmenu', function (event) {
                event.preventDefault();
                currentTitleElement = newItem;
                const contextModal = new bootstrap.Modal(document.getElementById('contextModal'));
                contextModal.show();
            });
            newItem.addEventListener('click', function (event) {
                event.preventDefault();
                cards.classList.toggle('hidden');
                chatList.style.display = 'none';
                playCardFooterButton.className = "btn btn-success rounded-circle d-block justify-content-center align-items-center shadow-lg";
            });
            chatList.appendChild(newItem);
        }
        function addCardToList(inputFront, inputBack) {
            const newCard = document.createElement('div')
            newCard.className = 'col-12 col-md-6 col-lg-4'
            newCard.innerHTML = `
            <div class="flip-card mb-3" onclick="flipCard(this)">
                <div class="flip-card-inner">
                    <div class="flip-card-front">
                        ${inputFront}
                    </div>
                    <div class="flip-card-back">
                        ${inputBack}
                    </div>
                </div>
            </div>`
            newCard.addEventListener('contextmenu', function (event) {
                event.preventDefault();
                currentTitleCardElement = newCard;
                const contextModal = new bootstrap.Modal(document.getElementById('contextModal'));
                contextModal.show();
            });
            cards.appendChild(newCard);
        }


        deleteBtn.addEventListener('click', function () {
            if (currentTitleElement || currentTitleCardElement) {
                let index;
                if (currentTitleElement) {
                    // 'titles' elementni o'chirish
                    const titleText = currentTitleElement.querySelector('h5').textContent;
                    const savedTitles = JSON.parse(localStorage.getItem('titles')) || [];
                    const updatedTitles = savedTitles.filter(title => title !== titleText);
                    localStorage.setItem('titles', JSON.stringify(updatedTitles));
                    currentTitleElement.remove();
                    currentTitleElement = null;
                } else if (currentTitleCardElement) {
                    // 'cardFronts' va 'cardBacks' elementni o'chirish
                    index = Array.from(cards.children).indexOf(currentTitleCardElement);
                    currentTitleCardElement.remove();

                    const savedCardFronts = JSON.parse(localStorage.getItem('cardFronts')) || [];
                    const savedCardBacks = JSON.parse(localStorage.getItem('cardBacks')) || [];

                    if (index >= 0) {
                        savedCardFronts.splice(index, 1);
                        savedCardBacks.splice(index, 1);
                        localStorage.setItem('cardFronts', JSON.stringify(savedCardFronts));
                        localStorage.setItem('cardBacks', JSON.stringify(savedCardBacks));
                    }
                }

                // Modalni yopish
                const modal = bootstrap.Modal.getInstance(document.getElementById('contextModal'));
                modal.hide();
            }
        });


        document.getElementById('editBtn').addEventListener('click', function () {
            if (currentTitleElement || currentTitleCardElement) {
                if (currentTitleElement) {
                    // 'titles'ni tahrirlash
                    const titleText = currentTitleElement.querySelector('h5').textContent;
                    const newValue = prompt("Yangi nomni kiriting:", titleText);

                    if (newValue && newValue.trim() !== "") {
                        // LocalStorage'ni yangilash
                        const savedTitles = JSON.parse(localStorage.getItem('titles')) || [];
                        const index = savedTitles.indexOf(titleText);
                        if (index >= 0) {
                            savedTitles[index] = newValue;
                            localStorage.setItem('titles', JSON.stringify(savedTitles));

                            // DOMni real vaqt rejimida yangilash
                            currentTitleElement.querySelector('h5').textContent = newValue;
                        }
                    }
                } else if (currentTitleCardElement) {
                    const frontText = currentTitleCardElement.querySelector('.flip-card-front').textContent.trim();
                    const backText = currentTitleCardElement.querySelector('.flip-card-back').textContent.trim();
                    const newFrontValue = prompt('1', frontText)
                    const newBackValue = prompt('2', backText)

                    if (newFrontValue && newFrontValue.trim() !== "" && newBackValue && newBackValue.trim() !== "") {
                        const savedCardFronts = JSON.parse(localStorage.getItem('cardFronts')) || [];
                        const indexFronts = savedCardFronts.indexOf(frontText);
                        const savedCardBacks = JSON.parse(localStorage.getItem('cardBacks')) || [];
                        const indexBacks = savedCardBacks.indexOf(backText);
                        if (indexFronts >= 0 || indexBacks >= 0) {
                            savedCardFronts[indexFronts] = newFrontValue;
                            savedCardBacks[indexBacks] = newBackValue;
                            localStorage.setItem('cardFronts', JSON.stringify(savedCardFronts))
                            localStorage.setItem('cardBacks', JSON.stringify(savedCardBacks))

                            currentTitleCardElement.querySelector('.flip-card-front').textContent = newFrontValue;
                            currentTitleCardElement.querySelector('.flip-card-back').textContent = newBackValue;
                        }
                    }
                }

                // Modalni yopish
                const modal = bootstrap.Modal.getInstance(document.getElementById('contextModal'));
                modal.hide();
            }
        });

        
        function flipCard(card) {
            card.classList.toggle('flipped');
        }
    </script>
</body>

</html>

I can’t figure out what the problem is. I asked for help from several places but to no avail.

Client can send but not receive. (server: Flask SocketIO python) (client: socket.io.min.js)

I’ve got about 100 lines of code that require some expertise. I’m working on a classic client-server structure using flask and websockets. The communication from the client to the server works fine, but the communication from the server to the client doesn’t work at all.

My client is pretty straightforward:

    var socket = io({
        transports: ['websocket'],
        pingInterval: 25000, // ping every 25 sec
        pingTimeout: 10000   // timeout 10 sec
    });

    socket.on('connect', function () 
    {
        console.log('Connected')
        socket.emit('pin:8989')

    });

    socket.on('update', function (data) {
        print(data)
        const json_data = JSON.parse(data);
        Object.keys(json_data).forEach(key => {
            const update = json_data[key];
            if (update.key) document.getElementById(update.key).innerHTML = update.value;});});

    function send(request) {
        if (request) {
            socket.emit('request', request);
            console.log('Sent:', request);}
        }

It should connect, receive and process JSON, and be able to send requests.

The server is a bit more juicy:

# Interface Terminal
from threading import Thread, Event
from flask import Flask, request, render_template
from flask_socketio import SocketIO
from abc import abstractmethod
from system.error_handler import err_terminal
import datacore
import time

flask = Flask('undefined') # flask webserver (HTTP)
socket = SocketIO(flask, cors_allowed_origins="*") # flask socket io (TCP)

class Terminal():
    def __init__(self, name, host, port, templates, statics):
        self.core = datacore.Cores.TERMINAL
        flask.terminal_instance = self  # store this instance in the app
        flask.name = name  # application name
        flask.template_folder = templates  # path to templates folder (index > .html)
        flask.static_folder = statics      # path to static files folder (.js .css .png ...)
        self.flask_thread = Thread(target=socket.run, args=(flask, host, port))
        self.feeding_thread = Thread(target=self.feed)
        self.stop_feeding = Event()             # event to signal stop feeding clients
        
    #run app
    def run(self):  
        try:
            self.flask_thread.start()
            self.feeding_thread.start()
        except Exception as e:
            err_terminal('Fail to run', e)

    #stop app
    def stop(self):
        try:
            self.stop_feeding.set()  # signal the feed task to stop
            self.feeding_thread.join()
            socket.stop()  # stop flask
        except Exception as e:
            err_terminal('Fail to stop', e)

    # send data
    def send(self, data, event='update'): 
        try:
            socket.emit(event, data)  # send data to all connected clients
        except Exception as e:
            err_terminal('Fail to send', e)

    # feed data (task: periodically emit to clients)
    def feed(self):
        while not self.stop_feeding.is_set():
            package = {}                # empty package
            self.on_packJSON(package)   # populate the package
            self.send(package)          # send package
            time.sleep(1)               # loop once a sec
    
    # hooks for the subclass
    @abstractmethod
    def on_packJSON(self, package): pass    # hook to packing data   
    @abstractmethod
    def on_receive(self, request): pass     # hook to incoming request (unpacking)
    @abstractmethod
    def on_connect(self, address): pass     # hook to client connection
    @abstractmethod
    def on_disconnect(self, address): pass  # hook to client disconnection

# flask handlers
@flask.route('/')           # render index.html
def index(): return render_template('index.html', title=flask.name)
@socket.on('request')       # incoming request
def receive(request): flask.terminal_instance.on_receive(request)
@socket.on('connect')       # client connection
def connect(): flask.terminal_instance.on_connect(request.remote_addr)
@socket.on('disconnect')    # client disconnection
def disconnect(): flask.terminal_instance.on_disconnect(request.remote_addr)
@socket.on('error')         # client error
def error(err): err_terminal('Client error', err)

I’m using Flask as webserver and websocket server. Class Terminal hosts two threads: the first one runs flask, while the second one runs a feeding stream to clients. Packing data to send, unpacking receiving data, and monitor clients is delegated to a sub class.

Clients can connect to the server and send data, but it didn’t matter what I tried, clients won’t receive anything back. I tested it extensively in a local net but nothing. If I stop sending requests from a client, it enters in a disconnect/connect loop.

>>> 05:22 192.168.158.127 connected
<<< 05:22 192.168.158.127 disconnected
>>> 05:23 192.168.158.127 connected
<<< 05:23 192.168.158.127 disconnected
>>> 05:23 192.168.158.127 connected

I’m assuming is because it’s not receiving anything from the server, but I’m not even sure it is related (it behave like that even without setting a timeout). I tried to monitor the client with something like this to catch all possible events:

socket.onAny((event, ...args) => {console.log(event,args);});

But it doesn’t even catch the ‘connect’ one. I feel the problem might lay in the way I’m dealing with events to handle incoming data, as the other channel (client to server) works perfectly, but it could also be my setup.

Ok, I’m out of bullets for this one. Any good insight or suggestion to be back on track to solve it?

How to insert image to place in cell using ExcelJS

here the code

      const img = new Image();
      img.src = reportTemplate.logo ? reportTemplate.logo : defaultLogo;
      const logoSize = 45;
      const imgHeight = img.width * (logoSize / img.height);

      const logo = workbook.addImage({
        base64: img.src,
        extension: 'png',
      });
      
      worksheet.addImage(logo, {
        tl: { col: tableLength - 1, row: 0 }, 
        ext: { width: imgHeight, height: logoSize},
        editAs:'oneCell',
      });

expected result here

So, how to set image to place in cell as expected result?

req.user is undefined in passport.js/express/mongodb application

I have implemented a basic passport.js authentication on top of an expressJS/node application. The user auth is working fine, but I would like to be able to store specific additional data for each user, so that the frontend will appear differently depending on the specific data for each user (saving/deleting favorite images to each user’s account). However, I cannot access req.user or req.passport at any stage in my routing.

I’ve seen similar questions about req.user being undefined but not one specifically for this scenario. Below is the relevant code. Please let me know where I am going wrong. Thanks in advance!

server.js:

import "dotenv/config";
import express from "express";
import session from "express-session";
import cors from "cors";
import { dirname } from "path";
import path from "path";
import { fileURLToPath } from "url";
import homeRoute from "./backend/routes/home_route.js";
import API_route from "./backend/routes/api_route.js";
import userRoute from "./backend/routes/user_route.js"
import User from "./backend/models/user.js"
import passport from "passport";  
import LocalStrategy from "passport-local";
import mongoose from "mongoose";

const PORT = process.env.PORT || 5050;
const app = express();
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// connect DB
mongoose.connect("mongodb://localhost/5000");

// middlewares
app.use(cors({
  credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(session({
  secret: "testUser",
  resave: false,
  saveUnitialized: false,
  cookie: { secure: true }
}))
app.use(passport.initialize());
app.use(passport.session());

passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

// routing
app.use("/", homeRoute);
app.use("/api", API_route);
app.use("/user", userRoute);

//public
app.use(express.static(path.join(__dirname, "/frontend")));

//views
app.set("views", path.join(__dirname, "frontend/views"));
app.set("view engine", "ejs");

// start the express server
app.listen(PORT, () => {
  console.log(`server listening on port ${PORT}`);
});

user.js:

import mongoose from "mongoose";
import { Schema } from "mongoose";
import PassportLocalMongoose from "passport-local-mongoose";

let newUser = new Schema({
    username: {
        type: String
    },
    password: {
        type: String
    },
    favorites: {
        type: Object
    }
});

newUser.plugin(PassportLocalMongoose);

const User = mongoose.model("User", newUser);

export default User;

user_route.js:

import express from "express";
import User from "../models/user.js";

const router = express.Router();

// show signup page
router.get("/register", (req, res) => {
  res.render("./user/register");
});

// handle user signup
router.post("/register", async (req, res) => {
  const user = await User.create({
    username: req.body.username,
    password: req.body.password,
    testString: 'test'
  });
  res.redirect('../../');
});

// show login
router.get("/login", (req, res)=> {
  res.render("./user/login");
});

// handle user login
router.post("/login", async (req, res)=> {
  try {
    const user = await User.findOne( {username: req.body.username})
    if (user) {
      const result = req.body.password === user.password;
      if (result) {
        res.render("./user/secret");
        console.log("user data:", req.user)
      } else {
        res.status(400).json({error: "password doesn't match"});
      }
    } else {
      res.status(400).json({error: "user doesn't exist"});
    }
  } catch (error) {
    res.status(400).json({error});
  };
});

// successful login
router.get("/secret", isLoggedIn, (req, res)=> {
  res.render("secret");
});

// handle user logout
router.get("/logout", (req, res)=> {
  req.logout(function(err) {
    if (err) return next(err);
    res.redirect('../../');
  });
});

// check if logged in
function isLoggedIn(req, res, next) {
  if (req.isAuthenticated()) return next();
  res.redirect("./user/login");
};

export default router;

Sending image created from canvas as binary data

I have image data and I want to send this to API as binary.

var formData = new FormData();
formData.append("data",{"test":1});
//make jpg image from canvas.        
var image = ref_canvas_release.current.toDataURL('image/jpeg', 0.85);

//////// confirm and test the image file is correctly created. it created and downloaded successfully.
var a = document.createElement('a');
a.href = image;
a.download = 'test.jpg';
a.click();
////////

formData.append("file",image);

axios.post("/myuploader",formData,{headers: {'Content-Type': 'application/form-data'}}).then(res=>{
    console.log(res)
});

In this case, payload (on googld chrome developper console) shows like this,

enter image description here

The file is sent like this below.

file: data:image/jpeg;base64, /9j/.....

However I want to send the data as binary,
(if so payload screen is supposed to be)

file: binary

How can I change the format to send the API?

Why don’t the ball and platform side collisions work in a JavaScript game?

I am creating a simple breakout game. The ball should bounce when it collides with the platform. The top and bottom collisions work, but the left and right collisions don’t work.

I am creating a 2D game with a ball and a platform in JavaScript. The goal is to make the ball bounce off the platform when it collides. The top and bottom collisions work fine, but the left and right collisions fail. I’m using a simple function to check for intersecting rectangles.

Here are the key details:

  1. The ball moves at velocity velocityX and velocityY.
  2. Collisions are detected through the detectCollision function.
  3. For side collisions I added additional conditions (leftCollision and rightCollision), but they never trigger.
let board;
let boardWidth = 200;
let boardHeight = 200;
let context;

let playerWidth = 80;
let playerHeight = 10;
let playerVelocity = 10;
let player = {
    x : boardWidth / 2 -  playerWidth / 2,
    y : boardHeight - playerHeight - 5,
    width : playerWidth,
    height : playerHeight,
    velocity : playerVelocity
};

let ballWidth = 10;
let ballHeight = 10;
let ballVelocityX = -2;
let ballVelocityY = 1;
let ball = {
    x : boardWidth / 2,
    y : boardHeight / 2,
    width : ballWidth,
    height : ballHeight,
    velocityX : ballVelocityX,
    velocityY : ballVelocityY,
}

function outOfBounds(xPosition) {
    return (xPosition < 0 || xPosition + player.width > boardWidth);
}

function playerMove(event) {
    let mouseX = event.clientX - board.offsetLeft;
    let nextPlayerX = mouseX - player.width / 2;

    if (!outOfBounds(nextPlayerX)) {
        player.x = nextPlayerX;
    }
}

// Обнаружение колизии
function detectCollision(rectA, rectB) {
    return (
        rectA.x < rectB.x + rectB.width &&
        rectA.x + rectA.width > rectB.x &&
        rectA.y < rectB.y + rectB.height &&
        rectA.y + rectA.height > rectB.y
    );
}


function topCollision(ball, block) {
    return detectCollision(ball, block) && (ball.y + ball.height) >= block.y;
}

function bottomCollision(ball, block) {
    return detectCollision(ball, block) && ball.y <= (block.y + block.height);
}

function leftCollision(ball, block) {
    return detectCollision(ball, block) && (ball.x + ball.width) >= block.x;
}

function rightCollision(ball, block) {
    return detectCollision(ball, block) && ball.x <= (block.x + block.width);
}

function update() {
    requestAnimationFrame(update);
    context.clearRect(0, 0, boardWidth, boardHeight);

    // игрок
    context.fillStyle = "yellow";
    context.fillRect(player.x, player.y, player.width, player.height);

    // мяч
    context.fillStyle = "white";
    ball.x += ballVelocityX;
    ball.y += ballVelocityY;
    context.fillRect(ball.x, ball.y, ball.width, ball.height);

    // отскок мяча
    if (ball.y <= 0) {
        ballVelocityY *= -1;
    } else if (ball.x <= 0 || (ball.x + ball.width) >= boardWidth) {
        ballVelocityX *= -1;
    } else if (ball.y + ball.height >= boardHeight) {
        // game over
    }

    if (topCollision(ball, player) || bottomCollision(ball, player)) {
        ballVelocityY *= -1;
    } else if (rightCollision(ball, player) || leftCollision(ball, player)) {
        ballVelocityX *= -1;
    }
}

// Функция отрисовки
window.onload = function () {
    board = document.getElementById("game-canvas");
    board.height = boardHeight;
    board.width = boardWidth;
    context = board.getContext("2d");

    context.fillStyle = "yellow";
    context.fillRect(player.x, player.y, player.width, player.height);

    requestAnimationFrame(update);
    document.addEventListener("mousemove", playerMove)
};
body {
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background: #000;
    color: #fff;
    font-family: Arial, sans-serif;
    overflow: hidden;
}
canvas {
    border: 2px solid #fff;
    background-color: #111;
}
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
            content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    <link rel="stylesheet" href="/style.css">
    <script src="/script.js" defer></script>

    <title>breakout</title>
</head>
<body>
    <canvas id="game-canvas"></canvas>
</body>
</html>

Problem:

The leftCollision and rightCollision collisions never work, even if the ball is clearly touching the sides of the platform.
The top and bottom collisions work without problems.

Scroll reveal text animation

I’m working on a webpage where I want text to behave in a specific way when it overflows its container: instead of breaking in the middle of a word, the entire word should move to the next line.

Here’s what I’ve done so far:

I added the following CSS properties to control word breaking:

word-break: keep-all;
overflow-wrap: normal;
word-wrap: break-word;
-webkit-hyphens: auto;
hyphens: auto;

Here’s the full snippet of my code, including JavaScript for animation and the HTML structure:

class Lenis {
  constructor(options) {
    this.options = options;
    this.currentScroll = 0;
  }

  raf(time) {
    // Dummy implementation for smooth scroll effect
    this.currentScroll += (window.scrollY - this.currentScroll) * this.options.easing(0.1);
  }

  destroy() {
    // Clean-up if necessary
  }
}

function splitText(element) {
  const text = element.textContent;
  element.textContent = "";
  const wrapper = document.createElement("div");
  wrapper.style.display = "inline-block";
  wrapper.style.width = "100%";

  const spans = [...text].map((char) => {
    const span = document.createElement("span");
    span.textContent = char === " " ? "u00A0" : char;
    span.style.display = "inline-block";
    wrapper.appendChild(span);
    return span;
  });

  element.appendChild(wrapper);
  return spans;
}

document.addEventListener("DOMContentLoaded", () => {
  // Initialize Lenis for smooth scrolling
  const lenis = new Lenis({
    duration: 1.2,
    easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
    direction: "vertical",
    gestureDirection: "vertical",
    smooth: true,
    smoothTouch: false,
    touchMultiplier: 2,
  });

  function raf(time) {
    lenis.raf(time);
    requestAnimationFrame(raf);
  }

  requestAnimationFrame(raf);

  window.addEventListener("unload", () => lenis.destroy());

  // Reveal text animation
  const revealElements = document.querySelectorAll(".reveal-type");

  revealElements.forEach((element) => {
    const chars = splitText(element);
    const bgColor = element.dataset.bgColor;
    const fgColor = element.dataset.fgColor;

    chars.forEach((char) => {
      char.style.color = bgColor;
    });

    const animateOnScroll = () => {
      const rect = element.getBoundingClientRect();
      const windowHeight = window.innerHeight;
      const elementHeight = rect.height;

      const visibleHeight = windowHeight - rect.top;
      const progressLength = elementHeight * 1.2;

      if (visibleHeight <= 0) return;

      let progress = visibleHeight / progressLength;
      progress = Math.max(0, Math.min(1, progress));

      const charsToColor = Math.floor(progress * chars.length);

      chars.forEach((char, index) => {
        if (index < charsToColor) {
          char.style.color = fgColor;
        } else {
          char.style.color = bgColor;
        }
      });
    };

    animateOnScroll();
    window.addEventListener("scroll", animateOnScroll);

    window.addEventListener("unload", () => {
      window.removeEventListener("scroll", animateOnScroll);
    });
  });
});
body {
  margin: 0;
  font-family: "Inter", sans-serif;
  background: rgb(5, 4, 4);
}

section {
  min-height: auto;
  margin: 2rem auto;
  padding: 2rem clamp(1rem, 5vw, 4rem);
  display: grid;
  place-content: center;
  box-sizing: border-box;
}
section p {
  font-size: clamp(1.2rem, 5vw, 9rem);
  word-break: keep-all;
  overflow-wrap: normal;
  word-wrap: break-word;
  -webkit-hyphens: auto;
          hyphens: auto;
}
section:nth-of-type(1) {
  border: 1px solid black;
  height: auto;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: flex-start;
}
section:nth-of-type(1) img {
  max-width: 100%;
  height: auto;
  -o-object-fit: cover;
     object-fit: cover;
}
section:nth-of-type(2) {
  max-width: 100%;
  margin: 0 auto;
  transform: translateX(0);
}/*# sourceMappingURL=main.css.map */
  <section>
    <img src="example" alt="Thumbnail">
  </section>
  <section>
    <p 
      class="reveal-type" 
      data-bg-color="#232323" 
      data-fg-color="white"
    >
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
    </p>
  </section>
  <section></section>

  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lenis.min.js"></script>

As you can see, i already add

      word-break: keep-all;
  overflow-wrap: normal;
  word-wrap: break-word;
  -webkit-hyphens: auto;
          hyphens: auto;

The Problem

Despite adding these CSS properties, the words are still breaking in the middle and wrapping to the next line. I expected the whole word to move to the next line if it doesn’t fit

What I Tried

1. Using word-break: keep-all, which, as I understand, should prevent breaking words in the middle.

2. Combining it with overflow-wrap: normal and word-wrap: break-word, thinking it would work for all browsers.

3. Adding hyphens: auto to handle hyphenation gracefully.
However, the behavior is still not what I expect. Words keep breaking mid-word.

My Environment

Browser: Arc or edge

Tools: React 19.0.0

Monaco Editor Custom Language Autocompletion Not Triggering (Tokenizer Issue? Syntax Highlighting Mostly Works)

I’m creating a custom language for Monaco Editor and having trouble with autocompletion. The provideCompletionItems function in my completion provider doesn’t seem to be called at all. I’ve registered the language and the provider, but nothing happens when I type. I suspect there might be an issue with my tokenizer.

Interestingly, syntax highlighting is working for keywords, types, constants, and operators, but not for built-in functions.

Here’s my language definition (CodeFrLanguage.js):

import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';

// Define keywords and tokens (keeping your existing definitions)
const keywords = [
  'Algorithme', 'Debut', 'Fin',
  'Variable', 'Variables', 'Constante',
  'Si', 'Alors', 'SinonSi', 'Sinon', 'FinSi',
  'TantQue', 'Faire', 'FinTantQue',
  'Pour', 'De', 'A', 'FinPour',
  'Lire', 'Ecrire',
  'Et', 'Ou', 'Non', 'Oux',
  'Mod', 'Tableau'
];

const typeKeywords = [
  'Entier', 'Decimal', 'Chaine', 'Caractere', 'Logique'
];

const constants = [
  'Vrai', 'Faux'
];

const builtins = [
  'Racine', 'Abs', 'Log', 'Log10', 'Arrondi'
];

const operators = [
  '=', '>', '<', '!', '~', '?', ':',
  '==', '<=', '>=', '!=', '+', '-', '*', '/',
  '^', 'Mod', 'Et', 'Ou', 'Non', 'Oux'
];

// Create completion items
const createCompletionItem = (label, kind, insertText, documentation = '') => ({
  label,
  kind,
  insertText,
  documentation: { value: documentation },
  insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
});

// Register language
export const registerCodeFrLanguage = () => {
  // Register the language first
  monaco.languages.register({ id: 'codefr' });

  // Set the tokens provider
  monaco.languages.setMonarchTokensProvider('codefr', {
    ignoreCase: false,
    defaultToken: '',
    tokenPostfix: '.codefr',
    keywords,
    typeKeywords,
    constants,
    builtins,
    operators,
    symbols: /[=><!~?:&|+-*/^%]+/,
    
    tokenizer: {
      root: [
        [/^(Algorithme)(s+)([a-zA-Z_]w*)/, ['keyword', 'white', 'identifier']],
        [/^(Debut|Fin)/, 'keyword'],
        [/[a-zA-Z_]w*/, {
          cases: {
            '@keywords': 'keyword',
            '@typeKeywords': 'type',
            '@constants': 'constant',
            '@builtins': 'function',
            '@default': 'identifier'
          }
        }],
        { include: '@whitespace' },
        [/[{}()[]]/, '@brackets'],
        [/[<>](?!@symbols)/, '@brackets'],
        [/@symbols/, {
          cases: {
            '@operators': 'operator',
            '@default': ''
          }
        }],
        [/d*.d+([eE][-+]?d+)?/, 'number.float'],
        [/d+/, 'number'],
        [/"([^"\]|\.)*$/, 'string.invalid'],
        [/"/, { token: 'string.quote', bracket: '@open', next: '@string' }],
      ],
      string: [
        [/[^\"]+/, 'string'],
        [/"/, { token: 'string.quote', bracket: '@close', next: '@pop' }]
      ],
      whitespace: [
        [/[ trn]+/, 'white'],
        [//*/, 'comment', '@comment'],
        [///.*$/, 'comment'],
      ],
      comment: [
        [/[^/*]+/, 'comment'],
        [//*/, 'comment', '@push'],
        ["\*/", 'comment', '@pop'],
        [/[/*]/, 'comment']
      ],
    }
  });

  // Register completion item provider
  monaco.languages.registerCompletionItemProvider('codefr', {
    provideCompletionItems: (model, position) => {
      const word = model.getWordUntilPosition(position);
      const range = {
        startLineNumber: position.lineNumber,
        endLineNumber: position.lineNumber,
        startColumn: word.startColumn,
        endColumn: word.endColumn
      };

      const suggestions = [
        // Keywords
        ...keywords.map(keyword => 
          createCompletionItem(keyword, monaco.languages.CompletionItemKind.Keyword, keyword)
        ),
        // Types
        ...typeKeywords.map(type => 
          createCompletionItem(type, monaco.languages.CompletionItemKind.Class, type)
        ),
        // Constants
        ...constants.map(constant => 
          createCompletionItem(constant, monaco.languages.CompletionItemKind.Constant, constant)
        ),
        // Built-in functions
        ...builtins.map(func => 
          createCompletionItem(func, monaco.languages.CompletionItemKind.Function, func)
        ),
        // Snippets
        createCompletionItem(
          'Algorithme',
          monaco.languages.CompletionItemKind.Snippet,
          'Algorithme ${1:nom}nVariablesnt${2}nDebutnt${3}nFin',
          'Structure de base d'un algorithme'
        ),
        createCompletionItem(
          'Si',
          monaco.languages.CompletionItemKind.Snippet,
          'Si ${1:condition} Alorsnt${2}nFinSi',
          'Structure conditionnelle Si'
        ),
        createCompletionItem(
          'Pour',
          monaco.languages.CompletionItemKind.Snippet,
          'Pour ${1:i} De ${2:debut} A ${3:fin} Fairent${4}nFinPour',
          'Boucle Pour'
        ),
        createCompletionItem(
          'TantQue',
          monaco.languages.CompletionItemKind.Snippet,
          'TantQue ${1:condition} Fairent${2}nFinTantQue',
          'Boucle TantQue'
        )
      ];

      return {
        suggestions: suggestions.map(s => ({
          ...s,
          range: range
        }))
      };
    },
    triggerCharacters: [' ', 'n', ':', '.', '(', '[', 'A']
  });

  // Register language configuration
  monaco.languages.setLanguageConfiguration('codefr', {
    comments: {
      lineComment: '//',
      blockComment: ['/*', '*/']
    },
    brackets: [
      ['{', '}'],
      ['[', ']'],
      ['(', ')']
    ],
    autoClosingPairs: [
      { open: '{', close: '}' },
      { open: '[', close: ']' },
      { open: '(', close: ')' },
      { open: '"', close: '"', notIn: ['string'] },
      { open: "'", close: "'", notIn: ['string', 'comment'] }
    ],
    surroundingPairs: [
      { open: '{', close: '}' },
      { open: '[', close: ']' },
      { open: '(', close: ')' },
      { open: '"', close: '"' },
      { open: "'", close: "'" }
    ],
  });
};

And here’s how I’m creating the editor in my React component (MonacoEditor.js):

import React, { useRef, useEffect } from 'react';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import { useTheme } from '../../contexts/ThemeContext';
import { Box } from '@mui/material';
import { registerCodeFrLanguage } from './CodeFrLanguage';

const MonacoEditor = ({ value, onChange, readOnly = false }) => {
  const editorRef = useRef(null);
  const containerRef = useRef(null);
  const { mode } = useTheme();

  useEffect(() => {
    // Register language
    registerCodeFrLanguage();

    if (containerRef.current && !editorRef.current) {
      // Create editor instance
      editorRef.current = monaco.editor.create(containerRef.current, {
        value: value || 'Algorithme Nom_AlgorithmenVariablesntVariable1: Type1nDebutntInstruction1nFin',
        language: 'codefr',
        theme: mode === 'dark' ? 'vs-dark' : 'vs',
        minimap: { enabled: true },
        fontSize: 14,
        automaticLayout: true,
        lineNumbers: 'on',
        readOnly: readOnly,
        tabSize: 2,
        wordWrap: 'on',
        quickSuggestions: {
          other: true,
          comments: true,
          strings: true
        },
        suggestOnTriggerCharacters: true,
        acceptSuggestionOnEnter: 'on',
        tabCompletion: 'on',
        wordBasedSuggestions: true,
        snippetSuggestions: 'top',
        suggest: {
          localityBonus: true,
          snippetsPreventQuickSuggestions: false,
          showIcons: true,
          maxVisibleSuggestions: 12,
          filterGraceful: true,
          showInlineDetails: true,
          preview: true
        }
      });

      // Handle changes
      editorRef.current.onDidChangeModelContent(() => {
        if (onChange) {
          onChange(editorRef.current.getValue());
        }
      });

      return () => {
        if (editorRef.current) {
          editorRef.current.dispose();
          editorRef.current = null;
        }
      };
    }
  }, []);

  // Update value when prop changes
  useEffect(() => {
    if (editorRef.current && value !== undefined && value !== editorRef.current.getValue()) {
      editorRef.current.setValue(value);
    }
  }, [value]);

  // Update theme when mode changes
  useEffect(() => {
    if (editorRef.current) {
      monaco.editor.setTheme(mode === 'dark' ? 'vs-dark' : 'vs');
    }
  }, [mode]);

  return (
    <Box
      ref={containerRef}
      sx={{
        width: '100%',
        height: '400px',
        '& .monaco-editor': {
          paddingTop: 1,
        },
      }}
    />
  );
};

export default MonacoEditor;

I’ve tried setting triggerCharacters to all letters and even added console.log statements inside provideCompletionItems, but they’re never executed. I’ve also checked the console for errors and verified that the language is being registered.

Any help in diagnosing why the completion provider isn’t being triggered and why built-in functions aren’t highlighted would be greatly appreciated.

Excel Office.js: How to preserve custom formula text in the formula bar when batch-updating cells?

I’m building an Excel add-in using the Office JavaScript API. I have custom formulas like =PMPBK(…) that work fine if I rely on Excel’s @customfunction approach. The user sees the formula in the formula bar and Excel calls my JavaScript code to get results.

However, I need to batch these formulas for performance. When I manually set:

targetCell.values = [[someResult]];

or

targetCell.formulas = [[originalFormulaString]];
targetCell.values = [[someResult]];

it overwrites the original formula in the formula bar. I want the user to see their typed formula (like =THe_FORMULA(MY_VLAUE)), but the displayed cell value should come from my server.

Question: Is there a way to keep the user’s original formula in the formula bar, while also displaying a manually retrieved result from my backend in the same cell? Or do I have to accept that Excel only supports storing either a formula or a value, but not both?

Any tips or recommended workarounds (like hidden helper sheets) are appreciated. Thanks!

  1. Setting .formulas then .values → The formula is replaced or recalculated.
  2. Restoring the formula after setting the value → Excel overwrites the displayed value on next recalc.

Unable to add text into TextInput using React JS

I recently started working with react.js and wanted to build a simple form. For some reason, I am unable to enter text into my text input. I have already tried troubleshooting with print statements and checking for errors but the terminal is not giving me print statements or errors. Any help would be appreciated.

import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Alert } from 'react-native';


const SimpleForm = () => {
  const [formData, setFormData] = useState({ //formData holds form data, setFormData updates formData
    name: '',
    airport: '', // initial states of all data
    gateNumber: '',
  });

  const handleChange = (field, value) => { // field = name of input(name, airport, gatenum), value = val entered
    console.log(`Updating ${field} with value: ${value}`);   
    setFormData(prevData => ({ //update form in specific field
      ...prevData,
      [field]: value,
    }));
  };

  const handleSubmit = () => {
    console.log('Form submitted:', formData);
    Alert.alert('Form Submitted', `Name: ${formData.name}nAirport: ${formData.airport}nGate Number: ${formData.gateNumber}`);
   
  };

  return (

    <View style={styles.container}>
      <Text style={styles.label}>Name:</Text>
      <TextInput
        style={styles.input}
        placeholder="Enter your name"
        value={formData.name}
        onChangeText={value => handleChange('name', value)}
      />

      <Text style={styles.label}>Airport:</Text>
      <TextInput
        style={styles.input}
        placeholder="Enter airport name"
        value={formData.airport}
        onChangeText={value => handleChange('airport', value)}
      />

      <Text style={styles.label}>Gate Number:</Text>
      <TextInput
        style={styles.input}
        placeholder="Enter gate number"
        value={formData.gateNumber}
        onChangeText={value => handleChange('gateNumber', value)}
       
      />

      <View style={styles.buttonContainer}>
        <Button title="Submit" onPress={handleSubmit} color="#4CAF50" />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#f5f5f5',
    justifyContent: 'center',
  },
  label: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 5,
    color: '#333',
  },
  input: {
    height: 40,
    borderColor: '#ccc',
    borderWidth: 1,
    marginBottom: 15,
    paddingHorizontal: 10,
    borderRadius: 5,
    backgroundColor: '#fff',
  },
  buttonContainer: {
    marginTop: 20,
  },
});

export default SimpleForm;

Format Dates in Chartjs x Axis

The dates in the x axis are showing times and time zones. I just want to show month and day.

function dateSeries(startDate, endDate) {
    let dates = [];
    let currentDate = new Date(startDate);
    while (currentDate <= endDate) {
        dates.push(new Date(currentDate));
        currentDate.setDate(currentDate.getDate() + 1);
    }
    return dates;
};

let ctx = document.getElementById('myChart');

let goal = [5,4,3,2,1,0];
let dates = dateSeries(new Date('2024-12-03'), new Date('2024-12-08'));

let myChart = new Chart(ctx, {
    type: 'line',
    data: {
        labels: dates,
        datasets: [
            {
                label: 'Goal',
                data: goal,
                borderColor: 'rgba(142, 180, 227, 1)',
            }
        ]
    },
    options: {
        scales: {
            x: {
                type: 'time',
                time: {
                    unit: 'day',
                    displayFormats: {
                        day: 'MMM DD'
                    }
                }
            }
        }
    }
});

It renders as:

Chartjs with verbose x axis

I feel like my “options” code looks like all the examples I found, but I can’t get the axis to format properly.

ESLint Flat Config: Multiple Configs in a Single Override?

I’m sure this has been asked before, but I am finding it hard to locate a good solution.

Basically, I want to do things differently for different file types, or folders, or whatever. Before ESLint 9, this was accomplished with overrides, and now with version 9 we are meant to have multiple objects in an array-like structure. So far, so good.

Where I am having trouble is in replicating the convenient functionality of the extends keyword to this new format. In a .eslintrc.js file, I was able to include multiple configs, with an array of strings, such as:

export default {
    extends: ['airbnb', 'prettier'],
    overrides: {
        {
            files: ['*.jsx'],
            extends: ['plugin:react/jsx-runtime', 'plugin:react-hooks/recommended'],
        },
    },
};

In ESLint 9, the configs are no longer represented as simple strings, they are required & merged with the project’s configuration. This makes sense, and I generally agree, but I am running into trouble where multiple imported configs conflict with each other. Because there is no (easy/efficient) way to do deep merging in JavaScript, the recommendation for using multiple imported configs is to merge them together. However, if the 2 configs have different rules, this becomes problematic. For example, going back to the previous example, the easiest way to include the 2 react configs would be something like:

import prettier from 'eslint-config-prettier';
import airbnb from 'eslint-config-airbnb';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';

export default {
    prettier,
    airbnb,
    {
        ...react.configs.flat['jsx-runtime'],
        ...reactHooks.configs.recommended,
        files: ['*.jsx'],
    },
};

However, these 2 configs both have different rules, so the end result is that only the rules for the hooks are used, and I don’t get any errors when I use duplicate props or rest props, but I get errors when I violate the rules of hooks.

How do I include multiple external configs in a folder-specific override without a whole bunch of manual merging?