@taprootio/site-authoring 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +106 -13
  2. package/examples/riverbend-wellness/manifest.fixture.json +79 -0
  3. package/examples/riverbend-wellness/nav.json +26 -0
  4. package/examples/riverbend-wellness/pages/index.md +93 -0
  5. package/examples/riverbend-wellness/pages/visit.md +33 -0
  6. package/examples/riverbend-wellness/redirects.json +33 -0
  7. package/examples/riverbend-wellness/settings/brand.json +7 -0
  8. package/examples/riverbend-wellness/settings/site-header.json +21 -0
  9. package/examples/riverbend-wellness/settings/site-publishing-preferences.json +107 -0
  10. package/examples/riverbend-wellness/settings/taproot-styles.json +730 -0
  11. package/package.json +2 -1
  12. package/src/api.js +203 -3
  13. package/src/cli-release.js +152 -0
  14. package/src/cli.js +120 -11
  15. package/src/constants.js +59 -2
  16. package/src/contact-url.js +63 -0
  17. package/src/content/components.js +26 -4
  18. package/src/content/free-form-section-registry.json +3 -2
  19. package/src/content/inline-facts.js +5 -1
  20. package/src/content/tiptap-prosemirror.ts +26 -12
  21. package/src/credentials.js +39 -4
  22. package/src/errors.js +25 -0
  23. package/src/fixture-contract.js +64 -0
  24. package/src/footer-contract.js +28 -30
  25. package/src/json-path-diff.js +167 -0
  26. package/src/output.js +9 -0
  27. package/src/presentation-reference.js +5 -3
  28. package/src/redirects-contract.js +444 -0
  29. package/src/redirects-workspace.js +72 -0
  30. package/src/reference-help.js +262 -10
  31. package/src/session.js +161 -7
  32. package/src/transport.js +111 -6
  33. package/src/verbs/approve.js +7 -2
  34. package/src/verbs/deploy.js +29 -2
  35. package/src/verbs/footer-push.js +7 -2
  36. package/src/verbs/index.js +7 -1
  37. package/src/verbs/login.js +11 -1
  38. package/src/verbs/media-upload.js +7 -2
  39. package/src/verbs/nav-push.js +17 -11
  40. package/src/verbs/pages-push.js +93 -8
  41. package/src/verbs/preview-page.js +7 -2
  42. package/src/verbs/preview-revoke.js +7 -2
  43. package/src/verbs/pull.js +357 -22
  44. package/src/verbs/redirects-pull.js +90 -0
  45. package/src/verbs/redirects-push.js +100 -0
  46. package/src/verbs/status.js +90 -3
  47. package/src/verbs/theme-push.js +7 -2
  48. package/src/verbs/validate.js +93 -18
  49. package/src/verbs/whoami.js +79 -1
  50. package/src/workspace.js +153 -15
package/README.md CHANGED
@@ -34,13 +34,50 @@ Node 22 or later. The package has one runtime dependency,
34
34
  against one Espalier theme contract, and a floating range would let an install
35
35
  resolve a different contract than the CLI was tested against.
36
36
 
37
+ ## Keeping the CLI current
38
+
39
+ Taproot accepts only the latest published release of this package. There is no
40
+ compatibility window: the contract moves with every release, and a CLI behind
41
+ the current one is refused at sign-in and at every online verb that exchanges
42
+ the sign-in for a site credential, before it validates or writes anything.
43
+ `TAPROOT_SITE_KEY` runs perform no sign-in and no exchange, so they are not
44
+ version-gated and their `status` reports `cliRelease.latestKnown: false`; keep
45
+ an automation's CLI current yourself.
46
+
47
+ ```bash
48
+ npm install -g @taprootio/site-authoring@latest
49
+ ```
50
+
51
+ A refusal names the field `CliUpgradeRequired`, classifies as
52
+ `refusal: "cli_outdated"`, and carries both versions and that command. Nothing
53
+ is wrong with the credential, the request, or the site, and no retry of an
54
+ outdated version succeeds. If the newer release is only minutes old, npm may
55
+ not serve it yet — wait a moment and run the upgrade again.
56
+
57
+ Each sign-in exchange also reports the latest published release. `status` shows
58
+ it as `cliRelease`, `whoami` reports what the last exchange recorded (dated with
59
+ that exchange), and once a newer release has been recorded the offline verbs —
60
+ `help`, `validate`, and `whoami` — refuse with the same message rather than
61
+ letting an agent keep authoring against a contract that has moved. With nothing
62
+ recorded they run, so a clean machine can still validate a fixture.
63
+ `taproot-site --version` and `--help` always answer.
64
+
65
+ Do not pin this package's version in a script. Every release retires the one
66
+ before it, so a pin is a scheduled outage.
67
+
68
+ **0.4.0 introduces this gate.** From the API deploy that ships with it, every
69
+ CLI older than the release that API was built with is refused with
70
+ `CliUpgradeRequired`. The release order is: merge to private `main`, then the
71
+ npm publish of 0.4.0, then the API deploy — publishing first is what keeps the
72
+ upgrade available to anyone the deploy refuses.
73
+
37
74
  ## First commands
