chesspal 0.3.0 → 0.3.2

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.
Files changed (188) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/cli.js +6 -6
  3. package/dist/commands/{analyse-game.d.ts → analysis/analyse.d.ts} +2 -2
  4. package/dist/commands/{analyse-game.js → analysis/analyse.js} +7 -11
  5. package/dist/commands/{annotate-game.d.ts → analysis/annotate.d.ts} +1 -1
  6. package/dist/commands/{annotate-game.js → analysis/annotate.js} +6 -9
  7. package/dist/commands/{delete-review.d.ts → analysis/delete-review.d.ts} +1 -1
  8. package/dist/commands/{delete-review.js → analysis/delete-review.js} +5 -5
  9. package/dist/commands/analysis/index.d.ts +4 -0
  10. package/dist/commands/analysis/index.js +5 -0
  11. package/dist/commands/{study-games.d.ts → analysis/study-games.d.ts} +1 -1
  12. package/dist/commands/{study-games.js → analysis/study-games.js} +4 -5
  13. package/dist/commands/{agents.d.ts → answers/agents.d.ts} +3 -3
  14. package/dist/commands/{agents.js → answers/agents.js} +5 -5
  15. package/dist/commands/{clock.d.ts → answers/clock.d.ts} +2 -2
  16. package/dist/commands/{clock.js → answers/clock.js} +2 -2
  17. package/dist/commands/{drawings.d.ts → answers/drawings.d.ts} +1 -1
  18. package/dist/commands/{drawings.js → answers/drawings.js} +1 -1
  19. package/dist/commands/answers/index.d.ts +12 -0
  20. package/dist/commands/answers/index.js +14 -0
  21. package/dist/commands/{lead-input.d.ts → answers/lead-input.d.ts} +1 -1
  22. package/dist/commands/{lead-input.js → answers/lead-input.js} +1 -1
  23. package/dist/commands/{answer-lines.d.ts → answers/lines.d.ts} +2 -2
  24. package/dist/commands/{answer-lines.js → answers/lines.js} +4 -4
  25. package/dist/commands/{opening-book.d.ts → answers/opening-book.d.ts} +1 -1
  26. package/dist/commands/{opening-book.js → answers/opening-book.js} +3 -3
  27. package/dist/commands/{play-move.d.ts → answers/play-move.d.ts} +2 -2
  28. package/dist/commands/{play-move.js → answers/play-move.js} +3 -3
  29. package/dist/commands/{say.d.ts → answers/say.d.ts} +3 -3
  30. package/dist/commands/{say.js → answers/say.js} +4 -4
  31. package/dist/commands/{speech.js → answers/speech.js} +1 -1
  32. package/dist/commands/{study.d.ts → answers/study.d.ts} +3 -3
  33. package/dist/commands/{study.js → answers/study.js} +3 -3
  34. package/dist/commands/{game-summary.d.ts → answers/summary.d.ts} +2 -2
  35. package/dist/commands/{game-summary.js → answers/summary.js} +4 -4
  36. package/dist/commands/{view.d.ts → answers/view.d.ts} +1 -1
  37. package/dist/commands/{ai-move.d.ts → game/ai-move.d.ts} +2 -2
  38. package/dist/commands/{ai-move.js → game/ai-move.js} +6 -11
  39. package/dist/commands/{coach.d.ts → game/coach.d.ts} +1 -1
  40. package/dist/commands/{coach.js → game/coach.js} +7 -13
  41. package/dist/commands/{draw-offer.d.ts → game/draw-offer.d.ts} +1 -1
  42. package/dist/commands/{draw-offer.js → game/draw-offer.js} +4 -4
  43. package/dist/commands/{end-game.d.ts → game/end.d.ts} +2 -2
  44. package/dist/commands/{end-game.js → game/end.js} +5 -9
  45. package/dist/commands/{export-game.d.ts → game/export.d.ts} +1 -1
  46. package/dist/commands/{export-game.js → game/export.js} +4 -4
  47. package/dist/commands/game/index.d.ts +11 -0
  48. package/dist/commands/game/index.js +13 -0
  49. package/dist/commands/{levels.d.ts → game/levels.d.ts} +3 -3
  50. package/dist/commands/{levels.js → game/levels.js} +3 -3
  51. package/dist/commands/{look.d.ts → game/look.d.ts} +3 -4
  52. package/dist/commands/{look.js → game/look.js} +5 -10
  53. package/dist/commands/{new-game.d.ts → game/new.d.ts} +3 -4
  54. package/dist/commands/{new-game.js → game/new.js} +17 -13
  55. package/dist/commands/{page-link.d.ts → game/page-link.d.ts} +1 -1
  56. package/dist/commands/{player-move.d.ts → game/player-move.d.ts} +2 -2
  57. package/dist/commands/{player-move.js → game/player-move.js} +4 -8
  58. package/dist/commands/{ponder.d.ts → game/ponder.d.ts} +3 -3
  59. package/dist/commands/{ponder.js → game/ponder.js} +6 -9
  60. package/dist/commands/{position.d.ts → game/position.d.ts} +2 -2
  61. package/dist/commands/{position.js → game/position.js} +5 -7
  62. package/dist/commands/{status.d.ts → game/status.d.ts} +2 -4
  63. package/dist/commands/{status.js → game/status.js} +12 -17
  64. package/dist/commands/index.d.ts +6 -17
  65. package/dist/commands/index.js +11 -38
  66. package/dist/commands/{delete-game.d.ts → profiles/delete-game.d.ts} +1 -1
  67. package/dist/commands/{delete-game.js → profiles/delete-game.js} +3 -3
  68. package/dist/commands/profiles/index.d.ts +4 -0
  69. package/dist/commands/profiles/index.js +5 -0
  70. package/dist/commands/{past-games.d.ts → profiles/past-games.d.ts} +1 -1
  71. package/dist/commands/{past-games.js → profiles/past-games.js} +5 -5
  72. package/dist/commands/{profiles.d.ts → profiles/profiles.d.ts} +7 -7
  73. package/dist/commands/{profiles.js → profiles/profiles.js} +4 -4
  74. package/dist/commands/{ratings.d.ts → profiles/ratings.d.ts} +1 -1
  75. package/dist/commands/{ratings.js → profiles/ratings.js} +2 -2
  76. package/dist/commands/{context.d.ts → registry/context.d.ts} +2 -0
  77. package/dist/commands/{context.js → registry/context.js} +2 -2
  78. package/dist/commands/registry/index.d.ts +2 -0
  79. package/dist/commands/registry/index.js +3 -0
  80. package/dist/commands/setup/index.d.ts +2 -0
  81. package/dist/commands/setup/index.js +3 -0
  82. package/dist/commands/{init.d.ts → setup/init.d.ts} +1 -1
  83. package/dist/commands/{init.js → setup/init.js} +2 -3
  84. package/dist/commands/{instructions.d.ts → setup/instructions.d.ts} +2 -2
  85. package/dist/commands/{instructions.js → setup/instructions.js} +3 -3
  86. package/dist/commands/{ai-say.d.ts → talk/ai-say.d.ts} +2 -3
  87. package/dist/commands/{ai-say.js → talk/ai-say.js} +5 -9
  88. package/dist/commands/talk/index.d.ts +2 -0
  89. package/dist/commands/talk/index.js +3 -0
  90. package/dist/commands/{player-say.d.ts → talk/player-say.d.ts} +1 -1
  91. package/dist/commands/{player-say.js → talk/player-say.js} +4 -4
  92. package/dist/page/app/app.js +44 -20
  93. package/dist/page/app/served.js +7 -0
  94. package/dist/page/avatars/torretta.js +10 -3
  95. package/dist/page/dialogs/dialogs.css +10 -0
  96. package/dist/page/dialogs/profiles.js +3 -7
  97. package/dist/page/dialogs/settings.js +18 -4
  98. package/dist/page/game/column.js +1 -1
  99. package/dist/page/game/game.css +1 -1
  100. package/dist/page/guide/blocks.js +13 -2
  101. package/dist/page/guide/credits.js +6 -0
  102. package/dist/page/guide/drawings/analysis.js +13 -0
  103. package/dist/page/guide/drawings/drawings.css +329 -0
  104. package/dist/page/guide/drawings/drawings.js +15 -0
  105. package/dist/page/guide/drawings/game.js +15 -0
  106. package/dist/page/guide/drawings/helps.js +13 -0
  107. package/dist/page/guide/drawings/how-it-works.js +13 -0
  108. package/dist/page/guide/drawings/parts.js +103 -0
  109. package/dist/page/guide/drawings/players.js +19 -0
  110. package/dist/page/guide/drawings/talk.js +9 -0
  111. package/dist/page/guide/en.md +39 -25
  112. package/dist/page/guide/guide.css +123 -91
  113. package/dist/page/guide/guide.js +47 -25
  114. package/dist/page/guide/it.md +39 -25
  115. package/dist/page/guide/markdown.js +5 -0
  116. package/dist/page/helps/helps.css +127 -83
  117. package/dist/page/helps/helps.js +45 -26
  118. package/dist/page/helps/view.js +13 -3
  119. package/dist/page/{guide/foot.js → layout/footer.js} +7 -8
  120. package/dist/page/layout/header.js +5 -3
  121. package/dist/page/layout/layout.css +114 -36
  122. package/dist/page/layout/window.js +6 -26
  123. package/dist/page/main.js +1 -1
  124. package/dist/page/pieces/pieces.js +5 -2
  125. package/dist/page/style.css +2 -1
  126. package/dist/page/table/table.css +192 -0
  127. package/dist/page/table/update.js +82 -0
  128. package/dist/page/table/window.js +130 -0
  129. package/dist/page/talk/chat.css +19 -8
  130. package/dist/page/talk/talk.css +17 -67
  131. package/dist/page/talk/talk.js +7 -7
  132. package/dist/page/talk/voice.js +47 -9
  133. package/dist/page/text.js +118 -22
  134. package/dist/page/theme.css +2 -0
  135. package/dist/server/{bell.js → bell/bell.js} +3 -3
  136. package/dist/server/bell/index.d.ts +1 -0
  137. package/dist/server/bell/index.js +2 -0
  138. package/dist/server/{caller.d.ts → mcp/caller.d.ts} +1 -1
  139. package/dist/server/{caller.js → mcp/caller.js} +1 -1
  140. package/dist/server/mcp/index.d.ts +1 -0
  141. package/dist/server/mcp/index.js +2 -0
  142. package/dist/server/{mcp.d.ts → mcp/mcp.d.ts} +1 -1
  143. package/dist/server/{mcp.js → mcp/mcp.js} +9 -9
  144. package/dist/server/page/chat.d.ts +5 -0
  145. package/dist/server/page/chat.js +9 -1
  146. package/dist/server/page/game.js +16 -3
  147. package/dist/server/page/http.d.ts +1 -1
  148. package/dist/server/page/index.d.ts +3 -0
  149. package/dist/server/page/index.js +3 -0
  150. package/dist/server/page/page.d.ts +16 -3
  151. package/dist/server/page/page.js +18 -4
  152. package/dist/server/page/table.d.ts +40 -15
  153. package/dist/server/page/table.js +203 -19
  154. package/dist/server/page/update.d.ts +18 -0
  155. package/dist/server/page/update.js +40 -0
  156. package/dist/server/table/browser.d.ts +1 -1
  157. package/dist/server/table/browser.js +1 -1
  158. package/dist/server/table/hosts/chats.d.ts +1 -1
  159. package/dist/server/table/hosts/chats.js +7 -4
  160. package/dist/server/table/hosts/index.d.ts +1 -1
  161. package/dist/server/table/hosts/index.js +1 -1
  162. package/dist/server/table/hosts/play.d.ts +15 -4
  163. package/dist/server/table/hosts/play.js +75 -11
  164. package/dist/server/table/hosts/seat.d.ts +17 -5
  165. package/dist/server/table/hosts/seat.js +21 -15
  166. package/dist/server/table/index.d.ts +3 -2
  167. package/dist/server/table/index.js +3 -2
  168. package/dist/server/table/launch.d.ts +5 -2
  169. package/dist/server/table/launch.js +6 -3
  170. package/dist/server/table/link.d.ts +1 -1
  171. package/dist/server/table/link.js +4 -3
  172. package/dist/server/table/table.d.ts +4 -5
  173. package/dist/server/table/table.js +15 -12
  174. package/dist/server/table/update.d.ts +17 -0
  175. package/dist/server/table/update.js +77 -0
  176. package/dist/sites/index.d.ts +1 -1
  177. package/dist/sites/index.js +2 -2
  178. package/dist/version.d.ts +4 -0
  179. package/dist/version.js +12 -1
  180. package/package.json +1 -1
  181. package/dist/page/table/opener.css +0 -65
  182. package/dist/page/table/opener.js +0 -59
  183. /package/dist/commands/{speech.d.ts → answers/speech.d.ts} +0 -0
  184. /package/dist/commands/{view.js → answers/view.js} +0 -0
  185. /package/dist/commands/{page-link.js → game/page-link.js} +0 -0
  186. /package/dist/commands/{registry.d.ts → registry/registry.d.ts} +0 -0
  187. /package/dist/commands/{registry.js → registry/registry.js} +0 -0
  188. /package/dist/server/{bell.d.ts → bell/bell.d.ts} +0 -0
