Rename to blocks (#502)

This commit is contained in:
Jeremy 2024-11-07 02:40:29 +03:00 committed by GitHub
parent 22a09de6e6
commit 729634e1d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 108 additions and 111 deletions

View file

@ -1,29 +1,29 @@
import { JSONValue } from 'ai';
import { Dispatch, memo, SetStateAction } from 'react';
import { UICanvas } from './canvas';
import { useCanvasStream } from './use-canvas-stream';
import { UIBlock } from './block';
import { useBlockStream } from './use-block-stream';
interface CanvasStreamHandlerProps {
setCanvas: Dispatch<SetStateAction<UICanvas>>;
interface BlockStreamHandlerProps {
setBlock: Dispatch<SetStateAction<UIBlock>>;
streamingData: JSONValue[] | undefined;
}
export function PureCanvasStreamHandler({
setCanvas,
export function PureBlockStreamHandler({
setBlock,
streamingData,
}: CanvasStreamHandlerProps) {
useCanvasStream({
}: BlockStreamHandlerProps) {
useBlockStream({
streamingData,
setCanvas,
setBlock,
});
return null;
}
function areEqual(
prevProps: CanvasStreamHandlerProps,
nextProps: CanvasStreamHandlerProps
prevProps: BlockStreamHandlerProps,
nextProps: BlockStreamHandlerProps
) {
if (!prevProps.streamingData && !nextProps.streamingData) {
return true;
@ -40,4 +40,4 @@ function areEqual(
return true;
}
export const CanvasStreamHandler = memo(PureCanvasStreamHandler, areEqual);
export const BlockStreamHandler = memo(PureBlockStreamHandler, areEqual);

View file

@ -31,7 +31,7 @@ import { useScrollToBottom } from './use-scroll-to-bottom';
import { VersionFooter } from './version-footer';
import { Button } from '../ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
export interface UICanvas {
export interface UIBlock {
title: string;
documentId: string;
content: string;
@ -45,7 +45,7 @@ export interface UICanvas {
};
}
export function Canvas({
export function Block({
chatId,
input,
setInput,
@ -55,8 +55,8 @@ export function Canvas({
attachments,
setAttachments,
append,
canvas,
setCanvas,
block,
setBlock,
messages,
setMessages,
votes,
@ -68,8 +68,8 @@ export function Canvas({
stop: () => void;
attachments: Array<Attachment>;
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
canvas: UICanvas;
setCanvas: Dispatch<SetStateAction<UICanvas>>;
block: UIBlock;
setBlock: Dispatch<SetStateAction<UIBlock>>;
messages: Array<Message>;
setMessages: Dispatch<SetStateAction<Array<Message>>>;
votes: Array<Vote> | undefined;
@ -92,15 +92,15 @@ export function Canvas({
isLoading: isDocumentsFetching,
mutate: mutateDocuments,
} = useSWR<Array<Document>>(
canvas && canvas.status !== 'streaming'
? `/api/document?id=${canvas.documentId}`
block && block.status !== 'streaming'
? `/api/document?id=${block.documentId}`
: null,
fetcher
);
const { data: suggestions } = useSWR<Array<Suggestion>>(
documents && canvas && canvas.status !== 'streaming'
? `/api/suggestions?documentId=${canvas.documentId}`
documents && block && block.status !== 'streaming'
? `/api/suggestions?documentId=${block.documentId}`
: null,
fetcher,
{
@ -119,27 +119,27 @@ export function Canvas({
if (mostRecentDocument) {
setDocument(mostRecentDocument);
setCurrentVersionIndex(documents.length - 1);
setCanvas((currentCanvas) => ({
...currentCanvas,
setBlock((currentBlock) => ({
...currentBlock,
content: mostRecentDocument.content ?? '',
}));
}
}
}, [documents, setCanvas]);
}, [documents, setBlock]);
useEffect(() => {
mutateDocuments();
}, [canvas.status, mutateDocuments]);
}, [block.status, mutateDocuments]);
const { mutate } = useSWRConfig();
const [isContentDirty, setIsContentDirty] = useState(false);
const handleContentChange = useCallback(
(updatedContent: string) => {
if (!canvas) return;
if (!block) return;
mutate<Array<Document>>(
`/api/document?id=${canvas.documentId}`,
`/api/document?id=${block.documentId}`,
async (currentDocuments) => {
if (!currentDocuments) return undefined;
@ -151,10 +151,10 @@ export function Canvas({
}
if (currentDocument.content !== updatedContent) {
await fetch(`/api/document?id=${canvas.documentId}`, {
await fetch(`/api/document?id=${block.documentId}`, {
method: 'POST',
body: JSON.stringify({
title: canvas.title,
title: block.title,
content: updatedContent,
}),
});
@ -175,7 +175,7 @@ export function Canvas({
{ revalidate: false }
);
},
[canvas, mutate]
[block, mutate]
);
const debouncedHandleContentChange = useDebounceCallback(
@ -295,8 +295,8 @@ export function Canvas({
chatId={chatId}
key={message.id}
message={message}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
isLoading={isLoading && index === messages.length - 1}
vote={
votes
@ -346,10 +346,10 @@ export function Canvas({
}
: {
opacity: 0,
x: canvas.boundingBox.left,
y: canvas.boundingBox.top,
height: canvas.boundingBox.height,
width: canvas.boundingBox.width,
x: block.boundingBox.left,
y: block.boundingBox.top,
height: block.boundingBox.height,
width: block.boundingBox.width,
borderRadius: 50,
}
}
@ -401,8 +401,8 @@ export function Canvas({
variant="outline"
className="h-fit p-2 dark:hover:bg-zinc-700"
onClick={() => {
setCanvas((currentCanvas) => ({
...currentCanvas,
setBlock((currentBlock) => ({
...currentBlock,
isVisible: false,
}));
}}
@ -412,7 +412,7 @@ export function Canvas({
<div className="flex flex-col">
<div className="font-medium">
{document?.title ?? canvas.title}
{document?.title ?? block.title}
</div>
{isContentDirty ? (
@ -442,10 +442,10 @@ export function Canvas({
variant="outline"
className="p-2 h-fit dark:hover:bg-zinc-700"
onClick={() => {
copyToClipboard(canvas.content);
copyToClipboard(block.content);
toast.success('Copied to clipboard!');
}}
disabled={canvas.status === 'streaming'}
disabled={block.status === 'streaming'}
>
<CopyIcon size={18} />
</Button>
@ -461,7 +461,7 @@ export function Canvas({
handleVersionChange('prev');
}}
disabled={
currentVersionIndex === 0 || canvas.status === 'streaming'
currentVersionIndex === 0 || block.status === 'streaming'
}
>
<UndoIcon size={18} />
@ -477,7 +477,7 @@ export function Canvas({
onClick={() => {
handleVersionChange('next');
}}
disabled={isCurrentVersion || canvas.status === 'streaming'}
disabled={isCurrentVersion || block.status === 'streaming'}
>
<RedoIcon size={18} />
</Button>
@ -498,7 +498,7 @@ export function Canvas({
handleVersionChange('toggle');
}}
disabled={
canvas.status === 'streaming' || currentVersionIndex === 0
block.status === 'streaming' || currentVersionIndex === 0
}
>
<DeltaIcon size={18} />
@ -511,18 +511,18 @@ export function Canvas({
<div className="prose dark:prose-invert dark:bg-muted bg-background h-full overflow-y-scroll px-4 py-8 md:p-20 !max-w-full pb-40 items-center">
<div className="flex flex-row max-w-[600px] mx-auto">
{isDocumentsFetching && !canvas.content ? (
{isDocumentsFetching && !block.content ? (
<DocumentSkeleton />
) : mode === 'edit' ? (
<Editor
content={
isCurrentVersion
? canvas.content
? block.content
: getDocumentContentById(currentVersionIndex)
}
isCurrentVersion={isCurrentVersion}
currentVersionIndex={currentVersionIndex}
status={canvas.status}
status={block.status}
saveContent={saveContent}
suggestions={isCurrentVersion ? (suggestions ?? []) : []}
/>
@ -555,7 +555,7 @@ export function Canvas({
<AnimatePresence>
{!isCurrentVersion && (
<VersionFooter
canvas={canvas}
block={block}
currentVersionIndex={currentVersionIndex}
documents={documents}
handleVersionChange={handleVersionChange}

View file

@ -13,8 +13,8 @@ import { useScrollToBottom } from '@/components/custom/use-scroll-to-bottom';
import { Vote } from '@/db/schema';
import { fetcher } from '@/lib/utils';
import { Canvas, UICanvas } from './canvas';
import { CanvasStreamHandler } from './canvas-stream-handler';
import { Block, UIBlock } from './block';
import { BlockStreamHandler } from './block-stream-handler';
import { MultimodalInput } from './multimodal-input';
import { Overview } from './overview';
@ -50,7 +50,7 @@ export function Chat({
const { width: windowWidth = 1920, height: windowHeight = 1080 } =
useWindowSize();
const [canvas, setCanvas] = useState<UICanvas>({
const [block, setBlock] = useState<UIBlock>({
documentId: 'init',
content: '',
title: '',
@ -89,8 +89,8 @@ export function Chat({
key={message.id}
chatId={id}
message={message}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
isLoading={isLoading && messages.length - 1 === index}
vote={
votes
@ -129,8 +129,8 @@ export function Chat({
</div>
<AnimatePresence>
{canvas && canvas.isVisible && (
<Canvas
{block && block.isVisible && (
<Block
chatId={id}
input={input}
setInput={setInput}
@ -140,8 +140,8 @@ export function Chat({
attachments={attachments}
setAttachments={setAttachments}
append={append}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
messages={messages}
setMessages={setMessages}
votes={votes}
@ -149,10 +149,7 @@ export function Chat({
)}
</AnimatePresence>
<CanvasStreamHandler
streamingData={streamingData}
setCanvas={setCanvas}
/>
<BlockStreamHandler streamingData={streamingData} setBlock={setBlock} />
</>
);
}

View file

@ -1,6 +1,6 @@
import { SetStateAction } from 'react';
import { UICanvas } from './canvas';
import { UIBlock } from './block';
import { FileIcon, LoaderIcon, MessageIcon, PencilEditIcon } from './icons';
const getActionText = (type: 'create' | 'update' | 'request-suggestions') => {
@ -19,15 +19,15 @@ const getActionText = (type: 'create' | 'update' | 'request-suggestions') => {
interface DocumentToolResultProps {
type: 'create' | 'update' | 'request-suggestions';
result: any;
canvas: UICanvas;
setCanvas: (value: SetStateAction<UICanvas>) => void;
block: UIBlock;
setBlock: (value: SetStateAction<UIBlock>) => void;
}
export function DocumentToolResult({
type,
result,
canvas,
setCanvas,
block,
setBlock,
}: DocumentToolResultProps) {
return (
<div
@ -42,7 +42,7 @@ export function DocumentToolResult({
height: rect.height,
};
setCanvas({
setBlock({
documentId: result.id,
content: '',
title: result.title,

View file

@ -7,7 +7,7 @@ import { Dispatch, SetStateAction } from 'react';
import { Vote } from '@/db/schema';
import { UICanvas } from './canvas';
import { UIBlock } from './block';
import { DocumentToolCall, DocumentToolResult } from './document';
import { SparklesIcon } from './icons';
import { Markdown } from './markdown';
@ -18,15 +18,15 @@ import { Weather } from './weather';
export const PreviewMessage = ({
chatId,
message,
canvas,
setCanvas,
block,
setBlock,
vote,
isLoading,
}: {
chatId: string;
message: Message;
canvas: UICanvas;
setCanvas: Dispatch<SetStateAction<UICanvas>>;
block: UIBlock;
setBlock: Dispatch<SetStateAction<UIBlock>>;
vote: Vote | undefined;
isLoading: boolean;
}) => {
@ -71,22 +71,22 @@ export const PreviewMessage = ({
<DocumentToolResult
type="create"
result={result}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
/>
) : toolName === 'updateDocument' ? (
<DocumentToolResult
type="update"
result={result}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
/>
) : toolName === 'requestSuggestions' ? (
<DocumentToolResult
type="request-suggestions"
result={result}
canvas={canvas}
setCanvas={setCanvas}
block={block}
setBlock={setBlock}
/>
) : (
<pre>{JSON.stringify(result, null, 2)}</pre>

View file

@ -4,19 +4,19 @@ import { useSWRConfig } from 'swr';
import { Suggestion } from '@/db/schema';
import { UICanvas } from './canvas';
import { UIBlock } from './block';
type StreamingDelta = {
type: 'text-delta' | 'title' | 'id' | 'suggestion' | 'clear' | 'finish';
content: string | Suggestion;
};
export function useCanvasStream({
export function useBlockStream({
streamingData,
setCanvas,
setBlock,
}: {
streamingData: JSONValue[] | undefined;
setCanvas: Dispatch<SetStateAction<UICanvas>>;
setBlock: Dispatch<SetStateAction<UIBlock>>;
}) {
const { mutate } = useSWRConfig();
const [optimisticSuggestions, setOptimisticSuggestions] = useState<
@ -37,30 +37,30 @@ export function useCanvasStream({
const delta = mostRecentDelta as StreamingDelta;
setCanvas((draftCanvas) => {
setBlock((draftBlock) => {
switch (delta.type) {
case 'id':
return {
...draftCanvas,
...draftBlock,
documentId: delta.content as string,
};
case 'title':
return {
...draftCanvas,
...draftBlock,
title: delta.content as string,
};
case 'text-delta':
return {
...draftCanvas,
content: draftCanvas.content + (delta.content as string),
...draftBlock,
content: draftBlock.content + (delta.content as string),
isVisible:
draftCanvas.status === 'streaming' &&
draftCanvas.content.length > 200 &&
draftCanvas.content.length < 250
draftBlock.status === 'streaming' &&
draftBlock.content.length > 200 &&
draftBlock.content.length < 250
? true
: draftCanvas.isVisible,
: draftBlock.isVisible,
status: 'streaming',
};
@ -72,24 +72,24 @@ export function useCanvasStream({
]);
}, 0);
return draftCanvas;
return draftBlock;
case 'clear':
return {
...draftCanvas,
...draftBlock,
content: '',
status: 'streaming',
};
case 'finish':
return {
...draftCanvas,
...draftBlock,
status: 'idle',
};
default:
return draftCanvas;
return draftBlock;
}
});
}, [streamingData, setCanvas]);
}, [streamingData, setBlock]);
}

View file

@ -9,19 +9,19 @@ import { useWindowSize } from 'usehooks-ts';
import { Document } from '@/db/schema';
import { getDocumentTimestampByIndex } from '@/lib/utils';
import { UICanvas } from './canvas';
import { UIBlock } from './block';
import { LoaderIcon } from './icons';
import { Button } from '../ui/button';
interface VersionFooterProps {
canvas: UICanvas;
block: UIBlock;
handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
documents: Array<Document> | undefined;
currentVersionIndex: number;
}
export const VersionFooter = ({
canvas,
block,
handleVersionChange,
documents,
currentVersionIndex,
@ -56,8 +56,8 @@ export const VersionFooter = ({
setIsMutating(true);
mutate(
`/api/document?id=${canvas.documentId}`,
await fetch(`/api/document?id=${canvas.documentId}`, {
`/api/document?id=${block.documentId}`,
await fetch(`/api/document?id=${block.documentId}`, {
method: 'PATCH',
body: JSON.stringify({
timestamp: getDocumentTimestampByIndex(