@bendyline/gilde 0.1.86 → 0.1.88
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/ca/caselaw-nevada-part-0001/manifest.json +24 -0
- package/data/knowledge-catalogs/ca/caselaw-nevada-part-0001/versions/2026.10.1/manifest.json +666 -0
- package/data/knowledge-catalogs/ca/caselaw-wyoming-part-0001/manifest.json +24 -0
- package/data/knowledge-catalogs/ca/caselaw-wyoming-part-0001/versions/2026.10.3/manifest.json +626 -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,248 @@
|
|
|
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>Meal planner</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
color-scheme: light dark;
|
|
11
|
+
--bg: #faf7f2; --card: #fff; --ink: #2b2620; --muted: #7a7166;
|
|
12
|
+
--accent: #b5651d; --track: #ece5da; --good: #4a7c59; --warn: #a4552e;
|
|
13
|
+
}
|
|
14
|
+
:root[data-theme='dark'] {
|
|
15
|
+
--bg: #1c1a17; --card: #262320; --ink: #efe9e0; --muted: #a59a8c;
|
|
16
|
+
--accent: #e0894a; --track: #3a352f; --good: #7fae8d; --warn: #d98a63;
|
|
17
|
+
}
|
|
18
|
+
@media (prefers-color-scheme: dark) {
|
|
19
|
+
:root:not([data-theme='light']) {
|
|
20
|
+
--bg: #1c1a17; --card: #262320; --ink: #efe9e0; --muted: #a59a8c;
|
|
21
|
+
--accent: #e0894a; --track: #3a352f; --good: #7fae8d; --warn: #d98a63;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
* { box-sizing: border-box; }
|
|
25
|
+
body { margin: 0; font: 15px/1.5 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--ink); padding: 1.5rem 1.25rem; }
|
|
26
|
+
main { max-width: 680px; margin: 0 auto; }
|
|
27
|
+
h1 { font-family: Georgia, 'PT Serif', 'Times New Roman', serif; font-size: 1.35rem; font-weight: 600; margin: 0 0 0.15rem; }
|
|
28
|
+
.sub { color: var(--muted); font-size: 0.9rem; margin: 0 0 0.9rem; }
|
|
29
|
+
.week { background: var(--card); border: 1px solid var(--track); border-radius: 10px; padding: 0.4rem 0; margin-bottom: 0.75rem; }
|
|
30
|
+
.day { display: grid; grid-template-columns: 6.5rem 1fr; gap: 0.6rem; padding: 0.4rem 1rem; font-size: 0.9rem; }
|
|
31
|
+
.day + .day { border-top: 1px solid var(--track); }
|
|
32
|
+
.day .name { color: var(--muted); text-transform: capitalize; }
|
|
33
|
+
.day.today { background: color-mix(in srgb, var(--accent) 8%, transparent); }
|
|
34
|
+
.day.today .name { color: var(--accent); font-weight: 600; }
|
|
35
|
+
.day .meal.unplanned { color: var(--muted); font-style: italic; }
|
|
36
|
+
.panel { background: var(--card); border: 1px solid var(--track); border-radius: 10px; padding: 0.85rem 1rem; font-size: 0.9rem; }
|
|
37
|
+
.panel h2 { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 0 0 0.5rem; }
|
|
38
|
+
.panel ul { margin: 0; padding-left: 1.05rem; }
|
|
39
|
+
.panel li { margin: 0.2rem 0; }
|
|
40
|
+
.panel .lvl { color: var(--warn); font-size: 0.78rem; }
|
|
41
|
+
.empty { color: var(--muted); font-style: italic; }
|
|
42
|
+
.demo-banner {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 0.75rem;
|
|
47
|
+
margin: 0 0 0.85rem;
|
|
48
|
+
padding: 0.5rem 0.65rem;
|
|
49
|
+
border: 1px solid var(--accent);
|
|
50
|
+
border-radius: 6px;
|
|
51
|
+
background: color-mix(in srgb, var(--card) 88%, var(--accent));
|
|
52
|
+
color: var(--ink);
|
|
53
|
+
font-size: 0.8rem;
|
|
54
|
+
}
|
|
55
|
+
.demo-banner[hidden] { display: none; }
|
|
56
|
+
.demo-banner strong { font-weight: 700; }
|
|
57
|
+
.demo-reset {
|
|
58
|
+
flex: none;
|
|
59
|
+
padding: 0.25rem 0.5rem;
|
|
60
|
+
border: 1px solid var(--accent);
|
|
61
|
+
border-radius: 6px;
|
|
62
|
+
background: var(--card);
|
|
63
|
+
color: var(--ink);
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
font: inherit;
|
|
66
|
+
}
|
|
67
|
+
.demo-reset:hover { border-color: var(--ink); }
|
|
68
|
+
</style>
|
|
69
|
+
</head>
|
|
70
|
+
<body>
|
|
71
|
+
<main>
|
|
72
|
+
<div class="demo-banner" data-gezel-demo-banner hidden>
|
|
73
|
+
<span><strong>Demo mode</strong> — sample data stays in this page.</span>
|
|
74
|
+
<button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
|
|
75
|
+
</div>
|
|
76
|
+
<h1>Meal planner</h1>
|
|
77
|
+
<p class="sub" id="sub">Loading the week…</p>
|
|
78
|
+
<div class="week" id="week"></div>
|
|
79
|
+
<div class="panel">
|
|
80
|
+
<h2>Grocery list</h2>
|
|
81
|
+
<ul id="groceries"><li class="empty">Nothing needed — the pantry is holding.</li></ul>
|
|
82
|
+
</div>
|
|
83
|
+
</main>
|
|
84
|
+
<script>
|
|
85
|
+
/* gezel-page-demo-stub v1 */
|
|
86
|
+
/*
|
|
87
|
+
* The canonical demo stub for Gezel type pages. MIT License.
|
|
88
|
+
*
|
|
89
|
+
* Paste this file's bytes verbatim into a <script> block ahead of the
|
|
90
|
+
* page's own code, then call makeDemoGezel({ page, tools, data }) with the
|
|
91
|
+
* page's demo handlers. Served by Gezel, the real window.gezel is already
|
|
92
|
+
* injected and the stub steps aside; opened as a raw file (the live demos
|
|
93
|
+
* on gezelgilde.com), it installs a window.gezel whose tools and data are
|
|
94
|
+
* the page-supplied handler maps — one code path in every mode. `tools`
|
|
95
|
+
* and `data` map a tool name / declared read path to a function; data
|
|
96
|
+
* handlers back read() and list(), and url() returns the handler's value
|
|
97
|
+
* or passes a data: URI through. See docs/page-authoring.md.
|
|
98
|
+
*/
|
|
99
|
+
(function () {
|
|
100
|
+
'use strict';
|
|
101
|
+
window.makeDemoGezel = function (opts) {
|
|
102
|
+
if (window.gezel) return window.gezel;
|
|
103
|
+
var page = (opts && opts.page) || {};
|
|
104
|
+
var tools = (opts && opts.tools) || {};
|
|
105
|
+
var data = (opts && opts.data) || {};
|
|
106
|
+
var themeListeners = [];
|
|
107
|
+
var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
108
|
+
var ui = {
|
|
109
|
+
theme: { mode: media && media.matches ? 'dark' : 'light' },
|
|
110
|
+
onTheme: function (cb) {
|
|
111
|
+
themeListeners.push(cb);
|
|
112
|
+
return function () {
|
|
113
|
+
var at = themeListeners.indexOf(cb);
|
|
114
|
+
if (at >= 0) themeListeners.splice(at, 1);
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
if (media && media.addEventListener) {
|
|
119
|
+
media.addEventListener('change', function (ev) {
|
|
120
|
+
ui.theme = { mode: ev.matches ? 'dark' : 'light' };
|
|
121
|
+
themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function fail(code, message) {
|
|
125
|
+
var err = new Error(message);
|
|
126
|
+
err.code = code;
|
|
127
|
+
return Promise.reject(err);
|
|
128
|
+
}
|
|
129
|
+
function run(map, key, arg) {
|
|
130
|
+
if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
|
|
131
|
+
return Promise.resolve().then(function () { return map[key](arg); });
|
|
132
|
+
}
|
|
133
|
+
window.gezel = {
|
|
134
|
+
page: {
|
|
135
|
+
api: 1,
|
|
136
|
+
projectId: 'demo',
|
|
137
|
+
source: 'type',
|
|
138
|
+
entry: page.entry || 'index.html',
|
|
139
|
+
typeName: page.typeName || 'demo',
|
|
140
|
+
params: page.params || {},
|
|
141
|
+
mode: 'demo',
|
|
142
|
+
},
|
|
143
|
+
tools: {
|
|
144
|
+
list: function () { return Promise.resolve(Object.keys(tools)); },
|
|
145
|
+
invoke: function (name, input) {
|
|
146
|
+
var call = run(tools, name, input || {});
|
|
147
|
+
if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
|
|
148
|
+
return call.then(function (output) { return { output: output }; });
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
data: {
|
|
152
|
+
read: function (path, o) {
|
|
153
|
+
return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
|
|
154
|
+
},
|
|
155
|
+
list: function (path, o) {
|
|
156
|
+
return run(data, path, o || {}) || Promise.resolve([]);
|
|
157
|
+
},
|
|
158
|
+
watch: function () { return function () {}; },
|
|
159
|
+
url: function (path) {
|
|
160
|
+
if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
|
|
161
|
+
return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
ui: ui,
|
|
165
|
+
refresh: function () {},
|
|
166
|
+
};
|
|
167
|
+
return window.gezel;
|
|
168
|
+
};
|
|
169
|
+
})();
|
|
170
|
+
</script>
|
|
171
|
+
<script>
|
|
172
|
+
(function () {
|
|
173
|
+
'use strict';
|
|
174
|
+
var DAYS = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'];
|
|
175
|
+
|
|
176
|
+
// ── demo week (used only when the page opens as a raw file) ──
|
|
177
|
+
var demoMenu = { days: {
|
|
178
|
+
monday: 'Roasted tomato pasta',
|
|
179
|
+
tuesday: 'Miso salmon bowls',
|
|
180
|
+
wednesday: 'Chickpea stew and bread',
|
|
181
|
+
thursday: 'Leftovers',
|
|
182
|
+
friday: 'Mushroom tacos',
|
|
183
|
+
saturday: 'Dinner with friends',
|
|
184
|
+
} };
|
|
185
|
+
var demoPantry = { records: [
|
|
186
|
+
{ id: 'food-1', status: 'low', fields: { name: 'Olive oil' } },
|
|
187
|
+
{ id: 'food-2', status: 'out', fields: { name: 'Limes' } },
|
|
188
|
+
{ id: 'food-3', status: 'low', fields: { name: 'Brown rice' } },
|
|
189
|
+
] };
|
|
190
|
+
|
|
191
|
+
makeDemoGezel({
|
|
192
|
+
page: { entry: 'dashboard/index.html', typeName: 'Meal Planner', params: { household: 'the household' } },
|
|
193
|
+
data: {
|
|
194
|
+
'menu.json': function () { return demoMenu; },
|
|
195
|
+
'pantry.json': function () { return demoPantry; },
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// ── one code path from here: window.gezel exists in every mode ──
|
|
200
|
+
var demoMode = gezel.page.mode === 'demo';
|
|
201
|
+
|
|
202
|
+
function applyTheme(mode) {
|
|
203
|
+
document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
|
|
204
|
+
}
|
|
205
|
+
applyTheme(gezel.ui.theme.mode);
|
|
206
|
+
gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
|
|
207
|
+
|
|
208
|
+
if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
|
|
209
|
+
|
|
210
|
+
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
|
211
|
+
function today() { return DAYS[(new Date().getUTCDay() + 6) % 7]; }
|
|
212
|
+
|
|
213
|
+
function render(menu, pantry) {
|
|
214
|
+
var days = (menu && menu.days) || {};
|
|
215
|
+
var planned = DAYS.filter(function (d) { return days[d]; }).length;
|
|
216
|
+
var tonight = days[today()];
|
|
217
|
+
document.getElementById('sub').textContent =
|
|
218
|
+
(tonight ? 'Tonight: ' + tonight + ' · ' : 'Nothing planned tonight · ') + planned + ' of 7 planned';
|
|
219
|
+
|
|
220
|
+
document.getElementById('week').innerHTML = DAYS.map(function (d) {
|
|
221
|
+
return '<div class="day' + (d === today() ? ' today' : '') + '"><span class="name">' + d + '</span>' +
|
|
222
|
+
(days[d] ? '<span class="meal">' + esc(days[d]) + '</span>' : '<span class="meal unplanned">not planned</span>') + '</div>';
|
|
223
|
+
}).join('');
|
|
224
|
+
|
|
225
|
+
var gap = ((pantry && pantry.records) || []).filter(function (r) { return r.status === 'low' || r.status === 'out'; });
|
|
226
|
+
if (gap.length) {
|
|
227
|
+
document.getElementById('groceries').innerHTML = gap.map(function (r) {
|
|
228
|
+
return '<li>' + esc((r.fields && r.fields.name) || r.id) + ' <span class="lvl">(' + r.status + ')</span></li>';
|
|
229
|
+
}).join('');
|
|
230
|
+
} else {
|
|
231
|
+
document.getElementById('groceries').innerHTML = '<li class="empty">Nothing needed — the pantry is holding.</li>';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function loadWeek() {
|
|
236
|
+
return Promise.all([
|
|
237
|
+
gezel.data.read('menu.json').catch(function () { return { days: {} }; }),
|
|
238
|
+
gezel.data.read('pantry.json').catch(function () { return { records: [] }; }),
|
|
239
|
+
]).then(function (results) { render(results[0], results[1]); });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
loadWeek();
|
|
243
|
+
gezel.data.watch('menu.json', function () { loadWeek(); });
|
|
244
|
+
gezel.data.watch('pantry.json', function () { loadWeek(); });
|
|
245
|
+
})();
|
|
246
|
+
</script>
|
|
247
|
+
</body>
|
|
248
|
+
</html>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
The writing room for **{{title}}**, aiming at {{weeklyWordGoal}} words a week.
|
|
2
|
+
Your Schrijfmaat logs every session, keeps the chapter board moving — outline,
|
|
3
|
+
drafting, revised, done — and reads pages like a friend who wants the book to
|
|
4
|
+
exist. The book is yours; the rhythm is shared.
|
|
5
|
+
|
|
6
|
+
The **Output** tab is the manuscript board: total words, this week's pace, and
|
|
7
|
+
where every chapter stands.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"releasedAt": "2026-10-06T00:00:00Z",
|
|
5
|
+
"meesterManaged": false,
|
|
6
|
+
"params": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"title": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"title": "Working title",
|
|
12
|
+
"description": "The novel, as it is known today. Change it any time.",
|
|
13
|
+
"default": "Untitled"
|
|
14
|
+
},
|
|
15
|
+
"weeklyWordGoal": {
|
|
16
|
+
"type": "number",
|
|
17
|
+
"title": "Words per week",
|
|
18
|
+
"description": "The weekly writing rhythm to aim for.",
|
|
19
|
+
"default": 5000
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"title",
|
|
24
|
+
"weeklyWordGoal"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"nameTemplate": "Novel: {{title}}",
|
|
28
|
+
"aboutTemplate": "about.md",
|
|
29
|
+
"missionTemplate": "mission.md",
|
|
30
|
+
"gezels": [
|
|
31
|
+
{
|
|
32
|
+
"templateId": "schrijfmaat",
|
|
33
|
+
"voorman": true
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"tools": [
|
|
37
|
+
{
|
|
38
|
+
"name": "log_session",
|
|
39
|
+
"description": "Log a writing session (words written) the moment it is reported.",
|
|
40
|
+
"script": "manuscript-store",
|
|
41
|
+
"inputs": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"words": {
|
|
45
|
+
"type": "number"
|
|
46
|
+
},
|
|
47
|
+
"note": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": [
|
|
52
|
+
"words"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"bind": {
|
|
56
|
+
"action": "log_session"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"name": "add_chapter",
|
|
61
|
+
"description": "Put a chapter on the board (starts in outline).",
|
|
62
|
+
"script": "manuscript-store",
|
|
63
|
+
"inputs": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"properties": {
|
|
66
|
+
"title": {
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
"summary": {
|
|
70
|
+
"type": "string"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"required": [
|
|
74
|
+
"title"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"bind": {
|
|
78
|
+
"action": "add_chapter"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "chapter_stage",
|
|
83
|
+
"description": "Move a chapter between stages: 'outline', 'drafting', 'revised', 'done'.",
|
|
84
|
+
"script": "manuscript-store",
|
|
85
|
+
"inputs": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"properties": {
|
|
88
|
+
"id": {
|
|
89
|
+
"type": "string"
|
|
90
|
+
},
|
|
91
|
+
"stage": {
|
|
92
|
+
"type": "string"
|
|
93
|
+
},
|
|
94
|
+
"note": {
|
|
95
|
+
"type": "string"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"required": [
|
|
99
|
+
"id",
|
|
100
|
+
"stage"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"bind": {
|
|
104
|
+
"action": "chapter_stage"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"name": "novel_status",
|
|
109
|
+
"description": "Total words, this week against the goal, the chapter shape.",
|
|
110
|
+
"script": "manuscript-store",
|
|
111
|
+
"inputs": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"properties": {}
|
|
114
|
+
},
|
|
115
|
+
"bind": {
|
|
116
|
+
"action": "status"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "outline",
|
|
121
|
+
"description": "Every chapter with its stage and summary.",
|
|
122
|
+
"script": "manuscript-store",
|
|
123
|
+
"inputs": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"properties": {}
|
|
126
|
+
},
|
|
127
|
+
"bind": {
|
|
128
|
+
"action": "outline"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"craftbooks": [
|
|
133
|
+
"copy-review",
|
|
134
|
+
"ebook-compile"
|
|
135
|
+
],
|
|
136
|
+
"schedules": [],
|
|
137
|
+
"pages": {
|
|
138
|
+
"entry": "dashboard/index.html",
|
|
139
|
+
"api": 1,
|
|
140
|
+
"reads": [
|
|
141
|
+
{
|
|
142
|
+
"source": "workspace",
|
|
143
|
+
"path": "sessions.json"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"source": "workspace",
|
|
147
|
+
"path": "chapters.json"
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
"workspaceSeed": [
|
|
152
|
+
"sessions.json",
|
|
153
|
+
"chapters.json"
|
|
154
|
+
],
|
|
155
|
+
"scripts": {
|
|
156
|
+
"manuscript-store": "import { type InferredInput, defineScript, gezel } from '@bendyline/gezel-sdk';\nimport { logStore, rosterStore } from '@bendyline/gezel-sdk/stores';\n\nexport const meta = defineScript({\n name: 'manuscript-store',\n description:\n \"Read and update the manuscript record (sessions.json + chapters.json). Call action:'status' for total words, weekly pace, and the chapter board; 'log_session' after every writing session (words written); 'add_chapter' / 'chapter_stage' to keep the board honest; 'outline' to list every chapter.\",\n inputs: {\n action: {\n type: 'string',\n description: \"One of 'status', 'log_session', 'add_chapter', 'chapter_stage', or 'outline'.\",\n required: true,\n },\n words: { type: 'number', description: 'Words written this session (log_session).' },\n note: { type: 'string', description: 'Anything worth remembering.' },\n title: { type: 'string', description: 'Chapter title (add_chapter).' },\n summary: { type: 'string', description: 'One-line chapter summary (add_chapter).' },\n id: { type: 'string', description: \"Chapter id, e.g. 'ch-3' (chapter_stage).\" },\n stage: {\n type: 'string',\n description: \"Target stage: 'outline', 'drafting', 'revised', or 'done' (chapter_stage).\",\n },\n },\n outputs: {\n totalWords: { type: 'number', description: 'Words logged across all sessions.' },\n thisWeekWords: { type: 'number', description: 'Words logged this week (Monday UTC start).' },\n chapters: { type: 'number', description: 'Chapters on the board.' },\n summary: { type: 'string', description: 'The manuscript picture.' },\n },\n requires: ['workspace.read', 'workspace.write'],\n} as const);\n\nconst input = gezel.input as InferredInput<typeof meta>;\n\n// ── novel-rules-start ──\n// Pure derivations over logStore-shaped writing sessions (kind 'writing',\n// data {words}) and rosterStore-shaped chapters (stages outline → drafting\n// → revised → done). Weeks start Monday 00:00 UTC; nowIso is a parameter\n// so the rules stay clock-independent and testable.\n\nfunction sessionWords(e) {\n var words = e && e.data ? Number(e.data.words) : NaN;\n return isFinite(words) && words > 0 ? Math.round(words) : 0;\n}\n\nfunction totalWords(events) {\n return events.reduce(function (sum, e) {\n return sum + sessionWords(e);\n }, 0);\n}\n\nfunction weekStartMs(nowIso) {\n var day = new Date(nowIso.slice(0, 10) + 'T00:00:00Z');\n var sinceMonday = (day.getUTCDay() + 6) % 7;\n return day.getTime() - sinceMonday * 86400000;\n}\n\nfunction thisWeekWords(events, nowIso) {\n var start = weekStartMs(nowIso);\n return totalWords(\n events.filter(function (e) {\n return Date.parse(e.at) >= start;\n }),\n );\n}\n\nfunction chapterShape(records) {\n var byStage = { outline: 0, drafting: 0, revised: 0, done: 0 };\n records.forEach(function (r) {\n if (byStage[r.status] !== undefined) byStage[r.status] += 1;\n });\n return byStage;\n}\n\nfunction novelLine(events, records, nowIso, weeklyWordGoal) {\n var total = totalWords(events);\n var week = thisWeekWords(events, nowIso);\n var shape = chapterShape(records);\n var goal = weeklyWordGoal > 0 ? weeklyWordGoal : 0;\n return (\n total +\n ' words logged; ' +\n week +\n (goal > 0 ? ' of ' + goal : '') +\n ' this week. Chapters: ' +\n shape.outline +\n ' outlined, ' +\n shape.drafting +\n ' drafting, ' +\n shape.revised +\n ' revised, ' +\n shape.done +\n ' done.'\n );\n}\n// ── novel-rules-end ──\n\nconst sessions = logStore(gezel.fs, 'sessions.json');\nconst chapters = rosterStore(gezel.fs, 'chapters.json', {\n stages: ['outline', 'drafting', 'revised', 'done'],\n idPrefix: 'ch',\n});\n\nasync function readWeeklyWordGoal(): Promise<number> {\n try {\n const parsed = JSON.parse(await gezel.fs.read('sessions.json')) as { weeklyWordGoal?: unknown };\n const goal = Number(parsed.weeklyWordGoal);\n return Number.isFinite(goal) && goal > 0 ? Math.round(goal) : 0;\n } catch {\n return 0;\n }\n}\n\nif (input.action === 'log_session') {\n const words = Number(input.words);\n if (!Number.isFinite(words) || words <= 0) {\n throw new Error(\"log_session needs a positive 'words' count.\");\n }\n await sessions.append({\n kind: 'writing',\n ...(input.note ? { note: input.note } : {}),\n data: { words: Math.round(words) },\n });\n} else if (input.action === 'add_chapter') {\n if (!input.title) throw new Error(\"add_chapter needs a 'title'.\");\n await chapters.add({\n fields: { title: input.title, ...(input.summary ? { summary: input.summary } : {}) },\n ...(input.note ? { note: input.note } : {}),\n });\n} else if (input.action === 'chapter_stage') {\n if (!input.id || !input.stage) {\n throw new Error(\"chapter_stage needs 'id' (e.g. 'ch-3') and 'stage'.\");\n }\n await chapters.transition(input.id, input.stage, input.note ? { note: input.note } : undefined);\n} else if (input.action !== 'status' && input.action !== 'outline') {\n throw new Error(\n `Unknown action '${input.action}'. Use status, log_session, add_chapter, chapter_stage, or outline.`,\n );\n}\n\nconst events = await sessions.list();\nconst records = await chapters.list();\nconst weeklyWordGoal = await readWeeklyWordGoal();\nconst nowIso = new Date().toISOString();\nconst listing =\n input.action === 'outline'\n ? records\n .map(\n (r) =>\n `${r.id} [${r.status}] ${String(r.fields.title ?? '')}${r.fields.summary ? ` — ${String(r.fields.summary)}` : ''}`,\n )\n .join('\\n') || 'No chapters on the board yet.'\n : '';\n\ngezel.output({\n totalWords: totalWords(events),\n thisWeekWords: thisWeekWords(events, nowIso),\n chapters: records.length,\n summary: listing || novelLine(events, records, nowIso, weeklyWordGoal),\n});\n"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
- Finish {{title}} — draft by draft, at a rhythm of about {{weeklyWordGoal}}
|
|
2
|
+
words a week.
|
|
3
|
+
- Every writing session gets logged, even the hundred-word days; the total and
|
|
4
|
+
the pace come from the record.
|
|
5
|
+
- The chapter board always tells the truth: outline, drafting, revised, done.
|
|
6
|
+
- Feedback serves the draft: what works first, then the one or two things that
|
|
7
|
+
would help most — never a rewrite in the writer's voice.
|