@@ -1,4 +1,4 @@
1
- /* The helps (D-78): their signs on the board, their card, the coach and the strategy's table */
1
+ /* The helps (D-78): their signs on the board, their card, the coach and the strategy's rows */
2
2
 
3
3
  /* A piece at risk: a corner folded, bottom left, the square's free corner; in ink, or in the player's red and green (D-91) */
4
4
  .help-notch {
@@ -362,17 +362,11 @@
362
362
  content: "▾";
363
363
  }
364
364
 
365
- /* A finger needs a taller line, and the whole of a strategy's name (Zoe) */
365
+ /* A finger needs a taller line (Zoe) */
366
366
  @media (pointer: coarse) {
367
367
  .help-rows .line-head {
368
368
  padding-block: 7px;
369
369
  }
370
-
371
- .strategy-table .term {
372
- display: block;
373
- width: 100%;
374
- padding: 6px 0;
375
- }
376
370
  }
377
371
 
378
372
  .help-rows .line-head:hover,
@@ -552,31 +546,81 @@
552
546
  border-color: var(--accent);
553
547
  }
554
548
 
555
- .strategy-table {
556
- width: 100%;
557
- border-collapse: collapse;
558
- font-size: 12.5px;
549
+ /* The strategy (Stefano): the phases, the bar of the squares controlled, the lines in groups apart only by the gap, each a name and its tile, then the notes */
550
+ .strategy {
551
+ display: flex;
552
+ flex-direction: column;
553
+ gap: 10px;
554
+ }
555
+
556
+ /* The three phases in a row, joined by a thin line; the current one in a pill */
557
+ .phases {
558
+ display: flex;
559
+ align-items: center;
560
+ margin: 0;
561
+ padding: 0;
562
+ list-style: none;
563
+ font-size: 12px;
559
564
  color: var(--soft);
560
565
  }
561
566
 
562
- .strategy-table th,
563
- .strategy-table td {
564
- padding: 2px 4px;
565
- text-align: left;
566
- font-weight: 400;
567
+ .phases li + li {
568
+ flex: 1 1 auto;
569
+ display: flex;
570
+ align-items: center;
571
+ gap: 6px;
572
+ margin-left: 6px;
573
+ }
574
+
575
+ .phases li + li::before {
576
+ content: "";
577
+ flex: 1;
578
+ height: 1px;
579
+ background: var(--line);
567
580
  }
568
581
 
569
- .strategy-table thead th {
582
+ .phases li span {
583
+ padding: 1px 9px;
584
+ white-space: nowrap;
585
+ }
586
+
587
+ .phases li[aria-current="step"] span {
588
+ border-radius: 999px;
589
+ background: color-mix(in srgb, var(--accent) 20%, transparent);
570
590
  color: var(--ink);
571
591
  font-weight: 700;
572
592
  }
573
593
 
574
- .strategy-table tbody tr + tr {
575
- border-top: 1px solid var(--line);
594
+ /* A group of lines, 3px apart (1px of gap and the heads' 1px of padding), with no line between them */
595
+ .strategy-rows {
596
+ display: flex;
597
+ flex-direction: column;
598
+ gap: 1px;
599
+ margin: 0;
600
+ padding: 0;
601
+ list-style: none;
602
+ font-size: 12.5px;
603
+ color: var(--soft);
604
+ }
605
+
606
+ .strategy-rows .row-head {
607
+ display: grid;
608
+ grid-template-columns: minmax(0, 1fr) auto;
609
+ align-items: center;
610
+ gap: 8px;
611
+ margin: 0 -4px;
612
+ padding: 1px 4px;
613
+ border-radius: 6px;
614
+ }
615
+
616
+ /* A soft ground under the pointer and on the open line, as the tactics' lines have */
617
+ .strategy-rows .row-head:hover,
618
+ .strategy-rows li.open .row-head {
619
+ background: color-mix(in srgb, var(--soft) 12%, transparent);
576
620
  }
577
621
 
578
622
  /* A term with its meaning under the pointer, and under its line at a click or a tap (Rosa). */
579
- .strategy-table .term {
623
+ .strategy-rows .term {
580
624
  padding: 0;
581
625
  border: 0;
582
626
  background: none;
@@ -588,60 +632,32 @@
588
632
  cursor: help;
589
633
  }
590
634
 
591
- .strategy-table tbody tr.meaning {
592
- border-top: 0;
635
+ /* A finger needs the whole of a strategy's name, after the base rule so that it wins */
636
+ @media (pointer: coarse) {
637
+ .strategy-rows .term {
638
+ display: block;
639
+ width: 100%;
640
+ padding: 6px 0;
641
+ }
593
642
  }
594
643
 
595
- .strategy-table tr.meaning td {
596
- padding-bottom: 4px;
644
+ .strategy-rows li.open .term {
645
+ color: var(--ink);
646
+ }
647
+
648
+ .strategy-rows .meaning {
649
+ padding: 2px 0 4px;
597
650
  font-size: 12px;
598
651
  font-style: italic;
599
652
  }
600
653
 
601
654
  /* Under the meaning, each side's squares or files (Stefano) */
602
- .strategy-table tr.meaning .listed {
655
+ .strategy-rows .meaning .listed {
603
656
  display: block;
604
657
  margin-top: 3px;
605
658
  font-style: normal;
606
659
  }
607
660
 
608
- /* The table's groups under their figure, in the accent; the sides' heads with their pawn */
609
- .strategy-table tr.group th {
610
- padding-top: 7px;
611
- color: var(--accent);
612
- font-size: 12px;
613
- font-weight: 700;
614
- }
615
-
616
- .strategy-table tbody tr.group + tr {
617
- border-top: 0;
618
- }
619
-
620
- /* The strategy (Stefano): the phase in a pill and the open files on top, then the bar of the squares controlled, the table in groups, the notes */
621
- .strategy {
622
- display: flex;
623
- flex-direction: column;
624
- gap: 8px;
625
- }
626
-
627
- .strategy-head {
628
- display: flex;
629
- flex-wrap: wrap;
630
- align-items: center;
631
- gap: 4px 10px;
632
- margin: 0;
633
- font-size: 12px;
634
- color: var(--soft);
635
- }
636
-
637
- .strategy .phase {
638
- padding: 1px 9px;
639
- border-radius: 999px;
640
- background: color-mix(in srgb, var(--accent) 20%, transparent);
641
- color: var(--ink);
642
- font-weight: 700;
643
- }
644
-
645
661
  /* Squares and files in chips, a count in bold, a yes as a green tick, nothing as a faint dash, no more as a faint cross */
646
662
  .strategy .chip {
647
663
  display: inline-block;
@@ -689,23 +705,12 @@
689
705
  }
690
706
 
691
707
  /* The squares each side controls: White's in white, both in grey, Black's in black, each as long as its count */
692
- .control {
693
- display: grid;
694
- gap: 3px;
695
- font-size: 11px;
696
- color: var(--soft);
697
- }
698
-
699
- .control-title {
700
- font-size: 12px;
701
- }
702
-
703
708
  .control-bar {
704
709
  display: flex;
705
710
  height: 18px;
706
- border: 1px solid var(--line);
707
711
  border-radius: 5px;
708
712
  overflow: hidden;
713
+ font-size: 11px;
709
714
  font-weight: 700;
710
715
  }
711
716
 
@@ -716,28 +721,67 @@
716
721
  min-width: 20px;
717
722
  }
718
723
 
719
- .control-bar .white {
724
+ /* The thread that keeps the black half in sight on the dark theme, as the lead bar's */
725
+ .control-bar,
726
+ .row-tile {
727
+ border: 1px solid rgb(138 144 153 / 0.6);
728
+ }
729
+
730
+ /* The bar's colours, which the lines' tiles share */
731
+ .control-bar .white,
732
+ .row-tile .white {
720
733
  background: #f4f6f8;
721
734
  color: var(--fixed-ink);
722
735
  }
723
736
 
724
- .control-bar .both {
737
+ .control-bar .both,
738
+ .row-tile .both {
725
739
  background: #8d98a7;
726
- color: #fff;
740
+ color: var(--fixed-ink);
727
741
  }
728
742
 
729
- .control-bar .black {
743
+ .control-bar .black,
744
+ .row-tile .black {
730
745
  background: var(--fixed-ink);
731
746
  color: #f4f6f8;
732
747
  }
733
748
 
734
- .control-legend {
749
+ /* A line's tile: a half for each side, the player's first; the open files in one half as wide as the two */
750
+ .row-tile {
735
751
  display: flex;
736
- justify-content: space-between;
752
+ height: 20px;
753
+ border-radius: 5px;
754
+ overflow: hidden;
755
+ font-size: 12px;
756
+ font-weight: 700;
757
+ font-variant-numeric: tabular-nums;
758
+ }
759
+
760
+ .row-tile > span {
761
+ display: grid;
762
+ place-items: center;
763
+ width: 42px;
764
+ }
765
+
766
+ .row-tile > .both {
767
+ width: 84px;
768
+ }
769
+
770
+ /* In a tile the marks take the half's colour, but the tick, green */
771
+ .strategy .row-tile .count {
772
+ color: inherit;
737
773
  }
738
774
 
739
- .strategy-table .figurine {
740
- margin-right: 3px;
775
+ .strategy .row-tile .chip {
776
+ margin: 0;
777
+ padding: 0;
778
+ background: none;
779
+ color: inherit;
780
+ }
781
+
782
+ .strategy .row-tile .no svg {
783
+ stroke: currentColor;
784
+ opacity: 0.6;
741
785
  }
742
786
 
743
787
  /* On a phone, or in a narrow window, the map of the squares and the coach's hint go under the board, out of the helps' card */
@@ -1,7 +1,4 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
2
- // The helps on the page (D-78, Zoe's sketch B): the board says where, the card says what, the facts the AI sees at the game's level and only those; and
3
- // the coach's words (D-88).
4
- import { Fragment } from "preact";
5
2
  import { useState } from "preact/hooks";
6
3
  import { Notch, Pushpin, Skewer } from "../board/marks.js";
7
4
  import { BadgeIcon, NoteIcon } from "../game/badges.js";
@@ -11,7 +8,7 @@ import { Figurine } from "../pieces/pieces.js";
11
8
  import { Legend, Marked } from "../talk/talk.js";
12
9
  import { groupOf, pinSquares, riskSquares, squaresOf, strategyOf } from "./view.js";
13
10
  /**
14
- * The helps' card, between the opening and the moves: the coach's words, then the tactics in lines, the strategy in a table and the control map, seen while
11
+ * The helps' card, between the opening and the moves: the coach's words, then the tactics in lines, the strategy in rows and the control map, seen while
15
12
  * 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
13
  * folds it to the title and opens it again (Stefano).
17
14
  */
@@ -146,8 +143,8 @@ function Squares({ squares }) {
146
143
  return _jsx("b", { class: "square-name", children: squares.join(", ") });
147
144
  }
148
145
  /**
149
- * The strategy (Stefano): the phase and the open files on top, the squares each side controls in a bar, the table in groups each under its figure, a count
150
- * or a tick in each cell, and the notes in lines as the tactics'. The player's side comes first, with no colour of good or bad (D-91).
146
+ * The strategy (Stefano): the phases with the current one in a pill, the bar of the squares each side controls, then the lines in groups, each a name and a
147
+ * tile of a half per side, and the notes as the tactics'. The player's side comes first, with no colour of good or bad (D-91).
151
148
  */
152
149
  function Strategy({ helps, onPoint, text }) {
153
150
  const words = text.helpsCard;
@@ -166,34 +163,56 @@ function Strategy({ helps, onPoint, text }) {
166
163
  ...(pawnEnding?.squares ?? []).map((square) => words.pawnSquare(square.pawn, square.caught)),
167
164
  pawnEnding?.opposition === undefined ? undefined : words.opposition(pawnEnding.opposition),
168
165
  ].filter((note) => note !== undefined);
169
- return (_jsxs("div", { class: "strategy", children: [_jsxs("p", { class: "strategy-head", children: [_jsx("span", { class: "phase", children: words.phases[profile.phase] }), _jsxs("span", { children: [words.openFiles, ": ", _jsx(Chips, { items: profile.openFiles })] })] }), _jsx(ControlBar, { controlled: profile.controlled, sides: helps.sides, sideName: sideName, words: words }), _jsxs("table", { class: "strategy-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", {}), helps.sides.map((side) => (_jsxs("th", { children: [_jsx(Figurine, { letter: "P", side: side }), sideName(side)] }, side)))] }) }), groups.map(({ group, letter }) => {
170
- const shown = lines.filter((line) => groupOf[line.row] === group);
171
- return shown.length === 0 ? null : (_jsxs("tbody", { children: [_jsx("tr", { class: "group", children: _jsxs("th", { colSpan: 3, children: [_jsx(Figurine, { letter: letter, side: "white" }), words.groups[group]] }) }), shown.map((line) => {
172
- const [name, meaning] = words.rows[line.row];
173
- const open = explained === line.row;
174
- const listed = helps.sides.filter((side) => typeof line[side] === "object");
175
- return (_jsxs(Fragment, { children: [_jsxs("tr", { class: open ? "open" : undefined, ...point(squaresIn(line)), children: [_jsx("th", { children: _jsx("button", { type: "button", class: "term", title: meaning, "aria-expanded": open, onClick: () => setExplained(open ? undefined : line.row), children: name }) }), helps.sides.map((side) => (_jsx("td", { children: _jsx(Cell, { cell: line[side], words: words }) }, side)))] }), open
176
- ? (_jsx("tr", { class: "meaning", children: _jsxs("td", { colSpan: 3, children: [meaning, listed.map((side) => (_jsxs("span", { class: "listed", children: [sideName(side), ": ", _jsx(Chips, { items: line[side] })] }, side)))] }) }))
177
- : null] }, line.row));
178
- })] }, group));
179
- })] }), notes.length === 0 ? null : (_jsx("ul", { class: "help-rows", children: notes.map((note) => (_jsxs("li", { class: "info", children: [_jsx(InfoIcon, {}), note] }, note))) }))] }));
180
- }
181
- // The groups of the strategy's table in their order, each with its figure.
182
- const groups = [{ group: "pawns", letter: "P" }, { group: "pieces", letter: "N" }, { group: "king", letter: "K" }];
166
+ return (_jsxs("div", { class: "strategy", children: [_jsx(Phases, { current: profile.phase, words: words }), _jsx(ControlBar, { controlled: profile.controlled, sides: helps.sides, you: helps.you, words: words }), groups.map((group) => {
167
+ const shown = lines.filter((line) => groupOf[line.row] === group);
168
+ return shown.length === 0 ? null : (_jsx("ul", { class: "strategy-rows", children: shown.map((line) => {
169
+ const [name, meaning] = words.rows[line.row];
170
+ const open = explained === line.row;
171
+ return (_jsxs("li", { class: open ? "open" : undefined, ...point(squaresIn(line)), children: [_jsxs("div", { class: "row-head", children: [_jsx("button", { type: "button", class: "term", title: meaning, "aria-expanded": open, onClick: () => setExplained(open ? undefined : line.row), children: name }), _jsx(Tile, { line: line, sides: helps.sides, sideName: sideName, words: words })] }), open ? _jsx(Meaning, { line: line, meaning: meaning, sides: helps.sides, sideName: sideName }) : null] }, line.row));
172
+ }) }, group));
173
+ }), notes.length === 0 ? null : (_jsx("ul", { class: "help-rows", children: notes.map((note) => (_jsxs("li", { class: "info", children: [_jsx(InfoIcon, {}), note] }, note))) }))] }));
174
+ }
175
+ // The strategy's groups in their order, apart only by a gap.
176
+ const groups = ["pawns", "pieces", "king"];
177
+ const phases = ["opening", "middlegame", "endgame"];
183
178
  // The fifty-move count shows only when the draw is near: ten moves each, or less.
184
179
  const fiftyMovesShown = 20;
185
180
  /** The squares a side's cells of a strategy's line name, to light on the board; files and islands light none. */
186
181
  function squaresIn(line) {
182
+ if (line.row === "openFiles") {
183
+ return [];
184
+ }
187
185
  return [line.white, line.black].flatMap((cell) => typeof cell === "object" ? cell.filter((item) => /^[a-h][1-8]$/.test(item)) : []);
188
186
  }
189
- /**
190
- * The squares only one side controls and those both do, in a bar as long as their counts, the player's side on the left; its title says it in words.
191
- */
192
- function ControlBar({ controlled, sides, sideName, words }) {
187
+ /** The game's three phases in a row, the current one in a pill. */
188
+ function Phases({ current, words }) {
189
+ return (_jsx("ol", { class: "phases", children: phases.map((phase) => (_jsx("li", { "aria-current": phase === current ? "step" : undefined, children: _jsx("span", { children: words.phases[phase] }) }, phase))) }));
190
+ }
191
+ /** The squares only one side controls and those both do, in a bar as long as their counts, the player's side on the left; each part's title says it in words. */
192
+ function ControlBar({ controlled, sides, you, words }) {
193
193
  const { white, black, contested } = controlled;
194
194
  const [left, right] = sides;
195
- const parts = [{ side: left, count: controlled[left] }, { side: "both", count: contested }, { side: right, count: controlled[right] }].filter(({ count }) => count > 0);
196
- return (_jsxs("div", { class: "control", title: words.controlled(white, black, contested), children: [_jsx("span", { class: "control-title", children: words.control }), _jsx("span", { class: "control-bar", role: "img", "aria-label": words.controlled(white, black, contested), children: parts.map(({ side, count }) => _jsx("span", { class: side, style: { flexGrow: count }, children: count }, side)) }), _jsxs("span", { class: "control-legend", "aria-hidden": "true", children: [_jsx("span", { children: sideName(left) }), _jsx("span", { children: words.both }), _jsx("span", { children: sideName(right) })] })] }));
195
+ const parts = [{ who: left, count: controlled[left] }, { who: "both", count: contested }, { who: right, count: controlled[right] }];
196
+ return (_jsx("span", { class: "control-bar", role: "img", "aria-label": words.controlled(white, black, contested), children: parts.filter(({ count }) => count > 0).map(({ who, count }) => _jsx("span", { class: who, style: { flexGrow: count }, title: words.controlledBy(who, count, who === you), children: count }, who)) }));
197
+ }
198
+ /** A line's tile: a half for each side, the player's first, in the bar's colours; the open files, both sides', in one half as wide as the two. */
199
+ function Tile({ line, sides, sideName, words }) {
200
+ if (line.row === "openFiles") {
201
+ return (_jsx("span", { class: "row-tile", children: _jsxs("span", { class: "both", title: words.bothSides, children: [_jsx("span", { class: "visually-hidden", children: `${words.bothSides} ` }), _jsx("b", { class: "count", children: line.both.length })] }) }));
202
+ }
203
+ return (_jsx("span", { class: "row-tile", children: sides.map((side) => (_jsxs("span", { class: side, title: sideName(side), children: [_jsx("span", { class: "visually-hidden", children: `${sideName(side)} ` }), _jsx(Cell, { cell: line[side], words: words })] }, side))) }));
204
+ }
205
+ /** A line opened: its meaning, then each listed side's squares or files in chips; the open files with no side's name. */
206
+ function Meaning({ line, meaning, sides, sideName }) {
207
+ if (line.row === "openFiles") {
208
+ return (_jsxs("div", { class: "meaning", children: [meaning, _jsx("span", { class: "listed", children: _jsx(Chips, { items: line.both }) })] }));
209
+ }
210
+ return (_jsxs("div", { class: "meaning", children: [meaning, sides.map((side) => {
211
+ const cell = line[side];
212
+ return typeof cell === "object"
213
+ ? (_jsxs("span", { class: "listed", children: [sideName(side), ": ", _jsx(Chips, { items: cell })] }, side))
214
+ : null;
215
+ })] }));
197
216
  }
198
217
  /** Squares, files or islands in chips; none as a faint dash. */
199
218
  function Chips({ items }) {
@@ -62,13 +62,14 @@ export function riskSquares(risk) {
62
62
  export function pinSquares(pin) {
63
63
  return [pin.piece.square, pin.by.square, pin.to.square];
64
64
  }
65
- /** Which group each line of the strategy's table stands in. */
65
+ /** Which group each line of the strategy stands in. */
66
66
  export const groupOf = {
67
67
  isolated: "pawns",
68
68
  doubled: "pawns",
69
69
  backward: "pawns",
70
70
  passed: "pawns",
71
71
  islands: "pawns",
72
+ openFiles: "pieces",
72
73
  halfOpen: "pieces",
73
74
  heavyOnFiles: "pieces",
74
75
  onSeventh: "pieces",
@@ -79,11 +80,20 @@ export const groupOf = {
79
80
  castled: "king",
80
81
  kingAttacks: "king",
81
82
  };
82
- /** The strategy's table: only the lines where a side has something; the pawn islands only when a side has more than one. */
83
+ /**
84
+ * The strategy's lines: only those where a side has something, the pawn islands only when a side has more than one; the open files, when there are any,
85
+ * first among the pieces' lines.
86
+ */
83
87
  export function strategyOf(profile) {
84
88
  const lines = Object.entries(cells).map(([row, cell]) => ({ row: row, white: cell(profile.white), black: cell(profile.black) }));
85
89
  const split = profile.white.pawns.islands.length > 1 || profile.black.pawns.islands.length > 1;
86
- return lines.filter((line) => line.row === "islands" ? split : something(line.white) || something(line.black));
90
+ const shown = lines.filter((line) => line.row === "islands" ? split : something(line.white) || something(line.black));
91
+ if (profile.openFiles.length === 0) {
92
+ return shown;
93
+ }
94
+ const pawnLines = shown.filter((line) => groupOf[line.row] === "pawns");
95
+ const otherLines = shown.filter((line) => groupOf[line.row] !== "pawns");
96
+ return [...pawnLines, { row: "openFiles", both: profile.openFiles }, ...otherLines];
87
97
  }
88
98
  const cells = {
89
99
  isolated: (side) => side.pawns.isolated,
@@ -1,17 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
2
- import { Inline } from "./markdown.js";
3
- /** Where Chesspal lives, and how to support it. */
4
- export const footLinks = {
2
+ /** The foot under the board, with the version of Chesspal that serves the page. */
3
+ export function PageFoot({ version, text }) {
4
+ const words = text.foot;
5
+ return (_jsxs("footer", { class: "page-foot", children: [_jsxs("span", { class: "foot-own", children: ["© 2026 ", _jsx("a", { href: footLinks.author, target: "_blank", rel: "noreferrer", children: "Stefano Caronia" }), version === undefined ? null : _jsx("span", { class: "foot-version", children: ` · Chesspal ${version}` })] }), _jsxs("span", { class: "foot-links", children: [_jsx("a", { href: footLinks.github, target: "_blank", rel: "noreferrer", title: words.onGitHub, "aria-label": words.onGitHub, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: githubPath }) }) }), _jsx("a", { href: footLinks.npm, target: "_blank", rel: "noreferrer", title: words.onNpm, "aria-label": words.onNpm, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: npmPath }) }) }), _jsxs("a", { class: "foot-support", href: footLinks.support, target: "_blank", rel: "noreferrer", children: [_jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: heartPath }) }), words.support] })] })] }));
6
+ }
7
+ // Where Chesspal lives, and how to support it.
8
+ const footLinks = {
5
9
  author: "https://stefanocaronia.it",
6
10
  github: "https://github.com/stefanocaronia/chesspal",
7
11
  npm: "https://www.npmjs.com/package/chesspal",
8
12
  support: "https://www.paypal.com/paypalme/stefanocaronia",
9
13
  };
