mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-04 05:39:06 +00:00
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.
This commit is contained in:
@@ -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<HTMLTextAreaElement, ChatInputProps>((props, ref) => {
|
||||
const [message, setMessage] = useState("");
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const fileInputButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const researchModeRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const [warning, setWarning] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -125,6 +135,22 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((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<HTMLTextAreaElement, ChatInputProps>((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}
|
||||
>
|
||||
<Paperclip className="w-8 h-8" />
|
||||
</Button>
|
||||
@@ -732,6 +759,7 @@ export const ChatInputArea = forwardRef<HTMLTextAreaElement, ChatInputProps>((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();
|
||||
|
||||
@@ -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 = (
|
||||
<Card className={cardClassName}>
|
||||
<div className="flex w-full">
|
||||
<CardContent className="m-0 p-2 w-full">
|
||||
<CardContent className="m-0 p-2 w-full flex flex-row">
|
||||
{convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())}
|
||||
<CardDescription
|
||||
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4 pt-1 break-words whitespace-pre-wrap max-w-full`}
|
||||
@@ -31,11 +38,45 @@ export default function SuggestionCard(data: SuggestionCardProps) {
|
||||
</Card>
|
||||
);
|
||||
|
||||
return data.link ? (
|
||||
<a href={data.link} className="no-underline">
|
||||
{cardContent}
|
||||
</a>
|
||||
) : (
|
||||
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 (
|
||||
<Card className={cardClassName}>
|
||||
<div className="flex w-full items-center">
|
||||
<CardContent className="m-0 p-2 w-full flex flex-row items-center">
|
||||
<ArrowRight className="w-6 h-6 text-muted-foreground inline-flex mr-1" />
|
||||
<CardDescription
|
||||
className={`sm:line-clamp-2 md:line-clamp-4 break-words whitespace-pre-wrap max-w-full text-sm text-wrap text-black dark:text-white`}
|
||||
>
|
||||
{data.prompt}
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Card className={cardClassName} onClick={data.onClick}>
|
||||
<div className="flex w-full">
|
||||
<CardContent className="m-0 p-2 w-full flex flex-row">
|
||||
<ArrowLeft className="w-4 h-4 text-muted-foreground inline-flex mr-1" />
|
||||
{convertSuggestionTitleToIconClass(data.title, data.color.toLowerCase())}
|
||||
<CardDescription
|
||||
className={`${descriptionClassName} sm:line-clamp-2 md:line-clamp-4 pt-1 break-words whitespace-pre-wrap max-w-full`}
|
||||
>
|
||||
{data.body}
|
||||
</CardDescription>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user