anywidget-vector 0.2.3__tar.gz → 0.2.5__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 (47) hide show
  1. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/PKG-INFO +86 -59
  2. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/README.md +78 -51
  3. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/pyproject.toml +9 -9
  4. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/__init__.py +1 -1
  5. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/static/icons.js +5 -0
  6. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/canvas.js +205 -2
  7. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/styles.css +69 -0
  8. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/toolbar.js +11 -0
  9. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/widget.py +425 -1
  10. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/tests/test_widget.py +141 -0
  11. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/uv.lock +145 -67
  12. anywidget_vector-0.2.3/src/anywidget_vector/traitlets.py +0 -84
  13. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/.github/workflows/ci.yml +0 -0
  14. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/.github/workflows/pypi.yml +0 -0
  15. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/.gitignore +0 -0
  16. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/.python-version +0 -0
  17. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/LICENSE +0 -0
  18. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/examples/demo.py +0 -0
  19. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/__init__.py +0 -0
  20. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/__init__.py +0 -0
  21. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/client.py +0 -0
  22. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/converter.py +0 -0
  23. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/__init__.py +0 -0
  24. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/browser.js +0 -0
  25. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/client.py +0 -0
  26. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/converter.py +0 -0
  27. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/embed.js +0 -0
  28. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/__init__.py +0 -0
  29. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/client.py +0 -0
  30. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/converter.py +0 -0
  31. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/__init__.py +0 -0
  32. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/client.js +0 -0
  33. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/converter.py +0 -0
  34. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/__init__.py +0 -0
  35. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/client.js +0 -0
  36. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/converter.py +0 -0
  37. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/__init__.py +0 -0
  38. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/client.js +0 -0
  39. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/converter.py +0 -0
  40. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/demo.py +0 -0
  41. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/py.typed +0 -0
  42. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/__init__.py +0 -0
  43. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/constants.js +0 -0
  44. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/properties.js +0 -0
  45. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/settings.js +0 -0
  46. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/sidebar.js +0 -0
  47. {anywidget_vector-0.2.3 → anywidget_vector-0.2.5}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anywidget-vector
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Interactive 3D vector visualization with query UI for vector databases
5
5
  Project-URL: Homepage, https://grafeo.dev/
6
6
  Project-URL: Repository, https://github.com/GrafeoDB/anywidget-vector
@@ -24,22 +24,22 @@ Provides-Extra: all
24
24
  Requires-Dist: chromadb>=1.5; extra == 'all'
25
25
  Requires-Dist: lancedb>=0.29.2; extra == 'all'
26
26
  Requires-Dist: numpy>=2.4; extra == 'all'
27
- Requires-Dist: pandas>=3.0; extra == 'all'
27
+ Requires-Dist: pandas>=3; extra == 'all'
28
28
  Requires-Dist: pinecone-client>=6.0; extra == 'all'
29
- Requires-Dist: qdrant-client>=1.0; extra == 'all'
30
- Requires-Dist: weaviate-client>=4.0; extra == 'all'
29
+ Requires-Dist: qdrant-client>=1; extra == 'all'
30
+ Requires-Dist: weaviate-client>=4.20; extra == 'all'
31
31
  Provides-Extra: chroma
32
32
  Requires-Dist: chromadb>=0.4; extra == 'chroma'
33
33
  Provides-Extra: demo
34
34
  Provides-Extra: dev
35
- Requires-Dist: marimo>=0.19.9; extra == 'dev'
35
+ Requires-Dist: marimo>=0.20; extra == 'dev'
36
36
  Requires-Dist: prek>=0.3.2; extra == 'dev'
37
37
  Requires-Dist: pytest>=9.0.2; extra == 'dev'
38
- Requires-Dist: ruff>=0.15.0; extra == 'dev'
39
- Requires-Dist: ty>=0.0.16; extra == 'dev'
38
+ Requires-Dist: ruff>=0.15; extra == 'dev'
39
+ Requires-Dist: ty>=0.0.20; extra == 'dev'
40
40
  Provides-Extra: embed
