ferst-core 0.7.4 → 0.7.5
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/bin/ferst-update.mjs +33 -2
- package/cms/config.template.yml +214 -206
- package/components/BlockRenderer.astro +4 -0
- package/components/SiteFooter.astro +274 -270
- package/components/SiteHeader.astro +1243 -1183
- package/components/blocks/Heading.astro +5 -1
- package/components/recipes/Contacts.astro +139 -0
- package/components/recipes/Document.astro +157 -152
- package/components/recipes/MediaText.astro +92 -0
- package/components/recipes/Table.astro +5 -2
- package/content/blocks.ts +49 -1
- package/content/schemas.ts +167 -161
- package/examples/recipes.ts +2 -0
- package/layouts/Base.astro +2 -0
- package/lib/links.ts +10 -0
- package/lib/siteSettings.ts +61 -60
- package/lib/slug.ts +32 -0
- package/lib/table.ts +10 -0
- package/package.json +1 -1
package/bin/ferst-update.mjs
CHANGED
|
@@ -161,6 +161,23 @@ export function replacePackageCoreDep(raw, next) {
|
|
|
161
161
|
return raw.replace(DEP, `$1${next}$2`);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/** The `templateVersion` a manifest's text declares, or null (absent / not JSON). */
|
|
165
|
+
export function readTemplateVersion(raw) {
|
|
166
|
+
try {
|
|
167
|
+
const v = JSON.parse(raw).templateVersion;
|
|
168
|
+
return typeof v === 'string' && v ? v : null;
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Rewrite the manifest's `templateVersion` in place (formatting kept); null if it has none. */
|
|
175
|
+
export function replaceTemplateVersion(raw, next) {
|
|
176
|
+
const RE = /("templateVersion"\s*:\s*")[^"]*(")/;
|
|
177
|
+
if (!RE.test(raw)) return null;
|
|
178
|
+
return raw.replace(RE, `$1${next}$2`);
|
|
179
|
+
}
|
|
180
|
+
|
|
164
181
|
/** package.json fields that describe the CLIENT's site, not the template's scaffold. */
|
|
165
182
|
export const CLIENT_PACKAGE_FIELDS = ['name', 'description', 'version', 'private', 'author', 'homepage', 'repository'];
|
|
166
183
|
|
|
@@ -205,10 +222,11 @@ export function tail(text, n = 40) {
|
|
|
205
222
|
* @param {{ upstream?: string, commit?: string|null, coreVersion?: string|null, now?: Date|string }} p
|
|
206
223
|
* @returns {{ templateUpstream: string|null, templateCommit: string|null, coreVersion: string|null, at: string }}
|
|
207
224
|
*/
|
|
208
|
-
export function buildProvenance({ upstream, commit, coreVersion, now = new Date() } = {}) {
|
|
225
|
+
export function buildProvenance({ upstream, commit, coreVersion, templateVersion, now = new Date() } = {}) {
|
|
209
226
|
return {
|
|
210
227
|
templateUpstream: upstream ?? null,
|
|
211
228
|
templateCommit: commit ?? null,
|
|
229
|
+
templateVersion: templateVersion ?? null,
|
|
212
230
|
coreVersion: coreVersion ?? null,
|
|
213
231
|
at: typeof now === 'string' ? now : now.toISOString(),
|
|
214
232
|
};
|
|
@@ -324,6 +342,7 @@ function main() {
|
|
|
324
342
|
// The exact template commit this run syncs from (for the provenance stamp / #18 ledger).
|
|
325
343
|
const templateCommit = (tryGit(`rev-parse upstream/${branch}`, cwd) || '').trim() || null;
|
|
326
344
|
|
|
345
|
+
let templateVersion = null; // the template version this sync brought (for the manifest + stamp)
|
|
327
346
|
let scaffoldCount = 0; // files the scaffold sync touched (counted BEFORE the npm churn)
|
|
328
347
|
if (dryRun) {
|
|
329
348
|
const spec = ownedPaths.map(q).join(' ');
|
|
@@ -352,6 +371,18 @@ function main() {
|
|
|
352
371
|
}
|
|
353
372
|
} catch { /* package.json not synced — nothing to restore */ }
|
|
354
373
|
}
|
|
374
|
+
// The manifest isn't a synced path (it holds the client's own settings), so carry the
|
|
375
|
+
// template's version across explicitly; `raw` is what the coreRange step builds on.
|
|
376
|
+
const upstreamVersion = readTemplateVersion(tryGit(`show upstream/${branch}:${MANIFEST}`, cwd) || '');
|
|
377
|
+
if (upstreamVersion) {
|
|
378
|
+
const bumped = replaceTemplateVersion(raw, upstreamVersion);
|
|
379
|
+
if (bumped && bumped !== raw) {
|
|
380
|
+
raw = bumped;
|
|
381
|
+
writeFileSync(manifestPath, raw);
|
|
382
|
+
console.log(`ferst-update: templateVersion → ${upstreamVersion}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
templateVersion = upstreamVersion ?? readTemplateVersion(raw);
|
|
355
386
|
const status = (tryGit('status --short', cwd) || '').trim();
|
|
356
387
|
scaffoldCount = status ? status.split(/\r?\n/).filter(Boolean).length : 0;
|
|
357
388
|
console.log('ferst-update: scaffold synced. git status --short:');
|
|
@@ -436,7 +467,7 @@ function main() {
|
|
|
436
467
|
// drift ledger (#18) always know its state. MERGE: preserve gitBase/productionBranch
|
|
437
468
|
// and any other existing ferst-site.json fields.
|
|
438
469
|
const coreVersion = coreBump?.after ?? readInstalledCoreVersion(cwd);
|
|
439
|
-
const provenance = buildProvenance({ upstream: upstream.url, commit: templateCommit, coreVersion });
|
|
470
|
+
const provenance = buildProvenance({ upstream: upstream.url, commit: templateCommit, coreVersion, templateVersion: templateVersion ?? readTemplateVersion(raw) });
|
|
440
471
|
writeFileSync(
|
|
441
472
|
resolve(cwd, SITE_META),
|
|
442
473
|
`${JSON.stringify(mergeSiteMeta(readSiteMeta(cwd), provenance), null, 2)}\n`,
|
package/cms/config.template.yml
CHANGED
|
@@ -1,206 +1,214 @@
|
|
|
1
|
-
# Sveltia CMS — the content editor for a Ferst site. SINGLE SOURCE: this file lives
|
|
2
|
-
# in ferst-core and is written to dist/admin/config.yml at build by `ferst-cms-config`,
|
|
3
|
-
# so every client's collections are identical and always match the engine. The client
|
|
4
|
-
# repo commits NO config.yml — it is a build product, so it can never drift by hand.
|
|
5
|
-
#
|
|
6
|
-
# The backend repo (owner/name), git-proxy origin, its /git api_root and this
|
|
7
|
-
# deployment's branch are injected at build by ferst-cms-config.
|
|
8
|
-
backend:
|
|
9
|
-
name: github
|
|
10
|
-
repo: __REPO__
|
|
11
|
-
branch: __BRANCH__
|
|
12
|
-
base_url: __BASE_URL__
|
|
13
|
-
auth_endpoint: auth
|
|
14
|
-
api_root: __API_ROOT__
|
|
15
|
-
|
|
16
|
-
media_folder: public/uploads
|
|
17
|
-
public_folder: /uploads
|
|
18
|
-
|
|
19
|
-
collections:
|
|
20
|
-
- name: settings
|
|
21
|
-
label: Site settings
|
|
22
|
-
files:
|
|
23
|
-
- name: theme
|
|
24
|
-
label: Design tokens
|
|
25
|
-
file: src/content/themeSettings/index.json
|
|
26
|
-
icon: palette
|
|
27
|
-
description: "Edit your brand in the Theme Studio, where you preview changes live before publishing — open portal.ferst.co.uk, choose this site, then the Design tab. The fields here still work as a fallback."
|
|
28
|
-
fields:
|
|
29
|
-
- { name: brand, label: Brand colour, widget: color, default: '#0E4D4A', hint: Buttons, links, icons, highlights. }
|
|
30
|
-
- { name: ink, label: Ink colour, widget: color, default: '#0C2123', hint: Headings and body text. }
|
|
31
|
-
- { name: surface, label: Surface colour, widget: color, default: '#FFFFFF', hint: Page background. }
|
|
32
|
-
- name: fonts
|
|
33
|
-
label: Typography
|
|
34
|
-
widget: object
|
|
35
|
-
collapsed: false
|
|
36
|
-
fields:
|
|
37
|
-
- name: heading
|
|
38
|
-
label: Heading font
|
|
39
|
-
widget: select
|
|
40
|
-
required: false
|
|
41
|
-
default: ''
|
|
42
|
-
options: &fonts
|
|
43
|
-
- { label: 'Default (theme)', value: '' }
|
|
44
|
-
- { label: 'Poppins', value: 'Poppins, system-ui, sans-serif' }
|
|
45
|
-
- { label: 'Inter', value: 'Inter, system-ui, sans-serif' }
|
|
46
|
-
- { label: 'Playfair Display', value: "'Playfair Display', Georgia, serif" }
|
|
47
|
-
- { label: 'Georgia', value: "Georgia, 'Times New Roman', serif" }
|
|
48
|
-
- { label: 'System', value: 'system-ui, sans-serif' }
|
|
49
|
-
- { name: body, label: Body font, widget: select, required: false, default: '', options: *fonts }
|
|
50
|
-
- { name: display, label: Display font, widget: select, required: false, default: '', options: *fonts }
|
|
51
|
-
- { name: corners, label: Corners, widget: select, default: rounded, options: [{ label: Sharp, value: sharp }, { label: Rounded, value: rounded }, { label: Soft, value: soft }] }
|
|
52
|
-
- { name: elevation, label: Elevation, widget: select, default: raised, options: [{ label: Raised, value: raised }, { label: Flat, value: flat }] }
|
|
53
|
-
- { name: stroke, label: Card stroke, widget: select, default: 'on', options: [{ label: 'On', value: 'on' }, { label: 'Off', value: 'off' }] }
|
|
54
|
-
- { name: fields, label: Input fields, widget: select, default: boxed, options: [{ label: Boxed, value: boxed }, { label: Filled, value: filled }, { label: Underline, value: underline }] }
|
|
55
|
-
- { name: darkDepth, label: Dark depth, widget: number, value_type: float, min: 0.14, max: 0.34, step: 0.01, default: 0.21 }
|
|
56
|
-
- { name: darkChroma, label: Dark colour intensity, widget: number, value_type: float, min: 0, max: 0.15, step: 0.01, default: 0.05 }
|
|
57
|
-
- name: darkOverrides
|
|
58
|
-
label: Dark colour overrides (optional)
|
|
59
|
-
widget: object
|
|
60
|
-
collapsed: true
|
|
61
|
-
fields:
|
|
62
|
-
- { name: ground, label: Ground, widget: string, required: false }
|
|
63
|
-
- { name: text, label: Text, widget: string, required: false }
|
|
64
|
-
- { name: accent, label: Accent, widget: string, required: false }
|
|
65
|
-
|
|
66
|
-
- name: identity
|
|
67
|
-
label: Identity & contact
|
|
68
|
-
file: src/content/siteSettings/index.json
|
|
69
|
-
icon: info
|
|
70
|
-
fields:
|
|
71
|
-
- name: identity
|
|
72
|
-
label: Identity
|
|
73
|
-
widget: object
|
|
74
|
-
fields:
|
|
75
|
-
- { name: siteTitle, label: Site title, widget: string }
|
|
76
|
-
- { name: shortName, label: Short name, widget: string }
|
|
77
|
-
- { name: description, label: Description, widget: text }
|
|
78
|
-
- { name: location, label: Location, widget: string, required: false }
|
|
79
|
-
- name: contact
|
|
80
|
-
label: Contact
|
|
81
|
-
widget: object
|
|
82
|
-
fields:
|
|
83
|
-
- { name: phoneDisplay, label: Phone (display), widget: string, required: false }
|
|
84
|
-
- { name: phoneTel, label: 'Phone (tel: digits)', widget: string, required: false }
|
|
85
|
-
- { name: email, label: Email, widget: string, required: false }
|
|
86
|
-
- { name: facebookUrl, label: Facebook URL, widget: string, required: false }
|
|
87
|
-
|
|
88
|
-
- name: logos
|
|
89
|
-
label: Logos
|
|
90
|
-
file: src/content/logoSettings/index.json
|
|
91
|
-
icon: image
|
|
92
|
-
fields:
|
|
93
|
-
- { name: primaryLogoLight, label: Header logo (light), widget: image, required: false }
|
|
94
|
-
- { name: primaryLogoDark, label: Header logo (dark), widget: image, required: false }
|
|
95
|
-
- { name: secondaryLogoLight, label: Footer logo (light), widget: image, required: false }
|
|
96
|
-
- { name: secondaryLogoDark, label: Footer logo (dark), widget: image, required: false }
|
|
97
|
-
|
|
98
|
-
- name: navbar
|
|
99
|
-
label: Navigation
|
|
100
|
-
file: src/content/navbarSettings/index.json
|
|
101
|
-
icon: menu
|
|
102
|
-
fields:
|
|
103
|
-
- name: primary
|
|
104
|
-
label: Primary links
|
|
105
|
-
widget: list
|
|
106
|
-
fields:
|
|
107
|
-
- { name: label, label: Label, widget: string }
|
|
108
|
-
- { name: href, label: Link, widget: string }
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
1
|
+
# Sveltia CMS — the content editor for a Ferst site. SINGLE SOURCE: this file lives
|
|
2
|
+
# in ferst-core and is written to dist/admin/config.yml at build by `ferst-cms-config`,
|
|
3
|
+
# so every client's collections are identical and always match the engine. The client
|
|
4
|
+
# repo commits NO config.yml — it is a build product, so it can never drift by hand.
|
|
5
|
+
#
|
|
6
|
+
# The backend repo (owner/name), git-proxy origin, its /git api_root and this
|
|
7
|
+
# deployment's branch are injected at build by ferst-cms-config.
|
|
8
|
+
backend:
|
|
9
|
+
name: github
|
|
10
|
+
repo: __REPO__
|
|
11
|
+
branch: __BRANCH__
|
|
12
|
+
base_url: __BASE_URL__
|
|
13
|
+
auth_endpoint: auth
|
|
14
|
+
api_root: __API_ROOT__
|
|
15
|
+
|
|
16
|
+
media_folder: public/uploads
|
|
17
|
+
public_folder: /uploads
|
|
18
|
+
|
|
19
|
+
collections:
|
|
20
|
+
- name: settings
|
|
21
|
+
label: Site settings
|
|
22
|
+
files:
|
|
23
|
+
- name: theme
|
|
24
|
+
label: Design tokens
|
|
25
|
+
file: src/content/themeSettings/index.json
|
|
26
|
+
icon: palette
|
|
27
|
+
description: "Edit your brand in the Theme Studio, where you preview changes live before publishing — open portal.ferst.co.uk, choose this site, then the Design tab. The fields here still work as a fallback."
|
|
28
|
+
fields:
|
|
29
|
+
- { name: brand, label: Brand colour, widget: color, default: '#0E4D4A', hint: Buttons, links, icons, highlights. }
|
|
30
|
+
- { name: ink, label: Ink colour, widget: color, default: '#0C2123', hint: Headings and body text. }
|
|
31
|
+
- { name: surface, label: Surface colour, widget: color, default: '#FFFFFF', hint: Page background. }
|
|
32
|
+
- name: fonts
|
|
33
|
+
label: Typography
|
|
34
|
+
widget: object
|
|
35
|
+
collapsed: false
|
|
36
|
+
fields:
|
|
37
|
+
- name: heading
|
|
38
|
+
label: Heading font
|
|
39
|
+
widget: select
|
|
40
|
+
required: false
|
|
41
|
+
default: ''
|
|
42
|
+
options: &fonts
|
|
43
|
+
- { label: 'Default (theme)', value: '' }
|
|
44
|
+
- { label: 'Poppins', value: 'Poppins, system-ui, sans-serif' }
|
|
45
|
+
- { label: 'Inter', value: 'Inter, system-ui, sans-serif' }
|
|
46
|
+
- { label: 'Playfair Display', value: "'Playfair Display', Georgia, serif" }
|
|
47
|
+
- { label: 'Georgia', value: "Georgia, 'Times New Roman', serif" }
|
|
48
|
+
- { label: 'System', value: 'system-ui, sans-serif' }
|
|
49
|
+
- { name: body, label: Body font, widget: select, required: false, default: '', options: *fonts }
|
|
50
|
+
- { name: display, label: Display font, widget: select, required: false, default: '', options: *fonts }
|
|
51
|
+
- { name: corners, label: Corners, widget: select, default: rounded, options: [{ label: Sharp, value: sharp }, { label: Rounded, value: rounded }, { label: Soft, value: soft }] }
|
|
52
|
+
- { name: elevation, label: Elevation, widget: select, default: raised, options: [{ label: Raised, value: raised }, { label: Flat, value: flat }] }
|
|
53
|
+
- { name: stroke, label: Card stroke, widget: select, default: 'on', options: [{ label: 'On', value: 'on' }, { label: 'Off', value: 'off' }] }
|
|
54
|
+
- { name: fields, label: Input fields, widget: select, default: boxed, options: [{ label: Boxed, value: boxed }, { label: Filled, value: filled }, { label: Underline, value: underline }] }
|
|
55
|
+
- { name: darkDepth, label: Dark depth, widget: number, value_type: float, min: 0.14, max: 0.34, step: 0.01, default: 0.21 }
|
|
56
|
+
- { name: darkChroma, label: Dark colour intensity, widget: number, value_type: float, min: 0, max: 0.15, step: 0.01, default: 0.05 }
|
|
57
|
+
- name: darkOverrides
|
|
58
|
+
label: Dark colour overrides (optional)
|
|
59
|
+
widget: object
|
|
60
|
+
collapsed: true
|
|
61
|
+
fields:
|
|
62
|
+
- { name: ground, label: Ground, widget: string, required: false }
|
|
63
|
+
- { name: text, label: Text, widget: string, required: false }
|
|
64
|
+
- { name: accent, label: Accent, widget: string, required: false }
|
|
65
|
+
|
|
66
|
+
- name: identity
|
|
67
|
+
label: Identity & contact
|
|
68
|
+
file: src/content/siteSettings/index.json
|
|
69
|
+
icon: info
|
|
70
|
+
fields:
|
|
71
|
+
- name: identity
|
|
72
|
+
label: Identity
|
|
73
|
+
widget: object
|
|
74
|
+
fields:
|
|
75
|
+
- { name: siteTitle, label: Site title, widget: string }
|
|
76
|
+
- { name: shortName, label: Short name, widget: string }
|
|
77
|
+
- { name: description, label: Description, widget: text }
|
|
78
|
+
- { name: location, label: Location, widget: string, required: false }
|
|
79
|
+
- name: contact
|
|
80
|
+
label: Contact
|
|
81
|
+
widget: object
|
|
82
|
+
fields:
|
|
83
|
+
- { name: phoneDisplay, label: Phone (display), widget: string, required: false }
|
|
84
|
+
- { name: phoneTel, label: 'Phone (tel: digits)', widget: string, required: false }
|
|
85
|
+
- { name: email, label: Email, widget: string, required: false }
|
|
86
|
+
- { name: facebookUrl, label: Facebook URL, widget: string, required: false }
|
|
87
|
+
|
|
88
|
+
- name: logos
|
|
89
|
+
label: Logos
|
|
90
|
+
file: src/content/logoSettings/index.json
|
|
91
|
+
icon: image
|
|
92
|
+
fields:
|
|
93
|
+
- { name: primaryLogoLight, label: Header logo (light), widget: image, required: false }
|
|
94
|
+
- { name: primaryLogoDark, label: Header logo (dark), widget: image, required: false }
|
|
95
|
+
- { name: secondaryLogoLight, label: Footer logo (light), widget: image, required: false }
|
|
96
|
+
- { name: secondaryLogoDark, label: Footer logo (dark), widget: image, required: false }
|
|
97
|
+
|
|
98
|
+
- name: navbar
|
|
99
|
+
label: Navigation
|
|
100
|
+
file: src/content/navbarSettings/index.json
|
|
101
|
+
icon: menu
|
|
102
|
+
fields:
|
|
103
|
+
- name: primary
|
|
104
|
+
label: Primary links
|
|
105
|
+
widget: list
|
|
106
|
+
fields:
|
|
107
|
+
- { name: label, label: Label, widget: string }
|
|
108
|
+
- { name: href, label: Link, widget: string }
|
|
109
|
+
- name: cta
|
|
110
|
+
label: Header button
|
|
111
|
+
widget: object
|
|
112
|
+
required: false
|
|
113
|
+
hint: The button at the right of the header (and in the footer). Leave as Contact → /contact/ unless your contact page lives elsewhere.
|
|
114
|
+
fields:
|
|
115
|
+
- { name: label, label: Label, widget: string, default: Contact }
|
|
116
|
+
- { name: href, label: Link, widget: string, default: /contact/ }
|
|
117
|
+
|
|
118
|
+
- name: footer
|
|
119
|
+
label: Footer
|
|
120
|
+
file: src/content/footerSettings/index.json
|
|
121
|
+
icon: table_rows
|
|
122
|
+
fields:
|
|
123
|
+
- name: footer
|
|
124
|
+
label: Footer links
|
|
125
|
+
widget: list
|
|
126
|
+
fields:
|
|
127
|
+
- { name: label, label: Label, widget: string }
|
|
128
|
+
- { name: href, label: Link, widget: string }
|
|
129
|
+
|
|
130
|
+
- name: calendar-settings
|
|
131
|
+
label: Calendar
|
|
132
|
+
file: src/content/calendarSettings/index.json
|
|
133
|
+
icon: calendar_today
|
|
134
|
+
fields:
|
|
135
|
+
- { name: title, label: Heading, widget: string, default: Calendar }
|
|
136
|
+
- { name: intro, label: Intro, widget: text, required: false }
|
|
137
|
+
- { name: embedUrl, label: Embed URL, widget: string, required: false }
|
|
138
|
+
- { name: publicUrl, label: Public link, widget: string, required: false }
|
|
139
|
+
- { name: height, label: Height (px), widget: number, default: 700, value_type: int, min: 200, max: 2000 }
|
|
140
|
+
- { name: provider, label: Provider name, widget: string, default: Google Calendar }
|
|
141
|
+
- { name: weekStart, label: Week starts on, widget: select, default: monday, options: [{ label: "Monday", value: monday }, { label: "Sunday", value: sunday }] }
|
|
142
|
+
|
|
143
|
+
- name: posts
|
|
144
|
+
label: Posts
|
|
145
|
+
label_singular: Post
|
|
146
|
+
folder: src/content/posts
|
|
147
|
+
create: true
|
|
148
|
+
slug: "{{slug}}"
|
|
149
|
+
extension: md
|
|
150
|
+
format: frontmatter
|
|
151
|
+
icon: article
|
|
152
|
+
summary: "{{title}} — {{date}}"
|
|
153
|
+
sortable_fields: [date, title]
|
|
154
|
+
fields:
|
|
155
|
+
- { name: title, label: Title, widget: string }
|
|
156
|
+
- { name: date, label: Date, widget: datetime, picker_utc: true }
|
|
157
|
+
- { name: summary, label: Summary, widget: text, required: false }
|
|
158
|
+
- name: tags
|
|
159
|
+
label: Tags
|
|
160
|
+
widget: relation
|
|
161
|
+
required: false
|
|
162
|
+
collection: tags
|
|
163
|
+
value_field: "{{slug}}"
|
|
164
|
+
display_fields: [label]
|
|
165
|
+
search_fields: [label]
|
|
166
|
+
multiple: true
|
|
167
|
+
- { name: heroImage, label: Hero image, widget: image, required: false }
|
|
168
|
+
- { name: attachment, label: Attachment (e.g. PDF), widget: file, required: false }
|
|
169
|
+
- { name: draft, label: Draft, widget: boolean, default: false, required: false }
|
|
170
|
+
- { name: body, label: Body, widget: markdown, required: false }
|
|
171
|
+
|
|
172
|
+
- name: tags
|
|
173
|
+
label: Tags
|
|
174
|
+
label_singular: Tag
|
|
175
|
+
folder: src/content/tags
|
|
176
|
+
create: true
|
|
177
|
+
slug: "{{slug}}"
|
|
178
|
+
extension: json
|
|
179
|
+
format: json
|
|
180
|
+
icon: label
|
|
181
|
+
identifier_field: label
|
|
182
|
+
fields:
|
|
183
|
+
- { name: label, label: Label, widget: string }
|
|
184
|
+
- { name: description, label: Description, widget: text, required: false }
|
|
185
|
+
- { name: hidden, label: Hidden, widget: boolean, default: false, required: false }
|
|
186
|
+
|
|
187
|
+
- name: pages
|
|
188
|
+
label: Pages
|
|
189
|
+
label_singular: Page
|
|
190
|
+
description: "See each page and request a change — our team makes it and sends it back for you to check. You don't edit the page layout here."
|
|
191
|
+
folder: src/content/pages
|
|
192
|
+
extension: json
|
|
193
|
+
format: json
|
|
194
|
+
icon: description
|
|
195
|
+
nested: { depth: 3, summary: "{{title}}" }
|
|
196
|
+
summary: "{{title}}"
|
|
197
|
+
identifier_field: title
|
|
198
|
+
fields:
|
|
199
|
+
- { name: title, label: Page title, widget: string }
|
|
200
|
+
- name: requests
|
|
201
|
+
label: Requested changes
|
|
202
|
+
label_singular: Request
|
|
203
|
+
widget: list
|
|
204
|
+
required: false
|
|
205
|
+
summary: "{{fields.text}}"
|
|
206
|
+
fields:
|
|
207
|
+
- { name: text, label: "What would you like changed?", widget: text }
|
|
208
|
+
- { name: status, label: Status, widget: select, default: open, options: [{ label: Open, value: open }, { label: Done, value: done }] }
|
|
209
|
+
- { name: description, label: SEO description, widget: hidden, required: false }
|
|
210
|
+
- { name: heroImage, widget: hidden, required: false }
|
|
211
|
+
- { name: align, widget: hidden, required: false }
|
|
212
|
+
- { name: templateId, widget: hidden, required: false }
|
|
213
|
+
- { name: draft, widget: hidden, required: false }
|
|
214
|
+
- { name: blocks, widget: hidden }
|
|
@@ -49,6 +49,8 @@ import Documents from './recipes/Documents.astro';
|
|
|
49
49
|
import LinkRow from './recipes/LinkRow.astro';
|
|
50
50
|
import DocumentRecipe from './recipes/Document.astro';
|
|
51
51
|
import Timetable from './recipes/Timetable.astro';
|
|
52
|
+
import Contacts from './recipes/Contacts.astro';
|
|
53
|
+
import MediaText from './recipes/MediaText.astro';
|
|
52
54
|
|
|
53
55
|
interface Props {
|
|
54
56
|
blocks: Block[];
|
|
@@ -100,6 +102,8 @@ const registry: Record<BlockType, any> = {
|
|
|
100
102
|
linkRow: LinkRow,
|
|
101
103
|
document: DocumentRecipe,
|
|
102
104
|
timetable: Timetable,
|
|
105
|
+
contacts: Contacts,
|
|
106
|
+
mediaText: MediaText,
|
|
103
107
|
};
|
|
104
108
|
---
|
|
105
109
|
|