anywidget-graph 0.2.8__tar.gz → 0.2.10__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 (46) hide show
  1. anywidget_graph-0.2.10/CHANGELOG.md +127 -0
  2. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/PKG-INFO +2 -2
  3. anywidget_graph-0.2.10/examples/test_hierarchy.py +99 -0
  4. anywidget_graph-0.2.10/examples/test_large_graph.py +91 -0
  5. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/pyproject.toml +2 -2
  6. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/__init__.py +1 -0
  7. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/index.js +430 -66
  8. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/properties.js +5 -4
  9. anywidget_graph-0.2.10/src/anywidget_graph/ui/schema.js +455 -0
  10. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/styles.css +83 -1
  11. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/widget.py +2 -2
  12. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/tests/test_graph.py +1 -1
  13. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/uv.lock +2 -2
  14. anywidget_graph-0.2.8/src/anywidget_graph/ui/schema.js +0 -239
  15. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/.github/workflows/ci.yml +0 -0
  16. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/.github/workflows/pypi.yml +0 -0
  17. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/.gitignore +0 -0
  18. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/.python-version +0 -0
  19. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/LICENSE +0 -0
  20. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/README.md +0 -0
  21. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/examples/demo.py +0 -0
  22. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/__init__.py +0 -0
  23. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/__init__.py +0 -0
  24. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/arango.py +0 -0
  25. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/cosmosdb.py +0 -0
  26. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/grafeo.py +0 -0
  27. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/ladybug.py +0 -0
  28. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/backends/neo4j.py +0 -0
  29. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/__init__.py +0 -0
  30. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/base.py +0 -0
  31. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/common.py +0 -0
  32. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/cypher.py +0 -0
  33. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/gql.py +0 -0
  34. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/graphql.py +0 -0
  35. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/gremlin.py +0 -0
  36. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/converters/sparql.py +0 -0
  37. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/demo.py +0 -0
  38. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/py.typed +0 -0
  39. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/grafeo-embed.js +0 -0
  40. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/grafeo.js +0 -0
  41. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/icons.js +0 -0
  42. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/neo4j.js +0 -0
  43. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/results.js +0 -0
  44. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/settings.js +0 -0
  45. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/src/anywidget_graph/ui/toolbar.js +0 -0
  46. {anywidget_graph-0.2.8 → anywidget_graph-0.2.10}/tests/__init__.py +0 -0
