feat: render board, wire up plugin and user config

This commit is contained in:
chrislicodes 2023-05-07 17:22:46 +02:00
parent 4df70d9fee
commit 2f92f24b4b
11 changed files with 430 additions and 54 deletions

3
assets/board/green.css Normal file
View file

@ -0,0 +1,3 @@
.green-board cg-board {
background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4PSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICAgIHZpZXdCb3g9IjAgMCA4IDgiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyI+CjxnIGlkPSJhIj4KICA8ZyBpZD0iYiI+CiAgICA8ZyBpZD0iYyI+CiAgICAgIDxnIGlkPSJkIj4KICAgICAgICA8cmVjdCB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjZmZmZmRkIiBpZD0iZSIvPgogICAgICAgIDx1c2UgeD0iMSIgeT0iMSIgaHJlZj0iI2UiIHg6aHJlZj0iI2UiLz4KICAgICAgICA8cmVjdCB5PSIxIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSIjODZhNjY2IiBpZD0iZiIvPgogICAgICAgIDx1c2UgeD0iMSIgeT0iLTEiIGhyZWY9IiNmIiB4OmhyZWY9IiNmIi8+CiAgICAgIDwvZz4KICAgICAgPHVzZSB4PSIyIiBocmVmPSIjZCIgeDpocmVmPSIjZCIvPgogICAgPC9nPgogICAgPHVzZSB4PSI0IiBocmVmPSIjYyIgeDpocmVmPSIjYyIvPgogIDwvZz4KICA8dXNlIHk9IjIiIGhyZWY9IiNiIiB4OmhyZWY9IiNiIi8+CjwvZz4KPHVzZSB5PSI0IiBocmVmPSIjYSIgeDpocmVmPSIjYSIvPgo8L3N2Zz4K");
}

View file

@ -1,6 +1,7 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { renameStyles } from "./rename-styles.mjs";
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
@ -38,6 +39,7 @@ const context = await esbuild.context({
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
plugins: [renameStyles],
});
if (prod) {

16
rename-styles.mjs Normal file
View file

@ -0,0 +1,16 @@
import { existsSync, renameSync } from "fs";
export const renameStyles = {
name: "rename-styles",
setup(build) {
build.onEnd(() => {
const { outfile } = build.initialOptions;
const outcss = outfile.replace(/\.js$/, ".css");
const fixcss = outfile.replace(/main\.js$/, "styles.css");
if (existsSync(outcss)) {
console.log("Renaming", outcss, "to", fixcss);
renameSync(outcss, fixcss);
}
});
},
};

View file

@ -2,11 +2,13 @@ import { PluginSettingTab, App, Setting } from "obsidian";
import ChessifyPlugin from "src/main";
export interface ChessifyPluginSettings {
defaultOrientation: "white" | "black";
boardOrientation: "white" | "black";
boardColor: "green" | "brown";
}
export const DEFAULT_SETTINGS: ChessifyPluginSettings = {
defaultOrientation: "white",
boardOrientation: "white",
boardColor: "green",
};
export class SettingsTab extends PluginSettingTab {
@ -25,17 +27,35 @@ export class SettingsTab extends PluginSettingTab {
containerEl.createEl("h2", { text: "Obsidian Chessify Settings" });
new Setting(containerEl)
.setName("Orientation")
.setName("Board Orientation")
.setDesc("Sets the default orientation of the board")
.addDropdown((dropdown) => {
dropdown.addOption("white", "White");
dropdown.addOption("black", "Black");
dropdown
.setValue(this.plugin.settings.defaultOrientation)
.setValue(this.plugin.settings.boardOrientation)
.onChange((orientation) => {
this.plugin.settings.defaultOrientation =
orientation as "white" | "black";
this.plugin.settings.boardOrientation = orientation as
| "white"
| "black";
this.plugin.saveSettings();
});
});
new Setting(containerEl)
.setName("Board Color")
.setDesc("Sets the default color of the board")
.addDropdown((dropdown) => {
dropdown.addOption("green", "Green");
dropdown.addOption("brown", "Brown");
dropdown
.setValue(this.plugin.settings.boardColor)
.onChange((boardColor) => {
this.plugin.settings.boardColor = boardColor as
| "green"
| "brown";
this.plugin.saveSettings();
});
});

View file

@ -0,0 +1,58 @@
import * as React from "react";
import { useEffect, useRef, useState } from "react";
import { Chessground as ChessgroundApi } from "chessground";
import { Api } from "chessground/api";
import { Config } from "chessground/config";
export interface ChessGroundSettings {
config?: Config;
boardColor?: "brown" | "green";
}
export function ChessgroundWrapper({
boardColor = "green",
config = {},
}: ChessGroundSettings) {
const [api, setApi] = useState<Api | null>(null);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref && ref.current && !api) {
//Render the Chessboard
const chessgroundApi = ChessgroundApi(ref.current, {
animation: { enabled: true, duration: 100 },
...config,
});
setApi(chessgroundApi);
} else if (ref && ref.current && api) {
api.set(config);
}
}, [ref]);
useEffect(() => {
api?.set(config);
}, [api, config]);
return (
<div
style={{
height: "450px",
width: "450px",
}}
className={`${boardColor}-board`}
>
<div
ref={ref}
style={{
height: "100%",
width: "100%",
display:
"table" /* hack: round to full pixel size in chrome */,
}}
/>
</div>
);
}

