@bendyline/gilde 0.1.88 → 0.1.90

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 (143) hide show
  1. package/data/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/manifest.json +24 -0
  2. package/data/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/versions/2026.10.2/manifest.json +182 -0
  3. package/data/knowledge-catalogs/ca/caselaw-utah-part-0001/manifest.json +24 -0
  4. package/data/knowledge-catalogs/ca/caselaw-utah-part-0001/versions/2026.10.1/manifest.json +558 -0
  5. package/data/knowledge-catalogs/ca/caselaw-utah-part-0002/manifest.json +24 -0
  6. package/data/knowledge-catalogs/ca/caselaw-utah-part-0002/versions/2026.10.1/manifest.json +258 -0
  7. package/data/knowledge-catalogs/index.json +1 -1
  8. package/data/knowledge-catalogs/raw-index.json +1 -1
  9. package/data/knowledge-catalogs/re/regional-american-south/art.json +12 -0
  10. package/data/knowledge-catalogs/re/regional-american-south/logo.webp +0 -0
  11. package/data/knowledge-catalogs/re/regional-american-south/manifest.json +48 -0
  12. package/data/knowledge-catalogs/re/regional-american-south/versions/2026.10.3/manifest.json +133 -0
  13. package/data/knowledge-catalogs/re/regional-andes-amazon/art.json +12 -0
  14. package/data/knowledge-catalogs/re/regional-andes-amazon/logo.webp +0 -0
  15. package/data/knowledge-catalogs/re/regional-andes-amazon/manifest.json +48 -0
  16. package/data/knowledge-catalogs/re/regional-andes-amazon/versions/2026.10.3/manifest.json +133 -0
  17. package/data/knowledge-catalogs/re/regional-australia-nz/art.json +12 -0
  18. package/data/knowledge-catalogs/re/regional-australia-nz/logo.webp +0 -0
  19. package/data/knowledge-catalogs/re/regional-australia-nz/manifest.json +48 -0
  20. package/data/knowledge-catalogs/re/regional-australia-nz/versions/2026.10.3/manifest.json +133 -0
  21. package/data/knowledge-catalogs/re/regional-brazil/art.json +12 -0
  22. package/data/knowledge-catalogs/re/regional-brazil/logo.webp +0 -0
  23. package/data/knowledge-catalogs/re/regional-brazil/manifest.json +48 -0
  24. package/data/knowledge-catalogs/re/regional-brazil/versions/2026.10.3/manifest.json +133 -0
  25. package/data/knowledge-catalogs/re/regional-britain-ireland/art.json +12 -0
  26. package/data/knowledge-catalogs/re/regional-britain-ireland/logo.webp +0 -0
  27. package/data/knowledge-catalogs/re/regional-britain-ireland/manifest.json +48 -0
  28. package/data/knowledge-catalogs/re/regional-britain-ireland/versions/2026.10.3/manifest.json +133 -0
  29. package/data/knowledge-catalogs/re/regional-california-southwest/art.json +12 -0
  30. package/data/knowledge-catalogs/re/regional-california-southwest/logo.webp +0 -0
  31. package/data/knowledge-catalogs/re/regional-california-southwest/manifest.json +48 -0
  32. package/data/knowledge-catalogs/re/regional-california-southwest/versions/2026.10.3/manifest.json +133 -0
  33. package/data/knowledge-catalogs/re/regional-canada-alaska/art.json +12 -0
  34. package/data/knowledge-catalogs/re/regional-canada-alaska/logo.webp +0 -0
  35. package/data/knowledge-catalogs/re/regional-canada-alaska/manifest.json +48 -0
  36. package/data/knowledge-catalogs/re/regional-canada-alaska/versions/2026.10.3/manifest.json +133 -0
  37. package/data/knowledge-catalogs/re/regional-central-eastern-europe/art.json +12 -0
  38. package/data/knowledge-catalogs/re/regional-central-eastern-europe/logo.webp +0 -0
  39. package/data/knowledge-catalogs/re/regional-central-eastern-europe/manifest.json +48 -0
  40. package/data/knowledge-catalogs/re/regional-central-eastern-europe/versions/2026.10.3/manifest.json +133 -0
  41. package/data/knowledge-catalogs/re/regional-east-asia/art.json +12 -0
  42. package/data/knowledge-catalogs/re/regional-east-asia/logo.webp +0 -0
  43. package/data/knowledge-catalogs/re/regional-east-asia/manifest.json +48 -0
  44. package/data/knowledge-catalogs/re/regional-east-asia/versions/2026.10.3/manifest.json +133 -0
  45. package/data/knowledge-catalogs/re/regional-east-southern-africa/art.json +12 -0
  46. package/data/knowledge-catalogs/re/regional-east-southern-africa/logo.webp +0 -0
  47. package/data/knowledge-catalogs/re/regional-east-southern-africa/manifest.json +48 -0
  48. package/data/knowledge-catalogs/re/regional-east-southern-africa/versions/2026.10.3/manifest.json +133 -0
  49. package/data/knowledge-catalogs/re/regional-mediterranean/art.json +12 -0
  50. package/data/knowledge-catalogs/re/regional-mediterranean/logo.webp +0 -0
  51. package/data/knowledge-catalogs/re/regional-mediterranean/manifest.json +48 -0
  52. package/data/knowledge-catalogs/re/regional-mediterranean/versions/2026.10.3/manifest.json +133 -0
  53. package/data/knowledge-catalogs/re/regional-mexico-central-america/art.json +12 -0
  54. package/data/knowledge-catalogs/re/regional-mexico-central-america/logo.webp +0 -0
  55. package/data/knowledge-catalogs/re/regional-mexico-central-america/manifest.json +48 -0
  56. package/data/knowledge-catalogs/re/regional-mexico-central-america/versions/2026.10.3/manifest.json +133 -0
  57. package/data/knowledge-catalogs/re/regional-middle-east/art.json +12 -0
  58. package/data/knowledge-catalogs/re/regional-middle-east/logo.webp +0 -0
  59. package/data/knowledge-catalogs/re/regional-middle-east/manifest.json +48 -0
  60. package/data/knowledge-catalogs/re/regional-middle-east/versions/2026.10.3/manifest.json +133 -0
  61. package/data/knowledge-catalogs/re/regional-midwest/art.json +12 -0
  62. package/data/knowledge-catalogs/re/regional-midwest/logo.webp +0 -0
  63. package/data/knowledge-catalogs/re/regional-midwest/manifest.json +48 -0
  64. package/data/knowledge-catalogs/re/regional-midwest/versions/2026.10.3/manifest.json +133 -0
  65. package/data/knowledge-catalogs/re/regional-north-africa/art.json +12 -0
  66. package/data/knowledge-catalogs/re/regional-north-africa/logo.webp +0 -0
  67. package/data/knowledge-catalogs/re/regional-north-africa/manifest.json +48 -0
  68. package/data/knowledge-catalogs/re/regional-north-africa/versions/2026.10.3/manifest.json +133 -0
  69. package/data/knowledge-catalogs/re/regional-northeast/art.json +12 -0
  70. package/data/knowledge-catalogs/re/regional-northeast/logo.webp +0 -0
  71. package/data/knowledge-catalogs/re/regional-northeast/manifest.json +48 -0
  72. package/data/knowledge-catalogs/re/regional-northeast/versions/2026.10.3/manifest.json +133 -0
  73. package/data/knowledge-catalogs/re/regional-northern-eurasia/art.json +12 -0
  74. package/data/knowledge-catalogs/re/regional-northern-eurasia/logo.webp +0 -0
  75. package/data/knowledge-catalogs/re/regional-northern-eurasia/manifest.json +48 -0
  76. package/data/knowledge-catalogs/re/regional-northern-eurasia/versions/2026.10.3/manifest.json +133 -0
  77. package/data/knowledge-catalogs/re/regional-northern-europe/art.json +12 -0
  78. package/data/knowledge-catalogs/re/regional-northern-europe/logo.webp +0 -0
  79. package/data/knowledge-catalogs/re/regional-northern-europe/manifest.json +48 -0
  80. package/data/knowledge-catalogs/re/regional-northern-europe/versions/2026.10.3/manifest.json +133 -0
  81. package/data/knowledge-catalogs/re/regional-pacific-islands/art.json +12 -0
  82. package/data/knowledge-catalogs/re/regional-pacific-islands/logo.webp +0 -0
  83. package/data/knowledge-catalogs/re/regional-pacific-islands/manifest.json +48 -0
  84. package/data/knowledge-catalogs/re/regional-pacific-islands/versions/2026.10.3/manifest.json +133 -0
  85. package/data/knowledge-catalogs/re/regional-pacific-northwest/art.json +12 -0
  86. package/data/knowledge-catalogs/re/regional-pacific-northwest/logo.webp +0 -0
  87. package/data/knowledge-catalogs/re/regional-pacific-northwest/manifest.json +48 -0
  88. package/data/knowledge-catalogs/re/regional-pacific-northwest/versions/2026.10.3/manifest.json +133 -0
  89. package/data/knowledge-catalogs/re/regional-rockies-plains/art.json +12 -0
  90. package/data/knowledge-catalogs/re/regional-rockies-plains/logo.webp +0 -0
  91. package/data/knowledge-catalogs/re/regional-rockies-plains/manifest.json +48 -0
  92. package/data/knowledge-catalogs/re/regional-rockies-plains/versions/2026.10.3/manifest.json +133 -0
  93. package/data/knowledge-catalogs/re/regional-south-asia/art.json +12 -0
  94. package/data/knowledge-catalogs/re/regional-south-asia/logo.webp +0 -0
  95. package/data/knowledge-catalogs/re/regional-south-asia/manifest.json +48 -0
  96. package/data/knowledge-catalogs/re/regional-south-asia/versions/2026.10.3/manifest.json +133 -0
  97. package/data/knowledge-catalogs/re/regional-southeast-asia/art.json +12 -0
  98. package/data/knowledge-catalogs/re/regional-southeast-asia/logo.webp +0 -0
  99. package/data/knowledge-catalogs/re/regional-southeast-asia/manifest.json +48 -0
  100. package/data/knowledge-catalogs/re/regional-southeast-asia/versions/2026.10.3/manifest.json +133 -0
  101. package/data/knowledge-catalogs/re/regional-southern-cone/art.json +12 -0
  102. package/data/knowledge-catalogs/re/regional-southern-cone/logo.webp +0 -0
  103. package/data/knowledge-catalogs/re/regional-southern-cone/manifest.json +48 -0
  104. package/data/knowledge-catalogs/re/regional-southern-cone/versions/2026.10.3/manifest.json +133 -0
  105. package/data/knowledge-catalogs/re/regional-west-central-africa/art.json +12 -0
  106. package/data/knowledge-catalogs/re/regional-west-central-africa/logo.webp +0 -0
  107. package/data/knowledge-catalogs/re/regional-west-central-africa/manifest.json +48 -0
  108. package/data/knowledge-catalogs/re/regional-west-central-africa/versions/2026.10.3/manifest.json +133 -0
  109. package/data/knowledge-catalogs/re/regional-western-europe/art.json +12 -0
  110. package/data/knowledge-catalogs/re/regional-western-europe/logo.webp +0 -0
  111. package/data/knowledge-catalogs/re/regional-western-europe/manifest.json +48 -0
  112. package/data/knowledge-catalogs/re/regional-western-europe/versions/2026.10.3/manifest.json +133 -0
  113. package/data/project-types/ch/checkers/versions/1.2.2/about.md +9 -0
  114. package/data/project-types/ch/checkers/versions/1.2.2/game.json +77 -0
  115. package/data/project-types/ch/checkers/versions/1.2.2/manifest.json +181 -0
  116. package/data/project-types/ch/checkers/versions/1.2.2/mission.md +9 -0
  117. package/data/project-types/ch/checkers/versions/1.2.2/pages/board/index.html +1158 -0
  118. package/data/project-types/ch/chess/versions/1.0.5/about.md +9 -0
  119. package/data/project-types/ch/chess/versions/1.0.5/game.json +127 -0
  120. package/data/project-types/ch/chess/versions/1.0.5/manifest.json +195 -0
  121. package/data/project-types/ch/chess/versions/1.0.5/mission.md +8 -0
  122. package/data/project-types/ch/chess/versions/1.0.5/pages/board/index.html +851 -0
  123. package/data/project-types/fi/fitness-coach/versions/1.0.3/about.md +7 -0
  124. package/data/project-types/fi/fitness-coach/versions/1.0.3/manifest.json +127 -0
  125. package/data/project-types/fi/fitness-coach/versions/1.0.3/mission.md +7 -0
  126. package/data/project-types/fi/fitness-coach/versions/1.0.3/pages/dashboard/index.html +273 -0
  127. package/data/project-types/fi/fitness-coach/versions/1.0.3/training.json +6 -0
  128. package/data/project-types/go/go/versions/1.0.3/about.md +9 -0
  129. package/data/project-types/go/go/versions/1.0.3/game.json +103 -0
  130. package/data/project-types/go/go/versions/1.0.3/manifest.json +181 -0
  131. package/data/project-types/go/go/versions/1.0.3/mission.md +8 -0
  132. package/data/project-types/go/go/versions/1.0.3/pages/board/index.html +855 -0
  133. package/data/project-types/index.json +1 -1
  134. package/data/project-types/la/language-trainer/versions/1.0.3/about.md +6 -0
  135. package/data/project-types/la/language-trainer/versions/1.0.3/manifest.json +81 -0
  136. package/data/project-types/la/language-trainer/versions/1.0.3/mission.md +4 -0
  137. package/data/project-types/la/language-trainer/versions/1.0.3/pages/dashboard/index.html +268 -0
  138. package/data/project-types/la/language-trainer/versions/1.0.3/progress.json +7 -0
  139. package/data/project-types/raw-index.json +1 -1
  140. package/package.json +1 -1
  141. package/schemas/craftbook-doc.schema.json +59 -0
  142. package/schemas/craftbook-template-version.schema.json +59 -0
  143. package/schemas/knowledge-catalog-version.schema.json +1 -0
