chesspal 0.6.0 → 0.7.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 +3 -3
- package/dist/commands/analysis/analyse.js +7 -2
- package/dist/commands/analysis/annotate.js +6 -2
- package/dist/commands/answers/agents.d.ts +2 -0
- package/dist/commands/answers/agents.js +5 -0
- package/dist/commands/answers/index.d.ts +2 -2
- package/dist/commands/answers/index.js +2 -2
- package/dist/commands/answers/lines.js +3 -1
- package/dist/commands/answers/say.d.ts +7 -1
- package/dist/commands/answers/say.js +25 -7
- package/dist/commands/answers/study.d.ts +27 -5
- package/dist/commands/answers/study.js +121 -10
- package/dist/commands/game/look.d.ts +1 -0
- package/dist/commands/game/look.js +8 -8
- package/dist/commands/game/position.d.ts +1 -0
- package/dist/commands/game/position.js +5 -5
- package/dist/commands/index.d.ts +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/moods.d.ts +2 -0
- package/dist/core/moods.js +11 -2
- package/dist/games/analysis.d.ts +11 -0
- package/dist/games/analysis.js +23 -0
- package/dist/games/archive.d.ts +9 -1
- package/dist/games/archive.js +18 -1
- package/dist/games/current-game.d.ts +23 -1
- package/dist/games/current-game.js +6 -1
- package/dist/games/index.d.ts +3 -3
- package/dist/games/index.js +2 -2
- package/dist/page/app/app.js +38 -6
- package/dist/page/game/column.js +3 -2
- package/dist/page/game/game.css +34 -5
- package/dist/page/game/move-list.js +3 -3
- package/dist/page/table/window.js +8 -5
- package/dist/page/talk/chat.css +8 -0
- package/dist/page/talk/talk.js +20 -2
- package/dist/page/text.js +20 -2
- package/dist/server/bell/bell.d.ts +5 -2
- package/dist/server/bell/bell.js +36 -12
- package/dist/server/page/game.js +34 -4
- package/dist/server/page/page.js +1 -1
- package/dist/server/page/table.js +2 -2
- package/instructions/analysis.md +2 -2
- package/instructions/opponent.md +1 -1
- package/package.json +1 -1
package/dist/games/analysis.d.ts
CHANGED
|
@@ -38,5 +38,16 @@ export declare function startAnalysis(folder: string, game: string, now: Date, {
|
|
|
38
38
|
export declare function forgetAnalyst(folder: string): void;
|
|
39
39
|
/** The game in analysis, or none when there is none, or analysis.json cannot be read. */
|
|
40
40
|
export declare function gameInAnalysis(folder: string): Analysis | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Stops the analysis of the game in analysis at the player's word on the board page: the page goes back to the game over, status no longer names it, and
|
|
43
|
+
* the review written so far stays on the game. analysis.json keeps the game stopped, with the analyst that held it, until the next analysis starts: their
|
|
44
|
+
* later writes are refused. False when another game, or none, is in analysis.
|
|
45
|
+
*/
|
|
46
|
+
export declare function stopAnalysis(folder: string, game: string): boolean;
|
|
47
|
+
/** The game whose analysis the player stopped on the board page, with the analyst that held it then; none once another analysis starts. */
|
|
48
|
+
export declare function stoppedAnalysis(folder: string): {
|
|
49
|
+
game: string;
|
|
50
|
+
analyst?: AnalystRun;
|
|
51
|
+
} | undefined;
|
|
41
52
|
/** Ends the analysis, when a new game starts or its review is deleted. */
|
|
42
53
|
export declare function endAnalysis(folder: string): void;
|
package/dist/games/analysis.js
CHANGED
|
@@ -50,6 +50,29 @@ export function gameInAnalysis(folder) {
|
|
|
50
50
|
return undefined;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Stops the analysis of the game in analysis at the player's word on the board page: the page goes back to the game over, status no longer names it, and
|
|
55
|
+
* the review written so far stays on the game. analysis.json keeps the game stopped, with the analyst that held it, until the next analysis starts: their
|
|
56
|
+
* later writes are refused. False when another game, or none, is in analysis.
|
|
57
|
+
*/
|
|
58
|
+
export function stopAnalysis(folder, game) {
|
|
59
|
+
const analysis = gameInAnalysis(folder);
|
|
60
|
+
if (analysis?.game !== game) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
writeAtomically(join(folder, fileName), `${JSON.stringify({ stopped: game, ...withAnalyst(analysis.analyst) }, null, 2)}\n`);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/** The game whose analysis the player stopped on the board page, with the analyst that held it then; none once another analysis starts. */
|
|
67
|
+
export function stoppedAnalysis(folder) {
|
|
68
|
+
try {
|
|
69
|
+
const stopped = JSON.parse(readFileSync(join(folder, fileName), "utf8"));
|
|
70
|
+
return typeof stopped.stopped === "string" ? { game: stopped.stopped, ...withAnalyst(isAnalystRun(stopped.analyst) ? stopped.analyst : undefined) } : undefined;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
53
76
|
/** Ends the analysis, when a new game starts or its review is deleted. */
|
|
54
77
|
export function endAnalysis(folder) {
|
|
55
78
|
rmSync(join(folder, fileName), { force: true });
|
package/dist/games/archive.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type GameRecord } from "../core/index.ts";
|
|
1
|
+
import { type GameRecord, type Mood } from "../core/index.ts";
|
|
2
2
|
import { type CurrentGame } from "./current-game.ts";
|
|
3
3
|
/** Where the game goes in the archive, in the player's folder: named by when it started, in the player's time, games/2026-11-02-153012.pgn. */
|
|
4
4
|
export declare function archiveName(current: CurrentGame): string;
|
|
@@ -22,6 +22,14 @@ export declare function archiveEnded(folder: string, current: CurrentGame, openi
|
|
|
22
22
|
archive?: string;
|
|
23
23
|
warning?: string;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Gives the mood the AI ends with to a game over already in the archive, on its last move, beside the review the analyst and Chesspal may have written there
|
|
27
|
+
* since; archives the game when it is not there.
|
|
28
|
+
*/
|
|
29
|
+
export declare function archiveMood(folder: string, current: CurrentGame, mood: Mood, opening: OpeningTags): {
|
|
30
|
+
archive?: string;
|
|
31
|
+
warning?: string;
|
|
32
|
+
};
|
|
25
33
|
/**
|
|
26
34
|
* Writes the coach's judgments into a game over already in the archive, beside what the analyst may have written there since (Federico);
|
|
27
35
|
* archives the game when it is not there.
|
package/dist/games/archive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The finished games, as PGN in games/ (D-9, D-41), with the AI's plans for the analyst (D-21); and the games played elsewhere, kept to study in study/ (D-68).
|
|
2
2
|
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { movesOnly, pgnDate, readPgn, readPgnTags, readPgnUnplayed, unsigned, withAiHost, withAiModel, writePgn } from "../core/index.js";
|
|
4
|
+
import { movesOnly, pgnDate, readPgn, readPgnTags, readPgnUnplayed, recordWithMood, unsigned, withAiHost, withAiModel, writePgn } from "../core/index.js";
|
|
5
5
|
import { aiHostOf, aiModelOf } from "./ai-model.js";
|
|
6
6
|
import { coachWords, withCoach } from "./coach.js";
|
|
7
7
|
import { endOf, loadGame } from "./current-game.js";
|
|
@@ -65,6 +65,23 @@ export function archiveEnded(folder, current, opening) {
|
|
|
65
65
|
return { warning: `${error.message} The game is saved in game.json: it goes to the archive when a new game starts.` };
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Gives the mood the AI ends with to a game over already in the archive, on its last move, beside the review the analyst and Chesspal may have written there
|
|
70
|
+
* since; archives the game when it is not there.
|
|
71
|
+
*/
|
|
72
|
+
export function archiveMood(folder, current, mood, opening) {
|
|
73
|
+
const name = archivedName(folder, current);
|
|
74
|
+
if (name === undefined) {
|
|
75
|
+
return archiveEnded(folder, current, opening);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
writeAtomically(join(folder, name), writePgn(recordWithMood(archivedGame(folder, name), mood)));
|
|
79
|
+
return { archive: name };
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return { warning: `Chesspal could not write the mood into ${name}: ${error.message}` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
68
85
|
/**
|
|
69
86
|
* Writes the coach's judgments into a game over already in the archive, beside what the analyst may have written there since (Federico);
|
|
70
87
|
* archives the game when it is not there.
|
|
@@ -28,11 +28,26 @@ export interface TalkLine {
|
|
|
28
28
|
readonly agent?: true;
|
|
29
29
|
readonly shown?: Shown;
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* A position of a game in analysis: the game's name, how many of its moves the page showed, and the last of them numbered, 52...Kf6; none at the start. With
|
|
33
|
+
* the variation the player played there (D-116), on the board or set aside.
|
|
34
|
+
*/
|
|
32
35
|
export interface Shown {
|
|
33
36
|
readonly game: string;
|
|
34
37
|
readonly at: number;
|
|
35
38
|
readonly move?: string;
|
|
39
|
+
readonly variation?: ShownVariation;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A variation of the page's (D-116): how many of the game's moves come before it, its moves in SAN, how many of them the board showed (0 with the board back
|
|
43
|
+
* on the game), the line as PGN writes it, 12...Kf8 13.Nxe5 Bxe2, and the move shown numbered, 13.Nxe5.
|
|
44
|
+
*/
|
|
45
|
+
export interface ShownVariation {
|
|
46
|
+
readonly at: number;
|
|
47
|
+
readonly moves: readonly string[];
|
|
48
|
+
readonly cursor: number;
|
|
49
|
+
readonly text: string;
|
|
50
|
+
readonly move?: string;
|
|
36
51
|
}
|
|
37
52
|
/** How a game ended: its result, and why when the record says it or the board decided it. */
|
|
38
53
|
export interface GameEnd {
|
|
@@ -76,5 +91,12 @@ export declare const savedLine: z.ZodObject<{
|
|
|
76
91
|
game: z.ZodString;
|
|
77
92
|
at: z.ZodInt;
|
|
78
93
|
move: z.ZodOptional<z.ZodString>;
|
|
94
|
+
variation: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
at: z.ZodInt;
|
|
96
|
+
moves: z.ZodReadonly<z.ZodArray<z.ZodString>>;
|
|
97
|
+
cursor: z.ZodInt;
|
|
98
|
+
text: z.ZodString;
|
|
99
|
+
move: z.ZodOptional<z.ZodString>;
|
|
100
|
+
}, z.core.$strip>>;
|
|
79
101
|
}, z.core.$strip>>;
|
|
80
102
|
}, z.core.$strip>;
|
|
@@ -94,7 +94,12 @@ export const savedLine = z.object({
|
|
|
94
94
|
ply: z.int().min(0),
|
|
95
95
|
at: z.iso.datetime(),
|
|
96
96
|
agent: z.literal(true).optional(),
|
|
97
|
-
shown: z.object({
|
|
97
|
+
shown: z.object({
|
|
98
|
+
game: z.string(),
|
|
99
|
+
at: z.int().min(0),
|
|
100
|
+
move: z.string().optional(),
|
|
101
|
+
variation: z.object({ at: z.int().min(0), moves: z.array(z.string()).min(1).readonly(), cursor: z.int().min(0), text: z.string(), move: z.string().optional() }).optional(),
|
|
102
|
+
}).optional(),
|
|
98
103
|
});
|
|
99
104
|
const savedClock = z.object({
|
|
100
105
|
base: z.int().positive(),
|
package/dist/games/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { aiHostOf, aiModelOf, forgetTheAi, noteAiHost, noteAiModel } from "./ai-model.ts";
|
|
2
|
-
export { type Analysis, type AnalysisStart, endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis } from "./analysis.ts";
|
|
3
|
-
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, type OpeningTags, playerPgn, rewriteGame, startOfNewGame, } from "./archive.ts";
|
|
2
|
+
export { type Analysis, type AnalysisStart, endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis, stopAnalysis, stoppedAnalysis } from "./analysis.ts";
|
|
3
|
+
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveMood, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, type OpeningTags, playerPgn, rewriteGame, startOfNewGame, } from "./archive.ts";
|
|
4
4
|
export { type ChatStart, cleanSince, lastChatStart, saveChatStart } from "./chat-start.ts";
|
|
5
5
|
export { type CoachHint, type CoachJudgment, type CoachLead, coachShown, coachStarted, coachWaitingSince, type CoachWords, coachWords, forgetCoach, keepHint, keepJudgment, keepLead, startCoach, withCoach } from "./coach.ts";
|
|
6
|
-
export { bothSidesMoved, type CurrentGame, drawOfferOf, endOf, GameChanged, type GameEnd, gameStamp, loadGame, removeGame, rerunIfChanged, saveGame, type Shown, type Speaker, type TalkLine } from "./current-game.ts";
|
|
6
|
+
export { bothSidesMoved, type CurrentGame, drawOfferOf, endOf, GameChanged, type GameEnd, gameStamp, loadGame, removeGame, rerunIfChanged, saveGame, type Shown, type ShownVariation, type Speaker, type TalkLine } from "./current-game.ts";
|
|
7
7
|
export { makeFolder, renameOver, writeAtomically } from "./files.ts";
|
|
8
8
|
export { commentWithPlan, currentPlan, planOf, withoutPlans } from "./plans.ts";
|
|
9
9
|
export { forgetPositionShown, hostProcess, notePositionShown, type PositionShown, positionShownNote } from "./position-shown.ts";
|
package/dist/games/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The player's games on disk: the current one in game.json, the finished ones in games/ (D-27, D-41), the games kept to study in study/ (D-68). Below the commands, above the core.
|
|
2
2
|
export { aiHostOf, aiModelOf, forgetTheAi, noteAiHost, noteAiModel } from "./ai-model.js";
|
|
3
|
-
export { endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis } from "./analysis.js";
|
|
4
|
-
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, playerPgn, rewriteGame, startOfNewGame, } from "./archive.js";
|
|
3
|
+
export { endAnalysis, forgetAnalyst, gameInAnalysis, startAnalysis, stopAnalysis, stoppedAnalysis } from "./analysis.js";
|
|
4
|
+
export { archiveCoached, archivedGame, archivedGames, archivedGameUnplayed, archivedName, archivedTags, archivedWhen, archiveEnded, archiveGame, archiveMood, archiveName, deleteGame, GameMissing, gameStemOf, hasListTags, isGameName, isOverOnTable, keepToStudy, keptGame, keptGames, listedName, movesPgn, playerPgn, rewriteGame, startOfNewGame, } from "./archive.js";
|
|
5
5
|
export { cleanSince, lastChatStart, saveChatStart } from "./chat-start.js";
|
|
6
6
|
export { coachShown, coachStarted, coachWaitingSince, coachWords, forgetCoach, keepHint, keepJudgment, keepLead, startCoach, withCoach } from "./coach.js";
|
|
7
7
|
export { bothSidesMoved, drawOfferOf, endOf, GameChanged, gameStamp, loadGame, removeGame, rerunIfChanged, saveGame } from "./current-game.js";
|
package/dist/page/app/app.js
CHANGED
|
@@ -73,7 +73,7 @@ export function App({ start }) {
|
|
|
73
73
|
};
|
|
74
74
|
const [dialog, setDialog] = useState(undefined);
|
|
75
75
|
const { data: served, problem, offline, reload } = useServed(() => setDialog("new-game"));
|
|
76
|
-
// Nuova partita with no chat at the table asks for one first (D-107): the chat's window, then the new game's
|
|
76
|
+
// Nuova partita with no chat at the table asks for one first (D-107): the chat's window, then the new game's once a chat starts; closed without, neither.
|
|
77
77
|
const [chatFirst, setChatFirst] = useState(false);
|
|
78
78
|
// 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).
|
|
79
79
|
const chatMissing = () => {
|
|
@@ -93,10 +93,14 @@ export function App({ start }) {
|
|
|
93
93
|
setChatToReview(served?.analysis !== undefined);
|
|
94
94
|
setDialog("chat");
|
|
95
95
|
};
|
|
96
|
-
const
|
|
96
|
+
const chatStarted = () => {
|
|
97
97
|
setDialog(chatFirst ? "new-game" : undefined);
|
|
98
98
|
setChatFirst(false);
|
|
99
99
|
};
|
|
100
|
+
const closeChat = () => {
|
|
101
|
+
setDialog(undefined);
|
|
102
|
+
setChatFirst(false);
|
|
103
|
+
};
|
|
100
104
|
const [selected, setSelected] = useState(undefined);
|
|
101
105
|
const [refusal, setRefusal] = useState(undefined);
|
|
102
106
|
// A link the page did not follow says why on the board, in the language of the settings the page has by then, until the player's next click or key.
|
|
@@ -298,6 +302,12 @@ export function App({ start }) {
|
|
|
298
302
|
setViewing(line.at >= total ? undefined : line.at);
|
|
299
303
|
}
|
|
300
304
|
};
|
|
305
|
+
// A variation a line of the talk was written on goes back on the board, at the move it showed, or at its end when it was set aside (D-116).
|
|
306
|
+
const putLineBack = (sent) => {
|
|
307
|
+
setSelected(undefined);
|
|
308
|
+
setLine({ at: sent.at, moves: sent.moves, cursor: sent.cursor > 0 ? sent.cursor : sent.moves.length });
|
|
309
|
+
setViewing(sent.at >= total ? undefined : sent.at);
|
|
310
|
+
};
|
|
301
311
|
// The cross and Esc close the variation: the board shows the game where it left it.
|
|
302
312
|
const closeLine = () => {
|
|
303
313
|
setSelected(undefined);
|
|
@@ -394,6 +404,8 @@ export function App({ start }) {
|
|
|
394
404
|
const reviewing = summary === undefined && studied?.reviewing === true && !offline;
|
|
395
405
|
// With nobody at it, a review not finished says so, and the player brings the game back to the AI (D-109).
|
|
396
406
|
const completeIt = summary === undefined && studied?.incomplete === true && !offline ? () => void studyGame({ game: studied.name }) : undefined;
|
|
407
|
+
// While it waits, the player may cancel the analysis asked, one click: what is written of the review stays, and the page goes back to the game.
|
|
408
|
+
const stopIt = reviewing ? () => act("/api/stop-analysis", { game: studied.name }) : undefined;
|
|
397
409
|
const closing = studied === undefined ? undefined : closingOf(live.record);
|
|
398
410
|
// How far the review has got, counted as status counts it for the AI: who is ahead after how many of the game's moves.
|
|
399
411
|
const count = reviewing || completeIt !== undefined ? reviewCount(plies) : undefined;
|
|
@@ -499,7 +511,7 @@ export function App({ start }) {
|
|
|
499
511
|
setRefusal({ text: words, square: from, piece: pieces.get(from), explanation: explain(illegal, to, king) });
|
|
500
512
|
}
|
|
501
513
|
};
|
|
502
|
-
// In analysis the words go with the game and the move shown (D-101).
|
|
514
|
+
// In analysis the words go with the game and the move shown (D-101), and the variation played there, on the board or set aside (D-116).
|
|
503
515
|
const sendWords = (position) => {
|
|
504
516
|
const words = draft;
|
|
505
517
|
// /clear and the other commands are for Claude's app: written here they would reach the AI as words (Sebastiano's playtest).
|
|
@@ -524,10 +536,29 @@ export function App({ start }) {
|
|
|
524
536
|
};
|
|
525
537
|
// 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).
|
|
526
538
|
const studiedCard = (side, place) => sides === undefined ? null : (_jsx(PlayerCard, { place: place, picture: pictureOf(sides[side], profile, served.profiles, 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 }));
|
|
539
|
+
// Where the words go in analysis: the game, the move shown, and the variation, on the board or set aside (D-116).
|
|
540
|
+
const shownIn = (game) => line === undefined ? { game, at } : { game, at, variation: line };
|
|
541
|
+
// «Analyse» on the variation sends the player's request, as their words would (D-116).
|
|
542
|
+
const analyseLine = () => {
|
|
543
|
+
if (studied !== undefined) {
|
|
544
|
+
setUnsent(undefined);
|
|
545
|
+
void sendMessage(text.variation.analyseWords, text, name, shownIn(studied.name)).then((refused) => setUnsent(refused?.text));
|
|
546
|
+
}
|
|
547
|
+
};
|
|
527
548
|
// The variation as the column and the steps show it.
|
|
528
549
|
const lineView = !analysing || line === undefined || lineNumbered === undefined
|
|
529
550
|
? undefined
|
|
530
|
-
: {
|
|
551
|
+
: {
|
|
552
|
+
plies: lineNumbered,
|
|
553
|
+
at: line.at,
|
|
554
|
+
cursor: line.cursor,
|
|
555
|
+
onShow: showLine,
|
|
556
|
+
onClose: closeLine,
|
|
557
|
+
onBack: () => goTo(line.at),
|
|
558
|
+
onAnalyse: analyseLine,
|
|
559
|
+
canAnalyse: !asleep && !offline,
|
|
560
|
+
analyst: name,
|
|
561
|
+
};
|
|
531
562
|
// 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
|
|
532
563
|
// chat at the table, opens the chat's window: the analyst works with the chat's model (D-109).
|
|
533
564
|
const studyGame = async (studied) => {
|
|
@@ -540,7 +571,7 @@ export function App({ start }) {
|
|
|
540
571
|
setDialog(toReview ? "chat" : undefined);
|
|
541
572
|
return undefined;
|
|
542
573
|
};
|
|
543
|
-
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), onProfile: () => setDialog("profile"), you: profile.name, accounts: profile.accounts, others: othersOf(served.profiles, profile.id), ai: name, text: text }), served.update === undefined || served.table === undefined
|
|
574
|
+
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, onStarted: chatStarted, 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), onProfile: () => setDialog("profile"), you: profile.name, accounts: profile.accounts, others: othersOf(served.profiles, profile.id), ai: name, text: text }), served.update === undefined || served.table === undefined
|
|
544
575
|
? null
|
|
545
576
|
: (_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
|
|
546
577
|
? _jsx("p", { class: "outdated", role: "status", children: text.outdated(name) })
|
|
@@ -552,7 +583,7 @@ export function App({ start }) {
|
|
|
552
583
|
? (_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 || onTheLine ? {} : { opening: shownOpening }), ...(at === total && closing !== undefined && !onTheLine ? { closing } : {}), ...(at === 0 && summary !== undefined && !onTheLine ? { summary } : {}), ...(reviewing ? { reviewing: true } : {}), ...(count === undefined ? {} : { count }), ...(completeIt === undefined ? {} : { onComplete: completeIt }), name: analyst, by: annotator, mood: mood, ...(voiced ? { 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) => {
|
|
553
584
|
setDraft(words);
|
|
554
585
|
setUnsent(undefined);
|
|
555
|
-
}, onSend: () => sendWords(
|
|
586
|
+
}, onSend: () => sendWords(shownIn(studied.name)), canMove: false, name: name, problem: unsent, about: { ...(at === 0 ? {} : { ply: plies[at - 1] }), onShow: goTo, onLine: putLineBack }, ...(chatName === undefined ? {} : { to: chatName }), text: text }) }), studiedCard(player, "you")] })] }))
|
|
556
587
|
: (_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, ...(voiced ? { voice: text } : {}) })] }), _jsxs("section", { class: "you-block", "aria-label": profile.name, children: [_jsx(Compose, { lines: said, newestFirst: flipped,
|
|
557
588
|
// Between games, and before the first, the player writes freely, a line at a time, as the AI does (D-74, D-77).
|
|
558
589
|
spent: going ? playerSpent(plies, ai, talk) : 0, draft: draft, onDraft: (words) => {
|
|
@@ -577,6 +608,7 @@ export function App({ start }) {
|
|
|
577
608
|
...(summary === undefined ? {} : { summary }),
|
|
578
609
|
...(reviewing ? { reviewing: true } : {}),
|
|
579
610
|
...(count === undefined ? {} : { count }),
|
|
611
|
+
...(stopIt === undefined ? {} : { onStop: stopIt }),
|
|
580
612
|
...(completeIt === undefined ? {} : { onComplete: completeIt }),
|
|
581
613
|
},
|
|
582
614
|
}), shownPlies: at, ...(lineView === undefined ? {} : { line: lineView }), 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 })] })] }) }));
|
package/dist/page/game/column.js
CHANGED
|
@@ -12,10 +12,11 @@ import { ReviewProgress } from "./review-progress.js";
|
|
|
12
12
|
* (D-69) says above the moves that the AI is going over it, until its word on the whole game comes, which the talk shows.
|
|
13
13
|
*/
|
|
14
14
|
export function GameColumn({ plies, result, how, lit, started, move, toMove, opening, outOfBook, helps, legend, standings, analysis, shownPlies, line, onShow, onStep, onDownload, onSaveMoves, onAnalyse, text }) {
|
|
15
|
-
|
|
15
|
+
// In a game the bar waits for the coach's first estimate: a host that runs no coach gives none (Stefano).
|
|
16
|
+
const bar = standings === undefined || (analysis === undefined && standings.length === 0) ? undefined : _jsx(LeadBar, { standing: standingAt(standings, shownPlies), shown: shownPlies, text: text });
|
|
16
17
|
return (_jsx("aside", { class: "side", id: "game", "aria-label": text.game, children: started === undefined && analysis === undefined
|
|
17
18
|
? (_jsxs(_Fragment, { children: [_jsx("p", { class: "card empty", children: text.noGame }), onAnalyse === undefined ? null : _jsx(AnalyseButton, { label: text.study.analyseLast, onAnalyse: onAnalyse })] }))
|
|
18
|
-
: (_jsxs(_Fragment, { children: [_jsxs("div", { class: "side-cards", children: [_jsx(GameCard, { plies: plies, result: result, how: how, lit: lit, started: started, move: move, toMove: toMove, bar: bar, text: text }), analysis?.reviewing !== true ? null : (_jsxs("div", { class: "card review waiting", children: [
|
|
19
|
+
: (_jsxs(_Fragment, { children: [_jsxs("div", { class: "side-cards", children: [_jsx(GameCard, { plies: plies, result: result, how: how, lit: lit, started: started, move: move, toMove: toMove, bar: bar, text: text }), analysis?.reviewing !== true ? null : (_jsxs("div", { class: "card review waiting", children: [_jsxs("p", { children: [_jsx("span", { role: "status", children: text.reviewing(analysis.by) }), analysis.onStop === undefined ? null : (_jsx("button", { type: "button", class: "stop-review", title: text.stopReview, "aria-label": text.stopReview, onClick: analysis.onStop, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M7 7L17 17M17 7L7 17" }) }) }))] }), analysis.count === undefined ? null : _jsx(ReviewProgress, { count: analysis.count, label: text.reviewing(analysis.by) })] })), analysis?.onComplete === undefined ? null : (_jsxs("div", { class: "card review incomplete", children: [_jsxs("p", { children: [_jsx("span", { role: "status", children: text.incomplete }), _jsx("button", { type: "button", title: text.completeHint, "aria-label": text.completeHint, onClick: analysis.onComplete, children: text.complete })] }), analysis.count === undefined || analysis.count.done === 0 ? null : _jsx(ReviewProgress, { count: analysis.count, label: text.incomplete, stopped: true })] })), analysis === undefined || standings === undefined ? null : _jsx(LeadChart, { plies: plies, standings: standings, shown: shownPlies, onShow: onShow, text: text }), opening === undefined ? null : _jsx(OpeningCard, { opening: opening, ...(outOfBook === undefined ? {} : { outOfBook }), text: text }), helps] }), _jsxs("div", { class: "side-foot", children: [result !== undefined && plies.length === 0 ? null : _jsx(MoveList, { plies: plies, result: result, shown: shownPlies, ...(line === undefined ? {} : { line }), onShow: onShow, tools: legend, text: text }), onDownload === undefined && onSaveMoves === undefined ? null : (_jsxs("div", { class: "card analysis-actions", children: [onDownload === undefined ? null : _jsx(SaveButton, { label: text.study.download, hint: text.study.downloadHint, onSave: onDownload }), onSaveMoves === undefined ? null : _jsx(SaveButton, { label: text.saveMoves, hint: text.saveMovesHint, onSave: onSaveMoves })] })), onAnalyse === undefined ? null : _jsx(AnalyseButton, { label: text.study.analyseIt, onAnalyse: onAnalyse }), _jsx(Steps, { shown: shownPlies, total: plies.length, ...stepsLine(line, plies), onStep: onStep, text: text })] })] })) }));
|
|
19
20
|
}
|
|
20
21
|
/** A button that saves the game as a file in the player's downloads, its tooltip saying what the file holds. */
|
|
21
22
|
function SaveButton({ label, hint, onSave }) {
|
package/dist/page/game/game.css
CHANGED
|
@@ -338,6 +338,7 @@
|
|
|
338
338
|
color: var(--soft);
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
.review.waiting > p,
|
|
341
342
|
.review.incomplete > p,
|
|
342
343
|
.say.incomplete > p {
|
|
343
344
|
display: flex;
|
|
@@ -605,8 +606,33 @@
|
|
|
605
606
|
opacity: 0.85;
|
|
606
607
|
}
|
|
607
608
|
|
|
608
|
-
/* The
|
|
609
|
-
.
|
|
609
|
+
/* The button that asks the AI to analyse it, small in the accent beside the cross; off while no chat hears the player */
|
|
610
|
+
.analyse-line {
|
|
611
|
+
flex: none;
|
|
612
|
+
min-height: 24px;
|
|
613
|
+
padding: 1px 8px;
|
|
614
|
+
border: 1px solid var(--accent);
|
|
615
|
+
border-radius: 6px;
|
|
616
|
+
background: none;
|
|
617
|
+
color: var(--accent);
|
|
618
|
+
font: inherit;
|
|
619
|
+
font-size: 12px;
|
|
620
|
+
font-weight: 600;
|
|
621
|
+
cursor: pointer;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.analyse-line:hover:not(:disabled) {
|
|
625
|
+
background: color-mix(in oklab, var(--accent) 14%, transparent);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.analyse-line:disabled {
|
|
629
|
+
opacity: 0.45;
|
|
630
|
+
cursor: default;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* The cross that closes it, at the row's end, as the one that stops the analysis asked; a finger's 32 pixels on a touch screen */
|
|
634
|
+
.close-line,
|
|
635
|
+
.stop-review {
|
|
610
636
|
display: grid;
|
|
611
637
|
place-items: center;
|
|
612
638
|
width: 24px;
|
|
@@ -620,19 +646,22 @@
|
|
|
620
646
|
cursor: pointer;
|
|
621
647
|
}
|
|
622
648
|
|
|
623
|
-
.close-line:hover
|
|
649
|
+
.close-line:hover,
|
|
650
|
+
.stop-review:hover {
|
|
624
651
|
background: color-mix(in oklab, var(--ink) 9%, transparent);
|
|
625
652
|
color: var(--ink);
|
|
626
653
|
}
|
|
627
654
|
|
|
628
|
-
.close-line .icon
|
|
655
|
+
.close-line .icon,
|
|
656
|
+
.stop-review .icon {
|
|
629
657
|
width: 14px;
|
|
630
658
|
height: 14px;
|
|
631
659
|
stroke-width: 2.2;
|
|
632
660
|
}
|
|
633
661
|
|
|
634
662
|
@media (pointer: coarse) {
|
|
635
|
-
.close-line
|
|
663
|
+
.close-line,
|
|
664
|
+
.stop-review {
|
|
636
665
|
width: 32px;
|
|
637
666
|
height: 32px;
|
|
638
667
|
margin-block: -4px;
|
|
@@ -67,15 +67,15 @@ function Move({ ply, current, fork, onShow, text }) {
|
|
|
67
67
|
return (_jsxs("button", { type: "button", class: current ? "ply current" : fork ? "ply fork" : "ply", "aria-current": current ? "step" : undefined, onClick: onShow, children: [_jsx("span", { class: "visually-hidden", children: ply.review?.badge === undefined ? ply.san : `${ply.san}, ${text.badges[ply.review.badge]}` }), _jsx("span", { "aria-hidden": "true", children: _jsx(Figurines, { san: ply.san, side: ply.side }) }), ply.review?.badge !== undefined ? _jsx(BadgeIcon, { badge: ply.review.badge }) : ply.review?.note !== undefined ? _jsx(NoteIcon, {}) : null] }));
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
|
-
* The variation's row: its moves numbered as a PGN writes them, the one shown in the gold of the moves,
|
|
71
|
-
* shows the game, its moves still a click away.
|
|
70
|
+
* The variation's row: its moves numbered as a PGN writes them, the one shown in the gold of the moves, the button that asks the AI to analyse it, and a
|
|
71
|
+
* cross that closes it; faded while the board shows the game, its moves still a click away.
|
|
72
72
|
*/
|
|
73
73
|
function LineRow({ line, text }) {
|
|
74
74
|
return (_jsxs("li", { class: line.cursor > 0 ? "line" : "line faded", "aria-label": `${text.variation.line}: ${lineText(line.plies)}`, children: [_jsx("span", { class: "line-moves", children: line.plies.map((ply, index) => {
|
|
75
75
|
const current = index + 1 === line.cursor;
|
|
76
76
|
const number = moveNumberOf(ply, index === 0);
|
|
77
77
|
return (_jsxs("button", { type: "button", class: current ? "ply current" : "ply", "aria-current": current ? "step" : undefined, onClick: () => line.onShow(index + 1), children: [_jsxs("span", { class: "visually-hidden", children: [number, ply.san] }), _jsxs("span", { "aria-hidden": "true", children: [number === "" ? null : _jsx("span", { class: "line-number", children: number.replace("...", "…") }), _jsx(Figurines, { san: ply.san, side: ply.side })] })] }, index));
|
|
78
|
-
}) }), _jsx("button", { type: "button", class: "close-line", title: text.variation.close, "aria-label": text.variation.close, onClick: line.onClose, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M7 7L17 17M17 7L7 17" }) }) })] }));
|
|
78
|
+
}) }), _jsx("button", { type: "button", class: "analyse-line", title: line.canAnalyse ? text.variation.analyseHint(line.analyst) : text.asleepInAnalysis(line.analyst), disabled: !line.canAnalyse, onClick: line.onAnalyse, children: text.variation.analyse }), _jsx("button", { type: "button", class: "close-line", title: text.variation.close, "aria-label": text.variation.close, onClick: line.onClose, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M7 7L17 17M17 7L7 17" }) }) })] }));
|
|
79
79
|
}
|
|
80
80
|
/** The fork with the game's move the variation leaves from: it takes the board back to the game there. */
|
|
81
81
|
function ForkButton({ fork, onBack, text }) {
|
|
@@ -3,9 +3,12 @@ import { useEffect, useState } from "preact/hooks";
|
|
|
3
3
|
import { Dialog } from "../dialogs/dialog.js";
|
|
4
4
|
import { checkIcon } from "../dialogs/profiles.js";
|
|
5
5
|
import { canAct, chatAction, choiceKeys, chosen, effortsOf, firstHost, offeredHosts, startedWith } from "./action.js";
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The chat's window: `onStarted` once a chat opens or starts again, the page then saying where it stands; `onClose` when the player closes it without;
|
|
8
|
+
* `review` when it opened to have a game analysed.
|
|
9
|
+
*/
|
|
10
|
+
export function ChatDialog({ open, review = false, table, settings, onChange, name, onStarted, onClose, text }) {
|
|
11
|
+
return (_jsx(Dialog, { open: open, title: review ? text.table.reviewTitle : text.table.title, onClose: onClose, text: text, children: _jsx(ChatForm, { review: review, table: table, settings: settings, onChange: onChange, name: name, onStarted: onStarted, text: text }) }));
|
|
9
12
|
}
|
|
10
13
|
/** The AI's name on its card, which opens the chat's window: with no chat at the table, a hollow ring before it says so. */
|
|
11
14
|
export function AiNameButton({ name, table, onOpen, text }) {
|
|
@@ -45,7 +48,7 @@ const hostTitles = { claude: "Claude Code", codex: "Codex", antigravity: "Antigr
|
|
|
45
48
|
/** The ↻ of a chat that starts again. */
|
|
46
49
|
const restartIcon = (_jsx("svg", { class: "icon in-use", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: "M19.5 12a7.5 7.5 0 1 1-2.2-5.3M19.5 4.5v4h-4" }) }));
|
|
47
50
|
/** The window's form, made again at each opening with the host at the table selected, else the last the button used, else the first. */
|
|
48
|
-
function ChatForm({ review, table, settings, onChange, name,
|
|
51
|
+
function ChatForm({ review, table, settings, onChange, name, onStarted, text }) {
|
|
49
52
|
const words = text.table;
|
|
50
53
|
const installed = offeredHosts(table);
|
|
51
54
|
const [selected, setSelected] = useState(() => firstHost(table, settings));
|
|
@@ -73,7 +76,7 @@ function ChatForm({ review, table, settings, onChange, name, onClose, text }) {
|
|
|
73
76
|
setSending(false);
|
|
74
77
|
if (done) {
|
|
75
78
|
onChange({ ...settings, chatHost: host.name });
|
|
76
|
-
|
|
79
|
+
onStarted();
|
|
77
80
|
}
|
|
78
81
|
else {
|
|
79
82
|
setFailed(true);
|
package/dist/page/talk/chat.css
CHANGED
|
@@ -106,3 +106,11 @@
|
|
|
106
106
|
opacity: 1;
|
|
107
107
|
background: color-mix(in oklab, currentColor 15%, transparent);
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
/* On a variation, the fork's sign under the moves before the move the board showed, in the bubble's colour (D-116) */
|
|
111
|
+
.shown-line .fork-icon {
|
|
112
|
+
width: 14px;
|
|
113
|
+
height: 14px;
|
|
114
|
+
margin-right: 3px;
|
|
115
|
+
stroke-width: 2.2;
|
|
116
|
+
}
|
package/dist/page/talk/talk.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createContext } from "preact";
|
|
|
4
4
|
import { useContext, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
|
5
5
|
import { Torretta } from "../avatars/torretta.js";
|
|
6
6
|
import { BadgeIcon, NoteIcon } from "../game/badges.js";
|
|
7
|
-
import { Figurines } from "../game/move-list.js";
|
|
7
|
+
import { Figurines, ForkIcon } from "../game/move-list.js";
|
|
8
8
|
import { talkLimits } from "../game/plies.js";
|
|
9
9
|
import { ReviewProgress } from "../game/review-progress.js";
|
|
10
10
|
import { signature } from "../players/model-name.js";
|
|
@@ -87,7 +87,7 @@ export function Compose({ lines, newestFirst, spent, draft, onDraft, onSend, can
|
|
|
87
87
|
};
|
|
88
88
|
return (_jsxs(_Fragment, { children: [_jsx("ol", { ref: list, class: "sent", "aria-live": "polite", children: inOrder(lines.map((line, index) => (_jsxs("li", { class: line.who === "ai" ? "from-ai" : undefined, style: about === undefined ? aged(lines.length - 1 - index) : reread(index, lastAnswer), children: [line.who === "ai"
|
|
89
89
|
? (_jsxs(_Fragment, { children: [_jsx(Torretta, { size: 24 }), _jsx("p", { class: "say", children: _jsx(Marked, { text: line.text }) })] }))
|
|
90
|
-
: (_jsxs("p", { class: "said", children: [about === undefined
|
|
90
|
+
: (_jsxs("p", { class: "said", children: [about === undefined ? null : _jsx(ShownPlace, { shown: line.shown, about: about, text: text }), line.text] })), index === lastWritten ? _jsx(Status, { heard: line.heard, name: name, text: text }) : null] }, index))), newestFirst) }), to, left <= 0 ? null : (_jsxs("form", { class: about === undefined ? "compose" : "compose asking", onSubmit: (event) => {
|
|
91
91
|
event.preventDefault();
|
|
92
92
|
submit();
|
|
93
93
|
}, children: [_jsx("p", { class: "limit", role: "status", children: problem ?? "" }), _jsx("textarea", { rows: 2, maxLength: left, placeholder: prompt, "aria-label": prompt, value: draft, onInput: (event) => onDraft(event.currentTarget.value), onKeyDown: (event) => {
|
|
@@ -106,6 +106,24 @@ export function Compose({ lines, newestFirst, spent, draft, onDraft, onSend, can
|
|
|
106
106
|
export function TalkFaces({ name, aiPicture, you, picture, ai, player, aiMoves, playerMoves, unread, text }) {
|
|
107
107
|
return (_jsxs("div", { class: "faces", children: [_jsx(RailPlayer, { place: "ai", picture: aiPicture ?? _jsx(Torretta, { size: 40, bust: true }), name: name, ...(ai === undefined ? {} : { side: text.sides[ai] }), moves: aiMoves, unread: unread }), _jsx(RailPlayer, { place: "you", picture: picture, name: you, ...(player === undefined ? {} : { side: text.sides[player] }), moves: playerMoves })] }));
|
|
108
108
|
}
|
|
109
|
+
/** Where a line of the player's was written: on a variation the board showed, its mark; else the game's move, none at the start. */
|
|
110
|
+
function ShownPlace({ shown, about, text }) {
|
|
111
|
+
const variation = shown?.variation;
|
|
112
|
+
if (variation?.move !== undefined) {
|
|
113
|
+
return _jsx(ShownLine, { variation: variation, onShow: () => about.onLine(variation), text: text });
|
|
114
|
+
}
|
|
115
|
+
return (_jsxs(_Fragment, { children: [shown?.move === undefined ? null : _jsx(ShownMove, { move: shown.move, onShow: () => about.onShow(shown.at), text: text }), variation === undefined ? null : _jsx(ShownLine, { variation: variation, onShow: () => about.onLine(variation), text: text })] }));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The variation a line of the player's was written on, small at the line's head: the fork's sign in the accent and the move the board showed, none when
|
|
119
|
+
* it was set aside; a press puts the variation back on the board (D-116).
|
|
120
|
+
*/
|
|
121
|
+
function ShownLine({ variation, onShow, text }) {
|
|
122
|
+
const { move } = variation;
|
|
123
|
+
const label = text.variation.show(variation.text);
|
|
124
|
+
const [, number = "", san = ""] = /^(\d+\.+)(.+)$/.exec(move ?? "") ?? [];
|
|
125
|
+
return (_jsxs("button", { type: "button", class: "shown-move shown-line", title: label, "aria-label": label, onClick: onShow, children: [_jsx(ForkIcon, {}), move === undefined ? null : (_jsxs(_Fragment, { children: [_jsx("span", { class: "move-number", children: number }), _jsx(Figurines, { san: san, side: number.endsWith("...") ? "black" : "white" })] }))] }));
|
|
126
|
+
}
|
|
109
127
|
/** The move a line of the player's was written on, 14.♘f3, small at the line's head: a press shows it on the board again (Zoe). */
|
|
110
128
|
function ShownMove({ move, onShow, text }) {
|
|
111
129
|
const [, number = "", san = move] = /^(\d+\.+)(.+)$/.exec(move) ?? [];
|
package/dist/page/text.js
CHANGED
|
@@ -84,7 +84,15 @@ const english = {
|
|
|
84
84
|
game: "The game",
|
|
85
85
|
moves: "Moves",
|
|
86
86
|
steps: { first: "Start", back: "Previous move", forward: "Next move", last: "Back to the game" },
|
|
87
|
-
variation: {
|
|
87
|
+
variation: {
|
|
88
|
+
line: "Variation",
|
|
89
|
+
close: "Close the variation (Esc)",
|
|
90
|
+
back: (move) => move === undefined ? "Back to the start of the game" : `Back to the game, at ${move}`,
|
|
91
|
+
analyse: "Analyse",
|
|
92
|
+
analyseHint: (name) => `Ask ${name} to analyse this variation`,
|
|
93
|
+
analyseWords: "Analyse this variation",
|
|
94
|
+
show: (line) => `Show the variation ${line} on the board`,
|
|
95
|
+
},
|
|
88
96
|
noGame: "No game yet: press + to start one, or ask for one in the chat.",
|
|
89
97
|
foldCard: "Fold the card",
|
|
90
98
|
openCard: "Open the card",
|
|
@@ -119,6 +127,7 @@ const english = {
|
|
|
119
127
|
mated: (side) => `${side === "white" ? "White" : "Black"} is mated`,
|
|
120
128
|
},
|
|
121
129
|
reviewing: (name) => `${name} is going over the game…`,
|
|
130
|
+
stopReview: "Cancel the analysis",
|
|
122
131
|
incomplete: "Analysis not complete",
|
|
123
132
|
complete: "Complete it",
|
|
124
133
|
completeHint: "Complete the analysis",
|
|
@@ -524,7 +533,15 @@ const italian = {
|
|
|
524
533
|
game: "La partita",
|
|
525
534
|
moves: "Mosse",
|
|
526
535
|
steps: { first: "Inizio", back: "Mossa precedente", forward: "Mossa successiva", last: "Torna alla partita" },
|
|
527
|
-
variation: {
|
|
536
|
+
variation: {
|
|
537
|
+
line: "Variante",
|
|
538
|
+
close: "Chiudi la variante (Esc)",
|
|
539
|
+
back: (move) => move === undefined ? "Torna all'inizio della partita" : `Torna alla partita, a ${move}`,
|
|
540
|
+
analyse: "Analizza",
|
|
541
|
+
analyseHint: (name) => `Chiedi a ${name} di analizzare questa variante`,
|
|
542
|
+
analyseWords: "Analizza questa variante",
|
|
543
|
+
show: (line) => `Rimetti sulla scacchiera la variante ${line}`,
|
|
544
|
+
},
|
|
528
545
|
noGame: "Nessuna partita: premi + per iniziarne una, o chiedila nella chat.",
|
|
529
546
|
foldCard: "Chiudi la scheda",
|
|
530
547
|
openCard: "Apri la scheda",
|
|
@@ -559,6 +576,7 @@ const italian = {
|
|
|
559
576
|
mated: (side) => `Matto al ${side === "white" ? "Bianco" : "Nero"}`,
|
|
560
577
|
},
|
|
561
578
|
reviewing: (name) => `${name} sta analizzando la partita…`,
|
|
579
|
+
stopReview: "Annulla l'analisi",
|
|
562
580
|
incomplete: "Analisi non completa",
|
|
563
581
|
complete: "Completa",
|
|
564
582
|
completeHint: "Completa l'analisi",
|
|
@@ -11,6 +11,7 @@ export declare class Bell {
|
|
|
11
11
|
private busy;
|
|
12
12
|
private woken;
|
|
13
13
|
private hooked;
|
|
14
|
+
private unheard;
|
|
14
15
|
private stopWatch;
|
|
15
16
|
private readonly news;
|
|
16
17
|
private readonly changed;
|
|
@@ -25,6 +26,8 @@ export declare class Bell {
|
|
|
25
26
|
get inTurn(): boolean;
|
|
26
27
|
/** Whether the doorbell woke the chat in the turn it is in. */
|
|
27
28
|
get woke(): boolean;
|
|
29
|
+
/** Whether the chat is out of its turn and hears nobody: its hook went without the bell, or it was stopped; until it calls a tool or a hook waits again. */
|
|
30
|
+
get deaf(): boolean;
|
|
28
31
|
/** Whether the chat hears the player: in a turn, or waiting for the bell with a hook. */
|
|
29
32
|
get listening(): boolean;
|
|
30
33
|
/** The chat calls a tool, so it is in a turn: true when nobody was listening before. */
|
|
@@ -97,5 +100,5 @@ export type BellHeard = {
|
|
|
97
100
|
* 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).
|
|
98
101
|
*/
|
|
99
102
|
export declare function bellHeard(input: string, ports?: number[]): Promise<BellHeard>;
|
|
100
|
-
/** Adds a line to the log of the hooks
|
|
101
|
-
export declare function
|
|
103
|
+
/** Adds a line to the log of the doorbell's hooks, keeping its last lines; a log that cannot be written never stops the hook. */
|
|
104
|
+
export declare function noteHook(folder: string, why: string, now?: Date): void;
|