@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,7 @@
|
|
|
1
|
+
The trip bench for **{{destination}}**, departing {{start}}. Your Reisleider
|
|
2
|
+
plans each day around one anchor with room to breathe, keeps confirmations and
|
|
3
|
+
addresses where they can be found, and builds a packing list that fits the trip.
|
|
4
|
+
|
|
5
|
+
The **Output** tab shows the countdown, the itinerary as it fills in, and the
|
|
6
|
+
packing list — tick items off right on the board. During the trip, a line a day
|
|
7
|
+
in the journal; afterward, it's the souvenir.
|
|
@@ -0,0 +1,194 @@
|
|
|
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
|
+
"destination": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"title": "Where to?",
|
|
12
|
+
"description": "e.g. 'Kyoto', 'the Scottish Highlands'.",
|
|
13
|
+
"default": "somewhere wonderful"
|
|
14
|
+
},
|
|
15
|
+
"start": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"title": "Departure date",
|
|
18
|
+
"description": "e.g. 2026-09-12. A rough date is fine.",
|
|
19
|
+
"default": "2026-12-01"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"required": [
|
|
23
|
+
"destination",
|
|
24
|
+
"start"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"nameTemplate": "Trip: {{destination}}",
|
|
28
|
+
"aboutTemplate": "about.md",
|
|
29
|
+
"missionTemplate": "mission.md",
|
|
30
|
+
"gezels": [
|
|
31
|
+
{
|
|
32
|
+
"templateId": "reisleider",
|
|
33
|
+
"voorman": true
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"tools": [
|
|
37
|
+
{
|
|
38
|
+
"name": "plan_day",
|
|
39
|
+
"description": "Set a day's plan (one anchor, options, slack).",
|
|
40
|
+
"script": "trip-store",
|
|
41
|
+
"inputs": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"date": {
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"plan": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"required": [
|
|
52
|
+
"date",
|
|
53
|
+
"plan"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"bind": {
|
|
57
|
+
"action": "plan_day"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "pack_add",
|
|
62
|
+
"description": "Add an item to the packing list.",
|
|
63
|
+
"script": "trip-store",
|
|
64
|
+
"inputs": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"item": {
|
|
68
|
+
"type": "string"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"required": [
|
|
72
|
+
"item"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
"bind": {
|
|
76
|
+
"action": "pack_add"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "pack_check",
|
|
81
|
+
"description": "Mark an item packed.",
|
|
82
|
+
"script": "trip-store",
|
|
83
|
+
"inputs": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": {
|
|
86
|
+
"id": {
|
|
87
|
+
"type": "string"
|
|
88
|
+
},
|
|
89
|
+
"item": {
|
|
90
|
+
"type": "string"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"bind": {
|
|
95
|
+
"action": "pack_check"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"name": "journal_entry",
|
|
100
|
+
"description": "Capture a trip moment — a line a day.",
|
|
101
|
+
"script": "trip-store",
|
|
102
|
+
"inputs": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"properties": {
|
|
105
|
+
"note": {
|
|
106
|
+
"type": "string"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"required": [
|
|
110
|
+
"note"
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
"bind": {
|
|
114
|
+
"action": "journal_entry"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "trip_status",
|
|
119
|
+
"description": "Countdown, days planned, and the packing state.",
|
|
120
|
+
"script": "trip-store",
|
|
121
|
+
"inputs": {
|
|
122
|
+
"type": "object",
|
|
123
|
+
"properties": {}
|
|
124
|
+
},
|
|
125
|
+
"bind": {
|
|
126
|
+
"action": "status"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "itinerary",
|
|
131
|
+
"description": "Read the full day-by-day itinerary.",
|
|
132
|
+
"script": "trip-store",
|
|
133
|
+
"inputs": {
|
|
134
|
+
"type": "object",
|
|
135
|
+
"properties": {}
|
|
136
|
+
},
|
|
137
|
+
"bind": {
|
|
138
|
+
"action": "itinerary"
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"name": "mark_packed",
|
|
143
|
+
"description": "The dashboard's packing checkbox.",
|
|
144
|
+
"script": "trip-store",
|
|
145
|
+
"inputs": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"properties": {
|
|
148
|
+
"id": {
|
|
149
|
+
"type": "string"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
"required": [
|
|
153
|
+
"id"
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
"bind": {
|
|
157
|
+
"action": "pack_check"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"craftbooks": [
|
|
162
|
+
"pre-departure-countdown"
|
|
163
|
+
],
|
|
164
|
+
"schedules": [],
|
|
165
|
+
"pages": {
|
|
166
|
+
"entry": "dashboard/index.html",
|
|
167
|
+
"api": 1,
|
|
168
|
+
"reads": [
|
|
169
|
+
{
|
|
170
|
+
"source": "workspace",
|
|
171
|
+
"path": "trip.json"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"source": "workspace",
|
|
175
|
+
"path": "packing.json"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"source": "workspace",
|
|
179
|
+
"path": "journal.json"
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
"tools": [
|
|
183
|
+
"mark_packed"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"workspaceSeed": [
|
|
187
|
+
"trip.json",
|
|
188
|
+
"packing.json",
|
|
189
|
+
"journal.json"
|
|
190
|
+
],
|
|
191
|
+
"scripts": {
|
|
192
|
+
"trip-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: 'trip-store',\n description:\n \"Read and update the trip: itinerary + packing (trip.json) and the journal (journal.json). Call action:'status' for the shape of the trip; 'plan_day' with date + plan; 'pack_add' / 'pack_check' for the packing list; 'journal_entry' during the trip.\",\n inputs: {\n action: {\n type: 'string',\n description:\n \"One of 'status', 'plan_day', 'pack_add', 'pack_check', 'journal_entry', or 'itinerary'.\",\n required: true,\n },\n date: { type: 'string', description: 'Day being planned, e.g. 2026-09-14.' },\n plan: { type: 'string', description: \"The day's plan (anchor + options).\" },\n item: { type: 'string', description: 'Packing item name.' },\n id: { type: 'string', description: \"Packing item id, e.g. 'pack-3'.\" },\n note: { type: 'string', description: 'Journal entry text.' },\n },\n outputs: {\n daysPlanned: { type: 'number', description: 'Days with a plan.' },\n unpacked: { type: 'number', description: 'Packing items still unpacked.' },\n summary: { type: 'string', description: 'The trip picture (or the itinerary).' },\n },\n requires: ['workspace.read', 'workspace.write'],\n} as const);\n\nconst input = gezel.input as InferredInput<typeof meta>;\n\n// ── trip-rules-start ──\n// Pure derivations over the trip doc: countdown and itinerary rendering.\n\nfunction daysUntil(dateStr, nowMs) {\n var when = Date.parse(dateStr);\n if (!isFinite(when)) return null;\n return Math.ceil((when - nowMs) / (24 * 60 * 60 * 1000));\n}\n\nfunction renderItinerary(days) {\n var dates = Object.keys(days).sort();\n if (dates.length === 0) return 'No days planned yet.';\n return dates\n .map(function (d) {\n return d + ': ' + days[d];\n })\n .join('\\n');\n}\n\nfunction tripLine(trip, unpackedCount, nowMs) {\n var start = daysUntil(trip.startDate, nowMs);\n var planned = Object.keys(trip.days || {}).length;\n var lead =\n start === null\n ? ''\n : start > 0\n ? start + ' day(s) until departure. '\n : start === 0\n ? 'Departure day! '\n : 'On the road (or home again). ';\n return (\n lead + planned + ' day(s) planned. Packing: ' +\n (unpackedCount === 0 ? 'all packed.' : unpackedCount + ' item(s) still unpacked.')\n );\n}\n// ── trip-rules-end ──\n\nconst TRIP_FILE = 'trip.json';\nconst packing = rosterStore(gezel.fs, 'packing.json', {\n stages: ['unpacked', 'packed'],\n idPrefix: 'pack',\n});\nconst journal = logStore(gezel.fs, 'journal.json');\n\ninterface Trip {\n version: number;\n destination: string;\n startDate: string;\n days: Record<string, string>;\n updatedAt: string;\n}\n\nasync function loadTrip(): Promise<Trip> {\n try {\n const parsed = JSON.parse(await gezel.fs.read(TRIP_FILE)) as Partial<Trip>;\n return {\n version: 1,\n destination: typeof parsed.destination === 'string' ? parsed.destination : '',\n startDate: typeof parsed.startDate === 'string' ? parsed.startDate : '',\n days: parsed.days && typeof parsed.days === 'object' ? (parsed.days as Record<string, string>) : {},\n updatedAt: new Date().toISOString(),\n };\n } catch {\n return { version: 1, destination: '', startDate: '', days: {}, updatedAt: new Date().toISOString() };\n }\n}\n\nasync function saveTrip(trip: Trip): Promise<void> {\n trip.updatedAt = new Date().toISOString();\n await gezel.fs.write(TRIP_FILE, `${JSON.stringify(trip, null, 2)}\\n`);\n}\n\nconst trip = await loadTrip();\n\nif (input.action === 'plan_day') {\n if (!input.date || !input.plan) throw new Error(\"plan_day needs 'date' (YYYY-MM-DD) and 'plan'.\");\n trip.days[input.date.slice(0, 10)] = input.plan;\n await saveTrip(trip);\n} else if (input.action === 'pack_add') {\n if (!input.item) throw new Error(\"pack_add needs 'item'.\");\n await packing.add({ status: 'unpacked', fields: { name: input.item } });\n} else if (input.action === 'pack_check') {\n let id = input.id;\n if (!id && input.item) {\n const wanted = input.item.toLowerCase();\n const matches = (await packing.list()).filter(\n (r) => String(r.fields.name ?? '').toLowerCase() === wanted,\n );\n if (matches.length === 1) id = matches[0]!.id;\n }\n if (!id) throw new Error(\"Pass 'id' (e.g. 'pack-3') or an exact packing 'item'.\");\n await packing.transition(id, 'packed');\n} else if (input.action === 'journal_entry') {\n if (!input.note) throw new Error(\"journal_entry needs 'note'.\");\n await journal.append({ kind: 'entry', note: input.note });\n} else if (input.action !== 'status' && input.action !== 'itinerary') {\n throw new Error(\n `Unknown action '${input.action}'. Use status, plan_day, pack_add, pack_check, journal_entry, or itinerary.`,\n );\n}\n\nconst packItems = await packing.list();\nconst unpacked = packItems.filter((r) => r.status === 'unpacked').length;\n\ngezel.output({\n daysPlanned: Object.keys(trip.days).length,\n unpacked,\n summary:\n input.action === 'itinerary' ? renderItinerary(trip.days) : tripLine(trip, unpacked, Date.now()),\n});\n"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
- Every trip day has one anchor and deliberate slack — a spine, not a cage.
|
|
2
|
+
- Bookings, confirmations, and addresses are recorded the moment they exist.
|
|
3
|
+
- Packing is a five-minute sweep the night before, not a panic.
|
|
4
|
+
- The journal gets a line a day; the trip ends, the record stays.
|
|
@@ -0,0 +1,308 @@
|
|
|
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>Trip 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
|
+
.banner { border: 1px solid var(--accent); border-radius: 6px; padding: 0.45rem 0.7rem; font-size: 0.85rem; margin-bottom: 0.75rem; display: none; }
|
|
30
|
+
.banner.show { display: block; }
|
|
31
|
+
.note { margin: 0 0 0.85rem; padding: 0.5rem 0.65rem; border: 1px solid var(--track); border-radius: 6px; background: var(--card); color: var(--muted); font-size: 0.8rem; }
|
|
32
|
+
.cols { display: grid; grid-template-columns: 1.2fr 1fr; gap: 0.6rem; }
|
|
33
|
+
.panel { background: var(--card); border: 1px solid var(--track); border-radius: 10px; padding: 0.85rem 1rem; font-size: 0.9rem; }
|
|
34
|
+
.panel h2 { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 0 0 0.5rem; }
|
|
35
|
+
.day { display: grid; grid-template-columns: 6rem 1fr; gap: 0.6rem; padding: 0.3rem 0; }
|
|
36
|
+
.day .date { color: var(--muted); }
|
|
37
|
+
.pack { list-style: none; margin: 0; padding: 0; }
|
|
38
|
+
.pack li { display: flex; gap: 0.5rem; align-items: center; min-height: 44px; margin: 0; cursor: pointer; }
|
|
39
|
+
.pack li.packed { color: var(--muted); text-decoration: line-through; cursor: default; }
|
|
40
|
+
.pack .box { display: inline-block; width: 0.85rem; height: 0.85rem; border: 1px solid var(--track); border-radius: 4px; flex: none; }
|
|
41
|
+
.pack li.packed .box { background: var(--good); border-color: var(--good); }
|
|
42
|
+
.journal li { color: var(--muted); margin: 0.25rem 0; }
|
|
43
|
+
.journal li strong { color: var(--ink); font-weight: 600; }
|
|
44
|
+
.empty { color: var(--muted); font-style: italic; }
|
|
45
|
+
@media (max-width: 640px) { .cols { grid-template-columns: 1fr; } }
|
|
46
|
+
.demo-banner {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
justify-content: space-between;
|
|
50
|
+
gap: 0.75rem;
|
|
51
|
+
margin: 0 0 0.85rem;
|
|
52
|
+
padding: 0.5rem 0.65rem;
|
|
53
|
+
border: 1px solid var(--accent);
|
|
54
|
+
border-radius: 6px;
|
|
55
|
+
background: color-mix(in srgb, var(--card) 88%, var(--accent));
|
|
56
|
+
color: var(--ink);
|
|
57
|
+
font-size: 0.8rem;
|
|
58
|
+
}
|
|
59
|
+
.demo-banner[hidden] { display: none; }
|
|
60
|
+
.demo-banner strong { font-weight: 700; }
|
|
61
|
+
.demo-reset {
|
|
62
|
+
flex: none;
|
|
63
|
+
padding: 0.25rem 0.5rem;
|
|
64
|
+
border: 1px solid var(--accent);
|
|
65
|
+
border-radius: 6px;
|
|
66
|
+
background: var(--card);
|
|
67
|
+
color: var(--ink);
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
font: inherit;
|
|
70
|
+
}
|
|
71
|
+
.demo-reset:hover { border-color: var(--ink); }
|
|
72
|
+
</style>
|
|
73
|
+
</head>
|
|
74
|
+
<body>
|
|
75
|
+
<main>
|
|
76
|
+
<div class="demo-banner" data-gezel-demo-banner hidden>
|
|
77
|
+
<span><strong>Demo mode</strong> — sample data stays in this page.</span>
|
|
78
|
+
<button type="button" class="demo-reset" onclick="location.reload()">Reset demo</button>
|
|
79
|
+
</div>
|
|
80
|
+
<p class="note" id="browser-note" hidden>
|
|
81
|
+
Viewing from a browser tab — the trip stays live, but packing happens in the app's Output pane.
|
|
82
|
+
</p>
|
|
83
|
+
<div class="banner" id="banner"></div>
|
|
84
|
+
<h1 id="title">Trip planner</h1>
|
|
85
|
+
<p class="sub" id="sub">Loading the trip…</p>
|
|
86
|
+
<div class="cols">
|
|
87
|
+
<div class="panel">
|
|
88
|
+
<h2>Itinerary</h2>
|
|
89
|
+
<div id="days"><p class="empty">No days planned yet — sketch the spine with your Reisleider.</p></div>
|
|
90
|
+
<h2 style="margin-top: 0.9rem">Journal</h2>
|
|
91
|
+
<ul class="journal" id="journal" style="margin:0;padding-left:1.05rem"><li class="empty">A line a day, once you're on the road.</li></ul>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="panel">
|
|
94
|
+
<h2>Packing <span id="packcount"></span></h2>
|
|
95
|
+
<ul class="pack" id="pack"><li class="empty">Nothing listed yet.</li></ul>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</main>
|
|
99
|
+
<script>
|
|
100
|
+
/* gezel-page-demo-stub v1 */
|
|
101
|
+
/*
|
|
102
|
+
* The canonical demo stub for Gezel type pages. MIT License.
|
|
103
|
+
*
|
|
104
|
+
* Paste this file's bytes verbatim into a <script> block ahead of the
|
|
105
|
+
* page's own code, then call makeDemoGezel({ page, tools, data }) with the
|
|
106
|
+
* page's demo handlers. Served by Gezel, the real window.gezel is already
|
|
107
|
+
* injected and the stub steps aside; opened as a raw file (the live demos
|
|
108
|
+
* on gezelgilde.com), it installs a window.gezel whose tools and data are
|
|
109
|
+
* the page-supplied handler maps — one code path in every mode. `tools`
|
|
110
|
+
* and `data` map a tool name / declared read path to a function; data
|
|
111
|
+
* handlers back read() and list(), and url() returns the handler's value
|
|
112
|
+
* or passes a data: URI through. See docs/page-authoring.md.
|
|
113
|
+
*/
|
|
114
|
+
(function () {
|
|
115
|
+
'use strict';
|
|
116
|
+
window.makeDemoGezel = function (opts) {
|
|
117
|
+
if (window.gezel) return window.gezel;
|
|
118
|
+
var page = (opts && opts.page) || {};
|
|
119
|
+
var tools = (opts && opts.tools) || {};
|
|
120
|
+
var data = (opts && opts.data) || {};
|
|
121
|
+
var themeListeners = [];
|
|
122
|
+
var media = window.matchMedia ? window.matchMedia('(prefers-color-scheme: dark)') : null;
|
|
123
|
+
var ui = {
|
|
124
|
+
theme: { mode: media && media.matches ? 'dark' : 'light' },
|
|
125
|
+
onTheme: function (cb) {
|
|
126
|
+
themeListeners.push(cb);
|
|
127
|
+
return function () {
|
|
128
|
+
var at = themeListeners.indexOf(cb);
|
|
129
|
+
if (at >= 0) themeListeners.splice(at, 1);
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
if (media && media.addEventListener) {
|
|
134
|
+
media.addEventListener('change', function (ev) {
|
|
135
|
+
ui.theme = { mode: ev.matches ? 'dark' : 'light' };
|
|
136
|
+
themeListeners.slice().forEach(function (cb) { cb(ui.theme); });
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
function fail(code, message) {
|
|
140
|
+
var err = new Error(message);
|
|
141
|
+
err.code = code;
|
|
142
|
+
return Promise.reject(err);
|
|
143
|
+
}
|
|
144
|
+
function run(map, key, arg) {
|
|
145
|
+
if (!Object.prototype.hasOwnProperty.call(map, key)) return null;
|
|
146
|
+
return Promise.resolve().then(function () { return map[key](arg); });
|
|
147
|
+
}
|
|
148
|
+
window.gezel = {
|
|
149
|
+
page: {
|
|
150
|
+
api: 1,
|
|
151
|
+
projectId: 'demo',
|
|
152
|
+
source: 'type',
|
|
153
|
+
entry: page.entry || 'index.html',
|
|
154
|
+
typeName: page.typeName || 'demo',
|
|
155
|
+
params: page.params || {},
|
|
156
|
+
mode: 'demo',
|
|
157
|
+
},
|
|
158
|
+
tools: {
|
|
159
|
+
list: function () { return Promise.resolve(Object.keys(tools)); },
|
|
160
|
+
invoke: function (name, input) {
|
|
161
|
+
var call = run(tools, name, input || {});
|
|
162
|
+
if (!call) return fail('not-allowed', "The demo does not handle the tool '" + name + "'.");
|
|
163
|
+
return call.then(function (output) { return { output: output }; });
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
data: {
|
|
167
|
+
read: function (path, o) {
|
|
168
|
+
return run(data, path, o || {}) || fail('not-allowed', "The demo has no data for '" + path + "'.");
|
|
169
|
+
},
|
|
170
|
+
list: function (path, o) {
|
|
171
|
+
return run(data, path, o || {}) || Promise.resolve([]);
|
|
172
|
+
},
|
|
173
|
+
watch: function () { return function () {}; },
|
|
174
|
+
url: function (path) {
|
|
175
|
+
if (Object.prototype.hasOwnProperty.call(data, path)) return String(data[path]({}));
|
|
176
|
+
return typeof path === 'string' && path.indexOf('data:') === 0 ? path : '';
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
ui: ui,
|
|
180
|
+
refresh: function () {},
|
|
181
|
+
};
|
|
182
|
+
return window.gezel;
|
|
183
|
+
};
|
|
184
|
+
})();
|
|
185
|
+
</script>
|
|
186
|
+
<script>
|
|
187
|
+
(function () {
|
|
188
|
+
'use strict';
|
|
189
|
+
var busy = false;
|
|
190
|
+
|
|
191
|
+
// ── demo trip (used only when the page opens as a raw file) ──
|
|
192
|
+
var demoStart = new Date(Date.now() + 18 * 86400000);
|
|
193
|
+
var demoDate = function (offset) { return new Date(demoStart.getTime() + offset * 86400000).toISOString().slice(0, 10); };
|
|
194
|
+
var demoTrip = { destination: 'Lisbon', startDate: demoDate(0), days: {} };
|
|
195
|
+
demoTrip.days[demoDate(0)] = 'Arrive, settle in, and walk the Alfama lanes';
|
|
196
|
+
demoTrip.days[demoDate(1)] = 'Markets, tile museum, and a sunset miradouro';
|
|
197
|
+
demoTrip.days[demoDate(2)] = 'Day train to Sintra';
|
|
198
|
+
var demoPacking = { records: [
|
|
199
|
+
{ id: 'pack-1', status: 'packed', fields: { name: 'Passport and wallet' } },
|
|
200
|
+
{ id: 'pack-2', status: 'unpacked', fields: { name: 'Walking shoes' } },
|
|
201
|
+
{ id: 'pack-3', status: 'unpacked', fields: { name: 'Light rain jacket' } },
|
|
202
|
+
{ id: 'pack-4', status: 'unpacked', fields: { name: 'Phone charger' } },
|
|
203
|
+
] };
|
|
204
|
+
var demoJournal = { events: [
|
|
205
|
+
{ at: new Date().toISOString(), note: 'Reserved the Sintra train and palace entry.' },
|
|
206
|
+
{ at: new Date(Date.now() - 86400000).toISOString(), note: 'Saved three neighborhood dinner options.' },
|
|
207
|
+
] };
|
|
208
|
+
|
|
209
|
+
makeDemoGezel({
|
|
210
|
+
page: { entry: 'dashboard/index.html', typeName: 'Trip Planner', params: { destination: 'somewhere wonderful', start: '2026-12-01' } },
|
|
211
|
+
tools: {
|
|
212
|
+
mark_packed: function (input) {
|
|
213
|
+
var item = demoPacking.records.find(function (r) { return r.id === (input && input.id); });
|
|
214
|
+
if (!item) throw new Error("No packing item '" + (input && input.id) + "' in the demo list.");
|
|
215
|
+
item.status = 'packed';
|
|
216
|
+
return { summary: (item.fields.name || item.id) + ' packed.' };
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
data: {
|
|
220
|
+
'trip.json': function () { return demoTrip; },
|
|
221
|
+
'packing.json': function () { return demoPacking; },
|
|
222
|
+
'journal.json': function () { return demoJournal; },
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// ── one code path from here: window.gezel exists in every mode ──
|
|
227
|
+
var demoMode = gezel.page.mode === 'demo';
|
|
228
|
+
var browserMode = gezel.page.mode === 'browser';
|
|
229
|
+
|
|
230
|
+
function applyTheme(mode) {
|
|
231
|
+
document.documentElement.setAttribute('data-theme', mode === 'dark' ? 'dark' : 'light');
|
|
232
|
+
}
|
|
233
|
+
applyTheme(gezel.ui.theme.mode);
|
|
234
|
+
gezel.ui.onTheme(function (theme) { applyTheme(theme.mode); });
|
|
235
|
+
|
|
236
|
+
if (demoMode) document.querySelector('[data-gezel-demo-banner]').hidden = false;
|
|
237
|
+
if (browserMode) document.getElementById('browser-note').hidden = false;
|
|
238
|
+
|
|
239
|
+
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"' }[c]; }); }
|
|
240
|
+
function setBanner(text) { var el = document.getElementById('banner'); el.textContent = text || ''; el.className = text ? 'banner show' : 'banner'; }
|
|
241
|
+
function invokeErrorText(err) {
|
|
242
|
+
if (err && err.code === 'unavailable') {
|
|
243
|
+
return "Packing happens in the app's Output pane — this browser tab stays view-only.";
|
|
244
|
+
}
|
|
245
|
+
return String(err && err.message ? err.message : err);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function render(trip, packing, journal) {
|
|
249
|
+
if (trip.destination) document.getElementById('title').textContent = 'Trip: ' + trip.destination;
|
|
250
|
+
var days = trip.days || {};
|
|
251
|
+
var dates = Object.keys(days).sort();
|
|
252
|
+
var lead = '';
|
|
253
|
+
if (trip.startDate) {
|
|
254
|
+
var d = Math.ceil((Date.parse(trip.startDate) - Date.now()) / 86400000);
|
|
255
|
+
if (isFinite(d)) lead = d > 0 ? d + ' day(s) until departure · ' : d === 0 ? 'Departure day! · ' : 'On the road · ';
|
|
256
|
+
}
|
|
257
|
+
document.getElementById('sub').textContent = lead + dates.length + ' day(s) planned';
|
|
258
|
+
|
|
259
|
+
if (dates.length) {
|
|
260
|
+
document.getElementById('days').innerHTML = dates.map(function (d) {
|
|
261
|
+
return '<div class="day"><span class="date">' + esc(d.slice(5)) + '</span><span>' + esc(days[d]) + '</span></div>';
|
|
262
|
+
}).join('');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
var items = (packing && packing.records) || [];
|
|
266
|
+
var unpacked = items.filter(function (r) { return r.status === 'unpacked'; }).length;
|
|
267
|
+
document.getElementById('packcount').textContent = items.length ? '· ' + unpacked + ' to go' : '';
|
|
268
|
+
if (items.length) {
|
|
269
|
+
document.getElementById('pack').innerHTML = items.map(function (r) {
|
|
270
|
+
return '<li class="' + r.status + '" data-id="' + esc(r.id) + '"><span class="box"></span><span>' + esc((r.fields && r.fields.name) || r.id) + '</span></li>';
|
|
271
|
+
}).join('');
|
|
272
|
+
Array.prototype.forEach.call(document.querySelectorAll('.pack li.unpacked'), function (li) {
|
|
273
|
+
li.addEventListener('click', function () {
|
|
274
|
+
if (busy) return;
|
|
275
|
+
busy = true;
|
|
276
|
+
gezel.tools
|
|
277
|
+
.invoke('mark_packed', { id: li.getAttribute('data-id') })
|
|
278
|
+
.then(function () { setBanner(''); return loadTrip(); })
|
|
279
|
+
.catch(function (err) { setBanner(invokeErrorText(err)); })
|
|
280
|
+
.then(function () { busy = false; });
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
var entries = ((journal && journal.events) || []).slice(-4).reverse();
|
|
286
|
+
if (entries.length) {
|
|
287
|
+
document.getElementById('journal').innerHTML = entries.map(function (e) {
|
|
288
|
+
return '<li><strong>' + esc(String(e.at).slice(5, 10)) + '</strong> — ' + esc(e.note || '') + '</li>';
|
|
289
|
+
}).join('');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function loadTrip() {
|
|
294
|
+
return Promise.all([
|
|
295
|
+
gezel.data.read('trip.json').catch(function () { return { days: {} }; }),
|
|
296
|
+
gezel.data.read('packing.json').catch(function () { return { records: [] }; }),
|
|
297
|
+
gezel.data.read('journal.json').catch(function () { return { events: [] }; }),
|
|
298
|
+
]).then(function (results) { render(results[0] || { days: {} }, results[1], results[2]); });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
loadTrip();
|
|
302
|
+
gezel.data.watch('trip.json', function () { loadTrip(); });
|
|
303
|
+
gezel.data.watch('packing.json', function () { loadTrip(); });
|
|
304
|
+
gezel.data.watch('journal.json', function () { loadTrip(); });
|
|
305
|
+
})();
|
|
306
|
+
</script>
|
|
307
|
+
</body>
|
|
308
|
+
</html>
|
package/package.json
CHANGED
|
@@ -61,6 +61,16 @@
|
|
|
61
61
|
"leanProfile": {
|
|
62
62
|
"type": "boolean"
|
|
63
63
|
},
|
|
64
|
+
"capabilityFloor": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"enum": [
|
|
67
|
+
"tiny",
|
|
68
|
+
"small",
|
|
69
|
+
"medium",
|
|
70
|
+
"large",
|
|
71
|
+
"cloud"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
64
74
|
"indexingEnabled": {
|
|
65
75
|
"type": "boolean"
|
|
66
76
|
},
|
|
@@ -197,6 +207,9 @@
|
|
|
197
207
|
},
|
|
198
208
|
"hideSeed": {
|
|
199
209
|
"type": "boolean"
|
|
210
|
+
},
|
|
211
|
+
"standalone": {
|
|
212
|
+
"type": "boolean"
|
|
200
213
|
}
|
|
201
214
|
},
|
|
202
215
|
"required": [
|