diff --git a/ui/client-app/src/App.js b/ui/client-app/src/App.js
index 8a9f0d9..ae36ee6 100644
--- a/ui/client-app/src/App.js
+++ b/ui/client-app/src/App.js
@@ -2,6 +2,7 @@
import Navbar from "./components/Navbar";
import HomePage from "./pages/HomePage"; // Import the new HomePage
import VoiceChatPage from "./pages/VoiceChatPage";
+import CodingAssistantPage from "./pages/CodingAssistantPage";
import LoginPage from "./pages/LoginPage";
const Icon = ({ path, onClick, className }) => (
@@ -34,6 +35,8 @@
return ;
case "voice-chat":
return ;
+ case "coding-assistant" :
+ return
case "login":
return ;
default:
diff --git a/ui/client-app/src/components/ChatArea.js b/ui/client-app/src/components/ChatArea.js
new file mode 100644
index 0000000..be3cf35
--- /dev/null
+++ b/ui/client-app/src/components/ChatArea.js
@@ -0,0 +1,57 @@
+// src/components/ChatArea.js
+import React, { useState, useRef, useEffect } from "react";
+import ChatWindow from "./ChatWindow";
+
+const ChatArea = ({ chatHistory, onSendMessage, isProcessing }) => {
+ const [inputValue, setInputValue] = useState("");
+ const inputRef = useRef(null);
+
+ const handleSendMessage = (e) => {
+ e.preventDefault();
+ if (inputValue.trim() !== "") {
+ onSendMessage(inputValue);
+ setInputValue("");
+ }
+ };
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, [isProcessing]);
+
+ return (
+
+ {/* Chat history display area */}
+
+
+
+
+ {/* Message input and send button area */}
+
+
+ );
+};
+
+export default ChatArea;
\ No newline at end of file
diff --git a/ui/client-app/src/components/CodeFolderAccess.js b/ui/client-app/src/components/CodeFolderAccess.js
new file mode 100644
index 0000000..37d4617
--- /dev/null
+++ b/ui/client-app/src/components/CodeFolderAccess.js
@@ -0,0 +1,42 @@
+// src/components/CodeFolderAccess.js
+import React from "react";
+
+const CodeFolderAccess = ({ selectedFolder, onSelectFolder }) => {
+ const handleFolderChange = async () => {
+ // This is a placeholder. In a real application, you would use a browser API
+ // or a desktop framework like Electron to access the file system.
+ // Example using the File System Access API (requires user gesture)
+ try {
+ const directoryHandle = await window.showDirectoryPicker();
+ const folderPath = directoryHandle.name; // Simplified path, you might get a full path in a real app
+ onSelectFolder(folderPath);
+ } catch (err) {
+ console.error("Failed to select directory:", err);
+ // You might want to handle user cancellation here
+ }
+ };
+
+ return (
+
+ {/* Button to trigger the folder selection dialog */}
+
+ Select a Local Code Folder
+
+
+ {/* Display the selected folder path */}
+
+
+ Selected Folder:
+
+
+ {selectedFolder || "No folder selected"}
+
+
+
+ );
+};
+
+export default CodeFolderAccess;
\ No newline at end of file
diff --git a/ui/client-app/src/components/InteractionLog.js b/ui/client-app/src/components/InteractionLog.js
new file mode 100644
index 0000000..4057b18
--- /dev/null
+++ b/ui/client-app/src/components/InteractionLog.js
@@ -0,0 +1,40 @@
+// src/components/InteractionLog.js
+import React from "react";
+
+const InteractionLog = ({ logs }) => {
+ return (
+
+
+ {logs.length > 0 ? (
+ logs.map((log, index) => (
+
+
+ {log.type.charAt(0).toUpperCase() + log.type.slice(1)}:
+
+
+ {log.message}
+
+
+ ))
+ ) : (
+
+ No interactions yet.
+
+ )}
+
+
+ );
+};
+
+export default InteractionLog;
\ No newline at end of file
diff --git a/ui/client-app/src/components/Navbar.js b/ui/client-app/src/components/Navbar.js
index 13220ea..0166164 100644
--- a/ui/client-app/src/components/Navbar.js
+++ b/ui/client-app/src/components/Navbar.js
@@ -4,6 +4,7 @@
const navItems = [
{ name: "Home", icon: "M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" , page: "home" },
{ name: "Voice Chat", icon:"M12 1a3 3 0 0 1 3 3v7a3 3 0 1 1-6 0V4a3 3 0 0 1 3-3zm5 10a5 5 0 0 1-10 0H5a7 7 0 0 0 14 0h-2zm-5 11v-4h-2v4h2z", page: "voice-chat" },
+ { name: "Coding Assistant", icon: "M9 16l-4-4 4-4M15 16l4-4-4-4", page: "coding-assistant" },
{ name: "History", icon: "M22 12h-4l-3 9L9 3l-3 9H2", page: "history" , disabled: true},
{ name: "Favorites", icon: "M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z", page: "favorites" , disabled: true},
];
diff --git a/ui/client-app/src/hooks/useCodeAssistant.js b/ui/client-app/src/hooks/useCodeAssistant.js
new file mode 100644
index 0000000..1be2c81
--- /dev/null
+++ b/ui/client-app/src/hooks/useCodeAssistant.js
@@ -0,0 +1,146 @@
+// src/hooks/useCodeAssistant.js
+import { useState, useEffect, useRef, useCallback } from "react";
+
+const useCodeAssistant = ({ pageContainerRef }) => {
+ const [chatHistory, setChatHistory] = useState([]);
+ const [thinkingProcess, setThinkingProcess] = useState([]);
+ const [selectedFolder, setSelectedFolder] = useState(null);
+ const [connectionStatus, setConnectionStatus] = useState("disconnected");
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [isPaused, setIsPaused] = useState(false);
+ const [errorMessage, setErrorMessage] = useState("");
+ const [showErrorModal, setShowErrorModal] = useState(false);
+
+ const ws = useRef(null);
+
+ // Initialize WebSocket connection
+ useEffect(() => {
+ // Replace with your WebSocket server URL
+ const websocketUrl = "wss://your-llm-assistant-server.com";
+ ws.current = new WebSocket(websocketUrl);
+
+ ws.current.onopen = () => {
+ console.log("WebSocket connected");
+ setConnectionStatus("connected");
+ };
+
+ ws.current.onmessage = (event) => {
+ const message = JSON.parse(event.data);
+ handleIncomingMessage(message);
+ };
+
+ ws.current.onclose = () => {
+ console.log("WebSocket disconnected");
+ setConnectionStatus("disconnected");
+ setIsProcessing(false);
+ };
+
+ ws.current.onerror = (error) => {
+ console.error("WebSocket error:", error);
+ setConnectionStatus("error");
+ setErrorMessage("WebSocket connection failed. Please check the server.");
+ setShowErrorModal(true);
+ };
+
+ return () => {
+ ws.current.close();
+ };
+ }, []);
+
+ // Handle incoming messages from the server
+ const handleIncomingMessage = useCallback((message) => {
+ switch (message.type) {
+ case "chat_message":
+ setChatHistory((prevHistory) => [...prevHistory, { isUser: false, text: message.content }]);
+ setIsProcessing(false);
+ break;
+ case "thinking_log":
+ setThinkingProcess((prevProcess) => [
+ ...prevProcess,
+ { type: message.subtype, message: message.content },
+ ]);
+ break;
+ case "file_lookup_request":
+ // In a real app, this would trigger a local file read and send the content back.
+ // For this hook, we just log the request.
+ setThinkingProcess((prevProcess) => [
+ ...prevProcess,
+ { type: "system", message: `AI requested file: ${message.filename}` },
+ ]);
+ // To send a file back:
+ // ws.current.send(JSON.stringify({ type: "file_content", content: "..." }));
+ break;
+ case "error":
+ setErrorMessage(message.content);
+ setShowErrorModal(true);
+ setIsProcessing(false);
+ break;
+ case "status_update":
+ setIsProcessing(message.processing);
+ setIsPaused(message.paused);
+ setConnectionStatus(message.status);
+ break;
+ default:
+ console.log("Unknown message type:", message);
+ }
+ }, []);
+
+ // Send a chat message to the server
+ const handleSendChat = useCallback(
+ (text) => {
+ if (ws.current && ws.current.readyState === WebSocket.OPEN) {
+ setChatHistory((prevHistory) => [...prevHistory, { isUser: true, text }]);
+ setIsProcessing(true);
+ ws.current.send(JSON.stringify({ type: "chat_message", content: text }));
+ }
+ },
+ []
+ );
+
+ // Send the selected folder path to the server
+ const handleSelectFolder = useCallback(
+ (folderPath) => {
+ if (ws.current && ws.current.readyState === WebSocket.OPEN) {
+ setSelectedFolder(folderPath);
+ ws.current.send(JSON.stringify({ type: "select_folder", path: folderPath }));
+ setThinkingProcess((prevProcess) => [
+ ...prevProcess,
+ { type: "user", message: `Selected local folder: ${folderPath}` },
+ ]);
+ }
+ },
+ []
+ );
+
+ // Pause the AI's processing
+ const handlePause = useCallback(() => {
+ if (ws.current && ws.current.readyState === WebSocket.OPEN) {
+ ws.current.send(JSON.stringify({ type: "control", command: "pause" }));
+ }
+ }, []);
+
+ // Stop the AI's processing
+ const handleStop = useCallback(() => {
+ if (ws.current && ws.current.readyState === WebSocket.OPEN) {
+ ws.current.send(JSON.stringify({ type: "control", command: "stop" }));
+ }
+ }, []);
+
+ return {
+ chatHistory,
+ thinkingProcess,
+ selectedFolder,
+ connectionStatus,
+ isProcessing,
+ isPaused,
+ errorMessage,
+ showErrorModal,
+ handleSendChat,
+ handleSelectFolder,
+ handlePause,
+ handleStop,
+ setShowErrorModal,
+ };
+};
+
+export default useCodeAssistant;
\ No newline at end of file
diff --git a/ui/client-app/src/pages/CodingAssistantPage.js b/ui/client-app/src/pages/CodingAssistantPage.js
new file mode 100644
index 0000000..05532d5
--- /dev/null
+++ b/ui/client-app/src/pages/CodingAssistantPage.js
@@ -0,0 +1,95 @@
+import React, { useState, useRef, useEffect, useCallback } from "react";
+// Import the components you'll create for each section
+import ChatArea from "../components/ChatArea";
+import CodeFolderAccess from "../components/CodeFolderAccess";
+import InteractionLog from "../components/InteractionLog";
+// import Controls from "../components/Controls";
+
+// A custom hook to manage WebSocket connection and state
+import useCodeAssistant from "../hooks/useCodeAssistant";
+
+const CodeAssistantPage = () => {
+ // Reference for the main container to manage scrolling
+ const pageContainerRef = useRef(null);
+
+ // Use a custom hook to handle the core logic
+ const {
+ chatHistory,
+ thinkingProcess,
+ connectionStatus,
+ selectedFolder,
+ isProcessing,
+ isPaused,
+ errorMessage,
+ showErrorModal,
+ handleSendChat,
+ handleSelectFolder,
+ handlePause,
+ handleStop,
+ setShowErrorModal,
+ } = useCodeAssistant({ pageContainerRef });
+
+ // Scroll to the bottom of the page when new content is added
+ useEffect(() => {
+ if (pageContainerRef.current) {
+ pageContainerRef.current.scrollTop = pageContainerRef.current.scrollHeight;
+ }
+ }, [chatHistory, thinkingProcess]);
+
+ return (
+
+ {/* Main content area */}
+
+
+ {/* Left Column: Chat and Code Access */}
+
+ {/* Area 1: Chat with LLM */}
+
+
Chat with the LLM
+
+
+
+ {/* Area 2: Code Folder Lookup */}
+
+
Code Folder Access
+
+
+
+
+ {/* Right Column: Thinking Process and Interaction Log */}
+
+
AI Thinking Process
+
+
+
+
+
+ {/* Controls at the bottom
+
*/}
+
+ {/* Error Modal */}
+ {showErrorModal && (
+
+
+
Error
+
{errorMessage}
+
setShowErrorModal(false)}
+ className="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
+ >
+ Close
+
+
+
+ )}
+
+ );
+};
+
+export default CodeAssistantPage;
\ No newline at end of file