chesspal 0.1.0 → 0.2.1
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 +45 -82
- package/dist/cli/chat-hook.js +1 -1
- package/dist/cli/command-line.d.ts +1 -2
- package/dist/cli/command-line.js +2 -3
- package/dist/commands/agents.d.ts +26 -0
- package/dist/commands/agents.js +72 -0
- package/dist/commands/ai-move.d.ts +5 -14
- package/dist/commands/ai-move.js +28 -35
- package/dist/commands/ai-say.d.ts +3 -1
- package/dist/commands/ai-say.js +24 -6
- package/dist/commands/analyse-game.d.ts +4 -0
- package/dist/commands/analyse-game.js +43 -16
- package/dist/commands/annotate-game.d.ts +4 -0
- package/dist/commands/annotate-game.js +65 -12
- package/dist/commands/answer-lines.d.ts +28 -0
- package/dist/commands/answer-lines.js +144 -0
- package/dist/commands/coach.d.ts +45 -0
- package/dist/commands/coach.js +344 -0
- package/dist/commands/context.d.ts +16 -5
- package/dist/commands/context.js +1 -0
- package/dist/commands/delete-game.d.ts +7 -0
- package/dist/commands/delete-game.js +30 -0
- package/dist/commands/delete-review.d.ts +8 -0
- package/dist/commands/delete-review.js +34 -0
- package/dist/commands/drawings.d.ts +0 -2
- package/dist/commands/drawings.js +5 -3
- package/dist/commands/end-game.js +5 -2
- package/dist/commands/export-game.d.ts +1 -1
- package/dist/commands/export-game.js +4 -3
- package/dist/commands/game-summary.d.ts +21 -38
- package/dist/commands/game-summary.js +30 -167
- package/dist/commands/index.d.ts +15 -0
- package/dist/commands/index.js +47 -2
- package/dist/commands/init.d.ts +3 -3
- package/dist/commands/init.js +4 -4
- package/dist/commands/lead-input.d.ts +8 -0
- package/dist/commands/lead-input.js +21 -0
- package/dist/commands/look.js +4 -1
- package/dist/commands/new-game.d.ts +2 -3
- package/dist/commands/new-game.js +7 -4
- package/dist/commands/play-move.d.ts +8 -15
- package/dist/commands/play-move.js +5 -13
- package/dist/commands/player-move.d.ts +2 -6
- package/dist/commands/player-move.js +13 -6
- package/dist/commands/player-say.d.ts +1 -1
- package/dist/commands/player-say.js +1 -1
- package/dist/commands/ponder.d.ts +10 -0
- package/dist/commands/ponder.js +33 -21
- package/dist/commands/position.d.ts +1 -1
- package/dist/commands/position.js +3 -1
- package/dist/commands/profiles.d.ts +13 -4
- package/dist/commands/profiles.js +49 -15
- package/dist/commands/registry.d.ts +8 -3
- package/dist/commands/registry.js +12 -11
- package/dist/commands/say.d.ts +1 -1
- package/dist/commands/say.js +16 -16
- package/dist/commands/speech.d.ts +8 -0
- package/dist/commands/speech.js +20 -0
- package/dist/commands/status.d.ts +9 -3
- package/dist/commands/status.js +40 -15
- package/dist/commands/study-games.js +1 -1
- package/dist/commands/study.js +3 -19
- package/dist/core/facts.js +15 -15
- package/dist/core/game.d.ts +3 -1
- package/dist/core/game.js +8 -4
- package/dist/core/illegal.js +1 -1
- package/dist/core/index.d.ts +5 -4
- package/dist/core/index.js +5 -4
- package/dist/core/lead.d.ts +15 -0
- package/dist/core/lead.js +40 -0
- package/dist/core/levels.d.ts +1 -1
- package/dist/core/levels.js +1 -1
- package/dist/core/moods.d.ts +0 -2
- package/dist/core/moods.js +2 -2
- package/dist/core/openings.d.ts +0 -2
- package/dist/core/openings.js +1 -1
- package/dist/core/profile.js +1 -1
- package/dist/core/record.d.ts +1 -1
- package/dist/core/record.js +1 -1
- package/dist/core/review.d.ts +18 -3
- package/dist/core/review.js +52 -9
- package/dist/games/ai-model.d.ts +4 -0
- package/dist/games/ai-model.js +9 -0
- package/dist/games/analysis.d.ts +1 -1
- package/dist/games/archive.d.ts +25 -4
- package/dist/games/archive.js +99 -25
- package/dist/games/coach.d.ts +57 -0
- package/dist/games/coach.js +144 -0
- package/dist/games/current-game.d.ts +2 -0
- package/dist/games/current-game.js +7 -1
- package/dist/games/files.js +1 -0
- package/dist/games/index.d.ts +5 -4
- package/dist/games/index.js +5 -4
- package/dist/games/talk.d.ts +1 -5
- package/dist/games/talk.js +14 -14
- package/dist/home/open-folder.js +1 -1
- package/dist/host-files/{ponder-agents.d.ts → agents.d.ts} +10 -2
- package/dist/host-files/agents.js +53 -0
- package/dist/host-files/host-files.d.ts +7 -2
- package/dist/host-files/host-files.js +27 -12
- package/dist/host-files/index.d.ts +2 -2
- package/dist/host-files/index.js +3 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -0
- package/dist/page/app/anchor.js +88 -0
- package/dist/page/{app.js → app/app.js} +116 -231
- package/dist/page/app/keys.js +94 -0
- package/dist/page/app/phase.js +30 -0
- package/dist/page/app/served.js +73 -0
- package/dist/page/avatars/avatars.js +19 -0
- package/dist/page/avatars/thought.js +10 -0
- package/dist/page/base.css +160 -0
- package/dist/page/board/board.css +959 -0
- package/dist/page/board/board.js +125 -0
- package/dist/page/{explain.js → board/explain.js} +1 -1
- package/dist/page/board/marks.js +160 -0
- package/dist/page/board/motion.js +71 -0
- package/dist/page/board/pointer.js +239 -0
- package/dist/page/board/squares.js +26 -0
- package/dist/page/dialogs/analyses.js +195 -0
- package/dist/page/{dialog.js → dialogs/dialog.js} +1 -1
- package/dist/page/dialogs/dialogs.css +1228 -0
- package/dist/page/{new-game-dialog.js → dialogs/new-game.js} +3 -3
- package/dist/page/{profile-dialogs.js → dialogs/profiles.js} +40 -11
- package/dist/page/dialogs/search.js +31 -0
- package/dist/page/dialogs/settings.js +42 -0
- package/dist/page/dialogs/studied.js +39 -0
- package/dist/page/dialogs/study.js +51 -0
- package/dist/page/{badges.js → game/badges.js} +1 -1
- package/dist/page/{browse.js → game/browse.js} +1 -1
- package/dist/page/game/card-fold.js +40 -0
- package/dist/page/game/column.js +55 -0
- package/dist/page/{ending.js → game/ending.js} +2 -2
- package/dist/page/game/game.css +577 -0
- package/dist/page/{move-list.js → game/move-list.js} +10 -6
- package/dist/page/{plies.js → game/plies.js} +30 -10
- package/dist/page/game/result-tile.js +14 -0
- package/dist/page/helps/helps.css +689 -0
- package/dist/page/helps/helps.js +220 -0
- package/dist/page/helps/view.js +127 -0
- package/dist/page/layout/fold-tab.js +11 -0
- package/dist/page/{folds.js → layout/folds.js} +1 -1
- package/dist/page/{header.js → layout/header.js} +5 -4
- package/dist/page/layout/layout.css +834 -0
- package/dist/page/lead/lead.js +82 -0
- package/dist/page/lead/view.js +38 -0
- package/dist/page/main.js +2 -2
- package/dist/page/pieces/CREDITS.md +12 -29
- package/dist/page/pieces/capablanca-small.svg +20 -0
- package/dist/page/pieces/capablanca.svg +22 -0
- package/dist/page/pieces/nitida-small.svg +3 -2
- package/dist/page/pieces/nitida.svg +3 -2
- package/dist/page/{pieces.js → pieces/pieces.js} +2 -2
- package/dist/page/pieces/torretta-small.svg +15 -14
- package/dist/page/pieces/torretta.svg +17 -15
- package/dist/page/{player-card.js → players/card.js} +2 -6
- package/dist/page/{clock-time.js → players/clock-time.js} +2 -2
- package/dist/page/{profile.js → profile/profile.js} +9 -5
- package/dist/page/{settings.js → profile/settings.js} +5 -4
- package/dist/page/style.css +9 -4398
- package/dist/page/talk/talk.css +958 -0
- package/dist/page/{talk.js → talk/talk.js} +39 -46
- package/dist/page/text.js +137 -19
- package/dist/page/theme.css +292 -0
- package/dist/profiles/index.d.ts +1 -1
- package/dist/profiles/profiles.d.ts +15 -4
- package/dist/profiles/profiles.js +24 -7
- package/dist/server/bell.d.ts +10 -2
- package/dist/server/bell.js +28 -12
- package/dist/server/caller.d.ts +14 -3
- package/dist/server/caller.js +44 -11
- package/dist/server/mcp.d.ts +2 -3
- package/dist/server/mcp.js +53 -19
- package/dist/server/page/analyses.d.ts +23 -0
- package/dist/server/page/analyses.js +65 -0
- package/dist/server/page/files.d.ts +5 -0
- package/dist/server/page/files.js +92 -0
- package/dist/server/page/game.d.ts +6 -0
- package/dist/server/page/game.js +381 -0
- package/dist/server/{http.d.ts → page/http.d.ts} +13 -0
- package/dist/server/{http.js → page/http.js} +11 -0
- package/dist/server/{page.d.ts → page/page.d.ts} +8 -13
- package/dist/server/page/page.js +229 -0
- package/dist/server/{profile-routes.d.ts → page/profiles.d.ts} +2 -2
- package/dist/server/{profile-routes.js → page/profiles.js} +47 -18
- package/instructions/analysis.md +4 -0
- package/instructions/analyst-agent.md +8 -0
- package/instructions/analyst.md +3 -7
- package/instructions/coach.md +11 -0
- package/instructions/opponent.md +4 -4
- package/package.json +1 -1
- package/dist/host-files/ponder-agents.js +0 -40
- package/dist/page/avatars.js +0 -16
- package/dist/page/board.js +0 -566
- package/dist/page/fold-tab.js +0 -9
- package/dist/page/game-column.js +0 -44
- package/dist/page/helps-view.js +0 -51
- package/dist/page/helps.js +0 -90
- package/dist/page/pieces/cburnett-small.svg +0 -20
- package/dist/page/pieces/cburnett.svg +0 -20
- package/dist/page/settings-panel.js +0 -32
- package/dist/page/studied.js +0 -14
- package/dist/page/study-dialog.js +0 -58
- package/dist/server/analyses.d.ts +0 -17
- package/dist/server/analyses.js +0 -46
- package/dist/server/page.js +0 -657
- /package/dist/page/{torretta.js → avatars/torretta.js} +0 -0
- /package/dist/page/{opening-book.js → game/opening-book.js} +0 -0
- /package/dist/page/{model-name.js → players/model-name.js} +0 -0
- /package/dist/page/{sounds.js → sounds/sounds.js} +0 -0
- /package/dist/page/{voice.js → talk/voice.js} +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page's phase: a game `analysing`, else the game the server sent, null before the first, whether it is `over`, and whether the page saw it end, which
|
|
3
|
+
* keeps its final position until the next starts.
|
|
4
|
+
*/
|
|
5
|
+
export function phaseOf(analysing, game, over, sawEnd) {
|
|
6
|
+
if (analysing) {
|
|
7
|
+
return "analysis";
|
|
8
|
+
}
|
|
9
|
+
if (game === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
if (game !== null && !over) {
|
|
13
|
+
return "going";
|
|
14
|
+
}
|
|
15
|
+
return sawEnd ? "over" : "idle";
|
|
16
|
+
}
|
|
17
|
+
/** What the header says of the game, with the kind that gives it a colour and an icon: in analysis, none going on, how it ended, or the draw offered. */
|
|
18
|
+
export function verdictOf(phase, how, winner, offer, declined, text) {
|
|
19
|
+
if (phase === "analysis") {
|
|
20
|
+
return { kind: "analysis", verdict: text.analysing };
|
|
21
|
+
}
|
|
22
|
+
if (phase === "idle") {
|
|
23
|
+
return { kind: "idle", verdict: text.noGameNow };
|
|
24
|
+
}
|
|
25
|
+
if (phase === "over") {
|
|
26
|
+
return { kind: "over", verdict: text.verdict(how, winner) };
|
|
27
|
+
}
|
|
28
|
+
const verdict = offer === "ai" ? text.offers : offer === "player" ? text.drawOffered : declined ? text.declines : undefined;
|
|
29
|
+
return verdict === undefined ? {} : { kind: "offer", verdict };
|
|
30
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// What the page asks the server (D-47): the game of the player's folder, read again each time it changes, and the player's moves, words and choices sent
|
|
2
|
+
// there, refused in the player's language.
|
|
3
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
4
|
+
/**
|
|
5
|
+
* The game of the player's folder, read from the server, and read again each time the server says it changed, whoever changed it; `onNewGame` when the chat
|
|
6
|
+
* asks for a new game, which the player chooses on the page.
|
|
7
|
+
*/
|
|
8
|
+
export function useServed(onNewGame) {
|
|
9
|
+
const [data, setData] = useState(undefined);
|
|
10
|
+
const [problem, setProblem] = useState(undefined);
|
|
11
|
+
const [offline, setOffline] = useState(false);
|
|
12
|
+
// Reads the game again: when the server says it changed, and when a clock runs out on the page.
|
|
13
|
+
const reload = useRef(() => { });
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
let latest = 0;
|
|
16
|
+
const load = () => {
|
|
17
|
+
// Two reads in flight may answer out of order: only the last one asked counts.
|
|
18
|
+
const asked = ++latest;
|
|
19
|
+
fetch("/api/game")
|
|
20
|
+
.then((response) => response.json())
|
|
21
|
+
.then((body) => {
|
|
22
|
+
if (asked !== latest) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setProblem(body.error);
|
|
26
|
+
if (body.error === undefined) {
|
|
27
|
+
setData(body);
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => setProblem(String(error)));
|
|
31
|
+
};
|
|
32
|
+
// The server says when the game changes, whoever changed it: the page reads it again, and as it connects, the first time or to a new chat's server.
|
|
33
|
+
// The server lives in the chat's MCP server: when the chat ends, after /clear in Claude's app too, the page waits for the next one and says so.
|
|
34
|
+
reload.current = load;
|
|
35
|
+
const events = new EventSource("/api/events");
|
|
36
|
+
events.onopen = () => {
|
|
37
|
+
setOffline(false);
|
|
38
|
+
load();
|
|
39
|
+
};
|
|
40
|
+
// The chat asks for a new game: the player chooses it in the New game window, which opens here.
|
|
41
|
+
events.onmessage = (event) => event.data === "new-game" ? onNewGame() : load();
|
|
42
|
+
events.onerror = () => setOffline(events.readyState !== EventSource.OPEN);
|
|
43
|
+
return () => events.close();
|
|
44
|
+
}, []);
|
|
45
|
+
return { data, problem, offline, reload: () => reload.current() };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Sends a request to the server: the reason it was refused, in the player's language when the page knows the refusal (D-57), with the host's name where
|
|
49
|
+
* it sends the player, or undefined.
|
|
50
|
+
*/
|
|
51
|
+
export async function post(path, body, text, name) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
|
|
54
|
+
if (response.ok) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const refused = (await response.json());
|
|
58
|
+
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 };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
return { text: String(error), words: false };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** 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. */
|
|
66
|
+
export function sendMove(move, words, text, name) {
|
|
67
|
+
const message = words.trim();
|
|
68
|
+
return post("/api/move", message === "" ? { move } : { move, message }, text, name);
|
|
69
|
+
}
|
|
70
|
+
/** Sends the player's words alone: the reason they were refused, or undefined. */
|
|
71
|
+
export function sendMessage(words, text, name) {
|
|
72
|
+
return post("/api/message", { text: words.trim() }, text, name);
|
|
73
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
// The player's picture (D-45): their own image when the profile has one, else one of the drawn avatars, else the plain face of a page without profiles.
|
|
3
|
+
import { imageAddress } from "../profile/profile.js";
|
|
4
|
+
/** The player's picture, `size` pixels wide: their image, their drawn avatar, or the plain face. */
|
|
5
|
+
export function PlayerPicture({ profile, size = 48 }) {
|
|
6
|
+
const image = profile === undefined ? undefined : imageAddress(profile);
|
|
7
|
+
if (image !== undefined) {
|
|
8
|
+
return _jsx("img", { class: "avatar photo", src: image, width: size, height: size, alt: "" });
|
|
9
|
+
}
|
|
10
|
+
return profile?.avatar === undefined ? _jsx(PlainFace, {}) : _jsx(DrawnAvatar, { id: profile.avatar, size: size });
|
|
11
|
+
}
|
|
12
|
+
/** One of the drawn avatars, its colour behind it in the picture; named for a screen reader when `label` is given. */
|
|
13
|
+
export function DrawnAvatar({ id, size, label }) {
|
|
14
|
+
return _jsx("img", { class: "avatar drawn", src: `/page/avatars/${id}.webp`, width: size, height: size, alt: label ?? "", decoding: "async" });
|
|
15
|
+
}
|
|
16
|
+
/** The player's picture on a page without profiles: a simple face, in a circle. */
|
|
17
|
+
function PlainFace() {
|
|
18
|
+
return (_jsxs("svg", { class: "avatar", viewBox: "0 0 64 64", width: "48", height: "48", "aria-hidden": "true", children: [_jsx("circle", { class: "back", cx: "32", cy: "32", r: "32" }), _jsx("path", { class: "shirt", d: "M10 66C11 52 20 45 32 45C44 45 53 52 54 66Z" }), _jsx("path", { class: "skin", d: "M27 37H37V47C37 50 27 50 27 47Z" }), _jsx("circle", { class: "skin", cx: "32", cy: "28", r: "12" }), _jsx("path", { class: "hair", d: "M19.5 29C18 18 24 12.5 32.5 12.5C41 12.5 46.5 18 44.5 29C43 23.5 39 20.5 33 21.5C28 22.5 23 24 19.5 29Z" }), _jsx("circle", { class: "eye", cx: "27.5", cy: "29.5", r: "1.7" }), _jsx("circle", { class: "eye", cx: "36.5", cy: "29.5", r: "1.7" }), _jsx("path", { class: "smile", d: "M28.5 34.5Q32 37.5 35.5 34.5" })] }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
/** The AI's picture with its thought cloud while it thinks; the picture alone else. */
|
|
3
|
+
export function Thinker({ thinking, words, children }) {
|
|
4
|
+
return (_jsxs("span", { class: "thinker", children: [children, thinking === undefined ? null : _jsx(Thought, { thinking: thinking, words: words })] }));
|
|
5
|
+
}
|
|
6
|
+
/** The cloud alone, as on the folded talk's tab: named for a screen reader that explores it, never announced, since the AI's move says it is done. */
|
|
7
|
+
export function Thought({ thinking, words }) {
|
|
8
|
+
const dots = thinking === "move" ? { radius: 1.8, at: [6.8, 11.5, 16.2] } : { radius: 1.4, at: [7.3, 11.5, 15.7] };
|
|
9
|
+
return (_jsxs("svg", { class: `thought ${thinking}`, viewBox: "0 0 24 20", role: "img", "aria-label": words[thinking], children: [_jsx("title", { children: words[thinking] }), _jsx("path", { class: "cloud", d: "M7 1.5h9a5.5 5.5 0 0 1 0 11H7a5.5 5.5 0 0 1 0-11Z" }), _jsx("circle", { class: "cloud", cx: "19", cy: "16.5", r: "2.1" }), dots.at.map((x) => _jsx("circle", { class: "dot", cx: x, cy: "7", r: dots.radius }, x))] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* What every part shares: the page's type, its buttons and icons, the focus ring, the spinner */
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
margin: 0;
|
|
9
|
+
min-height: 100vh;
|
|
10
|
+
background: var(--page);
|
|
11
|
+
color: var(--ink);
|
|
12
|
+
font-family: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
|
13
|
+
font-size: 15px;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
button,
|
|
18
|
+
textarea {
|
|
19
|
+
font: inherit;
|
|
20
|
+
color: inherit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
button {
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.icon {
|
|
28
|
+
width: 17px;
|
|
29
|
+
height: 17px;
|
|
30
|
+
fill: none;
|
|
31
|
+
stroke: currentColor;
|
|
32
|
+
stroke-width: 1.7;
|
|
33
|
+
stroke-linecap: round;
|
|
34
|
+
stroke-linejoin: round;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:focus-visible {
|
|
38
|
+
outline: 2px solid var(--accent);
|
|
39
|
+
outline-offset: 2px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.command {
|
|
43
|
+
display: inline-flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: 7px;
|
|
46
|
+
background: none;
|
|
47
|
+
border: 1px solid transparent;
|
|
48
|
+
border-radius: 8px;
|
|
49
|
+
padding: 7px 11px;
|
|
50
|
+
font-size: 14px;
|
|
51
|
+
color: var(--soft);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.command:hover,
|
|
55
|
+
.command[aria-pressed="true"] {
|
|
56
|
+
color: var(--ink);
|
|
57
|
+
border-color: var(--line);
|
|
58
|
+
background: var(--card);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.command.primary {
|
|
62
|
+
background: var(--accent);
|
|
63
|
+
border-color: var(--accent);
|
|
64
|
+
color: var(--on-accent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.command.primary:hover {
|
|
68
|
+
background: color-mix(in oklab, var(--accent) 85%, var(--ink));
|
|
69
|
+
border-color: transparent;
|
|
70
|
+
color: var(--on-accent);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* A draw the player cannot offer now: before a move each, on the AI's turn, or with an offer standing (D-65); their own offer stands pressed, at full strength */
|
|
74
|
+
.command[aria-disabled="true"]:not([aria-pressed="true"]) {
|
|
75
|
+
cursor: default;
|
|
76
|
+
opacity: 0.5;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.command[aria-disabled="true"]:not([aria-pressed="true"]):hover {
|
|
80
|
+
color: var(--soft);
|
|
81
|
+
border-color: transparent;
|
|
82
|
+
background: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.icon-button {
|
|
86
|
+
display: grid;
|
|
87
|
+
place-items: center;
|
|
88
|
+
width: 36px;
|
|
89
|
+
height: 36px;
|
|
90
|
+
background: none;
|
|
91
|
+
border: 1px solid var(--line);
|
|
92
|
+
border-radius: 8px;
|
|
93
|
+
color: var(--soft);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.icon-button:hover {
|
|
97
|
+
color: var(--ink);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
:root[data-theme="dark"] .icon-button .sun,
|
|
101
|
+
:root:not([data-theme="dark"]) .icon-button .moon {
|
|
102
|
+
display: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.spinner {
|
|
106
|
+
flex: none;
|
|
107
|
+
width: 14px;
|
|
108
|
+
height: 14px;
|
|
109
|
+
border: 2px solid color-mix(in srgb, var(--accent) 25%, transparent);
|
|
110
|
+
border-top-color: var(--accent);
|
|
111
|
+
border-radius: 50%;
|
|
112
|
+
animation: spin 900ms linear infinite;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@keyframes spin {
|
|
116
|
+
to {
|
|
117
|
+
transform: rotate(360deg);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (prefers-reduced-motion: reduce) {
|
|
122
|
+
.spinner {
|
|
123
|
+
animation-duration: 3s;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.visually-hidden {
|
|
128
|
+
position: absolute;
|
|
129
|
+
width: 1px;
|
|
130
|
+
height: 1px;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
clip-path: inset(50%);
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* The cards, in every column */
|
|
137
|
+
.card {
|
|
138
|
+
background: var(--card);
|
|
139
|
+
border: 1px solid var(--line);
|
|
140
|
+
border-radius: var(--radius);
|
|
141
|
+
padding: 12px 14px;
|
|
142
|
+
box-shadow: var(--shadow);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.note {
|
|
146
|
+
color: var(--soft);
|
|
147
|
+
font-size: 13px;
|
|
148
|
+
font-weight: 400;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.empty {
|
|
152
|
+
margin: 0;
|
|
153
|
+
color: var(--soft);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.problem {
|
|
157
|
+
max-width: 40em;
|
|
158
|
+
margin: 24px auto;
|
|
159
|
+
padding: 0 16px;
|
|
160
|
+
}
|