anywidget-vector 0.2.7__tar.gz → 0.3.0__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.
- anywidget_vector-0.3.0/CHANGELOG.md +109 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/PKG-INFO +3 -3
- anywidget_vector-0.3.0/examples/fashion-mnist-parallel-coords-6d.py +269 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/pyproject.toml +3 -3
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/__init__.py +35 -4
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/canvas.js +15 -4
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/settings.js +2 -1
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/sidebar.js +122 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/styles.css +18 -13
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/widget.py +1 -1
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/uv.lock +3 -3
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/.github/workflows/ci.yml +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/.github/workflows/pypi.yml +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/.gitignore +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/.python-version +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/LICENSE +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/README.md +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/assets/demo.png +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/examples/demo.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/examples/test_add_numpy.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/examples/test_projection.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/client.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/browser.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/client.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/embed.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/client.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/pinecone/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/pinecone/client.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/pinecone/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/client.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/weaviate/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/weaviate/client.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/weaviate/converter.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/demo.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/py.typed +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/static/icons.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/constants.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/properties.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/ui/toolbar.js +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/tests/__init__.py +0 -0
- {anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/tests/test_widget.py +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 (2026-03-16)
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
- **Distance metrics sidebar**: Distance section with metric selector (euclidean, cosine, manhattan, dot product), configurable k-neighbors (0-50), and live nearest-neighbor display for the selected point
|
|
8
|
+
- **Dimension mapping display**: Sidebar now shows active visual mapping fields: color field, size range, shape field and unique shape count
|
|
9
|
+
|
|
10
|
+
### Improvements
|
|
11
|
+
|
|
12
|
+
- Improved sidebar styling and layout
|
|
13
|
+
|
|
14
|
+
## 0.2.8 (2026-03-15)
|
|
15
|
+
|
|
16
|
+
### New Features
|
|
17
|
+
|
|
18
|
+
- **6D parallel coordinates example**: Fashion-MNIST demo with PCA across 6+ dimensions, interactive axis mapping, and shape mapping to categorical labels
|
|
19
|
+
|
|
20
|
+
## 0.2.7 (2026-03-15)
|
|
21
|
+
|
|
22
|
+
### New Features
|
|
23
|
+
|
|
24
|
+
- **Dark/light mode toggle**: `dark_mode` trait with real-time CSS color scheme switching and theme-aware styling across all panels
|
|
25
|
+
|
|
26
|
+
## 0.2.6 (2026-03-15)
|
|
27
|
+
|
|
28
|
+
### New Features
|
|
29
|
+
|
|
30
|
+
- **NumPy array support**: `add_numpy()` method for adding points from arrays with automatic PCA projection for high-dimensional data (D > 3)
|
|
31
|
+
- **Dimensionality reduction**: `project()` method with PCA, t-SNE and UMAP support; `set_vectors()` for storing high-dimensional vectors separately
|
|
32
|
+
- **Example scripts**: Projection demo and numpy integration examples
|
|
33
|
+
|
|
34
|
+
### Improvements
|
|
35
|
+
|
|
36
|
+
- High-dimensional vector storage without syncing to JavaScript
|
|
37
|
+
- Coordinate normalization after projection
|
|
38
|
+
- Added demo screenshot to README
|
|
39
|
+
|
|
40
|
+
## 0.2.5 (2026-03-04)
|
|
41
|
+
|
|
42
|
+
### Bug Fixes
|
|
43
|
+
|
|
44
|
+
- Fixed lasso selection stability issues
|
|
45
|
+
- Removed problematic custom traitlets implementation
|
|
46
|
+
|
|
47
|
+
### Improvements
|
|
48
|
+
|
|
49
|
+
- Added keyboard shortcuts: F to fit view, Escape to clear selection
|
|
50
|
+
- Better visual feedback for selection modes
|
|
51
|
+
- Cleaner toolbar layout with integrated mode controls
|
|
52
|
+
|
|
53
|
+
## 0.2.4 (2026-03-02)
|
|
54
|
+
|
|
55
|
+
### New Features
|
|
56
|
+
|
|
57
|
+
- **Zoom and pan controls**: Zoom in, zoom out and fit-to-view buttons in the toolbar
|
|
58
|
+
- **Box selection mode**: New selection mode alongside click and multi-select, with visual feedback during selection
|
|
59
|
+
- **HTML export**: `to_html()` and `save_html()` methods for self-contained HTML output with embedded data and Three.js
|
|
60
|
+
|
|
61
|
+
### Improvements
|
|
62
|
+
|
|
63
|
+
- Improved camera fitting algorithm for better initial view framing
|
|
64
|
+
- Updated README with comprehensive usage examples
|
|
65
|
+
- Updated dependencies
|
|
66
|
+
|
|
67
|
+
## 0.2.3 (2026-02-12)
|
|
68
|
+
|
|
69
|
+
### New Features
|
|
70
|
+
|
|
71
|
+
- **Sidebar explorer**: Three collapsible sections: collections browser, dimensions explorer (point count, dimensionality, axis ranges) and clusters explorer (grouped by color field with sizes)
|
|
72
|
+
- **Browser-side Grafeo backend**: JavaScript client for Grafeo server and WASM-embedded queries
|
|
73
|
+
- **Demo module**: Reference implementation showing various widget features
|
|
74
|
+
|
|
75
|
+
### Improvements
|
|
76
|
+
|
|
77
|
+
- Consistency pass across all backend modules
|
|
78
|
+
- Ruff configuration tuned for marimo compatibility
|
|
79
|
+
|
|
80
|
+
## 0.2.2 (2026-02-07)
|
|
81
|
+
|
|
82
|
+
### New Features
|
|
83
|
+
|
|
84
|
+
- **Streaming factory methods**: `from_numpy()`, `from_umap()` and `from_arrays()` constructors for various data formats
|
|
85
|
+
|
|
86
|
+
### Improvements
|
|
87
|
+
|
|
88
|
+
- Added Apache 2.0 license
|
|
89
|
+
- Expanded test suite with 194 additional test cases
|
|
90
|
+
- ESM bundle aggregation for widget JavaScript
|
|
91
|
+
|
|
92
|
+
## 0.2.1 (2026-02-03)
|
|
93
|
+
|
|
94
|
+
### New Features
|
|
95
|
+
|
|
96
|
+
- **Multi-backend architecture**: Six backends: Qdrant, Pinecone, Weaviate (browser-side REST), Chroma, LanceDB, Grafeo (Python-side)
|
|
97
|
+
- **Modular UI**: Separated rendering into canvas, settings, properties and toolbar components
|
|
98
|
+
- **Backend factory methods**: `from_qdrant()`, `from_pinecone()`, `from_weaviate()`, `from_chroma()`, `from_lancedb()`, `from_grafeo()`
|
|
99
|
+
- **Event system**: `on_click()`, `on_hover()` and `on_selection()` decorators with callback signatures
|
|
100
|
+
- **CI/CD**: GitHub Actions pipelines for testing and PyPI publishing
|
|
101
|
+
|
|
102
|
+
### Improvements
|
|
103
|
+
|
|
104
|
+
- Refactored monolithic widget into modular component architecture
|
|
105
|
+
- Added README with usage examples
|
|
106
|
+
|
|
107
|
+
## 0.1.0 (2026-02-03)
|
|
108
|
+
|
|
109
|
+
- Initial release: Three.js-based interactive 3D vector visualization with instanced rendering, six shape types, continuous and categorical color mapping, orbit controls, hover tooltips, axes/grid display and raycast-based selection
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: anywidget-vector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -33,10 +33,10 @@ Requires-Dist: chromadb>=0.4; extra == 'chroma'
|
|
|
33
33
|
Provides-Extra: demo
|
|
34
34
|
Provides-Extra: dev
|
|
35
35
|
Requires-Dist: marimo>=0.20; extra == 'dev'
|
|
36
|
-
Requires-Dist: prek>=0.3
|
|
36
|
+
Requires-Dist: prek>=0.3; extra == 'dev'
|
|
37
37
|
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
38
38
|
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
39
|
-
Requires-Dist: ty>=0.0
|
|
39
|
+
Requires-Dist: ty>=0.0; extra == 'dev'
|
|
40
40
|
Provides-Extra: embed
|
|
41
41
|
Provides-Extra: grafeo
|
|
42
42
|
Requires-Dist: grafeo>=0.5; extra == 'grafeo'
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Based on the Fashion MNIST Parallel Coordinates notebook by the marimo team:
|
|
2
|
+
# https://github.com/marimo-team/gallery-examples/blob/main/notebooks/wigglystuff/fashion-mnist-parallel-coords.py
|
|
3
|
+
#
|
|
4
|
+
# Extended with anywidget-vector 3D scatter view and 6D dimension mapping.
|
|
5
|
+
#
|
|
6
|
+
# /// script
|
|
7
|
+
# requires-python = ">=3.12"
|
|
8
|
+
# dependencies = [
|
|
9
|
+
# "marimo",
|
|
10
|
+
# "polars==1.38.1",
|
|
11
|
+
# "numpy==2.4.2",
|
|
12
|
+
# "scikit-learn==1.8.0",
|
|
13
|
+
# "wigglystuff==0.2.37",
|
|
14
|
+
# "matplotlib==3.10.8",
|
|
15
|
+
# "pandas==3.0.1",
|
|
16
|
+
# "anywidget-vector==0.2.7",
|
|
17
|
+
# ]
|
|
18
|
+
# ///
|
|
19
|
+
|
|
20
|
+
import marimo
|
|
21
|
+
|
|
22
|
+
__generated_with = "0.19.7"
|
|
23
|
+
app = marimo.App(width="full")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.cell
|
|
27
|
+
def _():
|
|
28
|
+
import marimo as mo
|
|
29
|
+
import matplotlib.pyplot as plt
|
|
30
|
+
import numpy as np
|
|
31
|
+
import polars as pl
|
|
32
|
+
from sklearn.datasets import fetch_openml
|
|
33
|
+
from sklearn.decomposition import PCA
|
|
34
|
+
from wigglystuff import ParallelCoordinates
|
|
35
|
+
|
|
36
|
+
from anywidget_vector import VectorSpace
|
|
37
|
+
|
|
38
|
+
return PCA, ParallelCoordinates, VectorSpace, fetch_openml, mo, np, pl, plt
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.cell(hide_code=True)
|
|
42
|
+
def _(mo):
|
|
43
|
+
mo.md(r"""
|
|
44
|
+
# Fashion MNIST: Parallel Coordinates + 6D Vector View
|
|
45
|
+
|
|
46
|
+
Brush the parallel coordinates axes to filter. The 3D scatter updates in real time.
|
|
47
|
+
Use the dropdowns to map PCA dimensions to visual channels.
|
|
48
|
+
""")
|
|
49
|
+
return
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.cell
|
|
53
|
+
def _(fetch_openml, np):
|
|
54
|
+
mnist = fetch_openml("Fashion-MNIST", version=1, as_frame=False, parser="auto")
|
|
55
|
+
images = mnist.data.astype(np.float32)
|
|
56
|
+
labels = mnist.target.astype(int)
|
|
57
|
+
|
|
58
|
+
label_names = {
|
|
59
|
+
0: "T-shirt/top",
|
|
60
|
+
1: "Trouser",
|
|
61
|
+
2: "Pullover",
|
|
62
|
+
3: "Dress",
|
|
63
|
+
4: "Coat",
|
|
64
|
+
5: "Sandal",
|
|
65
|
+
6: "Shirt",
|
|
66
|
+
7: "Sneaker",
|
|
67
|
+
8: "Bag",
|
|
68
|
+
9: "Ankle boot",
|
|
69
|
+
}
|
|
70
|
+
# d3 schemeCategory10, assigned alphabetically to match wigglystuff
|
|
71
|
+
_d3 = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"]
|
|
72
|
+
LABEL_COLORS = {_name: _d3[_i] for _i, _name in enumerate(sorted(label_names.values()))}
|
|
73
|
+
SHAPE_NAMES = ["sphere", "cube", "cone", "octahedron", "cylinder", "tetrahedron"]
|
|
74
|
+
return LABEL_COLORS, SHAPE_NAMES, images, label_names, labels
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@app.cell
|
|
78
|
+
def _(mo):
|
|
79
|
+
n_samples_slider = mo.ui.slider(start=500, stop=5000, step=500, value=2000, label="Samples")
|
|
80
|
+
n_components_slider = mo.ui.slider(start=3, stop=15, step=1, value=8, label="PCA dims")
|
|
81
|
+
mo.hstack([n_samples_slider, n_components_slider])
|
|
82
|
+
return n_components_slider, n_samples_slider
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@app.cell
|
|
86
|
+
def _(
|
|
87
|
+
PCA,
|
|
88
|
+
images,
|
|
89
|
+
label_names,
|
|
90
|
+
labels,
|
|
91
|
+
n_components_slider,
|
|
92
|
+
n_samples_slider,
|
|
93
|
+
np,
|
|
94
|
+
pl,
|
|
95
|
+
):
|
|
96
|
+
_rng = np.random.default_rng(42)
|
|
97
|
+
idx = _rng.choice(len(images), size=n_samples_slider.value, replace=False)
|
|
98
|
+
|
|
99
|
+
_pca = PCA(n_components=n_components_slider.value)
|
|
100
|
+
components = _pca.fit_transform(images[idx])
|
|
101
|
+
|
|
102
|
+
df = pl.DataFrame({f"PC{i + 1}": components[:, i] for i in range(n_components_slider.value)}).with_columns(
|
|
103
|
+
pl.Series("label", [label_names[labels[i]] for i in idx])
|
|
104
|
+
)
|
|
105
|
+
return components, df, idx
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@app.cell
|
|
109
|
+
def _(LABEL_COLORS, ParallelCoordinates, df, mo):
|
|
110
|
+
widget = mo.ui.anywidget(ParallelCoordinates(df, color_by="label", color_map=LABEL_COLORS))
|
|
111
|
+
widget
|
|
112
|
+
return (widget,)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@app.cell
|
|
116
|
+
def _(mo, n_components_slider):
|
|
117
|
+
pcs = [f"PC{i + 1}" for i in range(n_components_slider.value)]
|
|
118
|
+
x_dim = mo.ui.dropdown(options=pcs, value="PC1", label="X")
|
|
119
|
+
y_dim = mo.ui.dropdown(options=pcs, value="PC2", label="Y")
|
|
120
|
+
z_dim = mo.ui.dropdown(options=pcs, value="PC3", label="Z")
|
|
121
|
+
size_dim = mo.ui.dropdown(options=["none", *pcs], value="PC4", label="Size")
|
|
122
|
+
color_dim = mo.ui.dropdown(options=["label", *pcs], value="label", label="Color")
|
|
123
|
+
shape_dim = mo.ui.dropdown(options=["label", "none", *pcs], value="PC5", label="Shape")
|
|
124
|
+
mo.hstack([x_dim, y_dim, z_dim, size_dim, color_dim, shape_dim], gap=0.5)
|
|
125
|
+
return color_dim, shape_dim, size_dim, x_dim, y_dim, z_dim
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@app.cell
|
|
129
|
+
def _(
|
|
130
|
+
LABEL_COLORS,
|
|
131
|
+
SHAPE_NAMES,
|
|
132
|
+
VectorSpace,
|
|
133
|
+
color_dim,
|
|
134
|
+
components,
|
|
135
|
+
idx,
|
|
136
|
+
label_names,
|
|
137
|
+
labels,
|
|
138
|
+
mo,
|
|
139
|
+
np,
|
|
140
|
+
shape_dim,
|
|
141
|
+
size_dim,
|
|
142
|
+
x_dim,
|
|
143
|
+
y_dim,
|
|
144
|
+
z_dim,
|
|
145
|
+
):
|
|
146
|
+
def _pc(name):
|
|
147
|
+
return int(name[2:]) - 1
|
|
148
|
+
|
|
149
|
+
_xi, _yi, _zi = _pc(x_dim.value), _pc(y_dim.value), _pc(z_dim.value)
|
|
150
|
+
_color_pc = None if color_dim.value == "label" else _pc(color_dim.value)
|
|
151
|
+
_size_pc = None if size_dim.value == "none" else _pc(size_dim.value)
|
|
152
|
+
_shape_mode = shape_dim.value # "label", "none", or a PC name
|
|
153
|
+
|
|
154
|
+
# Shape mapping: label-based or PC-based (binned into 6 shape buckets)
|
|
155
|
+
_unique_labels = sorted(set(label_names.values()))
|
|
156
|
+
_shape_map = {_n: SHAPE_NAMES[_j % len(SHAPE_NAMES)] for _j, _n in enumerate(_unique_labels)}
|
|
157
|
+
_shape_bins = None
|
|
158
|
+
if _shape_mode not in ("label", "none"):
|
|
159
|
+
_shape_col = components[:, _pc(_shape_mode)]
|
|
160
|
+
_quantiles = np.percentile(_shape_col, np.linspace(0, 100, len(SHAPE_NAMES) + 1))
|
|
161
|
+
_shape_bins = list(zip(_quantiles[:-1], _quantiles[1:], SHAPE_NAMES, strict=False))
|
|
162
|
+
_shape_map = {_s: _s for _s in SHAPE_NAMES}
|
|
163
|
+
|
|
164
|
+
def _get_shape_bin(_val):
|
|
165
|
+
for _lo, _hi, _s in _shape_bins:
|
|
166
|
+
if _val <= _hi:
|
|
167
|
+
return _s
|
|
168
|
+
return SHAPE_NAMES[-1]
|
|
169
|
+
|
|
170
|
+
vs_points = []
|
|
171
|
+
for _i in range(len(components)):
|
|
172
|
+
_name = label_names[labels[idx[_i]]]
|
|
173
|
+
_p = {
|
|
174
|
+
"id": f"p_{_i}",
|
|
175
|
+
"x": float(components[_i, _xi]),
|
|
176
|
+
"y": float(components[_i, _yi]),
|
|
177
|
+
"z": float(components[_i, _zi]),
|
|
178
|
+
"label": _name,
|
|
179
|
+
}
|
|
180
|
+
if _color_pc is None:
|
|
181
|
+
_p["color"] = LABEL_COLORS[_name]
|
|
182
|
+
else:
|
|
183
|
+
_p["color_val"] = float(components[_i, _color_pc])
|
|
184
|
+
if _size_pc is not None:
|
|
185
|
+
_p["size_val"] = float(components[_i, _size_pc])
|
|
186
|
+
if _shape_mode == "label":
|
|
187
|
+
_p["shape_cat"] = _name
|
|
188
|
+
elif _shape_bins is not None:
|
|
189
|
+
_p["shape_cat"] = _get_shape_bin(components[_i, _pc(_shape_mode)])
|
|
190
|
+
vs_points.append(_p)
|
|
191
|
+
|
|
192
|
+
_vs_kwargs = {
|
|
193
|
+
"axis_labels": {"x": x_dim.value, "y": y_dim.value, "z": z_dim.value},
|
|
194
|
+
}
|
|
195
|
+
if _color_pc is not None:
|
|
196
|
+
_vs_kwargs["color_field"] = "color_val"
|
|
197
|
+
_vs_kwargs["color_scale"] = "viridis"
|
|
198
|
+
if _size_pc is not None:
|
|
199
|
+
_vs_kwargs["size_field"] = "size_val"
|
|
200
|
+
_vs_kwargs["size_range"] = [0.01, 0.03]
|
|
201
|
+
if _shape_mode != "none":
|
|
202
|
+
_vs_kwargs["shape_field"] = "shape_cat"
|
|
203
|
+
_vs_kwargs["shape_map"] = _shape_map
|
|
204
|
+
|
|
205
|
+
vs_widget = VectorSpace(
|
|
206
|
+
points=vs_points,
|
|
207
|
+
width=1600,
|
|
208
|
+
height=500,
|
|
209
|
+
show_toolbar=True,
|
|
210
|
+
show_settings=True,
|
|
211
|
+
show_properties=False,
|
|
212
|
+
**_vs_kwargs,
|
|
213
|
+
)
|
|
214
|
+
vs = mo.ui.anywidget(vs_widget)
|
|
215
|
+
vs
|
|
216
|
+
return vs_points, vs_widget
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@app.cell
|
|
220
|
+
def _(LABEL_COLORS, color_dim, mo, vs_points, vs_widget, widget):
|
|
221
|
+
_filtered = set(widget.widget.filtered_indices)
|
|
222
|
+
_dim_color = "#d1d5db"
|
|
223
|
+
_updated = []
|
|
224
|
+
for _i, _p in enumerate(vs_points):
|
|
225
|
+
_new_p = dict(_p)
|
|
226
|
+
if _i in _filtered:
|
|
227
|
+
if color_dim.value == "label":
|
|
228
|
+
_new_p["color"] = LABEL_COLORS[_p["label"]]
|
|
229
|
+
else:
|
|
230
|
+
_new_p.pop("color", None)
|
|
231
|
+
else:
|
|
232
|
+
_new_p["color"] = _dim_color
|
|
233
|
+
_updated.append(_new_p)
|
|
234
|
+
vs_widget.points = _updated
|
|
235
|
+
mo.md(f"**{len(_filtered)}** / {len(vs_points)} points selected")
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@app.cell
|
|
240
|
+
def _(LABEL_COLORS, idx, images, label_names, labels, np, plt, vs_widget, widget):
|
|
241
|
+
_filtered = widget.widget.filtered_indices
|
|
242
|
+
_sample_idx = np.array(_filtered[:10]) if len(_filtered) >= 10 else np.array(_filtered)
|
|
243
|
+
|
|
244
|
+
# Which points are selected in the 3D view?
|
|
245
|
+
_selected_ids = set(vs_widget.selected_points or [])
|
|
246
|
+
|
|
247
|
+
_fig, _axes = plt.subplots(1, len(_sample_idx), figsize=(2 * len(_sample_idx), 2.4))
|
|
248
|
+
if len(_sample_idx) == 1:
|
|
249
|
+
_axes = [_axes]
|
|
250
|
+
for _ax, _si in zip(_axes, _sample_idx, strict=False):
|
|
251
|
+
_name = label_names[labels[idx[_si]]]
|
|
252
|
+
_ax.imshow(images[idx[_si]].reshape(28, 28), cmap="gray")
|
|
253
|
+
_ax.set_title(_name, fontsize=9)
|
|
254
|
+
_ax.axis("off")
|
|
255
|
+
# Highlight if selected in 3D view
|
|
256
|
+
if f"p_{_si}" in _selected_ids:
|
|
257
|
+
_color = LABEL_COLORS.get(_name, "#0880ea")
|
|
258
|
+
for _spine in _ax.spines.values():
|
|
259
|
+
_spine.set_visible(True)
|
|
260
|
+
_spine.set_color(_color)
|
|
261
|
+
_spine.set_linewidth(3)
|
|
262
|
+
_ax.set_title(_name, fontsize=9, fontweight="bold", color=_color)
|
|
263
|
+
plt.tight_layout()
|
|
264
|
+
_fig
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
app.run()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "anywidget-vector"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Interactive 3D vector visualization with query UI for vector databases"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -26,10 +26,10 @@ dependencies = [
|
|
|
26
26
|
|
|
27
27
|
[project.optional-dependencies]
|
|
28
28
|
dev = [
|
|
29
|
-
"prek>=0.3
|
|
29
|
+
"prek>=0.3",
|
|
30
30
|
"pytest>=9.0.2",
|
|
31
31
|
"ruff>=0.15",
|
|
32
|
-
"ty>=0.0
|
|
32
|
+
"ty>=0.0",
|
|
33
33
|
"marimo>=0.20",
|
|
34
34
|
]
|
|
35
35
|
pandas = ["pandas>=2.0"]
|
|
@@ -122,16 +122,47 @@ async function executeBackendQuery(model) {{
|
|
|
122
122
|
function render({{ model, el }}) {{
|
|
123
123
|
const wrapper = document.createElement("div");
|
|
124
124
|
wrapper.className = "avs-wrapper";
|
|
125
|
-
if (model.get("dark_mode")) wrapper.classList.add("avs-dark");
|
|
126
125
|
wrapper.style.width = model.get("width") + "px";
|
|
127
126
|
wrapper.style.height = model.get("height") + "px";
|
|
128
127
|
el.appendChild(wrapper);
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
// Auto-detect host theme (marimo uses Tailwind class="dark" on <html>)
|
|
130
|
+
function detectHostDark() {{
|
|
131
|
+
const html = document.documentElement;
|
|
132
|
+
if (html.classList.contains("dark")) return true;
|
|
133
|
+
if (html.dataset.theme === "dark") return true;
|
|
134
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) return true;
|
|
135
|
+
return false;
|
|
136
|
+
}}
|
|
137
|
+
|
|
138
|
+
function applyTheme(dark) {{
|
|
132
139
|
wrapper.classList.toggle("avs-dark", dark);
|
|
133
|
-
model.set("background", dark ? "#
|
|
140
|
+
model.set("background", dark ? "#181c1a" : "#ffffff");
|
|
134
141
|
model.save_changes();
|
|
142
|
+
}}
|
|
143
|
+
|
|
144
|
+
// Detect if we're inside a themed host (marimo, jupyter, etc.)
|
|
145
|
+
const hasHostTheme = !!el.getRootNode()?.host?.tagName?.startsWith("MARIMO-");
|
|
146
|
+
if (hasHostTheme) {{
|
|
147
|
+
wrapper.classList.add("avs-auto-theme");
|
|
148
|
+
model.set("dark_mode", detectHostDark());
|
|
149
|
+
applyTheme(detectHostDark());
|
|
150
|
+
// Watch host for live theme changes
|
|
151
|
+
const themeObserver = new MutationObserver(() => {{
|
|
152
|
+
const dark = detectHostDark();
|
|
153
|
+
if (model.get("dark_mode") !== dark) {{
|
|
154
|
+
model.set("dark_mode", dark);
|
|
155
|
+
applyTheme(dark);
|
|
156
|
+
}}
|
|
157
|
+
}});
|
|
158
|
+
themeObserver.observe(document.documentElement, {{ attributes: true, attributeFilter: ["class", "data-theme"] }});
|
|
159
|
+
}} else {{
|
|
160
|
+
applyTheme(model.get("dark_mode"));
|
|
161
|
+
}}
|
|
162
|
+
|
|
163
|
+
// Manual toggle from settings panel still works
|
|
164
|
+
model.on("change:dark_mode", () => {{
|
|
165
|
+
applyTheme(model.get("dark_mode"));
|
|
135
166
|
}});
|
|
136
167
|
|
|
137
168
|
let sidebar = null;
|
|
@@ -175,12 +175,20 @@ export function createCanvas(model, container, callbacks) {
|
|
|
175
175
|
const points = model.get("points") || [];
|
|
176
176
|
if (points.length === 0) return;
|
|
177
177
|
|
|
178
|
+
// Auto-scale point sizes relative to data extent
|
|
179
|
+
const box = new THREE.Box3();
|
|
180
|
+
points.forEach(p => box.expandByPoint(new THREE.Vector3(p.x ?? 0, p.y ?? 0, p.z ?? 0)));
|
|
181
|
+
const dataSize = box.getSize(new THREE.Vector3()).length() || 1;
|
|
182
|
+
const scaleFactor = dataSize / 10;
|
|
183
|
+
const rawRange = model.get("size_range") || [0.02, 0.06];
|
|
184
|
+
const sizeRange = [rawRange[0] * scaleFactor, rawRange[1] * scaleFactor];
|
|
185
|
+
|
|
178
186
|
const opts = {
|
|
179
187
|
colorField: model.get("color_field"),
|
|
180
188
|
colorScale: model.get("color_scale") || "viridis",
|
|
181
189
|
colorDomain: model.get("color_domain"),
|
|
182
190
|
sizeField: model.get("size_field"),
|
|
183
|
-
sizeRange:
|
|
191
|
+
sizeRange: sizeRange,
|
|
184
192
|
shapeField: model.get("shape_field"),
|
|
185
193
|
shapeMap: model.get("shape_map") || {},
|
|
186
194
|
};
|
|
@@ -460,7 +468,10 @@ export function createCanvas(model, container, callbacks) {
|
|
|
460
468
|
const size = box.getSize(new THREE.Vector3()).length();
|
|
461
469
|
const distance = size / (2 * Math.tan(Math.PI * camera.fov / 360));
|
|
462
470
|
controls.target.copy(center);
|
|
463
|
-
camera.position.copy(center.clone().add(new THREE.Vector3(0, 0, distance *
|
|
471
|
+
camera.position.copy(center.clone().add(new THREE.Vector3(0, 0, distance * 0.55)));
|
|
472
|
+
camera.near = Math.max(0.01, distance * 0.001);
|
|
473
|
+
camera.far = Math.max(1000, distance * 10);
|
|
474
|
+
camera.updateProjectionMatrix();
|
|
464
475
|
controls.update();
|
|
465
476
|
}
|
|
466
477
|
|
|
@@ -482,8 +493,8 @@ export function createCanvas(model, container, callbacks) {
|
|
|
482
493
|
zoomOutBtn.innerHTML = ICONS.zoomOut;
|
|
483
494
|
zoomOutBtn.title = "Zoom out";
|
|
484
495
|
zoomOutBtn.addEventListener("click", () => {
|
|
485
|
-
const
|
|
486
|
-
camera.position.add(
|
|
496
|
+
const offset = camera.position.clone().sub(controls.target);
|
|
497
|
+
camera.position.add(offset.multiplyScalar(0.3));
|
|
487
498
|
controls.update();
|
|
488
499
|
});
|
|
489
500
|
|
|
@@ -21,8 +21,9 @@ export function createSettingsPanel(model, callbacks) {
|
|
|
21
21
|
header.appendChild(closeBtn);
|
|
22
22
|
inner.appendChild(header);
|
|
23
23
|
|
|
24
|
-
// Dark mode toggle
|
|
24
|
+
// Dark mode toggle (hidden when host provides theme)
|
|
25
25
|
const themeGroup = createFormGroup("Theme");
|
|
26
|
+
themeGroup.classList.add("avs-theme-toggle");
|
|
26
27
|
const toggle = document.createElement("label");
|
|
27
28
|
toggle.className = "avs-toggle";
|
|
28
29
|
const checkbox = document.createElement("input");
|
|
@@ -114,6 +114,22 @@ export function createSidebar(model, callbacks) {
|
|
|
114
114
|
addStatRow(dimContent, "X range", rangeStr(xs));
|
|
115
115
|
addStatRow(dimContent, "Y range", rangeStr(ys));
|
|
116
116
|
addStatRow(dimContent, "Z range", rangeStr(zs));
|
|
117
|
+
|
|
118
|
+
// Visual mapping dimensions
|
|
119
|
+
const colorField = model.get("color_field");
|
|
120
|
+
const sizeField = model.get("size_field");
|
|
121
|
+
const shapeField = model.get("shape_field");
|
|
122
|
+
if (colorField) addStatRow(dimContent, "Color", colorField);
|
|
123
|
+
if (sizeField) {
|
|
124
|
+
const sizeRange = model.get("size_range") || [0.02, 0.1];
|
|
125
|
+
addStatRow(dimContent, "Size", sizeField + " [" + sizeRange[0] + "," + sizeRange[1] + "]");
|
|
126
|
+
}
|
|
127
|
+
if (shapeField) {
|
|
128
|
+
const shapeMap = model.get("shape_map") || {};
|
|
129
|
+
const shapes = Object.values(shapeMap);
|
|
130
|
+
const unique = [...new Set(shapes)];
|
|
131
|
+
addStatRow(dimContent, "Shape", shapeField + " (" + unique.length + " shapes)");
|
|
132
|
+
}
|
|
117
133
|
}
|
|
118
134
|
|
|
119
135
|
function updateClusters() {
|
|
@@ -159,6 +175,107 @@ export function createSidebar(model, callbacks) {
|
|
|
159
175
|
});
|
|
160
176
|
}
|
|
161
177
|
|
|
178
|
+
// === Distance Section ===
|
|
179
|
+
const distSection = document.createElement("div");
|
|
180
|
+
distSection.className = "avs-sidebar-section";
|
|
181
|
+
|
|
182
|
+
const distHeader = document.createElement("div");
|
|
183
|
+
distHeader.className = "avs-section-header";
|
|
184
|
+
distHeader.textContent = "Distance";
|
|
185
|
+
distSection.appendChild(distHeader);
|
|
186
|
+
|
|
187
|
+
const distContent = document.createElement("div");
|
|
188
|
+
distContent.className = "avs-dimension-content";
|
|
189
|
+
|
|
190
|
+
// Metric selector
|
|
191
|
+
const metricGroup = document.createElement("div");
|
|
192
|
+
metricGroup.className = "avs-form-group";
|
|
193
|
+
const metricLabel = document.createElement("label");
|
|
194
|
+
metricLabel.className = "avs-label";
|
|
195
|
+
metricLabel.textContent = "Metric";
|
|
196
|
+
metricGroup.appendChild(metricLabel);
|
|
197
|
+
const metricSelect = document.createElement("select");
|
|
198
|
+
metricSelect.className = "avs-select";
|
|
199
|
+
metricSelect.style.width = "100%";
|
|
200
|
+
["euclidean", "cosine", "manhattan", "dot_product"].forEach(m => {
|
|
201
|
+
const opt = document.createElement("option");
|
|
202
|
+
opt.value = m;
|
|
203
|
+
opt.textContent = m;
|
|
204
|
+
opt.selected = m === (model.get("distance_metric") || "euclidean");
|
|
205
|
+
metricSelect.appendChild(opt);
|
|
206
|
+
});
|
|
207
|
+
metricSelect.addEventListener("change", () => {
|
|
208
|
+
model.set("distance_metric", metricSelect.value);
|
|
209
|
+
model.save_changes();
|
|
210
|
+
});
|
|
211
|
+
metricGroup.appendChild(metricSelect);
|
|
212
|
+
distContent.appendChild(metricGroup);
|
|
213
|
+
|
|
214
|
+
// K neighbors
|
|
215
|
+
const kGroup = document.createElement("div");
|
|
216
|
+
kGroup.className = "avs-form-group";
|
|
217
|
+
const kLabel = document.createElement("label");
|
|
218
|
+
kLabel.className = "avs-label";
|
|
219
|
+
kLabel.textContent = "K neighbors";
|
|
220
|
+
kGroup.appendChild(kLabel);
|
|
221
|
+
const kInput = document.createElement("input");
|
|
222
|
+
kInput.type = "number";
|
|
223
|
+
kInput.className = "avs-input";
|
|
224
|
+
kInput.min = "0";
|
|
225
|
+
kInput.max = "50";
|
|
226
|
+
kInput.value = model.get("k_neighbors") || 0;
|
|
227
|
+
kInput.addEventListener("input", () => {
|
|
228
|
+
const k = parseInt(kInput.value, 10) || 0;
|
|
229
|
+
model.set("k_neighbors", k);
|
|
230
|
+
model.set("show_connections", k > 0);
|
|
231
|
+
model.save_changes();
|
|
232
|
+
});
|
|
233
|
+
kGroup.appendChild(kInput);
|
|
234
|
+
distContent.appendChild(kGroup);
|
|
235
|
+
|
|
236
|
+
// Distance info (updates when points are selected)
|
|
237
|
+
const distInfo = document.createElement("div");
|
|
238
|
+
distInfo.className = "avs-note";
|
|
239
|
+
distInfo.textContent = "Select a point to see distances";
|
|
240
|
+
distContent.appendChild(distInfo);
|
|
241
|
+
|
|
242
|
+
distSection.appendChild(distContent);
|
|
243
|
+
inner.appendChild(distSection);
|
|
244
|
+
|
|
245
|
+
function updateDistanceInfo() {
|
|
246
|
+
const selected = model.get("selected_points") || [];
|
|
247
|
+
if (selected.length === 0) {
|
|
248
|
+
distInfo.textContent = "Select a point to see distances";
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const points = model.get("points") || [];
|
|
252
|
+
const ref = points.find(p => p.id === selected[0]);
|
|
253
|
+
if (!ref) return;
|
|
254
|
+
|
|
255
|
+
const metric = metricSelect.value;
|
|
256
|
+
const others = points.filter(p => p.id !== ref.id);
|
|
257
|
+
|
|
258
|
+
function dist(a, b) {
|
|
259
|
+
const dx = (a.x ?? 0) - (b.x ?? 0), dy = (a.y ?? 0) - (b.y ?? 0), dz = (a.z ?? 0) - (b.z ?? 0);
|
|
260
|
+
if (metric === "manhattan") return Math.abs(dx) + Math.abs(dy) + Math.abs(dz);
|
|
261
|
+
if (metric === "cosine") {
|
|
262
|
+
const dot = (a.x??0)*(b.x??0) + (a.y??0)*(b.y??0) + (a.z??0)*(b.z??0);
|
|
263
|
+
const ma = Math.sqrt((a.x??0)**2 + (a.y??0)**2 + (a.z??0)**2);
|
|
264
|
+
const mb = Math.sqrt((b.x??0)**2 + (b.y??0)**2 + (b.z??0)**2);
|
|
265
|
+
return (ma === 0 || mb === 0) ? 1 : 1 - dot / (ma * mb);
|
|
266
|
+
}
|
|
267
|
+
if (metric === "dot_product") return -((a.x??0)*(b.x??0) + (a.y??0)*(b.y??0) + (a.z??0)*(b.z??0));
|
|
268
|
+
return Math.sqrt(dx*dx + dy*dy + dz*dz);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const sorted = others.map(p => ({ id: p.id, label: p.label || p.id, d: dist(ref, p) }))
|
|
272
|
+
.sort((a, b) => a.d - b.d).slice(0, 5);
|
|
273
|
+
|
|
274
|
+
distInfo.innerHTML = "<strong>" + escapeHtml(ref.label || ref.id) + "</strong><br>" +
|
|
275
|
+
sorted.map(n => '<span class="avs-property-key">' + escapeHtml(n.label) +
|
|
276
|
+
'</span> <span class="avs-property-value">' + n.d.toFixed(2) + "</span>").join("<br>");
|
|
277
|
+
}
|
|
278
|
+
|
|
162
279
|
// === Event Bindings ===
|
|
163
280
|
model.on("change:points", () => {
|
|
164
281
|
updateDimensions();
|
|
@@ -167,6 +284,11 @@ export function createSidebar(model, callbacks) {
|
|
|
167
284
|
model.on("change:color_field", updateClusters);
|
|
168
285
|
model.on("change:backend_config", updateCollections);
|
|
169
286
|
model.on("change:backend", updateCollections);
|
|
287
|
+
model.on("change:selected_points", updateDistanceInfo);
|
|
288
|
+
model.on("change:distance_metric", () => {
|
|
289
|
+
metricSelect.value = model.get("distance_metric") || "euclidean";
|
|
290
|
+
updateDistanceInfo();
|
|
291
|
+
});
|
|
170
292
|
|
|
171
293
|
// Initial render
|
|
172
294
|
updateCollections();
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/* === CSS Variables === */
|
|
2
2
|
|
|
3
|
-
/* Light mode (default) */
|
|
3
|
+
/* Light mode (default, matches marimo light) */
|
|
4
4
|
.avs-wrapper {
|
|
5
5
|
--avs-bg: #ffffff;
|
|
6
6
|
--avs-surface: #f8f9fa;
|
|
7
|
-
--avs-border: #
|
|
8
|
-
--avs-text: #
|
|
7
|
+
--avs-border: #e2e8f0;
|
|
8
|
+
--avs-text: #0f172a;
|
|
9
9
|
--avs-text-muted: #6b7280;
|
|
10
|
-
--avs-primary: #
|
|
11
|
-
--avs-primary-hover: #
|
|
10
|
+
--avs-primary: #0880ea;
|
|
11
|
+
--avs-primary-hover: #0670d0;
|
|
12
12
|
--avs-success: #10b981;
|
|
13
13
|
--avs-warning: #f59e0b;
|
|
14
14
|
--avs-error: #ef4444;
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
flex-direction: column;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/* Dark mode */
|
|
26
|
+
/* Dark mode (matches marimo dark) */
|
|
27
27
|
.avs-wrapper.avs-dark {
|
|
28
|
-
--avs-bg: #
|
|
29
|
-
--avs-surface: #
|
|
30
|
-
--avs-border: #
|
|
31
|
-
--avs-text: #
|
|
32
|
-
--avs-text-muted: #
|
|
33
|
-
--avs-primary: #
|
|
34
|
-
--avs-primary-hover: #
|
|
28
|
+
--avs-bg: #181c1a;
|
|
29
|
+
--avs-surface: #252927;
|
|
30
|
+
--avs-border: #3b403e;
|
|
31
|
+
--avs-text: #eceeed;
|
|
32
|
+
--avs-text-muted: #9ca3af;
|
|
33
|
+
--avs-primary: #28879f;
|
|
34
|
+
--avs-primary-hover: #1d5b6a;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
/* === Main Layout === */
|
|
@@ -601,3 +601,8 @@
|
|
|
601
601
|
font-size: 13px;
|
|
602
602
|
color: var(--avs-text);
|
|
603
603
|
}
|
|
604
|
+
|
|
605
|
+
/* Hide dark mode toggle when host provides theme */
|
|
606
|
+
.avs-wrapper.avs-auto-theme .avs-theme-toggle {
|
|
607
|
+
display: none;
|
|
608
|
+
}
|
|
@@ -54,7 +54,7 @@ class VectorSpace(anywidget.AnyWidget):
|
|
|
54
54
|
|
|
55
55
|
# === Size Mapping ===
|
|
56
56
|
size_field = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
|
|
57
|
-
size_range = traitlets.List(default_value=[0.02, 0.
|
|
57
|
+
size_range = traitlets.List(default_value=[0.02, 0.06]).tag(sync=True)
|
|
58
58
|
|
|
59
59
|
# === Shape Mapping ===
|
|
60
60
|
shape_field = traitlets.Unicode(default_value=None, allow_none=True).tag(sync=True)
|
|
@@ -50,7 +50,7 @@ wheels = [
|
|
|
50
50
|
|
|
51
51
|
[[package]]
|
|
52
52
|
name = "anywidget-vector"
|
|
53
|
-
version = "0.
|
|
53
|
+
version = "0.3.0"
|
|
54
54
|
source = { editable = "." }
|
|
55
55
|
dependencies = [
|
|
56
56
|
{ name = "anywidget" },
|
|
@@ -123,13 +123,13 @@ requires-dist = [
|
|
|
123
123
|
{ name = "pandas", marker = "extra == 'pandas'", specifier = ">=2.0" },
|
|
124
124
|
{ name = "pinecone-client", marker = "extra == 'all'", specifier = ">=6.0" },
|
|
125
125
|
{ name = "pinecone-client", marker = "extra == 'pinecone'", specifier = ">=3.0" },
|
|
126
|
-
{ name = "prek", marker = "extra == 'dev'", specifier = ">=0.3
|
|
126
|
+
{ name = "prek", marker = "extra == 'dev'", specifier = ">=0.3" },
|
|
127
127
|
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.2" },
|
|
128
128
|
{ name = "qdrant-client", marker = "extra == 'all'", specifier = ">=1" },
|
|
129
129
|
{ name = "qdrant-client", marker = "extra == 'qdrant'", specifier = ">=1.0" },
|
|
130
130
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15" },
|
|
131
131
|
{ name = "scikit-learn", marker = "extra == 'projection'", specifier = ">=1.3" },
|
|
132
|
-
{ name = "ty", marker = "extra == 'dev'", specifier = ">=0.0
|
|
132
|
+
{ name = "ty", marker = "extra == 'dev'", specifier = ">=0.0" },
|
|
133
133
|
{ name = "umap-learn", marker = "extra == 'projection'", specifier = ">=0.5" },
|
|
134
134
|
{ name = "weaviate-client", marker = "extra == 'all'", specifier = ">=4.20" },
|
|
135
135
|
{ name = "weaviate-client", marker = "extra == 'weaviate'", specifier = ">=4.0" },
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/client.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/chroma/converter.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/browser.js
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/client.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/converter.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/grafeo/embed.js
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/client.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/lancedb/converter.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/pinecone/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/pinecone/client.js
RENAMED
|
File without changes
|
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/client.js
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/qdrant/converter.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/weaviate/__init__.py
RENAMED
|
File without changes
|
{anywidget_vector-0.2.7 → anywidget_vector-0.3.0}/src/anywidget_vector/backends/weaviate/client.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|