feat: adds fen support

This commit is contained in:
latenitecoding 2024-01-30 10:09:03 -06:00
parent 38e11cc131
commit 2e0e162608
6 changed files with 100 additions and 26 deletions

View file

@ -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.

View file

@ -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')

View file

@ -55,15 +55,18 @@ 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<Api | null>(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({
@ -72,7 +75,8 @@ export const ChessStudy = ({
promotion: move.promotion,
});
});
return chess;
return [firstPlayer, initialMoveNumber, chess];
}, [chessStudyData.moves]);
const [chessLogic, setChessLogic] = useState(initialChessLogic);
@ -258,6 +262,8 @@ export const ChessStudy = ({
<PgnViewer
history={gameState.study.moves}
currentMoveId={gameState.currentMove?.moveId}
firstPlayer={firstPlayer}
initialMoveNumber={initialMoveNumber}
onBackButtonClick={() =>
dispatch({ type: 'DISPLAY_PREVIOUS_MOVE_IN_HISTORY' })
}
@ -271,6 +277,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);
}
}}
/>
</div>
</div>

View file

@ -1,11 +1,11 @@
import { ArrowLeft, ArrowRight, Save } from 'lucide-react';
import { ArrowLeft, ArrowRight, Copy, Save } from 'lucide-react';
import * as React from 'react';
import { ReactNode, useEffect, useMemo, useRef } from 'react';
import { ChessStudyMove } from 'src/lib/storage';
const chunkArray = <T,>(array: T[], chunkSize: number) => {
const chunkArray = <T,>(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,19 +108,25 @@ export const PgnViewer = React.memo(
({
history,
currentMoveId,
firstPlayer,
initialMoveNumber,
onBackButtonClick,
onForwardButtonClick,
onMoveItemClick,
onSaveButtonClick,
onCopyButtonClick,
}: {
history: ChessStudyMove[];
currentMoveId: string;
firstPlayer: string;
initialMoveNumber: number;
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 (
<div className="height-width-100">
@ -132,8 +138,15 @@ export const PgnViewer = React.memo(
return (
<React.Fragment key={wMove.san + bMove?.san + currentMoveIndex}>
<p className="move-indicator center">
{currentMoveIndex + 1}
{currentMoveIndex + initialMoveNumber}
</p>
{(firstPlayer === 'b' && bMove === undefined && currentMoveIndex === 0) && (
<MoveItem
san={'...'}
isCurrentMove={false}
onMoveItemClick={() => {}}
/>
)}
<MoveItem
san={wMove.san}
isCurrentMove={wMove.moveId === currentMoveId}
@ -175,11 +188,20 @@ export const PgnViewer = React.memo(
}
moveIndicator={
(wMoveVarianti === 0 &&
(firstPlayer === 'w'
|| currentMoveIndex > 0) &&
`${
currentMoveIndex +
1 +
initialMoveNumber +
wMoveVarianti
}... `) ||
(firstPlayer === 'b' &&
currentMoveIndex === 0 &&
`${
currentMoveIndex +
initialMoveNumber +
wMoveVarianti
}. `) ||
null
}
/>
@ -193,13 +215,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
}
/>
)}
@ -236,9 +259,13 @@ export const PgnViewer = React.memo(
onMoveItemClick={() =>
onMoveItemClick(wMove.moveId)
}
moveIndicator={`${
currentMoveIndex + 2 + bMoveVarianti
}. `}
moveIndicator={
`${
currentMoveIndex
+ initialMoveNumber + 1
+ bMoveVarianti
}. `
}
/>
{bMove && (
<VariantMoveItem
@ -274,9 +301,14 @@ export const PgnViewer = React.memo(
<button onClick={() => onForwardButtonClick()}>
<ArrowRight />
</button>
</div>
<div className="button-section">
<button onClick={() => onSaveButtonClick()}>
<Save strokeWidth={'1px'} />
</button>
<button onClick={() => onCopyButtonClick()}>
<Copy strokeWidth={'1px'} />
</button>
</div>
</div>
);

View file

@ -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;
}

View file

@ -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) {