mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-28 04:49:21 +02:00
Cleaner initial chat screen (#2528)
* cleaner initial chat screen * slightly cleaner animation * cleaner cards * use display name + minor updates to models * minor udpate to ui * remove logs * update based on feedback * minor nits * formatting
This commit is contained in:
@@ -41,6 +41,19 @@ personas:
|
||||
icon_color: "#6FB1FF"
|
||||
display_priority: 1
|
||||
is_visible: true
|
||||
starter_messages:
|
||||
- name: "General Information"
|
||||
description: "Ask about available information"
|
||||
message: "Hello! I'm interested in learning more about the information available here. Could you give me an overview of the types of data or documents that might be accessible?"
|
||||
- name: "Specific Topic Search"
|
||||
description: "Search for specific information"
|
||||
message: "Hi! I'd like to learn more about a specific topic. Could you help me find relevant documents and information?"
|
||||
- name: "Recent Updates"
|
||||
description: "Inquire about latest additions"
|
||||
message: "Hello! I'm curious about any recent updates or additions to the knowledge base. Can you tell me what new information has been added lately?"
|
||||
- name: "Cross-referencing Information"
|
||||
description: "Connect information from different sources"
|
||||
message: "Hi! I'm working on a project that requires connecting information from multiple sources. How can I effectively cross-reference data across different documents or categories?"
|
||||
|
||||
- id: 1
|
||||
name: "General"
|
||||
@@ -57,6 +70,19 @@ personas:
|
||||
icon_color: "#FF6F6F"
|
||||
display_priority: 0
|
||||
is_visible: true
|
||||
starter_messages:
|
||||
- name: "Open Discussion"
|
||||
description: "Start an open-ended conversation"
|
||||
message: "Hi! Can you help me write a professional email?"
|
||||
- name: "Problem Solving"
|
||||
description: "Get help with a challenge"
|
||||
message: "Hello! I need help managing my daily tasks better. Do you have any simple tips?"
|
||||
- name: "Learn Something New"
|
||||
description: "Explore a new topic"
|
||||
message: "Hi! Could you explain what project management is in simple terms?"
|
||||
- name: "Creative Brainstorming"
|
||||
description: "Generate creative ideas"
|
||||
message: "Hello! I need to brainstorm some team building activities. Do you have any fun suggestions?"
|
||||
|
||||
- id: 2
|
||||
name: "Paraphrase"
|
||||
@@ -73,7 +99,19 @@ personas:
|
||||
icon_color: "#6FFF8D"
|
||||
display_priority: 2
|
||||
is_visible: false
|
||||
|
||||
starter_messages:
|
||||
- name: "Document Search"
|
||||
description: "Find exact information"
|
||||
message: "Hi! Could you help me find information about our team structure and reporting lines from our internal documents?"
|
||||
- name: "Process Verification"
|
||||
description: "Find exact quotes"
|
||||
message: "Hello! I need to understand our project approval process. Could you find the exact steps from our documentation?"
|
||||
- name: "Technical Documentation"
|
||||
description: "Search technical details"
|
||||
message: "Hi there! I'm looking for information about our deployment procedures. Can you find the specific steps from our technical guides?"
|
||||
- name: "Policy Reference"
|
||||
description: "Check official policies"
|
||||
message: "Hello! Could you help me find our official guidelines about client communication? I need the exact wording from our documentation."
|
||||
|
||||
- id: 3
|
||||
name: "Art"
|
||||
@@ -86,8 +124,21 @@ personas:
|
||||
llm_filter_extraction: false
|
||||
recency_bias: "no_decay"
|
||||
document_sets: []
|
||||
icon_shape: 234124
|
||||
icon_shape: 234124
|
||||
icon_color: "#9B59B6"
|
||||
image_generation: true
|
||||
image_generation: true
|
||||
display_priority: 3
|
||||
is_visible: true
|
||||
starter_messages:
|
||||
- name: "Landscape"
|
||||
description: "Generate a landscape image"
|
||||
message: "Create an image of a serene mountain lake at sunset, with snow-capped peaks reflected in the calm water and a small wooden cabin on the shore."
|
||||
- name: "Character"
|
||||
description: "Generate a character image"
|
||||
message: "Generate an image of a futuristic robot with glowing blue eyes, sleek metallic body, and intricate circuitry visible through transparent panels on its chest and arms."
|
||||
- name: "Abstract"
|
||||
description: "Create an abstract image"
|
||||
message: "Create an abstract image representing the concept of time, using swirling clock hands, fragmented hourglasses, and streaks of light to convey the passage of moments and eras."
|
||||
- name: "Urban Scene"
|
||||
description: "Generate an urban landscape"
|
||||
message: "Generate an image of a bustling futuristic cityscape at night, with towering skyscrapers, flying vehicles, holographic advertisements, and a mix of neon and bioluminescent lighting."
|
||||
|
@@ -1002,6 +1002,10 @@ export function AssistantEditor({
|
||||
Starter Messages (Optional){" "}
|
||||
</div>
|
||||
</div>
|
||||
<SubLabel>
|
||||
Add pre-defined messages to help users get started. Only
|
||||
the first 4 will be displayed.
|
||||
</SubLabel>
|
||||
<FieldArray
|
||||
name="starter_messages"
|
||||
render={(
|
||||
|
@@ -1,96 +1,44 @@
|
||||
import { getSourceMetadataForSources } from "@/lib/sources";
|
||||
import { ValidSources } from "@/lib/types";
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { Divider } from "@tremor/react";
|
||||
import { FiBookmark, FiInfo } from "react-icons/fi";
|
||||
import { HoverPopup } from "@/components/HoverPopup";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import { useState } from "react";
|
||||
import { DisplayAssistantCard } from "@/components/assistants/AssistantCards";
|
||||
|
||||
export function ChatIntro({
|
||||
availableSources,
|
||||
selectedPersona,
|
||||
}: {
|
||||
availableSources: ValidSources[];
|
||||
selectedPersona: Persona;
|
||||
}) {
|
||||
const availableSourceMetadata = getSourceMetadataForSources(availableSources);
|
||||
export function ChatIntro({ selectedPersona }: { selectedPersona: Persona }) {
|
||||
const [hoveredAssistant, setHoveredAssistant] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-center items-center h-full">
|
||||
<div className="mobile:w-[90%] mobile:px-4 w-message-xs 2xl:w-message-sm 3xl:w-message">
|
||||
<div className="flex">
|
||||
<div className="mx-auto">
|
||||
<div className="m-auto text-3xl font-strong font-bold text-strong w-fit">
|
||||
{selectedPersona?.name || "How can I help you today?"}
|
||||
<div className="mobile:w-[90%] mobile:px-4 w-message-xs 2xl:w-message-sm 3xl:w-message">
|
||||
<div className="relative flex w-fit mx-auto justify-center">
|
||||
<div className="absolute z-10 -left-20 top-1/2 -translate-y-1/2">
|
||||
<div className="relative">
|
||||
<div
|
||||
onMouseEnter={() => setHoveredAssistant(true)}
|
||||
onMouseLeave={() => setHoveredAssistant(false)}
|
||||
className="p-4 scale-[.8] cursor-pointer border-dashed rounded-full flex border border-border border-2 border-dashed"
|
||||
style={{
|
||||
borderStyle: "dashed",
|
||||
borderWidth: "1.5px",
|
||||
borderSpacing: "4px",
|
||||
}}
|
||||
>
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size={"large"}
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute right-full mr-2 w-[300px] top-0">
|
||||
{hoveredAssistant && (
|
||||
<DisplayAssistantCard selectedPersona={selectedPersona} />
|
||||
)}
|
||||
</div>
|
||||
{selectedPersona && (
|
||||
<div className="mt-1">{selectedPersona.description}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedPersona && selectedPersona.num_chunks !== 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<div>
|
||||
{selectedPersona.document_sets.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<p className="font-bold mb-1 mt-4 text-emphasis">
|
||||
Knowledge Sets:{" "}
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{selectedPersona.document_sets.map((documentSet) => (
|
||||
<div key={documentSet.id} className="w-fit">
|
||||
<HoverPopup
|
||||
mainContent={
|
||||
<span className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default">
|
||||
<div className="mr-1 my-auto">
|
||||
<FiBookmark />
|
||||
</div>
|
||||
{documentSet.name}
|
||||
</span>
|
||||
}
|
||||
popupContent={
|
||||
<div className="flex py-1 w-96">
|
||||
<FiInfo className="my-auto mr-2" />
|
||||
<div className="text-sm">
|
||||
{documentSet.description}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
direction="top"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{availableSources.length > 0 && (
|
||||
<div className="mt-1">
|
||||
<p className="font-bold mb-1 mt-4 text-emphasis">
|
||||
Connected Sources:{" "}
|
||||
</p>
|
||||
<div className={`flex flex-wrap gap-2`}>
|
||||
{availableSourceMetadata.map((sourceMetadata) => (
|
||||
<span
|
||||
key={sourceMetadata.internalName}
|
||||
className="flex w-fit p-1 rounded border border-border text-xs font-medium cursor-default"
|
||||
>
|
||||
<div className="mr-1 my-auto">
|
||||
{sourceMetadata.icon({})}
|
||||
</div>
|
||||
<div className="my-auto">
|
||||
{sourceMetadata.displayName}
|
||||
</div>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="text-3xl line-clamp-2 text-text-800 font-base font-semibold text-strong">
|
||||
{selectedPersona?.name || "How can I help you today?"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
@@ -103,6 +103,7 @@ import { ApiKeyModal } from "@/components/llm/ApiKeyModal";
|
||||
import BlurBackground from "./shared_chat_search/BlurBackground";
|
||||
import { NoAssistantModal } from "@/components/modals/NoAssistantModal";
|
||||
import { useAssistants } from "@/components/context/AssistantsContext";
|
||||
import { Divider } from "@tremor/react";
|
||||
|
||||
const TEMP_USER_MESSAGE_ID = -1;
|
||||
const TEMP_ASSISTANT_MESSAGE_ID = -2;
|
||||
@@ -741,12 +742,6 @@ export function ChatPage({
|
||||
}, [liveAssistant]);
|
||||
|
||||
const filterManager = useFilters();
|
||||
const [finalAvailableSources, finalAvailableDocumentSets] =
|
||||
computeAvailableFilters({
|
||||
selectedPersona: selectedAssistant,
|
||||
availableSources,
|
||||
availableDocumentSets,
|
||||
});
|
||||
|
||||
const [currentFeedback, setCurrentFeedback] = useState<
|
||||
[FeedbackType, number] | null
|
||||
@@ -2002,7 +1997,7 @@ export function ChatPage({
|
||||
{...getRootProps()}
|
||||
>
|
||||
<div
|
||||
className={`w-full h-full flex flex-col overflow-y-auto include-scrollbar overflow-x-hidden relative`}
|
||||
className={`w-full h-full flex flex-col overflow-y-auto include-scrollbar overflow-x-hidden relative`}
|
||||
ref={scrollableDivRef}
|
||||
>
|
||||
{/* ChatBanner is a custom banner that displays a admin-specified message at
|
||||
@@ -2012,11 +2007,51 @@ export function ChatPage({
|
||||
!isFetchingChatMessages &&
|
||||
currentSessionChatState == "input" &&
|
||||
!loadingError && (
|
||||
<ChatIntro
|
||||
availableSources={finalAvailableSources}
|
||||
selectedPersona={liveAssistant}
|
||||
/>
|
||||
<div className="h-full flex flex-col justify-center items-center">
|
||||
<ChatIntro selectedPersona={liveAssistant} />
|
||||
|
||||
<div
|
||||
key={-4}
|
||||
className={`
|
||||
mx-auto
|
||||
px-4
|
||||
w-full
|
||||
max-w-[750px]
|
||||
flex
|
||||
flex-wrap
|
||||
justify-center
|
||||
mt-2
|
||||
h-40
|
||||
items-start
|
||||
mb-6`}
|
||||
>
|
||||
{currentPersona?.starter_messages &&
|
||||
currentPersona.starter_messages.length >
|
||||
0 && (
|
||||
<>
|
||||
<Divider className="mx-2" />
|
||||
|
||||
{currentPersona.starter_messages
|
||||
.slice(0, 4)
|
||||
.map((starterMessage, i) => (
|
||||
<div key={i} className="w-1/2">
|
||||
<StarterMessage
|
||||
starterMessage={starterMessage}
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
messageOverride:
|
||||
starterMessage.message,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={
|
||||
"-ml-4 w-full mx-auto " +
|
||||
@@ -2383,45 +2418,6 @@ export function ChatPage({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{currentPersona &&
|
||||
currentPersona.starter_messages &&
|
||||
currentPersona.starter_messages.length > 0 &&
|
||||
selectedAssistant &&
|
||||
messageHistory.length === 0 &&
|
||||
!isFetchingChatMessages && (
|
||||
<div
|
||||
key={-4}
|
||||
className={`
|
||||
mx-auto
|
||||
px-4
|
||||
w-searchbar-xs
|
||||
2xl:w-searchbar-sm
|
||||
3xl:w-searchbar
|
||||
grid
|
||||
gap-4
|
||||
grid-cols-1
|
||||
grid-rows-1
|
||||
mt-4
|
||||
md:grid-cols-2
|
||||
mb-6`}
|
||||
>
|
||||
{currentPersona.starter_messages.map(
|
||||
(starterMessage, i) => (
|
||||
<div key={i} className="w-full">
|
||||
<StarterMessage
|
||||
starterMessage={starterMessage}
|
||||
onClick={() =>
|
||||
onSubmit({
|
||||
messageOverride:
|
||||
starterMessage.message,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Some padding at the bottom so the search bar has space at the bottom to not cover the last message*/}
|
||||
<div ref={endPaddingRef} className="h-[95px]" />
|
||||
|
@@ -1,21 +1,29 @@
|
||||
import { StarterMessage } from "../admin/assistants/interfaces";
|
||||
import { StarterMessage as StarterMessageType } from "../admin/assistants/interfaces";
|
||||
|
||||
export function StarterMessage({
|
||||
starterMessage,
|
||||
onClick,
|
||||
}: {
|
||||
starterMessage: StarterMessage;
|
||||
starterMessage: StarterMessageType;
|
||||
onClick: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
"py-2 px-3 rounded border border-border bg-white cursor-pointer hover:bg-hover-light h-full"
|
||||
}
|
||||
className="mb-4 mx-2 group relative overflow-hidden rounded-xl border border-border bg-gradient-to-br from-white to-background p-4 shadow-sm transition-all duration-300 hover:shadow-md hover:scale-[1.005] cursor-pointer"
|
||||
onClick={onClick}
|
||||
>
|
||||
<p className="font-medium text-emphasis">{starterMessage.name}</p>
|
||||
<p className="text-subtle text-sm">{starterMessage.description}</p>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-100 to-purple-100 opacity-0 group-hover:opacity-20 transition-opacity duration-300" />
|
||||
<h3
|
||||
className="text-base flex items-center font-medium text-text-800 group-hover:text-text-900 transition-colors duration-300
|
||||
line-clamp-2 gap-x-2 overflow-hidden"
|
||||
>
|
||||
{starterMessage.name}
|
||||
</h3>
|
||||
<div className={`overflow-hidden transition-all duration-300 max-h-20}`}>
|
||||
<p className="text-sm text-text-600 mt-2">
|
||||
{starterMessage.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -115,3 +115,63 @@ export function DraggableAssistantCard(props: {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DisplayAssistantCard({
|
||||
selectedPersona,
|
||||
}: {
|
||||
selectedPersona: Persona;
|
||||
}) {
|
||||
return (
|
||||
<div className="p-4 bg-white/90 backdrop-blur-sm rounded-lg shadow-md border border-border/50 max-w-md w-full mx-auto transition-all duration-300 ease-in-out hover:shadow-lg">
|
||||
<div className="flex items-center mb-3">
|
||||
<AssistantIcon
|
||||
disableToolip
|
||||
size="medium"
|
||||
assistant={selectedPersona}
|
||||
/>
|
||||
<h2 className="ml-3 text-xl font-semibold text-text-900">
|
||||
{selectedPersona.name}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-sm text-text-600 mb-3 leading-relaxed">
|
||||
{selectedPersona.description}
|
||||
</p>
|
||||
{selectedPersona.tools.length > 0 ||
|
||||
selectedPersona.llm_relevance_filter ||
|
||||
selectedPersona.llm_filter_extraction ? (
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-medium text-text-900">Capabilities:</h3>
|
||||
<ul className="space-y-.5">
|
||||
{/* display all tools */}
|
||||
{selectedPersona.tools.map((tool, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className="flex items-center text-sm text-text-700"
|
||||
>
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
{tool.display_name}
|
||||
</li>
|
||||
))}
|
||||
{/* Built in capabilities */}
|
||||
{selectedPersona.llm_relevance_filter && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span>{" "}
|
||||
Advanced Relevance Filtering
|
||||
</li>
|
||||
)}
|
||||
{selectedPersona.llm_filter_extraction && (
|
||||
<li className="flex items-center text-sm text-text-700">
|
||||
<span className="mr-2 text-text-500 opacity-70">•</span> Smart
|
||||
Information Extraction
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-text-600 italic">
|
||||
No specific capabilities listed for this assistant.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -23,22 +23,38 @@ export function AssistantIcon({
|
||||
assistant,
|
||||
size,
|
||||
border,
|
||||
disableToolip,
|
||||
}: {
|
||||
assistant: Persona;
|
||||
size?: "small" | "medium" | "large";
|
||||
size?: "small" | "medium" | "large" | "header";
|
||||
border?: boolean;
|
||||
disableToolip?: boolean;
|
||||
}) {
|
||||
const color = darkerGenerateColorFromId(assistant.id.toString());
|
||||
|
||||
return (
|
||||
<CustomTooltip showTick line wrap content={assistant.description}>
|
||||
<CustomTooltip
|
||||
disabled={disableToolip}
|
||||
showTick
|
||||
line
|
||||
wrap
|
||||
content={assistant.description}
|
||||
>
|
||||
{
|
||||
// Prioritization order: image, graph, defaults
|
||||
assistant.uploaded_image_id ? (
|
||||
<img
|
||||
alt={assistant.name}
|
||||
className={`object-cover object-center rounded-sm overflow-hidden transition-opacity duration-300 opacity-100
|
||||
${size === "large" ? "w-8 h-8" : "w-6 h-6"}`}
|
||||
${
|
||||
size === "large"
|
||||
? "w-10 h-10"
|
||||
: size === "header"
|
||||
? "w-14 h-14"
|
||||
: size === "medium"
|
||||
? "w-8 h-8"
|
||||
: "w-6 h-6"
|
||||
}`}
|
||||
src={buildImgUrl(assistant.uploaded_image_id)}
|
||||
loading="lazy"
|
||||
/>
|
||||
@@ -46,20 +62,36 @@ export function AssistantIcon({
|
||||
<div
|
||||
className={`flex-none
|
||||
${border && "ring ring-[1px] ring-border-strong "}
|
||||
${size === "large" ? "w-10 h-10" : "w-6 h-6"} `}
|
||||
${
|
||||
size === "large"
|
||||
? "w-10 h-10"
|
||||
: size === "header"
|
||||
? "w-14 h-14"
|
||||
: size === "medium"
|
||||
? "w-8 h-8"
|
||||
: "w-6 h-6"
|
||||
} `}
|
||||
>
|
||||
{createSVG(
|
||||
{ encodedGrid: assistant.icon_shape, filledSquares: 0 },
|
||||
assistant.icon_color,
|
||||
size == "large" ? 36 : 24
|
||||
size === "large"
|
||||
? 40
|
||||
: size === "header"
|
||||
? 56
|
||||
: size === "medium"
|
||||
? 32
|
||||
: 24
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={`flex-none rounded-sm overflow-hidden
|
||||
${border && "border border-.5 border-border-strong "}
|
||||
${size === "large" && "w-12 h-12"}
|
||||
${(!size || size === "small") && "w-6 h-6"} `}
|
||||
${size === "large" ? "w-10 h-10" : ""}
|
||||
${size === "header" ? "w-14 h-14" : ""}
|
||||
${size === "medium" ? "w-8 h-8" : ""}
|
||||
${!size || size === "small" ? "w-6 h-6" : ""} `}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
)
|
||||
|
@@ -124,9 +124,9 @@ export const CustomTooltip = ({
|
||||
!disabled &&
|
||||
createPortal(
|
||||
<div
|
||||
className={`min-w-8 fixed z-[1000] ${citation ? "max-w-[350px]" : "w-40"} ${
|
||||
large ? (medium ? "w-88" : "w-96") : line && "max-w-64 w-auto"
|
||||
}
|
||||
className={`min-w-8 fixed z-[1000] ${
|
||||
citation ? "max-w-[350px]" : "w-40"
|
||||
} ${large ? (medium ? "w-88" : "w-96") : line && "max-w-64 w-auto"}
|
||||
transform -translate-x-1/2 text-sm
|
||||
${
|
||||
light
|
||||
|
Reference in New Issue
Block a user