camox 0.22.0 → 0.24.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.
@@ -78,7 +78,8 @@ toMarkdown: (c) => [`# ${c.title}`, c.description, c.cta];
78
78
  - **Image**: `![alt](filename)`
79
79
  - **File**: `[filename](url)`
80
80
  - **Embed**: raw URL string
81
- - **RepeatableItem**: each item rendered via its own `toMarkdown` (if set on the RepeatableItem options), items joined with `\n\n`
81
+ - **Repeater**: each item rendered via its own `toMarkdown` (if set on the Repeater options), items joined with `\n\n`
82
+ - **ImageList / FileList**: each asset rendered as a markdown bullet
82
83
  - **Boolean/Enum**: raw string value
83
84
 
84
85
  Lines where ALL referenced fields resolve to empty are omitted from output.
@@ -98,13 +99,13 @@ createBlock({
98
99
  content: { ... },
99
100
  })
100
101
 
101
- // Statistics — RepeatableItem with its own toMarkdown
102
+ // Statistics — Repeater with its own toMarkdown
102
103
  createBlock({
103
104
  toMarkdown: (c) => [`## ${c.subtitle}`, c.description, c.statistics],
104
105
  content: {
105
106
  subtitle: Type.String({ default: "..." }),
106
107
  description: Type.String({ default: "..." }),
107
- statistics: Type.RepeatableItem({
108
+ statistics: Type.Repeater({
108
109
  content: {
109
110
  number: Type.String({ default: "100M+" }),
110
111
  label: Type.String({ default: "pages served" }),
@@ -142,7 +143,7 @@ toMarkdown: (c, s) => [
142
143
  ];
143
144
  ```
144
145
 
145
- Only `Type.Boolean` and `Type.Enum` settings can be used this way — trying to reference any other setting is a type error. On a `RepeatableItem`'s own `toMarkdown`, `s` refers to the item's own `settings` (not the parent block's).
146
+ Only `Type.Boolean` and `Type.Enum` settings can be used this way — trying to reference any other setting is a type error. On a `Repeater`'s own `toMarkdown`, `s` refers to the item's own `settings` (not the parent block's).
146
147
 
147
148
  ## Content Field Types
148
149
 
@@ -197,31 +198,38 @@ Type.Link({
197
198
 
198
199
  ### Type.Image
199
200
 
200
- A single image, or a flat array of images.
201
+ A single image render with `block.Image`.
201
202
 
202
203
  ```tsx
203
- // Single image — render with block.Image
204
204
  Type.Image({ title: "Cover photo" });
205
+ ```
206
+
207
+ ### Type.ImageList
208
+
209
+ A flat array of images — render with `block.ImageList` (NOT `block.Repeater`).
205
210
 
206
- // Multiple images — render with block.MultipleAssets (NOT block.Repeater)
207
- Type.Image({ multiple: true, defaultItems: 6, title: "Gallery images" });
211
+ ```tsx
212
+ Type.ImageList({ defaultItems: 6, title: "Gallery images" });
208
213
  ```
209
214
 
210
215
  ### Type.File
211
216
 
212
- A file upload, with MIME type filtering.
217
+ A single file upload, with MIME type filtering — render with `block.File`.
213
218
 
214
219
  ```tsx
215
- // Single file — render with block.File
216
220
  Type.File({
217
221
  accept: ["application/pdf"],
218
222
  title: "PDF Document",
219
223
  });
224
+ ```
220
225
 
221
- // Multiple files — render with block.MultipleAssets (NOT block.Repeater)
222
- Type.File({
226
+ ### Type.FileList
227
+
228
+ A flat array of files — render with `block.FileList` (NOT `block.Repeater`).
229
+
230
+ ```tsx
231
+ Type.FileList({
223
232
  accept: ["application/pdf"],
224
- multiple: true,
225
233
  defaultItems: 0,
226
234
  title: "Documents",
227
235
  });
@@ -241,12 +249,12 @@ Type.Embed({
241
249
 
242
250
  The `default` must match the `pattern` — an error is thrown at definition time otherwise.
243
251
 
244
- ### Type.RepeatableItem
252
+ ### Type.Repeater
245
253
 
246
254
  An array of structured items. Each item is an object with its own fields. This is how you create lists of things (testimonials, features, stats, links...).
247
255
 
248
256
  ```tsx
249
- Type.RepeatableItem({
257
+ Type.Repeater({
250
258
  content: {
251
259
  name: Type.String({ default: "Feature" }),
252
260
  description: Type.String({ default: "Description" }),
@@ -258,15 +266,15 @@ Type.RepeatableItem({
258
266
  });
259
267
  ```
260
268
 
261
- The `toMarkdown` option on RepeatableItem defines how each item is rendered as markdown when the parent block's `toMarkdown` references this field. It is required, same as on the block itself.
269
+ The `toMarkdown` option on Repeater defines how each item is rendered as markdown when the parent block's `toMarkdown` references this field. It is required, same as on the block itself.
262
270
 
263
- Repeatable items can be nested — an item can contain another RepeatableItem:
271
+ Repeaters can be nested — an item can contain another Repeater:
264
272
 
265
273
  ```tsx
266
- columns: Type.RepeatableItem({
274
+ columns: Type.Repeater({
267
275
  content: {
268
276
  title: Type.String({ default: "Column" }),
269
- links: Type.RepeatableItem({
277
+ links: Type.Repeater({
270
278
  content: {
271
279
  link: Type.Link({ default: { text: "Link", href: "#", newTab: false } }),
272
280
  },
@@ -359,7 +367,7 @@ The optional second argument `data` exposes raw values `{ text, href, newTab }`
359
367
  </myBlock.Embed>
360
368
  ```
361
369
 
362
- ### Rendering RepeatableItem fields — `block.Repeater`
370
+ ### Rendering Repeater fields — `block.Repeater`
363
371
 
364
372
  ```tsx
365
373
  <myBlock.Repeater name="features">
@@ -372,7 +380,7 @@ The optional second argument `data` exposes raw values `{ text, href, newTab }`
372
380
  </myBlock.Repeater>
373
381
  ```
374
382
 
375
- Inside a Repeater, the `item` callback argument exposes the same `.Field`, `.Link`, `.Image`, `.File`, `.Embed`, `.MultipleAssets`, and `.Repeater` methods — scoped to that item. This is how nested repeaters work too:
383
+ Inside a Repeater, the `item` callback argument exposes the same `.Field`, `.Link`, `.Image`, `.File`, `.Embed`, `.ImageList`, `.FileList`, and `.Repeater` methods — scoped to that item. This is how nested repeaters work too:
376
384
 
377
385
  ```tsx
378
386
  <footer.Repeater name="columns">
@@ -384,27 +392,27 @@ Inside a Repeater, the `item` callback argument exposes the same `.Field`, `.Lin
384
392
  </footer.Repeater>
385
393
  ```
386
394
 
387
- ### Multiple images / files — `block.MultipleAssets`
395
+ ### Image and File lists — `block.ImageList` / `block.FileList`
388
396
 
389
- For `Type.Image({ multiple: true })` or `Type.File({ multiple: true })`, use `MultipleAssets` (NOT `Repeater`). The render-prop is the same shape as `block.Image` / `block.File` — `(props, data) => …` — invoked once per asset:
397
+ For `Type.ImageList` use `block.ImageList`; for `Type.FileList` use `block.FileList` (NOT `block.Repeater`). The render-prop is the same shape as `block.Image` / `block.File` — `(props, data) => …` — invoked once per asset:
390
398
 
391
399
  ```tsx
392
400
  // Top-level
393
- <gallery.MultipleAssets name="images">
401
+ <gallery.ImageList name="images">
394
402
  {(props) => <img {...props} className="rounded-lg" />}
395
- </gallery.MultipleAssets>
403
+ </gallery.ImageList>
396
404
 
397
- // Nested inside a Type.RepeatableItem
405
+ // Nested inside a Type.Repeater
398
406
  <paragraphGrid.Repeater name="paragraphs">
399
407
  {(item) => (
400
- <item.MultipleAssets name="logos">
408
+ <item.ImageList name="logos">
401
409
  {(props) => <img {...props} className="size-10 object-contain" />}
402
- </item.MultipleAssets>
410
+ </item.ImageList>
403
411
  )}
404
412
  </paragraphGrid.Repeater>
405
413
  ```
406
414
 
407
- `Repeater` is only for `Type.RepeatableItem` arrays. Trying to use `Repeater` on a `Type.Image`/`File` with `multiple: true` is a TypeScript error.
415
+ `Repeater` is only for `Type.Repeater` arrays. Trying to use `Repeater` on an `ImageList` / `FileList` is a TypeScript error.
408
416
 
409
417
  ### Reading settings — `block.useSetting`
410
418
 
@@ -439,6 +447,6 @@ Renders content outside the block's DOM container. Useful for fixed/floating ele
439
447
  5. **Description is for the AI.** Write the `description` as guidance for an LLM — explain when to use this block, what kind of content it's for, and where it fits on a page.
440
448
  6. **`toMarkdown` is required.** Every block must define how its content renders as markdown. Use the builder function `(c) => [...]` and reference content fields via `c.fieldName`.
441
449
  7. **Settings = Enum and Boolean only.** Keep settings simple. Use `content` for everything the user edits inline.
442
- 8. **RepeatableItem minItems >= 1.** You can't have an empty repeatable — there's always at least one item.
450
+ 8. **Repeater minItems >= 1.** You can't have an empty repeater — there's always at least one item.
443
451
  9. **Import path is `"camox/createBlock"`.** Both `Type` and `createBlock` come from this import.
444
452
  10. **Use Tailwind CSS for styling.** All example blocks use Tailwind utility classes. Follow the same patterns: `container mx-auto px-4` for centered content, responsive breakpoints, etc.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: camox-cli
3
- description: "How to read or modify the website's content (pages and the sections inside them) via the Camox CLI. This is a Camox-powered site, so any request about its content — even when phrased generically — is a Camox operation. Use this skill whenever the user wants to add, remove, rename, reorder, or change anything visible on the site: a new page or route, a section / hero / footer / etc., the title or wording shown to visitors, what appears at a URL, the SEO title or social-share preview, the structure shared across pages, etc. Trigger broadly — on phrases like 'add a page', 'put a hero at the top', 'change the headline', 'move this section', 'what's on /about', 'fix the meta title', 'rename this route', 'why does this show up on every page' — and on similar requests even when the user doesn't say 'page', 'block', 'layout', 'CMS', 'Camox', or 'CLI'. When in doubt and the request touches site content, load this skill."
3
+ description: "How to read or modify the website's content (pages and the sections inside them) via the Camox CLI. This is a Camox-powered site, so any request about its content — even when phrased generically — is a Camox operation. Use this skill whenever the user wants to add, remove, rename, reorder, or change anything visible on the site: a new page or route, a section / hero / footer / etc., the title or wording shown to visitors, what appears at a URL, the SEO title or social-share preview, the structure shared across pages, etc. Also covers promoting or syncing content between the dev and production environments (push to prod, pull from prod, check whether the two envs are in sync). Trigger broadly — on phrases like 'add a page', 'put a hero at the top', 'change the headline', 'move this section', 'what's on /about', 'fix the meta title', 'rename this route', 'why does this show up on every page', 'push to production', 'promote my changes to prod', 'pull from production', 'sync dev with prod' — and on similar requests even when the user doesn't say 'page', 'block', 'layout', 'environment', 'CMS', 'Camox', or 'CLI'. When in doubt and the request touches site content or the dev/prod split, load this skill."
4
4
  ---
5
5
 
6
6
  # Using the Camox CLI
@@ -92,6 +92,24 @@ See **Block positioning** below for the full set of options.
92
92
  # Use --parent-page-id <ID> to nest the page under another route.
93
93
  ```
94
94
 
95
+ ### Promote dev to production (or pull production back into dev)
96
+
97
+ The `env` group replicates content in bulk between your dev environment and production — the same two operations the studio's environment menu exposes. Use this when the user wants to publish a batch of dev changes all at once, or wipe dev and start from the live site.
98
+
99
+ ```sh
100
+ # Inspect both directions before committing. Shows whether push/pull are
101
+ # currently safe, and lists any block-definition / layout divergences if not.
102
+ {{CAMOX_CMD}} env check
103
+
104
+ # Overwrite production with your dev environment.
105
+ {{CAMOX_CMD}} env push --yes
106
+
107
+ # Overwrite your dev environment with production.
108
+ {{CAMOX_CMD}} env pull --yes
109
+ ```
110
+
111
+ `--yes` (or `-y`) is required on `push` / `pull` — both replace every page, block, and file in the target environment and the change cannot be undone. See **Environments: dev vs production** below for when to use this vs the per-command `--production` flag.
112
+
95
113
  ## Block positioning
96
114
 
97
115
  `blocks create` and `blocks move` accept the same set of positioning flags. **Pass at most one** — combining them is rejected. `move` requires one (use `--position last` to send a block to the end); `create` defaults to appending at the end if you pass none.
@@ -107,11 +125,15 @@ See **Block positioning** below for the full set of options.
107
125
 
108
126
  Prefer the high-level flags (`--position`, `--after-id`, `--before-id`) — they read naturally and don't require you to know the fractional-index format. The `--after-position` / `--before-position` flags exist for cases where you already have a key in hand (e.g. piping output between commands).
109
127
 
110
- ## The `--production` flag
128
+ ## Environments: dev vs production
129
+
130
+ Every project has two environments: a per-user, isolated **dev** environment that the local dev server reads from, and the shared **production** environment that serves the live site. The CLI gives you two ways to interact with the split — pick the right one for what the user is asking.
111
131
 
112
- By default, every command runs against the **dev environment** — a per-user, isolated copy of the CMS that the dev server reads from. Changes you make here do not affect the live site, so dev is the safe place to experiment.
132
+ ### Per-command: the `--production` flag
113
133
 
114
- Pass `--production` to target the **live CMS** instead:
134
+ By default, every command runs against the dev environment. Changes you make here do not affect the live site, so dev is the safe place to experiment.
135
+
136
+ Pass `--production` to target the live CMS instead, one command at a time:
115
137
 
116
138
  ```sh
117
139
  {{CAMOX_CMD}} pages list --production
@@ -120,6 +142,20 @@ Pass `--production` to target the **live CMS** instead:
120
142
 
121
143
  Only use `--production` when the user has explicitly asked to operate on live content. For everything else — exploration, tentative edits, anything you'd want to be able to throw away — stay on the default dev environment.
122
144
 
145
+ ### Bulk replication: the `env` group
146
+
147
+ When the user wants to **promote a batch of dev changes to production** (or do the inverse — wipe dev and start from production), use the env group instead of running many commands with `--production`:
148
+
149
+ ```sh
150
+ {{CAMOX_CMD}} env check # report compatibility of both push and pull
151
+ {{CAMOX_CMD}} env push --yes # overwrite production with dev
152
+ {{CAMOX_CMD}} env pull --yes # overwrite dev with production
153
+ ```
154
+
155
+ `env check` returns `{ push: { compatible, reasons }, pull: { compatible, reasons } }`. The `reasons` array is empty when that direction is safe; otherwise each entry names a divergence (a block type or layout that exists in one env but not the other, or a block-definition schema mismatch). Fix the divergence first — replicate will refuse with `FAILED_PRECONDITION` and the same reasons in `data.reasons` if you don't.
156
+
157
+ `env push` and `env pull` require `--yes` (or `-y`) because they replace the target environment's entire content. Without it the command refuses and prints what it would have done.
158
+
123
159
  ## Don't write slop — build understanding first
124
160
 
125
161
  Anything you create with this CLI ends up on a real website read by real people. **Never invent generic filler copy** ("Welcome to our amazing platform", "Lorem ipsum"-grade headlines, plausible-sounding-but-fabricated stats, made-up testimonials, fake company names). That kind of content is worse than nothing — it ships, it gets indexed, and the user has to clean it up.