41
41
  Provides-Extra: grafeo
42
- Requires-Dist: grafeo>=0.5.0; extra == 'grafeo'
42
+ Requires-Dist: grafeo>=0.5; extra == 'grafeo'
43
43
  Provides-Extra: lancedb
44
44
  Requires-Dist: lancedb>=0.1; extra == 'lancedb'
45
45
  Provides-Extra: numpy
@@ -64,8 +64,8 @@ Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidg
64
64
 
65
65
  - **Universal**: One widget, every notebook environment
66
66
  - **6D Visualization**: X, Y, Z position + Color, Shape, Size encoding
67
- - **Backend-agnostic**: NumPy, pandas, Qdrant, Chroma, or raw dicts
68
- - **Interactive**: Orbit, pan, zoom, click, hover, select
67
+ - **Backend-agnostic**: NumPy, pandas, Qdrant, Chroma, Pinecone, Weaviate, LanceDB, or raw dicts
68
+ - **Interactive**: Orbit, pan, zoom, click, hover, box select
69
69
  - **Customizable**: Color scales, shapes, sizes, themes
70
70
  - **Performant**: Instanced rendering for large point clouds
71
71
 
@@ -94,8 +94,6 @@ widget
94
94
  ### Dictionary
95
95
 
96
96
  ```python
97
- from anywidget_vector import VectorSpace
98
-
99
97
  widget = VectorSpace.from_dict({
100
98
  "points": [
101
99
  {"id": "a", "x": 0, "y": 0, "z": 0},
@@ -108,7 +106,6 @@ widget = VectorSpace.from_dict({
108
106
 
109
107
  ```python
110
108
  import numpy as np
111
- from anywidget_vector import VectorSpace
112
109
 
113
110
  positions = np.random.randn(100, 3)
114
111
  widget = VectorSpace.from_numpy(positions)
@@ -118,7 +115,6 @@ widget = VectorSpace.from_numpy(positions)
118
115
 
119
116
  ```python
120
117
  import pandas as pd
121
- from anywidget_vector import VectorSpace
122
118
 
123
119
  df = pd.DataFrame({
124
120
  "x": [0.1, 0.5, 0.9],
@@ -139,9 +135,7 @@ widget = VectorSpace.from_dataframe(
139
135
 
140
136
  ```python
141
137
  import umap
142
- from anywidget_vector import VectorSpace
143
138
 
144
- # Reduce high-dimensional data to 3D
145
139
  embedding = umap.UMAP(n_components=3).fit_transform(high_dim_data)
146
140
  widget = VectorSpace.from_umap(embedding, labels=labels)
147
141
  ```
@@ -150,7 +144,6 @@ widget = VectorSpace.from_umap(embedding, labels=labels)
150
144
 
151
145
  ```python
152
146
  from qdrant_client import QdrantClient
153
- from anywidget_vector import VectorSpace
154
147
 
155
148
  client = QdrantClient("localhost", port=6333)
156
149
  widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
@@ -160,13 +153,41 @@ widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
160
153
 
161
154
  ```python
162
155
  import chromadb
163
- from anywidget_vector import VectorSpace
164
156
 
165
157
  client = chromadb.Client()
166
158
  collection = client.get_collection("embeddings")
167
159
  widget = VectorSpace.from_chroma(collection)
168
160
  ```
169
161
 
