fimo 0.9.0 → 0.10.0-staging.1
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/assets/content-templates/types-template.eta +30 -3
- package/assets/skills/fimo-cli/references/content.md +76 -4
- package/assets/skills/fimo-cli/references/locales.md +2 -0
- package/assets/skills/fimo-cli/references/webhooks.md +4 -0
- package/assets/skills/fimo-code/references/content.md +62 -4
- package/assets/skills/fimo-studio/references/cms.md +13 -0
- package/assets/skills/fimo-studio/references/webhooks.md +1 -1
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +14 -14
- package/dist/runtime/primitives/components/RichText.d.ts +2 -2
- package/dist/runtime/primitives/components/RichText.js +5 -3
- package/dist/runtime/primitives/lib/query.d.ts +1 -1
- package/dist/scripts/validate-schemas.js +32 -4
- package/package.json +1 -1
- package/release.json +2 -2
- package/templates/react-router/package.json +1 -1
|
@@ -48,9 +48,9 @@ export interface <%= model.pascal %> extends WithReferences<<%= model.pascal %>R
|
|
|
48
48
|
// Generate interface properties based on fields
|
|
49
49
|
if (model.fields) {
|
|
50
50
|
Object.entries(model.fields).forEach(([key, field]) => {
|
|
51
|
-
const type = getTypeForField(field);
|
|
51
|
+
const type = isNullableRichTextField(field) ? `${getTypeForField(field)} | null` : getTypeForField(field);
|
|
52
52
|
-%>
|
|
53
|
-
<%= key %>: <%= type %>;
|
|
53
|
+
<%= key %><%= isOptionalOutputField(field) ? '?' : '' %>: <%= type %>;
|
|
54
54
|
<%
|
|
55
55
|
});
|
|
56
56
|
}
|
|
@@ -68,7 +68,9 @@ export interface <%= model.pascal %>Input {
|
|
|
68
68
|
<%
|
|
69
69
|
if (model.fields) {
|
|
70
70
|
Object.entries(model.fields).forEach(([key, field]) => {
|
|
71
|
-
const type =
|
|
71
|
+
const type = isNullableRichTextField(field)
|
|
72
|
+
? `${getInputTypeForField(field)} | null`
|
|
73
|
+
: getInputTypeForField(field);
|
|
72
74
|
-%>
|
|
73
75
|
<%= key %><%= isRequiredField(field) ? '' : '?' %>: <%= type %>;
|
|
74
76
|
<%
|
|
@@ -85,6 +87,7 @@ if (model.fields) {
|
|
|
85
87
|
function getTypeForField(field) {
|
|
86
88
|
switch(field.type) {
|
|
87
89
|
case 'string':
|
|
90
|
+
return getStringLiteralUnion(field) || 'FimoString';
|
|
88
91
|
case 'text':
|
|
89
92
|
return 'FimoString';
|
|
90
93
|
case 'richtext':
|
|
@@ -113,6 +116,7 @@ function getTypeForField(field) {
|
|
|
113
116
|
function getInputTypeForField(field) {
|
|
114
117
|
switch(field.type) {
|
|
115
118
|
case 'string':
|
|
119
|
+
return getStringLiteralUnion(field) || 'string';
|
|
116
120
|
case 'text':
|
|
117
121
|
return 'string';
|
|
118
122
|
case 'richtext':
|
|
@@ -143,6 +147,7 @@ function getInputTypeForField(field) {
|
|
|
143
147
|
function getNestedFieldType(field, input) {
|
|
144
148
|
switch(field.type) {
|
|
145
149
|
case 'string':
|
|
150
|
+
return getStringLiteralUnion(field) || 'string';
|
|
146
151
|
case 'text':
|
|
147
152
|
return 'string';
|
|
148
153
|
case 'richtext':
|
|
@@ -195,9 +200,31 @@ function getBlocksType(field, input) {
|
|
|
195
200
|
return `${item}[]`;
|
|
196
201
|
}
|
|
197
202
|
|
|
203
|
+
function getStringLiteralUnion(field) {
|
|
204
|
+
if (!Array.isArray(field.options) || field.options.length === 0) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
return field.options.map((option) => JSON.stringify(String(option))).join(' | ');
|
|
208
|
+
}
|
|
209
|
+
|
|
198
210
|
function isRequiredField(field) {
|
|
211
|
+
if (isConditionalField(field)) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
199
214
|
return Boolean(field.required || (field.type === 'blocks' && Number(field.min || 0) > 0));
|
|
200
215
|
}
|
|
216
|
+
|
|
217
|
+
function isNullableRichTextField(field) {
|
|
218
|
+
return field.type === 'richtext' && !isRequiredField(field);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isOptionalOutputField(field) {
|
|
222
|
+
return isConditionalField(field) || isNullableRichTextField(field);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function isConditionalField(field) {
|
|
226
|
+
return field.showIf !== undefined || field.showIfAny !== undefined;
|
|
227
|
+
}
|
|
201
228
|
-%>
|
|
202
229
|
|
|
203
230
|
export interface <%= model.pascal %>References {
|
|
@@ -94,9 +94,46 @@ fimo entries query --file query.sql
|
|
|
94
94
|
cat query.sql | fimo entries query --stdin
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
SQL exposes the raw internal representation. `document_id` is the stable ID used by `fimo entries`, content tools, references, and the API. `id` is the internal ID of one exact locale variant. When query results will feed another Fimo command or tool, select `document_id AS id`; use `locale` to select a variant.
|
|
97
|
+
SQL exposes the raw internal representation. `document_id` is the stable ID used by `fimo entries`, content tools, references, and the API. `id` is the internal ID of one exact locale variant. Localized variants share one `document_id` and have one row per locale; non-localized entries have a null `locale`. When query results will feed another Fimo command or tool, select `document_id AS id`; use `locale` to select a variant.
|
|
98
|
+
|
|
99
|
+
The supported content columns are `id`, `document_id`, `content_type`, `locale`, `slug`, `data`, `created_at`, and `updated_at`. Schema fields live inside the JSONB `data` column. Fimo may add internal columns, so select the fields you need instead of `SELECT *` and do not depend on other columns. If a safe read is blocked because a SQL function is unavailable, tell the user which function is missing and use the built-in `supportReport` tool when the platform gap materially blocks the task. Do not bypass the restriction with a write.
|
|
100
|
+
|
|
101
|
+
For example, find English blog posts that do not yet have a Spanish variant:
|
|
102
|
+
|
|
103
|
+
```sql
|
|
104
|
+
SELECT source.document_id AS id, source.data
|
|
105
|
+
FROM entries source
|
|
106
|
+
WHERE source.content_type = 'BlogPost'
|
|
107
|
+
AND source.locale = 'en'
|
|
108
|
+
AND NOT EXISTS (
|
|
109
|
+
SELECT 1
|
|
110
|
+
FROM entries target
|
|
111
|
+
WHERE target.content_type = source.content_type
|
|
112
|
+
AND target.document_id = source.document_id
|
|
113
|
+
AND target.locale = 'es'
|
|
114
|
+
)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Find Spanish variants whose English source was edited later:
|
|
118
|
+
|
|
119
|
+
```sql
|
|
120
|
+
SELECT
|
|
121
|
+
target.document_id AS id,
|
|
122
|
+
target.content_type,
|
|
123
|
+
target.slug,
|
|
124
|
+
source.updated_at AS source_updated_at,
|
|
125
|
+
target.updated_at AS target_updated_at
|
|
126
|
+
FROM entries source
|
|
127
|
+
JOIN entries target
|
|
128
|
+
ON target.content_type = source.content_type
|
|
129
|
+
AND target.document_id = source.document_id
|
|
130
|
+
WHERE source.locale = 'en'
|
|
131
|
+
AND target.locale = 'es'
|
|
132
|
+
AND source.updated_at > target.updated_at
|
|
133
|
+
ORDER BY source.updated_at DESC
|
|
134
|
+
```
|
|
98
135
|
|
|
99
|
-
|
|
136
|
+
Treat these results as possibly stale, not proof of a bad translation. The query only proves that the source row was edited later; an agent should review the content and link each target with its stable `id`.
|
|
100
137
|
|
|
101
138
|
### Bulk mutations
|
|
102
139
|
|
|
@@ -150,7 +187,7 @@ Entry bodies use reference objects with target `id` values:
|
|
|
150
187
|
fimo entries create BlogPost --body '{"data":{"slug":"hello","title":"Hello","author":{"id":"750e8400-e29b-41d4-a716-446655440000"},"contributors":[]}}'
|
|
151
188
|
```
|
|
152
189
|
|
|
153
|
-
|
|
190
|
+
`localized` is not a supported spelling anywhere. A reference field may carry `"i18n": false` to point every locale of the document at the same target — see Localized and shared content below.
|
|
154
191
|
|
|
155
192
|
## Localized and shared content
|
|
156
193
|
|
|
@@ -158,7 +195,42 @@ Schemas are localized by default. Omit `--locale` to use the project default. Pa
|
|
|
158
195
|
|
|
159
196
|
When creating a localized variant of an existing entry, pass the source entry's `documentId` with `--document-id`. Keep projects single-locale unless the user asks for multilingual or localized output; when multiple locales are configured, create and update every locale explicitly.
|
|
160
197
|
|
|
161
|
-
For content shared across locales, define `"i18n": { "enabled": false }` at the schema root and create each entry once. Do not pass `--document-id` to manufacture locale variants. Raw API and CLI responses omit `locale`; generated collection models add the effective active/default locale without creating per-locale variants. No other `i18n` options
|
|
198
|
+
For content shared across locales, define `"i18n": { "enabled": false }` at the schema root and create each entry once. Do not pass `--document-id` to manufacture locale variants. Raw API and CLI responses omit `locale`; generated collection models add the effective active/default locale without creating per-locale variants. No other schema-root `i18n` options are supported.
|
|
199
|
+
|
|
200
|
+
### Shared fields inside a localized type
|
|
201
|
+
|
|
202
|
+
A localized type can still hold fields that are the same in every language. Mark the field `"i18n": false`. Use it for anything that is not translated — a price, a SKU, a product photo, a reference to the same target everywhere. Without it those values are re-entered per locale and drift apart.
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"uid": "Product",
|
|
207
|
+
"name": "Products",
|
|
208
|
+
"fields": {
|
|
209
|
+
"title": { "type": "string" },
|
|
210
|
+
"description": { "type": "richtext" },
|
|
211
|
+
"sku": { "type": "string", "i18n": false, "unique": true },
|
|
212
|
+
"price": { "type": "number", "i18n": false },
|
|
213
|
+
"photo": { "type": "media", "i18n": false },
|
|
214
|
+
"brand": { "type": "reference", "target": "Brand", "i18n": false }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Writing a shared field through any locale updates every locale of that document:
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
fimo entries update Product <id> --locale es --body '{"data":{"title":"Silla","price":120}}'
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
That sets the Spanish title and moves the price to `120` for every language. An update that does not mention a shared field leaves it untouched, so partial writes are safe.
|
|
226
|
+
|
|
227
|
+
Rules:
|
|
228
|
+
|
|
229
|
+
- `i18n` belongs on top-level scalar and reference fields only. Fields inside `blocks` follow the locale of the entry containing them and are rejected.
|
|
230
|
+
- On a non-localized type the flag is inert — there is one row already.
|
|
231
|
+
- `"i18n": true` is the default and never needs writing.
|
|
232
|
+
|
|
233
|
+
Turning an existing field shared is destructive when the languages currently hold different values. The push collapses each document to its default-locale value and discards the others; there is no prompt and no undo. Most documents already agree, so most collapses change nothing — but tell the user what will happen before adding `"i18n": false` to a field that already holds content, and check whether the languages differ if you are unsure.
|
|
162
234
|
|
|
163
235
|
Use `"isSingleton": true` for a type with exactly one logical document. Generated singleton clients expose `get`/`update` and their React Query hooks, not collection create/remove APIs.
|
|
164
236
|
|
|
@@ -81,3 +81,5 @@ Labels and content are separate translated surfaces:
|
|
|
81
81
|
- content: schema entries, managed with `fimo entries`
|
|
82
82
|
|
|
83
83
|
Omit `--locale` for default-locale writes. Pass `--locale <locale>` for each non-default localized entry you need to create or update. Shared schemas (`i18n.enabled: false`) have one locale-free entry instead.
|
|
84
|
+
|
|
85
|
+
Inside a localized schema, a single field can still be shared across every language with `"i18n": false` — use it for values that are not translated, such as a price or a SKU, so they are written once instead of per locale. See `content.md` § Shared fields inside a localized type.
|
|
@@ -24,6 +24,10 @@ Run this before writing a custom payload. The list shows supported event types,
|
|
|
24
24
|
- Analytics: `analytics.report_ready`
|
|
25
25
|
- Media: uploaded/updated/deleted/generated/processing_failed
|
|
26
26
|
- Entries: `entry.changed`
|
|
27
|
+
- Domains: `domain.created`, `domain.deleted`
|
|
28
|
+
- Labels: `label.changed`
|
|
29
|
+
- Locales: `locale.created`, `locale.default_changed`, `locale.deleted`
|
|
30
|
+
- Environments: `environment.created`, `environment.deleted`, `environment.merged`, `environment.restored`
|
|
27
31
|
|
|
28
32
|
Do not guess variable paths. The common envelope is:
|
|
29
33
|
|
|
@@ -27,7 +27,21 @@ This file covers the **package surface**: the JSON shape you write, the generate
|
|
|
27
27
|
|
|
28
28
|
**Required**: `uid` (PascalCase, matches filename), `name`, `fields`.
|
|
29
29
|
|
|
30
|
-
`i18n` is an optional type-level policy. Omit it or use `{ "enabled": true }` for localized content. Use `{ "enabled": false }` for one shared entry that appears in every locale, such as an Author, Category, or Settings type. No other `i18n` keys
|
|
30
|
+
`i18n` is an optional type-level policy. Omit it or use `{ "enabled": true }` for localized content. Use `{ "enabled": false }` for one shared entry that appears in every locale, such as an Author, Category, or Settings type. No other schema-root `i18n` keys are supported.
|
|
31
|
+
|
|
32
|
+
Inside a localized type, an individual top-level scalar or reference field may carry `"i18n": false`. That field then holds one value for the whole document: writing it through any locale updates all of them. Use it for values that are not translated, such as a price, a SKU, a product photo, or a reference that should point at the same target everywhere. Fields inside `blocks` follow the locale of the entry containing them and reject the flag.
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"uid": "Product",
|
|
37
|
+
"name": "Products",
|
|
38
|
+
"fields": {
|
|
39
|
+
"title": { "type": "string" },
|
|
40
|
+
"sku": { "type": "string", "i18n": false },
|
|
41
|
+
"price": { "type": "number", "i18n": false }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
31
45
|
|
|
32
46
|
### Reserved field names — do NOT declare these in `fields`
|
|
33
47
|
|
|
@@ -61,6 +75,48 @@ something else (e.g. `handle`, `permalink`).
|
|
|
61
75
|
|
|
62
76
|
A `date` field currently generates as `any` in the TS client, so for typed access (and the `<Date>` component) prefer a `string` field holding an ISO date — which is what the `publishDate` examples below do.
|
|
63
77
|
|
|
78
|
+
## Enumerated strings
|
|
79
|
+
|
|
80
|
+
A `string` field may declare `options` to close its value set. The CMS renders a select, the API validates the value as an enum, and the generated client types the field as a literal union instead of `string`.
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
"layout": { "type": "string", "options": ["article", "video"], "default": "article" }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- Values must be unique. Set a `default` that is one of them.
|
|
87
|
+
- `options` cannot be combined with `regex`, `minLength`, or `maxLength`, and is rejected on every field type other than `string`.
|
|
88
|
+
- Writes are validated against the set: a value outside it is rejected with a 400. An optional field also accepts `""` and `null` so the editor can clear the choice; a `required` one only accepts a declared value.
|
|
89
|
+
- Removing an option that entries still hold fails the push, the same way an unsafe type change does. Migrate those entries to a remaining value first.
|
|
90
|
+
|
|
91
|
+
## Conditional fields
|
|
92
|
+
|
|
93
|
+
A field may declare `showIf` (every clause must match) or `showIfAny` (one clause is enough) to apply only in some entries. A clause names a sibling field and a value.
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"layout": { "type": "string", "options": ["article", "video"], "default": "article" },
|
|
98
|
+
"body": { "type": "richtext", "showIf": { "field": "layout", "equals": "article" } },
|
|
99
|
+
"videoUrl": { "type": "string", "required": true, "showIf": { "field": "layout", "equals": "video" } },
|
|
100
|
+
"transcript": {
|
|
101
|
+
"type": "text",
|
|
102
|
+
"showIfAny": [
|
|
103
|
+
{ "field": "layout", "equals": "video" },
|
|
104
|
+
{ "field": "hasAudio", "equals": true }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- A clause is `{ "field": "<sibling>", "equals": <value> }` or `{ "field": "<sibling>", "in": [<values>] }`. Use `in` for several values of one field.
|
|
111
|
+
- `showIf` accepts one clause or an array of clauses, and every clause must match. `showIfAny` takes an array and one match is enough. A field declares one key or the other, never both.
|
|
112
|
+
- **A hidden field is not rendered and not validated, so `required` does not apply to it.** An entry whose condition never held can be saved without ever filling the field.
|
|
113
|
+
- **Hiding a field changes nothing about storage or reads.** The value stays in the entry and the API keeps returning it, so an editor who flips the trigger back gets their work back. Narrow on the trigger in site code rather than trusting the value.
|
|
114
|
+
- Because a conditional field can be absent, it is always optional in the generated types even when `required` is set.
|
|
115
|
+
- Conditions resolve against siblings in the same object. A field inside a block variant may only reference fields in that same block item.
|
|
116
|
+
- A clause may reference a `boolean`, `number`, or `string` field. A `string` trigger must declare `options`, which is what lets `fimo validate` reject a clause value that could never match.
|
|
117
|
+
- A trigger cannot itself be conditional — conditions are one level deep, so a chain like A shows B shows C is rejected.
|
|
118
|
+
- `fimo validate` and `fimo schemas push` reject an unknown trigger name, a trigger of the wrong type, a value outside the trigger's `options`, and a trigger that is itself conditional.
|
|
119
|
+
|
|
64
120
|
## Blocks
|
|
65
121
|
|
|
66
122
|
`blocks` is the only nested structured-content type and always stores an array. Use `schema.fields` for one homogeneous block shape. Set `max: 1` when the field is a single optional group. Use `schema.oneOf` when editors choose a variant for each item.
|
|
@@ -139,7 +195,7 @@ Rules:
|
|
|
139
195
|
- Self-references are allowed, e.g. a `Category` schema can have `parent: { "type": "reference", "target": "Category" }`.
|
|
140
196
|
- Entry values use the target's stable `id`, never the variant `entryId`: `{ "id": "..." }`.
|
|
141
197
|
- Do not include `contentType` for normal fixed-target references; the schema's `target` already defines it.
|
|
142
|
-
-
|
|
198
|
+
- `localized` is not a supported spelling. A reference may carry `"i18n": false` so every locale of the document points at the same target; without it the reference belongs to the entry variant that stores it.
|
|
143
199
|
- Use `null` for an empty to-one reference and `[]` for an empty to-many reference.
|
|
144
200
|
- The API validates that referenced target documents exist and rejects duplicate values in to-many reference arrays.
|
|
145
201
|
|
|
@@ -272,7 +328,7 @@ interface BlogPost {
|
|
|
272
328
|
};
|
|
273
329
|
title: FimoString;
|
|
274
330
|
summary: FimoString;
|
|
275
|
-
body
|
|
331
|
+
body?: FimoRichText | null;
|
|
276
332
|
coverImage: FimoMedia;
|
|
277
333
|
publishDate: FimoString;
|
|
278
334
|
featured: FimoBoolean;
|
|
@@ -282,6 +338,8 @@ interface BlogPost {
|
|
|
282
338
|
|
|
283
339
|
Content fields live at the top level. Fimo-owned metadata that is not part of the user schema lives under `__fimo`, so a schema can safely define fields named `contentType`.
|
|
284
340
|
|
|
341
|
+
Optional richtext fields are nullable in generated read and write types. `<RichText>` accepts the generated value directly and renders nothing when it is null or undefined. A non-conditional required richtext field stays non-null; conditional fields remain optional because their condition may not apply.
|
|
342
|
+
|
|
285
343
|
## Localized and shared content
|
|
286
344
|
|
|
287
345
|
Localized content has one variant per locale. Generated hooks read the active Fimo locale automatically.
|
|
@@ -296,7 +354,7 @@ For shared content, set `i18n: { "enabled": false }` on the schema and create ea
|
|
|
296
354
|
- ignore locale on owner reads and writes;
|
|
297
355
|
- still use the active/requested locale when a populated reference targets a localized schema.
|
|
298
356
|
|
|
299
|
-
Do not duplicate shared entries across locales
|
|
357
|
+
Do not duplicate shared entries across locales. This section is about a whole type being shared; a single shared field inside a localized type is the separate `"i18n": false` field flag described above.
|
|
300
358
|
|
|
301
359
|
A populated reference whose target has no variant in the requested locale resolves to null. Reads accept an opt-in `localeFallbacks: true` that resolves those to the best available variant instead (project default first), marked by its `locale` field — never implement fallbacks by hand; public reads stay strict unless the option is passed.
|
|
302
360
|
|
|
@@ -36,6 +36,17 @@ When you open a Collection, the entry list provides several tools:
|
|
|
36
36
|
- **Columns** dropdown (the eye button) next to the search bar to show/hide columns and reorder them by dragging each column row by its grip handle.
|
|
37
37
|
- **Pagination** at the bottom when there are many entries.
|
|
38
38
|
|
|
39
|
+
## Completing Missing Translations
|
|
40
|
+
|
|
41
|
+
Localized Collections show only entries for the locale selected beside the Collection name. When that locale is missing translations that exist in the project's default locale:
|
|
42
|
+
|
|
43
|
+
1. Click the amber **N missing** warning beside the locale selector.
|
|
44
|
+
2. Review the source and target languages in the popover.
|
|
45
|
+
3. Choose **Create missing translations**.
|
|
46
|
+
4. Review or edit the prompt placed in AI chat, then send it when ready.
|
|
47
|
+
|
|
48
|
+
Fimo does not send the prompt automatically. After the AI finishes, translated entries appear in the selected locale and the warning updates.
|
|
49
|
+
|
|
39
50
|
## Creating a New Entry
|
|
40
51
|
|
|
41
52
|
1. Navigate to the Collection you want to add content to.
|
|
@@ -53,6 +64,8 @@ Your edits save automatically as you change each field (the save is debounced),
|
|
|
53
64
|
|
|
54
65
|
If a field is invalid, the editor keeps the change unsaved and pins a status bar below the header. Choose **Review issues** to jump to the first invalid field, then **Next issue** to move through the remaining fields. Collapsed blocks open automatically. Once every issue is fixed, the editor confirms that all fields are valid and resumes saving.
|
|
55
66
|
|
|
67
|
+
A field labelled with a padlocked globe is shared across languages: it holds one value for the whole entry, so editing it in any language changes it in all of them. Fields without that marker are per language and only affect the language you are editing. Hover the icon for the same explanation in context.
|
|
68
|
+
|
|
56
69
|
## Editing Text
|
|
57
70
|
|
|
58
71
|
- Short text such as titles, names, and slugs uses one compact line.
|
|
@@ -15,7 +15,7 @@ Only project members with Full Access can see Webhooks in Settings or manage its
|
|
|
15
15
|
5. Optionally narrow form, agent, media, or entry events with the filter shown for that event.
|
|
16
16
|
6. Review the request and select **Save**.
|
|
17
17
|
|
|
18
|
-
The event groups are Entries, Forms, Publish, Agents, Analytics, and
|
|
18
|
+
The event groups are Entries, Forms, Publish, Agents, Analytics, Media, Domains, Labels, Locales, and Environments. Webhooks belong to the project, with up to 20 configurations. They listen to main by default or can include every environment.
|
|
19
19
|
|
|
20
20
|
Entry webhooks show **Combine rapid changes**. The default waits 5 seconds after direct editing stops and sends one delivery with the combined changes. Immediate, 30-second, and 1-minute windows are also available. Bulk edits and environment merges already produce batches and send immediately.
|
|
21
21
|
|