Code clean-up and some fit and finish

- Add a lot more suggestions cards, improve mobile rendering of suggestion cards, improve alignment of chat input, shift message when starts recording voice, remove dead code
This commit is contained in:
sabaimran 2024-07-28 15:19:36 +05:30
parent 1a1d9c7257
commit 551630f0f1
6 changed files with 324 additions and 72 deletions

View file

@ -398,7 +398,9 @@ export default function ChatInputArea(props: ChatInputProps) {
<Button
variant={'ghost'}
className="!bg-none p-1 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500"
onClick={() => setRecording(!recording)}
onClick={() => {
setRecording(!recording);
}}
disabled={props.sendDisabled}
>
<Stop weight='fill' className={`${props.isMobileWidth ? 'w-6 h-6' : 'w-8 h-8'}`} />
@ -420,7 +422,10 @@ export default function ChatInputArea(props: ChatInputProps) {
<Button
variant={'ghost'}
className="!bg-none p-1 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500"
onClick={() => setRecording(!recording)}
onClick={() => {
setMessage("Listening...");
setRecording(!recording);
}}
disabled={props.sendDisabled}
>
<Microphone weight='fill' className={`${props.isMobileWidth ? 'w-6 h-6' : 'w-8 h-8'}`} />

View file

@ -53,10 +53,6 @@ export default function NavMenu(props: NavMenuProps) {
useEffect(() => {
const mq = window.matchMedia(
"(prefers-color-scheme: dark)"
);
window.addEventListener('resize', () => {
setIsMobileWidth(window.innerWidth < 768);
});

View file

@ -35,11 +35,15 @@ export default function SuggestionCard(data: SuggestionCardProps) {
const cardContent = (
<Card className={cardClassName}>
<CardHeader className="m-0 p-2 pb-1 relative">
<div className="flex flex-row md:flex-col">
{convertSuggestionColorToIconClass(data.image)}
<CardTitle className={titleClassName}>{data.title}</CardTitle>
</div>
</CardHeader>
<CardContent className="m-0 p-2 pr-4 pt-1">
<CardDescription className={descriptionClassName}>{data.body}</CardDescription>
<CardDescription className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4`}>
{data.body}
</CardDescription>
</CardContent>
</Card>
);

View file

@ -0,0 +1,275 @@
export interface Suggestion {
type: string;
color: string;
description: string;
link: string;
}
//samples for suggestion cards (should be moved to json later)
export const suggestionsData: Suggestion[] = [
{
type: "Automation",
color: "blue",
description: "Send me a summary of HackerNews every morning.",
link: "/automations?subject=Summarizing%20Top%20Headlines%20from%20HackerNews&query=Summarize%20the%20top%20headlines%20on%20HackerNews&crontime=00%207%20*%20*%20*",
},
{
type: "Automation",
color: "blue",
description: "Compose a bedtime story that a five-year-old might enjoy.",
link: "/automations?subject=Daily%20Bedtime%20Story&query=Compose%20a%20bedtime%20story%20that%20a%20five-year-old%20might%20enjoy.%20It%20should%20not%20exceed%20five%20paragraphs.%20Appeal%20to%20the%20imagination%2C%20but%20weave%20in%20learnings.&crontime=0%2021%20*%20*%20*",
},
{
type: "Paint",
color: "green",
description: "Paint a picture of a sunset but it's made of stained glass tiles",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Search for the best attractions in Austria Hungary",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Generate a weekly meal plan with recipes.",
link: "/automations?subject=Weekly Meal Plan&query=Create a weekly meal plan with 7 dinner recipes, including ingredients and brief instructions. Focus on balanced, healthy meals.&crontime=0 18 * * 0",
},
{
type: "Paint",
color: "green",
description: "Paint a futuristic cityscape with flying cars.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Find the top-rated coffee shops in Seattle.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Send daily motivational quotes.",
link: "/automations?subject=Daily Motivation&query=Provide an inspiring quote for the day along with a brief explanation of its meaning.&crontime=0 7 * * *",
},
{
type: "Paint",
color: "green",
description: "Create an abstract representation of jazz music.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Research the history of the Eiffel Tower.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Compile a weekly news summary.",
link: "/automations?subject=Weekly News Digest&query=Summarize the top 5 most important news stories of the week across various categories.&crontime=0 18 * * 5",
},
{
type: "Paint",
color: "green",
description: "Paint a portrait of a cat wearing a Victorian-era costume.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Find beginner-friendly hiking trails near Los Angeles.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Generate a daily writing prompt.",
link: "/automations?subject=Daily Writing Prompt&query=Create an engaging writing prompt suitable for short story or journal writing.&crontime=0 9 * * *",
},
{
type: "Paint",
color: "green",
description: "Create a surrealist landscape inspired by Salvador Dali.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Research the benefits and drawbacks of electric vehicles.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Send weekly language learning tips for Spanish.",
link: "/automations?subject=Spanish Learning Tips&query=Provide a useful Spanish language learning tip, including vocabulary, grammar, or cultural insight.&crontime=0 19 * * 2",
},
{
type: "Paint",
color: "green",
description: "Paint a scene from a fairy tale in the style of Studio Ghibli.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Find the best-rated science fiction books of the last decade.",
link: "",
},
{
type: "Paint",
color: "green",
description: "Paint a still life of exotic fruits in a neon color palette.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Research the most eco-friendly cities in Europe.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Send daily reminders for habit tracking.",
link: "/automations?subject=Habit Tracker&query=Generate a daily reminder to track habits, including a motivational message.&crontime=0 20 * * *",
},
{
type: "Paint",
color: "green",
description: "Create a digital painting of a cyberpunk street market.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Summarize the biography of this figure: https://en.wikipedia.org/wiki/Jean_Baptiste_Point_du_Sable",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Send daily Spanish phrases used in Latin America.",
link: "/automations?subject=Daily Latin American Spanish&query=Provide a common Spanish phrase or slang term used in Latin America, its meaning, and an example of usage. Include which countries it's most common in.&crontime=0 8 * * *",
},
{
type: "Paint",
color: "green",
description: "Create a vibrant painting inspired by Frida Kahlo's style.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Find the best empanada recipee from Colombia countries.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Weekly update on the Brazilian startup ecosystems.",
link: "/automations?subject=LatAm Startup News&query=Provide a summary of the most significant developments in Latin American startup ecosystems this week. Include notable funding rounds, expansions, or policy changes.&crontime=0 18 * * 5",
},
{
type: "Paint",
color: "green",
description: "Paint a colorful scene of a traditional Day of the Dead celebration in Mexico.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Daily Swahili phrase with English translation.",
link: "/automations?subject=Daily Swahili Lesson&query=Provide a common Swahili phrase or proverb, its English translation, and a brief explanation of its cultural significance in East Africa.&crontime=0 7 * * *",
},
{
type: "Paint",
color: "green",
description: "Create a digital painting of the Serengeti during wildebeest migration.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Research the top M-Pesa alternatives in East Africa.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Weekly update on East African tech startups and innovations.",
link: "/automations?subject=East African Tech News&query=Summarize the most significant developments in East African tech startups and innovations this week. Include notable funding rounds, new product launches, or policy changes affecting the tech ecosystem.&crontime=0 18 * * 5",
},
{
type: "Paint",
color: "green",
description: "Paint a colorful scene inspired by Maasai traditional clothing and jewelry.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Weekly summary of EU policy changes and their impact.",
link: "/automations?subject=EU Policy Update&query=Summarize the most significant EU policy changes or proposals from this week. Explain their potential impact on European citizens and businesses.&crontime=0 17 * * 5",
},
{
type: "Paint",
color: "green",
description: "Paint a digital landscape of the Northern Lights over the Norwegian fjords.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Daily East Asian proverb with explanation.",
link: "/automations?subject=East Asian Wisdom&query=Provide a proverb from an East Asian language (rotating through Chinese, Japanese, Korean, etc.), its English translation, and a brief explanation of its cultural significance and practical application.&crontime=0 7 * * *",
},
{
type: "Paint",
color: "green",
description: "Create a digital painting in the style of traditional Chinese ink wash landscape.",
link: "",
},
{
type: "Online Search",
color: "yellow",
description: "Research the latest trends in K-pop and its global influence.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Weekly summary of technological innovations from East Asian tech giants.",
link: "/automations?subject=East Asian Tech Update&query=Summarize the most significant technological innovations or product launches from major East Asian tech companies (e.g., Samsung, Sony, Alibaba, Tencent) this week. Explain their potential impact on global markets.&crontime=0 18 * * 5",
},
{
type: "Paint",
color: "green",
description: "Paint a vibrant scene of a Japanese cherry blossom festival.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Daily South Asian recipe with cultural significance.",
link: "/automations?subject=South Asian Culinary Journey&query=Provide a traditional South Asian recipe (rotating through Indian, Pakistani, Bangladeshi, Sri Lankan, etc. cuisines), including ingredients, brief instructions, and its cultural significance or origin story.&crontime=0 10 * * *",
},
{
type: "Online Search",
color: "yellow",
description: "Research the impact of Bollywood on global cinema and fashion.",
link: "",
},
{
type: "Automation",
color: "blue",
description: "Weekly update on South Asian startup ecosystems and innovations.",
link: "/automations?subject=South Asian Startup Pulse&query=Summarize the most significant developments in South Asian startup ecosystems this week. Include notable funding rounds, innovative solutions to local challenges, and any policy changes affecting the tech landscape in countries like India, Bangladesh, Pakistan, and Sri Lanka.&crontime=0 18 * * 5",
},
];

View file

@ -3,8 +3,7 @@ div.main {
color: hsla(var(--foreground));
}
.suggestions {
display: flex;
div.suggestions {
overflow-x: none;
height: fit-content;
padding: 10px;
@ -106,6 +105,7 @@ div.sidePanel {
div.chatBoxBody {
width: 100%;
grid-template-rows: auto;
}
div.chatBox {

View file

@ -13,42 +13,36 @@ import Image from 'next/image';
import 'katex/dist/katex.min.css';
import { StreamMessage } from './components/chatMessage/chatMessage';
import ChatInputArea, { ChatOptions } from './components/chatInputArea/chatInputArea';
import { useAuthenticatedData } from './common/auth';
import { Card, CardContent, CardTitle } from '@/components/ui/card';
import { Card, CardTitle } from '@/components/ui/card';
import { convertSuggestionColorToTextClass, colorMap, convertColorToBorderClass } from './common/colorUtils';
import { getIconFromIconName } from './common/iconUtils';
import { ClockCounterClockwise } from '@phosphor-icons/react';
import { AgentData } from './agents/page';
//samples for suggestion cards (should be moved to json later)
const suggestions: Suggestion[] = [["Automation", "blue", "Send me a summary of HackerNews every morning.", "/automations?subject=Summarizing%20Top%20Headlines%20from%20HackerNews&query=Summarize%20the%20top%20headlines%20on%20HackerNews&crontime=00%207%20*%20*%20*"], ["Automation", "blue", "Compose a bedtime story that a five-year-old might enjoy.", "/automations?subject=Daily%20Bedtime%20Story&query=Compose%20a%20bedtime%20story%20that%20a%20five-year-old%20might%20enjoy.%20It%20should%20not%20exceed%20five%20paragraphs.%20Appeal%20to%20the%20imagination%2C%20but%20weave%20in%20learnings.&crontime=0%2021%20*%20*%20*"], ["Paint", "green", "Paint a picture of a sunset but it's made of stained glass tiles", ""], ["Online Search", "yellow", "Search for the best attractions in Austria Hungary", ""]];
import { Suggestion, suggestionsData } from './components/suggestions/suggestionsData';
//get today's day
const today = new Date();
const day = today.getDay();
const greetings = [
`Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What would you like to do today?`,
'How can I help you today?',
`Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What can I do for you today?`,
`Good ${today.getHours() < 12 ? 'morning' : today.getHours() < 15 ? 'afternoon' : 'evening'}! What's on your mind?`,
`Ready to breeze through your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]}?`,
`Need help navigating your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?`
`Want to navigate your ${['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][day]} workload?`
];
const greeting = greetings[~~(Math.random() * greetings.length)];
interface ChatBodyDataProps {
chatOptionsData: ChatOptions | null;
setTitle: (title: string) => void;
onConversationIdChange?: (conversationId: string) => void;
setQueryToProcess: (query: string) => void;
streamedMessages: StreamMessage[];
setUploadedFiles: (files: string[]) => void;
isMobileWidth?: boolean;
isLoggedIn: boolean;
conversationId: string | null; // Added this line
}
type Suggestion = [string, string, string, string];
async function createNewConvo(slug: string) {
try {
@ -79,10 +73,11 @@ function ChatBodyData(props: ChatBodyDataProps) {
const { data, error } = useSWR<AgentData[]>('agents', agentsFetcher, { revalidateOnFocus: false });
function shuffleAndSetOptions() {
const shuffled = [...suggestions].sort(() => 0.5 - Math.random());
const shuffled = [...suggestionsData].sort(() => 0.5 - Math.random());
setShuffledOptions(shuffled.slice(0, 3));
//use the text to color function above convertSuggestionColorToTextClass
const colors = shuffled.map(option => convertSuggestionColorToTextClass(option[1]));
// Use the text to color function above convertSuggestionColorToTextClass
const colors = shuffled.map(option => convertSuggestionColorToTextClass(option.color));
setShuffledColors(colors);
}
@ -116,26 +111,27 @@ function ChatBodyData(props: ChatBodyDataProps) {
processMessage();
if (message) {
setProcessingMessage(true);
props.setQueryToProcess(message);
};
}, [selectedAgent, message]);
useEffect(() => {
if (props.streamedMessages &&
props.streamedMessages.length > 0 &&
props.streamedMessages[props.streamedMessages.length - 1].completed) {
setProcessingMessage(false);
} else {
setMessage('');
}
}, [props.streamedMessages]);
const nSlice = props.isMobileWidth ? 3 : 4;
const agents = data ? data.slice(0, nSlice) : []; //select first 4 agents to show as options
const shuffledAgents = data ? [...data].sort(() => 0.5 - Math.random()) : [];
const agents = data ? [data[0]] : []; // Always add the first/default agent.
shuffledAgents.slice(0, nSlice - 1).forEach(agent => {
if (!agents.find(a => a.slug === agent.slug)) {
agents.push(agent);
}
});
//generate colored icons for the selected agents
const agentIcons = agents.map(agent => getIconFromIconName(agent.icon, agent.color) || <Image key={agent.name} src={agent.avatar} alt={agent.name} width={50} height={50} />);
const agentIcons = agents.map(
agent => getIconFromIconName(agent.icon, agent.color) || <Image key={agent.name} src={agent.avatar} alt={agent.name} width={50} height={50} />
);
function fillArea(link: string, type: string, prompt: string) {
if (!link) {
let message_str = "";
@ -163,26 +159,6 @@ function ChatBodyData(props: ChatBodyDataProps) {
return colorMap[color] || 'border-black'; // Default to black if color not found
}
function highlightHandler(slug: string): void {
const buttons = document.getElementsByClassName("agent");
const agent = agents.find(agent => agent.slug === slug);
const borderColorClass = getTailwindBorderClass(agent?.color || 'gray');
Array.from(buttons).forEach((button: Element) => {
const buttonElement = button as HTMLElement;
if (buttonElement.classList.contains(slug)) {
buttonElement.classList.add(borderColorClass, 'border');
buttonElement.classList.remove('border-stone-100', 'dark:border-neutral-700');
}
else {
Object.values(colorMap).forEach(colorClass => {
buttonElement.classList.remove(colorClass, 'border');
});
buttonElement.classList.add('border', 'border-stone-100', 'dark:border-neutral-700');
}
});
}
return (
<div className={`${styles.chatBoxBody}`}>
<div className="w-full text-center">
@ -226,14 +202,16 @@ function ChatBodyData(props: ChatBodyDataProps) {
setUploadedFiles={props.setUploadedFiles} />
</div>
}
<div className={`suggestions ${styles.suggestions} w-full ${props.isMobileWidth ? 'flex flex-col' : 'flex flex-row'} justify-center items-center`}>
{shuffledOptions.map(([key, styleClass, value, link], index) => (
<div key={`${key} ${value}`} onClick={() => fillArea(link, key, value)}>
<div className={`${styles.suggestions} w-full ${props.isMobileWidth ? 'grid' : 'flex flex-row'} justify-center items-center`}>
{shuffledOptions.map((suggestion, index) => (
<div
key={`${suggestion.type} ${suggestion.description}`}
onClick={() => fillArea(suggestion.link, suggestion.type, suggestion.description)}>
<SuggestionCard
key={key + Math.random()}
title={key}
body={value.length > 65 ? value.substring(0, 65) + '...' : value}
link={link}
key={suggestion.type + Math.random()}
title={suggestion.type}
body={suggestion.description}
link={suggestion.link}
color={shuffledColors[index]}
image={shuffledColors[index]}
/>
@ -288,8 +266,6 @@ export default function Home() {
const [isLoading, setLoading] = useState(true);
const [title, setTitle] = useState('');
const [conversationId, setConversationID] = useState<string | null>(null);
const [messages, setMessages] = useState<StreamMessage[]>([]);
const [queryToProcess, setQueryToProcess] = useState<string>('');
const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);
const [isMobileWidth, setIsMobileWidth] = useState(false);
@ -343,14 +319,10 @@ export default function Home() {
<div className={`${styles.chatBoxBody}`}>
<ChatBodyData
isLoggedIn={authenticatedData !== null}
streamedMessages={messages}
chatOptionsData={chatOptionsData}
setTitle={setTitle}
setQueryToProcess={setQueryToProcess}
setUploadedFiles={setUploadedFiles}
isMobileWidth={isMobileWidth}
onConversationIdChange={handleConversationIdChange}
conversationId={conversationId}
/>
</div>
</div>