@@ -0,0 +1,127 @@
1
+ # Changelog
2
+
3
+ ## 0.2.10
4
+
5
+ ### New Features
6
+
7
+ - **Spring layout (d3-force)**: New default layout using d3-force with charge, link-distance and collision forces; produces clean hub-spoke rings for hierarchical data like Neo4j browser
8
+ - **Degree-based node sizing**: When no explicit `size_field` is set, nodes are sized by their connection count; hub nodes appear larger and push neighbors into natural rings
9
+ - **Filter-aware layouts**: Switching layout or clicking refresh now re-runs the layout using only visible nodes, respecting type filters and search state
10
+
11
+ ### Improvements
12
+
13
+ - Spring layout is the new default (renamed to "Default" in the dropdown)
14
+ - Force layout uses `linLogMode` for better community separation
15
+ - Cluster layout filters out hidden node types before packing
16
+ - Filter panel refresh button triggers a layout refresh
17
+
18
+ ## 0.2.9
19
+
20
+ ### New Features
21
+
22
+ - **Filter panel**: Schema sidebar transformed into a filter panel with per-type checkboxes, counts, color pickers and "all / none" toggle links for both node and edge types
23
+ - **Type-based node coloring**: Nodes automatically colored by their type (first label), matching the filter panel swatches
24
+ - **Color pickers**: Click any type's color swatch in the filter panel to change it; graph updates in real-time
25
+ - **Level-of-detail labels**: Labels auto-hide on small or zoomed-out nodes; thresholds scale with graph size
26
+ - **Circle-packing cluster layout**: Deterministic circle packing with radius proportional to `sqrt(nodeCount)` and mini-force within each cluster for organic internal structure
27
+ - **Edge interaction**: Edges are now clickable with type shown in the properties panel
28
+
29
+ ### Bug Fixes
30
+
31
+ - Fixed widget height growing on scroll; wrapper now locked to fixed height
32
+ - Fixed search filtering: now shows matching nodes plus first-degree neighbors and connecting edges, hides everything else
33
+ - Fixed parallel edge crash by using Graphology multi-graph mode
34
+ - Fixed graph disappearing when side panels open by refreshing renderer on resize
35
+ - Fixed dark mode label color: adapts to current theme
36
+
37
+ ### Improvements
38
+
39
+ - Zoom controls reordered: fit, zoom-in, zoom-out at top; layout and mode at bottom
40
+ - Edge type shown in properties panel on click
41
+ - Internal attributes (`nodeType`, `edgeType`) hidden from properties panel
42
+
43
+ ## 0.2.8
44
+
45
+ ### New Features
46
+
47
+ - **Language-aware query dispatch**: GrafeoBackend routes queries to language-specific methods (`execute_cypher`, `execute_gremlin`, etc.) with fallback to generic `execute()`
48
+ - **Results drawer**: Bottom drawer with tabbed nodes/edges tables, pagination and query timing
49
+ - **Grafeo native dict support**: Detection and conversion of Grafeo's internal `_id`/`_labels`/`_source`/`_target`/`_type` dict format so native query results render correctly
50
+
51
+ ### Improvements
52
+
53
+ - Added playground link at grafeo.ai to README
54
+
55
+ ## 0.2.7
56
+
57
+ ### Bug Fixes
58
+
59
+ - Fixed lasso/box selection behavior
60
+
61
+ ### Improvements
62
+
63
+ - Quality-of-life improvements to search and toolbar
64
+
65
+ ## 0.2.6
66
+
67
+ ### New Features
68
+
69
+ - **Browser-side backends**: Added Grafeo server (HTTP), Grafeo WASM (browser-embedded) and Neo4j browser-side driver support
70
+ - **Settings panel**: Full connection UI with backend selector, connection mode, language picker and connect/disconnect
71
+ - **CosmosDB backend**: Added Azure CosmosDB Gremlin backend
72
+ - **Demo mode**: WASM-based demo with pre-populated dataset
73
+ - **Schema browser**: Sidebar with node labels and relationship types; click to query
74
+ - **Properties panel**: Right sidebar showing selected node/edge details
75
+ - **Zoom and pan controls**: Zoom in/out/fit buttons, box select mode, click select mode
76
+
77
+ ### Improvements
78
+
79
+ - Updated docs, tests and dependencies
80
+ - HTML export template fix
81
+ - Zoom controls added to graph container
82
+
83
+ ## 0.2.5
84
+
85
+ ### New Features
86
+
87
+ - **Major UI overhaul**: Modular component architecture with toolbar, schema panel, settings, properties and results drawer
88
+ - **Multiple backends**: Grafeo, Neo4j, LadybugDB, ArangoDB and CosmosDB support
89
+ - **Test suite**: Comprehensive test coverage with 55+ tests
90
+ - **Node interaction**: Click, drag, pin, double-click expand, delete and multi-select
91
+ - **Force layout**: ForceAtlas2 with dynamic parameters scaling by graph size
92
+ - **Keyboard shortcuts**: F to fit, Escape to deselect, Delete to remove selected
93
+
94
+ ### Improvements
95
+
96
+ - Consistent code formatting with ruff
97
+
98
+ ## 0.2.4
99
+
100
+ ### Bug Fixes
101
+
102
+ - Fixed ESM bundling issue in UI init
103
+
104
+ ## 0.2.3
105
+
106
+ ### Bug Fixes
107
+
108
+ - Fixed rendering issue in graph container
109
+ - Updated dependencies
110
+
111
+ ## 0.2.2
112
+
113
+ ### Improvements
114
+
115
+ - Added Apache 2.0 license
116
+ - ESM bundle aggregation for widget JS
117
+
118
+ ## 0.2.1
119
+
120
+ ### Improvements
121
+
122
+ - Updated README with usage examples
123
+ - Code formatting pass
124
+
125
+ ## 0.1.0
126
+
127
+ - Initial release of anywidget-graph
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anywidget-graph
3
- Version: 0.2.8
3
+ Version: 0.2.10
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
@@ -29,7 +29,7 @@ Requires-Dist: marimo>=0.20; extra == 'dev'
29
29
  Requires-Dist: prek>=0.3; extra == 'dev'