162
+ ### Pinecone
163
+
164
+ ```python
165
+ from pinecone import Pinecone
166
+
167
+ pc = Pinecone(api_key="...")
168
+ index = pc.Index("my-index")
169
+ widget = VectorSpace.from_pinecone(index, limit=5000)
170
+ ```
171
+
172
+ ### Weaviate
173
+
174
+ ```python
175
+ import weaviate
176
+
177
+ client = weaviate.Client("http://localhost:8080")
178
+ widget = VectorSpace.from_weaviate(client, "Article", limit=5000)
179
+ ```
180
+
181
+ ### LanceDB
182
+
183
+ ```python
184
+ import lancedb
185
+
186
+ db = lancedb.connect("~/.lancedb")
187
+ table = db.open_table("vectors")
188
+ widget = VectorSpace.from_lancedb(table, limit=5000)
189
+ ```
190
+
170
191
  ## Visual Encoding
171
192
 
172
193
  ### 6 Dimensions
@@ -225,7 +246,6 @@ widget = VectorSpace(points=data)
225
246
  @widget.on_click
226
247
  def handle_click(point_id, point_data):
227
248
  print(f"Clicked: {point_id}")
228
- print(f"Data: {point_data}")
229
249
 
230
250
  @widget.on_hover
231
251
  def handle_hover(point_id, point_data):
@@ -240,36 +260,19 @@ def handle_selection(point_ids, points_data):
240
260
  ### Selection
241
261
 
242
262
  ```python
243
- widget.selected_points # Get current selection
244
- widget.select(["a", "b"]) # Select points
245
- widget.clear_selection() # Clear
263
+ widget.selected_points # Current selection
264
+ widget.select(["a", "b"]) # Select points
265
+ widget.clear_selection() # Clear
266
+ widget.selection_mode = "box" # Switch to box-select mode
246
267
  ```
247
268
 
248
269
  ### Camera
249
270
 
250
271
  ```python
251
- widget.camera_position # Get position [x, y, z]
252
- widget.camera_target # Get target [x, y, z]
253
- widget.reset_camera() # Reset to default
254
- widget.focus_on(["a", "b"]) # Focus on specific points
255
- ```
256
-
257
- ## Options
258
-
259
- ```python
260
- widget = VectorSpace(
261
- points=data,
262
- width=1000,
263
- height=700,
264
- background="#1a1a2e", # Dark theme default
265
- show_axes=True,
266
- show_grid=True,
267
- axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
268
- show_tooltip=True,
269
- tooltip_fields=["label", "x", "y", "z", "cluster"],
270
- selection_mode="click", # "click" or "multi"
271
- use_instancing=True, # Performance: instanced rendering
272
- )
272
+ widget.camera_position # Get position [x, y, z]
273
+ widget.camera_target # Get target [x, y, z]
274
+ widget.reset_camera() # Reset to default
275
+ widget.focus_on(["a", "b"]) # Focus on specific points
273
276
  ```
274
277
 
275
278
  ## Distance Metrics
@@ -288,7 +291,6 @@ Compute distances and visualize similarity relationships between points.
288
291
  ### Color by Distance
289
292
 
290
293
  ```python
291
- # Color points by distance from a reference
292
294
  widget.color_by_distance("point_a")
293
295
  widget.color_by_distance("point_a", metric="cosine")
294
296
  ```
@@ -296,21 +298,16 @@ widget.color_by_distance("point_a", metric="cosine")
296
298
  ### Find Neighbors
297
299
 
298
300
  ```python
299
- # Find k nearest neighbors
300
301
  neighbors = widget.find_neighbors("point_a", k=5)
301
302
  # Returns: [("point_b", 0.1), ("point_c", 0.2), ...]
302
303
 
303
- # Find neighbors within distance threshold
304
304
  neighbors = widget.find_neighbors("point_a", threshold=0.5)
305
305
  ```
306
306
 
307
307
  ### Show Connections
308
308
 
309
309
  ```python
310
- # Draw lines to k-nearest neighbors
311
310
  widget.show_neighbors("point_a", k=5)
312
-
313
- # Draw lines to all points within threshold
314
311
  widget.show_neighbors("point_a", threshold=0.3)
315
312
 
316
313
  # Manual connection settings
@@ -327,7 +324,6 @@ widget = VectorSpace(
327
324
  ### Compute Distances
328
325
 
329
326
  ```python
