diff --git a/src/board/Board.ts b/src/board/Board.ts index 61ca26d..c7cd4d9 100644 --- a/src/board/Board.ts +++ b/src/board/Board.ts @@ -1,10 +1,10 @@ -import { BoardConfig, PiecesStyle, Position } from "./../types"; +import { BoardConfig, Header, PiecesStyle, Position } from "./../types"; import { Style, BoardStyle } from "../types"; import drawRectangle from "./layers/drawRectangle"; import drawCoords from "./layers/drawCoords"; import drawMoveIndicators from "./layers/drawMoveIndicators"; import drawPieces from "./layers/drawPieces"; -import drawHeader from "./layers/drawHeader.ts"; +import drawHeader from "./layers/drawHeader"; import drawExtraInfo from "./layers/drawExtraInfo"; import boards from "./styles-board"; @@ -20,6 +20,22 @@ const defaultConfig: BoardConfig = { showChecks: true, showCoords: true, flipped: false, + anonymous: false, +}; + +const defaultHeader: Header = { + White: "White", + Black: "Black", + WhitePretty: "White", + BlackPretty: "Black", + WhiteElo: null, + BlackElo: null, + Date: null, + DatePretty: null, + Event: null, + Round: null, + Site: null, + Result: null, }; class Board { @@ -34,7 +50,7 @@ class Board { private margin: number = 0; private style: Style = boards.standard; - private header: { [key: string]: string | undefined } = {}; + private header: Header = defaultHeader; private lastPosition: Position | null = null; private background: HTMLCanvasElement | null = null; private currentScreen: "title" | "move" = "move"; @@ -158,7 +174,19 @@ class Board { return this; } - async titleFrame(header: { [key: string]: string | undefined }) { + private getFinalHeader() { + return this.cfg.anonymous + ? { + ...this.header, + White: "Anonymous", + Black: "Anonymous", + WhitePretty: "Anonymous", + BlackPretty: "Anonymous", + } + : this.header; + } + + async titleFrame(header: Header) { this.currentScreen = "title"; this.header = header; @@ -168,7 +196,7 @@ class Board { this.scale, this.margin, this.style, - header + this.getFinalHeader() ); } @@ -224,13 +252,10 @@ class Board { this.background = canvas; } - async frame( - position: Position | null, - header: { [key: string]: string | undefined } - ) { + async frame(position: Position | null, header?: Header) { this.currentScreen = "move"; this.lastPosition = position; - this.header = header; + this.header = header ?? this.header; this.tempCtx.clearRect(0, 0, this.size, this.size); @@ -289,7 +314,7 @@ class Board { this.scale, this.margin, this.style, - this.header, + this.getFinalHeader(), this.cfg.flipped, this.lastPosition ); diff --git a/src/board/layers/drawCoords.ts b/src/board/layers/drawCoords.ts index 0a4f1fe..c9e27b1 100644 --- a/src/board/layers/drawCoords.ts +++ b/src/board/layers/drawCoords.ts @@ -16,7 +16,7 @@ const drawCoords = ( ) => { const scale = size / 1024; - if (scale <= 0.25) { + if (scale <= 0.32) { return; } diff --git a/src/board/layers/drawExtraInfo.ts b/src/board/layers/drawExtraInfo.ts index 39be0c8..9ed703a 100644 --- a/src/board/layers/drawExtraInfo.ts +++ b/src/board/layers/drawExtraInfo.ts @@ -1,4 +1,4 @@ -import { Style, Position } from "./../../types"; +import { Style, Position, Header } from "./../../types"; import drawText from "./drawText"; const chessFontMapping: { [key: string]: string } = { @@ -17,7 +17,7 @@ const drawExtraInfo = async ( scale: number, margin: number, style: Style, - data: { [key: string]: string | undefined }, + data: Header, flipped: boolean, position: Position ) => { @@ -32,7 +32,7 @@ const drawExtraInfo = async ( { const w = drawText( ctx, - data.White ?? "White", + data.White === "Anonymous" ? "White" : data.White, "Ubuntu", fontSize, 700, @@ -41,8 +41,7 @@ const drawExtraInfo = async ( "left" ); - const elo = - data.WhiteElo && data.WhiteElo !== "?" ? ` ${data.WhiteElo}` : ""; + const elo = data.WhiteElo ? ` ${data.WhiteElo}` : ""; drawText( ctx, @@ -59,7 +58,7 @@ const drawExtraInfo = async ( { const w = drawText( ctx, - data.Black ?? "Black", + data.Black === "Anonymous" ? "Black" : data.Black, "Ubuntu", fontSize, 700, @@ -68,8 +67,7 @@ const drawExtraInfo = async ( "left" ); - const elo = - data.BlackElo && data.BlackElo !== "?" ? ` ${data.BlackElo}` : ""; + const elo = data.BlackElo ? ` ${data.BlackElo}` : ""; drawText( ctx, diff --git a/src/board/layers/drawHeader.ts.ts b/src/board/layers/drawHeader.ts similarity index 52% rename from src/board/layers/drawHeader.ts.ts rename to src/board/layers/drawHeader.ts index c4b2372..2cccfab 100644 --- a/src/board/layers/drawHeader.ts.ts +++ b/src/board/layers/drawHeader.ts @@ -1,53 +1,14 @@ -import { Style } from "./../../types"; +import { Header, Style } from "../../types"; import drawRectangle from "./drawRectangle"; import drawText from "./drawText"; -const MONTHS = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", -]; - -const formatDate = (date: string) => { - const [y, m, d] = date.split(".").map(Number); - - const month = Number.isNaN(m) ? null : MONTHS[m - 1]; - const day = Number.isNaN(d) || month === null ? null : d; - const year = Number.isNaN(y) ? null : y; - - return month && day && year - ? `${month} ${day}, ${year}` - : month && year - ? `${month} ${year}` - : year - ? String(year) - : ""; -}; - -const formatName = (name: string) => { - return name - .split(",") - .map((x) => x.trim()) - .reverse() - .join(" "); -}; - const drawHeader = async ( ctx: CanvasRenderingContext2D, size: number, scale: number, margin: number, style: Style, - data: { [key: string]: string | undefined } + data: Header ) => { ctx.clearRect(0, 0, size, size); await drawRectangle(ctx, size, size + margin * 2, 0, 0, style.border); @@ -55,11 +16,11 @@ const drawHeader = async ( const font = "Ubuntu"; const allSizes = [ - { key: "White", line: 60 * scale, fontSize: 42 * scale, n: 0 }, - { key: "Black", line: 60 * scale, fontSize: 42 * scale, n: 2 }, + { key: "WhitePretty", line: 60 * scale, fontSize: 42 * scale, n: 0 }, + { key: "BlackPretty", line: 60 * scale, fontSize: 42 * scale, n: 2 }, { key: "Event", line: 30 * scale, fontSize: 20 * scale, n: 4 }, { key: "Round", line: 30 * scale, fontSize: 20 * scale, n: 5 }, - { key: "Date", line: 30 * scale, fontSize: 20 * scale, n: 7 }, + { key: "DatePretty", line: 30 * scale, fontSize: 20 * scale, n: 7 }, { key: "Site", line: 30 * scale, fontSize: 20 * scale, n: 8 }, ]; @@ -67,16 +28,16 @@ const drawHeader = async ( const sizes = allSizes.filter(({ key }) => keys.has(key)); - if (data.White && data.Black) { - sizes.push({ key: "vs", line: 50, fontSize: 20, n: 1 }); + if (data.WhitePretty && data.BlackPretty) { + sizes.push({ key: "vs", line: 50 * scale, fontSize: 20 * scale, n: 1 }); } if (data.Event || data.Round) { - sizes.push({ key: "margin", line: 100, fontSize: 0, n: 3 }); + sizes.push({ key: "margin", line: 100 * scale, fontSize: 0, n: 3 }); } if (data.Date || data.Site) { - const line = data.Event || data.Round ? 20 : 100; + const line = data.Event || data.Round ? 20 * scale : 100 * scale; sizes.push({ key: "margin", line, fontSize: 0, n: 6 }); } @@ -101,17 +62,10 @@ const drawHeader = async ( return; } - const item = data[key]; + const item = data[key as keyof Header]; if (item) { - const text = - key === "Date" - ? formatDate(item) - : key === "Black" || key === "White" - ? formatName(item) - : key === "Round" - ? `Round ${item}` - : item; + const text = key === "Round" ? `Round ${item}` : item; const y = fromTop + line / 2; diff --git a/src/encoders/createAnimation.ts b/src/encoders/createAnimation.ts index c58a6e5..f8ee71b 100644 --- a/src/encoders/createAnimation.ts +++ b/src/encoders/createAnimation.ts @@ -1,6 +1,7 @@ -import { BoardConfig } from "./../types"; +import { BoardConfig, Size } from "./../types"; import Board from "../board/Board"; import Game from "../game/Game"; +import sizeToPX from "./sizeToPX"; import GIF from "./GIF"; import WebM from "./WebM"; import MP4 from "./MP4"; @@ -16,10 +17,11 @@ const getData = (board: Board, encoder: GIF | WebM | MP4) => { const createAnimation = async ( pgn: string, boardConfig: BoardConfig, - format: "GIF" | "WebM" | "MP4" + format: "GIF" | "WebM" | "MP4", + size: Size ) => { const game = new Game().loadPGN(pgn); - const board = new Board(boardConfig); + const board = new Board({ ...boardConfig, size: sizeToPX[size] }); const encoder = format === "GIF" ? new GIF(board.width, board.height, true) diff --git a/src/encoders/createImage.ts b/src/encoders/createImage.ts index 100d07d..df9781d 100644 --- a/src/encoders/createImage.ts +++ b/src/encoders/createImage.ts @@ -10,7 +10,6 @@ const createImage = async ( boardConfig: BoardConfig, size: Size ) => { - console.log({ fen, pgn, ply, size }); const game = new Game(); if (pgn) { diff --git a/src/encoders/sizeToPX.ts b/src/encoders/sizeToPX.ts index 355f5a2..aba9b34 100644 --- a/src/encoders/sizeToPX.ts +++ b/src/encoders/sizeToPX.ts @@ -1,5 +1,5 @@ const sizeToPX = { - XS: 256, + XS: 360, S: 512, M: 720, L: 1024, diff --git a/src/game/Game.ts b/src/game/Game.ts index 077a798..98609fa 100644 --- a/src/game/Game.ts +++ b/src/game/Game.ts @@ -1,6 +1,7 @@ import { PieceType, PieceColor, BoardData, Position } from "../types"; import { Chess, ChessInstance } from "chess.js"; import { cleanPGN } from "./PGNHelpers"; +import { formatDate, formatName } from "../utils/formatters"; const MATERIAL_VALUE: Map = new Map([ ["q", 9], @@ -10,6 +11,13 @@ const MATERIAL_VALUE: Map = new Map([ ["p", 1], ]); +const prepareHeaderEntry = ( + entry: string | undefined, + ifEmpty: null | string = null +) => { + return !entry || entry === "?" ? ifEmpty : entry; +}; + class Game { private positions: Position[] = []; private game: ChessInstance = new Chess(); @@ -149,7 +157,30 @@ class Game { } get header() { - return this.game.header(); + const header = this.game.header(); + + const white = prepareHeaderEntry(header.White, "Anonymous") as string; + const black = prepareHeaderEntry(header.Black, "Anonymous") as string; + const date = prepareHeaderEntry(header.Date); + + return { + White: white, + Black: black, + WhitePretty: formatName(white), + BlackPretty: formatName(black), + WhiteElo: prepareHeaderEntry(header.WhiteElo), + BlackElo: prepareHeaderEntry(header.BlackElo), + Date: date, + DatePretty: date === null ? null : formatDate(date), + Event: prepareHeaderEntry(header.Event), + Round: prepareHeaderEntry(header.Round), + Site: prepareHeaderEntry(header.Site), + Result: prepareHeaderEntry(header.Result), + }; + } + + get pgn() { + return this.game.pgn(); } getPosition(ply: number) { diff --git a/src/main.tsx b/src/main.tsx index 4771efc..7732157 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,78 +1,28 @@ -import { BoardConfig, BoardStyle, GameConfig, PiecesStyle } from "./types"; +import WebFont from "webfontloader"; +import * as Hammer from "hammerjs"; +import { render } from "solid-js/web"; + +import { BoardStyle, PiecesStyle } from "./types"; + import Board from "./board/Board"; import Game from "./game/Game"; -import pgns from "./test-data/pgns"; -import createAnimation from "./encoders/createAnimation"; -// import { decompressPGN } from "./game/PGNHelpers"; -import WebFont from "webfontloader"; import Player from "./player/Player"; -import * as Hammer from "hammerjs"; -// import Moves from "./ui/Moves"; -// import Controls from "./ui/Controls"; +import App from "./ui/App"; + import { state, setState } from "./state"; -import { render } from "solid-js/web"; -import App from "./ui/App"; -import download from "./utils/download"; import createImage from "./encoders/createImage"; - -const boardConfig: BoardConfig = { - size: 1024, - tiles: 8, - boardStyle: "calm", - piecesStyle: "tatiana", - showBorder: true, - showExtraInfo: true, - showMaterial: true, - showMoveIndicator: true, - showChecks: true, - showCoords: true, - flipped: false, -}; - -const gameConfig: GameConfig = { - titleScreen: true, - fromPly: null, - toPly: null, - loop: true, - format: "GIF", - picSize: "M", - animationSize: "M", -}; - -const createDownloadLink = async (pgn: string, boardConfig: BoardConfig) => { - const file = await createAnimation(pgn, { ...boardConfig, size: 720 }, "MP4"); - const link = document.createElement("a"); - link.innerText = "DOWNLOAD"; - link.setAttribute("href", URL.createObjectURL(file)); - link.setAttribute("download", file.name); - return link; -}; - -console.log(createDownloadLink.name); +import createAnimation from "./encoders/createAnimation"; +import readFile from "./utils/readFile"; +import download from "./utils/download"; +import { compressPGN } from "./game/PGNHelpers"; +import extractUrlData from "./persistance/extractUrlData"; const main = async () => { - // window.location.hash = - // "#QiBEdWtlIEthcmwgLyBDb3VudCBJc291YXJkCkQgMTg1OC4/Py4/PwpFIFBhcmlzClIgMS0wClMgUGFyaXMgRlJBClcgUGF1bCBNb3JwaHkKCmU0IGU1IE5mMyBkNiBkNCBCZzQgZHhlNSBCeGYzIFF4ZjMgZHhlNSBCYzQgTmY2IFFiMyBRZTcgTmMzIGM2IEJnNSBiNSBOeGI1IGN4YjUgQnhiNSsgTmJkNyBPLU8tTyBSZDggUnhkNyBSeGQ3IFJkMSBRZTYgQnhkNysgTnhkNyBRYjgrIE54YjggUmQ4Iw=="; + const board = new Board(state.boardConfig); + const player = new Player(board, state.gameConfig); - // const hash = window.location.hash; - // const pgn = hash === "" ? null : decompressPGN(hash.slice(1)); - const pgn = pgns[pgns.length - 1]; - // const pgn = pgns[2]; - const board = new Board(boardConfig); - - // const interval = 1000; - // play(board, gameConfig, pgn, interval); - - const player = new Player(board, gameConfig); - const game = new Game().loadPGN(pgn); - - setState({ - moves: game.getMoves(), - pgn, - ply: 0, - fen: game.getPosition(0).fen, - }); + /* Register handlers */ const handlers = { prev() { @@ -93,24 +43,36 @@ const main = async () => { }, toggleBorder() { board.toggleBorder(); - setState("board", "showBorder", !state.board.showBorder); + setState("boardConfig", "showBorder", !state.boardConfig.showBorder); }, showBorder() { board.showBorder(); - setState("board", "showBorder", true); + setState("boardConfig", "showBorder", true); }, hideBorder() { board.hideBorder(); - setState("board", "showBorder", false); + setState("boardConfig", "showBorder", false); }, toggleExtraInfo() { board.toggleExtraInfo(); - setState("board", "showExtraInfo", !state.board.showExtraInfo); + setState( + "boardConfig", + "showExtraInfo", + !state.boardConfig.showExtraInfo + ); + }, + toggleAnonymous() { + setState("boardConfig", "anonymous", !state.boardConfig.anonymous); + board.updateConfig({ anonymous: state.boardConfig.anonymous }); + }, + toggleTitleScreen() { + setState("gameConfig", "titleScreen", !state.gameConfig.titleScreen); }, flip() { + console.log("FLIP"); board.flip(); - setState("board", "flipped", !state.board.flipped); + setState("boardConfig", "flipped", !state.boardConfig.flipped); }, togglePlay() { player.playing ? player.pause() : player.play(); @@ -121,20 +83,28 @@ const main = async () => { }, changeBoardStyle(style: BoardStyle) { board.setStyle(style); - setState("board", "boardStyle", style); + setState("boardConfig", "boardStyle", style); }, changePiecesStyle(style: PiecesStyle) { board.setPiecesStyle(style); - setState("board", "piecesStyle", style); + setState("boardConfig", "piecesStyle", style); }, async loadPGN(pgn: string) { const game = new Game().loadPGN(pgn); - setState({ pgn, fen: "", moves: game.getMoves() }); + setState({ + pgn: game.pgn, + fen: "", + moves: game.getMoves(), + ply: 0, + game, + }); + window.location.hash = `v1/pgn/${compressPGN(game.pgn)}`; + await player.load(game); }, async loadFEN(fen: string) { const game = new Game().loadFEN(fen); - setState({ pgn: null, fen, moves: game.getMoves() }); + setState({ pgn: "", fen, moves: game.getMoves(), ply: 0, game }); await player.load(game); }, async downloadImage() { @@ -142,21 +112,24 @@ const main = async () => { state.fen, state.pgn, state.ply, - state.board, - state.game.picSize + state.boardConfig, + state.gameConfig.picSize ); download(data, "fen", "png"); }, + async downloadAnimation() { + const data = await createAnimation( + state.pgn, + state.boardConfig, + state.gameConfig.format, + state.gameConfig.animationSize + ); + download(data, "game", state.gameConfig.format.toLowerCase()); + }, }; - // @ts-ignore - window.handlers = handlers; - // @ts-ignore - window.state = state; + /* Render the page */ - /** - * RENDER - **/ render( () => , document.getElementById("root") as HTMLElement @@ -165,31 +138,19 @@ const main = async () => { const $board = document.querySelector("#board"); $board?.appendChild(board.canvas); - // const moves = new Moves($moves as HTMLElement, player).load(game.getMoves()); - // const controls = new Controls($controls as HTMLElement, handlers).load(); + /* Restore game from the url */ - await player.load(game); + const { pgn, fen } = extractUrlData(); - // @ts-ignore - window.load = async (pgn: string) => { - const game = new Game().loadPGN(pgn); - await player.load(game); - // moves.load(game.getMoves()); - // controls.load(); - }; + await (pgn + ? handlers.loadPGN(pgn) + : fen + ? handlers.loadFEN(fen) + : handlers.loadFEN( + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" + )); - // @ts-ignore - window.board = board; - - document.addEventListener( - "contextmenu", - (e) => { - e.preventDefault(); - handlers.prev(); - return false; - }, - false - ); + /* Register events */ const keyMapping: { [key: string]: () => void } = { ArrowLeft: handlers.prev, @@ -202,9 +163,31 @@ const main = async () => { e: handlers.toggleExtraInfo, }; - document.addEventListener("keydown", ({ key }) => { - if (keyMapping[key]) { - keyMapping[key](); + document.addEventListener("keydown", (e) => { + const target = e.target as HTMLElement | null; + + if ( + keyMapping[e.key] && + target?.nodeName !== "INPUT" && + target?.nodeName !== "TEXTAREA" + ) { + keyMapping[e.key](); + } + }); + + const preventDefaults = (e: Event) => { + e.preventDefault(); + e.stopPropagation(); + }; + + ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + document.addEventListener(eventName, preventDefaults, false); + }); + + document.addEventListener("drop", async (e) => { + if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { + const content = await readFile(e.dataTransfer.files[0]); + handlers.loadPGN(content); } }); @@ -222,12 +205,10 @@ const main = async () => { hammer.on("pinchout", handlers.hideBorder); hammer.on("tap", handlers.next); hammer.on("press", handlers.flip); - - // createDownloadLink(pgn, boardConfig).then((link) => { - // document.body.appendChild(link); - // }); }; +/* Boot */ + WebFont.load({ google: { families: ["Ubuntu:500,700", "Fira Mono"], diff --git a/src/persistance/extractUrlData.ts b/src/persistance/extractUrlData.ts new file mode 100644 index 0000000..aacc0e5 --- /dev/null +++ b/src/persistance/extractUrlData.ts @@ -0,0 +1,27 @@ +import { decompressPGN } from "../game/PGNHelpers"; + +const HEADER_REGEX = /^#v\d+\/(pgn|fen)\//; + +const extractUrlData = () => { + const hash = window.location.hash; + + if (!HEADER_REGEX.test(hash)) { + return { + pgn: "", + fen: "", + }; + } + + const [rawVersion, format, ...chunks] = hash.split("/"); + + const version = Number((rawVersion.match(/\d+/g) ?? [])[0]); + const data = chunks.join("/"); + + return { + version, + pgn: format === "pgn" ? decompressPGN(data) : "", + fen: format === "fen" ? decodeURI(data) : "", + }; +}; + +export default extractUrlData; diff --git a/src/state.ts b/src/state.ts index f885eb9..7a2a37f 100644 --- a/src/state.ts +++ b/src/state.ts @@ -1,5 +1,6 @@ import isMobile from "is-mobile"; import { createStore } from "solid-js/store"; +import Game from "./game/Game"; import { BoardConfig, GameConfig } from "./types"; const boardConfig: BoardConfig = { @@ -14,6 +15,7 @@ const boardConfig: BoardConfig = { showChecks: true, showCoords: true, flipped: false, + anonymous: false, }; const gameConfig: GameConfig = { @@ -27,9 +29,10 @@ const gameConfig: GameConfig = { }; export type State = { - board: BoardConfig; - game: GameConfig; - pgn: string | null; + boardConfig: BoardConfig; + gameConfig: GameConfig; + game: Game; + pgn: string; fen: string; moves: string[]; ply: number; @@ -37,9 +40,10 @@ export type State = { }; const initialState: State = { - board: boardConfig, - game: gameConfig, - pgn: null, + boardConfig, + gameConfig, + game: new Game(), + pgn: "", fen: "", moves: [], ply: 0, diff --git a/src/test-data/fens.ts b/src/test-data/fens.ts deleted file mode 100644 index d98e81e..0000000 --- a/src/test-data/fens.ts +++ /dev/null @@ -1,3 +0,0 @@ -export default [ - "r1bqr1k1/pppnppbp/3p1np1/8/1P6/1B2PN2/PBPP1PPP/RN1Q1RK1 w - - 6 8", -]; diff --git a/src/test-data/pgns.ts b/src/test-data/pgns.ts deleted file mode 100644 index 74e31e4..0000000 --- a/src/test-data/pgns.ts +++ /dev/null @@ -1,26 +0,0 @@ -const pgns = [ - `[Event "Hoogovens Group A"] -[Site "Wijk aan Zee NED"] -[Date "1999.01.20"] -[EventDate "1999.01.16"] -[Round "4"] -[Result "1-0"] -[White "Garry Kasparov"] -[Black "Veselin Topalov"] -[ECO "B07"] -[WhiteElo "2812"] -[BlackElo "2700"] -[PlyCount "87"] - -1. e4 d6 2. d4 Nf6 3. Nc3 g6 4. Be3 Bg7 5. Qd2 c6 6. f3 b5 -7. Nge2 Nbd7 8. Bh6 Bxh6 9. Qxh6 Bb7 10. a3 e5 11. O-O-O Qe7 -12. Kb1 a6 13. Nc1 O-O-O 14. Nb3 exd4 15. Rxd4 c5 16. Rd1 Nb6 -17. g3 Kb8 18. Na5 Ba8 19. Bh3 d5 20. Qf4+ Ka7 21. Rhe1 d4 -22. Nd5 Nbxd5 23. exd5 Qd6 24. Rxd4 cxd4 25. Re7+ Kb6 -26. Qxd4+ Kxa5 27. b4+ Ka4 28. Qc3 Qxd5 29. Ra7 Bb7 30. Rxb7 -Qc4 31. Qxf6 Kxa3 32. Qxa6+ Kxb4 33. c3+ Kxc3 34. Qa1+ Kd2 -35. Qb2+ Kd1 36. Bf1 Rd2 37. Rd7 Rxd7 38. Bxc4 bxc4 39. Qxh8 -Rd3 40. Qa8 c3 41. Qa4+ Ke1 42. f4 f5 43. Kc1 Rd2 44. Qa7 1-0`, -]; - -export default pgns; diff --git a/src/types.ts b/src/types.ts index 90f7dd0..95790f9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -129,6 +129,7 @@ export type BoardConfig = { showChecks: boolean; showCoords: boolean; flipped: boolean; + anonymous: boolean; }; export type Size = "XS" | "S" | "M" | "L" | "XL"; @@ -199,6 +200,8 @@ export type Handlers = { showBorder(): void; hideBorder(): void; toggleExtraInfo(): void; + toggleAnonymous(): void; + toggleTitleScreen(): void; flip(): void; togglePlay(): void; goto(ply: number): void; @@ -206,5 +209,21 @@ export type Handlers = { changePiecesStyle: (style: PiecesStyle) => void; loadPGN: (pgn: string) => Promise; loadFEN: (fen: string) => Promise; - downloadImage: () => void; + downloadImage: () => Promise; + downloadAnimation: () => Promise; +}; + +export type Header = { + White: string; + Black: string; + WhitePretty: string; + BlackPretty: string; + WhiteElo: string | null; + BlackElo: string | null; + Date: string | null; + DatePretty: string | null; + Event: string | null; + Round: string | null; + Site: string | null; + Result: string | null; }; diff --git a/src/ui/App.css b/src/ui/App.css index 24d11e4..1f8d8ce 100644 --- a/src/ui/App.css +++ b/src/ui/App.css @@ -22,7 +22,21 @@ body { font-family: Ubuntu, sans-serif; } -button { +.upload { + visibility: hidden; + padding: 0; +} + +.upload::before { + content: "UPLOAD PGN FILE"; + visibility: visible; + display: inline-block; + text-align: center; + box-sizing: border-box; +} + +button, +.upload::before { padding: 10px; font-family: "Ubuntu"; font-size: 1.5rem; @@ -32,7 +46,8 @@ button { width: 100%; } -button:hover { +button:hover, +.upload:hover::before { background: rgb(0, 207, 162); cursor: pointer; } @@ -69,6 +84,11 @@ h3 { margin: 15px 0 10px 0; } +hr { + margin-top: 20px; + border-top: solid 1px #ffffff22; +} + .dark { background-color: #191d24; /* background-image: url(src/ui/img/pattern.png); */ diff --git a/src/ui/components/Boards.tsx b/src/ui/components/Boards.tsx index d11c837..b9b9fed 100644 --- a/src/ui/components/Boards.tsx +++ b/src/ui/components/Boards.tsx @@ -25,7 +25,7 @@ const prepareBoards = async () => { for (const [key, style] of Object.entries(styles) as [BoardStyle, Style][]) { await board.updateConfig({ boardStyle: key }); - await board.frame(null, {}); + await board.frame(null); board.render(); boards.push({ key, @@ -51,12 +51,12 @@ const Boards: Component<{ handlers: Handlers }> = (props) => { { - setState("board", "boardStyle", board.key); + setState("boardConfig", "boardStyle", board.key); props.handlers.changeBoardStyle(board.key); }} src={board.img} diff --git a/src/ui/components/GameTabs.tsx b/src/ui/components/GameTabs.tsx index f0bcd3b..0b5ad70 100644 --- a/src/ui/components/GameTabs.tsx +++ b/src/ui/components/GameTabs.tsx @@ -20,7 +20,7 @@ const GameTabs: Component<{ moves: readonly string[]; handlers: Handlers }> = ( } onClick={() => setTab("moves")} > - MOVES + GAME +