SumAI-Search / client /components /AiResponse /AiResponseContent.tsx
github-actions[bot]
Sync to HuggingFace Spaces
6b3405c
import {
ActionIcon,
Alert,
Badge,
Box,
Card,
CopyButton,
Group,
ScrollArea,
Text,
Tooltip,
} from "@mantine/core";
import {
IconArrowsMaximize,
IconCheck,
IconCopy,
IconHandStop,
IconInfoCircle,
} from "@tabler/icons-react";
import { PublishFunction } from "create-pubsub";
import { lazy, ReactNode, Suspense, useMemo, useState } from "react";
import { match } from "ts-pattern";
const FormattedMarkdown = lazy(() => import("./FormattedMarkdown"));
export default function AiResponseContent({
textGenerationState,
response,
setTextGenerationState,
}: {
textGenerationState: string;
response: string;
setTextGenerationState: PublishFunction<
| "failed"
| "awaitingSearchResults"
| "preparingToGenerate"
| "idle"
| "loadingModel"
| "generating"
| "interrupted"
| "completed"
>;
}) {
const [isScrollAreaEnabled, setScrollAreaEnabled] = useState(true);
const ConditionalScrollArea = useMemo(
() =>
({ children }: { children: ReactNode }) => {
return isScrollAreaEnabled ? (
<ScrollArea.Autosize mah={300} type="auto" offsetScrollbars>
{children}
</ScrollArea.Autosize>
) : (
<Box>{children}</Box>
);
},
[isScrollAreaEnabled],
);
return (
<Card withBorder shadow="sm" radius="md">
<Card.Section withBorder inheritPadding py="xs">
<Group justify="space-between">
<Group gap="xs" align="center">
<Text fw={500}>
{match(textGenerationState)
.with("generating", () => "Generating AI Response...")
.otherwise(() => "AI Response")}
</Text>
{match(textGenerationState)
.with("interrupted", () => (
<Badge variant="light" color="yellow" size="xs">
Interrupted
</Badge>
))
.otherwise(() => null)}
</Group>
<Group gap="xs" align="center">
{match(textGenerationState)
.with("generating", () => (
<Tooltip label="Interrupt generation">
<ActionIcon
onClick={() => setTextGenerationState("interrupted")}
variant="subtle"
color="gray"
>
<IconHandStop size={16} />
</ActionIcon>
</Tooltip>
))
.otherwise(() => null)}
{isScrollAreaEnabled && (
<Tooltip label="Show full response without scroll bar">
<ActionIcon
onClick={() => setScrollAreaEnabled(false)}
variant="subtle"
color="gray"
>
<IconArrowsMaximize size={16} />
</ActionIcon>
</Tooltip>
)}
<CopyButton value={response} timeout={2000}>
{({ copied, copy }) => (
<Tooltip
label={copied ? "Copied" : "Copy response"}
withArrow
position="right"
>
<ActionIcon
color={copied ? "teal" : "gray"}
variant="subtle"
onClick={copy}
>
{copied ? <IconCheck size={16} /> : <IconCopy size={16} />}
</ActionIcon>
</Tooltip>
)}
</CopyButton>
</Group>
</Group>
</Card.Section>
<Card.Section withBorder>
<ConditionalScrollArea>
<Suspense>
<FormattedMarkdown>{response}</FormattedMarkdown>
</Suspense>
</ConditionalScrollArea>
{match(textGenerationState)
.with("failed", () => (
<Alert
variant="light"
color="yellow"
title="Failed to generate response"
icon={<IconInfoCircle />}
>
Could not generate response. It's possible that your browser or
your system is out of memory.
</Alert>
))
.otherwise(() => null)}
</Card.Section>
</Card>
);
}