streamlit-schema-editor 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.
- streamlit_schema_editor-0.1.0/LICENSE +1 -0
- streamlit_schema_editor-0.1.0/PKG-INFO +433 -0
- streamlit_schema_editor-0.1.0/README.md +417 -0
- streamlit_schema_editor-0.1.0/examples/databricks_mapping.py +197 -0
- streamlit_schema_editor-0.1.0/examples/er_diagram.py +74 -0
- streamlit_schema_editor-0.1.0/examples/playground.py +369 -0
- streamlit_schema_editor-0.1.0/examples/schema_viewer.py +98 -0
- streamlit_schema_editor-0.1.0/pyproject.toml +58 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/__init__.py +37 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/_component.py +50 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/api.py +232 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/build/index-DfzQj94y.js +16536 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/build/index-_hash_.css +1 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/package-lock.json +3039 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/package.json +46 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/preview.html +12 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/SchemaEditorCanvas.tsx +2638 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/base-handle.tsx +28 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/base-node.tsx +47 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/button-edge.tsx +46 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/database-schema-node.tsx +83 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/labeled-handle.tsx +51 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/ui/button.tsx +52 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/components/ui/table.tsx +56 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/global.css +198 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/index.tsx +63 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/lib/utils.ts +6 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/preview.tsx +126 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/src/vite-env.d.ts +1 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/tsconfig.json +21 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/frontend/vite.config.ts +43 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/py.typed +1 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/pyproject.toml +8 -0
- streamlit_schema_editor-0.1.0/streamlit_schema_editor/types.py +110 -0
- streamlit_schema_editor-0.1.0/tests/fixtures/e2e_app.py +66 -0
- streamlit_schema_editor-0.1.0/tests/smoke_test.py +35 -0
- streamlit_schema_editor-0.1.0/tests/test_api.py +238 -0
- streamlit_schema_editor-0.1.0/tests/test_component_e2e.py +162 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
YOUR LICENSE HERE
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: streamlit-schema-editor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Streamlit custom component for rendering and editing schema graphs
|
|
5
|
+
Keywords: streamlit,schema,er-diagram,lineage,metadata
|
|
6
|
+
Author: Parth Mishra
|
|
7
|
+
Author-email: Parth Mishra <pmishraworld@mac.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: streamlit>=1.51
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Project-URL: Homepage, https://github.com/parthmishra/streamlit-schema-editor
|
|
13
|
+
Project-URL: Repository, https://github.com/parthmishra/streamlit-schema-editor
|
|
14
|
+
Project-URL: Issues, https://github.com/parthmishra/streamlit-schema-editor/issues
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# streamlit-schema-editor
|
|
18
|
+
|
|
19
|
+
Streamlit custom component for viewing and editing schema, ER, and source-to-target
|
|
20
|
+
mapping graphs with React Flow and Streamlit Custom Components v2.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
uv pip install streamlit-schema-editor
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## What It Supports
|
|
29
|
+
|
|
30
|
+
- schema viewing with table and column metadata
|
|
31
|
+
- source-to-target mapping with interactive relationship creation and deletion
|
|
32
|
+
- inline column editing for `name` and `data_type`
|
|
33
|
+
- optional edge action button for relationship inspection or custom workflows
|
|
34
|
+
- read-only or editable canvases
|
|
35
|
+
- validation styling for tables, columns, and relationships
|
|
36
|
+
- metadata passthrough on tables, columns, and relationships
|
|
37
|
+
- structured `event` + `event_context` payloads for Streamlit-side workflows
|
|
38
|
+
|
|
39
|
+
## Usage
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import streamlit as st
|
|
43
|
+
|
|
44
|
+
from streamlit_schema_editor import streamlit_schema_editor
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if "tables" not in st.session_state:
|
|
48
|
+
st.session_state.tables = [
|
|
49
|
+
{
|
|
50
|
+
"id": "crm_customer",
|
|
51
|
+
"label": "crm_customer",
|
|
52
|
+
"metadata": {"system": "crm"},
|
|
53
|
+
"columns": [
|
|
54
|
+
{"id": "customer_id", "name": "customer_id", "data_type": "uuid"},
|
|
55
|
+
{"id": "cust_name", "name": "cust_name", "data_type": "varchar"},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "dim_customer",
|
|
60
|
+
"label": "dim_customer",
|
|
61
|
+
"metadata": {"system": "warehouse"},
|
|
62
|
+
"columns": [
|
|
63
|
+
{"id": "customer_key", "name": "customer_key", "data_type": "bigint"},
|
|
64
|
+
{"id": "customer_name", "name": "customer_name", "data_type": "varchar"},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
if "relationships" not in st.session_state:
|
|
70
|
+
st.session_state.relationships = [
|
|
71
|
+
{
|
|
72
|
+
"id": "rel::crm_customer::cust_name::dim_customer::customer_name",
|
|
73
|
+
"source_table": "crm_customer",
|
|
74
|
+
"source_column": "cust_name",
|
|
75
|
+
"target_table": "dim_customer",
|
|
76
|
+
"target_column": "customer_name",
|
|
77
|
+
"validation": {
|
|
78
|
+
"status": "loading",
|
|
79
|
+
"summary": "Awaiting warehouse validation for this mapping.",
|
|
80
|
+
},
|
|
81
|
+
"metadata": {
|
|
82
|
+
"rule_id": "MAP-1024",
|
|
83
|
+
"sql_expression": "UPPER(TRIM(cust_name))",
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
value = streamlit_schema_editor(
|
|
89
|
+
st.session_state.tables,
|
|
90
|
+
st.session_state.relationships,
|
|
91
|
+
height=700,
|
|
92
|
+
show_controls=True,
|
|
93
|
+
max_connections_per_handle=2,
|
|
94
|
+
key="schema-editor",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
st.session_state.tables = value["tables"]
|
|
98
|
+
st.session_state.relationships = value["relationships"]
|
|
99
|
+
|
|
100
|
+
st.write(value["event"])
|
|
101
|
+
st.json(value["event_context"])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## API
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
streamlit_schema_editor(
|
|
108
|
+
tables,
|
|
109
|
+
relationships,
|
|
110
|
+
*,
|
|
111
|
+
groups=None,
|
|
112
|
+
height=600,
|
|
113
|
+
fit_view=True,
|
|
114
|
+
editable=True,
|
|
115
|
+
connectable=None,
|
|
116
|
+
draggable=None,
|
|
117
|
+
deletable=None,
|
|
118
|
+
show_controls=False,
|
|
119
|
+
show_arrowheads=True,
|
|
120
|
+
show_edge_button=False,
|
|
121
|
+
show_column_count_badge=True,
|
|
122
|
+
show_groups=True,
|
|
123
|
+
group_layout="manual",
|
|
124
|
+
group_order=None,
|
|
125
|
+
table_layout_within_group="manual",
|
|
126
|
+
show_validation=True,
|
|
127
|
+
validation_refresh_key=None,
|
|
128
|
+
column_type_options=None,
|
|
129
|
+
allow_zoom=True,
|
|
130
|
+
allow_duplicate_edges=False,
|
|
131
|
+
max_connections_per_handle=None,
|
|
132
|
+
max_incoming_connections_per_handle=None,
|
|
133
|
+
max_outgoing_connections_per_handle=None,
|
|
134
|
+
key=None,
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`editable` acts as the default for `connectable` and `deletable`. Dragging is
|
|
139
|
+
enabled by default even for read-only canvases, and `editable` also enables
|
|
140
|
+
inline column editing, row add/remove controls, and column edit events. Override
|
|
141
|
+
the interaction flags individually when you need a mixed mode.
|
|
142
|
+
|
|
143
|
+
Use `show_arrowheads=False` for ER-style views where you want the canvas to read
|
|
144
|
+
more like an undirected diagram, and `show_column_count_badge=False` when long
|
|
145
|
+
table names need the extra header space. The legacy aliases
|
|
146
|
+
`max_incoming_per_target` and `max_outgoing_per_source` are still accepted for
|
|
147
|
+
backward compatibility, but the generic connection-limit parameters are the
|
|
148
|
+
preferred public API.
|
|
149
|
+
|
|
150
|
+
`show_edge_button` is optional and defaults to `False`. Turn it on only when you
|
|
151
|
+
want a per-relationship action affordance in the middle of the edge.
|
|
152
|
+
|
|
153
|
+
Use `groups` plus per-table `group_id` when you want optional labeled containers
|
|
154
|
+
such as `Source` / `Target`, database lanes, or bronze/silver/gold layers.
|
|
155
|
+
`show_groups` controls whether those containers are currently rendered without
|
|
156
|
+
requiring you to change the underlying table or relationship payload.
|
|
157
|
+
|
|
158
|
+
Use `group_layout="columns"` or `group_layout="rows"` when you want the
|
|
159
|
+
component to place groups automatically without manually setting every group
|
|
160
|
+
`position`. Use `group_order` to control that automatic order, and
|
|
161
|
+
`table_layout_within_group="stack"` to vertically arrange grouped tables for
|
|
162
|
+
lane-style views. Automatic layout modes intentionally own placement, so they
|
|
163
|
+
are best paired with read-only or lightly interactive canvases.
|
|
164
|
+
|
|
165
|
+
`show_validation` controls whether validation-derived colors, badges, and
|
|
166
|
+
summaries are rendered. If you want to force those visuals to refresh without
|
|
167
|
+
changing the schema payload, change `validation_refresh_key` between reruns.
|
|
168
|
+
|
|
169
|
+
### Edge Button
|
|
170
|
+
|
|
171
|
+
Enable the button when you want relationship inspection or a custom workflow
|
|
172
|
+
trigger from the canvas:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
value = streamlit_schema_editor(
|
|
176
|
+
tables,
|
|
177
|
+
relationships,
|
|
178
|
+
show_edge_button=True,
|
|
179
|
+
key="schema-editor-with-edge-actions",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if value["event"] == "edge_details_requested":
|
|
183
|
+
relationship_id = (value["event_context"] or {}).get("relationship_id")
|
|
184
|
+
st.write(f"Inspect relationship: {relationship_id}")
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
When enabled, the edge button can show:
|
|
188
|
+
|
|
189
|
+
- `relationship["label"]` when present
|
|
190
|
+
- `i` as a compact fallback for inspection
|
|
191
|
+
|
|
192
|
+
This button is useful for cases like opening a details panel, surfacing lineage
|
|
193
|
+
metadata, showing validation context, or launching a relationship editor. For
|
|
194
|
+
pure ER or schema-viewing use cases, leave it disabled.
|
|
195
|
+
|
|
196
|
+
### Return Value
|
|
197
|
+
|
|
198
|
+
- `tables`: current table list with updated positions
|
|
199
|
+
- `groups`: current group list with updated positions and sizes
|
|
200
|
+
- `relationships`: current relationships after connect/delete actions
|
|
201
|
+
- `selection`: current selected table, column, and relationship ids
|
|
202
|
+
- `event`: semantic event name or `None`
|
|
203
|
+
- `event_context`: structured payload for the last event
|
|
204
|
+
|
|
205
|
+
### Event Names
|
|
206
|
+
|
|
207
|
+
- `selection_changed`
|
|
208
|
+
- `node_moved`
|
|
209
|
+
- `table_deleted`
|
|
210
|
+
- `column_created`
|
|
211
|
+
- `column_updated`
|
|
212
|
+
- `column_deleted`
|
|
213
|
+
- `relationship_created`
|
|
214
|
+
- `relationship_deleted`
|
|
215
|
+
- `relationship_rejected`
|
|
216
|
+
- `edge_details_requested`
|
|
217
|
+
|
|
218
|
+
`edge_details_requested` is only emitted when `show_edge_button=True`.
|
|
219
|
+
|
|
220
|
+
### Schema Fields
|
|
221
|
+
|
|
222
|
+
Tables, columns, and relationships support generic `metadata` passthrough. Tables,
|
|
223
|
+
columns, and relationships can also opt into a top-level `validation` object.
|
|
224
|
+
Tables can optionally declare a single `group_id`, and groups are defined
|
|
225
|
+
separately with `id`, `label`, `position`, `width`, `height`, and `metadata`:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
{
|
|
229
|
+
"validation": {
|
|
230
|
+
"status": "error",
|
|
231
|
+
"code": "missing_column",
|
|
232
|
+
"summary": "Column not found in upstream schema.",
|
|
233
|
+
"detail": "Latest introspection did not return crm_customer.region_code.",
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Use `validation` for generic UI state that the component understands, and keep
|
|
239
|
+
app-specific semantics in `metadata`. For example, SQL expressions, lineage
|
|
240
|
+
attributes, ownership, or workflow IDs should live in `metadata`, not as
|
|
241
|
+
first-class component fields.
|
|
242
|
+
|
|
243
|
+
### Inline Column Editing
|
|
244
|
+
|
|
245
|
+
When `editable=True`, table nodes support lightweight inline schema editing:
|
|
246
|
+
|
|
247
|
+
- click a column name to edit `column["name"]`
|
|
248
|
+
- click a data type to edit `column["data_type"]`
|
|
249
|
+
- use the placeholder row at the bottom of a table to add a column row
|
|
250
|
+
- use the `×` control on a row to remove that column
|
|
251
|
+
|
|
252
|
+
Column ids remain stable and are not edited inline. If you delete a column, any
|
|
253
|
+
attached relationships are removed from the graph at the same time.
|
|
254
|
+
|
|
255
|
+
If you pass `column_type_options`, the inline data-type editor uses an in-node
|
|
256
|
+
combobox with suggested values while still allowing custom typed entries such as
|
|
257
|
+
`varchar(255)`.
|
|
258
|
+
|
|
259
|
+
### Common Recipes
|
|
260
|
+
|
|
261
|
+
Viewer-style canvas with dragging enabled but editing disabled:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
value = streamlit_schema_editor(
|
|
265
|
+
tables,
|
|
266
|
+
relationships,
|
|
267
|
+
editable=False,
|
|
268
|
+
draggable=True,
|
|
269
|
+
connectable=False,
|
|
270
|
+
deletable=False,
|
|
271
|
+
show_controls=True,
|
|
272
|
+
key="schema-viewer",
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Mapping editor with inline column editing and relationship inspection:
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
value = streamlit_schema_editor(
|
|
280
|
+
tables,
|
|
281
|
+
relationships,
|
|
282
|
+
editable=True,
|
|
283
|
+
show_edge_button=True,
|
|
284
|
+
show_controls=True,
|
|
285
|
+
key="mapping-editor",
|
|
286
|
+
)
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Toggle labeled source / target lanes on and off at runtime:
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
groups = [
|
|
293
|
+
{"id": "source", "label": "Source", "width": 420, "height": 640},
|
|
294
|
+
{"id": "target", "label": "Target", "width": 380, "height": 640},
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
tables = [
|
|
298
|
+
{**source_table, "group_id": "source"},
|
|
299
|
+
{**target_table, "group_id": "target"},
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
show_groups = st.toggle("Show groups", value=True)
|
|
303
|
+
|
|
304
|
+
value = streamlit_schema_editor(
|
|
305
|
+
tables,
|
|
306
|
+
relationships,
|
|
307
|
+
groups=groups,
|
|
308
|
+
show_groups=show_groups,
|
|
309
|
+
group_layout="columns",
|
|
310
|
+
group_order=["source", "target"],
|
|
311
|
+
table_layout_within_group="stack",
|
|
312
|
+
key="schema-editor-groups",
|
|
313
|
+
)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Hide validation visuals until the user opts in:
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
show_validation = st.toggle("Show validation", value=True)
|
|
320
|
+
|
|
321
|
+
value = streamlit_schema_editor(
|
|
322
|
+
tables,
|
|
323
|
+
relationships,
|
|
324
|
+
show_validation=show_validation,
|
|
325
|
+
key="schema-editor-validation-toggle",
|
|
326
|
+
)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Force validation visuals to refresh on demand:
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
if "validation_refresh_nonce" not in st.session_state:
|
|
333
|
+
st.session_state.validation_refresh_nonce = 0
|
|
334
|
+
|
|
335
|
+
if st.button("Refresh validation visuals"):
|
|
336
|
+
st.session_state.validation_refresh_nonce += 1
|
|
337
|
+
|
|
338
|
+
value = streamlit_schema_editor(
|
|
339
|
+
tables,
|
|
340
|
+
relationships,
|
|
341
|
+
show_validation=True,
|
|
342
|
+
validation_refresh_key=st.session_state.validation_refresh_nonce,
|
|
343
|
+
key="schema-editor-validation-refresh",
|
|
344
|
+
)
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Use known type options while still allowing custom values:
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
value = streamlit_schema_editor(
|
|
351
|
+
tables,
|
|
352
|
+
relationships,
|
|
353
|
+
editable=True,
|
|
354
|
+
column_type_options=[
|
|
355
|
+
"uuid",
|
|
356
|
+
"bigint",
|
|
357
|
+
"integer",
|
|
358
|
+
"varchar",
|
|
359
|
+
"text",
|
|
360
|
+
"timestamp",
|
|
361
|
+
"json",
|
|
362
|
+
],
|
|
363
|
+
key="schema-editor-type-options",
|
|
364
|
+
)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Handle inline column edit events in Streamlit:
|
|
368
|
+
|
|
369
|
+
```python
|
|
370
|
+
value = streamlit_schema_editor(
|
|
371
|
+
tables,
|
|
372
|
+
relationships,
|
|
373
|
+
editable=True,
|
|
374
|
+
key="schema-editor-events",
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
if value["event"] == "column_created":
|
|
378
|
+
st.success(f"Added column: {(value['event_context'] or {}).get('column_id')}")
|
|
379
|
+
|
|
380
|
+
if value["event"] == "column_updated":
|
|
381
|
+
context = value["event_context"] or {}
|
|
382
|
+
st.info(
|
|
383
|
+
"Updated "
|
|
384
|
+
f"{context.get('table_id')}.{context.get('column_id')} "
|
|
385
|
+
f"fields={context.get('fields')}"
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if value["event"] == "column_deleted":
|
|
389
|
+
context = value["event_context"] or {}
|
|
390
|
+
st.warning(
|
|
391
|
+
"Deleted "
|
|
392
|
+
f"{context.get('table_id')}.{context.get('column_id')} "
|
|
393
|
+
f"and removed relationships={context.get('deleted_relationship_ids')}"
|
|
394
|
+
)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Examples
|
|
398
|
+
|
|
399
|
+
Run the examples from the project root:
|
|
400
|
+
|
|
401
|
+
```sh
|
|
402
|
+
uv run streamlit run example.py
|
|
403
|
+
uv run streamlit run examples/playground.py
|
|
404
|
+
uv run streamlit run examples/schema_viewer.py
|
|
405
|
+
uv run streamlit run examples/er_diagram.py
|
|
406
|
+
uv run streamlit run examples/databricks_mapping.py
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
- `example.py`: full source-to-target mapping demo
|
|
410
|
+
- `examples/playground.py`: interactive playground for toggling runtime options, grouping visibility, and validation live
|
|
411
|
+
- `examples/schema_viewer.py`: read-only schema browser
|
|
412
|
+
- `examples/er_diagram.py`: ER-style relationship view with arrowheads hidden
|
|
413
|
+
- `examples/databricks_mapping.py`: Databricks-inspired source-to-target mapping demo with labeled group lanes
|
|
414
|
+
|
|
415
|
+
## Development
|
|
416
|
+
|
|
417
|
+
Build the frontend first, then build the Python package:
|
|
418
|
+
|
|
419
|
+
```sh
|
|
420
|
+
cd streamlit_schema_editor/frontend
|
|
421
|
+
npm install
|
|
422
|
+
npm run build
|
|
423
|
+
cd ../..
|
|
424
|
+
uv build
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## Testing
|
|
428
|
+
|
|
429
|
+
Run the Python tests:
|
|
430
|
+
|
|
431
|
+
```sh
|
|
432
|
+
uv run pytest
|
|
433
|
+
```
|