Tic-Tac-Toe Project: Restart Button Not Resetting Game State [closed]

I’m building a Tic-Tac-Toe game as part of The Odin Project JavaScript curriculum. My implementation works fine in the console version, but I’m facing an issue in the browser version.

function GameBoard() {
  let rows = 3;
  let columns = 3;

  const board = [];

  for(let i = 0; i < rows; i++) {
    board[i] = [];
    for(let j = 0; j < columns; j++) {
      board[i].push(Cell());
    }
  }

  const getBoard = () => board;

  const markCell = (row, col, token) => {
    if(board[row][col].getValue() != 0) return -1; 
    board[row][col].setValue(token);
    return 0;
  };

  const printBoard = () => {
    const boardWithCellValues = board.map((row) => row.map((cell) => cell.getValue()));
    console.log(boardWithCellValues);
  };

  return {
    getBoard,
    markCell,
    printBoard,
  };
}

function Cell() {
  let value = '';

  const setValue = (token) => {
    value = token;
  };

  const getValue = () => value;

  return {
    setValue,
    getValue,
  };
}

function GameController(
  playerOne="Player 1", 
  playerTwo="Player 2"
) {
  let turnsPlayed = 0;
  const players = [
    {
      name: playerOne,
      token: 'X'
    },
    {
      name: playerTwo,
      token: 'O'
    }
  ];
  const board = GameBoard();
  let activePlayer = players[0];

  const switchPlayerTurn = () => {
    activePlayer = activePlayer === players[0] ? players[1] : players[0];
  };

  const getActivePlayer = () => activePlayer;

  const printNewRound = () => {
    board.printBoard();
    console.log(`${getActivePlayer().name}'s turn ...`);
  }

  const checkWin = () => {
    // logic for tic tac toe game
    let hasWon = false;
    let playerToken = getActivePlayer().token;
    const boardArray = board.getBoard();

    let a = boardArray[0][0].getValue();
    let b = boardArray[1][1].getValue();
    let c = boardArray[2][2].getValue();
    if(a === b && b === c && a === playerToken) {
      hasWon = true;
    } else {
      a = boardArray[0][2].getValue();
      b = boardArray[1][1].getValue();
      c = boardArray[2][0].getValue();
      if(a === b && b === c && a === playerToken) {
        hasWon = true;
      } else {
        for(let i = 0; i < boardArray.length; i++) {
          a = boardArray[i][0].getValue();
          b = boardArray[i][1].getValue();
          c = boardArray[i][2].getValue();
          if(a === b && b === c && a === playerToken) {
            hasWon = true;
            break;
          }
    
          a = boardArray[0][i].getValue();
          b = boardArray[1][i].getValue();
          c = boardArray[2][i].getValue();
          if(a === b && b === c && a === playerToken) {
            hasWon = true;
            break;
          }
        }
      }
    }

    return hasWon;
  }

  const checkDraw = () => {
    return turnsPlayed == 9;
  }

  const playRound = (r, c) => {
    console.log(
      `Marking ${getActivePlayer().name}'s token in Cell (${r}, ${c}).`
    );
    const exit_status = board.markCell(r,c, getActivePlayer().token);
    // Do nothing if an occupied cell is marked by player
    if(exit_status == -1) return; 
    turnsPlayed++;
    if(checkWin()) {
      board.printBoard();
      console.log(`${getActivePlayer().name} has won the game.`);
    } else if (checkDraw()) {
      board.printBoard();
      console.log(`It is a Draw. Play again ...`)
    } else {
      switchPlayerTurn();
      printNewRound();
    }
  }

  // Intial Render
  printNewRound();

  return {
    playRound,
    getActivePlayer,
    checkWin,
    checkDraw,
    getBoard: board.getBoard
  };
}


