@chesscam/recorder 0.1.0 → 0.2.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/README.md +129 -87
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,151 +1,193 @@
|
|
|
1
1
|
# @chesscam/recorder
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@chesscam/recorder)
|
|
4
|
+
|
|
5
|
+
Point a camera at a real chess board and get the moves as they're played. Everything runs in the browser: no server,
|
|
6
|
+
no uploads.
|
|
7
|
+
|
|
8
|
+
This is the move detection from [ChessCam](https://www.chesscam.net), as a library. It works with a live camera, a
|
|
9
|
+
video file or a video stream.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npm install @chesscam/recorder
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
6
18
|
|
|
7
19
|
```ts
|
|
8
20
|
import { createRecorder, loadModel } from "@chesscam/recorder";
|
|
9
21
|
|
|
22
|
+
// 1. Load the model (about 24 MB, downloaded once)
|
|
10
23
|
const model = await loadModel();
|
|
24
|
+
|
|
25
|
+
// 2. Point a recorder at a <video> showing the board
|
|
11
26
|
const recorder = createRecorder({
|
|
12
27
|
model,
|
|
13
28
|
video: document.querySelector("video")!,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
onUpdate: (game) => showMoves(game.moves),
|
|
17
|
-
onError: (error) => showStatus(error.code)
|
|
29
|
+
roi: { x: 438, y: 384, width: 444, height: 280 }, // where the board is, in video pixels
|
|
30
|
+
onUpdate: (game) => console.log(game.moves) // e.g. "1. e4 e5 2. Nf3"
|
|
18
31
|
});
|
|
32
|
+
|
|
33
|
+
// 3. Start recording when the game starts
|
|
19
34
|
recorder.start();
|
|
20
35
|
|
|
21
|
-
//
|
|
36
|
+
// 4. When the game ends, stop and save it as PGN
|
|
22
37
|
recorder.pause();
|
|
23
|
-
const pgn = recorder.pgn({ White: "Alice", Black: "Bob"
|
|
38
|
+
const pgn = recorder.pgn({ White: "Alice", Black: "Bob" });
|
|
24
39
|
```
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
For a full working page with a camera or video file, see [`examples/demo`](examples/demo).
|
|
27
42
|
|
|
28
|
-
##
|
|
43
|
+
## Good to know
|
|
29
44
|
|
|
30
|
-
**
|
|
31
|
-
|
|
45
|
+
- **Tell it where the board is.** `roi` is the rectangle around the board in the video. Without it the whole frame is
|
|
46
|
+
used, which only works if the board fills the picture.
|
|
47
|
+
- **Start when the game starts.** Anything else on camera (an empty board, hands setting up the pieces) can be mistaken
|
|
48
|
+
for moves.
|
|
49
|
+
- **Earlier moves can change.** If the recorder realises it misread a move, often because a hand was in the way, it
|
|
50
|
+
fixes it a few seconds later. Always show the whole `game.moves` from each `onUpdate`, not just the newest move.
|
|
51
|
+
- **Which videos work.** Anything a `<video>` element can play and the page can read the pixels of: a camera, a local
|
|
52
|
+
file, a WebRTC stream, or HLS/DASH served with CORS. YouTube or Twitch embeds and DRM-protected video can't be read.
|
|
53
|
+
|
|
54
|
+
## Setup with a bundler
|
|
55
|
+
|
|
56
|
+
The model runs in a web worker, which Vite and webpack pick up automatically. With Vite, add this to your config so the
|
|
57
|
+
worker file is found:
|
|
32
58
|
|
|
33
59
|
```ts
|
|
34
60
|
optimizeDeps: { exclude: ["@chesscam/recorder"] }
|
|
35
61
|
```
|
|
36
62
|
|
|
37
|
-
|
|
38
|
-
`node_modules/@litertjs/core/wasm/` and pass `loadModel({ wasmUrl })`) to work offline, and so the model can use
|
|
39
|
-
threads: a browser only starts the runtime's threads from the page's own origin. It must be version `LITERT_VERSION`.
|
|
63
|
+
## Making it faster (optional)
|
|
40
64
|
|
|
41
|
-
|
|
42
|
-
|
|
65
|
+
By default the model runs on one thread, about 6 frames a second on a Pixel 7a. That's already more than the 4 frames
|
|
66
|
+
a second it reads by default. To use up to four threads (about 15 frames a second):
|
|
43
67
|
|
|
44
|
-
|
|
45
|
-
Cross-Origin-Opener-Policy: same-origin
|
|
46
|
-
Cross-Origin-Embedder-Policy: credentialless
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
On a Pixel 7a the model manages about 6 frames a second on one thread and 15 on four (it uses at most four, as more
|
|
50
|
-
were slower), both above the cap of 4.
|
|
51
|
-
|
|
52
|
-
**The model**, chessq-chesscam-mnv4m-320-v1 (23.9 MB), loads from Hugging Face, pinned to a commit
|
|
53
|
-
(`DEFAULT_MODEL_URL`). Self-host it with `loadModel({ modelUrl })` to load it from the page's own origin, e.g. where
|
|
54
|
-
huggingface.co is blocked.
|
|
68
|
+
1. Send these headers with your page:
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
```
|
|
71
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
72
|
+
Cross-Origin-Embedder-Policy: credentialless
|
|
73
|
+
```
|
|
57
74
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
(YouTube, Twitch) or DRM-protected video can't be read. Detection runs at up to `maxFps` frames a second (default 4),
|
|
61
|
-
scaled by the video's playback rate.
|
|
75
|
+
2. Serve the LiteRT.js runtime from your own site: copy `node_modules/@litertjs/core/wasm/` into your public files and
|
|
76
|
+
pass its URL with `loadModel({ wasmUrl: "/litert/" })`. It must be version `LITERT_VERSION`.
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
Serving the runtime yourself also lets the recorder work offline. You can host the model file yourself too, with
|
|
79
|
+
`loadModel({ modelUrl })`. By default it comes from Hugging Face.
|
|
65
80
|
|
|
66
81
|
## API
|
|
67
82
|
|
|
68
|
-
### `loadModel(options?)
|
|
83
|
+
### `loadModel(options?)`
|
|
69
84
|
|
|
70
|
-
Loads the model
|
|
71
|
-
gives the same one until it's deleted.
|
|
85
|
+
Loads the model and returns a promise of it. Calling it again returns the same model.
|
|
72
86
|
|
|
73
|
-
| Option | |
|
|
87
|
+
| Option | What it does |
|
|
74
88
|
|---|---|
|
|
75
|
-
| `modelUrl` |
|
|
76
|
-
| `wasmUrl` |
|
|
89
|
+
| `modelUrl` | Load the `.tflite` model from here instead of Hugging Face |
|
|
90
|
+
| `wasmUrl` | Load the LiteRT.js runtime from here (a folder URL ending in `/`) instead of jsDelivr |
|
|
77
91
|
|
|
78
|
-
### `createRecorder(options)
|
|
92
|
+
### `createRecorder(options)`
|
|
79
93
|
|
|
80
|
-
|
|
94
|
+
Creates a recorder. It starts paused; call `start()` to begin.
|
|
81
95
|
|
|
82
|
-
| Option | |
|
|
96
|
+
| Option | What it does |
|
|
83
97
|
|---|---|
|
|
84
|
-
| `model
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| Method | |
|
|
98
|
+
| `model` | The model from `loadModel()` (required) |
|
|
99
|
+
| `video` | The `<video>` element to read (required) |
|
|
100
|
+
| `roi` | Where the board is: `{ x, y, width, height }` in video pixels |
|
|
101
|
+
| `start` | The starting position as FEN, if not the normal start |
|
|
102
|
+
| `timed` | Record how long each move took, saved in the PGN (default `true`) |
|
|
103
|
+
| `finalMovesOnly` | Only report moves once they're certain, about 1.5 s after they're played. Useful when sending moves to an online game. |
|
|
104
|
+
| `maxFps` | How many frames a second to read (default 4) |
|
|
105
|
+
| `onUpdate(game)` | Called whenever the moves change |
|
|
106
|
+
| `onError(error)` | Called when a frame fails. Recording carries on. |
|
|
107
|
+
| `onFps(fps)` | The current frame rate, about once a second (`null` once it stops) |
|
|
108
|
+
| `onOrientation(corners)` | Called when it works out which way round the board is |
|
|
109
|
+
| `onDetect(detection)` | Called with the raw result of every frame |
|
|
110
|
+
|
|
111
|
+
A recorder has these methods and properties:
|
|
112
|
+
|
|
113
|
+
| | What it does |
|
|
101
114
|
|---|---|
|
|
102
|
-
| `start()
|
|
103
|
-
| `game
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
| `start()` / `pause()` | Start or pause reading. Paused time doesn't count towards move times. |
|
|
116
|
+
| `game` | The game so far |
|
|
117
|
+
| `running` | Whether it's currently reading frames |
|
|
118
|
+
| `pgn(headers?)` | The game as PGN text, with any headers you pass (e.g. `White`, `Event`) |
|
|
119
|
+
| `reset(start?)` | Start a new game |
|
|
120
|
+
| `setGame({ moves, start?, fromOpponent?, times? })` | Replace the game yourself, e.g. for a takeback or an online opponent's move |
|
|
121
|
+
| `setRoi(roi)` / `setVideo(video)` | Change where the board is, or which video to read |
|
|
122
|
+
| `destroy()` | Stop and clean up |
|
|
123
|
+
|
|
124
|
+
A **game** has:
|
|
125
|
+
|
|
126
|
+
| | |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `moves` | The moves so far, e.g. `"1. e4 e5 2. Nf3"` |
|
|
129
|
+
| `fen` | The current position |
|
|
130
|
+
| `start` | The starting position |
|
|
131
|
+
| `lastMove` | The last move, e.g. `"g1f3"` |
|
|
132
|
+
| `fromOpponent` | Whether the last move came from `setGame` rather than the board |
|
|
133
|
+
| `times` | When each move was played, in ms since recording started |
|
|
112
134
|
|
|
113
135
|
### `readPosition(model, video, roi?, turn?)`
|
|
114
136
|
|
|
115
|
-
Reads the position
|
|
116
|
-
|
|
117
|
-
`"pawns-on-backrank"
|
|
137
|
+
Reads the current position from the board, to start recording a game that's already under way. Returns
|
|
138
|
+
`{ ok: true, fen }`, or `{ ok: false, problem }` if the board doesn't show a legal position (e.g. `"kings"`,
|
|
139
|
+
`"pawns-on-backrank"`).
|
|
118
140
|
|
|
119
141
|
### Errors
|
|
120
142
|
|
|
121
|
-
`RecorderError`
|
|
143
|
+
Errors are a `RecorderError` with a `code`, so you can show your own message:
|
|
122
144
|
|
|
123
|
-
| Code | |
|
|
145
|
+
| Code | Meaning |
|
|
124
146
|
|---|---|
|
|
125
|
-
| `not-ready` |
|
|
126
|
-
| `worker-stopped` | The
|
|
147
|
+
| `not-ready` | The model isn't loaded or the video hasn't got a frame yet |
|
|
148
|
+
| `worker-stopped` | The model's worker crashed, e.g. out of memory |
|
|
127
149
|
| `unloaded` | The model was deleted |
|
|
128
|
-
| `detection-failed` |
|
|
150
|
+
| `detection-failed` | Something else went wrong; see `error.message` |
|
|
151
|
+
|
|
152
|
+
### Games, moves and rules
|
|
153
|
+
|
|
154
|
+
For apps that keep the game themselves (e.g. in a store) and pass changes to the recorder with `setGame`:
|
|
155
|
+
|
|
156
|
+
| Export | |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `newGame(start?)`, `makePgn(game, headers?)` | A game with no moves, and a game as PGN |
|
|
159
|
+
| `makeBoard(game)`, `GameBoard` | The board after a game's moves, to play more (`playSan`, `playUci`, `undo`) and read its `moves`, `fen`, `history` |
|
|
160
|
+
| `makeUpdatePayload(board, fromOpponent?)` | A board's `moves`, `fen`, `lastMove` and `fromOpponent`, for a `Game` |
|
|
161
|
+
| `getMoveText(board)` | The last one or two moves, numbered, e.g. `3...Nc6 4.Bb5` |
|
|
162
|
+
| `replay(probs, fps, start?)` | Runs saved model outputs through the tracking, frame by frame, giving the moves and when each was played; for scoring the tracking on recorded games |
|
|
163
|
+
|
|
164
|
+
The chess rules are exported too: `Position` (legal moves, `play`, `isCheck`, `outcome`, `fen()`), `parseFen`,
|
|
165
|
+
`makeFen`, `parseSan`, `makeSan`, `parseUci`, `makeUci`, and the types `Color`, `Role`, `Piece`, `Square`,
|
|
166
|
+
`SquareName` and `Move`. Castling is the king's two-square move, e.g. `e1g1`.
|
|
129
167
|
|
|
130
168
|
## Development
|
|
131
169
|
|
|
132
170
|
```
|
|
133
171
|
npm install
|
|
134
172
|
npm run check # tests, types, lint
|
|
135
|
-
npm run build # dist/
|
|
173
|
+
npm run build # builds dist/
|
|
136
174
|
```
|
|
137
175
|
|
|
138
|
-
To
|
|
139
|
-
video, and `&wasm=<url>` to
|
|
176
|
+
To run the demo: `cd examples/demo && npm install && npm run dev`. You can add `?video=<url>&t=<start s>&roi=x,y,w,h`
|
|
177
|
+
to the URL to play a video, and `&wasm=<url>` to load the runtime from the page's own site.
|
|
140
178
|
|
|
141
179
|
The code came from ChessCam's `src/vision` and `src/chess` at commit `880c130`. The tracker's settings are tuned on
|
|
142
180
|
replays of recorded games in ChessCam's research repo.
|
|
143
181
|
|
|
182
|
+
To release: bump `version` in `package.json`, commit and push, then publish a GitHub release tagged `v<version>`
|
|
183
|
+
(e.g. `v0.1.1`). `.github/workflows/publish.yml` publishes it to npm with trusted publishing, so no npm token is
|
|
184
|
+
needed.
|
|
185
|
+
|
|
144
186
|
## Licence
|
|
145
187
|
|
|
146
|
-
MIT. The chess rules, SAN, FEN and PGN
|
|
188
|
+
MIT. The chess rules, SAN, FEN and PGN code is its own (`src/rules`). Its dependencies have their own licences:
|
|
147
189
|
|
|
148
190
|
- [LiteRT.js](https://www.npmjs.com/package/@litertjs/core), which runs the model, is Apache-2.0.
|
|
149
|
-
- The default model, [chessq-chesscam](https://huggingface.co/PBatch23888/chessq-chesscam), is
|
|
150
|
-
|
|
151
|
-
licence for it.
|
|
191
|
+
- The default model, [chessq-chesscam](https://huggingface.co/PBatch23888/chessq-chesscam), is downloaded at runtime,
|
|
192
|
+
not included in the package. It is under the PolyForm Noncommercial License 1.0.0, so commercial use needs a
|
|
193
|
+
separate licence for it.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
export { createRecorder, type GameChange, type Recorder, type RecorderOptions } from "./recorder.js";
|
|
2
2
|
export { DEFAULT_MODEL_URL, DEFAULT_WASM_URL, type Detection, getOrientation, LITERT_VERSION, loadModel, type Model, type ModelOptions } from "./vision/model.js";
|
|
3
3
|
export { type PositionProblem, readPosition, type ReadPositionResult } from "./vision/position.js";
|
|
4
|
+
export { replay, type TimedMove, type Until } from "./vision/replay.js";
|
|
4
5
|
export { videoReady } from "./vision/video.js";
|
|
5
6
|
export { RecorderError, type RecorderErrorCode } from "./errors.js";
|
|
6
|
-
export { makePgn, newGame } from "./game.js";
|
|
7
|
+
export { type GameUpdate, makePgn, makeUpdatePayload, newGame } from "./game.js";
|
|
7
8
|
export { GameBoard, makeBoard } from "./chess/board.js";
|
|
9
|
+
export { getMoveText } from "./chess/moves.js";
|
|
8
10
|
export { START_FEN } from "./constants.js";
|
|
9
11
|
export type { CornerName, Game, Roi } from "./types.js";
|
|
12
|
+
export type { Color, Piece, Role, Square, SquareName } from "./rules/board.js";
|
|
13
|
+
export { FenError, IllegalSetupError, type IllegalSetup, makeFen, type Move, type Outcome, parseFen, Position, type Setup } from "./rules/position.js";
|
|
14
|
+
export { makeSan, makeUci, parseSan, parseUci } from "./rules/san.js";
|
|
10
15
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EACL,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACrG,OAAO,EACL,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,KAAK,EAC1G,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,KAAK,UAAU,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAExD,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EACL,QAAQ,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,KAAK,EACjH,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export { createRecorder } from "./recorder.js";
|
|
2
2
|
export { DEFAULT_MODEL_URL, DEFAULT_WASM_URL, getOrientation, LITERT_VERSION, loadModel } from "./vision/model.js";
|
|
3
3
|
export { readPosition } from "./vision/position.js";
|
|
4
|
+
export { replay } from "./vision/replay.js";
|
|
4
5
|
export { videoReady } from "./vision/video.js";
|
|
5
6
|
export { RecorderError } from "./errors.js";
|
|
6
|
-
export { makePgn, newGame } from "./game.js";
|
|
7
|
+
export { makePgn, makeUpdatePayload, newGame } from "./game.js";
|
|
7
8
|
export { GameBoard, makeBoard } from "./chess/board.js";
|
|
9
|
+
export { getMoveText } from "./chess/moves.js";
|
|
8
10
|
export { START_FEN } from "./constants.js";
|
|
11
|
+
export { FenError, IllegalSetupError, makeFen, parseFen, Position } from "./rules/position.js";
|
|
12
|
+
export { makeSan, makeUci, parseSan, parseUci } from "./rules/san.js";
|
|
9
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AACrG,OAAO,EACL,iBAAiB,EAAE,gBAAgB,EAAkB,cAAc,EAAE,cAAc,EAAE,SAAS,EAE/F,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAwB,YAAY,EAA2B,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAA0B,MAAM,aAAa,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAwD,MAAM,eAAe,CAAC;AACrG,OAAO,EACL,iBAAiB,EAAE,gBAAgB,EAAkB,cAAc,EAAE,cAAc,EAAE,SAAS,EAE/F,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAwB,YAAY,EAA2B,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,MAAM,EAA8B,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAA0B,MAAM,aAAa,CAAC;AACpE,OAAO,EAAmB,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAI3C,OAAO,EACL,QAAQ,EAAE,iBAAiB,EAAqB,OAAO,EAA2B,QAAQ,EAAE,QAAQ,EACrG,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC","sourcesContent":["export { createRecorder, type GameChange, type Recorder, type RecorderOptions } from \"./recorder.js\";\nexport {\n DEFAULT_MODEL_URL, DEFAULT_WASM_URL, type Detection, getOrientation, LITERT_VERSION, loadModel, type Model,\n type ModelOptions\n} from \"./vision/model.js\";\nexport { type PositionProblem, readPosition, type ReadPositionResult } from \"./vision/position.js\";\nexport { replay, type TimedMove, type Until } from \"./vision/replay.js\";\nexport { videoReady } from \"./vision/video.js\";\nexport { RecorderError, type RecorderErrorCode } from \"./errors.js\";\nexport { type GameUpdate, makePgn, makeUpdatePayload, newGame } from \"./game.js\";\nexport { GameBoard, makeBoard } from \"./chess/board.js\";\nexport { getMoveText } from \"./chess/moves.js\";\nexport { START_FEN } from \"./constants.js\";\nexport type { CornerName, Game, Roi } from \"./types.js\";\n// The chess rules, for apps that work with the moves themselves\nexport type { Color, Piece, Role, Square, SquareName } from \"./rules/board.js\";\nexport {\n FenError, IllegalSetupError, type IllegalSetup, makeFen, type Move, type Outcome, parseFen, Position, type Setup\n} from \"./rules/position.js\";\nexport { makeSan, makeUci, parseSan, parseUci } from \"./rules/san.js\";\n"]}
|