diff --git a/apps/web/src/api/buster-electric/messages/hooks.ts b/apps/web/src/api/buster-electric/messages/hooks.ts index 23c3541bb..85c9d6ed8 100644 --- a/apps/web/src/api/buster-electric/messages/hooks.ts +++ b/apps/web/src/api/buster-electric/messages/hooks.ts @@ -158,13 +158,19 @@ const useCheckIfWeHaveAFollowupDashboard = (messageId: string) => { return useMemoizedFn(method); }; -export const useTrackAndUpdateNewMessages = ({ chatId }: { chatId: string | undefined }) => { +export const useTrackAndUpdateNewMessages = ({ + chatId, + isEmbed, +}: { + chatId: string | undefined; + isEmbed: boolean; +}) => { const { onUpdateChat } = useChatUpdate(); const getChatMemoized = useGetChatMemoized(); const getChatMessageMemoized = useGetChatMessageMemoized(); const queryClient = useQueryClient(); - const subscribe = !!chatId; + const subscribe = !!chatId && !isEmbed; const shape = useMemo(() => { return messagesShape({ chatId: chatId || '', columns: ['id'] }); diff --git a/apps/web/src/components/features/chat/ClosePageButton.tsx b/apps/web/src/components/features/chat/ClosePageButton.tsx index 06782b153..98a87bba4 100644 --- a/apps/web/src/components/features/chat/ClosePageButton.tsx +++ b/apps/web/src/components/features/chat/ClosePageButton.tsx @@ -3,11 +3,11 @@ import { SelectableButton } from '@/components/ui/buttons/SelectableButton'; import { useGetChatId } from '@/context/Chats/useGetChatId'; import { Xmark } from '../../ui/icons'; -export const ClosePageButton = () => { +export const ClosePageButton = ({ isEmbed }: { isEmbed: boolean }) => { const chatId = useGetChatId() || ''; return ( - + } /> ); diff --git a/apps/web/src/components/features/global/NotFoundCard.tsx b/apps/web/src/components/features/global/NotFoundCard.tsx index a5ea19bf8..e48643a73 100644 --- a/apps/web/src/components/features/global/NotFoundCard.tsx +++ b/apps/web/src/components/features/global/NotFoundCard.tsx @@ -4,7 +4,7 @@ import { Card, CardContent, CardFooter } from '@/components/ui/card/CardBase'; // Displays a full-screen, visually polished 404 not found state // inspired by GlobalErrorCard with consistent styling and components. -export const NotFoundCard: NotFoundRouteComponent = () => { +export const NotFoundCard = () => { return (
{ + const chatId = useGetChatId() || ''; + const messageId = useGetReasoningMessageId() || ''; return ( } headerBorderVariant="ghost" scrollable> ); -} +}; const ReasoningControllerHeader: React.FC = () => { + const isEmbed = useIsEmbed(); return (
{ }, ]} /> - +
); }; diff --git a/apps/web/src/context/BusterAssets/useGetReasoningMessageId.ts b/apps/web/src/context/BusterAssets/useGetReasoningMessageId.ts new file mode 100644 index 000000000..95d1c38d5 --- /dev/null +++ b/apps/web/src/context/BusterAssets/useGetReasoningMessageId.ts @@ -0,0 +1,6 @@ +import { useParams } from '@tanstack/react-router'; + +export const useGetReasoningMessageId = () => { + const params = useParams({ strict: false }); + return params?.messageId; +}; diff --git a/apps/web/src/context/BusterAssets/useIsEmbed.ts b/apps/web/src/context/BusterAssets/useIsEmbed.ts new file mode 100644 index 000000000..738580212 --- /dev/null +++ b/apps/web/src/context/BusterAssets/useIsEmbed.ts @@ -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; +}; diff --git a/apps/web/src/context/BusterAssets/useSelectedAssetType.ts b/apps/web/src/context/BusterAssets/useSelectedAssetType.ts index 76930b812..d7c626e26 100644 --- a/apps/web/src/context/BusterAssets/useSelectedAssetType.ts +++ b/apps/web/src/context/BusterAssets/useSelectedAssetType.ts @@ -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 => { + const lastMatch = useMatches({ + select: (matches) => { + return findLast(matches, (match) => match.staticData?.assetType); + }, + }); -const stableCtxSelector = (ctx: RouteContext) => ctx.assetType; -export const useSelectedAssetType = () => { - const data = assetRouteApi.useRouteContext({ select: stableCtxSelector }); + if (typeof lastMatch === 'number') { + return 'chat'; + } + // @ts-expect-error - lastMatch is not undefined + const data = lastMatch?.staticData?.assetType as StaticDataRouteOption['assetType']; const { messageId } = useParams({ strict: false, }); diff --git a/apps/web/src/layouts/AssetContainer/DashboardAssetContainer/DashboardHeaderButtons.tsx b/apps/web/src/layouts/AssetContainer/DashboardAssetContainer/DashboardHeaderButtons.tsx index d557c4360..922b93990 100644 --- a/apps/web/src/layouts/AssetContainer/DashboardAssetContainer/DashboardHeaderButtons.tsx +++ b/apps/web/src/layouts/AssetContainer/DashboardAssetContainer/DashboardHeaderButtons.tsx @@ -5,6 +5,7 @@ import { CreateChatButton } from '@/components/features/AssetLayout/CreateChatBu import { ShareDashboardButton } from '@/components/features/buttons/ShareDashboardButton'; import { ClosePageButton } from '@/components/features/chat/ClosePageButton'; import { DashboardThreeDotMenu } from '@/components/features/dashboard/DashboardThreeDotMenu'; +import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed'; import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode'; import { useIsDashboardReadOnly } from '@/context/Dashboards/useIsDashboardReadOnly'; import { canEdit, getIsEffectiveOwner } from '@/lib/share'; @@ -17,6 +18,7 @@ export const DashboardContainerHeaderButtons: React.FC<{ }> = React.memo(({ dashboardId, dashboardVersionNumber }) => { const isChatMode = useIsChatMode(); const isFileMode = useIsFileMode(); + const isEmbed = useIsEmbed(); const { isViewingOldVersion } = useIsDashboardReadOnly({ dashboardId: dashboardId || '', }); @@ -36,15 +38,17 @@ export const DashboardContainerHeaderButtons: React.FC<{ dashboardVersionNumber={dashboardVersionNumber} /> )} - + {!isEmbed && ( + + )} - {isChatMode && } + {isChatMode && } ); }); diff --git a/apps/web/src/layouts/AssetContainer/MetricAssetContainer/MetricHeaderButtons.tsx b/apps/web/src/layouts/AssetContainer/MetricAssetContainer/MetricHeaderButtons.tsx index 3a3b7c36d..a4f7e3f81 100644 --- a/apps/web/src/layouts/AssetContainer/MetricAssetContainer/MetricHeaderButtons.tsx +++ b/apps/web/src/layouts/AssetContainer/MetricAssetContainer/MetricHeaderButtons.tsx @@ -9,6 +9,7 @@ import { ClosePageButton } from '@/components/features/chat/ClosePageButton'; import { MetricThreeDotMenuButton } from '@/components/features/metrics/MetricThreeDotMenu'; import { SelectableButton } from '@/components/ui/buttons/SelectableButton'; import { SquareChartPen } from '@/components/ui/icons'; +import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed'; import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode'; import { useIsMetricReadOnly } from '@/context/Metrics/useIsMetricReadOnly'; import { canEdit, getIsEffectiveOwner } from '@/lib/share'; @@ -22,6 +23,7 @@ export const MetricContainerHeaderButtons: React.FC<{ }> = React.memo(({ metricId, metricVersionNumber }) => { const isChatMode = useIsChatMode(); const isFileMode = useIsFileMode(); + const isEmbed = useIsEmbed(); const { isViewingOldVersion } = useIsMetricReadOnly({ metricId: metricId || '', }); @@ -42,15 +44,17 @@ export const MetricContainerHeaderButtons: React.FC<{ {isEffectiveOwner && !isViewingOldVersion && ( )} - + {!isEmbed && ( + + )} - {isChatMode && } + {isChatMode && } ); }); diff --git a/apps/web/src/layouts/AssetContainer/ReportAssetContainer/ReportContainerHeaderButtons.tsx b/apps/web/src/layouts/AssetContainer/ReportAssetContainer/ReportContainerHeaderButtons.tsx index efcb5e86d..2ad36c01b 100644 --- a/apps/web/src/layouts/AssetContainer/ReportAssetContainer/ReportContainerHeaderButtons.tsx +++ b/apps/web/src/layouts/AssetContainer/ReportAssetContainer/ReportContainerHeaderButtons.tsx @@ -6,6 +6,7 @@ import { CreateChatButton } from '@/components/features/AssetLayout/CreateChatBu import { ShareReportButton } from '@/components/features/buttons/ShareReportButton'; import { ClosePageButton } from '@/components/features/chat/ClosePageButton'; import { ReportThreeDotMenu } from '@/components/features/reports/ReportThreeDotMenu'; +import { useIsEmbed } from '@/context/BusterAssets/useIsEmbed'; import { useIsChatMode, useIsFileMode } from '@/context/Chats/useMode'; import { useIsReportReadOnly } from '@/context/Reports/useIsReportReadOnly'; import { canEdit, getIsEffectiveOwner } from '@/lib/share'; @@ -23,6 +24,7 @@ export const ReportContainerHeaderButtons: React.FC { const isChatMode = useIsChatMode(); const isFileMode = useIsFileMode(); + const isEmbed = useIsEmbed(); const { isViewingOldVersion } = useIsReportReadOnly({ reportId: reportId || '', }); @@ -38,16 +40,18 @@ export const ReportContainerHeaderButtons: React.FC {isEffectiveOwner && } - + {!isEmbed && ( + + )} - {isChatMode && } + {isChatMode && } ); }; diff --git a/apps/web/src/layouts/ChatLayout/ChatContainer.tsx b/apps/web/src/layouts/ChatLayout/ChatContainer.tsx index 1680222ac..ebcd75613 100644 --- a/apps/web/src/layouts/ChatLayout/ChatContainer.tsx +++ b/apps/web/src/layouts/ChatLayout/ChatContainer.tsx @@ -5,21 +5,23 @@ import { ChatHeader } from './ChatHeader'; export const CHAT_CONTAINER_ID = 'chat-container-content'; -export const ChatContainer = React.memo(({ chatId }: { chatId: string | undefined }) => { - return ( - } - headerBorderVariant="ghost" - headerClassName="bg-page-background" - mainClassName="bg-page-background" - scrollable - id={CHAT_CONTAINER_ID} - className="flex h-full w-full min-w-[295px] flex-col" - > - - - ); -}); +export const ChatContainer = React.memo( + ({ chatId, isEmbed }: { chatId: string | undefined; isEmbed: boolean }) => { + return ( + } + headerBorderVariant="ghost" + headerClassName="bg-page-background" + mainClassName="bg-page-background" + scrollable + id={CHAT_CONTAINER_ID} + className="flex h-full w-full min-w-[295px] flex-col" + > + + + ); + } +); ChatContainer.displayName = 'ChatContainer'; diff --git a/apps/web/src/layouts/ChatLayout/ChatContent/ChatContent.tsx b/apps/web/src/layouts/ChatLayout/ChatContent/ChatContent.tsx index 733eec9ae..71247c625 100644 --- a/apps/web/src/layouts/ChatLayout/ChatContent/ChatContent.tsx +++ b/apps/web/src/layouts/ChatLayout/ChatContent/ChatContent.tsx @@ -12,61 +12,65 @@ import { FollowUpChatInput } from './FollowupChatInput'; const autoClass = 'mx-auto max-w-[600px] w-full'; -export const ChatContent: React.FC<{ chatId: string | undefined }> = React.memo(({ chatId }) => { - const chatMessageIds = useGetChatMessageIds(chatId); - const containerRef = useRef(null); +export const ChatContent: React.FC<{ chatId: string | undefined; isEmbed: boolean }> = React.memo( + ({ chatId, isEmbed }) => { + const chatMessageIds = useGetChatMessageIds(chatId); + const containerRef = useRef(null); - const { isAutoScrollEnabled, isMountedAutoScrollObserver, scrollToBottom, enableAutoScroll } = - useAutoScroll(containerRef, { - observeSubTree: true, - enabled: false, + const { isAutoScrollEnabled, isMountedAutoScrollObserver, scrollToBottom, enableAutoScroll } = + useAutoScroll(containerRef, { + observeSubTree: true, + 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 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; - const showScrollToBottomButton = isMountedAutoScrollObserver && containerRef.current; + return ( + <> +
+ + {chatMessageIds?.map((messageId, index) => ( +
+ +
+ ))} +
+
- return ( - <> -
- - {chatMessageIds?.map((messageId, index) => ( -
- + {showScrollToBottomButton && ( + -
- ))} -
-
- - - {showScrollToBottomButton && ( - + )} + )} - - - ); -}); + + ); + } +); ChatContent.displayName = 'ChatContent'; diff --git a/apps/web/src/layouts/ChatLayout/ChatContent/ChatMessageOptions.tsx b/apps/web/src/layouts/ChatLayout/ChatContent/ChatMessageOptions.tsx index bd3ad9211..544cce771 100644 --- a/apps/web/src/layouts/ChatLayout/ChatContent/ChatMessageOptions.tsx +++ b/apps/web/src/layouts/ChatLayout/ChatContent/ChatMessageOptions.tsx @@ -89,18 +89,20 @@ export const ChatMessageOptions: React.FC<{ /> )} - -