10
- /** The foot of the guide. */
11
- export function GuideFoot({ text }) {
12
- const words = text.guide;
13
- return (_jsxs("footer", { class: "guide-foot", children: [_jsxs("p", { class: "foot-own", children: [_jsxs("span", { children: ["© 2026 ", _jsx("a", { href: footLinks.author, target: "_blank", rel: "noreferrer", children: "Stefano Caronia" }), " · Chesspal"] }), _jsx("a", { href: footLinks.github, target: "_blank", rel: "noreferrer", title: "GitHub", "aria-label": words.onGitHub, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: githubPath }) }) }), _jsx("a", { href: footLinks.npm, target: "_blank", rel: "noreferrer", title: "npm", "aria-label": words.onNpm, children: _jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: npmPath }) }) }), _jsxs("a", { class: "foot-support", href: footLinks.support, target: "_blank", rel: "noreferrer", children: [_jsx("svg", { class: "icon", viewBox: "0 0 24 24", "aria-hidden": "true", children: _jsx("path", { d: heartPath }) }), words.support] })] }), _jsxs("details", { class: "credits", children: [_jsx("summary", { children: words.credits }), _jsx("ul", { children: words.creditLines.map((line) => (_jsx("li", { children: _jsx(Inline, { text: line, go: () => undefined }) }, line))) })] })] }));
14
- }
15
14
  // The icons of rpg-engine's foot.
