diff --git a/ai/models.ts b/ai/models.ts index 81fda0b..f68fe32 100644 --- a/ai/models.ts +++ b/ai/models.ts @@ -21,8 +21,8 @@ export const models: Array = [ description: 'For complex, multi-step tasks', }, { - id: 'gpt-4o-canvas', - label: 'GPT 4o with Canvas', + id: 'gpt-4o-blocks', + label: 'GPT 4o with Blocks', apiIdentifier: 'gpt-4o', description: 'Collaborate with writing', }, diff --git a/ai/prompts.ts b/ai/prompts.ts index aede90f..535c030 100644 --- a/ai/prompts.ts +++ b/ai/prompts.ts @@ -1,7 +1,7 @@ -export const canvasPrompt = ` - 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. +export const blocksPrompt = ` + 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\`:** - For substantial content (>10 lines) diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 0543c61..ec73b3e 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -9,7 +9,7 @@ import { z } from 'zod'; import { customModel } from '@/ai'; import { models } from '@/ai/models'; -import { canvasPrompt, regularPrompt } from '@/ai/prompts'; +import { blocksPrompt, regularPrompt } from '@/ai/prompts'; import { auth } from '@/app/(auth)/auth'; import { deleteChatById, @@ -37,7 +37,7 @@ type AllowedTools = | 'requestSuggestions' | 'getWeather'; -const canvasTools: AllowedTools[] = [ +const blocksTools: AllowedTools[] = [ 'createDocument', 'updateDocument', 'requestSuggestions', @@ -89,11 +89,11 @@ export async function POST(request: Request) { const result = await streamText({ model: customModel(model.apiIdentifier), - system: modelId === 'gpt-4o-canvas' ? canvasPrompt : regularPrompt, + system: modelId === 'gpt-4o-blocks' ? blocksPrompt : regularPrompt, messages: coreMessages, maxSteps: 5, experimental_activeTools: - modelId === 'gpt-4o-canvas' ? canvasTools : weatherTools, + modelId === 'gpt-4o-blocks' ? blocksTools : weatherTools, tools: { getWeather: { description: 'Get the current weather at a location', diff --git a/components/custom/canvas-stream-handler.tsx b/components/custom/block-stream-handler.tsx similarity index 52% rename from components/custom/canvas-stream-handler.tsx rename to components/custom/block-stream-handler.tsx index 29a624f..541b5ea 100644 --- a/components/custom/canvas-stream-handler.tsx +++ b/components/custom/block-stream-handler.tsx @@ -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>; +interface BlockStreamHandlerProps { + setBlock: Dispatch>; 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); diff --git a/components/custom/canvas.tsx b/components/custom/block.tsx similarity index 90% rename from components/custom/canvas.tsx rename to components/custom/block.tsx index 10c1844..d01bd4c 100644 --- a/components/custom/canvas.tsx +++ b/components/custom/block.tsx @@ -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; setAttachments: Dispatch>>; - canvas: UICanvas; - setCanvas: Dispatch>; + block: UIBlock; + setBlock: Dispatch>; messages: Array; setMessages: Dispatch>>; votes: Array | undefined; @@ -92,15 +92,15 @@ export function Canvas({ isLoading: isDocumentsFetching, mutate: mutateDocuments, } = useSWR>( - canvas && canvas.status !== 'streaming' - ? `/api/document?id=${canvas.documentId}` + block && block.status !== 'streaming' + ? `/api/document?id=${block.documentId}` : null, fetcher ); const { data: suggestions } = useSWR>( - 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>( - `/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({
- {document?.title ?? canvas.title} + {document?.title ?? block.title}
{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'} > @@ -461,7 +461,7 @@ export function Canvas({ handleVersionChange('prev'); }} disabled={ - currentVersionIndex === 0 || canvas.status === 'streaming' + currentVersionIndex === 0 || block.status === 'streaming' } > @@ -477,7 +477,7 @@ export function Canvas({ onClick={() => { handleVersionChange('next'); }} - disabled={isCurrentVersion || canvas.status === 'streaming'} + disabled={isCurrentVersion || block.status === 'streaming'} > @@ -498,7 +498,7 @@ export function Canvas({ handleVersionChange('toggle'); }} disabled={ - canvas.status === 'streaming' || currentVersionIndex === 0 + block.status === 'streaming' || currentVersionIndex === 0 } > @@ -511,18 +511,18 @@ export function Canvas({
- {isDocumentsFetching && !canvas.content ? ( + {isDocumentsFetching && !block.content ? ( ) : mode === 'edit' ? ( @@ -555,7 +555,7 @@ export function Canvas({ {!isCurrentVersion && ( ({ + const [block, setBlock] = useState({ 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({
- {canvas && canvas.isVisible && ( - - + ); } diff --git a/components/custom/document.tsx b/components/custom/document.tsx index 472a80a..796b136 100644 --- a/components/custom/document.tsx +++ b/components/custom/document.tsx @@ -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) => void; + block: UIBlock; + setBlock: (value: SetStateAction) => void; } export function DocumentToolResult({ type, result, - canvas, - setCanvas, + block, + setBlock, }: DocumentToolResultProps) { return (
>; + block: UIBlock; + setBlock: Dispatch>; vote: Vote | undefined; isLoading: boolean; }) => { @@ -71,22 +71,22 @@ export const PreviewMessage = ({ ) : toolName === 'updateDocument' ? ( ) : toolName === 'requestSuggestions' ? ( ) : (
{JSON.stringify(result, null, 2)}
diff --git a/components/custom/use-canvas-stream.tsx b/components/custom/use-block-stream.tsx similarity index 73% rename from components/custom/use-canvas-stream.tsx rename to components/custom/use-block-stream.tsx index 3deb552..8871fa1 100644 --- a/components/custom/use-canvas-stream.tsx +++ b/components/custom/use-block-stream.tsx @@ -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>; + setBlock: Dispatch>; }) { 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]); } diff --git a/components/custom/version-footer.tsx b/components/custom/version-footer.tsx index 5440c6c..bb30cb5 100644 --- a/components/custom/version-footer.tsx +++ b/components/custom/version-footer.tsx @@ -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 | 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(