reflex-mermaidjs 0.1.0__tar.gz
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.
- reflex_mermaidjs-0.1.0/LICENSE +21 -0
- reflex_mermaidjs-0.1.0/PKG-INFO +261 -0
- reflex_mermaidjs-0.1.0/README.md +233 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/__init__.py +95 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/actions.py +88 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/constants.py +114 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/examples.json +634 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/examples.py +48 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/markdown.py +55 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/mermaid_diagram.jsx +842 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/mermaidjs.py +265 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/mermaidjs.pyi +237 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/py.typed +0 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs/urls.py +164 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs.egg-info/PKG-INFO +261 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs.egg-info/SOURCES.txt +21 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs.egg-info/dependency_links.txt +1 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs.egg-info/requires.txt +1 -0
- reflex_mermaidjs-0.1.0/custom_components/reflex_mermaidjs.egg-info/top_level.txt +1 -0
- reflex_mermaidjs-0.1.0/pyproject.toml +87 -0
- reflex_mermaidjs-0.1.0/setup.cfg +4 -0
- reflex_mermaidjs-0.1.0/tests/test_component.py +134 -0
- reflex_mermaidjs-0.1.0/tests/test_urls.py +79 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ernesto Crespo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: reflex-mermaidjs
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reflex custom component for mermaid-js: flowcharts, sequence, class, ER, gantt, mindmap and 30+ diagram types from text, with pan & zoom, themes, export and events
|
|
5
|
+
Author-email: Ernesto Crespo <ecrespo@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ecrespo/reflex-mermaidjs
|
|
8
|
+
Project-URL: Source, https://github.com/ecrespo/reflex-mermaidjs
|
|
9
|
+
Project-URL: Issues, https://github.com/ecrespo/reflex-mermaidjs/issues
|
|
10
|
+
Keywords: reflex,reflex-custom-components,mermaid,mermaid-js,diagrams,flowchart,uml
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: reflex>=0.9.11
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# reflex-mermaidjs
|
|
30
|
+
|
|
31
|
+
[mermaid-js](https://mermaid.js.org) diagrams for [Reflex](https://reflex.dev): flowcharts, sequence, class, state, ER, gantt, mindmap, architecture, C4, sankey, kanban, venn, wardley, railroad and 30+ other diagram types, generated from text the way markdown is.
|
|
32
|
+
|
|
33
|
+
- Wraps **mermaid 12.0.0** (ELK layout bundled), plus the **tidy-tree** layout and **ZenUML** plugins
|
|
34
|
+
- Every mermaid option as a typed prop: `theme`, `look` (`classic` / `handDrawn` / `neo`), `layout`, `security_level`, `theme_variables`, raw `config`...
|
|
35
|
+
- `appearance="dark"` automatic dark themes (same behaviour as mermaid.live)
|
|
36
|
+
- **Pan & zoom** (wheel, drag, double-click to fit) with a floating toolbar and optional dotted grid
|
|
37
|
+
- Events: `on_render`, `on_error`, `on_node_click`, `on_zoom_change`
|
|
38
|
+
- Python actions: `zoom_in`, `zoom_out`, `set_zoom`, `fit_view`, `reset_view`, `fullscreen`, `export_png`, `export_svg`, `copy_svg`, `get_svg`
|
|
39
|
+
- Icons: Iconify `logos` and `mdi` bundled, any other Iconify pack by name, Font Awesome `fa:` icons
|
|
40
|
+
- KaTeX math (`$$...$$`) in labels
|
|
41
|
+
- `rx.markdown` integration: ```` ```mermaid ```` fences become diagrams
|
|
42
|
+
- Share helpers in pure Python: `mermaid_live_url`, `deserialize_state` (mermaid.live `pako:` links), `mermaid_ink_url`, `kroki_url`
|
|
43
|
+
- 36 diagram types of sample diagrams (`get_diagram_examples()`), the same set mermaid.live offers
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install reflex-mermaidjs
|
|
49
|
+
# or
|
|
50
|
+
uv add reflex-mermaidjs
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Requires `reflex>=0.9.11`. npm dependencies (mermaid, plugins, icons) are installed automatically by Reflex.
|
|
54
|
+
|
|
55
|
+
## Quick start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import reflex as rx
|
|
59
|
+
from reflex_mermaidjs import mermaid
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def index() -> rx.Component:
|
|
63
|
+
return mermaid(
|
|
64
|
+
"""flowchart TD
|
|
65
|
+
A[Christmas] -->|Get money| B(Go shopping)
|
|
66
|
+
B --> C{Let me think}
|
|
67
|
+
C -->|One| D[Laptop]
|
|
68
|
+
C -->|Two| E[iPhone]
|
|
69
|
+
C -->|Three| F[fa:fa-car Car]""",
|
|
70
|
+
theme="forest",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
app = rx.App()
|
|
75
|
+
app.add_page(index)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The diagram text can be passed positionally or as `chart=`, and can be a state var — the diagram re-renders when it changes.
|
|
79
|
+
|
|
80
|
+
## Live editing with state, pan & zoom and events
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import reflex as rx
|
|
84
|
+
from reflex_mermaidjs import mermaid, export_png, fit_view
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class State(rx.State):
|
|
88
|
+
code: str = "sequenceDiagram\n Alice->>Bob: Hi Bob\n Bob-->>Alice: Hi Alice"
|
|
89
|
+
diagram_type: str = ""
|
|
90
|
+
error: str = ""
|
|
91
|
+
|
|
92
|
+
@rx.event
|
|
93
|
+
def set_code(self, value: str):
|
|
94
|
+
self.code = value
|
|
95
|
+
|
|
96
|
+
@rx.event
|
|
97
|
+
def rendered(self, event: dict):
|
|
98
|
+
self.diagram_type = event["diagram_type"]
|
|
99
|
+
self.error = ""
|
|
100
|
+
|
|
101
|
+
@rx.event
|
|
102
|
+
def failed(self, event: dict):
|
|
103
|
+
self.error = event["message"]
|
|
104
|
+
|
|
105
|
+
@rx.event
|
|
106
|
+
def clicked(self, event: dict):
|
|
107
|
+
return rx.toast.info(f"Clicked {event['node_id']}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def index() -> rx.Component:
|
|
111
|
+
return rx.hstack(
|
|
112
|
+
rx.text_area(value=State.code, on_change=State.set_code.debounce(250), width="40%", rows="20"),
|
|
113
|
+
rx.vstack(
|
|
114
|
+
mermaid(
|
|
115
|
+
State.code,
|
|
116
|
+
id="diagram",
|
|
117
|
+
appearance=rx.color_mode_cond("light", "dark"),
|
|
118
|
+
pan_zoom=True,
|
|
119
|
+
grid=True,
|
|
120
|
+
node_click=True,
|
|
121
|
+
error_mode="keep-last",
|
|
122
|
+
on_render=State.rendered,
|
|
123
|
+
on_error=State.failed,
|
|
124
|
+
on_node_click=State.clicked,
|
|
125
|
+
height="600px",
|
|
126
|
+
width="100%",
|
|
127
|
+
),
|
|
128
|
+
rx.hstack(
|
|
129
|
+
rx.button("Fit", on_click=fit_view("diagram")),
|
|
130
|
+
rx.button("PNG", on_click=export_png("diagram", "diagram.png")),
|
|
131
|
+
rx.text(State.diagram_type),
|
|
132
|
+
),
|
|
133
|
+
width="60%",
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Props
|
|
139
|
+
|
|
140
|
+
| Prop | Type | Description |
|
|
141
|
+
| --- | --- | --- |
|
|
142
|
+
| `chart` | `str` | Diagram definition (front matter and `%%{init}%%` directives are supported) |
|
|
143
|
+
| `config` | `dict` | Raw [MermaidConfig](https://mermaid.js.org/config/schema-docs/config.html) passed to `mermaid.initialize` |
|
|
144
|
+
| `theme` | `default`, `base`, `dark`, `forest`, `neutral`, `neo`, `neo-dark`, `redux`, `redux-dark`, `redux-color`, `redux-dark-color`, `null` | Theme |
|
|
145
|
+
| `look` | `classic`, `handDrawn`, `neo` | Visual look |
|
|
146
|
+
| `layout` | `str` | `dagre`, `elk`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, `elk.box`, `elk.rectpacking`, `tidy-tree`... |
|
|
147
|
+
| `security_level` | `strict`, `loose`, `antiscript`, `sandbox` | `loose` enables `click` callbacks and HTML labels |
|
|
148
|
+
| `theme_variables` / `theme_css` | `dict` / `str` | Theme customisation (most useful with `theme="base"`) |
|
|
149
|
+
| `font_family`, `font_size`, `hand_drawn_seed`, `dark_mode`, `html_labels`, `wrap`, `max_text_size`, `max_edges`, `deterministic_ids`, `log_level` | | Same as the mermaid options |
|
|
150
|
+
| `appearance` | `light`, `dark` | With no explicit theme, `dark` uses the dark variant of each diagram type's default theme |
|
|
151
|
+
| `tidy_tree` | `bool` | Register the tidy-tree layout (default `True`) |
|
|
152
|
+
| `zenuml` | `bool` | Register ZenUML eagerly (it is auto-registered when the text starts with `zenuml`) |
|
|
153
|
+
| `font_awesome` | `bool` | Load Font Awesome CSS for `fa:` icons (default `True`) |
|
|
154
|
+
| `icon_packs` | `list[str \| dict]` | Iconify packs. `logos` and `mdi` are bundled; other names load from jsDelivr; or `{"name": ..., "url": ...}` |
|
|
155
|
+
| `pan_zoom` | `bool` | Wheel zoom + drag pan. The component fills its box, so give it a height (defaults to 480px) |
|
|
156
|
+
| `show_controls`, `controls_position` | `bool`, `top-right`... | Floating toolbar (fit, zoom out, zoom in, 100%, fullscreen) |
|
|
157
|
+
| `min_zoom`, `max_zoom`, `zoom_step`, `max_fit_zoom` | `float` | Zoom limits |
|
|
158
|
+
| `fit_on_render` | `bool \| "auto"` | `auto` fits on first render and when the diagram type/size changes a lot |
|
|
159
|
+
| `grid` | `bool` | Dotted background grid (color via the `--mermaid-grid-color` CSS variable) |
|
|
160
|
+
| `center` | `bool` | Center the SVG in static mode |
|
|
161
|
+
| `debounce` | `int` | Debounce re-renders in ms |
|
|
162
|
+
| `error_mode` | `inline`, `keep-last`, `none` | How parse errors are shown |
|
|
163
|
+
| `click_callbacks` | `list[str]` | Callback names used in `click node callback` directives, routed to `on_node_click` |
|
|
164
|
+
| `node_click` | `bool` | Emit `on_node_click` for any node click |
|
|
165
|
+
|
|
166
|
+
### Events
|
|
167
|
+
|
|
168
|
+
| Event | Payload |
|
|
169
|
+
| --- | --- |
|
|
170
|
+
| `on_render` | `{svg, diagram_type, render_id, render_ms}` |
|
|
171
|
+
| `on_error` | `{message, line, expected}` |
|
|
172
|
+
| `on_node_click` | `{node_id, callback, label, args}` |
|
|
173
|
+
| `on_zoom_change` | `{zoom, x, y}` (debounced) |
|
|
174
|
+
|
|
175
|
+
## Python actions
|
|
176
|
+
|
|
177
|
+
Any diagram with an `id` can be driven from event handlers or triggers. They return `rx.call_script` events:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from reflex_mermaidjs import zoom_in, zoom_out, set_zoom, fit_view, reset_view, fullscreen
|
|
181
|
+
from reflex_mermaidjs import export_png, export_svg, copy_svg, get_svg, get_png_data_url
|
|
182
|
+
|
|
183
|
+
rx.button("PNG", on_click=export_png("diagram", "diagram.png", scale=2, background="transparent"))
|
|
184
|
+
rx.button("Send SVG to Python", on_click=get_svg("diagram", State.receive_svg))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Markdown
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from reflex_mermaidjs import markdown_with_mermaid, mermaid_component_map
|
|
191
|
+
|
|
192
|
+
markdown_with_mermaid(State.text, mermaid_props={"theme": "neutral"})
|
|
193
|
+
# or merge with your own map
|
|
194
|
+
rx.markdown(State.text, component_map={**mermaid_component_map(), "h1": my_h1})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Non-mermaid code fences keep the regular `rx.code_block` highlighting.
|
|
198
|
+
|
|
199
|
+
## Sharing helpers
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from reflex_mermaidjs import mermaid_live_url, mermaid_ink_url, kroki_url, deserialize_state
|
|
203
|
+
|
|
204
|
+
mermaid_live_url(code, {"theme": "dark"}) # https://mermaid.live/edit#pako:...
|
|
205
|
+
mermaid_ink_url(code, fmt="img", image_type="png") # server-rendered image
|
|
206
|
+
kroki_url(code, "svg")
|
|
207
|
+
state = deserialize_state("https://mermaid.live/edit#pako:eNp...") # {"code": ..., "mermaid": ...}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Icons
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
mermaid("""architecture-beta
|
|
214
|
+
group api(logos:aws-lambda)[API]
|
|
215
|
+
service db(logos:postgresql)[Database] in api
|
|
216
|
+
service server(logos:python)[Server] in api
|
|
217
|
+
db:L -- R:server""")
|
|
218
|
+
|
|
219
|
+
mermaid(
|
|
220
|
+
chart,
|
|
221
|
+
icon_packs=["logos", "mdi", "fa7-brands", {"name": "custom", "url": "https://example.com/icons.json"}],
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Demo app
|
|
226
|
+
|
|
227
|
+
The `mermaidjs_demo/` app is a mermaid.live-style editor built with this component:
|
|
228
|
+
|
|
229
|
+
- **Live editor** — code editor with line numbers, config tab (theme/look/layout/security selectors + JSON), all sample diagrams, history, share dialog (mermaid.live / mermaid.ink / kroki links, import a mermaid.live link, Python and Markdown snippets), PNG/SVG export, hand-drawn/grid/pan-zoom toggles, dark mode
|
|
230
|
+
- **Gallery** — every diagram type rendered live
|
|
231
|
+
- **Features** — themes, looks, layouts, theme variables from state, data-driven charts, events and actions, markdown, icons, math, ZenUML, error handling
|
|
232
|
+
- **API** — props table generated from the source
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
uv sync --all-groups
|
|
236
|
+
cd mermaidjs_demo
|
|
237
|
+
uv run reflex run
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
uv sync --all-groups
|
|
244
|
+
|
|
245
|
+
uv run pytest # tests
|
|
246
|
+
uv run ruff check . # lint
|
|
247
|
+
uv run ruff format . # format
|
|
248
|
+
uv run bandit -c pyproject.toml -r . -ll # SAST
|
|
249
|
+
|
|
250
|
+
uv run reflex component build # generates .pyi stubs and builds dist/
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The same checks run in CI on every push and pull request — see the
|
|
254
|
+
[Quality](.github/workflows/quality.yml) and
|
|
255
|
+
[Security](.github/workflows/security.yml) workflows. Releases are tag-driven
|
|
256
|
+
and documented in [RELEASING.md](RELEASING.md); the security posture of the
|
|
257
|
+
component is documented in [SECURITY.md](SECURITY.md).
|
|
258
|
+
|
|
259
|
+
## License
|
|
260
|
+
|
|
261
|
+
MIT © Ernesto Crespo
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# reflex-mermaidjs
|
|
2
|
+
|
|
3
|
+
[mermaid-js](https://mermaid.js.org) diagrams for [Reflex](https://reflex.dev): flowcharts, sequence, class, state, ER, gantt, mindmap, architecture, C4, sankey, kanban, venn, wardley, railroad and 30+ other diagram types, generated from text the way markdown is.
|
|
4
|
+
|
|
5
|
+
- Wraps **mermaid 12.0.0** (ELK layout bundled), plus the **tidy-tree** layout and **ZenUML** plugins
|
|
6
|
+
- Every mermaid option as a typed prop: `theme`, `look` (`classic` / `handDrawn` / `neo`), `layout`, `security_level`, `theme_variables`, raw `config`...
|
|
7
|
+
- `appearance="dark"` automatic dark themes (same behaviour as mermaid.live)
|
|
8
|
+
- **Pan & zoom** (wheel, drag, double-click to fit) with a floating toolbar and optional dotted grid
|
|
9
|
+
- Events: `on_render`, `on_error`, `on_node_click`, `on_zoom_change`
|
|
10
|
+
- Python actions: `zoom_in`, `zoom_out`, `set_zoom`, `fit_view`, `reset_view`, `fullscreen`, `export_png`, `export_svg`, `copy_svg`, `get_svg`
|
|
11
|
+
- Icons: Iconify `logos` and `mdi` bundled, any other Iconify pack by name, Font Awesome `fa:` icons
|
|
12
|
+
- KaTeX math (`$$...$$`) in labels
|
|
13
|
+
- `rx.markdown` integration: ```` ```mermaid ```` fences become diagrams
|
|
14
|
+
- Share helpers in pure Python: `mermaid_live_url`, `deserialize_state` (mermaid.live `pako:` links), `mermaid_ink_url`, `kroki_url`
|
|
15
|
+
- 36 diagram types of sample diagrams (`get_diagram_examples()`), the same set mermaid.live offers
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install reflex-mermaidjs
|
|
21
|
+
# or
|
|
22
|
+
uv add reflex-mermaidjs
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires `reflex>=0.9.11`. npm dependencies (mermaid, plugins, icons) are installed automatically by Reflex.
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
import reflex as rx
|
|
31
|
+
from reflex_mermaidjs import mermaid
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def index() -> rx.Component:
|
|
35
|
+
return mermaid(
|
|
36
|
+
"""flowchart TD
|
|
37
|
+
A[Christmas] -->|Get money| B(Go shopping)
|
|
38
|
+
B --> C{Let me think}
|
|
39
|
+
C -->|One| D[Laptop]
|
|
40
|
+
C -->|Two| E[iPhone]
|
|
41
|
+
C -->|Three| F[fa:fa-car Car]""",
|
|
42
|
+
theme="forest",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
app = rx.App()
|
|
47
|
+
app.add_page(index)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The diagram text can be passed positionally or as `chart=`, and can be a state var — the diagram re-renders when it changes.
|
|
51
|
+
|
|
52
|
+
## Live editing with state, pan & zoom and events
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import reflex as rx
|
|
56
|
+
from reflex_mermaidjs import mermaid, export_png, fit_view
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class State(rx.State):
|
|
60
|
+
code: str = "sequenceDiagram\n Alice->>Bob: Hi Bob\n Bob-->>Alice: Hi Alice"
|
|
61
|
+
diagram_type: str = ""
|
|
62
|
+
error: str = ""
|
|
63
|
+
|
|
64
|
+
@rx.event
|
|
65
|
+
def set_code(self, value: str):
|
|
66
|
+
self.code = value
|
|
67
|
+
|
|
68
|
+
@rx.event
|
|
69
|
+
def rendered(self, event: dict):
|
|
70
|
+
self.diagram_type = event["diagram_type"]
|
|
71
|
+
self.error = ""
|
|
72
|
+
|
|
73
|
+
@rx.event
|
|
74
|
+
def failed(self, event: dict):
|
|
75
|
+
self.error = event["message"]
|
|
76
|
+
|
|
77
|
+
@rx.event
|
|
78
|
+
def clicked(self, event: dict):
|
|
79
|
+
return rx.toast.info(f"Clicked {event['node_id']}")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def index() -> rx.Component:
|
|
83
|
+
return rx.hstack(
|
|
84
|
+
rx.text_area(value=State.code, on_change=State.set_code.debounce(250), width="40%", rows="20"),
|
|
85
|
+
rx.vstack(
|
|
86
|
+
mermaid(
|
|
87
|
+
State.code,
|
|
88
|
+
id="diagram",
|
|
89
|
+
appearance=rx.color_mode_cond("light", "dark"),
|
|
90
|
+
pan_zoom=True,
|
|
91
|
+
grid=True,
|
|
92
|
+
node_click=True,
|
|
93
|
+
error_mode="keep-last",
|
|
94
|
+
on_render=State.rendered,
|
|
95
|
+
on_error=State.failed,
|
|
96
|
+
on_node_click=State.clicked,
|
|
97
|
+
height="600px",
|
|
98
|
+
width="100%",
|
|
99
|
+
),
|
|
100
|
+
rx.hstack(
|
|
101
|
+
rx.button("Fit", on_click=fit_view("diagram")),
|
|
102
|
+
rx.button("PNG", on_click=export_png("diagram", "diagram.png")),
|
|
103
|
+
rx.text(State.diagram_type),
|
|
104
|
+
),
|
|
105
|
+
width="60%",
|
|
106
|
+
),
|
|
107
|
+
)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Props
|
|
111
|
+
|
|
112
|
+
| Prop | Type | Description |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `chart` | `str` | Diagram definition (front matter and `%%{init}%%` directives are supported) |
|
|
115
|
+
| `config` | `dict` | Raw [MermaidConfig](https://mermaid.js.org/config/schema-docs/config.html) passed to `mermaid.initialize` |
|
|
116
|
+
| `theme` | `default`, `base`, `dark`, `forest`, `neutral`, `neo`, `neo-dark`, `redux`, `redux-dark`, `redux-color`, `redux-dark-color`, `null` | Theme |
|
|
117
|
+
| `look` | `classic`, `handDrawn`, `neo` | Visual look |
|
|
118
|
+
| `layout` | `str` | `dagre`, `elk`, `elk.stress`, `elk.force`, `elk.mrtree`, `elk.radial`, `elk.box`, `elk.rectpacking`, `tidy-tree`... |
|
|
119
|
+
| `security_level` | `strict`, `loose`, `antiscript`, `sandbox` | `loose` enables `click` callbacks and HTML labels |
|
|
120
|
+
| `theme_variables` / `theme_css` | `dict` / `str` | Theme customisation (most useful with `theme="base"`) |
|
|
121
|
+
| `font_family`, `font_size`, `hand_drawn_seed`, `dark_mode`, `html_labels`, `wrap`, `max_text_size`, `max_edges`, `deterministic_ids`, `log_level` | | Same as the mermaid options |
|
|
122
|
+
| `appearance` | `light`, `dark` | With no explicit theme, `dark` uses the dark variant of each diagram type's default theme |
|
|
123
|
+
| `tidy_tree` | `bool` | Register the tidy-tree layout (default `True`) |
|
|
124
|
+
| `zenuml` | `bool` | Register ZenUML eagerly (it is auto-registered when the text starts with `zenuml`) |
|
|
125
|
+
| `font_awesome` | `bool` | Load Font Awesome CSS for `fa:` icons (default `True`) |
|
|
126
|
+
| `icon_packs` | `list[str \| dict]` | Iconify packs. `logos` and `mdi` are bundled; other names load from jsDelivr; or `{"name": ..., "url": ...}` |
|
|
127
|
+
| `pan_zoom` | `bool` | Wheel zoom + drag pan. The component fills its box, so give it a height (defaults to 480px) |
|
|
128
|
+
| `show_controls`, `controls_position` | `bool`, `top-right`... | Floating toolbar (fit, zoom out, zoom in, 100%, fullscreen) |
|
|
129
|
+
| `min_zoom`, `max_zoom`, `zoom_step`, `max_fit_zoom` | `float` | Zoom limits |
|
|
130
|
+
| `fit_on_render` | `bool \| "auto"` | `auto` fits on first render and when the diagram type/size changes a lot |
|
|
131
|
+
| `grid` | `bool` | Dotted background grid (color via the `--mermaid-grid-color` CSS variable) |
|
|
132
|
+
| `center` | `bool` | Center the SVG in static mode |
|
|
133
|
+
| `debounce` | `int` | Debounce re-renders in ms |
|
|
134
|
+
| `error_mode` | `inline`, `keep-last`, `none` | How parse errors are shown |
|
|
135
|
+
| `click_callbacks` | `list[str]` | Callback names used in `click node callback` directives, routed to `on_node_click` |
|
|
136
|
+
| `node_click` | `bool` | Emit `on_node_click` for any node click |
|
|
137
|
+
|
|
138
|
+
### Events
|
|
139
|
+
|
|
140
|
+
| Event | Payload |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| `on_render` | `{svg, diagram_type, render_id, render_ms}` |
|
|
143
|
+
| `on_error` | `{message, line, expected}` |
|
|
144
|
+
| `on_node_click` | `{node_id, callback, label, args}` |
|
|
145
|
+
| `on_zoom_change` | `{zoom, x, y}` (debounced) |
|
|
146
|
+
|
|
147
|
+
## Python actions
|
|
148
|
+
|
|
149
|
+
Any diagram with an `id` can be driven from event handlers or triggers. They return `rx.call_script` events:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from reflex_mermaidjs import zoom_in, zoom_out, set_zoom, fit_view, reset_view, fullscreen
|
|
153
|
+
from reflex_mermaidjs import export_png, export_svg, copy_svg, get_svg, get_png_data_url
|
|
154
|
+
|
|
155
|
+
rx.button("PNG", on_click=export_png("diagram", "diagram.png", scale=2, background="transparent"))
|
|
156
|
+
rx.button("Send SVG to Python", on_click=get_svg("diagram", State.receive_svg))
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Markdown
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from reflex_mermaidjs import markdown_with_mermaid, mermaid_component_map
|
|
163
|
+
|
|
164
|
+
markdown_with_mermaid(State.text, mermaid_props={"theme": "neutral"})
|
|
165
|
+
# or merge with your own map
|
|
166
|
+
rx.markdown(State.text, component_map={**mermaid_component_map(), "h1": my_h1})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Non-mermaid code fences keep the regular `rx.code_block` highlighting.
|
|
170
|
+
|
|
171
|
+
## Sharing helpers
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from reflex_mermaidjs import mermaid_live_url, mermaid_ink_url, kroki_url, deserialize_state
|
|
175
|
+
|
|
176
|
+
mermaid_live_url(code, {"theme": "dark"}) # https://mermaid.live/edit#pako:...
|
|
177
|
+
mermaid_ink_url(code, fmt="img", image_type="png") # server-rendered image
|
|
178
|
+
kroki_url(code, "svg")
|
|
179
|
+
state = deserialize_state("https://mermaid.live/edit#pako:eNp...") # {"code": ..., "mermaid": ...}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Icons
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
mermaid("""architecture-beta
|
|
186
|
+
group api(logos:aws-lambda)[API]
|
|
187
|
+
service db(logos:postgresql)[Database] in api
|
|
188
|
+
service server(logos:python)[Server] in api
|
|
189
|
+
db:L -- R:server""")
|
|
190
|
+
|
|
191
|
+
mermaid(
|
|
192
|
+
chart,
|
|
193
|
+
icon_packs=["logos", "mdi", "fa7-brands", {"name": "custom", "url": "https://example.com/icons.json"}],
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Demo app
|
|
198
|
+
|
|
199
|
+
The `mermaidjs_demo/` app is a mermaid.live-style editor built with this component:
|
|
200
|
+
|
|
201
|
+
- **Live editor** — code editor with line numbers, config tab (theme/look/layout/security selectors + JSON), all sample diagrams, history, share dialog (mermaid.live / mermaid.ink / kroki links, import a mermaid.live link, Python and Markdown snippets), PNG/SVG export, hand-drawn/grid/pan-zoom toggles, dark mode
|
|
202
|
+
- **Gallery** — every diagram type rendered live
|
|
203
|
+
- **Features** — themes, looks, layouts, theme variables from state, data-driven charts, events and actions, markdown, icons, math, ZenUML, error handling
|
|
204
|
+
- **API** — props table generated from the source
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
uv sync --all-groups
|
|
208
|
+
cd mermaidjs_demo
|
|
209
|
+
uv run reflex run
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
uv sync --all-groups
|
|
216
|
+
|
|
217
|
+
uv run pytest # tests
|
|
218
|
+
uv run ruff check . # lint
|
|
219
|
+
uv run ruff format . # format
|
|
220
|
+
uv run bandit -c pyproject.toml -r . -ll # SAST
|
|
221
|
+
|
|
222
|
+
uv run reflex component build # generates .pyi stubs and builds dist/
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The same checks run in CI on every push and pull request — see the
|
|
226
|
+
[Quality](.github/workflows/quality.yml) and
|
|
227
|
+
[Security](.github/workflows/security.yml) workflows. Releases are tag-driven
|
|
228
|
+
and documented in [RELEASING.md](RELEASING.md); the security posture of the
|
|
229
|
+
component is documented in [SECURITY.md](SECURITY.md).
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT © Ernesto Crespo
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""reflex-mermaidjs: mermaid-js diagrams for Reflex."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError
|
|
4
|
+
from importlib.metadata import version as _package_version
|
|
5
|
+
|
|
6
|
+
from . import actions
|
|
7
|
+
from .actions import (
|
|
8
|
+
copy_svg,
|
|
9
|
+
export_png,
|
|
10
|
+
export_svg,
|
|
11
|
+
fit_view,
|
|
12
|
+
fullscreen,
|
|
13
|
+
get_png_data_url,
|
|
14
|
+
get_svg,
|
|
15
|
+
reset_view,
|
|
16
|
+
set_zoom,
|
|
17
|
+
zoom_in,
|
|
18
|
+
zoom_out,
|
|
19
|
+
)
|
|
20
|
+
from .constants import (
|
|
21
|
+
DIAGRAM_KEYWORDS,
|
|
22
|
+
LAYOUTS,
|
|
23
|
+
LOOKS,
|
|
24
|
+
MERMAID_VERSION,
|
|
25
|
+
SECURITY_LEVELS,
|
|
26
|
+
THEMES,
|
|
27
|
+
)
|
|
28
|
+
from .examples import get_default_example, get_diagram_examples
|
|
29
|
+
from .markdown import markdown_with_mermaid, mermaid_component_map
|
|
30
|
+
from .mermaidjs import (
|
|
31
|
+
ErrorEvent,
|
|
32
|
+
IconPack,
|
|
33
|
+
Mermaid,
|
|
34
|
+
NodeClickEvent,
|
|
35
|
+
RenderEvent,
|
|
36
|
+
ZoomEvent,
|
|
37
|
+
mermaid,
|
|
38
|
+
)
|
|
39
|
+
from .urls import (
|
|
40
|
+
build_state,
|
|
41
|
+
deserialize_state,
|
|
42
|
+
kroki_url,
|
|
43
|
+
mermaid_ink_url,
|
|
44
|
+
mermaid_live_url,
|
|
45
|
+
pako_decode,
|
|
46
|
+
pako_encode,
|
|
47
|
+
serialize_state,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# pyproject.toml is the single source of truth for the version; read it back
|
|
51
|
+
# from the installed metadata so the two can never drift.
|
|
52
|
+
try:
|
|
53
|
+
__version__ = _package_version("reflex-mermaidjs")
|
|
54
|
+
except PackageNotFoundError: # pragma: no cover - source tree without an install
|
|
55
|
+
__version__ = "0.0.0.dev0"
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
"DIAGRAM_KEYWORDS",
|
|
59
|
+
"LAYOUTS",
|
|
60
|
+
"LOOKS",
|
|
61
|
+
"MERMAID_VERSION",
|
|
62
|
+
"SECURITY_LEVELS",
|
|
63
|
+
"THEMES",
|
|
64
|
+
"ErrorEvent",
|
|
65
|
+
"IconPack",
|
|
66
|
+
"Mermaid",
|
|
67
|
+
"NodeClickEvent",
|
|
68
|
+
"RenderEvent",
|
|
69
|
+
"ZoomEvent",
|
|
70
|
+
"actions",
|
|
71
|
+
"build_state",
|
|
72
|
+
"copy_svg",
|
|
73
|
+
"deserialize_state",
|
|
74
|
+
"export_png",
|
|
75
|
+
"export_svg",
|
|
76
|
+
"fit_view",
|
|
77
|
+
"fullscreen",
|
|
78
|
+
"get_default_example",
|
|
79
|
+
"get_diagram_examples",
|
|
80
|
+
"get_png_data_url",
|
|
81
|
+
"get_svg",
|
|
82
|
+
"kroki_url",
|
|
83
|
+
"markdown_with_mermaid",
|
|
84
|
+
"mermaid",
|
|
85
|
+
"mermaid_component_map",
|
|
86
|
+
"mermaid_ink_url",
|
|
87
|
+
"mermaid_live_url",
|
|
88
|
+
"pako_decode",
|
|
89
|
+
"pako_encode",
|
|
90
|
+
"reset_view",
|
|
91
|
+
"serialize_state",
|
|
92
|
+
"set_zoom",
|
|
93
|
+
"zoom_in",
|
|
94
|
+
"zoom_out",
|
|
95
|
+
]
|