function ScreenController() {
  const game = GameController();
  const playerTurnDiv = document.querySelector('.turn');
  const boardDiv = document.querySelector('.board');

  const updateScreen = () => {
    boardDiv.textContent = '';

    const board = game.getBoard();
    const activePlayer = game.getActivePlayer();

    playerTurnDiv.textContent = `${activePlayer.name}'s Turn`;

    board.forEach((row, row_idx) => {
      row.forEach((cell, col_idx) => {
        const cellButton = document.createElement('button');
        cellButton.classList.add('cell');
        cellButton.textContent = cell.getValue();
        cellButton.dataset.row_idx = row_idx;
        cellButton.dataset.col_idx = col_idx;
        boardDiv.appendChild(cellButton);
      })
    })
  }

  const displayGameOver = () => {
    const msg = document.querySelector('.game-over-message');
    if(game.checkWin()) {
      msg.textContent = `${game.getActivePlayer().name} has won the game.`
    } else {
      msg.textContent = "It is a tie."
    }
    gameOverDialog.showModal();
  }


  function clickHandlerBoard(e) {
    const row = e.target.dataset.row_idx;
    const col = e.target.dataset.col_idx; 

    if(!row) return;

    game.playRound(row, col);
    updateScreen();
    if(game.checkWin() || game.checkDraw())
      displayGameOver();
  }

  boardDiv.addEventListener("click", clickHandlerBoard);

  // Initial Render
  updateScreen();
}

ScreenController();

const gameOverDialog = document.querySelector("#game-over-screen");
const restartBtn = document.querySelector('.restartBtn');

restartBtn.addEventListener('click', () => {
  gameOverDialog.close();
  ScreenController();
});
/*
Josh's Custom CSS Reset
https://www.joshwcomeau.com/css/custom-css-reset/
*/

*, *::before, *::after {
box-sizing: border-box;
}

* {
margin: 0;
}

@media (prefers-reduced-motion: no-preference) {
html {
    interpolate-size: allow-keywords;
}
}

body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}

input, button, textarea, select {
font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}

p {
text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}

#root, #__next {
isolation: isolate;
}

/* CSS Starts Here */

.main-container {
    height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: monospace, sans-serif;
}

.header {
    padding: 32px;
    text-align: center;
}

.header h1 {
    font-size: 48px;
}

.footer {
    text-align: center;
    padding: 16px;
}

.container {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 36px 0;
    gap: 1.5em;
}

.container .turn {
    font-size: 24px;
}

.board {
    display: grid;
    height: 500px;
    width: 500px;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    border: 1px solid black;
    gap: 2px;
    padding: 1px;
    background-color: rgb(255, 17, 0);
}
  
.cell {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 5rem;
    border: 1px solid grey;
    background: lightyellow;
    cursor: pointer;
}

dialog {
    font-size: 2em;
    position: fixed;
    margin: auto;
    width: 45ch;
    /* padding: 1em; */
    border: 3px solid greenyellow;
    border-radius: 8px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}

dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
}

.game-over-message {
    margin-bottom: 1.5em;
    text-align: center;
}

.game-over-toolbar {
    display: flex;
    justify-content: center;
}

.restartBtn {
    padding: 0.5em;
    border-radius: 32px;
    border: none;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
    background-color: rgb(173, 255, 47);
    display: flex;
    align-items: center;
    justify-content: center;
}

.restartBtn:hover {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
    cursor: pointer;
}
<div class="main-container">
        <div class="header">
            <h1>Tic Tac Toe</h1>
        </div>
        <div class="container">
            <div class="turn"></div>
            <div class="board"></div>
        </div>
        <div class="footer">&copy; GOTW 2025</div>
    </div>
    <dialog id="game-start-screen">
        <label for="playerOneName">Enter Player 1's name:</label>
        <input type="text" id="playerOneName" autofocus>
    </dialog>
    <dialog id="game-over-screen">
        <div class="game-over-message"></div>
        <div class="game-over-toolbar">
            <button class="restartBtn">&#x21bb; Restart</button>
        </div>
    </dialog>
</body>

Problem:
After a game is won, I display a “Game Over” dialog. I’ve added a Restart button inside this dialog, which is meant to reset the game state and allow a fresh game. However, after clicking restart, although the dialog closes, the game remains stuck — it behaves as if the previous state is still active, and clicking on a cell immediately brings back the “Game Over” dialog.

Here’s the relevant part of the code:

restartBtn.addEventListener('click', () => {   
     gameOverDialog.close();   
     ScreenController(); }); 

Question:
How can I properly reset both the UI and internal game state when restarting the game via the Restart button? Is there a better way to reinitialize the game components?