16
15
  const githubPath = "M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65S8.93 17.38 9 18v4 M9 18c-4.51 2-5-2-7-2";
17
16
  const npmPath = "M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73Z M12 22V12 M3.29 7 12 12l8.71-5 M7.5 4.27l9 5.15";
@@ -1,13 +1,15 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "preact/jsx-runtime";
2
2
  import { PlayerPicture } from "../avatars/avatars.js";
3
3
  import { Torretta } from "../avatars/torretta.js";
4
+ import { UpdateMark } from "../table/update.js";
4
5
  /**
5
6
  * 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
7
  * 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, the gear of the settings (D-56) and the book of the guide.
8
+ * turned, on the table the mark of a newer Chesspal (D-108), the switches of the sounds and the theme, the gear of the settings (D-56) and the book of
9
+ * the guide.
8
10
  */
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
+ export function Header({ text, verdict, kind, who, actions, onNewGame, standOut = false, onStudy, flipped, onFlip, sound, onSound, onTheme, onSettings, onGuide, profile, onProfile, update, onUpdate }) {
12
+ 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 : (_jsx("button", { class: "icon-button", type: "button", title: text.study.title, "aria-label": text.study.title, "aria-haspopup": "dialog", 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("button", { class: "icon-button flip-button", type: "button", title: text.flip, "aria-label": 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" }) }) }), update === undefined || onUpdate === undefined ? null : _jsx(UpdateMark, { version: update.version, installing: update.installing, onOpen: onUpdate, text: text }), _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
13
  }
12
14
  /** The answer to the AI's draw offer while it stands, else the player's own offer; then the resignation. */
13
15
  function Actions({ actions, text }) {