mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-11-30 01:44:58 +03:00
Removed UI folder
This commit is contained in:
parent
d4afda7189
commit
6ab2d13a47
@ -1,27 +0,0 @@
|
|||||||
/* eslint-env node */
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
// root: true,
|
|
||||||
// env: { browser: true, es2020: true },
|
|
||||||
// extends: [
|
|
||||||
// 'eslint:recommended',
|
|
||||||
// 'plugin:@typescript-eslint/recommended',
|
|
||||||
// 'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
|
||||||
// 'plugin:react-hooks/recommended',
|
|
||||||
// ],
|
|
||||||
// parser: '@typescript-eslint/parser',
|
|
||||||
// parserOptions: {
|
|
||||||
// ecmaVersion: 'latest',
|
|
||||||
// sourceType: 'module',
|
|
||||||
// project: true,
|
|
||||||
// tsconfigRootDir: __dirname,
|
|
||||||
// },
|
|
||||||
// plugins: ['react-refresh'],
|
|
||||||
// rules: {
|
|
||||||
// 'react-refresh/only-export-components': [
|
|
||||||
// 'warn',
|
|
||||||
// { allowConstantExport: true },
|
|
||||||
// ],
|
|
||||||
// '@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
// },
|
|
||||||
}
|
|
26
modules/chess/ui/.gitignore
vendored
26
modules/chess/ui/.gitignore
vendored
@ -1,26 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
.env
|
|
@ -1,3 +0,0 @@
|
|||||||
# chess-ui
|
|
||||||
|
|
||||||
The UI for the Uqbar chess app. This UI is available at /chess:chess:uqbar/ on any Uqbar node.
|
|
File diff suppressed because one or more lines are too long
4160
modules/chess/ui/package-lock.json
generated
4160
modules/chess/ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "api-maestro-ui",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.1.0",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"start": "vite",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"copy": "mkdir -p ../pkg/ui && rm -rf ../pkg/ui/* && cp -r dist/* ../pkg/ui/",
|
|
||||||
"build:copy": "npm run build && npm run copy",
|
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@react-oauth/google": "^0.11.0",
|
|
||||||
"@types/node-forge": "^1.3.5",
|
|
||||||
"@uqbar/client-encryptor-api": "^0.2.1",
|
|
||||||
"buffer": "^6.0.3",
|
|
||||||
"chess.js": "^1.0.0-beta.6",
|
|
||||||
"moment": "^2.29.4",
|
|
||||||
"node-forge": "^1.3.1",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-chessboard": "^4.2.1",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.14.1",
|
|
||||||
"zustand": "^4.3.9"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^20.4.2",
|
|
||||||
"@types/react": "^18.2.14",
|
|
||||||
"@types/react-dom": "^18.2.6",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
||||||
"@typescript-eslint/parser": "^5.61.0",
|
|
||||||
"@vitejs/plugin-react": "^4.0.1",
|
|
||||||
"autoprefixer": "^10.4.14",
|
|
||||||
"eslint": "^8.44.0",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.1",
|
|
||||||
"postcss": "^8.4.25",
|
|
||||||
"tailwindcss": "^3.3.2",
|
|
||||||
"typescript": "^5.0.2",
|
|
||||||
"vite": "^4.4.0",
|
|
||||||
"vite-plugin-mkcert": "^1.16.0"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#root {
|
|
||||||
max-width: 1280px;
|
|
||||||
margin: 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 6em;
|
|
||||||
padding: 1.5em;
|
|
||||||
will-change: filter;
|
|
||||||
transition: filter 300ms;
|
|
||||||
}
|
|
||||||
.logo:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
|
||||||
}
|
|
||||||
.logo.react:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
a:nth-of-type(2) .logo {
|
|
||||||
animation: logo-spin infinite 20s linear;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.read-the-docs {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
import { FormEvent, useCallback, useEffect, useMemo, useState, MouseEvent } from 'react'
|
|
||||||
import { Chess } from "chess.js";
|
|
||||||
import { Chessboard } from "react-chessboard";
|
|
||||||
import UqbarEncryptorApi from '@uqbar/client-encryptor-api'
|
|
||||||
import useChessStore, { Game } from './store';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
var window: Window & typeof globalThis;
|
|
||||||
var our: { node: string, process: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
import './App.css'
|
|
||||||
|
|
||||||
let inited = false
|
|
||||||
|
|
||||||
interface SelectedGame extends Game {
|
|
||||||
game: Chess
|
|
||||||
}
|
|
||||||
|
|
||||||
const isTurn = (game: Game, node: string) => (game.turns || 0) % 2 === 0 ? node === game.white : node === game.black
|
|
||||||
|
|
||||||
const BASE_URL = import.meta.env.BASE_URL;
|
|
||||||
if (window.our) window.our.process = BASE_URL?.replace("/", "");
|
|
||||||
|
|
||||||
const PROXY_TARGET = `${(import.meta.env.VITE_NODE_URL || "http://localhost:8080")}${BASE_URL}`;
|
|
||||||
|
|
||||||
// This env also has BASE_URL which should match the process + package name
|
|
||||||
const WEBSOCKET_URL = import.meta.env.DEV
|
|
||||||
? `${PROXY_TARGET.replace('http', 'ws')}`
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
const { games, handleWsMessage, set } = useChessStore()
|
|
||||||
const [screen, setScreen] = useState('new')
|
|
||||||
const [newGame, setNewGame] = useState('')
|
|
||||||
|
|
||||||
const game: SelectedGame | undefined = useMemo(() => games[screen] ? ({ ...games[screen], game: new Chess(games[screen].board) }) : undefined, [games, screen])
|
|
||||||
const currentTurn = useMemo(() => (game?.turns || 0) % 2 === 0 ? `${game?.white} (white)` : `${game?.black} (black)`, [game])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!inited) {
|
|
||||||
inited = true
|
|
||||||
|
|
||||||
new UqbarEncryptorApi({
|
|
||||||
uri: WEBSOCKET_URL,
|
|
||||||
nodeId: window.our.node,
|
|
||||||
processId: window.our.process,
|
|
||||||
onMessage: handleWsMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(`${BASE_URL}/games`).then(res => res.json()).then((games) => {
|
|
||||||
set({ games })
|
|
||||||
}).catch(console.error)
|
|
||||||
|
|
||||||
}, []) // eslint-disable-line
|
|
||||||
|
|
||||||
const startNewGame = useCallback(async (e: FormEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
try {
|
|
||||||
const createdGame = await fetch(`${BASE_URL}/games`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ id: newGame })
|
|
||||||
}).then(r => {
|
|
||||||
if (r.status === 409) {
|
|
||||||
if (games[newGame]) {
|
|
||||||
setScreen(newGame)
|
|
||||||
} else {
|
|
||||||
alert('Game already exists, please refresh the page and select it.')
|
|
||||||
}
|
|
||||||
throw new Error('Game already exists')
|
|
||||||
} else if (r.status === 503) {
|
|
||||||
alert(`${newGame} may be offline, please confirm it is online and try again.`)
|
|
||||||
throw new Error('Player offline')
|
|
||||||
} else if (r.status === 400) {
|
|
||||||
alert('Please enter a valid player ID')
|
|
||||||
throw new Error('Invalid player ID')
|
|
||||||
} else if (r.status > 399) {
|
|
||||||
alert('There was an error creating the game. Please try again.')
|
|
||||||
throw new Error('Error creating game')
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.json()
|
|
||||||
})
|
|
||||||
|
|
||||||
const allGames = { ...games }
|
|
||||||
allGames[createdGame.id] = createdGame
|
|
||||||
set({ games: allGames })
|
|
||||||
setScreen(newGame)
|
|
||||||
setNewGame('')
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}, [games, newGame, setNewGame, set])
|
|
||||||
|
|
||||||
const onDrop = useCallback((sourceSquare: string, targetSquare: string) => {
|
|
||||||
if (!game || !isTurn(game, window.our.node)) return false
|
|
||||||
|
|
||||||
const move = {
|
|
||||||
from: sourceSquare,
|
|
||||||
to: targetSquare,
|
|
||||||
promotion: "q", // always promote to a queen for example simplicity
|
|
||||||
}
|
|
||||||
const gameCopy = { ...game };
|
|
||||||
const result = gameCopy.game.move(move);
|
|
||||||
|
|
||||||
if (result === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gameCopy.board = gameCopy.game.fen()
|
|
||||||
const allGames = { ...games }
|
|
||||||
allGames[game.id] = gameCopy
|
|
||||||
set({ games: allGames })
|
|
||||||
|
|
||||||
fetch(`${BASE_URL}/games`, {
|
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify({ id: game.id, move: sourceSquare + targetSquare })
|
|
||||||
}).then(r => r.json())
|
|
||||||
.then((updatedGame) => {
|
|
||||||
const allGames = { ...games }
|
|
||||||
allGames[game.id] = updatedGame
|
|
||||||
set({ games: allGames })
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
alert('There was an error making your move. Please try again')
|
|
||||||
// reset the board
|
|
||||||
const allGames = { ...games }
|
|
||||||
const gameCopy = { ...game }
|
|
||||||
gameCopy.game.undo()
|
|
||||||
allGames[game.id] = gameCopy
|
|
||||||
set({ games: allGames })
|
|
||||||
})
|
|
||||||
|
|
||||||
return true
|
|
||||||
}, [game, games, set])
|
|
||||||
|
|
||||||
const resignGame = useCallback((e: MouseEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
if (!game) return
|
|
||||||
|
|
||||||
if (!window.confirm('Are you sure you want to resign this game?')) return
|
|
||||||
|
|
||||||
fetch(`${BASE_URL}/games?id=${game.id}`, {
|
|
||||||
method: 'DELETE',
|
|
||||||
}).then(r => r.json())
|
|
||||||
.then((updatedGame) => {
|
|
||||||
const allGames = { ...games }
|
|
||||||
allGames[game.id] = updatedGame
|
|
||||||
set({ games: allGames })
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
alert('There was an error resigning the game. Please try again')
|
|
||||||
})
|
|
||||||
}, [game])
|
|
||||||
|
|
||||||
const rematchGame = useCallback(async (e: MouseEvent) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
if (!game) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
const createdGame = await fetch(`${BASE_URL}/games`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ id: game.id })
|
|
||||||
}).then(r => r.json())
|
|
||||||
|
|
||||||
const allGames = { ...games }
|
|
||||||
allGames[createdGame.id] = createdGame
|
|
||||||
set({ games: allGames })
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
alert('You could not create the game. Please make sure your current game with this player (if any) has ended and try again.')
|
|
||||||
}
|
|
||||||
}, [game])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex flex-col justify-center items-center'>
|
|
||||||
<div className='flex flex-col justify-center' style={{ maxHeight: '100vh', maxWidth: '800px', width: '100%', position: 'relative' }}>
|
|
||||||
<a href="/" className='absolute top-6 left-0 m-4' style={{ fontSize: 24, color: 'white' }} onClick={e => { e.preventDefault(); window.history.back() }}>
|
|
||||||
◀ Back
|
|
||||||
</a>
|
|
||||||
<h1 className='m-4'>Chess by Uqbar</h1>
|
|
||||||
<div className='flex flex-row justify-center items-center h-screen border rounded'>
|
|
||||||
{Object.keys(games).length > 0 && <div className='flex flex-col border-r' style={{ width: '25%', height: '100%' }}>
|
|
||||||
<h3 className='m-2'>Games</h3>
|
|
||||||
<button className='bg-green-600 hover:bg-green-800 text-white font-bold py-2 px-4 m-2 rounded' onClick={() => setScreen('new')}>New</button>
|
|
||||||
<div className='flex flex-col overflow-scroll'>
|
|
||||||
{Object.values(games).map(game => (
|
|
||||||
<div key={game?.id} onClick={() => setScreen(game?.id)}
|
|
||||||
className={`game-entry m-2 ${screen !== game?.id && isTurn(game, window.our.node) ? 'is-turn' : ''} ${screen === game?.id ? 'selected' : ''} ${game?.ended ? 'ended' : ''}`}
|
|
||||||
>
|
|
||||||
{game?.id}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>}
|
|
||||||
<div className='flex flex-col justify-center items-center' style={{ width: '75%' }}>
|
|
||||||
{screen === 'new' || !game ? (
|
|
||||||
<>
|
|
||||||
<h2 className='mb-2'>Start New Game</h2>
|
|
||||||
<h4 className='mb-2'>(game creator will be white)</h4>
|
|
||||||
<form onSubmit={startNewGame} className='flex flex-col justify-center mb-40' style={{ maxWidth: 400 }}>
|
|
||||||
<label className='mb-2' style={{ alignSelf: 'flex-start', fontWeight: '600' }}>Player ID</label>
|
|
||||||
<input className='border rounded p-2 mb-2' style={{ color: 'black' }} type='text' placeholder='Player ID' value={newGame} onChange={e => setNewGame(e.target.value)} />
|
|
||||||
<button className='bg-green-600 hover:bg-green-800 text-white font-bold py-2 px-4 rounded' type="submit">Start Game</button>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className='flex flex-row justify-between items-center w-full px-4 pb-2'>
|
|
||||||
<h3>{screen}</h3>
|
|
||||||
<h4>{game?.ended ? 'Game Ended' : `Turn: ${currentTurn}`}</h4>
|
|
||||||
{game?.ended ? (
|
|
||||||
<button className='bg-green-600 hover:bg-green-800 text-white font-bold py-1 px-4 rounded' onClick={rematchGame}>Rematch</button>
|
|
||||||
) : (
|
|
||||||
<button className='bg-green-600 hover:bg-green-800 text-white font-bold py-1 px-4 rounded' onClick={resignGame}>Resign</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Chessboard position={game?.game.fen()} onPieceDrop={onDrop} boardOrientation={game?.white === window.our.node ? 'white' : 'black'} />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
@ -1,152 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
:root {
|
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
color: rgba(255, 255, 255, 0.87);
|
|
||||||
background-color: #242424;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
--uq-vlightpurple: #d9dcfc;
|
|
||||||
--uq-lightpurple: #c7cafa;
|
|
||||||
--uq-purple: #727bf2;
|
|
||||||
--uq-darkpurple: #5761ef;
|
|
||||||
--midnightpurp: #0a1170;
|
|
||||||
--forgottenpurp: #45475e;
|
|
||||||
--uq-lightpink: #f3ced2;
|
|
||||||
--uq-pink: #dd7c8a;
|
|
||||||
--uq-darkpink: #cd3c52;
|
|
||||||
--blush: #d55d6f;
|
|
||||||
--celeste: #adebe5;
|
|
||||||
--lturq: #6bdbd0;
|
|
||||||
--turq: #3acfc0;
|
|
||||||
--celadon: #21897e;
|
|
||||||
--deep-jungle: #14524c;
|
|
||||||
--old-mint: #659792;
|
|
||||||
--washed-gray: rgba(0, 0, 0, 0.03);
|
|
||||||
--light-gray: rgba(0, 0, 0, 0.1);
|
|
||||||
--medium-gray: rgba(0, 0, 0, 0.2);
|
|
||||||
--dark-gray: rgba(0, 0, 0, 0.5);
|
|
||||||
--charcoal: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
font-weight: 500;
|
|
||||||
color: #646cff;
|
|
||||||
text-decoration: inherit;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #535bf2;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
/* place-items: center; */
|
|
||||||
min-width: 320px;
|
|
||||||
min-height: 100vh;
|
|
||||||
color: white;
|
|
||||||
background-color: var(--midnightpurp);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
padding: 0.6em 1.2em;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: inherit;
|
|
||||||
background-color: #1a1a1a;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: border-color 0.25s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
border-color: #646cff;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
button:focus-visible {
|
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
|
||||||
:root {
|
|
||||||
color: #213547;
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: #747bff;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes colorChange {
|
|
||||||
0%, 100% {
|
|
||||||
background-color: var(--uq-purple); /* Start and end color */
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
background-color: var(--uq-pink); /* 2nd rhythmic change */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.game-entry {
|
|
||||||
width: calc(100% - 1em);
|
|
||||||
cursor: pointer;
|
|
||||||
color: white;
|
|
||||||
background-color: var(--uq-purple);
|
|
||||||
padding: 0.5em;
|
|
||||||
border-radius: 0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.game-entry.is-turn {
|
|
||||||
animation: colorChange 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.game-entry.selected {
|
|
||||||
background-color: var(--uq-darkpink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.game-entry.is-ended {
|
|
||||||
background-color: lightgray;
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.game-entry:hover {
|
|
||||||
background-color: var(--uq-pink);
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom/client'
|
|
||||||
import App from './App.tsx'
|
|
||||||
import './index.css'
|
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<App />
|
|
||||||
</React.StrictMode>,
|
|
||||||
)
|
|
@ -1,50 +0,0 @@
|
|||||||
import { create } from 'zustand'
|
|
||||||
import { persist, createJSONStorage } from 'zustand/middleware'
|
|
||||||
|
|
||||||
export interface Game {
|
|
||||||
id: string,
|
|
||||||
turns: number,
|
|
||||||
board: string, // FEN format of the board
|
|
||||||
white: string,
|
|
||||||
black: string,
|
|
||||||
ended: boolean,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Games {
|
|
||||||
[id: string]: Game
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ChessStore {
|
|
||||||
games: Games
|
|
||||||
handleWsMessage: (message: string) => void
|
|
||||||
set: (partial: ChessStore | Partial<ChessStore>) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
type WsMessage = { kind: 'game_update', data: Game }
|
|
||||||
|
|
||||||
const useChessStore = create<ChessStore>()(
|
|
||||||
persist(
|
|
||||||
(set, get) => ({
|
|
||||||
games: {},
|
|
||||||
handleWsMessage: (json: string) => {
|
|
||||||
try {
|
|
||||||
const { kind, data } = JSON.parse(json) as WsMessage
|
|
||||||
console.log(kind, data)
|
|
||||||
|
|
||||||
if (kind === 'game_update') {
|
|
||||||
set({ games: { ...get().games, [data.id]: data } })
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error parsing WebSocket message", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: 'chess', // unique name
|
|
||||||
storage: createJSONStorage(() => localStorage), // (optional) by default, 'localStorage' is used
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
export default useChessStore
|
|
@ -1,5 +0,0 @@
|
|||||||
export const genFetchRoute = (route: string) => {
|
|
||||||
return window.location.pathname.includes('/http-proxy/serve/') ?
|
|
||||||
`/http-proxy/serve/${window.location.pathname.split('/')[3]}/${route}`:
|
|
||||||
route
|
|
||||||
}
|
|
1
modules/chess/ui/src/vite-env.d.ts
vendored
1
modules/chess/ui/src/vite-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
@ -1,11 +0,0 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
export default {
|
|
||||||
content: [
|
|
||||||
"./index.html",
|
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
|
||||||
],
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Check if there are enough parameters provided.
|
|
||||||
if [ "$#" -ne 4 ]; then
|
|
||||||
echo "Usage: $0 <url> <node-id> <vfs-identifier> <destination_process>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
URL="$1"
|
|
||||||
NODE="$2"
|
|
||||||
IDENTIFIER="$3"
|
|
||||||
DEST_PROC="$4"
|
|
||||||
|
|
||||||
# Generate new vfs capabilities and give "read" to the destination process
|
|
||||||
curl "$URL/rpc/message" -H 'content-type: application/json' -d "{\"node\": \"$NODE\", \"process\": \"vfs\", \"inherit\": false, \"expects_response\": false, \"ipc\": \"{\\\"New\\\": {\\\"identifier\\\": \\\"$IDENTIFIER\\\"}}\", \"metadata\": null, \"context\": null, \"mime\": null, \"data\": null}"
|
|
||||||
curl "$URL/rpc/capabilities/transfer" -H 'content-type: application/json' -d "{\"destination_node\": \"$NODE\", \"destination_process\": \"$DEST_PROC\", \"node\": \"$NODE\", \"process\": \"vfs\", \"params\": \"{\\\"identifier\\\": \\\"$IDENTIFIER\\\",\\\"kind\\\":\\\"read\\\"}\"}"
|
|
@ -1,30 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Check if there are enough parameters provided.
|
|
||||||
if [ "$#" -ne 5 ]; then
|
|
||||||
echo "Usage: $0 <url> <node-id> <vfs-identifier> <index.html> <assets directory>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
URL="$1"
|
|
||||||
NODE="$2"
|
|
||||||
IDENTIFIER="$3"
|
|
||||||
INDEX_HTML="$4"
|
|
||||||
DIRECTORY="$5"
|
|
||||||
|
|
||||||
# Upload index.html
|
|
||||||
curl "$URL/rpc/message" -H 'content-type: application/json' -d "{\"node\": \"$NODE\", \"process\": \"vfs\", \"inherit\": false, \"expects_response\": false, \"ipc\": \"{\\\"Add\\\": {\\\"identifier\\\": \\\"$IDENTIFIER\\\", \\\"full_path\\\": \\\"/index.html\\\", \\\"entry_type\\\": {\\\"NewFile\\\": null}}}\", \"metadata\": null, \"context\": null, \"mime\": null, \"data\": \"$(base64 < $INDEX_HTML)\"}"
|
|
||||||
|
|
||||||
[[ "$DIRECTORY" != */ ]] && DIRECTORY="$DIRECTORY/"
|
|
||||||
|
|
||||||
# Iterate over files in the specified directory
|
|
||||||
for FILE in "$DIRECTORY"*; do
|
|
||||||
# Check if it's a regular file before proceeding
|
|
||||||
if [[ -f "$FILE" ]]; then
|
|
||||||
# Extract just the file name
|
|
||||||
FILE_NAME=$(basename "$FILE")
|
|
||||||
|
|
||||||
# upload file to Uqbar VFS
|
|
||||||
curl "$URL/rpc/message" -H 'content-type: application/json' -d "{\"node\": \"$NODE\", \"process\": \"vfs\", \"inherit\": false, \"expects_response\": false, \"ipc\": \"{\\\"Add\\\": {\\\"identifier\\\": \\\"$IDENTIFIER\\\", \\\"full_path\\\": \\\"/$FILE_NAME\\\", \\\"entry_type\\\": {\\\"NewFile\\\": null}}}\", \"metadata\": null, \"context\": null, \"mime\": null, \"data\": \"$(base64 < "$FILE")\"}"
|
|
||||||
fi
|
|
||||||
done
|
|
@ -1,67 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
|
|
||||||
/*
|
|
||||||
If you are developing a UI outside of an Uqbar project,
|
|
||||||
comment out the following 2 lines:
|
|
||||||
*/
|
|
||||||
import manifest from '../pkg/manifest.json'
|
|
||||||
import metadata from '../pkg/metadata.json'
|
|
||||||
|
|
||||||
/*
|
|
||||||
IMPORTANT:
|
|
||||||
This must match the process name from pkg/manifest.json + pkg/metadata.json
|
|
||||||
The format is "/" + "process_name:package_name:publisher_node"
|
|
||||||
*/
|
|
||||||
const BASE_URL = `/${manifest[0].process_name}:${metadata.package}:${metadata.publisher}`;
|
|
||||||
|
|
||||||
// This is the proxy URL, it must match the node you are developing against
|
|
||||||
const PROXY_URL = (process.env.VITE_NODE_URL || 'http://127.0.0.1:8080').replace('localhost', '127.0.0.1');
|
|
||||||
|
|
||||||
console.log('process.env.VITE_NODE_URL', process.env.VITE_NODE_URL, PROXY_URL);
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
base: BASE_URL,
|
|
||||||
build: {
|
|
||||||
rollupOptions: {
|
|
||||||
external: ['/our.js']
|
|
||||||
}
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
open: true,
|
|
||||||
proxy: {
|
|
||||||
'/our': {
|
|
||||||
target: PROXY_URL,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
[`${BASE_URL}/our.js`]: {
|
|
||||||
target: PROXY_URL,
|
|
||||||
changeOrigin: true,
|
|
||||||
rewrite: (path) => path.replace(BASE_URL, ''),
|
|
||||||
},
|
|
||||||
// This route will match all other HTTP requests to the backend
|
|
||||||
[`^${BASE_URL}/(?!(@vite/client|src/.*|node_modules/.*|@react-refresh|$))`]: {
|
|
||||||
target: PROXY_URL,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
// '/example': {
|
|
||||||
// target: PROXY_URL,
|
|
||||||
// changeOrigin: true,
|
|
||||||
// rewrite: (path) => path.replace(BASE_URL, ''),
|
|
||||||
// // This is only for debugging purposes
|
|
||||||
// configure: (proxy, _options) => {
|
|
||||||
// proxy.on('error', (err, _req, _res) => {
|
|
||||||
// console.log('proxy error', err);
|
|
||||||
// });
|
|
||||||
// proxy.on('proxyReq', (proxyReq, req, _res) => {
|
|
||||||
// console.log('Sending Request to the Target:', req.method, req.url);
|
|
||||||
// });
|
|
||||||
// proxy.on('proxyRes', (proxyRes, req, _res) => {
|
|
||||||
// console.log('Received Response from the Target:', proxyRes.statusCode, req.url);
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user