Any suggestions or patterns for managing game state reset cleanly would be appreciated!

What I expected:

  1. The board should be cleared.
  2. All game state (turns, board data, game over flag) should reset.
  3. The user should be able to play a new game from scratch.

What I’ve tried:

  1. Calling ScreenController() again in the restart handler.
  2. Confirmed that the console version resets just fine.

How to get rid of double quote missing error in aria snapshot?

I’m using the method toMatchAriaSnapshot. I have dynanic text so I’m generating my snapshot text like that:

await expect(page.locator('#contract')).toMatchAriaSnapshot(`
     - dialog:
       - document:
         - heading "Add" [level=5]
         - button "Close"

         - text: "` + (error["login"] ?? "") + messages["login"] + ` *"
         - textbox
`);

But, sometimes it will require the quote, and some other time, it will not. It will result in this:

-     - text: "Identifiant *"
+     - text: Identifiant *

the error I'm getting

Sometimes, when error["login"] contains something, I will get message like this: Missing information. Error code : A-002 Identifiant * which requires the double quotes.

When not using quotes, and when I have the full message, here is the result:

Error: expect.toMatchAriaSnapshot: Nested mappings are not allowed in compact mappings at line 13, column 13:

    - text: Missing information. Error code : A-002 Identifiant *
            ^

How can I make playwright ignore the error of missing quote, or too many quotes ?

Tic-Tac-Toe Project: Restart Button Not Resetting Game State [closed]

I’m building a Tic-Tac-Toe game as part of The Odin Project JavaScript curriculum. My implementation works fine in the console version, but I’m facing an issue in the browser version.

function GameBoard() {
  let rows = 3;
  let columns = 3;

  const board = [];

  for(let i = 0; i < rows; i++) {
    board[i] = [];
    for(let j = 0; j < columns; j++) {
      board[i].push(Cell());
    }
  }

  const getBoard = () => board;

  const markCell = (row, col, token) => {
    if(board[row][col].getValue() != 0) return -1; 
    board[row][col].setValue(token);
    return 0;
  };

  const printBoard = () => {
    const boardWithCellValues = board.map((row) => row.map((cell) => cell.getValue()));
    console.log(boardWithCellValues);
  };

  return {
    getBoard,
    markCell,
    printBoard,
  };
}

function Cell() {
  let value = '';

  const setValue = (token) => {
    value = token;
  };

  const getValue = () => value;

  return {
    setValue,
    getValue,
  };
}

function GameController(
  playerOne="Player 1", 
  playerTwo="Player 2"
) {
  let turnsPlayed = 0;
  const players = [
    {
      name: playerOne,
      token: 'X'
    },
    {
      name: playerTwo,
      token: 'O'
    }
  ];
  const board = GameBoard();
  let activePlayer = players[0];

  const switchPlayerTurn = () => {
    activePlayer = activePlayer === players[0] ? players[1] : players[0];
  };

  const getActivePlayer = () => activePlayer;

  const printNewRound = () => {
    board.printBoard();
    console.log(`${getActivePlayer().name}'s turn ...`);
  }

  const checkWin = () => {
    // logic for tic tac toe game
    let hasWon = false;
    let playerToken = getActivePlayer().token;
    const boardArray = board.getBoard();

    let a = boardArray[0][0].getValue();
    let b = boardArray[1][1].getValue();
    let c = boardArray[2][2].getValue();
    if(a === b && b === c && a === playerToken) {
      hasWon = true;
    } else {
      a = boardArray[0][2].getValue();
      b = boardArray[1][1].getValue();
      c = boardArray[2][0].getValue();
      if(a === b && b === c && a === playerToken) {
        hasWon = true;
      } else {
        for(let i = 0; i < boardArray.length; i++) {
          a = boardArray[i][0].getValue();
          b = boardArray[i][1].getValue();
          c = boardArray[i][2].getValue();
          if(a === b && b === c && a === playerToken) {
            hasWon = true;
            break;
          }
    
          a = boardArray[0][i].getValue();
          b = boardArray[1][i].getValue();
          c = boardArray[2][i].getValue();
          if(a === b && b === c && a === playerToken) {
            hasWon = true;
            break;
          }
        }
      }
    }

    return hasWon;
  }

  const checkDraw = () => {
    return turnsPlayed == 9;
  }

  const playRound = (r, c) => {
    console.log(
      `Marking ${getActivePlayer().name}'s token in Cell (${r}, ${c}).`
    );
    const exit_status = board.markCell(r,c, getActivePlayer().token);
    // Do nothing if an occupied cell is marked by player
    if(exit_status == -1) return; 
    turnsPlayed++;
    if(checkWin()) {
      board.printBoard();
      console.log(`${getActivePlayer().name} has won the game.`);
    } else if (checkDraw()) {
      board.printBoard();
      console.log(`It is a Draw. Play again ...`)
    } else {
      switchPlayerTurn();
      printNewRound();
    }
  }

  // Intial Render
  printNewRound();

  return {
    playRound,
    getActivePlayer,
    checkWin,
    checkDraw,
    getBoard: board.getBoard
  };
}


