mirror of https://github.com/buster-so/buster.git
added basic embed checks
This commit is contained in:
parent
ac19b16362
commit
88e3e1e6c5
|
@ -0,0 +1,9 @@
|
||||||
|
import { useMatch, useMatches } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
export const useIsEmbed = () => {
|
||||||
|
const match = useMatch({
|
||||||
|
from: '/embed',
|
||||||
|
});
|
||||||
|
console.log(match);
|
||||||
|
return !!match?.id;
|
||||||
|
};
|
|
@ -1,10 +1,21 @@
|
||||||
import { getRouteApi, type RouteContext, useParams, useSearch } from '@tanstack/react-router';
|
import type { AssetType } from '@buster/server-shared/assets';
|
||||||
|
import {
|
||||||
|
type StaticDataRouteOption,
|
||||||
|
useMatches,
|
||||||
|
useParams,
|
||||||
|
useSearch,
|
||||||
|
} from '@tanstack/react-router';
|
||||||
|
import findLast from 'lodash/findLast';
|
||||||
|
|
||||||
const assetRouteApi = getRouteApi('/app/_app/_asset');
|
export const useSelectedAssetType = (): NonNullable<StaticDataRouteOption['assetType']> => {
|
||||||
|
const matches = useMatches();
|
||||||
|
|
||||||
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
const lastMatch = findLast(matches, (match) => match.staticData?.assetType);
|
||||||
export const useSelectedAssetType = () => {
|
if (typeof lastMatch === 'number') {
|
||||||
const data = assetRouteApi.useRouteContext({ select: stableCtxSelector });
|
return 'chat';
|
||||||
|
}
|
||||||
|
// @ts-expect-error - lastMatch is not undefined
|
||||||
|
const data = lastMatch?.staticData?.assetType as StaticDataRouteOption['assetType'];
|
||||||
const { messageId } = useParams({
|
const { messageId } = useParams({
|
||||||
strict: false,
|
strict: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,21 +5,23 @@ import { ChatHeader } from './ChatHeader';
|
||||||
|
|
||||||
export const CHAT_CONTAINER_ID = 'chat-container-content';
|
export const CHAT_CONTAINER_ID = 'chat-container-content';
|
||||||
|
|
||||||
export const ChatContainer = React.memo(({ chatId }: { chatId: string | undefined }) => {
|
export const ChatContainer = React.memo(
|
||||||
return (
|
({ chatId, isEmbed }: { chatId: string | undefined; isEmbed: boolean }) => {
|
||||||
<AppPageLayout
|
return (
|
||||||
headerSizeVariant="default"
|
<AppPageLayout
|
||||||
header={<ChatHeader />}
|
headerSizeVariant="default"
|
||||||
headerBorderVariant="ghost"
|
header={<ChatHeader isEmbed={isEmbed} />}
|
||||||
headerClassName="bg-page-background"
|
headerBorderVariant="ghost"
|
||||||
mainClassName="bg-page-background"
|
headerClassName="bg-page-background"
|
||||||
scrollable
|
mainClassName="bg-page-background"
|
||||||
id={CHAT_CONTAINER_ID}
|
scrollable
|
||||||
className="flex h-full w-full min-w-[295px] flex-col"
|
id={CHAT_CONTAINER_ID}
|
||||||
>
|
className="flex h-full w-full min-w-[295px] flex-col"
|
||||||
<ChatContent chatId={chatId} />
|
>
|
||||||
</AppPageLayout>
|
<ChatContent chatId={chatId} isEmbed={isEmbed} />
|
||||||
);
|
</AppPageLayout>
|
||||||
});
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ChatContainer.displayName = 'ChatContainer';
|
ChatContainer.displayName = 'ChatContainer';
|
||||||
|
|
|
@ -12,61 +12,65 @@ import { FollowUpChatInput } from './FollowupChatInput';
|
||||||
|
|
||||||
const autoClass = 'mx-auto max-w-[600px] w-full';
|
const autoClass = 'mx-auto max-w-[600px] w-full';
|
||||||
|
|
||||||
export const ChatContent: React.FC<{ chatId: string | undefined }> = React.memo(({ chatId }) => {
|
export const ChatContent: React.FC<{ chatId: string | undefined; isEmbed: boolean }> = React.memo(
|
||||||
const chatMessageIds = useGetChatMessageIds(chatId);
|
({ chatId, isEmbed }) => {
|
||||||
const containerRef = useRef<HTMLElement>(null);
|
const chatMessageIds = useGetChatMessageIds(chatId);
|
||||||
|
const containerRef = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
const { isAutoScrollEnabled, isMountedAutoScrollObserver, scrollToBottom, enableAutoScroll } =
|
const { isAutoScrollEnabled, isMountedAutoScrollObserver, scrollToBottom, enableAutoScroll } =
|
||||||
useAutoScroll(containerRef, {
|
useAutoScroll(containerRef, {
|
||||||
observeSubTree: true,
|
observeSubTree: true,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useMount(() => {
|
||||||
|
const container = document
|
||||||
|
.getElementById(CHAT_CONTAINER_ID)
|
||||||
|
?.querySelector(`.${SCROLL_AREA_VIEWPORT_CLASS}`) as HTMLElement;
|
||||||
|
if (!container) return;
|
||||||
|
containerRef.current = container;
|
||||||
|
enableAutoScroll();
|
||||||
});
|
});
|
||||||
|
|
||||||
useMount(() => {
|
const showScrollToBottomButton = isMountedAutoScrollObserver && containerRef.current;
|
||||||
const container = document
|
|
||||||
.getElementById(CHAT_CONTAINER_ID)
|
|
||||||
?.querySelector(`.${SCROLL_AREA_VIEWPORT_CLASS}`) as HTMLElement;
|
|
||||||
if (!container) return;
|
|
||||||
containerRef.current = container;
|
|
||||||
enableAutoScroll();
|
|
||||||
});
|
|
||||||
|
|
||||||
const showScrollToBottomButton = isMountedAutoScrollObserver && containerRef.current;
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'mb-48 flex h-full w-full flex-col',
|
||||||
|
!isMountedAutoScrollObserver && 'invisible'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ClientOnly>
|
||||||
|
{chatMessageIds?.map((messageId, index) => (
|
||||||
|
<div key={messageId} className={autoClass}>
|
||||||
|
<ChatMessageBlock
|
||||||
|
key={messageId}
|
||||||
|
messageId={messageId}
|
||||||
|
chatId={chatId || ''}
|
||||||
|
messageIndex={index}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ClientOnly>
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
{!isEmbed && (
|
||||||
<>
|
<ChatInputWrapper>
|
||||||
<div
|
{showScrollToBottomButton && (
|
||||||
className={cn(
|
<ScrollToBottomButton
|
||||||
'mb-48 flex h-full w-full flex-col',
|
isAutoScrollEnabled={isAutoScrollEnabled}
|
||||||
!isMountedAutoScrollObserver && 'invisible'
|
scrollToBottom={scrollToBottom}
|
||||||
)}
|
className={'absolute -top-10'}
|
||||||
>
|
|
||||||
<ClientOnly>
|
|
||||||
{chatMessageIds?.map((messageId, index) => (
|
|
||||||
<div key={messageId} className={autoClass}>
|
|
||||||
<ChatMessageBlock
|
|
||||||
key={messageId}
|
|
||||||
messageId={messageId}
|
|
||||||
chatId={chatId || ''}
|
|
||||||
messageIndex={index}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
))}
|
</ChatInputWrapper>
|
||||||
</ClientOnly>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ChatInputWrapper>
|
|
||||||
{showScrollToBottomButton && (
|
|
||||||
<ScrollToBottomButton
|
|
||||||
isAutoScrollEnabled={isAutoScrollEnabled}
|
|
||||||
scrollToBottom={scrollToBottom}
|
|
||||||
className={'absolute -top-10'}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</ChatInputWrapper>
|
</>
|
||||||
</>
|
);
|
||||||
);
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
ChatContent.displayName = 'ChatContent';
|
ChatContent.displayName = 'ChatContent';
|
||||||
|
|
||||||
|
|
|
@ -89,18 +89,20 @@ export const ChatMessageOptions: React.FC<{
|
||||||
/>
|
/>
|
||||||
</AppTooltip>
|
</AppTooltip>
|
||||||
)}
|
)}
|
||||||
<AppTooltip title="Report message">
|
{canEditChat && (
|
||||||
<Button
|
<AppTooltip title="Report message">
|
||||||
variant="ghost"
|
<Button
|
||||||
prefix={feedback === 'negative' ? <ThumbsDownFilled /> : <ThumbsDown />}
|
variant="ghost"
|
||||||
onClick={() =>
|
prefix={feedback === 'negative' ? <ThumbsDownFilled /> : <ThumbsDown />}
|
||||||
updateChatMessageFeedback({
|
onClick={() =>
|
||||||
message_id: messageId,
|
updateChatMessageFeedback({
|
||||||
feedback: feedback === 'negative' ? null : 'negative',
|
message_id: messageId,
|
||||||
})
|
feedback: feedback === 'negative' ? null : 'negative',
|
||||||
}
|
})
|
||||||
/>
|
}
|
||||||
</AppTooltip>
|
/>
|
||||||
|
</AppTooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
{postProcessingMessage && (
|
{postProcessingMessage && (
|
||||||
<AppTooltip title="View assumptions">
|
<AppTooltip title="View assumptions">
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { ChatHeaderTitle } from '@/components/features/chat/ChatHeaderTitle';
|
||||||
import { useGetActiveChatTitle, useIsStreamingMessage } from '@/context/Chats';
|
import { useGetActiveChatTitle, useIsStreamingMessage } from '@/context/Chats';
|
||||||
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||||
|
|
||||||
export const ChatHeader: React.FC = React.memo(() => {
|
export const ChatHeader: React.FC<{ isEmbed: boolean }> = React.memo(({ isEmbed }) => {
|
||||||
const chatId = useGetChatId();
|
const chatId = useGetChatId();
|
||||||
const chatTitle = useGetActiveChatTitle();
|
const chatTitle = useGetActiveChatTitle();
|
||||||
const isStreamingMessage = useIsStreamingMessage();
|
const isStreamingMessage = useIsStreamingMessage();
|
||||||
|
@ -16,7 +16,7 @@ export const ChatHeader: React.FC = React.memo(() => {
|
||||||
chatId={chatId || ''}
|
chatId={chatId || ''}
|
||||||
isStreamingMessage={isStreamingMessage}
|
isStreamingMessage={isStreamingMessage}
|
||||||
/>
|
/>
|
||||||
<ChatHeaderOptions />
|
{!isEmbed && <ChatHeaderOptions />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
type AppSplitterRef,
|
type AppSplitterRef,
|
||||||
type LayoutSize,
|
type LayoutSize,
|
||||||
} from '@/components/ui/layouts/AppSplitter';
|
} from '@/components/ui/layouts/AppSplitter';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useGetCurrentMessageId, useIsStreamingMessage } from '@/context/Chats';
|
import { useGetCurrentMessageId, useIsStreamingMessage } from '@/context/Chats';
|
||||||
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||||
import type { LayoutMode } from '@/layouts/ChatLayout/config';
|
import type { LayoutMode } from '@/layouts/ChatLayout/config';
|
||||||
|
@ -38,6 +39,7 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({
|
||||||
const selectedAssetId = useSelectedAssetId();
|
const selectedAssetId = useSelectedAssetId();
|
||||||
const currentMessageId = useGetCurrentMessageId() || '';
|
const currentMessageId = useGetCurrentMessageId() || '';
|
||||||
const chatId = useGetChatId();
|
const chatId = useGetChatId();
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const isStreamingMessage = useIsStreamingMessage();
|
const isStreamingMessage = useIsStreamingMessage();
|
||||||
|
|
||||||
const leftPanelMinSize = selectedAssetId ? DEFAULT_CHAT_OPTION_SIDEBAR_SIZE : '0px';
|
const leftPanelMinSize = selectedAssetId ? DEFAULT_CHAT_OPTION_SIDEBAR_SIZE : '0px';
|
||||||
|
@ -57,7 +59,7 @@ export const ChatLayout: React.FC<ChatSplitterProps> = ({
|
||||||
return (
|
return (
|
||||||
<AppSplitter
|
<AppSplitter
|
||||||
ref={appSplitterRef}
|
ref={appSplitterRef}
|
||||||
leftChildren={renderLeftPanel && <ChatContainer chatId={chatId} />}
|
leftChildren={renderLeftPanel && <ChatContainer chatId={chatId} isEmbed={isEmbed} />}
|
||||||
rightChildren={renderRightPanel && children}
|
rightChildren={renderRightPanel && children}
|
||||||
autoSaveId={autoSaveId}
|
autoSaveId={autoSaveId}
|
||||||
defaultLayout={defaultLayout}
|
defaultLayout={defaultLayout}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Text } from '@/components/ui/typography';
|
||||||
import { getSupabaseSession } from '@/integrations/supabase/getSupabaseUserClient';
|
import { getSupabaseSession } from '@/integrations/supabase/getSupabaseUserClient';
|
||||||
import { signInWithAnonymousUser } from '@/integrations/supabase/signIn';
|
import { signInWithAnonymousUser } from '@/integrations/supabase/signIn';
|
||||||
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
import { AppAssetCheckLayout } from '@/layouts/AppAssetCheckLayout';
|
||||||
|
import { cn } from '@/lib/classMerge';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed')({
|
export const Route = createFileRoute('/embed')({
|
||||||
beforeLoad: async ({ context, matches }) => {
|
beforeLoad: async ({ context, matches }) => {
|
||||||
|
@ -36,18 +37,15 @@ function RouteComponent() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (assetType === 'chat') {
|
const isChat = assetType === 'chat';
|
||||||
return (
|
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
|
||||||
<Text className="text-lg">
|
|
||||||
Sharing a chat is not supported yet... But it is on our roadmap!
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="h-full w-full bg-page-background overflow-y-auto">
|
<main
|
||||||
|
className={cn(
|
||||||
|
'h-full w-full bg-page-background overflow-y-auto',
|
||||||
|
isChat && 'overflow-y-hidden bg-background-secondary'
|
||||||
|
)}
|
||||||
|
>
|
||||||
<AppAssetCheckLayout assetType={assetType}>
|
<AppAssetCheckLayout assetType={assetType}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</AppAssetCheckLayout>
|
</AppAssetCheckLayout>
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute, Outlet } from '@tanstack/react-router';
|
||||||
import * as chatLayoutServerContext from '@/context/BusterAssets/chat-server/chatLayoutServer';
|
import * as chatLayoutServerContext from '@/context/BusterAssets/chat-server/chatLayoutServer';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chat/$chatId')({
|
export const Route = createFileRoute('/embed/chat/$chatId')({
|
||||||
...chatLayoutServerContext,
|
...chatLayoutServerContext,
|
||||||
ssr: false,
|
ssr: false,
|
||||||
component: () => {
|
component: () => {
|
||||||
return <div>Hello "/embed/chat/$chatId"!</div>;
|
return (
|
||||||
|
<div className="h-full w-full p-2 max-h-[100vh] ">
|
||||||
|
<div className="h-full w-full border rounded bg-background">
|
||||||
|
{chatLayoutServerContext.component()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue