@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.
Files changed (54) hide show
  1. package/README.md +0 -1
  2. package/content/actions/link.md +0 -17
  3. package/content/agents/agentchat.md +1 -13
  4. package/content/concepts/ai-agent-docs.md +49 -232
  5. package/content/concepts/cli.md +0 -13
  6. package/content/concepts/custom-styling.md +5 -50
  7. package/content/concepts/i18n.md +1 -1
  8. package/content/concepts/layout-overview.md +1 -1
  9. package/content/connections/mongodb.md +3 -121
  10. package/content/container-blocks/collapse.md +2 -2
  11. package/content/container-blocks/layout.md +0 -1
  12. package/content/container-blocks/modal.md +1 -1
  13. package/content/container-blocks/pagesidebarlayout.md +0 -1
  14. package/content/container-blocks/sider.md +1 -1
  15. package/content/container-blocks/spinner.md +1 -1
  16. package/content/controls/reject.md +1 -3
  17. package/content/controls/throw.md +0 -1
  18. package/content/controls/try.md +2 -3
  19. package/content/display-blocks/aggrid.md +18 -18
  20. package/content/display-blocks/avatar.md +2 -2
  21. package/content/display-blocks/difflist.md +1 -1
  22. package/content/display-blocks/difftimeline.md +1 -1
  23. package/content/display-blocks/echart.md +1 -35
  24. package/content/display-blocks/menu.md +1 -1
  25. package/content/display-blocks/qrcode.md +2 -2
  26. package/content/display-blocks/s3download.md +1 -1
  27. package/content/display-blocks/search.md +0 -1
  28. package/content/display-blocks/skeletonbutton.md +0 -1
  29. package/content/display-blocks/skeletoninput.md +0 -1
  30. package/content/input-blocks/buttonselector.md +1 -1
  31. package/content/input-blocks/checkboxselector.md +5 -81
  32. package/content/input-blocks/colorselector.md +0 -14
  33. package/content/input-blocks/multipleselector.md +1 -1
  34. package/content/input-blocks/phonenumberinput.md +1 -2
  35. package/content/input-blocks/radioselector.md +4 -66
  36. package/content/input-blocks/s3uploadbutton.md +1 -1
  37. package/content/input-blocks/s3uploaddragger.md +1 -1
  38. package/content/input-blocks/s3uploadphoto.md +1 -1
  39. package/content/input-blocks/segmentedselector.md +1 -1
  40. package/content/input-blocks/selector.md +1 -1
  41. package/content/input-blocks/tiptapmentioninput.md +1 -1
  42. package/content/input-blocks/treeinput.md +1 -1
  43. package/content/input-blocks/treemultipleselector.md +1 -1
  44. package/content/input-blocks/treeselector.md +1 -1
  45. package/content/list-blocks/masonrylist.md +1 -1
  46. package/content/operators/_type.md +3 -14
  47. package/content/plugins/plugins-introduction.md +0 -15
  48. package/index.json +1 -31
  49. package/package.json +1 -3
  50. package/content/controls/while.md +0 -46
  51. package/content/input-blocks/tagmultipleselector.md +0 -101
  52. package/content/input-blocks/tagselector.md +0 -101
  53. package/content/testing/config-tests.md +0 -164
  54. 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
 
