Metadata-Version: 2.4
Name: anywidget-graph
Version: 0.1.0
Summary: Interactive graph visualization for Python notebooks using anywidget
Project-URL: Homepage, https://grafeo.dev/
Project-URL: Repository, https://github.com/GrafeoDB/anywidget-graph
Author-email: "S.T. Grond" <widget@grafeo.dev>
License: Apache-2.0
Keywords: anywidget,cypher,graph,jupyter,marimo,neo4j,visualization
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Jupyter
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.12
Requires-Dist: anywidget>=0.9.21
Provides-Extra: dev
Requires-Dist: marimo>=0.19.7; extra == 'dev'
Requires-Dist: prek>=0.3.1; extra == 'dev'
Requires-Dist: pytest>=9.0.2; extra == 'dev'
Requires-Dist: ruff>=0.14.14; extra == 'dev'
Requires-Dist: ty>=0.0.14; extra == 'dev'
Provides-Extra: networkx
Requires-Dist: networkx>=3.0; extra == 'networkx'
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == 'pandas'
Description-Content-Type: text/markdown

# anywidget-graph

Interactive graph visualization for Python notebooks.

Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidget.dev/) runs.

## Features

- **Universal** — One widget, every notebook environment
- **Backend-agnostic** — Grafeo, Neo4j, NetworkX, pandas, or raw dicts
- **Interactive** — Pan, zoom, click, expand neighbors, select paths
- **Customizable** — Colors, sizes, shapes, layouts
- **Performant** — Virtualized rendering for large graphs
- **Exportable** — PNG, SVG, JSON

## Installation

```bash
uv add anywidget-graph
```

## Quick Start

```python
from anywidget_graph import Graph

graph = Graph.from_dict({
    "nodes": [
        {"id": "alice", "label": "Alice", "group": "person"},
        {"id": "bob", "label": "Bob", "group": "person"},
        {"id": "paper", "label": "Graph Theory", "group": "document"},
    ],
    "edges": [
        {"source": "alice", "target": "bob", "label": "knows"},
        {"source": "alice", "target": "paper", "label": "authored"},
    ]
})

graph
```

## Data Sources

### Dictionary

```python
from anywidget_graph import Graph

graph = Graph.from_dict({
    "nodes": [{"id": "a"}, {"id": "b"}],
    "edges": [{"source": "a", "target": "b"}]
})
```

### Grafeo

```python
from grafeo import GrafeoDB
from anywidget_graph import Graph

db = GrafeoDB()
db.execute("INSERT (:Person {name: 'Alice'})-[:KNOWS]->(:Person {name: 'Bob'})")

result = db.execute("MATCH (a)-[r]->(b) RETURN a, r, b")
graph = Graph.from_grafeo(result)
```

### Neo4j

```python
from neo4j import GraphDatabase
from anywidget_graph import Graph

driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

with driver.session() as session:
    result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
    graph = Graph.from_neo4j(result)
```

### NetworkX

```python
import networkx as nx
from anywidget_graph import Graph

G = nx.karate_club_graph()
graph = Graph.from_networkx(G)
```

### pandas

```python
import pandas as pd
from anywidget_graph import Graph

edges = pd.DataFrame({
    "source": ["alice", "alice", "bob"],
    "target": ["bob", "carol", "carol"],
    "weight": [1.0, 0.5, 0.8]
})

graph = Graph.from_pandas(edges)
```

## Interactivity

### Events

```python
graph = Graph.from_dict(data)

@graph.on_node_click
def handle_node(node_id, node_data):
    print(f"Clicked: {node_id}")

@graph.on_edge_click  
def handle_edge(edge_id, edge_data):
    print(f"Edge: {edge_data['label']}")
```

### Selection

```python
graph.selected_nodes         # Get current selection
graph.select(["alice"])      # Select nodes
graph.clear_selection()      # Clear
```

### Expansion

```python
graph.expand("alice")        # Show neighbors
graph.collapse("alice")      # Hide neighbors
```

## Styling

### By Group

```python
graph = Graph.from_dict(
    data,
    node_styles={
        "person": {"color": "#4CAF50", "size": 30},
        "document": {"color": "#2196F3", "shape": "square"},
    }
)
```

### By Property

```python
graph = Graph.from_dict(
    data,
    node_color="group",                    # Color by field
    node_size=lambda n: n["score"] * 10,   # Size by function
    edge_width="weight",                   # Width by field
)
```

### Layouts

```python
Graph.from_dict(data, layout="force")        # Default
Graph.from_dict(data, layout="hierarchical")
Graph.from_dict(data, layout="circular")
Graph.from_dict(data, layout="grid")
```

## Options

```python
graph = Graph.from_dict(
    data,
    width=800,
    height=600,
    directed=True,
    labels=True,
    edge_labels=False,
    physics=True,
    zoom=(0.1, 4),
)
```

## Large Graphs

For 1000+ nodes:

```python
graph = Graph.from_dict(
    data,
    virtualize=True,
    cluster=True,
)
```

## Export

```python
graph.to_png("graph.png")
graph.to_svg("graph.svg")
graph.to_json("graph.json")
```

## Environment Support

| Environment | Supported |
|-------------|-----------|
| Marimo | ✅ |
| JupyterLab | ✅ |
| Jupyter Notebook | ✅ |
| VS Code | ✅ |
| Google Colab | ✅ |
| Databricks | ✅ |

## Related

- [anywidget](https://anywidget.dev/) — Custom Jupyter widgets made easy
- [Grafeo](https://github.com/GrafeoDB/grafeo) — Embeddable graph database
- [grafeo-wasm](https://github.com/GrafeoDB/grafeo-wasm) — Grafeo in the browser

## License

Apache-2.0