330
- # Get distances from reference to all points
331
327
  distances = widget.compute_distances("point_a")
332
328
  # Returns: {"point_b": 0.1, "point_c": 0.5, ...}
333
329
 
@@ -335,26 +331,57 @@ distances = widget.compute_distances("point_a")
335
331
  distances = widget.compute_distances(
336
332
  "point_a",
337
333
  metric="cosine",
338
- vector_field="embedding" # Use full embedding vector
334
+ vector_field="embedding"
339
335
  )
340
336
  ```
341
337
 
338
+ ## Options
339
+
340
+ ```python
341
+ widget = VectorSpace(
342
+ points=data,
343
+ width=1000,
344
+ height=700,
345
+ background="#1a1a2e", # Dark theme default
346
+ show_axes=True,
347
+ show_grid=True,
348
+ axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
349
+ show_tooltip=True,
350
+ tooltip_fields=["label", "x", "y", "z", "cluster"],
351
+ selection_mode="click", # "click", "multi", or "box"
352
+ use_instancing=True, # Performance: instanced rendering
353
+ )
354
+ ```
355
+
356
+ ## Backends
357
+
358
+ Configure a backend for interactive querying:
359
+
360
+ ```python
361
+ widget.set_backend("chroma", client=collection)
362
+ widget.set_backend("lancedb", client=table)
363
+ widget.set_backend("grafeo", client=db)
364
+ ```
365
+
342
366
  ## Export
343
367
 
344
368
  ```python
345
- widget.to_json() # Export points as JSON string
369
+ widget.to_json() # Points as JSON string
370
+ widget.to_html() # Self-contained HTML string
371
+ widget.to_html(title="My Vectors") # Custom title
372
+ widget.save_html("vectors.html") # Write HTML to file
346
373
  ```
347
374
 
348
375
  ## Environment Support
349
376
 
350
377
  | Environment | Supported |
351
378
  |-------------|-----------|
352
- | Marimo | ✅ |
353
- | JupyterLab | ✅ |
354
- | Jupyter Notebook | ✅ |
355
- | VS Code | ✅ |
356
- | Google Colab | ✅ |
357
- | Databricks | ✅ |
379
+ | Marimo | Yes |
380
+ | JupyterLab | Yes |
381
+ | Jupyter Notebook | Yes |
382
+ | VS Code | Yes |
383
+ | Google Colab | Yes |
384
+ | Databricks | Yes |
358
385
 
359
386
  ## Related
360
387
 
@@ -8,8 +8,8 @@ Works with Marimo, Jupyter, VS Code, Colab, anywhere [anywidget](https://anywidg
8
8
 
9
9
  - **Universal**: One widget, every notebook environment
10
10
  - **6D Visualization**: X, Y, Z position + Color, Shape, Size encoding
11
- - **Backend-agnostic**: NumPy, pandas, Qdrant, Chroma, or raw dicts
12
- - **Interactive**: Orbit, pan, zoom, click, hover, select
11
+ - **Backend-agnostic**: NumPy, pandas, Qdrant, Chroma, Pinecone, Weaviate, LanceDB, or raw dicts
12
+ - **Interactive**: Orbit, pan, zoom, click, hover, box select
13
13
  - **Customizable**: Color scales, shapes, sizes, themes
14
14
  - **Performant**: Instanced rendering for large point clouds
15
15
 
@@ -38,8 +38,6 @@ widget
38
38
  ### Dictionary
39
39
 
40
40
  ```python
