Skip to content

Commit a906bff

Browse files
committed
feat(vector-viz): add vector embedding visualizer plugin with initial setup and components
1 parent 476b312 commit a906bff

File tree

13 files changed

+5483
-172
lines changed

13 files changed

+5483
-172
lines changed

package-lock.json

Lines changed: 5190 additions & 172 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
ignores:
2+
- '@mongodb-js/prettier-config-compass'
3+
- '@mongodb-js/tsconfig-compass'
4+
- '@types/chai'
5+
- '@types/sinon-chai'
6+
- 'sinon'
7+
- '@types/chai-dom'
8+
- '@types/react'
9+
- '@types/react-dom'
10+
ignore-patterns:
11+
- 'dist'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.nyc-output
2+
dist
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.exports = {
2+
root: true,
3+
extends: ['@mongodb-js/eslint-config-compass/plugin'],
4+
parserOptions: {
5+
tsconfigRootDir: __dirname,
6+
project: ['./tsconfig-lint.json'],
7+
},
8+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@mongodb-js/mocha-config-compass/compass-plugin');
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"name": "@mongodb-js/compass-vector-embedding-visualizer",
3+
"description": "Vector embeddings viz",
4+
"author": {
5+
"name": "MongoDB Inc",
6+
"email": "[email protected]"
7+
},
8+
"private": true,
9+
"bugs": {
10+
"url": "https://jira.mongodb.org/projects/COMPASS/issues",
11+
"email": "[email protected]"
12+
},
13+
"homepage": "https://github.com/mongodb-js/compass",
14+
"version": "1.0.0",
15+
"repository": {
16+
"type": "git",
17+
"url": "https://github.com/mongodb-js/compass.git"
18+
},
19+
"files": [
20+
"dist"
21+
],
22+
"license": "SSPL",
23+
"main": "dist/index.js",
24+
"compass:main": "src/index.ts",
25+
"exports": {
26+
"import": "./dist/.esm-wrapper.mjs",
27+
"require": "./dist/index.js"
28+
},
29+
"compass:exports": {
30+
".": "./src/index.ts"
31+
},
32+
"types": "./dist/index.d.ts",
33+
"scripts": {
34+
"bootstrap": "npm run compile",
35+
"compile": "tsc -p tsconfig.json",
36+
"typecheck": "tsc -p tsconfig-lint.json --noEmit",
37+
"eslint": "eslint-compass",
38+
"prettier": "prettier-compass",
39+
"lint": "npm run eslint . && npm run prettier -- --check .",
40+
"depcheck": "compass-scripts check-peer-deps && depcheck",
41+
"check": "npm run typecheck && npm run lint && npm run depcheck",
42+
"check-ci": "npm run check",
43+
"test": "mocha",
44+
"test-electron": "xvfb-maybe electron-mocha --no-sandbox",
45+
"test-cov": "nyc --compact=false --produce-source-map=false -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test",
46+
"test-watch": "npm run test -- --watch",
47+
"test-ci": "npm run test-cov",
48+
"test-ci-electron": "npm run test-electron",
49+
"reformat": "npm run eslint . -- --fix && npm run prettier -- --write ."
50+
},
51+
"dependencies": {
52+
"@leafygreen-ui/tooltip": "^13.0.12",
53+
"@types/plotly.js": "^3.0.0",
54+
"ml-pca": "^4.1.1",
55+
"plotly.js": "^3.0.1",
56+
"react": "^17.0.2",
57+
"react-dom": "^17.0.2"
58+
},
59+
"devDependencies": {
60+
"@mongodb-js/eslint-config-compass": "^1.3.8",
61+
"@mongodb-js/mocha-config-compass": "^1.6.8",
62+
"@mongodb-js/prettier-config-compass": "^1.2.8",
63+
"@mongodb-js/testing-library-compass": "^1.2.8",
64+
"@mongodb-js/tsconfig-compass": "^1.2.8",
65+
"@types/chai": "^4.2.21",
66+
"@types/chai-dom": "^0.0.10",
67+
"@types/mocha": "^9.0.0",
68+
"@types/react": "^17.0.5",
69+
"@types/react-dom": "^17.0.10",
70+
"@types/sinon-chai": "^3.2.5",
71+
"chai": "^4.3.6",
72+
"depcheck": "^1.4.1",
73+
"hadron-app-registry": "^9.4.8",
74+
"mocha": "^10.2.0",
75+
"nyc": "^15.1.0",
76+
"sinon": "^17.0.1",
77+
"typescript": "^5.0.4",
78+
"xvfb-maybe": "^0.2.1"
79+
},
80+
"is_compass_plugin": true
81+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React, { useEffect, useState } from 'react';
2+
import Plotly from 'plotly.js';
3+
4+
type HoverInfo = {
5+
x: number;
6+
y: number;
7+
text: string;
8+
} | null;
9+
10+
export const VectorVisualizer: React.FC = () => {
11+
const [hoverInfo, setHoverInfo] = useState<HoverInfo>(null);
12+
13+
useEffect(() => {
14+
const container = document.getElementById('vector-plot');
15+
if (!container) return;
16+
17+
let isMounted = true;
18+
19+
const plot = async () => {
20+
await Plotly.newPlot(
21+
container,
22+
[
23+
{
24+
x: [1, 2, 3, 4, 5],
25+
y: [10, 15, 13, 17, 12],
26+
mode: 'markers',
27+
type: 'scatter',
28+
name: 'baskd',
29+
text: ['doc1', 'doc2', 'doc3', 'doc4', 'doc5'],
30+
hoverinfo: 'none',
31+
marker: {
32+
size: 15,
33+
color: 'teal',
34+
line: { width: 1, color: '#fff' },
35+
},
36+
},
37+
],
38+
{
39+
margin: { l: 40, r: 10, t: 40, b: 40 },
40+
hovermode: 'closest',
41+
hoverdistance: 30,
42+
dragmode: 'zoom',
43+
plot_bgcolor: '#f7f7f7',
44+
paper_bgcolor: '#f7f7f7',
45+
xaxis: { gridcolor: '#e0e0e0' },
46+
yaxis: { gridcolor: '#e0e0e0' },
47+
},
48+
{ responsive: true }
49+
);
50+
51+
const handleHover = (data: any) => {
52+
const point = data.points?.[0];
53+
if (!point) return;
54+
55+
const containerRect = container.getBoundingClientRect();
56+
const relX = data.event.clientX - containerRect.left;
57+
const relY = data.event.clientY - containerRect.top;
58+
59+
if (isMounted) {
60+
setHoverInfo({ x: relX, y: relY, text: point.text });
61+
}
62+
};
63+
64+
const handleUnhover = () => {
65+
if (isMounted) {
66+
setHoverInfo(null);
67+
}
68+
};
69+
70+
container.addEventListener('plotly_hover', handleHover);
71+
container.addEventListener('plotly_unhover', handleUnhover);
72+
73+
// Cleanup
74+
return () => {
75+
isMounted = false;
76+
container.removeEventListener('plotly_hover', handleHover);
77+
container.removeEventListener('plotly_unhover', handleUnhover);
78+
};
79+
};
80+
81+
let cleanup: (() => void) | undefined;
82+
void plot().then((c) => {
83+
if (typeof c === 'function') cleanup = c;
84+
});
85+
86+
return () => {
87+
isMounted = false;
88+
if (cleanup) cleanup();
89+
};
90+
}, []);
91+
92+
return (
93+
<div style={{ position: 'relative', width: '100%', height: '100%' }}>
94+
<div id="vector-plot" style={{ width: '100%', height: '100%' }} />
95+
{hoverInfo && (
96+
<div
97+
style={{
98+
position: 'absolute',
99+
left: hoverInfo.x,
100+
top: hoverInfo.y,
101+
background: 'white',
102+
border: '1px solid #ccc',
103+
padding: '4px 8px',
104+
borderRadius: 4,
105+
pointerEvents: 'none',
106+
whiteSpace: 'nowrap',
107+
zIndex: 1000,
108+
}}
109+
>
110+
{hoverInfo.text}
111+
</div>
112+
)}
113+
</div>
114+
);
115+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import { cleanup, render } from '@mongodb-js/testing-library-compass';
4+
import CompassPlugin from './index';
5+
6+
describe('Compass Plugin', function () {
7+
const Plugin = CompassPlugin.withMockServices({});
8+
9+
it('renders a Plugin', function () {
10+
render(<Plugin></Plugin>);
11+
});
12+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// plugin.tsx
2+
import React from 'react';
3+
import { registerHadronPlugin } from 'hadron-app-registry';
4+
import { createLoggerLocator } from '@mongodb-js/compass-logging/provider';
5+
6+
import { VectorVisualizer } from './components/vector-visualizer';
7+
import { createStore } from 'redux';
8+
9+
// Minimal reducer for the plugin store
10+
function reducer(state = {}, action: any) {
11+
return state;
12+
}
13+
14+
export const CompassVectorPluginProvider = registerHadronPlugin(
15+
{
16+
name: 'CompassVectorEmbeddingVisualizer',
17+
component: function VectorVisualizerProvider({ children }) {
18+
return React.createElement(React.Fragment, null, children);
19+
},
20+
activate: () => {
21+
const store = createStore(reducer);
22+
return {
23+
store: () => store,
24+
deactivate: () => {
25+
// ignore
26+
},
27+
};
28+
},
29+
},
30+
{
31+
// collection: collectionModelLocator,
32+
// dataService: dataServiceLocator,
33+
logger: createLoggerLocator('COMPASS-VECTOR-VISUALIZER'),
34+
// track: telemetryLocator,
35+
}
36+
);
37+
38+
export default CompassVectorPluginProvider;
39+
40+
export const CompassVectorPlugin = {
41+
name: 'VectorVisualizer',
42+
type: 'Collection' as const,
43+
provider: CompassVectorPluginProvider,
44+
content: VectorVisualizer,
45+
header: () => React.createElement('div', null, 'Vector Embeddings'),
46+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"include": ["**/*"],
4+
"exclude": ["node_modules", "dist"]
5+
}

0 commit comments

Comments
 (0)