@anyday/sanity-workspace-manager 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -6,17 +6,19 @@ the brand, the central Studio entry and the Vercel site. It holds no code for an
6
6
  Each kind of site (a **project type**) is described instead by a `workspace-manager.json` manifest
7
7
  that lives in that site's repo.
8
8
 
9
- This tool writes and saves that manifest:
9
+ This tool makes and saves that manifest, the way `sanity typegen generate` makes types:
10
10
 
11
- - **`init`** drafts `workspace-manager.json` from the site repo, its Sanity schema and, optionally,
12
- the live Sanity project, then lists what it guessed so you can review it.
13
- - **`register`** checks the manifest and saves it to the workspace manager, which then offers the
14
- project type in the Workspaces app.
11
+ - **`generate`** writes `workspace-manager.json` from the repo's schema, the live Sanity project
12
+ and `workspace-manager.config.json`, which holds your answers. Change the schema, run it again.
13
+ - **`check`** fails when the manifest is out of date with the schema or was edited by hand.
14
+ - **`register`** saves the manifest to the workspace manager, which then offers the project type in
15
+ the Workspaces app.
15
16
 
16
17
  ## Requirements
17
18
 
18
19
  - Node.js 20 or later.
19
- - A Sanity login: run `npx sanity login` once, or set `SANITY_AUTH_TOKEN`.
20
+ - A Sanity login for `generate` and `register`: run `npx sanity login` once, or set
21
+ `SANITY_AUTH_TOKEN`. `check` needs none.
20
22
  - For `register`, the **Administrator** role in the manifest's Sanity organisation.
21
23
 
22
24
  ## Quick start
@@ -27,117 +29,157 @@ From the root of the site repo:
27
29
  # 1. Export the Studio's schema (writes sanity.schema.json)
28
30
  cd studio && npx sanity schema extract && cd ..
29
31
 
30
- # 2. Draft the manifest from the repo and the live project; it lists the site's pages to pick
31
- # starter content from
32
- npx @anyday/sanity-workspace-manager init --live
32
+ # 2. Answer a few questions and pick starter pages; writes the config and the manifest
33
+ npx @anyday/sanity-workspace-manager generate
33
34
 
34
- # 3. Review workspace-manager.json, then commit it next to the schema
35
+ # 3. Review workspace-manager.config.json, commit both files next to the schema
35
36
 
36
37
  # 4. Save it to the workspace manager
37
38
  npx @anyday/sanity-workspace-manager register --api https://anyday-workspace-manager-sanity-api.vercel.app