View file

@ -0,0 +1,27 @@
import { App } from "obsidian";
import * as React from "react";
import { ChessifyPluginSettings } from "src/components/obsidian/SettingsTab";
import { ChessgroundWrapper, ChessGroundSettings } from "./ChessgroundWrapper";
import { parseUserConfig } from "src/utils";
export type ChessifyConfig = ChessGroundSettings;
interface AppProps {
source: string;
app: App;
pluginSettings: ChessifyPluginSettings;
}
export const Chessify = ({ source, pluginSettings }: AppProps) => {
const config = parseUserConfig(pluginSettings, source);
return (
<ChessgroundWrapper
boardColor={config.boardColor}
config={{
fen: config.fen,
orientation: config.boardOrientation,
}}
/>
);
};

View file

@ -3,19 +3,28 @@ import {
ChessifyPluginSettings,
DEFAULT_SETTINGS,
SettingsTab,
} from "./obsidian-components/SettingsTab";
} from "./components/obsidian/SettingsTab";
import * as ReactDOM from "react-dom/client";
import * as React from "react";
import { Chessify } from "./react-components/Chessify";
import { Chessify } from "./components/react/Chessify";
// these styles must be imported somewhere
import "chessground/assets/chessground.base.css";
import "chessground/assets/chessground.brown.css";
import "chessground/assets/chessground.cburnett.css";
import "assets/board/green.css";
export default class ChessifyPlugin extends Plugin {
settings: ChessifyPluginSettings;
async onload() {
// Load the Settings
// Load the settings
await this.loadSettings();
// Add settings tab
this.addSettingTab(new SettingsTab(this.app, this));
// Add chessify code block processor
this.registerMarkdownCodeBlockProcessor(
"chessify",
(source, el, ctx) => {
@ -70,7 +79,7 @@ class ReactView extends MarkdownRenderChild {
<Chessify
source={this.source}
app={this.app}
settings={this.settings}
pluginSettings={this.settings}
/>
</React.StrictMode>
);

View file

@ -1,21 +0,0 @@
import { App } from "obsidian";
import * as React from "react";
import { ChessifyPluginSettings } from "src/obsidian-components/SettingsTab";
import { AppContext } from "./store/obsidian";
interface AppProps {
source: string;
app: App;
settings: ChessifyPluginSettings;
}
export const Chessify = ({ source, app, settings }: AppProps) => {
return (
<AppContext.Provider value={{ app, settings }}>
<div>
<p>Hello React: This is the passed source: {source}</p>
<p>This is a reload test again</p>
</div>
</AppContext.Provider>
);
};

View file

@ -1,18 +0,0 @@
import * as React from "react";
import { App } from "obsidian";
import { ChessifyPluginSettings } from "src/obsidian-components/SettingsTab";
interface AppContextProps {
app: App;
settings: ChessifyPluginSettings;
}
export const AppContext = React.createContext<AppContextProps | null>(null);
export const useApp = (): AppContextProps | null => {
if (AppContext) {
return React.useContext(AppContext);
}
return null;
};

25
src/utils/index.ts Normal file
View file

@ -0,0 +1,25 @@
import { parseYaml } from "obsidian";
import { ChessifyPluginSettings } from "src/components/obsidian/SettingsTab";
type ChessifyAppConfig = ChessifyPluginSettings & {
fen: string;
};
export const parseUserConfig = (
settings: ChessifyPluginSettings,
content: string
): ChessifyAppConfig => {
const chessifyConfig: ChessifyAppConfig = {
...settings,
fen: "",
};
try {
return {
...chessifyConfig,
...parseYaml(content),
};
} catch (e) {
throw Error("Something went wrong during parsing. :(");
}
};

File diff suppressed because one or more lines are too long