30
30
  Requires-Dist: pytest>=9.0.2; extra == 'dev'
31
31
  Requires-Dist: ruff>=0.15; extra == 'dev'
32
- Requires-Dist: ty>=0.0.20; extra == 'dev'
32
+ Requires-Dist: ty>=0.0; extra == 'dev'
33
33
  Provides-Extra: embed
34
34
  Provides-Extra: grafeo
35
35
  Requires-Dist: grafeo>=0.5; extra == 'grafeo'
@@ -0,0 +1,99 @@
1
+ """Marimo demo: hierarchical repo-like graph to test force layout with hub-spoke data."""
2
+
3
+ import marimo
4
+
5
+ __generated_with = "0.19.5"
6
+ app = marimo.App(width="medium")
7
+
8
+
9
+ @app.cell
10
+ def _():
11
+ import marimo as mo
12
+
13
+ return (mo,)
14
+
15
+
16
+ @app.cell
17
+ def _():
18
+ from anywidget_graph import Graph
19
+
20
+ return (Graph,)
21
+
22
+
23
+ @app.cell
24
+ def _(Graph):
25
+ # Simulate a repository structure:
26
+ # 1 Repository -> 4 Directories -> Files -> TypeDefinitions
27
+ nodes = [
28
+ {"id": "repo", "labels": ["Repository"], "name": "my-app"},
29
+ ]
30
+ edges = []
31
+
32
+ dirs = ["src", "lib", "tests", "config"]
33
+ file_id = 0
34
+ type_id = 0
35
+
36
+ for d in dirs:
37
+ did = f"dir_{d}"
38
+ nodes.append({"id": did, "labels": ["Directory"], "name": d})
39
+ edges.append({"source": "repo", "target": did, "label": "CONTAINS", "type": "CONTAINS"})
40
+
41
+ # Each directory has 3-8 files
42
+ import random
43
+
44
+ random.seed(hash(d))
45
+ n_files = random.randint(3, 8)
46
+ for i in range(n_files):
47
+ fid = f"file_{file_id}"
48
+ fname = f"{d}/file_{i}.py"
49
+ nodes.append({"id": fid, "labels": ["File"], "name": fname})
50
+ edges.append({"source": did, "target": fid, "label": "CONTAINS", "type": "CONTAINS"})
51
+ file_id += 1
52
+
53
+ # Each file has 1-5 type definitions
54
+ n_types = random.randint(1, 5)
55
+ for _j in range(n_types):
56
+ tid = f"type_{type_id}"
57
+ tname = f"Class_{type_id}"
58
+ nodes.append({"id": tid, "labels": ["TypeDefinition"], "name": tname})
59
+ edges.append({"source": fid, "target": tid, "label": "DEFINES", "type": "DEFINES"})
60
+ type_id += 1
61
+
62
+ # Some types depend on other types (cross-file references)
63
+ if type_id > 3 and random.random() < 0.3:
64
+ dep = f"type_{random.randint(0, type_id - 2)}"
65
+ edges.append({"source": tid, "target": dep, "label": "DEPENDS_ON", "type": "DEPENDS_ON"})
66
+
67
+ # Some cross-directory imports
68
+ if d != "src":
69
+ edges.append(
70
+ {
71
+ "source": did,
72
+ "target": "dir_src",
73
+ "label": "IMPORTS_FROM",
74
+ "type": "IMPORTS_FROM",
75
+ }
76
+ )
77
+
78
+ graph = Graph.from_dict(
79
+ {"nodes": nodes, "edges": edges},
80
+ width=1000,
81
+ height=700,
82
+ )
83
+
84
+ graph # noqa: B018
85
+ return (graph,)
86
+
87
+
88
+ @app.cell
89
+ def _(graph, mo):
90
+ mo.md(f"""
91
+ **Selected node:** {graph.selected_node}
92
+
93
+ Hierarchical repo graph: Repository -> Directories -> Files -> TypeDefinitions
94
+ """)
95
+ return ()
96
+
97
+
98
+ if __name__ == "__main__":
99
+ app.run()
@@ -0,0 +1,91 @@
1
+ """Marimo demo: large graph to test filtering, search, and LOD."""
2
+
3
+ import marimo
4
+
5
+ __generated_with = "0.19.5"
6
+ app = marimo.App(width="medium")
7
+
8
+
9
+ @app.cell
10
+ def _():
11
+ import marimo as mo
12
+
13
+ return (mo,)
14
+
15
+
16
+ @app.cell
17
+ def _():
18
+ from anywidget_graph import Graph
19
+
20
+ return (Graph,)
21
+
22
+
23
+ @app.cell
24
+ def _(Graph):
25
+ import random
26
+
27
+ random.seed(42)
28
+
29
+ node_types = {
30
+ "Person": 40,
31
+ "Company": 15,
32
+ "Product": 25,
33
+ "City": 10,
34
+ "Skill": 20,
35
+ }
36
+
37
+ edge_types = ["WORKS_AT", "LIVES_IN", "KNOWS", "MANAGES", "USES", "SELLS", "HAS_SKILL"]
38
+
39
+ nodes = []
40
+ node_ids = []
41
+ for label, count in node_types.items():
42
+ for i in range(count):
43
+ nid = f"{label.lower()}_{i}"
44
+ node_ids.append(nid)
45
+ nodes.append(
46
+ {
47
+ "id": nid,
48
+ "labels": [label],
49
+ "name": f"{label} {i}",
50
+ }
51
+ )
52
+
53
+ edges = []
54
+ for _ in range(250):
55
+ src = random.choice(node_ids)
56
+ tgt = random.choice(node_ids)
57
+ if src != tgt:
58
+ edges.append(
59
+ {
60
+ "source": src,
61
+ "target": tgt,
62
+ "label": random.choice(edge_types),
63
+ "type": random.choice(edge_types),
64
+ }
65
+ )
66
+
67
+ graph = Graph.from_dict(
68
+ {"nodes": nodes, "edges": edges},
69
+ width=1000,
70
+ height=700,
71
+ )
72
+
73
+ graph # noqa: B018
74
+ return (graph,)
75
+
76
+
77
+ @app.cell
78
+ def _(graph, mo):
79
+ mo.md(f"""
80
+ **Selected node:** {graph.selected_node}
81
+
82
+ Try:
83
+ - Open the Filter panel (sidebar button) to toggle node/edge types
84
+ - Use the search bar to filter (e.g. type "Person" or "Company")
85
+ - Zoom in/out to see label LOD
86
+ """)
87
+ return ()
88
+
89
+
90
+ if __name__ == "__main__":
91
+ app.run()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anywidget-graph"
3
- version = "0.2.8"
3
+ version = "0.2.10"
4
4
  description = "Interactive graph visualization for Python notebooks using anywidget"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -30,7 +30,7 @@ dev = [
30
30
  "prek>=0.3",
31
31
  "pytest>=9.0.2",
32
32
  "ruff>=0.15",
33
- "ty>=0.0.20",
33
+ "ty>=0.0",
34
34
  "marimo>=0.20",
35
35
  ]
36
36
  networkx = ["networkx>=3.0"]
@@ -151,6 +151,7 @@ import Sigma from "https://esm.sh/sigma@3.0.0";
151
151
  import circular from "https://esm.sh/graphology-layout@0.6.1/circular.js";
152
152
  import random from "https://esm.sh/graphology-layout@0.6.1/random.js";
153
153
  import forceAtlas2 from "https://esm.sh/graphology-layout-forceatlas2@0.10.1";
154
+ import * as d3Force from "https://esm.sh/d3-force@3.0.0";
154
155
  import neo4j from "https://cdn.jsdelivr.net/npm/neo4j-driver@5.28.0/lib/browser/neo4j-web.esm.min.js";
155
156
 
156
157
  // === Icons ===