@bendyline/gilde 0.1.7 → 0.1.9

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