anywidget-graph 0.4.1__tar.gz → 0.4.2__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.
Files changed (57) hide show
  1. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/CHANGELOG.md +23 -0
  2. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/PKG-INFO +26 -1
  3. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/README.md +25 -0
  4. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/pyproject.toml +1 -1
  5. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/__init__.py +1 -1
  6. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/__init__.py +4 -0
  7. anywidget_graph-0.4.2/src/anywidget_graph/ui/defaults.js +75 -0
  8. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/index.js +369 -36
  9. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/lanes.js +177 -5
  10. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/schema.js +3 -2
  11. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/widget.py +3 -0
  12. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/js/lanes.test.mjs +155 -5
  13. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/test_graph.py +7 -0
  14. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/test_ui.py +23 -0
  15. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/uv.lock +1 -1
  16. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/.github/workflows/ci.yml +0 -0
  17. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/.github/workflows/grafeo-latest.yml +0 -0
  18. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/.github/workflows/pypi.yml +0 -0
  19. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/.gitignore +0 -0
  20. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/.python-version +0 -0
  21. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/LICENSE +0 -0
  22. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/assets/demo.png +0 -0
  23. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/examples/demo.py +0 -0
  24. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/examples/test_hierarchy.py +0 -0
  25. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/examples/test_large_graph.py +0 -0
  26. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/__init__.py +0 -0
  27. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/arango.py +0 -0
  28. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/cosmosdb.py +0 -0
  29. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/grafeo.py +0 -0
  30. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/ladybug.py +0 -0
  31. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/backends/neo4j.py +0 -0
  32. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/__init__.py +0 -0
  33. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/base.py +0 -0
  34. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/common.py +0 -0
  35. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/cypher.py +0 -0
  36. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/gql.py +0 -0
  37. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/graphql.py +0 -0
  38. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/gremlin.py +0 -0
  39. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/converters/sparql.py +0 -0
  40. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/demo.py +0 -0
  41. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/py.typed +0 -0
  42. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/grafeo-embed.js +0 -0
  43. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/grafeo-result.js +0 -0
  44. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/grafeo.js +0 -0
  45. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/icons.js +0 -0
  46. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/neo4j.js +0 -0
  47. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/properties.js +0 -0
  48. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/results.js +0 -0
  49. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/settings.js +0 -0
  50. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/styles.css +0 -0
  51. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/src/anywidget_graph/ui/toolbar.js +0 -0
  52. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/__init__.py +0 -0
  53. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/conftest.py +0 -0
  54. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/js/grafeo-result.test.mjs +0 -0
  55. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/test_backends_grafeo.py +0 -0
  56. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/test_demo.py +0 -0
  57. {anywidget_graph-0.4.1 → anywidget_graph-0.4.2}/tests/test_grafeo_contract.py +0 -0
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.2 2026-10-09
4
+
5
+ ### Features
6
+
7
+ All opt-in per lane: a lane without these options is laid out exactly as in 0.4.1.
8
+
9
+ - **Force layout for a lane**: `layout: "force"` lays the lane out with forces inside its band (repulsion, collision, edges as springs, a pull towards the height of its partners in the lanes to its left), so a dense graph spreads over its lane instead of shrinking into a core; nodes never leave their lane, a crowded lane draws its nodes smaller, and the layout is deterministic. Appended nodes settle from where they land while older nodes stay put
10
+ - **Rows in a lane**: `rows: {"field": ..., "order": [...]}` splits a lane into rows top to bottom by a node field (sized by node count, with a minimum; other values go to an extra row at the bottom), with each row's name (with room of its own, no node under it) and a faint separator
11
+ - **Lay a lane out again on a big batch**: `relayout: <share>` lays the lane out again when one batch removes or adds more than that share of its nodes (a re-sample); its nodes glide to their new places
12
+ - **Colours per type**: `type_colors` (`{"nodes": {"<type>": "#hex"}, "edges": {"<type>": "#hex"}}`) colours the items of a type and their legend swatches, so the legend matches the drawing; an item's own `color` still wins
13
+ - **Labels for a lane's largest nodes**: `labels: {"count": n}` shows the labels of a lane's n largest nodes at the default zoom, and no others in that lane (hover or select a node to see its label). A label may cover smaller nodes on a soft backing, never a node as large, a labelled node or another label; it goes on the left of its node at the lane's right edge (never over a row's name), and a long name is shortened with an ellipsis
14
+ - **Defaults for hosts without Python**: a setting an app's own model leaves out reads as the widget's default (as a fresh Python `Graph()` has it), so for example labels show without the host sending `show_labels`
15
+ - `_features` adds `force_layout`, `lane_rows`, `relayout`, `type_colors` and `lane_labels`
16
+
17
+ ### Documentation
18
+
19
+ - README: "Embedding in an app": the widget stays generic and the host decides looks, samples and lane layouts (all opt-in); the model contract for an app that mounts the front end without Python
20
+
21
+ ### Bug Fixes
22
+
23
+ - Fixed the zoom controls covering the caption of an action lane at the right edge on a narrow canvas: when the rightmost lane is an action lane, the framing leaves room for the controls
24
+ - Fixed the empty canvas mapping lanes with the y axis pointing down while sigma points it up (nothing visible depended on it before rows)
25
+
3
26
  ## 0.4.1 2026-10-09
