Improve shared chat page (#2066)

* improve look of shared chat page

* remove log

* cleaner display

* add initializing loader to shared chat page

* updated danswer loaders (for prism)

* remove default share
This commit is contained in:
pablodanswer 2024-08-07 09:13:55 -07:00 committed by GitHub
parent 291e6c4198
commit d2e16a599d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 83 additions and 87 deletions

View File

@ -129,7 +129,6 @@ def get_chat_session(
db_session: Session = Depends(get_session), db_session: Session = Depends(get_session),
) -> ChatSessionDetailResponse: ) -> ChatSessionDetailResponse:
user_id = user.id if user is not None else None user_id = user.id if user is not None else None
try: try:
chat_session = get_chat_session_by_id( chat_session = get_chat_session_by_id(
chat_session_id=session_id, chat_session_id=session_id,

View File

@ -15,8 +15,8 @@ import {
ToolCallMetadata, ToolCallMetadata,
} from "./interfaces"; } from "./interfaces";
import Prism from "prismjs";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { HistorySidebar } from "./sessionSidebar/HistorySidebar"; import { HistorySidebar } from "./sessionSidebar/HistorySidebar";
import { Persona } from "../admin/assistants/interfaces"; import { Persona } from "../admin/assistants/interfaces";
import { HealthCheckBanner } from "@/components/health/healthcheck"; import { HealthCheckBanner } from "@/components/health/healthcheck";
@ -193,6 +193,12 @@ export function ChatPage({
existingChatSessionId !== null existingChatSessionId !== null
); );
const [isReady, setIsReady] = useState(false);
useEffect(() => {
Prism.highlightAll();
setIsReady(true);
}, []);
// this is triggered every time the user switches which chat // this is triggered every time the user switches which chat
// session they are using // session they are using
useEffect(() => { useEffect(() => {
@ -1233,7 +1239,7 @@ export function ChatPage({
/> />
)} )}
{documentSidebarInitialWidth !== undefined ? ( {documentSidebarInitialWidth !== undefined && isReady ? (
<Dropzone onDrop={handleImageUpload} noClick> <Dropzone onDrop={handleImageUpload} noClick>
{({ getRootProps }) => ( {({ getRootProps }) => (
<div className="flex h-full w-full"> <div className="flex h-full w-full">
@ -1241,15 +1247,15 @@ export function ChatPage({
<div <div
style={{ transition: "width 0.30s ease-out" }} style={{ transition: "width 0.30s ease-out" }}
className={` className={`
flex-none flex-none
overflow-y-hidden overflow-y-hidden
bg-background-100 bg-background-100
transition-all transition-all
bg-opacity-80 bg-opacity-80
duration-300 duration-300
ease-in-out ease-in-out
h-full h-full
${toggledSidebar ? "w-[250px]" : "w-[0px]"} ${toggledSidebar ? "w-[250px]" : "w-[0px]"}
`} `}
></div> ></div>
)} )}

View File

@ -19,6 +19,7 @@ export function InMessageImage({ fileId }: { fileId: string }) {
{!imageLoaded && ( {!imageLoaded && (
<div className="absolute inset-0 bg-gray-200 animate-pulse rounded-lg" /> <div className="absolute inset-0 bg-gray-200 animate-pulse rounded-lg" />
)} )}
<Image <Image
width={1200} width={1200}
height={1200} height={1200}

View File

@ -1,11 +1,6 @@
"use client"; "use client";
import { import {
FiCpu,
FiImage,
FiThumbsDown,
FiThumbsUp,
FiUser,
FiEdit2, FiEdit2,
FiChevronRight, FiChevronRight,
FiChevronLeft, FiChevronLeft,
@ -37,9 +32,6 @@ import { InMessageImage } from "../files/images/InMessageImage";
import { CodeBlock } from "./CodeBlock"; import { CodeBlock } from "./CodeBlock";
import rehypePrism from "rehype-prism-plus"; import rehypePrism from "rehype-prism-plus";
// Prism stuff
import Prism from "prismjs";
import "prismjs/themes/prism-tomorrow.css"; import "prismjs/themes/prism-tomorrow.css";
import "./custom-code-styles.css"; import "./custom-code-styles.css";
import { Persona } from "@/app/admin/assistants/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces";
@ -110,6 +102,7 @@ function FileDisplay({
} }
export const AIMessage = ({ export const AIMessage = ({
shared,
isActive, isActive,
toggleDocumentSelection, toggleDocumentSelection,
alternativeAssistant, alternativeAssistant,
@ -132,6 +125,7 @@ export const AIMessage = ({
retrievalDisabled, retrievalDisabled,
currentPersona, currentPersona,
}: { }: {
shared?: boolean;
isActive?: boolean; isActive?: boolean;
selectedDocuments?: DanswerDocument[] | null; selectedDocuments?: DanswerDocument[] | null;
toggleDocumentSelection?: () => void; toggleDocumentSelection?: () => void;
@ -175,19 +169,10 @@ export const AIMessage = ({
const finalContent = processContent(content as string); const finalContent = processContent(content as string);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
Prism.highlightAll();
setIsReady(true);
}, []);
const { isHovering, trackedElementRef, hoverElementRef } = useMouseTracking(); const { isHovering, trackedElementRef, hoverElementRef } = useMouseTracking();
const settings = useContext(SettingsContext); const settings = useContext(SettingsContext);
// this is needed to give Prism a chance to load // this is needed to give Prism a chance to load
if (!isReady) {
return <div />;
}
const selectedDocumentIds = const selectedDocumentIds =
selectedDocuments?.map((document) => document.document_id) || []; selectedDocuments?.map((document) => document.document_id) || [];
@ -247,8 +232,10 @@ export const AIMessage = ({
return ( return (
<div ref={trackedElementRef} className={"py-5 px-2 lg:px-5 relative flex "}> <div ref={trackedElementRef} className={"py-5 px-2 lg:px-5 relative flex "}>
<div className="mx-auto w-[90%] max-w-message-max"> <div
<div className="mobile:ml-4 xl:ml-8"> className={`mx-auto ${shared ? "w-full" : "w-[90%]"} max-w-message-max`}
>
<div className={`${!shared && "mobile:ml-4 xl:ml-8"}`}>
<div className="flex"> <div className="flex">
<AssistantIcon <AssistantIcon
size="small" size="small"
@ -622,7 +609,9 @@ export const HumanMessage = ({
otherMessagesCanSwitchTo, otherMessagesCanSwitchTo,
onEdit, onEdit,
onMessageSelection, onMessageSelection,
shared,
}: { }: {
shared?: boolean;
content: string; content: string;
files?: FileDescriptor[]; files?: FileDescriptor[];
messageId?: number | null; messageId?: number | null;
@ -669,7 +658,9 @@ export const HumanMessage = ({
onMouseEnter={() => setIsHovered(true)} onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)} onMouseLeave={() => setIsHovered(false)}
> >
<div className="mx-auto w-[90%] max-w-searchbar-max"> <div
className={`mx-auto ${shared ? "w-full" : "w-[90%]"} max-w-searchbar-max`}
>
<div className="xl:ml-8"> <div className="xl:ml-8">
<div className="flex flex-col mr-4"> <div className="flex flex-col mr-4">
<FileDisplay alignBubble files={files || []} /> <FileDisplay alignBubble files={files || []} />

View File

@ -63,13 +63,6 @@ export function ShareChatSessionModal({
<h2 className="text-2xl text-emphasis font-bold flex my-auto"> <h2 className="text-2xl text-emphasis font-bold flex my-auto">
Share link to Chat Share link to Chat
</h2> </h2>
<div
onClick={onClose}
className="my-auto ml-auto p-2 hover:bg-hover rounded cursor-pointer"
>
<FiX size={20} />
</div>
</div> </div>
{linkGenerating && <Spinner />} {linkGenerating && <Spinner />}

View File

@ -1,14 +1,7 @@
"use client"; "use client";
import { FiArrowDown, FiEdit, FiFolderPlus } from "react-icons/fi"; import { FiEdit, FiFolderPlus } from "react-icons/fi";
import { import { ForwardedRef, forwardRef, useContext, useEffect } from "react";
Dispatch,
ForwardedRef,
forwardRef,
SetStateAction,
useContext,
useEffect,
} from "react";
import Link from "next/link"; import Link from "next/link";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { ChatSession } from "../interfaces"; import { ChatSession } from "../interfaces";
@ -26,10 +19,7 @@ import {
ClosedBookIcon, ClosedBookIcon,
} from "@/components/icons/icons"; } from "@/components/icons/icons";
import { PagesTab } from "./PagesTab"; import { PagesTab } from "./PagesTab";
import { Tooltip } from "@/components/tooltip/Tooltip";
import KeyboardSymbol from "@/lib/browserUtilities";
import { pageType } from "./types"; import { pageType } from "./types";
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
import LogoType from "@/components/header/LogoType"; import LogoType from "@/components/header/LogoType";
interface HistorySidebarProps { interface HistorySidebarProps {

View File

@ -1 +1 @@
export type pageType = "search" | "chat" | "assistants" | "admin"; export type pageType = "search" | "chat" | "assistants" | "admin" | "shared";

View File

@ -1,4 +1,5 @@
"use client"; "use client";
import Prism from "prismjs";
import { humanReadableFormat } from "@/lib/time"; import { humanReadableFormat } from "@/lib/time";
import { BackendChatSession } from "../../interfaces"; import { BackendChatSession } from "../../interfaces";
@ -11,20 +12,22 @@ import { AIMessage, HumanMessage } from "../../message/Messages";
import { Button, Callout, Divider } from "@tremor/react"; import { Button, Callout, Divider } from "@tremor/react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { Persona } from "@/app/admin/assistants/interfaces"; import { Persona } from "@/app/admin/assistants/interfaces";
import { useContext } from "react"; import { useContext, useEffect, useState } from "react";
import { SettingsContext } from "@/components/settings/SettingsProvider"; import { SettingsContext } from "@/components/settings/SettingsProvider";
import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader";
function BackToDanswerButton() { function BackToDanswerButton() {
const router = useRouter(); const router = useRouter();
const enterpriseSettings = useContext(SettingsContext)?.enterpriseSettings; const enterpriseSettings = useContext(SettingsContext)?.enterpriseSettings;
return ( return (
<div className="absolute bottom-4 w-full flex border-t border-border pt-4"> <div className="absolute bottom-0 bg-background w-full flex border-t border-border py-4">
<div className="mx-auto"> <div className="mx-auto">
<Button onClick={() => router.push("/chat")}> <Button onClick={() => router.push("/chat")}>
Back to {enterpriseSettings?.application_name || "Danswer Chat"} Back to {enterpriseSettings?.application_name || "Danswer Chat"}
</Button> </Button>
</div> </div>
pr
</div> </div>
); );
} }
@ -36,6 +39,11 @@ export function SharedChatDisplay({
chatSession: BackendChatSession | null; chatSession: BackendChatSession | null;
availableAssistants: Persona[]; availableAssistants: Persona[];
}) { }) {
const [isReady, setIsReady] = useState(false);
useEffect(() => {
Prism.highlightAll();
setIsReady(true);
}, []);
if (!chatSession) { if (!chatSession) {
return ( return (
<div className="min-h-full w-full"> <div className="min-h-full w-full">
@ -44,12 +52,10 @@ export function SharedChatDisplay({
Did not find a shared chat with the specified ID. Did not find a shared chat with the specified ID.
</Callout> </Callout>
</div> </div>
<BackToDanswerButton /> <BackToDanswerButton />
</div> </div>
); );
} }
const currentPersona = availableAssistants.find( const currentPersona = availableAssistants.find(
(persona) => persona.id === chatSession.persona_id (persona) => persona.id === chatSession.persona_id
); );
@ -59,10 +65,10 @@ export function SharedChatDisplay({
); );
return ( return (
<div className="w-full overflow-hidden"> <div className="w-full h-[100dvh] overflow-hidden">
<div className="flex max-h-full overflow-hidden pb-[72px]"> <div className="flex max-h-full overflow-hidden pb-[72px]">
<div className="flex w-full overflow-hidden overflow-y-scroll"> <div className="flex w-full overflow-hidden overflow-y-scroll">
<div className="mx-auto"> <div className="w-full h-full flex-col flex max-w-message-max mx-auto">
<div className="px-5 pt-8"> <div className="px-5 pt-8">
<h1 className="text-3xl text-strong font-bold"> <h1 className="text-3xl text-strong font-bold">
{chatSession.description || {chatSession.description ||
@ -74,32 +80,42 @@ export function SharedChatDisplay({
<Divider /> <Divider />
</div> </div>
{isReady ? (
<div className="pb-16"> <div className="w-full pb-16">
{messages.map((message) => { {messages.map((message) => {
if (message.type === "user") { if (message.type === "user") {
return ( return (
<HumanMessage <HumanMessage
key={message.messageId} shared
content={message.message} key={message.messageId}
files={message.files} content={message.message}
/> files={message.files}
); />
} else { );
return ( } else {
<AIMessage return (
currentPersona={currentPersona!} <AIMessage
key={message.messageId} shared
messageId={message.messageId} currentPersona={currentPersona!}
content={message.message} key={message.messageId}
personaName={chatSession.persona_name} messageId={message.messageId}
citedDocuments={getCitedDocumentsFromMessage(message)} content={message.message}
isComplete files={message.files || []}
/> personaName={chatSession.persona_name}
); citedDocuments={getCitedDocumentsFromMessage(message)}
} isComplete
})} />
</div> );
}
})}
</div>
) : (
<div className="grow flex-0 h-screen w-full flex items-center justify-center">
<div className="mb-[33vh]">
<DanswerInitializingLoader />
</div>
</div>
)}
</div> </div>
</div> </div>
</div> </div>

View File

@ -57,7 +57,7 @@ export default async function Page({ params }: { params: { chatId: string } }) {
return ( return (
<div> <div>
<div className="absolute top-0 z-40 w-full"> <div className="absolute top-0 z-40 w-full">
<FunctionalHeader page="chat" toggleSidebar={() => null} user={user} /> <FunctionalHeader page="shared" user={user} />
</div> </div>
<div className="flex relative bg-background text-default overflow-hidden pt-16 h-screen"> <div className="flex relative bg-background text-default overflow-hidden pt-16 h-screen">

View File

@ -18,7 +18,7 @@ export default function FunctionalHeader({
page, page,
currentChatSession, currentChatSession,
setSharingModalVisible, setSharingModalVisible,
toggleSidebar, toggleSidebar = () => null,
reset = () => null, reset = () => null,
sidebarToggled, sidebarToggled,
}: { }: {
@ -28,7 +28,7 @@ export default function FunctionalHeader({
sidebarToggled?: boolean; sidebarToggled?: boolean;
currentChatSession?: ChatSession | null | undefined; currentChatSession?: ChatSession | null | undefined;
setSharingModalVisible?: (value: SetStateAction<boolean>) => void; setSharingModalVisible?: (value: SetStateAction<boolean>) => void;
toggleSidebar: () => void; toggleSidebar?: () => void;
}) { }) {
const combinedSettings = useContext(SettingsContext); const combinedSettings = useContext(SettingsContext);
const enterpriseSettings = combinedSettings?.enterpriseSettings; const enterpriseSettings = combinedSettings?.enterpriseSettings;