@bendyline/gilde 0.1.86 → 0.1.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/craftbook-templates/index.json +1 -1
- package/data/craftbook-templates/raw-index.json +1 -1
- package/data/craftbook-templates/ta/tailor-resume/versions/1.0.5/craftbook.json +226 -0
- package/data/craftbook-templates/ta/tailor-resume/versions/1.0.5/test.json +79 -0
- package/data/knowledge-catalogs/ca/caselaw-alaska-part-0001/manifest.json +24 -0
- package/data/knowledge-catalogs/ca/caselaw-alaska-part-0001/versions/2026.10.1/manifest.json +658 -0
- package/data/knowledge-catalogs/index.json +1 -1
- package/data/knowledge-catalogs/raw-index.json +1 -1
- package/data/project-types/ch/checkers/versions/1.2.1/about.md +9 -0
- package/data/project-types/ch/checkers/versions/1.2.1/game.json +77 -0
- package/data/project-types/ch/checkers/versions/1.2.1/manifest.json +168 -0
- package/data/project-types/ch/checkers/versions/1.2.1/mission.md +9 -0
- package/data/project-types/ch/checkers/versions/1.2.1/pages/board/index.html +1158 -0
- package/data/project-types/ch/chess/versions/1.0.4/about.md +9 -0
- package/data/project-types/ch/chess/versions/1.0.4/game.json +127 -0
- package/data/project-types/ch/chess/versions/1.0.4/manifest.json +182 -0
- package/data/project-types/ch/chess/versions/1.0.4/mission.md +8 -0
- package/data/project-types/ch/chess/versions/1.0.4/pages/board/index.html +851 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/about.md +6 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/manifest.json +70 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/mission.md +3 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/pages/gallery/index.html +212 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/schemes/index.json +9 -0
- package/data/project-types/de/design-scheme/versions/1.0.2/schemes/warm-dusk.json +4 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/about.md +7 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/budget.json +5 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/guests.json +10 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/manifest.json +171 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/mission.md +6 -0
- package/data/project-types/ev/event-planner/versions/1.0.2/pages/dashboard/index.html +283 -0
- package/data/project-types/fi/fitness-coach/versions/1.0.2/about.md +7 -0
- package/data/project-types/fi/fitness-coach/versions/1.0.2/manifest.json +126 -0
- package/data/project-types/fi/fitness-coach/versions/1.0.2/mission.md +7 -0
- package/data/project-types/fi/fitness-coach/versions/1.0.2/pages/dashboard/index.html +273 -0
- package/data/project-types/fi/fitness-coach/versions/1.0.2/training.json +6 -0
- package/data/project-types/fl/flashcards/versions/1.1.1/about.md +8 -0
- package/data/project-types/fl/flashcards/versions/1.1.1/deck.json +7 -0
- package/data/project-types/fl/flashcards/versions/1.1.1/manifest.json +168 -0
- package/data/project-types/fl/flashcards/versions/1.1.1/mission.md +6 -0
- package/data/project-types/fl/flashcards/versions/1.1.1/pages/review/index.html +477 -0
- package/data/project-types/fr/freelance-office/versions/1.0.2/about.md +6 -0
- package/data/project-types/fr/freelance-office/versions/1.0.2/invoices.json +9 -0
- package/data/project-types/fr/freelance-office/versions/1.0.2/manifest.json +143 -0
- package/data/project-types/fr/freelance-office/versions/1.0.2/mission.md +6 -0
- package/data/project-types/fr/freelance-office/versions/1.0.2/pages/dashboard/index.html +258 -0
- package/data/project-types/fu/fundraiser-hq/versions/1.0.2/about.md +7 -0
- package/data/project-types/fu/fundraiser-hq/versions/1.0.2/donations.json +7 -0
- package/data/project-types/fu/fundraiser-hq/versions/1.0.2/manifest.json +127 -0
- package/data/project-types/fu/fundraiser-hq/versions/1.0.2/mission.md +6 -0
- package/data/project-types/fu/fundraiser-hq/versions/1.0.2/pages/dashboard/index.html +261 -0
- package/data/project-types/go/go/versions/1.0.2/about.md +9 -0
- package/data/project-types/go/go/versions/1.0.2/game.json +103 -0
- package/data/project-types/go/go/versions/1.0.2/manifest.json +168 -0
- package/data/project-types/go/go/versions/1.0.2/mission.md +8 -0
- package/data/project-types/go/go/versions/1.0.2/pages/board/index.html +855 -0
- package/data/project-types/ho/household-budget/versions/1.0.3/about.md +6 -0
- package/data/project-types/ho/household-budget/versions/1.0.3/ledger.json +5 -0
- package/data/project-types/ho/household-budget/versions/1.0.3/manifest.json +164 -0
- package/data/project-types/ho/household-budget/versions/1.0.3/mission.md +5 -0
- package/data/project-types/ho/household-budget/versions/1.0.3/pages/dashboard/index.html +340 -0
- package/data/project-types/ho/household-manual/versions/1.0.2/about.md +7 -0
- package/data/project-types/ho/household-manual/versions/1.0.2/manifest.json +153 -0
- package/data/project-types/ho/household-manual/versions/1.0.2/mission.md +8 -0
- package/data/project-types/ho/household-manual/versions/1.0.2/pages/dashboard/index.html +258 -0
- package/data/project-types/ho/household-manual/versions/1.0.2/systems.json +8 -0
- package/data/project-types/index.json +1 -1
- package/data/project-types/jo/job-hunt/versions/1.0.2/about.md +10 -0
- package/data/project-types/jo/job-hunt/versions/1.0.2/activity.json +4 -0
- package/data/project-types/jo/job-hunt/versions/1.0.2/manifest.json +213 -0
- package/data/project-types/jo/job-hunt/versions/1.0.2/mission.md +9 -0
- package/data/project-types/jo/job-hunt/versions/1.0.2/pages/dashboard/index.html +430 -0
- package/data/project-types/jo/job-hunt/versions/1.0.2/pipeline.json +6 -0
- package/data/project-types/la/language-trainer/versions/1.0.2/about.md +6 -0
- package/data/project-types/la/language-trainer/versions/1.0.2/manifest.json +80 -0
- package/data/project-types/la/language-trainer/versions/1.0.2/mission.md +4 -0
- package/data/project-types/la/language-trainer/versions/1.0.2/pages/dashboard/index.html +268 -0
- package/data/project-types/la/language-trainer/versions/1.0.2/progress.json +7 -0
- package/data/project-types/li/life-binder/versions/1.0.3/about.md +7 -0
- package/data/project-types/li/life-binder/versions/1.0.3/documents.json +145 -0
- package/data/project-types/li/life-binder/versions/1.0.3/manifest.json +138 -0
- package/data/project-types/li/life-binder/versions/1.0.3/mission.md +6 -0
- package/data/project-types/li/life-binder/versions/1.0.3/pages/dashboard/index.html +248 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/about.md +6 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/manifest.json +160 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/menu.json +5 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/mission.md +5 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/pages/dashboard/index.html +248 -0
- package/data/project-types/me/meal-planner/versions/1.0.2/pantry.json +9 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/about.md +7 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/chapters.json +10 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/manifest.json +158 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/mission.md +7 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/pages/dashboard/index.html +264 -0
- package/data/project-types/no/novel-writing-room/versions/1.0.2/sessions.json +6 -0
- package/data/project-types/raw-index.json +1 -1
- package/data/project-types/tr/trip-planner/versions/1.0.2/about.md +7 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/journal.json +4 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/manifest.json +194 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/mission.md +4 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/packing.json +8 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/pages/dashboard/index.html +308 -0
- package/data/project-types/tr/trip-planner/versions/1.0.2/trip.json +7 -0
- package/package.json +1 -1
- package/schemas/project-type-version.schema.json +13 -0
|
@@ -0,0 +1,430 @@
|
|
|
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>Job hunt pipeline</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 2rem;
|
|
49
|
+
}
|
|
50
|
+
main { max-width: 960px; margin: 0 auto; }
|
|
51
|
+
h1 {
|
|
52
|
+
font-family: Georgia, 'PT Serif', 'Times New Roman', serif;
|
|
53
|
+
font-size: 1.4rem;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
margin: 0 0 0.15rem;
|
|
56
|
+
}
|
|
57
|
+
.sub { color: var(--muted); margin: 0 0 1.25rem; font-size: 0.9rem; }
|
|
58
|
+
.tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.6rem; margin-bottom: 1.25rem; }
|
|
59
|
+
.tile {
|
|
60
|
+
background: var(--card);
|
|
61
|
+
border: 1px solid var(--track);
|
|
62
|
+
border-radius: 10px;
|
|
63
|
+
padding: 0.7rem 0.85rem;
|
|
64
|
+
}
|
|
65
|
+
.tile .n { font-size: 1.5rem; font-weight: 700; color: var(--accent); line-height: 1.2; }
|
|
66
|
+
.tile .l { font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
|
|
67
|
+
.board { display: grid; grid-template-columns: repeat(5, 1fr); gap: 0.6rem; margin-bottom: 1.25rem; }
|
|
68
|
+
.col { min-width: 0; }
|
|
69
|
+
.col h2 {
|
|
70
|
+
font-size: 0.72rem;
|
|
71
|
+
text-transform: uppercase;
|
|
72
|
+
letter-spacing: 0.05em;
|
|
73
|
+
color: var(--muted);
|
|
74
|
+
margin: 0 0 0.4rem;
|
|
75
|
+
font-weight: 600;
|
|
76
|
+
display: flex;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
align-items: baseline;
|
|
79
|
+
}
|
|
80
|
+
.col h2 .count { color: var(--ink); }
|
|
81
|
+
.card {
|
|
82
|
+
background: var(--card);
|
|
83
|
+
border: 1px solid var(--track);
|
|
84
|
+
border-radius: 6px;
|
|
85
|
+
padding: 0.5rem 0.6rem;
|
|
86
|
+
margin-bottom: 0.45rem;
|
|
87
|
+
font-size: 0.85rem;
|
|
88
|
+
}
|
|
89
|
+
.card .co { font-weight: 600; overflow-wrap: anywhere; }
|
|
90
|
+
.card .ro { color: var(--muted); font-size: 0.8rem; overflow-wrap: anywhere; }
|
|
91
|
+
.card .ft { margin-top: 0.3rem; display: flex; gap: 0.35rem; flex-wrap: wrap; align-items: center; color: var(--muted); font-size: 0.72rem; }
|
|
92
|
+
.chip {
|
|
93
|
+
display: inline-block;
|
|
94
|
+
border: 1px solid var(--track);
|
|
95
|
+
border-radius: 4px;
|
|
96
|
+
padding: 0 0.3rem;
|
|
97
|
+
font-size: 0.7rem;
|
|
98
|
+
line-height: 1.4;
|
|
99
|
+
}
|
|
100
|
+
.chip.stale { color: var(--warn); border-color: var(--warn); }
|
|
101
|
+
.chip.signed { color: var(--good); border-color: var(--good); }
|
|
102
|
+
.panels { display: grid; grid-template-columns: 1fr 1fr; gap: 0.6rem; }
|
|
103
|
+
.panel {
|
|
104
|
+
background: var(--card);
|
|
105
|
+
border: 1px solid var(--track);
|
|
106
|
+
border-radius: 10px;
|
|
107
|
+
padding: 0.85rem 1rem;
|
|
108
|
+
}
|
|
109
|
+
.panel h2 { font-size: 0.9rem; margin: 0 0 0.5rem; }
|
|
110
|
+
.panel ul { margin: 0; padding-left: 1.05rem; font-size: 0.85rem; }
|
|
111
|
+
.panel li { margin: 0.25rem 0; }
|
|
112
|
+
.panel .when { color: var(--muted); font-size: 0.78rem; }
|
|
113
|
+
.overdue { color: var(--warn); font-weight: 600; }
|
|
114
|
+
.empty { color: var(--muted); font-style: italic; }
|
|
115
|
+
.board-empty {
|
|
116
|
+
background: var(--card);
|
|
117
|
+
border: 1px dashed var(--track);
|
|
118
|
+
border-radius: 10px;
|
|
119
|
+
padding: 0.85rem 1.25rem;
|
|
120
|
+
text-align: center;
|
|
121
|
+
color: var(--muted);
|
|
122
|
+
margin-bottom: 0.75rem;
|
|
123
|
+
}
|
|
124
|
+
@media (max-width: 720px) {
|
|
125
|
+
.tiles { grid-template-columns: repeat(2, 1fr); }
|
|
126
|
+
.board { grid-template-columns: 1fr; }
|
|
127
|
+
.panels { grid-template-columns: 1fr; }
|
|
128
|
+
}
|
|
129
|
+
.demo-banner {
|
|
130
|
+
display: flex;
|
|
131
|
+
align-items: center;
|
|
132
|
+
justify-content: space-between;
|
|
133
|
+
gap: 0.75rem;
|
|
134
|
+
margin: 0 0 0.85rem;
|
|
135
|
+
padding: 0.5rem 0.65rem;
|
|
136
|
+
border: 1px solid var(--accent);
|
|
137
|
+
border-radius: 6px;
|
|
138
|
+
background: color-mix(in srgb, var(--card) 88%, var(--accent));
|
|
139
|
+
color: var(--ink);
|
|
140
|
+
font-size: 0.8rem;
|
|
141
|
+
}
|
|
142
|
+
.demo-banner[hidden] { display: none; }
|
|
143
|
+
.demo-banner strong { font-weight: 700; }
|
|
144
|
+
.demo-reset {
|
|
145
|
+
flex: none;
|
|
146
|
+
padding: 0.25rem 0.5rem;
|
|
147
|
+
border: 1px solid var(--accent);
|
|
148
|
+
border-radius: 6px;
|
|
149
|
+
background: var(--card);
|
|
150
|
+
color: var(--ink);
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
font: inherit;
|
|
153
|
+
}
|
|
154
|
+
.demo-reset:hover { border-color: var(--ink); }
|
|
155
|
+
</style>
|
|
156
|
+
</head>
|
|
157
|
+
<body>
|
|
158
|
+
<main>
|
|
159
|
+
<div class="demo-banner" data-gezel-demo-banner hidden>
|
|
160
|
+
<span><strong>Demo mode</strong> — sample data stays in this page.</span>
|
|
161
|
+
<button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
|
|
162
|
+
</div>
|
|
163
|
+
<h1 id="title">Job hunt</h1>
|
|
164
|
+
<p class="sub" id="sub">Loading the pipeline…</p>
|
|
165
|
+
<div class="tiles">
|
|
166
|
+
<div class="tile"><div class="n" id="t-play">—</div><div class="l">In play</div></div>
|
|
167
|
+
<div class="tile"><div class="n" id="t-interview">—</div><div class="l">Interviewing</div></div>
|
|
168
|
+
<div class="tile"><div class="n" id="t-offer">—</div><div class="l">Offers</div></div>
|
|
169
|
+
<div class="tile"><div class="n" id="t-follow">—</div><div class="l">Follow-ups this week</div></div>
|
|
170
|
+
</div>
|
|
171
|
+
<div class="board-empty" id="board-empty" hidden>
|
|
172
|
+
No applications yet — tell your Loopbaancoach about the first one.
|
|
173
|
+
</div>
|
|
174
|
+
<div class="board" id="board"></div>
|
|
175
|
+
<div class="panels">
|
|
176
|
+
<div class="panel">
|
|
177
|
+
<h2>This week</h2>
|
|
178
|
+
<ul id="week"><li class="empty">Nothing due — nice and current.</li></ul>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="panel">
|
|
181
|
+
<h2>Recent activity</h2>
|
|
182
|
+
<ul id="activity"><li class="empty">No activity yet.</li></ul>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
</main>
|
|
186
|
+
<script>
|
|
187
|
+
/* gezel-page-demo-stub v1 */
|
|
188
|
+
/*
|
|
189
|
+
* The canonical demo stub for Gezel type pages. MIT License.
|
|
190
|
+
*
|
|
191
|
+
* Paste this file's bytes verbatim into a <script> block ahead of the
|
|
192
|
+
* page's own code, then call makeDemoGezel({ page, tools, data }) with the
|
|
193
|
+
* page's demo handlers. Served by Gezel, the real window.gezel is already
|
|
194
|
+
* injected and the stub steps aside; opened as a raw file (the live demos
|
|
195
|
+
* on gezelgilde.com), it installs a window.gezel whose tools and data are
|
|
196
|
+
* the page-supplied handler maps — one code path in every mode. `tools`
|
|
197
|
+
* and `data` map a tool name / declared read path to a function; data
|
|
198
|
+
* handlers back read() and list(), and url() returns the handler's value
|
|
199
|
+
* or passes a data: URI through. See docs/page-authoring.md.
|
|
200
|
+
*/
|
|
201
|
+
(function () {
|
|
202
|
+
'use strict';
|
|
203
|
+
window.makeDemoGezel = function (opts) {
|
|
204
|
+
if (window.gezel) return window.gezel;
|
|
205
|
+
var page = (opts && opts.page) || {};
|
|
206
|
+
var tools = (opts && opts.tools) || {};
|
|
207
|
+
var data = (opts && opts.data) || {};
|
|
208
|
+
var themeListeners = [];
|
|
209
|
+
var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
210
|
+
var ui = {
|
|
211
|
+
theme: { mode: media && media.matches ? 'dark' : 'light' },
|
|
212
|
+
onTheme: function (cb) {
|
|
213
|
+
themeListeners.push(cb);
|
|
214
|
+
return function () {
|
|
215
|
+
var at = themeListeners.indexOf(cb);
|
|
216
|
+
if (at >= 0) themeListeners.splice(at, 1);
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
if (media && media.addEventListener) {
|
|
221
|
+
media.addEventListener('change', function (ev) {
|
|
222
|
+
ui.theme = { mode: ev.matches ? 'dark' : 'light' };
|
|
223
|
+
themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function fail(code, message) {
|
|
227
|
+
var err = new Error(message);
|
|
228
|
+
err.code = code;
|
|
229
|
+
return Promise.reject(err);
|
|
230
|
+
}
|
|
231
|
+
function run(map, key, arg) {
|
|
232
|
+
if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
|
|
233
|
+
return Promise.resolve().then(function () { return map[key](arg); });
|
|
234
|
+
}
|
|
235
|
+
window.gezel = {
|
|
236
|
+
page: {
|
|
237
|
+
api: 1,
|
|
238
|
+
projectId: 'demo',
|
|
239
|
+
source: 'type',
|
|
240
|
+
entry: page.entry || 'index.html',
|
|
241
|
+
typeName: page.typeName || 'demo',
|
|
242
|
+
params: page.params || {},
|
|
243
|
+
mode: 'demo',
|
|
244
|
+
},
|
|
245
|
+
tools: {
|
|
246
|
+
list: function () { return Promise.resolve(Object.keys(tools)); },
|
|
247
|
+
invoke: function (name, input) {
|
|
248
|
+
var call = run(tools, name, input || {});
|
|
249
|
+
if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
|
|
250
|
+
return call.then(function (output) { return { output: output }; });
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
data: {
|
|
254
|
+
read: function (path, o) {
|
|
255
|
+
return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
|
|
256
|
+
},
|
|
257
|
+
list: function (path, o) {
|
|
258
|
+
return run(data, path, o || {}) || Promise.resolve([]);
|
|
259
|
+
},
|
|
260
|
+
watch: function () { return function () {}; },
|
|
261
|
+
url: function (path) {
|
|
262
|
+
if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
|
|
263
|
+
return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
ui: ui,
|
|
267
|
+
refresh: function () {},
|
|
268
|
+
};
|
|
269
|
+
return window.gezel;
|
|
270
|
+
};
|
|
271
|
+
})();
|
|
272
|
+
</script>
|
|
273
|
+
<script>
|
|
274
|
+
// Reads the pipeline and activity records through window.gezel (both
|
|
275
|
+
// paths are declared in the manifest's pages.reads).
|
|
276
|
+
(function () {
|
|
277
|
+
'use strict';
|
|
278
|
+
var WEEK = 7 * 24 * 60 * 60 * 1000;
|
|
279
|
+
var DAY = 24 * 60 * 60 * 1000;
|
|
280
|
+
var FALLBACK_STAGES = ['applied', 'screening', 'interview', 'offer', 'closed'];
|
|
281
|
+
|
|
282
|
+
// ── demo pipeline (used only when the page opens as a raw file) ──
|
|
283
|
+
var demoPipeline = {
|
|
284
|
+
role: 'Product designer',
|
|
285
|
+
stages: FALLBACK_STAGES,
|
|
286
|
+
records: [
|
|
287
|
+
{ id: 'app-12', status: 'applied', updatedAt: new Date(Date.now() - 3 * DAY).toISOString(), fields: { company: 'Canal Labs', role: 'Senior Product Designer' }, history: [{ at: new Date(Date.now() - 3 * DAY).toISOString() }] },
|
|
288
|
+
{ id: 'app-11', status: 'screening', updatedAt: new Date(Date.now() - 9 * DAY).toISOString(), fields: { company: 'Northwind', role: 'Product Designer' }, history: [{ at: new Date(Date.now() - 9 * DAY).toISOString() }] },
|
|
289
|
+
{ id: 'app-10', status: 'interview', updatedAt: new Date(Date.now() - DAY).toISOString(), fields: { company: 'Veld Works', role: 'Staff Product Designer' }, history: [{ at: new Date(Date.now() - 5 * DAY).toISOString() }] },
|
|
290
|
+
{ id: 'app-09', status: 'offer', updatedAt: new Date().toISOString(), fields: { company: 'Juniper Press', role: 'Design Lead' }, history: [{ at: new Date().toISOString() }] },
|
|
291
|
+
{ id: 'app-08', status: 'closed', updatedAt: new Date(Date.now() - 12 * DAY).toISOString(), fields: { company: 'Old Mill', role: 'Product Designer', outcome: 'withdrew' }, history: [{ at: new Date(Date.now() - 12 * DAY).toISOString() }] },
|
|
292
|
+
],
|
|
293
|
+
};
|
|
294
|
+
var demoActivity = { events: [
|
|
295
|
+
{ kind: 'note', at: new Date(Date.now() - 2 * DAY).toISOString(), note: 'Prepared portfolio stories for Veld Works.' },
|
|
296
|
+
{ kind: 'follow-up', at: new Date(Date.now() - DAY).toISOString(), note: 'Send Northwind a short follow-up', data: { dueAt: new Date(Date.now() + DAY).toISOString() } },
|
|
297
|
+
{ kind: 'interview', at: new Date().toISOString(), note: 'Juniper Press final conversation completed.' },
|
|
298
|
+
] };
|
|
299
|
+
|
|
300
|
+
makeDemoGezel({
|
|
301
|
+
page: { entry: 'dashboard/index.html', typeName: 'Job Hunt', params: { role: 'Software Engineer', focus: 'Just getting started.' } },
|
|
302
|
+
data: {
|
|
303
|
+
'pipeline.json': function () { return demoPipeline; },
|
|
304
|
+
'activity.json': function () { return demoActivity; },
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// ── one code path from here: window.gezel exists in every mode ──
|
|
309
|
+
var demoMode = gezel.page.mode === 'demo';
|
|
310
|
+
|
|
311
|
+
function applyTheme(mode) {
|
|
312
|
+
document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
|
|
313
|
+
}
|
|
314
|
+
applyTheme(gezel.ui.theme.mode);
|
|
315
|
+
gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
|
|
316
|
+
|
|
317
|
+
if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
|
|
318
|
+
|
|
319
|
+
function esc(s) {
|
|
320
|
+
return String(s).replace(/[&<>"]/g, function (c) {
|
|
321
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"' }[c];
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function cap(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
|
|
325
|
+
function daysAgo(iso) { return Math.floor((Date.now() - Date.parse(iso)) / DAY); }
|
|
326
|
+
|
|
327
|
+
function render(pipeline, act) {
|
|
328
|
+
var stages = Array.isArray(pipeline.stages) && pipeline.stages.length ? pipeline.stages : FALLBACK_STAGES;
|
|
329
|
+
var records = Array.isArray(pipeline.records) ? pipeline.records : [];
|
|
330
|
+
var events = Array.isArray(act.events) ? act.events : [];
|
|
331
|
+
var now = Date.now();
|
|
332
|
+
|
|
333
|
+
if (pipeline.role) document.getElementById('title').textContent = 'Job hunt: ' + pipeline.role;
|
|
334
|
+
|
|
335
|
+
var inPlay = records.filter(function (r) { return r.status !== 'closed'; });
|
|
336
|
+
var followUps = events.filter(function (e) {
|
|
337
|
+
var due = e.data && typeof e.data.dueAt === 'string' ? Date.parse(e.data.dueAt) : null;
|
|
338
|
+
return e.kind === 'follow-up' && due !== null && due <= now + WEEK;
|
|
339
|
+
});
|
|
340
|
+
document.getElementById('t-play').textContent = inPlay.length;
|
|
341
|
+
document.getElementById('t-interview').textContent = records.filter(function (r) { return r.status === 'interview'; }).length;
|
|
342
|
+
document.getElementById('t-offer').textContent = records.filter(function (r) { return r.status === 'offer'; }).length;
|
|
343
|
+
document.getElementById('t-follow').textContent = followUps.length;
|
|
344
|
+
document.getElementById('sub').textContent =
|
|
345
|
+
inPlay.length + ' in play · ' + followUps.length + ' follow-up' + (followUps.length === 1 ? '' : 's') + ' this week';
|
|
346
|
+
|
|
347
|
+
// The stage columns always render — even empty, the board teaches
|
|
348
|
+
// the shape of the campaign. The empty-state line sits above it.
|
|
349
|
+
document.getElementById('board-empty').hidden = records.length > 0;
|
|
350
|
+
var board = document.getElementById('board');
|
|
351
|
+
board.innerHTML = stages
|
|
352
|
+
.map(function (stage) {
|
|
353
|
+
var here = records.filter(function (r) { return r.status === stage; });
|
|
354
|
+
var cards = here
|
|
355
|
+
.map(function (r) {
|
|
356
|
+
var f = r.fields || {};
|
|
357
|
+
var chips = '';
|
|
358
|
+
var last = (r.history && r.history[r.history.length - 1]) || null;
|
|
359
|
+
var inStage = last ? daysAgo(last.at) : null;
|
|
360
|
+
if (stage !== 'closed' && daysAgo(r.updatedAt) > 7) {
|
|
361
|
+
chips += '<span class="chip stale">stale</span>';
|
|
362
|
+
}
|
|
363
|
+
if (stage === 'closed' && f.outcome) {
|
|
364
|
+
chips += '<span class="chip ' + (f.outcome === 'signed' ? 'signed' : '') + '">' + esc(f.outcome) + '</span>';
|
|
365
|
+
}
|
|
366
|
+
return (
|
|
367
|
+
'<div class="card"><div class="co">' + esc(f.company || r.id) + '</div>' +
|
|
368
|
+
(f.role ? '<div class="ro">' + esc(f.role) + '</div>' : '') +
|
|
369
|
+
'<div class="ft">' + (inStage !== null ? '<span>' + inStage + 'd in stage</span>' : '') + chips + '</div></div>'
|
|
370
|
+
);
|
|
371
|
+
})
|
|
372
|
+
.join('');
|
|
373
|
+
return (
|
|
374
|
+
'<div class="col"><h2><span>' + esc(cap(stage)) + '</span><span class="count">' + here.length + '</span></h2>' + cards + '</div>'
|
|
375
|
+
);
|
|
376
|
+
})
|
|
377
|
+
.join('');
|
|
378
|
+
|
|
379
|
+
var week = document.getElementById('week');
|
|
380
|
+
var weekItems = [];
|
|
381
|
+
followUps
|
|
382
|
+
.slice()
|
|
383
|
+
.sort(function (a, b) { return Date.parse(a.data.dueAt) - Date.parse(b.data.dueAt); })
|
|
384
|
+
.forEach(function (e) {
|
|
385
|
+
var overdue = Date.parse(e.data.dueAt) < now;
|
|
386
|
+
weekItems.push(
|
|
387
|
+
'<li>' + (overdue ? '<span class="overdue">Overdue:</span> ' : '') + esc(e.note || 'Follow up') +
|
|
388
|
+
' <span class="when">(' + esc(String(e.data.dueAt).slice(0, 10)) + ')</span></li>',
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
inPlay.forEach(function (r) {
|
|
392
|
+
var quiet = daysAgo(r.updatedAt);
|
|
393
|
+
if (quiet > 7) {
|
|
394
|
+
weekItems.push(
|
|
395
|
+
'<li>' + esc((r.fields && r.fields.company) || r.id) + ' <span class="when">— no touch in ' + quiet + ' days</span></li>',
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
week.innerHTML = weekItems.length ? weekItems.join('') : '<li class="empty">Nothing due — nice and current.</li>';
|
|
400
|
+
|
|
401
|
+
var activityList = document.getElementById('activity');
|
|
402
|
+
var recent = events.slice(-6).reverse();
|
|
403
|
+
if (recent.length) {
|
|
404
|
+
activityList.innerHTML = recent
|
|
405
|
+
.map(function (e) {
|
|
406
|
+
return '<li>' + esc(e.note || e.kind) + ' <span class="when">(' + esc(String(e.at).slice(0, 10)) + ')</span></li>';
|
|
407
|
+
})
|
|
408
|
+
.join('');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function loadPipeline() {
|
|
413
|
+
return Promise.all([
|
|
414
|
+
gezel.data.read('pipeline.json'),
|
|
415
|
+
gezel.data.read('activity.json').catch(function () { return { events: [] }; }),
|
|
416
|
+
])
|
|
417
|
+
.then(function (results) { render(results[0] || {}, results[1] || { events: [] }); })
|
|
418
|
+
.catch(function () {
|
|
419
|
+
document.getElementById('sub').textContent =
|
|
420
|
+
'No pipeline yet — tell your Loopbaancoach about your first application.';
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
loadPipeline();
|
|
425
|
+
gezel.data.watch('pipeline.json', function () { loadPipeline(); });
|
|
426
|
+
gezel.data.watch('activity.json', function () { loadPipeline(); });
|
|
427
|
+
})();
|
|
428
|
+
</script>
|
|
429
|
+
</body>
|
|
430
|
+
</html>
|
|
@@ -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,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"releasedAt": "2026-10-06T00:00:00Z",
|
|
5
|
+
"extends": "content-writing",
|
|
6
|
+
"meesterManaged": false,
|
|
7
|
+
"params": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"language": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"title": "Which language?",
|
|
13
|
+
"description": "The language you want to practice, e.g. Spanish, Japanese, French.",
|
|
14
|
+
"default": "Spanish"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"required": [
|
|
18
|
+
"language"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"nameTemplate": "{{language}} Language Trainer",
|
|
22
|
+
"aboutTemplate": "about.md",
|
|
23
|
+
"missionTemplate": "mission.md",
|
|
24
|
+
"gezels": [
|
|
25
|
+
{
|
|
26
|
+
"templateId": "language-trainer",
|
|
27
|
+
"voorman": true
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"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"
|
|
32
|
+
},
|
|
33
|
+
"tools": [
|
|
34
|
+
{
|
|
35
|
+
"name": "record_session",
|
|
36
|
+
"description": "Log that the student practiced this session.",
|
|
37
|
+
"script": "progress-store",
|
|
38
|
+
"inputs": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"properties": {
|
|
41
|
+
"note": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"bind": {
|
|
47
|
+
"action": "record"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "advance_level",
|
|
52
|
+
"description": "Advance the student to the next level.",
|
|
53
|
+
"script": "progress-store",
|
|
54
|
+
"inputs": {
|
|
55
|
+
"type": "object",
|
|
56
|
+
"properties": {
|
|
57
|
+
"note": {
|
|
58
|
+
"type": "string"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"bind": {
|
|
63
|
+
"action": "advance"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"pages": {
|
|
68
|
+
"entry": "dashboard/index.html",
|
|
69
|
+
"api": 1,
|
|
70
|
+
"reads": [
|
|
71
|
+
{
|
|
72
|
+
"source": "workspace",
|
|
73
|
+
"path": "progress.json"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"workspaceSeed": [
|
|
78
|
+
"progress.json"
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -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.
|