function ScreenController() {
  const game = GameController();
  const playerTurnDiv = document.querySelector('.turn');
  const boardDiv = document.querySelector('.board');

  const updateScreen = () => {
    boardDiv.textContent = '';

    const board = game.getBoard();
    const activePlayer = game.getActivePlayer();

    playerTurnDiv.textContent = `${activePlayer.name}'s Turn`;

    board.forEach((row, row_idx) => {
      row.forEach((cell, col_idx) => {
        const cellButton = document.createElement('button');
        cellButton.classList.add('cell');
        cellButton.textContent = cell.getValue();
        cellButton.dataset.row_idx = row_idx;
        cellButton.dataset.col_idx = col_idx;
        boardDiv.appendChild(cellButton);
      })
    })
  }

  const displayGameOver = () => {
    const msg = document.querySelector('.game-over-message');
    if(game.checkWin()) {
      msg.textContent = `${game.getActivePlayer().name} has won the game.`
    } else {
      msg.textContent = "It is a tie."
    }
    gameOverDialog.showModal();
  }


  function clickHandlerBoard(e) {
    const row = e.target.dataset.row_idx;
    const col = e.target.dataset.col_idx; 

    if(!row) return;

    game.playRound(row, col);
    updateScreen();
    if(game.checkWin() || game.checkDraw())
      displayGameOver();
  }

  boardDiv.addEventListener("click", clickHandlerBoard);

  // Initial Render
  updateScreen();
}

ScreenController();

const gameOverDialog = document.querySelector("#game-over-screen");
const restartBtn = document.querySelector('.restartBtn');

restartBtn.addEventListener('click', () => {
  gameOverDialog.close();
  ScreenController();
});
/*
Josh's Custom CSS Reset
https://www.joshwcomeau.com/css/custom-css-reset/
*/

*, *::before, *::after {
box-sizing: border-box;
}

* {
margin: 0;
}

@media (prefers-reduced-motion: no-preference) {
html {
    interpolate-size: allow-keywords;
}
}

body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}

img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}

input, button, textarea, select {
font: inherit;
}

p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}

p {
text-wrap: pretty;
}
h1, h2, h3, h4, h5, h6 {
text-wrap: balance;
}

#root, #__next {
isolation: isolate;
}

/* CSS Starts Here */

.main-container {
    height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: monospace, sans-serif;
}

.header {
    padding: 32px;
    text-align: center;
}

.header h1 {
    font-size: 48px;
}

.footer {
    text-align: center;
    padding: 16px;
}

.container {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 36px 0;
    gap: 1.5em;
}

.container .turn {
    font-size: 24px;
}

.board {
    display: grid;
    height: 500px;
    width: 500px;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    border: 1px solid black;
    gap: 2px;
    padding: 1px;
    background-color: rgb(255, 17, 0);
}
  
.cell {
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 5rem;
    border: 1px solid grey;
    background: lightyellow;
    cursor: pointer;
}

dialog {
    font-size: 2em;
    position: fixed;
    margin: auto;
    width: 45ch;
    /* padding: 1em; */
    border: 3px solid greenyellow;
    border-radius: 8px;
    box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}

dialog::backdrop {
    background: rgba(0, 0, 0, 0.5);
}

.game-over-message {
    margin-bottom: 1.5em;
    text-align: center;
}

