Spaces:
Running
Running
File size: 4,280 Bytes
6b3405c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
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>
);
}
|