@chessviewer-org/chess-viewer 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.
- package/CHANGELOG.md +48 -0
- package/LICENSE +661 -0
- package/README.md +494 -0
- package/dist/index.cjs +1196 -0
- package/dist/index.d.cts +369 -0
- package/dist/index.d.ts +369 -0
- package/dist/index.js +1091 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# @chessviewer-org/chess-viewer
|
|
2
|
+
|
|
3
|
+
Chess diagram generator and FEN utilities. Parse and edit FEN positions, render SVG board diagrams, manipulate boards, work with colors, board themes, presets, images, and history — in Node.js or the browser with **no dependencies and no DOM required**.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@chessviewer-org/chess-viewer)
|
|
6
|
+
[](https://www.npmjs.com/package/@chessviewer-org/chess-viewer)
|
|
7
|
+
[](https://bundlephobia.com/package/@chessviewer-org/chess-viewer)
|
|
8
|
+
[](https://github.com/chessviewer-org/chess-viewer-utils/actions/workflows/ci.yml)
|
|
9
|
+
[](https://www.npmjs.com/package/@chessviewer-org/chess-viewer)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
- **Zero dependencies** — nothing pulled into your tree.
|
|
13
|
+
- **Universal** — runs in Node.js and the browser, no DOM, no canvas, no network.
|
|
14
|
+
- **Dual ESM + CJS** with first-class TypeScript types.
|
|
15
|
+
- **Batteries included** — FEN parsing/editing, SVG rendering with an inline piece set, 20 board themes, color & DPI utilities, and more.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @chessviewer-org/chess-viewer
|
|
23
|
+
# or
|
|
24
|
+
pnpm add @chessviewer-org/chess-viewer
|
|
25
|
+
# or
|
|
26
|
+
yarn add @chessviewer-org/chess-viewer
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Requirements:** Node.js ≥ 18, or any modern browser.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { generateDiagram, parseFEN, validateFEN } from '@chessviewer-org/chess-viewer';
|
|
37
|
+
|
|
38
|
+
// Generate a self-contained SVG diagram
|
|
39
|
+
const svg = generateDiagram({
|
|
40
|
+
fen: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
|
|
41
|
+
size: 400,
|
|
42
|
+
showCoords: true,
|
|
43
|
+
lightSquare: '#f0d9b5',
|
|
44
|
+
darkSquare: '#b58863',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Write to a file (Node.js)
|
|
48
|
+
import { writeFileSync } from 'fs';
|
|
49
|
+
writeFileSync('board.svg', svg);
|
|
50
|
+
|
|
51
|
+
// Or embed directly in HTML
|
|
52
|
+
document.getElementById('board').innerHTML = svg;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Example output
|
|
56
|
+
|
|
57
|
+
The diagram below is real SVG produced by `generateDiagram` — no images, no canvas:
|
|
58
|
+
|
|
59
|
+
<p align="center">
|
|
60
|
+
<img src="https://raw.githubusercontent.com/chessviewer-org/chess-viewer-utils/main/assets/preview.svg" width="360" alt="Chess diagram rendered by chess-vision (Italian Game, Wood theme)">
|
|
61
|
+
</p>
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Versioning
|
|
66
|
+
|
|
67
|
+
This package follows [Semantic Versioning](https://semver.org/):
|
|
68
|
+
- **Patch** (`1.0.x`) — bug fixes, no API changes
|
|
69
|
+
- **Minor** (`1.x.0`) — new exports added, fully backward-compatible
|
|
70
|
+
- **Major** (`x.0.0`) — breaking API changes
|
|
71
|
+
|
|
72
|
+
### Install a specific version
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Latest stable
|
|
76
|
+
npm install @chessviewer-org/chess-viewer
|
|
77
|
+
|
|
78
|
+
# Specific version
|
|
79
|
+
npm install @chessviewer-org/chess-viewer@1.0.0
|
|
80
|
+
|
|
81
|
+
# Latest minor of a major (e.g. 1.x)
|
|
82
|
+
npm install @chessviewer-org/chess-viewer@^1.0.0
|
|
83
|
+
|
|
84
|
+
# Exact patch
|
|
85
|
+
npm install @chessviewer-org/chess-viewer@~1.0.0
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Check what version you have
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm list @chessviewer-org/chess-viewer
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Check the latest version on npm
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm view @chessviewer-org/chess-viewer version
|
|
98
|
+
# or all published versions:
|
|
99
|
+
npm view @chessviewer-org/chess-viewer versions --json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Upgrade to latest
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm update @chessviewer-org/chess-viewer
|
|
106
|
+
# or force latest:
|
|
107
|
+
npm install @chessviewer-org/chess-viewer@latest
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
See [CHANGELOG.md](CHANGELOG.md) for what changed in each release.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## API Reference
|
|
115
|
+
|
|
116
|
+
### `generateDiagram(options)`
|
|
117
|
+
|
|
118
|
+
Generates a self-contained SVG chess diagram. No DOM, no network requests.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { generateDiagram } from '@chessviewer-org/chess-viewer';
|
|
122
|
+
|
|
123
|
+
const svg = generateDiagram({
|
|
124
|
+
fen: 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1',
|
|
125
|
+
size: 480, // board pixel size (default: 400)
|
|
126
|
+
showCoords: true, // show a/b/c… and 1/2/3… labels (default: false)
|
|
127
|
+
flipped: false, // show from Black's perspective (default: false)
|
|
128
|
+
showFrame: false, // thin outer frame (default: false)
|
|
129
|
+
lightSquare: '#d4af7a',
|
|
130
|
+
darkSquare: '#8b4513',
|
|
131
|
+
coordColor: 'white', // hex or 'white' | 'black' — keep coords legible on dark boards (default: '#000000')
|
|
132
|
+
label: 'Starting position after 1.e4', // aria-label (default: 'Chess position')
|
|
133
|
+
});
|
|
134
|
+
// → '<svg xmlns="http://www.w3.org/2000/svg" …>…</svg>'
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### FEN utilities
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import {
|
|
143
|
+
parseFEN,
|
|
144
|
+
validateFEN,
|
|
145
|
+
validateFENDetailed,
|
|
146
|
+
getFENValidationError,
|
|
147
|
+
boardToFEN,
|
|
148
|
+
createEmptyBoard,
|
|
149
|
+
isBoardEmpty,
|
|
150
|
+
pieceToName,
|
|
151
|
+
describeBoardPosition,
|
|
152
|
+
FENParseError,
|
|
153
|
+
} from '@chessviewer-org/chess-viewer';
|
|
154
|
+
|
|
155
|
+
// Parse FEN → 8×8 matrix
|
|
156
|
+
const board = parseFEN('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
|
|
157
|
+
// board[0][0] === 'r', board[7][4] === 'K'
|
|
158
|
+
|
|
159
|
+
// Quick validity check
|
|
160
|
+
validateFEN('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'); // → true
|
|
161
|
+
|
|
162
|
+
// Human-readable error for UI
|
|
163
|
+
getFENValidationError('bad/fen'); // → 'Board must have 8 ranks'
|
|
164
|
+
|
|
165
|
+
// Detailed error with user-facing messages
|
|
166
|
+
const result = validateFENDetailed('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1');
|
|
167
|
+
// → { isValid: true, errorMessage: null }
|
|
168
|
+
|
|
169
|
+
// Matrix → FEN piece placement
|
|
170
|
+
boardToFEN(board); // → 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'
|
|
171
|
+
|
|
172
|
+
// Screen-reader description of position
|
|
173
|
+
describeBoardPosition(board);
|
|
174
|
+
// → 'White: white king e1, white queen d1, … Black: black king e8, …'
|
|
175
|
+
|
|
176
|
+
// FEN parse errors
|
|
177
|
+
try {
|
|
178
|
+
parseFEN('not-valid');
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (e instanceof FENParseError) console.error(e.message);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Full FEN record
|
|
187
|
+
|
|
188
|
+
Parse and serialize all six FEN fields — board, side to move, castling rights,
|
|
189
|
+
en passant target, and move clocks. Placement-only strings parse too, filling
|
|
190
|
+
standard defaults.
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import {
|
|
194
|
+
parseFENRecord,
|
|
195
|
+
buildFENRecord,
|
|
196
|
+
toggleActiveColor,
|
|
197
|
+
fenPlacementField,
|
|
198
|
+
normalizeFEN,
|
|
199
|
+
} from '@chessviewer-org/chess-viewer';
|
|
200
|
+
|
|
201
|
+
const record = parseFENRecord('rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 5 12');
|
|
202
|
+
// → { board, activeColor: 'b', castling: 'KQkq', enPassant: 'e3', halfmove: 5, fullmove: 12 }
|
|
203
|
+
|
|
204
|
+
buildFENRecord(record); // → 'rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 5 12'
|
|
205
|
+
toggleActiveColor(record).activeColor; // → 'w' (pure, no mutation)
|
|
206
|
+
|
|
207
|
+
fenPlacementField('rnbqkbnr/… w KQkq - 0 1'); // → 'rnbqkbnr/…'
|
|
208
|
+
normalizeFEN('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR');
|
|
209
|
+
// → 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### Board manipulation
|
|
215
|
+
|
|
216
|
+
Pure helpers for editing positions — every function returns a new board and
|
|
217
|
+
never mutates its input. Squares accept either algebraic strings (`'e4'`) or
|
|
218
|
+
`[row, col]` matrix indices.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import {
|
|
222
|
+
cloneBoard,
|
|
223
|
+
getPieceAt, setPieceAt, removePieceAt, movePiece,
|
|
224
|
+
flipBoard, listPieces, countPieces,
|
|
225
|
+
materialBalance, findKing, hasBothKings,
|
|
226
|
+
} from '@chessviewer-org/chess-viewer';
|
|
227
|
+
|
|
228
|
+
const board = parseFEN('rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR');
|
|
229
|
+
|
|
230
|
+
getPieceAt(board, 'e1'); // → 'K'
|
|
231
|
+
const next = movePiece(board, 'b1', 'c3'); // develops the knight, returns a new board
|
|
232
|
+
removePieceAt(next, 'd8'); // → new board with Black's queen removed
|
|
233
|
+
|
|
234
|
+
flipBoard(board); // rotate 180° for Black's perspective
|
|
235
|
+
listPieces(board); // → [{ square: 'a8', piece: 'r' }, …] (a8 → h1)
|
|
236
|
+
countPieces(board); // → { r: 2, n: 2, …, P: 8 }
|
|
237
|
+
materialBalance(board); // → 0 (positive = White ahead)
|
|
238
|
+
findKing(board, 'w'); // → 'e1'
|
|
239
|
+
hasBothKings(board); // → true (exactly one king per side)
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### Theme & preset helpers
|
|
245
|
+
|
|
246
|
+
Look up board themes, piece sets, and quality presets, with contrast-aware
|
|
247
|
+
helpers for legible coordinates.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import {
|
|
251
|
+
getBoardTheme, listThemeIds,
|
|
252
|
+
getPieceSet, pieceSetsByPopularity,
|
|
253
|
+
getQualityPreset,
|
|
254
|
+
themeContrast, themeCoordinateColor,
|
|
255
|
+
} from '@chessviewer-org/chess-viewer';
|
|
256
|
+
|
|
257
|
+
getBoardTheme('ocean'); // → { name: 'Ocean', light: '#c9e4f5', dark: '#4a90a4' }
|
|
258
|
+
listThemeIds(); // → ['classic', 'brown', 'wood', …]
|
|
259
|
+
|
|
260
|
+
getPieceSet('cburnett'); // → { id: 'cburnett', name: 'Classic (CBurnett)' }
|
|
261
|
+
pieceSetsByPopularity()[0]; // → { id: 'cburnett', … } (most popular first)
|
|
262
|
+
|
|
263
|
+
getQualityPreset(2); // → { value: 2, label: 'Print 2× (600 DPI)', … }
|
|
264
|
+
|
|
265
|
+
const theme = getBoardTheme('classic')!;
|
|
266
|
+
themeContrast(theme); // → WCAG contrast ratio between squares
|
|
267
|
+
themeCoordinateColor(theme); // → 'white' | 'black' (best on dark squares)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### Image utilities
|
|
273
|
+
|
|
274
|
+
Read raster dimensions straight from PNG/JPEG headers (no decoding, no DOM) and
|
|
275
|
+
compute physical print sizes — pairs naturally with `changeDPI`.
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import { readImageDimensions, physicalSize } from '@chessviewer-org/chess-viewer';
|
|
279
|
+
|
|
280
|
+
const bytes = new Uint8Array(await blob.arrayBuffer());
|
|
281
|
+
readImageDimensions(bytes); // → { width: 1200, height: 1200 } | null
|
|
282
|
+
|
|
283
|
+
physicalSize(1200, 300); // → { inches: 4, mm: 101.6 }
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### Board themes & constants
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
import {
|
|
292
|
+
BOARD_THEMES,
|
|
293
|
+
PIECE_SETS,
|
|
294
|
+
PIECE_SET_POPULARITY,
|
|
295
|
+
QUALITY_PRESETS,
|
|
296
|
+
DEFAULT_LIGHT_SQUARE,
|
|
297
|
+
DEFAULT_DARK_SQUARE,
|
|
298
|
+
STARTING_FEN,
|
|
299
|
+
EMPTY_FEN,
|
|
300
|
+
} from '@chessviewer-org/chess-viewer';
|
|
301
|
+
|
|
302
|
+
// 20 built-in board color themes
|
|
303
|
+
BOARD_THEMES.classic // { name: 'Classic', light: '#f0d9b5', dark: '#b58863' }
|
|
304
|
+
BOARD_THEMES.ocean // { name: 'Ocean', light: '#c9e4f5', dark: '#4a90a4' }
|
|
305
|
+
|
|
306
|
+
// 23 Lichess-compatible piece sets
|
|
307
|
+
PIECE_SETS[0] // { id: 'alpha', name: 'Alpha' }
|
|
308
|
+
|
|
309
|
+
// Popularity-ranked piece set ids (most → least)
|
|
310
|
+
PIECE_SET_POPULARITY[0] // 'cburnett'
|
|
311
|
+
|
|
312
|
+
// Print/social quality presets (DPI multipliers)
|
|
313
|
+
QUALITY_PRESETS // [{value:1, label:'Print 1× (300 DPI)', mode:'print', …}, …]
|
|
314
|
+
|
|
315
|
+
// Convenient FEN constants
|
|
316
|
+
STARTING_FEN // 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
|
|
317
|
+
EMPTY_FEN // '8/8/8/8/8/8/8/8 w - - 0 1'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### Color utilities
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import {
|
|
326
|
+
hexToRgb, rgbToHex,
|
|
327
|
+
rgbToHsv, hsvToRgb,
|
|
328
|
+
hexToHsv, hsvToHex,
|
|
329
|
+
relativeLuminance,
|
|
330
|
+
contrastRatio,
|
|
331
|
+
bestTextColor,
|
|
332
|
+
} from '@chessviewer-org/chess-viewer';
|
|
333
|
+
|
|
334
|
+
hexToRgb('#b58863') // → { r: 181, g: 136, b: 99 }
|
|
335
|
+
rgbToHex(181, 136, 99) // → '#b58863'
|
|
336
|
+
|
|
337
|
+
const { h, s, v } = hexToHsv('#b58863');
|
|
338
|
+
hsvToHex(h, s, v) // → '#b58863'
|
|
339
|
+
|
|
340
|
+
contrastRatio('#000000', '#ffffff') // → 21
|
|
341
|
+
bestTextColor('#b58863') // → 'white' | 'black'
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
### Coordinate utilities
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import {
|
|
350
|
+
squareToIndices,
|
|
351
|
+
indicesToSquare,
|
|
352
|
+
getSquareBounds,
|
|
353
|
+
isLightSquare,
|
|
354
|
+
getCoordinateParams,
|
|
355
|
+
} from '@chessviewer-org/chess-viewer';
|
|
356
|
+
|
|
357
|
+
squareToIndices('e4') // → [4, 4] (row 0 = rank 8)
|
|
358
|
+
indicesToSquare(4, 4) // → 'e4'
|
|
359
|
+
squareToIndices('a8') // → [0, 0]
|
|
360
|
+
squareToIndices('h1') // → [7, 7]
|
|
361
|
+
|
|
362
|
+
isLightSquare(0, 0) // → true (a8 is light)
|
|
363
|
+
isLightSquare(7, 0) // → false (a1 is dark)
|
|
364
|
+
|
|
365
|
+
// Pixel bounds of a square (for canvas rendering)
|
|
366
|
+
getSquareBounds(0, 0, 50) // → { x:0, y:0, width:50, height:50, centerX:25, centerY:25 }
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### History utilities
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import {
|
|
375
|
+
createHistoryEntry,
|
|
376
|
+
calculateStatus,
|
|
377
|
+
sortByMostRecent,
|
|
378
|
+
applyFilters,
|
|
379
|
+
mergeById,
|
|
380
|
+
convertToArchivedEntry,
|
|
381
|
+
} from '@chessviewer-org/chess-viewer';
|
|
382
|
+
|
|
383
|
+
const entry = createHistoryEntry(
|
|
384
|
+
'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1',
|
|
385
|
+
'manual'
|
|
386
|
+
);
|
|
387
|
+
// → { id: 1234567890, fen: '…', createdAt: …, lastActiveAt: …, source: 'manual', isFavorite: false }
|
|
388
|
+
|
|
389
|
+
calculateStatus(entry.lastActiveAt) // → 'green' | 'yellow' | 'red'
|
|
390
|
+
|
|
391
|
+
// Filter entries
|
|
392
|
+
applyFilters(entries, { fenSearch: 'e4', favoritesOnly: false, source: 'manual' });
|
|
393
|
+
|
|
394
|
+
// Merge two lists (e.g. cloud + local), primary wins on id collision
|
|
395
|
+
mergeById(cloudEntries, localEntries);
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
### Validation utilities
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import {
|
|
404
|
+
isValidHexColor,
|
|
405
|
+
sanitizeHexColor,
|
|
406
|
+
sanitizeFileName,
|
|
407
|
+
sanitizeInput,
|
|
408
|
+
safeJSONParse,
|
|
409
|
+
} from '@chessviewer-org/chess-viewer';
|
|
410
|
+
|
|
411
|
+
isValidHexColor('#f0d9b5') // → true
|
|
412
|
+
isValidHexColor('red') // → false
|
|
413
|
+
|
|
414
|
+
sanitizeHexColor('bad', '#fff') // → '#fff'
|
|
415
|
+
sanitizeHexColor('#aabbcc') // → '#aabbcc'
|
|
416
|
+
|
|
417
|
+
sanitizeFileName('my<board>') // → 'my-board-'
|
|
418
|
+
sanitizeInput('<script>xss') // → '<script>xss'
|
|
419
|
+
|
|
420
|
+
safeJSONParse('{"a":1}', {}) // → { a: 1 }
|
|
421
|
+
safeJSONParse('bad json', {}) // → {} (fallback, no throw)
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
### DPI encoding
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
import { changeDPI } from '@chessviewer-org/chess-viewer';
|
|
430
|
+
|
|
431
|
+
// Rewrite DPI metadata in a PNG or JPEG blob
|
|
432
|
+
const correctedBlob = await changeDPI(originalBlob, 300, 'png');
|
|
433
|
+
const correctedJpeg = await changeDPI(originalBlob, 150, 'jpeg');
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
### Inline piece SVGs
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
import { getPieceSVG, PIECES } from '@chessviewer-org/chess-viewer';
|
|
442
|
+
|
|
443
|
+
getPieceSVG('K') // → '<svg …>…</svg>' (white king, CBurnett style)
|
|
444
|
+
getPieceSVG('k') // → '<svg …>…</svg>' (black king)
|
|
445
|
+
getPieceSVG('X') // → null
|
|
446
|
+
|
|
447
|
+
// All 12 pieces
|
|
448
|
+
Object.keys(PIECES) // → ['wK','wQ','wR','wB','wN','wP','bK','bQ','bR','bB','bN','bP']
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## TypeScript
|
|
454
|
+
|
|
455
|
+
This package ships full TypeScript types. No `@types/…` package needed.
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
import type {
|
|
459
|
+
PieceSymbol,
|
|
460
|
+
BoardMatrix,
|
|
461
|
+
DiagramOptions,
|
|
462
|
+
ValidationResult,
|
|
463
|
+
FENRecord,
|
|
464
|
+
ActiveColor,
|
|
465
|
+
SquareRef,
|
|
466
|
+
PiecePlacement,
|
|
467
|
+
ActiveHistoryEntry,
|
|
468
|
+
ArchivedHistoryEntry,
|
|
469
|
+
HistoryFilters,
|
|
470
|
+
BoardTheme,
|
|
471
|
+
QualityPreset,
|
|
472
|
+
PieceSet,
|
|
473
|
+
CoordinateParams,
|
|
474
|
+
SquareBounds,
|
|
475
|
+
ImageDimensions,
|
|
476
|
+
} from '@chessviewer-org/chess-viewer';
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Documentation & project
|
|
482
|
+
|
|
483
|
+
- [Documentation](docs/README.md) — guides, architecture, and references.
|
|
484
|
+
- [Roadmap](ROADMAP.md) — what's planned and what's out of scope.
|
|
485
|
+
- [Changelog](CHANGELOG.md) — what changed in each release.
|
|
486
|
+
- [Contributing](CONTRIBUTING.md) — how to propose and submit changes.
|
|
487
|
+
- [Security policy](SECURITY.md) — how to report a vulnerability.
|
|
488
|
+
- [Discussions](https://github.com/chessviewer-org/chess-viewer-utils/discussions) — questions and ideas.
|
|
489
|
+
|
|
490
|
+
---
|
|
491
|
+
|
|
492
|
+
## License
|
|
493
|
+
|
|
494
|
+
[AGPL-3.0](LICENSE)
|