4
27
 
5
28
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: anywidget-graph
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: Interactive graph visualization for Python notebooks using anywidget
5
5
  Project-URL: Homepage, https://grafeo.dev/
6
6
  Project-URL: Repository, https://github.com/GrafeoDB/anywidget-graph
@@ -253,8 +253,14 @@ graph.theme = {
253
253
  - `append(nodes, edges)`: adds items without a re-layout; existing nodes keep their positions, a new node is placed at the height of its neighbours in other lanes, where nodes of the same neighbours stack as rows one label line apart with their labels shown (or next to its neighbours in its lane; without lanes, next to its neighbours inside the area already drawn). The items are also merged into `nodes` and `edges`, so `to_json()`, `to_html()` and a widget shown again include them (a host that sets `append_batch` itself gets the merged lists written back by the widget). Changing `lanes` or a style keeps appended items; setting `nodes` or `edges` to other data replaces everything drawn. New items fade in and edges draw from source to target, `append_stagger_ms` (default 60) apart; a large batch is kept under 1.5 s. `append_animation="none"` turns the animation off.
254
254
  - `remove(nodes, edges)`: takes nodes (with every edge touching them) and edges out without a re-layout; they fade out and leave `nodes` and `edges`. An edge is `{"source", "target"}` (every edge between those ends) or with a `"label"` (that edge only). A host that sets `append_batch` itself puts them under `remove`: `{"seq": n, "nodes": [...], "edges": [...], "remove": {"nodes": [ids], "edges": [...]}}`; removals come first, so one batch can re-sample (an item removed and sent again stays where it is).
255
255
  - Lane options: `width` is a lane's share of the standard width (default 1); `arrange: "column"` stacks the lane's nodes in the middle of its band (for a few roots, such as repositories, whose edges then fan out into the next lane); `action: True` makes a lane without nodes that shows a button, with the host's `icon` (SVG markup, where `currentColor` takes the accent colour, or an image URL), or `glyph` (a list of colours drawn as stacked bars), else a neutral "open" icon; `caption` is text under it. An edge whose end is an action lane's id (and no node) is drawn as a curve into its button, like the cross-lane curves. The camera frames all lanes, and the lanes stretch to the canvas's shape.
256
+ - Lane layout options (all off by default; a lane without them is laid out as before):
257
+ - `layout: "force"`: a force layout inside the lane: nodes repel and do not overlap, edges inside the lane are springs, and a node is pulled towards the height of its partners in the lanes to its left. Nodes never leave their lane. A crowded lane draws its nodes smaller so the layout has room. Appended nodes settle from where they land while the older nodes stay put. Deterministic: the same data gives the same picture.
258
+ - `rows: {"field": "layer", "order": ["business", "application", "technology"]}`: splits the lane into rows, top to bottom, by a node field; a node with another value (or none) goes to an extra row at the bottom. Row heights follow the node counts (with a minimum), each row shows its name and a faint separator, and nodes stay inside their row. Implies `layout: "force"`.
259
+ - `labels: {"count": 12}`: at the default zoom, shows the labels of the lane's 12 largest nodes (by size, then degree), and no others (hover or select a node to see its label; zoomed in or out, sigma's label grid decides). A label may cover smaller nodes, on a soft backing so it stays readable, but never a node as large, a labelled node or another label; one that would cross the lane's right edge goes on the left of its node, and a long name is shortened with an ellipsis (hover shows it whole).
260
+ - `relayout: 0.33`: when one batch removes or adds more than this share of the lane's nodes (a re-sample), the lane is laid out again as a whole and its nodes glide to their new places; smaller batches keep older nodes where they are.
256
261
  - `lane_action`: set to `{"lane": <id>, "seq": <n>}` when an action lane's glyph is clicked, so the host can respond (open a panel, switch a view). `seq` counts on from the value the model holds, so every click is a change, also in a widget shown twice.
257
262
  - `pulse_nodes`: ids that pulse softly.
