anywidget-vector 0.2.4__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.4 → anywidget_vector-0.2.5}/PKG-INFO +1 -1
  2. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/pyproject.toml +1 -1
  3. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/__init__.py +1 -1
  4. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/canvas.js +31 -16
  5. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/styles.css +12 -30
  6. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/toolbar.js +11 -0
  7. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/uv.lock +12 -12
  8. anywidget_vector-0.2.4/src/anywidget_vector/traitlets.py +0 -84
  9. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/.github/workflows/ci.yml +0 -0
  10. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/.github/workflows/pypi.yml +0 -0
  11. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/.gitignore +0 -0
  12. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/.python-version +0 -0
  13. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/LICENSE +0 -0
  14. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/README.md +0 -0
  15. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/examples/demo.py +0 -0
  16. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/__init__.py +0 -0
  17. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/__init__.py +0 -0
  18. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/client.py +0 -0
  19. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/chroma/converter.py +0 -0
  20. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/__init__.py +0 -0
  21. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/browser.js +0 -0
  22. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/client.py +0 -0
  23. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/converter.py +0 -0
  24. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/grafeo/embed.js +0 -0
  25. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/__init__.py +0 -0
  26. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/client.py +0 -0
  27. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/lancedb/converter.py +0 -0
  28. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/__init__.py +0 -0
  29. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/client.js +0 -0
  30. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/pinecone/converter.py +0 -0
  31. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/__init__.py +0 -0
  32. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/client.js +0 -0
  33. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/qdrant/converter.py +0 -0
  34. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/__init__.py +0 -0
  35. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/client.js +0 -0
  36. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/backends/weaviate/converter.py +0 -0
  37. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/demo.py +0 -0
  38. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/py.typed +0 -0
  39. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/static/icons.js +0 -0
  40. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/__init__.py +0 -0
  41. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/constants.js +0 -0
  42. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/properties.js +0 -0
  43. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/settings.js +0 -0
  44. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/ui/sidebar.js +0 -0
  45. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/src/anywidget_vector/widget.py +0 -0
  46. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/tests/__init__.py +0 -0
  47. {anywidget_vector-0.2.4 → anywidget_vector-0.2.5}/tests/test_widget.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: anywidget-vector
3
- Version: 0.2.4
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "anywidget-vector"
3
- version = "0.2.4"
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"
@@ -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"
@@ -100,14 +100,32 @@ export function createCanvas(model, container, callbacks) {
100
100
  setupRaycaster();
101
101
  setupTooltip();
102
102
  setupZoomControls();
103
- setupModeControls();
104
103
  setupBoxSelection();
104
+ setupKeyboardShortcuts();
105
105
  createPoints();
106
106
  createConnections();
107
107
  fitToView();
108
108
  bindModelEvents();
109
109
  }
110
110
 