.game-over-toolbar {
    display: flex;
    justify-content: center;
}

.restartBtn {
    padding: 0.5em;
    border-radius: 32px;
    border: none;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
    background-color: rgb(173, 255, 47);
    display: flex;
    align-items: center;
    justify-content: center;
}

.restartBtn:hover {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.5);
    cursor: pointer;
}
<div class="main-container">
        <div class="header">
            <h1>Tic Tac Toe</h1>
        </div>
        <div class="container">
            <div class="turn"></div>
            <div class="board"></div>
        </div>
        <div class="footer">&copy; GOTW 2025</div>
    </div>
    <dialog id="game-start-screen">
        <label for="playerOneName">Enter Player 1's name:</label>
        <input type="text" id="playerOneName" autofocus>
    </dialog>
    <dialog id="game-over-screen">
        <div class="game-over-message"></div>
        <div class="game-over-toolbar">
            <button class="restartBtn">&#x21bb; Restart</button>
        </div>
    </dialog>
</body>

Problem:
After a game is won, I display a “Game Over” dialog. I’ve added a Restart button inside this dialog, which is meant to reset the game state and allow a fresh game. However, after clicking restart, although the dialog closes, the game remains stuck — it behaves as if the previous state is still active, and clicking on a cell immediately brings back the “Game Over” dialog.

Here’s the relevant part of the code:

restartBtn.addEventListener('click', () => {   
     gameOverDialog.close();   
     ScreenController(); }); 

Question:
How can I properly reset both the UI and internal game state when restarting the game via the Restart button? Is there a better way to reinitialize the game components?

Any suggestions or patterns for managing game state reset cleanly would be appreciated!

What I expected:

  1. The board should be cleared.
  2. All game state (turns, board data, game over flag) should reset.
  3. The user should be able to play a new game from scratch.

What I’ve tried:

  1. Calling ScreenController() again in the restart handler.
  2. Confirmed that the console version resets just fine.

GoogleAPI, Get User’s Additional Emails

I have a question about GAPI and People API. After the user signs in with Google OAuth I need to collect every email address attached to that Google Account (aliases / secondary addresses).

So what I’ve tried to do is to:

So the question is: is there a way to actually get the additional email addresses from the Google Accounts ? Because I’m lost between Google Docs and their support.

What I already checked

Can a select field be dependable of 2 or more fields?

I have followed instructions here https://filamentphp.com/docs/3.x/forms/advanced#dependant-select-options to make a select form dependable from another field in the Edit form. Here is the code of the select field workflow_transition_id dependable from workflow_id TextInput field, which works as expected:

This is in the resource.php

Select::make('workflow_id')
    ->options(Workflow::all()->pluck('name', 'id'))
    ->label('Workflow')
    ->searchable()
    ->preload()
    ->live()
    ->required(),
TextInput::make('workflow_status_id')
    ->label('Current Status')
    ->live()
    ->hiddenOn('create'),
Select::make('workflow_transition_id')
    ->relationship(
        name: 'workflow.workflow_transitions',
        titleAttribute: 'name',
        modifyQueryUsing: fn (Builder $query, Get $get) => $query->where('workflow_id', $get('workflow_id')),
    )
    ->label('New status')
    ->disabled(fn(Get $get) : bool => ! filled($get('workflow_id')))
    ->searchable()
    ->preload()
    ->required(),

Now, I want to make the select field workflow_transition_id dependable from one additional field workflow_status_id. To accomplish this, modified the “modifyQueryUsing” attribute by passing a second Get argument and adding a second **where **clause:

modifyQueryUsing: fn (Builder $query, Get $get, Get $get2) => $query->where('workflow_id', $get('workflow_id'))->where('from_workflow_status_id', $get2('workflow_status_id')),

After doing this, I get this error from Laravel:

Typed property FilamentFormsComponentsComponent::$container must not be accessed before initialization

Perhaps I am doing something that I am yet to spot it.

If I try to hardcode $get2('workflow_status_id') by a valid workflow id (i.e. 2), then it works.

I have also tried to use the option attribute (instead of the relationship) and I get the same error:

->options(function(Get $get, Get $get2) { return WorkflowTransition::where('workflow_id', $get('workflow_id'))->where('from_workflow_status_id', $get2('workflow_status_id'))->pluck('name'); })

This is in the edit form which means this workflow_status_id comes from the DB.

I have gone over several Stackoverflow, Google, Laracasts, Filamentphp website and other forums and I have not seen anybody implementing a dependable select with 2 dependent fields.

I am using:

  • Filament 3.3.29
  • PHP 8.4.8

“Google Apps Script stops working after sheet copy – emails not sending and status not updating”

Problem Description:
I have a Google Sheet with an Apps Script that:

  1. Creates new request sheets when a form is submitted
  2. Adds a submit checkbox to each request sheet
  3. When checked, should:
    • Update status in the main sheet
    • Send confirmation emails
    • Mark the request as submitted

This works perfectly in the original sheet, but after making a copy:
The checkbox triggers don’t work
No emails are sent
Status doesn’t update
No error messages appear.

What I’ve Tried:

  1. Reinstalled all triggers using setupCheckboxTrigger()
  2. Reauthorized the script (ran test functions that work)
  3. Verified all sheet references use getActiveSpreadsheet()
  4. Checked execution logs – shows trigger fires but no visible changes
  5. Tested email sending separately – works when run manually

Code Highlights:

Main trigger handler:
javascript

function handleCheckboxSubmit(e) {
  if (!e || !e.value || e.value !== "TRUE") return;
  
  const sheet = e.range.getSheet();
  const range = e.range;
  const row = range.getRow();
  
  // Verify this is our submit checkbox
  if (range.getColumn() !== 2 || 
      sheet.getRange(row, 1).getValue() !== "Submit Request") {
    return;
  }
  // ... rest of the function
}

function submitRequest() {
  // ... gets all required data
  try {
    MailApp.sendEmail({
      to: ${requestorEmail}, ${designerEmail}, ${groupEmail},
      name: senderName,
      subject: subject,
      htmlBody: body
    });
  } catch (e) {
    console.error("Email failed:", e);
  }
}

How to try Translator and Language Detection API in Chrome 138

I would like to use the new Translator and Language Detector APIs in a Greasemonkey script I use for personal-use. Aim is to translate on the fly some sites I usually navigate.
The problem is I noticed they are NOT available on HTTP-only websites, so, when I try to use them in my script Translator and LanguageDetector variable are never defined.
This only happens on HTTP websites, on HTTPS and localhost works fine.

If you navigate with Chrome 138 to an HTTP-only website, for example http://neverssl.com/, and open a console you can check for yourself.

Any suggestion?

electron-builder building issue “can’t detect abi”

I was trying to use electron-builder to build my app and I get an error.
it worked a month ago and I didn’t change anything in the package.json file but now it doesn’t work. The app des work when I open it using “npm start” but I can’t build it now for some reason.

I was trying to use this command in administrator cmd “npx electron-builder –dir”.
A month ago it did work with no issues and now I get an error after the “preparing” stage.

The error: ⨯ Could not detect abi for version 37.1.0 and runtime electron. Updating “node-abi” might help solve this issue if it is a new release of electron failedTask=build stackTrace=Error: Could not detect abi for version 37.1.0 and runtime electron.

I tried deleting node modules and running “npm install” but it didn’t help.
i also tried “npm audit fix” which didn’t help.
I also tried downgrading the version of electron and it also didn’t help.

infinite site loading, no refresh on file save, memory allocation failed

I’m using Vite 6, React, Redux and TailwindCSS v4 for my project. 2 month ago I started and made a lot of progress, then I left it for 2 month to make another one. Now I returned and I my project stopped working.

Primary issues:

  • No auto refresh.
  • Infinite site loading, plain white window.

I start my project by this command:

npm run dev -- --host

package.json

package.json

vite.config.ts

vite config

index.css

index.css

There are some CSS code lower, but it’s irrelevant plain CSS code.

After a lot of troubleshooting I found out that when I remove @import "tailwindcss"; from index.css all issues are fixed. Auto refresh works when files change, and site loads immediately.

But as soon as I return tailwindcss import, everything breaks again. When it breaks, I don’t get any errors, just those two problems occur. And if I remove @import "tailwindcss"; from index.css when project breaks it doesn’t restore. I need to manually stop it and start again by CTRLC and then command npm run dev -- --host.

