chess-snap 1.0.0

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 (162) hide show
  1. package/README.md +145 -0
  2. package/dist/config.d.ts +11 -0
  3. package/dist/config.js +23 -0
  4. package/dist/index.d.ts +43 -0
  5. package/dist/index.js +99 -0
  6. package/dist/renderer.d.ts +10 -0
  7. package/dist/renderer.js +145 -0
  8. package/dist/src/config.d.ts +11 -0
  9. package/dist/src/config.js +23 -0
  10. package/dist/src/index.d.ts +43 -0
  11. package/dist/src/index.js +99 -0
  12. package/dist/src/renderer.d.ts +10 -0
  13. package/dist/src/renderer.js +145 -0
  14. package/dist/src/types.d.ts +27 -0
  15. package/dist/src/types.js +1 -0
  16. package/dist/types.d.ts +27 -0
  17. package/dist/types.js +1 -0
  18. package/package.json +51 -0
  19. package/src/assets/pieces/alpha/BlackBishop.png +0 -0
  20. package/src/assets/pieces/alpha/BlackKing.png +0 -0
  21. package/src/assets/pieces/alpha/BlackKnight.png +0 -0
  22. package/src/assets/pieces/alpha/BlackPawn.png +0 -0
  23. package/src/assets/pieces/alpha/BlackQueen.png +0 -0
  24. package/src/assets/pieces/alpha/BlackRook.png +0 -0
  25. package/src/assets/pieces/alpha/WhiteBishop.png +0 -0
  26. package/src/assets/pieces/alpha/WhiteKing.png +0 -0
  27. package/src/assets/pieces/alpha/WhiteKnight.png +0 -0
  28. package/src/assets/pieces/alpha/WhitePawn.png +0 -0
  29. package/src/assets/pieces/alpha/WhiteQueen.png +0 -0
  30. package/src/assets/pieces/alpha/WhiteRook.png +0 -0
  31. package/src/assets/pieces/cburnett/BlackBishop.png +0 -0
  32. package/src/assets/pieces/cburnett/BlackKing.png +0 -0
  33. package/src/assets/pieces/cburnett/BlackKnight.png +0 -0
  34. package/src/assets/pieces/cburnett/BlackPawn.png +0 -0
  35. package/src/assets/pieces/cburnett/BlackQueen.png +0 -0
  36. package/src/assets/pieces/cburnett/BlackRook.png +0 -0
  37. package/src/assets/pieces/cburnett/WhiteBishop.png +0 -0
  38. package/src/assets/pieces/cburnett/WhiteKing.png +0 -0
  39. package/src/assets/pieces/cburnett/WhiteKnight.png +0 -0
  40. package/src/assets/pieces/cburnett/WhitePawn.png +0 -0
  41. package/src/assets/pieces/cburnett/WhiteQueen.png +0 -0
  42. package/src/assets/pieces/cburnett/WhiteRook.png +0 -0
  43. package/src/assets/pieces/cheq/BlackBishop.png +0 -0
  44. package/src/assets/pieces/cheq/BlackKing.png +0 -0
  45. package/src/assets/pieces/cheq/BlackKnight.png +0 -0
  46. package/src/assets/pieces/cheq/BlackPawn.png +0 -0
  47. package/src/assets/pieces/cheq/BlackQueen.png +0 -0
  48. package/src/assets/pieces/cheq/BlackRook.png +0 -0
  49. package/src/assets/pieces/cheq/WhiteBishop.png +0 -0
  50. package/src/assets/pieces/cheq/WhiteKing.png +0 -0
  51. package/src/assets/pieces/cheq/WhiteKnight.png +0 -0
  52. package/src/assets/pieces/cheq/WhitePawn.png +0 -0
  53. package/src/assets/pieces/cheq/WhiteQueen.png +0 -0
  54. package/src/assets/pieces/cheq/WhiteRook.png +0 -0
  55. package/src/assets/pieces/game_room/BlackBishop.png +0 -0
  56. package/src/assets/pieces/game_room/BlackKing.png +0 -0
  57. package/src/assets/pieces/game_room/BlackKnight.png +0 -0
  58. package/src/assets/pieces/game_room/BlackPawn.png +0 -0
  59. package/src/assets/pieces/game_room/BlackQueen.png +0 -0
  60. package/src/assets/pieces/game_room/BlackRook.png +0 -0
  61. package/src/assets/pieces/game_room/WhiteBishop.png +0 -0
  62. package/src/assets/pieces/game_room/WhiteKing.png +0 -0
  63. package/src/assets/pieces/game_room/WhiteKnight.png +0 -0
  64. package/src/assets/pieces/game_room/WhitePawn.png +0 -0
  65. package/src/assets/pieces/game_room/WhiteQueen.png +0 -0
  66. package/src/assets/pieces/game_room/WhiteRook.png +0 -0
  67. package/src/assets/pieces/game_room_gothic/BlackBishop.png +0 -0
  68. package/src/assets/pieces/game_room_gothic/BlackKing.png +0 -0
  69. package/src/assets/pieces/game_room_gothic/BlackKnight.png +0 -0
  70. package/src/assets/pieces/game_room_gothic/BlackPawn.png +0 -0
  71. package/src/assets/pieces/game_room_gothic/BlackQueen.png +0 -0
  72. package/src/assets/pieces/game_room_gothic/BlackRook.png +0 -0
  73. package/src/assets/pieces/game_room_gothic/WhiteBishop.png +0 -0
  74. package/src/assets/pieces/game_room_gothic/WhiteKing.png +0 -0
  75. package/src/assets/pieces/game_room_gothic/WhiteKnight.png +0 -0
  76. package/src/assets/pieces/game_room_gothic/WhitePawn.png +0 -0
  77. package/src/assets/pieces/game_room_gothic/WhiteQueen.png +0 -0
  78. package/src/assets/pieces/game_room_gothic/WhiteRook.png +0 -0
  79. package/src/assets/pieces/game_room_space/BlackBishop.png +0 -0
  80. package/src/assets/pieces/game_room_space/BlackKing.png +0 -0
  81. package/src/assets/pieces/game_room_space/BlackKnight.png +0 -0
  82. package/src/assets/pieces/game_room_space/BlackPawn.png +0 -0
  83. package/src/assets/pieces/game_room_space/BlackQueen.png +0 -0
  84. package/src/assets/pieces/game_room_space/BlackRook.png +0 -0
  85. package/src/assets/pieces/game_room_space/WhiteBishop.png +0 -0
  86. package/src/assets/pieces/game_room_space/WhiteKing.png +0 -0
  87. package/src/assets/pieces/game_room_space/WhiteKnight.png +0 -0
  88. package/src/assets/pieces/game_room_space/WhitePawn.png +0 -0
  89. package/src/assets/pieces/game_room_space/WhiteQueen.png +0 -0
  90. package/src/assets/pieces/game_room_space/WhiteRook.png +0 -0
  91. package/src/assets/pieces/glass/BlackBishop.png +0 -0
  92. package/src/assets/pieces/glass/BlackKing.png +0 -0
  93. package/src/assets/pieces/glass/BlackKnight.png +0 -0
  94. package/src/assets/pieces/glass/BlackPawn.png +0 -0
  95. package/src/assets/pieces/glass/BlackQueen.png +0 -0
  96. package/src/assets/pieces/glass/BlackRook.png +0 -0
  97. package/src/assets/pieces/glass/WhiteBishop.png +0 -0
  98. package/src/assets/pieces/glass/WhiteKing.png +0 -0
  99. package/src/assets/pieces/glass/WhiteKnight.png +0 -0
  100. package/src/assets/pieces/glass/WhitePawn.png +0 -0
  101. package/src/assets/pieces/glass/WhiteQueen.png +0 -0
  102. package/src/assets/pieces/glass/WhiteRook.png +0 -0
  103. package/src/assets/pieces/leipzig/BlackBishop.png +0 -0
  104. package/src/assets/pieces/leipzig/BlackKing.png +0 -0
  105. package/src/assets/pieces/leipzig/BlackKnight.png +0 -0
  106. package/src/assets/pieces/leipzig/BlackPawn.png +0 -0
  107. package/src/assets/pieces/leipzig/BlackQueen.png +0 -0
  108. package/src/assets/pieces/leipzig/BlackRook.png +0 -0
  109. package/src/assets/pieces/leipzig/WhiteBishop.png +0 -0
  110. package/src/assets/pieces/leipzig/WhiteKing.png +0 -0
  111. package/src/assets/pieces/leipzig/WhiteKnight.png +0 -0
  112. package/src/assets/pieces/leipzig/WhitePawn.png +0 -0
  113. package/src/assets/pieces/leipzig/WhiteQueen.png +0 -0
  114. package/src/assets/pieces/leipzig/WhiteRook.png +0 -0
  115. package/src/assets/pieces/merida/BlackBishop.png +0 -0
  116. package/src/assets/pieces/merida/BlackKing.png +0 -0
  117. package/src/assets/pieces/merida/BlackKnight.png +0 -0
  118. package/src/assets/pieces/merida/BlackPawn.png +0 -0
  119. package/src/assets/pieces/merida/BlackQueen.png +0 -0
  120. package/src/assets/pieces/merida/BlackRook.png +0 -0
  121. package/src/assets/pieces/merida/WhiteBishop.png +0 -0
  122. package/src/assets/pieces/merida/WhiteKing.png +0 -0
  123. package/src/assets/pieces/merida/WhiteKnight.png +0 -0
  124. package/src/assets/pieces/merida/WhitePawn.png +0 -0
  125. package/src/assets/pieces/merida/WhiteQueen.png +0 -0
  126. package/src/assets/pieces/merida/WhiteRook.png +0 -0
  127. package/src/assets/pieces/neo/BlackBishop.png +0 -0
  128. package/src/assets/pieces/neo/BlackKing.png +0 -0
  129. package/src/assets/pieces/neo/BlackKnight.png +0 -0
  130. package/src/assets/pieces/neo/BlackPawn.png +0 -0
  131. package/src/assets/pieces/neo/BlackQueen.png +0 -0
  132. package/src/assets/pieces/neo/BlackRook.png +0 -0
  133. package/src/assets/pieces/neo/WhiteBishop.png +0 -0
  134. package/src/assets/pieces/neo/WhiteKing.png +0 -0
  135. package/src/assets/pieces/neo/WhiteKnight.png +0 -0
  136. package/src/assets/pieces/neo/WhitePawn.png +0 -0
  137. package/src/assets/pieces/neo/WhiteQueen.png +0 -0
  138. package/src/assets/pieces/neo/WhiteRook.png +0 -0
  139. package/src/assets/pieces/neo_wood/BlackBishop.png +0 -0
  140. package/src/assets/pieces/neo_wood/BlackKing.png +0 -0
  141. package/src/assets/pieces/neo_wood/BlackKnight.png +0 -0
  142. package/src/assets/pieces/neo_wood/BlackPawn.png +0 -0
  143. package/src/assets/pieces/neo_wood/BlackQueen.png +0 -0
  144. package/src/assets/pieces/neo_wood/BlackRook.png +0 -0
  145. package/src/assets/pieces/neo_wood/WhiteBishop.png +0 -0
  146. package/src/assets/pieces/neo_wood/WhiteKing.png +0 -0
  147. package/src/assets/pieces/neo_wood/WhiteKnight.png +0 -0
  148. package/src/assets/pieces/neo_wood/WhitePawn.png +0 -0
  149. package/src/assets/pieces/neo_wood/WhiteQueen.png +0 -0
  150. package/src/assets/pieces/neo_wood/WhiteRook.png +0 -0
  151. package/src/assets/pieces/wood/BlackBishop.png +0 -0
  152. package/src/assets/pieces/wood/BlackKing.png +0 -0
  153. package/src/assets/pieces/wood/BlackKnight.png +0 -0
  154. package/src/assets/pieces/wood/BlackPawn.png +0 -0
  155. package/src/assets/pieces/wood/BlackQueen.png +0 -0
  156. package/src/assets/pieces/wood/BlackRook.png +0 -0
  157. package/src/assets/pieces/wood/WhiteBishop.png +0 -0
  158. package/src/assets/pieces/wood/WhiteKing.png +0 -0
  159. package/src/assets/pieces/wood/WhiteKnight.png +0 -0
  160. package/src/assets/pieces/wood/WhitePawn.png +0 -0
  161. package/src/assets/pieces/wood/WhiteQueen.png +0 -0
  162. package/src/assets/pieces/wood/WhiteRook.png +0 -0