38
39
  ```
39
40
 
40
- ## init
41
+ After a schema change: extract the schema, run `generate`, commit, `register`.
41
42
 
42
- ```bash
43
- npx @anyday/sanity-workspace-manager init [dir] [options]
44
- ```
43
+ ## The two files
44
+
45
+ | File | Holds | Edited by |
46
+ | ------------------------------- | ------------------------------------------------------------------------------ | ---------- |
47
+ | `workspace-manager.config.json` | Your answers: ids, copy sections, colour role descriptions, starter pages, ... | You |
48
+ | `workspace-manager.json` | Everything else: field mappings, theme defaults, starter page content | `generate` |
45
49
 
46
- `dir` is the site repo, and defaults to the current folder. `init` asks five questions, each with a
47
- suggestion you can accept with Enter:
50
+ Never edit `workspace-manager.json`: its `$generated` line holds a hash, and `check` fails after a
51
+ hand edit. Change the config and run `generate` instead. The config has a JSON Schema, so your
52
+ editor completes and checks it once the package is installed.
48
53
 
49
- | Question | Suggested from |
50
- | ------------------------------------------- | ------------------------------------------------------------------------------------- |
51
- | Project type id, short and lowercase | The repo name in the git remote, e.g. `acme-site` |
52
- | Name shown in the app | The same repo name, capitalised |
53
- | Sanity project to copy schemas from | A `projectId: '…'` written in the Studio's config, if there is one |
54
- | Sanity organisation to create workspaces in | Looked up from that project with your login |
55
- | Vercel team for the sites | Nothing: press Enter for Anyday's team; give a team id only for the client's own team |
54
+ Add this line to the site repo's `.gitattributes`, so GitHub marks the manifest as generated and
55
+ collapses its diff in PRs; `generate` reminds you until it's there:
56
+
57
+ ```text
58
+ workspace-manager.json linguist-generated=true
59
+ ```
56
60
 
57
- The id is permanent: jobs store it and it prefixes each site's Vercel project. Registering a
58
- different id later creates a second project type instead of updating this one.
61
+ | Config field | Holds |
62
+ | ---------------- | ----------------------------------------------------------------------------------------------------------- |
63
+ | `id` | Permanent: jobs store it and it prefixes each site's Vercel project |
64
+ | `title` | The name shown in the Workspaces app |
65
+ | `organizationId` | The Sanity organisation new workspaces are created in |
66
+ | `vercelTeamId` | The client's own Vercel team; left out for Anyday's |
67
+ | `site` | The GitHub repo and branch each new site deploys from |
68
+ | `studioOrigins` | The Studios that open new workspaces |
69
+ | `sections` | Each content type with its label in the Copy picker, or `false` to leave it out |
70
+ | `colorRoles` | Each theme colour with a short description of where it's used, shown to whoever sets up a brand |
71
+ | `themeFiles` | Theme fields with uploaded files (fonts) to copy into new sites; fonts are often licensed per brand |
72
+ | `starter` | What **Start fresh** writes: picked live pages, or `[]` for one empty home page (see below) |
73
+ | ... | `singletons`, `homePage`, `settingsFields`: guessed from the schema on the first run, asked when they break |
74
+
75
+ ## generate
59
76
 
60
- ### Which command to run
77
+ ```bash
78
+ npx @anyday/sanity-workspace-manager generate [dir] [options]
79
+ ```
61
80
 
62
- | Command | Questions | Starter pages | Use it when |
63
- | ---------------------------------- | ------------------------ | ---------------------------------------------- | --------------------------------------------- |
64
- | `init --live` | Yes, then a list to pick | The pages you pick (none: one empty home page) | Usually: real theme, colours and pages |
65
- | `init --live --starter home,about` | Yes, no list | Those pages | You already know which pages |
66
- | `init --live --yes` | None, suggestions taken | One empty home page | Scripts, or no starter content wanted |
67
- | `init` | Yes | One empty home page | The project has no content yet, or no sign-in |
81
+ `dir` is the site repo, and defaults to the current folder. The first run asks who the project type
82
+ is for, each with a suggestion you accept with Enter, then lists the live site's pages to pick
83
+ starter pages from. Answers go in the config. Later runs reuse them and ask only about what changed
84
+ in the schema:
68
85
 
69
- Without `--live`, `init` reads only the repo's files, so the manifest has the structure but no
70
- values: colour defaults are `#000000`, theme defaults and `studioOrigins` are empty. Fill them in
71
- by hand, or run again with `--live --force` once the project has content.
86
+ ```text
87
+ generalSettings.logo is gone. Which field is the logo? [brandLogo/favicon/none] (none):
88
+ Copy section mediaMention is gone from the schema. Was it renamed? [pressMention/no] (no):
89
+ New content type event: offer it as a copy section? [y/n] (y):
90
+ ```
72
91
 
73
- Starter pages are only what **Start fresh** writes. **Copy from a workspace** is picked in the
74
- Workspaces app for each new workspace, and reads its source live; it needs no `init` option.
92
+ A renamed field reads as one removed and one added, so `generate` asks rather than guesses.
93
+
94
+ | Option | Does |
95
+ | -------------------- | ---------------------------------------------------------------------------------- |
96
+ | `--yes` | Takes the suggested answers without asking, and lists them |
97
+ | `--offline` | Doesn't read the live project: keeps the live values already in the manifest |
98
+ | `--diff` | Shows what would change in both files, and writes nothing |
99
+ | `--ask-again` | Asks the saved questions again, e.g. to change copy sections or pick starter pages |
100
+ | `--project <id>` | The Sanity project new workspaces copy their schema from |
101
+ | `--org <id>` | The Sanity organisation new workspaces are created in |
102
+ | `--vercel-team <id>` | The client's own Vercel team for the sites |
103
+ | `--studio <origin>` | The Studio that opens new workspaces, e.g. `https://acme.sanity.studio` |
104
+ | `--dataset <name>` | The dataset live values are read from (default `production`) |
105
+ | `--starter <pages>` | Starter pages without the list: `home`, slugs or ids, comma-separated |
75
106
 
76
107
  ### What it reads
77
108
 
