@bendyline/gilde 0.1.88 → 0.1.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/data/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/manifest.json +24 -0
  2. package/data/knowledge-catalogs/ca/caselaw-us-300-399-part-0001/versions/2026.10.2/manifest.json +182 -0
  3. package/data/knowledge-catalogs/ca/caselaw-utah-part-0001/manifest.json +24 -0
  4. package/data/knowledge-catalogs/ca/caselaw-utah-part-0001/versions/2026.10.1/manifest.json +558 -0
  5. package/data/knowledge-catalogs/ca/caselaw-utah-part-0002/manifest.json +24 -0
  6. package/data/knowledge-catalogs/ca/caselaw-utah-part-0002/versions/2026.10.1/manifest.json +258 -0
  7. package/data/knowledge-catalogs/index.json +1 -1
  8. package/data/knowledge-catalogs/raw-index.json +1 -1
  9. package/data/knowledge-catalogs/re/regional-american-south/art.json +12 -0
  10. package/data/knowledge-catalogs/re/regional-american-south/logo.webp +0 -0
  11. package/data/knowledge-catalogs/re/regional-american-south/manifest.json +48 -0
  12. package/data/knowledge-catalogs/re/regional-american-south/versions/2026.10.3/manifest.json +133 -0
  13. package/data/knowledge-catalogs/re/regional-andes-amazon/art.json +12 -0
  14. package/data/knowledge-catalogs/re/regional-andes-amazon/logo.webp +0 -0
  15. package/data/knowledge-catalogs/re/regional-andes-amazon/manifest.json +48 -0
  16. package/data/knowledge-catalogs/re/regional-andes-amazon/versions/2026.10.3/manifest.json +133 -0
  17. package/data/knowledge-catalogs/re/regional-australia-nz/art.json +12 -0
  18. package/data/knowledge-catalogs/re/regional-australia-nz/logo.webp +0 -0
  19. package/data/knowledge-catalogs/re/regional-australia-nz/manifest.json +48 -0
  20. package/data/knowledge-catalogs/re/regional-australia-nz/versions/2026.10.3/manifest.json +133 -0
  21. package/data/knowledge-catalogs/re/regional-brazil/art.json +12 -0
  22. package/data/knowledge-catalogs/re/regional-brazil/logo.webp +0 -0
  23. package/data/knowledge-catalogs/re/regional-brazil/manifest.json +48 -0
  24. package/data/knowledge-catalogs/re/regional-brazil/versions/2026.10.3/manifest.json +133 -0
  25. package/data/knowledge-catalogs/re/regional-britain-ireland/art.json +12 -0
  26. package/data/knowledge-catalogs/re/regional-britain-ireland/logo.webp +0 -0
  27. package/data/knowledge-catalogs/re/regional-britain-ireland/manifest.json +48 -0
  28. package/data/knowledge-catalogs/re/regional-britain-ireland/versions/2026.10.3/manifest.json +133 -0
  29. package/data/knowledge-catalogs/re/regional-california-southwest/art.json +12 -0
  30. package/data/knowledge-catalogs/re/regional-california-southwest/logo.webp +0 -0
  31. package/data/knowledge-catalogs/re/regional-california-southwest/manifest.json +48 -0
  32. package/data/knowledge-catalogs/re/regional-california-southwest/versions/2026.10.3/manifest.json +133 -0
  33. package/data/knowledge-catalogs/re/regional-canada-alaska/art.json +12 -0
  34. package/data/knowledge-catalogs/re/regional-canada-alaska/logo.webp +0 -0
  35. package/data/knowledge-catalogs/re/regional-canada-alaska/manifest.json +48 -0
  36. package/data/knowledge-catalogs/re/regional-canada-alaska/versions/2026.10.3/manifest.json +133 -0
  37. package/data/knowledge-catalogs/re/regional-central-eastern-europe/art.json +12 -0
  38. package/data/knowledge-catalogs/re/regional-central-eastern-europe/logo.webp +0 -0
  39. package/data/knowledge-catalogs/re/regional-central-eastern-europe/manifest.json +48 -0
  40. package/data/knowledge-catalogs/re/regional-central-eastern-europe/versions/2026.10.3/manifest.json +133 -0
  41. package/data/knowledge-catalogs/re/regional-east-asia/art.json +12 -0
  42. package/data/knowledge-catalogs/re/regional-east-asia/logo.webp +0 -0
  43. package/data/knowledge-catalogs/re/regional-east-asia/manifest.json +48 -0
  44. package/data/knowledge-catalogs/re/regional-east-asia/versions/2026.10.3/manifest.json +133 -0
  45. package/data/knowledge-catalogs/re/regional-east-southern-africa/art.json +12 -0
  46. package/data/knowledge-catalogs/re/regional-east-southern-africa/logo.webp +0 -0
  47. package/data/knowledge-catalogs/re/regional-east-southern-africa/manifest.json +48 -0
  48. package/data/knowledge-catalogs/re/regional-east-southern-africa/versions/2026.10.3/manifest.json +133 -0
  49. package/data/knowledge-catalogs/re/regional-mediterranean/art.json +12 -0
  50. package/data/knowledge-catalogs/re/regional-mediterranean/logo.webp +0 -0
  51. package/data/knowledge-catalogs/re/regional-mediterranean/manifest.json +48 -0
  52. package/data/knowledge-catalogs/re/regional-mediterranean/versions/2026.10.3/manifest.json +133 -0
  53. package/data/knowledge-catalogs/re/regional-mexico-central-america/art.json +12 -0
  54. package/data/knowledge-catalogs/re/regional-mexico-central-america/logo.webp +0 -0
  55. package/data/knowledge-catalogs/re/regional-mexico-central-america/manifest.json +48 -0
  56. package/data/knowledge-catalogs/re/regional-mexico-central-america/versions/2026.10.3/manifest.json +133 -0
  57. package/data/knowledge-catalogs/re/regional-middle-east/art.json +12 -0
  58. package/data/knowledge-catalogs/re/regional-middle-east/logo.webp +0 -0
  59. package/data/knowledge-catalogs/re/regional-middle-east/manifest.json +48 -0
  60. package/data/knowledge-catalogs/re/regional-middle-east/versions/2026.10.3/manifest.json +133 -0
  61. package/data/knowledge-catalogs/re/regional-midwest/art.json +12 -0
  62. package/data/knowledge-catalogs/re/regional-midwest/logo.webp +0 -0
  63. package/data/knowledge-catalogs/re/regional-midwest/manifest.json +48 -0
  64. package/data/knowledge-catalogs/re/regional-midwest/versions/2026.10.3/manifest.json +133 -0
  65. package/data/knowledge-catalogs/re/regional-north-africa/art.json +12 -0
  66. package/data/knowledge-catalogs/re/regional-north-africa/logo.webp +0 -0
  67. package/data/knowledge-catalogs/re/regional-north-africa/manifest.json +48 -0
  68. package/data/knowledge-catalogs/re/regional-north-africa/versions/2026.10.3/manifest.json +133 -0
  69. package/data/knowledge-catalogs/re/regional-northeast/art.json +12 -0
  70. package/data/knowledge-catalogs/re/regional-northeast/logo.webp +0 -0
  71. package/data/knowledge-catalogs/re/regional-northeast/manifest.json +48 -0
  72. package/data/knowledge-catalogs/re/regional-northeast/versions/2026.10.3/manifest.json +133 -0
  73. package/data/knowledge-catalogs/re/regional-northern-eurasia/art.json +12 -0
  74. package/data/knowledge-catalogs/re/regional-northern-eurasia/logo.webp +0 -0
  75. package/data/knowledge-catalogs/re/regional-northern-eurasia/manifest.json +48 -0
  76. package/data/knowledge-catalogs/re/regional-northern-eurasia/versions/2026.10.3/manifest.json +133 -0
  77. package/data/knowledge-catalogs/re/regional-northern-europe/art.json +12 -0
  78. package/data/knowledge-catalogs/re/regional-northern-europe/logo.webp +0 -0
  79. package/data/knowledge-catalogs/re/regional-northern-europe/manifest.json +48 -0
  80. package/data/knowledge-catalogs/re/regional-northern-europe/versions/2026.10.3/manifest.json +133 -0
  81. package/data/knowledge-catalogs/re/regional-pacific-islands/art.json +12 -0
  82. package/data/knowledge-catalogs/re/regional-pacific-islands/logo.webp +0 -0
  83. package/data/knowledge-catalogs/re/regional-pacific-islands/manifest.json +48 -0
  84. package/data/knowledge-catalogs/re/regional-pacific-islands/versions/2026.10.3/manifest.json +133 -0
  85. package/data/knowledge-catalogs/re/regional-pacific-northwest/art.json +12 -0
  86. package/data/knowledge-catalogs/re/regional-pacific-northwest/logo.webp +0 -0
  87. package/data/knowledge-catalogs/re/regional-pacific-northwest/manifest.json +48 -0
  88. package/data/knowledge-catalogs/re/regional-pacific-northwest/versions/2026.10.3/manifest.json +133 -0
  89. package/data/knowledge-catalogs/re/regional-rockies-plains/art.json +12 -0
  90. package/data/knowledge-catalogs/re/regional-rockies-plains/logo.webp +0 -0
  91. package/data/knowledge-catalogs/re/regional-rockies-plains/manifest.json +48 -0
  92. package/data/knowledge-catalogs/re/regional-rockies-plains/versions/2026.10.3/manifest.json +133 -0
  93. package/data/knowledge-catalogs/re/regional-south-asia/art.json +12 -0
  94. package/data/knowledge-catalogs/re/regional-south-asia/logo.webp +0 -0
  95. package/data/knowledge-catalogs/re/regional-south-asia/manifest.json +48 -0
  96. package/data/knowledge-catalogs/re/regional-south-asia/versions/2026.10.3/manifest.json +133 -0
  97. package/data/knowledge-catalogs/re/regional-southeast-asia/art.json +12 -0
  98. package/data/knowledge-catalogs/re/regional-southeast-asia/logo.webp +0 -0
  99. package/data/knowledge-catalogs/re/regional-southeast-asia/manifest.json +48 -0
  100. package/data/knowledge-catalogs/re/regional-southeast-asia/versions/2026.10.3/manifest.json +133 -0
  101. package/data/knowledge-catalogs/re/regional-southern-cone/art.json +12 -0
  102. package/data/knowledge-catalogs/re/regional-southern-cone/logo.webp +0 -0
  103. package/data/knowledge-catalogs/re/regional-southern-cone/manifest.json +48 -0
  104. package/data/knowledge-catalogs/re/regional-southern-cone/versions/2026.10.3/manifest.json +133 -0
  105. package/data/knowledge-catalogs/re/regional-west-central-africa/art.json +12 -0
  106. package/data/knowledge-catalogs/re/regional-west-central-africa/logo.webp +0 -0
  107. package/data/knowledge-catalogs/re/regional-west-central-africa/manifest.json +48 -0
  108. package/data/knowledge-catalogs/re/regional-west-central-africa/versions/2026.10.3/manifest.json +133 -0
  109. package/data/knowledge-catalogs/re/regional-western-europe/art.json +12 -0
  110. package/data/knowledge-catalogs/re/regional-western-europe/logo.webp +0 -0
  111. package/data/knowledge-catalogs/re/regional-western-europe/manifest.json +48 -0
  112. package/data/knowledge-catalogs/re/regional-western-europe/versions/2026.10.3/manifest.json +133 -0
  113. package/data/project-types/ch/checkers/versions/1.2.2/about.md +9 -0
  114. package/data/project-types/ch/checkers/versions/1.2.2/game.json +77 -0
  115. package/data/project-types/ch/checkers/versions/1.2.2/manifest.json +181 -0
  116. package/data/project-types/ch/checkers/versions/1.2.2/mission.md +9 -0
  117. package/data/project-types/ch/checkers/versions/1.2.2/pages/board/index.html +1158 -0
  118. package/data/project-types/ch/chess/versions/1.0.5/about.md +9 -0
  119. package/data/project-types/ch/chess/versions/1.0.5/game.json +127 -0
  120. package/data/project-types/ch/chess/versions/1.0.5/manifest.json +195 -0
  121. package/data/project-types/ch/chess/versions/1.0.5/mission.md +8 -0
  122. package/data/project-types/ch/chess/versions/1.0.5/pages/board/index.html +851 -0
  123. package/data/project-types/fi/fitness-coach/versions/1.0.3/about.md +7 -0
  124. package/data/project-types/fi/fitness-coach/versions/1.0.3/manifest.json +127 -0
  125. package/data/project-types/fi/fitness-coach/versions/1.0.3/mission.md +7 -0
  126. package/data/project-types/fi/fitness-coach/versions/1.0.3/pages/dashboard/index.html +273 -0
  127. package/data/project-types/fi/fitness-coach/versions/1.0.3/training.json +6 -0
  128. package/data/project-types/go/go/versions/1.0.3/about.md +9 -0
  129. package/data/project-types/go/go/versions/1.0.3/game.json +103 -0
  130. package/data/project-types/go/go/versions/1.0.3/manifest.json +181 -0
  131. package/data/project-types/go/go/versions/1.0.3/mission.md +8 -0
  132. package/data/project-types/go/go/versions/1.0.3/pages/board/index.html +855 -0
  133. package/data/project-types/index.json +1 -1
  134. package/data/project-types/la/language-trainer/versions/1.0.3/about.md +6 -0
  135. package/data/project-types/la/language-trainer/versions/1.0.3/manifest.json +81 -0
  136. package/data/project-types/la/language-trainer/versions/1.0.3/mission.md +4 -0
  137. package/data/project-types/la/language-trainer/versions/1.0.3/pages/dashboard/index.html +268 -0
  138. package/data/project-types/la/language-trainer/versions/1.0.3/progress.json +7 -0
  139. package/data/project-types/raw-index.json +1 -1
  140. package/package.json +1 -1
  141. package/schemas/craftbook-doc.schema.json +59 -0
  142. package/schemas/craftbook-template-version.schema.json +59 -0
  143. package/schemas/knowledge-catalog-version.schema.json +1 -0