38
75
 
39
76
  `taproot-site --help` lists every verb and the configuration contract.
40
77
  `taproot-site help` is the offline reference family agents read before
41
78
  authoring: page types, components, navigation, media, previews, themes,
42
- appearance, and the footer, each with `--json` for stable machine-readable
43
- output.
79
+ appearance, the footer, and the offline fixture contract, each with `--json`
80
+ for stable machine-readable output.
44
81
 
45
82
  ```bash
46
83
  taproot-site help
@@ -50,7 +87,8 @@ taproot-site help component hero-section --json
50
87
 
51
88
  `taproot-site validate <fixture-directory>` checks a complete offline fixture
52
89
  with no credential, no network, and no write: a directory laid out like a
53
- pulled workspace — `pages/`, `nav.json`, and the four `settings/` documents —
90
+ pulled workspace — `pages/`, `nav.json`, `redirects.json`, and the four
91
+ `settings/` documents —
54
92
  whose `manifest.fixture.json` binds deterministic page, resource, image, and
55
93
  settings identities under a versioned `fixture` block. It proves structure,
56
94
  page content and theme contexts, navigation references, the complete theme,
@@ -58,6 +96,23 @@ appearance, header, brand, and footer, and fixture-local image identities. It
58
96
  does not prove authorization, site ownership, concurrency, server round trips,
59
97
  or rendering; a real pull and an authorized preview do that.
60
98
 
99
+ The package ships one, so `validate` is runnable the moment the install
100
+ finishes. `taproot-site help fixture` states the manifest contract and prints
101
+ the shipped fixture's absolute path along with the exact command to validate
102
+ it:
103
+
104
+ ```bash
105
+ taproot-site help fixture
106
+ taproot-site validate "$(npm root --global)/@taprootio/site-authoring/examples/riverbend-wellness"
107
+ ```
108
+
109
+ The fixture is a fictional wellness studio — `example.test` hostnames, a
110
+ reserved `555-01xx` telephone number, an invented street and town — with two
111
+ free-form pages, a three-item navigation tree, and all four settings
112
+ documents. Nothing in it describes a real business. Copy the directory
113
+ somewhere writable before you edit it; `validate` never writes to the fixture
114
+ it reads.
115
+
61
116
  ## Authorize
62
117
 
63
118
  Sign in once. `login` starts a device-style authorization: it prints an
@@ -169,9 +224,12 @@ taproot-site deploy --staging
169
224
  taproot-site deploy --production
170
225
  ```
171
226
 
172
- `pull` snapshots pages, navigation, and settings into the workspace:
173
- `pages/`, `nav.json`, `settings/`, `media/`, and two dot-prefixed manifests
174
- that carry site binding and page identity between commands. Every page path
227
+ `pull` snapshots pages, navigation, redirects, and settings into the
228
+ workspace: `pages/`, `nav.json`, `redirects.json`, `settings/`, `media/`, and
229
+ two dot-prefixed manifests that carry site binding and page identity between
230
+ commands. Against a Taproot that does not serve the redirect map yet, `pull`
231
+ writes no `redirects.json` and records no redirect baseline, and
232
+ `redirects push` asks for a pull once the site has one. Every page path
175
233
  has exactly one authoritative source — `pages/<name>.md` in the documented
