From 95826393e110f51a9c61031124621022f74e764f Mon Sep 17 00:00:00 2001 From: sabaimran Date: Sat, 21 Dec 2024 18:57:19 -0800 Subject: [PATCH 01/16] Update the home page suggestion cards - Rather than chunky generic cards, make the suggested actions more action oriented, around the problem a user might want to solve. Give them follow-up options. Design still in progress. --- src/interface/web/app/common/iconUtils.tsx | 3 + .../chatInputArea/chatInputArea.tsx | 28 + .../components/suggestions/suggestionCard.tsx | 67 +- .../components/suggestions/suggestionsData.ts | 958 ++++-------------- src/interface/web/app/page.tsx | 171 ++-- src/interface/web/tailwind.config.ts | 5 + 6 files changed, 387 insertions(+), 845 deletions(-) diff --git a/src/interface/web/app/common/iconUtils.tsx b/src/interface/web/app/common/iconUtils.tsx index 0ef7f09a..e1ebdd78 100644 --- a/src/interface/web/app/common/iconUtils.tsx +++ b/src/interface/web/app/common/iconUtils.tsx @@ -168,6 +168,9 @@ const iconMap: IconMap = { Broadcast: (color: string, width: string, height: string) => ( ), + Image: (color: string, width: string, height: string) => ( + + ), }; export function getIconForSlashCommand(command: string, customClassName: string | null = null) { diff --git a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx index 73dfce36..59ce835d 100644 --- a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx +++ b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx @@ -65,6 +65,12 @@ export interface AttachedFileText { size: number; } +export enum ChatInputFocus { + MESSAGE = "message", + FILE = "file", + RESEARCH = "research", +} + interface ChatInputProps { sendMessage: (message: string) => void; sendImage: (image: string) => void; @@ -77,11 +83,15 @@ interface ChatInputProps { agentColor?: string; isResearchModeEnabled?: boolean; setTriggeredAbort: (value: boolean) => void; + prefillMessage?: string; + focus?: ChatInputFocus; } export const ChatInputArea = forwardRef((props, ref) => { const [message, setMessage] = useState(""); const fileInputRef = useRef(null); + const fileInputButtonRef = useRef(null); + const researchModeRef = useRef(null); const [warning, setWarning] = useState(null); const [error, setError] = useState(null); @@ -125,6 +135,22 @@ export const ChatInputArea = forwardRef((pr } }, [uploading]); + useEffect(() => { + if (props.prefillMessage) { + setMessage(props.prefillMessage); + } + }, [props.prefillMessage]); + + useEffect(() => { + if (props.focus === ChatInputFocus.MESSAGE) { + chatInputRef?.current?.focus(); + } else if (props.focus === ChatInputFocus.FILE) { + fileInputButtonRef.current?.focus(); + } else if (props.focus === ChatInputFocus.RESEARCH) { + researchModeRef.current?.focus(); + } + }, [props.focus]); + useEffect(() => { async function fetchImageData() { if (imagePaths.length > 0) { @@ -630,6 +656,7 @@ export const ChatInputArea = forwardRef((pr className="!bg-none p-0 m-2 h-auto text-3xl rounded-full text-gray-300 hover:text-gray-500" disabled={props.sendDisabled || !props.isLoggedIn} onClick={handleFileButtonClick} + ref={fileInputButtonRef} > @@ -732,6 +759,7 @@ export const ChatInputArea = forwardRef((pr variant="ghost" className="float-right justify-center gap-1 flex items-center p-1.5 mr-2 h-fit" disabled={props.sendDisabled || !props.isLoggedIn} + ref={researchModeRef} onClick={() => { setUseResearchMode(!useResearchMode); chatInputRef?.current?.focus(); diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index 5f22de1f..8671dff6 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -1,25 +1,32 @@ "use client"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardDescription } from "@/components/ui/card"; import styles from "./suggestions.module.css"; -import { converColorToBgGradient } from "@/app/common/colorUtils"; import { convertSuggestionTitleToIconClass } from "./suggestionsData"; +import { ArrowLeft, ArrowRight } from "@phosphor-icons/react"; -interface SuggestionCardProps { +interface StepOneSuggestionCardProps { title: string; body: string; - link: string; color: string; } -export default function SuggestionCard(data: SuggestionCardProps) { - const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] md:h-[180px] cursor-pointer md:p-2`; +interface StepOneSuggestionRevertCardProps extends StepOneSuggestionCardProps { + onClick: () => void; +} + +interface StepTwoSuggestionCardProps { + prompt: string; +} + +export function StepOneSuggestionCard(data: StepOneSuggestionCardProps) { + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer md:p-2`; const descriptionClassName = `${styles.text} dark:text-white`; const cardContent = (
- + {convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())} ); - return data.link ? ( - - {cardContent} - - ) : ( - cardContent + return cardContent; +} + +export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) { + const cardClassName = `${styles.card} md:h-fit sm:w-full h-fit cursor-pointer md:p-2`; + + return ( + +
+ + + + {data.prompt} + + +
+
+ ); +} + +export function StepOneSuggestionRevertCard(data: StepOneSuggestionRevertCardProps) { + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer md:p-2 animate-fade-in-up`; + const descriptionClassName = `${styles.text} dark:text-white`; + + return ( + +
+ + + {convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())} + + {data.body} + + +
+
); } diff --git a/src/interface/web/app/components/suggestions/suggestionsData.ts b/src/interface/web/app/components/suggestions/suggestionsData.ts index 43058bd6..ab3e5a14 100644 --- a/src/interface/web/app/components/suggestions/suggestionsData.ts +++ b/src/interface/web/app/components/suggestions/suggestionsData.ts @@ -1,14 +1,7 @@ import { getIconFromIconName } from "@/app/common/iconUtils"; - -export interface Suggestion { - type: string; - color: string; - description: string; - link: string; -} +import { ChatInputFocus } from "../chatInputArea/chatInputArea"; export enum SuggestionType { - Automation = "Automation", Paint = "Paint", Travel = "Travel", Health = "Health", @@ -22,6 +15,19 @@ export enum SuggestionType { Fun = "Fun", Code = "Code", Finance = "Finance", + Document = "Document", + Image = "Image", +} + +export interface StepOneSuggestion { + type: SuggestionType; + color: string; + description: string; + focus: ChatInputFocus; +} + +export interface StepTwoSuggestion { + prompt: string; } const suggestionToColorMap: { [key in SuggestionType]?: string } = {}; @@ -30,7 +36,6 @@ function addSuggestionColorMap(type: SuggestionType, color: string) { suggestionToColorMap[type] = color; } -addSuggestionColorMap(SuggestionType.Automation, "blue"); addSuggestionColorMap(SuggestionType.Paint, "indigo"); addSuggestionColorMap(SuggestionType.Travel, "yellow"); addSuggestionColorMap(SuggestionType.Health, "teal"); @@ -44,12 +49,12 @@ addSuggestionColorMap(SuggestionType.Fun, "fuchsia"); addSuggestionColorMap(SuggestionType.Code, "purple"); addSuggestionColorMap(SuggestionType.Finance, "green"); addSuggestionColorMap(SuggestionType.Math, "blue"); +addSuggestionColorMap(SuggestionType.Image, "red"); +addSuggestionColorMap(SuggestionType.Document, "orange"); const DEFAULT_COLOR = "orange"; export function convertSuggestionTitleToIconClass(title: string, color: string) { - if (title === SuggestionType.Automation) - return getIconFromIconName("Robot", color, "w-6", "h-6"); if (title === SuggestionType.Paint) return getIconFromIconName("Palette", color, "w-6", "h-6"); if (title === SuggestionType.PopCulture) return getIconFromIconName("Confetti", color, "w-6", "h-6"); @@ -68,806 +73,217 @@ export function convertSuggestionTitleToIconClass(title: string, color: string) if (title === SuggestionType.Finance) return getIconFromIconName("Wallet", color, "w-6", "h-6"); if (title === SuggestionType.Math) return getIconFromIconName("MathOperations", color, "w-6", "h-6"); + if (title === SuggestionType.Image) return getIconFromIconName("Image", color, "w-6", "h-6"); else return getIconFromIconName("Lightbulb", color, "w-6", "h-6"); } -export const suggestionsData: Suggestion[] = [ - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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*", - }, +export const stepOneSuggestions: StepOneSuggestion[] = [ { type: SuggestionType.Paint, + description: "Create image", color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a picture of a sunset but it's made of stained glass tiles", - link: "", + focus: ChatInputFocus.MESSAGE, }, { - type: SuggestionType.Travel, - color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Search for the best attractions in Austria Hungary", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a futuristic cityscape with flying cars.", - link: "", - }, - { - type: SuggestionType.Travel, - color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Find the top-rated coffee shops in Seattle.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Create an abstract representation of jazz music.", - link: "", - }, - { - type: SuggestionType.Learning, + type: SuggestionType.Document, + description: "Summarize text", color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Research the history of the Eiffel Tower.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a portrait of a cat wearing a Victorian-era costume.", - link: "", + focus: ChatInputFocus.FILE, }, { type: SuggestionType.Travel, + description: "Find a place", color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Find beginner-friendly hiking trails near Los Angeles.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Create a surrealist landscape inspired by Salvador Dali.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Research the benefits and drawbacks of electric vehicles.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a scene from a fairy tale in the style of Studio Ghibli.", - link: "", - }, - { - type: SuggestionType.PopCulture, - color: "yellow", - description: "Find the best-rated science fiction books of the last decade.", - link: "", - }, - { - type: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a still life of exotic fruits in a neon color palette.", - link: "", - }, - { - type: SuggestionType.Travel, - color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Research the most eco-friendly cities in Europe.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Create a digital painting of a cyberpunk street market.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Summarize the biography of this figure: https://en.wikipedia.org/wiki/Jean_Baptiste_Point_du_Sable", - link: "", - }, - { - type: SuggestionType.Language, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Create a vibrant painting inspired by Frida Kahlo's style.", - link: "", - }, - { - type: SuggestionType.Food, - color: suggestionToColorMap[SuggestionType.Food] || DEFAULT_COLOR, - description: "Find the best empanada recipe from Colombia.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: - "Paint a colorful scene of a traditional Day of the Dead celebration in Mexico.", - link: "", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Language, + description: "Translate text", color: suggestionToColorMap[SuggestionType.Language] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Create a digital painting of the Serengeti during wildebeest migration.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Research the top M-Pesa alternatives in East Africa.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a colorful scene inspired by Maasai traditional clothing and jewelry.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a digital landscape of the Northern Lights over the Norwegian fjords.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: - "Create a digital painting in the style of traditional Chinese ink wash landscape.", - link: "", - }, - { - type: SuggestionType.PopCulture, - color: "yellow", - description: "Research the latest trends in K-pop and its global influence.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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: SuggestionType.Paint, - color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, - description: "Paint a vibrant scene of a Japanese cherry blossom festival.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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 * * *", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.PopCulture, + description: "Find a movie", color: suggestionToColorMap[SuggestionType.PopCulture] || DEFAULT_COLOR, - description: "Research the impact of Bollywood on global cinema and fashion.", - link: "", - }, - { - type: SuggestionType.Automation, - color: suggestionToColorMap[SuggestionType.Automation] || DEFAULT_COLOR, - 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", - }, - { - type: SuggestionType.Interviewing, - color: suggestionToColorMap[SuggestionType.Interviewing] || DEFAULT_COLOR, - description: "Create interview prep questions for a consulting job.", - link: "", - }, - { - type: SuggestionType.Interviewing, - color: suggestionToColorMap[SuggestionType.Interviewing] || DEFAULT_COLOR, - description: "What information should I include in a CV for a PhD application?", - link: "", - }, - { - type: SuggestionType.Home, - color: suggestionToColorMap[SuggestionType.Home] || DEFAULT_COLOR, - description: "Recommend plants that can grow well indoors.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Suggest healthy meal prep ideas for a busy work week.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "List effective time management techniques for students.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Provide tips for improving public speaking skills.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Recommend books for learning about personal finance.", - link: "", - }, - { - type: SuggestionType.Home, - color: suggestionToColorMap[SuggestionType.Home] || DEFAULT_COLOR, - description: "Suggest ways to reduce plastic waste in daily life.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Create a beginner's guide to meditation and mindfulness.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Give me some tips for improving my sleep quality.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "What are weight loss strategies supported by clinical studies?", - link: "", - }, - { - type: SuggestionType.Fun, - color: suggestionToColorMap[SuggestionType.Fun] || DEFAULT_COLOR, - description: "List creative date ideas for couples on a budget.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Interviewing] || DEFAULT_COLOR, - description: "Provide tips for writing an effective resume.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Explain the concept of recursion with a simple coding example.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Provide a coding challenge to reverse a string without using built-in functions.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Explain the difference between 'let', 'const', and 'var' in JavaScript.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Create a coding exercise to implement a basic sorting algorithm (e.g., bubble sort).", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Explain object-oriented programming principles with a simple class example.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Provide a coding challenge to find the longest palindromic substring in a given string.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Explain the concept of asynchronous programming with a JavaScript Promise example.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Create a coding exercise to implement a basic data structure (e.g., linked list or stack).", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Explain the time and space complexity of common algorithms (e.g., binary search).", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: - "Provide a coding challenge to implement a simple REST API using Node.js and Express.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Compare popular web frameworks in Rust and Python", - link: "", - }, - { - type: SuggestionType.Travel, - color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Craft an off-beat itinerary for a weekend in Lagos, Nigeria.", - link: "", - }, - { - type: SuggestionType.Language, - color: suggestionToColorMap[SuggestionType.Language] || DEFAULT_COLOR, - description: "Teach me about declensions in Latin.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Break down the concept of photosynthesis for a middle school student.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Use the Socratic method to explore the causes of World War I.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Explain the water cycle using an analogy suitable for elementary students.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Guide a high school student through solving a quadratic equation step-by-step.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Create a series of questions to help a student discover the principles of basic economics.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Develop a hands-on experiment to demonstrate the concept of density to middle schoolers.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Use guided discovery to help a student understand the structure of DNA.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Create a personalized learning plan for a student struggling with grammar concepts.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Design a series of questions to encourage critical thinking about climate change.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Develop a step-by-step guide for conducting a basic science experiment on plant growth.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Provide a detailed explanation about how to manage type 2 diabetes.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Explain the effects a stroke might have on the body.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Describe the recommended steps for preventing heart disease.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: - "Explain the differences between various types of headaches and their treatments.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Provide an overview of the most effective stress management techniques.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Explain the importance of vaccination and how vaccines work.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Describe the symptoms and treatment options for depression.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Explain the process of digestion and common digestive disorders.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: - "Provide an overview of the different types of cancer screenings and their importance.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Explain the effects of sleep deprivation on physical and mental health.", - link: "", - }, - { - type: SuggestionType.Fun, - color: suggestionToColorMap[SuggestionType.Fun] || DEFAULT_COLOR, - description: "Create a list of fun activities for a family game night.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: - "Explain the concept of compound interest and its importance in long-term savings.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: - "Provide an overview of different types of retirement accounts (e.g., 401(k), IRA, Roth IRA).", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Describe strategies for creating and sticking to a personal budget.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Explain the basics of stock market investing for beginners.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Outline the pros and cons of renting vs. buying a home.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: - "Describe different methods for paying off debt, such as the snowball and avalanche methods.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Explain the importance of an emergency fund and how to build one.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: - "Provide an overview of different types of insurance and their importance in financial planning.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Explain the concept of diversification in investment portfolios.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Describe strategies for minimizing tax liability legally.", - link: "", - }, - { - type: SuggestionType.Finance, - color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Draw a diagram illustrating the flow of money in a personal budget.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a mind map summarizing key concepts from the Industrial Revolution.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a diagram of the life cycle of a flowering plant.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a diagram illustrating the structure of the United States government.", - link: "", - }, - { - type: SuggestionType.Code, - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Draw a diagram illustrating the architecture of a computer network.", - link: "", - }, - { - type: SuggestionType.Health, - color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, - description: "Draw a diagram of the digestive system.", - link: "", - }, - { - type: SuggestionType.Travel, - color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, - description: "Create a sample flow chart of a travel itinerary for CDMX.", - link: "", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Food, + description: "Find a recipe", color: suggestionToColorMap[SuggestionType.Food] || DEFAULT_COLOR, - description: "Draw a diagram illustrating the steps in a recipe for lasagna.", - link: "", + focus: ChatInputFocus.MESSAGE, + }, + { + type: SuggestionType.Interviewing, + description: "Prepare for interview", + color: suggestionToColorMap[SuggestionType.Interviewing] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Fun, + description: "Find a game", color: suggestionToColorMap[SuggestionType.Fun] || DEFAULT_COLOR, - description: "Draw a diagram of a basketball court, labeling the different positions.", - link: "", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Code, + description: "Write code", color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - description: "Draw a diagram of a simple electrical circuit.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a diagram illustrating the notes on a musical staff.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a plot diagram of A Tale of Two Cities.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a timeline of major events in World War II.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a free-body diagram of a falling object.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw the Lewis structure of a water molecule.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a diagram of the layers of the Earth.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a diagram of the solar system.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Draw a graph of the equation y = x^2.", - link: "", - }, - { - type: SuggestionType.Math, - color: suggestionToColorMap[SuggestionType.Math] || DEFAULT_COLOR, - description: "Create a chart for the sine and cosine functions over one period.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Create a bar chart comparing the average monthly temperatures of two cities.", - link: "", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Finance, + description: "Create chart", color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, - description: "Create a line chart showing the GDP growth rate over the last decade.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Create a chart for historical rainfall in Karachi last year.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Create a chart showing the trend in smartphone usage in Korea over the past five years.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: - "Create a chart comparing the performance statistics of Lebron James and Kobe Bryant.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Create a chart showing historical casualties in major wars.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Create a chart illustrating the relationship between velocity and time.", - link: "", - }, - { - type: SuggestionType.Learning, - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - description: "Create a chart showing the frequency of earthquakes over the past century.", - link: "", + focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Math, + description: "Solve a problem", color: suggestionToColorMap[SuggestionType.Math] || DEFAULT_COLOR, - description: "Create a scatter plot of a set of random data points.", - link: "", + focus: ChatInputFocus.MESSAGE, + }, + { + type: SuggestionType.Image, + description: "Explain image", + color: suggestionToColorMap[SuggestionType.Image] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, + }, + { + type: SuggestionType.Learning, + description: "Explain concept", + color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, }, ]; + +export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { + [SuggestionType.Paint]: [ + { + prompt: "Paint a picture of a sunset but it's made of stained glass tiles.", + }, + { + prompt: "Paint a futuristic cityscape with flying cars.", + }, + { + prompt: "Paint a neon-lit street scene with reflections in the rain.", + }, + ], + [SuggestionType.Travel]: [ + { + prompt: "Search for the best attractions in Austria Hungary.", + }, + { + prompt: "Find the top-rated coffee shops in Seattle.", + }, + ], + [SuggestionType.Health]: [ + { + prompt: "Generate a weekly meal plan with recipes.", + }, + { + prompt: "Suggest healthy meal prep ideas for a busy work week.", + }, + ], + [SuggestionType.Learning]: [ + { + prompt: "Research the history of the Eiffel Tower.", + }, + { + prompt: "Summarize the biography of this figure: https://en.wikipedia.org/wiki/Jean_Baptiste_Point_du_Sable", + }, + ], + [SuggestionType.Language]: [ + { + prompt: "Translate the following text into Spanish: 'Hello, how are you?'", + }, + { + prompt: "Tell me how to greet someone in Arabic.", + }, + ], + [SuggestionType.PopCulture]: [ + { + prompt: "Find the best-rated science fiction books of the last decade.", + }, + { + prompt: "Research the latest trends in K-pop and its global influence.", + }, + ], + [SuggestionType.Food]: [ + { + prompt: "Find the best empanada recipe from Colombia.", + }, + { + prompt: "Suggest a healthy alternative to a popular fast food dish.", + }, + ], + [SuggestionType.Interviewing]: [ + { + prompt: "Create interview prep questions for a consulting job.", + }, + { + prompt: "What information should I include in a CV for a PhD application?", + }, + ], + [SuggestionType.Home]: [ + { + prompt: "Recommend plants that can grow well indoors.", + }, + { + prompt: "Suggest ways to reduce plastic waste in daily life.", + }, + ], + [SuggestionType.Fun]: [ + { + prompt: "List creative date ideas for couples on a budget.", + }, + { + prompt: "Create a list of fun activities for a family game night.", + }, + ], + [SuggestionType.Code]: [ + { + prompt: "Provide tips for writing an effective resume.", + }, + { + prompt: "Explain the concept of recursion with a simple coding example.", + }, + ], + [SuggestionType.Finance]: [ + { + prompt: "Explain the concept of compound interest and its importance in long-term savings.", + }, + { + prompt: "Provide an overview of different types of retirement accounts (e.g., 401(k), IRA, Roth IRA).", + }, + ], + [SuggestionType.Math]: [ + { + prompt: "Create a series of questions to help a student discover the principles of basic economics.", + }, + { + prompt: "Develop a hands-on experiment to demonstrate the concept of density to middle schoolers.", + }, + ], + [SuggestionType.Image]: [ + { + prompt: "Explain what is happening in this photograph", + }, + { + prompt: "Show me how I can improve this UI design", + }, + ], + [SuggestionType.Document]: [ + { + prompt: "Summarize the key concepts in this document.", + }, + { + prompt: "Provide a detailed explanation about the topic of this document.", + }, + ], +}; + +export function getStepTwoSuggestions(type: string): StepTwoSuggestion[] { + return stepTwoSuggestion[type] || []; +} diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index b22bf231..037ec032 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -8,14 +8,25 @@ import useSWR from "swr"; import { ArrowCounterClockwise } from "@phosphor-icons/react"; import { Card, CardTitle } from "@/components/ui/card"; -import SuggestionCard from "@/app/components/suggestions/suggestionCard"; +import { + StepOneSuggestionCard, + StepOneSuggestionRevertCard, + StepTwoSuggestionCard, +} from "@/app/components/suggestions/suggestionCard"; import Loading from "@/app/components/loading/loading"; import { AttachedFileText, ChatInputArea, + ChatInputFocus, ChatOptions, } from "@/app/components/chatInputArea/chatInputArea"; -import { Suggestion, suggestionsData } from "@/app/components/suggestions/suggestionsData"; +import { + StepOneSuggestion, + stepOneSuggestions, + StepTwoSuggestion, + getStepTwoSuggestions, + SuggestionType, +} from "@/app/components/suggestions/suggestionsData"; import LoginPrompt from "@/app/components/loginPrompt/loginPrompt"; import { @@ -38,6 +49,7 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s import { AppSidebar } from "./components/appSidebar/appSidebar"; import { Separator } from "@/components/ui/separator"; import { KhojLogoType } from "./components/logo/khojLogo"; +import { Button } from "@/components/ui/button"; interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; @@ -59,10 +71,19 @@ function FisherYatesShuffle(array: any[]) { function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = useState(""); + const [prefilledMessage, setPrefilledMessage] = useState(""); + const [chatInputFocus, setChatInputFocus] = useState(ChatInputFocus.MESSAGE); const [images, setImages] = useState([]); const [processingMessage, setProcessingMessage] = useState(false); const [greeting, setGreeting] = useState(""); - const [shuffledOptions, setShuffledOptions] = useState([]); + const [stepOneSuggestionOptions, setStepOneSuggestionOptions] = useState( + [], + ); + const [stepTwoSuggestionOptions, setStepTwoSuggestionOptions] = useState( + [], + ); + const [selectedStepOneSuggestion, setSelectedStepOneSuggestion] = + useState(null); const [hoveredAgent, setHoveredAgent] = useState(null); const debouncedHoveredAgent = useDebounce(hoveredAgent, 500); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -103,8 +124,8 @@ function ChatBodyData(props: ChatBodyDataProps) { }; function shuffleAndSetOptions() { - const shuffled = FisherYatesShuffle(suggestionsData); - setShuffledOptions(shuffled.slice(0, 3)); + const shuffled = FisherYatesShuffle(stepOneSuggestions); + setStepOneSuggestionOptions(shuffled.slice(0, 3)); } useEffect(() => { @@ -148,8 +169,8 @@ function ChatBodyData(props: ChatBodyDataProps) { setAgentIcons(agentIcons); }, [agentsData, props.isMobileWidth]); - function shuffleSuggestionsCards() { - shuffleAndSetOptions(); + function showAllSuggestionsCards() { + setStepOneSuggestionOptions(stepOneSuggestions); } useEffect(() => { @@ -193,27 +214,22 @@ function ChatBodyData(props: ChatBodyDataProps) { return () => scrollAreaEl?.removeEventListener("scroll", handleScroll); }, []); - function fillArea(link: string, type: string, prompt: string) { - if (!link) { - let message_str = ""; - prompt = prompt.charAt(0).toLowerCase() + prompt.slice(1); + function clickStepOneSuggestion(suggestion: StepOneSuggestion) { + let message_str = ""; + let prompt = + suggestion.description.charAt(0).toLowerCase() + suggestion.description.slice(1); - if (type === "Online Search") { - message_str = "/online " + prompt; - } else if (type === "Paint") { - message_str = "/image " + prompt; - } else { - message_str = prompt; - } - // Get the textarea element - const message_area = document.getElementById("message") as HTMLTextAreaElement; - - if (message_area) { - // Update the value directly - message_area.value = message_str; - setMessage(message_str); - } + if (suggestion.type === "Paint") { + message_str = "/image " + prompt; + } else { + message_str = prompt; } + + setPrefilledMessage(message_str); + const stepTwoSuggestions = getStepTwoSuggestions(suggestion.type); + setSelectedStepOneSuggestion(suggestion); + setStepTwoSuggestionOptions(stepTwoSuggestions); + setChatInputFocus(ChatInputFocus.FILE); } return ( @@ -306,13 +322,15 @@ function ChatBodyData(props: ChatBodyDataProps) { )}
-
+
{!props.isMobileWidth && (
setMessage(message)} sendImage={(image) => setImages((prevImages) => [...prevImages, image])} sendDisabled={processingMessage} @@ -327,43 +345,74 @@ function ChatBodyData(props: ChatBodyDataProps) {
)}
- {shuffledOptions.map((suggestion, index) => ( -
{ - if (props.isLoggedIn) { - fillArea( - suggestion.link, - suggestion.type, - suggestion.description, - ); - } else { - event.preventDefault(); - event.stopPropagation(); - setShowLoginPrompt(true); - } - }} - > - + {stepTwoSuggestionOptions.length == 0 && + stepOneSuggestionOptions.map((suggestion, index) => ( +
{ + if (props.isLoggedIn) { + clickStepOneSuggestion(suggestion); + } else { + event.preventDefault(); + event.stopPropagation(); + setShowLoginPrompt(true); + } + }} + > + +
+ ))} +
+ {stepTwoSuggestionOptions.length == 0 && + stepOneSuggestionOptions.length < stepOneSuggestions.length && ( +
+
- ))} -
-
- -
+ {stepTwoSuggestionOptions.map((suggestion, index) => ( +
{ + setMessage(suggestion.prompt); + }} + > + +
+ ))} +
+ )}
{props.isMobileWidth && ( <> diff --git a/src/interface/web/tailwind.config.ts b/src/interface/web/tailwind.config.ts index 904d0fa3..07bd0754 100644 --- a/src/interface/web/tailwind.config.ts +++ b/src/interface/web/tailwind.config.ts @@ -133,11 +133,16 @@ const config = { opacity: "0", }, }, + fadeInUp: { + "0%": { opacity: "0", transform: "translateY(20px)" }, + "100%": { opacity: "1", transform: "translateY(0)" }, + }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", "caret-blink": "caret-blink 1.25s ease-out infinite", + "fade-in-up": "fadeInUp 0.3s ease-out", }, }, }, From 62dd4c55d49b1438774b07580430f2c35fb67383 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Sat, 21 Dec 2024 20:10:54 -0800 Subject: [PATCH 02/16] Further improve UX of the suggestion cards --- src/interface/web/app/common/iconUtils.tsx | 3 + .../components/suggestions/suggestionCard.tsx | 2 +- .../components/suggestions/suggestionsData.ts | 205 +++++++++++++++--- src/interface/web/app/page.tsx | 33 +-- 4 files changed, 188 insertions(+), 55 deletions(-) diff --git a/src/interface/web/app/common/iconUtils.tsx b/src/interface/web/app/common/iconUtils.tsx index e1ebdd78..58d2f075 100644 --- a/src/interface/web/app/common/iconUtils.tsx +++ b/src/interface/web/app/common/iconUtils.tsx @@ -171,6 +171,9 @@ const iconMap: IconMap = { Image: (color: string, width: string, height: string) => ( ), + File: (color: string, width: string, height: string) => ( + + ), }; export function getIconForSlashCommand(command: string, customClassName: string | null = null) { diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index 8671dff6..5b32a061 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -61,7 +61,7 @@ export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) { } export function StepOneSuggestionRevertCard(data: StepOneSuggestionRevertCardProps) { - const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer md:p-2 animate-fade-in-up`; + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer m-2 md:p-2 animate-fade-in-up`; const descriptionClassName = `${styles.text} dark:text-white`; return ( diff --git a/src/interface/web/app/components/suggestions/suggestionsData.ts b/src/interface/web/app/components/suggestions/suggestionsData.ts index ab3e5a14..232c2c0e 100644 --- a/src/interface/web/app/components/suggestions/suggestionsData.ts +++ b/src/interface/web/app/components/suggestions/suggestionsData.ts @@ -22,7 +22,7 @@ export enum SuggestionType { export interface StepOneSuggestion { type: SuggestionType; color: string; - description: string; + actionTagline: string; focus: ChatInputFocus; } @@ -46,7 +46,7 @@ addSuggestionColorMap(SuggestionType.Food, "yellow"); addSuggestionColorMap(SuggestionType.Interviewing, "orange"); addSuggestionColorMap(SuggestionType.Home, "green"); addSuggestionColorMap(SuggestionType.Fun, "fuchsia"); -addSuggestionColorMap(SuggestionType.Code, "purple"); +addSuggestionColorMap(SuggestionType.Code, "teal"); addSuggestionColorMap(SuggestionType.Finance, "green"); addSuggestionColorMap(SuggestionType.Math, "blue"); addSuggestionColorMap(SuggestionType.Image, "red"); @@ -74,86 +74,99 @@ export function convertSuggestionTitleToIconClass(title: string, color: string) if (title === SuggestionType.Math) return getIconFromIconName("MathOperations", color, "w-6", "h-6"); if (title === SuggestionType.Image) return getIconFromIconName("Image", color, "w-6", "h-6"); + if (title === SuggestionType.Document) return getIconFromIconName("File", color, "w-6", "h-6"); else return getIconFromIconName("Lightbulb", color, "w-6", "h-6"); } export const stepOneSuggestions: StepOneSuggestion[] = [ + { + type: SuggestionType.Document, + actionTagline: "Summarize text", + color: suggestionToColorMap[SuggestionType.Document] || DEFAULT_COLOR, + focus: ChatInputFocus.FILE, + }, + { + type: SuggestionType.Code, + actionTagline: "Write code", + color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, + }, + { + type: SuggestionType.Learning, + actionTagline: "Explain concept", + color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, + }, { type: SuggestionType.Paint, - description: "Create image", + actionTagline: "Create image", color: suggestionToColorMap[SuggestionType.Paint] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, - { - type: SuggestionType.Document, - description: "Summarize text", - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, - focus: ChatInputFocus.FILE, - }, { type: SuggestionType.Travel, - description: "Find a place", + actionTagline: "Find a place", color: suggestionToColorMap[SuggestionType.Travel] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Language, - description: "Translate text", + actionTagline: "Translate text", color: suggestionToColorMap[SuggestionType.Language] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.PopCulture, - description: "Find a movie", + actionTagline: "Find a movie", color: suggestionToColorMap[SuggestionType.PopCulture] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Food, - description: "Find a recipe", + actionTagline: "Find a recipe", color: suggestionToColorMap[SuggestionType.Food] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Interviewing, - description: "Prepare for interview", + actionTagline: "Career advice", color: suggestionToColorMap[SuggestionType.Interviewing] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Fun, - description: "Find a game", + actionTagline: "Get creative", color: suggestionToColorMap[SuggestionType.Fun] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, - { - type: SuggestionType.Code, - description: "Write code", - color: suggestionToColorMap[SuggestionType.Code] || DEFAULT_COLOR, - focus: ChatInputFocus.MESSAGE, - }, { type: SuggestionType.Finance, - description: "Create chart", + actionTagline: "Explain money", color: suggestionToColorMap[SuggestionType.Finance] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Math, - description: "Solve a problem", + actionTagline: "Explain math", color: suggestionToColorMap[SuggestionType.Math] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { type: SuggestionType.Image, - description: "Explain image", + actionTagline: "Explain image", color: suggestionToColorMap[SuggestionType.Image] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, { - type: SuggestionType.Learning, - description: "Explain concept", - color: suggestionToColorMap[SuggestionType.Learning] || DEFAULT_COLOR, + type: SuggestionType.Health, + actionTagline: "Improve health", + color: suggestionToColorMap[SuggestionType.Health] || DEFAULT_COLOR, + focus: ChatInputFocus.MESSAGE, + }, + { + type: SuggestionType.Home, + actionTagline: "Improve home", + color: suggestionToColorMap[SuggestionType.Home] || DEFAULT_COLOR, focus: ChatInputFocus.MESSAGE, }, ]; @@ -169,6 +182,12 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Paint a neon-lit street scene with reflections in the rain.", }, + { + prompt: "Paint a portrait of a person with a unique hairstyle.", + }, + { + prompt: "Paint a landscape of a forest with a hidden waterfall.", + }, ], [SuggestionType.Travel]: [ { @@ -177,14 +196,32 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Find the top-rated coffee shops in Seattle.", }, + { + prompt: "Research the best hiking trails in the Swiss Alps.", + }, + { + prompt: "What is the best time of year to visit Bali?", + }, + { + prompt: "Find the best hidden gems in Nairobi.", + }, ], [SuggestionType.Health]: [ { - prompt: "Generate a weekly meal plan with recipes.", + prompt: "Explain to me how to improve my posture.", + }, + { + prompt: "Tell me how what I eat affects my insulin levels.", }, { prompt: "Suggest healthy meal prep ideas for a busy work week.", }, + { + prompt: "Recommend good exercises to improve my flexibility.", + }, + { + prompt: "Explain the benefits of a plant-based diet", + }, ], [SuggestionType.Learning]: [ { @@ -193,6 +230,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Summarize the biography of this figure: https://en.wikipedia.org/wiki/Jean_Baptiste_Point_du_Sable", }, + { + prompt: "Explain the concept of 'machine learning' in simple terms.", + }, + { + prompt: "Find the best resources to learn about the history of the Roman Empire.", + }, + { + prompt: "Explain the concept of 'quantum entanglement' in simple terms.", + }, ], [SuggestionType.Language]: [ { @@ -201,6 +247,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Tell me how to greet someone in Arabic.", }, + { + prompt: "Explain the difference between the words 'ser' and 'estar' in Spanish.", + }, + { + prompt: "Translate the following text into French: 'Where is the nearest metro station?'", + }, + { + prompt: "Translate the following text into Japanese: 'I am learning Japanese.'", + }, ], [SuggestionType.PopCulture]: [ { @@ -209,6 +264,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Research the latest trends in K-pop and its global influence.", }, + { + prompt: "Explain the plot of the movie 'Doctor Zhivago' in detail.", + }, + { + prompt: "What fashion is trending in jackets this season?", + }, + { + prompt: "Find the best indie movies of the last year", + }, ], [SuggestionType.Food]: [ { @@ -217,6 +281,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Suggest a healthy alternative to a popular fast food dish.", }, + { + prompt: "Find the best recipe for a vegan chocolate cake.", + }, + { + prompt: "Suggest a recipe for a quick and easy weeknight dinner.", + }, + { + prompt: "Create a diagram that explains how to make a traditional Italian lasagna.", + }, ], [SuggestionType.Interviewing]: [ { @@ -225,6 +298,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "What information should I include in a CV for a PhD application?", }, + { + prompt: "Suggest questions to ask during a job interview.", + }, + { + prompt: "What are the best ways to prepare for a technical interview?", + }, + { + prompt: "How can I improve my public speaking skills for an interview?", + }, ], [SuggestionType.Home]: [ { @@ -233,6 +315,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Suggest ways to reduce plastic waste in daily life.", }, + { + prompt: "Create a list of eco-friendly home cleaning products.", + }, + { + prompt: "Suggest ways to make a small apartment feel more spacious.", + }, + { + prompt: "Recommend ways to reduce energy consumption in a home.", + }, ], [SuggestionType.Fun]: [ { @@ -241,22 +332,49 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Create a list of fun activities for a family game night.", }, + { + prompt: "Give me three questions that can help me get to know someone better.", + }, + { + prompt: "Suggest fun activities for a group of friends on a rainy day.", + }, + { + prompt: "How can I improve my drawing skills?", + }, ], [SuggestionType.Code]: [ { - prompt: "Provide tips for writing an effective resume.", + prompt: "Teach me how to write a simple 'Hello World' program in Python.", + }, + { + prompt: "Write a single page application using vanilla HTML, CSS, and JavaScript that displays a list of items.", }, { prompt: "Explain the concept of recursion with a simple coding example.", }, + { + prompt: "Create a simple calculator app using React.", + }, + { + prompt: "Write a function that takes an array of numbers and returns the sum of all the numbers.", + }, ], [SuggestionType.Finance]: [ { - prompt: "Explain the concept of compound interest and its importance in long-term savings.", + prompt: "Create a chart to explain the concept of compound interest and its importance in long-term savings.", }, { prompt: "Provide an overview of different types of retirement accounts (e.g., 401(k), IRA, Roth IRA).", }, + { + prompt: "Explain the concept of diversification and its importance in investing.", + }, + { + prompt: "Create a budgeting plan for someone who wants to save money for a vacation.", + }, + { + prompt: "Explain the difference between a stock and a bond.", + }, ], [SuggestionType.Math]: [ { @@ -265,6 +383,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Develop a hands-on experiment to demonstrate the concept of density to middle schoolers.", }, + { + prompt: "Explain the concept of a derivative and its applications in real life.", + }, + { + prompt: "Create a lesson plan to teach students about the Pythagorean theorem.", + }, + { + prompt: "Explain the concept of a limit in calculus and its importance in mathematics.", + }, ], [SuggestionType.Image]: [ { @@ -273,6 +400,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Show me how I can improve this UI design", }, + { + prompt: "Explain the significance of this historical painting", + }, + { + prompt: "What emotions does this artwork evoke?", + }, + { + prompt: "What is the story behind this sculpture?", + }, ], [SuggestionType.Document]: [ { @@ -281,6 +417,15 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { { prompt: "Provide a detailed explanation about the topic of this document.", }, + { + prompt: "Create a visual representation of the information in this document.", + }, + { + prompt: "Find the main arguments in this document.", + }, + { + prompt: "Explain the relevance of this document to the topic at hand.", + }, ], }; diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index 037ec032..3c5e61fd 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -77,7 +77,7 @@ function ChatBodyData(props: ChatBodyDataProps) { const [processingMessage, setProcessingMessage] = useState(false); const [greeting, setGreeting] = useState(""); const [stepOneSuggestionOptions, setStepOneSuggestionOptions] = useState( - [], + stepOneSuggestions.slice(0, 3), ); const [stepTwoSuggestionOptions, setStepTwoSuggestionOptions] = useState( [], @@ -123,11 +123,6 @@ function ChatBodyData(props: ChatBodyDataProps) { router.push(`/agents?agent=${agentSlug}`); }; - function shuffleAndSetOptions() { - const shuffled = FisherYatesShuffle(stepOneSuggestions); - setStepOneSuggestionOptions(shuffled.slice(0, 3)); - } - useEffect(() => { if (props.isLoadingUserConfig) return; @@ -152,12 +147,6 @@ function ChatBodyData(props: ChatBodyDataProps) { setGreeting(greeting); }, [props.isLoadingUserConfig, props.userConfig]); - useEffect(() => { - if (props.chatOptionsData) { - shuffleAndSetOptions(); - } - }, [props.chatOptionsData]); - useEffect(() => { const agents = (agentsData || []).filter((agent) => agent !== null && agent !== undefined); setAgents(agents); @@ -217,19 +206,15 @@ function ChatBodyData(props: ChatBodyDataProps) { function clickStepOneSuggestion(suggestion: StepOneSuggestion) { let message_str = ""; let prompt = - suggestion.description.charAt(0).toLowerCase() + suggestion.description.slice(1); + suggestion.actionTagline.charAt(0).toLowerCase() + suggestion.actionTagline.slice(1); - if (suggestion.type === "Paint") { - message_str = "/image " + prompt; - } else { - message_str = prompt; - } + message_str = prompt; setPrefilledMessage(message_str); const stepTwoSuggestions = getStepTwoSuggestions(suggestion.type); setSelectedStepOneSuggestion(suggestion); setStepTwoSuggestionOptions(stepTwoSuggestions); - setChatInputFocus(ChatInputFocus.FILE); + setChatInputFocus(suggestion.focus); } return ( @@ -345,12 +330,12 @@ function ChatBodyData(props: ChatBodyDataProps) { )}
{stepTwoSuggestionOptions.length == 0 && stepOneSuggestionOptions.map((suggestion, index) => (
{ if (props.isLoggedIn) { clickStepOneSuggestion(suggestion); @@ -364,7 +349,7 @@ function ChatBodyData(props: ChatBodyDataProps) {
@@ -384,7 +369,7 @@ function ChatBodyData(props: ChatBodyDataProps) { {selectedStepOneSuggestion && ( { setSelectedStepOneSuggestion(null); @@ -395,7 +380,7 @@ function ChatBodyData(props: ChatBodyDataProps) { )} {stepTwoSuggestionOptions.length > 0 && (
{stepTwoSuggestionOptions.map((suggestion, index) => (
Date: Sat, 21 Dec 2024 20:46:21 -0800 Subject: [PATCH 03/16] Fix spacing of main content in the settings page --- src/interface/web/app/settings/page.tsx | 2 +- src/interface/web/app/settings/settings.module.css | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/interface/web/app/settings/page.tsx b/src/interface/web/app/settings/page.tsx index 0fe35b97..d94a3a9d 100644 --- a/src/interface/web/app/settings/page.tsx +++ b/src/interface/web/app/settings/page.tsx @@ -515,7 +515,7 @@ export default function SettingsView() { const title = "Settings"; const cardClassName = - "w-full lg:w-1/3 grid grid-flow-column border border-gray-300 shadow-md rounded-lg border dark:border-none dark:bg-muted border-opacity-50"; + "w-full lg:w-5/12 grid grid-flow-column border border-gray-300 shadow-md rounded-lg border dark:border-none dark:bg-muted border-opacity-50"; useEffect(() => { setUserConfig(initialUserConfig); diff --git a/src/interface/web/app/settings/settings.module.css b/src/interface/web/app/settings/settings.module.css index 20317590..d0c8c1b3 100644 --- a/src/interface/web/app/settings/settings.module.css +++ b/src/interface/web/app/settings/settings.module.css @@ -8,8 +8,6 @@ div.page { div.contentBody { display: grid; margin: auto; - margin-left: 20vw; - margin-top: 1rem; } div.phoneInput { From 45da6ec7506fa22ef5375e4f3f977bb1262c956d Mon Sep 17 00:00:00 2001 From: sabaimran Date: Sat, 21 Dec 2024 20:46:57 -0800 Subject: [PATCH 04/16] Separate the shorthand of each suggestion card from the prefilled text --- .../components/suggestions/suggestionCard.tsx | 11 +++++++---- .../components/suggestions/suggestionsData.ts | 18 +++++++++++++++++- src/interface/web/app/page.tsx | 8 +------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index 5b32a061..8bff1ba4 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -20,7 +20,7 @@ interface StepTwoSuggestionCardProps { } export function StepOneSuggestionCard(data: StepOneSuggestionCardProps) { - const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer md:p-2`; + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer md:p-2 animate-fade-in-up`; const descriptionClassName = `${styles.text} dark:text-white`; const cardContent = ( @@ -48,7 +48,10 @@ export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) {
- + @@ -61,14 +64,14 @@ export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) { } export function StepOneSuggestionRevertCard(data: StepOneSuggestionRevertCardProps) { - const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer m-2 md:p-2 animate-fade-in-up`; + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-[200px] cursor-pointer m-2 md:p-2 animate-fade-in-up border-none`; const descriptionClassName = `${styles.text} dark:text-white`; return (
- + {convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())} Date: Sun, 22 Dec 2024 11:54:27 -0800 Subject: [PATCH 05/16] Make suggestion cards a little more minimal --- .../app/components/chatInputArea/chatInputArea.tsx | 1 + .../app/components/suggestions/suggestionCard.tsx | 10 +++++----- src/interface/web/app/page.tsx | 14 ++++++++------ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx index 59ce835d..0f316698 100644 --- a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx +++ b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx @@ -138,6 +138,7 @@ export const ChatInputArea = forwardRef((pr useEffect(() => { if (props.prefillMessage) { setMessage(props.prefillMessage); + chatInputRef?.current?.focus(); } }, [props.prefillMessage]); diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index 8bff1ba4..b1d885d6 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -3,7 +3,7 @@ import { Card, CardContent, CardDescription } from "@/components/ui/card"; import styles from "./suggestions.module.css"; import { convertSuggestionTitleToIconClass } from "./suggestionsData"; -import { ArrowLeft, ArrowRight } from "@phosphor-icons/react"; +import { ArrowLeft, ArrowRight, MagicWand } from "@phosphor-icons/react"; interface StepOneSuggestionCardProps { title: string; @@ -47,10 +47,10 @@ export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) { return (
- - +
)} -
- {stepTwoSuggestionOptions.length == 0 && - stepOneSuggestionOptions.map((suggestion, index) => ( + {stepTwoSuggestionOptions.length == 0 && ( +
+ {stepOneSuggestionOptions.map((suggestion, index) => (
{ @@ -348,7 +348,8 @@ function ChatBodyData(props: ChatBodyDataProps) { />
))} -
+
+ )} {stepTwoSuggestionOptions.length == 0 && stepOneSuggestionOptions.length < stepOneSuggestions.length && (
@@ -369,6 +370,7 @@ function ChatBodyData(props: ChatBodyDataProps) { setSelectedStepOneSuggestion(null); setStepTwoSuggestionOptions([]); setChatInputFocus(ChatInputFocus.MESSAGE); + setPrefilledMessage(""); }} /> )} From c83709fdd1b2e387b48eda3a83f7f6bfdc33f006 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Sun, 22 Dec 2024 18:11:19 -0800 Subject: [PATCH 06/16] Further clean up in home page initial cards experience --- .../chatInputArea/chatInputArea.tsx | 7 +++---- .../components/suggestions/suggestionCard.tsx | 12 ++++++------ .../components/suggestions/suggestionsData.ts | 2 +- src/interface/web/app/page.module.css | 16 ++++++++++++---- src/interface/web/app/page.tsx | 19 +++++-------------- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx index 0f316698..9868c81c 100644 --- a/src/interface/web/app/components/chatInputArea/chatInputArea.tsx +++ b/src/interface/web/app/components/chatInputArea/chatInputArea.tsx @@ -136,10 +136,9 @@ export const ChatInputArea = forwardRef((pr }, [uploading]); useEffect(() => { - if (props.prefillMessage) { - setMessage(props.prefillMessage); - chatInputRef?.current?.focus(); - } + if (props.prefillMessage === undefined) return; + setMessage(props.prefillMessage); + chatInputRef?.current?.focus(); }, [props.prefillMessage]); useEffect(() => { diff --git a/src/interface/web/app/components/suggestions/suggestionCard.tsx b/src/interface/web/app/components/suggestions/suggestionCard.tsx index b1d885d6..354647f5 100644 --- a/src/interface/web/app/components/suggestions/suggestionCard.tsx +++ b/src/interface/web/app/components/suggestions/suggestionCard.tsx @@ -3,7 +3,7 @@ import { Card, CardContent, CardDescription } from "@/components/ui/card"; import styles from "./suggestions.module.css"; import { convertSuggestionTitleToIconClass } from "./suggestionsData"; -import { ArrowLeft, ArrowRight, MagicWand } from "@phosphor-icons/react"; +import { ArrowLeft, ArrowRight, MagicWand, XCircle } from "@phosphor-icons/react"; interface StepOneSuggestionCardProps { title: string; @@ -64,20 +64,20 @@ export function StepTwoSuggestionCard(data: StepTwoSuggestionCardProps) { } export function StepOneSuggestionRevertCard(data: StepOneSuggestionRevertCardProps) { - const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-fit cursor-pointer m-2 md:p-2 animate-fade-in-up border-none shadow-none`; + const cardClassName = `${styles.card} md:w-full md:h-fit sm:w-full h-fit md:w-fit cursor-pointer m-2 md:p-1 animate-fade-in-up border-opacity-50 shadow-none`; const descriptionClassName = `${styles.text} dark:text-white`; return ( -
- - +
+ {convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())} {data.body} +
diff --git a/src/interface/web/app/components/suggestions/suggestionsData.ts b/src/interface/web/app/components/suggestions/suggestionsData.ts index cacd57cb..ba870c24 100644 --- a/src/interface/web/app/components/suggestions/suggestionsData.ts +++ b/src/interface/web/app/components/suggestions/suggestionsData.ts @@ -440,7 +440,7 @@ export const stepTwoSuggestion: { [key: string]: StepTwoSuggestion[] } = { prompt: "Find the main arguments in this document.", }, { - prompt: "Explain the relevance of this document to the topic at hand.", + prompt: "Explain the relevance of this document to various other disciplines.", }, ], }; diff --git a/src/interface/web/app/page.module.css b/src/interface/web/app/page.module.css index faca97c7..eb1b09fe 100644 --- a/src/interface/web/app/page.module.css +++ b/src/interface/web/app/page.module.css @@ -98,16 +98,24 @@ div.homeGreetings { div.chatBox { padding: 0; - height: 100vh; + height: 100%; } div.chatLayout { gap: 0; - grid-template-columns: 1fr; - grid-template-rows: auto 1fr; + display: flex; + height: 100%; } div.homeGreetings { - grid-template-rows: auto 1fr; + display: flex; + flex-direction: column; + } + + div.inputBox { + margin-bottom: 0; + height: fit-content; + align-items: flex-end; + margin-top: auto; } } diff --git a/src/interface/web/app/page.tsx b/src/interface/web/app/page.tsx index 98c45a58..6374c162 100644 --- a/src/interface/web/app/page.tsx +++ b/src/interface/web/app/page.tsx @@ -49,7 +49,6 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s import { AppSidebar } from "./components/appSidebar/appSidebar"; import { Separator } from "@/components/ui/separator"; import { KhojLogoType } from "./components/logo/khojLogo"; -import { Button } from "@/components/ui/button"; interface ChatBodyDataProps { chatOptionsData: ChatOptions | null; @@ -61,17 +60,9 @@ interface ChatBodyDataProps { isLoadingUserConfig: boolean; } -function FisherYatesShuffle(array: any[]) { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; -} - function ChatBodyData(props: ChatBodyDataProps) { const [message, setMessage] = useState(""); - const [prefilledMessage, setPrefilledMessage] = useState(""); + const [prefillMessage, setPrefillMessage] = useState(""); const [chatInputFocus, setChatInputFocus] = useState(ChatInputFocus.MESSAGE); const [images, setImages] = useState([]); const [processingMessage, setProcessingMessage] = useState(false); @@ -156,7 +147,7 @@ function ChatBodyData(props: ChatBodyDataProps) { // generate colored icons for the available agents const agentIcons = agents.map((agent) => getIconFromIconName(agent.icon, agent.color)!); setAgentIcons(agentIcons); - }, [agentsData, props.isMobileWidth]); + }, [agentsData]); function showAllSuggestionsCards() { setStepOneSuggestionOptions(stepOneSuggestions); @@ -204,7 +195,7 @@ function ChatBodyData(props: ChatBodyDataProps) { }, []); function clickStepOneSuggestion(suggestion: StepOneSuggestion) { - setPrefilledMessage(suggestion.intent); + setPrefillMessage(suggestion.intent); const stepTwoSuggestions = getStepTwoSuggestions(suggestion.type); setSelectedStepOneSuggestion(suggestion); setStepTwoSuggestionOptions(stepTwoSuggestions); @@ -308,7 +299,7 @@ function ChatBodyData(props: ChatBodyDataProps) { > setMessage(message)} sendImage={(image) => setImages((prevImages) => [...prevImages, image])} @@ -367,10 +358,10 @@ function ChatBodyData(props: ChatBodyDataProps) { body={selectedStepOneSuggestion.actionTagline} color={selectedStepOneSuggestion.color} onClick={() => { + setPrefillMessage(""); setSelectedStepOneSuggestion(null); setStepTwoSuggestionOptions([]); setChatInputFocus(ChatInputFocus.MESSAGE); - setPrefilledMessage(""); }} /> )} From 3fd0c202ea6b21d95b4123d871304718e9eee81a Mon Sep 17 00:00:00 2001 From: sabaimran Date: Mon, 23 Dec 2024 16:43:50 -0800 Subject: [PATCH 07/16] Allow better spacing in the agent card and make the buttons sticky --- .../app/components/agentCard/agentCard.tsx | 144 +++++++++--------- 1 file changed, 75 insertions(+), 69 deletions(-) diff --git a/src/interface/web/app/components/agentCard/agentCard.tsx b/src/interface/web/app/components/agentCard/agentCard.tsx index f90be51f..db5f2074 100644 --- a/src/interface/web/app/components/agentCard/agentCard.tsx +++ b/src/interface/web/app/components/agentCard/agentCard.tsx @@ -92,6 +92,7 @@ import ShareLink from "@/app/components/shareLink/shareLink"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { ScrollArea } from "@/components/ui/scroll-area"; export interface AgentData { slug: string; @@ -967,7 +968,7 @@ export function AgentModificationForm(props: AgentModificationFormProps) { ? `${field.value.length} files selected` : "Select files"} - + @@ -1040,6 +1041,7 @@ export function AgentModificationForm(props: AgentModificationFormProps) { { const currentFiles = props.form.getValues("files") || @@ -1257,83 +1259,87 @@ export function AgentModificationForm(props: AgentModificationFormProps) { return (
- - - + + + + + {formGroups.map((group) => ( + + setCurrentStep( + formGroups.findIndex( + (g) => g.tabName === group.tabName, + ), + ) + } + > + {group.label}{" "} + {!areRequiredFieldsCompletedForCurrentStep(group) && "*"} + + ))} + {formGroups.map((group) => ( - - setCurrentStep( - formGroups.findIndex((g) => g.tabName === group.tabName), - ) - } - > - {group.label}{" "} - {!areRequiredFieldsCompletedForCurrentStep(group) && "*"} - + + {group.fields.map((field) => renderFormField(field.name))} + ))} - - {formGroups.map((group) => ( - - {group.fields.map((field) => renderFormField(field.name))} - - ))} - -
+ + + {props.errors && ( + + + + {props.errors} + + + )} + + +
+ + {currentStep < formGroups.length - 1 ? ( + ) : ( + - {currentStep < formGroups.length - 1 ? ( - - ) : ( - - )} -
- - {props.errors && ( - - - - {props.errors} - - )} - +
); } From fb111a944b10ff7258d24669b785fa6b9ea8a4a8 Mon Sep 17 00:00:00 2001 From: sabaimran Date: Mon, 23 Dec 2024 17:23:47 -0800 Subject: [PATCH 08/16] Use disable_https flag instead of is_in_debug_mode to decide whether to redirect google auth request --- src/khoj/routers/auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/khoj/routers/auth.py b/src/khoj/routers/auth.py index e5202f5b..94ac0a26 100644 --- a/src/khoj/routers/auth.py +++ b/src/khoj/routers/auth.py @@ -13,6 +13,7 @@ from starlette.requests import Request from starlette.responses import HTMLResponse, RedirectResponse, Response from starlette.status import HTTP_302_FOUND +from khoj.app.settings import DISABLE_HTTPS from khoj.database.adapters import ( acreate_khoj_token, aget_or_create_user_by_email, @@ -28,7 +29,6 @@ from khoj.routers.helpers import ( update_telemetry_state, ) from khoj.utils import state -from khoj.utils.helpers import in_debug_mode logger = logging.getLogger(__name__) @@ -216,7 +216,7 @@ async def auth(request: Request): # 1. Construct the full redirect URI including domain base_url = str(request.base_url).rstrip("/") - if not in_debug_mode(): + if not DISABLE_HTTPS: base_url = base_url.replace("http://", "https://") redirect_uri = f"{base_url}{request.app.url_path_for('auth')}" From 17f8ba732d1f200e826d9b62ee3d83483e251745 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Mon, 23 Dec 2024 17:03:32 -0800 Subject: [PATCH 09/16] Autofocus on email input box when email sign-in selected on web app --- src/interface/web/app/components/loginPrompt/loginPrompt.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/interface/web/app/components/loginPrompt/loginPrompt.tsx b/src/interface/web/app/components/loginPrompt/loginPrompt.tsx index 923a16fe..5cda444a 100644 --- a/src/interface/web/app/components/loginPrompt/loginPrompt.tsx +++ b/src/interface/web/app/components/loginPrompt/loginPrompt.tsx @@ -299,6 +299,7 @@ function EmailSignInContext({ className="p-6 w-[300px] mx-auto rounded-lg" disabled={checkEmail} value={email} + autoFocus={true} onKeyDown={(e) => { if (e.key === "Enter") { handleMagicLinkSignIn(); From 0b91383debff16243657a9bc53bd3c053d9a6831 Mon Sep 17 00:00:00 2001 From: Debanjum Date: Mon, 23 Dec 2024 17:48:57 -0800 Subject: [PATCH 10/16] Make post oauth next url redirect more robust, handle q params better --- src/khoj/routers/auth.py | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/khoj/routers/auth.py b/src/khoj/routers/auth.py index 94ac0a26..ff42e070 100644 --- a/src/khoj/routers/auth.py +++ b/src/khoj/routers/auth.py @@ -3,6 +3,7 @@ import datetime import logging import os from typing import Optional +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse import requests from fastapi import APIRouter, Depends @@ -204,23 +205,33 @@ async def auth_post(request: Request): @auth_router.get("/redirect") async def auth(request: Request): - next_url = get_next_url(request) - for q in request.query_params: - if q in ["code", "state", "scope", "authuser", "prompt", "session_state", "access_type"]: - continue - if q != "next": - next_url += f"&{q}={request.query_params[q]}" + next_url_path = get_next_url(request) - code = request.query_params.get("code") + # Add query params from request, excluding OAuth params to next URL + oauth_params = {"code", "state", "scope", "authuser", "prompt", "session_state", "access_type", "next"} + query_params = {param: value for param, value in request.query_params.items() if param not in oauth_params} - # 1. Construct the full redirect URI including domain + # Rebuild next URL with updated query params + parsed_next_url_path = urlparse(next_url_path) + next_url = urlunparse( + ( + parsed_next_url_path.scheme, + parsed_next_url_path.netloc, + parsed_next_url_path.path, + parsed_next_url_path.params, + urlencode(query_params, doseq=True), + parsed_next_url_path.fragment, + ) + ) + + # Construct the full redirect URI including domain base_url = str(request.base_url).rstrip("/") - if not DISABLE_HTTPS: base_url = base_url.replace("http://", "https://") - redirect_uri = f"{base_url}{request.app.url_path_for('auth')}" + # Build the payload for the token request + code = request.query_params.get("code") payload = { "code": code, "client_id": os.environ["GOOGLE_CLIENT_ID"], @@ -229,12 +240,14 @@ async def auth(request: Request): "grant_type": "authorization_code", } + # Request the token from Google verified_data = requests.post( "https://oauth2.googleapis.com/token", headers={"Content-Type": "application/x-www-form-urlencoded"}, data=payload, ) + # Validate the OAuth response if verified_data.status_code != 200: logger.error(f"Token request failed: {verified_data.text}") try: @@ -245,20 +258,24 @@ async def auth(request: Request): verified_data.raise_for_status() credential = verified_data.json().get("id_token") - if not credential: logger.error("Missing id_token in OAuth response") return RedirectResponse(url="/login?error=invalid_token", status_code=HTTP_302_FOUND) + # Validate the OAuth token try: idinfo = id_token.verify_oauth2_token(credential, google_requests.Request(), os.environ["GOOGLE_CLIENT_ID"]) except OAuthError as error: return HTMLResponse(f"

{error.error}

") + + # Get or create the authenticated user in the database khoj_user = await get_or_create_user(idinfo) + # Set the user session if the user is authenticated if khoj_user: request.session["user"] = dict(idinfo) + # Send a welcome email to new users if datetime.timedelta(minutes=3) > (datetime.datetime.now(datetime.timezone.utc) - khoj_user.date_joined): asyncio.create_task(send_welcome_email(idinfo["name"], idinfo["email"])) update_telemetry_state( @@ -269,6 +286,7 @@ async def auth(request: Request): ) logger.log(logging.INFO, f"🥳 New User Created: {khoj_user.uuid}") + # Redirect the user to the next URL return RedirectResponse(url=next_url, status_code=HTTP_302_FOUND) From 0d5fc70aa3ad26374d44e0b99aa4a7c6f1e0be5a Mon Sep 17 00:00:00 2001 From: Debanjum Date: Mon, 23 Dec 2024 19:46:37 -0800 Subject: [PATCH 11/16] Fix colors, title on create agent card --- src/interface/web/app/agents/page.tsx | 5 ++++- .../web/app/components/agentCard/agentCard.tsx | 10 ++++++---- src/interface/web/app/components/navMenu/navMenu.tsx | 2 +- .../web/app/components/suggestions/suggestionCard.tsx | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/interface/web/app/agents/page.tsx b/src/interface/web/app/agents/page.tsx index 89deb663..c42c3ff8 100644 --- a/src/interface/web/app/agents/page.tsx +++ b/src/interface/web/app/agents/page.tsx @@ -33,6 +33,7 @@ import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/s import { AppSidebar } from "../components/appSidebar/appSidebar"; import { Separator } from "@/components/ui/separator"; import { KhojLogoType } from "../components/logo/khojLogo"; +import { DialogTitle } from "@radix-ui/react-dialog"; export interface AgentData { slug: string; @@ -145,7 +146,9 @@ function CreateAgentCard(props: CreateAgentCardProps) { "lg:max-w-screen-lg py-4 overflow-y-scroll h-full md:h-4/6 rounded-lg flex flex-col" } > - Create Agent + + Create Agent + {!props.userProfile && showLoginPrompt && (
{getIconFromIconName(props.data.icon, props.data.color)} -

{props.data.name}

+

+ {props.data.name} +

@@ -938,7 +940,7 @@ export function AgentModificationForm(props: AgentModificationFormProps) {