| import { Artifact } from '@/components/create-artifact'; | |
| import { DiffView } from '@/components/diffview'; | |
| import { DocumentSkeleton } from '@/components/document-skeleton'; | |
| import { Editor } from '@/components/text-editor'; | |
| import { | |
| ClockRewind, | |
| CopyIcon, | |
| MessageIcon, | |
| PenIcon, | |
| RedoIcon, | |
| UndoIcon, | |
| } from '@/components/icons'; | |
| import type { Suggestion } from '@/lib/db/schema'; | |
| import { toast } from 'sonner'; | |
| import { getSuggestions } from '../actions'; | |
| interface TextArtifactMetadata { | |
| suggestions: Array<Suggestion>; | |
| } | |
| export const textArtifact = new Artifact<'text', TextArtifactMetadata>({ | |
| kind: 'text', | |
| description: 'Useful for text content, like drafting essays and emails.', | |
| initialize: async ({ documentId, setMetadata }) => { | |
| const suggestions = await getSuggestions({ documentId }); | |
| setMetadata({ | |
| suggestions, | |
| }); | |
| }, | |
| onStreamPart: ({ streamPart, setMetadata, setArtifact }) => { | |
| if (streamPart.type === 'data-suggestion') { | |
| setMetadata((metadata) => { | |
| return { | |
| suggestions: [...metadata.suggestions, streamPart.data], | |
| }; | |
| }); | |
| } | |
| if (streamPart.type === 'data-textDelta') { | |
| setArtifact((draftArtifact) => { | |
| return { | |
| ...draftArtifact, | |
| content: draftArtifact.content + streamPart.data, | |
| isVisible: | |
| draftArtifact.status === 'streaming' && | |
| draftArtifact.content.length > 400 && | |
| draftArtifact.content.length < 450 | |
| ? true | |
| : draftArtifact.isVisible, | |
| status: 'streaming', | |
| }; | |
| }); | |
| } | |
| }, | |
| content: ({ | |
| mode, | |
| status, | |
| content, | |
| isCurrentVersion, | |
| currentVersionIndex, | |
| onSaveContent, | |
| getDocumentContentById, | |
| isLoading, | |
| metadata, | |
| }) => { | |
| if (isLoading) { | |
| return <DocumentSkeleton artifactKind="text" />; | |
| } | |
| if (mode === 'diff') { | |
| const oldContent = getDocumentContentById(currentVersionIndex - 1); | |
| const newContent = getDocumentContentById(currentVersionIndex); | |
| return <DiffView oldContent={oldContent} newContent={newContent} />; | |
| } | |
| return ( | |
| <> | |
| <div className="flex flex-row py-8 md:p-20 px-4"> | |
| <Editor | |
| content={content} | |
| suggestions={metadata ? metadata.suggestions : []} | |
| isCurrentVersion={isCurrentVersion} | |
| currentVersionIndex={currentVersionIndex} | |
| status={status} | |
| onSaveContent={onSaveContent} | |
| /> | |
| {metadata?.suggestions && metadata.suggestions.length > 0 ? ( | |
| <div className="md:hidden h-dvh w-12 shrink-0" /> | |
| ) : null} | |
| </div> | |
| </> | |
| ); | |
| }, | |
| actions: [ | |
| { | |
| icon: <ClockRewind size={18} />, | |
| description: 'View changes', | |
| onClick: ({ handleVersionChange }) => { | |
| handleVersionChange('toggle'); | |
| }, | |
| isDisabled: ({ currentVersionIndex, setMetadata }) => { | |
| if (currentVersionIndex === 0) { | |
| return true; | |
| } | |
| return false; | |
| }, | |
| }, | |
| { | |
| icon: <UndoIcon size={18} />, | |
| description: 'View Previous version', | |
| onClick: ({ handleVersionChange }) => { | |
| handleVersionChange('prev'); | |
| }, | |
| isDisabled: ({ currentVersionIndex }) => { | |
| if (currentVersionIndex === 0) { | |
| return true; | |
| } | |
| return false; | |
| }, | |
| }, | |
| { | |
| icon: <RedoIcon size={18} />, | |
| description: 'View Next version', | |
| onClick: ({ handleVersionChange }) => { | |
| handleVersionChange('next'); | |
| }, | |
| isDisabled: ({ isCurrentVersion }) => { | |
| if (isCurrentVersion) { | |
| return true; | |
| } | |
| return false; | |
| }, | |
| }, | |
| { | |
| icon: <CopyIcon size={18} />, | |
| description: 'Copy to clipboard', | |
| onClick: ({ content }) => { | |
| navigator.clipboard.writeText(content); | |
| toast.success('Copied to clipboard!'); | |
| }, | |
| }, | |
| ], | |
| toolbar: [ | |
| { | |
| icon: <PenIcon />, | |
| description: 'Add final polish', | |
| onClick: ({ sendMessage }) => { | |
| sendMessage({ | |
| role: 'user', | |
| parts: [ | |
| { | |
| type: 'text', | |
| text: 'Please add final polish and check for grammar, add section titles for better structure, and ensure everything reads smoothly.', | |
| }, | |
| ], | |
| }); | |
| }, | |
| }, | |
| { | |
| icon: <MessageIcon />, | |
| description: 'Request suggestions', | |
| onClick: ({ sendMessage }) => { | |
| sendMessage({ | |
| role: 'user', | |
| parts: [ | |
| { | |
| type: 'text', | |
| text: 'Please add suggestions you have that could improve the writing.', | |
| }, | |
| ], | |
| }); | |
| }, | |
| }, | |
| ], | |
| }); | |