@bendyline/gilde 0.1.86 → 0.1.87

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 (104) hide show
  1. package/data/craftbook-templates/index.json +1 -1
  2. package/data/craftbook-templates/raw-index.json +1 -1
  3. package/data/craftbook-templates/ta/tailor-resume/versions/1.0.5/craftbook.json +226 -0
  4. package/data/craftbook-templates/ta/tailor-resume/versions/1.0.5/test.json +79 -0
  5. package/data/knowledge-catalogs/ca/caselaw-alaska-part-0001/manifest.json +24 -0
  6. package/data/knowledge-catalogs/ca/caselaw-alaska-part-0001/versions/2026.10.1/manifest.json +658 -0
  7. package/data/knowledge-catalogs/index.json +1 -1
  8. package/data/knowledge-catalogs/raw-index.json +1 -1
  9. package/data/project-types/ch/checkers/versions/1.2.1/about.md +9 -0
  10. package/data/project-types/ch/checkers/versions/1.2.1/game.json +77 -0
  11. package/data/project-types/ch/checkers/versions/1.2.1/manifest.json +168 -0
  12. package/data/project-types/ch/checkers/versions/1.2.1/mission.md +9 -0
  13. package/data/project-types/ch/checkers/versions/1.2.1/pages/board/index.html +1158 -0
  14. package/data/project-types/ch/chess/versions/1.0.4/about.md +9 -0
  15. package/data/project-types/ch/chess/versions/1.0.4/game.json +127 -0
  16. package/data/project-types/ch/chess/versions/1.0.4/manifest.json +182 -0
  17. package/data/project-types/ch/chess/versions/1.0.4/mission.md +8 -0
  18. package/data/project-types/ch/chess/versions/1.0.4/pages/board/index.html +851 -0
  19. package/data/project-types/de/design-scheme/versions/1.0.2/about.md +6 -0
  20. package/data/project-types/de/design-scheme/versions/1.0.2/manifest.json +70 -0
  21. package/data/project-types/de/design-scheme/versions/1.0.2/mission.md +3 -0
  22. package/data/project-types/de/design-scheme/versions/1.0.2/pages/gallery/index.html +212 -0
  23. package/data/project-types/de/design-scheme/versions/1.0.2/schemes/index.json +9 -0
  24. package/data/project-types/de/design-scheme/versions/1.0.2/schemes/warm-dusk.json +4 -0
  25. package/data/project-types/ev/event-planner/versions/1.0.2/about.md +7 -0
  26. package/data/project-types/ev/event-planner/versions/1.0.2/budget.json +5 -0
  27. package/data/project-types/ev/event-planner/versions/1.0.2/guests.json +10 -0
  28. package/data/project-types/ev/event-planner/versions/1.0.2/manifest.json +171 -0
  29. package/data/project-types/ev/event-planner/versions/1.0.2/mission.md +6 -0
  30. package/data/project-types/ev/event-planner/versions/1.0.2/pages/dashboard/index.html +283 -0
  31. package/data/project-types/fi/fitness-coach/versions/1.0.2/about.md +7 -0
  32. package/data/project-types/fi/fitness-coach/versions/1.0.2/manifest.json +126 -0
  33. package/data/project-types/fi/fitness-coach/versions/1.0.2/mission.md +7 -0
  34. package/data/project-types/fi/fitness-coach/versions/1.0.2/pages/dashboard/index.html +273 -0
  35. package/data/project-types/fi/fitness-coach/versions/1.0.2/training.json +6 -0
  36. package/data/project-types/fl/flashcards/versions/1.1.1/about.md +8 -0
  37. package/data/project-types/fl/flashcards/versions/1.1.1/deck.json +7 -0
  38. package/data/project-types/fl/flashcards/versions/1.1.1/manifest.json +168 -0
  39. package/data/project-types/fl/flashcards/versions/1.1.1/mission.md +6 -0
  40. package/data/project-types/fl/flashcards/versions/1.1.1/pages/review/index.html +477 -0
  41. package/data/project-types/fr/freelance-office/versions/1.0.2/about.md +6 -0
  42. package/data/project-types/fr/freelance-office/versions/1.0.2/invoices.json +9 -0
  43. package/data/project-types/fr/freelance-office/versions/1.0.2/manifest.json +143 -0
  44. package/data/project-types/fr/freelance-office/versions/1.0.2/mission.md +6 -0
  45. package/data/project-types/fr/freelance-office/versions/1.0.2/pages/dashboard/index.html +258 -0
  46. package/data/project-types/fu/fundraiser-hq/versions/1.0.2/about.md +7 -0
  47. package/data/project-types/fu/fundraiser-hq/versions/1.0.2/donations.json +7 -0
  48. package/data/project-types/fu/fundraiser-hq/versions/1.0.2/manifest.json +127 -0
  49. package/data/project-types/fu/fundraiser-hq/versions/1.0.2/mission.md +6 -0
  50. package/data/project-types/fu/fundraiser-hq/versions/1.0.2/pages/dashboard/index.html +261 -0
  51. package/data/project-types/go/go/versions/1.0.2/about.md +9 -0
  52. package/data/project-types/go/go/versions/1.0.2/game.json +103 -0
  53. package/data/project-types/go/go/versions/1.0.2/manifest.json +168 -0
  54. package/data/project-types/go/go/versions/1.0.2/mission.md +8 -0
  55. package/data/project-types/go/go/versions/1.0.2/pages/board/index.html +855 -0
  56. package/data/project-types/ho/household-budget/versions/1.0.3/about.md +6 -0
  57. package/data/project-types/ho/household-budget/versions/1.0.3/ledger.json +5 -0
  58. package/data/project-types/ho/household-budget/versions/1.0.3/manifest.json +164 -0
  59. package/data/project-types/ho/household-budget/versions/1.0.3/mission.md +5 -0
  60. package/data/project-types/ho/household-budget/versions/1.0.3/pages/dashboard/index.html +340 -0
  61. package/data/project-types/ho/household-manual/versions/1.0.2/about.md +7 -0
  62. package/data/project-types/ho/household-manual/versions/1.0.2/manifest.json +153 -0
  63. package/data/project-types/ho/household-manual/versions/1.0.2/mission.md +8 -0
  64. package/data/project-types/ho/household-manual/versions/1.0.2/pages/dashboard/index.html +258 -0
  65. package/data/project-types/ho/household-manual/versions/1.0.2/systems.json +8 -0
  66. package/data/project-types/index.json +1 -1
  67. package/data/project-types/jo/job-hunt/versions/1.0.2/about.md +10 -0
  68. package/data/project-types/jo/job-hunt/versions/1.0.2/activity.json +4 -0
  69. package/data/project-types/jo/job-hunt/versions/1.0.2/manifest.json +213 -0
  70. package/data/project-types/jo/job-hunt/versions/1.0.2/mission.md +9 -0
  71. package/data/project-types/jo/job-hunt/versions/1.0.2/pages/dashboard/index.html +430 -0
  72. package/data/project-types/jo/job-hunt/versions/1.0.2/pipeline.json +6 -0
  73. package/data/project-types/la/language-trainer/versions/1.0.2/about.md +6 -0
  74. package/data/project-types/la/language-trainer/versions/1.0.2/manifest.json +80 -0
  75. package/data/project-types/la/language-trainer/versions/1.0.2/mission.md +4 -0
  76. package/data/project-types/la/language-trainer/versions/1.0.2/pages/dashboard/index.html +268 -0
  77. package/data/project-types/la/language-trainer/versions/1.0.2/progress.json +7 -0
  78. package/data/project-types/li/life-binder/versions/1.0.3/about.md +7 -0
  79. package/data/project-types/li/life-binder/versions/1.0.3/documents.json +145 -0
  80. package/data/project-types/li/life-binder/versions/1.0.3/manifest.json +138 -0
  81. package/data/project-types/li/life-binder/versions/1.0.3/mission.md +6 -0
  82. package/data/project-types/li/life-binder/versions/1.0.3/pages/dashboard/index.html +248 -0
  83. package/data/project-types/me/meal-planner/versions/1.0.2/about.md +6 -0
  84. package/data/project-types/me/meal-planner/versions/1.0.2/manifest.json +160 -0
  85. package/data/project-types/me/meal-planner/versions/1.0.2/menu.json +5 -0
  86. package/data/project-types/me/meal-planner/versions/1.0.2/mission.md +5 -0
  87. package/data/project-types/me/meal-planner/versions/1.0.2/pages/dashboard/index.html +248 -0
  88. package/data/project-types/me/meal-planner/versions/1.0.2/pantry.json +9 -0
  89. package/data/project-types/no/novel-writing-room/versions/1.0.2/about.md +7 -0
  90. package/data/project-types/no/novel-writing-room/versions/1.0.2/chapters.json +10 -0
  91. package/data/project-types/no/novel-writing-room/versions/1.0.2/manifest.json +158 -0
  92. package/data/project-types/no/novel-writing-room/versions/1.0.2/mission.md +7 -0
  93. package/data/project-types/no/novel-writing-room/versions/1.0.2/pages/dashboard/index.html +264 -0
  94. package/data/project-types/no/novel-writing-room/versions/1.0.2/sessions.json +6 -0
  95. package/data/project-types/raw-index.json +1 -1
  96. package/data/project-types/tr/trip-planner/versions/1.0.2/about.md +7 -0
  97. package/data/project-types/tr/trip-planner/versions/1.0.2/journal.json +4 -0
  98. package/data/project-types/tr/trip-planner/versions/1.0.2/manifest.json +194 -0
  99. package/data/project-types/tr/trip-planner/versions/1.0.2/mission.md +4 -0
  100. package/data/project-types/tr/trip-planner/versions/1.0.2/packing.json +8 -0
  101. package/data/project-types/tr/trip-planner/versions/1.0.2/pages/dashboard/index.html +308 -0
  102. package/data/project-types/tr/trip-planner/versions/1.0.2/trip.json +7 -0
  103. package/package.json +1 -1
  104. package/schemas/project-type-version.schema.json +13 -0
@@ -0,0 +1,855 @@
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>Go</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
+ --wood-a: #d4a45f;
18
+ --wood-b: #b97d3f;
19
+ --wood-edge: #694322;
20
+ --line: rgba(49, 30, 18, 0.78);
21
+ --black-stone-a: #4a4540;
22
+ --black-stone-b: #171411;
23
+ --white-stone-a: #fffdf6;
24
+ --white-stone-b: #d8d0c3;
25
+ --last-move: #c55e32;
26
+ --legal: #52745a;
27
+ }
28
+ :root[data-theme='dark'] {
29
+ --bg: #1c1a17;
30
+ --card: #262320;
31
+ --ink: #efe9e0;
32
+ --muted: #a59a8c;
33
+ --accent: #e0894a;
34
+ --track: #3a352f;
35
+ --wood-a: #a7733c;
36
+ --wood-b: #744a29;
37
+ --wood-edge: #2b1a10;
38
+ --line: rgba(28, 18, 12, 0.88);
39
+ --last-move: #f19a5f;
40
+ --legal: #7da486;
41
+ }
42
+ @media (prefers-color-scheme: dark) {
43
+ :root:not([data-theme='light']) {
44
+ --bg: #1c1a17;
45
+ --card: #262320;
46
+ --ink: #efe9e0;
47
+ --muted: #a59a8c;
48
+ --accent: #e0894a;
49
+ --track: #3a352f;
50
+ --wood-a: #a7733c;
51
+ --wood-b: #744a29;
52
+ --wood-edge: #2b1a10;
53
+ --line: rgba(28, 18, 12, 0.88);
54
+ --last-move: #f19a5f;
55
+ --legal: #7da486;
56
+ }
57
+ }
58
+ * { box-sizing: border-box; }
59
+ body {
60
+ margin: 0;
61
+ padding: 1.25rem;
62
+ background: var(--bg);
63
+ color: var(--ink);
64
+ font: 15px/1.5 system-ui, -apple-system, 'Segoe UI', sans-serif;
65
+ }
66
+ main { max-width: 820px; margin: 0 auto; }
67
+ header {
68
+ display: flex;
69
+ align-items: baseline;
70
+ justify-content: space-between;
71
+ gap: 0.75rem;
72
+ flex-wrap: wrap;
73
+ }
74
+ h1 {
75
+ margin: 0;
76
+ font-family: Georgia, 'PT Serif', 'Times New Roman', serif;
77
+ font-size: 1.35rem;
78
+ }
79
+ button { font: inherit; }
80
+ .newgame,
81
+ .pass-button {
82
+ min-height: 44px;
83
+ border: 1px solid var(--track);
84
+ border-radius: 6px;
85
+ padding: 0.32rem 0.7rem;
86
+ background: var(--card);
87
+ color: var(--ink);
88
+ cursor: pointer;
89
+ }
90
+ .newgame:hover,
91
+ .pass-button:hover { border-color: var(--accent); }
92
+ .pass-button:disabled { cursor: default; opacity: 0.55; }
93
+ .status { min-height: 1.5em; margin: 0.15rem 0 0.9rem; color: var(--muted); }
94
+ .status.thinking { color: var(--accent); }
95
+ .banner {
96
+ display: none;
97
+ margin-bottom: 0.75rem;
98
+ padding: 0.45rem 0.7rem;
99
+ border: 1px solid var(--accent);
100
+ border-radius: 6px;
101
+ font-size: 0.85rem;
102
+ }
103
+ .banner.show { display: block; }
104
+ .note {
105
+ margin: 0 0 0.85rem;
106
+ padding: 0.5rem 0.65rem;
107
+ border: 1px solid var(--track);
108
+ border-radius: 6px;
109
+ background: var(--card);
110
+ color: var(--muted);
111
+ font-size: 0.8rem;
112
+ }
113
+ .table { display: grid; grid-template-columns: minmax(0, 1fr) 220px; gap: 0.9rem; }
114
+ .boardwrap {
115
+ min-width: 0;
116
+ padding: clamp(0.8rem, 3vw, 1.35rem);
117
+ border: 1px solid var(--wood-edge);
118
+ border-radius: 14px;
119
+ background:
120
+ linear-gradient(95deg, rgba(255,255,255,0.08), transparent 35%, rgba(74,37,16,0.08)),
121
+ linear-gradient(145deg, var(--wood-a), var(--wood-b));
122
+ box-shadow:
123
+ inset 0 1px 0 rgba(255, 255, 255, 0.22),
124
+ inset 0 -5px 9px rgba(74, 37, 16, 0.24),
125
+ 0 10px 24px rgba(31, 21, 15, 0.22);
126
+ }
127
+ .board {
128
+ display: grid;
129
+ grid-template-columns: repeat(var(--board-size), 1fr);
130
+ aspect-ratio: 1;
131
+ user-select: none;
132
+ }
133
+ .point {
134
+ position: relative;
135
+ display: grid;
136
+ min-width: 0;
137
+ min-height: 0;
138
+ place-items: center;
139
+ padding: 0;
140
+ border: 0;
141
+ background: transparent;
142
+ color: inherit;
143
+ }
144
+ .point::before,
145
+ .point::after {
146
+ position: absolute;
147
+ z-index: 0;
148
+ background: var(--line);
149
+ content: '';
150
+ pointer-events: none;
151
+ }
152
+ .point::before {
153
+ top: calc(50% - 0.5px);
154
+ right: var(--right-edge, 0);
155
+ left: var(--left-edge, 0);
156
+ height: 1px;
157
+ }
158
+ .point::after {
159
+ top: var(--top-edge, 0);
160
+ bottom: var(--bottom-edge, 0);
161
+ left: calc(50% - 0.5px);
162
+ width: 1px;
163
+ }
164
+ .point.star:not(.occupied) .star-dot {
165
+ position: relative;
166
+ z-index: 1;
167
+ width: clamp(3px, 14%, 7px);
168
+ aspect-ratio: 1;
169
+ border-radius: 50%;
170
+ background: var(--line);
171
+ }
172
+ .point.legal { cursor: pointer; }
173
+ .point.legal:not(.occupied):hover .star-dot {
174
+ position: relative;
175
+ z-index: 2;
176
+ width: 30%;
177
+ aspect-ratio: 1;
178
+ border-radius: 50%;
179
+ background: var(--legal);
180
+ box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.16);
181
+ }
182
+ .stone {
183
+ position: relative;
184
+ z-index: 2;
185
+ width: 88%;
186
+ aspect-ratio: 1;
187
+ border: 1px solid rgba(32, 22, 16, 0.45);
188
+ border-radius: 50%;
189
+ box-shadow:
190
+ inset 0 4px 5px rgba(255, 255, 255, 0.2),
191
+ inset 0 -5px 7px rgba(0, 0, 0, 0.28),
192
+ 0 3px 5px rgba(45, 27, 15, 0.35);
193
+ transition: transform 140ms ease-out;
194
+ }
195
+ .stone.black { background: radial-gradient(circle at 34% 27%, var(--black-stone-a), var(--black-stone-b) 72%); }
196
+ .stone.white { background: radial-gradient(circle at 34% 27%, var(--white-stone-a), var(--white-stone-b) 72%); }
197
+ .point.last .stone::after {
198
+ position: absolute;
199
+ inset: 34%;
200
+ border-radius: 50%;
201
+ background: var(--last-move);
202
+ content: '';
203
+ }
204
+ .coord {
205
+ position: absolute;
206
+ z-index: 3;
207
+ color: rgba(44, 27, 16, 0.72);
208
+ font-size: clamp(0.38rem, 1vw, 0.62rem);
209
+ font-weight: 700;
210
+ line-height: 1;
211
+ pointer-events: none;
212
+ }
213
+ .coord.rank { top: 4%; left: 4%; }
214
+ .coord.file { right: 4%; bottom: 4%; }
215
+ .aside { display: flex; flex-direction: column; gap: 0.6rem; min-width: 0; }
216
+ .panel {
217
+ padding: 0.7rem 0.85rem;
218
+ border: 1px solid var(--track);
219
+ border-radius: 10px;
220
+ background: var(--card);
221
+ font-size: 0.85rem;
222
+ }
223
+ .panel h2 {
224
+ margin: 0 0 0.35rem;
225
+ color: var(--muted);
226
+ font-size: 0.72rem;
227
+ letter-spacing: 0.05em;
228
+ text-transform: uppercase;
229
+ }
230
+ .move-thought { font-style: italic; }
231
+ .move-thought .who {
232
+ display: block;
233
+ margin-bottom: 0.15rem;
234
+ color: var(--muted);
235
+ font-size: 0.78rem;
236
+ font-style: normal;
237
+ }
238
+ .game-actions { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
239
+ .capture-count { color: var(--muted); font-size: 0.78rem; }
240
+ .log { max-height: 210px; margin: 0; padding-left: 1.2rem; overflow-y: auto; }
241
+ .log li { margin: 0.12rem 0; color: var(--muted); }
242
+ .log li.latest { color: var(--ink); }
243
+ dialog {
244
+ width: min(430px, calc(100vw - 2rem));
245
+ padding: 0;
246
+ border: 1px solid var(--track);
247
+ border-radius: 10px;
248
+ background: var(--card);
249
+ color: var(--ink);
250
+ box-shadow: 0 18px 55px rgba(0, 0, 0, 0.35);
251
+ }
252
+ dialog::backdrop { background: rgba(20, 17, 14, 0.62); }
253
+ .dialog-content { padding: 1rem; }
254
+ .dialog-content h2 {
255
+ margin: 0;
256
+ font-family: Georgia, 'PT Serif', serif;
257
+ font-size: 1.1rem;
258
+ }
259
+ .dialog-intro { margin: 0.2rem 0 0.85rem; color: var(--muted); font-size: 0.85rem; }
260
+ fieldset { margin: 0 0 0.75rem; padding: 0; border: 0; }
261
+ legend {
262
+ margin-bottom: 0.35rem;
263
+ color: var(--muted);
264
+ font-size: 0.72rem;
265
+ font-weight: 700;
266
+ letter-spacing: 0.05em;
267
+ text-transform: uppercase;
268
+ }
269
+ .key-tray {
270
+ display: grid;
271
+ grid-template-columns: repeat(3, 1fr);
272
+ gap: 0.35rem;
273
+ padding: 0.35rem;
274
+ border: 1px solid var(--track);
275
+ border-radius: 10px;
276
+ background: var(--track);
277
+ }
278
+ .key-tray.two { grid-template-columns: repeat(2, 1fr); }
279
+ .rule-key {
280
+ padding: 0.45rem;
281
+ border: 1px solid transparent;
282
+ border-radius: 6px;
283
+ background: var(--card);
284
+ color: var(--ink);
285
+ cursor: pointer;
286
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.14);
287
+ }
288
+ .rule-key:hover { border-color: var(--accent); }
289
+ .rule-key[aria-checked='true'] {
290
+ border-color: var(--accent);
291
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
292
+ transform: translateY(1px);
293
+ }
294
+ .rule-key strong { display: block; font-size: 0.88rem; }
295
+ .rule-key span { display: block; color: var(--muted); font-size: 0.72rem; }
296
+ .dialog-actions { display: flex; justify-content: flex-end; gap: 0.45rem; margin-top: 0.9rem; }
297
+ .dialog-button {
298
+ min-height: 44px;
299
+ padding: 0.4rem 0.75rem;
300
+ border: 1px solid var(--accent);
301
+ border-radius: 6px;
302
+ background: var(--accent);
303
+ color: var(--bg);
304
+ cursor: pointer;
305
+ }
306
+ .dialog-button.secondary { border-color: var(--track); background: var(--card); color: var(--ink); }
307
+ @media (max-width: 660px) {
308
+ .table { grid-template-columns: 1fr; }
309
+ .aside { display: grid; grid-template-columns: 1fr 1fr; }
310
+ }
311
+ @media (prefers-reduced-motion: reduce) {
312
+ .stone { transition: none; }
313
+ }
314
+ .demo-banner {
315
+ display: flex;
316
+ align-items: center;
317
+ justify-content: space-between;
318
+ gap: 0.75rem;
319
+ margin: 0 0 0.85rem;
320
+ padding: 0.5rem 0.65rem;
321
+ border: 1px solid var(--accent);
322
+ border-radius: 6px;
323
+ background: color-mix(in srgb, var(--card) 88%, var(--accent));
324
+ color: var(--ink);
325
+ font-size: 0.8rem;
326
+ }
327
+ .demo-banner[hidden] { display: none; }
328
+ .demo-banner strong { font-weight: 700; }
329
+ .demo-reset {
330
+ flex: none;
331
+ padding: 0.25rem 0.5rem;
332
+ border: 1px solid var(--accent);
333
+ border-radius: 6px;
334
+ background: var(--card);
335
+ color: var(--ink);
336
+ cursor: pointer;
337
+ font: inherit;
338
+ }
339
+ .demo-reset:hover { border-color: var(--ink); }
340
+ </style>
341
+ </head>
342
+ <body>
343
+ <main>
344
+ <div class="demo-banner" data-gezel-demo-banner hidden>
345
+ <span><strong>Demo mode</strong> — sample data stays in this page.</span>
346
+ <button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
347
+ </div>
348
+ <p class="note" id="browser-note" hidden>
349
+ Viewing from a browser tab — the board stays live, but moves happen in the app's Output pane.
350
+ </p>
351
+ <div class="banner" id="banner"></div>
352
+ <header>
353
+ <h1>Go</h1>
354
+ <button type="button" class="newgame" id="newgame">New game</button>
355
+ </header>
356
+ <p class="status" id="status">Setting up the board…</p>
357
+ <div class="table">
358
+ <div class="boardwrap"><div class="board" id="board" aria-label="Go board"></div></div>
359
+ <div class="aside">
360
+ <div class="panel move-thought" id="move-thought-panel" hidden>
361
+ <span class="who">Go-speler says</span>
362
+ <span id="move-thought"></span>
363
+ </div>
364
+ <div class="panel game-actions">
365
+ <button type="button" class="pass-button" id="pass">Pass</button>
366
+ <span class="capture-count" id="captures">Captures — 0 / 0</span>
367
+ </div>
368
+ <div class="panel">
369
+ <h2>Moves</h2>
370
+ <ol class="log" id="log"><li>No moves yet — black opens.</li></ol>
371
+ </div>
372
+ </div>
373
+ </div>
374
+ <dialog id="new-game-dialog" aria-labelledby="new-game-title">
375
+ <div class="dialog-content">
376
+ <h2 id="new-game-title">New game</h2>
377
+ <p class="dialog-intro">Choose a board and scoring balance.</p>
378
+ <fieldset>
379
+ <legend>Board size</legend>
380
+ <div class="key-tray" role="radiogroup" aria-label="Board size">
381
+ <button type="button" class="rule-key size-key" role="radio" aria-checked="true" data-size="9">
382
+ <strong>9 × 9</strong><span>Quick</span>
383
+ </button>
384
+ <button type="button" class="rule-key size-key" role="radio" aria-checked="false" data-size="13">
385
+ <strong>13 × 13</strong><span>Medium</span>
386
+ </button>
387
+ <button type="button" class="rule-key size-key" role="radio" aria-checked="false" data-size="19">
388
+ <strong>19 × 19</strong><span>Full</span>
389
+ </button>
390
+ </div>
391
+ </fieldset>
392
+ <fieldset>
393
+ <legend>Komi</legend>
394
+ <div class="key-tray two" role="radiogroup" aria-label="Komi">
395
+ <button type="button" class="rule-key komi-key" role="radio" aria-checked="true" data-komi="6.5">
396
+ <strong>6.5 points</strong><span>Standard balance</span>
397
+ </button>
398
+ <button type="button" class="rule-key komi-key" role="radio" aria-checked="false" data-komi="0">
399
+ <strong>No komi</strong><span>Even score</span>
400
+ </button>
401
+ </div>
402
+ </fieldset>
403
+ <div class="dialog-actions">
404
+ <button type="button" class="dialog-button secondary" id="cancel-new-game">Cancel</button>
405
+ <button type="button" class="dialog-button" id="start-new-game">Start game</button>
406
+ </div>
407
+ </div>
408
+ </dialog>
409
+ </main>
410
+ <script>
411
+ /* gezel-page-demo-stub v1 */
412
+ /*
413
+ * The canonical demo stub for Gezel type pages. MIT License.
414
+ *
415
+ * Paste this file's bytes verbatim into a <script> block ahead of the
416
+ * page's own code, then call makeDemoGezel({ page, tools, data }) with the
417
+ * page's demo handlers. Served by Gezel, the real window.gezel is already
418
+ * injected and the stub steps aside; opened as a raw file (the live demos
419
+ * on gezelgilde.com), it installs a window.gezel whose tools and data are
420
+ * the page-supplied handler maps — one code path in every mode. `tools`
421
+ * and `data` map a tool name / declared read path to a function; data
422
+ * handlers back read() and list(), and url() returns the handler's value
423
+ * or passes a data: URI through. See docs/page-authoring.md.
424
+ */
425
+ (function () {
426
+ 'use strict';
427
+ window.makeDemoGezel = function (opts) {
428
+ if (window.gezel) return window.gezel;
429
+ var page = (opts && opts.page) || {};
430
+ var tools = (opts && opts.tools) || {};
431
+ var data = (opts && opts.data) || {};
432
+ var themeListeners = [];
433
+ var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
434
+ var ui = {
435
+ theme: { mode: media && media.matches ? 'dark' : 'light' },
436
+ onTheme: function (cb) {
437
+ themeListeners.push(cb);
438
+ return function () {
439
+ var at = themeListeners.indexOf(cb);
440
+ if (at >= 0) themeListeners.splice(at, 1);
441
+ };
442
+ },
443
+ };
444
+ if (media && media.addEventListener) {
445
+ media.addEventListener('change', function (ev) {
446
+ ui.theme = { mode: ev.matches ? 'dark' : 'light' };
447
+ themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
448
+ });
449
+ }
450
+ function fail(code, message) {
451
+ var err = new Error(message);
452
+ err.code = code;
453
+ return Promise.reject(err);
454
+ }
455
+ function run(map, key, arg) {
456
+ if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
457
+ return Promise.resolve().then(function () { return map[key](arg); });
458
+ }
459
+ window.gezel = {
460
+ page: {
461
+ api: 1,
462
+ projectId: 'demo',
463
+ source: 'type',
464
+ entry: page.entry || 'index.html',
465
+ typeName: page.typeName || 'demo',
466
+ params: page.params || {},
467
+ mode: 'demo',
468
+ },
469
+ tools: {
470
+ list: function () { return Promise.resolve(Object.keys(tools)); },
471
+ invoke: function (name, input) {
472
+ var call = run(tools, name, input || {});
473
+ if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
474
+ return call.then(function (output) { return { output: output }; });
475
+ },
476
+ },
477
+ data: {
478
+ read: function (path, o) {
479
+ return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
480
+ },
481
+ list: function (path, o) {
482
+ return run(data, path, o || {}) || Promise.resolve([]);
483
+ },
484
+ watch: function () { return function () {}; },
485
+ url: function (path) {
486
+ if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
487
+ return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
488
+ },
489
+ },
490
+ ui: ui,
491
+ refresh: function () {},
492
+ };
493
+ return window.gezel;
494
+ };
495
+ })();
496
+ </script>
497
+ <script>
498
+ (function () {
499
+ 'use strict';
500
+
501
+ var GO_FILES = 'ABCDEFGHJKLMNOPQRST';
502
+ var state = null;
503
+ var busy = false;
504
+ var newBoardSize = 9;
505
+ var newKomi = 6.5;
506
+ var demoGame = null;
507
+
508
+ function pointName(x, y, size) { return GO_FILES[x] + String(size - y); }
509
+ function demoCoords(name, size) { return { x: GO_FILES.indexOf(name[0]), y: size - Number(name.slice(1)) }; }
510
+ function demoNeighbors(x, y, size) {
511
+ return [[x - 1, y], [x + 1, y], [x, y - 1], [x, y + 1]].filter(function (p) {
512
+ return p[0] >= 0 && p[0] < size && p[1] >= 0 && p[1] < size;
513
+ });
514
+ }
515
+ function demoGroup(board, x, y, size) {
516
+ var color = board[y * size + x];
517
+ var seen = {}; var stones = []; var liberties = {};
518
+ var queue = [[x, y]];
519
+ while (queue.length) {
520
+ var p = queue.pop(); var key = p[0] + ',' + p[1];
521
+ if (seen[key]) continue;
522
+ seen[key] = true; stones.push(p);
523
+ demoNeighbors(p[0], p[1], size).forEach(function (n) {
524
+ var ch = board[n[1] * size + n[0]];
525
+ if (ch === '.') liberties[n[0] + ',' + n[1]] = true;
526
+ else if (ch === color && !seen[n[0] + ',' + n[1]]) queue.push(n);
527
+ });
528
+ }
529
+ return { stones: stones, liberties: Object.keys(liberties).length };
530
+ }
531
+ function demoTryMove(game, move) {
532
+ if (move === 'pass') return { squares: game.squares, captures: [] };
533
+ var size = game.boardSize; var at = demoCoords(move, size);
534
+ if (at.x < 0 || at.y < 0 || at.x >= size || at.y >= size) return null;
535
+ var board = game.squares.split('');
536
+ var index = at.y * size + at.x;
537
+ if (board[index] !== '.') return null;
538
+ var color = game.turn === 'user' ? 'X' : 'O';
539
+ var other = color === 'X' ? 'O' : 'X';
540
+ board[index] = color;
541
+ var captures = [];
542
+ demoNeighbors(at.x, at.y, size).forEach(function (n) {
543
+ if (board[n[1] * size + n[0]] !== other) return;
544
+ var group = demoGroup(board, n[0], n[1], size);
545
+ if (group.liberties === 0) group.stones.forEach(function (p) {
546
+ board[p[1] * size + p[0]] = '.';
547
+ captures.push(pointName(p[0], p[1], size));
548
+ });
549
+ });
550
+ if (demoGroup(board, at.x, at.y, size).liberties === 0) return null;
551
+ var squares = board.join('');
552
+ if (game.previousPosition && squares === game.previousPosition) return null;
553
+ return { squares: squares, captures: captures };
554
+ }
555
+ function demoLegalMoves(game) {
556
+ var out = [];
557
+ for (var y = 0; y < game.boardSize; y += 1) for (var x = 0; x < game.boardSize; x += 1) {
558
+ if (game.squares[y * game.boardSize + x] !== '.') continue;
559
+ var name = pointName(x, y, game.boardSize);
560
+ if (demoTryMove(game, name)) out.push(name);
561
+ }
562
+ return out;
563
+ }
564
+ function demoScore(game) {
565
+ var black = game.captures.user; var white = game.captures.ai + game.komi;
566
+ for (var i = 0; i < game.squares.length; i += 1) {
567
+ if (game.squares[i] === 'X') black += 1;
568
+ if (game.squares[i] === 'O') white += 1;
569
+ }
570
+ if (black === white) return { winner: 'draw', margin: 0 };
571
+ return { winner: black > white ? 'user' : 'ai', margin: Math.abs(black - white) };
572
+ }
573
+ function demoNewGame(boardSize, komi) {
574
+ var game = {
575
+ version: 1, boardSize: boardSize, squares: '.'.repeat(boardSize * boardSize),
576
+ turn: 'user', status: 'playing', komi: komi, consecutivePasses: 0,
577
+ previousPosition: null, legalMoves: [], captures: { user: 0, ai: 0 },
578
+ lastMove: null, lastMoveThought: '', moveLog: [], score: null,
579
+ };
580
+ demoGame = game;
581
+ game.legalMoves = demoLegalMoves(game);
582
+ return game;
583
+ }
584
+ function demoUserMove(input) {
585
+ var move = input.move;
586
+ if (move !== 'pass' && !demoGame.legalMoves.includes(move)) throw new Error('That point is not legal in the hot-seat demo.');
587
+ var result = demoTryMove(demoGame, move);
588
+ if (!result) throw new Error('That point is not legal in the hot-seat demo.');
589
+ var by = demoGame.turn;
590
+ var before = demoGame.squares;
591
+ demoGame.squares = result.squares;
592
+ demoGame.previousPosition = before;
593
+ demoGame.captures[by] += result.captures.length;
594
+ demoGame.consecutivePasses = move === 'pass' ? demoGame.consecutivePasses + 1 : 0;
595
+ demoGame.lastMove = { by: by, move: move };
596
+ demoGame.moveLog.push({ by: by, move: move, captures: result.captures });
597
+ demoGame.turn = by === 'user' ? 'ai' : 'user';
598
+ if (demoGame.consecutivePasses >= 2) {
599
+ demoGame.status = 'finished';
600
+ demoGame.score = demoScore(demoGame);
601
+ demoGame.legalMoves = [];
602
+ } else {
603
+ demoGame.legalMoves = demoLegalMoves(demoGame);
604
+ }
605
+ return { status: demoGame.status, turn: demoGame.turn, summary: 'Move played.' };
606
+ }
607
+
608
+ demoNewGame(9, 6.5);
609
+ makeDemoGezel({
610
+ page: { entry: 'board/index.html', typeName: 'Go', params: { personality: 'peppy', playStyle: 'Opponent' } },
611
+ tools: {
612
+ user_move: demoUserMove,
613
+ new_game: function (input) {
614
+ demoNewGame(Number(input.boardSize) || 9, input.komi === undefined ? 6.5 : Number(input.komi));
615
+ return { status: demoGame.status, turn: demoGame.turn, summary: 'New game.' };
616
+ },
617
+ },
618
+ data: {
619
+ 'game.json': function () { return demoGame; },
620
+ },
621
+ });
622
+
623
+ // ── one code path from here: window.gezel exists in every mode ──
624
+ var demoMode = gezel.page.mode === 'demo';
625
+ var browserMode = gezel.page.mode === 'browser';
626
+
627
+ function applyTheme(mode) {
628
+ document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
629
+ }
630
+ applyTheme(gezel.ui.theme.mode);
631
+ gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
632
+
633
+ if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
634
+ if (browserMode) document.getElementById('browser-note').hidden = false;
635
+
636
+ function isStar(x, y, size) {
637
+ var points =
638
+ size === 9 ? [2, 4, 6] :
639
+ size === 13 ? [3, 6, 9] :
640
+ [3, 9, 15];
641
+ if (size === 9) return (x === 4 && y === 4) || (x !== 4 && y !== 4 && points.includes(x) && points.includes(y));
642
+ return points.includes(x) && points.includes(y);
643
+ }
644
+ function setBanner(text) {
645
+ var banner = document.getElementById('banner');
646
+ banner.textContent = text || '';
647
+ banner.className = text ? 'banner show' : 'banner';
648
+ }
649
+ function invokeErrorText(error) {
650
+ if (error && error.code === 'unavailable') {
651
+ return "Moves happen in the app's Output pane — this browser tab stays view-only.";
652
+ }
653
+ return String(error && error.message ? error.message : error);
654
+ }
655
+ function scoreText() {
656
+ if (!state || !state.score) return '';
657
+ if (state.score.winner === 'draw') return 'The final score is even.';
658
+ var color = state.score.winner === 'user' ? 'Black' : 'White';
659
+ return color + ' wins by ' + state.score.margin + ' points.';
660
+ }
661
+ function statusLine() {
662
+ if (!state) return 'Setting up the board…';
663
+ if (state.status !== 'playing') return scoreText() + ' Ready for another game?';
664
+ if (demoMode) return 'Hot seat — ' + (state.turn === 'user' ? 'black' : 'white') + ' to move, or pass.';
665
+ if (busy) return 'Sending your move…';
666
+ if (state.turn === 'ai') return 'Go-speler is thinking…';
667
+ return 'Your move — place black, or pass.';
668
+ }
669
+ function render() {
670
+ var status = document.getElementById('status');
671
+ status.textContent = statusLine();
672
+ status.className =
673
+ state && !demoMode && state.turn === 'ai' && state.status === 'playing'
674
+ ? 'status thinking'
675
+ : 'status';
676
+
677
+ var thoughtPanel = document.getElementById('move-thought-panel');
678
+ if (state && state.lastMoveThought) {
679
+ thoughtPanel.hidden = false;
680
+ document.getElementById('move-thought').textContent = state.lastMoveThought;
681
+ } else {
682
+ thoughtPanel.hidden = true;
683
+ }
684
+ document.getElementById('captures').textContent = state
685
+ ? 'Captures — black ' + state.captures.user + ' / white ' + state.captures.ai
686
+ : 'Captures — 0 / 0';
687
+ document.getElementById('pass').disabled =
688
+ !state || (!demoMode && state.turn !== 'user') || state.status !== 'playing' || busy;
689
+
690
+ var log = document.getElementById('log');
691
+ if (state && state.moveLog.length) {
692
+ log.innerHTML = '';
693
+ state.moveLog.forEach(function (entry, index) {
694
+ var item = document.createElement('li');
695
+ var captures = entry.captures && entry.captures.length
696
+ ? ' (captures ' + entry.captures.length + ')'
697
+ : '';
698
+ item.textContent = (entry.by === 'user' ? 'Black ' : 'White ') + entry.move + captures;
699
+ if (index === state.moveLog.length - 1) item.className = 'latest';
700
+ log.appendChild(item);
701
+ });
702
+ log.scrollTop = log.scrollHeight;
703
+ } else {
704
+ log.innerHTML = '<li>No moves yet — black opens.</li>';
705
+ }
706
+
707
+ var board = document.getElementById('board');
708
+ board.innerHTML = '';
709
+ var size = state ? state.boardSize : 9;
710
+ board.style.setProperty('--board-size', size);
711
+ var legal = {};
712
+ if (state) state.legalMoves.forEach(function (move) { legal[move] = true; });
713
+ var myTurn = state && (demoMode || state.turn === 'user') && state.status === 'playing' && !busy;
714
+ for (var y = 0; y < size; y += 1) {
715
+ for (var x = 0; x < size; x += 1) {
716
+ var name = pointName(x, y, size);
717
+ var stone = state ? state.squares[y * size + x] : '.';
718
+ var point = document.createElement('button');
719
+ point.type = 'button';
720
+ point.className = 'point';
721
+ point.dataset.point = name;
722
+ point.setAttribute('aria-label', name + (stone === '.' ? ', empty' : stone === 'X' ? ', black' : ', white'));
723
+ if (x === 0) point.style.setProperty('--left-edge', '50%');
724
+ if (x === size - 1) point.style.setProperty('--right-edge', '50%');
725
+ if (y === 0) point.style.setProperty('--top-edge', '50%');
726
+ if (y === size - 1) point.style.setProperty('--bottom-edge', '50%');
727
+ if (isStar(x, y, size)) point.className += ' star';
728
+ if (stone !== '.') point.className += ' occupied';
729
+ if (myTurn && legal[name]) point.className += ' legal';
730
+ if (state && state.lastMove && state.lastMove.move === name) point.className += ' last';
731
+
732
+ if (x === 0) {
733
+ var rank = document.createElement('span');
734
+ rank.className = 'coord rank';
735
+ rank.textContent = String(size - y);
736
+ point.appendChild(rank);
737
+ }
738
+ if (y === size - 1) {
739
+ var file = document.createElement('span');
740
+ file.className = 'coord file';
741
+ file.textContent = GO_FILES[x];
742
+ point.appendChild(file);
743
+ }
744
+ if (stone === '.') {
745
+ var dot = document.createElement('span');
746
+ dot.className = 'star-dot';
747
+ point.appendChild(dot);
748
+ } else {
749
+ var piece = document.createElement('span');
750
+ piece.className = 'stone ' + (stone === 'X' ? 'black' : 'white');
751
+ point.appendChild(piece);
752
+ }
753
+ point.addEventListener('click', onPointClick);
754
+ board.appendChild(point);
755
+ }
756
+ }
757
+ }
758
+
759
+ function onPointClick(event) {
760
+ if (!state || (!demoMode && state.turn !== 'user') || state.status !== 'playing' || busy) return;
761
+ var move = event.currentTarget.dataset.point;
762
+ if (!state.legalMoves.includes(move)) return;
763
+ submitMove(move);
764
+ }
765
+ function submitMove(move) {
766
+ busy = true;
767
+ render();
768
+ gezel.tools
769
+ .invoke('user_move', { move: move })
770
+ .then(function (result) {
771
+ busy = false;
772
+ if (
773
+ result.reaction &&
774
+ result.reaction.delivered === false &&
775
+ result.reaction.reason === 'engagement-off'
776
+ ) {
777
+ setBanner('Move played — but AI engagement is off, so the Go-speler cannot reply.');
778
+ }
779
+ return loadGame();
780
+ })
781
+ .catch(function (error) {
782
+ busy = false;
783
+ setBanner(invokeErrorText(error));
784
+ render();
785
+ });
786
+ }
787
+ function syncDialog() {
788
+ document.querySelectorAll('.size-key').forEach(function (button) {
789
+ button.setAttribute('aria-checked', String(Number(button.dataset.size) === newBoardSize));
790
+ });
791
+ document.querySelectorAll('.komi-key').forEach(function (button) {
792
+ button.setAttribute('aria-checked', String(Number(button.dataset.komi) === newKomi));
793
+ });
794
+ }
795
+ function openNewGame() {
796
+ if (busy) return;
797
+ newBoardSize = state ? state.boardSize : 9;
798
+ newKomi = state ? state.komi : 6.5;
799
+ syncDialog();
800
+ document.getElementById('new-game-dialog').showModal();
801
+ }
802
+ function newGame() {
803
+ busy = true;
804
+ render();
805
+ gezel.tools
806
+ .invoke('new_game', { boardSize: newBoardSize, komi: newKomi })
807
+ .then(function () {
808
+ busy = false;
809
+ setBanner('');
810
+ return loadGame();
811
+ })
812
+ .catch(function (error) {
813
+ busy = false;
814
+ setBanner(invokeErrorText(error));
815
+ render();
816
+ });
817
+ }
818
+ function loadGame() {
819
+ return gezel.data.read('game.json').then(
820
+ function (next) {
821
+ if (!next) return;
822
+ state = next;
823
+ render();
824
+ },
825
+ function () {},
826
+ );
827
+ }
828
+
829
+ document.getElementById('pass').addEventListener('click', function () { submitMove('pass'); });
830
+ document.getElementById('newgame').addEventListener('click', openNewGame);
831
+ document.getElementById('cancel-new-game').addEventListener('click', function () {
832
+ document.getElementById('new-game-dialog').close();
833
+ });
834
+ document.getElementById('start-new-game').addEventListener('click', function () {
835
+ document.getElementById('new-game-dialog').close();
836
+ newGame();
837
+ });
838
+ document.querySelectorAll('.size-key').forEach(function (button) {
839
+ button.addEventListener('click', function () {
840
+ newBoardSize = Number(button.dataset.size);
841
+ syncDialog();
842
+ });
843
+ });
844
+ document.querySelectorAll('.komi-key').forEach(function (button) {
845
+ button.addEventListener('click', function () {
846
+ newKomi = Number(button.dataset.komi);
847
+ syncDialog();
848
+ });
849
+ });
850
+ loadGame();
851
+ gezel.data.watch('game.json', function () { loadGame(); });
852
+ })();
853
+ </script>
854
+ </body>
855
+ </html>