@hackersheet/next-document-content-kifu 0.1.0-alpha.6 → 0.1.0-alpha.8

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.
@@ -47,5 +47,5 @@ function Kifu({ code, language }) {
47
47
  setPly(newPly);
48
48
  }
49
49
  }, [searchParams, id]);
50
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "kifu-block", id }, /* @__PURE__ */ import_react.default.createElement(import_shogi_player.ShogiPlayer, { kifuText: code, tesuu: ply }));
50
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "kifu-block", id }, /* @__PURE__ */ import_react.default.createElement(import_shogi_player.ShogiPlayer, { kifuText: code, tesuu: ply, size: 320 }));
51
51
  }
@@ -36,7 +36,7 @@ function Button({ children, onClick }) {
36
36
  return /* @__PURE__ */ import_react.default.createElement(
37
37
  "button",
38
38
  {
39
- className: "border-2 text-black p-2 border-black rounded-lg hover:bg-amber-100 cursor-pointer",
39
+ className: "border-2 text-xs text-black p-2 border-black rounded-lg hover:bg-amber-100 cursor-pointer",
40
40
  onClick
41
41
  },
42
42
  children
@@ -66,7 +66,7 @@ const drawBoard = (ctx, margin, boardSize, lineColor) => {
66
66
  ctx.stroke();
67
67
  });
68
68
  };
