chesspal 0.3.1 → 0.3.3
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 +1 -1
- package/dist/cli/cli.js +6 -6
- package/dist/commands/{analyse-game.d.ts → analysis/analyse.d.ts} +2 -2
- package/dist/commands/{analyse-game.js → analysis/analyse.js} +7 -11
- package/dist/commands/{annotate-game.d.ts → analysis/annotate.d.ts} +1 -1
- package/dist/commands/{annotate-game.js → analysis/annotate.js} +6 -9
- package/dist/commands/{delete-review.d.ts → analysis/delete-review.d.ts} +1 -1
- package/dist/commands/{delete-review.js → analysis/delete-review.js} +5 -5
- package/dist/commands/analysis/index.d.ts +4 -0
- package/dist/commands/analysis/index.js +5 -0
- package/dist/commands/{study-games.d.ts → analysis/study-games.d.ts} +1 -1
- package/dist/commands/{study-games.js → analysis/study-games.js} +4 -5
- package/dist/commands/{agents.d.ts → answers/agents.d.ts} +4 -4
- package/dist/commands/{agents.js → answers/agents.js} +13 -9
- package/dist/commands/{clock.d.ts → answers/clock.d.ts} +2 -2
- package/dist/commands/{clock.js → answers/clock.js} +2 -2
- package/dist/commands/{drawings.d.ts → answers/drawings.d.ts} +1 -1
- package/dist/commands/{drawings.js → answers/drawings.js} +1 -1
- package/dist/commands/answers/index.d.ts +12 -0
- package/dist/commands/answers/index.js +14 -0
- package/dist/commands/{lead-input.d.ts → answers/lead-input.d.ts} +1 -1
- package/dist/commands/{lead-input.js → answers/lead-input.js} +1 -1
- package/dist/commands/{answer-lines.d.ts → answers/lines.d.ts} +2 -2
- package/dist/commands/{answer-lines.js → answers/lines.js} +4 -4
- package/dist/commands/{opening-book.d.ts → answers/opening-book.d.ts} +1 -1
- package/dist/commands/{opening-book.js → answers/opening-book.js} +3 -3
- package/dist/commands/{play-move.d.ts → answers/play-move.d.ts} +2 -2
- package/dist/commands/{play-move.js → answers/play-move.js} +3 -3
- package/dist/commands/{say.d.ts → answers/say.d.ts} +3 -3
- package/dist/commands/{say.js → answers/say.js} +4 -4
- package/dist/commands/{speech.js → answers/speech.js} +1 -1
- package/dist/commands/{study.d.ts → answers/study.d.ts} +3 -3
- package/dist/commands/{study.js → answers/study.js} +3 -3
- package/dist/commands/{game-summary.d.ts → answers/summary.d.ts} +2 -2
- package/dist/commands/{game-summary.js → answers/summary.js} +4 -4
- package/dist/commands/{view.d.ts → answers/view.d.ts} +1 -1
- package/dist/commands/{ai-move.d.ts → game/ai-move.d.ts} +2 -2
- package/dist/commands/{ai-move.js → game/ai-move.js} +6 -11
- package/dist/commands/{coach.d.ts → game/coach.d.ts} +1 -1
- package/dist/commands/{coach.js → game/coach.js} +7 -13
- package/dist/commands/{draw-offer.d.ts → game/draw-offer.d.ts} +1 -1
- package/dist/commands/{draw-offer.js → game/draw-offer.js} +4 -4
- package/dist/commands/{end-game.d.ts → game/end.d.ts} +2 -2
- package/dist/commands/{end-game.js → game/end.js} +5 -9
- package/dist/commands/{export-game.d.ts → game/export.d.ts} +1 -1
- package/dist/commands/{export-game.js → game/export.js} +4 -4
- package/dist/commands/game/index.d.ts +11 -0
- package/dist/commands/game/index.js +13 -0
- package/dist/commands/{levels.d.ts → game/levels.d.ts} +3 -3
- package/dist/commands/{levels.js → game/levels.js} +3 -3
- package/dist/commands/{look.d.ts → game/look.d.ts} +3 -4
- package/dist/commands/{look.js → game/look.js} +5 -10
- package/dist/commands/{new-game.d.ts → game/new.d.ts} +3 -4
- package/dist/commands/{new-game.js → game/new.js} +17 -13
- package/dist/commands/{page-link.d.ts → game/page-link.d.ts} +1 -1
- package/dist/commands/{player-move.d.ts → game/player-move.d.ts} +2 -2
- package/dist/commands/{player-move.js → game/player-move.js} +4 -8
- package/dist/commands/{ponder.d.ts → game/ponder.d.ts} +3 -3
- package/dist/commands/{ponder.js → game/ponder.js} +6 -9
- package/dist/commands/{position.d.ts → game/position.d.ts} +2 -2
- package/dist/commands/{position.js → game/position.js} +5 -7
- package/dist/commands/{status.d.ts → game/status.d.ts} +2 -4
- package/dist/commands/{status.js → game/status.js} +12 -17
- package/dist/commands/index.d.ts +6 -17
- package/dist/commands/index.js +11 -38
- package/dist/commands/{delete-game.d.ts → profiles/delete-game.d.ts} +1 -1
- package/dist/commands/{delete-game.js → profiles/delete-game.js} +3 -3
- package/dist/commands/profiles/index.d.ts +4 -0
- package/dist/commands/profiles/index.js +5 -0
- package/dist/commands/{past-games.d.ts → profiles/past-games.d.ts} +1 -1
- package/dist/commands/{past-games.js → profiles/past-games.js} +5 -5
- package/dist/commands/{profiles.d.ts → profiles/profiles.d.ts} +7 -7
- package/dist/commands/{profiles.js → profiles/profiles.js} +4 -4
- package/dist/commands/{ratings.d.ts → profiles/ratings.d.ts} +1 -1
- package/dist/commands/{ratings.js → profiles/ratings.js} +2 -2
- package/dist/commands/{context.js → registry/context.js} +2 -2
- package/dist/commands/registry/index.d.ts +2 -0
- package/dist/commands/registry/index.js +3 -0
- package/dist/commands/setup/index.d.ts +2 -0
- package/dist/commands/setup/index.js +3 -0
- package/dist/commands/{init.d.ts → setup/init.d.ts} +1 -1
- package/dist/commands/{init.js → setup/init.js} +2 -3
- package/dist/commands/{instructions.d.ts → setup/instructions.d.ts} +2 -2
- package/dist/commands/{instructions.js → setup/instructions.js} +3 -3
- package/dist/commands/{ai-say.d.ts → talk/ai-say.d.ts} +2 -3
- package/dist/commands/{ai-say.js → talk/ai-say.js} +5 -9
- package/dist/commands/talk/index.d.ts +2 -0
- package/dist/commands/talk/index.js +3 -0
- package/dist/commands/{player-say.d.ts → talk/player-say.d.ts} +1 -1
- package/dist/commands/{player-say.js → talk/player-say.js} +4 -4
- package/dist/page/app/app.js +53 -21
- package/dist/page/app/served.js +15 -3
- package/dist/page/avatars/torretta.js +10 -3
- package/dist/page/dialogs/dialogs.css +10 -0
- package/dist/page/dialogs/profiles.js +1 -5
- package/dist/page/dialogs/settings.js +18 -4
- package/dist/page/game/column.js +1 -1
- package/dist/page/game/game.css +26 -3
- package/dist/page/guide/blocks.js +13 -2
- package/dist/page/guide/credits.js +6 -0
- package/dist/page/guide/drawings/analysis.js +13 -0
- package/dist/page/guide/drawings/drawings.css +329 -0
- package/dist/page/guide/drawings/drawings.js +15 -0
- package/dist/page/guide/drawings/game.js +15 -0
- package/dist/page/guide/drawings/helps.js +13 -0
- package/dist/page/guide/drawings/how-it-works.js +13 -0
- package/dist/page/guide/drawings/parts.js +103 -0
- package/dist/page/guide/drawings/players.js +19 -0
- package/dist/page/guide/drawings/talk.js +9 -0
- package/dist/page/guide/en.md +39 -25
- package/dist/page/guide/guide.css +123 -91
- package/dist/page/guide/guide.js +47 -25
- package/dist/page/guide/it.md +39 -25
- package/dist/page/guide/markdown.js +5 -0
- package/dist/page/helps/helps.css +201 -88
- package/dist/page/helps/helps.js +50 -31
- package/dist/page/helps/view.js +13 -3
- package/dist/page/{guide/foot.js → layout/footer.js} +7 -8
- package/dist/page/layout/header.js +5 -3
- package/dist/page/layout/layout.css +114 -10
- package/dist/page/layout/window.js +6 -26
- package/dist/page/main.js +1 -1
- package/dist/page/pieces/pieces.js +5 -2
- package/dist/page/style.css +1 -0
- package/dist/page/table/table.css +69 -0
- package/dist/page/table/update.js +82 -0
- package/dist/page/table/window.js +6 -5
- package/dist/page/talk/chat.css +6 -6
- package/dist/page/talk/talk.css +17 -67
- package/dist/page/talk/talk.js +6 -6
- package/dist/page/talk/voice.js +12 -0
- package/dist/page/text.js +78 -20
- package/dist/page/theme.css +2 -0
- package/dist/server/{bell.js → bell/bell.js} +17 -4
- package/dist/server/bell/index.d.ts +1 -0
- package/dist/server/bell/index.js +2 -0
- package/dist/server/{caller.d.ts → mcp/caller.d.ts} +1 -1
- package/dist/server/{caller.js → mcp/caller.js} +1 -1
- package/dist/server/mcp/index.d.ts +1 -0
- package/dist/server/mcp/index.js +2 -0
- package/dist/server/{mcp.d.ts → mcp/mcp.d.ts} +1 -1
- package/dist/server/{mcp.js → mcp/mcp.js} +9 -9
- package/dist/server/page/attach.js +8 -3
- package/dist/server/page/game.js +19 -3
- package/dist/server/page/http.d.ts +1 -1
- package/dist/server/page/index.d.ts +3 -0
- package/dist/server/page/index.js +3 -0
- package/dist/server/page/page.d.ts +12 -3
- package/dist/server/page/page.js +26 -5
- package/dist/server/page/table.d.ts +7 -1
- package/dist/server/page/table.js +79 -3
- package/dist/server/page/update.d.ts +18 -0
- package/dist/server/page/update.js +40 -0
- package/dist/server/table/browser.d.ts +1 -1
- package/dist/server/table/browser.js +1 -1
- package/dist/server/table/hosts/chats.d.ts +1 -1
- package/dist/server/table/hosts/chats.js +2 -2
- package/dist/server/table/hosts/index.d.ts +1 -1
- package/dist/server/table/hosts/index.js +1 -1
- package/dist/server/table/hosts/play.d.ts +8 -1
- package/dist/server/table/hosts/play.js +40 -4
- package/dist/server/table/hosts/seat.d.ts +5 -1
- package/dist/server/table/hosts/seat.js +1 -1
- package/dist/server/table/index.d.ts +3 -2
- package/dist/server/table/index.js +3 -2
- package/dist/server/table/launch.d.ts +5 -2
- package/dist/server/table/launch.js +6 -3
- package/dist/server/table/link.d.ts +1 -1
- package/dist/server/table/link.js +3 -3
- package/dist/server/table/table.d.ts +4 -5
- package/dist/server/table/table.js +15 -12
- package/dist/server/table/update.d.ts +17 -0
- package/dist/server/table/update.js +77 -0
- package/dist/sites/index.d.ts +1 -1
- package/dist/sites/index.js +2 -2
- package/dist/version.d.ts +4 -0
- package/dist/version.js +12 -1
- package/package.json +1 -1
- /package/dist/commands/{speech.d.ts → answers/speech.d.ts} +0 -0
- /package/dist/commands/{view.js → answers/view.js} +0 -0
- /package/dist/commands/{page-link.js → game/page-link.js} +0 -0
- /package/dist/commands/{context.d.ts → registry/context.d.ts} +0 -0
- /package/dist/commands/{registry.d.ts → registry/registry.d.ts} +0 -0
- /package/dist/commands/{registry.js → registry/registry.js} +0 -0
- /package/dist/server/{bell.d.ts → bell/bell.d.ts} +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { type DrawnAvatar, type PlayerProfile } from "
|
|
3
|
-
import type
|
|
2
|
+
import { type DrawnAvatar, type PlayerProfile } from "../../profiles/index.ts";
|
|
3
|
+
import { type Context } from "../registry/index.ts";
|
|
4
4
|
/** A change of profile refused while a game goes on: the page says it in the player's language. */
|
|
5
5
|
export declare class GameGoingOn extends Error {
|
|
6
6
|
constructor();
|
|
7
7
|
}
|
|
8
8
|
/** The profiles, the one in use first. */
|
|
9
|
-
export declare const profiles: import("
|
|
9
|
+
export declare const profiles: import("../registry/registry.ts").Command<z.ZodObject<{}, z.core.$strip>, {
|
|
10
10
|
profiles: PlayerProfile[];
|
|
11
11
|
active: string;
|
|
12
12
|
}>;
|
|
@@ -15,7 +15,7 @@ export declare const profiles: import("./registry.ts").Command<z.ZodObject<{}, z
|
|
|
15
15
|
* the first start, while new, becomes it, game and all (D-71). With the player's names on Lichess and Chess.com, their ratings are read there; their
|
|
16
16
|
* FIDE rating they type (D-106).
|
|
17
17
|
*/
|
|
18
|
-
export declare const newProfile: import("
|
|
18
|
+
export declare const newProfile: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
19
19
|
name: z.ZodString;
|
|
20
20
|
level: z.ZodOptional<z.ZodEnum<{
|
|
21
21
|
learning: "learning";
|
|
@@ -43,11 +43,11 @@ export declare const newProfile: import("./registry.ts").Command<z.ZodObject<{
|
|
|
43
43
|
readonly inUse: boolean;
|
|
44
44
|
}>;
|
|
45
45
|
/** Another profile put in use, once the game going on has ended. */
|
|
46
|
-
export declare const useProfile: import("
|
|
46
|
+
export declare const useProfile: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
47
47
|
profile: z.ZodString;
|
|
48
48
|
}, z.core.$strip>, PlayerProfile>;
|
|
49
49
|
/** The picture of the profile in use: a drawn avatar, or an image of the player's, which replaces any before. */
|
|
50
|
-
export declare const setAvatar: import("
|
|
50
|
+
export declare const setAvatar: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
51
51
|
avatar: z.ZodString;
|
|
52
52
|
}, z.core.$strip>, {
|
|
53
53
|
profile: string;
|
|
@@ -57,7 +57,7 @@ export declare const setAvatar: import("./registry.ts").Command<z.ZodObject<{
|
|
|
57
57
|
* What the player tells of themselves in the profile in use: their name, how they play, their names on Lichess and Chess.com, by which the games they
|
|
58
58
|
* load are theirs (D-93), and their FIDE rating. Each one given is written, an empty one goes; the profile's folder keeps its name (D-106).
|
|
59
59
|
*/
|
|
60
|
-
export declare const editProfile: import("
|
|
60
|
+
export declare const editProfile: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
61
61
|
name: z.ZodOptional<z.ZodString>;
|
|
62
62
|
level: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
63
63
|
learning: "learning";
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { endOf } from "
|
|
6
|
-
import { activateProfile, activeProfile, createProfile, drawnAvatars, fideHighest, fideLowest, gameFolder, levels, listProfiles, nameLimit, removeOwnImage, saveOwnImage, updateProfile, } from "
|
|
7
|
-
import { gameAt } from "
|
|
5
|
+
import { endOf } from "../../games/index.js";
|
|
6
|
+
import { activateProfile, activeProfile, createProfile, drawnAvatars, fideHighest, fideLowest, gameFolder, levels, listProfiles, nameLimit, removeOwnImage, saveOwnImage, updateProfile, } from "../../profiles/index.js";
|
|
7
|
+
import { gameAt } from "../answers/index.js";
|
|
8
|
+
import { defineCommand } from "../registry/index.js";
|
|
8
9
|
import { refreshRatings } from "./ratings.js";
|
|
9
|
-
import { defineCommand } from "./registry.js";
|
|
10
10
|
/** A change of profile refused while a game goes on: the page says it in the player's language. */
|
|
11
11
|
export class GameGoingOn extends Error {
|
|
12
12
|
constructor() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The player's ratings read again from the sites (D-105, D-106): when their names there change, and when the page opens a day after the last reading;
|
|
2
2
|
// never from a tool of the AI's.
|
|
3
|
-
import { listProfiles, profileFolder, saveReadings } from "
|
|
4
|
-
import { readingsStale, readSites } from "
|
|
3
|
+
import { listProfiles, profileFolder, saveReadings } from "../../profiles/index.js";
|
|
4
|
+
import { readingsStale, readSites } from "../../sites/index.js";
|
|
5
5
|
/** A day, in milliseconds: the page reads the ratings again once they are older. */
|
|
6
6
|
export const ratingsAge = 24 * 60 * 60 * 1000;
|
|
7
7
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// What every command runs with besides its input, injected by the command line, the MCP server or the tests: the folders, the time, the randomness.
|
|
2
2
|
import { userInfo } from "node:os";
|
|
3
|
-
import { dataFolder, openFolder } from "
|
|
4
|
-
import { ensureProfile } from "
|
|
3
|
+
import { dataFolder, openFolder } from "../../home/index.js";
|
|
4
|
+
import { ensureProfile } from "../../profiles/index.js";
|
|
5
5
|
/** The context of a process started in the player's folder: the data folder is found at the first command that needs it, once. */
|
|
6
6
|
export function defaultContext() {
|
|
7
7
|
let found;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { type Caller, type Context, defaultContext, type SeenPosition, speaksItalian } from "./context.ts";
|
|
2
|
+
export { type AnyCommand, baseType, type Command, commandLineFieldProblem, defineCommand, inputProblems, isBoolean, isList, sentence, takesNumbers } from "./registry.ts";
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// What a command is and what it runs with: the definition and the reading of its input, and the context of folders, time and randomness.
|
|
2
|
+
export { defaultContext, speaksItalian } from "./context.js";
|
|
3
|
+
export { baseType, commandLineFieldProblem, defineCommand, inputProblems, isBoolean, isList, sentence, takesNumbers } from "./registry.js";
|
|
@@ -9,7 +9,7 @@ interface FolderSetUp {
|
|
|
9
9
|
hooksChanged?: true;
|
|
10
10
|
}
|
|
11
11
|
/** Makes a folder the player's chess table: the files through which Claude Code and Codex find Chesspal and the opponent's instructions (D-31). */
|
|
12
|
-
export declare const init: import("
|
|
12
|
+
export declare const init: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
13
13
|
folder: z.ZodOptional<z.ZodString>;
|
|
14
14
|
}, z.core.$strip>, FolderSetUp>;
|
|
15
15
|
export {};
|
|
@@ -3,9 +3,8 @@ import { relative, resolve } from "node:path";
|
|
|
3
3
|
// Colours only on a terminal that shows them: none in a file, a pipe or with NO_COLOR.
|
|
4
4
|
import { styleText } from "node:util";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { codexHooksPath, setUpHosts } from "
|
|
7
|
-
import { speaksItalian } from "
|
|
8
|
-
import { defineCommand } from "./registry.js";
|
|
6
|
+
import { codexHooksPath, setUpHosts } from "../../host-files/index.js";
|
|
7
|
+
import { defineCommand, speaksItalian } from "../registry/index.js";
|
|
9
8
|
/** Makes a folder the player's chess table: the files through which Claude Code and Codex find Chesspal and the opponent's instructions (D-31). */
|
|
10
9
|
export const init = defineCommand({
|
|
11
10
|
name: "init",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type
|
|
2
|
+
import { type Context } from "../registry/index.ts";
|
|
3
3
|
/** How to play as the player's opponent, which the chat reads before its first reply and again after a summary (D-102). */
|
|
4
|
-
export declare const instructions: import("
|
|
4
|
+
export declare const instructions: import("../registry/registry.ts").Command<z.ZodObject<{}, z.core.$strip>, string>;
|
|
5
5
|
/**
|
|
6
6
|
* Whether the chat has read the opponent's instructions since it started, or was cleared or summarized, as its start keys them (D-57); without the host's
|
|
7
7
|
* note of it, since the MCP server started. Always on the command line, which keeps nothing.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// The opponent's instructions, which the MCP server gives so that they follow the version installed (D-102).
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { lastChatStart } from "
|
|
4
|
-
import { opponentInstructions } from "
|
|
5
|
-
import { defineCommand } from "
|
|
3
|
+
import { lastChatStart } from "../../games/index.js";
|
|
4
|
+
import { opponentInstructions } from "../../host-files/index.js";
|
|
5
|
+
import { defineCommand } from "../registry/index.js";
|
|
6
6
|
/** How to play as the player's opponent, which the chat reads before its first reply and again after a summary (D-102). */
|
|
7
7
|
export const instructions = defineCommand({
|
|
8
8
|
name: "instructions",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { type GameSummary } from "
|
|
3
|
-
import { type Written } from "./say.ts";
|
|
2
|
+
import { type GameSummary, type Written } from "../answers/index.ts";
|
|
4
3
|
interface Said {
|
|
5
4
|
/** The game, none when the AI spoke before the first (D-77). */
|
|
6
5
|
game?: GameSummary;
|
|
@@ -9,7 +8,7 @@ interface Said {
|
|
|
9
8
|
coach?: true;
|
|
10
9
|
}
|
|
11
10
|
/** The AI's words on the board page without a move, read only there, on the player's time: an answer, or a word it has to say (D-54, D-72). */
|
|
12
|
-
export declare const aiSay: import("
|
|
11
|
+
export declare const aiSay: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
13
12
|
text: z.ZodString;
|
|
14
13
|
mood: z.ZodOptional<z.ZodEnum<{
|
|
15
14
|
neutral: "neutral";
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { moods } from "
|
|
3
|
-
import { archivedName, talkLimits } from "
|
|
4
|
-
import { gameFolder } from "
|
|
5
|
-
import { analystLine, coachOnEnd, coachOnEndLine, reviewedLine, withAnalyst } from "
|
|
6
|
-
import {
|
|
7
|
-
import { summaryOf } from "./game-summary.js";
|
|
8
|
-
import { refuseOtherGame } from "./play-move.js";
|
|
9
|
-
import { defineCommand } from "./registry.js";
|
|
10
|
-
import { give, say, waitingWords } from "./say.js";
|
|
2
|
+
import { moods } from "../../core/index.js";
|
|
3
|
+
import { archivedName, talkLimits } from "../../games/index.js";
|
|
4
|
+
import { gameFolder } from "../../profiles/index.js";
|
|
5
|
+
import { analystLine, coachOnEnd, coachOnEndLine, give, newGameStep, nextStep, refuseOtherGame, reviewedLine, say, summaryOf, waitingWords, withAnalyst, wordLines } from "../answers/index.js";
|
|
6
|
+
import { defineCommand } from "../registry/index.js";
|
|
11
7
|
/** The AI's words on the board page without a move, read only there, on the player's time: an answer, or a word it has to say (D-54, D-72). */
|
|
12
8
|
export const aiSay = defineCommand({
|
|
13
9
|
name: "ai_say",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/** The player's words to the AI, as the board page sends them: on the command line only, since the AI never writes for the player. */
|
|
3
|
-
export declare const playerSay: import("
|
|
3
|
+
export declare const playerSay: import("../registry/registry.ts").Command<z.ZodObject<{
|
|
4
4
|
text: z.ZodString;
|
|
5
5
|
}, z.core.$strip>, string>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { talkLimits } from "
|
|
3
|
-
import { gameFolder } from "
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { talkLimits } from "../../games/index.js";
|
|
3
|
+
import { gameFolder } from "../../profiles/index.js";
|
|
4
|
+
import { say } from "../answers/index.js";
|
|
5
|
+
import { defineCommand } from "../registry/index.js";
|
|
6
6
|
/** The player's words to the AI, as the board page sends them: on the command line only, since the AI never writes for the player. */
|
|
7
7
|
export const playerSay = defineCommand({
|
|
8
8
|
name: "player_say",
|
package/dist/page/app/app.js
CHANGED
|
@@ -9,7 +9,7 @@ import { explain } from "../board/explain.js";
|
|
|
9
9
|
import { kingSigns } from "../board/signs.js";
|
|
10
10
|
import { piecesOf } from "../board/squares.js";
|
|
11
11
|
import { NewGameDialog } from "../dialogs/new-game.js";
|
|
12
|
-
import { NameButton,
|
|
12
|
+
import { NameButton, ProfileDialog, ProfilesDialog, Wizard } from "../dialogs/profiles.js";
|
|
13
13
|
import { SettingsPanel } from "../dialogs/settings.js";
|
|
14
14
|
import { playerIn, studiedSides } from "../dialogs/studied.js";
|
|
15
15
|
import { StudyDialog } from "../dialogs/study.js";
|
|
@@ -25,6 +25,7 @@ import { BareButton, HelpsCard, HintLine, MapButton } from "../helps/helps.js";
|
|
|
25
25
|
import { helpsOf } from "../helps/view.js";
|
|
26
26
|
import { FoldTab } from "../layout/fold-tab.js";
|
|
27
27
|
import { nextFold, rememberFold, startFolds } from "../layout/folds.js";
|
|
28
|
+
import { PageFoot } from "../layout/footer.js";
|
|
28
29
|
import { Header } from "../layout/header.js";
|
|
29
30
|
import { standingsOf } from "../lead/view.js";
|
|
30
31
|
import { MarksLegend } from "../legend/legend.js";
|
|
@@ -35,13 +36,14 @@ import { signature } from "../players/model-name.js";
|
|
|
35
36
|
import { keepSettings } from "../profile/profile.js";
|
|
36
37
|
import { languagesOf, saveSettings, settingsFrom, showSettings } from "../profile/settings.js";
|
|
37
38
|
import { endSoundOf, playSound, soundOf, wakeSounds } from "../sounds/sounds.js";
|
|
39
|
+
import { UpdateDialog, UpdateNotice } from "../table/update.js";
|
|
38
40
|
import { AiNameButton, ChatDialog, tableNote } from "../table/window.js";
|
|
39
41
|
import { AiNote, AiWords, Compose, PointSquare, TalkFaces } from "../talk/talk.js";
|
|
40
42
|
import { textFor } from "../text.js";
|
|
41
43
|
import { anchorName, useAnchor } from "./anchor.js";
|
|
42
44
|
import { useBrowseKeys, useFoldKeys, useLegendKey, useMapKey, useWheelSteps } from "./keys.js";
|
|
43
45
|
import { phaseOf, verdictOf } from "./phase.js";
|
|
44
|
-
import { post, sendMessage, sendMove, useServed } from "./served.js";
|
|
46
|
+
import { ask, post, sendMessage, sendMove, useServed } from "./served.js";
|
|
45
47
|
import { tabTitle, useTabTitle } from "./title.js";
|
|
46
48
|
/**
|
|
47
49
|
* The page: the board of the game in the player's folder, where the player moves on their turn, the talk with the AI beside it, and the game's column; in the
|
|
@@ -69,12 +71,23 @@ export function App({ start }) {
|
|
|
69
71
|
const { data: served, problem, offline, reload } = useServed(() => setDialog("new-game"));
|
|
70
72
|
// Nuova partita with no chat at the table asks for one first (D-107): the chat's window, then the new game's, however the first closes.
|
|
71
73
|
const [chatFirst, setChatFirst] = useState(false);
|
|
72
|
-
|
|
74
|
+
// At the table with no chat, and none opening: what asks for the AI opens the chat's window first, to choose its host and model (D-107, D-109).
|
|
75
|
+
const chatMissing = () => {
|
|
73
76
|
const table = offline ? undefined : served?.table;
|
|
74
|
-
|
|
77
|
+
return table !== undefined && !table.chat && table.opening === undefined;
|
|
78
|
+
};
|
|
79
|
+
// The chat's window opened for an analysis asks who reviews the game, not whom the player plays (Rosa, Zoe).
|
|
80
|
+
const [chatToReview, setChatToReview] = useState(false);
|
|
81
|
+
const askNewGame = () => {
|
|
82
|
+
const first = chatMissing();
|
|
75
83
|
setChatFirst(first);
|
|
84
|
+
setChatToReview(false);
|
|
76
85
|
setDialog(first ? "chat" : "new-game");
|
|
77
86
|
};
|
|
87
|
+
const askChat = () => {
|
|
88
|
+
setChatToReview(false);
|
|
89
|
+
setDialog("chat");
|
|
90
|
+
};
|
|
78
91
|
const closeChat = () => {
|
|
79
92
|
setDialog(chatFirst ? "new-game" : undefined);
|
|
80
93
|
setChatFirst(false);
|
|
@@ -251,7 +264,7 @@ export function App({ start }) {
|
|
|
251
264
|
const { kind, verdict } = aborted && phase === "over" ? { kind: "over", verdict: text.aborted } : verdictOf(phase, how, winner, offer, declined, text);
|
|
252
265
|
const header = (_jsx(Header, { text: text, kind: kind, verdict: verdict, ...(!ended && (offer === "ai" || declined) ? { who: name } : {}), actions: actions, onNewGame: served === undefined ? undefined : askNewGame,
|
|
253
266
|
// Between games the new one stands out in the player's card (D-77): in the header only when the talk, and the card with it, is folded away.
|
|
254
|
-
standOut: studied !== undefined || (between && folds.talk !== "open"), onStudy: served === undefined || going ? undefined : () => setDialog("study"), flipped: flipped, onFlip: () => setFlipped(!flipped), sound: sound, onSound: () => change({ ...settings, sound: !sound }), onTheme: switchTheme, onSettings: () => setSetting(true), onGuide: () => setDialog("guide"), ...(served === undefined ? {} : { profile: served.profile, onProfile: () => setDialog("profiles") }) }));
|
|
267
|
+
standOut: studied !== undefined || (between && folds.talk !== "open"), onStudy: served === undefined || going ? undefined : () => setDialog("study"), flipped: flipped, onFlip: () => setFlipped(!flipped), sound: sound, onSound: () => change({ ...settings, sound: !sound }), onTheme: switchTheme, onSettings: () => setSetting(true), onGuide: () => setDialog("guide"), ...(served === undefined ? {} : { profile: served.profile, onProfile: () => setDialog("profiles") }), ...(served?.update === undefined || served.table === undefined || offline ? {} : { update: { version: served.update.version, installing: served.update.installing === true }, onUpdate: () => setDialog("update") }) }));
|
|
255
268
|
const panel = _jsx(SettingsPanel, { open: setting, settings: settings, onChange: change, onClose: () => setSetting(false), text: text });
|
|
256
269
|
// The guide opens before the page has its data too.
|
|
257
270
|
const guide = _jsx(GuideDialog, { open: dialog === "guide", onClose: () => setDialog(undefined), text: text });
|
|
@@ -298,6 +311,10 @@ export function App({ start }) {
|
|
|
298
311
|
const legendAt = (under) => (_jsx(MarksLegend, { rows: legendRows, open: legendOpen, onOpen: setLegendOpen, drawn: { words: drawnByCoach ? text.helpsCard.drawn : text.drawn, by: drawer }, onPoint: setPointed, under: under, text: text }));
|
|
299
312
|
const legend = legendAt(false);
|
|
300
313
|
const summary = studied === undefined ? undefined : gameReviewOf(live.record);
|
|
314
|
+
// An AI is going over the game only when the server says so: with no chat at it, nobody is (Stefano).
|
|
315
|
+
const reviewing = summary === undefined && studied?.reviewing === true && !offline;
|
|
316
|
+
// With nobody at it, a review not finished says so, and the player brings the game back to the AI (D-109).
|
|
317
|
+
const completeIt = summary === undefined && studied?.incomplete === true && !offline ? () => void studyGame({ game: studied.name }) : undefined;
|
|
301
318
|
const closing = studied === undefined ? undefined : closingOf(live.record);
|
|
302
319
|
// In analysis the AI's card says who played that game, as its PGN keeps it (D-82), not who plays now.
|
|
303
320
|
const playedBy = studied === undefined ? undefined : aiModelIn(live.record);
|
|
@@ -309,14 +326,14 @@ export function App({ start }) {
|
|
|
309
326
|
const asleep = served.listening === false && !offline;
|
|
310
327
|
// What the AI thinks on, a cloud at the Torretta's corner (Zoe): only in a game going on, and never while no chat hears the player, which the note says.
|
|
311
328
|
const thinking = going && !asleep && !offline && studied === undefined ? served.thinking : undefined;
|
|
312
|
-
const thinker = (size) => (_jsx(Thinker, { thinking: thinking, words: text.thinking, children: _jsx(Torretta, { size: size, mood: mood }) }));
|
|
329
|
+
const thinker = (size) => (_jsx(Thinker, { thinking: thinking, words: text.thinking, children: _jsx(Torretta, { size: size, mood: mood, bust: true }) }));
|
|
313
330
|
// The clocks wait for the player from the game's start: when the AI moves first, a button gives it the start; else the player's first move does (Stefano).
|
|
314
331
|
const clockWaits = clock?.waiting === true && !ended && studied === undefined && viewing === undefined && live.position.turn === ai;
|
|
315
332
|
const clockOf = (side) => clock === undefined ? undefined : (_jsx(ClockFace, { clock: clock, side: side, toMove: !ended && live.position.turn === side, held: pending !== undefined || (asleep && side === ai), onFlag: reload, text: text }));
|
|
316
333
|
// Where to wake the chat, under the AI's card (over the board when the talk is folded); the button that starts the clocks, under the player's (Zoe).
|
|
317
334
|
// On the table, what its chat is doing stands there, and in a game with no chat at it a line that opens the chat's window (D-107).
|
|
318
335
|
const table = offline ? undefined : served.table;
|
|
319
|
-
const atTable = table === undefined ? null : tableNote({ table, name, inGame: going, onOpen:
|
|
336
|
+
const atTable = table === undefined ? null : tableNote({ table, name, inGame: going, onOpen: askChat, text });
|
|
320
337
|
const asleepNote = atTable !== null
|
|
321
338
|
? atTable
|
|
322
339
|
: asleep && table?.chat !== false
|
|
@@ -397,40 +414,55 @@ export function App({ start }) {
|
|
|
397
414
|
act("/api/new", { ai_colour: colour === "random" ? "random" : colour === "white" ? "black" : "white", level: chosen, ...(control === undefined ? {} : { clock: control }) });
|
|
398
415
|
};
|
|
399
416
|
// In analysis the AI's note goes with the other side's card and the talk with the player's, turned round with the board as in a game (D-101, Zoe).
|
|
400
|
-
const studiedCard = (side, place) => sides === undefined ? null : (_jsx(PlayerCard, { place: place,
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
// A game brought to analyse: the page shows it once the server has it in analysis; a refusal stays in the dialog.
|
|
417
|
+
const studiedCard = (side, place) => sides === undefined ? null : (_jsx(PlayerCard, { place: place, picture: pictureOf(sides[side], profile, 48, mood), name: sides[side].name, signed: sides[side].who === "ai" && playedBy !== undefined ? signature(playedBy.model, playedBy.effort) : undefined, side: side, taken: material.taken[side], ahead: ahead(side), badges: _jsx(BadgeCounts, { plies: plies, side: side, names: text.badges }), text: text }));
|
|
418
|
+
// A game brought to analyse: the page shows it once the server has it in analysis; a refusal stays in the dialog. One the AI has to go over, with no
|
|
419
|
+
// chat at the table, opens the chat's window: the analyst works with the chat's model (D-109).
|
|
404
420
|
const studyGame = async (studied) => {
|
|
405
|
-
const
|
|
406
|
-
if (refused
|
|
407
|
-
|
|
421
|
+
const sent = await ask("/api/study", studied, text, name);
|
|
422
|
+
if ("refused" in sent) {
|
|
423
|
+
return sent.refused.text;
|
|
408
424
|
}
|
|
409
|
-
|
|
425
|
+
const toReview = sent.answer.reviewed === false && chatMissing();
|
|
426
|
+
setChatToReview(toReview);
|
|
427
|
+
setDialog(toReview ? "chat" : undefined);
|
|
428
|
+
return undefined;
|
|
410
429
|
};
|
|
411
|
-
return (_jsx(ChosenSet.Provider, { value: settings.set, children: _jsxs(PointSquare.Provider, { value: pointSquare, children: [panel, _jsx(ProfileDialog, { open: dialog === "profile", profile: profile, onClose: () => setDialog(undefined), text: text }), _jsx(ProfilesDialog, { open: dialog === "profiles", profiles: served.profiles, active: profile, locked: going ? text.profile.whilePlaying : undefined, dataFolder: served.dataFolder, onEdit: () => setDialog("profile"), onClose: () => setDialog(undefined), text: text }), table === undefined ? null : _jsx(ChatDialog, { open: dialog === "chat", table: table, settings: settings, onChange: change, name: name, onClose: closeChat, text: text }), _jsx(NewGameDialog, { open: dialog === "new-game", choice: choice, going: going, onStart: startNewGame, onClose: () => setDialog(undefined), text: text }), guide, _jsx(StudyDialog, { open: dialog === "study", onStudy: studyGame, onClose: () => setDialog(undefined), you: profile.name, accounts: profile.accounts, ai: name, text: text }),
|
|
430
|
+
return (_jsx(ChosenSet.Provider, { value: settings.set, children: _jsxs(PointSquare.Provider, { value: pointSquare, children: [panel, _jsx(ProfileDialog, { open: dialog === "profile", profile: profile, onClose: () => setDialog(undefined), text: text }), _jsx(ProfilesDialog, { open: dialog === "profiles", profiles: served.profiles, active: profile, locked: going ? text.profile.whilePlaying : undefined, dataFolder: served.dataFolder, onEdit: () => setDialog("profile"), onClose: () => setDialog(undefined), text: text }), table === undefined ? null : _jsx(ChatDialog, { open: dialog === "chat", review: chatToReview, table: table, settings: settings, onChange: change, name: name, onClose: closeChat, text: text }), _jsx(NewGameDialog, { open: dialog === "new-game", choice: choice, going: going, onStart: startNewGame, onClose: () => setDialog(undefined), text: text }), guide, _jsx(StudyDialog, { open: dialog === "study", onStudy: studyGame, onClose: () => setDialog(undefined), you: profile.name, accounts: profile.accounts, ai: name, text: text }), served.update === undefined || served.table === undefined
|
|
431
|
+
? null
|
|
432
|
+
: (_jsx(UpdateDialog, { open: dialog === "update", version: served.update.version, current: served.version, installing: served.update.installing === true, installed: served.update.installed === true, going: going, chat: served.table.chat ? (served.table.held ? "held" : "byHand") : undefined, name: name, onClose: () => setDialog(undefined), text: text })), offline ? _jsx("p", { class: "offline", role: "alert", children: text.offline(name) }) : null, served.outdated === true
|
|
433
|
+
? _jsx("p", { class: "outdated", role: "status", children: text.outdated(name) })
|
|
434
|
+
: served.update !== undefined && served.table === undefined
|
|
435
|
+
? _jsx(UpdateNotice, { version: served.update.version, name: name, text: text })
|
|
436
|
+
: null, _jsxs("main", { class: `app${flipped ? " flipped" : ""}${folds.talk === "open" ? "" : ` talk-${folds.talk}`}${folds.game === "folded" ? " folded" : ""}`, inert: offline, children: [header, sides === undefined
|
|
412
437
|
? (_jsx(TalkFaces, { name: name, aiPicture: thinker(40), you: profile.name, picture: picture, ...(idle ? {} : { ai, player }), aiMoves: going && !playerToMove, playerMoves: playerToMove, unread: unread || asleep, text: text }))
|
|
413
438
|
: (_jsx(TalkFaces, { name: sides[ai].name, aiPicture: pictureOf(sides[ai], profile, 40, mood), you: sides[player].name, picture: pictureOf(sides[player], profile, 40, mood), ai: ai, player: player, aiMoves: false, playerMoves: false, unread: asleep, text: text })), _jsx(FoldTab, { column: "talk", folded: folds.talk === "folded", label: thinking === undefined || folds.talk !== "folded" ? text.talk : `${text.talk}: ${text.thinking[thinking]}`, unread: (unread || asleep) && folds.talk === "folded", ...(thinking === undefined || folds.talk !== "folded" ? {} : { mark: _jsx(Thought, { thinking: thinking, words: text.thinking }) }), onFold: fold }), _jsx(FoldTab, { column: "game", folded: folds.game === "folded", label: text.game, onFold: fold }), _jsx("span", { class: "visually-hidden", "aria-live": "polite", children: unread ? text.wrote(name) : "" }), _jsx("div", { class: sides === undefined ? "talk" : "talk studied", id: "talk", children: sides !== undefined && studied !== undefined
|
|
414
|
-
? (_jsxs(_Fragment, { children: [_jsxs("section", { class: "ai-block", "aria-label": sides[ai].name, children: [studiedCard(ai, "ai"), asleepNote, _jsx(AiNote, { ...(reviewed === undefined ? {} : { ply: reviewed }), ...(shownOpening === undefined ? {} : { opening: shownOpening }), ...(at === total && closing !== undefined ? { closing } : {}), ...(at === 0 && summary !== undefined ? { summary } : {}), ...(
|
|
439
|
+
? (_jsxs(_Fragment, { children: [_jsxs("section", { class: "ai-block", "aria-label": sides[ai].name, children: [studiedCard(ai, "ai"), asleepNote, _jsx(AiNote, { ...(reviewed === undefined ? {} : { ply: reviewed }), ...(shownOpening === undefined ? {} : { opening: shownOpening }), ...(at === total && closing !== undefined ? { closing } : {}), ...(at === 0 && summary !== undefined ? { summary } : {}), ...(reviewing ? { reviewing: true } : {}), ...(completeIt === undefined ? {} : { onComplete: completeIt }), name: analyst, by: annotator, mood: mood, ...(settings.voice ? { voice: text } : {}), text: text })] }), _jsxs("section", { class: "you-block", "aria-label": sides[player].name, children: [_jsx("div", { class: "chat", children: _jsx(Compose, { lines: analysisTalk(talk, studied.name), newestFirst: false, spent: 0, draft: draft, onDraft: (words) => {
|
|
415
440
|
setDraft(words);
|
|
416
441
|
setUnsent(undefined);
|
|
417
442
|
}, onSend: () => sendWords({ game: studied.name, at }), canMove: false, name: name, problem: unsent, about: { ...(at === 0 ? {} : { ply: plies[at - 1] }), onShow: goTo }, text: text }) }), studiedCard(player, "you")] })] }))
|
|
418
|
-
: (_jsxs(_Fragment, { children: [_jsxs("section", { class: "ai-block", "aria-label": name, children: [_jsx(PlayerCard, { place: "ai", picture: thinker(48), name: table === undefined ? name : _jsx(AiNameButton, { name: name, table: table, onOpen:
|
|
443
|
+
: (_jsxs(_Fragment, { children: [_jsxs("section", { class: "ai-block", "aria-label": name, children: [_jsx(PlayerCard, { place: "ai", picture: thinker(48), name: table === undefined ? name : _jsx(AiNameButton, { name: name, table: table, onOpen: askChat, text: text }), signed: signed, side: idle ? undefined : ai, ...(idle ? {} : { level: text.start.levels[level] }), taken: material.taken[ai], ahead: ahead(ai), clock: clockOf(ai), text: text }), asleepNote, _jsx(AiWords, { words: aiWords(plies, ai, talk), newestFirst: !flipped, ...(settings.voice ? { voice: text } : {}) })] }), _jsxs("section", { class: "you-block", "aria-label": profile.name, children: [_jsx(Compose, { lines: said, newestFirst: flipped,
|
|
419
444
|
// Between games, and before the first, the player writes freely, a line at a time, as the AI does (D-74, D-77).
|
|
420
445
|
spent: going ? playerSpent(plies, ai, talk) : 0, draft: draft, onDraft: (words) => {
|
|
421
446
|
setDraft(words);
|
|
422
447
|
setUnsent(undefined);
|
|
423
|
-
}, onSend: sendWords, canMove: playerToMove, name: name, problem: unsent, text: text }), _jsx(PlayerCard, { place: "you", picture:
|
|
448
|
+
}, onSend: sendWords, canMove: playerToMove, name: name, problem: unsent, text: text }), _jsx(PlayerCard, { place: "you", picture: picture, name: _jsx(NameButton, { profile: profile, onOpen: () => setDialog("profiles"), text: text }), side: idle ? undefined : player, taken: material.taken[player], ahead: ahead(player), turn: playerToMove, clock: _jsxs(_Fragment, { children: [clockOf(player), startClocks, nextGame] }), text: text })] })] })) }), _jsxs("div", {
|
|
424
449
|
// Between games the board goes off (D-77): a game the page saw end once its effects are over, its final position less off than the table set.
|
|
425
450
|
class: between && !justEnded ? "board-wrap off" : "board-wrap", onWheel: wheel,
|
|
426
451
|
// A press on the board while the player looks back brings the game as it is: the next one plays.
|
|
427
452
|
onPointerDownCapture: viewing === undefined || studied !== undefined ? undefined : (event) => event.button === 0 && goTo(total), children: [_jsx(Board, { fen: shown.position.fen, bottom: flipped ? ai : player, lastMove: shown.lastMove, pointed: pointed, ...(helps === undefined ? {} : { helps }), ...(mapping && helps !== undefined ? { control: helps.strategy.control } : {}), bare: bare && helps !== undefined, ...(review?.badge === undefined ? {} : { badge: { badge: review.badge, name: text.badges[review.badge] } }), ...(drawn === undefined ? {} : { drawn }), nextMove: next, jumped: jumped, check: shown.position.check, end: boardEnd, player: playerToMove && pending === undefined && viewing === undefined ? player : undefined, selected: selected, targets: selected === undefined || viewing !== undefined ? [] : live.legalMoves(selected), dots: settings.dots, names: settings.squareNames, notice: viewing === undefined ? refusal : undefined, promotion: promoting === undefined || viewing !== undefined || !playerToMove ? undefined : { ...promoting, side: player }, onPromote: (piece) => piece === undefined ? setPromoting(undefined) : play(promoting.from, promoting.to, piece), onSelect: select, onMove: play, onRefuse: refuse, text: text }), folds.talk !== "open" && (asleepNote !== null || clockWaits)
|
|
428
453
|
? (_jsxs("div", { class: "over-board", children: [asleepNote, startClocks] }))
|
|
429
|
-
: null] }), studied === undefined ? null : (_jsx("div", { class: "board-steps", children: _jsx(Steps, { shown: at, total: total, onStep: step, text: text }) })), helps === undefined ? null : (_jsxs("div", { class: "board-map", children: [legendAt(true), _jsx(BareButton, { bare: bare, onBare: setBare, text: text }), _jsx(MapButton, { mapping: mapping, onMap: setMapping, text: text }), hint === undefined || viewing !== undefined ? null : _jsx(HintLine, { hint: hint, open: hintOpen, onOpen: openTheHint, text: text })] })), _jsx(GameColumn, { plies: idle ? [] : plies, result: ended ? result : undefined, how: how, lit: justEnded, started: idle ? undefined : played?.started, move: live.position.moveNumber, toMove: live.position.turn, opening: opening, helps: helps === undefined ? undefined : (_jsx(HelpsCard, { helps: helps, judged: judgments, hint: viewing === undefined ? hint : undefined, hintOpen: hintOpen, onHint: openTheHint, onPoint: setPointed, onMap: setMapping, mapping: mapping, onBare: setBare, bare: bare, legend: legend, text: text })), ...(helps === undefined && legendRows.length > 0 ? { legend } : {}), ...(standings === undefined ? {} : { standings }), ...(studied === undefined ? {} : {
|
|
454
|
+
: null] }), studied === undefined ? null : (_jsx("div", { class: "board-steps", children: _jsx(Steps, { shown: at, total: total, onStep: step, text: text }) })), helps === undefined ? null : (_jsxs("div", { class: "board-map", children: [legendAt(true), _jsx(BareButton, { bare: bare, onBare: setBare, text: text }), _jsx(MapButton, { mapping: mapping, onMap: setMapping, text: text }), hint === undefined || viewing !== undefined ? null : _jsx(HintLine, { hint: hint, open: hintOpen, onOpen: openTheHint, text: text })] })), _jsx(GameColumn, { plies: idle ? [] : plies, result: ended ? result : undefined, how: how, lit: justEnded, started: idle ? undefined : played?.started, move: live.position.moveNumber, toMove: live.position.turn, opening: opening, helps: helps === undefined ? undefined : (_jsx(HelpsCard, { helps: helps, judged: judgments, hint: viewing === undefined ? hint : undefined, hintOpen: hintOpen, onHint: openTheHint, onPoint: setPointed, onMap: setMapping, mapping: mapping, onBare: setBare, bare: bare, legend: legend, text: text })), ...(helps === undefined && legendRows.length > 0 ? { legend } : {}), ...(standings === undefined ? {} : { standings }), ...(studied === undefined ? {} : {
|
|
455
|
+
analysis: {
|
|
456
|
+
by: analyst,
|
|
457
|
+
...(summary === undefined ? {} : { summary }),
|
|
458
|
+
...(reviewing ? { reviewing: true } : {}),
|
|
459
|
+
...(completeIt === undefined ? {} : { onComplete: completeIt }),
|
|
460
|
+
},
|
|
461
|
+
}), shownPlies: at, onShow: goTo, onStep: step, ...(studied === undefined ? {} : { onDownload: () => download(studied.pgn, pgnName(live.record.tags, "analysis")) }), ...(total === 0 ? {} : { onSaveMoves: () => download(writePgn(movesOnly(live.record)), pgnName(live.record.tags)) }), ...(between && game !== null && replayed.record.moves.length > 0 ? { onAnalyse: () => void studyGame({ last: true }) } : {}), text: text }), _jsx(PageFoot, { version: served.version, text: text })] })] }) }));
|
|
430
462
|
}
|
|
431
463
|
/** The picture of a player of the game in analysis: the player's own, the AI's Torretta with its mood, or the plain face of someone else. */
|
|
432
464
|
function pictureOf({ who }, profile, size, mood) {
|
|
433
|
-
return who === "ai" ? _jsx(Torretta, { size: size, mood: mood }) : _jsx(PlayerPicture, { profile: who === "player" ? profile : undefined, size: size });
|
|
465
|
+
return who === "ai" ? _jsx(Torretta, { size: size, mood: mood, bust: true }) : _jsx(PlayerPicture, { profile: who === "player" ? profile : undefined, size: size });
|
|
434
466
|
}
|
|
435
467
|
/**
|
|
436
468
|
* Plays the sound of each move the game gains while the page watches it, the AI's and the player's, of its end without a move and of a draw offered:
|
package/dist/page/app/served.js
CHANGED
|
@@ -13,6 +13,8 @@ export function useServed(onNewGame) {
|
|
|
13
13
|
const reload = useRef(() => { });
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
let latest = 0;
|
|
16
|
+
// The Chesspal version of the first answer: a server of another, after an update, sends data this page may not read.
|
|
17
|
+
let pageVersion;
|
|
16
18
|
const load = () => {
|
|
17
19
|
// Two reads in flight may answer out of order: only the last one asked counts.
|
|
18
20
|
const asked = ++latest;
|
|
@@ -22,6 +24,11 @@ export function useServed(onNewGame) {
|
|
|
22
24
|
if (asked !== latest) {
|
|
23
25
|
return;
|
|
24
26
|
}
|
|
27
|
+
pageVersion ??= body.version;
|
|
28
|
+
if (body.version !== undefined && body.version !== pageVersion) {
|
|
29
|
+
window.location.reload();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
25
32
|
setProblem(body.error);
|
|
26
33
|
if (body.error === undefined) {
|
|
27
34
|
setData(body);
|
|
@@ -49,17 +56,22 @@ export function useServed(onNewGame) {
|
|
|
49
56
|
* it sends the player, or undefined.
|
|
50
57
|
*/
|
|
51
58
|
export async function post(path, body, text, name) {
|
|
59
|
+
const sent = await ask(path, body, text, name);
|
|
60
|
+
return "refused" in sent ? sent.refused : undefined;
|
|
61
|
+
}
|
|
62
|
+
/** Sends a request to the server as `post` does: its answer, or the reason it was refused. */
|
|
63
|
+
export async function ask(path, body, text, name) {
|
|
52
64
|
try {
|
|
53
65
|
const response = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
54
66
|
if (response.ok) {
|
|
55
|
-
return
|
|
67
|
+
return { answer: await response.json().catch(() => ({})) };
|
|
56
68
|
}
|
|
57
69
|
const refused = (await response.json());
|
|
58
70
|
const known = refused.refusal === undefined ? undefined : text.refused[refused.refusal];
|
|
59
|
-
return { text: known?.(refused.most ?? 0, name) ?? refused.error ?? response.statusText, words: refused.refusal?.startsWith("words-") === true };
|
|
71
|
+
return { refused: { text: known?.(refused.most ?? 0, name) ?? refused.error ?? response.statusText, words: refused.refusal?.startsWith("words-") === true } };
|
|
60
72
|
}
|
|
61
73
|
catch (error) {
|
|
62
|
-
return { text: String(error), words: false };
|
|
74
|
+
return { refused: { text: String(error), words: false } };
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
/** Sends the player's move to the server, which plays it as the chat would, with the words written for the AI: the reason it was refused, or undefined. */
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
// The Torretta (D-45, D-46): the opponent and the logo, a rook that is a robot. Its screen shows the AI's mood (D-14, D-70): only the eyes, the mouth and the light change.
|
|
3
3
|
import { useRef } from "preact/hooks";
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* The Torretta at a size in pixels, with the face of a mood (neutral by default); with a label it is an image a screen reader names, else a decoration.
|
|
6
|
+
* As a bust it is its own profile picture, on a player's card: a disc that frames it from the antenna to the screen, so that its face reads (Stefano).
|
|
7
|
+
*/
|
|
8
|
+
export function Torretta({ size, label, mood = "neutral", bust = false }) {
|
|
6
9
|
// The face pops in when the mood changes, never when the Torretta first shows (the logo, a card, a dialog).
|
|
7
10
|
const changed = useChanged(mood);
|
|
8
|
-
return (_jsxs("svg", { class: `torretta mood-${mood}`, viewBox:
|
|
11
|
+
return (_jsxs("svg", { class: `torretta mood-${mood}${bust ? " bust" : ""}`, viewBox: bust ? bustFrame : fullFrame, width: size, height: size, role: label === undefined ? undefined : "img", "aria-label": label, "aria-hidden": label === undefined ? "true" : undefined, children: [bust ? _jsx("circle", { class: "back", cx: "64", cy: "56", r: "48" }) : null, _jsx("path", { class: "antenna", d: "M64 31V19" }), _jsx("circle", { class: "light", cx: "64", cy: "16", r: "5" }), _jsx("rect", { class: "shade", x: "29", y: "64", width: "12", height: "26", rx: "2" }), _jsx("rect", { class: "shade", x: "87", y: "64", width: "12", height: "26", rx: "2" }), _jsx("path", { class: "body", d: "M34 30H47V40H57V30H71V40H81V30H94V54L89 58L92 106H36L39 58L34 54Z" }), _jsx("rect", { class: "shade", x: "30", y: "104", width: "68", height: "12", rx: "4" }), _jsx("rect", { class: "screen", x: "44", y: "63", width: "40", height: "32", rx: "7" }), _jsx("g", { class: changed ? "expression changed" : "expression", children: _jsx(Face, { mood: mood }) }, mood)] }));
|
|
9
12
|
}
|
|
13
|
+
// The whole figure's frame: the square around it, from the antenna's light (y 11) to the base (y 116), two units clear of each, so that it fills its size.
|
|
14
|
+
const fullFrame = "9.5 9 109 109";
|
|
15
|
+
// The bust's frame, the square around its disc: the antenna's light three units under its top, the base left out below.
|
|
16
|
+
const bustFrame = "16 8 96 96";
|
|
10
17
|
/** Whether the mood has changed since the Torretta first showed: true from its first change on, so a face already shown never starts its entrance again. */
|
|
11
18
|
function useChanged(mood) {
|
|
12
19
|
const first = useRef(mood);
|
|
@@ -291,6 +291,16 @@ h3 + .switch {
|
|
|
291
291
|
translate: 14px 0;
|
|
292
292
|
}
|
|
293
293
|
|
|
294
|
+
/* A switch that cannot be used here: faded, and its tooltip says why */
|
|
295
|
+
.switch:has(input:disabled),
|
|
296
|
+
.switch input:disabled {
|
|
297
|
+
cursor: help;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.switch:has(input:disabled) > * {
|
|
301
|
+
opacity: 0.5;
|
|
302
|
+
}
|
|
303
|
+
|
|
294
304
|
@media (prefers-reduced-motion: reduce) {
|
|
295
305
|
.switch input,
|
|
296
306
|
.switch input::after {
|
|
@@ -11,11 +11,7 @@ import { Dialog } from "./dialog.js";
|
|
|
11
11
|
export function Wizard({ text, hasGame, account, dataFolder, onMade }) {
|
|
12
12
|
return (_jsxs("main", { class: "wizard", children: [_jsx(Torretta, { size: 56 }), _jsx("h1", { children: text.profile.welcome }), _jsxs("p", { class: "lead", children: [text.profile.lead, hasGame ? _jsx("span", { children: text.profile.gameMoves }) : null] }), _jsx(ProfileForm, { text: text, submit: text.profile.start, suggestion: account, onMade: onMade }), _jsx(DataFolder, { folders: dataFolder, text: text })] }));
|
|
13
13
|
}
|
|
14
|
-
/** The player's
|
|
15
|
-
export function PictureButton({ profile, onOpen, text }) {
|
|
16
|
-
return (_jsxs("button", { class: "me", type: "button", title: text.profile.editProfile, "aria-label": text.profile.yourPicture, "aria-haspopup": "dialog", onClick: onOpen, children: [_jsx(PlayerPicture, { profile: profile }), _jsx("span", { class: "me-cover", children: pencilIcon }), _jsx("span", { class: "me-badge", children: pencilIcon })] }));
|
|
17
|
-
}
|
|
18
|
-
/** The player's name on their card: a click opens the menu of the players. */
|
|
14
|
+
/** The player's name on their card: a click opens the menu of the players, as their picture in the header does; from there the pencil opens their profile. */
|
|
19
15
|
export function NameButton({ profile, onOpen, text }) {
|
|
20
16
|
return (_jsxs("button", { class: "profile-name", type: "button", title: text.profile.changePlayer, "aria-label": `${profile.name}: ${text.profile.changePlayer}`, "aria-haspopup": "dialog", onClick: onOpen, children: [_jsx("span", { children: profile.name }), _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M7 10L12 15L17 10" }) })] }));
|
|
21
17
|
}
|