@@ -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. `rating` is `like` or `dislike`, or `default` when the user clicks the selected thumb again to clear the rating — branch on it explicitly rather than treating anything that is not `like` as a dislike. | `messageId`, `messageContent`, `rating` |
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 | 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_run_journey` | Drive a page headless through declarative steps (`click`, `fill`, `select`, `press`, `wait`, `screenshot`, `expect`) and assert state, visibility, text or url — verify behaviour, not just layout |
33
- | `lowdefy_find_config` | Which yaml file (and line) defines a given page, block, or request id |
34
- | `lowdefy_scaffold_page` | Create a new page yaml file with a canonical minimal structure |
35
- | `lowdefy_app_map` | The whole-app graph: every page, menu, connection, endpoint, and agent in one call |
36
- | `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 |
37
- | `lowdefy_eval_operator` | Evaluate any operator expression against live page state — a REPL for config |
38
- | `lowdefy_run_request` | Execute a request with a test payload, as a given `user`, to verify data shape (read-only unless opted in) |
39
- | `lowdefy_run_endpoint` | Execute an Api endpoint routine with a test payload, as a given `user`, to see what it returns, rejects or throws (needs `allowWriteRequests`; a `:reject` comes back as data) |
40
- | `lowdefy_snapshot_state` | Capture live page state + request responses into a committable checkpoint folder |
41
- | `lowdefy_load_state` | Restore a state checkpoint — headless, or a `?_checkpoint=` URL for manual testing |
42
- | `lowdefy_list_state_checkpoints` | List saved state checkpoints |
43
- | `lowdefy_checkpoint_to_mocks` | Convert a state checkpoint into e2e `mocks.yaml` fixtures |
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` under `clientErrors`, so problems that only show at runtime still reach the agent.
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 (`lowdefy_screenshot_page`, `lowdefy_run_journey`, `lowdefy_inspect_state`, `lowdefy_eval_operator`, `lowdefy_load_state`), as do `lowdefy_run_request` and `lowdefy_run_endpoint`:
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/journey`, `POST /lowdefy-docs/eval-operator`, `POST /lowdefy-docs/state-checkpoints/load`, `POST /lowdefy-docs/run-request` and `POST /lowdefy-docs/run-endpoint`. They answer a malformed or contradictory `user` with a `400`, distinct from the `502` a failed render returns.
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 | Purpose |
374
- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
375
- | `GET /lowdefy-docs` | Overview and route index |
376
- | `GET /lowdefy-docs/{kind}` | List all available types of a kind, e.g. `/lowdefy-docs/blocks` |
377
- | `GET /lowdefy-docs/plugins` | Installed plugin packages and the types each provides |
378
- | `GET /lowdefy-docs/schema/{kind}/{type}` | JSON schema for a type, e.g. `/lowdefy-docs/schema/blocks/Button` |
379
- | `GET /lowdefy-docs/examples/{type}` | Example YAML for a block type |
380
- | `GET /lowdefy-docs/content/{slug}` | A docs page as markdown, e.g. `/lowdefy-docs/content/operators/_get` |
381
- | `GET /lowdefy-docs/search?q={query}` | Search the docs |
382
- | `GET /lowdefy-docs/plugin-doc/{package}` | Markdown shipped inside a plugin package |
383
- | `GET /lowdefy-docs/build-status` | Current build errors/warnings + recent browser runtime errors |
384
- | `GET /lowdefy-docs/events` | SSE stream of `restart`, `build`, `client_error` and `server_error` events |
385
- | `GET /lowdefy-docs/page-config/{pageId}` | Fully built page config, or its build errors |
386
- | `GET /lowdefy-docs/screenshot/{pageId}` | PNG screenshot of the rendered page |
387
- | `POST /lowdefy-docs/journey` | Drive a page headless through declarative steps and assert what happens; screenshots returned as base64 |
388
- | `GET /lowdefy-docs/find/{id}?pageId=` | Locate where a page/block/request id is defined |
389
- | `GET /lowdefy-docs/app-map` | Whole-app graph: pages, menus, connections, endpoints, agents |
390
- | `GET /lowdefy-docs/inspect-state/{pageId}` | Live state/requests/eventLog of a running page (tab or headless) |
391
- | `POST /lowdefy-docs/eval-operator` | Evaluate an operator expression against live page state |
392
- | `POST /lowdefy-docs/run-request` | Execute a request with a test payload (read-only unless opted in) |
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
 
@@ -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 `.`-prefixed keys to target specific parts (CSS slots).
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, or an operator that computes the classes at runtime:
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 (. prefix)
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 `.` prefix), it targets the block's layout wrapper:
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 `.`-prefixed versions of these keys to target specific parts of a block. The `.` prefix mirrors CSS selector convention and avoids confusion with CSS property names.
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
 
@@ -214,7 +214,7 @@ config:
214
214
  agent.antdx.actions.feedbackDislike: 'Gefällt mir nicht'
215
215
  ```
216
216
 
217
- **Agent runtime errors.** HTTP error responses from the agent endpoint (a 405 for a wrong method, a 400 for a malformed request, and so on: `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).
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. In Lowdefy v4 `layout.align` aligned the block itself within its parent — that is now `layout.selfAlign`. Setting `layout.align` on a block aligns the block's own content, never the block itself.
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.