@@ -0,0 +1,851 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="gezel-demo" content="standalone" />
7
+ <title>Chess</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: light dark;
11
+ --bg: #faf7f2;
12
+ --card: #fff;
13
+ --ink: #2b2620;
14
+ --muted: #7a7166;
15
+ --accent: #9a5928;
16
+ --track: #e8dfd2;
17
+ --sq-light: #efe4d1;
18
+ --sq-dark: #9a7455;
19
+ --sq-hint: #557a5b;
20
+ --frame-a: #8d694d;
21
+ --frame-b: #4d3424;
22
+ --frame-edge: #342116;
23
+ --white-piece-face: #fff8e8;
24
+ --white-piece-side: #a8987e;
25
+ --white-piece-edge: #594b3c;
26
+ --white-piece-highlight: rgba(255, 255, 255, 0.9);
27
+ --black-piece-face: #302a24;
28
+ --black-piece-side: #100d0a;
29
+ --black-piece-edge: #090705;
30
+ --black-piece-highlight: rgba(255, 255, 255, 0.2);
31
+ --last-move: rgba(241, 184, 84, 0.42);
32
+ }
33
+ :root[data-theme='dark'] {
34
+ --bg: #1c1a17;
35
+ --card: #262320;
36
+ --ink: #efe9e0;
37
+ --muted: #a59a8c;
38
+ --accent: #e0894a;
39
+ --track: #3a352f;
40
+ --sq-light: #b8a58b;
41
+ --sq-dark: #674a35;
42
+ --sq-hint: #6f9b70;
43
+ --frame-a: #74543d;
44
+ --frame-b: #34241a;
45
+ --frame-edge: #1b120d;
46
+ --white-piece-face: #fff5dc;
47
+ --white-piece-side: #9d8b70;
48
+ --white-piece-edge: #4b3d30;
49
+ --white-piece-highlight: rgba(255, 255, 255, 0.88);
50
+ --black-piece-face: #29221c;
51
+ --black-piece-side: #0b0907;
52
+ --black-piece-edge: #050403;
53
+ --black-piece-highlight: rgba(255, 255, 255, 0.18);
54
+ --last-move: rgba(236, 159, 85, 0.38);
55
+ }
56
+ @media (prefers-color-scheme: dark) {
57
+ :root:not([data-theme='light']) {
58
+ --bg: #1c1a17;
59
+ --card: #262320;
60
+ --ink: #efe9e0;
61
+ --muted: #a59a8c;
62
+ --accent: #e0894a;
63
+ --track: #3a352f;
64
+ --sq-light: #b8a58b;
65
+ --sq-dark: #674a35;
66
+ --sq-hint: #6f9b70;
67
+ --frame-a: #74543d;
68
+ --frame-b: #34241a;
69
+ --frame-edge: #1b120d;
70
+ --white-piece-face: #fff5dc;
71
+ --white-piece-side: #9d8b70;
72
+ --white-piece-edge: #4b3d30;
73
+ --white-piece-highlight: rgba(255, 255, 255, 0.88);
74
+ --black-piece-face: #29221c;
75
+ --black-piece-side: #0b0907;
76
+ --black-piece-edge: #050403;
77
+ --black-piece-highlight: rgba(255, 255, 255, 0.18);
78
+ --last-move: rgba(236, 159, 85, 0.38);
79
+ }
80
+ }
81
+ * { box-sizing: border-box; }
82
+ body {
83
+ margin: 0;
84
+ padding: 1.25rem;
85
+ background: var(--bg);
86
+ color: var(--ink);
87
+ font: 15px/1.5 system-ui, -apple-system, 'Segoe UI', sans-serif;
88
+ }
89
+ main { max-width: 760px; margin: 0 auto; }
90
+ header {
91
+ display: flex;
92
+ align-items: baseline;
93
+ justify-content: space-between;
94
+ gap: 0.75rem;
95
+ flex-wrap: wrap;
96
+ }
97
+ h1 {
98
+ margin: 0;
99
+ font-family: Georgia, 'PT Serif', 'Times New Roman', serif;
100
+ font-size: 1.35rem;
101
+ }
102
+ button { font: inherit; }
103
+ .newgame,
104
+ .passive-button {
105
+ min-height: 44px;
106
+ border: 1px solid var(--track);
107
+ border-radius: 6px;
108
+ padding: 0.32rem 0.7rem;
109
+ background: var(--card);
110
+ color: var(--ink);
111
+ cursor: pointer;
112
+ }
113
+ .newgame:hover,
114
+ .passive-button:hover { border-color: var(--accent); }
115
+ .status { min-height: 1.5em; margin: 0.15rem 0 0.9rem; color: var(--muted); }
116
+ .status.thinking { color: var(--accent); }
117
+ .banner {
118
+ display: none;
119
+ margin-bottom: 0.75rem;
120
+ padding: 0.45rem 0.7rem;
121
+ border: 1px solid var(--accent);
122
+ border-radius: 6px;
123
+ font-size: 0.85rem;
124
+ }
125
+ .banner.show { display: block; }
126
+ .note {
127
+ margin: 0 0 0.85rem;
128
+ padding: 0.5rem 0.65rem;
129
+ border: 1px solid var(--track);
130
+ border-radius: 6px;
131
+ background: var(--card);
132
+ color: var(--muted);
133
+ font-size: 0.8rem;
134
+ }
135
+ .table { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 0.9rem; }
136
+ .boardwrap {
137
+ padding: 0.85rem;
138
+ border: 1px solid var(--frame-edge);
139
+ border-radius: 14px;
140
+ background: linear-gradient(145deg, var(--frame-a), var(--frame-b));
141
+ box-shadow:
142
+ inset 0 1px 0 rgba(255, 255, 255, 0.2),
143
+ inset 0 -4px 8px rgba(0, 0, 0, 0.25),
144
+ 0 10px 24px rgba(31, 21, 15, 0.22);
145
+ }
146
+ .board {
147
+ display: grid;
148
+ grid-template-columns: repeat(8, minmax(0, 1fr));
149
+ width: 100%;
150
+ aspect-ratio: 1;
151
+ overflow: hidden;
152
+ border: 1px solid var(--frame-edge);
153
+ border-radius: 8px;
154
+ box-shadow: 0 5px 12px rgba(0, 0, 0, 0.3);
155
+ user-select: none;
156
+ }
157
+ .cell {
158
+ position: relative;
159
+ display: grid;
160
+ min-width: 0;
161
+ min-height: 0;
162
+ aspect-ratio: 1;
163
+ place-items: center;
164
+ padding: 0;
165
+ border: 0;
166
+ color: inherit;
167
+ }
168
+ .cell.light { background: var(--sq-light); }
169
+ .cell.dark { background: var(--sq-dark); }
170
+ .cell.selectable { cursor: pointer; }
171
+ .cell.selected {
172
+ outline: 3px solid var(--accent);
173
+ outline-offset: -3px;
174
+ z-index: 2;
175
+ }
176
+ .cell.hint { cursor: pointer; }
177
+ .cell.hint::after {
178
+ position: absolute;
179
+ z-index: 2;
180
+ width: 24%;
181
+ height: 24%;
182
+ border-radius: 50%;
183
+ background: var(--sq-hint);
184
+ box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.18);
185
+ content: '';
186
+ }
187
+ .cell.hint.capture::after {
188
+ width: 72%;
189
+ height: 72%;
190
+ border: 5px solid var(--sq-hint);
191
+ background: transparent;
192
+ box-shadow: none;
193
+ }
194
+ .cell.last-from { box-shadow: inset 0 0 0 3px var(--last-move); }
195
+ .cell.last-to { box-shadow: inset 0 0 0 999px var(--last-move); }
196
+ .piece {
197
+ position: relative;
198
+ z-index: 1;
199
+ display: grid;
200
+ width: 100%;
201
+ height: 100%;
202
+ place-items: center;
203
+ font-family: Georgia, 'Times New Roman', serif;
204
+ font-size: clamp(1.5rem, 7vw, 3.25rem);
205
+ line-height: 1;
206
+ isolation: isolate;
207
+ transform: translate(-2px, -3px);
208
+ transition: transform 140ms ease-out;
209
+ }
210
+ .piece.white {
211
+ --piece-face: var(--white-piece-face);
212
+ --piece-side: var(--white-piece-side);
213
+ --piece-edge: var(--white-piece-edge);
214
+ --piece-highlight: var(--white-piece-highlight);
215
+ }
216
+ .piece.black {
217
+ --piece-face: var(--black-piece-face);
218
+ --piece-side: var(--black-piece-side);
219
+ --piece-edge: var(--black-piece-edge);
220
+ --piece-highlight: var(--black-piece-highlight);
221
+ }
222
+ .piece::before,
223
+ .piece::after {
224
+ position: absolute;
225
+ inset: 0;
226
+ display: grid;
227
+ place-items: center;
228
+ content: attr(data-glyph);
229
+ }
230
+ .piece::before {
231
+ z-index: -1;
232
+ color: var(--piece-side);
233
+ -webkit-text-stroke: 1px var(--piece-edge);
234
+ text-shadow:
235
+ 1px 1px 0 var(--piece-edge),
236
+ 2px 2px 0 var(--piece-edge),
237
+ 3px 3px 0 var(--piece-edge),
238
+ 4px 5px 4px rgba(0, 0, 0, 0.34);
239
+ transform: translate(3px, 4px);
240
+ }
241
+ .piece::after {
242
+ color: var(--piece-face);
243
+ -webkit-text-stroke: 1px var(--piece-edge);
244
+ text-shadow:
245
+ -1px -1px 0 var(--piece-highlight),
246
+ 1px 1px 0 color-mix(in srgb, var(--piece-side) 68%, transparent);
247
+ }
248
+ .cell.selectable:hover .piece,
249
+ .cell.selected .piece { transform: translate(-3px, -5px) scale(1.04); }
250
+ .coord {
251
+ position: absolute;
252
+ z-index: 3;
253
+ color: rgba(43, 38, 32, 0.55);
254
+ font-size: clamp(0.45rem, 1.3vw, 0.65rem);
255
+ font-weight: 700;
256
+ line-height: 1;
257
+ pointer-events: none;
258
+ }
259
+ .coord.rank { top: 5%; left: 6%; }
260
+ .coord.file { right: 6%; bottom: 5%; }
261
+ .dark .coord { color: rgba(255, 250, 242, 0.58); }
262
+ .aside { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; }
263
+ .panel {
264
+ padding: 0.7rem 0.85rem;
265
+ border: 1px solid var(--track);
266
+ border-radius: 10px;
267
+ background: var(--card);
268
+ font-size: 0.85rem;
269
+ }
270
+ .panel h2 {
271
+ margin: 0 0 0.35rem;
272
+ color: var(--muted);
273
+ font-size: 0.72rem;
274
+ letter-spacing: 0.05em;
275
+ text-transform: uppercase;
276
+ }
277
+ .move-thought { font-style: italic; }
278
+ .move-thought .who {
279
+ display: block;
280
+ margin-bottom: 0.15rem;
281
+ color: var(--muted);
282
+ font-size: 0.78rem;
283
+ font-style: normal;
284
+ }
285
+ .log { max-height: 230px; margin: 0; padding-left: 1.2rem; overflow-y: auto; }
286
+ .log li { margin: 0.12rem 0; color: var(--muted); }
287
+ .log li.latest { color: var(--ink); }
288
+ dialog {
289
+ width: min(410px, calc(100vw - 2rem));
290
+ padding: 0;
291
+ border: 1px solid var(--track);
292
+ border-radius: 10px;
293
+ background: var(--card);
294
+ color: var(--ink);
295
+ box-shadow: 0 18px 55px rgba(0, 0, 0, 0.35);
296
+ }
297
+ dialog::backdrop { background: rgba(20, 17, 14, 0.62); }
298
+ .dialog-content { padding: 1rem; }
299
+ .dialog-content h2 {
300
+ margin: 0 0 0.2rem;
301
+ font-family: Georgia, 'PT Serif', serif;
302
+ font-size: 1.1rem;
303
+ }
304
+ .dialog-content p { margin: 0 0 0.85rem; color: var(--muted); font-size: 0.85rem; }
305
+ .piece-tray {
306
+ display: grid;
307
+ grid-template-columns: repeat(4, 1fr);
308
+ gap: 0.35rem;
309
+ padding: 0.35rem;
310
+ border: 1px solid var(--track);
311
+ border-radius: 10px;
312
+ background: var(--track);
313
+ }
314
+ .promotion-key {
315
+ min-height: 3.2rem;
316
+ border: 1px solid transparent;
317
+ border-radius: 6px;
318
+ background: var(--card);
319
+ color: var(--ink);
320
+ cursor: pointer;
321
+ font-family: Georgia, 'Times New Roman', serif;
322
+ font-size: 1.8rem;
323
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14);
324
+ }
325
+ .promotion-key:hover { border-color: var(--accent); }
326
+ .dialog-actions { display: flex; justify-content: flex-end; margin-top: 0.8rem; }
327
+ @media (max-width: 640px) {
328
+ .table { grid-template-columns: 1fr; }
329
+ .aside { display: grid; grid-template-columns: 1fr 1fr; }
330
+ }
331
+ @media (prefers-reduced-motion: reduce) {
332
+ .piece { transition: none; }
333
+ }
334
+ .demo-banner {
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: space-between;
338
+ gap: 0.75rem;
339
+ margin: 0 0 0.85rem;
340
+ padding: 0.5rem 0.65rem;
341
+ border: 1px solid var(--accent);
342
+ border-radius: 6px;
343
+ background: color-mix(in srgb, var(--card) 88%, var(--accent));
344
+ color: var(--ink);
345
+ font-size: 0.8rem;
346
+ }
347
+ .demo-banner[hidden] { display: none; }
348
+ .demo-banner strong { font-weight: 700; }
349
+ .demo-reset {
350
+ flex: none;
351
+ padding: 0.25rem 0.5rem;
352
+ border: 1px solid var(--accent);
353
+ border-radius: 6px;
354
+ background: var(--card);
355
+ color: var(--ink);
356
+ cursor: pointer;
357
+ font: inherit;
358
+ }
359
+ .demo-reset:hover { border-color: var(--ink); }
360
+ </style>
361
+ </head>
362
+ <body>
363
+ <main>
364
+ <div class="demo-banner" data-gezel-demo-banner hidden>
365
+ <span><strong>Demo mode</strong> — sample data stays in this page.</span>
366
+ <button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
367
+ </div>
368
+ <p class="note" id="browser-note" hidden>
369
+ Viewing from a browser tab — the board stays live, but moves happen in the app's Output pane.
370
+ </p>
371
+ <div class="banner" id="banner"></div>
372
+ <header>
373
+ <h1>Chess</h1>
374
+ <button type="button" class="newgame" id="newgame">New game</button>
375
+ </header>
376
+ <p class="status" id="status">Setting up the board…</p>
377
+ <div class="table">
378
+ <div class="boardwrap"><div class="board" id="board" aria-label="Chess board"></div></div>
379
+ <div class="aside">
380
+ <div class="panel move-thought" id="move-thought-panel" hidden>
381
+ <span class="who">Schaakspeler says</span>
382
+ <span id="move-thought"></span>
383
+ </div>
384
+ <div class="panel">
385
+ <h2>Moves</h2>
386
+ <ol class="log" id="log"><li>No moves yet — white opens.</li></ol>
387
+ </div>
388
+ </div>
389
+ </div>
390
+ <dialog id="promotion-dialog" aria-labelledby="promotion-title">
391
+ <div class="dialog-content">
392
+ <h2 id="promotion-title">Promote your pawn</h2>
393
+ <p>Choose the piece that arrives on the back rank.</p>
394
+ <div class="piece-tray" id="promotion-choices"></div>
395
+ <div class="dialog-actions">
396
+ <button type="button" class="passive-button" id="cancel-promotion">Cancel</button>
397
+ </div>
398
+ </div>
399
+ </dialog>
400
+ </main>
401
+ <script>
402
+ /* gezel-page-demo-stub v1 */
403
+ /*
404
+ * The canonical demo stub for Gezel type pages. MIT License.
405
+ *
406
+ * Paste this file's bytes verbatim into a <script> block ahead of the
407
+ * page's own code, then call makeDemoGezel({ page, tools, data }) with the
408
+ * page's demo handlers. Served by Gezel, the real window.gezel is already
409
+ * injected and the stub steps aside; opened as a raw file (the live demos
410
+ * on gezelgilde.com), it installs a window.gezel whose tools and data are
411
+ * the page-supplied handler maps — one code path in every mode. `tools`
412
+ * and `data` map a tool name / declared read path to a function; data
413
+ * handlers back read() and list(), and url() returns the handler's value
414
+ * or passes a data: URI through. See docs/page-authoring.md.
415
+ */
416
+ (function () {
417
+ 'use strict';
418
+ window.makeDemoGezel = function (opts) {
419
+ if (window.gezel) return window.gezel;
420
+ var page = (opts && opts.page) || {};
421
+ var tools = (opts && opts.tools) || {};
422
+ var data = (opts && opts.data) || {};
423
+ var themeListeners = [];
424
+ var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
425
+ var ui = {
426
+ theme: { mode: media && media.matches ? 'dark' : 'light' },
427
+ onTheme: function (cb) {
428
+ themeListeners.push(cb);
429
+ return function () {
430
+ var at = themeListeners.indexOf(cb);
431
+ if (at >= 0) themeListeners.splice(at, 1);
432
+ };
433
+ },
434
+ };
435
+ if (media && media.addEventListener) {
436
+ media.addEventListener('change', function (ev) {
437
+ ui.theme = { mode: ev.matches ? 'dark' : 'light' };
438
+ themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
439
+ });
440
+ }
441
+ function fail(code, message) {
442
+ var err = new Error(message);
443
+ err.code = code;
444
+ return Promise.reject(err);
445
+ }
446
+ function run(map, key, arg) {
447
+ if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
448
+ return Promise.resolve().then(function () { return map[key](arg); });
449
+ }
450
+ window.gezel = {
451
+ page: {
452
+ api: 1,
453
+ projectId: 'demo',
454
+ source: 'type',
455
+ entry: page.entry || 'index.html',
456
+ typeName: page.typeName || 'demo',
457
+ params: page.params || {},
458
+ mode: 'demo',
459
+ },
460
+ tools: {
461
+ list: function () { return Promise.resolve(Object.keys(tools)); },
462
+ invoke: function (name, input) {
463
+ var call = run(tools, name, input || {});
464
+ if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
465
+ return call.then(function (output) { return { output: output }; });
466
+ },
467
+ },
468
+ data: {
469
+ read: function (path, o) {
470
+ return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
471
+ },
472
+ list: function (path, o) {
473
+ return run(data, path, o || {}) || Promise.resolve([]);
474
+ },
475
+ watch: function () { return function () {}; },
476
+ url: function (path) {
477
+ if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
478
+ return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
479
+ },
480
+ },
481
+ ui: ui,
482
+ refresh: function () {},
483
+ };
484
+ return window.gezel;
485
+ };
486
+ })();
487
+ </script>
488
+ <script>
489
+ (function () {
490
+ 'use strict';
491
+
492
+ var FILES = 'abcdefgh';
493
+ var PIECES = {
494
+ K: '♚', Q: '♛', R: '♜', B: '♝', N: '♞', P: '♟',
495
+ k: '♚', q: '♛', r: '♜', b: '♝', n: '♞', p: '♟',
496
+ };
497
+ var PROMOTION_PIECES = { q: '♛', r: '♜', b: '♝', n: '♞' };
498
+ var state = null;
499
+ var selected = null;
500
+ var busy = false;
501
+ var promotionMoves = [];
502
+ var demoGame = null;
503
+
504
+ function squareName(x, y) { return FILES[x] + String(8 - y); }
505
+ function sideOf(piece) {
506
+ if (!piece || piece === '.') return null;
507
+ return piece === piece.toUpperCase() ? 'user' : 'ai';
508
+ }
509
+ function demoCoords(name) { return { x: FILES.indexOf(name[0]), y: 8 - Number(name[1]) }; }
510
+ function demoAt(squares, x, y) { return x < 0 || x > 7 || y < 0 || y > 7 ? null : squares[y * 8 + x]; }
511
+ function demoSet(squares, x, y, piece) {
512
+ var i = y * 8 + x;
513
+ return squares.slice(0, i) + piece + squares.slice(i + 1);
514
+ }
515
+ function demoMoves(game) {
516
+ var out = [];
517
+ var squares = game.squares;
518
+ var side = game.turn;
519
+ function add(fromX, fromY, toX, toY, extra) {
520
+ var target = demoAt(squares, toX, toY);
521
+ if (target === null || sideOf(target) === side) return;
522
+ var base = { from: squareName(fromX, fromY), to: squareName(toX, toY), capture: target !== '.' };
523
+ out.push(Object.assign(base, extra || {}));
524
+ }
525
+ function ray(x, y, dx, dy) {
526
+ var tx = x + dx; var ty = y + dy;
527
+ while (demoAt(squares, tx, ty) !== null) {
528
+ var target = demoAt(squares, tx, ty);
529
+ if (sideOf(target) === side) break;
530
+ add(x, y, tx, ty);
531
+ if (target !== '.') break;
532
+ tx += dx; ty += dy;
533
+ }
534
+ }
535
+ for (var y = 0; y < 8; y += 1) {
536
+ for (var x = 0; x < 8; x += 1) {
537
+ var piece = demoAt(squares, x, y);
538
+ if (sideOf(piece) !== side) continue;
539
+ var kind = piece.toLowerCase();
540
+ if (kind === 'p') {
541
+ var dir = side === 'user' ? -1 : 1;
542
+ var start = side === 'user' ? 6 : 1;
543
+ var last = side === 'user' ? 0 : 7;
544
+ if (demoAt(squares, x, y + dir) === '.') {
545
+ if (y + dir === last) {
546
+ ['q', 'r', 'b', 'n'].forEach(function (promotion) { add(x, y, x, y + dir, { promotion: promotion }); });
547
+ } else {
548
+ add(x, y, x, y + dir);
549
+ if (y === start && demoAt(squares, x, y + dir * 2) === '.') add(x, y, x, y + dir * 2);
550
+ }
551
+ }
552
+ [-1, 1].forEach(function (dx) {
553
+ var target = demoAt(squares, x + dx, y + dir);
554
+ if (target && target !== '.' && sideOf(target) !== side) {
555
+ if (y + dir === last) ['q', 'r', 'b', 'n'].forEach(function (promotion) { add(x, y, x + dx, y + dir, { promotion: promotion }); });
556
+ else add(x, y, x + dx, y + dir);
557
+ }
558
+ });
559
+ } else if (kind === 'n') {
560
+ [[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]].forEach(function (d) { add(x, y, x + d[0], y + d[1]); });
561
+ } else if (kind === 'k') {
562
+ for (var ky = -1; ky <= 1; ky += 1) for (var kx = -1; kx <= 1; kx += 1) if (kx || ky) add(x, y, x + kx, y + ky);
563
+ } else {
564
+ if (kind === 'b' || kind === 'q') [[-1,-1],[-1,1],[1,-1],[1,1]].forEach(function (d) { ray(x, y, d[0], d[1]); });
565
+ if (kind === 'r' || kind === 'q') [[-1,0],[1,0],[0,-1],[0,1]].forEach(function (d) { ray(x, y, d[0], d[1]); });
566
+ }
567
+ }
568
+ }
569
+ return out;
570
+ }
571
+ function demoNewGame() {
572
+ var game = {
573
+ version: 1,
574
+ squares: 'rnbqkbnrpppppppp................................PPPPPPPPRNBQKBNR',
575
+ turn: 'user', status: 'playing', legalMoves: [], lastMove: null,
576
+ lastMoveThought: '', moveLog: [], fullmoveNumber: 1,
577
+ };
578
+ demoGame = game;
579
+ game.legalMoves = demoMoves(game);
580
+ return game;
581
+ }
582
+ function demoUserMove(input) {
583
+ var move = demoGame.legalMoves.find(function (m) {
584
+ return m.from === input.from && m.to === input.to && (!m.promotion || m.promotion === (input.promotion || 'q'));
585
+ });
586
+ if (!move) throw new Error('That move is not available in the hot-seat demo.');
587
+ var from = demoCoords(move.from); var to = demoCoords(move.to);
588
+ var piece = demoAt(demoGame.squares, from.x, from.y);
589
+ var captured = demoAt(demoGame.squares, to.x, to.y);
590
+ demoGame.squares = demoSet(demoSet(demoGame.squares, from.x, from.y, '.'), to.x, to.y,
591
+ move.promotion ? (demoGame.turn === 'user' ? move.promotion.toUpperCase() : move.promotion) : piece);
592
+ demoGame.lastMove = { from: move.from, to: move.to };
593
+ demoGame.moveLog.push({ by: demoGame.turn, notation: move.from + '–' + move.to + (move.promotion ? '=' + move.promotion.toUpperCase() : '') });
594
+ if (captured && captured.toLowerCase() === 'k') {
595
+ demoGame.status = demoGame.turn === 'user' ? 'user_won' : 'ai_won';
596
+ demoGame.legalMoves = [];
597
+ } else {
598
+ demoGame.turn = demoGame.turn === 'user' ? 'ai' : 'user';
599
+ if (demoGame.turn === 'user') demoGame.fullmoveNumber += 1;
600
+ demoGame.legalMoves = demoMoves(demoGame);
601
+ if (demoGame.legalMoves.length === 0) demoGame.status = 'draw';
602
+ }
603
+ return { status: demoGame.status, turn: demoGame.turn, summary: 'Move played.' };
604
+ }
605
+
606
+ demoNewGame();
607
+ makeDemoGezel({
608
+ page: { entry: 'board/index.html', typeName: 'Chess', params: { personality: 'peppy', playStyle: 'Opponent' } },
609
+ tools: {
610
+ user_move: demoUserMove,
611
+ new_game: function () {
612
+ demoNewGame();
613
+ return { status: demoGame.status, turn: demoGame.turn, summary: 'New game.' };
614
+ },
615
+ },
616
+ data: {
617
+ 'game.json': function () { return demoGame; },
618
+ },
619
+ });
620
+
621
+ // ── one code path from here: window.gezel exists in every mode ──
622
+ var demoMode = gezel.page.mode === 'demo';
623
+ var browserMode = gezel.page.mode === 'browser';
624
+
625
+ function applyTheme(mode) {
626
+ document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
627
+ }
628
+ applyTheme(gezel.ui.theme.mode);
629
+ gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
630
+
631
+ if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
632
+ if (browserMode) document.getElementById('browser-note').hidden = false;
633
+
634
+ function setBanner(text) {
635
+ var banner = document.getElementById('banner');
636
+ banner.textContent = text || '';
637
+ banner.className = text ? 'banner show' : 'banner';
638
+ }
639
+ function invokeErrorText(error) {
640
+ if (error && error.code === 'unavailable') {
641
+ return "Moves happen in the app's Output pane — this browser tab stays view-only.";
642
+ }
643
+ return String(error && error.message ? error.message : error);
644
+ }
645
+ function statusLine() {
646
+ if (!state) return 'Setting up the board…';
647
+ if (demoMode && state.status === 'playing') return 'Hot seat — ' + (state.turn === 'user' ? 'white' : 'black') + ' to move.';
648
+ if (demoMode && state.status === 'user_won') return 'White wins the hot-seat demo.';
649
+ if (demoMode && state.status === 'ai_won') return 'Black wins the hot-seat demo.';
650
+ if (demoMode && state.status === 'draw') return 'Hot-seat draw — reset for another game.';
651
+ if (state.status === 'user_won') return 'Checkmate — you won. The Schaakspeler is ready for a rematch.';
652
+ if (state.status === 'ai_won') return 'Checkmate — the Schaakspeler takes this one.';
653
+ if (state.status === 'draw') return 'Draw — a balanced board, and a rematch whenever you like.';
654
+ if (busy) return 'Sending your move…';
655
+ if (state.turn === 'ai') return 'Schaakspeler is thinking…';
656
+ return selected ? 'Choose a highlighted destination.' : 'Your move — you have white.';
657
+ }
658
+ function movesFrom(name) {
659
+ if (!state) return [];
660
+ return state.legalMoves.filter(function (move) { return move.from === name; });
661
+ }
662
+ function movesTo(name) {
663
+ return selected
664
+ ? movesFrom(selected).filter(function (move) { return move.to === name; })
665
+ : [];
666
+ }
667
+ function render() {
668
+ var status = document.getElementById('status');
669
+ status.textContent = statusLine();
670
+ status.className =
671
+ state && !demoMode && state.turn === 'ai' && state.status === 'playing'
672
+ ? 'status thinking'
673
+ : 'status';
674
+
675
+ var thoughtPanel = document.getElementById('move-thought-panel');
676
+ if (state && state.lastMoveThought) {
677
+ thoughtPanel.hidden = false;
678
+ document.getElementById('move-thought').textContent = state.lastMoveThought;
679
+ } else {
680
+ thoughtPanel.hidden = true;
681
+ }
682
+
683
+ var log = document.getElementById('log');
684
+ if (state && state.moveLog.length) {
685
+ log.innerHTML = '';
686
+ state.moveLog.forEach(function (entry, index) {
687
+ var item = document.createElement('li');
688
+ item.textContent = (entry.by === 'user' ? 'White ' : 'Black ') + entry.notation;
689
+ if (index === state.moveLog.length - 1) item.className = 'latest';
690
+ log.appendChild(item);
691
+ });
692
+ log.scrollTop = log.scrollHeight;
693
+ } else {
694
+ log.innerHTML = '<li>No moves yet — white opens.</li>';
695
+ }
696
+
697
+ var board = document.getElementById('board');
698
+ board.innerHTML = '';
699
+ var myTurn = state && (demoMode || state.turn === 'user') && state.status === 'playing' && !busy;
700
+ for (var y = 0; y < 8; y += 1) {
701
+ for (var x = 0; x < 8; x += 1) {
702
+ var name = squareName(x, y);
703
+ var piece = state ? state.squares[y * 8 + x] : '.';
704
+ var choices = movesTo(name);
705
+ var cell = document.createElement('button');
706
+ cell.type = 'button';
707
+ cell.className = 'cell ' + ((x + y) % 2 ? 'dark' : 'light');
708
+ cell.setAttribute('aria-label', name + (piece === '.' ? ', empty' : ', ' + piece));
709
+ cell.dataset.square = name;
710
+ if (state && state.lastMove) {
711
+ if (state.lastMove.from === name) cell.className += ' last-from';
712
+ if (state.lastMove.to === name) cell.className += ' last-to';
713
+ }
714
+ if (selected === name) cell.className += ' selected';
715
+ if (choices.length) {
716
+ cell.className += ' hint';
717
+ if (piece !== '.') cell.className += ' capture';
718
+ }
719
+ if (myTurn && (movesFrom(name).length || choices.length)) cell.className += ' selectable';
720
+
721
+ if (x === 0) {
722
+ var rank = document.createElement('span');
723
+ rank.className = 'coord rank';
724
+ rank.textContent = String(8 - y);
725
+ cell.appendChild(rank);
726
+ }
727
+ if (y === 7) {
728
+ var file = document.createElement('span');
729
+ file.className = 'coord file';
730
+ file.textContent = FILES[x];
731
+ cell.appendChild(file);
732
+ }
733
+ if (piece !== '.') {
734
+ var figure = document.createElement('span');
735
+ figure.className = 'piece ' + (sideOf(piece) === 'user' ? 'white' : 'black');
736
+ figure.dataset.glyph = PIECES[piece];
737
+ figure.setAttribute('aria-hidden', 'true');
738
+ cell.appendChild(figure);
739
+ }
740
+ cell.addEventListener('click', onSquareClick);
741
+ board.appendChild(cell);
742
+ }
743
+ }
744
+ }
745
+
746
+ function onSquareClick(event) {
747
+ if (!state || (!demoMode && state.turn !== 'user') || state.status !== 'playing' || busy) return;
748
+ var name = event.currentTarget.dataset.square;
749
+ var destinations = movesTo(name);
750
+ if (selected && destinations.length) {
751
+ if (destinations.some(function (move) { return move.promotion; })) {
752
+ openPromotion(destinations);
753
+ } else {
754
+ submitMove(destinations[0]);
755
+ }
756
+ return;
757
+ }
758
+ if (movesFrom(name).length) selected = name;
759
+ else selected = null;
760
+ render();
761
+ }
762
+
763
+ function openPromotion(moves) {
764
+ promotionMoves = moves;
765
+ var tray = document.getElementById('promotion-choices');
766
+ tray.innerHTML = '';
767
+ moves.forEach(function (move) {
768
+ var button = document.createElement('button');
769
+ button.type = 'button';
770
+ button.className = 'promotion-key';
771
+ button.textContent = PROMOTION_PIECES[move.promotion];
772
+ button.setAttribute('aria-label', 'Promote to ' + move.promotion);
773
+ button.addEventListener('click', function () {
774
+ document.getElementById('promotion-dialog').close();
775
+ submitMove(move);
776
+ });
777
+ tray.appendChild(button);
778
+ });
779
+ document.getElementById('promotion-dialog').showModal();
780
+ }
781
+
782
+ function submitMove(move) {
783
+ selected = null;
784
+ busy = true;
785
+ render();
786
+ gezel.tools
787
+ .invoke('user_move', {
788
+ from: move.from,
789
+ to: move.to,
790
+ ...(move.promotion ? { promotion: move.promotion } : {}),
791
+ })
792
+ .then(function (result) {
793
+ busy = false;
794
+ if (
795
+ result.reaction &&
796
+ result.reaction.delivered === false &&
797
+ result.reaction.reason === 'engagement-off'
798
+ ) {
799
+ setBanner('Move played — but AI engagement is off, so the Schaakspeler cannot reply.');
800
+ }
801
+ return loadGame();
802
+ })
803
+ .catch(function (error) {
804
+ busy = false;
805
+ setBanner(invokeErrorText(error));
806
+ render();
807
+ });
808
+ }
809
+
810
+ function newGame() {
811
+ if (busy) return;
812
+ selected = null;
813
+ busy = true;
814
+ render();
815
+ gezel.tools
816
+ .invoke('new_game', {})
817
+ .then(function () {
818
+ busy = false;
819
+ setBanner('');
820
+ return loadGame();
821
+ })
822
+ .catch(function (error) {
823
+ busy = false;
824
+ setBanner(invokeErrorText(error));
825
+ render();
826
+ });
827
+ }
828
+
829
+ function transitionState(next) {
830
+ state = next;
831
+ if (selected && !movesFrom(selected).length) selected = null;
832
+ render();
833
+ }
834
+ function loadGame() {
835
+ return gezel.data.read('game.json').then(
836
+ function (next) { if (next) transitionState(next); },
837
+ function () {},
838
+ );
839
+ }
840
+
841
+ document.getElementById('newgame').addEventListener('click', newGame);
842
+ document.getElementById('cancel-promotion').addEventListener('click', function () {
843
+ promotionMoves = [];
844
+ document.getElementById('promotion-dialog').close();
845
+ });
846
+ loadGame();
847
+ gezel.data.watch('game.json', function () { loadGame(); });
848
+ })();
849
+ </script>
850
+ </body>
851
+ </html>