diff --git a/v2/app/package-lock.json b/v2/app/package-lock.json
index 1a193d1..b7c63f6 100644
--- a/v2/app/package-lock.json
+++ b/v2/app/package-lock.json
@@ -15,6 +15,7 @@
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-http": "^2.5.4",
"@tauri-apps/plugin-opener": "^2",
+ "gsap": "^3.14.2",
"html-to-text": "^9.0.5",
"react": "^19.1.0",
"react-dom": "^19.1.0",
@@ -2302,6 +2303,12 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC"
},
+ "node_modules/gsap": {
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
+ "integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==",
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
+ },
"node_modules/hls.js": {
"version": "1.6.15",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.15.tgz",
diff --git a/v2/app/package.json b/v2/app/package.json
index 643c98c..55288fe 100644
--- a/v2/app/package.json
+++ b/v2/app/package.json
@@ -17,6 +17,7 @@
"@tauri-apps/plugin-fs": "^2.4.4",
"@tauri-apps/plugin-http": "^2.5.4",
"@tauri-apps/plugin-opener": "^2",
+ "gsap": "^3.14.2",
"html-to-text": "^9.0.5",
"react": "^19.1.0",
"react-dom": "^19.1.0",
diff --git a/v2/app/src/components/graph.jsx b/v2/app/src/components/graph.jsx
index 40a066e..592c80f 100644
--- a/v2/app/src/components/graph.jsx
+++ b/v2/app/src/components/graph.jsx
@@ -1,15 +1,18 @@
import { Canvas } from '@react-three/fiber'
-import * as THREE from 'three';
+
import { useEffect, useState } from 'react'
-import { get3dCoordinates } from '../utils/draw';
-import { Box, Html, OrbitControls, PerspectiveCamera, Bounds, useBounds } from '@react-three/drei'
+import { generateLinesFromPoints, get3dCoordinates } from '../utils/draw';
+import { OrbitControls, PerspectiveCamera, Bounds, useBounds, Line } from '@react-three/drei'
+import Point from './point.jsx';
+import * as THREE from 'three';
-const PointScale=200;
-const PointSize=6;
export default function Graph({results}){
const [points, setPoints]=useState();
+ const [lines, setLines]=useState();
+
+ const [showContent, setShowContent]=useState(false);
const bounds = useBounds();
function zoomToFit(){
@@ -38,6 +41,11 @@ export default function Graph({results}){
get3dCoordinates(results.map(item=>item.vector)).then(newPoints=>{
console.log("3D Points:", newPoints);
setPoints(newPoints);
+
+ generateLinesFromPoints(newPoints).then(newLines=>{
+ console.log("Lines:", newLines);
+ setLines(newLines);
+ });
});
@@ -45,7 +53,11 @@ export default function Graph({results}){
return (<>
-
+
+ setShowContent(e.target.checked)} />
+
+
>
)
diff --git a/v2/app/src/components/point.jsx b/v2/app/src/components/point.jsx
new file mode 100644
index 0000000..38350d7
--- /dev/null
+++ b/v2/app/src/components/point.jsx
@@ -0,0 +1,99 @@
+import { Html } from '@react-three/drei'
+import { useEffect, useRef, useState } from 'react';
+import * as THREE from 'three';
+import gsap from 'gsap';
+
+
+export const PointScale=200;
+const PointSize=6;
+
+const PointDuration=5;
+const KeywordOffset=0;
+
+export default function Point({point, index, totalPoints, result, showContent}) {
+
+ const [payload, setPayload]=useState();
+ const [showPayload, setShowPayload]=useState(false);
+
+ const meshRef=useRef();
+ const refText=useRef();
+
+ useEffect(()=>{
+ if(!payload) return;
+ const lifeTime=payload.number/payload.total;
+ console.log("Point lifeTime:", lifeTime);
+
+ // animate point size based on lifeTime
+ const targetSize=PointSize *0.2;
+ const initialSize=0;
+
+ const timeline=gsap.timeline({ repeat:-1});
+ timeline.set(meshRef.current.scale, {
+ x: initialSize, y: initialSize, z: initialSize,
+ });
+ timeline.set(refText.current, {
+ scale: initialSize,
+ opacity: 0,
+ });
+ timeline.fromTo(meshRef.current.scale, {
+ x: initialSize, y: initialSize, z: initialSize,
+ }, {
+ x: PointSize, y: PointSize, z: PointSize,
+ duration:PointDuration,
+ delay: lifeTime *5,
+ ease: "power3.in",
+ onUpdate:()=>{
+ if(refText.current){
+ refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`;
+ refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`;
+ }
+ }
+ });
+ timeline.to(meshRef.current.scale, {
+ x: targetSize, y: targetSize, z: targetSize,
+ duration:PointDuration,
+ ease: "power3.out",
+ onUpdate:()=>{
+ if(refText.current){
+ refText.current.style.transform=`scale(${meshRef.current.scale.x / PointSize})`;
+ refText.current.style.opacity=`${meshRef.current.scale.x / PointSize}`;
+ }
+ }
+ });
+
+ timeline.play();
+
+ return ()=>{
+ timeline.kill();
+ };
+
+ },[payload]);
+
+ useEffect(()=>{
+
+ setPayload(JSON.parse(result.payload.text));
+
+ },[point]);
+
+
+ return (
+
+
+
+
+
+
+ {payload?.keywords.map((el, index)=>{el})}
+
+ {showContent &&
{(()=>{
+ return `${payload?.content}`;
+ })()}}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/v2/app/src/utils/draw.js b/v2/app/src/utils/draw.js
index dc68fec..8877fcf 100644
--- a/v2/app/src/utils/draw.js
+++ b/v2/app/src/utils/draw.js
@@ -1,4 +1,5 @@
import { UMAP } from 'umap-js';
+import { PointScale } from '../components/point';
export async function get3dCoordinates(rawPoints) {
@@ -15,4 +16,28 @@ export async function get3dCoordinates(rawPoints) {
return [];
}
+}
+
+export async function generateLinesFromPoints(points){
+ if(!Array.isArray(points) || points.length<2){
+ return [];
+ }
+
+ const lines=[];
+
+ for(let i=0; i 20) continue; // skip if too far apart
+
+ lines.push([start.map(coord => coord * PointScale), end.map(coord => coord * PointScale)]);
+ }
+
+ return lines;
}
\ No newline at end of file
diff --git a/v2/app/src/utils/parsing.js b/v2/app/src/utils/parsing.js
index 7a59616..e7b42d9 100644
--- a/v2/app/src/utils/parsing.js
+++ b/v2/app/src/utils/parsing.js
@@ -172,7 +172,7 @@ async function searchQdrant(query_embeddings){
},
body: JSON.stringify({
vector: query_embeddings,
- limit: 10,
+ limit: 50,
with_payload: true,
with_vector: true
})