@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 CHANGED
@@ -1,151 +1,193 @@
1
1
  # @chesscam/recorder
2
2
 
3
- Records chess moves from a camera, video file or stream of a real board, entirely in the browser. It's the move
4
- detection from [ChessCam](https://www.chesscam.net), packaged on its own: a piece model (on the CPU, in a worker)
5
- and a move tracker that follows the game frame by frame and corrects misreads a few seconds on.
3
+ [![npm](https://img.shields.io/npm/v/@chesscam/recorder)](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
- // The board's region of the video, in video pixels
15
- roi: { x: 438, y: 384, width: 444, height: 280 },
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
- // End & Save
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", Event: "Club blitz" });
38
+ const pgn = recorder.pgn({ White: "Alice", Black: "Bob" });
24
39
  ```
25
40
 
26
- `examples/demo` is a working page: a camera or a video file, Start, and End & Save.
41
+ For a full working page with a camera or video file, see [`examples/demo`](examples/demo).
27
42
 
28
- ## Setup
43
+ ## Good to know
29
44
 
30
- **Bundler.** The CPU fallback runs in a worker, found with `new URL("./worker.js", import.meta.url)`, which Vite and
31
- webpack follow. With Vite, keep the package out of dependency pre-bundling, which would move its files:
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
- **The LiteRT.js runtime.** By default it loads from jsDelivr. Serve it yourself (copy
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
- **Cross-origin isolation.** Threads also need these headers on the page. `credentialless` still lets the model load
42
- from Hugging Face.
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
- ## Video sources
70
+ ```
71
+ Cross-Origin-Opener-Policy: same-origin
72
+ Cross-Origin-Embedder-Policy: credentialless
73
+ ```
57
74
 
58
- Anything a `<video>` can play and a page can read the pixels of: a camera (`getUserMedia`), a file (blob URL), a
59
- WebRTC `MediaStream`, or HLS/DASH served with CORS and played with `crossOrigin="anonymous"`. A cross-origin iframe
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
- Start recording when the game starts: frames of anything else (title cards, an empty board, hands setting up) can be
64
- read as moves.
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?): Promise<Model>`
83
+ ### `loadModel(options?)`
69
84
 
70
- Loads the model on the CPU in a worker, warmed up so the first frame is quick. A page has one model: calling again
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` | The `.tflite` model |
76
- | `wasmUrl` | Where LiteRT.js's runtime is served, ending in `/` |
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): Recorder`
92
+ ### `createRecorder(options)`
79
93
 
80
- Reads moves off the board until destroyed. It starts paused.
94
+ Creates a recorder. It starts paused; call `start()` to begin.
81
95
 
82
- | Option | |
96
+ | Option | What it does |
83
97
  |---|---|
84
- | `model`, `video` | Required |
85
- | `roi` | The board's region, in video pixels. Without one the whole frame is read, which only works when the board fills it. |
86
- | `start` | The start position as FEN |
87
- | `timed` | Time each move, for `[%emt]` in the PGN (default true). Times are in recording time, which a pause stops. |
88
- | `finalMovesOnly` | Report only moves that can't be taken back, about 1.5 s after they are played; for moves that go straight to an online game |
89
- | `maxFps` | Detections a second at normal speed (default 4) |
90
- | `onUpdate(game)` | The game whenever its moves change |
91
- | `onFps(fps \| null)` | The detection rate about once a second, and null once it stops |
92
- | `onOrientation(corners)` | The board's corners clockwise from the region's top left, when it turns |
93
- | `onDetect(detection)` | Each frame read |
94
- | `onError(error)` | A frame failed; detection carries on with the next |
95
-
96
- **A later update can change earlier moves.** When the tracker finds it misread a move (often a capture and recapture
97
- that a hand over the board seemed to show), it replaces it. Keep the whole game from each `onUpdate`, not just the
98
- newest move.
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()`, `pause()` | A pause counts as no time, for move times or for corrections |
103
- | `game`, `running` | The game so far, and whether frames are being read |
104
- | `setGame({ moves, start?, fromOpponent?, times? })` | Changes the game other than by the board, e.g. a takeback or an online opponent's move. The tracker starts again from it. |
105
- | `reset(start?)` | A new game |
106
- | `setRoi(roi)`, `setVideo(video)` | |
107
- | `pgn(headers?)` | The game as PGN, with the seven standard tags, any extra headers, and `[%emt]` move times |
108
- | `destroy()` | |
109
-
110
- A game has `moves` (numbered SAN, e.g. `1. e4 e5 2. Nf3`), `fen`, `start`, `lastMove` (UCI), `fromOpponent` and
111
- `times` (ms since recording started, per move).
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 on the board, averaged over three frames, to start a game somewhere other than the start position.
116
- Gives `{ ok: true, fen }`, or `{ ok: false, problem }` when the board isn't a legal position (`"kings"`,
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` has a `code` for the app to show in its own words:
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` | No model, or the video has no frame |
126
- | `worker-stopped` | The CPU worker crashed, e.g. out of memory |
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` | Anything else; see `message` |
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 try the demo, run `npm install && npm run dev` in `examples/demo`. Add `?video=<url>&t=<start s>&roi=x,y,w,h` to play a
139
- video, and `&wasm=<url>` to serve the runtime from the page's origin.
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 are its own (`src/rules`). Its dependencies have their own licences:
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 loaded at runtime, not
150
- shipped in the package, and is under the PolyForm Noncommercial License 1.0.0. Commercial use needs a separate
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
@@ -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,EAC9F,KAAK,KAAK,EAAE,KAAK,YAAY,EAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,eAAe,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC"}
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,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,SAAS,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,\n type Model, type ModelOptions\n} from \"./vision/model.js\";\nexport { type PositionProblem, readPosition, type ReadPositionResult } from \"./vision/position.js\";\nexport { videoReady } from \"./vision/video.js\";\nexport { RecorderError, type RecorderErrorCode } from \"./errors.js\";\nexport { makePgn, newGame } from \"./game.js\";\nexport { GameBoard, makeBoard } from \"./chess/board.js\";\nexport { START_FEN } from \"./constants.js\";\nexport type { CornerName, Game, Roi } from \"./types.js\";\n"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chesscam/recorder",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Record chess games from a camera or video of a real board, in the browser. Moves as SAN/PGN, on-device.",
5
5
  "keywords": [
6
6
  "chess",