78
- | Source | Used for |
79
- | ------------------------------- | ---------------------------------------------------------------------------------------------------- |
80
- | git remote and default branch | `site.repo`, `site.branch`, the suggested id and name |
81
- | `.env.example`, names only | `siteEnv`, the variables each new site needs |
82
- | `sanity.schema.json` | copy sections, singletons, home page, language documents, settings fields, colour and font templates |
83
- | the live project, with `--live` | theme defaults, colour defaults, starter pages, and its deployed Studio for `studioOrigins` |
109
+ | Source | Used for |
110
+ | ----------------------------- | -------------------------------------------------------------------------------------------- |
111
+ | git remote and default branch | `site`, the suggested id and name |
112
+ | `.env.example`, names only | `siteEnv`, the variables each new site needs |
113
+ | `sanity.schema.json` | copy sections, singletons, home page, language documents, settings fields, colours and fonts |
114
+ | the live project | organisation, Studio, theme colours and defaults, fonts named in `themeFiles`, starter pages |
84
115
 
85
116
  `sanity.schema.json` is looked for in the repo root, `frontend`, `web`, `site`, `studio`, `apps`
86
117
  and each folder in `apps`.
87
118
 
88
- ### Options
89
-
90
- | Option | Does |
91
- | -------------------- | ----------------------------------------------------------------------------------------------- |
92
- | `--yes` | Asks nothing: takes every suggestion and skips the starter list |
93
- | `--project <id>` | The Sanity project new workspaces copy their schema from |
94
- | `--org <id>` | The Sanity organisation new workspaces are created in (found with `--live`) |
95
- | `--vercel-team <id>` | The client's own Vercel team for the sites; leave out for Anyday's team |
96
- | `--studio <origin>` | The Studio that opens new workspaces, e.g. `https://acme.sanity.studio` (found with `--live`) |
97
- | `--live` | Reads the current theme, settings and starter pages from that project |
98
- | `--dataset <name>` | The dataset `--live` reads (default `production`) |
99
- | `--starter <pages>` | Starter pages without the list: `home`, slugs or document ids, comma-separated (needs `--live`) |
100
- | `--force` | Replaces an existing `workspace-manager.json`, keeping its file-backed theme defaults (fonts) |
101
- | `--print` | Prints the draft instead of writing it |
119
+ ### Without access to the live project
120
+
121
+ `generate` says first why it can't read the live project, and what to do:
122
+
123
+ | Situation | What it prints | Fix |
124
+ | ---------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
125
+ | Not signed in | `Not signed in to Sanity: run npx sanity login to read live values` | Run `npx sanity login` |
126
+ | Expired or bad session | `Your Sanity session is missing or expired: run npx sanity login …` | Run `npx sanity login`; if `SANITY_AUTH_TOKEN` is set, it wins over the login, so fix or unset it |
127
+ | No access | `Your Sanity account can't read project <id>: ask one of its Administrators …` | Ask an Administrator of that project to add you (Viewer is enough) |
128
+ | Anything else | `Couldn't read the live project <id>: <reason>` | Follow the reason, e.g. a wrong `--project` or `--dataset` |
129
+
130
+ It then offers to continue from the schema only. Values only the live project holds (colours,
131
+ fonts, starter pages) are kept from the existing manifest, and the last line says so. On a first
132
+ run they start as placeholders: black colours, system fonts and one empty home page, and
133
+ `register` asks before saving them. Sign in and run `generate` again to read the live values.
102
134
 
103
135
  ### Starter pages
104
136
 
105
- Starter pages are what a new workspace starts with when it doesn't copy another workspace. With
106
- `--live`, `init` lists the live site's pages in the home page's language, home first:
137
+ Starter pages are what a new workspace starts with when it doesn't copy another workspace. They
138
+ always come from the live site, so you update them in the Studio, never by hand in a file. The
139
+ first run lists the live pages in the home page's language, home first:
107
140
 
108
141
  ```text
109
142
  Pages of the live site:
110
143
  1. Home (/home) ← home
111
144
  2. About us (/about-us)
112
- 3. Accessibility (/accessibility)
145
+ 3. Contact (/contact)
113
146
  Starter pages, by number (e.g. 1,3; Enter for one empty home page):
114
147
  ```
115
148
 
