diff --git a/public/img/baner.png b/public/img/baner.png index 5b53d6f..cdf169d 100644 Binary files a/public/img/baner.png and b/public/img/baner.png differ diff --git a/src/board/Board.ts b/src/board/Board.ts index 52e4b36..b5da2bf 100644 --- a/src/board/Board.ts +++ b/src/board/Board.ts @@ -83,6 +83,14 @@ class Board { this.updateConfig(config, false); } + async setCanvas(canvas: HTMLCanvasElement) { + this.canvas = canvas; + this.ctx = canvas.getContext("2d") as CanvasRenderingContext2D; + this.setSize(this.cfg.size); + + await this.refresh(); + } + async updateConfig(config: Partial, refresh: boolean = true) { const cfg = { ...this.cfg, ...config }; diff --git a/src/board/layers/drawHeader.ts b/src/board/layers/drawHeader.ts index 2cccfab..5cda030 100644 --- a/src/board/layers/drawHeader.ts +++ b/src/board/layers/drawHeader.ts @@ -1,6 +1,7 @@ import { Header, Style } from "../../types"; import drawRectangle from "./drawRectangle"; import drawText from "./drawText"; +import loadImage from "../loaders/loadImage"; const drawHeader = async ( ctx: CanvasRenderingContext2D, @@ -84,6 +85,25 @@ const drawHeader = async ( fromTop += line; }); + + const logo = await loadImage("/img/logo-full.svg"); + const logoHeight = size * 0.114 * 0.5; + + ctx.shadowColor = "rgba(0, 0, 0, 1)"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = 10 * scale; + ctx.drawImage( + logo, + size / 4, + margin + size + margin - logoHeight * 2, + size / 2, + logoHeight + ); + ctx.shadowColor = "rgba(0, 0, 0, 0)"; + ctx.shadowBlur = 0; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; }; export default drawHeader; diff --git a/src/board/styles-board/gradient/contrast-b.ts b/src/board/styles-board/gradient/contrast-b.ts index a8c76ea..ddcb606 100644 --- a/src/board/styles-board/gradient/contrast-b.ts +++ b/src/board/styles-board/gradient/contrast-b.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#3cff0055", + data: "#ffaa0055", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/contrast-c.ts b/src/board/styles-board/gradient/contrast-c.ts index 5c91bf2..ab3bf3e 100644 --- a/src/board/styles-board/gradient/contrast-c.ts +++ b/src/board/styles-board/gradient/contrast-c.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#3cff0055", + data: "#00ffff55", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/contrast-d.ts b/src/board/styles-board/gradient/contrast-d.ts index 6c1df96..9894ccf 100644 --- a/src/board/styles-board/gradient/contrast-d.ts +++ b/src/board/styles-board/gradient/contrast-d.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#3cff0055", + data: "#ffaa0055", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/smooth-forest.ts b/src/board/styles-board/gradient/smooth-forest.ts index f20df3f..021b29a 100644 --- a/src/board/styles-board/gradient/smooth-forest.ts +++ b/src/board/styles-board/gradient/smooth-forest.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#00ffee55", + data: "#ffff0055", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/smooth-sea.ts b/src/board/styles-board/gradient/smooth-sea.ts index 4e9c129..af55114 100644 --- a/src/board/styles-board/gradient/smooth-sea.ts +++ b/src/board/styles-board/gradient/smooth-sea.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#3cff0055", + data: "#ff00ff55", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/smooth-spring.ts b/src/board/styles-board/gradient/smooth-spring.ts index 558b215..7f5a92a 100644 --- a/src/board/styles-board/gradient/smooth-spring.ts +++ b/src/board/styles-board/gradient/smooth-spring.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#00ffee55", + data: "#00ffff55", }, border: { type: "gradient", diff --git a/src/board/styles-board/gradient/smooth-violet.ts b/src/board/styles-board/gradient/smooth-violet.ts index f2c0202..0042461 100644 --- a/src/board/styles-board/gradient/smooth-violet.ts +++ b/src/board/styles-board/gradient/smooth-violet.ts @@ -24,7 +24,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#3cff0055", + data: "#ffaa0055", }, border: { type: "gradient", diff --git a/src/board/styles-board/solid/azure.ts b/src/board/styles-board/solid/azure.ts index ebfe5c5..d115b4d 100644 --- a/src/board/styles-board/solid/azure.ts +++ b/src/board/styles-board/solid/azure.ts @@ -23,7 +23,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#ffff0055", + data: "#ff000044", }, border: { type: "solid", diff --git a/src/board/styles-board/solid/spring.ts b/src/board/styles-board/solid/spring.ts index a04b58e..2d12633 100644 --- a/src/board/styles-board/solid/spring.ts +++ b/src/board/styles-board/solid/spring.ts @@ -23,7 +23,7 @@ const style: Style = { }, moveIndicator: { type: "color", - data: "#ffff0055", + data: "#00ffff55", }, border: { type: "solid", diff --git a/src/imports/importToLichess.ts b/src/imports/importToLichess.ts new file mode 100644 index 0000000..9732a2a --- /dev/null +++ b/src/imports/importToLichess.ts @@ -0,0 +1,28 @@ +const importToLichess = async (pgn: string, site: string | null) => { + if (site && /^https:\/\/lichess\.org\/.{8}/.test(site)) { + return site; + } + + const queryString = new URLSearchParams({ pgn }).toString(); + + try { + const res = await fetch("https://lichess.org/api/import", { + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + body: queryString, + method: "POST", + }); + + if (res.status === 200) { + const data = await res.json(); + return data.url; + } else { + throw new Error("Cannot import to lichess"); + } + } catch (e) { + throw new Error("Cannot import to lichess"); + } +}; + +export default importToLichess; diff --git a/src/main.tsx b/src/main.tsx index 7356a07..760e5fe 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -22,6 +22,7 @@ import isPGN from "./utils/isPGN"; import isSafeLink from "./utils/isSafeLink"; import { PiecesStyle } from "./board/styles-pieces/piecesStyles"; import link from "./persistance/link"; +import importToLichess from "./imports/importToLichess"; const main = async () => { const board = new Board(state.boardConfig); @@ -232,8 +233,27 @@ const main = async () => { setState("boardConfig", "speech", !state.boardConfig.speech); saveConfig("board"); }, + async openOnLichess() { + if (state.pgn === "") { + window.open( + `https://lichess.org/analysis/${state.fen.replace(/\s+/g, "_")}` + ); + return true; + } + + try { + const url = await importToLichess(state.pgn, state.game.header.Site); + window.open(`${url}/${state.boardConfig.flipped ? "black" : ""}`); + return true; + } catch (e) { + return false; + } + }, }; + // @ts-ignore + window.handlers = handlers; + /* Render the page */ render( @@ -241,8 +261,10 @@ const main = async () => { document.getElementById("root") as HTMLElement ); - const $board = document.querySelector("#board"); - $board?.prepend(board.canvas); + const canvas = document.getElementById("canvas") as HTMLCanvasElement; + + await board.setCanvas(canvas); + await player.init(); /* Load game from the url */ diff --git a/src/player/Player.ts b/src/player/Player.ts index d204526..07a685f 100644 --- a/src/player/Player.ts +++ b/src/player/Player.ts @@ -15,12 +15,18 @@ class Player { private speech: Speech; public playing: boolean = false; - private firstRender: Promise; + // private firstRender: Promise; constructor(private board: Board, private config: GameConfig) { this.speech = new Speech(); - this.firstRender = this.board + // this.firstRender = this.board + // .frame(this.game.getPosition(0), this.game.header) + // .then((_) => this.board.render()); + } + + async init() { + await this.board .frame(this.game.getPosition(0), this.game.header) .then((_) => this.board.render()); } @@ -43,7 +49,7 @@ class Player { async load(game: Game) { this.pause(); - await this.firstRender; + // await this.firstRender; this.game = game; this.ply = 0; diff --git a/src/types.ts b/src/types.ts index ddf82a3..36b4971 100644 --- a/src/types.ts +++ b/src/types.ts @@ -175,6 +175,7 @@ export type Handlers = { downloadAnimation: () => Promise; toggleSound(): void; toggleSpeech(): void; + openOnLichess: () => Promise; }; export type Header = { diff --git a/src/ui/App.css b/src/ui/App.css index acfdee8..11d5764 100644 --- a/src/ui/App.css +++ b/src/ui/App.css @@ -94,6 +94,11 @@ button:hover, cursor: pointer; } +.btn--error, +.btn--error:hover { + background-color: #fc4444; +} + input, textarea { width: 100%; @@ -155,6 +160,10 @@ a:hover { padding: var(--header-margin) 0 2rem 0; } +.board-box--left { + padding-left: 2rem; +} + .board { box-shadow: 0 0 2rem #00000099; border-radius: 5px; diff --git a/src/ui/App.tsx b/src/ui/App.tsx index 2d1607a..0498600 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -23,7 +23,14 @@ const App: Component<{ handlers: Handlers; state: DeepReadonly }> = ( -
+
+ diff --git a/src/ui/components/Controls.css b/src/ui/components/Controls.css index 397312f..0404623 100644 --- a/src/ui/components/Controls.css +++ b/src/ui/components/Controls.css @@ -5,7 +5,7 @@ .controls { background: var(--color-bg-block); - padding: 2rem; + padding: 2rem 1rem; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } @@ -85,3 +85,9 @@ max-height: 6rem; } } + +@media (orientation: landscape) and (max-height: 400px) { + .controls { + padding-top: 0; + } +} diff --git a/src/ui/components/GameTabs.css b/src/ui/components/GameTabs.css index a7de052..b5f230c 100644 --- a/src/ui/components/GameTabs.css +++ b/src/ui/components/GameTabs.css @@ -9,7 +9,7 @@ .game { height: 100%; display: grid; - grid-template-rows: 3.8rem 19.5rem 1fr 8.4rem; + grid-template-rows: 3.8rem 22rem auto 8.4rem; } .game-tabs { @@ -42,3 +42,9 @@ display: block; } } + +@media (orientation: landscape) and (max-height: 400px) { + .game { + grid-template-rows: 3.8rem 1fr 6.4rem; + } +} diff --git a/src/ui/components/Info.css b/src/ui/components/Info.css index d62290a..ab46a90 100644 --- a/src/ui/components/Info.css +++ b/src/ui/components/Info.css @@ -5,9 +5,10 @@ .info { background: var(--color-bg-block); - padding: 3rem 2rem; + padding: 2rem; font-size: 1.4rem; text-align: left; + position: relative; } .info__players { @@ -65,3 +66,10 @@ color: var(--color-text); padding: 0 1rem; } + +.info__analyze { + position: absolute; + bottom: 2rem; + width: 100%; + padding-right: 4rem; +} diff --git a/src/ui/components/Info.tsx b/src/ui/components/Info.tsx index 50585e3..78707f3 100644 --- a/src/ui/components/Info.tsx +++ b/src/ui/components/Info.tsx @@ -1,11 +1,13 @@ -import { Component, Show } from "solid-js"; +import { Component, Show, createSignal } from "solid-js"; import { Handlers } from "../../types"; import { state } from "../../state"; import "./Info.css"; import isSafeLink from "../../utils/isSafeLink"; import isLink from "../../utils/isLink"; -const Info: Component<{ handlers: Handlers }> = () => { +const Info: Component<{ handlers: Handlers }> = (props) => { + const [error, setError] = createSignal(false); + return (
@@ -83,22 +85,26 @@ const Info: Component<{ handlers: Handlers }> = () => {

- -

- - Analyze on Lichess - -

-

{state.game.header.DatePretty}

+
+ +
); }; diff --git a/src/ui/components/Load.css b/src/ui/components/Load.css index 534e748..e0b586d 100644 --- a/src/ui/components/Load.css +++ b/src/ui/components/Load.css @@ -1,12 +1,11 @@ .load { background: var(--color-bg-block); - padding: 2rem; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; } .load__game-input { - height: 50vh; + height: 40vh; margin-top: 2rem; } @@ -44,3 +43,15 @@ .load__link-input { margin-top: 2rem; } + +@media (orientation: portrait) { + .load__game-input { + height: 20vh; + } +} + +@media (orientation: landscape) and (max-height: 400px) { + .load__game-input { + height: 30vh; + } +} diff --git a/src/ui/components/Load.tsx b/src/ui/components/Load.tsx index be926c3..c3ea01c 100644 --- a/src/ui/components/Load.tsx +++ b/src/ui/components/Load.tsx @@ -1,16 +1,35 @@ import { Component, createSignal, Show } from "solid-js"; import { Handlers } from "../../types"; import readFile from "../../utils/readFile"; +import Scrollable from "./reusable/Scrollable"; import { setState, state } from "../../state"; import "./Load.css"; const Load: Component<{ handlers: Handlers; class?: string }> = (props) => { const [data, setData] = createSignal(""); + const [clipError, setClipError] = createSignal(false); + const [inputError, setInputError] = createSignal(false); let filePicker: HTMLInputElement | undefined = undefined; return ( -
+ + +

= (props) => { } }} > - UPLOAD PGN FILE + Upload PGN file @@ -62,7 +84,7 @@ const Load: Component<{ handlers: Handlers; class?: string }> = (props) => {

drop the PGN file anywhere on the page

-
+ ); }; diff --git a/src/ui/components/Moves.css b/src/ui/components/Moves.css index e8fa33c..aa02dbb 100644 --- a/src/ui/components/Moves.css +++ b/src/ui/components/Moves.css @@ -51,3 +51,9 @@ width: auto; } } + +@media (orientation: landscape) and (max-height: 400px) { + .moves { + display: none; + } +}