176
234
  Markdown subset with `title`, `path`, and `description` front matter, or
177
235
  `pages/<name>.pm.json` as a ProseMirror document — and pull never writes a
@@ -182,11 +240,18 @@ edited on the site since the last pull is a `pages.pull_conflict` refusal
182
240
  before anything under `pages/` changes, with both recovery paths named.
183
241
 
184
242
  `pages push [page-path...]` validates and sends the selected pages, or every
185
- workspace page when none is named; the homepage is addressed as `/`.
243
+ workspace page when none is named; the homepage is addressed as `/`. It fails
244
+ closed with `pages.push_conflict` when a page's stored-state revision has
245
+ moved since this workspace last reconciled with it, naming both revisions;
246
+ `pull` first, or push after a refused pull has shown you the site's version.
186
247
  `media upload [path...]` uploads raster files and records component-ready
187
248
  delivery fields. `nav push`, `theme push`, and `footer push` replace the whole
188
249
  navigation tree, the complete light/dark theme pair with its appearance
189
- settings, and the closed footer document.
250
+ settings, and the closed footer document. `redirects pull` writes the site's
251
+ redirect map to `redirects.json` with a baseline, and `redirects push`
252
+ validates the file offline and replaces the whole map; `help redirects`
253
+ states the entry shape (`path`, `kind` redirect or gone, `target`, `status`
254
+ defaulting to 301), the normalization rule, and every refusal.
190
255
 
191
256
  `preview page <page-path-or-id>` renders one persisted draft behind the
192
257
  staging gate and mints a browser handoff URL, reported only in the final JSON
@@ -199,7 +264,8 @@ for another. The handoff also expires two minutes after it is minted;
199
264
  `approve [page-path...]` publishes drafts into the approved candidate pool.
200
265
  It stages; nothing reaches an audience until `deploy --staging` publishes the
201
266
  candidate and `deploy --production` promotes that completed staging
202
- deployment. `status` reports deployments, readiness, and image processing.
267
+ deployment. `status` reports the platform authoring switch, the CLI release,
268
+ deployments, readiness, and image processing.
203
269
 
204
270
  ## Output contract
205
271
 
@@ -208,11 +274,17 @@ whose approval code and URL exist only as progress). Stdout carries exactly
208
274
  one JSON object per run, schema version `1`: `ok`, the CLI name and version,
209
275
  the verb, and the verb's own result on success; a stable `error.code` with an
210
276
  optional `field`, `status`, and classified `refusal` on failure. Exit codes are
