Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agent UI fixes #283

Merged
merged 5 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agenthub/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const updateChatName = (chatId: number, newName: string) => {

const ChatInterface: React.FC = () => {
const [messages, setMessages] = useState<Message[]>([]);
const [darkMode, setDarkMode] = useState<boolean>(false);
const [darkMode, setDarkMode] = useState<boolean>(true);
const [chats, setChats] = useState<Chat[]>([{ id: 1, name: 'General' }]);
const [activeChat, setActiveChat] = useState<number>(1);
const messagesEndRef = useRef<HTMLDivElement>(null);
Expand Down
95 changes: 52 additions & 43 deletions agenthub/components/agentchat/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import React, { useState, useRef, useEffect } from 'react';

import { ActionIcon, CopyButton } from '@mantine/core';
import { Clipboard, Check} from 'lucide-react';
import { Clipboard, Check } from 'lucide-react';

import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
Expand All @@ -14,58 +14,67 @@ import rehypeRaw from 'rehype-raw';
interface MarkdownProps {
content: string;
darkMode: boolean;
animation: boolean;
}

export const Markdown: React.FC<MarkdownProps> = ({ content, darkMode }) => {
export const Markdown: React.FC<MarkdownProps> = ({ content, darkMode, animation }) => {
const [displayedText, setDisplayedText] = useState('');
const animationRef = useRef<number | null>(null);
const currentIndexRef = useRef(0);

useEffect(() => {
let lastTimestamp: number | null = null;

const streamText = (timestamp: number) => {
if (lastTimestamp === null) {
lastTimestamp = timestamp;
}

const elapsed = timestamp - lastTimestamp;

if (elapsed >= 30) { // Minimum 30ms between updates
if (currentIndexRef.current < content.length) {
const chunkSize = Math.floor(Math.random() * 3) + 1;
const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize);

setDisplayedText(prevText => prevText + nextChunk);
currentIndexRef.current += chunkSize;

// Determine the next delay
let delay = Math.floor(Math.random() * 50) + 30;

if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
delay += Math.floor(Math.random() * 300) + 200;
} else if (nextChunk.includes(',') || nextChunk.includes(';')) {
delay += Math.floor(Math.random() * 150) + 100;
}

if (nextChunk.length > 5) {
delay += nextChunk.length * 10;
if (animation) {
let lastTimestamp: number | null = null;

const streamText = (timestamp: number) => {
if (lastTimestamp === null) {
lastTimestamp = timestamp;
}

const elapsed = timestamp - lastTimestamp;

if (elapsed >= 10) { // Reduced minimum delay between updates from 30ms to 10ms
if (currentIndexRef.current < content.length) {
// Increased chunk size for faster typing
const chunkSize = Math.floor(Math.random() * 4) + 2; // Now 2-5 characters at once
const nextChunk = content.slice(currentIndexRef.current, currentIndexRef.current + chunkSize);

setDisplayedText(prevText => prevText + nextChunk);
currentIndexRef.current += chunkSize;

// Reduced delays for a faster animation
let delay = Math.floor(Math.random() * 20) + 10; // Base delay between 10-30ms

// Shorter pauses for punctuation
if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
delay += Math.floor(Math.random() * 100) + 50; // Reduced to 50-150ms for major punctuation
} else if (nextChunk.includes(',') || nextChunk.includes(';')) {
delay += Math.floor(Math.random() * 50) + 25; // Reduced to 25-75ms for minor punctuation
}

// Reduced delay for word complexity
if (nextChunk.length > 5) {
delay += nextChunk.length * 3; // Reduced to 3ms per character
}

lastTimestamp = timestamp + delay;
}

lastTimestamp = timestamp + delay;
}
}


animationRef.current = requestAnimationFrame(streamText);
};

animationRef.current = requestAnimationFrame(streamText);
};

animationRef.current = requestAnimationFrame(streamText);

return () => {
if (animationRef.current !== null) {
cancelAnimationFrame(animationRef.current);
}
};

return () => {
if (animationRef.current !== null) {
cancelAnimationFrame(animationRef.current);
}
};
} else {
setDisplayedText(content);
}

}, [content]);


Expand Down
2 changes: 1 addition & 1 deletion agenthub/components/agentchat/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const MessageBubble: React.FC<MessageBubbleProps> = ({ message, darkMode,
</span>
</div>
<div className={`mt-1 text-sm break-words ${darkMode ? 'text-gray-300' : 'text-gray-700'}`}>
{isThinking ? <AgentLoader /> : <Markdown content={message.text} darkMode={darkMode} />}
{isThinking ? <AgentLoader /> : <Markdown content={message.text} darkMode={darkMode} animation={message.sender === 'user' ? false : true} />}
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion agenthub/components/agentchat/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const Sidebar: React.FC<SidebarProps> = ({ chats, activeChat, setActiveCh
return (
<div className={`w-60 flex-shrink-0 ${darkMode ? 'bg-gray-800' : 'bg-gray-100'} p-3 flex flex-col`}>
<div className={`p-4 ${darkMode ? 'bg-gray-700' : 'bg-gray-200'} rounded-lg mb-4`}>
<h2 className={`font-bold ${darkMode ? 'text-white' : 'text-gray-800'}`}>Your Workspace</h2>
<h2 className={`font-bold ${darkMode ? 'text-white' : 'text-gray-800'}`}>Your AIOS Workspace</h2>
</div>

<div className="flex-grow overflow-y-auto">
Expand Down
96 changes: 48 additions & 48 deletions agenthub/components/chat/body/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import { useState, useEffect } from 'react';

interface IProps {
content: string;
animation: boolean;
}

export default function Markdown({ content }: IProps) {
export default function Markdown({ content, animation }: IProps) {
const [copiedIndex, setCopiedIndex] = useState<number | null>(null);
const [displayedText, setDisplayedText] = useState('');

Expand All @@ -25,50 +26,49 @@ export default function Markdown({ content }: IProps) {
// }, [displayedText])

useEffect(() => {
let currentIndex = 0;
let timeoutId: any;

const streamText = () => {
if (currentIndex < content.length) {
// Determine a random chunk size between 1 and 3
const chunkSize = Math.floor(Math.random() * 3) + 1;
const nextChunk = content.slice(currentIndex, currentIndex + chunkSize);

setDisplayedText(content.slice(0, currentIndex + chunkSize));
currentIndex += chunkSize;

// Determine the next timeout
let timeout = Math.floor(Math.random() * 50) + 30; // Base timeout between 30ms and 80ms

// Add occasional longer pauses
if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
timeout += Math.floor(Math.random() * 300) + 200; // Add 200-500ms for punctuation
} else if (nextChunk.includes(',') || nextChunk.includes(';')) {
timeout += Math.floor(Math.random() * 150) + 100; // Add 100-250ms for minor punctuation
}

// Slightly vary speed based on word complexity (simplified approach)
if (nextChunk.length > 5) {
timeout += nextChunk.length * 10; // Add 10ms per character for longer words
}

timeoutId = setTimeout(streamText, timeout);
}
};

streamText();

// // Cursor blinking effect
// const cursorInterval = setInterval(() => {
// setShowCursor(prev => !prev);
// }, 530);

// Cleanup function
return () => {
clearTimeout(timeoutId);
// clearInterval(cursorInterval);
};
}, [content]);
if (animation) {
let currentIndex = 0;
let timeoutId: any;

const streamText = () => {
if (currentIndex < content.length) {
// Increased chunk size for faster typing
const chunkSize = Math.floor(Math.random() * 4) + 2; // Now 2-5 characters at once
const nextChunk = content.slice(currentIndex, currentIndex + chunkSize);

setDisplayedText(content.slice(0, currentIndex + chunkSize));
currentIndex += chunkSize;

// Reduced base timeout for faster typing
let timeout = Math.floor(Math.random() * 20) + 10; // Base timeout between 10ms and 30ms

// Shorter pauses for punctuation
if (nextChunk.includes('.') || nextChunk.includes('!') || nextChunk.includes('?')) {
timeout += Math.floor(Math.random() * 150) + 100; // Add 100-250ms for punctuation
} else if (nextChunk.includes(',') || nextChunk.includes(';')) {
timeout += Math.floor(Math.random() * 75) + 50; // Add 50-125ms for minor punctuation
}

// Reduced delay for word complexity
if (nextChunk.length > 5) {
timeout += nextChunk.length * 5; // Add 5ms per character for longer words
}

timeoutId = setTimeout(streamText, timeout);
}
};

streamText();

// Cleanup function
return () => {
clearTimeout(timeoutId);
};
} else {
setDisplayedText(content)
}

}, [content]);

return (
<ReactMarkdown
Expand All @@ -81,7 +81,7 @@ export default function Markdown({ content }: IProps) {
<div className='text-base !text-[#e06c75] '>
{match[1]}
</div>
<CopyButton
<CopyButton
text={String(children).replace(/\n$/, '')}
index={node!.position?.start.line || 0}
onCopy={handleCopy}
Expand All @@ -91,7 +91,7 @@ export default function Markdown({ content }: IProps) {
<SyntaxHighlighter
language={match[1]}
style={gruvboxDark}

>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
Expand All @@ -116,7 +116,7 @@ interface CopyButtonProps {

function CopyButton({ text, index, onCopy, isCopied }: CopyButtonProps) {
return (
<button
<button
onClick={() => onCopy(text, index)}
className="p-2 rounded-md bg-white/10 hover:bg-white/20 transition-colors duration-200"
>
Expand Down
2 changes: 1 addition & 1 deletion agenthub/components/chat/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export const ChatEditor: React.FC<ChatEditorProps> = ({ onSend, darkMode }) => {
},
}),
Placeholder.configure({
placeholder: 'Type a message...',
placeholder: '@ an agent to chat...',
}),
Mention.configure({
HTMLAttributes: {
Expand Down
Loading