@hackersheet/next-document-content-kifu 0.1.0-alpha.1 → 0.1.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/cjs/components/kifu/kifu.d.ts +1 -1
  2. package/dist/cjs/components/kifu/kifu.js +5 -21
  3. package/dist/cjs/components/kifu-to/kifu-to.js +1 -1
  4. package/dist/cjs/components/shogi-player/button.d.ts +8 -0
  5. package/dist/cjs/components/shogi-player/button.js +44 -0
  6. package/dist/cjs/components/shogi-player/index.d.ts +2 -0
  7. package/dist/cjs/components/shogi-player/index.js +38 -0
  8. package/dist/cjs/components/shogi-player/moves-area.d.ts +11 -0
  9. package/dist/cjs/components/shogi-player/moves-area.js +78 -0
  10. package/dist/cjs/components/shogi-player/shogi-board-canvas.d.ts +17 -0
  11. package/dist/cjs/components/shogi-player/shogi-board-canvas.js +180 -0
  12. package/dist/cjs/components/shogi-player/shogi-hands-canvas.d.ts +16 -0
  13. package/dist/cjs/components/shogi-player/shogi-hands-canvas.js +140 -0
  14. package/dist/cjs/components/shogi-player/shogi-player.d.ts +10 -0
  15. package/dist/cjs/components/shogi-player/shogi-player.js +112 -0
  16. package/dist/cjs/index.js +3 -3
  17. package/dist/esm/components/kifu/kifu.d.mts +1 -1
  18. package/dist/esm/components/kifu/kifu.mjs +5 -21
  19. package/dist/esm/components/kifu-to/kifu-to.mjs +1 -1
  20. package/dist/esm/components/shogi-player/button.d.mts +8 -0
  21. package/dist/esm/components/shogi-player/button.mjs +14 -0
  22. package/dist/esm/components/shogi-player/index.d.mts +2 -0
  23. package/dist/esm/components/shogi-player/index.mjs +4 -0
  24. package/dist/esm/components/shogi-player/moves-area.d.mts +11 -0
  25. package/dist/esm/components/shogi-player/moves-area.mjs +44 -0
  26. package/dist/esm/components/shogi-player/shogi-board-canvas.d.mts +17 -0
  27. package/dist/esm/components/shogi-player/shogi-board-canvas.mjs +150 -0
  28. package/dist/esm/components/shogi-player/shogi-hands-canvas.d.mts +16 -0
  29. package/dist/esm/components/shogi-player/shogi-hands-canvas.mjs +110 -0
  30. package/dist/esm/components/shogi-player/shogi-player.d.mts +10 -0
  31. package/dist/esm/components/shogi-player/shogi-player.mjs +82 -0
  32. package/package.json +13 -13
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+ var shogi_player_exports = {};
31
+ __export(shogi_player_exports, {
32
+ default: () => ShogiPlayer
33
+ });
34
+ module.exports = __toCommonJS(shogi_player_exports);
35
+ var import_json_kifu_format = require("json-kifu-format");
36
+ var import_react = __toESM(require("react"));
37
+ var import_button = __toESM(require("./button"));
38
+ var import_moves_area = require("./moves-area");
39
+ var import_shogi_board_canvas = __toESM(require("./shogi-board-canvas"));
40
+ var import_shogi_hands_canvas = __toESM(require("./shogi-hands-canvas"));
41
+ function ShogiPlayer(props) {
42
+ const [player] = (0, import_react.useState)(import_json_kifu_format.JKFPlayer.parse(props.kifuText.trim()));
43
+ const [pieces, setPieces] = (0, import_react.useState)(player.shogi.board);
44
+ const [hands, setHands] = (0, import_react.useState)(player.shogi.hands);
45
+ const [moves, setMoves] = (0, import_react.useState)(player.kifu.moves);
46
+ const [tesuu, setTesuu] = (0, import_react.useState)(player.tesuu);
47
+ const [currentMove, setCurrentMove] = (0, import_react.useState)(player.getMove());
48
+ const [isSente, setIsSente] = (0, import_react.useState)(true);
49
+ const [maxTesuu, setMaxTesuu] = (0, import_react.useState)(player.getMaxTesuu());
50
+ const [comments, setComments] = (0, import_react.useState)(player.getComments());
51
+ const size = props.size ? props.size : 360;
52
+ const updateState = () => {
53
+ setPieces([...player.shogi.board]);
54
+ setHands([...player.shogi.hands]);
55
+ setMoves([...player.kifu.moves]);
56
+ setCurrentMove(player.getMove());
57
+ setMaxTesuu(player.getMaxTesuu());
58
+ setComments(player.getComments());
59
+ setTesuu(player.tesuu);
60
+ };
61
+ const handleForward = () => {
62
+ player.forward();
63
+ updateState();
64
+ };
65
+ const handleBackward = () => {
66
+ player.backward();
67
+ updateState();
68
+ };
69
+ const handleGoto = (tesuu2) => {
70
+ player.goto(tesuu2);
71
+ updateState();
72
+ };
73
+ const handeleToggle = () => {
74
+ setIsSente(!isSente);
75
+ updateState();
76
+ };
77
+ const handleKeydown = (0, import_react.useCallback)(
78
+ (event) => {
79
+ event.preventDefault();
80
+ event.stopPropagation();
81
+ switch (event.key) {
82
+ case "Up":
83
+ case "ArrowUp":
84
+ handleGoto(0);
85
+ break;
86
+ case "Down":
87
+ case "ArrowDown":
88
+ handleGoto(maxTesuu);
89
+ break;
90
+ case "Left":
91
+ case "ArrowLeft":
92
+ handleBackward();
93
+ break;
94
+ case " ":
95
+ case "Right":
96
+ case "ArrowRight":
97
+ handleForward();
98
+ break;
99
+ case "r":
100
+ handeleToggle();
101
+ break;
102
+ }
103
+ },
104
+ [maxTesuu, isSente]
105
+ );
106
+ (0, import_react.useEffect)(() => {
107
+ if (props.tesuu !== void 0) {
108
+ handleGoto(props.tesuu);
109
+ }
110
+ }, [props.tesuu]);
111
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col md:flex-row w-fit", tabIndex: 1, onKeyDown: handleKeydown }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "bg-[#f9d27a] text-black text-xs text-right p-1" }, isSente ? "\u2616 " + player.kifu.header["\u5F8C\u624B"] : "\u2617 " + player.kifu.header["\u5148\u624B"]), /* @__PURE__ */ import_react.default.createElement(import_shogi_hands_canvas.default, { size, hands, isSente, isTop: true }), /* @__PURE__ */ import_react.default.createElement(import_shogi_board_canvas.default, { size, pieces, isSente, currentMove }), /* @__PURE__ */ import_react.default.createElement(import_shogi_hands_canvas.default, { size, hands, isSente, isTop: false }), /* @__PURE__ */ import_react.default.createElement("div", { className: "bg-[#f9d27a] text-black text-xs p-1" }, isSente ? "\u2617 " + player.kifu.header["\u5148\u624B"] : "\u2616 " + player.kifu.header["\u5F8C\u624B"])), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col md:w-fit bg-[#f9d27a] p-4 gap-4" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hidden md:block flex-1 relative w-full" }, /* @__PURE__ */ import_react.default.createElement(import_moves_area.MovesArea, { moves, tesuu, onTesuuChange: handleGoto })), /* @__PURE__ */ import_react.default.createElement("div", { className: "hidden md:block text-black border-black border-2 p-1 text-xs max-h-40 w-0 min-w-full overflow-auto" }, comments.map((comment, index) => /* @__PURE__ */ import_react.default.createElement("div", { key: index }, comment)), comments.length === 0 && tesuu !== 0 && /* @__PURE__ */ import_react.default.createElement("div", null, "\xA0"), tesuu === 0 && Object.entries(player.kifu.header).map(([key, value], i) => /* @__PURE__ */ import_react.default.createElement("div", { key: i, className: "whitespace-nowrap" }, key, ": ", value))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ import_react.default.createElement(import_button.default, { onClick: () => handleGoto(0) }, "\u6700\u521D"), /* @__PURE__ */ import_react.default.createElement(import_button.default, { onClick: handleBackward }, "\u524D"), /* @__PURE__ */ import_react.default.createElement(import_button.default, { onClick: handleForward }, "\u6B21"), /* @__PURE__ */ import_react.default.createElement(import_button.default, { onClick: () => handleGoto(maxTesuu) }, "\u6700\u5F8C"), /* @__PURE__ */ import_react.default.createElement(import_button.default, { onClick: handeleToggle }, "\u53CD\u8EE2"))));
112
+ }
package/dist/cjs/index.js CHANGED
@@ -13,9 +13,9 @@ var __copyProps = (to, from, except, desc) => {
13
13
  };
14
14
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
15
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
- var src_exports = {};
17
- module.exports = __toCommonJS(src_exports);
18
- __reExport(src_exports, require("./components"), module.exports);
16
+ var index_exports = {};
17
+ module.exports = __toCommonJS(index_exports);
18
+ __reExport(index_exports, require("./components"), module.exports);
19
19
  // Annotate the CommonJS export names for ESM import in node:
20
20
  0 && (module.exports = {
21
21
  ...require("./components")
@@ -1,6 +1,6 @@
1
1
  import { KifuComponentProps } from '@hackersheet/react-document-content';
2
2
  import React from 'react';
3
3
 
4
- declare function Kifu({ code, language }: KifuComponentProps): React.JSX.Element | null;
4
+ declare function Kifu({ code, language }: KifuComponentProps): React.JSX.Element;
5
5
 
6
6
  export { Kifu as default };
@@ -1,36 +1,20 @@
1
1
  "use client";
2
- import { KifuLite, KifuStore } from "kifu-for-js";
3
2
  import { useSearchParams } from "next/navigation";
4
3
  import React, { useEffect, useState } from "react";
4
+ import { ShogiPlayer } from "../shogi-player";
5
5
  function Kifu({ code, language }) {
6
+ const [ply, setPly] = useState(0);
6
7
  const [, filename] = language.split(":");
7
8
  const searchParams = useSearchParams();
8
9
  const id = filename ? `user-content-${filename}` : void 0;
9
- const [kifuStore] = useState(() => new KifuStore({ kifu: code }));
10
- useEffect(() => {
11
- initKifuUserSettings();
12
- }, []);
13
10
  useEffect(() => {
14
11
  const newPly = Number(searchParams.get("ply") ?? 0);
15
12
  const hash = typeof window !== "undefined" ? window.location.hash.replace(/^#!?/, "") : "";
16
13
  if (hash === id) {
17
- kifuStore.player.goto(newPly);
14
+ setPly(newPly);
18
15
  }
19
- }, [searchParams, kifuStore, id]);
20
- if (!kifuStore) {
21
- return null;
22
- }
23
- return /* @__PURE__ */ React.createElement("div", { className: "my-4 flex justify-center", id }, /* @__PURE__ */ React.createElement(KifuLite, { style: { width: "100%" }, kifuStore }));
24
- }
25
- function initKifuUserSettings() {
26
- const LOCALSTORAGE_KEY = "kifuforjs";
27
- const defaultSettings = {
28
- hapticFeedback: false
29
- };
30
- const settings = localStorage.getItem(LOCALSTORAGE_KEY);
31
- if (!settings) {
32
- localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(defaultSettings));
33
- }
16
+ }, [searchParams, id]);
17
+ return /* @__PURE__ */ React.createElement("div", { className: "kifu-block", id }, /* @__PURE__ */ React.createElement(ShogiPlayer, { kifuText: code, tesuu: ply, size: 320 }));
34
18
  }
35
19
  export {
36
20
  Kifu as default
@@ -4,7 +4,7 @@ function KifuTo({ id, ply, label: defaultLabel }) {
4
4
  const fullId = `user-content-${id}`;
5
5
  const href = `?ply=${ply}#${fullId}`;
6
6
  const label = defaultLabel ?? `${ply}\u624B\u76EE`;
7
- return /* @__PURE__ */ React.createElement(Link, { href }, label);
7
+ return /* @__PURE__ */ React.createElement(Link, { href, prefetch: false }, label);
8
8
  }
9
9
  export {
10
10
  KifuTo as default
@@ -0,0 +1,8 @@
1
+ import React, { MouseEventHandler, PropsWithChildren } from 'react';
2
+
3
+ type ButtonProps = {
4
+ onClick?: MouseEventHandler<HTMLButtonElement>;
5
+ } & PropsWithChildren;
6
+ declare function Button({ children, onClick }: ButtonProps): React.JSX.Element;
7
+
8
+ export { type ButtonProps, Button as default };
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ function Button({ children, onClick }) {
3
+ return /* @__PURE__ */ React.createElement(
4
+ "button",
5
+ {
6
+ className: "border-2 text-xs text-black p-2 border-black rounded-lg hover:bg-amber-100 cursor-pointer",
7
+ onClick
8
+ },
9
+ children
10
+ );
11
+ }
12
+ export {
13
+ Button as default
14
+ };
@@ -0,0 +1,2 @@
1
+ export { default as ShogiPlayer } from './shogi-player.mjs';
2
+ import 'react';
@@ -0,0 +1,4 @@
1
+ import ShogiPlayer from "./shogi-player";
2
+ export {
3
+ ShogiPlayer
4
+ };
@@ -0,0 +1,11 @@
1
+ import { IMoveFormat } from 'json-kifu-format/dist/src/Formats';
2
+ import React from 'react';
3
+
4
+ type MovesAreaProps = {
5
+ moves: IMoveFormat[];
6
+ tesuu: number;
7
+ onTesuuChange?: (tesuu: number) => void;
8
+ };
9
+ declare function MovesArea(props: MovesAreaProps): React.JSX.Element;
10
+
11
+ export { MovesArea, type MovesAreaProps };
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { JKFPlayer } from "json-kifu-format";
3
+ import React, { Fragment, useEffect, useRef } from "react";
4
+ function MovesArea(props) {
5
+ const moves = props.moves;
6
+ const scrollRef = useRef(null);
7
+ const containerRef = useRef(null);
8
+ useEffect(() => {
9
+ if (scrollRef.current && containerRef.current) {
10
+ const containerRect = containerRef.current.getBoundingClientRect();
11
+ const scrollRect = scrollRef.current.getBoundingClientRect();
12
+ const offset = scrollRect.top - containerRect.top + containerRef.current.scrollTop - 72;
13
+ containerRef.current.scrollTo({
14
+ top: offset,
15
+ behavior: "auto"
16
+ });
17
+ }
18
+ }, [props.tesuu]);
19
+ const current = 0 === props.tesuu ? " bg-amber-600" : "";
20
+ return /* @__PURE__ */ React.createElement("div", { className: "absolute overflow-y-auto h-full w-full border-2 border-black text-black", ref: containerRef }, /* @__PURE__ */ React.createElement("div", { className: "grid gap-0 text-xs" }, /* @__PURE__ */ React.createElement(
21
+ "div",
22
+ {
23
+ onClick: () => props.onTesuuChange && props.onTesuuChange(0),
24
+ className: "col-span-500 grid grid-cols-subgrid gap-2 py-1 px-2 cursor-pointer hover:bg-amber-100" + current
25
+ },
26
+ /* @__PURE__ */ React.createElement("div", null, 0 === props.tesuu && /* @__PURE__ */ React.createElement("div", { ref: scrollRef })),
27
+ /* @__PURE__ */ React.createElement("div", null, "\u958B\u59CB\u5C40\u9762")
28
+ ), moves.map((move, index) => {
29
+ if (index === 0) return;
30
+ const current2 = index === props.tesuu ? " bg-amber-600" : "";
31
+ return /* @__PURE__ */ React.createElement(Fragment, { key: index }, /* @__PURE__ */ React.createElement(
32
+ "div",
33
+ {
34
+ className: "col-span-500 grid grid-cols-subgrid border-black gap-2 border-t py-1 px-2 cursor-pointer hover:bg-amber-100" + current2,
35
+ onClick: () => props.onTesuuChange && props.onTesuuChange(index)
36
+ },
37
+ /* @__PURE__ */ React.createElement("div", { className: "flex" }, index === props.tesuu && /* @__PURE__ */ React.createElement("div", { ref: scrollRef }), /* @__PURE__ */ React.createElement("div", { className: "tabular-nums text-right flex-auto" }, index)),
38
+ /* @__PURE__ */ React.createElement("div", null, JKFPlayer.moveToReadableKifu(move))
39
+ ));
40
+ })));
41
+ }
42
+ export {
43
+ MovesArea
44
+ };
@@ -0,0 +1,17 @@
1
+ import { IMoveMoveFormat } from 'json-kifu-format/dist/src/Formats';
2
+ import React from 'react';
3
+ import { Piece } from 'shogi.js';
4
+
5
+ type Props = {
6
+ size?: number;
7
+ boardColor?: string;
8
+ lineColor?: string;
9
+ fontFamily?: string;
10
+ fontSizeRatio?: number;
11
+ pieces: Piece[][];
12
+ isSente?: boolean;
13
+ currentMove?: IMoveMoveFormat;
14
+ };
15
+ declare const ShogiBoardCanvas: React.FC<Props>;
16
+
17
+ export { ShogiBoardCanvas as default };
@@ -0,0 +1,150 @@
1
+ "use client";
2
+ import { JKFPlayer } from "json-kifu-format";
3
+ import React from "react";
4
+ import { Color } from "shogi.js";
5
+ const getCanvasDimensions = (size) => {
6
+ const dpr = window.devicePixelRatio || 1;
7
+ return { width: size * dpr, height: size * dpr, dpr };
8
+ };
9
+ const getBoardLayout = (size) => {
10
+ const margin = size * 0.06;
11
+ const boardSize = size - margin * 2;
12
+ const cell = boardSize / 9;
13
+ return { margin, boardSize, cell };
14
+ };
15
+ const drawBackground = (ctx, size, boardColor) => {
16
+ ctx.fillStyle = boardColor;
17
+ ctx.fillRect(0, 0, size, size);
18
+ };
19
+ const drawBoard = (ctx, margin, boardSize, lineColor) => {
20
+ ctx.strokeStyle = lineColor;
21
+ ctx.lineWidth = 2;
22
+ ctx.strokeRect(margin, margin, boardSize, boardSize);
23
+ ctx.lineWidth = 1;
24
+ Array.from({ length: 8 }).forEach((_, i) => {
25
+ const offset = (i + 1) * (boardSize / 9);
26
+ ctx.beginPath();
27
+ ctx.moveTo(margin + offset, margin);
28
+ ctx.lineTo(margin + offset, margin + boardSize);
29
+ ctx.stroke();
30
+ ctx.beginPath();
31
+ ctx.moveTo(margin, margin + offset);
32
+ ctx.lineTo(margin + boardSize, margin + offset);
33
+ ctx.stroke();
34
+ });
35
+ };
36
+ const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente) => {
37
+ ctx.textAlign = "center";
38
+ ctx.textBaseline = "middle";
39
+ pieces.forEach((row, rowIndex) => {
40
+ row.forEach((piece, colIndex) => {
41
+ if (!piece) return;
42
+ const x = isSente ? 8 - rowIndex : rowIndex;
43
+ const y = isSente ? colIndex : 8 - colIndex;
44
+ const px = margin + x * cell + cell / 2;
45
+ const py = margin + y * cell + cell / 2;
46
+ ctx.save();
47
+ ctx.translate(px, py);
48
+ if (isSente && piece.color === Color.White) {
49
+ ctx.rotate(Math.PI);
50
+ } else if (!isSente && piece.color === Color.Black) {
51
+ ctx.rotate(Math.PI);
52
+ }
53
+ const kan = JKFPlayer.kindToKan(piece.kind);
54
+ ctx.fillStyle = "#000";
55
+ if (kan.length === 2) {
56
+ const baseFontSize = cell * fontSizeRatio * 0.5;
57
+ ctx.font = `${baseFontSize}px ${fontFamily}`;
58
+ const scaleX = 2;
59
+ const scaleY = 1;
60
+ const offsetY = cell * 0.18;
61
+ ctx.save();
62
+ ctx.scale(scaleX, scaleY);
63
+ ctx.fillText(kan[0], 0 / scaleX, -offsetY / scaleY);
64
+ ctx.restore();
65
+ ctx.save();
66
+ ctx.scale(scaleX, scaleY);
67
+ ctx.fillText(kan[1], 0 / scaleX, offsetY / scaleY);
68
+ ctx.restore();
69
+ } else {
70
+ const fontSize = cell * fontSizeRatio;
71
+ ctx.font = `${fontSize}px ${fontFamily}`;
72
+ ctx.fillText(kan, 0, 0);
73
+ }
74
+ ctx.restore();
75
+ });
76
+ });
77
+ };
78
+ const drawCoordinates = (ctx, margin, boardSize, cell, fontFamily, isSente) => {
79
+ ctx.fillStyle = "#000";
80
+ ctx.font = `${cell * 0.35}px ${fontFamily}`;
81
+ ctx.textAlign = "center";
82
+ ctx.textBaseline = "middle";
83
+ Array.from({ length: 9 }).forEach((_, i) => {
84
+ const x = margin + i * cell + cell / 2;
85
+ const y = margin / 2;
86
+ const label = isSente ? JKFPlayer.numToZen(9 - i) : JKFPlayer.numToZen(i + 1);
87
+ ctx.fillText(label, x, y);
88
+ });
89
+ Array.from({ length: 9 }).forEach((_, i) => {
90
+ const x = margin + boardSize + margin / 2;
91
+ const y = margin + i * cell + cell / 2;
92
+ const label = isSente ? JKFPlayer.numToKan(i + 1) : JKFPlayer.numToKan(9 - i);
93
+ ctx.fillText(label, x, y);
94
+ });
95
+ };
96
+ const drawHighlightedCell = (ctx, margin, cell, isSente, currentMove) => {
97
+ if (!currentMove) return;
98
+ if (currentMove.to) {
99
+ const toRow = currentMove.to.x - 1;
100
+ const toCol = currentMove.to.y - 1;
101
+ const toX = isSente ? 8 - toRow : toRow;
102
+ const toY = isSente ? toCol : 8 - toCol;
103
+ ctx.fillStyle = "rgba(255,0,0,0.1)";
104
+ ctx.fillRect(margin + toX * cell, margin + toY * cell, cell, cell);
105
+ }
106
+ if (currentMove.from) {
107
+ const fromRow = currentMove.from.x - 1;
108
+ const fromCol = currentMove.from.y - 1;
109
+ const fromX = isSente ? 8 - fromRow : fromRow;
110
+ const fromY = isSente ? fromCol : 8 - fromCol;
111
+ ctx.fillStyle = "rgba(255,0,0,0.1)";
112
+ ctx.fillRect(margin + fromX * cell, margin + fromY * cell, cell, cell);
113
+ }
114
+ };
115
+ const ShogiBoardCanvas = ({
116
+ size = 360,
117
+ boardColor = "#f9d27a",
118
+ lineColor = "#000",
119
+ fontFamily = "serif",
120
+ fontSizeRatio = 0.7,
121
+ pieces,
122
+ isSente = true,
123
+ currentMove
124
+ }) => {
125
+ const canvasRef = React.useRef(null);
126
+ React.useEffect(() => {
127
+ const canvas = canvasRef.current;
128
+ if (!canvas) return;
129
+ const ctx = canvas.getContext("2d");
130
+ if (!ctx) return;
131
+ const { width, height, dpr } = getCanvasDimensions(size);
132
+ canvas.width = width;
133
+ canvas.height = height;
134
+ canvas.style.width = `${size}px`;
135
+ canvas.style.height = `${size}px`;
136
+ ctx.scale(dpr, dpr);
137
+ ctx.clearRect(0, 0, size, size);
138
+ const { margin, boardSize, cell } = getBoardLayout(size);
139
+ drawBackground(ctx, size, boardColor);
140
+ drawBoard(ctx, margin, boardSize, lineColor);
141
+ drawHighlightedCell(ctx, margin, cell, isSente, currentMove);
142
+ drawPieces(ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente);
143
+ drawCoordinates(ctx, margin, boardSize, cell, fontFamily, isSente);
144
+ }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, pieces, isSente, currentMove]);
145
+ return /* @__PURE__ */ React.createElement("canvas", { ref: canvasRef, width: size, height: size });
146
+ };
147
+ var shogi_board_canvas_default = ShogiBoardCanvas;
148
+ export {
149
+ shogi_board_canvas_default as default
150
+ };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { Piece } from 'shogi.js';
3
+
4
+ type Props = {
5
+ size?: number;
6
+ boardColor?: string;
7
+ lineColor?: string;
8
+ fontFamily?: string;
9
+ fontSizeRatio?: number;
10
+ hands: Piece[][];
11
+ isSente?: boolean;
12
+ isTop?: boolean;
13
+ };
14
+ declare const ShogiHandsCanvas: React.FC<Props>;
15
+
16
+ export { ShogiHandsCanvas as default };
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ import { JKFPlayer } from "json-kifu-format";
3
+ import React from "react";
4
+ import { Color } from "shogi.js";
5
+ const getCanvasDimensions = (size) => {
6
+ const dpr = window.devicePixelRatio || 1;
7
+ return { width: size * dpr, height: size * dpr, dpr };
8
+ };
9
+ const getHandsLayout = (size) => {
10
+ const margin = size * 0.06;
11
+ const boardSize = size - margin * 2;
12
+ const cellSize = boardSize / 9;
13
+ const handsHeight = cellSize + margin + 2;
14
+ return { margin, handsHeight, boardSize, cellSize };
15
+ };
16
+ const drawBackground = (ctx, width, height, color) => {
17
+ ctx.fillStyle = color;
18
+ ctx.fillRect(0, 0, width, height);
19
+ };
20
+ const drawHandsFrame = (ctx, x, y, width, height, lineColor, isTop) => {
21
+ ctx.strokeStyle = lineColor;
22
+ ctx.lineWidth = 2;
23
+ if (isTop) {
24
+ ctx.strokeRect(x, y, width, height);
25
+ } else {
26
+ ctx.strokeRect(x, 2, width, height);
27
+ }
28
+ };
29
+ const drawCoordinates = (ctx, fontSize, fontFamily) => {
30
+ ctx.fillStyle = "#000";
31
+ ctx.font = `${fontSize}px ${fontFamily}`;
32
+ ctx.textAlign = "center";
33
+ ctx.textBaseline = "middle";
34
+ };
35
+ const drawPieces = (ctx, hands, margin, boardSize, cell, fontFamily, fontSizeRatio, isSente, isTop) => {
36
+ ctx.textAlign = "center";
37
+ ctx.textBaseline = "middle";
38
+ const pieces = isSente && isTop || !isSente && !isTop ? hands[Color.White] : hands[Color.Black];
39
+ if (!pieces || pieces.length === 0) return;
40
+ const grouped = pieces.reduce(
41
+ (acc, piece) => {
42
+ if (!piece) return acc;
43
+ const key = piece.kind;
44
+ if (!acc[key]) acc[key] = { count: 0, color: piece.color };
45
+ acc[key].count += 1;
46
+ return acc;
47
+ },
48
+ {}
49
+ );
50
+ const order = ["OU", "HI", "KA", "KI", "GI", "KE", "KY", "FU"];
51
+ const kinds = order.filter((kind) => grouped[kind]);
52
+ kinds.forEach((kind, index) => {
53
+ const { count, color } = grouped[kind];
54
+ const px = isTop ? margin + boardSize - (index * cell + cell / 2) : margin + index * cell + cell / 2;
55
+ const py = isTop ? margin + cell / 2 : cell / 2 + 2;
56
+ ctx.save();
57
+ ctx.translate(px, py);
58
+ if (isSente && color === Color.White) {
59
+ ctx.rotate(Math.PI);
60
+ } else if (!isSente && color === Color.Black) {
61
+ ctx.rotate(Math.PI);
62
+ }
63
+ const fontSize = cell * fontSizeRatio;
64
+ const kan = JKFPlayer.kindToKan(kind);
65
+ ctx.font = `${fontSize}px ${fontFamily}`;
66
+ ctx.fillText(kan, 0, 0);
67
+ if (count > 1) {
68
+ ctx.font = `${fontSize * 0.5}px ${fontFamily}`;
69
+ const countOffsetX = cell * 0;
70
+ const countOffsetY = cell * 0.8;
71
+ ctx.fillText(String(count), countOffsetX, countOffsetY);
72
+ }
73
+ ctx.restore();
74
+ });
75
+ };
76
+ const ShogiHandsCanvas = ({
77
+ size = 360,
78
+ boardColor = "#f9d27a",
79
+ lineColor = "#000",
80
+ fontFamily = "serif",
81
+ fontSizeRatio = 0.7,
82
+ hands,
83
+ isSente = true,
84
+ isTop = false
85
+ }) => {
86
+ const canvasRef = React.useRef(null);
87
+ React.useEffect(() => {
88
+ const canvas = canvasRef.current;
89
+ if (!canvas) return;
90
+ const ctx = canvas.getContext("2d");
91
+ if (!ctx) return;
92
+ const { margin, handsHeight, boardSize, cellSize } = getHandsLayout(size);
93
+ const { width: canvasWidth, dpr } = getCanvasDimensions(size);
94
+ canvas.width = canvasWidth;
95
+ canvas.height = handsHeight * dpr;
96
+ canvas.style.width = `${size}px`;
97
+ canvas.style.height = `${handsHeight}px`;
98
+ ctx.scale(dpr, dpr);
99
+ ctx.clearRect(0, 0, size, handsHeight);
100
+ drawBackground(ctx, size, handsHeight, boardColor);
101
+ drawHandsFrame(ctx, margin, margin, boardSize, cellSize, lineColor, isTop);
102
+ drawCoordinates(ctx, cellSize * 0.35, fontFamily);
103
+ drawPieces(ctx, hands, margin, boardSize, cellSize, fontFamily, fontSizeRatio, isSente, isTop);
104
+ }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, isSente, hands, isTop]);
105
+ return /* @__PURE__ */ React.createElement("canvas", { ref: canvasRef });
106
+ };
107
+ var shogi_hands_canvas_default = ShogiHandsCanvas;
108
+ export {
109
+ shogi_hands_canvas_default as default
110
+ };
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+
3
+ type ShogiPlayerProps = {
4
+ kifuText: string;
5
+ size?: number;
6
+ tesuu?: number;
7
+ };
8
+ declare function ShogiPlayer(props: ShogiPlayerProps): React.JSX.Element;
9
+
10
+ export { type ShogiPlayerProps, ShogiPlayer as default };