69
- const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente, currentMove) => {
69
+ const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente) => {
70
70
  ctx.textAlign = "center";
71
71
  ctx.textBaseline = "middle";
72
72
  pieces.forEach((row, rowIndex) => {
@@ -85,12 +85,6 @@ const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSent
85
85
  }
86
86
  const kan = import_json_kifu_format.JKFPlayer.kindToKan(piece.kind);
87
87
  ctx.fillStyle = "#000";
88
- if (currentMove) {
89
- const to = currentMove.to;
90
- if (to && to.x === rowIndex + 1 && to.y === colIndex + 1) {
91
- ctx.fillStyle = "red";
92
- }
93
- }
94
88
  if (kan.length === 2) {
95
89
  const baseFontSize = cell * fontSizeRatio * 0.5;
96
90
  ctx.font = `${baseFontSize}px ${fontFamily}`;
@@ -122,17 +116,35 @@ const drawCoordinates = (ctx, margin, boardSize, cell, fontFamily, isSente) => {
122
116
  Array.from({ length: 9 }).forEach((_, i) => {
123
117
  const x = margin + i * cell + cell / 2;
124
118
  const y = margin / 2;
125
- const label = isSente ? (9 - i).toString() : (i + 1).toString();
119
+ const label = isSente ? import_json_kifu_format.JKFPlayer.numToZen(9 - i) : import_json_kifu_format.JKFPlayer.numToZen(i + 1);
126
120
  ctx.fillText(label, x, y);
127
121
  });
128
- const kanji = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u4E03", "\u516B", "\u4E5D"];
129
- kanji.forEach((k, i) => {
122
+ Array.from({ length: 9 }).forEach((_, i) => {
130
123
  const x = margin + boardSize + margin / 2;
131
124
  const y = margin + i * cell + cell / 2;
132
- const label = isSente ? k : kanji[8 - i];
125
+ const label = isSente ? import_json_kifu_format.JKFPlayer.numToKan(i + 1) : import_json_kifu_format.JKFPlayer.numToKan(9 - i);
133
126
  ctx.fillText(label, x, y);
134
127
  });
135
128
  };
129
+ const drawHighlightedCell = (ctx, margin, cell, isSente, currentMove) => {
130
+ if (!currentMove) return;
131
+ if (currentMove.to) {
132
+ const toRow = currentMove.to.x - 1;
133
+ const toCol = currentMove.to.y - 1;
134
+ const toX = isSente ? 8 - toRow : toRow;
135
+ const toY = isSente ? toCol : 8 - toCol;
136
+ ctx.fillStyle = "rgba(255,0,0,0.1)";
137
+ ctx.fillRect(margin + toX * cell, margin + toY * cell, cell, cell);
138
+ }
139
+ if (currentMove.from) {
140
+ const fromRow = currentMove.from.x - 1;
141
+ const fromCol = currentMove.from.y - 1;
142
+ const fromX = isSente ? 8 - fromRow : fromRow;
143
+ const fromY = isSente ? fromCol : 8 - fromCol;
144
+ ctx.fillStyle = "rgba(255,0,0,0.1)";
145
+ ctx.fillRect(margin + fromX * cell, margin + fromY * cell, cell, cell);
146
+ }
147
+ };
136
148
  const ShogiBoardCanvas = ({
137
149
  size = 360,
138
150
  boardColor = "#f9d27a",
@@ -159,7 +171,8 @@ const ShogiBoardCanvas = ({
159
171
  const { margin, boardSize, cell } = getBoardLayout(size);
160
172
  drawBackground(ctx, size, boardColor);
161
173
  drawBoard(ctx, margin, boardSize, lineColor);
162
- drawPieces(ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente, currentMove);
174
+ drawHighlightedCell(ctx, margin, cell, isSente, currentMove);
175
+ drawPieces(ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente);
163
176
  drawCoordinates(ctx, margin, boardSize, cell, fontFamily, isSente);
164
177
  }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, pieces, isSente, currentMove]);
165
178
  return /* @__PURE__ */ import_react.default.createElement("canvas", { ref: canvasRef, width: size, height: size });
@@ -65,25 +65,44 @@ const drawCoordinates = (ctx, fontSize, fontFamily) => {
65
65
  ctx.textAlign = "center";
66
66
  ctx.textBaseline = "middle";
67
67
  };
68
- const drawPieces = (ctx, hands, margin, cell, fontFamily, fontSizeRatio, isSente, isTop) => {
68
+ const drawPieces = (ctx, hands, margin, boardSize, cell, fontFamily, fontSizeRatio, isSente, isTop) => {
69
69
  ctx.textAlign = "center";
70
70
  ctx.textBaseline = "middle";
71
71
  const pieces = isSente && isTop || !isSente && !isTop ? hands[import_shogi.Color.White] : hands[import_shogi.Color.Black];
72
- pieces.forEach((piece, index) => {
73
- if (!piece) return;
74
- const px = margin + index * cell + cell / 2;
72
+ if (!pieces || pieces.length === 0) return;
73
+ const grouped = pieces.reduce(
74
+ (acc, piece) => {
75
+ if (!piece) return acc;
76
+ const key = piece.kind;
77
+ if (!acc[key]) acc[key] = { count: 0, color: piece.color };
78
+ acc[key].count += 1;
79
+ return acc;
80
+ },
81
+ {}
82
+ );
83
+ const order = ["OU", "HI", "KA", "KI", "GI", "KE", "KY", "FU"];
84
+ const kinds = order.filter((kind) => grouped[kind]);
85
+ kinds.forEach((kind, index) => {
86
+ const { count, color } = grouped[kind];
87
+ const px = isTop ? margin + boardSize - (index * cell + cell / 2) : margin + index * cell + cell / 2;
75
88
  const py = isTop ? margin + cell / 2 : cell / 2 + 2;
76
89
  ctx.save();
77
90
  ctx.translate(px, py);
78
- if (isSente && piece.color === import_shogi.Color.White) {
91
+ if (isSente && color === import_shogi.Color.White) {
79
92
  ctx.rotate(Math.PI);
80
- } else if (!isSente && piece.color === import_shogi.Color.Black) {
93
+ } else if (!isSente && color === import_shogi.Color.Black) {
81
94
  ctx.rotate(Math.PI);
82
95
  }
83
96
  const fontSize = cell * fontSizeRatio;
84
- const kan = import_json_kifu_format.JKFPlayer.kindToKan(piece.kind);
97
+ const kan = import_json_kifu_format.JKFPlayer.kindToKan(kind);
85
98
  ctx.font = `${fontSize}px ${fontFamily}`;
86
99
  ctx.fillText(kan, 0, 0);
100
+ if (count > 1) {
101
+ ctx.font = `${fontSize * 0.5}px ${fontFamily}`;
102
+ const countOffsetX = cell * 0;
103
+ const countOffsetY = cell * 0.8;
104
+ ctx.fillText(String(count), countOffsetX, countOffsetY);
105
+ }
87
106
  ctx.restore();
88
107
  });
89
108
  };
@@ -114,7 +133,7 @@ const ShogiHandsCanvas = ({
114
133
  drawBackground(ctx, size, handsHeight, boardColor);
115
134
  drawHandsFrame(ctx, margin, margin, boardSize, cellSize, lineColor, isTop);
116
135
  drawCoordinates(ctx, cellSize * 0.35, fontFamily);
117
- drawPieces(ctx, hands, margin, cellSize, fontFamily, fontSizeRatio, isSente, isTop);
136
+ drawPieces(ctx, hands, margin, boardSize, cellSize, fontFamily, fontSizeRatio, isSente, isTop);
118
137
  }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, isSente, hands, isTop]);
119
138
  return /* @__PURE__ */ import_react.default.createElement("canvas", { ref: canvasRef });
120
139
  };
@@ -2,6 +2,7 @@ import React from 'react';
2
2
 
3
3
  type ShogiPlayerProps = {
4
4
  kifuText: string;
5
+ size?: number;
5
6
  tesuu?: number;
6
7
  };
7
8
  declare function ShogiPlayer(props: ShogiPlayerProps): React.JSX.Element;
@@ -48,7 +48,7 @@ function ShogiPlayer(props) {
48
48
  const [isSente, setIsSente] = (0, import_react.useState)(true);
49
49
  const [maxTesuu, setMaxTesuu] = (0, import_react.useState)(player.getMaxTesuu());
50
50
  const [comments, setComments] = (0, import_react.useState)(player.getComments());
51
- const size = 360;
51
+ const size = props.size ? props.size : 360;
52
52
  const updateState = () => {
53
53
  setPieces([...player.shogi.board]);
54
54
  setHands([...player.shogi.hands]);
@@ -74,10 +74,39 @@ function ShogiPlayer(props) {
74
74
  setIsSente(!isSente);
75
75
  updateState();
76
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
+ );
77
106
  (0, import_react.useEffect)(() => {
78
107
  if (props.tesuu !== void 0) {
79
108
  handleGoto(props.tesuu);
80
109
  }
81
110
  }, [props.tesuu]);
82
- return /* @__PURE__ */ import_react.default.createElement("div", { className: "flex w-fit", tabIndex: 1 }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col" }, /* @__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: "flex flex-col w-fit bg-[#f9d27a] p-4 gap-4" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "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: "text-black border-black border-2 p-1 text-xs" }, comments.map((comment, index) => /* @__PURE__ */ import_react.default.createElement("div", { key: index }, comment)), comments.length === 0 && /* @__PURE__ */ import_react.default.createElement("div", null, "\xA0")), /* @__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"))));
111
+ return /* @__PURE__ */ import_react.default.createElement("div", { className: "flex 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 w-fit bg-[#f9d27a] p-4 gap-4" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "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: "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"))));
83
112
  }
@@ -14,7 +14,7 @@ function Kifu({ code, language }) {
14
14
  setPly(newPly);
15
15
  }
16
16
  }, [searchParams, id]);
17
- return /* @__PURE__ */ React.createElement("div", { className: "kifu-block", id }, /* @__PURE__ */ React.createElement(ShogiPlayer, { kifuText: code, tesuu: ply }));
17
+ return /* @__PURE__ */ React.createElement("div", { className: "kifu-block", id }, /* @__PURE__ */ React.createElement(ShogiPlayer, { kifuText: code, tesuu: ply, size: 320 }));
18
18
  }
19
19
  export {
20
20
  Kifu as default
@@ -3,7 +3,7 @@ function Button({ children, onClick }) {
3
3
  return /* @__PURE__ */ React.createElement(
4
4
  "button",
5
5
  {
6
- className: "border-2 text-black p-2 border-black rounded-lg hover:bg-amber-100 cursor-pointer",
6
+ className: "border-2 text-xs text-black p-2 border-black rounded-lg hover:bg-amber-100 cursor-pointer",
7
7
  onClick
8
8
  },
9
9
  children
@@ -33,7 +33,7 @@ const drawBoard = (ctx, margin, boardSize, lineColor) => {
33
33
  ctx.stroke();
34
34
  });
35
35
  };
36
- const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente, currentMove) => {
36
+ const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente) => {
37
37
  ctx.textAlign = "center";
38
38
  ctx.textBaseline = "middle";
39
39
  pieces.forEach((row, rowIndex) => {
@@ -52,12 +52,6 @@ const drawPieces = (ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSent
52
52
  }
53
53
  const kan = JKFPlayer.kindToKan(piece.kind);
54
54
  ctx.fillStyle = "#000";
55
- if (currentMove) {
56
- const to = currentMove.to;
57
- if (to && to.x === rowIndex + 1 && to.y === colIndex + 1) {
58
- ctx.fillStyle = "red";
59
- }
60
- }
61
55
  if (kan.length === 2) {
62
56
  const baseFontSize = cell * fontSizeRatio * 0.5;
63
57
  ctx.font = `${baseFontSize}px ${fontFamily}`;
@@ -89,17 +83,35 @@ const drawCoordinates = (ctx, margin, boardSize, cell, fontFamily, isSente) => {
89
83
  Array.from({ length: 9 }).forEach((_, i) => {
90
84
  const x = margin + i * cell + cell / 2;
91
85
  const y = margin / 2;
92
- const label = isSente ? (9 - i).toString() : (i + 1).toString();
86
+ const label = isSente ? JKFPlayer.numToZen(9 - i) : JKFPlayer.numToZen(i + 1);
93
87
  ctx.fillText(label, x, y);
94
88
  });
95
- const kanji = ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u4E03", "\u516B", "\u4E5D"];
96
- kanji.forEach((k, i) => {
89
+ Array.from({ length: 9 }).forEach((_, i) => {
97
90
  const x = margin + boardSize + margin / 2;
98
91
  const y = margin + i * cell + cell / 2;
99
- const label = isSente ? k : kanji[8 - i];
92
+ const label = isSente ? JKFPlayer.numToKan(i + 1) : JKFPlayer.numToKan(9 - i);
100
93
  ctx.fillText(label, x, y);
101
94
  });
102
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
+ };
103
115
  const ShogiBoardCanvas = ({
104
116
  size = 360,
105
117
  boardColor = "#f9d27a",
@@ -126,7 +138,8 @@ const ShogiBoardCanvas = ({
126
138
  const { margin, boardSize, cell } = getBoardLayout(size);
127
139
  drawBackground(ctx, size, boardColor);
128
140
  drawBoard(ctx, margin, boardSize, lineColor);
129
- drawPieces(ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente, currentMove);
141
+ drawHighlightedCell(ctx, margin, cell, isSente, currentMove);
142
+ drawPieces(ctx, pieces, margin, cell, fontFamily, fontSizeRatio, isSente);
130
143
  drawCoordinates(ctx, margin, boardSize, cell, fontFamily, isSente);
131
144
  }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, pieces, isSente, currentMove]);
132
145
  return /* @__PURE__ */ React.createElement("canvas", { ref: canvasRef, width: size, height: size });
@@ -32,25 +32,44 @@ const drawCoordinates = (ctx, fontSize, fontFamily) => {
32
32
  ctx.textAlign = "center";
33
33
  ctx.textBaseline = "middle";
34
34
  };
35
- const drawPieces = (ctx, hands, margin, cell, fontFamily, fontSizeRatio, isSente, isTop) => {
35
+ const drawPieces = (ctx, hands, margin, boardSize, cell, fontFamily, fontSizeRatio, isSente, isTop) => {
36
36
  ctx.textAlign = "center";
37
37
  ctx.textBaseline = "middle";
38
38
  const pieces = isSente && isTop || !isSente && !isTop ? hands[Color.White] : hands[Color.Black];
39
- pieces.forEach((piece, index) => {
40
- if (!piece) return;
41
- const px = margin + index * cell + cell / 2;
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;
42
55
  const py = isTop ? margin + cell / 2 : cell / 2 + 2;
43
56
  ctx.save();
44
57
  ctx.translate(px, py);
45
- if (isSente && piece.color === Color.White) {
58
+ if (isSente && color === Color.White) {
46
59
  ctx.rotate(Math.PI);
47
- } else if (!isSente && piece.color === Color.Black) {
60
+ } else if (!isSente && color === Color.Black) {
48
61
  ctx.rotate(Math.PI);
49
62
  }
50
63
  const fontSize = cell * fontSizeRatio;
51
- const kan = JKFPlayer.kindToKan(piece.kind);
64
+ const kan = JKFPlayer.kindToKan(kind);
52
65
  ctx.font = `${fontSize}px ${fontFamily}`;
53
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
+ }
54
73
  ctx.restore();
55
74
  });
56
75
  };
@@ -81,7 +100,7 @@ const ShogiHandsCanvas = ({
81
100
  drawBackground(ctx, size, handsHeight, boardColor);
82
101
  drawHandsFrame(ctx, margin, margin, boardSize, cellSize, lineColor, isTop);
83
102
  drawCoordinates(ctx, cellSize * 0.35, fontFamily);
84
- drawPieces(ctx, hands, margin, cellSize, fontFamily, fontSizeRatio, isSente, isTop);
103
+ drawPieces(ctx, hands, margin, boardSize, cellSize, fontFamily, fontSizeRatio, isSente, isTop);
85
104
  }, [size, boardColor, lineColor, fontFamily, fontSizeRatio, isSente, hands, isTop]);
86
105
  return /* @__PURE__ */ React.createElement("canvas", { ref: canvasRef });
87
106
  };
@@ -2,6 +2,7 @@ import React from 'react';
2
2
 
3
3
  type ShogiPlayerProps = {
4
4
  kifuText: string;
5
+ size?: number;
5
6
  tesuu?: number;
6
7
  };
7
8
  declare function ShogiPlayer(props: ShogiPlayerProps): React.JSX.Element;
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { JKFPlayer } from "json-kifu-format";
3
- import React, { useEffect, useState } from "react";
3
+ import React, { useCallback, useEffect, useState } from "react";
4
4
  import Button from "./button";
5
5
  import { MovesArea } from "./moves-area";
6
6
  import ShogiBoardCanvas from "./shogi-board-canvas";
@@ -15,7 +15,7 @@ function ShogiPlayer(props) {
15
15
  const [isSente, setIsSente] = useState(true);
16
16
  const [maxTesuu, setMaxTesuu] = useState(player.getMaxTesuu());
17
17
  const [comments, setComments] = useState(player.getComments());
18
- const size = 360;
18
+ const size = props.size ? props.size : 360;
19
19
  const updateState = () => {
20
20
  setPieces([...player.shogi.board]);
21
21
  setHands([...player.shogi.hands]);
@@ -41,12 +41,41 @@ function ShogiPlayer(props) {
41
41
  setIsSente(!isSente);
42
42
  updateState();
43
43
  };
44
+ const handleKeydown = useCallback(
45
+ (event) => {
46
+ event.preventDefault();
47
+ event.stopPropagation();
48
+ switch (event.key) {
49
+ case "Up":
50
+ case "ArrowUp":
51
+ handleGoto(0);
52
+ break;
53
+ case "Down":
54
+ case "ArrowDown":
55
+ handleGoto(maxTesuu);
56
+ break;
57
+ case "Left":
58
+ case "ArrowLeft":
59
+ handleBackward();
60
+ break;
61
+ case " ":
62
+ case "Right":
63
+ case "ArrowRight":
64
+ handleForward();
65
+ break;
66
+ case "r":
67
+ handeleToggle();
68
+ break;
69
+ }
70
+ },
71
+ [maxTesuu, isSente]
72
+ );
44
73
  useEffect(() => {
45
74
  if (props.tesuu !== void 0) {
46
75
  handleGoto(props.tesuu);
47
76
  }
48
77
  }, [props.tesuu]);
49
- return /* @__PURE__ */ React.createElement("div", { className: "flex w-fit", tabIndex: 1 }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React.createElement(ShogiHandsCanvas, { size, hands, isSente, isTop: true }), /* @__PURE__ */ React.createElement(ShogiBoardCanvas, { size, pieces, isSente, currentMove }), /* @__PURE__ */ React.createElement(ShogiHandsCanvas, { size, hands, isSente, isTop: false })), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col w-fit bg-[#f9d27a] p-4 gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex-1 relative w-full" }, /* @__PURE__ */ React.createElement(MovesArea, { moves, tesuu, onTesuuChange: handleGoto })), /* @__PURE__ */ React.createElement("div", { className: "text-black border-black border-2 p-1 text-xs" }, comments.map((comment, index) => /* @__PURE__ */ React.createElement("div", { key: index }, comment)), comments.length === 0 && /* @__PURE__ */ React.createElement("div", null, "\xA0")), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement(Button, { onClick: () => handleGoto(0) }, "\u6700\u521D"), /* @__PURE__ */ React.createElement(Button, { onClick: handleBackward }, "\u524D"), /* @__PURE__ */ React.createElement(Button, { onClick: handleForward }, "\u6B21"), /* @__PURE__ */ React.createElement(Button, { onClick: () => handleGoto(maxTesuu) }, "\u6700\u5F8C"), /* @__PURE__ */ React.createElement(Button, { onClick: handeleToggle }, "\u53CD\u8EE2"))));
78
+ return /* @__PURE__ */ React.createElement("div", { className: "flex w-fit", tabIndex: 1, onKeyDown: handleKeydown }, /* @__PURE__ */ React.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React.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__ */ React.createElement(ShogiHandsCanvas, { size, hands, isSente, isTop: true }), /* @__PURE__ */ React.createElement(ShogiBoardCanvas, { size, pieces, isSente, currentMove }), /* @__PURE__ */ React.createElement(ShogiHandsCanvas, { size, hands, isSente, isTop: false }), /* @__PURE__ */ React.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__ */ React.createElement("div", { className: "flex flex-col w-fit bg-[#f9d27a] p-4 gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex-1 relative w-full" }, /* @__PURE__ */ React.createElement(MovesArea, { moves, tesuu, onTesuuChange: handleGoto })), /* @__PURE__ */ React.createElement("div", { className: "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__ */ React.createElement("div", { key: index }, comment)), comments.length === 0 && tesuu !== 0 && /* @__PURE__ */ React.createElement("div", null, "\xA0"), tesuu === 0 && Object.entries(player.kifu.header).map(([key, value], i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "whitespace-nowrap" }, key, ": ", value))), /* @__PURE__ */ React.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React.createElement(Button, { onClick: () => handleGoto(0) }, "\u6700\u521D"), /* @__PURE__ */ React.createElement(Button, { onClick: handleBackward }, "\u524D"), /* @__PURE__ */ React.createElement(Button, { onClick: handleForward }, "\u6B21"), /* @__PURE__ */ React.createElement(Button, { onClick: () => handleGoto(maxTesuu) }, "\u6700\u5F8C"), /* @__PURE__ */ React.createElement(Button, { onClick: handeleToggle }, "\u53CD\u8EE2"))));
50
79
  }
51
80
  export {
52
81
  ShogiPlayer as default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hackersheet/next-document-content-kifu",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "Hacker Sheet document content kifu components for Next.js",
5
5
  "keywords": [],
6
6
  "repository": {