263
+ - `type_colors`: colours per type, `{"nodes": {"<type>": "#hex"}, "edges": {"<type>": "#hex"}}` (types as the schema panel groups them). They colour the items of that type and the legend swatches; an item's own `color` still wins, and other types keep the palette.
258
264
  - `totals`: for a host that sends a sample of a larger graph, `{"nodes": {"<type>": n}, "edges": {"<type>": n}}` (types as the schema panel groups them: a node's first label, an edge's type). The count badge and the schema panel show "shown / total"; a type the sample lacks shows as 0, a type without a total shows its count (and counts as shown in full).
259
265
  - `theme`: host colours (`background`, `panel`, `text`, `muted`, `border`, `accent`) that replace the widget's light and dark defaults; a key left out (or `theme = {}`) brings the default back.
260
266
  - `_features`: the widget sets the capabilities it supports when it renders, so a host can detect an older build.
@@ -366,6 +372,25 @@ graph = Graph(
366
372
  graph = Graph(backend=my_backend) # Any object implementing DatabaseBackend protocol
367
373
  ```
368
374
 
375
+ ## Embedding in an app
376
+
377
+ The widget knows graphs, not your domain. The host (a notebook, or an app that mounts the widget's front end) decides how its data looks and behaves, and the widget's own defaults stay neutral:
378
+
379
+ - **Looks**: colours per type (`type_colors`), the host's colours (`theme`), action lane icons (`icon`, `glyph`), lane titles and captions.
380
+ - **What to show**: when a graph is too large to draw whole, the host picks the sample and sends it, with the totals behind it (`totals`); the widget never makes counts up. `append()` and `remove()` keep the drawing up to date without a re-layout.
381
+ - **Layout per lane**: `width`, `arrange`, `layout`, `rows`, `relayout` and `labels` are lane options the host sets.
382
+
383
+ New behaviour is opt-in and off by default: an update does not change a view that does not ask for it. `_features` lists what a build supports, so a host can tell an older build apart and fall back.
384
+
385
+ ### Without Python
386
+
387
+ An app can mount the bundled front end (`anywidget_graph.ui.get_esm()` and `get_css()`) with any model object that has `get`, `set`, `on`, `off`, `save_changes` and `send`, as anywidget's model does.
388
+
389
+ - The host sets `nodes`, `edges`, `lanes`, `append_batch`, `pulse_nodes`, `theme`, `type_colors`, `totals`, `dark_mode`, and the size (`width` and `height`, or `fill`).
390
+ - The widget sets `selected_node`, `selected_nodes`, `selected_edge`, `hovered_node`, `lane_action` and `_features`. After an `append_batch` it writes the merged `nodes` and `edges` back, unless they already hold the batch (as `Graph.append()` sends them).
391
+ - A setting the host leaves out reads as the widget's default, the same as a fresh Python `Graph()` has it.
392
+ - The widget sends no query when it mounts: the first view is the host's.
393
+
369
394
  ## Export
370
395
 
371
396
  ```python
@@ -212,8 +212,14 @@ graph.theme = {
212
212
  - `append(nodes, edges)`: adds items without a re-layout; existing nodes keep their positions, a new node is placed at the height of its neighbours in other lanes, where nodes of the same neighbours stack as rows one label line apart with their labels shown (or next to its neighbours in its lane; without lanes, next to its neighbours inside the area already drawn). The items are also merged into `nodes` and `edges`, so `to_json()`, `to_html()` and a widget shown again include them (a host that sets `append_batch` itself gets the merged lists written back by the widget). Changing `lanes` or a style keeps appended items; setting `nodes` or `edges` to other data replaces everything drawn. New items fade in and edges draw from source to target, `append_stagger_ms` (default 60) apart; a large batch is kept under 1.5 s. `append_animation="none"` turns the animation off.
213
213
  - `remove(nodes, edges)`: takes nodes (with every edge touching them) and edges out without a re-layout; they fade out and leave `nodes` and `edges`. An edge is `{"source", "target"}` (every edge between those ends) or with a `"label"` (that edge only). A host that sets `append_batch` itself puts them under `remove`: `{"seq": n, "nodes": [...], "edges": [...], "remove": {"nodes": [ids], "edges": [...]}}`; removals come first, so one batch can re-sample (an item removed and sent again stays where it is).
214
214
  - Lane options: `width` is a lane's share of the standard width (default 1); `arrange: "column"` stacks the lane's nodes in the middle of its band (for a few roots, such as repositories, whose edges then fan out into the next lane); `action: True` makes a lane without nodes that shows a button, with the host's `icon` (SVG markup, where `currentColor` takes the accent colour, or an image URL), or `glyph` (a list of colours drawn as stacked bars), else a neutral "open" icon; `caption` is text under it. An edge whose end is an action lane's id (and no node) is drawn as a curve into its button, like the cross-lane curves. The camera frames all lanes, and the lanes stretch to the canvas's shape.
215
+ - Lane layout options (all off by default; a lane without them is laid out as before):
216
+ - `layout: "force"`: a force layout inside the lane: nodes repel and do not overlap, edges inside the lane are springs, and a node is pulled towards the height of its partners in the lanes to its left. Nodes never leave their lane. A crowded lane draws its nodes smaller so the layout has room. Appended nodes settle from where they land while the older nodes stay put. Deterministic: the same data gives the same picture.
217
+ - `rows: {"field": "layer", "order": ["business", "application", "technology"]}`: splits the lane into rows, top to bottom, by a node field; a node with another value (or none) goes to an extra row at the bottom. Row heights follow the node counts (with a minimum), each row shows its name and a faint separator, and nodes stay inside their row. Implies `layout: "force"`.
218
+ - `labels: {"count": 12}`: at the default zoom, shows the labels of the lane's 12 largest nodes (by size, then degree), and no others (hover or select a node to see its label; zoomed in or out, sigma's label grid decides). A label may cover smaller nodes, on a soft backing so it stays readable, but never a node as large, a labelled node or another label; one that would cross the lane's right edge goes on the left of its node, and a long name is shortened with an ellipsis (hover shows it whole).
219
+ - `relayout: 0.33`: when one batch removes or adds more than this share of the lane's nodes (a re-sample), the lane is laid out again as a whole and its nodes glide to their new places; smaller batches keep older nodes where they are.
215
220
  - `lane_action`: set to `{"lane": <id>, "seq": <n>}` when an action lane's glyph is clicked, so the host can respond (open a panel, switch a view). `seq` counts on from the value the model holds, so every click is a change, also in a widget shown twice.
216
221
  - `pulse_nodes`: ids that pulse softly.
222
+ - `type_colors`: colours per type, `{"nodes": {"<type>": "#hex"}, "edges": {"<type>": "#hex"}}` (types as the schema panel groups them). They colour the items of that type and the legend swatches; an item's own `color` still wins, and other types keep the palette.
217
223
  - `totals`: for a host that sends a sample of a larger graph, `{"nodes": {"<type>": n}, "edges": {"<type>": n}}` (types as the schema panel groups them: a node's first label, an edge's type). The count badge and the schema panel show "shown / total"; a type the sample lacks shows as 0, a type without a total shows its count (and counts as shown in full).
218
224
  - `theme`: host colours (`background`, `panel`, `text`, `muted`, `border`, `accent`) that replace the widget's light and dark defaults; a key left out (or `theme = {}`) brings the default back.
219
225
  - `_features`: the widget sets the capabilities it supports when it renders, so a host can detect an older build.
@@ -325,6 +331,25 @@ graph = Graph(
325
331
  graph = Graph(backend=my_backend) # Any object implementing DatabaseBackend protocol
326
332
  ```
327
333
 
334
+ ## Embedding in an app
335
+
336
+ The widget knows graphs, not your domain. The host (a notebook, or an app that mounts the widget's front end) decides how its data looks and behaves, and the widget's own defaults stay neutral:
337
+
338
+ - **Looks**: colours per type (`type_colors`), the host's colours (`theme`), action lane icons (`icon`, `glyph`), lane titles and captions.
339
+ - **What to show**: when a graph is too large to draw whole, the host picks the sample and sends it, with the totals behind it (`totals`); the widget never makes counts up. `append()` and `remove()` keep the drawing up to date without a re-layout.
340
+ - **Layout per lane**: `width`, `arrange`, `layout`, `rows`, `relayout` and `labels` are lane options the host sets.
341
+
342
+ New behaviour is opt-in and off by default: an update does not change a view that does not ask for it. `_features` lists what a build supports, so a host can tell an older build apart and fall back.
343
+
344
+ ### Without Python
345
+
346
+ An app can mount the bundled front end (`anywidget_graph.ui.get_esm()` and `get_css()`) with any model object that has `get`, `set`, `on`, `off`, `save_changes` and `send`, as anywidget's model does.
347
+
348
+ - The host sets `nodes`, `edges`, `lanes`, `append_batch`, `pulse_nodes`, `theme`, `type_colors`, `totals`, `dark_mode`, and the size (`width` and `height`, or `fill`).
349
+ - The widget sets `selected_node`, `selected_nodes`, `selected_edge`, `hovered_node`, `lane_action` and `_features`. After an `append_batch` it writes the merged `nodes` and `edges` back, unless they already hold the batch (as `Graph.append()` sends them).
350
+ - A setting the host leaves out reads as the widget's default, the same as a fresh Python `Graph()` has it.
351
+ - The widget sends no query when it mounts: the first view is the host's.
352
+
328
353
  ## Export
329
354
 
330
355
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anywidget-graph"
3
- version = "0.4.1"
3
+ version = "0.4.2"
4
4
  description = "Interactive graph visualization for Python notebooks using anywidget"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -3,4 +3,4 @@
3
3
  from anywidget_graph.widget import Graph
4
4
 
5
5
  __all__ = ["Graph"]
6
- __version__ = "0.4.1"
6
+ __version__ = "0.4.2"
@@ -93,6 +93,7 @@ def _resolve_schema_imports(code: str) -> str:
93
93
 
94
94
  def get_esm() -> str:
95
95
  """Get aggregated ESM JavaScript."""
96
+ defaults_js = _read_file(_UI_DIR / "defaults.js")
96
97
  icons_js = _read_file(_UI_DIR / "icons.js")
97
98
  neo4j_js = _read_file(_UI_DIR / "neo4j.js")
98
99
  grafeo_result_js = _read_file(_UI_DIR / "grafeo-result.js")
@@ -116,6 +117,9 @@ import forceAtlas2 from "https://esm.sh/graphology-layout-forceatlas2@0.10.1";
116
117
  import * as d3Force from "https://esm.sh/d3-force@3.0.0";
117
118
  import neo4j from "https://cdn.jsdelivr.net/npm/neo4j-driver@5.28.0/lib/browser/neo4j-web.esm.min.js";
118
119
 
120
+ // === Defaults (for a host without Python) ===
121
+ {_strip_imports_exports(defaults_js)}
122
+
119
123
  // === Icons ===
120
124
  {_strip_imports_exports(icons_js)}
121
125
 
@@ -0,0 +1,75 @@
1
+ // The widget's settings as a fresh Python Graph() has them. A host without Python (an app's own model) may leave
2
+ // settings out; they then read as these. tests/test_ui.py keeps this equal to the Python trait defaults.
3
+ export const TRAIT_DEFAULTS = {
4
+ "_demo_data": "",
5
+ "_demo_mode": false,
6
+ "_execute_query": 0,
7
+ "_expand_request": {},
8
+ "_features": [],
9
+ "append_animation": "draw",
10
+ "append_batch": {},
11
+ "append_stagger_ms": 60,
12
+ "background": "#fafafa",
13
+ "color_domain": null,
14
+ "color_field": null,
15
+ "color_scale": "viridis",
16
+ "connection_database": "default",
17
+ "connection_password": "",
18
+ "connection_status": "disconnected",
19
+ "connection_uri": "",
20
+ "connection_username": "",
21
+ "dark_mode": true,
22
+ "database_backend": "grafeo",
23
+ "edge_color_field": null,
24
+ "edge_color_scale": "viridis",
25
+ "edge_size_field": null,
26
+ "edge_size_range": [
27
+ 1,
28
+ 8
29
+ ],
30
+ "edges": [],
31
+ "fill": false,
32
+ "grafeo_connection_mode": "embedded",
33
+ "grafeo_server_url": "http://localhost:7474",
34
+ "height": 600,
35
+ "hovered_edge": null,
36
+ "hovered_node": null,
37
+ "lane_action": {},
38
+ "lanes": [],
39
+ "layout": "spring",
40
+ "max_nodes": 300,
41
+ "nodes": [],
42
+ "pinned_nodes": {},
43
+ "pulse_nodes": [],
44
+ "query": "",
45
+ "query_error": "",
46
+ "query_language": "cypher",
47
+ "query_running": false,
48
+ "query_time": 0.0,
49
+ "schema_edge_types": [],
50
+ "schema_node_types": [],
51
+ "selected_edge": null,
52
+ "selected_edges": [],
53
+ "selected_node": null,
54
+ "selected_nodes": [],
55
+ "selection_mode": "click",
56
+ "show_edge_labels": false,
57
+ "show_labels": true,
58
+ "show_query_input": true,
59
+ "show_settings": true,
60
+ "show_toolbar": true,
61
+ "show_tooltip": true,
62
+ "size_field": null,
63
+ "size_range": [
64
+ 5,
65
+ 30
66
+ ],
67
+ "theme": {},
68
+ "tooltip_fields": [
69
+ "label",
70
+ "id"
71
+ ],
72
+ "totals": {},
73
+ "type_colors": {},
74
+ "width": 800
75
+ };