211
- `0` success, `1` failure, and `2` usage fault. The four refusal classes an
212
- automation should branch on are `platform_paused` (external authoring is
277
+ `0` success, `1` failure, and `2` usage fault. The six refusal classes an
278
+ automation should branch on are `cli_outdated` (this package is behind the
279
+ latest published release, which is the only one Taproot accepts; upgrade it,
280
+ and see "Keeping the CLI current"), `platform_paused` (external authoring is
213
281
  paused; retry later), `credential_rejected` (stop and re-issue),
214
- `plan_limit` (the plan's published-page allowance, surfaced at deploy), and
215
- `throttled` (back off).
282
+ `capability_missing` (the credential is valid and correctly scoped but was
283
+ not exchanged for a capability the request needs; the refusal carries
284
+ `field: "GrantedCapabilities"`, and the CLI's human progress names the
285
+ granted and required capabilities, so report the verb's declared set rather
286
+ than re-issuing the key), `plan_limit` (the plan's published-page allowance,
287
+ surfaced at deploy), and `throttled` (back off).
216
288
 
217
289
  When `GITHUB_OUTPUT` names the runner's existing output file, every
218
290
  operational verb appends the same JSON under `taproot_site_result` through a
@@ -221,6 +293,27 @@ Authorization values, signed upload headers, preview handoffs outside the
221
293
  final result, and page bodies never appear in progress, diagnostics, JSON, or
222
294
  Actions output.
223
295
 
296
+ ### Every write answers `platform_paused`
297
+
298
+ `pull` and `status` succeed, and every write is refused with
299
+ `refusal: "platform_paused"` and `field: "SiteAuthoringRollout"`. Nothing is
300
+ wrong with the credential, the request, or the site: Taproot has external site
301
+ authoring switched off platform-wide. A Taproot administrator turns it back on
302
+ with the platform setting `site_authoring.external_writes_enabled`, under
303
+ Admin → Platform settings → Site Authoring ("External site authoring writes
304
+ enabled"); nobody else can, and no amount of retrying or re-issuing changes it.
305
+
306
+ You do not have to discover this from a refusal. Each sign-in exchange reports
307
+ the switch's state, so `status` names it as `platform.externalWritesEnabled`
308
+ and every write verb prints one warning before it does any work when the last
309
+ exchange said the platform was paused. The warning is advisory and never a
310
+ gate — the switch is enforced inside each write's own transaction, so a run
311
+ that starts while it is off still succeeds if an operator turns it on
312
+ meanwhile. `whoami` stays offline and reports what the last exchange recorded,
313
+ dated with that exchange; `status` is where a current answer comes from.
314
+ `TAPROOT_SITE_KEY` runs perform no exchange, so their `status` reports
315
+ `platform.externalWritesKnown: false` rather than guessing.
316
+
224
317
  ## Public release
225
318
 
226
319
  Private Taproot is the source authority. Merging a version bump to private
@@ -0,0 +1,79 @@
1
+ {
2
+ "manifestVersion": 6,
3
+ "siteId": "a0000000-0000-4000-8000-000000000001",
4
+ "pulledAt": "2026-09-04T00:00:00.000Z",
5
+ "pages": [
6
+ {
7
+ "pageId": "a0000000-0000-4000-8000-000000000010",
8
+ "resourceId": "a0000000-0000-4000-8000-000000000011",
9
+ "path": "",
10
+ "title": "Riverbend Wellness",
11
+ "status": "PAGE_STATUS_DRAFT",
12
+ "templateType": "TEMPLATE_TYPE_FREE_FORM",
13
+ "hasDraft": true,
14
+ "isGenerated": false,
15
+ "description": "An example home page for the offline authoring fixture.",
16
+ "file": "pages/index.md",
17
+ "sourceFormat": "markdown",
18
+ "workspaceMode": "editable"
19
+ },
20
+ {
21
+ "pageId": "a0000000-0000-4000-8000-000000000012",
22
+ "resourceId": "a0000000-0000-4000-8000-000000000013",
23
+ "path": "visit",
24
+ "title": "Visit the studio",
25
+ "status": "PAGE_STATUS_DRAFT",
26
+ "templateType": "TEMPLATE_TYPE_FREE_FORM",
27
+ "hasDraft": true,
28
+ "isGenerated": false,
29
+ "description": "An example second page for the offline authoring fixture.",
30
+ "file": "pages/visit.md",
31
+ "sourceFormat": "markdown",
32
+ "workspaceMode": "editable"
33
+ }
34
+ ],
35
+ "pagesTruncated": false,
36
+ "navigation": {
37
+ "file": "nav.json",
38
+ "items": 3
39
+ },
40
+ "redirects": {
41
+ "file": "redirects.json",
42
+ "revision": "0000000000000000000000000000000000000000000000000000000000000001",
43
+ "entries": 4
44
+ },
45
+ "settings": [
46
+ {
47
+ "settingsType": "SETTING_TYPE_TAPROOT_STYLES",
48
+ "file": "settings/taproot-styles.json",
49
+ "entityId": "a0000000-0000-4000-8000-000000000020"
50
+ },
51
+ {
52
+ "settingsType": "SETTING_TYPE_BRAND",
53
+ "file": "settings/brand.json",
54
+ "entityId": "a0000000-0000-4000-8000-000000000021"
55
+ },
56
+ {
57
+ "settingsType": "SETTING_TYPE_SITE_HEADER",
58
+ "file": "settings/site-header.json",
59
+ "entityId": "a0000000-0000-4000-8000-000000000022"
60
+ },
61
+ {
62
+ "settingsType": "SETTING_TYPE_SITE_PUBLISHING_PREFERENCES",
63
+ "file": "settings/site-publishing-preferences.json",
64
+ "entityId": "a0000000-0000-4000-8000-000000000023"
65
+ }
66
+ ],
67
+ "settingsSkipped": [],
68
+ "fixture": {
69
+ "contractVersion": 1,
70
+ "imageIds": [
71
+ "a0000000-0000-4000-8000-0000000000a0",
72
+ "a0000000-0000-4000-8000-0000000000a1",
73
+ "a0000000-0000-4000-8000-0000000000a2"
74
+ ],
75
+ "deliveryOrigins": [
76
+ "https://static.example.test"
77
+ ]
78
+ }
79
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "siteId": "a0000000-0000-4000-8000-000000000001",
3
+ "navItems": [
4
+ {
5
+ "id": "a0000000-0000-4000-8000-000000000030",
6
+ "kind": "NAV_ITEM_KIND_PAGE",
7
+ "title": "Home",
8
+ "resourceId": "a0000000-0000-4000-8000-000000000011",
9
+ "children": []
10
+ },
11
+ {
12
+ "id": "a0000000-0000-4000-8000-000000000031",
13
+ "kind": "NAV_ITEM_KIND_PAGE",
14
+ "title": "Visit",
15
+ "resourceId": "a0000000-0000-4000-8000-000000000013",
16
+ "children": []
17
+ },
18
+ {
19
+ "id": "a0000000-0000-4000-8000-000000000032",
20
+ "kind": "NAV_ITEM_KIND_EXTERNAL_URL",
21
+ "title": "Book a class",
22
+ "externalUrl": "https://schedule.example.test/riverbend",
23
+ "children": []
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Riverbend Wellness
3
+ path: ""
4
+ description: An example home page for the offline authoring fixture.
5
+ ---
6
+
7
+ ```component:hero-section
8
+ {
9
+ "overline": "Riverbend Wellness · Elm Harbor",
10
+ "title": "An example site you can validate before you have one.",
11
+ "titleSize": "display",
12
+ "lead": "Riverbend Wellness is invented. It exists so taproot-site validate has a complete workspace to check on a machine that has never pulled a real site.",
13
+ "primaryAction": {
14
+ "label": "Visit the studio",
15
+ "url": "/visit"
16
+ },
17
+ "secondaryAction": {
18
+ "label": "Book a class",
19
+ "url": "https://schedule.example.test/riverbend"
20
+ },
21
+ "alignment": "start",
22
+ "media": {
23
+ "imageId": "a0000000-0000-4000-8000-0000000000a2",
24
+ "src": "https://static.example.test/site/img/riverbend-studio-1280.webp",
25
+ "urls": [
26
+ {
27
+ "minWidth": 640,
28
+ "url": "https://static.example.test/site/img/riverbend-studio-640.webp"
29
+ },
30
+ {
31
+ "minWidth": 1280,
32
+ "url": "https://static.example.test/site/img/riverbend-studio-1280.webp"
33
+ }
34
+ ],
35
+ "width": 1600,
36
+ "height": 1067,
37
+ "alt": "An example photograph placeholder for the fixture studio"
38
+ },
39
+ "mediaArrangement": "split",
40
+ "mediaPosition": "after",
41
+ "mediaWidth": "equal"
42
+ }
43
+ ```
44
+
45
+ :::section {"context":"subtle","contentPadding":"standard","surface":"none"}
46
+ ## What this fixture is for
47
+
48
+ Every value here is fictional. The studio, the street, the telephone number,
49
+ and the image delivery host are all invented, and the image identities are
50
+ declared in `manifest.fixture.json` under the `fixture` block so `validate`
51
+ can bind them without a network.
52
+
53
+ Run `taproot-site help fixture` for the manifest contract this directory
54
+ follows, and `taproot-site help page free-form` for the document vocabulary
55
+ these pages are written in.
56
+
57
+ ```inline-facts
58
+ [
59
+ {
60
+ "value": "2 pages",
61
+ "label": "Free-form sources"
62
+ },
63
+ {
64
+ "value": "(555) 555-0147",
65
+ "label": "Reserved example number",
66
+ "url": "tel:+15555550147"
67
+ },
68
+ {
69
+ "value": "12 Alder Street, Elm Harbor",
70
+ "label": "Invented address"
71
+ }
72
+ ]
73
+ ```
74
+ :::
75
+
76
+ :::section {"context":"inverted","contentPadding":"standard","surface":"none"}
77
+ ## Change one thing and watch it fail
78
+
79
+ The fastest way to learn the contract is to break it. Point a navigation item
80
+ at a resource identity this fixture does not declare, or set
81
+ `defaultScheme` to a value outside `system`, `light`, and `dark`, and
82
+ `validate` names the file and the field it refused.
83
+
84
+ ```component:cta
85
+ {
86
+ "heading": "Then put it back.",
87
+ "description": "Copy this directory somewhere writable before you edit it; validate never writes to the fixture it reads.",
88
+ "buttonText": "Visit the studio",
89
+ "buttonUrl": "/visit",
90
+ "variant": "primary"
91
+ }
92
+ ```
93
+ :::
@@ -0,0 +1,33 @@
1
+ ---
2
+ title: Visit the studio
3
+ path: visit
4
+ description: An example second page for the offline authoring fixture.
5
+ ---
6
+
7
+ :::section {"contentPadding":"standard","surface":"none"}
8
+ ## Visit the studio
9
+
10
+ Riverbend Wellness is a fictional studio in the fictional town of Elm Harbor.
11
+ Nothing on this page describes a real place, and no address, telephone number,
12
+ or hostname here resolves to one.
13
+
14
+ - **12 Alder Street, Elm Harbor** — an invented address
15
+ - **(555) 555-0147** — from the reserved example telephone range
16
+ - **hello@example.test** — a reserved example domain
17
+
18
+ This page exists so the fixture binds more than one page identity: the
19
+ navigation tree points at both, the footer links to both, and `validate`
20
+ proves every reference resolves inside the fixture.
21
+ :::
22
+
23
+ :::section {"context":"raised","contentPadding":"standard","surface":"elevated"}
24
+ ## Hours
25
+
26
+ | Day | Doors | Last class |
27
+ | --- | --- | --- |
28
+ | Monday to Friday | 6:00 AM | 7:30 PM |
29
+ | Saturday | 7:30 AM | 4:00 PM |
30
+ | Sunday | 8:00 AM | 2:00 PM |
31
+
32
+ Every figure above is invented for the fixture.
33
+ :::
@@ -0,0 +1,33 @@
1
+ {
2
+ "siteId": "a0000000-0000-4000-8000-000000000001",
3
+ "revision": "0000000000000000000000000000000000000000000000000000000000000001",
4
+ "entries": [
5
+ {
6
+ "path": "/classes.html",
7
+ "kind": "redirect",
8
+ "target": "/visit",
9
+ "status": 301,
10
+ "origin": "authored"
11
+ },
12
+ {
13
+ "path": "/schedule",
14
+ "kind": "redirect",
15
+ "target": "https://booking.example.test/riverbend",
16
+ "status": 302,
17
+ "origin": "authored"
18
+ },
19
+ {
20
+ "path": "/summer-2019-retreat.html",
21
+ "kind": "gone",
22
+ "status": 410,
23
+ "origin": "authored"
24
+ },
25
+ {
26
+ "path": "/welcome.html",
27
+ "kind": "redirect",
28
+ "target": "/",
29
+ "status": 301,
30
+ "origin": "path_history"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "entityId": "a0000000-0000-4000-8000-000000000021",
3
+ "settingsType": "SETTING_TYPE_BRAND",
4
+ "settings": {
5
+ "faviconId": "a0000000-0000-4000-8000-0000000000a0"
6
+ }
7
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "entityId": "a0000000-0000-4000-8000-000000000022",
3
+ "settingsType": "SETTING_TYPE_SITE_HEADER",
4
+ "settings": {
5
+ "headerLayout": "standard",
6
+ "headerWidth": "contained",
7
+ "navDrawerStyle": "full-screen",
8
+ "navDrawerTransition": "fade",
9
+ "brandText": "Riverbend Wellness",
10
+ "logoAlt": "Riverbend Wellness",
11
+ "navMenuDisplay": "auto",
12
+ "headerPosition": "sticky",
13
+ "showThemeToggle": true,
14
+ "showBrandText": true,
15
+ "brandLogoSize": "standard",
16
+ "brandHoverGrow": false,
17
+ "brandColor": "--esp-color-headings",
18
+ "lightBrandColor": "--esp-color-headings",
19
+ "darkBrandColor": "--esp-color-headings"
20
+ }
21
+ }
@@ -0,0 +1,107 @@
1
+ {
2
+ "entityId": "a0000000-0000-4000-8000-000000000023",
3
+ "settingsType": "SETTING_TYPE_SITE_PUBLISHING_PREFERENCES",
4
+ "settings": {
5
+ "footerSettings": {
6
+ "enabled": true,
7
+ "showBrand": true,
8
+ "showBrandText": true,
9
+ "asideCta": {
10
+ "id": "a0000000-0000-4000-8000-000000000040",
11
+ "label": "Book a class",
12
+ "externalUrl": "https://schedule.example.test/riverbend"
13
+ },
14
+ "bottomLinks": [
15
+ {
16
+ "id": "a0000000-0000-4000-8000-000000000041",
17
+ "label": "Visit",
18
+ "pageResourceId": "a0000000-0000-4000-8000-000000000013"
19
+ }
20
+ ],
21
+ "light": {
22
+ "backgroundColor": "--esp-color-layer-1",
23
+ "textColor": "--esp-color-text",
24
+ "headingColor": "--esp-color-headings",
25
+ "linkColor": "--esp-color-link",
26
+ "linkHoverColor": "--esp-color-link-hover",
27
+ "backgroundImageId": "",
28
+ "backgroundImageOpacity": 0,
29
+ "backgroundPresentation": "FOOTER_BACKGROUND_PRESENTATION_COVER",
30
+ "backgroundFade": "FOOTER_FADE_MODE_NONE",
31
+ "backgroundRepeatHeightPx": 160,
32
+ "additionalTopPaddingRem": 1
33
+ },
34
+ "dark": {
35
+ "backgroundColor": "--esp-color-layer-1",
36
+ "textColor": "--esp-color-text",
37
+ "headingColor": "--esp-color-headings",
38
+ "linkColor": "--esp-color-link",
39
+ "linkHoverColor": "--esp-color-link-hover",
40
+ "backgroundImageId": "",
41
+ "backgroundImageOpacity": 0,
42
+ "backgroundPresentation": "FOOTER_BACKGROUND_PRESENTATION_COVER",
43
+ "backgroundFade": "FOOTER_FADE_MODE_NONE",
44
+ "backgroundRepeatHeightPx": 160,
45
+ "additionalTopPaddingRem": 1
46
+ },
47
+ "linkColumns": [
48
+ {
49
+ "id": "a0000000-0000-4000-8000-000000000042",
50
+ "groups": [
51
+ {
52
+ "id": "a0000000-0000-4000-8000-000000000043",
53
+ "heading": "Studio",
54
+ "links": [
55
+ {
56
+ "id": "a0000000-0000-4000-8000-000000000044",
57
+ "label": "Home",
58
+ "pageResourceId": "a0000000-0000-4000-8000-000000000011"
59
+ },
60
+ {
61
+ "id": "a0000000-0000-4000-8000-000000000045",
62
+ "label": "Visit",
63
+ "pageResourceId": "a0000000-0000-4000-8000-000000000013"
64
+ }
65
+ ]
66
+ }
67
+ ]
68
+ }
69
+ ],
70
+ "asideHeadingContent": {
71
+ "paragraphs": [
72
+ {
73
+ "runs": [
74
+ {
75
+ "text": "Everything here is invented.",
76
+ "bold": true
77
+ }
78
+ ]
79
+ }
80
+ ]
81
+ },
82
+ "asideBodyContent": {
83
+ "paragraphs": [
84
+ {
85
+ "runs": [
86
+ {
87
+ "text": "Riverbend Wellness is a fictional studio used to demonstrate the authoring contract offline."
88
+ }
89
+ ]
90
+ }
91
+ ]
92
+ },
93
+ "bottomContent": {
94
+ "paragraphs": [
95
+ {
96
+ "runs": [
97
+ {
98
+ "text": "An example fixture. Not a real business."
99
+ }
100
+ ]
101
+ }
102
+ ]
103
+ },
104
+ "featureImage": null
105
+ }
106
+ }
107
+ }