@lowdefy/blocks-markdown 5.5.1 → 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.
@@ -0,0 +1,407 @@
1
+ # Copyright 2020-2026 Lowdefy, Inc
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ - title: Inline Code
16
+ fullWidth: true
17
+ blocks:
18
+ - id: inline_basic
19
+ type: MarkdownWithCode
20
+ properties:
21
+ content: |
22
+ Use the `MarkdownWithCode` block to render code with syntax highlighting.
23
+
24
+ Set properties with `properties.content` and use backticks for `inline code`.
25
+ - id: inline_in_context
26
+ type: MarkdownWithCode
27
+ properties:
28
+ content: |
29
+ Install with `npm install lowdefy` or `pnpm add lowdefy`.
30
+
31
+ The config file is called `lowdefy.yaml` and lives in your project root.
32
+
33
+ Run `npx lowdefy@4 dev` to start the dev server on `http://localhost:3000`.
34
+
35
+ - title: YAML
36
+ fullWidth: true
37
+ blocks:
38
+ - id: code_yaml
39
+ type: MarkdownWithCode
40
+ properties:
41
+ content: |
42
+ ```yaml
43
+ lowdefy: 4.0.0
44
+
45
+ pages:
46
+ - id: home
47
+ type: PageHeaderMenu
48
+ properties:
49
+ title: My Application
50
+ blocks:
51
+ - id: welcome_title
52
+ type: Title
53
+ properties:
54
+ content: Welcome
55
+ level: 2
56
+ - id: user_form
57
+ type: Card
58
+ properties:
59
+ title: User Information
60
+ blocks:
61
+ - id: name_input
62
+ type: TextInput
63
+ properties:
64
+ label:
65
+ title: Full Name
66
+ - id: email_input
67
+ type: TextInput
68
+ properties:
69
+ label:
70
+ title: Email Address
71
+ - id: submit_button
72
+ type: Button
73
+ properties:
74
+ title: Submit
75
+ type: primary
76
+ events:
77
+ onClick:
78
+ - id: validate
79
+ type: Validate
80
+ ```
81
+
82
+ - title: JavaScript
83
+ fullWidth: true
84
+ blocks:
85
+ - id: code_javascript
86
+ type: MarkdownWithCode
87
+ properties:
88
+ content: |
89
+ ```javascript
90
+ function buildConnections({ components, context }) {
91
+ const connections = components.connections ?? [];
92
+
93
+ connections.forEach((connection) => {
94
+ if (!connection.id) {
95
+ throw new Error('Connection id is missing.');
96
+ }
97
+ if (!connection.type) {
98
+ throw new Error(`Connection "${connection.id}" does not have a type.`);
99
+ }
100
+ });
101
+
102
+ return components;
103
+ }
104
+
105
+ export default buildConnections;
106
+ ```
107
+
108
+ - title: JSON
109
+ fullWidth: true
110
+ blocks:
111
+ - id: code_json
112
+ type: MarkdownWithCode
113
+ properties:
114
+ content: |
115
+ ```json
116
+ {
117
+ "name": "@lowdefy/blocks-antd",
118
+ "version": "4.0.0",
119
+ "description": "Ant Design blocks for Lowdefy",
120
+ "main": "dist/index.js",
121
+ "types": [
122
+ {
123
+ "typeName": "Button",
124
+ "category": "display"
125
+ },
126
+ {
127
+ "typeName": "TextInput",
128
+ "category": "input"
129
+ },
130
+ {
131
+ "typeName": "Card",
132
+ "category": "container"
133
+ }
134
+ ],
135
+ "dependencies": {
136
+ "antd": "^6.0.0",
137
+ "react": "^19.0.0"
138
+ }
139
+ }
140
+ ```
141
+
142
+ - title: Python
143
+ fullWidth: true
144
+ blocks:
145
+ - id: code_python
146
+ type: MarkdownWithCode
147
+ properties:
148
+ content: |
149
+ ```python
150
+ import json
151
+ from pathlib import Path
152
+
153
+
154
+ class ConfigLoader:
155
+ """Load and validate Lowdefy configuration files."""
156
+
157
+ def __init__(self, config_path: str):
158
+ self.config_path = Path(config_path)
159
+ self.config = {}
160
+
161
+ def load(self) -> dict:
162
+ with open(self.config_path) as f:
163
+ self.config = json.load(f)
164
+ return self.config
165
+
166
+ def validate(self) -> bool:
167
+ required_keys = ["lowdefy", "pages"]
168
+ return all(key in self.config for key in required_keys)
169
+
170
+
171
+ if __name__ == "__main__":
172
+ loader = ConfigLoader("lowdefy.json")
173
+ config = loader.load()
174
+ print(f"Valid: {loader.validate()}")
175
+ ```
176
+
177
+ - title: TypeScript
178
+ fullWidth: true
179
+ blocks:
180
+ - id: code_typescript
181
+ type: MarkdownWithCode
182
+ properties:
183
+ content: |
184
+ ```typescript
185
+ interface BlockMeta {
186
+ category: 'display' | 'input' | 'container' | 'list';
187
+ icons: string[];
188
+ styles: string[];
189
+ }
190
+
191
+ interface BlockComponent<P = Record<string, unknown>> {
192
+ blockId: string;
193
+ properties: P;
194
+ methods: {
195
+ setState: (state: Record<string, unknown>) => void;
196
+ triggerEvent: (event: { name: string }) => Promise<void>;
197
+ };
198
+ }
199
+
200
+ function createBlock<P>(
201
+ render: (props: BlockComponent<P>) => JSX.Element,
202
+ meta: BlockMeta
203
+ ): typeof render & { meta: BlockMeta } {
204
+ const block = render as typeof render & { meta: BlockMeta };
205
+ block.meta = meta;
206
+ return block;
207
+ }
208
+ ```
209
+
210
+ - title: HTML and XML
211
+ fullWidth: true
212
+ blocks:
213
+ - id: code_html
214
+ type: MarkdownWithCode
215
+ properties:
216
+ content: |
217
+ ```html
218
+ <!DOCTYPE html>
219
+ <html lang="en">
220
+ <head>
221
+ <meta charset="UTF-8" />
222
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
223
+ <title>Lowdefy App</title>
224
+ <link rel="stylesheet" href="/styles.css" />
225
+ </head>
226
+ <body>
227
+ <div id="root">
228
+ <header class="app-header">
229
+ <h1>My Application</h1>
230
+ <nav>
231
+ <a href="/dashboard">Dashboard</a>
232
+ <a href="/settings">Settings</a>
233
+ </nav>
234
+ </header>
235
+ <main class="content"></main>
236
+ </div>
237
+ <script src="/bundle.js"></script>
238
+ </body>
239
+ </html>
240
+ ```
241
+
242
+ - title: Nunjucks Templates
243
+ fullWidth: true
244
+ blocks:
245
+ - id: code_nunjucks
246
+ type: MarkdownWithCode
247
+ properties:
248
+ content: |
249
+ ```nunjucks
250
+ {# Lowdefy docs page template #}
251
+ _ref:
252
+ path: templates/general.yaml.njk
253
+ vars:
254
+ pageId: {{ pageId }}
255
+ pageTitle: {{ pageTitle }}
256
+ section: {{ section }}
257
+ fullWidth: true
258
+ content:
259
+ - id: intro
260
+ type: Markdown
261
+ properties:
262
+ content: |
263
+ {{ description }}
264
+ {% for example in examples %}
265
+ - id: example_{{ loop.index }}
266
+ type: {{ example.block_type }}
267
+ properties:
268
+ {{ example.properties | dump | safe }}
269
+ {% endfor %}
270
+ ```
271
+
272
+ - title: Mixed Content with Code
273
+ fullWidth: true
274
+ blocks:
275
+ - id: mixed_tutorial
276
+ type: MarkdownWithCode
277
+ properties:
278
+ content: |
279
+ ## Building a Form with Lowdefy
280
+
281
+ Follow these steps to create a simple contact form.
282
+
283
+ ### 1. Create the Page
284
+
285
+ Start by defining a page in your `lowdefy.yaml`:
286
+
287
+ ```yaml
288
+ pages:
289
+ - id: contact
290
+ type: PageHeaderMenu
291
+ properties:
292
+ title: Contact Us
293
+ ```
294
+
295
+ ### 2. Add Form Fields
296
+
297
+ Add input blocks inside the page:
298
+
299
+ ```yaml
300
+ blocks:
301
+ - id: name
302
+ type: TextInput
303
+ properties:
304
+ label:
305
+ title: Your Name
306
+ - id: message
307
+ type: TextArea
308
+ properties:
309
+ label:
310
+ title: Message
311
+ ```
312
+
313
+ ### 3. Handle Submission
314
+
315
+ Use an action to process the form data:
316
+
317
+ ```javascript
318
+ // The _state operator accesses form values
319
+ // _state.name returns the value of the "name" TextInput
320
+ // _state.message returns the value of the "message" TextArea
321
+ ```
322
+
323
+ > **Tip:** Use the `Validate` action before `Request` to ensure all required fields are filled in.
324
+
325
+ For the complete guide, see the [Lowdefy documentation](https://docs.lowdefy.com).
326
+
327
+ - title: Multiple Languages Side by Side
328
+ fullWidth: true
329
+ blocks:
330
+ - id: multi_language
331
+ type: MarkdownWithCode
332
+ properties:
333
+ content: |
334
+ ### Configuration vs Code
335
+
336
+ The same logic expressed in YAML config and JavaScript:
337
+
338
+ **Lowdefy YAML:**
339
+ ```yaml
340
+ - id: greeting
341
+ type: Paragraph
342
+ properties:
343
+ content:
344
+ _if:
345
+ test:
346
+ _state: logged_in
347
+ then:
348
+ _string.concat:
349
+ - 'Welcome back, '
350
+ - _state: username
351
+ - '!'
352
+ else: Please log in to continue.
353
+ ```
354
+
355
+ **Equivalent JavaScript:**
356
+ ```javascript
357
+ function getGreeting(state) {
358
+ if (state.logged_in) {
359
+ return `Welcome back, ${state.username}!`;
360
+ }
361
+ return 'Please log in to continue.';
362
+ }
363
+ ```
364
+
365
+ - title: skipHtml Property
366
+ fullWidth: true
367
+ blocks:
368
+ - id: skip_html_false
369
+ type: MarkdownWithCode
370
+ properties:
371
+ content: |
372
+ **skipHtml: false (default)**
373
+
374
+ HTML tags like `<b>` are escaped and shown as text in the output. This is the default, safe behavior.
375
+ - id: skip_html_true
376
+ type: MarkdownWithCode
377
+ properties:
378
+ skipHtml: true
379
+ content: |
380
+ **skipHtml: true**
381
+
382
+ With skipHtml enabled, any HTML tags like <b>bold</b> are completely removed from the output rather than shown as escaped text.
383
+
384
+ - title: Style Override
385
+ fullWidth: true
386
+ blocks:
387
+ - id: style_element
388
+ type: MarkdownWithCode
389
+ style:
390
+ .element:
391
+ backgroundColor: var(--ant-color-success-bg)
392
+ padding: 16
393
+ borderRadius: 8
394
+ border: 1px solid var(--ant-color-success-border)
395
+ properties:
396
+ content: |
397
+ ### Styled Code Container
398
+
399
+ The `element` style key wraps the entire block:
400
+
401
+ ```yaml
402
+ style:
403
+ .element:
404
+ backgroundColor: var(--ant-color-success-bg)
405
+ padding: 16
406
+ borderRadius: 8
407
+ ```
@@ -0,0 +1,35 @@
1
+ # Copyright 2020-2026 Lowdefy, Inc
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ id: markdownwithcode
16
+ type: Box
17
+
18
+ blocks:
19
+ - id: markdownwithcode_basic
20
+ type: MarkdownWithCode
21
+ properties:
22
+ content: Hello **world**
23
+
24
+ - id: markdownwithcode_codeblock
25
+ type: MarkdownWithCode
26
+ properties:
27
+ content: |
28
+ ```javascript
29
+ const x = 1;
30
+ ```
31
+
32
+ - id: markdownwithcode_inline
33
+ type: MarkdownWithCode
34
+ properties:
35
+ content: 'Use `console.log` for debugging'
@@ -0,0 +1,139 @@
1
+ # Copyright 2020-2026 Lowdefy, Inc
2
+
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ - id: default
16
+ type: MarkdownWithCode
17
+ - id: properties.content
18
+ type: Box
19
+ properties:
20
+ content: |
21
+ # One Heading
22
+
23
+ A paragraph with *emphasis* and **strong importance**.
24
+
25
+ > A block quote with ~strikethrough~ and a URL: https://reactjs.org.
26
+
27
+ #### Link
28
+
29
+ [A link to Lowdefy](https://lowdefy.com)
30
+
31
+ # Two
32
+
33
+ * Lists
34
+ * [ ] todo
35
+ * [x] done
36
+
37
+ A table:
38
+
39
+ | a | b |
40
+ | - | - |
41
+
42
+ - id: properties.style and headings
43
+ type: MarkdownWithCode
44
+ properties:
45
+ content: |
46
+ # One
47
+ ## Two
48
+ ### Three
49
+ #### Four
50
+ ##### Five
51
+ ###### Six
52
+ style:
53
+ border: '1px solid blue'
54
+ - id: properties.content = table
55
+ type: MarkdownWithCode
56
+ properties:
57
+ content: |
58
+ ### Table
59
+
60
+ | A | B | C |
61
+ |---:|:---:|:---|
62
+ | one | two | three |
63
+ | 123 | 345 | 34892 |
64
+ | true | false | none |
65
+
66
+ - id: properties.content DangerousHtml
67
+ type: MarkdownWithCode
68
+ properties:
69
+ content: |
70
+ ### Link
71
+
72
+ <a href="https://lowdefy.com">Lowdefy link</a>
73
+
74
+ <font size="+10">Description</font>
75
+
76
+ ### Bad HTML
77
+
78
+ <div onmouseover="alert('alpha')">
79
+ <a href="javascript:alert('bravo')">delta</a>
80
+ <img src="x" onerror="alert('charlie')">
81
+ <iframe src="javascript:alert('delta')"></iframe>
82
+ <math>
83
+ <mi xlink:href="data:x,<script>alert('echo')</script>"></mi>
84
+ </math>
85
+ </div>
86
+ <script>
87
+ alert('script tag');
88
+ </script>
89
+
90
+ - id: properties.content with code
91
+ type: MarkdownWithCode
92
+ properties:
93
+ content: |
94
+ ### Code
95
+
96
+ Default
97
+ ```
98
+ <a href="">Default style</a>
99
+ ```
100
+
101
+ Html
102
+ ```html
103
+ <a href="">html style</a>
104
+ <script>
105
+ alert('script tag');
106
+ </script>
107
+ ```
108
+
109
+ Json
110
+ ```json
111
+ {
112
+ "obj": "one",
113
+ "list": [
114
+ 1,
115
+ 2
116
+ ]
117
+ }
118
+ ```
119
+
120
+ Bad HTML
121
+ ```html
122
+ <div onmouseover="alert('alpha')">
123
+ <a href="javascript:alert('bravo')">delta</a>
124
+ <img src="x" onerror="alert('charlie')">
125
+ <iframe src="javascript:alert('delta')"></iframe>
126
+ <math>
127
+ <mi xlink:href="data:x,<script>alert('echo')</script>"></mi>
128
+ </math>
129
+ </div>
130
+ <script>
131
+ alert('script tag');
132
+ </script>
133
+ ```
134
+ - id: properties.html-iframe
135
+ type: MarkdownWithCode
136
+ properties:
137
+ content: |
138
+ No additional ADD_TAGS:
139
+ <iframe width="560" height="315" src="https://www.youtube.com/embed/7N7GWdlQJlU" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
@@ -116,6 +116,18 @@
116
116
  padding-left: 0;
117
117
  }
118
118
 
119
+ /* Restore top-level markers. The Tailwind preflight (base layer) resets
120
+ `list-style: none`; without an explicit type here the markers vanish, since
121
+ only the nested rules below set a type. (Indentation comes from the
122
+ `padding-left` rule lower in this file.) */
123
+ .markdown-body ol {
124
+ list-style-type: decimal;
125
+ }
126
+
127
+ .markdown-body ul {
128
+ list-style-type: disc;
129
+ }
130
+
119
131
  .markdown-body ol ol,
120
132
  .markdown-body ul ol {
121
133
  list-style-type: lower-roman;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/blocks-markdown",
3
- "version": "5.5.1",
3
+ "version": "6.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy markdown blocks.",
6
6
  "homepage": "https://lowdefy.com",
@@ -40,8 +40,8 @@
40
40
  "dist/*"
41
41
  ],
42
42
  "dependencies": {
43
- "@lowdefy/block-utils": "5.5.1",
44
- "dompurify": "3.2.4",
43
+ "@lowdefy/block-utils": "6.0.0",
44
+ "dompurify": "3.4.2",
45
45
  "react-markdown": "8.0.7",
46
46
  "react-syntax-highlighter": "15.5.0",
47
47
  "rehype-raw": "6.1.1",
@@ -53,11 +53,11 @@
53
53
  "react-dom": ">=18"
54
54
  },
55
55
  "devDependencies": {
56
- "@lowdefy/block-dev-e2e": "5.5.1",
57
- "@lowdefy/e2e-utils": "5.5.1",
58
- "@playwright/test": "1.50.1",
59
- "@swc/cli": "0.8.0",
60
- "@swc/core": "1.15.18",
56
+ "@lowdefy/block-dev-e2e": "6.0.0",
57
+ "@lowdefy/e2e-utils": "6.0.0",
58
+ "@playwright/test": "1.59.1",
59
+ "@swc/cli": "0.8.1",
60
+ "@swc/core": "1.15.32",
61
61
  "copyfiles": "2.4.1"
62
62
  },
63
63
  "publishConfig": {
@@ -66,7 +66,7 @@
66
66
  "scripts": {
67
67
  "build": "swc src --out-dir dist --config-file ../../../../.swcrc --cli-config-file ../../../../.swc-cli.json && pnpm copyfiles",
68
68
  "clean": "rm -rf dist",
69
- "copyfiles": "copyfiles -u 1 \"./src/**/*\" dist -e \"./src/**/*.js\" -e \"./src/**/*.yaml\" -e \"./src/**/*.snap\"",
69
+ "copyfiles": "copyfiles -u 1 \"./src/**/*\" dist -e \"./src/**/*.js\" -e \"./src/**/*.snap\"",
70
70
  "e2e": "playwright test --config e2e/playwright.config.js",
71
71
  "e2e:ui": "playwright test --config e2e/playwright.config.js --ui"
72
72
  }