@lowdefy/docs-content 0.0.0-experimental-20260710073520

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 (304) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +20 -0
  3. package/content/actions/callapi.md +66 -0
  4. package/content/actions/callmethod.md +41 -0
  5. package/content/actions/copytoclipboard.md +32 -0
  6. package/content/actions/displaymessage.md +53 -0
  7. package/content/actions/downloadcsv.md +89 -0
  8. package/content/actions/fetch.md +50 -0
  9. package/content/actions/geolocationcurrentposition.md +56 -0
  10. package/content/actions/link.md +104 -0
  11. package/content/actions/login.md +85 -0
  12. package/content/actions/logout.md +56 -0
  13. package/content/actions/pdfmake.md +304 -0
  14. package/content/actions/request.md +67 -0
  15. package/content/actions/reset.md +27 -0
  16. package/content/actions/resetvalidation.md +85 -0
  17. package/content/actions/scrollto.md +85 -0
  18. package/content/actions/setdarkmode.md +62 -0
  19. package/content/actions/setfocus.md +33 -0
  20. package/content/actions/setglobal.md +63 -0
  21. package/content/actions/setlocale.md +69 -0
  22. package/content/actions/setstate.md +70 -0
  23. package/content/actions/throw.md +72 -0
  24. package/content/actions/validate.md +87 -0
  25. package/content/actions/wait.md +24 -0
  26. package/content/agents/agent-endpoint-tools.md +190 -0
  27. package/content/agents/agent-mcp-tools.md +124 -0
  28. package/content/agents/agent-multi-agent.md +136 -0
  29. package/content/agents/agent-properties.md +174 -0
  30. package/content/agents/agent-server-hooks.md +336 -0
  31. package/content/agents/agentchat.md +515 -0
  32. package/content/agents/agentconversations.md +148 -0
  33. package/content/concepts/ai-agent-docs.md +190 -0
  34. package/content/concepts/cli.md +213 -0
  35. package/content/concepts/connections-and-requests.md +7 -0
  36. package/content/concepts/custom-html.md +3 -0
  37. package/content/concepts/custom-styling.md +161 -0
  38. package/content/concepts/events-and-actions.md +271 -0
  39. package/content/concepts/hosting-files.md +18 -0
  40. package/content/concepts/i18n.md +320 -0
  41. package/content/concepts/keyboard-shortcuts.md +218 -0
  42. package/content/concepts/layout-overview.md +478 -0
  43. package/content/concepts/lists.md +144 -0
  44. package/content/concepts/logger.md +43 -0
  45. package/content/concepts/lowdefy-schema.md +365 -0
  46. package/content/concepts/menus.md +144 -0
  47. package/content/concepts/module-authoring.md +564 -0
  48. package/content/concepts/modules.md +338 -0
  49. package/content/concepts/operators.md +45 -0
  50. package/content/concepts/overview.md +19 -0
  51. package/content/concepts/references-and-templates.md +207 -0
  52. package/content/concepts/secrets.md +24 -0
  53. package/content/concepts/selectors.md +143 -0
  54. package/content/connections/amazonredshift.md +176 -0
  55. package/content/connections/anthropic.md +232 -0
  56. package/content/connections/awss3.md +313 -0
  57. package/content/connections/azureblobstorage.md +133 -0
  58. package/content/connections/elasticsearch.md +371 -0
  59. package/content/connections/google.md +228 -0
  60. package/content/connections/googlecloudstorage.md +139 -0
  61. package/content/connections/googlesheet.md +313 -0
  62. package/content/connections/knex.md +206 -0
  63. package/content/connections/mariadb.md +177 -0
  64. package/content/connections/mcp.md +134 -0
  65. package/content/connections/mongodb.md +739 -0
  66. package/content/connections/mssql.md +191 -0
  67. package/content/connections/mysql.md +192 -0
  68. package/content/connections/openai-connection.md +228 -0
  69. package/content/connections/postgresql.md +200 -0
  70. package/content/connections/redis.md +116 -0
  71. package/content/connections/sendgridmail.md +94 -0
  72. package/content/connections/smtp.md +149 -0
  73. package/content/connections/sqlite.md +187 -0
  74. package/content/connections/stripe.md +115 -0
  75. package/content/container-blocks/affix.md +413 -0
  76. package/content/container-blocks/alert.md +937 -0
  77. package/content/container-blocks/badge.md +1222 -0
  78. package/content/container-blocks/box.md +1453 -0
  79. package/content/container-blocks/card.md +1200 -0
  80. package/content/container-blocks/carousel.md +2247 -0
  81. package/content/container-blocks/collapse.md +1335 -0
  82. package/content/container-blocks/configprovider.md +1070 -0
  83. package/content/container-blocks/confirmmodal.md +1159 -0
  84. package/content/container-blocks/content.md +597 -0
  85. package/content/container-blocks/descriptions.md +870 -0
  86. package/content/container-blocks/drawer.md +1379 -0
  87. package/content/container-blocks/dropdownmenu.md +643 -0
  88. package/content/container-blocks/flex.md +1510 -0
  89. package/content/container-blocks/footer.md +380 -0
  90. package/content/container-blocks/googlemaps.md +546 -0
  91. package/content/container-blocks/googlemapsheatmap.md +607 -0
  92. package/content/container-blocks/googlemapsscript.md +61 -0
  93. package/content/container-blocks/header.md +302 -0
  94. package/content/container-blocks/label.md +739 -0
  95. package/content/container-blocks/layout.md +604 -0
  96. package/content/container-blocks/masonry.md +1090 -0
  97. package/content/container-blocks/mobilemenu.md +1244 -0
  98. package/content/container-blocks/modal.md +1180 -0
  99. package/content/container-blocks/pageheadermenu.md +614 -0
  100. package/content/container-blocks/pagesidebarlayout.md +438 -0
  101. package/content/container-blocks/pagesidermenu.md +651 -0
  102. package/content/container-blocks/popover.md +1421 -0
  103. package/content/container-blocks/result.md +972 -0
  104. package/content/container-blocks/sider.md +1064 -0
  105. package/content/container-blocks/span.md +747 -0
  106. package/content/container-blocks/spinner.md +267 -0
  107. package/content/container-blocks/splitter.md +1735 -0
  108. package/content/container-blocks/tabs.md +1718 -0
  109. package/content/container-blocks/tooltip.md +1274 -0
  110. package/content/container-blocks/watermark.md +515 -0
  111. package/content/controls/for.md +103 -0
  112. package/content/controls/if.md +107 -0
  113. package/content/controls/log.md +50 -0
  114. package/content/controls/parallel.md +71 -0
  115. package/content/controls/parallel_for.md +36 -0
  116. package/content/controls/reject.md +56 -0
  117. package/content/controls/return.md +66 -0
  118. package/content/controls/set_state.md +98 -0
  119. package/content/controls/switch.md +90 -0
  120. package/content/controls/throw.md +79 -0
  121. package/content/controls/try.md +88 -0
  122. package/content/deployment/docker.md +149 -0
  123. package/content/deployment/node-server.md +27 -0
  124. package/content/deployment/vercel.md +101 -0
  125. package/content/display-blocks/aggrid.md +1459 -0
  126. package/content/display-blocks/anchor.md +336 -0
  127. package/content/display-blocks/avatar.md +1154 -0
  128. package/content/display-blocks/breadcrumb.md +517 -0
  129. package/content/display-blocks/button.md +758 -0
  130. package/content/display-blocks/calendar.md +156 -0
  131. package/content/display-blocks/dangeroushtml.md +314 -0
  132. package/content/display-blocks/dangerousmarkdown.md +423 -0
  133. package/content/display-blocks/diffgit.md +44 -0
  134. package/content/display-blocks/difflist.md +225 -0
  135. package/content/display-blocks/diffsidebyside.md +56 -0
  136. package/content/display-blocks/difftimeline.md +94 -0
  137. package/content/display-blocks/divider.md +406 -0
  138. package/content/display-blocks/download.md +60 -0
  139. package/content/display-blocks/dropdownbutton.md +661 -0
  140. package/content/display-blocks/echart.md +418 -0
  141. package/content/display-blocks/floatbutton.md +545 -0
  142. package/content/display-blocks/html.md +311 -0
  143. package/content/display-blocks/icon.md +717 -0
  144. package/content/display-blocks/img.md +479 -0
  145. package/content/display-blocks/markdown.md +520 -0
  146. package/content/display-blocks/markdownwithcode.md +730 -0
  147. package/content/display-blocks/menu.md +1123 -0
  148. package/content/display-blocks/message.md +941 -0
  149. package/content/display-blocks/notification.md +1410 -0
  150. package/content/display-blocks/paragraph.md +643 -0
  151. package/content/display-blocks/progress.md +818 -0
  152. package/content/display-blocks/qrcode.md +519 -0
  153. package/content/display-blocks/s3download.md +240 -0
  154. package/content/display-blocks/search.md +360 -0
  155. package/content/display-blocks/skeleton.md +272 -0
  156. package/content/display-blocks/skeletonavatar.md +222 -0
  157. package/content/display-blocks/skeletonbutton.md +245 -0
  158. package/content/display-blocks/skeletoninput.md +223 -0
  159. package/content/display-blocks/skeletonparagraph.md +201 -0
  160. package/content/display-blocks/statistic.md +485 -0
  161. package/content/display-blocks/steps.md +585 -0
  162. package/content/display-blocks/tag.md +620 -0
  163. package/content/display-blocks/title.md +654 -0
  164. package/content/display-blocks/tour.md +2359 -0
  165. package/content/input-blocks/autocomplete.md +786 -0
  166. package/content/input-blocks/buttonselector.md +964 -0
  167. package/content/input-blocks/checkboxselector.md +765 -0
  168. package/content/input-blocks/checkboxswitch.md +497 -0
  169. package/content/input-blocks/colorselector.md +884 -0
  170. package/content/input-blocks/daterangeselector.md +777 -0
  171. package/content/input-blocks/dateselector.md +693 -0
  172. package/content/input-blocks/datetimeselector.md +791 -0
  173. package/content/input-blocks/inputgallery.md +528 -0
  174. package/content/input-blocks/monthselector.md +717 -0
  175. package/content/input-blocks/multipleselector.md +1337 -0
  176. package/content/input-blocks/numberinput.md +855 -0
  177. package/content/input-blocks/pagination.md +679 -0
  178. package/content/input-blocks/paragraphinput.md +1664 -0
  179. package/content/input-blocks/passwordinput.md +480 -0
  180. package/content/input-blocks/phonenumberinput.md +688 -0
  181. package/content/input-blocks/radioselector.md +820 -0
  182. package/content/input-blocks/ratingslider.md +684 -0
  183. package/content/input-blocks/s3uploadbutton.md +384 -0
  184. package/content/input-blocks/s3uploaddragger.md +305 -0
  185. package/content/input-blocks/s3uploadphoto.md +288 -0
  186. package/content/input-blocks/segmentedselector.md +782 -0
  187. package/content/input-blocks/selector.md +1162 -0
  188. package/content/input-blocks/switch.md +552 -0
  189. package/content/input-blocks/textarea.md +714 -0
  190. package/content/input-blocks/textinput.md +639 -0
  191. package/content/input-blocks/tiptapinput.md +226 -0
  192. package/content/input-blocks/tiptapmentioninput.md +471 -0
  193. package/content/input-blocks/titleinput.md +1094 -0
  194. package/content/input-blocks/treeinput.md +144 -0
  195. package/content/input-blocks/treemultipleselector.md +162 -0
  196. package/content/input-blocks/treeselector.md +185 -0
  197. package/content/input-blocks/upload.md +149 -0
  198. package/content/input-blocks/uploaddragger.md +85 -0
  199. package/content/input-blocks/uploadphoto.md +84 -0
  200. package/content/input-blocks/weekselector.md +630 -0
  201. package/content/list-blocks/controlledlist.md +498 -0
  202. package/content/list-blocks/list.md +529 -0
  203. package/content/list-blocks/listselector.md +301 -0
  204. package/content/list-blocks/masonrylist.md +230 -0
  205. package/content/migration/upgrade-guide.md +106 -0
  206. package/content/migration/v4-to-v5.md +589 -0
  207. package/content/migration/v5-to-v6.md +36 -0
  208. package/content/operators/_actions.md +58 -0
  209. package/content/operators/_and.md +76 -0
  210. package/content/operators/_api.md +254 -0
  211. package/content/operators/_app.md +114 -0
  212. package/content/operators/_args.md +65 -0
  213. package/content/operators/_array.md +331 -0
  214. package/content/operators/_base64.md +49 -0
  215. package/content/operators/_boolean.md +33 -0
  216. package/content/operators/_build.md +166 -0
  217. package/content/operators/_change_case.md +357 -0
  218. package/content/operators/_date.md +415 -0
  219. package/content/operators/_dayjs.md +128 -0
  220. package/content/operators/_diff.md +61 -0
  221. package/content/operators/_divide.md +32 -0
  222. package/content/operators/_eq.md +52 -0
  223. package/content/operators/_event.md +86 -0
  224. package/content/operators/_function.md +52 -0
  225. package/content/operators/_get.md +92 -0
  226. package/content/operators/_global.md +86 -0
  227. package/content/operators/_gt.md +50 -0
  228. package/content/operators/_gte.md +50 -0
  229. package/content/operators/_hash.md +112 -0
  230. package/content/operators/_if.md +30 -0
  231. package/content/operators/_if_none.md +47 -0
  232. package/content/operators/_index.md +48 -0
  233. package/content/operators/_input.md +86 -0
  234. package/content/operators/_intl.md +135 -0
  235. package/content/operators/_item.md +166 -0
  236. package/content/operators/_js.md +113 -0
  237. package/content/operators/_json.md +120 -0
  238. package/content/operators/_jsonata.md +152 -0
  239. package/content/operators/_locale.md +65 -0
  240. package/content/operators/_location.md +40 -0
  241. package/content/operators/_log.md +22 -0
  242. package/content/operators/_lt.md +50 -0
  243. package/content/operators/_lte.md +50 -0
  244. package/content/operators/_math.md +407 -0
  245. package/content/operators/_media.md +83 -0
  246. package/content/operators/_menu.md +62 -0
  247. package/content/operators/_module.md +340 -0
  248. package/content/operators/_mql.md +95 -0
  249. package/content/operators/_ne.md +62 -0
  250. package/content/operators/_not.md +33 -0
  251. package/content/operators/_number.md +159 -0
  252. package/content/operators/_nunjucks.md +117 -0
  253. package/content/operators/_object.md +253 -0
  254. package/content/operators/_operator.md +33 -0
  255. package/content/operators/_or.md +67 -0
  256. package/content/operators/_product.md +45 -0
  257. package/content/operators/_random.md +90 -0
  258. package/content/operators/_ref.md +80 -0
  259. package/content/operators/_regex.md +69 -0
  260. package/content/operators/_request.md +35 -0
  261. package/content/operators/_request_details.md +29 -0
  262. package/content/operators/_secret.md +56 -0
  263. package/content/operators/_state.md +134 -0
  264. package/content/operators/_step.md +98 -0
  265. package/content/operators/_string.md +327 -0
  266. package/content/operators/_subtract.md +24 -0
  267. package/content/operators/_sum.md +45 -0
  268. package/content/operators/_switch.md +37 -0
  269. package/content/operators/_t.md +96 -0
  270. package/content/operators/_theme.md +65 -0
  271. package/content/operators/_type.md +78 -0
  272. package/content/operators/_uri.md +49 -0
  273. package/content/operators/_url_query.md +92 -0
  274. package/content/operators/_user.md +154 -0
  275. package/content/operators/_uuid.md +118 -0
  276. package/content/operators/_var.md +88 -0
  277. package/content/operators/_yaml.md +96 -0
  278. package/content/other/introduction.md +39 -0
  279. package/content/other/next-auth-providers.md +3 -0
  280. package/content/other/updatesession.md +26 -0
  281. package/content/plugins/plugins-actions.md +80 -0
  282. package/content/plugins/plugins-connections.md +98 -0
  283. package/content/plugins/plugins-dev.md +138 -0
  284. package/content/plugins/plugins-introduction.md +29 -0
  285. package/content/plugins/plugins-operators.md +71 -0
  286. package/content/testing/e2e-auth.md +108 -0
  287. package/content/testing/e2e-configuration.md +107 -0
  288. package/content/testing/e2e-getting-started.md +96 -0
  289. package/content/testing/e2e-introduction.md +53 -0
  290. package/content/testing/e2e-mocking.md +155 -0
  291. package/content/testing/e2e-writing-tests.md +214 -0
  292. package/content/tutorial/tutorial-actions-operators.md +222 -0
  293. package/content/tutorial/tutorial-add-blocks.md +86 -0
  294. package/content/tutorial/tutorial-create-page.md +57 -0
  295. package/content/tutorial/tutorial-display-data-page.md +181 -0
  296. package/content/tutorial/tutorial-next-steps.md +53 -0
  297. package/content/tutorial/tutorial-requests-sql.md +22 -0
  298. package/content/tutorial/tutorial-start.md +71 -0
  299. package/content/user-authentication/auth-providers.md +15 -0
  300. package/content/user-authentication/login-and-logout.md +122 -0
  301. package/content/user-authentication/users-introduction.md +31 -0
  302. package/index.json +2281 -0
  303. package/package.json +34 -0
  304. package/scripts/extractAgentDocs.js +130 -0
