diff --git a/llm-fe/src/llm-fe/components/Tracker/Tracker.tsx b/llm-fe/src/llm-fe/components/Tracker/Tracker.tsx
new file mode 100644
index 0000000..a5ec347
--- /dev/null
+++ b/llm-fe/src/llm-fe/components/Tracker/Tracker.tsx
@@ -0,0 +1,23 @@
+import React, { useEffect } from 'react';
+
+const Tracker: React.FC = () => {
+ useEffect(() => {
+ if (process.env.NODE_ENV === 'production') {
+ const script = document.createElement('script');
+ script.src = "https://tianji.aimloperations.com/tracker.js";
+ script.async = true;
+ script.defer = true;
+ script.setAttribute('data-website-id', 'cm7x7m52m03kbddswbswrt17y');
+
+ document.body.appendChild(script);
+
+ return () => {
+ document.body.removeChild(script);
+ };
+ }
+ }, []);
+
+ return null;
+};
+
+export default Tracker;
diff --git a/llm-fe/src/llm-fe/contexts/WebSocketContext.js b/llm-fe/src/llm-fe/contexts/WebSocketContext.js
index 4f5d579..b86292d 100644
--- a/llm-fe/src/llm-fe/contexts/WebSocketContext.js
+++ b/llm-fe/src/llm-fe/contexts/WebSocketContext.js
@@ -18,6 +18,8 @@ function WebSocketProvider({ children }) {
const channels = useRef({}); // maps each channel to the callback
const { account, setAccount } = useContext(AccountContext);
const [currentChannel, setCurrentChannel] = useState("");
+ const [isConnected, setIsConnected] = useState(false);
+
/* called from a component that registers a callback for a channel */
const subscribe = (channel, callback) => {
//console.log(`Subbing to ${channel}`)
@@ -76,8 +78,12 @@ function WebSocketProvider({ children }) {
ws.current.onopen = () => {
setSocket(ws.current);
+ setIsConnected(true);
+ };
+ ws.current.onclose = () => {
+ console.log('websocket closed');
+ setIsConnected(false);
};
- ws.current.onclose = () => { };
ws.current.onmessage = (message) => {
const data = message.data;
// lookup for an existing chat in which this message belongs
@@ -103,7 +109,7 @@ function WebSocketProvider({ children }) {
/* subscribe and unsubscribe are the only required prop for the context */
return (
{children}
diff --git a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
index e094490..8c9aa1c 100644
--- a/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
+++ b/llm-fe/src/llm-fe/pages/AsyncDashboard2/AsyncDashboard2.tsx
@@ -3,6 +3,7 @@ import styled, { ThemeContext } from "styled-components";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { AttachFile, Delete, Send } from "@mui/icons-material"; // Keeping icons for now, can replace later if needed
+import { Tooltip } from "@mui/material";
import Markdown from "markdown-to-jsx";
import {
@@ -110,7 +111,7 @@ const StyledInputContainer = styled.div`
}
`;
-const StyledInput = styled.input`
+const StyledInput = styled.textarea`
flex: 1;
background: transparent;
border: none;
@@ -118,6 +119,11 @@ const StyledInput = styled.input`
font-size: 1rem;
padding: 0.8rem;
outline: none;
+ resize: none;
+ overflow-y: auto;
+ max-height: 150px; /* Approx 5 lines */
+ font-family: inherit;
+ line-height: 1.5;
&::placeholder {
color: ${({ theme }) => theme.darkMode ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.4)'};
@@ -147,6 +153,23 @@ const IconButton = styled.button`
}
`;
+const StyledSelect = styled.select`
+ background: transparent;
+ border: none;
+ color: ${({ theme }) => theme.colors.text};
+ font-size: 0.9rem;
+ padding: 0.5rem;
+ outline: none;
+ cursor: pointer;
+ margin-right: 0.5rem;
+ border-right: 1px solid ${({ theme }) => theme.colors.cardBorder};
+
+ option {
+ background: ${({ theme }) => theme.colors.background || '#1a1a1a'};
+ color: ${({ theme }) => theme.colors.text};
+ }
+`;
+
const ConversationItem = styled.div<{ $active: boolean }>`
padding: 0.8rem 1rem;
margin-bottom: 0.5rem;
@@ -226,7 +249,7 @@ const AlwaysScrollToBottom = (): JSX.Element => {
const AsyncDashboardInner = ({ }): JSX.Element => {
const [announcements, setAnnouncement] = useState
([]);
- const [subscribe, unsubscribe, socket, sendMessage] =
+ const [subscribe, unsubscribe, socket, sendMessage, isConnected] =
useContext(WebSocketContext);
const { account } = useContext(AccountContext);
@@ -242,6 +265,7 @@ const AsyncDashboardInner = ({ }): JSX.Element => {
const conversationRef = useRef(conversationDetails);
const theme = useContext(ThemeContext);
+ const textareaRef = useRef(null);
async function GetAnnouncements() {
const response: AxiosResponse =
@@ -271,12 +295,32 @@ const AsyncDashboardInner = ({ }): JSX.Element => {
conversationRef.current = tempConversations;
setConversationDetails(tempConversations);
sendMessage(prompt, selectedConversation, file, fileType, modelName);
- resetForm();
+ resetForm({
+ values: {
+ prompt: "",
+ file: null,
+ fileType: null,
+ modelName: modelName, // Keep the selected model
+ }
+ });
+
+ // Reset textarea height
+ if (textareaRef.current) {
+ textareaRef.current.style.height = 'auto';
+ }
} catch (e) {
console.log(`error ${e}`);
}
};
+ const adjustHeight = () => {
+ const textarea = textareaRef.current;
+ if (textarea) {
+ textarea.style.height = 'auto';
+ textarea.style.height = `${textarea.scrollHeight}px`;
+ }
+ };
+
return (
@@ -350,13 +394,24 @@ const AsyncDashboardInner = ({ }): JSX.Element => {
prompt: "",
file: null,
fileType: null,
- modelName: "",
+ modelName: "FAST",
}}
validationSchema={validationSchema}
onSubmit={handlePromptSubmit}
>
{(formik) => (