mirror of https://github.com/buster-so/buster.git
Merge pull request #1237 from buster-so/big-nate-bus-1988-view-only-chat-ui
Big nate bus 1988 view only chat UI
This commit is contained in:
commit
a33477a093
|
@ -158,13 +158,19 @@ const useCheckIfWeHaveAFollowupDashboard = (messageId: string) => {
|
||||||
return useMemoizedFn(method);
|
return useMemoizedFn(method);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTrackAndUpdateNewMessages = ({ chatId }: { chatId: string | undefined }) => {
|
export const useTrackAndUpdateNewMessages = ({
|
||||||
|
chatId,
|
||||||
|
isEmbed,
|
||||||
|
}: {
|
||||||
|
chatId: string | undefined;
|
||||||
|
isEmbed: boolean;
|
||||||
|
}) => {
|
||||||
const { onUpdateChat } = useChatUpdate();
|
const { onUpdateChat } = useChatUpdate();
|
||||||
const getChatMemoized = useGetChatMemoized();
|
const getChatMemoized = useGetChatMemoized();
|
||||||
const getChatMessageMemoized = useGetChatMessageMemoized();
|
const getChatMessageMemoized = useGetChatMessageMemoized();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const subscribe = !!chatId;
|
const subscribe = !!chatId && !isEmbed;
|
||||||
|
|
||||||
const shape = useMemo(() => {
|
const shape = useMemo(() => {
|
||||||
return messagesShape({ chatId: chatId || '', columns: ['id'] });
|
return messagesShape({ chatId: chatId || '', columns: ['id'] });
|
||||||
|
|
|
@ -3,11 +3,11 @@ import { SelectableButton } from '@/components/ui/buttons/SelectableButton';
|
||||||
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||||
import { Xmark } from '../../ui/icons';
|
import { Xmark } from '../../ui/icons';
|
||||||
|
|
||||||
export const ClosePageButton = () => {
|
export const ClosePageButton = ({ isEmbed }: { isEmbed: boolean }) => {
|
||||||
const chatId = useGetChatId() || '';
|
const chatId = useGetChatId() || '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link to="/app/chats/$chatId" params={{ chatId }}>
|
<Link to={isEmbed ? '/embed/chat/$chatId' : '/app/chats/$chatId'} params={{ chatId }}>
|
||||||
<SelectableButton selected={false} tooltipText="Close" icon={<Xmark />} />
|
<SelectableButton selected={false} tooltipText="Close" icon={<Xmark />} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase';
|
||||||
|
|
||||||
// Displays a full-screen, visually polished 404 not found state
|
// Displays a full-screen, visually polished 404 not found state
|
||||||
// inspired by GlobalErrorCard with consistent styling and components.
|
// inspired by GlobalErrorCard with consistent styling and components.
|
||||||
export const NotFoundCard: NotFoundRouteComponent = () => {
|
export const NotFoundCard = () => {
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className="flex flex-col items-center z-[999] absolute inset-0 top-0 left-0 right-0 bottom-0 justify-center h-full w-full p-8 bg-background"
|
className="flex flex-col items-center z-[999] absolute inset-0 top-0 left-0 right-0 bottom-0 justify-center h-full w-full p-8 bg-background"
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
|
||||||
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
||||||
import { AppSegmented } from '@/components/ui/segmented';
|
import { AppSegmented } from '@/components/ui/segmented';
|
||||||
import { ReasoningController } from '@/controllers/ReasoningController/ReasoningController';
|
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||||
|
import { ReasoningController } from '@/controllers/ReasoningController';
|
||||||
import { AssetContainer } from '@/layouts/AssetContainer/AssetContainer';
|
import { AssetContainer } from '@/layouts/AssetContainer/AssetContainer';
|
||||||
|
import { useGetReasoningMessageId } from '../useGetReasoningMessageId';
|
||||||
|
import { useIsEmbed } from '../useIsEmbed';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/reasoning/$messageId/')({
|
export const component = () => {
|
||||||
component: RouteComponent,
|
const chatId = useGetChatId() || '';
|
||||||
});
|
const messageId = useGetReasoningMessageId() || '';
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const { chatId, messageId } = Route.useParams();
|
|
||||||
return (
|
return (
|
||||||
<AssetContainer header={<ReasoningControllerHeader />} headerBorderVariant="ghost" scrollable>
|
<AssetContainer header={<ReasoningControllerHeader />} headerBorderVariant="ghost" scrollable>
|
||||||
<ReasoningController chatId={chatId} messageId={messageId} />
|
<ReasoningController chatId={chatId} messageId={messageId} />
|
||||||
</AssetContainer>
|
</AssetContainer>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const ReasoningControllerHeader: React.FC = () => {
|
const ReasoningControllerHeader: React.FC = () => {
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex items-center justify-between">
|
<div className="w-full flex items-center justify-between">
|
||||||
<AppSegmented
|
<AppSegmented
|
||||||
|
@ -29,7 +29,7 @@ const ReasoningControllerHeader: React.FC = () => {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<ClosePageButton />
|
<ClosePageButton isEmbed={isEmbed} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { useParams } from '@tanstack/react-router';
|
||||||
|
|
||||||
|
export const useGetReasoningMessageId = () => {
|
||||||
|
const params = useParams({ strict: false });
|
||||||
|
return params?.messageId;
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { useMatchRoute } from '@tanstack/react-router';
|
||||||
|
import { Route as EmbedRoute } from '@/routes/embed';
|
||||||
|
|
||||||
|
export const useIsEmbed = () => {
|
||||||
|
const matchRoute = useMatchRoute();
|
||||||
|
const matches = matchRoute({
|
||||||
|
to: EmbedRoute.id,
|
||||||
|
fuzzy: true,
|
||||||
|
});
|
||||||
|
return !!matches;
|
||||||
|
};
|
|
@ -1,10 +1,24 @@
|
||||||
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 lastMatch = useMatches({
|
||||||
|
select: (matches) => {
|
||||||
|
return findLast(matches, (match) => match.staticData?.assetType);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
if (typeof lastMatch === 'number') {
|
||||||
export const useSelectedAssetType = () => {
|
return 'chat';
|
||||||
const data = assetRouteApi.useRouteContext({ select: stableCtxSelector });
|
}
|
||||||
|
// @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,6 +5,7 @@ import { CreateChatButton } from '@/components/features/AssetLayout/CreateChatBu
|
||||||
import { ShareDashboardButton } from '@/components/features/buttons/ShareDashboardButton';
|
import { ShareDashboardButton } from '@/components/features/buttons/ShareDashboardButton';
|
||||||
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
||||||
import { DashboardThreeDotMenu } from '@/components/features/dashboard/DashboardThreeDotMenu';
|
import { DashboardThreeDotMenu } from '@/components/features/dashboard/DashboardThreeDotMenu';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
||||||
import { useIsDashboardReadOnly } from '@/context/Dashboards/useIsDashboardReadOnly';
|
import { useIsDashboardReadOnly } from '@/context/Dashboards/useIsDashboardReadOnly';
|
||||||
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
||||||
|
@ -17,6 +18,7 @@ export const DashboardContainerHeaderButtons: React.FC<{
|
||||||
}> = React.memo(({ dashboardId, dashboardVersionNumber }) => {
|
}> = React.memo(({ dashboardId, dashboardVersionNumber }) => {
|
||||||
const isChatMode = useIsChatMode();
|
const isChatMode = useIsChatMode();
|
||||||
const isFileMode = useIsFileMode();
|
const isFileMode = useIsFileMode();
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const { isViewingOldVersion } = useIsDashboardReadOnly({
|
const { isViewingOldVersion } = useIsDashboardReadOnly({
|
||||||
dashboardId: dashboardId || '',
|
dashboardId: dashboardId || '',
|
||||||
});
|
});
|
||||||
|
@ -36,15 +38,17 @@ export const DashboardContainerHeaderButtons: React.FC<{
|
||||||
dashboardVersionNumber={dashboardVersionNumber}
|
dashboardVersionNumber={dashboardVersionNumber}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<DashboardThreeDotMenu
|
{!isEmbed && (
|
||||||
dashboardId={dashboardId}
|
<DashboardThreeDotMenu
|
||||||
isViewingOldVersion={isViewingOldVersion}
|
dashboardId={dashboardId}
|
||||||
dashboardVersionNumber={dashboardVersionNumber}
|
isViewingOldVersion={isViewingOldVersion}
|
||||||
/>
|
dashboardVersionNumber={dashboardVersionNumber}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<HideButtonContainer show={isFileMode && isEditor}>
|
<HideButtonContainer show={isFileMode && isEditor}>
|
||||||
<CreateChatButton assetId={dashboardId} assetType="dashboard_file" />
|
<CreateChatButton assetId={dashboardId} assetType="dashboard_file" />
|
||||||
</HideButtonContainer>
|
</HideButtonContainer>
|
||||||
{isChatMode && <ClosePageButton />}
|
{isChatMode && <ClosePageButton isEmbed={isEmbed} />}
|
||||||
</FileButtonContainer>
|
</FileButtonContainer>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
||||||
import { MetricThreeDotMenuButton } from '@/components/features/metrics/MetricThreeDotMenu';
|
import { MetricThreeDotMenuButton } from '@/components/features/metrics/MetricThreeDotMenu';
|
||||||
import { SelectableButton } from '@/components/ui/buttons/SelectableButton';
|
import { SelectableButton } from '@/components/ui/buttons/SelectableButton';
|
||||||
import { SquareChartPen } from '@/components/ui/icons';
|
import { SquareChartPen } from '@/components/ui/icons';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
||||||
import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly';
|
import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly';
|
||||||
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
||||||
|
@ -22,6 +23,7 @@ export const MetricContainerHeaderButtons: React.FC<{
|
||||||
}> = React.memo(({ metricId, metricVersionNumber }) => {
|
}> = React.memo(({ metricId, metricVersionNumber }) => {
|
||||||
const isChatMode = useIsChatMode();
|
const isChatMode = useIsChatMode();
|
||||||
const isFileMode = useIsFileMode();
|
const isFileMode = useIsFileMode();
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const { isViewingOldVersion } = useIsMetricReadOnly({
|
const { isViewingOldVersion } = useIsMetricReadOnly({
|
||||||
metricId: metricId || '',
|
metricId: metricId || '',
|
||||||
});
|
});
|
||||||
|
@ -42,15 +44,17 @@ export const MetricContainerHeaderButtons: React.FC<{
|
||||||
{isEffectiveOwner && !isViewingOldVersion && (
|
{isEffectiveOwner && !isViewingOldVersion && (
|
||||||
<ShareMetricButton metricId={metricId} metricVersionNumber={metricVersionNumber} />
|
<ShareMetricButton metricId={metricId} metricVersionNumber={metricVersionNumber} />
|
||||||
)}
|
)}
|
||||||
<MetricThreeDotMenuButton
|
{!isEmbed && (
|
||||||
metricId={metricId}
|
<MetricThreeDotMenuButton
|
||||||
isViewingOldVersion={isViewingOldVersion}
|
metricId={metricId}
|
||||||
versionNumber={metricVersionNumber}
|
isViewingOldVersion={isViewingOldVersion}
|
||||||
/>
|
versionNumber={metricVersionNumber}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<HideButtonContainer show={isFileMode && isEditor}>
|
<HideButtonContainer show={isFileMode && isEditor}>
|
||||||
<CreateChatButton assetId={metricId} assetType="metric_file" />
|
<CreateChatButton assetId={metricId} assetType="metric_file" />
|
||||||
</HideButtonContainer>
|
</HideButtonContainer>
|
||||||
{isChatMode && <ClosePageButton />}
|
{isChatMode && <ClosePageButton isEmbed={isEmbed} />}
|
||||||
</FileButtonContainer>
|
</FileButtonContainer>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { CreateChatButton } from '@/components/features/AssetLayout/CreateChatBu
|
||||||
import { ShareReportButton } from '@/components/features/buttons/ShareReportButton';
|
import { ShareReportButton } from '@/components/features/buttons/ShareReportButton';
|
||||||
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
||||||
import { ReportThreeDotMenu } from '@/components/features/reports/ReportThreeDotMenu';
|
import { ReportThreeDotMenu } from '@/components/features/reports/ReportThreeDotMenu';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode';
|
||||||
import { useIsReportReadOnly } from '@/context/Reports/useIsReportReadOnly';
|
import { useIsReportReadOnly } from '@/context/Reports/useIsReportReadOnly';
|
||||||
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
import { canEdit, getIsEffectiveOwner } from '@/lib/share';
|
||||||
|
@ -23,6 +24,7 @@ export const ReportContainerHeaderButtons: React.FC<ReportContainerHeaderButtons
|
||||||
}) => {
|
}) => {
|
||||||
const isChatMode = useIsChatMode();
|
const isChatMode = useIsChatMode();
|
||||||
const isFileMode = useIsFileMode();
|
const isFileMode = useIsFileMode();
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const { isViewingOldVersion } = useIsReportReadOnly({
|
const { isViewingOldVersion } = useIsReportReadOnly({
|
||||||
reportId: reportId || '',
|
reportId: reportId || '',
|
||||||
});
|
});
|
||||||
|
@ -38,16 +40,18 @@ export const ReportContainerHeaderButtons: React.FC<ReportContainerHeaderButtons
|
||||||
<FileButtonContainer>
|
<FileButtonContainer>
|
||||||
{isEffectiveOwner && <ShareReportButton reportId={reportId} />}
|
{isEffectiveOwner && <ShareReportButton reportId={reportId} />}
|
||||||
|
|
||||||
<ReportThreeDotMenu
|
{!isEmbed && (
|
||||||
reportId={reportId}
|
<ReportThreeDotMenu
|
||||||
reportVersionNumber={reportVersionNumber}
|
reportId={reportId}
|
||||||
isViewingOldVersion={isViewingOldVersion}
|
reportVersionNumber={reportVersionNumber}
|
||||||
/>
|
isViewingOldVersion={isViewingOldVersion}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<HideButtonContainer show={isFileMode && isEditor}>
|
<HideButtonContainer show={isFileMode && isEditor}>
|
||||||
<CreateChatButton assetId={reportId} assetType="report_file" />
|
<CreateChatButton assetId={reportId} assetType="report_file" />
|
||||||
</HideButtonContainer>
|
</HideButtonContainer>
|
||||||
{isChatMode && <ClosePageButton />}
|
{isChatMode && <ClosePageButton isEmbed={isEmbed} />}
|
||||||
</FileButtonContainer>
|
</FileButtonContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import type { BusterChatMessage, BusterChatResponseMessage_file } from '@/api/asset_interfaces';
|
import type { BusterChatMessage, BusterChatResponseMessage_file } from '@/api/asset_interfaces';
|
||||||
import { useGetChatMessage } from '@/api/buster_rest/chats';
|
import { useGetChatMessage } from '@/api/buster_rest/chats';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { createChatAssetRoute } from '@/lib/routes/createSimpleAssetRoute';
|
import { createChatAssetRoute } from '@/lib/routes/createSimpleAssetRoute';
|
||||||
import type { ILinkProps } from '@/types/routes';
|
import type { ILinkProps } from '@/types/routes';
|
||||||
import type { ChatResponseMessageProps } from '../ChatResponseMessageSelector';
|
import type { ChatResponseMessageProps } from '../ChatResponseMessageSelector';
|
||||||
|
@ -21,12 +22,14 @@ export const ChatResponseMessage_File: React.FC<ChatResponseMessageProps> = Reac
|
||||||
const { file_type } = responseMessage;
|
const { file_type } = responseMessage;
|
||||||
|
|
||||||
const { isSelectedFile } = useGetIsSelectedFile({ responseMessage });
|
const { isSelectedFile } = useGetIsSelectedFile({ responseMessage });
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
|
|
||||||
const linkParams = createChatAssetRoute({
|
const linkParams = createChatAssetRoute({
|
||||||
asset_type: file_type,
|
asset_type: file_type,
|
||||||
id: responseMessage.id,
|
id: responseMessage.id,
|
||||||
chatId,
|
chatId,
|
||||||
versionNumber: responseMessage.version_number,
|
versionNumber: responseMessage.version_number,
|
||||||
|
isEmbed,
|
||||||
}) as unknown as ILinkProps;
|
}) as unknown as ILinkProps;
|
||||||
|
|
||||||
const SelectedComponent = useMemo(() => {
|
const SelectedComponent = useMemo(() => {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Text } from '@/components/ui/typography';
|
||||||
import { ShimmerText } from '@/components/ui/typography/ShimmerText';
|
import { ShimmerText } from '@/components/ui/typography/ShimmerText';
|
||||||
import { useGetBlackBoxMessage } from '@/context/BlackBox/blackbox-store';
|
import { useGetBlackBoxMessage } from '@/context/BlackBox/blackbox-store';
|
||||||
import { BLACK_BOX_INITIAL_THOUGHT } from '@/context/BlackBox/useBlackboxMessage';
|
import { BLACK_BOX_INITIAL_THOUGHT } from '@/context/BlackBox/useBlackboxMessage';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useSelectedAssetType } from '@/context/BusterAssets/useSelectedAssetType';
|
import { useSelectedAssetType } from '@/context/BusterAssets/useSelectedAssetType';
|
||||||
import { useGetCurrentMessageId } from '@/context/Chats';
|
import { useGetCurrentMessageId } from '@/context/Chats';
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ export const ChatResponseReasoning: React.FC<{
|
||||||
const { data: lastMessageTitle } = useGetChatMessage(messageId, {
|
const { data: lastMessageTitle } = useGetChatMessage(messageId, {
|
||||||
select: stableLastMessageTitleSelector,
|
select: stableLastMessageTitleSelector,
|
||||||
});
|
});
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
const selectedFileType = useSelectedAssetType();
|
const selectedFileType = useSelectedAssetType();
|
||||||
const isReasonginFileSelected = selectedFileType === 'reasoning' && urlMessageId === messageId;
|
const isReasonginFileSelected = selectedFileType === 'reasoning' && urlMessageId === messageId;
|
||||||
const showShimmerText = isReasonginFileSelected
|
const showShimmerText = isReasonginFileSelected
|
||||||
|
@ -49,7 +51,11 @@ export const ChatResponseReasoning: React.FC<{
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={
|
to={
|
||||||
isReasonginFileSelected ? '/app/chats/$chatId' : '/app/chats/$chatId/reasoning/$messageId'
|
isEmbed
|
||||||
|
? '/embed/chat/$chatId/reasoning/$messageId'
|
||||||
|
: isReasonginFileSelected
|
||||||
|
? '/app/chats/$chatId'
|
||||||
|
: '/app/chats/$chatId/reasoning/$messageId'
|
||||||
}
|
}
|
||||||
params={{
|
params={{
|
||||||
chatId,
|
chatId,
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
import { chatQueryKeys } from '@/api/query_keys/chat';
|
import { chatQueryKeys } from '@/api/query_keys/chat';
|
||||||
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
||||||
import { useBlackboxMessage } from '@/context/BlackBox/useBlackboxMessage';
|
import { useBlackboxMessage } from '@/context/BlackBox/useBlackboxMessage';
|
||||||
|
import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed';
|
||||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||||
import { updateChatToIChat } from '@/lib/chat';
|
import { updateChatToIChat } from '@/lib/chat';
|
||||||
|
|
||||||
|
@ -105,8 +106,9 @@ export const useChatStreaming = ({
|
||||||
});
|
});
|
||||||
|
|
||||||
//HOOKS FOR TRACKING CHAT AND MESSAGE CHANGES
|
//HOOKS FOR TRACKING CHAT AND MESSAGE CHANGES
|
||||||
|
const isEmbed = useIsEmbed();
|
||||||
useTrackAndUpdateChatChanges({ chatId, isStreamingMessage });
|
useTrackAndUpdateChatChanges({ chatId, isStreamingMessage });
|
||||||
useTrackAndUpdateNewMessages({ chatId });
|
useTrackAndUpdateNewMessages({ chatId, isEmbed });
|
||||||
useTrackAndUpdateMessageChanges({ chatId, messageId, isStreamingMessage }, (c) => {
|
useTrackAndUpdateMessageChanges({ chatId, messageId, isStreamingMessage }, (c) => {
|
||||||
const {
|
const {
|
||||||
reasoning_messages,
|
reasoning_messages,
|
||||||
|
|
|
@ -59,8 +59,16 @@ export const createChatAssetRoute = (asset: {
|
||||||
id: string | undefined;
|
id: string | undefined;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
versionNumber?: number;
|
versionNumber?: number;
|
||||||
|
isEmbed: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
if (asset.asset_type === 'chat' || !asset.asset_type || !asset.id) {
|
if (asset.asset_type === 'chat' || !asset.asset_type || !asset.id) {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
return defineLink({
|
||||||
|
to: '/embed/chat/$chatId',
|
||||||
|
params: { chatId: asset.chatId },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/chats/$chatId',
|
to: '/app/chats/$chatId',
|
||||||
params: { chatId: asset.chatId },
|
params: { chatId: asset.chatId },
|
||||||
|
@ -68,6 +76,13 @@ export const createChatAssetRoute = (asset: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset.asset_type === 'metric_file') {
|
if (asset.asset_type === 'metric_file') {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
return defineLink({
|
||||||
|
to: '/embed/chat/$chatId/metrics/$metricId',
|
||||||
|
params: { metricId: asset.id || '', chatId: asset.chatId },
|
||||||
|
search: { metric_version_number: asset.versionNumber },
|
||||||
|
});
|
||||||
|
}
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/chats/$chatId/metrics/$metricId',
|
to: '/app/chats/$chatId/metrics/$metricId',
|
||||||
params: { metricId: asset.id || '', chatId: asset.chatId },
|
params: { metricId: asset.id || '', chatId: asset.chatId },
|
||||||
|
@ -76,6 +91,13 @@ export const createChatAssetRoute = (asset: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset.asset_type === 'dashboard_file') {
|
if (asset.asset_type === 'dashboard_file') {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
return defineLink({
|
||||||
|
to: '/embed/chat/$chatId/dashboards/$dashboardId',
|
||||||
|
params: { dashboardId: asset.id || '', chatId: asset.chatId },
|
||||||
|
search: { dashboard_version_number: asset.versionNumber },
|
||||||
|
});
|
||||||
|
}
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/chats/$chatId/dashboards/$dashboardId',
|
to: '/app/chats/$chatId/dashboards/$dashboardId',
|
||||||
params: { dashboardId: asset.id || '', chatId: asset.chatId },
|
params: { dashboardId: asset.id || '', chatId: asset.chatId },
|
||||||
|
@ -84,6 +106,13 @@ export const createChatAssetRoute = (asset: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset.asset_type === 'report_file') {
|
if (asset.asset_type === 'report_file') {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
return defineLink({
|
||||||
|
to: '/embed/chat/$chatId/reports/$reportId',
|
||||||
|
params: { reportId: asset.id || '', chatId: asset.chatId },
|
||||||
|
search: { report_version_number: asset.versionNumber },
|
||||||
|
});
|
||||||
|
}
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/chats/$chatId/reports/$reportId',
|
to: '/app/chats/$chatId/reports/$reportId',
|
||||||
params: { reportId: asset.id || '', chatId: asset.chatId },
|
params: { reportId: asset.id || '', chatId: asset.chatId },
|
||||||
|
@ -92,6 +121,12 @@ export const createChatAssetRoute = (asset: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset.asset_type === 'reasoning') {
|
if (asset.asset_type === 'reasoning') {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
return defineLink({
|
||||||
|
to: '/embed/chat/$chatId/reasoning/$messageId',
|
||||||
|
params: { messageId: asset.id || '', chatId: asset.chatId },
|
||||||
|
});
|
||||||
|
}
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/chats/$chatId/reasoning/$messageId',
|
to: '/app/chats/$chatId/reasoning/$messageId',
|
||||||
params: { chatId: asset.chatId, messageId: asset.id || '' },
|
params: { chatId: asset.chatId, messageId: asset.id || '' },
|
||||||
|
@ -99,6 +134,12 @@ export const createChatAssetRoute = (asset: {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset.asset_type === 'collection') {
|
if (asset.asset_type === 'collection') {
|
||||||
|
if (asset.isEmbed) {
|
||||||
|
console.warn('collection is actually not supported for embeds...', asset.id);
|
||||||
|
return defineLink({
|
||||||
|
to: '/auth/login',
|
||||||
|
});
|
||||||
|
}
|
||||||
return defineLink({
|
return defineLink({
|
||||||
to: '/app/collections/$collectionId',
|
to: '/app/collections/$collectionId',
|
||||||
params: { collectionId: asset.id || '' },
|
params: { collectionId: asset.id || '' },
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,35 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import { ClosePageButton } from '@/components/features/chat/ClosePageButton';
|
import * as reportContent from '@/context/BusterAssets/reasoning-server/reasoningContent';
|
||||||
import { AppSegmented } from '@/components/ui/segmented';
|
|
||||||
import { ReasoningController } from '@/controllers/ReasoningController/ReasoningController';
|
|
||||||
import { AssetContainer } from '@/layouts/AssetContainer/AssetContainer';
|
|
||||||
|
|
||||||
export const Route = createFileRoute('/app/_app/_asset/chats/$chatId/reasoning/$messageId/')({
|
export const Route = createFileRoute('/app/_app/_asset/chats/$chatId/reasoning/$messageId/')({
|
||||||
component: RouteComponent,
|
...reportContent,
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
|
||||||
const { chatId, messageId } = Route.useParams();
|
|
||||||
return (
|
|
||||||
<AssetContainer header={<ReasoningControllerHeader />} headerBorderVariant="ghost" scrollable>
|
|
||||||
<ReasoningController chatId={chatId} messageId={messageId} />
|
|
||||||
</AssetContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ReasoningControllerHeader: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<div className="w-full flex items-center justify-between">
|
|
||||||
<AppSegmented
|
|
||||||
type="button"
|
|
||||||
options={[
|
|
||||||
{
|
|
||||||
value: 'reasoning',
|
|
||||||
label: 'Reasoning',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<ClosePageButton />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import type { AssetType } from '@buster/server-shared/assets';
|
import type { AssetType } from '@buster/server-shared/assets';
|
||||||
import { createFileRoute, Outlet, type RouteContext } from '@tanstack/react-router';
|
import { createFileRoute, Outlet, type RouteContext } from '@tanstack/react-router';
|
||||||
import { prefetchGetMyUserInfo } from '@/api/buster_rest/users';
|
import { prefetchGetMyUserInfo } from '@/api/buster_rest/users';
|
||||||
import { Text } from '@/components/ui/typography';
|
import { NotFoundCard } from '@/components/features/global/NotFoundCard';
|
||||||
|
import { useGetChatId } from '@/context/Chats/useGetChatId';
|
||||||
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 }) => {
|
||||||
|
@ -14,6 +16,7 @@ export const Route = createFileRoute('/embed')({
|
||||||
|
|
||||||
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
|
const assetType = [...matches].reverse().find(({ staticData }) => staticData?.assetType)
|
||||||
?.staticData?.assetType as AssetType;
|
?.staticData?.assetType as AssetType;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
assetType,
|
assetType,
|
||||||
};
|
};
|
||||||
|
@ -29,25 +32,26 @@ export const Route = createFileRoute('/embed')({
|
||||||
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
const stableCtxSelector = (ctx: RouteContext) => ctx.assetType;
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const assetType = Route.useLoaderData({ select: stableCtxSelector });
|
const assetType = Route.useLoaderData({ select: stableCtxSelector });
|
||||||
|
const chatId = useGetChatId();
|
||||||
|
|
||||||
if (!assetType) {
|
if (!assetType) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">No asset type found</div>
|
<div className="flex flex-col gap-3 h-full w-full items-center justify-center">
|
||||||
);
|
<NotFoundCard />
|
||||||
}
|
|
||||||
|
|
||||||
if (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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isChat = assetType === 'chat' || !!chatId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="h-full w-full bg-page-background overflow-y-auto">
|
<main
|
||||||
|
data-testid={`embed-main-${assetType}`}
|
||||||
|
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,18 @@
|
||||||
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: RouteComponent,
|
||||||
return <div>Hello "/embed/chat/$chatId"!</div>;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full p-2 max-h-[100vh]" data-testid="embed-chat-container">
|
||||||
|
<div className="h-full w-full border rounded bg-background">
|
||||||
|
{chatLayoutServerContext.component()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as dashboardLayoutServerAssetContext from '@/context/BusterAssets/dashboard-server/dashboardLayoutServerAssetContext';
|
import * as dashboardLayoutServerAssetContext from '@/context/BusterAssets/dashboard-server/dashboardLayoutServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/dashboards/$dashboardId/_layout')({
|
export const Route = createFileRoute('/embed/chat/$chatId/dashboards/$dashboardId/_layout')({
|
||||||
...dashboardLayoutServerAssetContext,
|
...dashboardLayoutServerAssetContext,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as dashboardContentServerContext from '@/context/BusterAssets/dashboard-server/dashboardContentContext';
|
import * as dashboardContentServerContext from '@/context/BusterAssets/dashboard-server/dashboardContentContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/dashboards/$dashboardId/_layout/')({
|
export const Route = createFileRoute('/embed/chat/$chatId/dashboards/$dashboardId/_layout/')({
|
||||||
...dashboardContentServerContext,
|
...dashboardContentServerContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/dashboards/$dashboardId/metrics/$metricId/_content'
|
'/embed/chat/$chatId/dashboards/$dashboardId/metrics/$metricId/_content'
|
||||||
)({
|
)({
|
||||||
...metricLayoutServerContext,
|
...metricLayoutServerContext,
|
||||||
loader: metricLayoutServerContext.loader<{ metricId: string; dashboardId: string }>,
|
loader: metricLayoutServerContext.loader<{ metricId: string; dashboardId: string }>,
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/chart'
|
'/embed/chat/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/chart'
|
||||||
)({
|
)({
|
||||||
...metricChartServerAssetContext,
|
...metricChartServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/'
|
'/embed/chat/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/'
|
||||||
)({
|
)({
|
||||||
...metricIndexServerContext,
|
...metricIndexServerContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/results'
|
'/embed/chat/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/results'
|
||||||
)({
|
)({
|
||||||
...metricResultsServerAssetContext,
|
...metricResultsServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/sql'
|
'/embed/chat/$chatId/dashboards/$dashboardId/metrics/$metricId/_content/sql'
|
||||||
)({
|
)({
|
||||||
...metricSQLServerAsssetContext,
|
...metricSQLServerAsssetContext,
|
||||||
});
|
});
|
|
@ -1,7 +1,10 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/')({
|
export const Route = createFileRoute('/embed/chat/$chatId/')({
|
||||||
component: RouteComponent,
|
component: RouteComponent,
|
||||||
|
staticData: {
|
||||||
|
assetType: 'chat',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
|
@ -1,7 +1,7 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/metrics/$metricId/_layout')({
|
export const Route = createFileRoute('/embed/chat/$chatId/metrics/$metricId/_layout')({
|
||||||
...metricLayoutServerContext,
|
...metricLayoutServerContext,
|
||||||
loader: metricLayoutServerContext.loader<{ metricId: string }>,
|
loader: metricLayoutServerContext.loader<{ metricId: string }>,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/metrics/$metricId/_layout/chart')({
|
export const Route = createFileRoute('/embed/chat/$chatId/metrics/$metricId/_layout/chart')({
|
||||||
...metricChartServerAssetContext,
|
...metricChartServerAssetContext,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/metrics/$metricId/_layout/')({
|
export const Route = createFileRoute('/embed/chat/$chatId/metrics/$metricId/_layout/')({
|
||||||
...metricIndexServerContext,
|
...metricIndexServerContext,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/metrics/$metricId/_layout/results')({
|
export const Route = createFileRoute('/embed/chat/$chatId/metrics/$metricId/_layout/results')({
|
||||||
...metricResultsServerAssetContext,
|
...metricResultsServerAssetContext,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/metrics/$metricId/_layout/sql')({
|
export const Route = createFileRoute('/embed/chat/$chatId/metrics/$metricId/_layout/sql')({
|
||||||
...metricSQLServerAsssetContext,
|
...metricSQLServerAsssetContext,
|
||||||
});
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
|
import * as reportContent from '@/context/BusterAssets/reasoning-server/reasoningContent';
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/embed/chat/$chatId/reasoning/$messageId/')({
|
||||||
|
...reportContent,
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as reportLayoutServerAssetContext from '@/context/BusterAssets/report-server/reportLayoutServerAssetContext';
|
import * as reportLayoutServerAssetContext from '@/context/BusterAssets/report-server/reportLayoutServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/reports/$reportId/_layout')({
|
export const Route = createFileRoute('/embed/chat/$chatId/reports/$reportId/_layout')({
|
||||||
...reportLayoutServerAssetContext,
|
...reportLayoutServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,6 +2,6 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
|
|
||||||
import * as reportContentServerContext from '@/context/BusterAssets/report-server/reportContentServerAssetContext';
|
import * as reportContentServerContext from '@/context/BusterAssets/report-server/reportContentServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/reports/$reportId/_layout/content')({
|
export const Route = createFileRoute('/embed/chat/$chatId/reports/$reportId/_layout/content')({
|
||||||
...reportContentServerContext,
|
...reportContentServerContext,
|
||||||
});
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { createFileRoute } from '@tanstack/react-router';
|
import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as reportIndexServerAssetContext from '@/context/BusterAssets/report-server/reportContentServerAssetContext';
|
import * as reportIndexServerAssetContext from '@/context/BusterAssets/report-server/reportContentServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute('/embed/chats/$chatId/reports/$reportId/_layout/')({
|
export const Route = createFileRoute('/embed/chat/$chatId/reports/$reportId/_layout/')({
|
||||||
...reportIndexServerAssetContext,
|
...reportIndexServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
import * as metricLayoutServerContext from '@/context/BusterAssets/metric-server/metricLayoutServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/reports/$reportId/metrics/$metricId/_content'
|
'/embed/chat/$chatId/reports/$reportId/metrics/$metricId/_content'
|
||||||
)({
|
)({
|
||||||
...metricLayoutServerContext,
|
...metricLayoutServerContext,
|
||||||
loader: metricLayoutServerContext.loader<{ metricId: string }>,
|
loader: metricLayoutServerContext.loader<{ metricId: string }>,
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
import * as metricChartServerAssetContext from '@/context/BusterAssets/metric-server/metricChartServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/reports/$reportId/metrics/$metricId/_content/chart'
|
'/embed/chat/$chatId/reports/$reportId/metrics/$metricId/_content/chart'
|
||||||
)({
|
)({
|
||||||
...metricChartServerAssetContext,
|
...metricChartServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
import * as metricIndexServerContext from '@/context/BusterAssets/metric-server/metricIndexServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/reports/$reportId/metrics/$metricId/_content/'
|
'/embed/chat/$chatId/reports/$reportId/metrics/$metricId/_content/'
|
||||||
)({
|
)({
|
||||||
...metricIndexServerContext,
|
...metricIndexServerContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
import * as metricResultsServerAssetContext from '@/context/BusterAssets/metric-server/metricResultsServerAssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/reports/$reportId/metrics/$metricId/_content/results'
|
'/embed/chat/$chatId/reports/$reportId/metrics/$metricId/_content/results'
|
||||||
)({
|
)({
|
||||||
...metricResultsServerAssetContext,
|
...metricResultsServerAssetContext,
|
||||||
});
|
});
|
|
@ -2,7 +2,7 @@ import { createFileRoute } from '@tanstack/react-router';
|
||||||
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
import * as metricSQLServerAsssetContext from '@/context/BusterAssets/metric-server/metricSQLServerAsssetContext';
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute(
|
||||||
'/embed/chats/$chatId/reports/$reportId/metrics/$metricId/_content/sql'
|
'/embed/chat/$chatId/reports/$reportId/metrics/$metricId/_content/sql'
|
||||||
)({
|
)({
|
||||||
...metricSQLServerAsssetContext,
|
...metricSQLServerAsssetContext,
|
||||||
});
|
});
|
Loading…
Reference in New Issue