chesspal 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -12
- package/dist/cli/cli.js +83 -3
- package/dist/cli/command-line.js +26 -9
- package/dist/cli/icon.d.ts +25 -0
- package/dist/cli/icon.js +80 -0
- package/dist/commands/agents.d.ts +19 -2
- package/dist/commands/agents.js +41 -14
- package/dist/commands/ai-say.d.ts +2 -1
- package/dist/commands/ai-say.js +3 -4
- package/dist/commands/analyse-game.d.ts +6 -0
- package/dist/commands/analyse-game.js +51 -15
- package/dist/commands/annotate-game.d.ts +4 -0
- package/dist/commands/annotate-game.js +60 -20
- package/dist/commands/answer-lines.d.ts +10 -4
- package/dist/commands/answer-lines.js +30 -4
- package/dist/commands/coach.js +6 -4
- package/dist/commands/context.d.ts +11 -1
- package/dist/commands/context.js +5 -0
- package/dist/commands/delete-review.js +9 -4
- package/dist/commands/game-summary.d.ts +9 -2
- package/dist/commands/game-summary.js +11 -0
- package/dist/commands/index.d.ts +4 -3
- package/dist/commands/index.js +8 -5
- package/dist/commands/init.js +2 -4
- package/dist/commands/instructions.d.ts +9 -0
- package/dist/commands/instructions.js +30 -0
- package/dist/commands/lead-input.d.ts +1 -1
- package/dist/commands/lead-input.js +7 -4
- package/dist/commands/look.d.ts +9 -5
- package/dist/commands/look.js +19 -9
- package/dist/commands/page-link.js +6 -5
- package/dist/commands/past-games.d.ts +1 -1
- package/dist/commands/past-games.js +12 -5
- package/dist/commands/player-move.js +2 -3
- package/dist/commands/ponder.js +1 -1
- package/dist/commands/position.js +2 -2
- package/dist/commands/profiles.d.ts +23 -9
- package/dist/commands/profiles.js +76 -35
- package/dist/commands/ratings.d.ts +8 -0
- package/dist/commands/ratings.js +45 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +3 -2
- package/dist/commands/say.d.ts +11 -6
- package/dist/commands/say.js +21 -13
- package/dist/commands/status.d.ts +6 -2
- package/dist/commands/status.js +70 -26
- package/dist/commands/study.d.ts +11 -1
- package/dist/commands/study.js +39 -7
- package/dist/commands/view.d.ts +17 -5
- package/dist/commands/view.js +67 -10
- package/dist/core/facts.d.ts +14 -2
- package/dist/core/facts.js +32 -7
- package/dist/core/game.d.ts +5 -0
- package/dist/core/game.js +9 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -1
- package/dist/core/placement.d.ts +63 -0
- package/dist/core/placement.js +259 -0
- package/dist/core/possibilities.d.ts +38 -0
- package/dist/core/possibilities.js +254 -0
- package/dist/core/review.d.ts +6 -1
- package/dist/core/review.js +21 -6
- package/dist/games/ai-model.d.ts +4 -2
- package/dist/games/ai-model.js +10 -1
- package/dist/games/analysis.d.ts +31 -4
- package/dist/games/analysis.js +44 -5
- package/dist/games/archive.d.ts +6 -3
- package/dist/games/archive.js +13 -5
- package/dist/games/current-game.d.ts +26 -2
- package/dist/games/current-game.js +57 -21
- package/dist/games/files.d.ts +7 -0
- package/dist/games/files.js +48 -1
- package/dist/games/index.d.ts +4 -4
- package/dist/games/index.js +4 -4
- package/dist/games/table-talk.d.ts +2 -0
- package/dist/games/table-talk.js +5 -1
- package/dist/games/talk.d.ts +4 -3
- package/dist/games/talk.js +3 -3
- package/dist/home/data-folder.d.ts +2 -0
- package/dist/home/data-folder.js +5 -1
- package/dist/home/index.d.ts +1 -1
- package/dist/home/index.js +1 -1
- package/dist/host-files/agents.d.ts +2 -2
- package/dist/host-files/agents.js +12 -6
- package/dist/host-files/host-files.d.ts +5 -3
- package/dist/host-files/host-files.js +15 -8
- package/dist/host-files/index.d.ts +2 -2
- package/dist/host-files/index.js +2 -2
- package/dist/page/app/app.js +59 -22
- package/dist/page/app/keys.js +16 -2
- package/dist/page/app/served.js +2 -2
- package/dist/page/app/title.js +17 -0
- package/dist/page/avatars/torretta.js +4 -4
- package/dist/page/board/board.js +2 -1
- package/dist/page/board/marks.js +4 -24
- package/dist/page/board/signs.js +23 -0
- package/dist/page/dialogs/analyses.js +4 -4
- package/dist/page/dialogs/dialog.js +4 -1
- package/dist/page/dialogs/dialogs.css +84 -10
- package/dist/page/dialogs/profiles.js +95 -42
- package/dist/page/game/column.js +6 -2
- package/dist/page/game/game.css +12 -0
- package/dist/page/game/move-list.js +2 -2
- package/dist/page/game/plies.js +11 -0
- package/dist/page/guide/blocks.js +67 -0
- package/dist/page/guide/en.md +66 -0
- package/dist/page/guide/foot.js +18 -0
- package/dist/page/guide/guide.css +200 -0
- package/dist/page/guide/guide.js +89 -0
- package/dist/page/guide/it.md +66 -0
- package/dist/page/guide/markdown.js +67 -0
- package/dist/page/helps/helps.css +10 -11
- package/dist/page/helps/helps.js +15 -15
- package/dist/page/layout/favicon.svg +13 -0
- package/dist/page/layout/header.js +3 -3
- package/dist/page/layout/icon.icns +0 -0
- package/dist/page/layout/icon.ico +0 -0
- package/dist/page/layout/icon.png +0 -0
- package/dist/page/layout/layout.css +140 -9
- package/dist/page/layout/window.js +45 -0
- package/dist/page/legend/legend.css +203 -0
- package/dist/page/legend/legend.js +169 -0
- package/dist/page/legend/view.js +54 -0
- package/dist/page/main.js +4 -1
- package/dist/page/players/card.js +1 -1
- package/dist/page/profile/profile.js +14 -9
- package/dist/page/profile/settings.js +9 -0
- package/dist/page/style.css +4 -0
- package/dist/page/table/opener.css +65 -0
- package/dist/page/table/opener.js +59 -0
- package/dist/page/talk/chat.css +97 -0
- package/dist/page/talk/talk.css +38 -26
- package/dist/page/talk/talk.js +31 -7
- package/dist/page/text.js +120 -18
- package/dist/page/theme.css +7 -0
- package/dist/profiles/index.d.ts +2 -1
- package/dist/profiles/index.js +2 -1
- package/dist/profiles/profiles.d.ts +10 -15
- package/dist/profiles/profiles.js +30 -15
- package/dist/profiles/ratings.d.ts +35 -0
- package/dist/profiles/ratings.js +61 -0
- package/dist/server/bell.d.ts +24 -3
- package/dist/server/bell.js +84 -10
- package/dist/server/caller.d.ts +2 -1
- package/dist/server/caller.js +3 -2
- package/dist/server/mcp.d.ts +7 -3
- package/dist/server/mcp.js +106 -56
- package/dist/server/page/analyses.d.ts +2 -0
- package/dist/server/page/analyses.js +20 -5
- package/dist/server/page/attach.d.ts +4 -0
- package/dist/server/page/attach.js +112 -0
- package/dist/server/page/chat.d.ts +60 -0
- package/dist/server/page/chat.js +79 -0
- package/dist/server/page/events.d.ts +7 -0
- package/dist/server/page/events.js +20 -0
- package/dist/server/page/files.js +10 -1
- package/dist/server/page/game.js +62 -29
- package/dist/server/page/page.d.ts +47 -7
- package/dist/server/page/page.js +74 -51
- package/dist/server/page/profiles.d.ts +5 -0
- package/dist/server/page/profiles.js +26 -10
- package/dist/server/page/table.d.ts +39 -0
- package/dist/server/page/table.js +102 -0
- package/dist/server/table/browser.d.ts +6 -0
- package/dist/server/table/browser.js +48 -0
- package/dist/server/table/hosts/chats.d.ts +7 -0
- package/dist/server/table/hosts/chats.js +31 -0
- package/dist/server/table/hosts/find.d.ts +2 -0
- package/dist/server/table/hosts/find.js +20 -0
- package/dist/server/table/hosts/hosts.d.ts +34 -0
- package/dist/server/table/hosts/hosts.js +49 -0
- package/dist/server/table/hosts/index.d.ts +3 -0
- package/dist/server/table/hosts/index.js +4 -0
- package/dist/server/table/hosts/play.d.ts +25 -0
- package/dist/server/table/hosts/play.js +152 -0
- package/dist/server/table/hosts/seat.d.ts +25 -0
- package/dist/server/table/hosts/seat.js +74 -0
- package/dist/server/table/hosts/terminal.d.ts +6 -0
- package/dist/server/table/hosts/terminal.js +63 -0
- package/dist/server/table/icon/index.d.ts +32 -0
- package/dist/server/table/icon/index.js +83 -0
- package/dist/server/table/icon/linux.d.ts +5 -0
- package/dist/server/table/icon/linux.js +47 -0
- package/dist/server/table/icon/macos.d.ts +3 -0
- package/dist/server/table/icon/macos.js +38 -0
- package/dist/server/table/icon/windows.d.ts +3 -0
- package/dist/server/table/icon/windows.js +48 -0
- package/dist/server/table/index.d.ts +6 -0
- package/dist/server/table/index.js +7 -0
- package/dist/server/table/launch.d.ts +19 -0
- package/dist/server/table/launch.js +78 -0
- package/dist/server/table/link.d.ts +10 -0
- package/dist/server/table/link.js +230 -0
- package/dist/server/table/table.d.ts +35 -0
- package/dist/server/table/table.js +94 -0
- package/dist/sites/chesscom.d.ts +4 -0
- package/dist/sites/chesscom.js +25 -0
- package/dist/sites/choice.d.ts +17 -0
- package/dist/sites/choice.js +13 -0
- package/dist/sites/index.d.ts +2 -0
- package/dist/sites/index.js +4 -0
- package/dist/sites/lichess.d.ts +4 -0
- package/dist/sites/lichess.js +32 -0
- package/dist/sites/request.d.ts +11 -0
- package/dist/sites/request.js +15 -0
- package/dist/sites/sites.d.ts +6 -0
- package/dist/sites/sites.js +33 -0
- package/instructions/analysis.md +1 -1
- package/instructions/analyst-agent.md +1 -1
- package/instructions/coach.md +2 -1
- package/instructions/opponent.md +6 -6
- package/instructions/ponder.md +1 -0
- package/instructions/start.md +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/* The guide: a tall dialog, an index of chips, each part folding under its title, and the foot with who made Chesspal and the credits (Zoe) */
|
|
2
|
+
|
|
3
|
+
.dialog:has(> .guide) {
|
|
4
|
+
height: calc(100vh - 48px);
|
|
5
|
+
max-height: 90vh;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.guide {
|
|
9
|
+
font-size: 14px;
|
|
10
|
+
line-height: 1.55;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.guide p,
|
|
14
|
+
.guide ul,
|
|
15
|
+
.guide ol {
|
|
16
|
+
margin: 0 0 10px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.guide ul,
|
|
20
|
+
.guide ol {
|
|
21
|
+
padding-inline-start: 22px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.guide li + li {
|
|
25
|
+
margin-top: 4px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.guide a {
|
|
29
|
+
color: var(--accent);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.guide code {
|
|
33
|
+
padding: 1px 5px;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
background: var(--quote);
|
|
36
|
+
font-size: 13px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* A heading a chip or a link brings into sight stays under the dialog's own title */
|
|
40
|
+
.guide [id] {
|
|
41
|
+
scroll-margin-top: 64px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.guide-index {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-wrap: wrap;
|
|
47
|
+
gap: 8px;
|
|
48
|
+
margin: 4px 0 14px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.guide-chip {
|
|
52
|
+
min-height: 32px;
|
|
53
|
+
padding: 5px 12px;
|
|
54
|
+
border: 1px solid var(--line);
|
|
55
|
+
border-radius: 999px;
|
|
56
|
+
background: var(--card);
|
|
57
|
+
color: var(--ink);
|
|
58
|
+
font: inherit;
|
|
59
|
+
font-size: 13px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.guide-chip:hover {
|
|
63
|
+
border-color: var(--accent);
|
|
64
|
+
color: var(--accent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* A part: its title, a chevron that turns when it opens */
|
|
68
|
+
.guide-part {
|
|
69
|
+
border-top: 1px solid var(--line);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.guide-part summary {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: 8px;
|
|
76
|
+
padding: 10px 0;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
list-style: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.guide-part summary::-webkit-details-marker {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.guide-part summary::before,
|
|
86
|
+
.credits summary::before {
|
|
87
|
+
content: "";
|
|
88
|
+
width: 7px;
|
|
89
|
+
height: 7px;
|
|
90
|
+
border-right: 1.7px solid var(--soft);
|
|
91
|
+
border-bottom: 1.7px solid var(--soft);
|
|
92
|
+
transform: rotate(-45deg);
|
|
93
|
+
transition: transform 0.15s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.guide-part[open] summary::before,
|
|
97
|
+
.credits[open] summary::before {
|
|
98
|
+
transform: rotate(45deg);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.guide-part h3 {
|
|
102
|
+
margin: 0;
|
|
103
|
+
font-size: 16px;
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.guide-part[open] {
|
|
108
|
+
padding-bottom: 6px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.guide-table {
|
|
112
|
+
overflow-x: auto;
|
|
113
|
+
margin: 0 0 10px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.guide-table table {
|
|
117
|
+
border-collapse: collapse;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.guide-table th,
|
|
121
|
+
.guide-table td {
|
|
122
|
+
padding: 6px 10px;
|
|
123
|
+
border-bottom: 1px solid var(--line);
|
|
124
|
+
text-align: start;
|
|
125
|
+
vertical-align: top;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* The foot: who made Chesspal, where it lives, the heart that supports it; the credits folded under it */
|
|
129
|
+
.guide-foot {
|
|
130
|
+
display: grid;
|
|
131
|
+
gap: 6px;
|
|
132
|
+
margin-top: 18px;
|
|
133
|
+
padding-top: 14px;
|
|
134
|
+
border-top: 1px solid var(--line);
|
|
135
|
+
color: var(--soft);
|
|
136
|
+
font-size: 12.5px;
|
|
137
|
+
text-align: center;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.guide-foot p {
|
|
141
|
+
margin: 0;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.foot-own {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-wrap: wrap;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
gap: 6px 14px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.guide .foot-own a {
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: 5px;
|
|
156
|
+
color: inherit;
|
|
157
|
+
text-decoration: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.guide .foot-own a:hover,
|
|
161
|
+
.guide .foot-own a:focus-visible {
|
|
162
|
+
color: var(--ink);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.foot-own .icon {
|
|
166
|
+
width: 16px;
|
|
167
|
+
height: 16px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.foot-support .icon {
|
|
171
|
+
color: var(--low-time);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.credits summary {
|
|
175
|
+
display: inline-flex;
|
|
176
|
+
align-items: center;
|
|
177
|
+
gap: 6px;
|
|
178
|
+
cursor: pointer;
|
|
179
|
+
list-style: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.credits summary::-webkit-details-marker {
|
|
183
|
+
display: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.credits ul {
|
|
187
|
+
margin: 8px auto 0;
|
|
188
|
+
max-width: 60ch;
|
|
189
|
+
text-align: start;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@media (max-width: 600px) {
|
|
193
|
+
.dialog:has(> .guide) {
|
|
194
|
+
width: 100vw;
|
|
195
|
+
max-width: 100vw;
|
|
196
|
+
height: 100vh;
|
|
197
|
+
max-height: 100vh;
|
|
198
|
+
border-radius: 0;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
// The guide, from the book in the header: how to play, in the page's language (guide/<code>.md), an index of chips, its parts folding, the first two
|
|
3
|
+
// open, and its foot; its anchors are the English guide's, so that a link finds its heading in both languages.
|
|
4
|
+
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
5
|
+
import { Dialog } from "../dialogs/dialog.js";
|
|
6
|
+
import { anchorOf, markdownBlocks } from "./blocks.js";
|
|
7
|
+
import { GuideFoot } from "./foot.js";
|
|
8
|
+
import { Blocks, Inline } from "./markdown.js";
|
|
9
|
+
/** The guide in its dialog. */
|
|
10
|
+
export function GuideDialog({ open, onClose, text }) {
|
|
11
|
+
return (_jsx(Dialog, { open: open, title: text.guide.title, onClose: onClose, text: text, children: _jsx(Guide, { text: text }) }));
|
|
12
|
+
}
|
|
13
|
+
function Guide({ text }) {
|
|
14
|
+
const guide = useGuide(text.language);
|
|
15
|
+
const [open, setOpen] = useState(() => new Set([0, 1]));
|
|
16
|
+
const [sought, setSought] = useState();
|
|
17
|
+
const { lead, parts, anchors } = useMemo(() => guideParts(guide.own, guide.english), [guide]);
|
|
18
|
+
const toggle = (index, event) => {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
setOpen((before) => new Set(before.has(index) ? [...before].filter((one) => one !== index) : [...before, index]));
|
|
21
|
+
};
|
|
22
|
+
// A chip of the index, or a link to a heading, opens its part and brings the heading into sight.
|
|
23
|
+
const go = (id) => {
|
|
24
|
+
const index = parts.findIndex((part) => [part.heading, ...part.blocks].some((block) => anchors.get(block) === id));
|
|
25
|
+
if (index >= 0) {
|
|
26
|
+
setOpen((before) => new Set([...before, index]));
|
|
27
|
+
setSought(id);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const goToAnchor = (anchor) => go(idOf(anchor));
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (sought === undefined) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setSought(undefined);
|
|
36
|
+
document.getElementById(sought)?.scrollIntoView({ block: "start", behavior: matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth" });
|
|
37
|
+
}, [sought]);
|
|
38
|
+
return (_jsxs("div", { class: "guide", children: [_jsx(Blocks, { blocks: lead, anchors: anchors, go: goToAnchor }), _jsx("nav", { class: "guide-index", "aria-label": text.guide.contents, children: parts.map((part) => (_jsx("button", { type: "button", class: "guide-chip", onClick: () => go(anchors.get(part.heading) ?? ""), children: _jsx(Inline, { text: part.heading.text, go: goToAnchor }) }, part.heading.text))) }), parts.map((part, index) => (_jsxs("details", { id: anchors.get(part.heading), class: "guide-part", open: open.has(index), children: [_jsx("summary", { onClick: (event) => toggle(index, event), children: _jsx("h3", { children: part.heading.text }) }), _jsx(Blocks, { blocks: part.blocks, anchors: anchors, go: goToAnchor })] }, part.heading.text))), _jsx(GuideFoot, { text: text })] }));
|
|
39
|
+
}
|
|
40
|
+
/** The guide in a language, and in English for its anchors; empty until they are read. */
|
|
41
|
+
function useGuide(language) {
|
|
42
|
+
const [guide, setGuide] = useState({ own: "", english: "" });
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
let current = true;
|
|
45
|
+
void Promise.all([guideIn("en"), language === "en" ? Promise.resolve("") : guideIn(language)]).then(([english, own]) => {
|
|
46
|
+
if (current) {
|
|
47
|
+
setGuide({ own: own === "" ? english : own, english });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return () => {
|
|
51
|
+
current = false;
|
|
52
|
+
};
|
|
53
|
+
}, [language]);
|
|
54
|
+
return guide;
|
|
55
|
+
}
|
|
56
|
+
// Each language's guide is read once while the page lives; one the server did not give is asked again the next time.
|
|
57
|
+
const read = new Map();
|
|
58
|
+
function guideIn(language) {
|
|
59
|
+
const known = read.get(language) ?? fetch(`/page/guide/${language}.md`).then((response) => (response.ok ? response.text() : Promise.reject(new Error(response.statusText))));
|
|
60
|
+
read.set(language, known);
|
|
61
|
+
return known.catch(() => {
|
|
62
|
+
read.delete(language);
|
|
63
|
+
return "";
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** The guide's lead, the lines before its first part, its title aside; its parts; and each heading's anchor, the English one at its place. */
|
|
67
|
+
function guideParts(own, english) {
|
|
68
|
+
const blocks = markdownBlocks(own);
|
|
69
|
+
const headings = blocks.filter((block) => block.kind === "heading");
|
|
70
|
+
const englishHeadings = markdownBlocks(english).filter((block) => block.kind === "heading");
|
|
71
|
+
const sameShape = headings.length === englishHeadings.length && headings.every((heading, index) => heading.level === englishHeadings[index]?.level);
|
|
72
|
+
const anchors = new Map(headings.map((heading, index) => [heading, idOf(anchorOf((sameShape ? englishHeadings[index] : heading)?.text ?? heading.text))]));
|
|
73
|
+
const first = blocks.findIndex((block) => block.kind === "heading" && block.level === 2);
|
|
74
|
+
const lead = (first < 0 ? blocks : blocks.slice(0, first)).filter((block) => !(block.kind === "heading" && block.level === 1));
|
|
75
|
+
const parts = [];
|
|
76
|
+
for (const block of first < 0 ? [] : blocks.slice(first)) {
|
|
77
|
+
if (block.kind === "heading" && block.level === 2) {
|
|
78
|
+
parts.push({ heading: block, blocks: [] });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
parts.at(-1)?.blocks.push(block);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { lead, parts, anchors };
|
|
85
|
+
}
|
|
86
|
+
// A heading's id on the page, apart from the page's own.
|
|
87
|
+
function idOf(anchor) {
|
|
88
|
+
return `guide-${anchor}`;
|
|
89
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Chesspal: la guida
|
|
2
|
+
|
|
3
|
+
Con **Chesspal** giochi a scacchi contro un'**AI**, su una scacchiera vera nel browser, e impari con lei. Questa guida è per chi gioca: non serve saper programmare.
|
|
4
|
+
|
|
5
|
+
## Come funziona
|
|
6
|
+
|
|
7
|
+
| Chi | Cosa fa |
|
|
8
|
+
|---|---|
|
|
9
|
+
| **Tu** | Muovi sulla scacchiera e parli con l'AI. |
|
|
10
|
+
| **L'AI** | Gioca, ragiona, giudica le mosse e ti risponde, nella sua chat. |
|
|
11
|
+
| **Chesspal** | Tiene le regole, la scacchiera, l'orologio e le tue partite, e passa all'AI i fatti della posizione. |
|
|
12
|
+
|
|
13
|
+
Le finestre sono due: questa **pagina**, con la scacchiera, e la **chat** dell'AI, in una finestra a parte. L'AI gioca da lì, e Chesspal la collega alla pagina.
|
|
14
|
+
|
|
15
|
+
Nessun motore di scacchi gioca al posto dell'AI: ogni mossa e ogni giudizio sono suoi. Chesspal controlla che ogni mossa sia legale e dice all'AI cosa c'è sulla scacchiera: i pezzi attaccati e difesi, le inchiodature, il nome dell'apertura.
|
|
16
|
+
|
|
17
|
+
## Avvio rapido
|
|
18
|
+
|
|
19
|
+
1. Installa **Node.js** da [nodejs.org](https://nodejs.org), la versione consigliata (LTS), e un'AI col tuo abbonamento: **Claude Code** o **Codex**.
|
|
20
|
+
2. Apri un terminale, la finestra dove si scrivono i comandi (su Windows il Prompt dei comandi: tasto Start, scrivi `cmd`, Invio; su macOS il Terminale), e scrivi `npm install -g chesspal`, poi `chesspal`: si apre questa pagina. La prima volta ti propone un'icona sul desktop, per aprirla con un doppio clic.
|
|
21
|
+
3. Premi **Claude Code** o **Codex**: la chat dell'AI si apre in una finestra sua. Tienila aperta mentre giochi.
|
|
22
|
+
4. Premi **Nuova partita**, scegli il colore, l'orologio e il livello dell'AI, e premi **Inizia**.
|
|
23
|
+
|
|
24
|
+
## Se ti sei perso
|
|
25
|
+
|
|
26
|
+
- **L'AI non risponde**: guarda la sua finestra. Se ti sta chiedendo qualcosa, rispondi lì; se l'hai chiusa, premi di nuovo **Claude Code** o **Codex**.
|
|
27
|
+
- **La pagina non si apre**: fai doppio clic sull'icona di Chesspal, o aprila come nell'[Avvio rapido](#quick-start).
|
|
28
|
+
- **Le tue partite** stanno in una cartella sul tuo computer: **Chi gioca?**, dalla tua immagine in alto a destra, dice dove, e **Apri la cartella** la apre.
|
|
29
|
+
|
|
30
|
+
## La chat dell'AI
|
|
31
|
+
|
|
32
|
+
- La pagina apre la **chat** dell'AI coi suoi pulsanti, in una finestra a parte: lì l'AI pensa e scrive. Accanto ai pulsanti scegli il modello (la versione dell'AI) e quanto ragiona: più ragiona, più gioca lenta.
|
|
33
|
+
- Parli con l'AI nella casella sotto il suo nome, qualche parola a ogni mossa: le legge con la tua mossa. Puoi scriverle anche nella sua finestra.
|
|
34
|
+
- Anche una **mossa scritta** nella chat vale: `e4`, `Nf3`, con le lettere inglesi dei pezzi (K Q R B N), o a parole.
|
|
35
|
+
- A ogni **nuova partita** la chat aperta dalla pagina riparte pulita da sola, se l'AI ha finito di pensare: una chat lunga rende l'AI più lenta.
|
|
36
|
+
|
|
37
|
+
## La partita
|
|
38
|
+
|
|
39
|
+
- **Muovi** trascinando un pezzo, o cliccando il pezzo e poi la sua casa. I pallini segnano le mosse legali; quando una mossa è vietata, la pagina dice perché.
|
|
40
|
+
- **L'orologio**, se ne hai scelto uno, parte con **Inizia**. Anche l'AI ha il suo, e sa quanto tempo le resta.
|
|
41
|
+
- **Proponi patta** nel tuo turno, dopo una mossa a testa; **Abbandona** chiude la partita.
|
|
42
|
+
- **Gli aiuti**: con «L'AI ti insegna mentre giochi», nelle impostazioni, la scheda **Aiuti** mostra i pezzi in pericolo, le inchiodature e i pedoni, e l'AI, da allenatore, ti dà un'idea. L'AI disegna frecce colorate sulla scacchiera: il **?** accanto alle mosse spiega ogni segno.
|
|
43
|
+
- **Gira** volta la scacchiera; **PGN** salva le mosse nel file che leggono tutti i siti di scacchi, da aprire su Lichess o Chess.com.
|
|
44
|
+
|
|
45
|
+
## Il ripasso e l'analisi
|
|
46
|
+
|
|
47
|
+
- A partita finita premi **Analizza la partita**: l'AI la ripassa e mette sulle mosse i suoi giudizi e le sue note. Le leggi scorrendo le mosse con le frecce.
|
|
48
|
+
- **Analisi**, nell'intestazione, apre le tue analisi e carica una partita giocata altrove: incolla il suo PGN, o scegli un file .pgn.
|
|
49
|
+
- Chiedi all'AI della partita nella casella sotto il suo nome.
|
|
50
|
+
- **PGN annotato** salva la partita coi commenti.
|
|
51
|
+
|
|
52
|
+
## I giocatori e il profilo
|
|
53
|
+
|
|
54
|
+
- **Chi gioca?**: la tua immagine in alto a destra sceglie un giocatore o ne aggiunge uno. Ognuno ha le sue partite e le sue impostazioni.
|
|
55
|
+
- **Il tuo profilo**: un clic sulla tua immagine accanto alla scacchiera, o la matita in **Chi gioca?**, cambia la tua immagine, il nome, come giochi, i tuoi nomi su Lichess e Chess.com e il tuo punteggio FIDE.
|
|
56
|
+
- **Il tuo punteggio**: dai tuoi nomi lì, Chesspal legge il tuo punteggio pubblico, quello delle partite rapide se le giochi, e lo dice all'AI. Niente password: quel punteggio lo vede chiunque sul sito. Senza un nome lì, l'AI sa come giochi da **Come giochi?**
|
|
57
|
+
- **Il punteggio FIDE**, se giochi tornei, lo scrivi tu: nessun sito lo dà a Chesspal. L'AI lo legge accanto a quello del sito, ognuno per sé.
|
|
58
|
+
- Se carichi una partita giocata su Lichess o Chess.com, Chesspal sa da che colore giocavi tu.
|
|
59
|
+
|
|
60
|
+
## Domande e problemi
|
|
61
|
+
|
|
62
|
+
- **L'AI è lenta**: ogni mossa la pensa davvero. Scegli un modello più veloce, o meno ragionamento, accanto ai pulsanti della chat.
|
|
63
|
+
- **Quanto costa**: Chesspal è gratis; l'AI usa il tuo abbonamento, e non paghi niente in più.
|
|
64
|
+
- **L'AI può fare una mossa illegale?** No: Chesspal controlla ogni mossa. I suoi giudizi però sono suoi, e un'AI può sbagliare, come una persona.
|
|
65
|
+
- **Un backup**: copia la cartella delle tue partite, con la chat e la pagina chiuse.
|
|
66
|
+
- **Qualcosa non va?** Raccontacelo su [GitHub](https://github.com/stefanocaronia/chesspal/issues): cosa hai fatto, cosa ti aspettavi, cosa è successo. GitHub è il sito dove si fa Chesspal: ci scrivi con un account gratuito, e chiunque può leggere.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
/** Blocks drawn as the page's elements: headings one level under the part's, each with the anchor given by its place among them; a link to an anchor calls go. */
|
|
3
|
+
export function Blocks({ blocks, anchors, go }) {
|
|
4
|
+
return _jsx(_Fragment, { children: blocks.map((block, index) => _jsx(BlockView, { block: block, anchors: anchors, go: go }, index)) });
|
|
5
|
+
}
|
|
6
|
+
/** A line's text with its bold, italic, code and links. */
|
|
7
|
+
export function Inline({ text, go }) {
|
|
8
|
+
const parts = [];
|
|
9
|
+
const marks = /\*\*(.+?)\*\*|\*(.+?)\*|`([^`]+)`|\[([^\]]+)\]\(([^)\s]+)\)/g;
|
|
10
|
+
let from = 0;
|
|
11
|
+
for (const match of text.matchAll(marks)) {
|
|
12
|
+
parts.push(text.slice(from, match.index));
|
|
13
|
+
const [, bold, italic, code, label, target] = match;
|
|
14
|
+
const key = match.index;
|
|
15
|
+
if (bold !== undefined) {
|
|
16
|
+
parts.push(_jsx("strong", { children: _jsx(Inline, { text: bold, go: go }) }, key));
|
|
17
|
+
}
|
|
18
|
+
else if (italic !== undefined) {
|
|
19
|
+
parts.push(_jsx("em", { children: _jsx(Inline, { text: italic, go: go }) }, key));
|
|
20
|
+
}
|
|
21
|
+
else if (code !== undefined) {
|
|
22
|
+
parts.push(_jsx("code", { children: code }, key));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
parts.push(_jsx(LinkView, { label: label ?? "", target: target ?? "", go: go }, key));
|
|
26
|
+
}
|
|
27
|
+
from = match.index + match[0].length;
|
|
28
|
+
}
|
|
29
|
+
parts.push(text.slice(from));
|
|
30
|
+
return _jsx(_Fragment, { children: parts });
|
|
31
|
+
}
|
|
32
|
+
function BlockView({ block, anchors, go }) {
|
|
33
|
+
switch (block.kind) {
|
|
34
|
+
case "heading": {
|
|
35
|
+
const Tag = `h${Math.min(block.level + 1, 6)}`;
|
|
36
|
+
return (_jsx(Tag, { id: anchors.get(block), children: _jsx(Inline, { text: block.text, go: go }) }));
|
|
37
|
+
}
|
|
38
|
+
case "paragraph":
|
|
39
|
+
return (_jsx("p", { children: _jsx(Inline, { text: block.text, go: go }) }));
|
|
40
|
+
case "list": {
|
|
41
|
+
const items = block.items.map((item, index) => (_jsx("li", { children: _jsx(Inline, { text: item, go: go }) }, index)));
|
|
42
|
+
return block.ordered ? _jsx("ol", { children: items }) : _jsx("ul", { children: items });
|
|
43
|
+
}
|
|
44
|
+
case "table":
|
|
45
|
+
return (_jsx("div", { class: "guide-table", children: _jsxs("table", { children: [_jsx("thead", { children: _jsx("tr", { children: block.head.map((cell, index) => (_jsx("th", { scope: "col", children: _jsx(Inline, { text: cell, go: go }) }, index))) }) }), _jsx("tbody", { children: block.rows.map((row, index) => (_jsx("tr", { children: row.map((cell, column) => (_jsx("td", { children: _jsx(Inline, { text: cell, go: go }) }, column))) }, index))) })] }) }));
|
|
46
|
+
case "quote":
|
|
47
|
+
return (_jsx("blockquote", { children: _jsx(Blocks, { blocks: block.blocks, anchors: anchors, go: go }) }));
|
|
48
|
+
case "code":
|
|
49
|
+
return _jsx("pre", { children: _jsx("code", { children: block.text }) });
|
|
50
|
+
case "rule":
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** A link: to a heading of the guide, which go brings into sight, leaving the address as it is (it names the game); to a page of the web, in a new tab. */
|
|
55
|
+
function LinkView({ label, target, go }) {
|
|
56
|
+
if (target.startsWith("#")) {
|
|
57
|
+
return (_jsx("a", { href: target, onClick: (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
go(target.slice(1));
|
|
60
|
+
}, children: _jsx(Inline, { text: label, go: go }) }));
|
|
61
|
+
}
|
|
62
|
+
// Only a web address is a link, never a script.
|
|
63
|
+
if (!/^https?:\/\//i.test(target)) {
|
|
64
|
+
return _jsx(Inline, { text: label, go: go });
|
|
65
|
+
}
|
|
66
|
+
return (_jsx("a", { href: target, target: "_blank", rel: "noreferrer", children: _jsx(Inline, { text: label, go: go }) }));
|
|
67
|
+
}
|
|
@@ -146,8 +146,9 @@
|
|
|
146
146
|
stroke-dasharray: 0.22 0.11 0 0.11;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
/* The map (Zoe,
|
|
150
|
-
|
|
149
|
+
/* The map (Zoe's veil, Stefano's choice): a grey veil flattens the board, sand where only White reaches, slate where only Black does; the squares both reach
|
|
150
|
+
and those none does stay grey, and their counts speak, together in the middle as a domino. Never an opaque ground: the square shows through. The drawings
|
|
151
|
+
and the pieces step back */
|
|
151
152
|
.board.mapping ~ .arrows,
|
|
152
153
|
.board.mapping .move-badge {
|
|
153
154
|
opacity: 0.12;
|
|
@@ -158,26 +159,22 @@
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
.board.mapping .piece {
|
|
161
|
-
opacity: 0.
|
|
162
|
+
opacity: 0.5;
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
.control-ground {
|
|
165
166
|
position: absolute;
|
|
166
167
|
inset: 0;
|
|
167
168
|
pointer-events: none;
|
|
168
|
-
background:
|
|
169
|
+
background: var(--map-veil);
|
|
169
170
|
}
|
|
170
171
|
|
|
171
172
|
.control-ground.white {
|
|
172
|
-
background:
|
|
173
|
+
background: var(--map-white);
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
.control-ground.black {
|
|
176
|
-
background:
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.control-ground.both {
|
|
180
|
-
background: linear-gradient(135deg, #eef1f4 50%, #2b323c 50%);
|
|
177
|
+
background: var(--map-black);
|
|
181
178
|
}
|
|
182
179
|
|
|
183
180
|
.control-count {
|
|
@@ -456,13 +453,15 @@
|
|
|
456
453
|
fill: var(--card);
|
|
457
454
|
}
|
|
458
455
|
|
|
459
|
-
/* The skewer of the board, in the line's colour and without
|
|
456
|
+
/* The pushpin and the skewer of the board, in the line's colour and without their halo */
|
|
457
|
+
.help-rows .help-pin,
|
|
460
458
|
.help-rows .help-skewer {
|
|
461
459
|
left: 7px;
|
|
462
460
|
top: 4px;
|
|
463
461
|
height: 17px;
|
|
464
462
|
}
|
|
465
463
|
|
|
464
|
+
.help-rows .help-pin path,
|
|
466
465
|
.help-rows .help-skewer path {
|
|
467
466
|
fill: var(--row);
|
|
468
467
|
stroke: none;
|
package/dist/page/helps/helps.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-ru
|
|
|
3
3
|
// the coach's words (D-88).
|
|
4
4
|
import { Fragment } from "preact";
|
|
5
5
|
import { useState } from "preact/hooks";
|
|
6
|
-
import { Notch, Skewer } from "../board/marks.js";
|
|
6
|
+
import { Notch, Pushpin, Skewer } from "../board/marks.js";
|
|
7
7
|
import { BadgeIcon, NoteIcon } from "../game/badges.js";
|
|
8
8
|
import { FoldButton, useCardFold } from "../game/card-fold.js";
|
|
9
9
|
import { Figurines } from "../game/move-list.js";
|
|
@@ -15,7 +15,7 @@ import { groupOf, pinSquares, riskSquares, squaresOf, strategyOf } from "./view.
|
|
|
15
15
|
* its button or M is held, and the board without its drawings while their button is. A line under the pointer lights its squares on the board. Its title
|
|
16
16
|
* folds it to the title and opens it again (Stefano).
|
|
17
17
|
*/
|
|
18
|
-
export function HelpsCard({ helps, judged, hint, hintOpen, onHint, onPoint, onMap, mapping, onBare, bare, text }) {
|
|
18
|
+
export function HelpsCard({ helps, judged, hint, hintOpen, onHint, onPoint, onMap, mapping, onBare, bare, legend, text }) {
|
|
19
19
|
const [tab, setTab] = useState("tactics");
|
|
20
20
|
// The tactics' lines the player opened stay open from move to move, so that a pin is followed as the game goes on.
|
|
21
21
|
const [opened, setOpened] = useState(new Set());
|
|
@@ -29,7 +29,7 @@ export function HelpsCard({ helps, judged, hint, hintOpen, onHint, onPoint, onMa
|
|
|
29
29
|
}
|
|
30
30
|
toggleFold();
|
|
31
31
|
};
|
|
32
|
-
return (_jsxs("section", { class: `card helps-card${folded ? " folded" : ""}`, "aria-label": words.title, children: [_jsxs("div", { class: "helps-head", children: [_jsx("h3", { class: "card-title", children: _jsxs(FoldButton, { folded: folded, onFold: fold, text: text, children: [_jsx(HelpsIcon, {}), words.title] }) }), _jsxs("span", { class: "helps-tools", children: [_jsx(MapButton, { mapping: mapping, onMap: onMap, text: text }), _jsx(BareButton, { bare: bare, onBare: onBare, text: text })] })] }), folded ? null : (_jsxs(_Fragment, { children: [judged.length === 0 && hint === undefined ? null : (_jsxs("section", { class: `coach${judged.length === 0 ? " hint-only" : ""}`, "aria-label": words.coach, children: [judged.map((ply) => _jsx(Judged, { ply: ply, text: text }, `${ply.number} ${ply.side}`)), hint === undefined ? null : _jsx(HintLine, { hint: hint, open: hintOpen, onOpen: onHint, text: text })] })), _jsxs("div", { class: "tabs", role: "tablist", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab === "tactics", onClick: () => setTab("tactics"), children: words.tactics }), _jsx("button", { type: "button", role: "tab", "aria-selected": tab === "strategy", onClick: () => setTab("strategy"), children: words.strategy })] }), tab === "tactics"
|
|
32
|
+
return (_jsxs("section", { class: `card helps-card${folded ? " folded" : ""}`, "aria-label": words.title, children: [_jsxs("div", { class: "helps-head", children: [_jsx("h3", { class: "card-title", children: _jsxs(FoldButton, { folded: folded, onFold: fold, text: text, children: [_jsx(HelpsIcon, {}), words.title] }) }), _jsxs("span", { class: "helps-tools", children: [_jsx(MapButton, { mapping: mapping, onMap: onMap, text: text }), _jsx(BareButton, { bare: bare, onBare: onBare, text: text }), legend] })] }), folded ? null : (_jsxs(_Fragment, { children: [judged.length === 0 && hint === undefined ? null : (_jsxs("section", { class: `coach${judged.length === 0 ? " hint-only" : ""}`, "aria-label": words.coach, children: [judged.map((ply) => _jsx(Judged, { ply: ply, text: text }, `${ply.number} ${ply.side}`)), hint === undefined ? null : _jsx(HintLine, { hint: hint, open: hintOpen, onOpen: onHint, text: text })] })), _jsxs("div", { class: "tabs", role: "tablist", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab === "tactics", onClick: () => setTab("tactics"), children: words.tactics }), _jsx("button", { type: "button", role: "tab", "aria-selected": tab === "strategy", onClick: () => setTab("strategy"), children: words.strategy })] }), tab === "tactics"
|
|
33
33
|
? _jsx(Tactics, { helps: helps, opened: opened, onOpen: openLine, onPoint: onPoint, text: text })
|
|
34
34
|
: _jsx(Strategy, { helps: helps, onPoint: onPoint, text: text })] }))] }));
|
|
35
35
|
}
|
|
@@ -86,20 +86,20 @@ function pointing(onPoint) {
|
|
|
86
86
|
onPointerLeave: (event) => event.pointerType === "mouse" && onPoint([]),
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
|
-
/** A line's sign: the corner of a piece at risk,
|
|
89
|
+
/** A line's sign, as the board draws it: the corner of a piece at risk, the pushpin, the skewer; a check's plus, the loose pieces' i; its colour is the line's. */
|
|
90
90
|
function LineIcon({ kind }) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
switch (kind) {
|
|
92
|
+
case "risk":
|
|
93
|
+
return _jsx(Notch, {});
|
|
94
|
+
case "pin":
|
|
95
|
+
return _jsx(Pushpin, {});
|
|
96
|
+
case "skewer":
|
|
97
|
+
return _jsx(Skewer, {});
|
|
98
|
+
case "loose":
|
|
99
|
+
return _jsx(InfoIcon, {});
|
|
100
|
+
case "check":
|
|
101
|
+
return (_jsxs(RowIcon, { children: [_jsx("circle", { cx: "8", cy: "8", r: "6.5" }), _jsx("path", { d: "M8 5v6M5 8h6" })] }));
|
|
99
102
|
}
|
|
100
|
-
return (_jsx(RowIcon, { children: kind === "pin"
|
|
101
|
-
? _jsx("path", { d: "M6.5 9.5l3-3M7.5 4.5l1-1a2.5 2.5 0 0 1 3.5 3.5l-1 1M8.5 11.5l-1 1a2.5 2.5 0 0 1-3.5-3.5l1-1" })
|
|
102
|
-
: (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "8", cy: "8", r: "6.5" }), _jsx("path", { d: "M8 5v6M5 8h6" })] })) }));
|
|
103
103
|
}
|
|
104
104
|
/** How many: the loose pieces by side, each after its pawn, the player's first (Zoe); the others in one number. */
|
|
105
105
|
function LineCount({ line, helps, words }) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<!-- Chesspal, the favicon: the Torretta with its neutral face, on a grid of 16 pixels. © 2026 Stefano Caronia, Chesspal (https://github.com/stefanocaronia/chesspal), under the licence CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/), crediting "Stefano Caronia, Chesspal". -->
|
|
3
|
+
<rect x="15" y="4" width="2" height="4" fill="#4b7cb0"/>
|
|
4
|
+
<rect x="14" y="0" width="4" height="4" rx="2" fill="#f4c84c"/>
|
|
5
|
+
<rect x="2" y="18" width="4" height="8" rx="1" fill="#4b7cb0"/>
|
|
6
|
+
<rect x="26" y="18" width="4" height="8" rx="1" fill="#4b7cb0"/>
|
|
7
|
+
<path d="M4 8H10V12H14V8H18V12H22V8H28V16L26 18V28H6V18L4 16Z" fill="#6d9fd1"/>
|
|
8
|
+
<rect x="2" y="28" width="28" height="4" rx="1" fill="#4b7cb0"/>
|
|
9
|
+
<rect x="8" y="14" width="16" height="12" rx="1.5" fill="#16263a"/>
|
|
10
|
+
<rect x="10" y="16" width="4" height="4" rx="1" fill="#f2f7fc"/>
|
|
11
|
+
<rect x="18" y="16" width="4" height="4" rx="1" fill="#f2f7fc"/>
|
|
12
|
+
<rect x="14" y="22" width="4" height="2" rx="1" fill="#f2f7fc"/>
|
|
13
|
+
</svg>
|
|
@@ -4,10 +4,10 @@ import { Torretta } from "../avatars/torretta.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* The header, with how a game over ended, or that none goes on (D-77), or the AI's draw offer with the player's answer, the player's draw
|
|
6
6
|
* and resignation in a game going on (D-65), a new game once the page has the folder's, the command that turns the board round and says whether it is
|
|
7
|
-
* turned, the switches of the sounds and the theme,
|
|
7
|
+
* turned, the switches of the sounds and the theme, the gear of the settings (D-56) and the book of the guide.
|
|
8
8
|
*/
|
|
9
|
-
export function Header({ text, verdict, kind, who, actions, onNewGame, standOut = false, onStudy, flipped, onFlip, sound, onSound, onTheme, onSettings, profile, onProfile }) {
|
|
10
|
-
return (_jsxs("header", { class: "top", children: [_jsx(Torretta, { size: 34 }), _jsx("b", { class: "title", children: "Chesspal" }), _jsxs("p", { class: `verdict${kind === undefined ? "" : ` ${kind}`}`, role: "status", children: [verdict === undefined || kind === undefined ? null : _jsx(Icon, { class: "kind", children: kindIcons[kind] }), who === undefined || verdict === undefined ? verdict : (_jsxs(_Fragment, { children: [_jsx("span", { class: "ai-name", children: who }), _jsx("span", { children: verdict })] }))] }), actions === undefined ? null : _jsx(Actions, { actions: actions, text: text }), onNewGame === undefined ? null : (_jsxs("button", { class: standOut ? "command primary" : "command", type: "button", title: text.newGame, onClick: onNewGame, children: [_jsx(Icon, { class: "new", children: _jsx("path", { d: "M12 5V19M5 12H19" }) }), _jsx("span", { children: text.newGame })] })), onStudy === undefined ? null : (_jsxs("button", { class: "command", type: "button", title: text.study.title, onClick: onStudy, children: [_jsxs(Icon, { class: "study", children: [_jsx("circle", { cx: "10.5", cy: "10.5", r: "5.5" }), _jsx("path", { d: "M14.5 14.5L20 20" })] }), _jsx("span", { children: text.study.title })] })), _jsxs("button", { class: "command", type: "button", title: text.flip, "aria-pressed": flipped, onClick: onFlip, children: [_jsx(Icon, { class: "flip", children: _jsx("path", { d: "M7 4V17M7 17L4 14M7 17L10 14M17 20V7M17 7L14 10M17 7L20 10" }) }), _jsx("span", { children: text.flip })] }), _jsx("button", { class: "icon-button", type: "button", title: text.sounds, "aria-label": text.sounds, "aria-pressed": sound, onClick: onSound, children: _jsxs(Icon, { class: "speaker", children: [_jsx("path", { d: "M5 9.5H8.5L13 5.5V18.5L8.5 14.5H5Z" }), sound ? _jsx("path", { d: "M16.5 9A4 4 0 0 1 16.5 15M19 6.5A7.5 7.5 0 0 1 19 17.5" }) : _jsx("path", { d: "M15.5 9L21.5 15M21.5 9L15.5 15" })] }) }), _jsxs("button", { class: "icon-button", type: "button", title: text.theme, "aria-label": text.theme, onClick: onTheme, children: [_jsxs(Icon, { class: "sun", children: [_jsx("circle", { cx: "12", cy: "12", r: "4" }), _jsx("path", { d: "M12 2V4M12 20V22M2 12H4M20 12H22M4.9 4.9L6.3 6.3M17.7 17.7L19.1 19.1M4.9 19.1L6.3 17.7M17.7 6.3L19.1 4.9" })] }), _jsx(Icon, { class: "moon", children: _jsx("path", { d: "M20 14.5A8 8 0 1 1 9.5 4A6.5 6.5 0 0 0 20 14.5Z" }) })] }), _jsx("button", { class: "icon-button", type: "button", title: text.settings, "aria-label": text.settings, onClick: onSettings, children: _jsxs(Icon, { class: "gear", children: [_jsx("circle", { cx: "12", cy: "12", r: "3" }), _jsx("path", { d: "M9.72 5.38L10.19 2.67L13.81 2.67L14.28 5.38L15.07 5.71L17.31 4.12L19.88 6.69L18.29 8.93L18.62 9.72L21.33 10.19L21.33 13.81L18.62 14.28L18.29 15.07L19.88 17.31L17.31 19.88L15.07 18.29L14.28 18.62L13.81 21.33L10.19 21.33L9.72 18.62L8.93 18.29L6.69 19.88L4.12 17.31L5.71 15.07L5.38 14.28L2.67 13.81L2.67 10.19L5.38 9.72L5.71 8.93L4.12 6.69L6.69 4.12L8.93 5.71Z" })] }) }), profile === undefined || onProfile === undefined ? null : (_jsx("button", { class: "icon-button player", type: "button", title: `${profile.name}: ${text.profile.profiles}`, "aria-label": `${profile.name}: ${text.profile.profiles}`, "aria-haspopup": "dialog", onClick: onProfile, children: _jsx(PlayerPicture, { profile: profile, size: 28 }) }))] }));
|
|
9
|
+
export function Header({ text, verdict, kind, who, actions, onNewGame, standOut = false, onStudy, flipped, onFlip, sound, onSound, onTheme, onSettings, onGuide, profile, onProfile }) {
|
|
10
|
+
return (_jsxs("header", { class: "top", children: [_jsx(Torretta, { size: 34 }), _jsx("b", { class: "title", children: "Chesspal" }), _jsxs("p", { class: `verdict${kind === undefined ? "" : ` ${kind}`}`, role: "status", children: [verdict === undefined || kind === undefined ? null : _jsx(Icon, { class: "kind", children: kindIcons[kind] }), who === undefined || verdict === undefined ? verdict : (_jsxs(_Fragment, { children: [_jsx("span", { class: "ai-name", children: who }), _jsx("span", { children: verdict })] }))] }), actions === undefined ? null : _jsx(Actions, { actions: actions, text: text }), onNewGame === undefined ? null : (_jsxs("button", { class: standOut ? "command primary" : "command", type: "button", title: text.newGame, onClick: onNewGame, children: [_jsx(Icon, { class: "new", children: _jsx("path", { d: "M12 5V19M5 12H19" }) }), _jsx("span", { children: text.newGame })] })), onStudy === undefined ? null : (_jsxs("button", { class: "command", type: "button", title: text.study.title, onClick: onStudy, children: [_jsxs(Icon, { class: "study", children: [_jsx("circle", { cx: "10.5", cy: "10.5", r: "5.5" }), _jsx("path", { d: "M14.5 14.5L20 20" })] }), _jsx("span", { children: text.study.title })] })), _jsxs("button", { class: "command", type: "button", title: text.flip, "aria-pressed": flipped, onClick: onFlip, children: [_jsx(Icon, { class: "flip", children: _jsx("path", { d: "M7 4V17M7 17L4 14M7 17L10 14M17 20V7M17 7L14 10M17 7L20 10" }) }), _jsx("span", { children: text.flip })] }), _jsx("button", { class: "icon-button sound-button", type: "button", title: text.sounds, "aria-label": text.sounds, "aria-pressed": sound, onClick: onSound, children: _jsxs(Icon, { class: "speaker", children: [_jsx("path", { d: "M5 9.5H8.5L13 5.5V18.5L8.5 14.5H5Z" }), sound ? _jsx("path", { d: "M16.5 9A4 4 0 0 1 16.5 15M19 6.5A7.5 7.5 0 0 1 19 17.5" }) : _jsx("path", { d: "M15.5 9L21.5 15M21.5 9L15.5 15" })] }) }), _jsxs("button", { class: "icon-button theme-button", type: "button", title: text.theme, "aria-label": text.theme, onClick: onTheme, children: [_jsxs(Icon, { class: "sun", children: [_jsx("circle", { cx: "12", cy: "12", r: "4" }), _jsx("path", { d: "M12 2V4M12 20V22M2 12H4M20 12H22M4.9 4.9L6.3 6.3M17.7 17.7L19.1 19.1M4.9 19.1L6.3 17.7M17.7 6.3L19.1 4.9" })] }), _jsx(Icon, { class: "moon", children: _jsx("path", { d: "M20 14.5A8 8 0 1 1 9.5 4A6.5 6.5 0 0 0 20 14.5Z" }) })] }), _jsx("button", { class: "icon-button", type: "button", title: text.settings, "aria-label": text.settings, onClick: onSettings, children: _jsxs(Icon, { class: "gear", children: [_jsx("circle", { cx: "12", cy: "12", r: "3" }), _jsx("path", { d: "M9.72 5.38L10.19 2.67L13.81 2.67L14.28 5.38L15.07 5.71L17.31 4.12L19.88 6.69L18.29 8.93L18.62 9.72L21.33 10.19L21.33 13.81L18.62 14.28L18.29 15.07L19.88 17.31L17.31 19.88L15.07 18.29L14.28 18.62L13.81 21.33L10.19 21.33L9.72 18.62L8.93 18.29L6.69 19.88L4.12 17.31L5.71 15.07L5.38 14.28L2.67 13.81L2.67 10.19L5.38 9.72L5.71 8.93L4.12 6.69L6.69 4.12L8.93 5.71Z" })] }) }), _jsx("button", { class: "icon-button guide-button", type: "button", title: text.guide.title, "aria-label": text.guide.title, "aria-haspopup": "dialog", onClick: onGuide, children: _jsx(Icon, { class: "book", children: _jsx("path", { d: "M12 6.5C10 5 7 4.5 3.5 5V18.5C7 18 10 18.5 12 20C14 18.5 17 18 20.5 18.5V5C17 4.5 14 5 12 6.5ZM12 6.5V20" }) }) }), profile === undefined || onProfile === undefined ? null : (_jsx("button", { class: "icon-button player", type: "button", title: `${profile.name}: ${text.profile.profiles}`, "aria-label": `${profile.name}: ${text.profile.profiles}`, "aria-haspopup": "dialog", onClick: onProfile, children: _jsx(PlayerPicture, { profile: profile, size: 28 }) }))] }));
|
|
11
11
|
}
|
|
12
12
|
/** The answer to the AI's draw offer while it stands, else the player's own offer; then the resignation. */
|
|
13
13
|
function Actions({ actions, text }) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|