Minor Update to UI (#1692)

New sidebar / chatbar color, hidable right-panel, and many more small tweaks.

---------

Co-authored-by: pablodanswer <“pablo@danswer.ai”>
This commit is contained in:
pablodanswer
2024-06-26 13:54:41 -07:00
committed by GitHub
parent 20c4cdbdda
commit d6e5a98a22
16 changed files with 341 additions and 220 deletions

View File

@@ -7,6 +7,7 @@ import { FiBookmark, FiCpu, FiInfo, FiX, FiZoomIn } from "react-icons/fi";
import { HoverPopup } from "@/components/HoverPopup"; import { HoverPopup } from "@/components/HoverPopup";
import { Modal } from "@/components/Modal"; import { Modal } from "@/components/Modal";
import { useState } from "react"; import { useState } from "react";
import { FaCaretDown, FaCaretRight } from "react-icons/fa";
import { Logo } from "@/components/Logo"; import { Logo } from "@/components/Logo";
const MAX_PERSONAS_TO_DISPLAY = 4; const MAX_PERSONAS_TO_DISPLAY = 4;
@@ -37,6 +38,8 @@ export function ChatIntro({
}) { }) {
const availableSourceMetadata = getSourceMetadataForSources(availableSources); const availableSourceMetadata = getSourceMetadataForSources(availableSources);
const [displaySources, setDisplaySources] = useState(false);
return ( return (
<> <>
<div className="flex justify-center items-center h-full"> <div className="flex justify-center items-center h-full">
@@ -90,12 +93,13 @@ export function ChatIntro({
</div> </div>
</div> </div>
)} )}
{availableSources.length > 0 && ( {availableSources.length > 0 && (
<div className="mt-2"> <div className="mt-1">
<p className="font-bold mb-1 mt-4 text-emphasis"> <p className="font-bold mb-1 mt-4 text-emphasis">
Connected Sources:{" "} Connected Sources:{" "}
</p> </p>
<div className="flex flex-wrap gap-2"> <div className={`flex flex-wrap gap-2`}>
{availableSourceMetadata.map((sourceMetadata) => ( {availableSourceMetadata.map((sourceMetadata) => (
<span <span
key={sourceMetadata.internalName} key={sourceMetadata.internalName}

View File

@@ -45,7 +45,6 @@ import { useDocumentSelection } from "./useDocumentSelection";
import { useFilters, useLlmOverride } from "@/lib/hooks"; import { useFilters, useLlmOverride } from "@/lib/hooks";
import { computeAvailableFilters } from "@/lib/filters"; import { computeAvailableFilters } from "@/lib/filters";
import { FeedbackType } from "./types"; import { FeedbackType } from "./types";
import ResizableSection from "@/components/resizable/ResizableSection";
import { DocumentSidebar } from "./documentSidebar/DocumentSidebar"; import { DocumentSidebar } from "./documentSidebar/DocumentSidebar";
import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader"; import { DanswerInitializingLoader } from "@/components/DanswerInitializingLoader";
import { FeedbackModal } from "./modal/FeedbackModal"; import { FeedbackModal } from "./modal/FeedbackModal";
@@ -74,6 +73,10 @@ import { v4 as uuidv4 } from "uuid";
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants"; import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
import { ChatPopup } from "./ChatPopup"; import { ChatPopup } from "./ChatPopup";
import { ChatBanner } from "./ChatBanner"; import { ChatBanner } from "./ChatBanner";
import { TbLayoutSidebarRightExpand } from "react-icons/tb";
import { SIDEBAR_WIDTH_CONST } from "@/lib/constants";
import ResizableSection from "@/components/resizable/ResizableSection";
const MAX_INPUT_HEIGHT = 200; const MAX_INPUT_HEIGHT = 200;
const TEMP_USER_MESSAGE_ID = -1; const TEMP_USER_MESSAGE_ID = -1;
@@ -256,6 +259,19 @@ export function ChatPage({
initialSessionFetch(); initialSessionFetch();
}, [existingChatSessionId]); }, [existingChatSessionId]);
const [usedSidebarWidth, setUsedSidebarWidth] = useState<number>(
documentSidebarInitialWidth || parseInt(SIDEBAR_WIDTH_CONST)
);
const updateSidebarWidth = (newWidth: number) => {
setUsedSidebarWidth(newWidth);
if (sidebarElementRef.current && innerSidebarElementRef.current) {
sidebarElementRef.current.style.transition = "";
sidebarElementRef.current.style.width = `${newWidth}px`;
innerSidebarElementRef.current.style.width = `${newWidth}px`;
}
};
const [chatSessionId, setChatSessionId] = useState<number | null>( const [chatSessionId, setChatSessionId] = useState<number | null>(
existingChatSessionId existingChatSessionId
); );
@@ -472,6 +488,7 @@ export function ChatPage({
} }
} }
}; };
useEffect(() => { useEffect(() => {
adjustDocumentSidebarWidth(); // Adjust the width on initial render adjustDocumentSidebarWidth(); // Adjust the width on initial render
window.addEventListener("resize", adjustDocumentSidebarWidth); // Add resize event listener window.addEventListener("resize", adjustDocumentSidebarWidth); // Add resize event listener
@@ -865,12 +882,26 @@ export function ChatPage({
router.push("/search"); router.push("/search");
} }
const [showDocSidebar, setShowDocSidebar] = useState(true); // State to track if sidebar is open
const toggleSidebar = () => {
if (sidebarElementRef.current) {
sidebarElementRef.current.style.transition = "width 0.3s ease-in-out";
sidebarElementRef.current.style.width = showDocSidebar
? "0px"
: `${usedSidebarWidth}px`;
}
setShowDocSidebar((showDocSidebar) => !showDocSidebar); // Toggle the state which will in turn toggle the class
};
const retrievalDisabled = !personaIncludesRetrieval(livePersona); const retrievalDisabled = !personaIncludesRetrieval(livePersona);
const sidebarElementRef = useRef<HTMLDivElement>(null);
const innerSidebarElementRef = useRef<HTMLDivElement>(null);
return ( return (
<> <>
{/* <div className="absolute top-0 z-40 w-full">
<Header user={user} />
</div> */}
<HealthCheckBanner /> <HealthCheckBanner />
<InstantSSRAutoRefresh /> <InstantSSRAutoRefresh />
@@ -886,7 +917,7 @@ export function ChatPage({
openedFolders={openedFolders} openedFolders={openedFolders}
/> />
<div className="flex w-full overflow-x-hidden" ref={masterFlexboxRef}> <div ref={masterFlexboxRef} className="flex w-full overflow-x-hidden">
{popup} {popup}
{currentFeedback && ( {currentFeedback && (
<FeedbackModal <FeedbackModal
@@ -939,7 +970,10 @@ export function ChatPage({
<div <div
className={`w-full sm:relative h-screen ${ className={`w-full sm:relative h-screen ${
retrievalDisabled ? "pb-[111px]" : "pb-[140px]" retrievalDisabled ? "pb-[111px]" : "pb-[140px]"
}`} }
flex-auto transition-margin duration-300
overflow-x-auto
`}
{...getRootProps()} {...getRootProps()}
> >
{/* <input {...getInputProps()} /> */} {/* <input {...getInputProps()} /> */}
@@ -963,7 +997,7 @@ export function ChatPage({
/> />
</div> </div>
<div className="ml-auto mr-8 flex"> <div className="ml-auto mr-6 flex">
{chatSessionId !== null && ( {chatSessionId !== null && (
<div <div
onClick={() => setSharingModalVisible(true)} onClick={() => setSharingModalVisible(true)}
@@ -979,8 +1013,16 @@ export function ChatPage({
</div> </div>
)} )}
<div className="ml-4 my-auto"> <div className="ml-4 flex my-auto">
<UserDropdown user={user} /> <UserDropdown user={user} />
{!retrievalDisabled && !showDocSidebar && (
<button
className="ml-4 mt-auto"
onClick={() => toggleSidebar()}
>
<TbLayoutSidebarRightExpand size={24} />
</button>
)}
</div> </div>
</div> </div>
</div> </div>
@@ -1047,7 +1089,6 @@ export function ChatPage({
newCompleteMessageMap newCompleteMessageMap
); );
setSelectedMessageForDocDisplay(messageId); setSelectedMessageForDocDisplay(messageId);
// set message as latest so we can edit this message // set message as latest so we can edit this message
// and so it sticks around on page reload // and so it sticks around on page reload
setMessageAsLatest(messageId); setMessageAsLatest(messageId);
@@ -1076,9 +1117,7 @@ export function ChatPage({
citedDocuments={getCitedDocumentsFromMessage( citedDocuments={getCitedDocumentsFromMessage(
message message
)} )}
toolCall={ toolCall={message?.toolCalls?.[0]}
message.toolCalls && message.toolCalls[0]
}
isComplete={ isComplete={
i !== messageHistory.length - 1 || i !== messageHistory.length - 1 ||
!isStreaming !isStreaming
@@ -1273,12 +1312,21 @@ export function ChatPage({
</div> </div>
{!retrievalDisabled ? ( {!retrievalDisabled ? (
<div
ref={sidebarElementRef}
className={`relative flex-none overflow-y-hidden sidebar bg-background-weak h-screen`}
style={{ width: showDocSidebar ? usedSidebarWidth : 0 }}
>
<ResizableSection <ResizableSection
intialWidth={documentSidebarInitialWidth as number} updateSidebarWidth={updateSidebarWidth}
minWidth={400} intialWidth={usedSidebarWidth}
minWidth={300}
maxWidth={maxDocumentSidebarWidth || undefined} maxWidth={maxDocumentSidebarWidth || undefined}
> >
<DocumentSidebar <DocumentSidebar
initialWidth={showDocSidebar ? usedSidebarWidth : 0}
ref={innerSidebarElementRef}
closeSidebar={() => toggleSidebar()}
selectedMessage={aiMessage} selectedMessage={aiMessage}
selectedDocuments={selectedDocuments} selectedDocuments={selectedDocuments}
toggleDocumentSelection={toggleDocumentSelection} toggleDocumentSelection={toggleDocumentSelection}
@@ -1288,6 +1336,7 @@ export function ChatPage({
isLoading={isFetchingChatMessages} isLoading={isFetchingChatMessages}
/> />
</ResizableSection> </ResizableSection>
</div>
) : // Another option is to use a div with the width set to the initial width, so that the ) : // Another option is to use a div with the width set to the initial width, so that the
// chat section appears in the same place as before // chat section appears in the same place as before
// <div style={documentSidebarInitialWidth ? {width: documentSidebarInitialWidth} : {}}></div> // <div style={documentSidebarInitialWidth ? {width: documentSidebarInitialWidth} : {}}></div>

View File

@@ -130,8 +130,8 @@ export function ChatPersonaSelector({
</div> </div>
} }
> >
<div className="select-none text-xl text-strong font-bold flex px-2 py-1.5 rounded cursor-pointer hover:bg-hover-light"> <div className="select-none text-xl text-strong font-bold flex px-2 rounded cursor-pointer hover:bg-hover-light">
<div className="my-auto"> <div className="mt-auto">
{currentlySelectedPersona?.name || "Default"} {currentlySelectedPersona?.name || "Default"}
</div> </div>
<FiChevronDown className="my-auto ml-1" /> <FiChevronDown className="my-auto ml-1" />

View File

@@ -7,33 +7,41 @@ import { SelectedDocumentDisplay } from "./SelectedDocumentDisplay";
import { removeDuplicateDocs } from "@/lib/documentUtils"; import { removeDuplicateDocs } from "@/lib/documentUtils";
import { BasicSelectable } from "@/components/BasicClickable"; import { BasicSelectable } from "@/components/BasicClickable";
import { Message, RetrievalType } from "../interfaces"; import { Message, RetrievalType } from "../interfaces";
import { SIDEBAR_WIDTH } from "@/lib/constants";
import { HoverPopup } from "@/components/HoverPopup"; import { HoverPopup } from "@/components/HoverPopup";
import { HEADER_PADDING } from "@/lib/constants"; import { TbLayoutSidebarLeftExpand } from "react-icons/tb";
import { ForwardedRef, forwardRef } from "react";
function SectionHeader({ function SectionHeader({
name, name,
icon, icon,
closeHeader,
}: { }: {
name: string; name: string;
icon: React.FC<{ className: string }>; icon: React.FC<{ className: string }>;
closeHeader?: () => void;
}) { }) {
return ( return (
<div className="text-lg text-emphasis font-medium flex pb-0.5 mb-3.5 mt-2 font-bold"> <div
className={`w-full mt-3 flex text-lg text-emphasis font-medium flex mb-3.5 font-bold flex items-end`}
>
<div className="flex mt-auto justify-between w-full">
<p className="flex">
{icon({ className: "my-auto mr-1" })} {icon({ className: "my-auto mr-1" })}
{name} {name}
</p>
{closeHeader && (
<button onClick={() => closeHeader()}>
<TbLayoutSidebarLeftExpand size={24} />
</button>
)}
</div>
</div> </div>
); );
} }
export function DocumentSidebar({ interface DocumentSidebarProps {
selectedMessage, closeSidebar: () => void;
selectedDocuments,
toggleDocumentSelection,
clearSelectedDocuments,
selectedDocumentTokens,
maxTokens,
isLoading,
}: {
selectedMessage: Message | null; selectedMessage: Message | null;
selectedDocuments: DanswerDocument[] | null; selectedDocuments: DanswerDocument[] | null;
toggleDocumentSelection: (document: DanswerDocument) => void; toggleDocumentSelection: (document: DanswerDocument) => void;
@@ -41,7 +49,24 @@ export function DocumentSidebar({
selectedDocumentTokens: number; selectedDocumentTokens: number;
maxTokens: number; maxTokens: number;
isLoading: boolean; isLoading: boolean;
}) { initialWidth: number;
}
export const DocumentSidebar = forwardRef<HTMLDivElement, DocumentSidebarProps>(
(
{
closeSidebar,
selectedMessage,
selectedDocuments,
toggleDocumentSelection,
clearSelectedDocuments,
selectedDocumentTokens,
maxTokens,
isLoading,
initialWidth,
},
ref: ForwardedRef<HTMLDivElement>
) => {
const { popup, setPopup } = usePopup(); const { popup, setPopup } = usePopup();
const selectedMessageRetrievalType = selectedMessage?.retrievalType || null; const selectedMessageRetrievalType = selectedMessage?.retrievalType || null;
@@ -58,22 +83,23 @@ export function DocumentSidebar({
// start of the document (since title + metadata + misc overhead) takes up // start of the document (since title + metadata + misc overhead) takes up
// space // space
const tokenLimitReached = selectedDocumentTokens > maxTokens - 75; const tokenLimitReached = selectedDocumentTokens > maxTokens - 75;
return ( return (
<div <div
className={` style={{ width: initialWidth }}
flex-initial ref={ref}
className={`sidebar absolute right-0 h-screen border-l border-l-border`}
>
<div
className="w-full flex-initial
overflow-y-hidden overflow-y-hidden
flex flex
flex-col flex-col h-screen"
w-full
h-screen
`}
id="document-sidebar"
> >
{popup} {popup}
<div className="h-4/6 flex flex-col mt-4"> <div className="h-4/6 flex flex-col">
<div className="px-3 mb-3 flex border-b border-border"> <div className="pl-3 pr-6 mb-3 flex border-b border-border">
<SectionHeader <SectionHeader
name={ name={
selectedMessageRetrievalType === RetrievalType.SelectedDocs selectedMessageRetrievalType === RetrievalType.SelectedDocs
@@ -81,6 +107,7 @@ export function DocumentSidebar({
: "Retrieved Documents" : "Retrieved Documents"
} }
icon={FiFileText} icon={FiFileText}
closeHeader={closeSidebar}
/> />
</div> </div>
@@ -108,7 +135,8 @@ export function DocumentSidebar({
handleSelect={(documentId) => { handleSelect={(documentId) => {
toggleDocumentSelection( toggleDocumentSelection(
dedupedDocuments.find( dedupedDocuments.find(
(document) => document.document_id === documentId (document) =>
document.document_id === documentId
)! )!
); );
}} }}
@@ -127,8 +155,8 @@ export function DocumentSidebar({
!isLoading && ( !isLoading && (
<div className="ml-4 mr-3"> <div className="ml-4 mr-3">
<Text> <Text>
When you run ask a question, the retrieved documents will show When you run ask a question, the retrieved documents will
up here! show up here!
</Text> </Text>
</div> </div>
) )
@@ -139,7 +167,6 @@ export function DocumentSidebar({
<div className="flex border-b border-border px-3"> <div className="flex border-b border-border px-3">
<div className="flex"> <div className="flex">
<SectionHeader name="Selected Documents" icon={FiFileText} /> <SectionHeader name="Selected Documents" icon={FiFileText} />
{tokenLimitReached && ( {tokenLimitReached && (
<div className="ml-2 my-auto"> <div className="ml-2 my-auto">
<div className="mb-2"> <div className="mb-2">
@@ -156,13 +183,15 @@ export function DocumentSidebar({
<i>{selectedDocumentTokens - maxTokens} tokens</i> <i>{selectedDocumentTokens - maxTokens} tokens</i>
<br /> <br />
<br /> <br />
{selectedDocuments && selectedDocuments.length > 0 && ( {selectedDocuments &&
selectedDocuments.length > 0 && (
<> <>
Truncating: &quot; Truncating: &quot;
<i> <i>
{ {
selectedDocuments[selectedDocuments.length - 1] selectedDocuments[
.semantic_identifier selectedDocuments.length - 1
].semantic_identifier
} }
</i> </i>
&quot; &quot;
@@ -176,10 +205,14 @@ export function DocumentSidebar({
</div> </div>
)} )}
</div> </div>
{selectedDocuments && selectedDocuments.length > 0 && ( {selectedDocuments && selectedDocuments.length > 0 && (
<div className="ml-auto my-auto" onClick={clearSelectedDocuments}> <div
<BasicSelectable selected={false}>De-Select All</BasicSelectable> className="ml-auto my-auto"
onClick={clearSelectedDocuments}
>
<BasicSelectable selected={false}>
De-Select All
</BasicSelectable>
</div> </div>
)} )}
</div> </div>
@@ -210,5 +243,9 @@ export function DocumentSidebar({
)} )}
</div> </div>
</div> </div>
</div>
); );
} }
);
DocumentSidebar.displayName = "DocumentSidebar";

View File

@@ -26,7 +26,7 @@ export function DocumentPreview({
flex flex
items-center items-center
p-2 p-2
bg-hover-light bg-hover
border border
border-border border-border
rounded-md rounded-md

View File

@@ -84,9 +84,10 @@ export function ChatInputBar({
flex flex
flex-col flex-col
border border
border-border border-border-medium
rounded-lg rounded-lg
bg-background overflow-hidden
bg-background-weak
[&:has(textarea:focus)]::ring-1 [&:has(textarea:focus)]::ring-1
[&:has(textarea:focus)]::ring-black [&:has(textarea:focus)]::ring-black
" "
@@ -118,13 +119,14 @@ export function ChatInputBar({
shrink shrink
resize-none resize-none
border-0 border-0
bg-transparent bg-background-weak
${ ${
textAreaRef.current && textAreaRef.current &&
textAreaRef.current.scrollHeight > MAX_INPUT_HEIGHT textAreaRef.current.scrollHeight > MAX_INPUT_HEIGHT
? "overflow-y-auto mt-2" ? "overflow-y-auto mt-2"
: "" : ""
} }
overflow-hidden
whitespace-normal whitespace-normal
break-word break-word
overscroll-contain overscroll-contain

View File

@@ -497,6 +497,7 @@ export const HumanMessage = ({
placeholder-gray-400 placeholder-gray-400
resize-none resize-none
pl-4 pl-4
overflow-y-auto
pr-12 pr-12
py-4`} py-4`}
aria-multiline aria-multiline
@@ -505,7 +506,6 @@ export const HumanMessage = ({
style={{ scrollbarWidth: "thin" }} style={{ scrollbarWidth: "thin" }}
onChange={(e) => { onChange={(e) => {
setEditedContent(e.target.value); setEditedContent(e.target.value);
e.target.style.height = "auto";
e.target.style.height = `${e.target.scrollHeight}px`; e.target.style.height = `${e.target.scrollHeight}px`;
}} }}
onKeyDown={(e) => { onKeyDown={(e) => {
@@ -514,6 +514,10 @@ export const HumanMessage = ({
setEditedContent(content); setEditedContent(content);
setIsEditing(false); setIsEditing(false);
} }
// Submit edit if "Command Enter" is pressed, like in ChatGPT
if (e.key === "Enter" && e.metaKey) {
handleEditSubmit();
}
}} }}
// ref={(textarea) => { // ref={(textarea) => {
// if (textarea) { // if (textarea) {

View File

@@ -198,7 +198,7 @@ export function ChatSessionDisplay({
<div className="absolute bottom-0 right-0 top-0 bg-gradient-to-l to-transparent from-hover w-20 from-60% rounded" /> <div className="absolute bottom-0 right-0 top-0 bg-gradient-to-l to-transparent from-hover w-20 from-60% rounded" />
)} )}
{!isSelected && !delayedSkipGradient && ( {!isSelected && !delayedSkipGradient && (
<div className="absolute bottom-0 right-0 top-0 bg-gradient-to-l to-transparent from-background w-8 from-0% rounded" /> <div className="absolute bottom-0 right-0 top-0 bg-gradient-to-l to-transparent from-background-weak w-8 from-0% rounded" />
)} )}
</> </>
</BasicSelectable> </BasicSelectable>

View File

@@ -7,10 +7,12 @@ import Image from "next/image";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { BasicClickable, BasicSelectable } from "@/components/BasicClickable"; import { BasicClickable, BasicSelectable } from "@/components/BasicClickable";
import { ChatSession } from "../interfaces"; import { ChatSession } from "../interfaces";
import { import {
NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA,
NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED, NEXT_PUBLIC_DO_NOT_USE_TOGGLE_OFF_DANSWER_POWERED,
NEXT_PUBLIC_NEW_CHAT_DIRECTS_TO_SAME_PERSONA,
} from "@/lib/constants"; } from "@/lib/constants";
import { ChatTab } from "./ChatTab"; import { ChatTab } from "./ChatTab";
import { Folder } from "../folders/interfaces"; import { Folder } from "../folders/interfaces";
import { createFolder } from "../folders/FolderManagement"; import { createFolder } from "../folders/FolderManagement";
@@ -56,8 +58,10 @@ export const ChatSidebar = ({
{popup} {popup}
<div <div
className={` className={`
flex-none
w-64 w-64
flex
flex-none
bg-background-weak
3xl:w-72 3xl:w-72
border-r border-r
border-border border-border

View File

@@ -20,8 +20,9 @@ export function BasicClickable({
text-sm text-sm
p-1 p-1
h-full h-full
bg-background
select-none select-none
hover:bg-hover hover:bg-hover-light
${fullWidth ? "w-full" : ""}`} ${fullWidth ? "w-full" : ""}`}
> >
{children} {children}
@@ -65,11 +66,13 @@ export function BasicSelectable({
selected, selected,
hasBorder, hasBorder,
fullWidth = false, fullWidth = false,
padding = true,
}: { }: {
children: string | JSX.Element; children: string | JSX.Element;
selected: boolean; selected: boolean;
hasBorder?: boolean; hasBorder?: boolean;
fullWidth?: boolean; fullWidth?: boolean;
padding?: boolean;
}) { }) {
return ( return (
<div <div
@@ -78,7 +81,7 @@ export function BasicSelectable({
font-medium font-medium
text-emphasis text-emphasis
text-sm text-sm
p-1 ${padding && "p-1"}
select-none select-none
${hasBorder ? "border border-border" : ""} ${hasBorder ? "border border-border" : ""}
${selected ? "bg-hover" : "hover:bg-hover"} ${selected ? "bg-hover" : "hover:bg-hover"}

View File

@@ -49,12 +49,12 @@ export function UserDropdown({
open={userInfoVisible} open={userInfoVisible}
onOpenChange={setUserInfoVisible} onOpenChange={setUserInfoVisible}
content={ content={
<BasicSelectable selected={false}> <BasicSelectable padding={false} selected={false}>
<div <div
onClick={() => setUserInfoVisible(!userInfoVisible)} onClick={() => setUserInfoVisible(!userInfoVisible)}
className="flex cursor-pointer" className="flex cursor-pointer"
> >
<div className="my-auto bg-user rounded-lg px-2 text-base font-normal"> <div className="my-auto bg-user hover:bg-user-hover rounded-lg px-2 text-base font-normal">
{user && user.email ? user.email[0].toUpperCase() : "A"} {user && user.email ? user.email[0].toUpperCase() : "A"}
</div> </div>
</div> </div>

View File

@@ -69,7 +69,7 @@ export async function Layout({ children }: { children: React.ReactNode }) {
<Header user={user} /> <Header user={user} />
</div> </div>
<div className="flex h-full pt-16"> <div className="flex h-full pt-16">
<div className="w-80 pt-12 pb-8 h-full border-r border-border overflow-auto"> <div className="w-80 bg-background-weak pt-12 pb-8 h-full border-r border-border overflow-auto">
<AdminSidebar <AdminSidebar
collections={[ collections={[
{ {

View File

@@ -23,7 +23,7 @@ export function AdminSidebar({ collections }: { collections: Collection[] }) {
</h2> </h2>
{collection.items.map((item) => ( {collection.items.map((item) => (
<Link key={item.link} href={item.link}> <Link key={item.link} href={item.link}>
<button className="text-sm block w-48 py-2 px-2 text-left hover:bg-hover-light rounded"> <button className="text-sm block w-48 py-2 px-2 text-left hover:bg-hover rounded">
<div className="">{item.name}</div> <div className="">{item.name}</div>
</button> </button>
</Link> </Link>

View File

@@ -20,11 +20,13 @@ function applyMinAndMax(
} }
export function ResizableSection({ export function ResizableSection({
updateSidebarWidth,
children, children,
intialWidth, intialWidth,
minWidth, minWidth,
maxWidth, maxWidth,
}: { }: {
updateSidebarWidth?: (newWidth: number) => void;
children: JSX.Element; children: JSX.Element;
intialWidth: number; intialWidth: number;
minWidth: number; minWidth: number;
@@ -56,6 +58,9 @@ export function ResizableSection({
const delta = mouseMoveEvent.clientX - startX; const delta = mouseMoveEvent.clientX - startX;
let newWidth = applyMinAndMax(width - delta, minWidth, maxWidth); let newWidth = applyMinAndMax(width - delta, minWidth, maxWidth);
setWidth(newWidth); setWidth(newWidth);
if (updateSidebarWidth) {
updateSidebarWidth(newWidth);
}
Cookies.set(DOCUMENT_SIDEBAR_WIDTH_COOKIE_NAME, newWidth.toString(), { Cookies.set(DOCUMENT_SIDEBAR_WIDTH_COOKIE_NAME, newWidth.toString(), {
path: "/", path: "/",
}); });

View File

@@ -1,6 +1,9 @@
export type AuthType = "disabled" | "basic" | "google_oauth" | "oidc" | "saml"; export type AuthType = "disabled" | "basic" | "google_oauth" | "oidc" | "saml";
export const HOST_URL = process.env.WEB_DOMAIN || "http://127.0.0.1:3000"; export const HOST_URL = process.env.WEB_DOMAIN || "http://127.0.0.1:3000";
export const HEADER_HEIGHT = "h-16";
export const SUB_HEADER = "h-12";
export const INTERNAL_URL = process.env.INTERNAL_URL || "http://127.0.0.1:8080"; export const INTERNAL_URL = process.env.INTERNAL_URL || "http://127.0.0.1:8080";
export const NEXT_PUBLIC_DISABLE_STREAMING = export const NEXT_PUBLIC_DISABLE_STREAMING =
process.env.NEXT_PUBLIC_DISABLE_STREAMING?.toLowerCase() === "true"; process.env.NEXT_PUBLIC_DISABLE_STREAMING?.toLowerCase() === "true";
@@ -20,7 +23,8 @@ export const GOOGLE_DRIVE_AUTH_IS_ADMIN_COOKIE_NAME =
export const SEARCH_TYPE_COOKIE_NAME = "search_type"; export const SEARCH_TYPE_COOKIE_NAME = "search_type";
export const HEADER_PADDING = `pt-[64px]`; export const SIDEBAR_WIDTH_CONST = "350px";
export const SIDEBAR_WIDTH = `w-[350px]`;
export const LOGOUT_DISABLED = export const LOGOUT_DISABLED =
process.env.NEXT_PUBLIC_DISABLE_LOGOUT?.toLowerCase() === "true"; process.env.NEXT_PUBLIC_DISABLE_LOGOUT?.toLowerCase() === "true";
@@ -31,6 +35,12 @@ export const LOGOUT_DISABLED =
// it will not be accurate (will always be false). // it will not be accurate (will always be false).
export const SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED = export const SERVER_SIDE_ONLY__PAID_ENTERPRISE_FEATURES_ENABLED =
process.env.ENABLE_PAID_ENTERPRISE_EDITION_FEATURES?.toLowerCase() === "true"; process.env.ENABLE_PAID_ENTERPRISE_EDITION_FEATURES?.toLowerCase() === "true";
// NOTE: since this is a `NEXT_PUBLIC_` variable, it will be set at
// build-time
// TODO: consider moving this to an API call so that the api_server
// can be the single source of truth
export const EE_ENABLED =
process.env.NEXT_PUBLIC_ENABLE_PAID_EE_FEATURES?.toLowerCase() === "true";
export const CUSTOM_ANALYTICS_ENABLED = process.env.CUSTOM_ANALYTICS_SECRET_KEY export const CUSTOM_ANALYTICS_ENABLED = process.env.CUSTOM_ANALYTICS_SECRET_KEY
? true ? true

View File

@@ -43,6 +43,8 @@ module.exports = {
"background-strong": "#eaecef", "background-strong": "#eaecef",
"background-search": "#ffffff", "background-search": "#ffffff",
"background-custom-header": "#f3f4f6", "background-custom-header": "#f3f4f6",
"background-inverted": "#000000",
"background-weak": "#f3f4f6", // gray-100
// text or icons // text or icons
link: "#3b82f6", // blue-500 link: "#3b82f6", // blue-500
@@ -60,6 +62,7 @@ module.exports = {
// borders // borders
border: "#e5e7eb", // gray-200 border: "#e5e7eb", // gray-200
"border-light": "#f3f4f6", // gray-100 "border-light": "#f3f4f6", // gray-100
"border-medium": "#d1d5db", // gray-300
"border-strong": "#9ca3af", // gray-400 "border-strong": "#9ca3af", // gray-400
// hover // hover
@@ -73,13 +76,6 @@ module.exports = {
text: "#fef9c3", // yellow-100 text: "#fef9c3", // yellow-100
}, },
// bubbles in chat for each "user"
user: "#fb7185", // yellow-400
ai: "#60a5fa", // blue-400
// for display documents
document: "#ec4899", // pink-500
// scrollbar // scrollbar
scrollbar: { scrollbar: {
track: "#f9fafb", track: "#f9fafb",
@@ -92,6 +88,13 @@ module.exports = {
}, },
}, },
// bubbles in chat for each "user"
user: "#fb7185", // yellow-400
ai: "#60a5fa", // blue-400
// for display documents
document: "#ec4899", // pink-500
// light mode // light mode
tremor: { tremor: {
brand: { brand: {