Also I found out, that if I leave my broken project open for some time, it closes with error in console:

memory allocation of 64424509440 bytes failed

If I understand right, it means that when I connect TailwindCSS it breaks and enters infinite loop trying to get all memory from pc.

The most ridiculous thing about this situation, that 2 month ago everything worked without any error. But now it just randomly broke.

Vite 6.3.5, TailwindCSS 4.1.11: Tailwind crashes project: infinite site loading, no refresh on file save, memory allocation failed

I’m using Vite, React, Redux and TailwindCSS for my project. 2 month ago I started and made a lot of progress, then I left it for 2 month to make another one. Now I returned and I my project stopped working.

Primary issues:
No auto refresh.
Infinite site loading, plain white window.

I start my project by this command: npm run dev — –host

My package.json:

package.json

My vite.config.ts file:

vite config

My index.css file:

index.css

There are some css code lower, but it’s irrelevant plain css code.

After a lot of troubleshooting I found out that when I remove @import “tailwindcss”; from index.css all issues are fixed. Auto refresh works when files change, and site loads immediately.

But as soon as I return tailwindcss import, everything breaks again. When it breaks, I don’t get any errors, just those two problems occur. And if I remove @import “tailwindcss”; from index.css when project breaks it doesn’t restore. I need to manually stop it and start again by “ctrl + c” and then command “npm run dev — –host”

Also I found out, that if I leave my broken project open for some time, it closes with error in console “memory allocation of 64424509440 bytes failed”. If I understand right, it means that when I connect tailwindcss it breaks and enters infinite loop trying to get all memory from pc.

The most ridiculous thing about this situation, that 2 month ago everything worked without any error. But now it just randomly broke.

Using Prettier as beautifier user’s code in Angular project

In my project Prettier is already connected for the project code formatting. I’m trying to use Prettier for user’s code formatting in Ace Editor:

import * as prettier from 'prettier';
import * as babel from 'prettier/plugins/babel';
import * as estree from 'prettier/plugins/estree';
...
const code = this.editor.getValue();
const formatted = prettier.format(code, {
  parser: 'babel',
  plugins: [babel, estree]
});

…but I get an error:

Uncaught TypeError: Super constructor null of anonymous class is not a constructor

I’ve tried different options – nothing has helped yet.
Maybe someone has a successful experience – I would be very grateful.

Angular ReferenceError: document is not defined

Note my knowledge of JS frameworks and TS is essentially non-existent.

I’m trying to create an SPA with SSG. I have a main component I’m trying to display a webAMP in. Which needs to access document. Right now I have the code to create a webAMP object in the main-component.ts. But it’s throwing this error:

ReferenceError: document is not defined
at eval (eval at runInlinedModule (file:.../node_modules/vite/dist/node/module-runner.js:1062:11), <anonymous>:19481:10)
at async ESModulesEvaluator.runInlinedModule (file:.../node_modules/vite/dist/node/module-runner.js:1062:5)
at async SSRCompatModuleRunner.directRequest (file:.../node_modules/vite/dist/node/module-runner.js:1284:61)
at async SSRCompatModuleRunner.directRequest (file:.../node_modules/vite/dist/node/chunks/dep-DBxKXgDP.js:25274:23)
at async SSRCompatModuleRunner.cachedRequest (file:.../node_modules/vite/dist/node/module-runner.js:1180:76)

I think I’ve tried these but found no progress:

main.component.ts

import { NgOptimizedImage } from '@angular/common';
import { Component, DOCUMENT, Inject} from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
import Webamp from 'webamp';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-main',
  imports: [RouterLink, RouterOutlet, NgOptimizedImage, CommonModule],
  templateUrl: './main.component.html',
  styleUrl: './main.component.css',
})

export class MainComponent {
  constructor(@Inject(DOCUMENT) private document: Document) {
    this.musicPlayer();
  }
  musicPlayer() {
    alert("the playerrrr"); // for testing
    const webamp = new Webamp({/* ... */});
    webamp.renderWhenReady(this.document.getElementById('winamp-container') as HTMLElement);
  }
}
}