Rename to blocks (#502)
This commit is contained in:
parent
22a09de6e6
commit
729634e1d6
10 changed files with 108 additions and 111 deletions
|
|
@ -21,8 +21,8 @@ export const models: Array<Model> = [
|
||||||
description: 'For complex, multi-step tasks',
|
description: 'For complex, multi-step tasks',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gpt-4o-canvas',
|
id: 'gpt-4o-blocks',
|
||||||
label: 'GPT 4o with Canvas',
|
label: 'GPT 4o with Blocks',
|
||||||
apiIdentifier: 'gpt-4o',
|
apiIdentifier: 'gpt-4o',
|
||||||
description: 'Collaborate with writing',
|
description: 'Collaborate with writing',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
export const canvasPrompt = `
|
export const blocksPrompt = `
|
||||||
Canvas is a special user interface mode that helps users with writing, editing, and other content creation tasks. When canvas is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the canvas and visible to the user.
|
Blocks is a special user interface mode that helps users with writing, editing, and other content creation tasks. When block is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the blocks and visible to the user.
|
||||||
|
|
||||||
This is a guide for using canvas tools: \`createDocument\` and \`updateDocument\`, which render content on a canvas beside the conversation.
|
This is a guide for using blocks tools: \`createDocument\` and \`updateDocument\`, which render content on a blocks beside the conversation.
|
||||||
|
|
||||||
**When to use \`createDocument\`:**
|
**When to use \`createDocument\`:**
|
||||||
- For substantial content (>10 lines)
|
- For substantial content (>10 lines)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { z } from 'zod';
|
||||||
|
|
||||||
import { customModel } from '@/ai';
|
import { customModel } from '@/ai';
|
||||||
import { models } from '@/ai/models';
|
import { models } from '@/ai/models';
|
||||||
import { canvasPrompt, regularPrompt } from '@/ai/prompts';
|
import { blocksPrompt, regularPrompt } from '@/ai/prompts';
|
||||||
import { auth } from '@/app/(auth)/auth';
|
import { auth } from '@/app/(auth)/auth';
|
||||||
import {
|
import {
|
||||||
deleteChatById,
|
deleteChatById,
|
||||||
|
|
@ -37,7 +37,7 @@ type AllowedTools =
|
||||||
| 'requestSuggestions'
|
| 'requestSuggestions'
|
||||||
| 'getWeather';
|
| 'getWeather';
|
||||||
|
|
||||||
const canvasTools: AllowedTools[] = [
|
const blocksTools: AllowedTools[] = [
|
||||||
'createDocument',
|
'createDocument',
|
||||||
'updateDocument',
|
'updateDocument',
|
||||||
'requestSuggestions',
|
'requestSuggestions',
|
||||||
|
|
@ -89,11 +89,11 @@ export async function POST(request: Request) {
|
||||||
|
|
||||||
const result = await streamText({
|
const result = await streamText({
|
||||||
model: customModel(model.apiIdentifier),
|
model: customModel(model.apiIdentifier),
|
||||||
system: modelId === 'gpt-4o-canvas' ? canvasPrompt : regularPrompt,
|
system: modelId === 'gpt-4o-blocks' ? blocksPrompt : regularPrompt,
|
||||||
messages: coreMessages,
|
messages: coreMessages,
|
||||||
maxSteps: 5,
|
maxSteps: 5,
|
||||||
experimental_activeTools:
|
experimental_activeTools:
|
||||||
modelId === 'gpt-4o-canvas' ? canvasTools : weatherTools,
|
modelId === 'gpt-4o-blocks' ? blocksTools : weatherTools,
|
||||||
tools: {
|
tools: {
|
||||||
getWeather: {
|
getWeather: {
|
||||||
description: 'Get the current weather at a location',
|
description: 'Get the current weather at a location',
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
import { JSONValue } from 'ai';
|
import { JSONValue } from 'ai';
|
||||||
import { Dispatch, memo, SetStateAction } from 'react';
|
import { Dispatch, memo, SetStateAction } from 'react';
|
||||||
|
|
||||||
import { UICanvas } from './canvas';
|
import { UIBlock } from './block';
|
||||||
import { useCanvasStream } from './use-canvas-stream';
|
import { useBlockStream } from './use-block-stream';
|
||||||
|
|
||||||
interface CanvasStreamHandlerProps {
|
interface BlockStreamHandlerProps {
|
||||||
setCanvas: Dispatch<SetStateAction<UICanvas>>;
|
setBlock: Dispatch<SetStateAction<UIBlock>>;
|
||||||
streamingData: JSONValue[] | undefined;
|
streamingData: JSONValue[] | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PureCanvasStreamHandler({
|
export function PureBlockStreamHandler({
|
||||||
setCanvas,
|
setBlock,
|
||||||
streamingData,
|
streamingData,
|
||||||
}: CanvasStreamHandlerProps) {
|
}: BlockStreamHandlerProps) {
|
||||||
useCanvasStream({
|
useBlockStream({
|
||||||
streamingData,
|
streamingData,
|
||||||
setCanvas,
|
setBlock,
|
||||||
});
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function areEqual(
|
function areEqual(
|
||||||
prevProps: CanvasStreamHandlerProps,
|
prevProps: BlockStreamHandlerProps,
|
||||||
nextProps: CanvasStreamHandlerProps
|
nextProps: BlockStreamHandlerProps
|
||||||
) {
|
) {
|
||||||
if (!prevProps.streamingData && !nextProps.streamingData) {
|
if (!prevProps.streamingData && !nextProps.streamingData) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -40,4 +40,4 @@ function areEqual(
|
||||||
return true;
|
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 { VersionFooter } from './version-footer';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
||||||
export interface UICanvas {
|
export interface UIBlock {
|
||||||
title: string;
|
title: string;
|
||||||
documentId: string;
|
documentId: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
|
@ -45,7 +45,7 @@ export interface UICanvas {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Canvas({
|
export function Block({
|
||||||
chatId,
|
chatId,
|
||||||
input,
|
input,
|
||||||
setInput,
|
setInput,
|
||||||
|
|
@ -55,8 +55,8 @@ export function Canvas({
|
||||||
attachments,
|
attachments,
|
||||||
setAttachments,
|
setAttachments,
|
||||||
append,
|
append,
|
||||||
canvas,
|
block,
|
||||||
setCanvas,
|
setBlock,
|
||||||
messages,
|
messages,
|
||||||
setMessages,
|
setMessages,
|
||||||
votes,
|
votes,
|
||||||
|
|
@ -68,8 +68,8 @@ export function Canvas({
|
||||||
stop: () => void;
|
stop: () => void;
|
||||||
attachments: Array<Attachment>;
|
attachments: Array<Attachment>;
|
||||||
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
|
setAttachments: Dispatch<SetStateAction<Array<Attachment>>>;
|
||||||
canvas: UICanvas;
|
block: UIBlock;
|
||||||
setCanvas: Dispatch<SetStateAction<UICanvas>>;
|
setBlock: Dispatch<SetStateAction<UIBlock>>;
|
||||||
messages: Array<Message>;
|
messages: Array<Message>;
|
||||||
setMessages: Dispatch<SetStateAction<Array<Message>>>;
|
setMessages: Dispatch<SetStateAction<Array<Message>>>;
|
||||||
votes: Array<Vote> | undefined;
|
votes: Array<Vote> | undefined;
|
||||||
|
|
@ -92,15 +92,15 @@ export function Canvas({
|
||||||
isLoading: isDocumentsFetching,
|
isLoading: isDocumentsFetching,
|
||||||
mutate: mutateDocuments,
|
mutate: mutateDocuments,
|
||||||
} = useSWR<Array<Document>>(
|
} = useSWR<Array<Document>>(
|
||||||
canvas && canvas.status !== 'streaming'
|
block && block.status !== 'streaming'
|
||||||
? `/api/document?id=${canvas.documentId}`
|
? `/api/document?id=${block.documentId}`
|
||||||
: null,
|
: null,
|
||||||
fetcher
|
fetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data: suggestions } = useSWR<Array<Suggestion>>(
|
const { data: suggestions } = useSWR<Array<Suggestion>>(
|
||||||
documents && canvas && canvas.status !== 'streaming'
|
documents && block && block.status !== 'streaming'
|
||||||
? `/api/suggestions?documentId=${canvas.documentId}`
|
? `/api/suggestions?documentId=${block.documentId}`
|
||||||
: null,
|
: null,
|
||||||
fetcher,
|
fetcher,
|
||||||
{
|
{
|
||||||
|
|
@ -119,27 +119,27 @@ export function Canvas({
|
||||||
if (mostRecentDocument) {
|
if (mostRecentDocument) {
|
||||||
setDocument(mostRecentDocument);
|
setDocument(mostRecentDocument);
|
||||||
setCurrentVersionIndex(documents.length - 1);
|
setCurrentVersionIndex(documents.length - 1);
|
||||||
setCanvas((currentCanvas) => ({
|
setBlock((currentBlock) => ({
|
||||||
...currentCanvas,
|
...currentBlock,
|
||||||
content: mostRecentDocument.content ?? '',
|
content: mostRecentDocument.content ?? '',
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [documents, setCanvas]);
|
}, [documents, setBlock]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mutateDocuments();
|
mutateDocuments();
|
||||||
}, [canvas.status, mutateDocuments]);
|
}, [block.status, mutateDocuments]);
|
||||||
|
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const [isContentDirty, setIsContentDirty] = useState(false);
|
const [isContentDirty, setIsContentDirty] = useState(false);
|
||||||
|
|
||||||
const handleContentChange = useCallback(
|
const handleContentChange = useCallback(
|
||||||
(updatedContent: string) => {
|
(updatedContent: string) => {
|
||||||
if (!canvas) return;
|
if (!block) return;
|
||||||
|
|
||||||
mutate<Array<Document>>(
|
mutate<Array<Document>>(
|
||||||
`/api/document?id=${canvas.documentId}`,
|
`/api/document?id=${block.documentId}`,
|
||||||
async (currentDocuments) => {
|
async (currentDocuments) => {
|
||||||
if (!currentDocuments) return undefined;
|
if (!currentDocuments) return undefined;
|
||||||
|
|
||||||
|
|
@ -151,10 +151,10 @@ export function Canvas({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentDocument.content !== updatedContent) {
|
if (currentDocument.content !== updatedContent) {
|
||||||
await fetch(`/api/document?id=${canvas.documentId}`, {
|
await fetch(`/api/document?id=${block.documentId}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
title: canvas.title,
|
title: block.title,
|
||||||
content: updatedContent,
|
content: updatedContent,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
@ -175,7 +175,7 @@ export function Canvas({
|
||||||
{ revalidate: false }
|
{ revalidate: false }
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[canvas, mutate]
|
[block, mutate]
|
||||||
);
|
);
|
||||||
|
|
||||||
const debouncedHandleContentChange = useDebounceCallback(
|
const debouncedHandleContentChange = useDebounceCallback(
|
||||||
|
|
@ -295,8 +295,8 @@ export function Canvas({
|
||||||
chatId={chatId}
|
chatId={chatId}
|
||||||
key={message.id}
|
key={message.id}
|
||||||
message={message}
|
message={message}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
isLoading={isLoading && index === messages.length - 1}
|
isLoading={isLoading && index === messages.length - 1}
|
||||||
vote={
|
vote={
|
||||||
votes
|
votes
|
||||||
|
|
@ -346,10 +346,10 @@ export function Canvas({
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
x: canvas.boundingBox.left,
|
x: block.boundingBox.left,
|
||||||
y: canvas.boundingBox.top,
|
y: block.boundingBox.top,
|
||||||
height: canvas.boundingBox.height,
|
height: block.boundingBox.height,
|
||||||
width: canvas.boundingBox.width,
|
width: block.boundingBox.width,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -401,8 +401,8 @@ export function Canvas({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-fit p-2 dark:hover:bg-zinc-700"
|
className="h-fit p-2 dark:hover:bg-zinc-700"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCanvas((currentCanvas) => ({
|
setBlock((currentBlock) => ({
|
||||||
...currentCanvas,
|
...currentBlock,
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
|
|
@ -412,7 +412,7 @@ export function Canvas({
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="font-medium">
|
<div className="font-medium">
|
||||||
{document?.title ?? canvas.title}
|
{document?.title ?? block.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isContentDirty ? (
|
{isContentDirty ? (
|
||||||
|
|
@ -442,10 +442,10 @@ export function Canvas({
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="p-2 h-fit dark:hover:bg-zinc-700"
|
className="p-2 h-fit dark:hover:bg-zinc-700"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
copyToClipboard(canvas.content);
|
copyToClipboard(block.content);
|
||||||
toast.success('Copied to clipboard!');
|
toast.success('Copied to clipboard!');
|
||||||
}}
|
}}
|
||||||
disabled={canvas.status === 'streaming'}
|
disabled={block.status === 'streaming'}
|
||||||
>
|
>
|
||||||
<CopyIcon size={18} />
|
<CopyIcon size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -461,7 +461,7 @@ export function Canvas({
|
||||||
handleVersionChange('prev');
|
handleVersionChange('prev');
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
currentVersionIndex === 0 || canvas.status === 'streaming'
|
currentVersionIndex === 0 || block.status === 'streaming'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<UndoIcon size={18} />
|
<UndoIcon size={18} />
|
||||||
|
|
@ -477,7 +477,7 @@ export function Canvas({
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleVersionChange('next');
|
handleVersionChange('next');
|
||||||
}}
|
}}
|
||||||
disabled={isCurrentVersion || canvas.status === 'streaming'}
|
disabled={isCurrentVersion || block.status === 'streaming'}
|
||||||
>
|
>
|
||||||
<RedoIcon size={18} />
|
<RedoIcon size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -498,7 +498,7 @@ export function Canvas({
|
||||||
handleVersionChange('toggle');
|
handleVersionChange('toggle');
|
||||||
}}
|
}}
|
||||||
disabled={
|
disabled={
|
||||||
canvas.status === 'streaming' || currentVersionIndex === 0
|
block.status === 'streaming' || currentVersionIndex === 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<DeltaIcon size={18} />
|
<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="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">
|
<div className="flex flex-row max-w-[600px] mx-auto">
|
||||||
{isDocumentsFetching && !canvas.content ? (
|
{isDocumentsFetching && !block.content ? (
|
||||||
<DocumentSkeleton />
|
<DocumentSkeleton />
|
||||||
) : mode === 'edit' ? (
|
) : mode === 'edit' ? (
|
||||||
<Editor
|
<Editor
|
||||||
content={
|
content={
|
||||||
isCurrentVersion
|
isCurrentVersion
|
||||||
? canvas.content
|
? block.content
|
||||||
: getDocumentContentById(currentVersionIndex)
|
: getDocumentContentById(currentVersionIndex)
|
||||||
}
|
}
|
||||||
isCurrentVersion={isCurrentVersion}
|
isCurrentVersion={isCurrentVersion}
|
||||||
currentVersionIndex={currentVersionIndex}
|
currentVersionIndex={currentVersionIndex}
|
||||||
status={canvas.status}
|
status={block.status}
|
||||||
saveContent={saveContent}
|
saveContent={saveContent}
|
||||||
suggestions={isCurrentVersion ? (suggestions ?? []) : []}
|
suggestions={isCurrentVersion ? (suggestions ?? []) : []}
|
||||||
/>
|
/>
|
||||||
|
|
@ -555,7 +555,7 @@ export function Canvas({
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{!isCurrentVersion && (
|
{!isCurrentVersion && (
|
||||||
<VersionFooter
|
<VersionFooter
|
||||||
canvas={canvas}
|
block={block}
|
||||||
currentVersionIndex={currentVersionIndex}
|
currentVersionIndex={currentVersionIndex}
|
||||||
documents={documents}
|
documents={documents}
|
||||||
handleVersionChange={handleVersionChange}
|
handleVersionChange={handleVersionChange}
|
||||||
|
|
@ -13,8 +13,8 @@ import { useScrollToBottom } from '@/components/custom/use-scroll-to-bottom';
|
||||||
import { Vote } from '@/db/schema';
|
import { Vote } from '@/db/schema';
|
||||||
import { fetcher } from '@/lib/utils';
|
import { fetcher } from '@/lib/utils';
|
||||||
|
|
||||||
import { Canvas, UICanvas } from './canvas';
|
import { Block, UIBlock } from './block';
|
||||||
import { CanvasStreamHandler } from './canvas-stream-handler';
|
import { BlockStreamHandler } from './block-stream-handler';
|
||||||
import { MultimodalInput } from './multimodal-input';
|
import { MultimodalInput } from './multimodal-input';
|
||||||
import { Overview } from './overview';
|
import { Overview } from './overview';
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ export function Chat({
|
||||||
const { width: windowWidth = 1920, height: windowHeight = 1080 } =
|
const { width: windowWidth = 1920, height: windowHeight = 1080 } =
|
||||||
useWindowSize();
|
useWindowSize();
|
||||||
|
|
||||||
const [canvas, setCanvas] = useState<UICanvas>({
|
const [block, setBlock] = useState<UIBlock>({
|
||||||
documentId: 'init',
|
documentId: 'init',
|
||||||
content: '',
|
content: '',
|
||||||
title: '',
|
title: '',
|
||||||
|
|
@ -89,8 +89,8 @@ export function Chat({
|
||||||
key={message.id}
|
key={message.id}
|
||||||
chatId={id}
|
chatId={id}
|
||||||
message={message}
|
message={message}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
isLoading={isLoading && messages.length - 1 === index}
|
isLoading={isLoading && messages.length - 1 === index}
|
||||||
vote={
|
vote={
|
||||||
votes
|
votes
|
||||||
|
|
@ -129,8 +129,8 @@ export function Chat({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{canvas && canvas.isVisible && (
|
{block && block.isVisible && (
|
||||||
<Canvas
|
<Block
|
||||||
chatId={id}
|
chatId={id}
|
||||||
input={input}
|
input={input}
|
||||||
setInput={setInput}
|
setInput={setInput}
|
||||||
|
|
@ -140,8 +140,8 @@ export function Chat({
|
||||||
attachments={attachments}
|
attachments={attachments}
|
||||||
setAttachments={setAttachments}
|
setAttachments={setAttachments}
|
||||||
append={append}
|
append={append}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
setMessages={setMessages}
|
setMessages={setMessages}
|
||||||
votes={votes}
|
votes={votes}
|
||||||
|
|
@ -149,10 +149,7 @@ export function Chat({
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<CanvasStreamHandler
|
<BlockStreamHandler streamingData={streamingData} setBlock={setBlock} />
|
||||||
streamingData={streamingData}
|
|
||||||
setCanvas={setCanvas}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { SetStateAction } from 'react';
|
import { SetStateAction } from 'react';
|
||||||
|
|
||||||
import { UICanvas } from './canvas';
|
import { UIBlock } from './block';
|
||||||
import { FileIcon, LoaderIcon, MessageIcon, PencilEditIcon } from './icons';
|
import { FileIcon, LoaderIcon, MessageIcon, PencilEditIcon } from './icons';
|
||||||
|
|
||||||
const getActionText = (type: 'create' | 'update' | 'request-suggestions') => {
|
const getActionText = (type: 'create' | 'update' | 'request-suggestions') => {
|
||||||
|
|
@ -19,15 +19,15 @@ const getActionText = (type: 'create' | 'update' | 'request-suggestions') => {
|
||||||
interface DocumentToolResultProps {
|
interface DocumentToolResultProps {
|
||||||
type: 'create' | 'update' | 'request-suggestions';
|
type: 'create' | 'update' | 'request-suggestions';
|
||||||
result: any;
|
result: any;
|
||||||
canvas: UICanvas;
|
block: UIBlock;
|
||||||
setCanvas: (value: SetStateAction<UICanvas>) => void;
|
setBlock: (value: SetStateAction<UIBlock>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DocumentToolResult({
|
export function DocumentToolResult({
|
||||||
type,
|
type,
|
||||||
result,
|
result,
|
||||||
canvas,
|
block,
|
||||||
setCanvas,
|
setBlock,
|
||||||
}: DocumentToolResultProps) {
|
}: DocumentToolResultProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
@ -42,7 +42,7 @@ export function DocumentToolResult({
|
||||||
height: rect.height,
|
height: rect.height,
|
||||||
};
|
};
|
||||||
|
|
||||||
setCanvas({
|
setBlock({
|
||||||
documentId: result.id,
|
documentId: result.id,
|
||||||
content: '',
|
content: '',
|
||||||
title: result.title,
|
title: result.title,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { Dispatch, SetStateAction } from 'react';
|
||||||
|
|
||||||
import { Vote } from '@/db/schema';
|
import { Vote } from '@/db/schema';
|
||||||
|
|
||||||
import { UICanvas } from './canvas';
|
import { UIBlock } from './block';
|
||||||
import { DocumentToolCall, DocumentToolResult } from './document';
|
import { DocumentToolCall, DocumentToolResult } from './document';
|
||||||
import { SparklesIcon } from './icons';
|
import { SparklesIcon } from './icons';
|
||||||
import { Markdown } from './markdown';
|
import { Markdown } from './markdown';
|
||||||
|
|
@ -18,15 +18,15 @@ import { Weather } from './weather';
|
||||||
export const PreviewMessage = ({
|
export const PreviewMessage = ({
|
||||||
chatId,
|
chatId,
|
||||||
message,
|
message,
|
||||||
canvas,
|
block,
|
||||||
setCanvas,
|
setBlock,
|
||||||
vote,
|
vote,
|
||||||
isLoading,
|
isLoading,
|
||||||
}: {
|
}: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
message: Message;
|
message: Message;
|
||||||
canvas: UICanvas;
|
block: UIBlock;
|
||||||
setCanvas: Dispatch<SetStateAction<UICanvas>>;
|
setBlock: Dispatch<SetStateAction<UIBlock>>;
|
||||||
vote: Vote | undefined;
|
vote: Vote | undefined;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -71,22 +71,22 @@ export const PreviewMessage = ({
|
||||||
<DocumentToolResult
|
<DocumentToolResult
|
||||||
type="create"
|
type="create"
|
||||||
result={result}
|
result={result}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
/>
|
/>
|
||||||
) : toolName === 'updateDocument' ? (
|
) : toolName === 'updateDocument' ? (
|
||||||
<DocumentToolResult
|
<DocumentToolResult
|
||||||
type="update"
|
type="update"
|
||||||
result={result}
|
result={result}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
/>
|
/>
|
||||||
) : toolName === 'requestSuggestions' ? (
|
) : toolName === 'requestSuggestions' ? (
|
||||||
<DocumentToolResult
|
<DocumentToolResult
|
||||||
type="request-suggestions"
|
type="request-suggestions"
|
||||||
result={result}
|
result={result}
|
||||||
canvas={canvas}
|
block={block}
|
||||||
setCanvas={setCanvas}
|
setBlock={setBlock}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<pre>{JSON.stringify(result, null, 2)}</pre>
|
<pre>{JSON.stringify(result, null, 2)}</pre>
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,19 @@ import { useSWRConfig } from 'swr';
|
||||||
|
|
||||||
import { Suggestion } from '@/db/schema';
|
import { Suggestion } from '@/db/schema';
|
||||||
|
|
||||||
import { UICanvas } from './canvas';
|
import { UIBlock } from './block';
|
||||||
|
|
||||||
type StreamingDelta = {
|
type StreamingDelta = {
|
||||||
type: 'text-delta' | 'title' | 'id' | 'suggestion' | 'clear' | 'finish';
|
type: 'text-delta' | 'title' | 'id' | 'suggestion' | 'clear' | 'finish';
|
||||||
content: string | Suggestion;
|
content: string | Suggestion;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCanvasStream({
|
export function useBlockStream({
|
||||||
streamingData,
|
streamingData,
|
||||||
setCanvas,
|
setBlock,
|
||||||
}: {
|
}: {
|
||||||
streamingData: JSONValue[] | undefined;
|
streamingData: JSONValue[] | undefined;
|
||||||
setCanvas: Dispatch<SetStateAction<UICanvas>>;
|
setBlock: Dispatch<SetStateAction<UIBlock>>;
|
||||||
}) {
|
}) {
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const [optimisticSuggestions, setOptimisticSuggestions] = useState<
|
const [optimisticSuggestions, setOptimisticSuggestions] = useState<
|
||||||
|
|
@ -37,30 +37,30 @@ export function useCanvasStream({
|
||||||
|
|
||||||
const delta = mostRecentDelta as StreamingDelta;
|
const delta = mostRecentDelta as StreamingDelta;
|
||||||
|
|
||||||
setCanvas((draftCanvas) => {
|
setBlock((draftBlock) => {
|
||||||
switch (delta.type) {
|
switch (delta.type) {
|
||||||
case 'id':
|
case 'id':
|
||||||
return {
|
return {
|
||||||
...draftCanvas,
|
...draftBlock,
|
||||||
documentId: delta.content as string,
|
documentId: delta.content as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'title':
|
case 'title':
|
||||||
return {
|
return {
|
||||||
...draftCanvas,
|
...draftBlock,
|
||||||
title: delta.content as string,
|
title: delta.content as string,
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'text-delta':
|
case 'text-delta':
|
||||||
return {
|
return {
|
||||||
...draftCanvas,
|
...draftBlock,
|
||||||
content: draftCanvas.content + (delta.content as string),
|
content: draftBlock.content + (delta.content as string),
|
||||||
isVisible:
|
isVisible:
|
||||||
draftCanvas.status === 'streaming' &&
|
draftBlock.status === 'streaming' &&
|
||||||
draftCanvas.content.length > 200 &&
|
draftBlock.content.length > 200 &&
|
||||||
draftCanvas.content.length < 250
|
draftBlock.content.length < 250
|
||||||
? true
|
? true
|
||||||
: draftCanvas.isVisible,
|
: draftBlock.isVisible,
|
||||||
status: 'streaming',
|
status: 'streaming',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -72,24 +72,24 @@ export function useCanvasStream({
|
||||||
]);
|
]);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
return draftCanvas;
|
return draftBlock;
|
||||||
|
|
||||||
case 'clear':
|
case 'clear':
|
||||||
return {
|
return {
|
||||||
...draftCanvas,
|
...draftBlock,
|
||||||
content: '',
|
content: '',
|
||||||
status: 'streaming',
|
status: 'streaming',
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'finish':
|
case 'finish':
|
||||||
return {
|
return {
|
||||||
...draftCanvas,
|
...draftBlock,
|
||||||
status: 'idle',
|
status: 'idle',
|
||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return draftCanvas;
|
return draftBlock;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [streamingData, setCanvas]);
|
}, [streamingData, setBlock]);
|
||||||
}
|
}
|
||||||
|
|
@ -9,19 +9,19 @@ import { useWindowSize } from 'usehooks-ts';
|
||||||
import { Document } from '@/db/schema';
|
import { Document } from '@/db/schema';
|
||||||
import { getDocumentTimestampByIndex } from '@/lib/utils';
|
import { getDocumentTimestampByIndex } from '@/lib/utils';
|
||||||
|
|
||||||
import { UICanvas } from './canvas';
|
import { UIBlock } from './block';
|
||||||
import { LoaderIcon } from './icons';
|
import { LoaderIcon } from './icons';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
|
|
||||||
interface VersionFooterProps {
|
interface VersionFooterProps {
|
||||||
canvas: UICanvas;
|
block: UIBlock;
|
||||||
handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
|
handleVersionChange: (type: 'next' | 'prev' | 'toggle' | 'latest') => void;
|
||||||
documents: Array<Document> | undefined;
|
documents: Array<Document> | undefined;
|
||||||
currentVersionIndex: number;
|
currentVersionIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VersionFooter = ({
|
export const VersionFooter = ({
|
||||||
canvas,
|
block,
|
||||||
handleVersionChange,
|
handleVersionChange,
|
||||||
documents,
|
documents,
|
||||||
currentVersionIndex,
|
currentVersionIndex,
|
||||||
|
|
@ -56,8 +56,8 @@ export const VersionFooter = ({
|
||||||
setIsMutating(true);
|
setIsMutating(true);
|
||||||
|
|
||||||
mutate(
|
mutate(
|
||||||
`/api/document?id=${canvas.documentId}`,
|
`/api/document?id=${block.documentId}`,
|
||||||
await fetch(`/api/document?id=${canvas.documentId}`, {
|
await fetch(`/api/document?id=${block.documentId}`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
timestamp: getDocumentTimestampByIndex(
|
timestamp: getDocumentTimestampByIndex(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue