From e5381f1cf56c9e244fc4e0febcc4eeadc7d4042d Mon Sep 17 00:00:00 2001 From: latenitecode <85068850+latenitecoding@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:06:51 -0600 Subject: [PATCH] feat: FEN support --- README.md | 1 + src/components/obsidian/SettingsTab.ts | 14 ++++++ src/components/react/ChessStudy.tsx | 24 ++++++++-- src/components/react/PgnViewer/index.tsx | 60 ++++++++++++++++++------ src/main.css | 4 +- src/main.tsx | 21 +++++++-- 6 files changed, 99 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index beb59c6..81f0ca0 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Here are the available settings for a `chessStudy` code block: | `chessStudyId` | Valid nanoid | Valid ID for a file stored in the plugin storage | | `boardOrientation` | `white` \| `black` | Orientation of the board | | `boardColor` | `green` \| `brown` | Color of the board | +| `fen` | Valid FEN string | FEN string | | `viewComments` | `true` \| `false` | Whether to display the comments section | You can permanently set some settings in the [Obsidian](https://obsidian.md/) plugin settings for Obsidian Chess Study. diff --git a/src/components/obsidian/SettingsTab.ts b/src/components/obsidian/SettingsTab.ts index 5eb0c48..9dbdf9b 100644 --- a/src/components/obsidian/SettingsTab.ts +++ b/src/components/obsidian/SettingsTab.ts @@ -4,12 +4,14 @@ import ChessStudyPlugin from 'src/main'; export interface ChessStudyPluginSettings { boardOrientation: 'white' | 'black'; boardColor: 'green' | 'brown'; + fen: string; viewComments: true | false; } export const DEFAULT_SETTINGS: ChessStudyPluginSettings = { boardOrientation: 'white', boardColor: 'green', + fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1', viewComments: true, }; @@ -58,6 +60,18 @@ export class SettingsTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName('FEN') + .setDesc('Sets the default board position') + .addText((text) => { + text + .setValue(this.plugin.settings.fen) + .onChange((fen) => { + this.plugin.settings.fen = fen as string; + this.plugin.saveSettings(); + }); + }); + new Setting(containerEl) .setName('View Comments') .setDesc('Sets the default view of the comments') diff --git a/src/components/react/ChessStudy.tsx b/src/components/react/ChessStudy.tsx index c7300b1..06471e9 100644 --- a/src/components/react/ChessStudy.tsx +++ b/src/components/react/ChessStudy.tsx @@ -56,15 +56,19 @@ export const ChessStudy = ({ dataAdapter, }: AppProps) => { // Parse Obsidian / Code Block Settings - const { boardColor, boardOrientation, viewComments, chessStudyId } = + const { boardColor, boardOrientation, fen, viewComments, chessStudyId } = + parseUserConfig(pluginSettings, source); // Setup Chessground API const [chessView, setChessView] = useState(null); // Setup Chess.js API - const initialChessLogic = useMemo(() => { - const chess = new Chess(); + const [firstPlayer, initialMoveNumber, initialChessLogic] = useMemo(() => { + const chess = (fen) ? new Chess(fen) : new Chess(); + + const firstPlayer = chess.turn(); + const initialMoveNumber = chess.moveNumber(); chessStudyData.moves.forEach((move) => { chess.move({ @@ -73,7 +77,8 @@ export const ChessStudy = ({ promotion: move.promotion, }); }); - return chess; + + return [firstPlayer, initialMoveNumber, chess]; }, [chessStudyData.moves]); const [chessLogic, setChessLogic] = useState(initialChessLogic); @@ -312,6 +317,9 @@ export const ChessStudy = ({ dispatch({ type: 'REMOVE_LAST_MOVE_FROM_HISTORY' }) } @@ -328,6 +336,14 @@ export const ChessStudy = ({ }) } onSaveButtonClick={onSaveButtonClick} + onCopyButtonClick={() => { + try { + navigator.clipboard.writeText(chessLogic.fen()) + new Notice('Copied to clipboard!'); + } catch (e) { + new Notice('Could not copy to clipboard:', e); + } + }} /> diff --git a/src/components/react/PgnViewer/index.tsx b/src/components/react/PgnViewer/index.tsx index 0dea7e9..054efd6 100644 --- a/src/components/react/PgnViewer/index.tsx +++ b/src/components/react/PgnViewer/index.tsx @@ -3,9 +3,9 @@ import * as React from 'react'; import { ReactNode, useEffect, useMemo, useRef } from 'react'; import { ChessStudyMove } from 'src/lib/storage'; -const chunkArray = (array: T[], chunkSize: number) => { +const chunkArray = (array: T[], chunkSize: number, offsetByOne: boolean = false) => { return array.reduce((resultArray, item, index) => { - const chunkIndex = Math.floor(index / chunkSize); + const chunkIndex = Math.floor((index + ((offsetByOne) ? 1 : 0)) / chunkSize); if (!resultArray[chunkIndex]) { resultArray[chunkIndex] = []; @@ -108,21 +108,27 @@ export const PgnViewer = React.memo( ({ history, currentMoveId, + firstPlayer, + initialMoveNumber, onUndoButtonClick, onBackButtonClick, onForwardButtonClick, onMoveItemClick, onSaveButtonClick, + onCopyButtonClick, }: { history: ChessStudyMove[]; currentMoveId: string; + firstPlayer: string; + initialMoveNumber: number; onUndoButtonClick: () => void; onBackButtonClick: () => void; onForwardButtonClick: () => void; onMoveItemClick: (moveId: string) => void; onSaveButtonClick: () => void; + onCopyButtonClick: () => void; }) => { - const movePairs = useMemo(() => chunkArray(history, 2), [history]); + const movePairs = useMemo(() => chunkArray(history, 2, firstPlayer === 'b'), [history]); return (
@@ -134,8 +140,15 @@ export const PgnViewer = React.memo( return (

- {currentMoveIndex + 1} + {currentMoveIndex + initialMoveNumber}

+ {(firstPlayer === 'b' && bMove === undefined && currentMoveIndex === 0) && ( + {}} + /> + )} 0) && `${ currentMoveIndex + - 1 + + initialMoveNumber + wMoveVarianti }... `) || + (firstPlayer === 'b' && + currentMoveIndex === 0 && + `${ + currentMoveIndex + + initialMoveNumber + + wMoveVarianti + }. `) || null } /> @@ -195,13 +217,14 @@ export const PgnViewer = React.memo( onMoveItemClick(wMove.moveId) } moveIndicator={ - (wMoveVarianti % 2 === 0 && - null) || - `${ - currentMoveIndex + - 2 + - wMoveVarianti - }. ` + ((firstPlayer === 'w' || + currentMoveIndex > 0) && + `${ + currentMoveIndex + + initialMoveNumber + 1 + + wMoveVarianti + }. `) || + null } /> )} @@ -238,9 +261,13 @@ export const PgnViewer = React.memo( onMoveItemClick={() => onMoveItemClick(wMove.moveId) } - moveIndicator={`${ - currentMoveIndex + 2 + bMoveVarianti - }. `} + moveIndicator={ + `${ + currentMoveIndex + + initialMoveNumber + 1 + + bMoveVarianti + }. ` + } /> {bMove && ( onSaveButtonClick()}> +
); diff --git a/src/main.css b/src/main.css index 322cb71..7d186f9 100644 --- a/src/main.css +++ b/src/main.css @@ -78,7 +78,7 @@ /* PGN Viewer */ .chess-study .move-item-section { - height: 415px; + height: 380px; } .chess-study .button-section { @@ -97,7 +97,7 @@ grid-template-columns: 0.15fr 0.425fr 0.425fr; grid-auto-rows: minmax(30px, auto); width: 100%; - max-height: 415px; + max-height: 380px; overflow-y: scroll; } diff --git a/src/main.tsx b/src/main.tsx index bf8912b..3f24f38 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -47,13 +47,22 @@ export default class ChessStudyPlugin extends Plugin { // Add command this.addCommand({ id: 'insert-chess-study', - name: 'Insert PGN-Editor at cursor position', + name: 'Insert FEN/PGN-Editor at cursor position', editorCallback: (editor: Editor) => { const cursorPosition = editor.getCursor(); - const onSubmit = async (pgn: string) => { + const onSubmit = async (pgn_or_fen: string) => { try { - const chess = new Chess(); + let pgn = '', fen = ''; + if (pgn_or_fen) { + if (pgn_or_fen.includes('/')) { + fen = pgn_or_fen.trim(); + } else { + pgn = pgn_or_fen.trim(); + } + } + + const chess = (fen) ? new Chess(fen) : new Chess(); if (pgn) { //Try to parse the PGN @@ -80,8 +89,12 @@ export default class ChessStudyPlugin extends Plugin { const id = await this.dataAdapter.saveFile(chessStudyFileData); + const blockStr = (fen) + ? `\`\`\`chessStudy\nchessStudyId: ${id}\nfen: ${fen}\n\`\`\`` + : `\`\`\`chessStudy\nchessStudyId: ${id}\n\`\`\``; + editor.replaceRange( - `\`\`\`chessStudy\nchessStudyId: ${id}\n\`\`\``, + blockStr, cursorPosition ); } catch (e) {