anywidget-vector 0.3.2__tar.gz → 0.4.1__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 (94) hide show
  1. anywidget_vector-0.4.1/.github/workflows/ci.yml +98 -0
  2. anywidget_vector-0.4.1/.github/workflows/grafeo-latest.yml +72 -0
  3. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/.github/workflows/pypi.yml +20 -11
  4. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/.gitignore +1 -1
  5. anywidget_vector-0.4.1/CHANGELOG.md +218 -0
  6. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/PKG-INFO +129 -13
  7. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/README.md +114 -2
  8. anywidget_vector-0.4.1/package-lock.json +996 -0
  9. anywidget_vector-0.4.1/package.json +22 -0
  10. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/pyproject.toml +35 -16
  11. anywidget_vector-0.4.1/scripts/build-esm.mjs +65 -0
  12. anywidget_vector-0.4.1/scripts/vendor/build.mjs +40 -0
  13. anywidget_vector-0.4.1/scripts/vendor/entry.mjs +70 -0
  14. anywidget_vector-0.4.1/scripts/vendor/package-lock.json +506 -0
  15. anywidget_vector-0.4.1/scripts/vendor/package.json +15 -0
  16. anywidget_vector-0.4.1/src/anywidget_vector/__init__.py +13 -0
  17. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/__init__.py +23 -0
  18. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/browser.ts +65 -0
  19. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/client.py +53 -0
  20. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/converter.py +133 -0
  21. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/embed.ts +130 -0
  22. anywidget_vector-0.4.1/src/anywidget_vector/backends/grafeo/points.ts +119 -0
  23. anywidget_vector-0.3.2/src/anywidget_vector/backends/pinecone/client.js → anywidget_vector-0.4.1/src/anywidget_vector/backends/pinecone/client.ts +13 -7
  24. anywidget_vector-0.3.2/src/anywidget_vector/backends/qdrant/client.js → anywidget_vector-0.4.1/src/anywidget_vector/backends/qdrant/client.ts +6 -5
  25. anywidget_vector-0.3.2/src/anywidget_vector/backends/weaviate/client.js → anywidget_vector-0.4.1/src/anywidget_vector/backends/weaviate/client.ts +9 -10
  26. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/demo.py +1 -1
  27. anywidget_vector-0.3.2/src/anywidget_vector/static/icons.js → anywidget_vector-0.4.1/src/anywidget_vector/static/icons.ts +2 -1
  28. anywidget_vector-0.4.1/src/anywidget_vector/ui/__init__.py +47 -0
  29. anywidget_vector-0.3.2/src/anywidget_vector/ui/canvas.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/canvas.ts +193 -131
  30. anywidget_vector-0.3.2/src/anywidget_vector/ui/constants.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/constants.ts +19 -3
  31. anywidget_vector-0.4.1/src/anywidget_vector/ui/dist/export.js +233 -0
  32. anywidget_vector-0.4.1/src/anywidget_vector/ui/dist/widget.js +2446 -0
  33. anywidget_vector-0.4.1/src/anywidget_vector/ui/export.html +43 -0
  34. anywidget_vector-0.4.1/src/anywidget_vector/ui/export.ts +214 -0
  35. anywidget_vector-0.4.1/src/anywidget_vector/ui/index.ts +171 -0
  36. anywidget_vector-0.3.2/src/anywidget_vector/ui/properties.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/properties.ts +24 -18
  37. anywidget_vector-0.4.1/src/anywidget_vector/ui/query.ts +60 -0
  38. anywidget_vector-0.3.2/src/anywidget_vector/ui/settings.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/settings.ts +15 -14
  39. anywidget_vector-0.3.2/src/anywidget_vector/ui/sidebar.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/sidebar.ts +34 -28
  40. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/ui/styles.css +8 -2
  41. anywidget_vector-0.4.1/src/anywidget_vector/ui/theme.ts +134 -0
  42. anywidget_vector-0.3.2/src/anywidget_vector/ui/toolbar.js → anywidget_vector-0.4.1/src/anywidget_vector/ui/toolbar.ts +13 -7
  43. anywidget_vector-0.4.1/src/anywidget_vector/ui/types.ts +75 -0
  44. anywidget_vector-0.4.1/src/anywidget_vector/ui/vendor/three.LICENSES.txt +25 -0
  45. anywidget_vector-0.4.1/src/anywidget_vector/ui/vendor/three.min.js +4220 -0
  46. anywidget_vector-0.4.1/src/anywidget_vector/ui/vendor-globals.ts +15 -0
  47. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/widget.py +219 -335
  48. anywidget_vector-0.4.1/tests/conftest.py +20 -0
  49. anywidget_vector-0.4.1/tests/js/export.test.ts +109 -0
  50. anywidget_vector-0.4.1/tests/js/package-lock.json +528 -0
  51. anywidget_vector-0.4.1/tests/js/package.json +7 -0
  52. anywidget_vector-0.4.1/tests/js/render.test.ts +221 -0
  53. anywidget_vector-0.4.1/tests/js/wasm_smoke.ts +111 -0
  54. anywidget_vector-0.4.1/tests/support.py +57 -0
  55. anywidget_vector-0.4.1/tests/test_grafeo_backend.py +202 -0
  56. anywidget_vector-0.4.1/tests/test_grafeo_contract.py +383 -0
  57. anywidget_vector-0.4.1/tests/test_theme.py +340 -0
  58. anywidget_vector-0.4.1/tests/test_ui_bundle.py +566 -0
  59. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/tests/test_widget.py +21 -3
  60. anywidget_vector-0.4.1/tests/test_widget_robustness.py +309 -0
  61. anywidget_vector-0.4.1/tsconfig.json +30 -0
  62. anywidget_vector-0.4.1/uv.lock +4228 -0
  63. anywidget_vector-0.3.2/.github/workflows/ci.yml +0 -71
  64. anywidget_vector-0.3.2/CHANGELOG.md +0 -145
  65. anywidget_vector-0.3.2/src/anywidget_vector/__init__.py +0 -6
  66. anywidget_vector-0.3.2/src/anywidget_vector/backends/grafeo/__init__.py +0 -20
  67. anywidget_vector-0.3.2/src/anywidget_vector/backends/grafeo/browser.js +0 -90
  68. anywidget_vector-0.3.2/src/anywidget_vector/backends/grafeo/client.py +0 -33
  69. anywidget_vector-0.3.2/src/anywidget_vector/backends/grafeo/converter.py +0 -46
  70. anywidget_vector-0.3.2/src/anywidget_vector/backends/grafeo/embed.js +0 -95
  71. anywidget_vector-0.3.2/src/anywidget_vector/ui/__init__.py +0 -340
  72. anywidget_vector-0.3.2/uv.lock +0 -3122
  73. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/.python-version +0 -0
  74. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/LICENSE +0 -0
  75. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/assets/demo.png +0 -0
  76. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/examples/demo.py +0 -0
  77. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/examples/fashion-mnist-parallel-coords-6d.py +0 -0
  78. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/examples/test_add_numpy.py +0 -0
  79. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/examples/test_projection.py +0 -0
  80. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/__init__.py +0 -0
  81. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/chroma/__init__.py +0 -0
  82. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/chroma/client.py +0 -0
  83. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/chroma/converter.py +0 -0
  84. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/lancedb/__init__.py +0 -0
  85. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/lancedb/client.py +0 -0
  86. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/lancedb/converter.py +0 -0
  87. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/pinecone/__init__.py +0 -0
  88. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/pinecone/converter.py +0 -0
  89. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/qdrant/__init__.py +0 -0
  90. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/qdrant/converter.py +0 -0
  91. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/weaviate/__init__.py +0 -0
  92. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/backends/weaviate/converter.py +0 -0
  93. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/src/anywidget_vector/py.typed +0 -0
  94. {anywidget_vector-0.3.2 → anywidget_vector-0.4.1}/tests/__init__.py +0 -0