116
- Pick by number, or press Enter for one empty home page. To skip the list, name the pages with
117
- `--starter`: `home` means whichever page the settings mark as the home page, and any other entry
118
- is a page's slug or document id, e.g. `--starter home,about-us`. When translations share a
119
- slug, the home page's language wins. With `--yes` and no `--starter`, the starter is one empty
120
- home page.
149
+ The config saves only your pick, e.g. `"starter": ["home", "about-us"]`, and every run reads those
150
+ pages again. `home` means whichever page the settings mark as the home page; anything else is a
151
+ slug or document id. `[]` means one empty home page.
152
+
153
+ **To change the starter pages later**, run `generate --ask-again` and pick again from the list, or
154
+ edit the config's `starter` and run `generate`.
155
+
156
+ A new workspace has only its starter pages, so `generate` removes each link to another document (a
157
+ doctor card, a button to another page) and lists what it removed. Fields the schema no longer has
158
+ are dropped too. Starter pages get `{{siteName}}` and `{{language}}` where the source site's name
159
+ and language were.
121
160
 
122
- Starter pages get `{{siteName}}` and `{{language}}` where the source site's name and language
123
- were. Fonts are left out of theme defaults, since uploaded fonts are often licensed per brand: add
124
- them by hand when they may be reused, and a later `--force` keeps them.
161
+ ### Moving from a hand-made manifest
162
+
163
+ Run `generate` in a repo that has a `workspace-manager.json` and no config: it moves every choice
164
+ the manifest holds, including copy section labels, colour descriptions and fonts, into the config.
165
+ It then lists the live pages to pick starter pages from; until you pick, it keeps the manifest's
166
+ existing starter pages.
167
+
168
+ ## check
169
+
170
+ ```bash
171
+ npx @anyday/sanity-workspace-manager check [dir]
172
+ ```
125
173
 
126
- ### Review before registering
174
+ Read-only. Fails, naming each difference, when:
127
175
 
128
- The draft is checked against the manifest's JSON Schema before it is written, and every guess is
129
- listed afterwards. Check these in particular:
176
+ - the schema changed in a way the manifest uses (a renamed or removed field, a new content type),
177
+ - `workspace-manager.json` was edited by hand, or
178
+ - the starter pages have fields the schema no longer has.
130
179
 
131
- - **Copy sections** start as every content type. Keep only the ones editors should be able to copy
132
- between workspaces.
133
- - **Starter pages** must not link to documents outside the starter: a new workspace won't have
134
- them, and saving the page fails. `init` names any such links.
135
- - **Colour roles** come with empty descriptions. Add a short one each, saying where the colour is
136
- used, so whoever sets up a brand knows what they are picking.
137
- - **Language documents and singletons** are guessed from type names (`…Settings`, `l10n.locale`).
138
- - **Vercel team:** sites go to Anyday's team unless you gave a team id. A client's own team needs
139
- its token on the API as `VERCEL_TOKEN_<team id>` (a secret, added on Vercel by Anyday, never in
140
- the manifest); `register` warns while it is missing.
180
+ Only changes to fields **the manifest uses** matter: the logo, site name, theme colours and fonts,
181
+ home page, language fields, content types, and the fields on starter pages. `check` reads only the
182
+ repo, so it can run in CI without signing in.
141
183
 
142
184
  ## register
143
185
 
@@ -152,40 +194,48 @@ workspace manager. The live API is `https://anyday-workspace-manager-sanity-api.
152
194
  - You must be an **Administrator** of the manifest's `organizationId`.
153
195
  - Registering again replaces the saved copy for that `id`, so a changed manifest needs no delete
154
196
  first.
155
- - It prints `Registered <name> (<id>)` when saved.
156
- - It warns when the manifest names a Vercel team the API has no token for yet.
197
+ - It asks before saving placeholder colours or fonts; `--yes` skips the question.
198
+ - `--dry-run` checks the manifest without saving it.
199
+ - It warns when the manifest names a Vercel team the API has no token for yet. A client's own team
200
+ needs its token on the API as `VERCEL_TOKEN_<team id>` (a secret, added on Vercel by Anyday).
157
201
 
158
202
  ## Signing in
159
203
 
160
- Both commands use `SANITY_AUTH_TOKEN` when it is set, and otherwise the token `npx sanity login`
204
+ All commands use `SANITY_AUTH_TOKEN` when it is set, and otherwise the token `npx sanity login`
161
205
  saved. Use your own login: the workspace manager refuses robot tokens for `register`, so that
162
206
  every project type traces back to a person. If a robot token is exported in your shell (for
163
207
  example for a Studio deploy), `unset SANITY_AUTH_TOKEN` before registering.
164
208
 
165
209
  ## Troubleshooting
166
210
 
167
- | Message | Fix |
168
- | ----------------------------------------------------- | ------------------------------------------------------------------ |
169
- | `No sanity.schema.json` | Run `npx sanity schema extract` in the Studio folder |
170
- | `workspace-manager.json exists: pass --force` | Add `--force` to replace it, or `--print` to compare |
171
- | `--starter needs --live` | Add `--live`; starter pages are read from the live project |
172
- | `Pass --api <url> or set WORKSPACE_MANAGER_API_URL` | Add `--api https://anyday-workspace-manager-sanity-api.vercel.app` |
173
- | `Use your own Sanity login, not a robot token` | `unset SANITY_AUTH_TOKEN` and use `npx sanity login` |
174
- | `Register only as an Administrator of organisation …` | Ask an Administrator of that organisation to run `register` |
211
+ | Message | Fix |
212
+ | ----------------------------------------------------- | ----------------------------------------------------------------------- |
213
+ | `No sanity.schema.json` | Run `npx sanity schema extract` in the Studio folder |
214
+ | `workspace-manager.json is out of date` | Run `generate` and commit both files |
215
+ | `was edited by hand` | Put the change in the config, then run `generate` |
216
+ | `No page with the slug or id "…"` | Fix the config's `starter`, or run `generate --ask-again` to pick again |
217
+ | `No GitHub remote found` | Add an `origin` remote, or set `site.repo` in the config |
218
+ | `Not signed in to Sanity` | Run `npx sanity login` |
219
+ | `Your Sanity session is missing or expired` | Run `npx sanity login`, or fix or unset `SANITY_AUTH_TOKEN` |
220
+ | `Your Sanity account can't read project …` | Ask an Administrator of that project to add you (Viewer is enough) |
221
+ | `Couldn't read the live project …` | Follow the reason, e.g. a wrong `--project` or `--dataset` |
222
+ | `Pass --api <url> or set WORKSPACE_MANAGER_API_URL` | Add `--api https://anyday-workspace-manager-sanity-api.vercel.app` |
223
+ | `Use your own Sanity login, not a robot token` | `unset SANITY_AUTH_TOKEN` and use `npx sanity login` |
224
+ | `Register only as an Administrator of organisation …` | Ask an Administrator of that organisation to run `register` |
175
225
 
176
226
  ## How it fits together
177
227
 
178
228
  The full design, including every manifest field, is in
179
229
  [docs/project-config.md](https://github.com/anydayagency/anyday-workspace-manager-sanity/blob/main/docs/project-config.md),
180
- and an overview in
181
- [docs/how-it-works.md](https://github.com/anydayagency/anyday-workspace-manager-sanity/blob/main/docs/how-it-works.md)
230
+ and how generate works in
231
+ [docs/manifest-generate.md](https://github.com/anydayagency/anyday-workspace-manager-sanity/blob/main/docs/manifest-generate.md)
182
232
  (Anyday's GitHub organisation only).
183
233
 
184
234
  ## Developing and publishing
185
235
 
186
236
  From a clone of the workspace manager repo, run the CLI without npm:
187
- `npm run cli -- init ../my-site`. `npm run build` bundles `src/cli.ts`, with the manifest code it
188
- borrows from the API, into `dist/cli.mjs`; only `@sanity/client` and `ajv` stay dependencies.
237
+ `npm run cli -- generate ../my-site`. `npm run build` bundles `src/cli.ts`, with the manifest code
238
+ it borrows from the API, into `dist/cli.mjs`; only `@sanity/client` and `ajv` stay dependencies.
189
239
 
190
240
  Publish from `main`: bump `version`, then run
191
241
  `npm publish --workspace=@anyday/sanity-workspace-manager --access public --auth-type=web` from the
@@ -0,0 +1,157 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://github.com/anydayagency/anyday-workspace-manager-sanity/workspace-manager.config.schema.json",
4
+ "title": "Workspace manager config",
5
+ "description": "The decisions behind a site's workspace-manager.json. Edit this file, then run `sanity-workspace-manager generate`.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "id",
10
+ "title",
11
+ "organizationId",
12
+ "schemaSourceProjectId",
13
+ "site",
14
+ "singletons",
15
+ "homePage",
16
+ "settingsFields",
17
+ "sections",
18
+ "colorRoles"
19
+ ],
20
+ "definitions": {
21
+ "nullableField": {
22
+ "type": ["string", "null"]
23
+ }
24
+ },
25
+ "properties": {
26
+ "$schema": {
27
+ "type": "string"
28
+ },
29
+ "id": {
30
+ "description": "Permanent: jobs store it and it prefixes each site's Vercel project.",
31
+ "type": "string",
32
+ "pattern": "^[a-z0-9][a-z0-9-]{0,31}$"
33
+ },
34
+ "title": {
35
+ "description": "The name shown in the Workspaces app.",
36
+ "type": "string"
37
+ },
38
+ "organizationId": {
39
+ "description": "The Sanity organisation new workspaces are created in.",
40
+ "type": "string"
41
+ },
42
+ "vercelTeamId": {
43
+ "description": "The client's own Vercel team; leave out for Anyday's.",
44
+ "type": "string"
45
+ },
46
+ "schemaSourceProjectId": {
47
+ "description": "The Sanity project new workspaces copy their schema from.",
48
+ "type": "string"
49
+ },
50
+ "dataset": {
51
+ "description": "The dataset live values are read from. Default: production.",
52
+ "type": "string"
53
+ },
54
+ "site": {
55
+ "type": "object",
56
+ "additionalProperties": false,
57
+ "required": ["repo", "branch"],
58
+ "properties": {
59
+ "repo": {
60
+ "description": "owner/name on GitHub.",
61
+ "type": "string",
62
+ "pattern": "^[\\w.-]+/[\\w.-]+$"
63
+ },
64
+ "branch": {
65
+ "type": "string"
66
+ },
67
+ "revalidatePath": {
68
+ "type": "string"
69
+ }
70
+ }
71
+ },
72
+ "studioOrigins": {
73
+ "description": "The Studios that open new workspaces, e.g. https://acme.sanity.studio.",
74
+ "type": "array",
75
+ "items": {
76
+ "type": "string",
77
+ "pattern": "^https://"
78
+ }
79
+ },
80
+ "singletons": {
81
+ "description": "Settings documents: one each, never offered for copy.",
82
+ "type": "array",
83
+ "items": {
84
+ "type": "string"
85
+ }
86
+ },
87
+ "homePage": {
88
+ "description": "The settings field that references the home page.",
89
+ "type": "object",
90
+ "additionalProperties": false,
91
+ "required": ["documentId", "field"],
92
+ "properties": {
93
+ "documentId": {
94
+ "type": "string"
95
+ },
96
+ "field": {
97
+ "type": "string"
98
+ }
99
+ }
100
+ },
101
+ "settingsFields": {
102
+ "description": "The field on the home page's settings document that holds each brand value; null for none.",
103
+ "type": "object",
104
+ "additionalProperties": false,
105
+ "properties": {
106
+ "siteName": {
107
+ "$ref": "#/definitions/nullableField"
108
+ },
109
+ "domain": {
110
+ "$ref": "#/definitions/nullableField"
111
+ },
112
+ "logo": {
113
+ "$ref": "#/definitions/nullableField"
114
+ },
115
+ "favicon": {
116
+ "$ref": "#/definitions/nullableField"
117
+ }
118
+ }
119
+ },
120
+ "sections": {
121
+ "description": "Each content type, with its label in the Copy picker, or false to leave it out.",
122
+ "type": "object",
123
+ "additionalProperties": {
124
+ "anyOf": [
125
+ {
126
+ "type": "string",
127
+ "minLength": 1
128
+ },
129
+ {
130
+ "const": false
131
+ }
132
+ ]
133
+ }
134
+ },
135
+ "colorRoles": {
136
+ "description": "Each colour role on the theme, with where it is used, shown to whoever sets up a brand.",
137
+ "type": "object",
138
+ "additionalProperties": {
139
+ "type": "string"
140
+ }
141
+ },
142
+ "themeFiles": {
143
+ "description": "Theme fields with uploaded files, e.g. fonts, to copy into new sites. Fonts are often licensed per brand.",
144
+ "type": "array",
145
+ "items": {
146
+ "type": "string"
147
+ }
148
+ },
149
+ "starter": {
150
+ "description": "What Start fresh writes: live pages (`home`, slugs or ids), or [] for one empty home page.",
151
+ "type": "array",
152
+ "items": {
153
+ "type": "string"
154
+ }
155
+ }
156
+ }
157
+ }