@@ -0,0 +1,145 @@
1
+ import { createCanvas, loadImage } from '@napi-rs/canvas';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ const _filename = fileURLToPath(import.meta.url);
5
+ const _dirname = dirname(_filename);
6
+ import { cols, filePaths } from './config.js';
7
+ import * as fs from 'fs/promises';
8
+ const imageCache = new Map();
9
+ /**
10
+ * Ensures the piece image for a given style is loaded into memory exactly once.
11
+ */
12
+ async function getPieceImage(style, pieceKey) {
13
+ const cacheKey = `${style}_${pieceKey}`;
14
+ if (imageCache.has(cacheKey)) {
15
+ return imageCache.get(cacheKey);
16
+ }
17
+ const fileName = filePaths[pieceKey];
18
+ if (!fileName)
19
+ throw new Error(`Unknown piece key: ${pieceKey}`);
20
+ const imgPath = resolve(_dirname, `../src/assets/pieces/${style}/${fileName}.png`);
21
+ try {
22
+ const image = await loadImage(imgPath);
23
+ imageCache.set(cacheKey, image);
24
+ return image;
25
+ }
26
+ catch (err) {
27
+ throw new Error(`Failed to load piece image from ${imgPath}: ${err}`);
28
+ }
29
+ }
30
+ /**
31
+ * Renders the algebraic notations (A-H, 1-8) around the board.
32
+ */
33
+ function drawNotations(ctx, size, flipped, light, dark, padding, notationStyle, notationColor) {
34
+ const paddingTop = padding[0];
35
+ const paddingLeft = padding[3];
36
+ const paddingBottom = padding[2];
37
+ const paddingRight = padding[1];
38
+ const squareSize = size / 8;
39
+ const fontSize = Math.max(16, Math.floor(squareSize / 3.5));
40
+ ctx.font = `bold ${fontSize}px sans-serif`;
41
+ ctx.textBaseline = 'top';
42
+ const rows = flipped ? [1, 2, 3, 4, 5, 6, 7, 8] : [8, 7, 6, 5, 4, 3, 2, 1];
43
+ const columns = flipped ? [...cols].reverse() : cols;
44
+ for (let i = 0; i < 8; i++) {
45
+ const ySquare = (squareSize * i) + paddingTop;
46
+ const xSquare = (squareSize * i) + paddingLeft;
47
+ if (notationStyle === 'inside') {
48
+ const isLightSquareForNumber = (i % 2 === 0);
49
+ ctx.fillStyle = notationColor || (isLightSquareForNumber ? dark : light);
50
+ ctx.lineWidth = 1.5;
51
+ ctx.strokeStyle = notationColor ? "rgba(0,0,0,0.5)" : (isLightSquareForNumber ? light : dark);
52
+ ctx.strokeText(rows[i].toString(), paddingLeft + 4, ySquare + 4);
53
+ ctx.fillText(rows[i].toString(), paddingLeft + 4, ySquare + 4);
54
+ const isLightSquareForLetter = (i % 2 !== 0);
55
+ ctx.fillStyle = notationColor || (isLightSquareForLetter ? dark : light);
56
+ const textWidth = ctx.measureText(columns[i]).width;
57
+ const letterX = xSquare + squareSize - textWidth - 2;
58
+ const letterY = size - fontSize - 2 + paddingTop;
59
+ ctx.lineWidth = 1.5;
60
+ ctx.strokeStyle = notationColor ? "rgba(0,0,0,0.5)" : (isLightSquareForLetter ? light : dark);
61
+ ctx.strokeText(columns[i], letterX, letterY);
62
+ ctx.fillText(columns[i], letterX, letterY);
63
+ }
64
+ else {
65
+ ctx.fillStyle = notationColor || dark;
66
+ ctx.textBaseline = 'middle';
67
+ ctx.textAlign = 'center';
68
+ const numberY = ySquare + (squareSize / 2);
69
+ ctx.fillText(rows[i].toString(), paddingLeft / 2, numberY);
70
+ const letterX = xSquare + (squareSize / 2);
71
+ const letterY = (paddingTop / 2) - 4;
72
+ ctx.fillText(columns[i], letterX, letterY);
73
+ ctx.textBaseline = 'top';
74
+ ctx.textAlign = 'left';
75
+ }
76
+ }
77
+ }
78
+ /**
79
+ * The core buffer generator function.
80
+ */
81
+ export async function generateBoardBuffer(chess, options, highlightedSquares) {
82
+ const canvasWidth = options.size + options.padding[1] + options.padding[3];
83
+ const canvasHeight = options.size + options.padding[0] + options.padding[2];
84
+ const canvas = createCanvas(canvasWidth, canvasHeight);
85
+ const ctx = canvas.getContext('2d');
86
+ ctx.fillStyle = options.light;
87
+ ctx.fillRect(0, 0, canvasWidth, canvasHeight);
88
+ const squareSize = options.size / 8;
89
+ const paddingTop = options.padding[0];
90
+ const paddingLeft = options.padding[3];
91
+ const rowFn = options.flipped ? (r) => r + 1 : (r) => 8 - r;
92
+ const colFn = options.flipped ? (c) => c : (c) => 7 - c;
93
+ for (let i = 0; i < 8; i++) {
94
+ for (let j = 0; j < 8; j++) {
95
+ const coordCol = cols[colFn(j)];
96
+ const coordRow = rowFn(i);
97
+ const coords = `${coordCol}${coordRow}`;
98
+ const x = (squareSize * (7 - j)) + paddingLeft;
99
+ const y = (squareSize * i) + paddingTop;
100
+ if ((i + j) % 2 !== 0) {
101
+ ctx.fillStyle = options.dark;
102
+ ctx.fillRect(x, y, squareSize, squareSize);
103
+ }
104
+ if (highlightedSquares.includes(coords)) {
105
+ ctx.fillStyle = options.highlight;
106
+ ctx.fillRect(x, y, squareSize, squareSize);
107
+ }
108
+ const piece = chess.get(coords);
109
+ if (piece) {
110
+ const pieceKey = `${piece.color}${piece.type}`;
111
+ try {
112
+ const img = await getPieceImage(options.style, pieceKey);
113
+ ctx.drawImage(img, x, y, squareSize, squareSize);
114
+ }
115
+ catch (e) {
116
+ console.error(`Missing piece image for: ${pieceKey} in style ${options.style}`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ if (options.notationStyle === 'outside') {
122
+ ctx.lineWidth = 2;
123
+ ctx.strokeStyle = options.dark;
124
+ ctx.strokeRect(paddingLeft, paddingTop, options.size, options.size);
125
+ }
126
+ if (options.notations) {
127
+ drawNotations(ctx, options.size, options.flipped, options.light, options.dark, options.padding, options.notationStyle, options.notationColor);
128
+ }
129
+ const format = options.format;
130
+ const quality = options.quality;
131
+ const mimeType = `image/${format}`;
132
+ if (format === 'jpeg' || format === 'webp') {
133
+ return canvas.toBuffer(mimeType, quality);
134
+ }
135
+ else {
136
+ return canvas.toBuffer('image/png');
137
+ }
138
+ }
139
+ /**
140
+ * Helper to write a buffer to file securely.
141
+ */
142
+ export async function writePNG(buffer, filePath) {
143
+ await fs.writeFile(filePath, buffer);
144
+ return filePath;
145
+ }
@@ -0,0 +1,27 @@
1
+ export type PieceStyle = 'neo' | 'glass' | 'wood' | 'game_room' | 'merida' | 'alpha' | 'cheq' | 'cburnett' | 'game_room_gothic' | 'game_room_space' | 'leipzig' | 'neo_wood';
2
+ export interface ChessImageOptions {
3
+ /** Pixel size of the generated board (e.g. 720) */
4
+ size?: number;
5
+ /** Padding around the board [top, right, bottom, left] */
6
+ padding?: [number, number, number, number];
7
+ /** Color of light squares */
8
+ light?: string;
9
+ /** Color of dark squares */
10
+ dark?: string;
11
+ /** Color of highlighted squares */
12
+ highlight?: string;
13
+ /** Desired style of standard pieces */
14
+ style?: PieceStyle;
15
+ /** View the board from Black's perspective */
16
+ flipped?: boolean;
17
+ /** Draw algebraic notations (A-H, 1-8) around the board */
18
+ notations?: boolean;
19
+ /** Custom color for the notation texts */
20
+ notationColor?: string;
21
+ /** Placement of the notations */
22
+ notationStyle?: 'inside' | 'outside';
23
+ /** Image output format (png, jpeg, webp) */
24
+ format?: 'png' | 'jpeg' | 'webp';
25
+ /** Image quality (0-100) for jpeg and webp formats */
26
+ quality?: number;
27
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ export type PieceStyle = 'neo' | 'glass' | 'wood' | 'game_room' | 'merida' | 'alpha' | 'cheq' | 'cburnett' | 'game_room_gothic' | 'game_room_space' | 'leipzig' | 'neo_wood';
2
+ export interface ChessImageOptions {
3
+ /** Pixel size of the generated board (e.g. 720) */
4
+ size?: number;
5
+ /** Padding around the board [top, right, bottom, left] */
6
+ padding?: [number, number, number, number];
7
+ /** Color of light squares */
8
+ light?: string;
9
+ /** Color of dark squares */
10
+ dark?: string;
11
+ /** Color of highlighted squares */
12
+ highlight?: string;
13
+ /** Desired style of standard pieces */
14
+ style?: PieceStyle;
15
+ /** View the board from Black's perspective */
16
+ flipped?: boolean;
17
+ /** Draw algebraic notations (A-H, 1-8) around the board */
18
+ notations?: boolean;
19
+ /** Custom color for the notation texts */
20
+ notationColor?: string;
21
+ /** Placement of the notations */
22
+ notationStyle?: 'inside' | 'outside';
23
+ /** Image output format (png, jpeg, webp) */
24
+ format?: 'png' | 'jpeg' | 'webp';
25
+ /** Image quality (0-100) for jpeg and webp formats */
26
+ quality?: number;
27
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "chess-snap",
3
+ "version": "1.0.0",
4
+ "description": "Generate high-quality Chess Boards (like chess.com & lichess) through FEN, PGN & Arrays using Napi-RS.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "test": "node --experimental-test-coverage --test --loader ts-node/esm tests/index.test.ts",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "src/assets"
22
+ ],
23
+ "keywords": [
24
+ "chess",
25
+ "board",
26
+ "generator",
27
+ "image",
28
+ "canvas",
29
+ "napi",
30
+ "fen",
31
+ "pgn",
32
+ "lichess",
33
+ "chess.com",
34
+ "snapshot"
35
+ ],
36
+ "author": "Carlyle Katto",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/kogakisaki/chess-snap.git"
40
+ },
41
+ "license": "MIT",
42
+ "dependencies": {
43
+ "@napi-rs/canvas": "^0.1.97",
44
+ "chess.js": "^1.4.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^25.5.0",
48
+ "ts-node": "^10.9.2",
49
+ "typescript": "^6.0.2"
50
+ }
51
+ }