111
+ function setupKeyboardShortcuts() {
112
+ container.tabIndex = 0;
113
+ container.style.outline = "none";
114
+ container.addEventListener("keydown", (e) => {
115
+ const tag = e.target.tagName;
116
+ if (tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA") return;
117
+
118
+ if (e.key === "f" || e.key === "F") {
119
+ e.preventDefault();
120
+ fitToView();
121
+ } else if (e.key === "Escape") {
122
+ model.set("selected_points", []);
123
+ model.set("hovered_point", null);
124
+ model.save_changes();
125
+ }
126
+ });
127
+ }
128
+
111
129
  function setupAxesAndGrid() {
112
130
  while (axesGroup.children.length > 0) axesGroup.remove(axesGroup.children[0]);
113
131
  if (gridHelper) { scene.remove(gridHelper); gridHelper = null; }
@@ -471,27 +489,19 @@ export function createCanvas(model, container, callbacks) {
471
489
  zoomFitBtn.title = "Fit to view";
472
490
  zoomFitBtn.addEventListener("click", fitToView);
473
491
 
474
- zoomControls.appendChild(zoomInBtn);
475
- zoomControls.appendChild(zoomOutBtn);
476
- zoomControls.appendChild(zoomFitBtn);
477
- container.appendChild(zoomControls);
478
- }
479
-
480
- function setupModeControls() {
492
+ // Mode switcher (above zoom buttons, inside same column)
481
493
  const modeGroup = document.createElement("div");
482
494
  modeGroup.className = "avs-mode-group";
483
495
 
484
496
  const clickBtn = document.createElement("button");
485
- clickBtn.className = "avs-mode-btn avs-mode-active";
497
+ clickBtn.className = "avs-zoom-btn avs-mode-btn avs-mode-active";
486
498
  clickBtn.innerHTML = ICONS.cursor;
487
499
  clickBtn.title = "Click / multi-select mode";
488
- clickBtn.dataset.mode = "click";
489
500
 
490
501
  const boxBtn = document.createElement("button");
491
- boxBtn.className = "avs-mode-btn";
502
+ boxBtn.className = "avs-zoom-btn avs-mode-btn";
492
503
  boxBtn.innerHTML = ICONS.boxSelect;
493
504
  boxBtn.title = "Box select mode";
494
- boxBtn.dataset.mode = "box";
495
505
 
496
506
  function setActiveMode(mode) {
497
507
  currentMode = mode;
@@ -513,15 +523,20 @@ export function createCanvas(model, container, callbacks) {
513
523
  clickBtn.addEventListener("click", () => setActiveMode("click"));
514
524
  boxBtn.addEventListener("click", () => setActiveMode("box"));
515
525
 
516
- modeGroup.appendChild(clickBtn);
517
- modeGroup.appendChild(boxBtn);
518
- container.appendChild(modeGroup);
519
-
520
526
  // Sync with model changes from Python side
521
527
  model.on("change:selection_mode", () => {
522
528
  const mode = model.get("selection_mode") || "click";
523
529
  if (mode !== currentMode) setActiveMode(mode);
524
530
  });
531
+
532
+ modeGroup.appendChild(clickBtn);
533
+ modeGroup.appendChild(boxBtn);
534
+
535
+ zoomControls.appendChild(modeGroup);
536
+ zoomControls.appendChild(zoomInBtn);
537
+ zoomControls.appendChild(zoomOutBtn);
538
+ zoomControls.appendChild(zoomFitBtn);
539
+ container.appendChild(zoomControls);
525
540
  }
526
541
 
527
542
  function setupBoxSelection() {
@@ -47,7 +47,7 @@
47
47
  display: flex;
48
48
  flex-direction: column;
49
49
  gap: 2px;
50
- z-index: 10;
50
+ z-index: 25;
51
51
  }
52
52
 
53
53
  .avs-zoom-btn {
@@ -87,37 +87,11 @@
87
87
 
88
88
  /* === Mode Switcher === */
89
89
  .avs-mode-group {
90
- position: absolute;
91
- bottom: 12px;
92
- left: 12px;
93
90
  display: flex;
94
91
  gap: 2px;
95
- z-index: 10;
96
- }
97
-
98
- .avs-mode-btn {
99
- width: 32px;
100
- height: 32px;
101
- border: 1px solid var(--avs-border);
102
- background: var(--avs-bg);
103
- color: var(--avs-text);
104
- cursor: pointer;
105
- display: flex;
106
- align-items: center;
107
- justify-content: center;
108
- border-radius: 4px;
109
- transition: all 0.15s;
110
- padding: 0;
111
- }
112
-
113
- .avs-mode-btn:hover {
114
- background: var(--avs-surface);
115
- border-color: var(--avs-text-muted);
116
- }
117
-
118
- .avs-mode-btn svg {
119
- width: 16px;
120
- height: 16px;
92
+ margin-bottom: 4px;
93
+ border-bottom: 1px solid rgba(255,255,255,0.15);
94
+ padding-bottom: 6px;
121
95
  }
122
96
 
123
97
  .avs-mode-btn.avs-mode-active {
@@ -159,6 +133,14 @@
159
133
  color: var(--avs-text-muted);
160
134
  }
161
135
 
136
+ .avs-count-badge {
137
+ font-size: 11px;
138
+ color: var(--avs-text-muted);
139
+ white-space: nowrap;
140
+ flex-shrink: 0;
141
+ font-variant-numeric: tabular-nums;
142
+ }
143
+
162
144
  .avs-filter-count {
163
145
  font-size: 11px;
164
146
  font-weight: 600;
@@ -39,6 +39,17 @@ export function createToolbar(model, callbacks) {
39
39
  filterCount.style.display = "none";
40
40
  toolbar.appendChild(filterCount);
41
41
 
42
+ // Point count badge
43
+ const countBadge = document.createElement("span");
44
+ countBadge.className = "avs-count-badge";
45
+ function updatePointCount() {
46
+ const pts = model.get("points") || [];
47
+ countBadge.textContent = `${pts.length} points`;
48
+ }
49
+ updatePointCount();
50
+ model.on("change:points", updatePointCount);
51
+ toolbar.appendChild(countBadge);
52
+
42
53
  // Run button
43
54
  const runBtn = document.createElement("button");
44
55
  runBtn.className = "avs-btn avs-btn-primary";
@@ -50,7 +50,7 @@ wheels = [
50
50
 
51
51
  [[package]]
52
52
  name = "anywidget-vector"
53
- version = "0.2.3"
53
+ version = "0.2.5"
54
54
  source = { editable = "." }
55
55
  dependencies = [
56
56
  { name = "anywidget" },
@@ -109,30 +109,30 @@ requires-dist = [
109
109
  { name = "anywidget", specifier = ">=0.9.21" },
110
110
  { name = "chromadb", marker = "extra == 'all'", specifier = ">=1.5" },
111
111
  { name = "chromadb", marker = "extra == 'chroma'", specifier = ">=0.4" },
112
- { name = "grafeo", marker = "extra == 'grafeo'", specifier = ">=0.5.0" },
112
+ { name = "grafeo", marker = "extra == 'grafeo'", specifier = ">=0.5" },
113
113
  { name = "lancedb", marker = "extra == 'all'", specifier = ">=0.29.2" },
114
114
  { name = "lancedb", marker = "extra == 'lancedb'", specifier = ">=0.1" },
115
- { name = "marimo", marker = "extra == 'dev'", specifier = ">=0.19.9" },
115
+ { name = "marimo", marker = "extra == 'dev'", specifier = ">=0.20" },
116
116
  { name = "numpy", marker = "extra == 'all'", specifier = ">=2.4" },
117
117
  { name = "numpy", marker = "extra == 'numpy'", specifier = ">=1.24" },
118
- { name = "pandas", marker = "extra == 'all'", specifier = ">=3.0" },
118
+ { name = "pandas", marker = "extra == 'all'", specifier = ">=3" },
119
119
  { name = "pandas", marker = "extra == 'pandas'", specifier = ">=2.0" },
120
120
  { name = "pinecone-client", marker = "extra == 'all'", specifier = ">=6.0" },
121
121
  { name = "pinecone-client", marker = "extra == 'pinecone'", specifier = ">=3.0" },
122
122
  { name = "prek", marker = "extra == 'dev'", specifier = ">=0.3.2" },
123
123
  { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.2" },
124
- { name = "qdrant-client", marker = "extra == 'all'", specifier = ">=1.0" },
124
+ { name = "qdrant-client", marker = "extra == 'all'", specifier = ">=1" },
125
125
  { name = "qdrant-client", marker = "extra == 'qdrant'", specifier = ">=1.0" },
126
- { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15.0" },
127
- { name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.16" },
128
- { name = "weaviate-client", marker = "extra == 'all'", specifier = ">=4.0" },
126
+ { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15" },
127
+ { name = "ty", marker = "extra == 'dev'", specifier = ">=0.0.20" },
128
+ { name = "weaviate-client", marker = "extra == 'all'", specifier = ">=4.20" },
129
129
  { name = "weaviate-client", marker = "extra == 'weaviate'", specifier = ">=4.0" },
130
130
  ]
131
131
 
132
132
  [package.metadata.requires-dev]
133
133
  dev = [
134
134
  { name = "pytest", specifier = ">=9.0.2" },
135
- { name = "ruff", specifier = ">=0.14.14" },
135
+ { name = "ruff", specifier = ">=0.15" },
136
136
  ]
137
137
 
138
138
  [[package]]
@@ -2827,7 +2827,7 @@ wheels = [
2827
2827
 
2828
2828
  [[package]]
2829
2829
  name = "weaviate-client"
2830
- version = "4.19.2"
2830
+ version = "4.20.1"
2831
2831
  source = { registry = "https://pypi.org/simple" }
2832
2832
  dependencies = [
2833
2833
  { name = "authlib" },
@@ -2838,9 +2838,9 @@ dependencies = [
2838
2838
  { name = "pydantic" },
2839
2839
  { name = "validators" },
2840
2840
  ]
2841
- sdist = { url = "https://files.pythonhosted.org/packages/0b/57/f3201e5047d91aa9e7a06aa004b995bc8b05b2ea95dd178ace17eaaec16e/weaviate_client-4.19.2.tar.gz", hash = "sha256:99e76e912c95762436089cd5feedbfeea31e892aa13b6ad94729a2a54b316c45", size = 786448 }
2841
+ sdist = { url = "https://files.pythonhosted.org/packages/6a/0e/450732a620dec30e5c13d0e5ba6ea81cd2b168ffb5a1e6cfa034da2fd988/weaviate_client-4.20.1.tar.gz", hash = "sha256:7d3c4835292b17d54757c3f62921e87975c01f26869db787250cebef2a17bd80", size = 807802 }
2842
2842
  wheels = [
2843
- { url = "https://files.pythonhosted.org/packages/d5/09/636abb5ecfae2316b6584eaae7001a03abfe131f72d747fbec3419075524/weaviate_client-4.19.2-py3-none-any.whl", hash = "sha256:e78306d47c574c4035c87223e480bb77bd6e54142a21c4c58522dd43019fe493", size = 603652 },
2843
+ { url = "https://files.pythonhosted.org/packages/de/3a/a2c74bfbdc5f8acbab8025205520448dcece82942206a9861ec48ccad03f/weaviate_client-4.20.1-py3-none-any.whl", hash = "sha256:6ca36bb8752c39589bfc856c1232cbcb627f69141e7a741b592e2516888f59d8", size = 618735 },
2844
2844
  ]
2845
2845
 
2846
2846
  [[package]]
@@ -1,84 +0,0 @@
1
- """Traitlet definitions for VectorSpace widget."""
2
-
3
- from __future__ import annotations
4
-
5
- import traitlets
6
-
7
-
8
- class VectorSpaceTraits(traitlets.HasTraits):
9
- """All traitlets for the VectorSpace widget, grouped logically."""
10
-
11
- # === Data ===
12
- points = traitlets.List(trait=traitlets.Dict()).tag(sync=True)
13
-
14
- # === Display ===
15
- width = traitlets.Int(default_value=800).tag(sync=True)
16
- height = traitlets.Int(default_value=600).tag(sync=True)
17
- background = traitlets.Unicode(default_value="#1a1a2e").tag(sync=True)
18
-
19
- # === Axes and Grid ===
20
- show_axes = traitlets.Bool(default_value=True).tag(sync=True)
21
- show_grid = traitlets.Bool(default_value=True).tag(sync=True)
22
- axis_labels = traitlets.Dict(default_value={"x": "X", "y": "Y", "z": "Z"}).tag(sync=True)
23
- grid_divisions = traitlets.Int(default_value=10).tag(sync=True)
24
-
25
- # === Color Mapping ===
26
- color_field = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
27
- color_scale = traitlets.Unicode(default_value="viridis").tag(sync=True)
28
- color_domain = traitlets.List(default_value=None, allow_none=True).tag(sync=True)
29
-
30
- # === Size Mapping ===
31
- size_field = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
32
- size_range = traitlets.List(default_value=[0.02, 0.1]).tag(sync=True)
33
-
34
- # === Shape Mapping ===
35
- shape_field = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
36
- shape_map = traitlets.Dict(default_value={}).tag(sync=True)
37
-
38
- # === Camera ===
39
- camera_position = traitlets.List(default_value=[2, 2, 2]).tag(sync=True)
40
- camera_target = traitlets.List(default_value=[0, 0, 0]).tag(sync=True)
41
-
42
- # === Interaction ===
43
- selected_points = traitlets.List(default_value=[]).tag(sync=True)
44
- hovered_point = traitlets.Dict(default_value=None, allow_none=True).tag(sync=True)
45
- selection_mode = traitlets.Unicode(default_value="click").tag(sync=True)
46
-
47
- # === Tooltip ===
48
- show_tooltip = traitlets.Bool(default_value=True).tag(sync=True)
49
- tooltip_fields = traitlets.List(default_value=["label", "x", "y", "z"]).tag(sync=True)
50
-
51
- # === Performance ===
52
- use_instancing = traitlets.Bool(default_value=True).tag(sync=True)
53
- point_budget = traitlets.Int(default_value=100000).tag(sync=True)
54
-
55
- # === Distance Metrics and Connections ===
56
- distance_metric = traitlets.Unicode(default_value="euclidean").tag(sync=True)
57
- show_connections = traitlets.Bool(default_value=False).tag(sync=True)
58
- k_neighbors = traitlets.Int(default_value=0).tag(sync=True)
59
- distance_threshold = traitlets.Float(default_value=None, allow_none=True).tag(sync=True)
60
- reference_point = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
61
- connection_color = traitlets.Unicode(default_value="#ffffff").tag(sync=True)
62
- connection_opacity = traitlets.Float(default_value=0.3).tag(sync=True)
63
-
64
- # === UI Visibility ===
65
- show_toolbar = traitlets.Bool(default_value=False).tag(sync=True)
66
- show_settings = traitlets.Bool(default_value=False).tag(sync=True)
67
- show_properties = traitlets.Bool(default_value=False).tag(sync=True)
68
-
69
- # === Backend Configuration ===
70
- backend = traitlets.Unicode(default_value="qdrant").tag(sync=True)
71
- backend_config = traitlets.Dict(default_value={}).tag(sync=True)
72
- connection_status = traitlets.Unicode(default_value="disconnected").tag(sync=True)
73
-
74
- # === Query Parameters ===
75
- query_type = traitlets.Unicode(default_value="text").tag(sync=True)
76
- query_input = traitlets.Unicode(default_value="").tag(sync=True)
77
- query_limit = traitlets.Int(default_value=100).tag(sync=True)
78
- query_error = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
79
-
80
- # === Embedding Configuration ===
81
- embedding_config = traitlets.Dict(default_value={}).tag(sync=True)
82
-
83
- # === Internal Triggers ===
84
- _execute_query = traitlets.Int(default_value=0).tag(sync=True)