@@ -0,0 +1,98 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, "release/**", "support/**"]
6
+ pull_request:
7
+ branches: [main]
8
+ # The release workflow runs the full suite before it publishes
9
+ workflow_call:
10
+
11
+ jobs:
12
+ lint:
13
+ name: Lint
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v7
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
20
+
21
+ - name: Check that uv.lock matches pyproject.toml
22
+ run: uv lock --check
23
+
24
+ - name: Run ruff check
25
+ run: uv run --locked --only-group dev ruff check .
26
+
27
+ - name: Run ruff format check
28
+ run: uv run --locked --only-group dev ruff format --check .
29
+
30
+ test:
31
+ name: Test (Python ${{ matrix.python-version }})
32
+ runs-on: ubuntu-latest
33
+ strategy:
34
+ matrix:
35
+ python-version: ["3.12", "3.13", "3.14", "3.15"]
36
+ steps:
37
+ - uses: actions/checkout@v7
38
+
39
+ # Sets UV_PYTHON, so uv uses this version instead of .python-version
40
+ - name: Install uv and Python ${{ matrix.python-version }}
41
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
42
+ with:
43
+ python-version: ${{ matrix.python-version }}
44
+
45
+ # The ESM bundle tests parse and run the widget's JavaScript with Node (the tsc, jsdom and stale-bundle
46
+ # checks need node_modules and skip here: the frontend job runs them once)
47
+ - name: Set up Node.js
48
+ uses: actions/setup-node@v7
49
+ with:
50
+ node-version: "22"
51
+
52
+ - name: Install dependencies
53
+ run: uv sync --locked --extra dev
54
+
55
+ - name: Run tests
56
+ run: uv run --locked --extra dev pytest
57
+
58
+ frontend:
59
+ name: Front end
60
+ runs-on: ubuntu-latest
61
+ steps:
62
+ - uses: actions/checkout@v7
63
+
64
+ - name: Set up Node.js
65
+ uses: actions/setup-node@v7
66
+ with:
67
+ node-version: "22"
68
+
69
+ # esbuild, TypeScript and the type packages, so tsc can check every module in src/ and tests/js/
70
+ - name: Install the front-end dependencies
71
+ run: npm ci --ignore-scripts
72
+
73
+ - name: Install the JavaScript test dependencies
74
+ run: npm ci --ignore-scripts --prefix tests/js
75
+
76
+ - name: Check that the committed bundles are built from the current sources
77
+ run: npm run check
78
+
79
+ - name: Type-check the front end
80
+ run: npm run typecheck
81
+
82
+ - name: Run the JavaScript tests
83
+ run: npm test
84
+
85
+ build:
86
+ name: Build
87
+ runs-on: ubuntu-latest
88
+ steps:
89
+ - uses: actions/checkout@v7
90
+
91
+ - name: Install uv
92
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
93
+
94
+ - name: Build package
95
+ run: uv build
96
+
97
+ - name: Check package
98
+ run: uvx twine check dist/*
@@ -0,0 +1,72 @@
1
+ name: Grafeo Latest
2
+
3
+ # Early warning for engine changes: runs the suite against the newest grafeo on
4
+ # PyPI (pre-releases included, even outside the pinned range) and the browser
5
+ # backend against the pinned and the newest @grafeo-db/wasm on npm.
6
+
7
+ on:
8
+ schedule:
9
+ - cron: "0 6 * * 1" # Mondays 06:00 UTC
10
+ workflow_dispatch:
11
+
12
+ jobs:
13
+ python:
14
+ name: Test with newest grafeo (Python ${{ matrix.python-version }})
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.12", "3.13", "3.14", "3.15"]
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+
23
+ - name: Install uv and Python ${{ matrix.python-version }}
24
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
25
+ with:
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Install dependencies
29
+ run: |
30
+ uv sync --extra dev
31
+ uv pip install --upgrade --prerelease allow grafeo
32
+
33
+ - name: Show grafeo build
34
+ run: uv run --no-sync python -c "import grafeo; print(grafeo.__version__); print(grafeo.build_info())"
35
+
36
+ - name: Run tests
37
+ run: uv run --no-sync pytest
38
+
39
+ wasm:
40
+ name: WASM smoke test (${{ matrix.release }} @grafeo-db/wasm)
41
+ runs-on: ubuntu-latest
42
+ strategy:
43
+ fail-fast: false
44
+ matrix:
45
+ release: [pinned, newest]
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+
49
+ - name: Set up Node.js
50
+ uses: actions/setup-node@v7
51
+ with:
52
+ node-version: "22"
53
+
54
+ - name: Resolve version
55
+ id: version
56
+ run: |
57
+ if [ "${{ matrix.release }}" = "pinned" ]; then
58
+ version=$(sed -n 's/^export const GRAFEO_WASM_VERSION = "\(.*\)";$/\1/p' src/anywidget_vector/backends/grafeo/embed.ts)
59
+ else
60
+ version=$(npm view @grafeo-db/wasm versions --json | node -e 'const v = JSON.parse(require("fs").readFileSync(0, "utf8")); console.log([].concat(v).pop())')
61
+ fi
62
+ echo "version=$version" >> "$GITHUB_OUTPUT"
63
+
64
+ - name: Download @grafeo-db/wasm@${{ steps.version.outputs.version }}
65
+ run: |
66
+ mkdir -p "$RUNNER_TEMP/wasm"
67
+ cd "$RUNNER_TEMP/wasm"
68
+ npm pack "@grafeo-db/wasm@${{ steps.version.outputs.version }}"
69
+ tar -xzf grafeo-db-wasm-*.tgz
70
+
71
+ - name: Run smoke test
72
+ run: node tests/js/wasm_smoke.ts "$RUNNER_TEMP/wasm/package"
@@ -15,25 +15,34 @@ on:
15
15
  - pypi
16
16
 
17
17
  jobs:
18
+ test:
19
+ name: Test
20
+ uses: ./.github/workflows/ci.yml
21
+
18
22
  build:
19
23
  name: Build distribution
24
+ needs: test
20
25
  runs-on: ubuntu-latest
21
26
  steps:
22
- - uses: actions/checkout@v4
27
+ - uses: actions/checkout@v7
23
28
 
24
- - name: Set up Python
25
- uses: actions/setup-python@v5
26
- with:
27
- python-version: "3.12"
29
+ - name: Check the release tag matches the package version
30
+ if: github.event_name == 'release'
31
+ run: |
32
+ version=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
33
+ if [ "$GITHUB_REF_NAME" != "v$version" ]; then
34
+ echo "Release tag $GITHUB_REF_NAME does not match the package version $version"
35
+ exit 1
36
+ fi
28
37
 
29
- - name: Install build dependencies
30
- run: pip install build
38
+ - name: Install uv
39
+ uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
31
40
 
32
41
  - name: Build package
33
- run: python -m build
42
+ run: uv build
34
43
 
35
44
  - name: Upload distribution artifacts
36
- uses: actions/upload-artifact@v4
45
+ uses: actions/upload-artifact@v7
37
46
  with:
38
47
  name: dist
39
48
  path: dist/
@@ -48,7 +57,7 @@ jobs:
48
57
  id-token: write
49
58
  steps:
50
59
  - name: Download distribution artifacts
51
- uses: actions/download-artifact@v4
60
+ uses: actions/download-artifact@v8
52
61
  with:
53
62
  name: dist
54
63
  path: dist/
@@ -68,7 +77,7 @@ jobs:
68
77
  id-token: write
69
78
  steps:
70
79
  - name: Download distribution artifacts
71
- uses: actions/download-artifact@v4
80
+ uses: actions/download-artifact@v8
72
81
  with:
73
82
  name: dist
74
83
  path: dist/
@@ -20,7 +20,7 @@ __pycache__/
20
20
  *.so
21
21
  *.egg-info/
22
22
  *.egg
23
- dist/
23
+ /dist/
24
24
  build/
25
25
  .eggs/
26
26
  .venv/
@@ -0,0 +1,218 @@
1
+ # Changelog
2
+
3
+ ## [0.4.1] - 2026-11-12
4
+
5
+ Grafeo 0.6.2 release: the Grafeo backend moves to grafeo 0.6.2 in Python and in the browser, and the front end is TypeScript, bundled by esbuild. An environment with the `grafeo` extra upgrades grafeo to 0.6.2 or later; to keep grafeo 0.6.0 or 0.6.1, stay on anywidget-vector 0.4.0 and pin grafeo.
6
+
7
+ ### Changed
8
+
9
+ - **Front end in TypeScript**: The widget loads `ui/dist/widget.js`, built by esbuild from the modules in `ui/` and `backends/` (`npm run build`), instead of the JavaScript modules joined as text. `to_html()` uses its own bundle, `ui/dist/export.js`, with the page template in `ui/export.html`.
10
+ - **Release checks**: CI type-checks the front end with `tsc` (`npm run typecheck`), runs the JavaScript tests (`npm test`), and fails when a committed bundle is stale.
11
+ - **grafeo-server version**: The README says the `server` Grafeo mode needs grafeo-server 0.6.0 or later, the Grafeo 0.6 engine, which sends decimals as `{"$decimal": "..."}` (the widget turns them into numbers).
12
+ - **Exported pages draw points in one call**: `to_html()` and `save_html()` draw every point as an instance of one mesh instead of one mesh per point.
13
+
14
+ ### Fixed
15
+
16
+ - **Exported point sizes match the widget**: `to_html()` scales `size_range` by the data extent, as the live view does.
17
+ - **An exported single point is in view**: A page with one point, or with points that share a position, places the camera away from them instead of on them.
18
+ - **The exported tooltip hides on leave**: Moving the pointer off an exported page hides the tooltip.
19
+ - **Pinecone fetches by id show their points**: A `{"ids": [...]}` query reads the fetched records (previously it showed no points), sends the configured `namespace`, and encodes each id, so ids with `&` or `#` resolve.
20
+ - **WASM mode creates one database**: Queries started while `@grafeo-db/wasm` loads share one database instead of each creating one.
21
+ - **The Run button shows Python-side queries**: For backends that run in Python (Grafeo embedded, Chroma, LanceDB), the button stays in its loading state until the result arrives.
22
+ - **A widget without WebGL leaves no listeners**: When the 3D canvas cannot be created, the view removes the model and theme listeners it had added.
23
+ - **Neighbor names fall back to the id**: The sidebar's distance list shows the id for a point whose `label` is empty.
24
+
25
+ ### Dependencies
26
+
27
+ - **grafeo 0.6.2** (requires `>=0.6.2,<0.7`): The `grafeo` and `dev` extras need grafeo 0.6.2, and the `wasm` mode loads `@grafeo-db/wasm` 0.6.2 (`GRAFEO_WASM_VERSION` in `backends/grafeo/embed.ts`).
28
+ - **Locked versions refreshed**: 35 packages, among them ty 0.0.100, typer 0.29.0, pandas 3.1.1, lancedb 0.41.0, ruff 0.17.4 and python-dateutil 4.0.0. Front-end build tools: esbuild 0.28.2, TypeScript 7.0.2 and `@types/three` 0.186.0.
29
+
30
+ ## [0.4.0] - 2026-10-28
31
+
32
+ Requires Python 3.12 or later; the Grafeo backend requires grafeo 0.6. See "Upgrading to 0.4" in the README.
33
+
34
+ ### Breaking
35
+
36
+ - **Grafeo 0.6**: The `grafeo` extra requires `grafeo>=0.6.0,<0.7`; grafeo 0.5.x is no longer supported. Grafeo 0.6 changes query results and files, and these reach the widget's Grafeo backend:
37
+ - Every database is a single file. The first read-write open of a 0.5.x database migrates it and keeps the old file as `<file>.pre-0.6`, which 0.5.x needs to go back; opening a directory that is not a 0.5.x database fails
38
+ - A GQL number with a point is an exact decimal, so `0.0/0.0` and `1/0` fail the query (division by zero) where they gave NaN or null, and so does integer overflow. Use exponent literals (`0.0e0/0.0e0`) for IEEE floats
39
+ - A `WHERE` right after `INSERT`, `CREATE`, `MERGE` or `DELETE` is an error, and an `INSERT` alone returns no rows, so running one from the toolbar clears the scene
40
+ - GQL `ORDER BY` puts nulls last in both directions
41
+ - **Theme follows the page until you choose**: `dark_mode` reports the theme in use. The widget follows the host page until a theme is chosen (`VectorSpace(dark_mode=...)`, `widget.dark_mode = ...` before or after display, even to the value it already reports, or the settings toggle) and then keeps the choice, also in later views; before, the page theme overwrote `dark_mode` passed from Python, and the widget stopped following the page after its first automatic change. The canvas `background` follows the theme unless chosen the same way; before, the theme overwrote it. Same contract as anywidget-graph and anywidget-archimate (`_theme_explicit`, `_background_explicit`)
42
+ - **Compact toolbar in a narrow widget**: The toolbar tightens when the widget itself is narrower than 600 px (a container query), not only when the window is narrow, as in anywidget-graph. Panels over the canvas (window narrower than 768 px) now cover the canvas's zoom and selection controls instead of sitting under them
43
+ - **WASM engine 0.6**: The browser-side Grafeo backend loads `@grafeo-db/wasm` 0.6.0 (was 0.5.0 in 0.3.2) from jsDelivr. The version is set once, `GRAFEO_WASM_VERSION` in `backends/grafeo/embed.js`, and a test keeps it in lockstep with the Python pin (npm and PyPI spellings of a pre-release, such as `0.6.1-rc.2` and `0.6.1rc2`, compare as the same version)
44
+
45
+ ### Improvements
46
+
47
+ - **Decimals become numbers**: Grafeo 0.6 returns decimals (`decimal.Decimal` in Python, `{"$decimal": "1.50"}` in WASM). The widget turns them into floats in every Grafeo mode, also inside lists, maps and node properties, so `RETURN 0.5 AS score` maps to color and size, `1.5 AS x` places a point, and a plain list `[1.0, 0.0, 0.0]` still gives a vector. Without this, Python points could not be sent to the browser as JSON and WASM points landed at the origin
48
+ - **Vector search errors**: A query vector of the wrong size or with NaN is a regular `grafeo.GrafeoError` in Grafeo 0.6 (0.5.44 panicked, and its WASM build trapped), shown in the widget with the sizes it names. The panic guard stays for any other engine bug
49
+ - **Three.js 0.186.1, bundled**: The widget and `to_html()` use Three.js 0.186.1 (was 0.160.0 from esm.sh), bundled with the package (`ui/vendor/three.min.js` with its license, built by `scripts/vendor`), so both work offline and under a strict Content Security Policy. Only the `wasm` Grafeo mode loads code from a CDN. Three.js dropped WebGL 1 in 0.163, so a browser needs WebGL 2
50
+ - **Theme detection**: A `data-theme="dark"` or `"light"` on the widget's closest ancestor wins, then on `<html>` and `<body>`, then a `dark` or `light` class, then the operating system. `data-theme="light"` no longer loses to a dark operating system, and other `data-theme` values (design-system names) are ignored
51
+ - **Release safety**: The PyPI workflow runs the full CI suite first and stops when the release tag is not `v` plus the `pyproject.toml` version. CI also runs on `release/**` and `support/**` branches
52
+ - **Credentials documented**: The README says where backend credentials end up (the browser and saved notebook widget state) and how to limit that
53
+ - **Dependencies**: Requires anywidget 0.11 or later (was 0.9.21). The `chroma` and `all` extras pin `chromadb==1.5.8`, because 1.5.9 has security issues. All locked dependencies refreshed (`uv lock --upgrade`), and the dev tools need current releases (pytest 9.2, ruff 0.17.3, ty 0.0.94, prek 0.5.7, marimo 0.25.2); the `dev` extra includes grafeo, numpy and packaging so the full test suite runs everywhere. CI and the release workflow use uv (`uv lock --check`, `uv sync --locked`, `uv build`) and current GitHub Actions (checkout v7, setup-uv v10.3.3 pinned by commit since setup-uv publishes no moving major tag, setup-node v7, upload-artifact v7, download-artifact v8). The `tool.uv.dev-dependencies` table moved to `[dependency-groups]`, and ruff leaves Markdown files alone
54
+ - **Engine contract tests**: The tests that pin Grafeo behavior now cover 0.6: error classes and messages for unknown metrics, zero dimensions, wrong-size and NaN query vectors and NaN in an indexed vector, filter operators (`$in`, `$ne`), tied distances, decimal literals and an `INSERT` without rows. `tests/js/wasm_smoke.mjs` checks decimals and the wrong-size error in WASM
55
+ - **Same points in every Grafeo mode**: Embedded, server and WASM results go through the same rules: node values (`RETURN n`) contribute their properties, labels and id, unaliased columns such as `n.embedding` or `id(n)` are recognized, and explicit `x`/`y`/`z` win over the first vector components
56
+ - **Server errors**: grafeo-server error responses show their `detail` text instead of raw JSON
57
+ - **Scheduled engine check**: A weekly workflow runs the suite against the newest grafeo on PyPI (pre-releases included) and a smoke test against the pinned and the newest `@grafeo-db/wasm`
58
+ - **Python 3.15**: Tested on Python 3.15; CI runs the suite on 3.12 to 3.15 and the classifiers list 3.12 to 3.15
59
+
60
+ ### Bug Fixes
61
+
62
+ - **Embedded Grafeo queries**: Queries failed with "'QueryBuilder' object is not iterable" because the client called `GrafeoDB.query()`, which returns a query builder. It now calls `execute()`
63
+ - **WASM mode**: `@grafeo-db/wasm` is published for bundlers since 0.5.x, so importing it from esm.sh no longer yields a working `Database`. The loader now imports the JS glue and instantiates the binary itself. WASM vectors (`Float32Array`) are converted to arrays, so they place points and sync to Python
64
+ - **Engine panics**: A Grafeo panic (for example a vector search with the wrong number of dimensions) escaped the query callback because pyo3 raises it as a `BaseException`. It is now reported as a query error
65
+ - **Query status**: A Python-side query with an empty result left the status at "connecting" and kept the old points. The result now replaces the points and the status becomes "connected"; a new query clears the previous error
66
+ - **NaN and infinity in points**: These values are not valid JSON and one NaN coordinate broke camera framing for the whole scene. Top-level non-finite values are now replaced with `None` (with a `RuntimeWarning`), and distance and focus methods treat `None` coordinates as 0
67
+ - **HTML injection**: Point labels and values from a database were inserted as HTML in the tooltip, the properties panel and the exported HTML page. They are now set as text. `to_html()` also escapes the title and keeps a `</script>` inside the data from ending the script
68
+ - **Vector edge cases**: `add_numpy()` with a single high-dimensional row, a 1-D or one-column array, or ids/labels/metadata of the wrong length raised an unrelated `IndexError`; it now works (single row) or raises a clear `ValueError`. `project()` handles a single point and reports ragged or non-finite vectors clearly. `compute_distances(vector_field=...)` leaves out points without that vector instead of returning infinity, and raises on vectors of different lengths instead of truncating them
69
+ - **Version**: `anywidget_vector.__version__` reported 0.2.5; it now reads the installed package version
70
+ - **Removed views kept running**: A removed or re-rendered widget kept its model listeners, its theme observers and its dark-mode media listener, so old views went on updating `dark_mode` and `background`. Removing a view now detaches all of them
71
+ - **WASM engine loaded twice**: Two copies of the widget module on one page (a cell run again, a marimo reconnect, anywidget-graph next to it) each instantiated the cached `@grafeo-db/wasm` glue, which broke the databases of the first copy. The loading promise now lives on `globalThis`, keyed by the glue URL
72
+ - **Stale query results**: A slower, older browser-side query could overwrite the result of a newer one. Only the newest query per widget now sets points, errors and the loading state
73
+ - **HTML export sizes**: `to_html(width=..., height=...)` placed the values in the page's `<style>` unchecked; a value that is not a plain CSS size now raises `ValueError`
74
+ - **Source distribution**: A local `uv build` packed the `.claude/` folder (agent notes and local settings) into the sdist; it is now excluded
75
+
76
+ ## [0.3.2] - 2026-03-16
77
+
78
+ ### Improvements
79
+
80
+ - **Marimo reactivity fix**: Removed camera and hover JS-to-Python sync to prevent continuous cell re-execution. Only user-initiated actions (selection, mode change) now trigger marimo reactive updates
81
+ - **Custom scroll zoom**: Replaced OrbitControls zoom with a custom wheel handler for much finer granularity
82
+ - **Selection hides completely**: Lasso/box selection now fully hides non-selected points (scale-to-zero for instanced meshes) instead of dimming
83
+ - **Auto zoom-to-fit on selection**: Camera automatically frames the selected points after lasso/box selection
84
+ - **Search filter modes**: Typing in the search bar dims non-matching points as a preview, pressing Run/Enter hides them completely
85
+ - **Hover via callback**: Properties panel hover now uses a direct JS callback instead of model sync, eliminating unnecessary Python round-trips
86
+
87
+ ### Bug Fixes
88
+
89
+ - **setSelection scoping**: Moved `setSelection` helper to `createCanvas` scope so lasso/box/click handlers can access it
90
+ - **Camera debounce variable**: Fixed `_cameraDebounce` lexical declaration error by moving it to the correct scope
91
+
92
+ ## [0.3.1] - 2026-03-16
93
+
94
+ ### New Features
95
+
96
+ - **Lasso selection**: New lasso mode for freehand point selection in the 3D canvas, with point-in-polygon hit testing
97
+ - **Selection highlighting**: Selected points show wireframe highlight rings and non-selected points are dimmed to 15% brightness
98
+ - **Auto-sizing**: Width defaults to 100% (fill container), height defaults to 1:1 aspect ratio. Both can still be set to fixed pixel values
99
+
100
+ ### Bug Fixes
101
+
102
+ - **Properties panel**: Panel is now always created (previously skipped when `show_properties=False`), so the toolbar button always works. Starts closed unless `show_properties=True`
103
+ - **Distance metric feedback**: Changing the metric now auto-enables k=5 connections when a point is selected, giving immediate visual feedback
104
+
105
+ ### Improvements
106
+
107
+ - Reduced scroll zoom sensitivity (`zoomSpeed` halved to 0.5)
108
+ - Search filter dimming increased from 12% to 6% opacity for clearer visual contrast
109
+ - Camera no longer resets on every point update (only on initial load)
110
+ - Cross-widget filtering: example updated so VectorSpace lasso selection feeds back into ParallelCoordinates dimming via marimo reactivity
111
+
112
+ ## [0.3.0] - 2026-03-16
113
+
114
+ ### New Features
115
+
116
+ - **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
117
+ - **Dimension mapping display**: Sidebar now shows active visual mapping fields: color field, size range, shape field and unique shape count
118
+
119
+ ### Improvements
120
+
121
+ - Improved sidebar styling and layout
122
+
123
+ ## [0.2.8] - 2026-03-15
124
+
125
+ ### New Features
126
+
127
+ - **6D parallel coordinates example**: Fashion-MNIST demo with PCA across 6+ dimensions, interactive axis mapping and shape mapping to categorical labels
128
+
129
+ ## [0.2.7] - 2026-03-15
130
+
131
+ ### New Features
132
+
133
+ - **Dark/light mode toggle**: `dark_mode` trait with real-time CSS color scheme switching and theme-aware styling across all panels
134
+
135
+ ## [0.2.6] - 2026-03-15
136
+
137
+ ### New Features
138
+
139
+ - **NumPy array support**: `add_numpy()` method for adding points from arrays with automatic PCA projection for high-dimensional data (D > 3)
140
+ - **Dimensionality reduction**: `project()` method with PCA, t-SNE and UMAP support; `set_vectors()` for storing high-dimensional vectors separately
141
+ - **Example scripts**: Projection demo and numpy integration examples
142
+
143
+ ### Improvements
144
+
145
+ - High-dimensional vector storage without syncing to JavaScript
146
+ - Coordinate normalization after projection
147
+ - Added demo screenshot to README
148
+
149
+ ## [0.2.5] - 2026-03-04
150
+
151
+ ### Bug Fixes
152
+
153
+ - Fixed lasso selection stability issues
154
+ - Removed problematic custom traitlets implementation
155
+
156
+ ### Improvements
157
+
158
+ - Added keyboard shortcuts: F to fit view, Escape to clear selection
159
+ - Better visual feedback for selection modes
160
+ - Cleaner toolbar layout with integrated mode controls
161
+
162
+ ## [0.2.4] - 2026-03-02
163
+
164
+ ### New Features
165
+
166
+ - **Zoom and pan controls**: Zoom in, zoom out and fit-to-view buttons in the toolbar
167
+ - **Box selection mode**: New selection mode alongside click and multi-select, with visual feedback during selection
168
+ - **HTML export**: `to_html()` and `save_html()` methods for self-contained HTML output with embedded data and Three.js
169
+
170
+ ### Improvements
171
+
172
+ - Improved camera fitting algorithm for better initial view framing
173
+ - Updated README with comprehensive usage examples
174
+ - Updated dependencies
175
+
176
+ ## [0.2.3] - 2026-02-12
177
+
178
+ ### New Features
179
+
180
+ - **Sidebar explorer**: Three collapsible sections: collections browser, dimensions explorer (point count, dimensionality, axis ranges) and clusters explorer (grouped by color field with sizes)
181
+ - **Browser-side Grafeo backend**: JavaScript client for Grafeo server and WASM-embedded queries
182
+ - **Demo module**: Reference implementation showing various widget features
183
+
184
+ ### Improvements
185
+
186
+ - Consistency pass across all backend modules
187
+ - Ruff configuration tuned for marimo compatibility
188
+
189
+ ## [0.2.2] - 2026-02-07
190
+
191
+ ### New Features
192
+
193
+ - **Streaming factory methods**: `from_numpy()`, `from_umap()` and `from_arrays()` constructors for various data formats
194
+
195
+ ### Improvements
196
+
197
+ - Added Apache 2.0 license
198
+ - Expanded test suite with 194 additional test cases
199
+ - ESM bundle aggregation for widget JavaScript
200
+
201
+ ## [0.2.1] - 2026-02-03
202
+
203
+ ### New Features
204
+
205
+ - **Multi-backend architecture**: Six backends: Qdrant, Pinecone, Weaviate (browser-side REST), Chroma, LanceDB, Grafeo (Python-side)
206
+ - **Modular UI**: Separated rendering into canvas, settings, properties and toolbar components
207
+ - **Backend factory methods**: `from_qdrant()`, `from_pinecone()`, `from_weaviate()`, `from_chroma()`, `from_lancedb()`, `from_grafeo()`
208
+ - **Event system**: `on_click()`, `on_hover()` and `on_selection()` decorators with callback signatures
209
+ - **CI/CD**: GitHub Actions pipelines for testing and PyPI publishing
210
+
211
+ ### Improvements
212
+
213
+ - Refactored monolithic widget into modular component architecture
214
+ - Added README with usage examples
215
+
216
+ ## [0.1.0] - 2026-02-03
217
+
218
+ - 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