mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-24 06:36:37 +00:00
enhanced
This commit is contained in:
parent
813b74e94c
commit
ba771d54af
5
apps_zoo/chess_game/description.yaml
Normal file
5
apps_zoo/chess_game/description.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
description: This is a 2 players chess game
|
||||||
|
version: 1.0
|
||||||
|
author: ParisNeo
|
||||||
|
model: gpt-4o-mini
|
||||||
|
disclaimer: None
|
BIN
apps_zoo/chess_game/icon.png
Normal file
BIN
apps_zoo/chess_game/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
639
apps_zoo/chess_game/index.html
Normal file
639
apps_zoo/chess_game/index.html
Normal file
@ -0,0 +1,639 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Chess Game with Check and Checkmate</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
.game-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
#chessboard {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(8, 60px);
|
||||||
|
grid-template-rows: repeat(8, 60px);
|
||||||
|
border: 2px solid #333;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.square {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
.white {
|
||||||
|
background-color: #f0d9b5;
|
||||||
|
}
|
||||||
|
.black {
|
||||||
|
background-color: #b58863;
|
||||||
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: #7fc97f;
|
||||||
|
}
|
||||||
|
.possible-move {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.possible-move::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.in-check {
|
||||||
|
background-color: #ff6b6b;
|
||||||
|
}
|
||||||
|
.turn-indicator {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.game-status {
|
||||||
|
position: absolute;
|
||||||
|
top: 60px;
|
||||||
|
left: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #ff6b6b;
|
||||||
|
}
|
||||||
|
.scoreboard {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.scoreboard h2 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.score {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.leaderboard {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.leaderboard ol {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.winner-input {
|
||||||
|
display: none;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.credits {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.8);
|
||||||
|
color: white;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.credits h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
.credits p {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.credits button {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="turn-indicator" id="turnIndicator"></div>
|
||||||
|
<div class="game-status" id="gameStatus"></div>
|
||||||
|
<div class="game-container">
|
||||||
|
<div id="chessboard"></div>
|
||||||
|
<div class="scoreboard">
|
||||||
|
<h2>Scoreboard</h2>
|
||||||
|
<div class="score" id="whiteScore">White: 0</div>
|
||||||
|
<div class="score" id="blackScore">Black: 0</div>
|
||||||
|
<div class="leaderboard">
|
||||||
|
<h3>Leaderboard</h3>
|
||||||
|
<ol id="leaderboardList"></ol>
|
||||||
|
</div>
|
||||||
|
<div class="winner-input" id="winnerInput">
|
||||||
|
<input type="text" id="winnerName" placeholder="Enter your name">
|
||||||
|
<button onclick="saveWinner()">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="credits" id="credits">
|
||||||
|
<h1>Congratulations!</h1>
|
||||||
|
<p>Game created by WebCraft Maestro, prompted by ParisNeo on lollms system.</p>
|
||||||
|
<button onclick="playAgain()">Play Again</button>
|
||||||
|
<audio id="creditMusic" src="https://www.bensound.com/bensound-music/bensound-sunny.mp3" loop></audio>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const board = document.getElementById('chessboard');
|
||||||
|
const turnIndicator = document.getElementById('turnIndicator');
|
||||||
|
const gameStatus = document.getElementById('gameStatus');
|
||||||
|
const whiteScoreElement = document.getElementById('whiteScore');
|
||||||
|
const blackScoreElement = document.getElementById('blackScore');
|
||||||
|
const leaderboardList = document.getElementById('leaderboardList');
|
||||||
|
const winnerInput = document.getElementById('winnerInput');
|
||||||
|
const winnerNameInput = document.getElementById('winnerName');
|
||||||
|
const credits = document.getElementById('credits');
|
||||||
|
const creditMusic = document.getElementById('creditMusic');
|
||||||
|
let selectedPiece = null;
|
||||||
|
let currentPlayer = 'white';
|
||||||
|
let whiteScore = 0;
|
||||||
|
let blackScore = 0;
|
||||||
|
let leaderboard = [];
|
||||||
|
let gameBoard = [];
|
||||||
|
|
||||||
|
const initialBoard = [
|
||||||
|
['♜', '♞', '♝', '♛', '♚', '♝', '♞', '♜'],
|
||||||
|
['♟', '♟', '♟', '♟', '♟', '♟', '♟', '♟'],
|
||||||
|
['', '', '', '', '', '', '', ''],
|
||||||
|
['', '', '', '', '', '', '', ''],
|
||||||
|
['', '', '', '', '', '', '', ''],
|
||||||
|
['', '', '', '', '', '', '', ''],
|
||||||
|
['♙', '♙', '♙', '♙', '♙', '♙', '♙', '♙'],
|
||||||
|
['♖', '♘', '♗', '♕', '♔', '♗', '♘', '♖']
|
||||||
|
];
|
||||||
|
|
||||||
|
const pieceValues = {
|
||||||
|
'♙': 1, '♟': 1, // Pawns
|
||||||
|
'♘': 3, '♞': 3, // Knights
|
||||||
|
'♗': 3, '♝': 3, // Bishops
|
||||||
|
'♖': 5, '♜': 5, // Rooks
|
||||||
|
'♕': 9, '♛': 9, // Queens
|
||||||
|
'♔': 0, '♚': 0 // Kings (no point value)
|
||||||
|
};
|
||||||
|
|
||||||
|
function createBoard() {
|
||||||
|
for (let row = 0; row < 8; row++) {
|
||||||
|
gameBoard[row] = [];
|
||||||
|
for (let col = 0; col < 8; col++) {
|
||||||
|
const square = document.createElement('div');
|
||||||
|
square.className = `square ${(row + col) % 2 === 0 ? 'white' : 'black'}`;
|
||||||
|
square.dataset.row = row;
|
||||||
|
square.dataset.col = col;
|
||||||
|
square.textContent = initialBoard[row][col];
|
||||||
|
square.addEventListener('click', handleClick);
|
||||||
|
board.appendChild(square);
|
||||||
|
gameBoard[row][col] = initialBoard[row][col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateTurnIndicator();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick(event) {
|
||||||
|
const square = event.target;
|
||||||
|
const row = parseInt(square.dataset.row);
|
||||||
|
const col = parseInt(square.dataset.col);
|
||||||
|
|
||||||
|
if (selectedPiece) {
|
||||||
|
if (square.classList.contains('possible-move')) {
|
||||||
|
const capturedPiece = square.textContent;
|
||||||
|
const fromRow = parseInt(selectedPiece.dataset.row);
|
||||||
|
const fromCol = parseInt(selectedPiece.dataset.col);
|
||||||
|
|
||||||
|
// Make the move
|
||||||
|
movePiece(selectedPiece, square);
|
||||||
|
updateGameBoard(fromRow, fromCol, row, col);
|
||||||
|
|
||||||
|
// Check for check and checkmate
|
||||||
|
const oppositeColor = currentPlayer === 'white' ? 'black' : 'white';
|
||||||
|
if (isInCheck(oppositeColor)) {
|
||||||
|
if (isCheckmate(oppositeColor)) {
|
||||||
|
gameStatus.textContent = `Checkmate! ${currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1)} wins!`;
|
||||||
|
winnerInput.style.display = 'block';
|
||||||
|
showCredits();
|
||||||
|
} else {
|
||||||
|
gameStatus.textContent = `${oppositeColor.charAt(0).toUpperCase() + oppositeColor.slice(1)} is in check!`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gameStatus.textContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScore(capturedPiece);
|
||||||
|
clearSelection();
|
||||||
|
currentPlayer = oppositeColor;
|
||||||
|
updateTurnIndicator();
|
||||||
|
} else {
|
||||||
|
clearSelection();
|
||||||
|
selectPiece(square);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectPiece(square);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectPiece(square) {
|
||||||
|
const piece = square.textContent;
|
||||||
|
if (piece && ((currentPlayer === 'white' && piece.charCodeAt(0) >= 9812 && piece.charCodeAt(0) <= 9817) ||
|
||||||
|
(currentPlayer === 'black' && piece.charCodeAt(0) >= 9818 && piece.charCodeAt(0) <= 9823))) {
|
||||||
|
selectedPiece = square;
|
||||||
|
square.classList.add('selected');
|
||||||
|
showPossibleMoves(square);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPossibleMoves(square) {
|
||||||
|
const piece = square.textContent;
|
||||||
|
const row = parseInt(square.dataset.row);
|
||||||
|
const col = parseInt(square.dataset.col);
|
||||||
|
|
||||||
|
clearPossibleMoves();
|
||||||
|
|
||||||
|
switch (piece) {
|
||||||
|
case '♔': case '♚': // King
|
||||||
|
showKingMoves(row, col);
|
||||||
|
break;
|
||||||
|
case '♕': case '♛': // Queen
|
||||||
|
showQueenMoves(row, col);
|
||||||
|
break;
|
||||||
|
case '♖': case '♜': // Rook
|
||||||
|
showRookMoves(row, col);
|
||||||
|
break;
|
||||||
|
case '♗': case '♝': // Bishop
|
||||||
|
showBishopMoves(row, col);
|
||||||
|
break;
|
||||||
|
case '♘': case '♞': // Knight
|
||||||
|
showKnightMoves(row, col);
|
||||||
|
break;
|
||||||
|
case '♙': case '♟': // Pawn
|
||||||
|
showPawnMoves(row, col, piece === '♙');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showKingMoves(row, col) {
|
||||||
|
const directions = [
|
||||||
|
[-1, -1], [-1, 0], [-1, 1],
|
||||||
|
[0, -1], [0, 1],
|
||||||
|
[1, -1], [1, 0], [1, 1]
|
||||||
|
];
|
||||||
|
for (const [dx, dy] of directions) {
|
||||||
|
const newRow = row + dx;
|
||||||
|
const newCol = col + dy;
|
||||||
|
if (isValidMove(newRow, newCol)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||||
|
if (isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showQueenMoves(row, col) {
|
||||||
|
showRookMoves(row, col);
|
||||||
|
showBishopMoves(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showRookMoves(row, col) {
|
||||||
|
const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
|
||||||
|
for (const [dx, dy] of directions) {
|
||||||
|
let newRow = row + dx;
|
||||||
|
let newCol = col + dy;
|
||||||
|
while (isValidMove(newRow, newCol)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||||
|
if (isValidTarget(targetSquare)) {
|
||||||
|
if (!wouldBeInCheck(row, col, newRow, newCol)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
if (targetSquare.textContent !== '') break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newRow += dx;
|
||||||
|
newCol += dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBishopMoves(row, col) {
|
||||||
|
const directions = [[-1, -1], [-1, 1], [1, -1], [1, 1]];
|
||||||
|
for (const [dx, dy] of directions) {
|
||||||
|
let newRow = row + dx;
|
||||||
|
let newCol = col + dy;
|
||||||
|
while (isValidMove(newRow, newCol)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||||
|
if (isValidTarget(targetSquare)) {
|
||||||
|
if (!wouldBeInCheck(row, col, newRow, newCol)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
if (targetSquare.textContent !== '') break;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
newRow += dx;
|
||||||
|
newCol += dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showKnightMoves(row, col) {
|
||||||
|
const moves = [
|
||||||
|
[-2, -1], [-2, 1], [-1, -2], [-1, 2],
|
||||||
|
[1, -2], [1, 2], [2, -1], [2, 1]
|
||||||
|
];
|
||||||
|
for (const [dx, dy] of moves) {
|
||||||
|
const newRow = row + dx;
|
||||||
|
const newCol = col + dy;
|
||||||
|
if (isValidMove(newRow, newCol)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||||
|
if (isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showPawnMoves(row, col, isWhite) {
|
||||||
|
const direction = isWhite ? -1 : 1;
|
||||||
|
const startRow = isWhite ? 6 : 1;
|
||||||
|
|
||||||
|
// Move forward
|
||||||
|
let newRow = row + direction;
|
||||||
|
if (isValidMove(newRow, col)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${col}"]`);
|
||||||
|
if (targetSquare.textContent === '' && !wouldBeInCheck(row, col, newRow, col)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
|
||||||
|
// Double move from starting position
|
||||||
|
if (row === startRow) {
|
||||||
|
newRow = row + 2 * direction;
|
||||||
|
const doubleSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${col}"]`);
|
||||||
|
if (doubleSquare.textContent === '' && !wouldBeInCheck(row, col, newRow, col)) {
|
||||||
|
doubleSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture diagonally
|
||||||
|
for (const dcol of [-1, 1]) {
|
||||||
|
newRow = row + direction;
|
||||||
|
const newCol = col + dcol;
|
||||||
|
if (isValidMove(newRow, newCol)) {
|
||||||
|
const targetSquare = document.querySelector(`.square[data-row="${newRow}"][data-col="${newCol}"]`);
|
||||||
|
if (targetSquare.textContent !== '' && isValidTarget(targetSquare) && !wouldBeInCheck(row, col, newRow, newCol)) {
|
||||||
|
targetSquare.classList.add('possible-move');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidMove(row, col) {
|
||||||
|
return row >= 0 && row < 8 && col >= 0 && col < 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidTarget(square) {
|
||||||
|
const piece = square.textContent;
|
||||||
|
if (!piece) return true;
|
||||||
|
if (currentPlayer === 'white') {
|
||||||
|
return piece.charCodeAt(0) >= 9818;
|
||||||
|
} else {
|
||||||
|
return piece.charCodeAt(0) <= 9817;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearPossibleMoves() {
|
||||||
|
document.querySelectorAll('.possible-move').forEach(square => square.classList.remove('possible-move'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSelection() {
|
||||||
|
if (selectedPiece) {
|
||||||
|
selectedPiece.classList.remove('selected');
|
||||||
|
selectedPiece = null;
|
||||||
|
clearPossibleMoves();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function movePiece(fromSquare, toSquare) {
|
||||||
|
toSquare.textContent = fromSquare.textContent;
|
||||||
|
fromSquare.textContent = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGameBoard(fromRow, fromCol, toRow, toCol) {
|
||||||
|
gameBoard[toRow][toCol] = gameBoard[fromRow][fromCol];
|
||||||
|
gameBoard[fromRow][fromCol] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTurnIndicator() {
|
||||||
|
turnIndicator.textContent = `${currentPlayer.charAt(0).toUpperCase() + currentPlayer.slice(1)}'s turn`;
|
||||||
|
turnIndicator.style.color = currentPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScore(capturedPiece) {
|
||||||
|
if (capturedPiece) {
|
||||||
|
const points = pieceValues[capturedPiece] || 0;
|
||||||
|
if (currentPlayer === 'white') {
|
||||||
|
whiteScore += points;
|
||||||
|
} else {
|
||||||
|
blackScore += points;
|
||||||
|
}
|
||||||
|
whiteScoreElement.textContent = `White: ${whiteScore}`;
|
||||||
|
blackScoreElement.textContent = `Black: ${blackScore}`;
|
||||||
|
updateLeaderboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLeaderboard() {
|
||||||
|
leaderboard = [
|
||||||
|
{ name: 'White', score: whiteScore },
|
||||||
|
{ name: 'Black', score: blackScore },
|
||||||
|
...leaderboard.filter(entry => entry.name !== 'White' && entry.name !== 'Black')
|
||||||
|
];
|
||||||
|
leaderboard.sort((a, b) => b.score - a.score);
|
||||||
|
leaderboard = leaderboard.slice(0, 5); // Keep only top 5
|
||||||
|
|
||||||
|
leaderboardList.innerHTML = '';
|
||||||
|
leaderboard.forEach(entry => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = `${entry.name}: ${entry.score}`;
|
||||||
|
leaderboardList.appendChild(li);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInCheck(color) {
|
||||||
|
const king = color === 'white' ? '♔' : '♚';
|
||||||
|
let kingPosition;
|
||||||
|
|
||||||
|
// Find the king's position
|
||||||
|
for (let row = 0; row < 8; row++) {
|
||||||
|
for (let col = 0; col < 8; col++) {
|
||||||
|
if (gameBoard[row][col] === king) {
|
||||||
|
kingPosition = { row, col };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (kingPosition) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any opponent's piece can attack the king
|
||||||
|
for (let row = 0; row < 8; row++) {
|
||||||
|
for (let col = 0; col < 8; col++) {
|
||||||
|
const piece = gameBoard[row][col];
|
||||||
|
if (piece && ((color === 'white' && piece.charCodeAt(0) >= 9818) || (color === 'black' && piece.charCodeAt(0) <= 9817))) {
|
||||||
|
if (canAttack(row, col, kingPosition.row, kingPosition.col)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canAttack(fromRow, fromCol, toRow, toCol) {
|
||||||
|
const piece = gameBoard[fromRow][fromCol];
|
||||||
|
switch (piece) {
|
||||||
|
case '♔': case '♚': // King
|
||||||
|
return Math.abs(fromRow - toRow) <= 1 && Math.abs(fromCol - toCol) <= 1;
|
||||||
|
case '♕': case '♛': // Queen
|
||||||
|
return canAttackStraight(fromRow, fromCol, toRow, toCol) || canAttackDiagonal(fromRow, fromCol, toRow, toCol);
|
||||||
|
case '♖': case '♜': // Rook
|
||||||
|
return canAttackStraight(fromRow, fromCol, toRow, toCol);
|
||||||
|
case '♗': case '♝': // Bishop
|
||||||
|
return canAttackDiagonal(fromRow, fromCol, toRow, toCol);
|
||||||
|
case '♘': case '♞': // Knight
|
||||||
|
const dx = Math.abs(fromRow - toRow);
|
||||||
|
const dy = Math.abs(fromCol - toCol);
|
||||||
|
return (dx === 2 && dy === 1) || (dx === 1 && dy === 2);
|
||||||
|
case '♙': case '♟': // Pawn
|
||||||
|
const direction = piece === '♙' ? -1 : 1;
|
||||||
|
return (fromCol === toCol && fromRow + direction === toRow) ||
|
||||||
|
(Math.abs(fromCol - toCol) === 1 && fromRow + direction === toRow);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canAttackStraight(fromRow, fromCol, toRow, toCol) {
|
||||||
|
if (fromRow === toRow) {
|
||||||
|
const step = fromCol < toCol ? 1 : -1;
|
||||||
|
for (let col = fromCol + step; col !== toCol; col += step) {
|
||||||
|
if (gameBoard[fromRow][col] !== '') return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (fromCol === toCol) {
|
||||||
|
const step = fromRow < toRow ? 1 : -1;
|
||||||
|
for (let row = fromRow + step; row !== toRow; row += step) {
|
||||||
|
if (gameBoard[row][fromCol] !== '') return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canAttackDiagonal(fromRow, fromCol, toRow, toCol) {
|
||||||
|
if (Math.abs(fromRow - toRow) === Math.abs(fromCol - toCol)) {
|
||||||
|
const rowStep = fromRow < toRow ? 1 : -1;
|
||||||
|
const colStep = fromCol < toCol ? 1 : -1;
|
||||||
|
let row = fromRow + rowStep;
|
||||||
|
let col = fromCol + colStep;
|
||||||
|
while (row !== toRow && col !== toCol) {
|
||||||
|
if (gameBoard[row][col] !== '') return false;
|
||||||
|
row += rowStep;
|
||||||
|
col += colStep;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wouldBeInCheck(fromRow, fromCol, toRow, toCol) {
|
||||||
|
const tempPiece = gameBoard[toRow][toCol];
|
||||||
|
gameBoard[toRow][toCol] = gameBoard[fromRow][fromCol];
|
||||||
|
gameBoard[fromRow][fromCol] = '';
|
||||||
|
const inCheck = isInCheck(currentPlayer);
|
||||||
|
gameBoard[fromRow][fromCol] = gameBoard[toRow][toCol];
|
||||||
|
gameBoard[toRow][toCol] = tempPiece;
|
||||||
|
return inCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCheckmate(color) {
|
||||||
|
for (let fromRow = 0; fromRow < 8; fromRow++) {
|
||||||
|
for (let fromCol = 0; fromCol < 8; fromCol++) {
|
||||||
|
const piece = gameBoard[fromRow][fromCol];
|
||||||
|
if (piece && ((color === 'white' && piece.charCodeAt(0) <= 9817) || (color === 'black' && piece.charCodeAt(0) >= 9818))) {
|
||||||
|
for (let toRow = 0; toRow < 8; toRow++) {
|
||||||
|
for (let toCol = 0; toCol < 8; toCol++) {
|
||||||
|
if (canAttack(fromRow, fromCol, toRow, toCol) && !wouldBeInCheck(fromRow, fromCol, toRow, toCol)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveWinner() {
|
||||||
|
const winnerName = winnerNameInput.value.trim();
|
||||||
|
if (winnerName) {
|
||||||
|
const winner = leaderboard.find(entry => entry.name === winnerName);
|
||||||
|
if (winner) {
|
||||||
|
winner.score += 1;
|
||||||
|
} else {
|
||||||
|
leaderboard.push({ name: winnerName, score: 1 });
|
||||||
|
}
|
||||||
|
updateLeaderboard();
|
||||||
|
winnerInput.style.display = 'none';
|
||||||
|
winnerNameInput.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCredits() {
|
||||||
|
credits.style.display = 'flex';
|
||||||
|
creditMusic.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
function playAgain() {
|
||||||
|
credits.style.display = 'none';
|
||||||
|
creditMusic.pause();
|
||||||
|
creditMusic.currentTime = 0;
|
||||||
|
resetGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetGame() {
|
||||||
|
board.innerHTML = '';
|
||||||
|
gameStatus.textContent = '';
|
||||||
|
winnerInput.style.display = 'none';
|
||||||
|
selectedPiece = null;
|
||||||
|
currentPlayer = 'white';
|
||||||
|
whiteScore = 0;
|
||||||
|
blackScore = 0;
|
||||||
|
gameBoard = [];
|
||||||
|
createBoard();
|
||||||
|
updateTurnIndicator();
|
||||||
|
updateScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
createBoard();
|
||||||
|
updateLeaderboard();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -8,6 +8,12 @@ import uuid
|
|||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
import yaml
|
import yaml
|
||||||
|
from lollms.security import check_access
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import yaml
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
lollmsElfServer: LOLLMSWebUI = LOLLMSWebUI.get_instance()
|
lollmsElfServer: LOLLMSWebUI = LOLLMSWebUI.get_instance()
|
||||||
@ -76,25 +82,28 @@ async def get_app_code(app_name: str, auth: AuthRequest):
|
|||||||
|
|
||||||
@router.post("/install/{app_name}")
|
@router.post("/install/{app_name}")
|
||||||
async def install_app(app_name: str, auth: AuthRequest):
|
async def install_app(app_name: str, auth: AuthRequest):
|
||||||
|
check_access(lollmsElfServer, auth.client_id)
|
||||||
|
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||||
|
|
||||||
# Create the app directory
|
# Create the app directory
|
||||||
app_path = lollmsElfServer.lollms_paths.apps_zoo_path / app_name
|
app_path = lollmsElfServer.lollms_paths.apps_zoo_path/app_name # Adjust the path as needed
|
||||||
os.makedirs(app_path, exist_ok=True)
|
os.makedirs(app_path, exist_ok=True)
|
||||||
|
|
||||||
# Define the URLs for the files to download
|
# Define the local paths for the files to copy
|
||||||
files_to_download = {
|
files_to_copy = {
|
||||||
"icon.png": f"https://github.com/ParisNeo/lollms_apps_zoo/raw/main/{app_name}/icon.png",
|
"icon.png": REPO_DIR/app_name/"icon.png",
|
||||||
"description.yaml": f"https://raw.githubusercontent.com/ParisNeo/lollms_apps_zoo/main/{app_name}/description.yaml",
|
"description.yaml": REPO_DIR/app_name/"description.yaml",
|
||||||
"index.html": f"https://raw.githubusercontent.com/ParisNeo/lollms_apps_zoo/main/{app_name}/index.html"
|
"index.html": REPO_DIR/app_name/"index.html"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Download each file
|
# Copy each file from the local repo
|
||||||
for file_name, url in files_to_download.items():
|
for file_name, local_path in files_to_copy.items():
|
||||||
response = requests.get(url)
|
if local_path.exists():
|
||||||
if response.status_code == 200:
|
with open(local_path, 'rb') as src_file:
|
||||||
with open(app_path / file_name, 'wb') as f:
|
with open(app_path/file_name, 'wb') as dest_file:
|
||||||
f.write(response.content)
|
dest_file.write(src_file.read())
|
||||||
else:
|
else:
|
||||||
raise HTTPException(status_code=404, detail=f"{file_name} not found on GitHub")
|
raise HTTPException(status_code=404, detail=f"{file_name} not found in the local repository")
|
||||||
|
|
||||||
return {"message": f"App {app_name} installed successfully."}
|
return {"message": f"App {app_name} installed successfully."}
|
||||||
|
|
||||||
@ -108,28 +117,43 @@ async def uninstall_app(app_name: str, auth: AuthRequest):
|
|||||||
raise HTTPException(status_code=404, detail="App not found")
|
raise HTTPException(status_code=404, detail="App not found")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/github/apps")
|
REPO_URL = "https://github.com/ParisNeo/lollms_apps_zoo.git"
|
||||||
async def fetch_github_apps():
|
|
||||||
github_repo_url = "https://api.github.com/repos/ParisNeo/lollms_apps_zoo/contents"
|
|
||||||
response = requests.get(github_repo_url)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
|
def clone_repo():
|
||||||
|
REPO_DIR = Path(lollmsElfServer.lollms_paths.personal_path) / "apps_zoo_repo"
|
||||||
|
|
||||||
|
# Check if the directory exists and if it is empty
|
||||||
|
if REPO_DIR.exists():
|
||||||
|
if any(REPO_DIR.iterdir()): # Check if the directory is not empty
|
||||||
|
print(f"Directory {REPO_DIR} is not empty. Aborting clone.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
REPO_DIR.mkdir(parents=True, exist_ok=True) # Create the directory if it doesn't exist
|
||||||
|
|
||||||
|
# Clone the repository
|
||||||
|
subprocess.run(["git", "clone", REPO_URL, str(REPO_DIR)], check=True)
|
||||||
|
print(f"Repository cloned into {REPO_DIR}")
|
||||||
|
|
||||||
|
def pull_repo():
|
||||||
|
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||||
|
subprocess.run(["git", "-C", str(REPO_DIR), "pull"], check=True)
|
||||||
|
|
||||||
|
def load_apps_data():
|
||||||
apps = []
|
apps = []
|
||||||
for item in response.json():
|
REPO_DIR = lollmsElfServer.lollms_paths.personal_path/"apps_zoo_repo"
|
||||||
if item['type'] == 'dir':
|
for item in os.listdir(REPO_DIR):
|
||||||
app_name = item['name']
|
item_path = os.path.join(REPO_DIR, item)
|
||||||
description_url = f"https://api.github.com/repos/ParisNeo/lollms_apps_zoo/contents/{app_name}/description.yaml"
|
if os.path.isdir(item_path):
|
||||||
icon_url = f"https://github.com/ParisNeo/lollms_apps_zoo/blob/main/{app_name}/icon.png?raw=true"
|
description_path = os.path.join(item_path, "description.yaml")
|
||||||
|
icon_url = f"https://github.com/ParisNeo/lollms_apps_zoo/blob/main/{item}/icon.png?raw=true"
|
||||||
# Fetch description.yaml
|
|
||||||
description_response = requests.get(description_url)
|
|
||||||
description_data = {}
|
|
||||||
if description_response.status_code == 200:
|
|
||||||
description_data = yaml.safe_load(requests.get(description_url).text)
|
|
||||||
|
|
||||||
|
if os.path.exists(description_path):
|
||||||
|
with open(description_path, 'r') as file:
|
||||||
|
description_data = yaml.safe_load(file)
|
||||||
apps.append(AppInfo(
|
apps.append(AppInfo(
|
||||||
uid=str(uuid.uuid4()),
|
uid=str(uuid.uuid4()),
|
||||||
name=app_name,
|
name=item,
|
||||||
icon=icon_url,
|
icon=icon_url,
|
||||||
description=description_data.get('description', ''),
|
description=description_data.get('description', ''),
|
||||||
author=description_data.get('author', ''),
|
author=description_data.get('author', ''),
|
||||||
@ -137,9 +161,19 @@ async def fetch_github_apps():
|
|||||||
model_name=description_data.get('model_name', ''),
|
model_name=description_data.get('model_name', ''),
|
||||||
disclaimer=description_data.get('disclaimer', 'No disclaimer provided.')
|
disclaimer=description_data.get('disclaimer', 'No disclaimer provided.')
|
||||||
))
|
))
|
||||||
|
return apps
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/github/apps")
|
||||||
|
async def fetch_github_apps():
|
||||||
|
try:
|
||||||
|
clone_repo()
|
||||||
|
pull_repo()
|
||||||
|
apps = load_apps_data()
|
||||||
return {"apps": apps}
|
return {"apps": apps}
|
||||||
else:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=response.status_code, detail="Failed to fetch apps from GitHub")
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@router.get("/apps/{app_name}/icon")
|
@router.get("/apps/{app_name}/icon")
|
||||||
async def get_app_icon(app_name: str):
|
async def get_app_icon(app_name: str):
|
||||||
|
1
lollms_apps_zoo
Submodule
1
lollms_apps_zoo
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 10ea733d4354e9cb3b293d7c6393c60e7dfb3504
|
8
web/dist/assets/index-0db1108c.css
vendored
Normal file
8
web/dist/assets/index-0db1108c.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
web/dist/assets/index-4578000a.css
vendored
8
web/dist/assets/index-4578000a.css
vendored
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
@ -6,8 +6,8 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>LoLLMS WebUI</title>
|
<title>LoLLMS WebUI</title>
|
||||||
<script type="module" crossorigin src="/assets/index-b768925e.js"></script>
|
<script type="module" crossorigin src="/assets/index-11efc659.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/index-4578000a.css">
|
<link rel="stylesheet" href="/assets/index-0db1108c.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-zoo w-full">
|
<div class="app-zoo w-full">
|
||||||
<button @click="fetchGithubApps" class="bg-green-500 text-white px-4 py-2 rounded mb-4">Refresh apps from github</button>
|
<button @click="fetchGithubApps" class="bg-green-500 text-white px-4 py-2 rounded mb-4">Refresh apps from GitHub</button>
|
||||||
|
<div v-if="loading" class="loading-animation">Loading...</div>
|
||||||
<div class="app-list grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full">
|
<div class="app-list grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 w-full">
|
||||||
<div
|
<div
|
||||||
v-for="app in combinedApps"
|
v-for="app in combinedApps"
|
||||||
@ -8,7 +9,7 @@
|
|||||||
class="app-card border rounded-lg shadow-lg p-4 cursor-pointer hover:shadow-xl transition"
|
class="app-card border rounded-lg shadow-lg p-4 cursor-pointer hover:shadow-xl transition"
|
||||||
@click="handleAppClick(app)"
|
@click="handleAppClick(app)"
|
||||||
>
|
>
|
||||||
<img :src="app.icon" alt="App Icon" class="w-16 h-16 mx-auto mb-2" />
|
<img :src="app.icon" alt="App Icon" class="app-icon w-16 h-16 mx-auto mb-2 rounded-full border border-gray-300" />
|
||||||
<p class="text-center font-semibold">{{ app.name }}</p>
|
<p class="text-center font-semibold">{{ app.name }}</p>
|
||||||
<p class="text-center text-sm text-gray-600">Author: {{ app.author }}</p>
|
<p class="text-center text-sm text-gray-600">Author: {{ app.author }}</p>
|
||||||
<p class="text-center text-sm text-gray-600">Version: {{ app.version }}</p>
|
<p class="text-center text-sm text-gray-600">Version: {{ app.version }}</p>
|
||||||
@ -28,6 +29,7 @@
|
|||||||
<iframe v-if="appCode" :srcdoc="appCode" class="app-frame w-full h-full border-none"></iframe>
|
<iframe v-if="appCode" :srcdoc="appCode" class="app-frame w-full h-full border-none"></iframe>
|
||||||
<p v-else class="text-center text-red-500">Please install this app to view its code.</p>
|
<p v-else class="text-center text-red-500">Please install this app to view its code.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="message" class="message" :class="{ success: successMessage, error: !successMessage }">{{ message }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -41,6 +43,9 @@ export default {
|
|||||||
githubApps: [],
|
githubApps: [],
|
||||||
selectedApp: null,
|
selectedApp: null,
|
||||||
appCode: '',
|
appCode: '',
|
||||||
|
loading: false,
|
||||||
|
message: '',
|
||||||
|
successMessage: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -59,13 +64,28 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async fetchApps() {
|
async fetchApps() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
const response = await axios.get('/apps');
|
const response = await axios.get('/apps');
|
||||||
this.apps = response.data;
|
this.apps = response.data;
|
||||||
|
this.showMessage('Refresh successful!', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.showMessage('Failed to refresh apps.', false);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async fetchGithubApps() {
|
async fetchGithubApps() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
const response = await axios.get('/github/apps');
|
const response = await axios.get('/github/apps');
|
||||||
this.githubApps = response.data.apps;
|
this.githubApps = response.data.apps;
|
||||||
this.fetchApps(); // Refresh the app list after fetching GitHub apps
|
await this.fetchApps(); // Refresh the app list after fetching GitHub apps
|
||||||
|
} catch (error) {
|
||||||
|
this.showMessage('Failed to refresh GitHub apps.', false);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async handleAppClick(app) {
|
async handleAppClick(app) {
|
||||||
if (app.installed) {
|
if (app.installed) {
|
||||||
@ -83,18 +103,41 @@ export default {
|
|||||||
this.appCode = '';
|
this.appCode = '';
|
||||||
},
|
},
|
||||||
async installApp(appName) {
|
async installApp(appName) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
await axios.post(`/install/${appName}`, {
|
await axios.post(`/install/${appName}`, {
|
||||||
client_id: this.$store.state.client_id,
|
client_id: this.$store.state.client_id,
|
||||||
});
|
});
|
||||||
|
this.showMessage('Installation succeeded!', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.showMessage('Installation failed.', false);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
this.fetchApps(); // Refresh the app list
|
this.fetchApps(); // Refresh the app list
|
||||||
this.fetchGithubApps(); // Refresh GitHub apps
|
this.fetchGithubApps(); // Refresh GitHub apps
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async uninstallApp(appName) {
|
async uninstallApp(appName) {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
await axios.post(`/uninstall/${appName}`, {
|
await axios.post(`/uninstall/${appName}`, {
|
||||||
client_id: this.$store.state.client_id,
|
client_id: this.$store.state.client_id,
|
||||||
});
|
});
|
||||||
|
this.showMessage('Uninstallation succeeded!', true);
|
||||||
|
} catch (error) {
|
||||||
|
this.showMessage('Uninstallation failed.', false);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
this.fetchApps(); // Refresh the app list
|
this.fetchApps(); // Refresh the app list
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
showMessage(msg, success) {
|
||||||
|
this.message = msg;
|
||||||
|
this.successMessage = success;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.message = '';
|
||||||
|
}, 3000); // Clear message after 3 seconds
|
||||||
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchGithubApps(); // Fetch GitHub apps when the component is mounted
|
this.fetchGithubApps(); // Fetch GitHub apps when the component is mounted
|
||||||
@ -115,9 +158,41 @@ export default {
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
.app-icon {
|
||||||
|
border-radius: 50%; /* Rounded edges */
|
||||||
|
border: 2px solid #d1d5db; /* Light gray border */
|
||||||
|
}
|
||||||
.app-frame {
|
.app-frame {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
.loading-animation {
|
||||||
|
animation: fadeIn 1s ease-in-out;
|
||||||
|
margin: 20px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #4a5568; /* Gray color */
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: opacity 0.5s ease;
|
||||||
|
}
|
||||||
|
.message.success {
|
||||||
|
background-color: #c6f6d5; /* Green background */
|
||||||
|
color: #2f855a; /* Dark green text */
|
||||||
|
}
|
||||||
|
.message.error {
|
||||||
|
background-color: #fed7d7; /* Red background */
|
||||||
|
color: #c53030; /* Dark red text */
|
||||||
|
}
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user