mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-05-11 20:30:07 +02:00
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:
parent
291e6c4198
commit
d2e16a599d
@ -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,
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
@ -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}
|
||||||
|
@ -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 || []} />
|
||||||
|
@ -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 />}
|
||||||
|
@ -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 {
|
||||||
|
@ -1 +1 @@
|
|||||||
export type pageType = "search" | "chat" | "assistants" | "admin";
|
export type pageType = "search" | "chat" | "assistants" | "admin" | "shared";
|
||||||
|
@ -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>
|
||||||
|
@ -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">
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user