@lowdefy/docs-content 0.0.0-experimental-20260910082708 → 6.0.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 +0 -1
- package/content/actions/link.md +0 -17
- package/content/agents/agentchat.md +1 -13
- package/content/concepts/ai-agent-docs.md +49 -232
- package/content/concepts/cli.md +0 -13
- package/content/concepts/custom-styling.md +5 -50
- package/content/concepts/i18n.md +1 -1
- package/content/concepts/layout-overview.md +1 -1
- package/content/connections/mongodb.md +3 -121
- package/content/container-blocks/collapse.md +2 -2
- package/content/container-blocks/layout.md +0 -1
- package/content/container-blocks/modal.md +1 -1
- package/content/container-blocks/pagesidebarlayout.md +0 -1
- package/content/container-blocks/sider.md +1 -1
- package/content/container-blocks/spinner.md +1 -1
- package/content/controls/reject.md +1 -3
- package/content/controls/throw.md +0 -1
- package/content/controls/try.md +2 -3
- package/content/display-blocks/aggrid.md +18 -18
- package/content/display-blocks/avatar.md +2 -2
- package/content/display-blocks/difflist.md +1 -1
- package/content/display-blocks/difftimeline.md +1 -1
- package/content/display-blocks/echart.md +1 -35
- package/content/display-blocks/menu.md +1 -1
- package/content/display-blocks/qrcode.md +2 -2
- package/content/display-blocks/s3download.md +1 -1
- package/content/display-blocks/search.md +0 -1
- package/content/display-blocks/skeletonbutton.md +0 -1
- package/content/display-blocks/skeletoninput.md +0 -1
- package/content/input-blocks/buttonselector.md +1 -1
- package/content/input-blocks/checkboxselector.md +5 -81
- package/content/input-blocks/colorselector.md +0 -14
- package/content/input-blocks/multipleselector.md +1 -1
- package/content/input-blocks/phonenumberinput.md +1 -2
- package/content/input-blocks/radioselector.md +4 -66
- package/content/input-blocks/s3uploadbutton.md +1 -1
- package/content/input-blocks/s3uploaddragger.md +1 -1
- package/content/input-blocks/s3uploadphoto.md +1 -1
- package/content/input-blocks/segmentedselector.md +1 -1
- package/content/input-blocks/selector.md +1 -1
- package/content/input-blocks/tiptapmentioninput.md +1 -1
- package/content/input-blocks/treeinput.md +1 -1
- package/content/input-blocks/treemultipleselector.md +1 -1
- package/content/input-blocks/treeselector.md +1 -1
- package/content/list-blocks/masonrylist.md +1 -1
- package/content/operators/_type.md +3 -14
- package/content/plugins/plugins-introduction.md +0 -15
- package/index.json +1 -31
- package/package.json +1 -3
- package/content/controls/while.md +0 -46
- package/content/input-blocks/tagmultipleselector.md +0 -101
- package/content/input-blocks/tagselector.md +0 -101
- package/content/testing/config-tests.md +0 -164
- package/hazards.json +0 -44
package/README.md
CHANGED
|
@@ -6,7 +6,6 @@ Lowdefy documentation extracted as markdown, for serving to AI coding agents via
|
|
|
6
6
|
|
|
7
7
|
- `content/<section>/<slug>.md` — one markdown file per docs page (concepts, tutorial, operators, actions, connections, blocks, ...).
|
|
8
8
|
- `index.json` — manifest of all docs: `{ version, docs: [{ slug, title, section, path, kind?, typeName? }] }`. `kind`/`typeName` map a plugin type (e.g. operator `_get`) to its doc.
|
|
9
|
-
- `hazards.json` — framework-level hazards served by the dev MCP alongside schemas, docs and config lookups: `[{ id, appliesTo: { kinds?, types?, when? }, message, see }]`. This file is hand-authored and committed, not generated — `pnpm docs:content` wipes `content/` but leaves it untouched. Add an entry only for a behaviour verified in the framework source.
|
|
10
9
|
|
|
11
10
|
## Regenerating content
|
|
12
11
|
|
package/content/actions/link.md
CHANGED
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
input?: object,
|
|
10
10
|
newTab?:boolean,
|
|
11
11
|
pageId?: string,
|
|
12
|
-
replace?: boolean,
|
|
13
|
-
scroll?: boolean,
|
|
14
12
|
url?: string,
|
|
15
13
|
urlQuery? object
|
|
16
14
|
}): void
|
|
@@ -32,8 +30,6 @@ The pageId of a page in the app to link to.
|
|
|
32
30
|
- `input: object`: Object to set as the input for the linked page.
|
|
33
31
|
- `newTab: boolean`: Open the link in a new tab.
|
|
34
32
|
- `pageId: string`: The pageId of a page in the app to link to.
|
|
35
|
-
- `replace: boolean`: Replace the current browser history entry instead of pushing a new one, so the browser back button skips this navigation. Defaults to `false`.
|
|
36
|
-
- `scroll: boolean`: Scroll to the top of the page after navigating. Set to `false` to keep the current scroll position, for example when a same-page link only updates the `urlQuery`. Defaults to `true`.
|
|
37
33
|
- `url: string`: Link to an external url.
|
|
38
34
|
- `urlQuery: object`: Object to set as the urlQuery for the linked page.
|
|
39
35
|
|
|
@@ -109,19 +105,6 @@ The pageId of a page in the app to link to.
|
|
|
109
105
|
_args: row.id
|
|
110
106
|
```
|
|
111
107
|
|
|
112
|
-
###### Reflect state into the urlQuery without moving the page:
|
|
113
|
-
```yaml
|
|
114
|
-
- id: sync_url
|
|
115
|
-
type: Link
|
|
116
|
-
params:
|
|
117
|
-
pageId: my_page_id
|
|
118
|
-
urlQuery:
|
|
119
|
-
selected:
|
|
120
|
-
_state: selected_id
|
|
121
|
-
replace: true
|
|
122
|
-
scroll: false
|
|
123
|
-
```
|
|
124
|
-
|
|
125
108
|
###### Go to the previous page:
|
|
126
109
|
```yaml
|
|
127
110
|
- id: link_back
|
|
@@ -68,7 +68,6 @@ The `AgentChat` block renders a streaming AI chat interface. It connects to a [L
|
|
|
68
68
|
| `agentId` | string | | __Required__ - The `id` of the agent to connect to. |
|
|
69
69
|
| `conversationId` | string | | Active conversation ID. When this changes, messages are cleared. If left empty, the block auto-mints a stable id for the session and surfaces it via `onConversationStart`, so every turn posts a consistent id. App-supplied ids are always authoritative. |
|
|
70
70
|
| `messages` | array | | Load messages externally. `undefined` = no sync, `null` = clear, array = load. |
|
|
71
|
-
| `feedbackValues` | object | | Ratings already recorded for messages in this conversation, keyed by message id, each `like` or `dislike`. The block does not persist a rating, so without this a reload or a conversation switch shows every message unrated even where your app stored it. A rating clicked this visit takes precedence, so the thumb still responds immediately and a rating the user has just withdrawn is not re-lit by a stale value. |
|
|
72
71
|
| `urlQuery` | object | | Query parameters sent with each request. Available server-side via `_payload`. |
|
|
73
72
|
| `sharedState` | object | | Two-way bridge between page state and the agent. See [Shared State](#shared-state). |
|
|
74
73
|
| `height` | string | `'calc(100dvh - 170px)'` | CSS height of the chat container. Only applies when `display` is `'inline'`. |
|
|
@@ -327,7 +326,7 @@ With the curated form above, the agent can read `legal_name` and `registration_n
|
|
|
327
326
|
| `onToolResult` | A tool returns a result. | `toolName`, `toolCallId`, `output`, `error` |
|
|
328
327
|
| `onError` | A streaming error occurs. | `message` |
|
|
329
328
|
| `onStop` | User stops generation. | `messages` |
|
|
330
|
-
| `onFeedback` | User clicks thumbs up/down.
|
|
329
|
+
| `onFeedback` | User clicks thumbs up/down. | `messageId`, `messageContent`, `rating` |
|
|
331
330
|
| `onRegenerate` | User clicks regenerate. | `messageId`, `messages` |
|
|
332
331
|
| `onDeleteMessage` | User deletes a message. | `messageId`, `content`, `messages` |
|
|
333
332
|
| `onEditMessage` | User edits and resubmits. | `messageId`, `originalContent`, `newContent`, `messages` |
|
|
@@ -335,7 +334,6 @@ With the curated form above, the agent can read `legal_name` and `registration_n
|
|
|
335
334
|
| `onSwitchChange` | User toggles a sender switch. | `key`, `checked` |
|
|
336
335
|
| `onTitleGenerated` | A `data-chat-title` data part arrives — emitted automatically when the agent's [`generateTitle`](/agent-properties) property is set, or manually from an `onFinish` hook. | `title` |
|
|
337
336
|
| `onDataPart` | Any data part arrives from the server. | `type`, `data`, `id` |
|
|
338
|
-
| `onLinkClick` | User clicks a link in a message. Wiring this event suppresses the default navigation for plain left clicks, so the app can open the target in place; modified clicks (new tab, middle click) always navigate. | `href`, `text` |
|
|
339
337
|
|
|
340
338
|
###### Validate messages before sending:
|
|
341
339
|
```yaml
|
|
@@ -367,16 +365,6 @@ events:
|
|
|
367
365
|
type: success
|
|
368
366
|
```
|
|
369
367
|
|
|
370
|
-
Saving is only half of it — the block does not persist a rating, so a
|
|
371
|
-
reload or a conversation switch shows the message unrated again. Pass
|
|
372
|
-
what you stored back through `feedbackValues`, keyed by message id:
|
|
373
|
-
|
|
374
|
-
```yaml
|
|
375
|
-
properties:
|
|
376
|
-
feedbackValues:
|
|
377
|
-
_request: saved_feedback_for_conversation
|
|
378
|
-
```
|
|
379
|
-
|
|
380
368
|
###### Adapt behavior based on sender switches:
|
|
381
369
|
```yaml
|
|
382
370
|
events:
|
|
@@ -16,42 +16,31 @@ http://localhost:3000/lowdefy-docs/mcp
|
|
|
16
16
|
|
|
17
17
|
It provides these tools:
|
|
18
18
|
|
|
19
|
-
| Tool
|
|
20
|
-
|
|
|
21
|
-
| `lowdefy_overview`
|
|
22
|
-
| `lowdefy_list_types`
|
|
23
|
-
| `lowdefy_list_plugins`
|
|
24
|
-
| `lowdefy_get_schema`
|
|
25
|
-
| `lowdefy_get_examples`
|
|
26
|
-
| `lowdefy_get_doc`
|
|
27
|
-
| `lowdefy_search_docs`
|
|
28
|
-
| `lowdefy_get_plugin_doc`
|
|
29
|
-
| `lowdefy_build_status`
|
|
30
|
-
| `lowdefy_get_page_config
|
|
31
|
-
| `lowdefy_screenshot_page
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
44
|
-
| `lowdefy_restart` | Restart the dev server process — after editing a local plugin's server-side code, or when `build_status` looks stale. Wait ~2s, then call `lowdefy_build_status` |
|
|
45
|
-
| `lowdefy_checkpoint` | Snapshot all config files before risky changes |
|
|
46
|
-
| `lowdefy_revert_checkpoint` | Restore config files from a checkpoint |
|
|
47
|
-
|
|
48
|
-
## Hazards — what the schema cannot tell you
|
|
49
|
-
|
|
50
|
-
A hazard is a behaviour of a type that its schema does not show, but that an agent must know before writing config: the `Html` block sanitises its content, so a `<style>` tag is stripped; closing a `Modal` keeps the state of the blocks inside it, while `visible: false` prunes it; `_state` inside a request's `properties` is always `undefined`; a `_ref` to a `.njk` file renders at build time. Each hazard is `{ id, message, see }` — a stable kebab-case `id`, one or two sentences saying what happens and what to do instead, and a docs slug (or `null`) for `lowdefy_get_doc`.
|
|
51
|
-
|
|
52
|
-
`lowdefy_get_schema`, `lowdefy_get_doc` (when looked up by kind and type) and `lowdefy_find_config` (per match) return `hazards` alongside their normal result — `lowdefy_get_doc` appends them as a `## Hazards` section of the markdown. Some hazards are contextual: `tenant-wall-lookup` is only returned for a request whose connection is tenant-walled, so `find_config` on that request explains the wall's `$lookup` injection while the same request over a `tenant: shared` connection stays quiet.
|
|
53
|
-
|
|
54
|
-
Hazards come from two places. Framework-level hazards ship with `@lowdefy/docs-content` (`hazards.json`). Type-attached hazards are declared by the plugin: a block sets `meta.hazards`, a request sets `meta.hazards` on the request function, and an operator package exports a `./metas` module with `{ hazards }` per operator — see [Plugins](/plugins-introduction). Local plugins in your project use the same channel, so your own blocks can warn an agent about their own surprises.
|
|
19
|
+
| Tool | Purpose |
|
|
20
|
+
| ------------------------ | ----------------------------------------------------------------------- |
|
|
21
|
+
| `lowdefy_overview` | Start here — what is installed, counts per kind, and how to use the rest |
|
|
22
|
+
| `lowdefy_list_types` | List ALL available types of a kind (blocks, operators, actions, connections, requests) |
|
|
23
|
+
| `lowdefy_list_plugins` | Installed plugin packages, including local plugins, and the types each provides |
|
|
24
|
+
| `lowdefy_get_schema` | JSON Schema for a specific type — all properties and events |
|
|
25
|
+
| `lowdefy_get_examples` | Real YAML usage examples for a block type |
|
|
26
|
+
| `lowdefy_get_doc` | A Lowdefy docs page as markdown, by slug or by type name |
|
|
27
|
+
| `lowdefy_search_docs` | Keyword search over the Lowdefy docs |
|
|
28
|
+
| `lowdefy_get_plugin_doc` | Markdown (READMEs, guides) shipped inside an installed plugin package |
|
|
29
|
+
| `lowdefy_build_status` | Current build errors and warnings (with source file locations) plus recent browser runtime errors — call after every edit |
|
|
30
|
+
| `lowdefy_get_page_config`| The fully built config for a page, or its structured build errors |
|
|
31
|
+
| `lowdefy_screenshot_page`| PNG screenshot of a rendered page (headless Chromium) for visual verification |
|
|
32
|
+
| `lowdefy_find_config` | Which yaml file (and line) defines a given page, block, or request id |
|
|
33
|
+
| `lowdefy_scaffold_page` | Create a new page yaml file with a canonical minimal structure |
|
|
34
|
+
| `lowdefy_app_map` | The whole-app graph: every page, menu, connection, endpoint, and agent in one call |
|
|
35
|
+
| `lowdefy_inspect_state` | The LIVE state, request results, and event log of a running page — reads your open browser tab, or runs the page headless |
|
|
36
|
+
| `lowdefy_eval_operator` | Evaluate any operator expression against live page state — a REPL for config |
|
|
37
|
+
| `lowdefy_run_request` | Execute a request with a test payload to verify data shape (read-only unless opted in) |
|
|
38
|
+
| `lowdefy_snapshot_state` | Capture live page state + request responses into a committable checkpoint folder |
|
|
39
|
+
| `lowdefy_load_state` | Restore a state checkpoint — headless, or a `?_checkpoint=` URL for manual testing |
|
|
40
|
+
| `lowdefy_list_state_checkpoints` | List saved state checkpoints |
|
|
41
|
+
| `lowdefy_checkpoint_to_mocks` | Convert a state checkpoint into e2e `mocks.yaml` fixtures |
|
|
42
|
+
| `lowdefy_checkpoint` | Snapshot all config files before risky changes |
|
|
43
|
+
| `lowdefy_revert_checkpoint` | Restore config files from a checkpoint |
|
|
55
44
|
|
|
56
45
|
## Annotate your app — point, draw, copy, paste
|
|
57
46
|
|
|
@@ -81,53 +70,17 @@ The dev server rebuilds automatically when config changes, so an agent works in
|
|
|
81
70
|
1. Discover types and schemas, write or edit YAML.
|
|
82
71
|
2. Call `lowdefy_build_status` — did the build succeed? Errors come back with the exact source file and location.
|
|
83
72
|
3. Call `lowdefy_get_page_config` to confirm the page builds, and `lowdefy_screenshot_page` to see it rendered.
|
|
84
|
-
4. Runtime errors from the browser (operator errors, block render errors) also appear in `lowdefy_build_status
|
|
85
|
-
5. Server-side failures appear beside them under `serverErrors` — a request whose database filter is malformed, an endpoint step that throws, an MCP tool call or an agent tool call that fails — each with the yaml `source` (`file:line`) and `config` path that produced it, plus the `endpointId`, `requestId` and `pageId` where known. The store holds the last 50 errors and is cleared on dev server restart.
|
|
86
|
-
|
|
87
|
-
### Events are pushed — no need to poll
|
|
88
|
-
|
|
89
|
-
The dev server pushes what changed instead of waiting to be asked. Two channels carry the same events:
|
|
90
|
-
|
|
91
|
-
- **MCP clients** receive them as `notifications/message` from logger `lowdefy` on the standalone GET stream of `/lowdefy-docs/mcp` (the server declares the `logging` capability, so any MCP client surfaces them). Failed builds arrive at level `error`, everything else at `info`.
|
|
92
|
-
- **Everything else** uses `GET /lowdefy-docs/events`, a Server-Sent Events stream — `curl -N http://localhost:3000/lowdefy-docs/events` — with one frame per event, named by the event type.
|
|
93
|
-
|
|
94
|
-
Every event carries `type` and an ISO `timestamp`. The four types:
|
|
95
|
-
|
|
96
|
-
| Type | When | Carries |
|
|
97
|
-
| -------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
98
|
-
| `restart` | First event on every connection | `bootedAt` — the dev server process start time, so a reconnecting client can tell a restart from a dropped connection |
|
|
99
|
-
| `build` | Every time a rebuild finishes, success or failure | `status`, `errorCount`, `warningCount`, `errors`, `warnings`, and `stale` / `staleSince` (see below) |
|
|
100
|
-
| `client_error` | A browser reports a runtime error | The same entry `lowdefy_build_status` lists under `clientErrors` |
|
|
101
|
-
| `server_error` | A request, endpoint, MCP tool or agent tool fails on the server | The same entry `lowdefy_build_status` lists under `serverErrors` |
|
|
102
|
-
|
|
103
|
-
Events are not buffered: a client that connects after an event missed it, and should call `lowdefy_build_status` — which is derived from the same `build/buildStatus.json` — for the current picture. Act on a `build` event with `status: "error"` immediately; `lowdefy_build_status` remains the full report.
|
|
104
|
-
|
|
105
|
-
### When a build fails, answers are marked stale
|
|
106
|
-
|
|
107
|
-
A failed rebuild does not take the dev server down — it keeps serving the last build that succeeded, so you can carry on looking at the app while you fix the error. That means every tool can keep answering from a build that predates the latest edits, which is a trap for an agent.
|
|
108
|
-
|
|
109
|
-
So while the last build is failing, every answer says so:
|
|
110
|
-
|
|
111
|
-
- MCP tool results start with a `STALE: ...` text item.
|
|
112
|
-
- JSON responses from `/lowdefy-docs` carry `stale: true`, `staleSince` and `staleReason`.
|
|
113
|
-
- Markdown responses are prefixed with a `> STALE: ...` banner.
|
|
114
|
-
- Every `/lowdefy-docs` response carries the `X-Lowdefy-Stale` and `X-Lowdefy-Stale-Since` headers.
|
|
115
|
-
|
|
116
|
-
Nothing is refused — the last-known-good schema or page config is often exactly what you need while fixing the build. The flag disappears as soon as a build succeeds. Call `lowdefy_build_status` (or `GET /lowdefy-docs/build-status`) for the errors.
|
|
117
|
-
|
|
118
|
-
Some build warnings are only warnings in `lowdefy dev` — they fail `lowdefy build`. Those carry `"prodError": true` in `lowdefy_build_status`, are printed in the dev terminal as `[ConfigWarning · fails in prod] …`, and are badged **fails in prod** on a dark-orange bar in the browser error bar. Treat them as errors: the production build will not pass until they are fixed.
|
|
73
|
+
4. Runtime errors from the browser (operator errors, block render errors) also appear in `lowdefy_build_status`, so problems that only show at runtime still reach the agent.
|
|
119
74
|
|
|
120
75
|
## Live state — the agent sees what you see
|
|
121
76
|
|
|
122
77
|
When you have a page open in your browser, the agent can read its **actual live state** — page state, request results, and the event log of recent actions — via `lowdefy_inspect_state`. Reproduce a problem by clicking through the app, then ask the agent to look: it inspects your exact tab, not a guess. `lowdefy_eval_operator` then evaluates any operator expression (like `{"_state": "customer.name"}`) against that same live context, so `_state`/`_request` binding bugs get debugged against real data. With no tab open, both tools run the page headless instead.
|
|
123
78
|
|
|
124
|
-
A headless capture waits for the page's full async lifecycle before it reads anything: `onInit`, `onInitAsync`, `onMount` and `onMountAsync`, every in-flight request, and the first message on each websocket subscription. A page whose data arrives through an `onMountAsync` `Request` action is therefore captured with its data, not empty. If a page has not settled within 15 seconds the result is still returned, carrying `ready: false` and a note — read that result as a snapshot of a page that was still loading, not as the page's settled state. One case escapes the wait: an event with a `debounce` is not yet marked loading during its delay, so a debounced `onMountAsync` can be captured before it runs.
|
|
125
|
-
|
|
126
79
|
## Auth-protected pages
|
|
127
80
|
|
|
128
81
|
The headless renderer behind `lowdefy_screenshot_page` and `lowdefy_inspect_state` authenticates as a signed-in user, so pages with `auth.public: false` render for the agent instead of returning a 404. That default user carries **no roles**, so a page or request gated on a role still comes back refused or empty.
|
|
129
82
|
|
|
130
|
-
To act as a specific caller, pass `user` — every tool that renders a page headless accepts it
|
|
83
|
+
To act as a specific caller, pass `user` — every tool that renders a page headless accepts it: `lowdefy_screenshot_page`, `lowdefy_inspect_state`, `lowdefy_eval_operator` and `lowdefy_load_state`:
|
|
131
84
|
|
|
132
85
|
```json
|
|
133
86
|
{ "pageId": "users", "user": { "roles": ["admin"] } }
|
|
@@ -135,7 +88,7 @@ To act as a specific caller, pass `user` — every tool that renders a page head
|
|
|
135
88
|
|
|
136
89
|
It is merged over the default user, so `{"roles": [...]}` is usually all you need. No auth engine runs for an injected caller, so nothing derives the rest of the record — include `email`, `profile` or `attributes` in the object if the page reads them. Every call opens its own browser context, so one call can act as an admin and the next as a plain member.
|
|
137
90
|
|
|
138
|
-
`user` applies to the headless renderer only — it is never applied to a page you open in your own browser, which carries your real session and cannot be re-identified. `lowdefy_inspect_state` and `lowdefy_eval_operator` normally prefer your open tab, so passing `user` selects the headless source instead; combining it with `source: "tab"` is an error rather than a silently ignored role, as is combining it with `lowdefy_load_state`'s `mode: "registry-only"` (that mode hands you a URL to open yourself). The plain HTTP routes take the same param: `?user={"roles":["admin"]}` on the GET routes, a `user` key in the body of `POST /lowdefy-docs/
|
|
91
|
+
`user` applies to the headless renderer only — it is never applied to a page you open in your own browser, which carries your real session and cannot be re-identified. `lowdefy_inspect_state` and `lowdefy_eval_operator` normally prefer your open tab, so passing `user` selects the headless source instead; combining it with `source: "tab"` is an error rather than a silently ignored role, as is combining it with `lowdefy_load_state`'s `mode: "registry-only"` (that mode hands you a URL to open yourself). The plain HTTP routes take the same param: `?user={"roles":["admin"]}` on the GET routes, a `user` key in the body of `POST /lowdefy-docs/eval-operator` and `POST /lowdefy-docs/state-checkpoints/load`. They answer a malformed or contradictory `user` with a `400`, distinct from the `502` a failed render returns.
|
|
139
92
|
|
|
140
93
|
To bypass login for the whole dev server — your own browser included — start it with a mock user instead: `lowdefy dev --mock-user '{"sub":"dev","roles":["admin"]}'` (or configure `auth.dev.mockUser`). See [Auth Configuration](/auth-configuration#mock-user-for-testing-dev-server-only).
|
|
141
94
|
|
|
@@ -160,9 +113,7 @@ Loading it back (`lowdefy_load_state`) serves the recorded request data from the
|
|
|
160
113
|
|
|
161
114
|
## Running requests safely
|
|
162
115
|
|
|
163
|
-
`lowdefy_run_request` executes a request with a test payload so the agent can verify the data shape a page will receive. Read-only request types (like `MongoDBFind`) always run.
|
|
164
|
-
|
|
165
|
-
Pass `user` to run the request as a specific caller — `{ "pageId": "users", "requestId": "get_users", "user": { "roles": ["admin"] } }`. Without `user` the request runs as a roleless anonymous caller, so a tenant-walled or role-gated request returns empty rows rather than an error. Impersonation never unlocks writes: the write gate below applies to every caller. Write requests are refused unless you opt in:
|
|
116
|
+
`lowdefy_run_request` executes a request with a test payload so the agent can verify the data shape a page will receive. Read-only request types (like `MongoDBFind`) always run. Write requests are refused unless you opt in:
|
|
166
117
|
|
|
167
118
|
```yaml
|
|
168
119
|
cli:
|
|
@@ -170,137 +121,8 @@ cli:
|
|
|
170
121
|
allowWriteRequests: true
|
|
171
122
|
```
|
|
172
123
|
|
|
173
|
-
## Running endpoints
|
|
174
|
-
|
|
175
|
-
`lowdefy_run_endpoint` (or `POST /lowdefy-docs/run-endpoint`) executes an `Api` endpoint routine headlessly with a test payload, so the agent can verify what a routine returns, rejects or throws without clicking through the page that calls it:
|
|
176
|
-
|
|
177
|
-
```json
|
|
178
|
-
{ "endpointId": "create_order", "payload": { "sku": "A1" }, "user": { "roles": ["admin"] } }
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
Endpoints are not classified read-only — a routine has no `checkWrite` meta, and a single routine can read, write, call other endpoints and send notifications — so running one always requires the same `cli.agentTools.allowWriteRequests: true` opt-in as write requests. Without it the tool answers `refused: true` with the reason and how to enable it, and nothing runs.
|
|
182
|
-
|
|
183
|
-
The result is the same `{ error, response, status, success }` object the HTTP endpoint route returns. A `:reject` or `:throw` in the routine is not a tool failure: it comes back as `success: false` with `status: "reject"` or `"error"` and the routine's own `error`, so the agent can assert on the shape it designed. `InternalApi` endpoints are refused with the same message HTTP callers get, an unknown `endpointId` answers `refused: true`, and faults that escape the routine (an auth refusal, a missing connection) come back as `error: { name, message, source, configKey }`. Only malformed input — a missing `endpointId` or a non-object `user` — is a `400`.
|
|
184
|
-
|
|
185
124
|
This is dev-only — enable it when you're comfortable with the agent writing to your dev data.
|
|
186
125
|
|
|
187
|
-
## Journeys — verify behaviour, not just layout
|
|
188
|
-
|
|
189
|
-
A screenshot shows what rendered; it cannot tell the agent whether the form submits, the modal opens or the filter works. `lowdefy_run_journey` (or `POST /lowdefy-docs/journey`) opens a page headless and drives it through a declarative list of steps — a tiny Playwright — then reports what happened as data. Blocks are addressed by their `blockId`, through the same `#bl-<blockId>` DOM contract `@lowdefy/e2e-utils` uses, so a journey reads like the config it tests:
|
|
190
|
-
|
|
191
|
-
```json
|
|
192
|
-
{
|
|
193
|
-
"pageId": "new_customer",
|
|
194
|
-
"user": { "roles": ["admin"] },
|
|
195
|
-
"urlQuery": { "ref": "campaign-1" },
|
|
196
|
-
"steps": [
|
|
197
|
-
{ "fill": { "blockId": "name", "value": "Ada Lovelace" } },
|
|
198
|
-
{ "select": { "blockId": "country", "value": "United Kingdom" } },
|
|
199
|
-
{ "click": "submit" },
|
|
200
|
-
{ "wait": { "request": "create_customer" } },
|
|
201
|
-
{ "expect": { "state": { "path": "created", "equals": true } } },
|
|
202
|
-
{ "expect": { "visible": "success_modal" } },
|
|
203
|
-
{ "expect": { "text": { "blockId": "success_message", "contains": "Ada Lovelace" } } },
|
|
204
|
-
{ "screenshot": "after-submit" },
|
|
205
|
-
{ "press": "Escape" },
|
|
206
|
-
{ "click": "go_to_list" },
|
|
207
|
-
{ "expect": { "url": { "contains": "/customers" } } }
|
|
208
|
-
]
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
Every step is an object with exactly one key:
|
|
213
|
-
|
|
214
|
-
| Step | What it does |
|
|
215
|
-
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
216
|
-
| `{ "click": target }` | Clicks the block — its interactive control (button, link, input, checkbox…) when it has one, otherwise the block itself — or the control a target object narrows to (below) |
|
|
217
|
-
| `{ "fill": { ...target, value } }` | Fills the block's (or grid cell's) `input` or `textarea` with `value` (converted to a string) |
|
|
218
|
-
| `{ "select": { ...target, value } }` | Opens the block's (or grid cell's) dropdown and clicks the option whose text is exactly `value`; a native `<select>` inside the block is set by label instead |
|
|
219
|
-
| `{ "press": key }` | Presses a key or chord, e.g. `"Enter"`, `"Escape"`, `"Mod+k"` — `Mod` becomes `Meta` or `Control` for the platform the page reports, the same way the app's shortcuts resolve it |
|
|
220
|
-
| `{ "wait": { "ms": n } }` | Sleeps `n` milliseconds |
|
|
221
|
-
| `{ "wait": { "request": requestId } }` | Waits until the request has finished loading |
|
|
222
|
-
| `{ "wait": { "state": path } }` | Waits until the state path is defined |
|
|
223
|
-
| `{ "screenshot": name? }` | Captures a PNG of the viewport; returned with the result (`true`, `null` or a missing name defaults to `step-<index>`) |
|
|
224
|
-
| `{ "expect": { "state": { path, equals } } }` | The state at `path` structurally equals `equals` (key order does not matter) |
|
|
225
|
-
| `{ "expect": { "visible": target } }` | The block, or the control a target narrows to, is visible |
|
|
226
|
-
| `{ "expect": { "text": { ...target, contains } } }` | The block's text — or a grid row's or cell's — contains the string |
|
|
227
|
-
| `{ "expect": { "url": { contains } } }` | The page url contains the string |
|
|
228
|
-
|
|
229
|
-
A **target** is a `blockId` string, or an object that narrows the search to a control that is not itself a block — a grid row's cell buttons, a confirm dialog's OK, a dropdown menu's items:
|
|
230
|
-
|
|
231
|
-
| Target key | Meaning |
|
|
232
|
-
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
233
|
-
| `blockId` | The block to search inside |
|
|
234
|
-
| `row` | A grid row, zero-based as displayed (`AgGrid*` blocks, `.ag-row[row-index]`); needs `blockId` |
|
|
235
|
-
| `column` | A grid cell in that row, by the column's `field` or `colId` (`.ag-cell[col-id]`); needs `blockId` |
|
|
236
|
-
| `text` | The visible interactive control whose text is exactly this — a button label, a tab, a menu item (`"Cat"` never matches `"Category"`) |
|
|
237
|
-
| `nth` | When several controls match, the zero-based one to use |
|
|
238
|
-
|
|
239
|
-
`text` without a `blockId` searches the page, front-most layer first — an open dropdown menu (`[role="menu"]`), then an open dialog (`[role="dialog"]`), then the page — so `{ "click": { "text": "Delete" } }` with a confirm dialog open clicks the dialog's Delete and not the grid's, without counting buttons. `fill`, `select` and `expect.text` always need a `blockId`. Unknown target keys are rejected before a browser opens.
|
|
240
|
-
|
|
241
|
-
```json
|
|
242
|
-
[
|
|
243
|
-
{ "click": { "blockId": "controls_grid", "row": 1, "text": "Delete" } },
|
|
244
|
-
{ "expect": { "visible": { "text": "Are you sure?" } } },
|
|
245
|
-
{ "click": { "text": "Delete" } },
|
|
246
|
-
{ "wait": { "request": "delete_control" } },
|
|
247
|
-
{
|
|
248
|
-
"expect": {
|
|
249
|
-
"text": {
|
|
250
|
-
"blockId": "controls_grid",
|
|
251
|
-
"row": 1,
|
|
252
|
-
"column": "title",
|
|
253
|
-
"contains": "Key rotation"
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
{ "click": { "blockId": "controls_grid", "row": 0, "column": "more" } },
|
|
258
|
-
{ "click": { "text": "Archive" } }
|
|
259
|
-
]
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Each step gets 5 seconds (Playwright's own auto-waiting — a `click` waits for the block to be attached, visible and enabled; nothing is retried). After an interaction the runner waits for the page to settle — the event the interaction fired, the requests it called — using the same readiness check the page open uses, so the next `expect` asserts against the outcome instead of racing it. The page opens with the same headless `user` and `urlQuery` handling as the other tools.
|
|
263
|
-
|
|
264
|
-
A step that fails **stops the journey and comes back as data**, never as a tool error — a failed journey is the answer, not a fault:
|
|
265
|
-
|
|
266
|
-
```json
|
|
267
|
-
{
|
|
268
|
-
"pageId": "new_customer",
|
|
269
|
-
"passed": false,
|
|
270
|
-
"steps": [
|
|
271
|
-
{
|
|
272
|
-
"index": 0,
|
|
273
|
-
"step": { "fill": { "blockId": "name", "value": "Ada Lovelace" } },
|
|
274
|
-
"status": "ok",
|
|
275
|
-
"durationMs": 18
|
|
276
|
-
},
|
|
277
|
-
{ "index": 1, "step": { "click": "submit" }, "status": "ok", "durationMs": 71 },
|
|
278
|
-
{
|
|
279
|
-
"index": 2,
|
|
280
|
-
"step": { "expect": { "state": { "path": "created", "equals": true } } },
|
|
281
|
-
"status": "failed",
|
|
282
|
-
"durationMs": 1
|
|
283
|
-
},
|
|
284
|
-
{ "index": 3, "step": { "screenshot": "after-submit" }, "status": "skipped", "durationMs": 0 }
|
|
285
|
-
],
|
|
286
|
-
"failure": {
|
|
287
|
-
"index": 2,
|
|
288
|
-
"step": { "expect": { "state": { "path": "created", "equals": true } } },
|
|
289
|
-
"expected": true,
|
|
290
|
-
"actual": null,
|
|
291
|
-
"message": "Expected state \"created\" to equal true but found undefined."
|
|
292
|
-
},
|
|
293
|
-
"screenshots": [],
|
|
294
|
-
"state": { "name": "Ada Lovelace", "country": null }
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
`expected` and `actual` hold the compared values for an `expect` step, and for an interaction step what the runner needed (`block "submit" to be actionable`, `option "Chile" in the dropdown of block "country"`) against Playwright's own message. The remaining steps are marked `skipped`. A passing journey returns `passed: true` and no `failure`. The final page `state` is always included, pass or fail — it is what the agent needs to write the next assertion. Screenshots taken before the failure are kept: over MCP they arrive as image content after the JSON (with the JSON listing only their names); over HTTP they are base64 in the JSON body.
|
|
299
|
-
|
|
300
|
-
Malformed steps are answered before a browser opens — an unknown key returns `Unknown journey step "hover". Steps are: click, fill, select, press, wait, screenshot, expect.` (a `400` on the HTTP route), distinct from the `502` a render that could not run returns.
|
|
301
|
-
|
|
302
|
-
Journeys are also the file format of `tests/journeys/*.yaml`, which `lowdefy test` runs through this same route — write the journey the agent used to verify a change, and it becomes the regression test for it.
|
|
303
|
-
|
|
304
126
|
## Setting up a project — one command
|
|
305
127
|
|
|
306
128
|
```bash
|
|
@@ -370,30 +192,26 @@ operator expressions against real state, and `lowdefy_snapshot_state` /
|
|
|
370
192
|
|
|
371
193
|
Everything the MCP tools serve is also available as plain GET routes — useful for `curl`, scripts, or agents without MCP support:
|
|
372
194
|
|
|
373
|
-
| Route
|
|
374
|
-
|
|
|
375
|
-
| `GET /lowdefy-docs`
|
|
376
|
-
| `GET /lowdefy-docs/{kind}`
|
|
377
|
-
| `GET /lowdefy-docs/plugins`
|
|
378
|
-
| `GET /lowdefy-docs/schema/{kind}/{type}`
|
|
379
|
-
| `GET /lowdefy-docs/examples/{type}`
|
|
380
|
-
| `GET /lowdefy-docs/content/{slug}`
|
|
381
|
-
| `GET /lowdefy-docs/search?q={query}
|
|
382
|
-
| `GET /lowdefy-docs/plugin-doc/{package}`
|
|
383
|
-
| `GET /lowdefy-docs/build-status`
|
|
384
|
-
| `GET /lowdefy-docs/
|
|
385
|
-
| `GET /lowdefy-docs/
|
|
386
|
-
| `GET /lowdefy-docs/
|
|
387
|
-
| `
|
|
388
|
-
| `GET /lowdefy-docs/
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
391
|
-
| `POST /lowdefy-docs/
|
|
392
|
-
| `POST /lowdefy-docs/
|
|
393
|
-
| `POST /lowdefy-docs/run-endpoint` | Execute an Api endpoint routine with a test payload and caller (needs `allowWriteRequests`; rejects return as data) |
|
|
394
|
-
| `GET/POST /lowdefy-docs/checkpoints` + `/revert` | Config-file checkpoints |
|
|
395
|
-
| `GET/POST /lowdefy-docs/state-checkpoints` + `/snapshot`, `/load` | State & data checkpoints |
|
|
396
|
-
| `POST /lowdefy-docs/restart` | Restart the dev server process (`{reason}` optional; poll `build-status` after ~2s) |
|
|
195
|
+
| Route | Purpose |
|
|
196
|
+
| --------------------------- | -------------------------------------------------------------- |
|
|
197
|
+
| `GET /lowdefy-docs` | Overview and route index |
|
|
198
|
+
| `GET /lowdefy-docs/{kind}` | List all available types of a kind, e.g. `/lowdefy-docs/blocks` |
|
|
199
|
+
| `GET /lowdefy-docs/plugins` | Installed plugin packages and the types each provides |
|
|
200
|
+
| `GET /lowdefy-docs/schema/{kind}/{type}` | JSON schema for a type, e.g. `/lowdefy-docs/schema/blocks/Button` |
|
|
201
|
+
| `GET /lowdefy-docs/examples/{type}` | Example YAML for a block type |
|
|
202
|
+
| `GET /lowdefy-docs/content/{slug}` | A docs page as markdown, e.g. `/lowdefy-docs/content/operators/_get` |
|
|
203
|
+
| `GET /lowdefy-docs/search?q={query}`| Search the docs |
|
|
204
|
+
| `GET /lowdefy-docs/plugin-doc/{package}` | Markdown shipped inside a plugin package |
|
|
205
|
+
| `GET /lowdefy-docs/build-status` | Current build errors/warnings + recent browser runtime errors |
|
|
206
|
+
| `GET /lowdefy-docs/page-config/{pageId}` | Fully built page config, or its build errors |
|
|
207
|
+
| `GET /lowdefy-docs/screenshot/{pageId}` | PNG screenshot of the rendered page |
|
|
208
|
+
| `GET /lowdefy-docs/find/{id}?pageId=` | Locate where a page/block/request id is defined |
|
|
209
|
+
| `GET /lowdefy-docs/app-map` | Whole-app graph: pages, menus, connections, endpoints, agents |
|
|
210
|
+
| `GET /lowdefy-docs/inspect-state/{pageId}` | Live state/requests/eventLog of a running page (tab or headless) |
|
|
211
|
+
| `POST /lowdefy-docs/eval-operator` | Evaluate an operator expression against live page state |
|
|
212
|
+
| `POST /lowdefy-docs/run-request` | Execute a request with a test payload (read-only unless opted in) |
|
|
213
|
+
| `GET/POST /lowdefy-docs/checkpoints` + `/revert` | Config-file checkpoints |
|
|
214
|
+
| `GET/POST /lowdefy-docs/state-checkpoints` + `/snapshot`, `/load` | State & data checkpoints |
|
|
397
215
|
|
|
398
216
|
## Local plugins
|
|
399
217
|
|
|
@@ -403,7 +221,6 @@ Your project's own plugins (declared under `plugins:` in `lowdefy.yaml`) are inc
|
|
|
403
221
|
- Block schemas are derived from each block's `meta`, and connection/request schemas from the `schema` property on your connection and request definitions.
|
|
404
222
|
- Ship a `gallery.yaml` or `examples.yaml` next to a block in your plugin's `dist/blocks/{BlockName}/` and it is served by `lowdefy_get_examples`.
|
|
405
223
|
- A `README.md` or `docs/*.md` files in your plugin package are served by `lowdefy_get_plugin_doc`.
|
|
406
|
-
- Editing a local plugin's server-side source (connections, requests, server operators, agents, websockets, notifications, auth) under its `src/` restarts the dev server automatically, so the new implementation is what runs. Block, action and client-operator edits hot-reload through Vite without a restart. If the server still looks stale, call `lowdefy_restart` or `POST /lowdefy-docs/restart`.
|
|
407
224
|
|
|
408
225
|
## Docs for crawling agents
|
|
409
226
|
|
package/content/concepts/cli.md
CHANGED
|
@@ -113,19 +113,6 @@ The `init-vercel` command initializes the installation scripts needed to deploy
|
|
|
113
113
|
- `--disable-telemetry`: Disable telemetry.
|
|
114
114
|
- `--log-level <level>`: The minimum severity of logs to show in the CLI output. Options are `debug`, `info`, `warn` or `error`. The default is `info`.
|
|
115
115
|
|
|
116
|
-
## test
|
|
117
|
-
|
|
118
|
-
The `test` command runs the app's config tests — the journeys in `tests/journeys/*.yaml`. It starts the development server headless on a free port, runs every journey through the dev server's journey route, prints `PASS`/`FAIL` per journey with the failing step, stops the server, and exits with code `1` if any journey failed. See [Config Tests](/config-tests).
|
|
119
|
-
|
|
120
|
-
- `--config-directory <config-directory>`: Change the config directory. The default is the current working directory.
|
|
121
|
-
- `--dev-directory <dev-directory>`: Change the dev directory, the directory in which the development server is placed. The default is `<config-directory>/.lowdefy/dev`.
|
|
122
|
-
- `--disable-telemetry`: Disable telemetry.
|
|
123
|
-
- `--filter <name>`: Only run journeys whose `name` contains this string (case-insensitive). Exits with code `1` if no journey matches.
|
|
124
|
-
- `--log-level <level>`: The minimum severity of logs to show in the CLI output. Options are `debug`, `info`, `warn` or `error`. The default is `info`.
|
|
125
|
-
- `--port <port>`: The port to start the development server on. If it is in use, the next free port is used. The default is `3000`.
|
|
126
|
-
- `--ref-resolver <ref-resolver-function-path>`: Path to a JavaScript file containing a `_ref` resolver function to be used as the app default `_ref` resolver.
|
|
127
|
-
- `--url <url>`: Run the journeys against an already running development server (for example `--url http://localhost:3000` while `lowdefy dev` is running) instead of starting one.
|
|
128
|
-
|
|
129
116
|
## upgrade
|
|
130
117
|
|
|
131
118
|
The `upgrade` command upgrades a Lowdefy app to a newer version by walking you through migration prompts that handle breaking changes.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Lowdefy provides several ways to style blocks and customize the look of your app:
|
|
4
4
|
|
|
5
5
|
- **`class`** — Apply CSS class names (including Tailwind CSS utility classes) to blocks.
|
|
6
|
-
- **`style`** — Apply inline CSS styles to blocks. Use
|
|
6
|
+
- **`style`** — Apply inline CSS styles to blocks. Use `--`-prefixed keys to target specific parts (CSS slots).
|
|
7
7
|
- **`properties.theme`** — Override antd design tokens per block. See [Theming](/theming) for details.
|
|
8
8
|
- **`public/styles.css`** — A global CSS file for custom styles.
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ Lowdefy provides several ways to style blocks and customize the look of your app
|
|
|
11
11
|
|
|
12
12
|
The `class` property applies CSS class names to a block. [Tailwind CSS](https://tailwindcss.com/) is always available in Lowdefy apps, so you can use any Tailwind utility classes directly.
|
|
13
13
|
|
|
14
|
-
`class` can be a string, an array of strings, an object keyed by the block's CSS keys
|
|
14
|
+
`class` can be a string, an array of strings, or an object keyed by the block's CSS keys:
|
|
15
15
|
|
|
16
16
|
```yaml
|
|
17
17
|
# String — applied to the block wrapper
|
|
@@ -27,60 +27,15 @@ The `class` property applies CSS class names to a block. [Tailwind CSS](https://
|
|
|
27
27
|
- shadow-lg
|
|
28
28
|
- rounded-xl
|
|
29
29
|
|
|
30
|
-
# Object — target specific parts of the block using CSS keys (
|
|
30
|
+
# Object — target specific parts of the block using CSS keys (-- prefix)
|
|
31
31
|
- id: my_card
|
|
32
32
|
type: Card
|
|
33
33
|
class:
|
|
34
34
|
.element: "shadow-lg"
|
|
35
35
|
.header: "bg-gray-100"
|
|
36
36
|
.body: "p-8"
|
|
37
|
-
|
|
38
|
-
# Operator — the block's classes are computed at runtime
|
|
39
|
-
- id: status_badge
|
|
40
|
-
type: Box
|
|
41
|
-
class:
|
|
42
|
-
_if:
|
|
43
|
-
test:
|
|
44
|
-
_eq:
|
|
45
|
-
- _state: status
|
|
46
|
-
- error
|
|
47
|
-
then: bg-red-100 text-red-700
|
|
48
|
-
else: bg-green-100 text-green-700
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
An operator's result may be a string, an array of strings, or an object of `{ class: boolean }` where each class with a truthy value is applied:
|
|
52
|
-
|
|
53
|
-
```yaml
|
|
54
|
-
- id: status_badge
|
|
55
|
-
type: Box
|
|
56
|
-
class:
|
|
57
|
-
_if:
|
|
58
|
-
test:
|
|
59
|
-
_eq:
|
|
60
|
-
- _state: status
|
|
61
|
-
- error
|
|
62
|
-
then:
|
|
63
|
-
bg-red-100: true
|
|
64
|
-
text-red-700: true
|
|
65
|
-
else: bg-green-100
|
|
66
37
|
```
|
|
67
38
|
|
|
68
|
-
An operator at the root of `class` computes the classes of the block wrapper. To compute a specific part's classes, place the operator under that CSS key:
|
|
69
|
-
|
|
70
|
-
```yaml
|
|
71
|
-
- id: my_card
|
|
72
|
-
type: Card
|
|
73
|
-
class:
|
|
74
|
-
.header:
|
|
75
|
-
_if:
|
|
76
|
-
test:
|
|
77
|
-
_state: highlighted
|
|
78
|
-
then: bg-yellow-100
|
|
79
|
-
else: bg-gray-100
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
> **Note:** An operator's result is never expanded into a map of CSS keys — a root operator always sets the block wrapper's classes.
|
|
83
|
-
|
|
84
39
|
Tailwind responsive prefixes work as expected:
|
|
85
40
|
|
|
86
41
|
```yaml
|
|
@@ -103,7 +58,7 @@ See [Theming](/theming) for the full list of bridged tokens and how to add custo
|
|
|
103
58
|
|
|
104
59
|
## The `style` property
|
|
105
60
|
|
|
106
|
-
The `style` property applies inline CSS styles to a block. When used with flat CSS properties (no
|
|
61
|
+
The `style` property applies inline CSS styles to a block. When used with flat CSS properties (no `--` prefix), it targets the block's layout wrapper:
|
|
107
62
|
|
|
108
63
|
```yaml
|
|
109
64
|
- id: my_block
|
|
@@ -148,7 +103,7 @@ Each block's documentation page lists its supported CSS keys.
|
|
|
148
103
|
|
|
149
104
|
Each block declares named style targets in its `meta.cssKeys`. For example, the [Card](/Card) block supports: `element`, `header`, `body`, `cover`, `actions`, `extra`.
|
|
150
105
|
|
|
151
|
-
Both `class` (object form) and `style` use
|
|
106
|
+
Both `class` (object form) and `style` use `--`-prefixed versions of these keys to target specific parts of a block. The `--` prefix mirrors CSS custom property convention and avoids confusion with CSS property names.
|
|
152
107
|
|
|
153
108
|
To find the CSS keys for a block, check the block's documentation page.
|
|
154
109
|
|
package/content/concepts/i18n.md
CHANGED
|
@@ -214,7 +214,7 @@ config:
|
|
|
214
214
|
agent.antdx.actions.feedbackDislike: 'Gefällt mir nicht'
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
**Agent runtime errors.** HTTP
|
|
217
|
+
**Agent runtime errors.** HTTP 400/500 errors from the agent endpoint (`Only POST requests are supported.`, `Invalid agent path`, `Agent "X" does not exist.`, etc.) translate via `agent.runtime.*` keys against the request's `Accept-Language`. Server-side validation errors and stack traces stay in English (developer diagnostics).
|
|
218
218
|
|
|
219
219
|
**Agent config translation.** `agent.properties.instructions` passes through the operator parser at request time, so `_t:` works:
|
|
220
220
|
|
|
@@ -103,7 +103,7 @@ Blocks can also be laid out using [CSS flexbox](https://developer.mozilla.org/en
|
|
|
103
103
|
The `layout` object on blocks can be used to control how a block is placed in the layout. The `layout` properties that can be defined are:
|
|
104
104
|
|
|
105
105
|
- `selfAlign`: _Enum_ - Align block vertically in the area. Options are `top`, `middle`, and `bottom`. Default `top.`
|
|
106
|
-
- `align`: _Enum_ - Align content area children vertically. Options are `top`, `middle`, and `bottom`. Default `top.` This value flows to the content area as a default.
|
|
106
|
+
- `align`: _Enum_ - Align content area children vertically. Options are `top`, `middle`, and `bottom`. Default `top.` This value flows to the content area as a default.
|
|
107
107
|
- `flex`: _String_ - Set the [`flex`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) CSS property. This overwrites the `grow`, `shrink`, and `size` properties.
|
|
108
108
|
- `gap`: _Number_ | _Array_ - Set the gap (space) between blocks in the content area. If an array, the first element is the horizontal gap, and the second is the vertical gap. This value flows to the content area as a default.
|
|
109
109
|
- `grow`: _Number_ - Set the [`flex-grow`](https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow) CSS property. Default 0.
|