41
- from anywidget_vector import VectorSpace
42
-
43
41
  widget = VectorSpace.from_dict({
44
42
  "points": [
45
43
  {"id": "a", "x": 0, "y": 0, "z": 0},
@@ -52,7 +50,6 @@ widget = VectorSpace.from_dict({
52
50
 
53
51
  ```python
54
52
  import numpy as np
55
- from anywidget_vector import VectorSpace
56
53
 
57
54
  positions = np.random.randn(100, 3)
58
55
  widget = VectorSpace.from_numpy(positions)
@@ -62,7 +59,6 @@ widget = VectorSpace.from_numpy(positions)
62
59
 
63
60
  ```python
64
61
  import pandas as pd
65
- from anywidget_vector import VectorSpace
66
62
 
67
63
  df = pd.DataFrame({
68
64
  "x": [0.1, 0.5, 0.9],
@@ -83,9 +79,7 @@ widget = VectorSpace.from_dataframe(
83
79
 
84
80
  ```python
85
81
  import umap
86
- from anywidget_vector import VectorSpace
87
82
 
88
- # Reduce high-dimensional data to 3D
89
83
  embedding = umap.UMAP(n_components=3).fit_transform(high_dim_data)
90
84
  widget = VectorSpace.from_umap(embedding, labels=labels)
91
85
  ```
@@ -94,7 +88,6 @@ widget = VectorSpace.from_umap(embedding, labels=labels)
94
88
 
95
89
  ```python
96
90
  from qdrant_client import QdrantClient
97
- from anywidget_vector import VectorSpace
98
91
 
99
92
  client = QdrantClient("localhost", port=6333)
100
93
  widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
@@ -104,13 +97,41 @@ widget = VectorSpace.from_qdrant(client, "my_collection", limit=5000)
104
97
 
105
98
  ```python
106
99
  import chromadb
107
- from anywidget_vector import VectorSpace
108
100
 
109
101
  client = chromadb.Client()
110
102
  collection = client.get_collection("embeddings")
111
103
  widget = VectorSpace.from_chroma(collection)
112
104
  ```
113
105
 
106
+ ### Pinecone
107
+
108
+ ```python
109
+ from pinecone import Pinecone
110
+
111
+ pc = Pinecone(api_key="...")
112
+ index = pc.Index("my-index")
113
+ widget = VectorSpace.from_pinecone(index, limit=5000)
114
+ ```
115
+
116
+ ### Weaviate
117
+
118
+ ```python
119
+ import weaviate
120
+
121
+ client = weaviate.Client("http://localhost:8080")
122
+ widget = VectorSpace.from_weaviate(client, "Article", limit=5000)
123
+ ```
124
+
125
+ ### LanceDB
126
+
127
+ ```python
128
+ import lancedb
129
+
130
+ db = lancedb.connect("~/.lancedb")
131
+ table = db.open_table("vectors")
132
+ widget = VectorSpace.from_lancedb(table, limit=5000)
133
+ ```
134
+
114
135
  ## Visual Encoding
115
136
 
116
137
  ### 6 Dimensions
@@ -169,7 +190,6 @@ widget = VectorSpace(points=data)
169
190
  @widget.on_click
170
191
  def handle_click(point_id, point_data):
171
192
  print(f"Clicked: {point_id}")
172
- print(f"Data: {point_data}")
173
193
 
174
194
  @widget.on_hover
175
195
  def handle_hover(point_id, point_data):
@@ -184,36 +204,19 @@ def handle_selection(point_ids, points_data):
184
204
  ### Selection
185
205
 
186
206
  ```python
187
- widget.selected_points # Get current selection
188
- widget.select(["a", "b"]) # Select points
189
- widget.clear_selection() # Clear
207
+ widget.selected_points # Current selection
208
+ widget.select(["a", "b"]) # Select points
209
+ widget.clear_selection() # Clear
210
+ widget.selection_mode = "box" # Switch to box-select mode
190
211
  ```
191
212
 
192
213
  ### Camera
193
214
 
194
215
  ```python
195
- widget.camera_position # Get position [x, y, z]
196
- widget.camera_target # Get target [x, y, z]
197
- widget.reset_camera() # Reset to default
198
- widget.focus_on(["a", "b"]) # Focus on specific points
199
- ```
200
-
201
- ## Options
202
-
203
- ```python
204
- widget = VectorSpace(
205
- points=data,
206
- width=1000,
207
- height=700,
208
- background="#1a1a2e", # Dark theme default
209
- show_axes=True,
210
- show_grid=True,
211
- axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
212
- show_tooltip=True,
213
- tooltip_fields=["label", "x", "y", "z", "cluster"],
214
- selection_mode="click", # "click" or "multi"
215
- use_instancing=True, # Performance: instanced rendering
216
- )
216
+ widget.camera_position # Get position [x, y, z]
217
+ widget.camera_target # Get target [x, y, z]
218
+ widget.reset_camera() # Reset to default
219
+ widget.focus_on(["a", "b"]) # Focus on specific points
217
220
  ```
218
221
 
219
222
  ## Distance Metrics
@@ -232,7 +235,6 @@ Compute distances and visualize similarity relationships between points.
232
235
  ### Color by Distance
233
236
 
234
237
  ```python
235
- # Color points by distance from a reference
236
238
  widget.color_by_distance("point_a")
237
239
  widget.color_by_distance("point_a", metric="cosine")
238
240
  ```
@@ -240,21 +242,16 @@ widget.color_by_distance("point_a", metric="cosine")
240
242
  ### Find Neighbors
241
243
 
242
244
  ```python
243
- # Find k nearest neighbors
244
245
  neighbors = widget.find_neighbors("point_a", k=5)
245
246
  # Returns: [("point_b", 0.1), ("point_c", 0.2), ...]
246
247
 
247
- # Find neighbors within distance threshold
248
248
  neighbors = widget.find_neighbors("point_a", threshold=0.5)
249
249
  ```
250
250
 
251
251
  ### Show Connections
252
252
 
253
253
  ```python
254
- # Draw lines to k-nearest neighbors
255
254
  widget.show_neighbors("point_a", k=5)
256
-
257
- # Draw lines to all points within threshold
258
255
  widget.show_neighbors("point_a", threshold=0.3)
259
256
 
260
257
  # Manual connection settings
@@ -271,7 +268,6 @@ widget = VectorSpace(
271
268
  ### Compute Distances
272
269
 
273
270
  ```python
274
- # Get distances from reference to all points
275
271
  distances = widget.compute_distances("point_a")
276
272
  # Returns: {"point_b": 0.1, "point_c": 0.5, ...}
277
273
 
@@ -279,26 +275,57 @@ distances = widget.compute_distances("point_a")
279
275
  distances = widget.compute_distances(
280
276
  "point_a",
281
277
  metric="cosine",
282
- vector_field="embedding" # Use full embedding vector
278
+ vector_field="embedding"
283
279
  )
284
280
  ```
285
281
 
282
+ ## Options
283
+
284
+ ```python
285
+ widget = VectorSpace(
286
+ points=data,
287
+ width=1000,
288
+ height=700,
289
+ background="#1a1a2e", # Dark theme default
290
+ show_axes=True,
291
+ show_grid=True,
292
+ axis_labels={"x": "PC1", "y": "PC2", "z": "PC3"},
293
+ show_tooltip=True,
294
+ tooltip_fields=["label", "x", "y", "z", "cluster"],
295
+ selection_mode="click", # "click", "multi", or "box"
296
+ use_instancing=True, # Performance: instanced rendering
297
+ )
298
+ ```
299
+
300
+ ## Backends
301
+
302
+ Configure a backend for interactive querying:
303
+
304
+ ```python
305
+ widget.set_backend("chroma", client=collection)
306
+ widget.set_backend("lancedb", client=table)
307
+ widget.set_backend("grafeo", client=db)
308
+ ```
309
+
286
310
  ## Export
287
311
 
288
312
  ```python
289
- widget.to_json() # Export points as JSON string
313
+ widget.to_json() # Points as JSON string
314
+ widget.to_html() # Self-contained HTML string
315
+ widget.to_html(title="My Vectors") # Custom title
316
+ widget.save_html("vectors.html") # Write HTML to file
290
317
  ```
291
318
 
292
319
  ## Environment Support
293
320
 
294
321
  | Environment | Supported |
295
322
  |-------------|-----------|
296
- | Marimo | ✅ |
297
- | JupyterLab | ✅ |
298
- | Jupyter Notebook | ✅ |
299
- | VS Code | ✅ |
300
- | Google Colab | ✅ |
301
- | Databricks | ✅ |
323
+ | Marimo | Yes |
324
+ | JupyterLab | Yes |
325
+ | Jupyter Notebook | Yes |
326
+ | VS Code | Yes |
327
+ | Google Colab | Yes |
328
+ | Databricks | Yes |
302
329
 
303
330
  ## Related
304
331
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anywidget-vector"
3
- version = "0.2.3"
3
+ version = "0.2.5"
4
4
  description = "Interactive 3D vector visualization with query UI for vector databases"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -28,9 +28,9 @@ dependencies = [
28
28
  dev = [
29
29
  "prek>=0.3.2",
30
30
  "pytest>=9.0.2",
31
- "ruff>=0.15.0",
32
- "ty>=0.0.16",
33
- "marimo>=0.19.9",
31
+ "ruff>=0.15",
32
+ "ty>=0.0.20",
33
+ "marimo>=0.20",
34
34
  ]
35
35
  pandas = ["pandas>=2.0"]
36
36
  numpy = ["numpy>=1.24"]
@@ -39,16 +39,16 @@ chroma = ["chromadb>=0.4"]
39
39
  pinecone = ["pinecone-client>=3.0"]
40
40
  weaviate = ["weaviate-client>=4.0"]
41
41
  lancedb = ["lancedb>=0.1"]
42
- grafeo = ["grafeo>=0.5.0"]
42
+ grafeo = ["grafeo>=0.5"]
43
43
  embed = [] # Marker extra — WASM loads from CDN on demand
44
44
  demo = [] # Marker extra — enables demo mode with pre-populated dataset
45
45
  all = [
46
- "pandas>=3.0",
46
+ "pandas>=3",
47
47
  "numpy>=2.4",
48
- "qdrant-client>=1.0",
48
+ "qdrant-client>=1",
49
49
  "chromadb>=1.5",
50
50
  "pinecone-client>=6.0",
51
- "weaviate-client>=4.0",
51
+ "weaviate-client>=4.20",
52
52
  "lancedb>=0.29.2",
53
53
  ]
54
54
 
@@ -93,5 +93,5 @@ repos = [
93
93
  [tool.uv]
94
94
  dev-dependencies = [
95
95
  "pytest>=9.0.2",
96
- "ruff>=0.14.14",
96
+ "ruff>=0.15",
97
97
  ]
@@ -3,4 +3,4 @@
3
3
  from anywidget_vector.widget import VectorSpace
4
4
 
5
5
  __all__ = ["VectorSpace"]
6
- __version__ = "0.2.3"
6
+ __version__ = "0.2.5"
@@ -11,4 +11,9 @@ export const ICONS = {
11
11
  loader: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10" stroke-dasharray="31.4" stroke-dashoffset="10"/></svg>',
12
12
  cube: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27,6.96 12,12.01 20.73,6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>',
13
13
  reset: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>',
14
+ zoomIn: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>',
15
+ zoomOut: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/></svg>',
16
+ zoomFit: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>',
17
+ cursor: '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M4 2l12 14h-5l-2.3 5.5L6 20.3 8.3 15H4V2z"/></svg>',
18
+ boxSelect: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-dasharray="3 2"><rect x="3" y="3" width="18" height="18" rx="1"/></svg>',
14
19
  };