@@ -0,0 +1,144 @@
1
+ # Menus
2
+
3
+ > The menu items are filtered according to pages the user is authorized to see. This can be controlled by making pages public or private, or by assigning roles to authenticated users. See <a href = "/protected-pages-apis">protected pages and APIs</a> and <a href = "/roles">roles</a> for more on how to set this up.
4
+
5
+ Menu objects describe links to pages, within the app or external. Menu lists are filtered to only show pages that the user is authorized to see as a result of public, private or role based access controlled (RBAC) configuration. Blocks such as [PageSiderMenu](/PageSiderMenu) render menu links. If no menu defined, a default menu is created, containing links to all pages defined in the app.
6
+
7
+ By default blocks using a menu, like `PageSiderMenu`, will use the menu with `id: default`. This means that if you create a menu object with `id: default`, this will be used unless another menu is configured and referenced under the block properties, allowing for page specific menu configurations.
8
+
9
+ More than one menu can be configured in an app. As an example, this can be used when two logically different sections in the app need different menus.
10
+
11
+ The menu objects can be referenced by using the [`_menu`](/_menu) operator.
12
+
13
+ The schema for a menu object is:
14
+
15
+ - `id: string`: __Required__ - A identifier for the menu. If it is `default`, it will be used as default by blocks, like [PageHeaderMenu](/PageHeaderMenu), which display a menu.
16
+ - `links: object[]`: An array of `MenuLink` or `MenuGroup` objects that form the links in the menu. `MenuGroups` can be two levels deep.
17
+
18
+ The schema for a `MenuLink` is:
19
+ - `id: string`: __Required__ - A identifier for the link unique to the menu.
20
+ - `type: string`: __Required__ - The type should be `MenuLink`.
21
+ - `pageId: string`: The id of the page to link to. Used as the menu item title if no title is provided.
22
+ - `url: string`: An external url to link to.
23
+ - `class: string | array | object`: CSS classes (including Tailwind utilities) for the item. Flat applies to the item wrapper; use an object with dot-prefixed slot keys (`.element`, `.icon`, `.label`) to target specific parts.
24
+ - `style: object`: CSS styles for the item. Flat applies to the wrapper; use dot-prefixed slot keys (`.element`, `.icon`, `.label`) to target specific parts.
25
+ - `properties: object`: The properties of the menu link. These are:
26
+ - `title: string`: The title to display for the link.
27
+ - `icon: string | object`: The name of a [React Icon](https://react-icons.github.io/react-icons/search) or properties of an Icon block to use as the icon for the link.
28
+ - `danger: boolean`: Apply danger style (switches the item onto antd's `dangerItem*` token set).
29
+ - `disabled: boolean`: Disable the item — blocks clicks and applies a greyed style.
30
+ - `tooltip: string`: Tooltip text shown when the menu is collapsed.
31
+ - `extra: string`: Free-form right-aligned label on the item (e.g. `beta`, `soon`). For real keyboard shortcuts use `shortcut`, which both renders a kbd badge AND wires the key handler. When both are set, `shortcut` sits to the far right of `extra`.
32
+ - `shortcut: string`: Keyboard shortcut for this menu item. Renders a kbd badge floated to the far right of the item AND wires the key handler (fires `onSelect` when pressed). Use `mod` for Cmd/Ctrl. Supports sequences like `g s` and multi-key combos like `mod+shift+q`.
33
+
34
+ The schema for a `MenuGroup` is:
35
+ - `id: string`: __Required__ - A identifier for the group unique to the menu.
36
+ - `type: string`: __Required__ - The type should be `MenuGroup`.
37
+ - `class: string | array | object`: CSS classes for the group's title row. Use the `.popup` slot key (object form) to style the floating SubMenu popup.
38
+ - `style: object`: CSS styles for the group's title row. Use dot-prefixed slot keys to target specific parts.
39
+ - `properties: object`: The properties of the menu group. These are:
40
+ - `title: string`: The title to display for the group.
41
+ - `icon: string | object`: The name of a [React Icon](https://react-icons.github.io/react-icons/search) or properties of an Icon block to use as the icon for the group.
42
+ - `disabled: boolean`: Disable the group — blocks expansion and applies a greyed style.
43
+ - `tooltip: string`: Tooltip text shown when the menu is collapsed.
44
+ - `links: object[]`: An array of `MenuLink` or `MenuGroup` objects that should be grouped together in the group.
45
+
46
+ The schema for a `MenuDivider` is:
47
+ - `id: string`: __Required__ - A identifier for the divider unique to the menu.
48
+ - `type: string`: __Required__ - The type should be `MenuDivider`.
49
+ - `class: string | array | object`: CSS classes for the divider line.
50
+ - `style: object`: CSS styles for the divider line.
51
+ - `properties: object`: The properties of the divider. These are:
52
+ - `dashed: boolean`: Render the divider as a dashed line.
53
+
54
+ ###### Menus example:
55
+ ```yaml
56
+ lowdefy: LOWDEFY_VERSION
57
+ menus:
58
+ - id: default
59
+ links:
60
+ - id: page1
61
+ type: MenuLink
62
+ pageId: page1
63
+ properties:
64
+ title: Page 1
65
+ icon: AiOutlineFile
66
+ - id: top-group
67
+ properties:
68
+ title: Group
69
+ icon: AiOutlineGroup
70
+ links:
71
+ - id: page2
72
+ type: MenuLink
73
+ pageId: page2 # pageId will be used as link title
74
+ - id: external
75
+ type: MenuLink
76
+ url: https://external.com
77
+ properties:
78
+ title: External site
79
+ - id: nested-group
80
+ type: MenuGroup
81
+ properties:
82
+ title: Nested Group
83
+ links:
84
+ - id: page3
85
+ type: MenuLink
86
+ pageId: page3
87
+ properties:
88
+ title: Page 3
89
+ - id: page-1-and-3
90
+ links:
91
+ - id: page1
92
+ type: MenuLink
93
+ pageId: page1
94
+ properties:
95
+ title: Page 1
96
+ icon: AiOutlineFile
97
+ - id: page3
98
+ type: MenuLink
99
+ pageId: page3
100
+ properties:
101
+ title: Page 3
102
+ icon: AiOutlineControl
103
+ ```
104
+
105
+ We can make use of [protected pages and APIs](/protected-pages-apis) and [roles](/roles) to filter the menu items according to the pages that the user has access to.
106
+
107
+ ###### lowdefy.yaml
108
+ ```yaml
109
+ lowdefy: LOWDEFY_VERSION
110
+
111
+ auth:
112
+ # ...
113
+ pages:
114
+ protected: true
115
+ public:
116
+ - login
117
+ - '404'
118
+ roles:
119
+ admin:
120
+ - page2
121
+ - page3
122
+ user:
123
+ - page1
124
+
125
+ menus:
126
+ - id: default
127
+ links:
128
+ - id: page1
129
+ type: MenuLink
130
+ pageId: page1
131
+ properties:
132
+ title: Page 1
133
+ icon: AiOutlineFile
134
+ - id: page2
135
+ type: MenuLink
136
+ pageId: page2 # pageId will be used as link title
137
+ - id: page3
138
+ type: MenuLink
139
+ pageId: page3
140
+ properties:
141
+ title: Page 3
142
+ ```
143
+
144
+ In the example above, public users can only view the `login` and `404` pages, all other pages require the user to be logged in. If the authorized user had been given only the `user` role, they will only be able to see `page1`. In order to see `page2` and `page3`, the authorized user will need to have the `admin` role assigned to them. The menu will be filtered accordingly and will only contain the menu items that the user has access to.
@@ -0,0 +1,564 @@
1
+ # Writing Modules
2
+
3
+ This guide covers how to create, test, and publish Lowdefy modules.
4
+
5
+ ## Module Project Structure
6
+
7
+ A module is a directory containing a `module.lowdefy.yaml` manifest and supporting config files:
8
+
9
+ ```
10
+ my-module/
11
+ ├── module.lowdefy.yaml # Module manifest (entry point)
12
+ ├── pages/
13
+ │ ├── users-list.yaml
14
+ │ ├── user-edit.yaml
15
+ │ └── user-invite.yaml
16
+ ├── connections/
17
+ │ └── users-db.yaml
18
+ ├── api/
19
+ │ └── invite-user.yaml
20
+ ├── components/
21
+ │ ├── user-avatar.yaml
22
+ │ └── user-search.yaml
23
+ └── menus.yaml
24
+ ```
25
+
26
+ For monorepos with multiple modules:
27
+
28
+ ```
29
+ my-modules/
30
+ ├── user-admin/
31
+ │ ├── module.lowdefy.yaml
32
+ │ ├── pages/
33
+ │ └── connections/
34
+ ├── notifications/
35
+ │ ├── module.lowdefy.yaml
36
+ │ ├── pages/
37
+ │ └── components/
38
+ └── shared/ # Shared files (not a module)
39
+ ├── page-layout.yaml
40
+ └── validate-email.yaml
41
+ ```
42
+
43
+ Modules in a monorepo can reference shared files outside their own directory with `../`. See [Ref Path Resolution](#ref-path-resolution).
44
+
45
+ ### Ref Path Resolution
46
+
47
+ `_ref` paths inside a module are relative to the module root (the directory containing `module.lowdefy.yaml`), not to the file doing the `_ref`. The same path works from any file in the module, regardless of nesting depth:
48
+
49
+ ```yaml
50
+ # module root: my-module/
51
+ # This ref works the same from:
52
+ # my-module/module.lowdefy.yaml
53
+ # my-module/pages/users-list.yaml
54
+ # my-module/pages/admin/users-edit.yaml
55
+ header:
56
+ _ref: components/page-header.yaml # always resolves to my-module/components/page-header.yaml
57
+ ```
58
+
59
+ Use `../` only to reference files outside the module — typically shared files in a monorepo:
60
+
61
+ ```yaml
62
+ # module root: my-modules/user-admin/
63
+ # _ref resolves to my-modules/shared/page-layout.yaml
64
+ layout:
65
+ _ref: ../shared/page-layout.yaml
66
+ ```
67
+
68
+ The build prevents `../` from escaping the package root — the git repo root for local (`file:`) modules (falling back to the module directory itself if not in a git repo), or the fetched repo root for `github:` modules. Breaking out throws a config error.
69
+
70
+ ## How Modules Stay Scoped and Composable
71
+
72
+ Three mechanisms keep modules working at scale. **Auto-scoping** prefixes every page, connection, API endpoint, notification, and menu item ID with the entry's `id`, so module authors write plain IDs and installing the same module twice under different entry IDs produces no collisions. **`_module.var`** lets any file in the module read the entry's vars at any depth, without the app threading values through nested `_ref`s. **Typed exports** declared in `exports:` mark which pages, components, endpoints, connections, and menus are public, so internal files stay internal and renaming one does not break consumers.
73
+
74
+ ## Writing module.lowdefy.yaml
75
+
76
+ The manifest declares the module's interface and contents:
77
+
78
+ ```yaml
79
+ name: User Admin
80
+ description: User administration — list, edit, invite users
81
+
82
+ vars:
83
+ roles:
84
+ type: array
85
+ required: true
86
+ description: List of available user roles
87
+ collection:
88
+ type: string
89
+ default: users
90
+ description: MongoDB collection name
91
+ components:
92
+ type: object
93
+ description: Component overrides (table_columns, form_fields)
94
+ request_stages:
95
+ type: object
96
+ description: Request pipeline stage overrides
97
+
98
+ connections:
99
+ - _ref: connections/users-db.yaml
100
+
101
+ pages:
102
+ - _ref: pages/users-list.yaml
103
+ - _ref: pages/user-edit.yaml
104
+ - _ref: pages/user-invite.yaml
105
+
106
+ api:
107
+ - _ref: api/invite-user.yaml
108
+
109
+ notifications:
110
+ - _ref: notifications/invite-user.yaml
111
+
112
+ components:
113
+ - id: user-avatar
114
+ component:
115
+ _ref: components/user-avatar.yaml
116
+ - id: user-search
117
+ component:
118
+ _ref: components/user-search.yaml
119
+
120
+ menus:
121
+ - _ref: menus.yaml
122
+
123
+ # Default Lowdefy plugins (blocks-antd, blocks-basic, etc.) do not
124
+ # need to be declared — only declare non-default plugins.
125
+ plugins:
126
+ - name: "@lowdefy/blocks-aggrid"
127
+ version: "^4"
128
+
129
+ secrets:
130
+ - name: MONGODB
131
+ description: MongoDB connection URI
132
+ - name: SENDGRID_API_KEY
133
+ description: SendGrid API key for sending invitations
134
+ required: false
135
+ ```
136
+
137
+ ## Declaring Vars
138
+
139
+ The `vars` section declares what configuration the module accepts. Each variable can specify:
140
+ - `type` — Expected type (`string`, `number`, `boolean`, `array`, `object`).
141
+ - `required` — Whether the consuming app must provide this variable. Defaults to `false`.
142
+ - `default` — Default value used when the app does not provide one.
143
+ - `description` — Documentation for the variable's purpose.
144
+ - `properties` — For `object` vars, declares nested keys with their own type, default, required, and description. Consumer values are merged at the property level — only overridden keys change.
145
+
146
+ Defaults are expressions. They can be literals, or operator trees — `_ref`, `_module.var`, `_build.*`, or any combination. The build resolves a default lazily, only when `_module.var` reads the var and the consumer did not provide a value. Vars with `properties` merge at the property level: the consumer overrides only the keys they provide, and the rest resolve their own defaults independently.
147
+
148
+ Unused vars are never read, so their defaults never resolve and their declared `type` is never checked.
149
+
150
+ ```yaml
151
+ vars:
152
+ collection:
153
+ type: string
154
+ default: contacts
155
+ components:
156
+ type: object
157
+ properties:
158
+ table:
159
+ type: boolean
160
+ default: true
161
+ filter:
162
+ type: boolean
163
+ default: true
164
+ ```
165
+
166
+ If the consumer provides `components: { filter: false }`, the effective value is `{ table: true, filter: false }`.
167
+
168
+ ### Dynamic Defaults
169
+
170
+ Defaults are expressions. A `default:` value can be a literal, a `_ref` to a file, a `_module.var` reference to another var, or any `_build.*` tree. The build resolves a default on read — only when `_module.var` accesses the var and the consumer did not provide a value.
171
+
172
+ The two most common patterns are loading a default from a file and deriving one var from another:
173
+
174
+ ```yaml
175
+ vars:
176
+ table_columns:
177
+ default:
178
+ _ref: defaults/table-columns.yaml
179
+ page_title:
180
+ default:
181
+ _module.var: label_plural
182
+ ```
183
+
184
+ `_ref` lets the module ship a structured default (a block tree, a column schema, a default request body) as its own file. `_module.var` inside a default turns one consumer-provided value into several — set `label_plural: "Users"` and `page_title` derives from it unless the consumer overrides it directly.
185
+
186
+ Defaults scale up to full operator trees and namespace-level merging:
187
+
188
+ ```yaml
189
+ vars:
190
+ show_title:
191
+ type: boolean
192
+ default: true
193
+
194
+ # Default built from another var
195
+ page_title:
196
+ type: string
197
+ default:
198
+ _build.if:
199
+ - _module.var: show_title
200
+ - 'Contact Form'
201
+ - null
202
+
203
+ # Namespace var — each property's default resolves on its own
204
+ components:
205
+ type: object
206
+ properties:
207
+ form_fields:
208
+ type: array
209
+ default:
210
+ _ref: components/form_contact.yaml
211
+ show_header:
212
+ type: boolean
213
+ default:
214
+ _module.var: show_title
215
+ ```
216
+
217
+ When a consumer passes `components: { show_header: false }`, `show_header` keeps the consumer value and the build still resolves the `form_fields` default — property-level merging plus lazy resolution combine without any extra work from the author.
218
+
219
+ A default that reads another var via `_module.var` triggers that var's resolution first. The build walks the dependency graph on demand, caching results per module entry. Circular `_module.var` graphs between defaults have no natural terminator — the build stack-overflows. Keep default graphs acyclic.
220
+
221
+ ## Using _module.var
222
+
223
+ The [`_module.var`](/_module) operator reads module entry variables — it returns the consumer value when provided, or resolves the manifest default on read. It takes a string argument and works at any depth within module files, without threading vars through nested `_ref`s:
224
+
225
+ ```yaml
226
+ # Any file in the module, at any nesting depth
227
+ collection:
228
+ _module.var: collection
229
+
230
+ # Nested path access (dot notation)
231
+ show_filter:
232
+ _module.var: components.filter
233
+ ```
234
+
235
+ `_var` continues to work for normal `_ref`-level vars (local composition between files within the module). `_module.var` is specifically for the vars passed by the app developer.
236
+
237
+ ## Using ID Operators
238
+
239
+ Module authors use [`_module.pageId`](/_module), [`_module.connectionId`](/_module), [`_module.endpointId`](/_module), and [`_module.notificationId`](/_module) to reference sibling resources. These resolve to the fully scoped ID at build time:
240
+
241
+ ```yaml
242
+ # Link to a sibling page
243
+ - id: link_to_edit
244
+ type: Link
245
+ params:
246
+ pageId:
247
+ _module.pageId: user-edit
248
+
249
+ # Reference a module connection
250
+ connectionId:
251
+ _module.connectionId: users-db
252
+
253
+ # Call a module API endpoint
254
+ - id: call_invite
255
+ type: CallAPI
256
+ params:
257
+ endpointId:
258
+ _module.endpointId: invite-user
259
+ ```
260
+
261
+ Use [`_module.id`](/_module) for the raw module entry ID:
262
+
263
+ ```yaml
264
+ label:
265
+ _string.concat:
266
+ - _module.id: true
267
+ - " - User Admin"
268
+ ```
269
+
270
+ ## Exposing Components
271
+
272
+ Components are reusable config fragments that consuming apps can `_ref` into any page. Declare them in the manifest:
273
+
274
+ ```yaml
275
+ components:
276
+ - id: user-avatar
277
+ component:
278
+ _ref: components/user-avatar.yaml
279
+ ```
280
+
281
+ Components can use both `_module.var` (module-level config) and `_var` (per-ref vars passed at the point of inclusion):
282
+
283
+ ```yaml
284
+ # components/contact-selector.yaml
285
+ id:
286
+ _var:
287
+ key: field_id
288
+ default: contact
289
+ type: Selector
290
+ properties:
291
+ label:
292
+ _module.var: contact_label
293
+ options:
294
+ _request: get-contacts.contacts
295
+ ```
296
+
297
+ The consuming app includes it:
298
+
299
+ ```yaml
300
+ blocks:
301
+ - _ref:
302
+ module: crm
303
+ component: contact-selector
304
+ vars:
305
+ field_id: primary_contact
306
+ ```
307
+
308
+ ## Declaring Dependencies
309
+
310
+ When your module needs resources from another module — pages to link to, components to embed, APIs to call — declare those dependencies in `module.lowdefy.yaml`:
311
+
312
+ ```yaml
313
+ dependencies:
314
+ - id: contacts
315
+ description: Contact management (selector component, detail pages)
316
+ - id: layout
317
+ description: Page layout components
318
+ - id: events
319
+ description: Audit event logging
320
+ ```
321
+
322
+ Each dependency has an `id` (the abstract name you use in your module content) and a `description` (documents what kind of module is expected). The `id` is your choice — it's not tied to any specific entry name.
323
+
324
+ ## Declaring Exports
325
+
326
+ Declare what your module exposes so the build can validate cross-module references:
327
+
328
+ ```yaml
329
+ exports:
330
+ pages:
331
+ - id: company-list
332
+ - id: company-detail
333
+ components:
334
+ - id: company-selector
335
+ description: Dropdown selector for picking companies
336
+ menus:
337
+ - id: default
338
+ api:
339
+ - id: save-company
340
+ ```
341
+
342
+ Omit empty sections. The build validates that cross-module references target declared exports.
343
+
344
+ ## Using Cross-Module References
345
+
346
+ ### Linking to Pages in Another Module
347
+
348
+ Use the object form of `_module.pageId` with a `module` field:
349
+
350
+ ```yaml
351
+ # Link to a page in the contacts dependency
352
+ - id: view-contact
353
+ type: Link
354
+ params:
355
+ pageId:
356
+ _module.pageId:
357
+ id: contact-detail
358
+ module: contacts
359
+ ```
360
+
361
+ The string form (`_module.pageId: company-detail`) continues to reference the current module's own pages.
362
+
363
+ ### Calling APIs in Another Module
364
+
365
+ ```yaml
366
+ - id: log-event
367
+ type: CallAPI
368
+ params:
369
+ endpointId:
370
+ _module.endpointId:
371
+ id: log-event
372
+ module: events
373
+ ```
374
+
375
+ ### Embedding Components from Another Module
376
+
377
+ Use `_ref: { module, component }` where `module` is the abstract dependency name:
378
+
379
+ ```yaml
380
+ blocks:
381
+ - _ref:
382
+ module: contacts
383
+ component: contact-selector
384
+ vars:
385
+ field_id: primary_contact
386
+ ```
387
+
388
+ The embedded component resolves in its source module's context. Its `_module.var` and `_module.*Id` operators read from the source module's entry — not yours. Per-ref `_var` works for customizing the component at the point of inclusion.
389
+
390
+ ### Including Menus from Another Module
391
+
392
+ Use `_build.array.concat` to combine module and local links:
393
+
394
+ ```yaml
395
+ links:
396
+ _build.array.concat:
397
+ - _ref:
398
+ module: contacts
399
+ menu: default
400
+ - _ref: menus/company-links.yaml
401
+ ```
402
+
403
+ Cross-module `_ref` supports `component` and `menu` types only. Pages, connections, and APIs have dedicated ID operators.
404
+
405
+ ## Menu Export Convention
406
+
407
+ Module menus should export only one level of links — flat MenuLink items, no nested MenuGroups. The app controls grouping. This keeps module menus composable: the app can wrap the links in any MenuGroup structure it needs.
408
+
409
+ ```yaml
410
+ # menus.yaml — good: flat links only
411
+ id: default
412
+ links:
413
+ - id: users
414
+ type: MenuLink
415
+ pageId:
416
+ _module.pageId: users-list
417
+ properties:
418
+ title: Users
419
+ icon: AiOutlineUser
420
+ - id: invite
421
+ type: MenuLink
422
+ pageId:
423
+ _module.pageId: user-invite
424
+ properties:
425
+ title: Invite User
426
+ icon: AiOutlineUserAdd
427
+ ```
428
+
429
+ The app wraps these in a MenuGroup:
430
+
431
+ ```yaml
432
+ # lowdefy.yaml — app controls grouping
433
+ menus:
434
+ - id: default
435
+ links:
436
+ - id: user-admin
437
+ type: MenuGroup
438
+ properties:
439
+ title: User Admin
440
+ links:
441
+ _ref:
442
+ module: user-admin
443
+ menu: default
444
+ ```
445
+
446
+ ## Data-Like Exports
447
+
448
+ Components can export any config — not just UI blocks. Enum maps, config templates, and schema fragments all work through the same `components` mechanism:
449
+
450
+ ```yaml
451
+ # In module.lowdefy.yaml
452
+ vars:
453
+ event_types:
454
+ type: object
455
+ default: {}
456
+ change_stamp:
457
+ type: object
458
+ default:
459
+ timestamp:
460
+ _user: updated_at
461
+
462
+ components:
463
+ # Enum map merged with app additions
464
+ - id: event_types
465
+ component:
466
+ _build.object.assign:
467
+ - _ref: ../shared/enums/event_types.yaml
468
+ - _module.var: event_types
469
+
470
+ # Config template with var override
471
+ - id: change_stamp
472
+ component:
473
+ _module.var: change_stamp
474
+ ```
475
+
476
+ Consumers use the same `_ref: { module, component }` syntax. The `key` argument lets consumers extract nested values from the resolved content:
477
+
478
+ ```yaml
479
+ # Get the full enum map
480
+ event_types:
481
+ _ref:
482
+ module: events
483
+ component: event_types
484
+
485
+ # Get a single event type's icon
486
+ icon:
487
+ _ref:
488
+ module: events
489
+ component: event_types
490
+ key: login.icon
491
+ ```
492
+
493
+ ## Declaring Plugin Dependencies
494
+
495
+ Modules declare required plugins with semver version constraints. The build validates these against the app's `plugins` array. Default Lowdefy plugins do not need to be declared — only declare non-default plugins your module requires:
496
+
497
+ ```yaml
498
+ plugins:
499
+ - name: "@lowdefy/blocks-aggrid"
500
+ version: "^4"
501
+ ```
502
+
503
+ Use `^major` for major version pinning — the standard convention.
504
+
505
+ ## Declaring Secrets
506
+
507
+ The `secrets` array is an allowlist. The module can only access secrets it explicitly declares:
508
+
509
+ ```yaml
510
+ secrets:
511
+ - name: MONGODB
512
+ description: MongoDB connection URI
513
+ - name: SENDGRID_API_KEY
514
+ description: SendGrid API key
515
+ required: false
516
+ ```
517
+
518
+ ## Testing Locally
519
+
520
+ Create a test app that imports the module via local path:
521
+
522
+ ```yaml
523
+ # test-app/lowdefy.yaml
524
+ lowdefy: LOWDEFY_VERSION
525
+
526
+ modules:
527
+ - id: user-admin
528
+ source: "file:.."
529
+ vars:
530
+ roles:
531
+ _ref: config/test-roles.yaml
532
+ collection: test_users
533
+ ```
534
+
535
+ Run `lowdefy dev` in the test-app directory. The `file:..` path reads from disk and picks up module changes on rebuild. Include the module multiple times with different vars to test multi-instance behaviour.
536
+
537
+ ## Publishing to GitHub
538
+
539
+ Publish your module by pushing to a GitHub repository and creating tagged releases:
540
+
541
+ ```bash
542
+ git tag v1.0.0
543
+ git push origin v1.0.0
544
+ ```
545
+
546
+ Consumers reference the tag:
547
+
548
+ ```yaml
549
+ source: "github:my-org/user-admin@v1.0.0"
550
+ ```
551
+
552
+ For monorepos, the path specifies the subdirectory:
553
+
554
+ ```yaml
555
+ source: "github:my-org/modules/user-admin@v1.0.0"
556
+ ```
557
+
558
+ ## Versioning Strategy
559
+
560
+ - **Tags** (e.g., `v1.0.0`) — Recommended for production. Immutable and cacheable.
561
+ - **Branches** (e.g., `main`) — Useful during development. Re-fetched on each build.
562
+ - **Commit SHAs** — Most precise. Immutable.
563
+
564
+ Follow [semantic versioning](https://semver.org/): increment the major version for breaking changes to the vars interface, minor for new features, patch for fixes.