Rename to blocks (#502)
This commit is contained in:
parent
22a09de6e6
commit
729634e1d6
10 changed files with 108 additions and 111 deletions
|
|
@ -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);
|
||||
|
|
@ -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}
|
||||
|
|
@ -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} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue