@bendyline/gilde 0.1.92 → 0.1.94

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 (137) hide show
  1. package/README.md +6 -4
  2. package/authoring/chat-models/underdog-saluki-27b-iq2-mix.json +159 -0
  3. package/data/chat-models/index.json +1 -1
  4. package/data/chat-models/raw-index.json +1 -1
  5. package/data/chat-models/un/underdog-saluki-27b-iq2-mix/manifest.json +164 -0
  6. package/data/chat-models/un/underdog-saluki-27b-iq2-mix/versions/1.0.0/manifest.json +14 -0
  7. package/data/craftbook-templates/br/branding-website/manifest.json +2 -1
  8. package/data/craftbook-templates/br/branding-website/versions/1.1.4/craftbook.json +346 -0
  9. package/data/craftbook-templates/br/branding-website/versions/1.1.4/test.json +164 -0
  10. package/data/craftbook-templates/fr/freeze-scope/versions/1.4.0/craftbook.json +94 -0
  11. package/data/craftbook-templates/fr/freeze-scope/versions/1.4.0/test.json +131 -0
  12. package/data/craftbook-templates/index.json +1 -1
  13. package/data/craftbook-templates/na/narrated-slideshow/manifest.json +2 -1
  14. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.7/craftbook.json +315 -0
  15. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.7/test.json +89 -0
  16. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.8/craftbook.json +323 -0
  17. package/data/craftbook-templates/na/narrated-slideshow/versions/1.1.8/test.json +217 -0
  18. package/data/craftbook-templates/ph/photo-library-nightly/manifest.json +22 -0
  19. package/data/craftbook-templates/ph/photo-library-nightly/versions/1.0.0/craftbook.json +186 -0
  20. package/data/craftbook-templates/ph/photo-library-nightly/versions/1.0.0/test.json +69 -0
  21. package/data/craftbook-templates/po/powerpoint-deck/manifest.json +2 -1
  22. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.17/craftbook.json +486 -0
  23. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.17/test.json +265 -0
  24. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.18/craftbook.json +501 -0
  25. package/data/craftbook-templates/po/powerpoint-deck/versions/1.7.18/test.json +266 -0
  26. package/data/craftbook-templates/raw-index.json +1 -1
  27. package/data/craftbook-templates/re/report-pdf/manifest.json +2 -1
  28. package/data/craftbook-templates/re/report-pdf/versions/1.1.7/craftbook.json +316 -0
  29. package/data/craftbook-templates/re/report-pdf/versions/1.1.7/test.json +101 -0
  30. package/data/craftbook-templates/re/report-pdf/versions/1.1.8/craftbook.json +324 -0
  31. package/data/craftbook-templates/re/report-pdf/versions/1.1.8/test.json +258 -0
  32. package/data/craftbook-templates/re/research-report/manifest.json +2 -1
  33. package/data/craftbook-templates/re/research-report/versions/1.0.4/craftbook.json +286 -0
  34. package/data/craftbook-templates/re/research-report/versions/1.0.4/test.json +183 -0
  35. package/data/craftbook-templates/re/research-to-document/manifest.json +2 -1
  36. package/data/craftbook-templates/re/research-to-document/versions/1.2.7/craftbook.json +387 -0
  37. package/data/craftbook-templates/re/research-to-document/versions/1.2.7/test.json +242 -0
  38. package/data/craftbook-templates/re/research-to-document/versions/1.2.8/craftbook.json +395 -0
  39. package/data/craftbook-templates/re/research-to-document/versions/1.2.8/test.json +254 -0
  40. package/data/gezel-templates/cu/curator/manifest.json +20 -0
  41. package/data/gezel-templates/cu/curator/versions/1.0.0/about.md +22 -0
  42. package/data/gezel-templates/cu/curator/versions/1.0.0/manifest.json +23 -0
  43. package/data/gezel-templates/index.json +1 -1
  44. package/data/gezel-templates/pu/puzzelaar/manifest.json +19 -0
  45. package/data/gezel-templates/pu/puzzelaar/versions/1.0.0/about.md +18 -0
  46. package/data/gezel-templates/pu/puzzelaar/versions/1.0.0/manifest.json +16 -0
  47. package/data/gezel-templates/raw-index.json +1 -1
  48. package/data/gezel-templates/wo/woordsmid/manifest.json +19 -0
  49. package/data/gezel-templates/wo/woordsmid/versions/1.0.0/about.md +18 -0
  50. package/data/gezel-templates/wo/woordsmid/versions/1.0.0/manifest.json +16 -0
  51. package/data/image-models/index.json +1 -1
  52. package/data/image-models/mi/ming-image-0.1-design-bf16/manifest.json +39 -0
  53. package/data/image-models/mi/ming-image-0.1-design-bf16/versions/1.0.0/manifest.json +31 -0
  54. package/data/image-models/raw-index.json +1 -1
  55. package/data/knowledge-catalogs/be/bendyline-how-to-books/manifest.json +26 -0
  56. package/data/knowledge-catalogs/be/bendyline-how-to-books/versions/2026.10.8/manifest.json +8950 -0
  57. package/data/knowledge-catalogs/index.json +1 -1
  58. package/data/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/manifest.json +22 -0
  59. package/data/knowledge-catalogs/mi/microsoftdocs-aspnet-core-en/versions/2026.10.16/manifest.json +18270 -0
  60. package/data/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/manifest.json +23 -0
  61. package/data/knowledge-catalogs/mi/microsoftdocs-azure-ai-platform-en/versions/2026.10.16/manifest.json +1098 -0
  62. package/data/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/manifest.json +23 -0
  63. package/data/knowledge-catalogs/mi/microsoftdocs-azure-apps-compute-en/versions/2026.10.16/manifest.json +722 -0
  64. package/data/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/manifest.json +23 -0
  65. package/data/knowledge-catalogs/mi/microsoftdocs-azure-data-aiot-en/versions/2026.10.16/manifest.json +530 -0
  66. package/data/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/manifest.json +23 -0
  67. package/data/knowledge-catalogs/mi/microsoftdocs-azure-management-hybrid-en/versions/2026.10.16/manifest.json +602 -0
  68. package/data/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/manifest.json +23 -0
  69. package/data/knowledge-catalogs/mi/microsoftdocs-azure-network-security-en/versions/2026.10.16/manifest.json +250 -0
  70. package/data/knowledge-catalogs/mi/microsoftdocs-dotnet-en/manifest.json +22 -0
  71. package/data/knowledge-catalogs/mi/microsoftdocs-dotnet-en/versions/2026.10.16/manifest.json +16794 -0
  72. package/data/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/manifest.json +22 -0
  73. package/data/knowledge-catalogs/mi/microsoftdocs-entity-framework-en/versions/2026.10.16/manifest.json +1106 -0
  74. package/data/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/manifest.json +22 -0
  75. package/data/knowledge-catalogs/mi/microsoftdocs-microsoft-365-en/versions/2026.10.16/manifest.json +234 -0
  76. package/data/knowledge-catalogs/mi/microsoftdocs-powershell-en/manifest.json +22 -0
  77. package/data/knowledge-catalogs/mi/microsoftdocs-powershell-en/versions/2026.10.16/manifest.json +522 -0
  78. package/data/knowledge-catalogs/mi/microsoftdocs-sql-server-en/manifest.json +22 -0
  79. package/data/knowledge-catalogs/mi/microsoftdocs-sql-server-en/versions/2026.10.16/manifest.json +1850 -0
  80. package/data/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/manifest.json +22 -0
  81. package/data/knowledge-catalogs/mi/microsoftdocs-windows-apps-en/versions/2026.10.16/manifest.json +1734 -0
  82. package/data/knowledge-catalogs/mi/microsoftdocs-windows-server-en/manifest.json +22 -0
  83. package/data/knowledge-catalogs/mi/microsoftdocs-windows-server-en/versions/2026.10.16/manifest.json +774 -0
  84. package/data/knowledge-catalogs/raw-index.json +1 -1
  85. package/data/project-types/ch/checkers/versions/1.2.3/about.md +9 -0
  86. package/data/project-types/ch/checkers/versions/1.2.3/game.json +77 -0
  87. package/data/project-types/ch/checkers/versions/1.2.3/manifest.json +182 -0
  88. package/data/project-types/ch/checkers/versions/1.2.3/mission.md +9 -0
  89. package/data/project-types/ch/checkers/versions/1.2.3/pages/board/index.html +1158 -0
  90. package/data/project-types/ch/chess/versions/1.0.6/about.md +9 -0
  91. package/data/project-types/ch/chess/versions/1.0.6/game.json +127 -0
  92. package/data/project-types/ch/chess/versions/1.0.6/manifest.json +196 -0
  93. package/data/project-types/ch/chess/versions/1.0.6/mission.md +8 -0
  94. package/data/project-types/ch/chess/versions/1.0.6/pages/board/index.html +851 -0
  95. package/data/project-types/fi/fitness-coach/versions/1.1.0/about.md +8 -0
  96. package/data/project-types/fi/fitness-coach/versions/1.1.0/craftbooks/training-recap.json +34 -0
  97. package/data/project-types/fi/fitness-coach/versions/1.1.0/manifest.json +229 -0
  98. package/data/project-types/fi/fitness-coach/versions/1.1.0/mission.md +7 -0
  99. package/data/project-types/fi/fitness-coach/versions/1.1.0/pages/dashboard/index.html +383 -0
  100. package/data/project-types/fi/fitness-coach/versions/1.1.0/training.json +6 -0
  101. package/data/project-types/fl/flashcards/versions/1.1.2/about.md +8 -0
  102. package/data/project-types/fl/flashcards/versions/1.1.2/deck.json +7 -0
  103. package/data/project-types/fl/flashcards/versions/1.1.2/manifest.json +169 -0
  104. package/data/project-types/fl/flashcards/versions/1.1.2/mission.md +6 -0
  105. package/data/project-types/fl/flashcards/versions/1.1.2/pages/review/index.html +477 -0
  106. package/data/project-types/fl/flashcards/versions/1.2.0/about.md +8 -0
  107. package/data/project-types/fl/flashcards/versions/1.2.0/deck.json +7 -0
  108. package/data/project-types/fl/flashcards/versions/1.2.0/manifest.json +207 -0
  109. package/data/project-types/fl/flashcards/versions/1.2.0/mission.md +6 -0
  110. package/data/project-types/fl/flashcards/versions/1.2.0/pages/review/index.html +549 -0
  111. package/data/project-types/go/go/versions/1.0.4/about.md +9 -0
  112. package/data/project-types/go/go/versions/1.0.4/game.json +103 -0
  113. package/data/project-types/go/go/versions/1.0.4/manifest.json +182 -0
  114. package/data/project-types/go/go/versions/1.0.4/mission.md +8 -0
  115. package/data/project-types/go/go/versions/1.0.4/pages/board/index.html +855 -0
  116. package/data/project-types/index.json +1 -1
  117. package/data/project-types/la/language-trainer/versions/1.1.0/about.md +8 -0
  118. package/data/project-types/la/language-trainer/versions/1.1.0/manifest.json +245 -0
  119. package/data/project-types/la/language-trainer/versions/1.1.0/mission.md +4 -0
  120. package/data/project-types/la/language-trainer/versions/1.1.0/pages/dashboard/index.html +468 -0
  121. package/data/project-types/la/language-trainer/versions/1.1.0/progress.json +10 -0
  122. package/data/project-types/la/language-trainer/versions/1.1.0/reviews.json +3 -0
  123. package/data/project-types/la/language-trainer/versions/1.1.0/scenarios.json +84 -0
  124. package/data/project-types/raw-index.json +1 -1
  125. package/data/project-types/wo/word-clues/manifest.json +20 -0
  126. package/data/project-types/wo/word-clues/versions/1.0.0/about.md +7 -0
  127. package/data/project-types/wo/word-clues/versions/1.0.0/game.json +14 -0
  128. package/data/project-types/wo/word-clues/versions/1.0.0/manifest.json +205 -0
  129. package/data/project-types/wo/word-clues/versions/1.0.0/mission.md +3 -0
  130. package/data/project-types/wo/word-clues/versions/1.0.0/pages/board/index.html +399 -0
  131. package/data/project-types/wo/word-clues/versions/1.0.0/words.json +216 -0
  132. package/package.json +5 -3
  133. package/schemas/craftbook-doc.schema.json +14 -0
  134. package/schemas/craftbook-template-version.schema.json +14 -0
  135. package/schemas/craftbook-test.schema.json +4 -1
  136. package/schemas/image-model-version.schema.json +2 -1
  137. package/schemas/project-type-version.schema.json +131 -0
@@ -0,0 +1,477 @@
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>Flashcards</title>
8
+ <style>
9
+ :root {
10
+ color-scheme: light dark;
11
+ --bg: #faf7f2;
12
+ --card: #fff;
13
+ --ink: #2b2620;
14
+ --muted: #7a7166;
15
+ --accent: #b5651d;
16
+ --track: #ece5da;
17
+ --good: #4a7c59;
18
+ --warn: #a4552e;
19
+ }
20
+ :root[data-theme='dark'] {
21
+ --bg: #1c1a17;
22
+ --card: #262320;
23
+ --ink: #efe9e0;
24
+ --muted: #a59a8c;
25
+ --accent: #e0894a;
26
+ --track: #3a352f;
27
+ --good: #7fae8d;
28
+ --warn: #d98a63;
29
+ }
30
+ @media (prefers-color-scheme: dark) {
31
+ :root:not([data-theme='light']) {
32
+ --bg: #1c1a17;
33
+ --card: #262320;
34
+ --ink: #efe9e0;
35
+ --muted: #a59a8c;
36
+ --accent: #e0894a;
37
+ --track: #3a352f;
38
+ --good: #7fae8d;
39
+ --warn: #d98a63;
40
+ }
41
+ }
42
+ * { box-sizing: border-box; }
43
+ body {
44
+ margin: 0;
45
+ font: 15px/1.5 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
46
+ background: var(--bg);
47
+ color: var(--ink);
48
+ padding: 1.5rem 1.25rem;
49
+ }
50
+ main { max-width: 560px; margin: 0 auto; }
51
+ h1 {
52
+ font-family: Georgia, 'PT Serif', 'Times New Roman', serif;
53
+ font-size: 1.35rem;
54
+ font-weight: 600;
55
+ margin: 0 0 0.15rem;
56
+ }
57
+ .sub { color: var(--muted); font-size: 0.9rem; margin: 0 0 1rem; }
58
+ .dots { display: flex; gap: 0.3rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
59
+ .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--track); }
60
+ .dot.done-good { background: var(--good); }
61
+ .dot.done-miss { background: var(--warn); }
62
+ .dot.current { background: var(--accent); }
63
+ .flashcard {
64
+ background: var(--card);
65
+ border: 1px solid var(--track);
66
+ border-radius: 10px;
67
+ min-height: 200px;
68
+ display: grid;
69
+ place-items: center;
70
+ text-align: center;
71
+ padding: 1.5rem;
72
+ font-size: 1.15rem;
73
+ cursor: pointer;
74
+ user-select: none;
75
+ }
76
+ .flashcard .face-label {
77
+ display: block;
78
+ font-size: 0.7rem;
79
+ text-transform: uppercase;
80
+ letter-spacing: 0.05em;
81
+ color: var(--muted);
82
+ margin-bottom: 0.6rem;
83
+ }
84
+ .actions { display: flex; gap: 0.6rem; margin-top: 0.85rem; justify-content: center; }
85
+ .actions button {
86
+ border: 1px solid var(--track);
87
+ background: var(--card);
88
+ color: var(--ink);
89
+ border-radius: 6px;
90
+ padding: 0.45rem 1.1rem;
91
+ min-height: 44px;
92
+ font: inherit;
93
+ cursor: pointer;
94
+ }
95
+ .actions button.got { border-color: var(--good); color: var(--good); }
96
+ .actions button.miss { border-color: var(--warn); color: var(--warn); }
97
+ .actions button:disabled { opacity: 0.5; cursor: default; }
98
+ .empty, .done {
99
+ background: var(--card);
100
+ border: 1px dashed var(--track);
101
+ border-radius: 10px;
102
+ padding: 1.5rem;
103
+ text-align: center;
104
+ color: var(--muted);
105
+ }
106
+ .banner {
107
+ border: 1px solid var(--accent);
108
+ border-radius: 6px;
109
+ padding: 0.45rem 0.7rem;
110
+ font-size: 0.85rem;
111
+ margin-bottom: 0.75rem;
112
+ display: none;
113
+ }
114
+ .banner.show { display: block; }
115
+ .hint { font-size: 0.78rem; color: var(--muted); text-align: center; margin-top: 0.5rem; }
116
+ .note {
117
+ margin: 0 0 0.85rem;
118
+ padding: 0.5rem 0.65rem;
119
+ border: 1px solid var(--track);
120
+ border-radius: 6px;
121
+ background: var(--card);
122
+ color: var(--muted);
123
+ font-size: 0.8rem;
124
+ }
125
+ .demo-banner {
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: space-between;
129
+ gap: 0.75rem;
130
+ margin: 0 0 0.85rem;
131
+ padding: 0.5rem 0.65rem;
132
+ border: 1px solid var(--accent);
133
+ border-radius: 6px;
134
+ background: color-mix(in srgb, var(--card) 88%, var(--accent));
135
+ color: var(--ink);
136
+ font-size: 0.8rem;
137
+ }
138
+ .demo-banner[hidden] { display: none; }
139
+ .demo-banner strong { font-weight: 700; }
140
+ .demo-reset {
141
+ flex: none;
142
+ padding: 0.25rem 0.5rem;
143
+ border: 1px solid var(--accent);
144
+ border-radius: 6px;
145
+ background: var(--card);
146
+ color: var(--ink);
147
+ cursor: pointer;
148
+ font: inherit;
149
+ }
150
+ .demo-reset:hover { border-color: var(--ink); }
151
+ </style>
152
+ </head>
153
+ <body>
154
+ <main>
155
+ <div class="demo-banner" data-gezel-demo-banner hidden>
156
+ <span><strong>Demo mode</strong> — sample data stays in this page.</span>
157
+ <button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
158
+ </div>
159
+ <p class="note" id="browser-note" hidden>
160
+ Viewing from a browser tab — the deck stays live, but reviews happen in the app's Output pane.
161
+ </p>
162
+ <div class="banner" id="banner"></div>
163
+ <h1 id="title">Flashcards</h1>
164
+ <p class="sub" id="sub">Loading the deck…</p>
165
+ <div class="dots" id="dots"></div>
166
+ <div id="stage"></div>
167
+ <p class="hint" id="hint"></p>
168
+ </main>
169
+ <script>
170
+ /* gezel-page-demo-stub v1 */
171
+ /*
172
+ * The canonical demo stub for Gezel type pages. MIT License.
173
+ *
174
+ * Paste this file's bytes verbatim into a <script> block ahead of the
175
+ * page's own code, then call makeDemoGezel({ page, tools, data }) with the
176
+ * page's demo handlers. Served by Gezel, the real window.gezel is already
177
+ * injected and the stub steps aside; opened as a raw file (the live demos
178
+ * on gezelgilde.com), it installs a window.gezel whose tools and data are
179
+ * the page-supplied handler maps — one code path in every mode. `tools`
180
+ * and `data` map a tool name / declared read path to a function; data
181
+ * handlers back read() and list(), and url() returns the handler's value
182
+ * or passes a data: URI through. See docs/page-authoring.md.
183
+ */
184
+ (function () {
185
+ 'use strict';
186
+ window.makeDemoGezel = function (opts) {
187
+ if (window.gezel) return window.gezel;
188
+ var page = (opts && opts.page) || {};
189
+ var tools = (opts && opts.tools) || {};
190
+ var data = (opts && opts.data) || {};
191
+ var themeListeners = [];
192
+ var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
193
+ var ui = {
194
+ theme: { mode: media && media.matches ? 'dark' : 'light' },
195
+ onTheme: function (cb) {
196
+ themeListeners.push(cb);
197
+ return function () {
198
+ var at = themeListeners.indexOf(cb);
199
+ if (at >= 0) themeListeners.splice(at, 1);
200
+ };
201
+ },
202
+ };
203
+ if (media && media.addEventListener) {
204
+ media.addEventListener('change', function (ev) {
205
+ ui.theme = { mode: ev.matches ? 'dark' : 'light' };
206
+ themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
207
+ });
208
+ }
209
+ function fail(code, message) {
210
+ var err = new Error(message);
211
+ err.code = code;
212
+ return Promise.reject(err);
213
+ }
214
+ function run(map, key, arg) {
215
+ if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
216
+ return Promise.resolve().then(function () { return map[key](arg); });
217
+ }
218
+ window.gezel = {
219
+ page: {
220
+ api: 1,
221
+ projectId: 'demo',
222
+ source: 'type',
223
+ entry: page.entry || 'index.html',
224
+ typeName: page.typeName || 'demo',
225
+ params: page.params || {},
226
+ mode: 'demo',
227
+ },
228
+ tools: {
229
+ list: function () { return Promise.resolve(Object.keys(tools)); },
230
+ invoke: function (name, input) {
231
+ var call = run(tools, name, input || {});
232
+ if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
233
+ return call.then(function (output) { return { output: output }; });
234
+ },
235
+ },
236
+ data: {
237
+ read: function (path, o) {
238
+ return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
239
+ },
240
+ list: function (path, o) {
241
+ return run(data, path, o || {}) || Promise.resolve([]);
242
+ },
243
+ watch: function () { return function () {}; },
244
+ url: function (path) {
245
+ if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
246
+ return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
247
+ },
248
+ },
249
+ ui: ui,
250
+ refresh: function () {},
251
+ };
252
+ return window.gezel;
253
+ };
254
+ })();
255
+ </script>
256
+ <script>
257
+ (function () {
258
+ 'use strict';
259
+
260
+ // ── demo deck (used only when the page opens as a raw file) ──
261
+ var demoDeck = {
262
+ subject: 'Dutch for a market morning',
263
+ cards: [
264
+ { id: 'card-1', front: 'Hoeveel kost dit?', back: 'How much does this cost?' },
265
+ { id: 'card-2', front: 'een bos bloemen', back: 'a bunch of flowers' },
266
+ { id: 'card-3', front: 'Mag ik pinnen?', back: 'Can I pay by card?' },
267
+ { id: 'card-4', front: 'Dat is alles, dank u.', back: 'That is all, thank you.' },
268
+ ],
269
+ dueCardIds: ['card-1', 'card-2', 'card-3', 'card-4'],
270
+ };
271
+ function demoCard(id) {
272
+ return demoDeck.cards.find(function (c) { return c.id === id; }) || null;
273
+ }
274
+
275
+ makeDemoGezel({
276
+ page: { entry: 'review/index.html', typeName: 'Flashcards' },
277
+ tools: {
278
+ record_review: function (input) {
279
+ var card = demoCard(input && input.cardId);
280
+ if (!card) throw new Error("No card '" + (input && input.cardId) + "' in the demo deck.");
281
+ // The demo deck stays due so the session can be replayed;
282
+ // Reset demo restores everything else.
283
+ return { summary: card.id + ' marked ' + input.verdict + '.' };
284
+ },
285
+ finish_session: function (input) {
286
+ var reviewed = (input && input.reviewed) || 0;
287
+ var missed = ((input && input.missedIds) || []).length;
288
+ return { summary: 'Reviewed ' + reviewed + ' card(s): ' + (reviewed - missed) + ' got it, ' + missed + ' missed.' };
289
+ },
290
+ },
291
+ data: {
292
+ 'deck.json': function () { return demoDeck; },
293
+ },
294
+ });
295
+
296
+ // ── one code path from here: window.gezel exists in every mode ──
297
+ var demoMode = gezel.page.mode === 'demo';
298
+ var browserMode = gezel.page.mode === 'browser';
299
+
300
+ function applyTheme(mode) {
301
+ document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
302
+ }
303
+ applyTheme(gezel.ui.theme.mode);
304
+ gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
305
+
306
+ if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
307
+ if (browserMode) document.getElementById('browser-note').hidden = false;
308
+
309
+ var deck = null;
310
+ // The session queue is snapshotted from dueCardIds at start so a
311
+ // background change never reshuffles cards mid-session.
312
+ var queue = null;
313
+ var idx = 0;
314
+ var flipped = false;
315
+ var results = [];
316
+ var busy = false;
317
+
318
+ function esc(s) {
319
+ return String(s).replace(/[&<>"]/g, function (c) {
320
+ return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c];
321
+ });
322
+ }
323
+ function setBanner(text) {
324
+ var el = document.getElementById('banner');
325
+ el.textContent = text || '';
326
+ el.className = text ? 'banner show' : 'banner';
327
+ }
328
+ function invokeErrorText(err) {
329
+ if (err && err.code === 'unavailable') {
330
+ return "Reviews happen in the app's Output pane — this browser tab stays view-only.";
331
+ }
332
+ return String(err && err.message ? err.message : err);
333
+ }
334
+ function cardById(id) {
335
+ return (deck.cards || []).find(function (c) { return c.id === id; }) || null;
336
+ }
337
+
338
+ function render() {
339
+ var stage = document.getElementById('stage');
340
+ var dots = document.getElementById('dots');
341
+ var hint = document.getElementById('hint');
342
+ if (!deck) return;
343
+ if (deck.subject) document.getElementById('title').textContent = 'Flashcards: ' + deck.subject;
344
+
345
+ if (!deck.cards || deck.cards.length === 0) {
346
+ document.getElementById('sub').textContent = 'The deck is empty.';
347
+ dots.innerHTML = '';
348
+ hint.textContent = '';
349
+ stage.innerHTML =
350
+ '<div class="empty">No cards yet — ask your Studiemaat to write the first ones.</div>';
351
+ return;
352
+ }
353
+
354
+ if (queue === null || (idx >= queue.length && queue.length === 0)) {
355
+ document.getElementById('sub').textContent =
356
+ deck.dueCardIds.length > 0
357
+ ? deck.dueCardIds.length + ' card(s) due.'
358
+ : 'Nothing due right now.';
359
+ dots.innerHTML = '';
360
+ hint.textContent = '';
361
+ stage.innerHTML =
362
+ deck.dueCardIds.length > 0
363
+ ? '<div class="actions"><button type="button" id="start">Start review (' + deck.dueCardIds.length + ')</button></div>'
364
+ : '<div class="done">All caught up — nothing due. Your Studiemaat can always add more.</div>';
365
+ var startBtn = document.getElementById('start');
366
+ if (startBtn) startBtn.addEventListener('click', startSession);
367
+ return;
368
+ }
369
+
370
+ if (idx >= queue.length) {
371
+ var missed = results.filter(function (r) { return r.verdict === 'missed'; }).length;
372
+ document.getElementById('sub').textContent =
373
+ 'Session done: ' + (results.length - missed) + ' got it, ' + missed + ' missed.';
374
+ hint.textContent = '';
375
+ stage.innerHTML = '<div class="done">Nice work — your Studiemaat has the recap.</div>';
376
+ return;
377
+ }
378
+
379
+ var card = cardById(queue[idx]);
380
+ if (!card) {
381
+ idx += 1;
382
+ render();
383
+ return;
384
+ }
385
+ document.getElementById('sub').textContent = 'Card ' + (idx + 1) + ' of ' + queue.length;
386
+ dots.innerHTML = queue
387
+ .map(function (id, i) {
388
+ if (i === idx) return '<span class="dot current"></span>';
389
+ var r = results[i];
390
+ if (!r) return '<span class="dot"></span>';
391
+ return '<span class="dot ' + (r.verdict === 'missed' ? 'done-miss' : 'done-good') + '"></span>';
392
+ })
393
+ .join('');
394
+ stage.innerHTML =
395
+ '<div class="flashcard" id="card"><div><span class="face-label">' +
396
+ (flipped ? 'Back' : 'Front') + '</span>' + esc(flipped ? card.back : card.front) +
397
+ '</div></div>' +
398
+ (flipped
399
+ ? '<div class="actions">' +
400
+ '<button type="button" class="got" id="got"' + (busy ? ' disabled' : '') + '>Got it</button>' +
401
+ '<button type="button" class="miss" id="miss"' + (busy ? ' disabled' : '') + '>Missed</button>' +
402
+ '</div>'
403
+ : '');
404
+ hint.textContent = flipped ? '' : 'Tap the card to reveal the answer.';
405
+ document.getElementById('card').addEventListener('click', function () {
406
+ if (!flipped) {
407
+ flipped = true;
408
+ render();
409
+ }
410
+ });
411
+ if (flipped) {
412
+ document.getElementById('got').addEventListener('click', function () { grade('got-it'); });
413
+ document.getElementById('miss').addEventListener('click', function () { grade('missed'); });
414
+ }
415
+ }
416
+
417
+ function startSession() {
418
+ queue = deck.dueCardIds.slice();
419
+ idx = 0;
420
+ flipped = false;
421
+ results = [];
422
+ render();
423
+ }
424
+
425
+ function grade(verdict) {
426
+ if (busy) return;
427
+ busy = true;
428
+ render();
429
+ var cardId = queue[idx];
430
+ gezel.tools
431
+ .invoke('record_review', { cardId: cardId, verdict: verdict })
432
+ .then(function () {
433
+ busy = false;
434
+ results[idx] = { cardId: cardId, verdict: verdict };
435
+ idx += 1;
436
+ flipped = false;
437
+ if (idx >= queue.length) return finishSession();
438
+ render();
439
+ })
440
+ .catch(function (err) {
441
+ busy = false;
442
+ setBanner(invokeErrorText(err));
443
+ render();
444
+ });
445
+ }
446
+
447
+ function finishSession() {
448
+ var missedIds = results
449
+ .filter(function (r) { return r.verdict === 'missed'; })
450
+ .map(function (r) { return r.cardId; });
451
+ return gezel.tools
452
+ .invoke('finish_session', { reviewed: results.length, missedIds: missedIds })
453
+ .catch(function () {})
454
+ .then(function () {
455
+ render();
456
+ return loadDeck();
457
+ });
458
+ }
459
+
460
+ function loadDeck() {
461
+ return gezel.data
462
+ .read('deck.json')
463
+ .then(function (next) {
464
+ deck = next;
465
+ // Only re-render outside an active session — mid-session the
466
+ // snapshot queue drives the UI.
467
+ if (queue === null || idx >= queue.length) render();
468
+ })
469
+ .catch(function () {});
470
+ }
471
+
472
+ loadDeck();
473
+ gezel.data.watch('deck.json', function () { loadDeck(); });
474
+ })();
475
+ </script>
476
+ </body>
477
+ </html>
@@ -0,0 +1,8 @@
1
+ A spaced-repetition practice bench for **{{subject}}**.
2
+
3
+ Your Studiemaat writes the cards — ask for them in chat, or let them propose
4
+ cards when a weak spot shows. You review in the **Output** tab: flip the card,
5
+ mark it got-it or missed, and the deck's schedule decides when each card comes
6
+ back (daily, every three days, weekly, as you prove them).
7
+
8
+ After each session your Studiemaat checks in on how it went.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 1,
3
+ "subject": "{{subject}}",
4
+ "cards": [],
5
+ "dueCardIds": [],
6
+ "updatedAt": "2026-07-18T00:00:00.000Z"
7
+ }