@bettercms-ai/mcp 0.66.3 → 0.66.4
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/index.js +48 -17
- package/dist/index.js.map +1 -1
- package/dist/skills/bettercms/SKILL.md +3 -1
- package/dist/skills/bettercms/references/dock-input.md +115 -0
- package/dist/skills/bettercms/references/getting-started.md +3 -1
- package/dist/skills/bettercms/references/localization.md +6 -4
- package/package.json +1 -1
|
@@ -84,7 +84,8 @@ These hold on every task. The reference in brackets has the detail and the codes
|
|
|
84
84
|
13. **A build makes a site live, not editable.** It brings in SEO and meta, media, the brand and
|
|
85
85
|
the models `bcms-content.json` declares. Pages, collections, forms, bindings and components
|
|
86
86
|
are `editor_setup`'s: run it after an import or a rebuild (`get_deploy_status` `editorSetup`
|
|
87
|
-
`never` or `stale`).
|
|
87
|
+
`never` or `stale`). It also fills missing meta, merges identical components and adopts
|
|
88
|
+
declared bindings when proven; do only what get_next_steps still lists. [getting-started.md]
|
|
88
89
|
|
|
89
90
|
## What you do, and what only the owner does
|
|
90
91
|
|
|
@@ -118,6 +119,7 @@ resources: `bettercms://skill/bettercms` for this file, and each reference under
|
|
|
118
119
|
| Move content out of another CMS (Cosmic, Contentful, Sanity, Strapi, Prismic, WordPress…) | [external-content.md](references/external-content.md) | a CMS SDK in package.json; `envWarning` on import; pages that render empty without a third-party key | Contentful, Sanity, Strapi, envWarning, missing key, empty pages |
|
|
119
120
|
| Pages vs collections, folders, fields, schema changes | [content-modeling.md](references/content-modeling.md) | modelling a route family; a model write returns 409 | schema, model, collection, field, folder, rename, remove field |
|
|
120
121
|
| Dock layout: groups, rows, tabs, icons, item names, showIf order | [editor-chrome.md](references/editor-chrome.md) | `get_next_steps` lists a `dock-*` rule; the `field-ui` prompt | dock, fieldset, tab, group, itemLabel, showIf, field-ui |
|
|
122
|
+
| A custom input for one field in the dock: when, the user's approval, the contract | [dock-input.md](references/dock-input.md) | `get_next_steps` lists `dock-input-candidate`; a 409 `input_needs_confirmation` | custom input, ui.input, dock input, confirmed, swatch |
|
|
121
123
|
| What the server does and does not check on a write | [write-limits.md](references/write-limits.md) | before relying on a limit; reading a refused write | validation, maxLength, unique, 400, refused write |
|
|
122
124
|
| Plan a site by business type | [site-blueprints.md](references/site-blueprints.md) | portfolio, agency, corporate, SaaS/AI or ecommerce IA | new site, IA, sitemap plan, portfolio, SaaS, ecommerce |
|
|
123
125
|
| Components, props, variants, component publish codes | [components.md](references/components.md) | creating, swapping, publishing or validating a component | component, prop, variant, sectionType, publish component |
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Custom dock inputs"
|
|
3
|
+
description: "When a field earns its own input in the editor dock, how to ask the user first, the input contract and protocol, the writes allowlist, the file layout, and the fallback guarantee."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Custom dock inputs
|
|
7
|
+
|
|
8
|
+
**Outcome.** A field whose choice the stock dock cannot show (a colour, a preset scale, one look
|
|
9
|
+
set by several props) gets a small input the user approved, and every other field keeps the stock
|
|
10
|
+
control. Nothing an author or a visitor sees can break because of it.
|
|
11
|
+
|
|
12
|
+
## 1. The restraint ladder
|
|
13
|
+
|
|
14
|
+
Industry practice: a custom input is the last resort. Climb in this order and stop at the first
|
|
15
|
+
rung that works.
|
|
16
|
+
|
|
17
|
+
1. **The stock control.** The field type already draws one: text, number, select, boolean, image.
|
|
18
|
+
2. **A composition of ui keys.** `ui.control` (`segmented` for a short single choice), `ui.preview`
|
|
19
|
+
and `ui.layout` for list items, `ui.group` for props, model `fieldsets` for groups, rows and
|
|
20
|
+
tabs. See editor-chrome.md.
|
|
21
|
+
3. **A custom input.** Only when rungs 1 and 2 cannot show the choice, and only with the user's yes.
|
|
22
|
+
|
|
23
|
+
A custom input never adds data. It draws fields that already exist in the schema.
|
|
24
|
+
|
|
25
|
+
## 2. Suggest, then the user approves
|
|
26
|
+
|
|
27
|
+
`get_next_steps` lists `dock-input-candidate` when a field or prop looks like it needs one. Each
|
|
28
|
+
row has the field `key`, a `reason` and, for a look set by several props, the sibling keys it would
|
|
29
|
+
`writes`. The rule is a candidate list, never a defect.
|
|
30
|
+
|
|
31
|
+
1. Check rungs 1 and 2 for each row. Drop rows they cover.
|
|
32
|
+
2. Ask the user, in one message per field: name the field, the input you would write and the
|
|
33
|
+
reason the stock control cannot do it. Wait for an explicit yes.
|
|
34
|
+
3. On yes: write the input file (section 5), then set `ui.input` with `confirmed: true`. These
|
|
35
|
+
merge into the stored `ui`:
|
|
36
|
+
`update_content_model { modelId | pageId, fieldUi: { "<path>": { input: { name, writes } } }, ifMatch, confirmed: true }`,
|
|
37
|
+
`update_component { componentId, propUi: { "<key>": { input: { name, writes } } }, ifMatch, confirmed: true }`.
|
|
38
|
+
`update_field`'s `ui` REPLACES the field's whole `ui`: send every key you read with it.
|
|
39
|
+
4. On no: leave it. Nothing is broken.
|
|
40
|
+
|
|
41
|
+
A write through the agent tools that sets or changes `ui.input` without `confirmed: true` is
|
|
42
|
+
refused 409 `input_needs_confirmation`, naming each path. `confirmed: true` is true only after the
|
|
43
|
+
user approved that input; never send it to get past the refusal. Removing `ui.input` (send
|
|
44
|
+
`input: null`) needs no approval. The create tools carry no approval: create the field without
|
|
45
|
+
`ui.input`, then add it with `update_field` or `update_component`. A person saving in the
|
|
46
|
+
dashboard is the approver and is never asked.
|
|
47
|
+
|
|
48
|
+
## 3. The contract
|
|
49
|
+
|
|
50
|
+
The input is a React component that receives `DockInputProps` from `@bettercms-ai/dock-input`:
|
|
51
|
+
|
|
52
|
+
| Prop | What it is |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `value` | this field's current value |
|
|
55
|
+
| `field` | `{ key, label, type, required, readOnly, writes, config? }`: the stored options and limits |
|
|
56
|
+
| `document` | a read-only snapshot of the whole document's values, by field key |
|
|
57
|
+
| `readOnly` | true when the author may not edit; draw the value, accept nothing |
|
|
58
|
+
| `theme` | `"light"` or `"dark"`, for the kit theme |
|
|
59
|
+
| `onChange(value)` | set this field |
|
|
60
|
+
| `writeFields(patches)` | set this field and siblings listed in `field.writes`, together |
|
|
61
|
+
|
|
62
|
+
`writeFields` takes `[{ field, value }]`, applies all of them or none to the DRAFT, as one undo
|
|
63
|
+
step, and resolves `{ ok, reason? }`. Show the `reason` when `ok` is false.
|
|
64
|
+
|
|
65
|
+
Build the UI from `@bettercms-ai/kit` so it matches the dock in both themes. The input never sees a
|
|
66
|
+
token and makes no network call for content: everything it needs arrives as props.
|
|
67
|
+
|
|
68
|
+
## 4. Writes discipline
|
|
69
|
+
|
|
70
|
+
`ui.input.writes` lists EVERY sibling field key the input writes, and nothing else. The dock
|
|
71
|
+
refuses a patch to a key outside it, and checks each value against that field's own schema. A
|
|
72
|
+
button-look input over `buttonVariant` set on `buttonVariant` declares
|
|
73
|
+
`writes: ["buttonSize", "buttonColor"]`. An input that only edits its own field omits `writes`.
|
|
74
|
+
At most 16 keys, each an existing sibling.
|
|
75
|
+
|
|
76
|
+
## 5. File layout
|
|
77
|
+
|
|
78
|
+
One file per input, in the site's repository:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
bettercms/inputs/<name>.tsx
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- `<name>` is the `ui.input.name`: lowercase letters, digits and dashes, 1 to 48 characters,
|
|
85
|
+
starting with a letter (`button-look`, `brand-swatch`).
|
|
86
|
+
- The default export is the component. The file also calls `mountDockInput(Component)`, which
|
|
87
|
+
speaks the protocol (section 6) so the component only deals with props.
|
|
88
|
+
- Keep it small and self-contained: kit components and the field's own data, no app imports.
|
|
89
|
+
|
|
90
|
+
## 6. Protocol summary
|
|
91
|
+
|
|
92
|
+
`bcms-input/1`, over `window.postMessage` between the dock and a sandboxed frame served from the
|
|
93
|
+
project's own inputs origin, never the dashboard's. `mountDockInput` handles all of it.
|
|
94
|
+
|
|
95
|
+
- Dock to input: `init` (field, value, document, theme), `value` (after a commit), `result`
|
|
96
|
+
(the verdict on a `change`), `readOnly`.
|
|
97
|
+
- Input to dock: `ready`, `change` (an id and its patches), `height`, `focus`, `blur`, `error`.
|
|
98
|
+
- Every message carries the session nonce from `init`; the dock drops anything else.
|
|
99
|
+
- Limits: a patch is at most 64 KB of JSON, at most 10 patches per second, and `ready` must arrive
|
|
100
|
+
within 3 seconds. Larger or faster messages are refused, never truncated.
|
|
101
|
+
|
|
102
|
+
## 7. Build, serve and scope
|
|
103
|
+
|
|
104
|
+
- v1 is managed sites only (BetterCMS hosting). On a headless or hybrid site, do not suggest one.
|
|
105
|
+
- Once custom inputs are enabled for the project, the inputs are built with the site and served
|
|
106
|
+
on its inputs origin automatically. Push the file, wait for the build, then open the field in the
|
|
107
|
+
dock to check it.
|
|
108
|
+
- Name only inputs that exist in `bettercms/inputs/`. An unknown name is not an error, it falls back.
|
|
109
|
+
|
|
110
|
+
## 8. The fallback guarantee
|
|
111
|
+
|
|
112
|
+
The stock control comes back whenever the input cannot run: an unknown name, inputs disabled for
|
|
113
|
+
the project, a build that failed, a frame that throws or misses `ready`. The value is unchanged
|
|
114
|
+
and stays editable. So a custom input can only add to the dock, never take a field away from an
|
|
115
|
+
author. Removing `ui.input` returns the field to the stock control at once.
|
|
@@ -68,7 +68,9 @@ and `get_componentize_plan` proposes nothing more.
|
|
|
68
68
|
0. A build brings in only SEO and meta, media, the brand and the models `bcms-content.json`
|
|
69
69
|
declares. Pages, collections, forms, bindings and components come from `editor_setup`: run it
|
|
70
70
|
and poll `editor_setup { check: true }` until `ready` before anything below. Run it again
|
|
71
|
-
whenever `get_deploy_status` reports `editorSetup` `stale`.
|
|
71
|
+
whenever `get_deploy_status` reports `editorSetup` `stale`. It also fills missing meta from the
|
|
72
|
+
page copy, merges identical components and switches to declared bindings when the build proves
|
|
73
|
+
them; the receipt stays yours (it is the codemod's account of your repo).
|
|
72
74
|
1. Explore the repo (the "Read when" column of SKILL.md's reference table names the signals) and
|
|
73
75
|
read `get_site_composition`.
|
|
74
76
|
2. If `list_env_vars` reports `missing` variables, run `npx @bettercms-ai/cli@latest env push`
|
|
@@ -31,10 +31,12 @@ Two things are called a "language" here, and they are separate:
|
|
|
31
31
|
| Layout locale variants | A Global Layout Section can have a variant per locale (`navigation--fr`). A page renders the variant whose locale is its route's first segment, else the base |
|
|
32
32
|
| `locale-unvarianted` | A `get_binding_report` unmatched reason: the route resolved in a locale, the Section has no variant for it, and the base still renders there. Named, never written |
|
|
33
33
|
|
|
34
|
-
Import derives deeper locale routes (`/fr/about
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
entries of
|
|
34
|
+
Import derives deeper locale routes (`/fr/about`) as pages in a URL folder (`fr`), each recording
|
|
35
|
+
the language its `<html lang>` declares, and the Content sidebar files them under Pages › <language>.
|
|
36
|
+
A locale prefix is never a collection, but a collection's localized posts (`/fr/blog/post-1`, its
|
|
37
|
+
`<html lang>` agreeing) are entries of that collection with `locale` set, `translationOf` the post
|
|
38
|
+
their hreflang alternate (else their slug) names, slug `<slug>-<locale>`; the project's locales gain
|
|
39
|
+
each language found. An edit to `/about` never reaches `/fr/about`; edit each page or entry.
|
|
38
40
|
|
|
39
41
|
## 2. Never model
|
|
40
42
|
|
package/package.json
CHANGED