@@ -0,0 +1,6 @@
1
+ A project for learning **{{language}}** by actually speaking it. Your tutor chats with
2
+ you in {{language}}, meets you at your level, corrects mistakes gently, and keeps track of
3
+ how far you've come.
4
+
5
+ Open the **Output** tab any time to see your progress dashboard — your current level,
6
+ practice sessions, and streak. The more you show up and talk, the further it moves.
@@ -0,0 +1,81 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "version": "1.0.3",
4
+ "releasedAt": "2026-10-07T00:00:00Z",
5
+ "extends": "content-writing",
6
+ "meesterManaged": false,
7
+ "leanProfile": true,
8
+ "params": {
9
+ "type": "object",
10
+ "properties": {
11
+ "language": {
12
+ "type": "string",
13
+ "title": "Which language?",
14
+ "description": "The language you want to practice, e.g. Spanish, Japanese, French.",
15
+ "default": "Spanish"
16
+ }
17
+ },
18
+ "required": [
19
+ "language"
20
+ ]
21
+ },
22
+ "nameTemplate": "{{language}} Language Trainer",
23
+ "aboutTemplate": "about.md",
24
+ "missionTemplate": "mission.md",
25
+ "gezels": [
26
+ {
27
+ "templateId": "language-trainer",
28
+ "voorman": true
29
+ }
30
+ ],
31
+ "scripts": {
32
+ "progress-store": "import { type InferredInput, defineScript, gezel } from '@bendyline/gezel-sdk';\n\nexport const meta = defineScript({\n name: 'progress-store',\n description:\n \"Read and update the student's language-learning progress (level, sessions, streak, notes). Call action:'status' at the start of a session to see where they are; action:'record' after a practice exchange to log it; action:'advance' when they're ready for the next level.\",\n inputs: {\n action: {\n type: 'string',\n description: \"One of 'status', 'record', or 'advance'.\",\n required: true,\n },\n note: {\n type: 'string',\n description: 'Optional short note to append (what was practiced, a struggle to remember).',\n },\n },\n outputs: {\n level: { type: 'number', description: 'Current level (1+).' },\n sessions: { type: 'number', description: 'Total practice sessions recorded.' },\n streak: { type: 'number', description: 'Consecutive-session streak.' },\n message: { type: 'string', description: 'Human-readable summary of the current state.' },\n },\n requires: ['workspace.read', 'workspace.write'],\n} as const);\n\nconst input = gezel.input as InferredInput<typeof meta>;\n\ninterface Progress {\n language: string;\n level: number;\n sessions: number;\n streak: number;\n notes: string[];\n}\n\nasync function load(): Promise<Progress> {\n try {\n const raw = await gezel.fs.read('progress.json');\n const p = JSON.parse(raw) as Partial<Progress>;\n return {\n language: p.language ?? 'your language',\n level: p.level ?? 1,\n sessions: p.sessions ?? 0,\n streak: p.streak ?? 0,\n notes: Array.isArray(p.notes) ? p.notes : [],\n };\n } catch {\n return { language: 'your language', level: 1, sessions: 0, streak: 0, notes: [] };\n }\n}\n\nconst p = await load();\nif (input.action === 'record') {\n p.sessions += 1;\n p.streak += 1;\n if (input.note) p.notes.push(input.note);\n} else if (input.action === 'advance') {\n p.level += 1;\n p.notes.push(`Advanced to level ${p.level}${input.note ? `: ${input.note}` : ''}`);\n}\nif (input.action !== 'status') {\n await gezel.fs.write('progress.json', `${JSON.stringify(p, null, 2)}\\n`);\n}\n\ngezel.output({\n level: p.level,\n sessions: p.sessions,\n streak: p.streak,\n message: `Learning ${p.language}: level ${p.level}, ${p.sessions} session(s), ${p.streak}-session streak.`,\n});\n"
33
+ },
34
+ "tools": [
35
+ {
36
+ "name": "record_session",
37
+ "description": "Log that the student practiced this session.",
38
+ "script": "progress-store",
39
+ "inputs": {
40
+ "type": "object",
41
+ "properties": {
42
+ "note": {
43
+ "type": "string"
44
+ }
45
+ }
46
+ },
47
+ "bind": {
48
+ "action": "record"
49
+ }
50
+ },
51
+ {
52
+ "name": "advance_level",
53
+ "description": "Advance the student to the next level.",
54
+ "script": "progress-store",
55
+ "inputs": {
56
+ "type": "object",
57
+ "properties": {
58
+ "note": {
59
+ "type": "string"
60
+ }
61
+ }
62
+ },
63
+ "bind": {
64
+ "action": "advance"
65
+ }
66
+ }
67
+ ],
68
+ "pages": {
69
+ "entry": "dashboard/index.html",
70
+ "api": 1,
71
+ "reads": [
72
+ {
73
+ "source": "workspace",
74
+ "path": "progress.json"
75
+ }
76
+ ]
77
+ },
78
+ "workspaceSeed": [
79
+ "progress.json"
80
+ ]
81
+ }
@@ -0,0 +1,4 @@
1
+ - Practice speaking and understanding {{language}} in regular, low-pressure conversation.
2
+ - Build a daily habit — short sessions beat rare long ones.
3
+ - Advance a level only when the current one feels comfortable.
4
+ - Keep the tone encouraging: celebrate progress, treat mistakes as normal.
@@ -0,0 +1,268 @@
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>Language progress</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
+ }
18
+ :root[data-theme='dark'] {
19
+ --bg: #1c1a17;
20
+ --card: #262320;
21
+ --ink: #efe9e0;
22
+ --muted: #a59a8c;
23
+ --accent: #e0894a;
24
+ --track: #3a352f;
25
+ }
26
+ @media (prefers-color-scheme: dark) {
27
+ :root:not([data-theme='light']) {
28
+ --bg: #1c1a17;
29
+ --card: #262320;
30
+ --ink: #efe9e0;
31
+ --muted: #a59a8c;
32
+ --accent: #e0894a;
33
+ --track: #3a352f;
34
+ }
35
+ }
36
+ * { box-sizing: border-box; }
37
+ body {
38
+ margin: 0;
39
+ font: 16px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
40
+ background: var(--bg);
41
+ color: var(--ink);
42
+ padding: 2rem 1.25rem;
43
+ }
44
+ main { max-width: 640px; margin: 0 auto; }
45
+ h1 { font-size: 1.5rem; margin: 0 0 0.25rem; }
46
+ .sub { color: var(--muted); margin: 0 0 1.5rem; }
47
+ .tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; margin-bottom: 1.5rem; }
48
+ .tile {
49
+ background: var(--card);
50
+ border: 1px solid var(--track);
51
+ border-radius: 12px;
52
+ padding: 1rem;
53
+ text-align: center;
54
+ }
55
+ .tile .n { font-size: 2rem; font-weight: 700; color: var(--accent); }
56
+ .tile .l { font-size: 0.8rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
57
+ .bar { background: var(--track); border-radius: 999px; height: 10px; overflow: hidden; margin: 0.5rem 0 1.5rem; }
58
+ .bar > span { display: block; height: 100%; background: var(--accent); border-radius: 999px; transition: width 0.4s ease; }
59
+ .notes { background: var(--card); border: 1px solid var(--track); border-radius: 12px; padding: 1rem 1.25rem; }
60
+ .notes h2 { font-size: 0.95rem; margin: 0 0 0.5rem; }
61
+ .notes ul { margin: 0; padding-left: 1.1rem; color: var(--muted); }
62
+ .notes li { margin: 0.2rem 0; }
63
+ .empty { color: var(--muted); font-style: italic; }
64
+ .demo-banner {
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: space-between;
68
+ gap: 0.75rem;
69
+ margin: 0 0 0.85rem;
70
+ padding: 0.5rem 0.65rem;
71
+ border: 1px solid var(--accent);
72
+ border-radius: 6px;
73
+ background: color-mix(in srgb, var(--card) 88%, var(--accent));
74
+ color: var(--ink);
75
+ font-size: 0.8rem;
76
+ }
77
+ .demo-banner[hidden] { display: none; }
78
+ .demo-banner strong { font-weight: 700; }
79
+ .demo-reset {
80
+ flex: none;
81
+ padding: 0.25rem 0.5rem;
82
+ border: 1px solid var(--accent);
83
+ border-radius: 6px;
84
+ background: var(--card);
85
+ color: var(--ink);
86
+ cursor: pointer;
87
+ font: inherit;
88
+ }
89
+ .demo-reset:hover { border-color: var(--ink); }
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <main>
94
+ <div class="demo-banner" data-gezel-demo-banner hidden>
95
+ <span><strong>Demo mode</strong> — sample data stays in this page.</span>
96
+ <button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
97
+ </div>
98
+ <h1 id="title">Your language journey</h1>
99
+ <p class="sub" id="sub">Loading your progress…</p>
100
+ <div class="tiles">
101
+ <div class="tile"><div class="n" id="level">—</div><div class="l">Level</div></div>
102
+ <div class="tile"><div class="n" id="sessions">—</div><div class="l">Sessions</div></div>
103
+ <div class="tile"><div class="n" id="streak">—</div><div class="l">Day streak</div></div>
104
+ </div>
105
+ <div class="bar"><span id="levelbar" style="width: 0%"></span></div>
106
+ <div class="notes">
107
+ <h2>Recent notes</h2>
108
+ <ul id="notes"><li class="empty">No notes yet — start a conversation with your tutor.</li></ul>
109
+ </div>
110
+ </main>
111
+ <script>
112
+ /* gezel-page-demo-stub v1 */
113
+ /*
114
+ * The canonical demo stub for Gezel type pages. MIT License.
115
+ *
116
+ * Paste this file's bytes verbatim into a <script> block ahead of the
117
+ * page's own code, then call makeDemoGezel({ page, tools, data }) with the
118
+ * page's demo handlers. Served by Gezel, the real window.gezel is already
119
+ * injected and the stub steps aside; opened as a raw file (the live demos
120
+ * on gezelgilde.com), it installs a window.gezel whose tools and data are
121
+ * the page-supplied handler maps — one code path in every mode. `tools`
122
+ * and `data` map a tool name / declared read path to a function; data
123
+ * handlers back read() and list(), and url() returns the handler's value
124
+ * or passes a data: URI through. See docs/page-authoring.md.
125
+ */
126
+ (function () {
127
+ 'use strict';
128
+ window.makeDemoGezel = function (opts) {
129
+ if (window.gezel) return window.gezel;
130
+ var page = (opts && opts.page) || {};
131
+ var tools = (opts && opts.tools) || {};
132
+ var data = (opts && opts.data) || {};
133
+ var themeListeners = [];
134
+ var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
135
+ var ui = {
136
+ theme: { mode: media && media.matches ? 'dark' : 'light' },
137
+ onTheme: function (cb) {
138
+ themeListeners.push(cb);
139
+ return function () {
140
+ var at = themeListeners.indexOf(cb);
141
+ if (at >= 0) themeListeners.splice(at, 1);
142
+ };
143
+ },
144
+ };
145
+ if (media && media.addEventListener) {
146
+ media.addEventListener('change', function (ev) {
147
+ ui.theme = { mode: ev.matches ? 'dark' : 'light' };
148
+ themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
149
+ });
150
+ }
151
+ function fail(code, message) {
152
+ var err = new Error(message);
153
+ err.code = code;
154
+ return Promise.reject(err);
155
+ }
156
+ function run(map, key, arg) {
157
+ if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
158
+ return Promise.resolve().then(function () { return map[key](arg); });
159
+ }
160
+ window.gezel = {
161
+ page: {
162
+ api: 1,
163
+ projectId: 'demo',
164
+ source: 'type',
165
+ entry: page.entry || 'index.html',
166
+ typeName: page.typeName || 'demo',
167
+ params: page.params || {},
168
+ mode: 'demo',
169
+ },
170
+ tools: {
171
+ list: function () { return Promise.resolve(Object.keys(tools)); },
172
+ invoke: function (name, input) {
173
+ var call = run(tools, name, input || {});
174
+ if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
175
+ return call.then(function (output) { return { output: output }; });
176
+ },
177
+ },
178
+ data: {
179
+ read: function (path, o) {
180
+ return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
181
+ },
182
+ list: function (path, o) {
183
+ return run(data, path, o || {}) || Promise.resolve([]);
184
+ },
185
+ watch: function () { return function () {}; },
186
+ url: function (path) {
187
+ if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
188
+ return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
189
+ },
190
+ },
191
+ ui: ui,
192
+ refresh: function () {},
193
+ };
194
+ return window.gezel;
195
+ };
196
+ })();
197
+ </script>
198
+ <script>
199
+ // Reads the student's progress through window.gezel (progress.json is
200
+ // declared in the manifest's pages.reads).
201
+ (function () {
202
+ 'use strict';
203
+
204
+ // ── demo progress (used only when the page opens as a raw file) ──
205
+ var demoProgress = {
206
+ language: 'Dutch',
207
+ level: 7,
208
+ sessions: 18,
209
+ streak: 5,
210
+ notes: [
211
+ 'Word order is settling in after omdat clauses.',
212
+ 'Practice separable verbs in everyday errands.',
213
+ 'Listening: caught the main idea without subtitles.',
214
+ 'Next session: ordering at a café.',
215
+ ],
216
+ };
217
+
218
+ makeDemoGezel({
219
+ page: { entry: 'dashboard/index.html', typeName: 'Language Trainer', params: { language: 'Spanish' } },
220
+ data: {
221
+ 'progress.json': function () { return demoProgress; },
222
+ },
223
+ });
224
+
225
+ // ── one code path from here: window.gezel exists in every mode ──
226
+ var demoMode = gezel.page.mode === 'demo';
227
+
228
+ function applyTheme(mode) {
229
+ document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
230
+ }
231
+ applyTheme(gezel.ui.theme.mode);
232
+ gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
233
+
234
+ if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
235
+
236
+ function esc(s) { return String(s).replace(/[&<>]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]; }); }
237
+ function render(p) {
238
+ document.getElementById('sub').textContent = 'Learning ' + (p.language || 'a new language');
239
+ document.getElementById('level').textContent = p.level ?? 1;
240
+ document.getElementById('sessions').textContent = p.sessions ?? 0;
241
+ document.getElementById('streak').textContent = p.streak ?? 0;
242
+ var lvl = Math.max(1, p.level ?? 1);
243
+ document.getElementById('levelbar').style.width = Math.min(100, ((lvl - 1) % 10) * 10 + 5) + '%';
244
+ var notes = Array.isArray(p.notes) ? p.notes.slice(-6).reverse() : [];
245
+ var ul = document.getElementById('notes');
246
+ if (notes.length) {
247
+ ul.innerHTML = notes.map(function (n) { return '<li>' + esc(n) + '</li>'; }).join('');
248
+ }
249
+ }
250
+
251
+ function loadProgress() {
252
+ return gezel.data
253
+ .read('progress.json')
254
+ .then(function (p) {
255
+ if (!p) throw new Error('no progress yet');
256
+ render(p);
257
+ })
258
+ .catch(function () {
259
+ document.getElementById('sub').textContent = 'No sessions recorded yet — say hello to your tutor to begin.';
260
+ });
261
+ }
262
+
263
+ loadProgress();
264
+ gezel.data.watch('progress.json', function () { loadProgress(); });
265
+ })();
266
+ </script>
267
+ </body>
268
+ </html>
@@ -0,0 +1,7 @@
1
+ {
2
+ "language": "{{language}}",
3
+ "level": 1,
4
+ "sessions": 0,
5
+ "streak": 0,
6
+ "notes": []
7
+ }