chesspal 0.2.4 → 0.3.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 +11 -12
- package/dist/cli/cli.js +83 -3
- package/dist/cli/command-line.js +26 -9
- package/dist/cli/icon.d.ts +25 -0
- package/dist/cli/icon.js +80 -0
- package/dist/commands/agents.d.ts +19 -2
- package/dist/commands/agents.js +39 -13
- package/dist/commands/ai-say.js +3 -4
- package/dist/commands/analyse-game.d.ts +4 -0
- package/dist/commands/analyse-game.js +46 -13
- package/dist/commands/annotate-game.d.ts +2 -0
- package/dist/commands/annotate-game.js +40 -18
- package/dist/commands/answer-lines.d.ts +2 -0
- package/dist/commands/answer-lines.js +13 -3
- package/dist/commands/coach.js +4 -2
- package/dist/commands/context.d.ts +9 -1
- package/dist/commands/context.js +5 -0
- package/dist/commands/delete-review.js +9 -5
- package/dist/commands/game-summary.d.ts +2 -0
- package/dist/commands/game-summary.js +4 -0
- package/dist/commands/index.d.ts +3 -2
- package/dist/commands/index.js +7 -4
- package/dist/commands/init.js +2 -4
- package/dist/commands/instructions.d.ts +9 -0
- package/dist/commands/instructions.js +30 -0
- package/dist/commands/lead-input.d.ts +1 -1
- package/dist/commands/lead-input.js +7 -4
- package/dist/commands/page-link.js +6 -5
- package/dist/commands/past-games.d.ts +1 -1
- package/dist/commands/past-games.js +12 -5
- package/dist/commands/player-move.js +2 -3
- package/dist/commands/profiles.d.ts +23 -9
- package/dist/commands/profiles.js +76 -35
- package/dist/commands/ratings.d.ts +8 -0
- package/dist/commands/ratings.js +45 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +3 -2
- package/dist/commands/say.d.ts +1 -1
- package/dist/commands/say.js +1 -1
- package/dist/commands/status.d.ts +3 -1
- package/dist/commands/status.js +46 -27
- package/dist/commands/study.js +8 -5
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/review.d.ts +2 -0
- package/dist/core/review.js +5 -1
- package/dist/games/ai-model.d.ts +4 -2
- package/dist/games/ai-model.js +10 -1
- package/dist/games/analysis.d.ts +26 -3
- package/dist/games/analysis.js +41 -6
- package/dist/games/archive.d.ts +6 -3
- package/dist/games/archive.js +13 -5
- package/dist/games/current-game.d.ts +10 -1
- package/dist/games/current-game.js +49 -15
- package/dist/games/files.d.ts +5 -0
- package/dist/games/files.js +42 -1
- package/dist/games/index.d.ts +3 -3
- package/dist/games/index.js +3 -3
- package/dist/home/data-folder.d.ts +2 -0
- package/dist/home/data-folder.js +5 -1
- package/dist/home/index.d.ts +1 -1
- package/dist/home/index.js +1 -1
- package/dist/host-files/agents.d.ts +2 -2
- package/dist/host-files/agents.js +12 -6
- package/dist/host-files/host-files.d.ts +5 -3
- package/dist/host-files/host-files.js +15 -8
- package/dist/host-files/index.d.ts +2 -2
- package/dist/host-files/index.js +2 -2
- package/dist/page/app/app.js +29 -16
- package/dist/page/app/title.js +17 -0
- package/dist/page/avatars/torretta.js +4 -4
- package/dist/page/dialogs/analyses.js +4 -4
- package/dist/page/dialogs/dialog.js +4 -1
- package/dist/page/dialogs/dialogs.css +84 -10
- package/dist/page/dialogs/profiles.js +95 -42
- package/dist/page/game/column.js +5 -1
- package/dist/page/game/plies.js +7 -4
- package/dist/page/guide/blocks.js +67 -0
- package/dist/page/guide/en.md +66 -0
- package/dist/page/guide/foot.js +18 -0
- package/dist/page/guide/guide.css +200 -0
- package/dist/page/guide/guide.js +89 -0
- package/dist/page/guide/it.md +66 -0
- package/dist/page/guide/markdown.js +67 -0
- package/dist/page/helps/helps.css +7 -10
- package/dist/page/layout/favicon.svg +13 -0
- package/dist/page/layout/header.js +3 -3
- package/dist/page/layout/icon.icns +0 -0
- package/dist/page/layout/icon.ico +0 -0
- package/dist/page/layout/icon.png +0 -0
- package/dist/page/layout/layout.css +111 -4
- package/dist/page/layout/window.js +45 -0
- package/dist/page/main.js +4 -1
- package/dist/page/players/card.js +1 -1
- package/dist/page/profile/profile.js +14 -9
- package/dist/page/profile/settings.js +9 -0
- package/dist/page/style.css +2 -0
- package/dist/page/table/opener.css +65 -0
- package/dist/page/table/opener.js +59 -0
- package/dist/page/talk/chat.css +10 -3
- package/dist/page/talk/talk.css +38 -26
- package/dist/page/talk/talk.js +4 -2
- package/dist/page/text.js +83 -17
- package/dist/page/theme.css +7 -0
- package/dist/profiles/index.d.ts +2 -1
- package/dist/profiles/index.js +2 -1
- package/dist/profiles/profiles.d.ts +10 -15
- package/dist/profiles/profiles.js +30 -15
- package/dist/profiles/ratings.d.ts +35 -0
- package/dist/profiles/ratings.js +61 -0
- package/dist/server/bell.d.ts +24 -3
- package/dist/server/bell.js +84 -10
- package/dist/server/caller.d.ts +2 -1
- package/dist/server/caller.js +3 -2
- package/dist/server/mcp.d.ts +7 -3
- package/dist/server/mcp.js +100 -52
- package/dist/server/page/analyses.d.ts +2 -0
- package/dist/server/page/analyses.js +20 -5
- package/dist/server/page/attach.d.ts +4 -0
- package/dist/server/page/attach.js +112 -0
- package/dist/server/page/chat.d.ts +60 -0
- package/dist/server/page/chat.js +79 -0
- package/dist/server/page/events.d.ts +7 -0
- package/dist/server/page/events.js +20 -0
- package/dist/server/page/files.js +10 -1
- package/dist/server/page/game.js +43 -23
- package/dist/server/page/page.d.ts +47 -7
- package/dist/server/page/page.js +69 -48
- package/dist/server/page/profiles.d.ts +5 -0
- package/dist/server/page/profiles.js +26 -10
- package/dist/server/page/table.d.ts +39 -0
- package/dist/server/page/table.js +102 -0
- package/dist/server/table/browser.d.ts +6 -0
- package/dist/server/table/browser.js +48 -0
- package/dist/server/table/hosts/chats.d.ts +7 -0
- package/dist/server/table/hosts/chats.js +31 -0
- package/dist/server/table/hosts/find.d.ts +2 -0
- package/dist/server/table/hosts/find.js +20 -0
- package/dist/server/table/hosts/hosts.d.ts +34 -0
- package/dist/server/table/hosts/hosts.js +49 -0
- package/dist/server/table/hosts/index.d.ts +3 -0
- package/dist/server/table/hosts/index.js +4 -0
- package/dist/server/table/hosts/play.d.ts +25 -0
- package/dist/server/table/hosts/play.js +152 -0
- package/dist/server/table/hosts/seat.d.ts +25 -0
- package/dist/server/table/hosts/seat.js +74 -0
- package/dist/server/table/hosts/terminal.d.ts +6 -0
- package/dist/server/table/hosts/terminal.js +63 -0
- package/dist/server/table/icon/index.d.ts +32 -0
- package/dist/server/table/icon/index.js +83 -0
- package/dist/server/table/icon/linux.d.ts +5 -0
- package/dist/server/table/icon/linux.js +47 -0
- package/dist/server/table/icon/macos.d.ts +3 -0
- package/dist/server/table/icon/macos.js +38 -0
- package/dist/server/table/icon/windows.d.ts +3 -0
- package/dist/server/table/icon/windows.js +48 -0
- package/dist/server/table/index.d.ts +6 -0
- package/dist/server/table/index.js +7 -0
- package/dist/server/table/launch.d.ts +19 -0
- package/dist/server/table/launch.js +78 -0
- package/dist/server/table/link.d.ts +10 -0
- package/dist/server/table/link.js +230 -0
- package/dist/server/table/table.d.ts +35 -0
- package/dist/server/table/table.js +94 -0
- package/dist/sites/chesscom.d.ts +4 -0
- package/dist/sites/chesscom.js +25 -0
- package/dist/sites/choice.d.ts +17 -0
- package/dist/sites/choice.js +13 -0
- package/dist/sites/index.d.ts +2 -0
- package/dist/sites/index.js +4 -0
- package/dist/sites/lichess.d.ts +4 -0
- package/dist/sites/lichess.js +32 -0
- package/dist/sites/request.d.ts +11 -0
- package/dist/sites/request.js +15 -0
- package/dist/sites/sites.d.ts +6 -0
- package/dist/sites/sites.js +33 -0
- package/instructions/analysis.md +1 -1
- package/instructions/analyst-agent.md +1 -1
- package/instructions/coach.md +2 -1
- package/instructions/opponent.md +6 -6
- package/instructions/ponder.md +1 -0
- package/instructions/start.md +1 -0
- package/package.json +1 -1
package/dist/page/text.js
CHANGED
|
@@ -108,6 +108,7 @@ const english = {
|
|
|
108
108
|
talk: "The talk",
|
|
109
109
|
move: "Move",
|
|
110
110
|
draw: "draw",
|
|
111
|
+
aborted: "Game aborted",
|
|
111
112
|
checkmate: "checkmate",
|
|
112
113
|
ended: {
|
|
113
114
|
checkmate: "checkmate",
|
|
@@ -132,6 +133,18 @@ const english = {
|
|
|
132
133
|
thinking: { move: "Thinking about its move", ponder: "Thinking while you play" },
|
|
133
134
|
asleep: (name) => `${name} is not listening: write a message, such as "hi", in the window where you opened ${name}, not here. Its clock waits until it answers.`,
|
|
134
135
|
asleepInAnalysis: (name) => `${name} is not listening: write to it in the window where you opened it, not here.`,
|
|
136
|
+
table: {
|
|
137
|
+
label: "Open a chat",
|
|
138
|
+
noChat: (name) => `To play with ${name}, open its chat: it starts in a window of its own, to keep open.`,
|
|
139
|
+
install: (name) => `To play with ${name}, install Claude Code or Codex, then open this page again.`,
|
|
140
|
+
model: "Model",
|
|
141
|
+
effort: "Effort",
|
|
142
|
+
asHost: (host) => `as set in ${host}`,
|
|
143
|
+
opening: "Opening the chat… In the terminal, if it asks whether you trust the folder, choose Yes with the arrow keys and press Enter.",
|
|
144
|
+
again: (name) => `${name} starts again with a clean chat for the new game…`,
|
|
145
|
+
failed: "The terminal did not open: type the command below in a terminal.",
|
|
146
|
+
typeIt: "Or type in a terminal:",
|
|
147
|
+
},
|
|
135
148
|
commandHere: (name) => `/clear and the other commands go in the window where you opened ${name}, not here.`,
|
|
136
149
|
refused: {
|
|
137
150
|
"no-game": () => "There is no game going on: press + to start one, or ask for one in the chat.",
|
|
@@ -187,14 +200,15 @@ const english = {
|
|
|
187
200
|
learning: ["I've just learned", "I know the rules, little more"],
|
|
188
201
|
casual: ["I play now and then", "With friends or on my phone"],
|
|
189
202
|
regular: ["I play often", "Online or at a club"],
|
|
190
|
-
tournament: ["I play tournaments", "
|
|
203
|
+
tournament: ["I play tournaments", "At a club, with a rating"],
|
|
191
204
|
},
|
|
192
|
-
rating: "Do you have a rating?",
|
|
193
205
|
optional: "optional",
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
privacy: "We only read your public rating. No password.",
|
|
207
|
+
speeds: { rapid: "rapid", classical: "classical", blitz: "blitz" },
|
|
208
|
+
ratingTitle: (speed, played) => (played === undefined ? speed : `${speed} · last game ${played}`),
|
|
209
|
+
notFound: (site) => `${site} has no player of this name`,
|
|
210
|
+
unreached: (site) => `${site} isn't answering: we'll try again later`,
|
|
211
|
+
pending: "Reading your rating…",
|
|
198
212
|
start: "Start playing",
|
|
199
213
|
create: "Done",
|
|
200
214
|
newProfile: "Add a player",
|
|
@@ -202,12 +216,17 @@ const english = {
|
|
|
202
216
|
eachOwn: "Each player has their own games and settings.",
|
|
203
217
|
changePlayer: "Change player",
|
|
204
218
|
back: "Back",
|
|
205
|
-
|
|
206
|
-
|
|
219
|
+
yourProfile: "Your profile",
|
|
220
|
+
editProfile: "Edit your profile",
|
|
221
|
+
yourPicture: "Your picture: edit your profile",
|
|
207
222
|
avatars: { "short-hair": "Short hair", bun: "Bun", glasses: "Glasses", beanie: "Beanie", beard: "Beard", curls: "Curls", moustache: "Moustache", "silver-bob": "Silver bob", cat: "Cat", owl: "Owl" },
|
|
208
223
|
whilePlaying: "Finish the game, or resign, before changing player.",
|
|
224
|
+
accounts: "Do you play on Lichess, Chess.com or in FIDE tournaments?",
|
|
209
225
|
username: "your username",
|
|
210
226
|
accountTitle: "With your name there, a game you load from there knows which side is yours.",
|
|
227
|
+
fidePlaceholder: "your rating",
|
|
228
|
+
fideTitle: "Your FIDE rating, as FIDE lists it: Chesspal cannot read it by itself.",
|
|
229
|
+
fideRange: (lowest, highest) => `A FIDE rating goes from ${lowest} to ${highest}`,
|
|
211
230
|
badImage: "That picture won't open: try another photo (the ones from a phone usually work).",
|
|
212
231
|
},
|
|
213
232
|
start: {
|
|
@@ -343,6 +362,20 @@ const english = {
|
|
|
343
362
|
control: "How many of White's pieces and of Black's reach the square",
|
|
344
363
|
ends: { crown: "Won", mate: "Checkmated", flag: "Resigned", half: "Draw, half a point each" },
|
|
345
364
|
},
|
|
365
|
+
guide: {
|
|
366
|
+
title: "Guide",
|
|
367
|
+
contents: "Contents",
|
|
368
|
+
onGitHub: "Chesspal on GitHub",
|
|
369
|
+
onNpm: "Chesspal on npm",
|
|
370
|
+
support: "Support Chesspal",
|
|
371
|
+
credits: "Credits",
|
|
372
|
+
creditLines: [
|
|
373
|
+
"The rules: [chess.js](https://github.com/jhlywa/chess.js), under the BSD 2-Clause license.",
|
|
374
|
+
"The openings' names: Lichess's [chess-openings](https://github.com/lichess-org/chess-openings), under CC0.",
|
|
375
|
+
"The pieces: Nitida, Torretta and Capablanca are Chesspal's own, the last after the diagrams of Capablanca's *Chess Fundamentals* (1921), under CC BY 4.0: [their credits](https://github.com/stefanocaronia/chesspal/blob/main/src/page/pieces/CREDITS.md).",
|
|
376
|
+
"The sounds: Chesspal's own.",
|
|
377
|
+
],
|
|
378
|
+
},
|
|
346
379
|
};
|
|
347
380
|
const italian = {
|
|
348
381
|
language: "it",
|
|
@@ -447,6 +480,7 @@ const italian = {
|
|
|
447
480
|
talk: "Conversazione",
|
|
448
481
|
move: "Mossa",
|
|
449
482
|
draw: "patta",
|
|
483
|
+
aborted: "Partita annullata",
|
|
450
484
|
checkmate: "scacco matto",
|
|
451
485
|
ended: {
|
|
452
486
|
checkmate: "scacco matto",
|
|
@@ -471,6 +505,18 @@ const italian = {
|
|
|
471
505
|
thinking: { move: "Sta pensando alla sua mossa", ponder: "Pensa mentre giochi tu" },
|
|
472
506
|
asleep: (name) => `${name} non ti sta ascoltando: scrivi un messaggio, per esempio "ciao", nella finestra dove hai aperto ${name}, non qui. Il suo orologio aspetta che risponda.`,
|
|
473
507
|
asleepInAnalysis: (name) => `${name} non ti sta ascoltando: scrivigli nella finestra dove l'hai aperto, non qui.`,
|
|
508
|
+
table: {
|
|
509
|
+
label: "Apri una chat",
|
|
510
|
+
noChat: (name) => `Per giocare con ${name}, apri la sua chat: parte in una finestra a parte, da tenere aperta.`,
|
|
511
|
+
install: (name) => `Per giocare con ${name}, installa Claude Code o Codex, poi apri di nuovo questa pagina.`,
|
|
512
|
+
model: "Modello",
|
|
513
|
+
effort: "Ragionamento",
|
|
514
|
+
asHost: (host) => `come è impostato in ${host}`,
|
|
515
|
+
opening: "Apro la chat… Nel terminale, se ti chiede se ti fidi della cartella, scegli Yes con le frecce e premi Invio.",
|
|
516
|
+
again: (name) => `${name} riparte con una chat pulita per la nuova partita…`,
|
|
517
|
+
failed: "Il terminale non si è aperto: scrivi il comando qui sotto in un terminale.",
|
|
518
|
+
typeIt: "Oppure scrivi in un terminale:",
|
|
519
|
+
},
|
|
474
520
|
commandHere: (name) => `/clear e gli altri comandi vanno scritti nella finestra dove hai aperto ${name}, non qui.`,
|
|
475
521
|
refused: {
|
|
476
522
|
"no-game": () => "Nessuna partita in corso: premi + per iniziarne una, o chiedila nella chat.",
|
|
@@ -492,7 +538,7 @@ const italian = {
|
|
|
492
538
|
piece: (letter, side) => `${italianNames[letter]} ${side === "white" ? (letter === "Q" || letter === "R" ? "bianca" : "bianco") : (letter === "Q" || letter === "R" ? "nera" : "nero")}`,
|
|
493
539
|
taken: (pieces, ahead) => `Pezzi presi: ${pieces.length === 0 ? "nessuno" : pieces.join(", ")}${ahead > 0 ? `; avanti di ${ahead}` : ""}`,
|
|
494
540
|
writeTo: (name) => `Scrivi a ${name}…`,
|
|
495
|
-
askAbout: (name) => `Chiedi a ${name} di questa partita…`,
|
|
541
|
+
askAbout: (name) => `Chiedi ${name === "AI" ? "all'AI" : `a ${name}`} di questa partita…`,
|
|
496
542
|
goTo: (move) => `Vai a ${move}`,
|
|
497
543
|
readBy: (name) => `Letto da ${name}`,
|
|
498
544
|
wrote: (name) => `${name} ha scritto`,
|
|
@@ -527,14 +573,15 @@ const italian = {
|
|
|
527
573
|
learning: ["Ho appena imparato", "Conosco le regole, poco di più"],
|
|
528
574
|
casual: ["Gioco ogni tanto", "Con amici o sul telefono"],
|
|
529
575
|
regular: ["Gioco spesso", "Online o al circolo"],
|
|
530
|
-
tournament: ["Gioco a torneo", "
|
|
576
|
+
tournament: ["Gioco a torneo", "Al circolo, con punteggio"],
|
|
531
577
|
},
|
|
532
|
-
rating: "Hai un punteggio?",
|
|
533
578
|
optional: "facoltativo",
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
579
|
+
privacy: "Leggiamo solo il tuo punteggio pubblico. Niente password.",
|
|
580
|
+
speeds: { rapid: "rapid", classical: "classica", blitz: "blitz" },
|
|
581
|
+
ratingTitle: (speed, played) => (played === undefined ? speed : `${speed} · ultima partita il ${played.split("-").reverse().join("/")}`),
|
|
582
|
+
notFound: (site) => `Su ${site} non trovo questo nome`,
|
|
583
|
+
unreached: (site) => `${site} non risponde: riproviamo più tardi`,
|
|
584
|
+
pending: "Leggo il tuo punteggio…",
|
|
538
585
|
start: "Inizia a giocare",
|
|
539
586
|
create: "Fatto",
|
|
540
587
|
newProfile: "Aggiungi un giocatore",
|
|
@@ -542,12 +589,17 @@ const italian = {
|
|
|
542
589
|
eachOwn: "Ognuno ha le sue partite e le sue impostazioni.",
|
|
543
590
|
changePlayer: "Cambia giocatore",
|
|
544
591
|
back: "Indietro",
|
|
545
|
-
|
|
546
|
-
|
|
592
|
+
yourProfile: "Il tuo profilo",
|
|
593
|
+
editProfile: "Modifica il profilo",
|
|
594
|
+
yourPicture: "La tua immagine: modifica il profilo",
|
|
547
595
|
avatars: { "short-hair": "Capelli corti", bun: "Chignon", glasses: "Occhiali", beanie: "Berretto", beard: "Barba", curls: "Ricci", moustache: "Baffi", "silver-bob": "Caschetto grigio", cat: "Gatto", owl: "Gufo" },
|
|
548
596
|
whilePlaying: "Finisci la partita, o abbandona, prima di cambiare giocatore.",
|
|
597
|
+
accounts: "Giochi su Lichess, Chess.com o nei tornei FIDE?",
|
|
549
598
|
username: "il tuo nome utente",
|
|
550
599
|
accountTitle: "Col tuo nome lì, una partita che carichi da lì sa qual è la tua parte.",
|
|
600
|
+
fidePlaceholder: "il tuo punteggio",
|
|
601
|
+
fideTitle: "Il tuo punteggio FIDE, come lo trovi sul sito della FIDE: Chesspal non può leggerlo da sé.",
|
|
602
|
+
fideRange: (lowest, highest) => `Un punteggio FIDE va da ${lowest} a ${highest}`,
|
|
551
603
|
badImage: "Questa immagine non si apre: prova con un'altra foto (quelle del telefono di solito vanno bene).",
|
|
552
604
|
},
|
|
553
605
|
start: {
|
|
@@ -683,6 +735,20 @@ const italian = {
|
|
|
683
735
|
control: "Quanti pezzi del Bianco e del Nero raggiungono la casa",
|
|
684
736
|
ends: { crown: "Ha vinto", mate: "Scacco matto", flag: "Ha abbandonato", half: "Patta, mezzo punto a testa" },
|
|
685
737
|
},
|
|
738
|
+
guide: {
|
|
739
|
+
title: "Guida",
|
|
740
|
+
contents: "Indice",
|
|
741
|
+
onGitHub: "Chesspal su GitHub",
|
|
742
|
+
onNpm: "Chesspal su npm",
|
|
743
|
+
support: "Sostieni Chesspal",
|
|
744
|
+
credits: "Crediti",
|
|
745
|
+
creditLines: [
|
|
746
|
+
"Le regole: [chess.js](https://github.com/jhlywa/chess.js), con licenza BSD 2-Clause.",
|
|
747
|
+
"I nomi delle aperture: [chess-openings](https://github.com/lichess-org/chess-openings) di Lichess, con licenza CC0.",
|
|
748
|
+
"I pezzi: Nitida, Torretta e Capablanca sono di Chesspal, l'ultimo dai diagrammi di *Chess Fundamentals* di Capablanca (1921), con licenza CC BY 4.0: [i loro crediti](https://github.com/stefanocaronia/chesspal/blob/main/src/page/pieces/CREDITS.md).",
|
|
749
|
+
"I suoni: di Chesspal.",
|
|
750
|
+
],
|
|
751
|
+
},
|
|
686
752
|
};
|
|
687
753
|
// How each piece moves, said when it was taken where it cannot go (Anatolij's words).
|
|
688
754
|
const englishMoves = {
|
package/dist/page/theme.css
CHANGED
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
--help-on: #fff;
|
|
34
34
|
--help-line: rgba(17, 22, 29, 0.78);
|
|
35
35
|
--help-halo: rgba(255, 255, 255, 0.85);
|
|
36
|
+
/* The control map's veils (Zoe's variant A): grey over the board, sand where White alone reaches, slate where Black alone does */
|
|
37
|
+
--map-veil: rgba(160, 166, 176, 0.5);
|
|
38
|
+
--map-white: rgba(242, 233, 210, 0.78);
|
|
39
|
+
--map-black: rgba(56, 66, 84, 0.5);
|
|
36
40
|
--torretta-body: #6d9fd1;
|
|
37
41
|
--torretta-shade: #4b7cb0;
|
|
38
42
|
--torretta-screen: #16263a;
|
|
@@ -102,6 +106,9 @@
|
|
|
102
106
|
--badge-blunder: #d93434;
|
|
103
107
|
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 22px rgba(0, 0, 0, 0.25);
|
|
104
108
|
--board-shadow: var(--shadow);
|
|
109
|
+
--map-veil: rgba(112, 120, 132, 0.66);
|
|
110
|
+
--map-white: rgba(226, 214, 188, 0.64);
|
|
111
|
+
--map-black: rgba(28, 35, 48, 0.7);
|
|
105
112
|
color-scheme: dark;
|
|
106
113
|
}
|
|
107
114
|
|
package/dist/profiles/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { type ImageType, imageTypeOf, imageTypes, ownImage, ownImageStamp, removeOwnImage, saveOwnImage, sentImageLimit } from "./avatar.ts";
|
|
2
|
-
export { type Accounts, activateProfile, activeProfile, createProfile, type DrawnAvatar, drawnAvatars, ensureProfile, gameFolder, gameFolders, type Level, levels, listProfiles, nameLimit, type PlayerProfile, type ProfileFields, profileFolder,
|
|
2
|
+
export { type Accounts, activateProfile, activeProfile, createProfile, type DrawnAvatar, drawnAvatars, ensureProfile, fideHighest, fideLowest, gameFolder, gameFolders, type Level, levels, listProfiles, nameLimit, type PlayerProfile, type ProfileFields, profileFolder, updateProfile, } from "./profiles.ts";
|
|
3
|
+
export { type Rating, type RatingSite, ratingSites, saveReadings, type SiteReading, type SiteReadings, type Speed, speeds } from "./ratings.ts";
|
|
3
4
|
export { helpsIn, languageIn, type PageSettings, profileSettings, saveProfileSettings, settingsOf } from "./settings.ts";
|
package/dist/profiles/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// The player's profiles (D-23): who plays, their games, their picture and their page settings, each in profiles/<id>/ of the data folder (D-71). Below the
|
|
2
2
|
// commands, beside the games.
|
|
3
3
|
export { imageTypeOf, imageTypes, ownImage, ownImageStamp, removeOwnImage, saveOwnImage, sentImageLimit } from "./avatar.js";
|
|
4
|
-
export { activateProfile, activeProfile, createProfile, drawnAvatars, ensureProfile, gameFolder, gameFolders, levels, listProfiles, nameLimit, profileFolder,
|
|
4
|
+
export { activateProfile, activeProfile, createProfile, drawnAvatars, ensureProfile, fideHighest, fideLowest, gameFolder, gameFolders, levels, listProfiles, nameLimit, profileFolder, updateProfile, } from "./profiles.js";
|
|
5
|
+
export { ratingSites, saveReadings, speeds } from "./ratings.js";
|
|
5
6
|
export { helpsIn, languageIn, profileSettings, saveProfileSettings, settingsOf } from "./settings.js";
|
|
@@ -1,30 +1,21 @@
|
|
|
1
|
+
import { type Rating, type SiteReadings } from "./ratings.ts";
|
|
1
2
|
/** How the player says they play, from who has just learned to who plays tournaments. */
|
|
2
3
|
export declare const levels: readonly ["learning", "casual", "regular", "tournament"];
|
|
3
4
|
export type Level = (typeof levels)[number];
|
|
4
|
-
/** Where a rating comes from. */
|
|
5
|
-
export declare const ratingSites: readonly ["lichess", "chess.com", "fide"];
|
|
6
|
-
export type RatingSite = (typeof ratingSites)[number];
|
|
7
|
-
/** The lowest and highest rating a profile takes. */
|
|
8
|
-
export declare const ratingLimits: {
|
|
9
|
-
readonly lowest: 100;
|
|
10
|
-
readonly highest: 3500;
|
|
11
|
-
};
|
|
12
5
|
/** The longest name a profile keeps. */
|
|
13
6
|
export declare const nameLimit = 40;
|
|
14
7
|
/** The avatars Zoe drew, for a player without an image of their own (D-45). */
|
|
15
8
|
export declare const drawnAvatars: readonly ["short-hair", "bun", "glasses", "beanie", "beard", "curls", "moustache", "silver-bob", "cat", "owl"];
|
|
16
9
|
export type DrawnAvatar = (typeof drawnAvatars)[number];
|
|
17
|
-
/**
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
/** What the player tells of themselves: their name, how they play, their rating, the avatar they chose. */
|
|
10
|
+
/** The FIDE ratings Chesspal takes: whole numbers from FIDE's lowest, 1400 since 2024, to 3000. */
|
|
11
|
+
export declare const fideLowest = 1400;
|
|
12
|
+
export declare const fideHighest = 3000;
|
|
13
|
+
/** What the player tells of themselves: their name, how they play, the avatar they chose, their FIDE rating, typed since FIDE has no API (D-106). */
|
|
23
14
|
export interface ProfileFields {
|
|
24
15
|
readonly name: string;
|
|
25
16
|
readonly level?: Level;
|
|
26
|
-
readonly rating?: Rating;
|
|
27
17
|
readonly avatar?: DrawnAvatar;
|
|
18
|
+
readonly fide?: number;
|
|
28
19
|
}
|
|
29
20
|
/** The player's names on the sites they play on. */
|
|
30
21
|
export interface Accounts {
|
|
@@ -38,6 +29,10 @@ export interface PlayerProfile extends ProfileFields {
|
|
|
38
29
|
readonly new?: true;
|
|
39
30
|
/** The player's names on Lichess and chess.com, written in profile.md by hand or from the page, by which the games they load are theirs (D-93). */
|
|
40
31
|
readonly accounts?: Accounts;
|
|
32
|
+
/** What Lichess and chess.com said of those names, the last time Chesspal asked (D-105). */
|
|
33
|
+
readonly sites?: SiteReadings;
|
|
34
|
+
/** The rating that counts, of the names the profile has now: of two, the one the sites are surer of (D-106). */
|
|
35
|
+
readonly rating?: Rating;
|
|
41
36
|
}
|
|
42
37
|
/** What may change in a profile: what the player tells of themselves, and their names on the sites, each one written whole. */
|
|
43
38
|
export type ProfileChanges = Partial<ProfileFields> & {
|
|
@@ -4,16 +4,16 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "node:f
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { makeFolder, renameOver, writeAtomically } from "../games/index.js";
|
|
6
6
|
import { readFrontMatter, writeFrontMatter } from "./front-matter.js";
|
|
7
|
+
import { savedReadings } from "./ratings.js";
|
|
7
8
|
/** How the player says they play, from who has just learned to who plays tournaments. */
|
|
8
9
|
export const levels = ["learning", "casual", "regular", "tournament"];
|
|
9
|
-
/** Where a rating comes from. */
|
|
10
|
-
export const ratingSites = ["lichess", "chess.com", "fide"];
|
|
11
|
-
/** The lowest and highest rating a profile takes. */
|
|
12
|
-
export const ratingLimits = { lowest: 100, highest: 3500 };
|
|
13
10
|
/** The longest name a profile keeps. */
|
|
14
11
|
export const nameLimit = 40;
|
|
15
12
|
/** The avatars Zoe drew, for a player without an image of their own (D-45). */
|
|
16
13
|
export const drawnAvatars = ["short-hair", "bun", "glasses", "beanie", "beard", "curls", "moustache", "silver-bob", "cat", "owl"];
|
|
14
|
+
/** The FIDE ratings Chesspal takes: whole numbers from FIDE's lowest, 1400 since 2024, to 3000. */
|
|
15
|
+
export const fideLowest = 1400;
|
|
16
|
+
export const fideHighest = 3000;
|
|
17
17
|
/** The profiles in the data folder, by their folders' names. */
|
|
18
18
|
export function listProfiles(root) {
|
|
19
19
|
return profileIds(root).map((id) => readProfile(root, id));
|
|
@@ -47,9 +47,9 @@ export function profileFolder(root, id) {
|
|
|
47
47
|
export function createProfile(root, fields, use = true) {
|
|
48
48
|
const id = activeId(root);
|
|
49
49
|
if (id !== undefined && readProfile(root, id).new === true) {
|
|
50
|
-
const { level,
|
|
50
|
+
const { level, avatar, fide } = fields;
|
|
51
51
|
const named = renamed(root, id, fields.name);
|
|
52
|
-
writeProfile(root, named, { name: fields.name, level,
|
|
52
|
+
writeProfile(root, named, { name: fields.name, level, avatar, fide, new: false });
|
|
53
53
|
return readProfile(root, named);
|
|
54
54
|
}
|
|
55
55
|
let made = freeId(root, slugOf(fields.name));
|
|
@@ -172,22 +172,38 @@ function writeActive(root, id) {
|
|
|
172
172
|
}
|
|
173
173
|
function readProfile(root, id) {
|
|
174
174
|
const { fields } = readFrontMatter(profileText(root, id));
|
|
175
|
-
const value = Number(fields.rating);
|
|
176
|
-
const site = oneOf(ratingSites, fields.site);
|
|
177
175
|
const level = oneOf(levels, fields.level);
|
|
178
176
|
const avatar = oneOf(drawnAvatars, fields.avatar);
|
|
179
|
-
const
|
|
177
|
+
const fide = fideIn(fields.fide);
|
|
178
|
+
const { accounts } = accountsIn(fields);
|
|
179
|
+
const sites = savedReadings(profileFolder(root, id));
|
|
180
|
+
const rating = sites === undefined ? undefined : surest([ofAccount(sites.lichess, accounts?.lichess), ofAccount(sites.chesscom, accounts?.chesscom)]);
|
|
180
181
|
// A name written by hand is cut as the page and the CLI cut it: the AI reads it.
|
|
181
182
|
return {
|
|
182
183
|
id,
|
|
183
184
|
name: fields.name?.trim().slice(0, nameLimit).trim() || id,
|
|
184
185
|
...(level === undefined ? {} : { level }),
|
|
185
|
-
...(rated ? { rating: { value, site } } : {}),
|
|
186
186
|
...(avatar === undefined ? {} : { avatar }),
|
|
187
|
+
...(fide === undefined ? {} : { fide }),
|
|
187
188
|
...(fields.new === "true" ? { new: true } : {}),
|
|
188
|
-
...
|
|
189
|
+
...(accounts === undefined ? {} : { accounts }),
|
|
190
|
+
...(sites === undefined ? {} : { sites }),
|
|
191
|
+
...(rating === undefined ? {} : { rating }),
|
|
189
192
|
};
|
|
190
193
|
}
|
|
194
|
+
/** A site's rating, when it was read for the name the profile has there now. */
|
|
195
|
+
function ofAccount(reading, account) {
|
|
196
|
+
return account !== undefined && reading?.account.toLowerCase() === account.toLowerCase() ? reading.rating : undefined;
|
|
197
|
+
}
|
|
198
|
+
/** Of the ratings, the one its site is surest of: the lowest RD (D-106). */
|
|
199
|
+
function surest(ratings) {
|
|
200
|
+
return ratings.reduce((best, one) => (one !== undefined && (best === undefined || one.deviation < best.deviation) ? one : best), undefined);
|
|
201
|
+
}
|
|
202
|
+
/** The FIDE rating of profile.md's fide line: none when it is not a whole number in FIDE's range, as one written by hand may not be. */
|
|
203
|
+
function fideIn(text) {
|
|
204
|
+
const fide = Number(text?.trim());
|
|
205
|
+
return Number.isInteger(fide) && fide >= fideLowest && fide <= fideHighest ? fide : undefined;
|
|
206
|
+
}
|
|
191
207
|
/** The player's names on the sites, from profile.md's lichess and chesscom lines; none without either. */
|
|
192
208
|
function accountsIn(fields) {
|
|
193
209
|
const named = (site) => fields[site]?.trim().slice(0, nameLimit).trim() || undefined;
|
|
@@ -208,13 +224,12 @@ function writeProfile(root, id, changes) {
|
|
|
208
224
|
if ("level" in changes) {
|
|
209
225
|
written.level = changes.level;
|
|
210
226
|
}
|
|
211
|
-
if ("rating" in changes) {
|
|
212
|
-
written.rating = changes.rating?.value;
|
|
213
|
-
written.site = changes.rating?.site;
|
|
214
|
-
}
|
|
215
227
|
if ("avatar" in changes) {
|
|
216
228
|
written.avatar = changes.avatar;
|
|
217
229
|
}
|
|
230
|
+
if ("fide" in changes) {
|
|
231
|
+
written.fide = changes.fide;
|
|
232
|
+
}
|
|
218
233
|
if ("accounts" in changes) {
|
|
219
234
|
written.lichess = changes.accounts?.lichess;
|
|
220
235
|
written.chesscom = changes.accounts?.chesscom;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** The sites a rating is read from, by the player's names there (D-105). */
|
|
2
|
+
export declare const ratingSites: readonly ["lichess", "chess.com"];
|
|
3
|
+
export type RatingSite = (typeof ratingSites)[number];
|
|
4
|
+
/** The speeds whose rating counts, in the order they count: the games with the AI are like a slow rapid (D-106). */
|
|
5
|
+
export declare const speeds: readonly ["rapid", "classical", "blitz"];
|
|
6
|
+
export type Speed = (typeof speeds)[number];
|
|
7
|
+
/** A rating a site gives: the number as it is, never converted, its speed, how sure the site is of it (its rating deviation, RD), and the day of the last game at that speed. */
|
|
8
|
+
export interface Rating {
|
|
9
|
+
readonly value: number;
|
|
10
|
+
readonly site: RatingSite;
|
|
11
|
+
readonly speed: Speed;
|
|
12
|
+
readonly deviation: number;
|
|
13
|
+
/** YYYY-MM-DD. */
|
|
14
|
+
readonly played?: string;
|
|
15
|
+
}
|
|
16
|
+
/** What a site said of the player's name there, the last time Chesspal asked. */
|
|
17
|
+
export interface SiteReading {
|
|
18
|
+
readonly account: string;
|
|
19
|
+
/** False when the site has no player of that name; unknown while it has never answered. */
|
|
20
|
+
readonly found?: boolean;
|
|
21
|
+
/** The rating that counts there: none when the player has no established rating at a speed that counts. */
|
|
22
|
+
readonly rating?: Rating;
|
|
23
|
+
/** The last try did not reach the site: what it said before stays. */
|
|
24
|
+
readonly unreached?: true;
|
|
25
|
+
}
|
|
26
|
+
/** What the sites said, and when Chesspal last asked them. */
|
|
27
|
+
export interface SiteReadings {
|
|
28
|
+
readonly read: string;
|
|
29
|
+
readonly lichess?: SiteReading;
|
|
30
|
+
readonly chesscom?: SiteReading;
|
|
31
|
+
}
|
|
32
|
+
/** The readings kept in a profile's folder; undefined when it has none, or they cannot be read. */
|
|
33
|
+
export declare function savedReadings(folder: string): SiteReadings | undefined;
|
|
34
|
+
/** Keeps the readings in a profile's folder, over the ones before; none removes them. */
|
|
35
|
+
export declare function saveReadings(folder: string, readings: SiteReadings | undefined): void;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// What Lichess and Chess.com said of the player's names there (D-105, D-106): ratings.json in the profile's folder, written by Chesspal each time it reads
|
|
2
|
+
// them, never by hand; the rating that counts is chosen from it as the profile is read.
|
|
3
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { writeAtomically } from "../games/index.js";
|
|
6
|
+
/** The sites a rating is read from, by the player's names there (D-105). */
|
|
7
|
+
export const ratingSites = ["lichess", "chess.com"];
|
|
8
|
+
/** The speeds whose rating counts, in the order they count: the games with the AI are like a slow rapid (D-106). */
|
|
9
|
+
export const speeds = ["rapid", "classical", "blitz"];
|
|
10
|
+
/** The readings kept in a profile's folder; undefined when it has none, or they cannot be read. */
|
|
11
|
+
export function savedReadings(folder) {
|
|
12
|
+
try {
|
|
13
|
+
return readingsOf(JSON.parse(readFileSync(join(folder, fileName), "utf8")));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Keeps the readings in a profile's folder, over the ones before; none removes them. */
|
|
20
|
+
export function saveReadings(folder, readings) {
|
|
21
|
+
if (readings === undefined) {
|
|
22
|
+
rmSync(join(folder, fileName), { force: true });
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
writeAtomically(join(folder, fileName), `${JSON.stringify(readings, null, 2)}\n`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const fileName = "ratings.json";
|
|
29
|
+
// Readings as the file has them, each part checked: a file edited by hand never gives the AI a rating that is not one.
|
|
30
|
+
function readingsOf(value) {
|
|
31
|
+
const file = value;
|
|
32
|
+
if (typeof file?.read !== "string" || Number.isNaN(Date.parse(file.read))) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const lichess = readingOf(file.lichess, "lichess");
|
|
36
|
+
const chesscom = readingOf(file.chesscom, "chess.com");
|
|
37
|
+
return { read: file.read, ...(lichess === undefined ? {} : { lichess }), ...(chesscom === undefined ? {} : { chesscom }) };
|
|
38
|
+
}
|
|
39
|
+
function readingOf(value, site) {
|
|
40
|
+
const reading = value;
|
|
41
|
+
if (typeof reading?.account !== "string" || reading.account === "") {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const rating = ratingOf(reading.rating, site);
|
|
45
|
+
return {
|
|
46
|
+
account: reading.account,
|
|
47
|
+
...(typeof reading.found === "boolean" ? { found: reading.found } : {}),
|
|
48
|
+
...(rating === undefined ? {} : { rating }),
|
|
49
|
+
...(reading.unreached === true ? { unreached: true } : {}),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function ratingOf(value, site) {
|
|
53
|
+
const rating = value;
|
|
54
|
+
const speed = speeds.find((one) => one === rating?.speed);
|
|
55
|
+
const plausible = (number) => typeof number === "number" && Number.isInteger(number) && number > 0 && number < 4000;
|
|
56
|
+
if (rating?.site !== site || speed === undefined || !plausible(rating.value) || !plausible(rating.deviation)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const played = typeof rating.played === "string" && /^\d{4}-\d{2}-\d{2}$/.test(rating.played) ? rating.played : undefined;
|
|
60
|
+
return { value: rating.value, site, speed, deviation: rating.deviation, ...(played === undefined ? {} : { played }) };
|
|
61
|
+
}
|
package/dist/server/bell.d.ts
CHANGED
|
@@ -29,16 +29,32 @@ export declare class Bell {
|
|
|
29
29
|
ring(): void;
|
|
30
30
|
/** A hook waits for the bell, so the chat's turn has ended; the hook before it, of a turn already ended, is let go. */
|
|
31
31
|
listen(request: IncomingMessage, response: ServerResponse): void;
|
|
32
|
+
/** The chat left the table (D-104): its process ended, and nobody is in a turn or waits for the bell; a ring no hook heard is no longer news for anyone. */
|
|
33
|
+
chatLeft(): void;
|
|
32
34
|
private tellIf;
|
|
33
35
|
}
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Whose page answers at a port, for the hook to find its own: the chat's session, the player's folder, whether a browser has the page open, Chesspal's
|
|
38
|
+
* version, and whether the table serves it (D-104), with the session and the folder of the chat attached to it, never its own process's.
|
|
39
|
+
*/
|
|
40
|
+
export declare function bellProbe(folder: string, open?: boolean, table?: TableChat): BellProbe;
|
|
41
|
+
/** The chat attached to the table, as its probe says it: its session, null for none or for Codex, and its folder; neither while no chat is attached. */
|
|
42
|
+
export interface TableChat {
|
|
43
|
+
session: string | null;
|
|
44
|
+
chat?: string;
|
|
45
|
+
}
|
|
36
46
|
/** What a page says of itself at `/api/bell?probe`. */
|
|
37
47
|
export interface BellProbe {
|
|
38
48
|
session: string | null;
|
|
39
49
|
folder: string;
|
|
40
50
|
open: boolean;
|
|
51
|
+
version?: string;
|
|
52
|
+
table?: true;
|
|
53
|
+
/** The folder of the chat attached to the table. */
|
|
54
|
+
chat?: string;
|
|
41
55
|
}
|
|
56
|
+
/** What the page at a port of this computer says of itself; undefined when none answers there. */
|
|
57
|
+
export declare function pageAt(port: number): Promise<BellProbe | undefined>;
|
|
42
58
|
/** The ports a folder's page tries first: one in each of a few ranges, by its path, since Windows may reserve a range. The hook looks for it there. */
|
|
43
59
|
export declare function pagePorts(folder: string): number[];
|
|
44
60
|
/** What Codex's Stop hook answers to wake the chat: its turn goes on with the doorbell's line. */
|
|
@@ -62,7 +78,12 @@ export type BellHeard = {
|
|
|
62
78
|
rang: false;
|
|
63
79
|
why: string;
|
|
64
80
|
};
|
|
65
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* Waits at the page of the hook's chat, as waitForBell does, and says what it heard. The chat may be attached to the table (D-104), beside another chat
|
|
83
|
+
* of its folder that serves its own page: the table is one more page to choose from, unless the ports to try are given (Priya).
|
|
84
|
+
*/
|
|
66
85
|
export declare function bellHeard(input: string, ports?: number[]): Promise<BellHeard>;
|
|
67
86
|
/** Adds a line to the log of the hooks that heard no bell, keeping its last lines; a log that cannot be written never stops the hook. */
|
|
68
87
|
export declare function noteSilence(folder: string, why: string, now?: Date): void;
|
|
88
|
+
/** Whether two paths name the same folder: without regard to case on Windows. */
|
|
89
|
+
export declare function samePath(one: string, other: string): boolean;
|