Skip to content

Commit 66bec15

Browse files
Add llm initial set up to the project
1 parent 4a283bc commit 66bec15

File tree

16 files changed

+2358
-429
lines changed

16 files changed

+2358
-429
lines changed

client/package-lock.json

Lines changed: 1493 additions & 424 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,27 @@
1111
},
1212
"dependencies": {
1313
"@radix-ui/react-dialog": "^1.1.14",
14-
"@radix-ui/react-separator": "^1.1.7",
15-
"@radix-ui/react-slot": "^1.2.3",
16-
"@radix-ui/react-tooltip": "^1.2.7",
14+
"@radix-ui/react-dropdown-menu": "^2.1.15",
1715
"@radix-ui/react-popover": "^1.1.13",
1816
"@radix-ui/react-select": "^2.2.5",
17+
"@radix-ui/react-separator": "^1.1.7",
1918
"@radix-ui/react-slider": "^1.3.4",
19+
"@radix-ui/react-slot": "^1.2.3",
2020
"@radix-ui/react-tabs": "^1.1.11",
21+
"@radix-ui/react-toast": "^1.2.14",
22+
"@radix-ui/react-tooltip": "^1.2.7",
2123
"@radix-ui/themes": "^3.2.1",
2224
"@xyflow/react": "^12.6.1",
25+
"axios": "^1.10.0",
2326
"class-variance-authority": "^0.7.1",
2427
"clsx": "^2.1.1",
2528
"lucide-react": "^0.509.0",
2629
"next": "15.3.2",
30+
"next-themes": "^0.4.6",
2731
"react": "^18.2.0",
2832
"react-colorful": "^5.6.1",
2933
"react-dom": "^18.2.0",
34+
"sonner": "^2.0.5",
3035
"tailwind-merge": "^3.2.0"
3136
},
3237
"devDependencies": {

client/src/components/WhiteBoard.tsx

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback } from "react";
1+
import React, { useCallback, useState } from "react";
22
import {
33
ReactFlow,
44
Node,
@@ -14,6 +14,7 @@ import "@xyflow/react/dist/style.css";
1414
import Sidebar from "@/components/sidebar/Sidebar";
1515
import TextNode from "@/components/text-node/TextNode";
1616
import ShapeNode from "@/components/shape-node/ShapeNode";
17+
import { AIActionDropdown } from "./aiActionDropdown/aiActionDropdown";
1718

1819
const nodeTypes = {
1920
text: TextNode,
@@ -22,6 +23,7 @@ const nodeTypes = {
2223

2324
export default function WhiteBoard() {
2425
const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
26+
const [selectedNodes, setSelectedNodes] = useState<Node[]>([]);
2527

2628
const handleAddNode = useCallback(
2729
(newNode: Node) => {
@@ -37,15 +39,66 @@ export default function WhiteBoard() {
3739
[setEdges],
3840
);
3941

42+
const handleNodesChange = useCallback((changes: any) => {
43+
onNodesChange(changes);
44+
setSelectedNodes(nodes.filter(node => node.selected));
45+
}, [nodes, onNodesChange]);
46+
47+
const handleAIAction = async (action: 'complete' | 'summarize' | 'rephrase') => {
48+
const textNodes = selectedNodes.filter(node => node.type === 'text');
49+
if (textNodes.length === 0) return;
50+
51+
const nodeToUpdate = textNodes[0]; // first selected text node
52+
const currentText = nodeToUpdate.data.label as string;
53+
54+
try {
55+
const response = await fetch(`http://localhost:8080/api/llm/rephrase`, {
56+
method: 'POST',
57+
headers: { 'Content-Type': 'application/json' },
58+
body: JSON.stringify({ user_text: [currentText] }),
59+
});
60+
61+
if (!response.ok) {
62+
throw new Error(`Server error: ${response.statusText}`);
63+
}
64+
65+
const data = await response.json();
66+
67+
setNodes((nds) =>
68+
nds.map((node) =>
69+
node.id === nodeToUpdate.id
70+
? {
71+
...node,
72+
data: {
73+
...node.data,
74+
label: data.llm_response || '' // update node text from API response
75+
}
76+
}
77+
: node
78+
)
79+
);
80+
} catch (error) {
81+
console.error("Error calling LLM service:", error);
82+
}
83+
};
84+
85+
86+
4087
return (
4188
<div style={{ width: "100vw", height: "100vh" }}>
4289
<div className="fixed left-4 top-1/2 -translate-y-1/2 z-10 ">
4390
<Sidebar onAddNode={handleAddNode} />
4491
</div>
92+
93+
<AIActionDropdown
94+
selectedNodes={selectedNodes}
95+
onAIAction={handleAIAction}
96+
/>
97+
4598
<ReactFlow
4699
nodes={nodes}
47100
edges={edges}
48-
onNodesChange={onNodesChange}
101+
onNodesChange={handleNodesChange}
49102
onEdgesChange={onEdgesChange}
50103
onConnect={onConnect}
51104
nodeTypes={nodeTypes}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Node } from "@xyflow/react";
2+
import { Sparkles, Type, FileText, RefreshCw } from "lucide-react";
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
DropdownMenu,
6+
DropdownMenuContent,
7+
DropdownMenuItem,
8+
DropdownMenuTrigger,
9+
} from "@/components/ui/dropdown-menu";
10+
import { useState } from "react";
11+
12+
interface AIActionsProps {
13+
selectedNodes: Node[];
14+
onAIAction: (action: 'complete' | 'summarize' | 'rephrase') => void;
15+
}
16+
17+
export function AIActionDropdown({ selectedNodes, onAIAction }: AIActionsProps) {
18+
const [loading, setLoading] = useState(false);
19+
const hasSelectedTextNodes = selectedNodes.some(node => node.type === 'text');
20+
21+
const handleAIAction = async (action: 'complete' | 'summarize' | 'rephrase') => {
22+
if (!hasSelectedTextNodes) return;
23+
24+
setLoading(true);
25+
try {
26+
await onAIAction(action);
27+
} finally {
28+
setLoading(false);
29+
}
30+
};
31+
32+
return (
33+
<div className="fixed right-4 top-4 z-10">
34+
<DropdownMenu>
35+
<DropdownMenuTrigger asChild>
36+
<Button
37+
variant="outline"
38+
size="icon"
39+
className={`p-2 transition-all ${
40+
hasSelectedTextNodes
41+
? 'bg-purple-100 text-purple-600 hover:bg-purple-200'
42+
: 'opacity-50 cursor-not-allowed'
43+
}`}
44+
disabled={!hasSelectedTextNodes || loading}
45+
>
46+
<Sparkles className={`h-5 w-5 ${loading ? 'animate-spin' : ''}`} />
47+
</Button>
48+
</DropdownMenuTrigger>
49+
<DropdownMenuContent align="end" className="w-48">
50+
<DropdownMenuItem
51+
onClick={() => handleAIAction('complete')}
52+
className="cursor-pointer"
53+
disabled={loading}
54+
>
55+
<Type className="mr-2 h-4 w-4" />
56+
Complete Text
57+
</DropdownMenuItem>
58+
<DropdownMenuItem
59+
onClick={() => handleAIAction('summarize')}
60+
className="cursor-pointer"
61+
disabled={loading}
62+
>
63+
<FileText className="mr-2 h-4 w-4" />
64+
Summarize Text
65+
</DropdownMenuItem>
66+
<DropdownMenuItem
67+
onClick={() => handleAIAction('rephrase')}
68+
className="cursor-pointer"
69+
disabled={loading}
70+
>
71+
<RefreshCw className="mr-2 h-4 w-4" />
72+
Rephrase Text
73+
</DropdownMenuItem>
74+
</DropdownMenuContent>
75+
</DropdownMenu>
76+
</div>
77+
);
78+
}

0 commit comments

Comments
 (0)