| import { memo } from 'react'; |
|
|
| import type { ArtifactKind } from './artifact'; |
| import { FileIcon, LoaderIcon, MessageIcon, PencilEditIcon } from './icons'; |
| import { toast } from 'sonner'; |
| import { useArtifact } from '@/hooks/use-artifact'; |
|
|
| const getActionText = ( |
| type: 'create' | 'update' | 'request-suggestions', |
| tense: 'present' | 'past', |
| ) => { |
| switch (type) { |
| case 'create': |
| return tense === 'present' ? 'Creating' : 'Created'; |
| case 'update': |
| return tense === 'present' ? 'Updating' : 'Updated'; |
| case 'request-suggestions': |
| return tense === 'present' |
| ? 'Adding suggestions' |
| : 'Added suggestions to'; |
| default: |
| return null; |
| } |
| }; |
|
|
| interface DocumentToolResultProps { |
| type: 'create' | 'update' | 'request-suggestions'; |
| result: { id: string; title: string; kind: ArtifactKind }; |
| isReadonly: boolean; |
| } |
|
|
| function PureDocumentToolResult({ |
| type, |
| result, |
| isReadonly, |
| }: DocumentToolResultProps) { |
| const { setArtifact } = useArtifact(); |
|
|
| return ( |
| <button |
| type="button" |
| className="bg-background cursor-pointer border py-2 px-3 rounded-xl w-fit flex flex-row gap-3 items-start" |
| onClick={(event) => { |
| if (isReadonly) { |
| toast.error( |
| 'Viewing files in shared chats is currently not supported.', |
| ); |
| return; |
| } |
| |
| const rect = event.currentTarget.getBoundingClientRect(); |
| |
| const boundingBox = { |
| top: rect.top, |
| left: rect.left, |
| width: rect.width, |
| height: rect.height, |
| }; |
| |
| setArtifact({ |
| documentId: result.id, |
| kind: result.kind, |
| content: '', |
| title: result.title, |
| isVisible: true, |
| status: 'idle', |
| boundingBox, |
| }); |
| }} |
| > |
| <div className="text-muted-foreground mt-1"> |
| {type === 'create' ? ( |
| <FileIcon /> |
| ) : type === 'update' ? ( |
| <PencilEditIcon /> |
| ) : type === 'request-suggestions' ? ( |
| <MessageIcon /> |
| ) : null} |
| </div> |
| <div className="text-left"> |
| {`${getActionText(type, 'past')} "${result.title}"`} |
| </div> |
| </button> |
| ); |
| } |
|
|
| export const DocumentToolResult = memo(PureDocumentToolResult, () => true); |
|
|
| interface DocumentToolCallProps { |
| type: 'create' | 'update' | 'request-suggestions'; |
| args: |
| | { title: string; kind: ArtifactKind } |
| | { id: string; description: string } |
| | { documentId: string }; |
| isReadonly: boolean; |
| } |
|
|
| function PureDocumentToolCall({ |
| type, |
| args, |
| isReadonly, |
| }: DocumentToolCallProps) { |
| const { setArtifact } = useArtifact(); |
|
|
| return ( |
| <button |
| type="button" |
| className="cursor pointer w-fit border py-2 px-3 rounded-xl flex flex-row items-start justify-between gap-3" |
| onClick={(event) => { |
| if (isReadonly) { |
| toast.error( |
| 'Viewing files in shared chats is currently not supported.', |
| ); |
| return; |
| } |
| |
| const rect = event.currentTarget.getBoundingClientRect(); |
| |
| const boundingBox = { |
| top: rect.top, |
| left: rect.left, |
| width: rect.width, |
| height: rect.height, |
| }; |
| |
| setArtifact((currentArtifact) => ({ |
| ...currentArtifact, |
| isVisible: true, |
| boundingBox, |
| })); |
| }} |
| > |
| <div className="flex flex-row gap-3 items-start"> |
| <div className="text-zinc-500 mt-1"> |
| {type === 'create' ? ( |
| <FileIcon /> |
| ) : type === 'update' ? ( |
| <PencilEditIcon /> |
| ) : type === 'request-suggestions' ? ( |
| <MessageIcon /> |
| ) : null} |
| </div> |
| |
| <div className="text-left"> |
| {`${getActionText(type, 'present')} ${ |
| type === 'create' && 'title' in args && args.title |
| ? `"${args.title}"` |
| : type === 'update' && 'description' in args |
| ? `"${args.description}"` |
| : type === 'request-suggestions' |
| ? 'for document' |
| : '' |
| }`} |
| </div> |
| </div> |
| |
| <div className="animate-spin mt-1">{<LoaderIcon />}</div> |
| </button> |
| ); |
| } |
|
|
| export const DocumentToolCall = memo(PureDocumentToolCall, () => true); |
|
|