camox 0.22.0 → 0.23.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/dist/core/createApp.d.ts +88 -60
- package/dist/core/createBlock.d.ts +54 -232
- package/dist/core/createBlock.js +46 -38
- package/dist/core/lib/contentType.d.ts +42 -24
- package/dist/core/lib/contentType.js +55 -29
- package/dist/core/lib/fieldTypes.js +28 -16
- package/dist/core/lib/imageTransform.js +73 -0
- package/dist/features/content/CamoxContent.js +3 -3
- package/dist/features/content/components/AssetCard.js +30 -25
- package/dist/features/preview/components/AssetFieldEditor.js +9 -5
- package/dist/features/preview/components/AssetLightbox.js +194 -12
- package/dist/features/preview/components/ItemFieldsEditor.js +8 -9
- package/dist/features/preview/components/MultipleAssetFieldEditor.js +47 -42
- package/dist/features/preview/components/PageContentSheet.js +2 -3
- package/dist/features/preview/components/PageTree.js +66 -68
- package/dist/features/preview/components/useRepeatableItemActions.js +5 -5
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/skills/camox-block/SKILL.md +38 -30
- package/skills/camox-cli/SKILL.md +40 -4
|
@@ -78,7 +78,8 @@ toMarkdown: (c) => [`# ${c.title}`, c.description, c.cta];
|
|
|
78
78
|
- **Image**: ``
|
|
79
79
|
- **File**: `[filename](url)`
|
|
80
80
|
- **Embed**: raw URL string
|
|
81
|
-
- **
|
|
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 —
|
|
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.
|
|
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 `
|
|
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
|
|
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
|
-
|
|
207
|
-
Type.
|
|
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
|
-
|
|
222
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
271
|
+
Repeaters can be nested — an item can contain another Repeater:
|
|
264
272
|
|
|
265
273
|
```tsx
|
|
266
|
-
columns: Type.
|
|
274
|
+
columns: Type.Repeater({
|
|
267
275
|
content: {
|
|
268
276
|
title: Type.String({ default: "Column" }),
|
|
269
|
-
links: Type.
|
|
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
|
|
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`, `.
|
|
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
|
-
###
|
|
395
|
+
### Image and File lists — `block.ImageList` / `block.FileList`
|
|
388
396
|
|
|
389
|
-
For `Type.
|
|
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.
|
|
401
|
+
<gallery.ImageList name="images">
|
|
394
402
|
{(props) => <img {...props} className="rounded-lg" />}
|
|
395
|
-
</gallery.
|
|
403
|
+
</gallery.ImageList>
|
|
396
404
|
|
|
397
|
-
// Nested inside a Type.
|
|
405
|
+
// Nested inside a Type.Repeater
|
|
398
406
|
<paragraphGrid.Repeater name="paragraphs">
|
|
399
407
|
{(item) => (
|
|
400
|
-
<item.
|
|
408
|
+
<item.ImageList name="logos">
|
|
401
409
|
{(props) => <img {...props} className="size-10 object-contain" />}
|
|
402
|
-
</item.
|
|
410
|
+
</item.ImageList>
|
|
403
411
|
)}
|
|
404
412
|
</paragraphGrid.Repeater>
|
|
405
413
|
```
|
|
406
414
|
|
|
407
|
-
`Repeater` is only for `Type.
|
|
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. **
|
|
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
|
-
##
|
|
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
|
-
|
|
132
|
+
### Per-command: the `--production` flag
|
|
113
133
|
|
|
114
|
-
|
|
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.
|